التوثيق المتقدم لمنصة Bun.js
دليل مفصل للميزات المتقدمة والعناصر الإضافية في Bun.js
واجهة الدوال الخارجية (FFI)
تتيح واجهة الدوال الخارجية (Foreign Function Interface) في Bun.js استدعاء الوظائف المكتوبة بلغات أخرى مثل C و C++ و Rust مباشرة من JavaScript، مما يوفر أداءً عالياً للمهام كثيفة الحوسبة.
استخدام FFI الأساسي
لاستخدام FFI في Bun.js، تحتاج إلى استيراد وحدة bun:ffi
:
import { dlopen, FFIType, ptr, CString } from "bun:ffi";
تحميل مكتبة خارجية
يمكنك تحميل المكتبات الديناميكية باستخدام وظيفة dlopen
:
// math.c
/*
#include
int add(int a, int b) {
return a + b;
}
float multiply(float a, float b) {
return a * b;
}
*/
// تجميع المكتبة
// gcc -shared -o libmath.so -fPIC math.c
// في JavaScript
import { dlopen, FFIType } from "bun:ffi";
// تحميل المكتبة المجمعة
const mathLib = dlopen("./libmath.so", {
// تعريف واجهات الدوال
add: {
// نوع الوسيطات
args: [FFIType.i32, FFIType.i32],
// نوع القيمة المرجعة
returns: FFIType.i32
},
multiply: {
args: [FFIType.f32, FFIType.f32],
returns: FFIType.f32
}
});
// استدعاء الدوال
const sum = mathLib.symbols.add(40, 2);
console.log("المجموع:", sum); // المجموع: 42
const product = mathLib.symbols.multiply(3.5, 2.0);
console.log("الناتج:", product); // الناتج: 7
أنواع البيانات المدعومة
تدعم FFI في Bun.js العديد من أنواع البيانات:
// أنواع البيانات الأساسية المدعومة
const types = {
void: FFIType.void, // بدون قيمة عائدة
bool: FFIType.bool, // قيمة منطقية
u8: FFIType.u8, // عدد صحيح 8-بت غير موقع
i8: FFIType.i8, // عدد صحيح 8-بت موقع
u16: FFIType.u16, // عدد صحيح 16-بت غير موقع
i16: FFIType.i16, // عدد صحيح 16-بت موقع
u32: FFIType.u32, // عدد صحيح 32-بت غير موقع
i32: FFIType.i32, // عدد صحيح 32-بت موقع
u64: FFIType.u64, // عدد صحيح 64-بت غير موقع
i64: FFIType.i64, // عدد صحيح 64-بت موقع
f32: FFIType.f32, // عدد عشري 32-بت
f64: FFIType.f64, // عدد عشري 64-بت
ptr: FFIType.ptr, // مؤشر
cstring: FFIType.cstring // سلسلة نصية بنهاية C-style
};
التعامل مع السلاسل النصية والمؤشرات
للتعامل مع السلاسل النصية والمؤشرات:
// التعامل مع السلاسل النصية
import { dlopen, FFIType, CString, ptr } from "bun:ffi";
// تحميل مكتبة C تتضمن دالة للتعامل مع السلاسل النصية
const strLib = dlopen("./libstring.so", {
concatStrings: {
args: [FFIType.cstring, FFIType.cstring],
returns: FFIType.cstring
},
getStringLength: {
args: [FFIType.cstring],
returns: FFIType.i32
}
});
// إنشاء سلسلة نصية بتنسيق C
const firstName = new CString("أحمد");
const lastName = new CString("محمد");
// استدعاء دالة من المكتبة
const fullName = strLib.symbols.concatStrings(firstName, lastName);
console.log("الاسم الكامل:", fullName.toString());
// التحكم في تحرير الذاكرة
firstName.free(); // تحرير الذاكرة المستخدمة
lastName.free(); // تحرير الذاكرة المستخدمة
استخدام FFI مع Rust
يمكن أيضًا استخدام FFI مع مكتبات Rust:
// lib.rs
#[no_mangle]
pub extern "C" fn fibonacci(n: u32) -> u32 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
تجميع كود Rust إلى مكتبة مشتركة:
# تجميع مكتبة Rust
rustc --crate-type=cdylib lib.rs -o libfib.so
استخدام المكتبة في Bun.js:
import { dlopen, FFIType } from "bun:ffi";
const lib = dlopen("./libfib.so", {
fibonacci: {
args: [FFIType.u32],
returns: FFIType.u32
}
});
// حساب أرقام فيبوناتشي
for (let i = 0; i < 10; i++) {
console.log(`فيبوناتشي(${i}) = ${lib.symbols.fibonacci(i)}`);
}
ملاحظة أمنية هامة
استخدام FFI يتيح وصولاً مباشراً إلى ذاكرة النظام، لذا يجب توخي الحذر عند استخدامه. تأكد من التحقق من الإدخالات وتحرير الذاكرة عند الانتهاء لتجنب تسرب الذاكرة.
العمليات المتوازية (Workers)
توفر Bun.js دعمًا للعمليات المتوازية، مما يسمح بتنفيذ الكود في مواضيع متعددة لتحسين الأداء وتحقيق التوازي في تنفيذ المهام.
العمليات الفرعية (Worker Threads)
يمكن استخدام العمليات الفرعية لتنفيذ المهام المعقدة والطويلة دون حظر الحلقة الرئيسية:
// main.js - الملف الرئيسي
import { Worker } from "bun";
// إنشاء عملية فرعية جديدة
const worker = new Worker("./worker.js");
// إرسال رسالة إلى العملية الفرعية
worker.postMessage({ type: "CALCULATE", data: { numbers: [1, 2, 3, 4, 5] } });
// استلام رسائل من العملية الفرعية
worker.addEventListener("message", (event) => {
console.log("نتيجة من العملية الفرعية:", event.data);
// إنهاء العملية عندما نستلم النتيجة
if (event.data.type === "RESULT") {
worker.terminate();
}
});
// معالجة الأخطاء
worker.addEventListener("error", (error) => {
console.error("حدث خطأ في العملية الفرعية:", error);
});
وملف العملية الفرعية:
// worker.js - ملف العملية الفرعية
// استلام الرسائل من العملية الرئيسية
self.addEventListener("message", async (event) => {
const { type, data } = event.data;
console.log("استلام مهمة:", type);
if (type === "CALCULATE") {
// تنفيذ عملية حسابية معقدة
const result = await performHeavyCalculation(data.numbers);
// إرسال النتيجة إلى العملية الرئيسية
self.postMessage({ type: "RESULT", result });
}
});
// دالة لتنفيذ عملية حسابية (مثال لعملية معقدة)
async function performHeavyCalculation(numbers) {
// محاكاة عملية تستغرق وقتًا
await new Promise(resolve => setTimeout(resolve, 2000));
// حساب مجموع المربعات مثلاً
return numbers.map(n => n * n).reduce((sum, n) => sum + n, 0);
}
مجموعات العمليات (Worker Pools)
يمكن إنشاء مجموعة من العمليات الفرعية لمعالجة المهام المتوازية بشكل أكثر كفاءة:
// worker-pool.js
import { Worker } from "bun";
class WorkerPool {
constructor(workerPath, numWorkers = 4) {
this.workerPath = workerPath;
this.numWorkers = numWorkers;
this.workers = [];
this.taskQueue = [];
this.availableWorkers = [];
this.initialize();
}
// تهيئة مجموعة العمليات
initialize() {
for (let i = 0; i < this.numWorkers; i++) {
const worker = new Worker(this.workerPath);
worker.addEventListener("message", (event) => {
const taskId = event.data.taskId;
const result = event.data.result;
// إيجاد المهمة المقترنة بهذا العامل
const taskIndex = this.taskQueue.findIndex(task => task.id === taskId);
if (taskIndex !== -1) {
const task = this.taskQueue[taskIndex];
// استدعاء دالة الاستجابة مع النتيجة
task.resolve(result);
// إزالة المهمة من قائمة الانتظار
this.taskQueue.splice(taskIndex, 1);
}
// إعادة العامل إلى قائمة المتاحين
this.availableWorkers.push(worker);
// معالجة المهمة التالية إن وجدت
this.processQueue();
});
// إضافة العامل إلى قائمة المتاحين
this.availableWorkers.push(worker);
// إضافة العامل إلى قائمة العاملين
this.workers.push(worker);
}
}
// إضافة مهمة جديدة إلى القائمة
async runTask(data) {
return new Promise((resolve, reject) => {
const taskId = Date.now() + Math.random().toString(36).substring(2, 10);
// إضافة المهمة إلى قائمة الانتظار
this.taskQueue.push({
id: taskId,
data,
resolve,
reject
});
// معالجة القائمة
this.processQueue();
});
}
// معالجة قائمة المهام
processQueue() {
// إذا كانت هناك مهام في الانتظار وعمال متاحون
if (this.taskQueue.length > 0 && this.availableWorkers.length > 0) {
// استخراج عامل متاح
const worker = this.availableWorkers.pop();
// استخراج المهمة التالية من القائمة
const task = this.taskQueue[0];
// إرسال المهمة إلى العامل
worker.postMessage({
taskId: task.id,
data: task.data
});
}
}
// تنظيف الموارد
terminate() {
for (const worker of this.workers) {
worker.terminate();
}
this.workers = [];
this.availableWorkers = [];
}
}
// استخدام مجموعة العمليات
const pool = new WorkerPool("./calculation-worker.js", 4);
// تنفيذ عدة مهام متوازية
async function runTasks() {
const tasks = [
{ numbers: [1, 2, 3, 4, 5] },
{ numbers: [10, 20, 30] },
{ numbers: [2, 4, 6, 8, 10] },
{ numbers: [1, 3, 5, 7, 9] },
{ numbers: [5, 10, 15, 20] },
{ numbers: [100, 200, 300] }
];
// تنفيذ جميع المهام وانتظار النتائج
const results = await Promise.all(
tasks.map(task => pool.runTask(task))
);
console.log("جميع النتائج:", results);
// تنظيف موارد المجموعة
pool.terminate();
}
runTasks();
وملف العملية الفرعية للحسابات:
// calculation-worker.js
// استلام المهام
self.addEventListener("message", async (event) => {
const { taskId, data } = event.data;
// تنفيذ الحسابات
const result = await performCalculation(data.numbers);
// إعادة النتيجة مع معرف المهمة
self.postMessage({
taskId,
result
});
});
// دالة الحساب
async function performCalculation(numbers) {
// محاكاة عملية حسابية تستغرق وقتًا
await new Promise(resolve => setTimeout(resolve, 500 + Math.random() * 1000));
// حساب مجموع المربعات
return {
sum: numbers.reduce((sum, n) => sum + n, 0),
sumOfSquares: numbers.map(n => n * n).reduce((sum, n) => sum + n, 0),
count: numbers.length,
original: numbers
};
}
عمليات النظام (Child Processes)
يمكن أيضًا تنفيذ عمليات نظام خارجية باستخدام Bun.spawn
:
import { spawn } from "bun";
// تنفيذ أمر نظام
const proc = spawn({
cmd: ["ls", "-la"], // أمر للتنفيذ وأي معلمات
stdout: "pipe", // توجيه المخرج القياسي
stderr: "pipe", // توجيه مخرج الأخطاء
env: { // متغيرات بيئية إضافية
CUSTOM_VAR: "value"
}
});
// قراءة المخرج
const output = await new Response(proc.stdout).text();
console.log("مخرج الأمر:", output);
// التحقق من حالة انتهاء العملية
const exitCode = await proc.exited;
console.log("رمز الخروج:", exitCode);
ممارسات جيدة للعمليات المتوازية
- استخدم العمليات الفرعية للمهام كثيفة الحوسبة لتجنب حظر الخيط الرئيسي.
- حدد عدد العمليات المتوازية بناءً على عدد النوى المتاحة في النظام.
- تأكد من إنهاء العمليات عند الانتهاء منها لتجنب تسرب الموارد.
- استخدم آليات القفل عند الوصول إلى الموارد المشتركة لتجنب مشاكل التزامن.
استبدال الوحدات الساخن (HMR)
استبدال الوحدات الساخن (Hot Module Replacement) هو ميزة تتيح تحديث أجزاء من التطبيق أثناء التشغيل دون الحاجة إلى إعادة تحميل الصفحة بالكامل أو إعادة تشغيل الخادم.
تمكين HMR في Bun.js
يمكنك تمكين HMR عند تشغيل خادم التطوير:
# تشغيل التطبيق مع تمكين المراقبة والتحديث التلقائي
bun --hot run index.ts
استخدام HMR في تطبيق بسيط
إليك مثال لاستخدام HMR في تطبيق بسيط:
// index.ts - الملف الرئيسي للتطبيق
import { serve } from "bun";
import { renderMessage } from "./message";
// تنفيذ عند تحديث الوحدة
if (import.meta.hot) {
import.meta.hot.accept();
console.log("تم تحديث التطبيق!");
}
// إنشاء خادم HTTP
serve({
port: 3000,
fetch(req) {
return new Response(
`
تطبيق HMR مع Bun
مرحبًا بالعالم!
${renderMessage()}
`,
{
headers: { "Content-Type": "text/html" },
}
);
},
error(error) {
return new Response(`حدث خطأ: ${error.message}`, { status: 500 });
},
});
ملف الرسالة القابل للتحديث:
// message.ts - وحدة قابلة للتحديث
export function renderMessage() {
return `الوقت الحالي: ${new Date().toLocaleTimeString()}. جرب تعديل هذا الملف وشاهد التغييرات تظهر تلقائيًا!`;
}
ملف العميل لتفعيل HMR في المتصفح:
// client.js - تنفيذ في المتصفح
// إنشاء اتصال WebSocket للتحديثات التلقائية
let socket;
function connect() {
socket = new WebSocket(`ws://${location.host}/_hmr`);
socket.addEventListener("open", () => {
console.log("تم الاتصال بخادم HMR");
});
socket.addEventListener("message", (event) => {
const data = JSON.parse(event.data);
if (data.type === "update") {
// تحديث الصفحة عند تغيير الملفات
console.log("تم العثور على تحديثات، إعادة تحميل الصفحة...");
location.reload();
}
});
socket.addEventListener("close", () => {
console.log("انقطع الاتصال بخادم HMR، إعادة المحاولة...");
setTimeout(connect, 1000);
});
}
// بدء الاتصال
connect();
استخدام HMR مع إطارات العمل الشهيرة
يعمل Bun.js بشكل جيد مع إطارات العمل التي تدعم HMR:
// مثال لاستخدام HMR مع React
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
hmr: true
}
});
// في ملف React Component
import React, { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
تطبيق React مع HMR
العدد: {count}
{/* جرب تغيير هذا النص وشاهد التحديث التلقائي */}
هذا النص سيتم تحديثه دون فقدان حالة التطبيق
);
}
export default App;
فوائد استخدام HMR
- تسريع عملية التطوير عن طريق رؤية التغييرات فوراً دون إعادة التحميل
- الحفاظ على حالة التطبيق أثناء التطوير (مثل قيم النماذج، وحالة التمرير، وما إلى ذلك)
- تقليل دورة التغيير-الاختبار-التحقق
- تجربة مستخدم أفضل أثناء التطوير
مكتبات التشفير في Bun.js
توفر Bun.js مجموعة من واجهات برمجة التطبيقات المدمجة للتشفير والأمان، مما يسهل تنفيذ وظائف التشفير والتجزئة والتوقيع الرقمي.
التجزئة (Hashing)
يمكن استخدام دوال التجزئة لإنشاء بصمات فريدة للبيانات:
import { crypto } from "bun";
// إنشاء تجزئة MD5
const md5Hash = await crypto.subtle.digest(
"MD5",
new TextEncoder().encode("نص للتجزئة")
);
// تحويل إلى تنسيق هكس
const md5Hex = Array.from(new Uint8Array(md5Hash))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
console.log("MD5:", md5Hex);
// تجزئة SHA-256
const sha256Hash = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode("نص للتجزئة")
);
const sha256Hex = Array.from(new Uint8Array(sha256Hash))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
console.log("SHA-256:", sha256Hex);
التشفير والفك (Encryption & Decryption)
يمكن تشفير البيانات وفك تشفيرها باستخدام خوارزميات مختلفة:
import { crypto } from "bun";
// إنشاء مفتاح AES
const key = await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256
},
true,
["encrypt", "decrypt"]
);
// النص المراد تشفيره
const data = new TextEncoder().encode("بيانات سرية للتشفير");
// إنشاء متجه التهيئة (IV)
const iv = crypto.getRandomValues(new Uint8Array(12));
// تشفير البيانات
const encryptedData = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv
},
key,
data
);
console.log("البيانات المشفرة:", new Uint8Array(encryptedData));
// فك تشفير البيانات
const decryptedData = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv
},
key,
encryptedData
);
const decryptedText = new TextDecoder().decode(decryptedData);
console.log("النص بعد فك التشفير:", decryptedText);
توليد الأرقام العشوائية
يمكن توليد أرقام عشوائية آمنة للاستخدام في التطبيقات الأمنية:
import { crypto } from "bun";
// إنشاء مصفوفة من الأرقام العشوائية
const randomBytes = crypto.getRandomValues(new Uint8Array(16));
console.log("أرقام عشوائية:", randomBytes);
// إنشاء UUID (v4)
function generateUUID() {
const bytes = crypto.getRandomValues(new Uint8Array(16));
// تعديل البتات حسب مواصفات UUID v4
bytes[6] = (bytes[6] & 0x0f) | 0x40;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
// تحويل إلى تنسيق UUID
const hex = Array.from(bytes)
.map(b => b.toString(16).padStart(2, "0"))
.join("");
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}
const uuid = generateUUID();
console.log("UUID:", uuid);
تجزئة كلمات المرور
لتجزئة وتحقق من كلمات المرور بأمان باستخدام Bun.js:
import { password } from "bun";
// تجزئة كلمة المرور
const hash = await password.hash("كلمة_المرور_السرية");
console.log("تجزئة كلمة المرور:", hash);
// التحقق من كلمة المرور
const isMatch = await password.verify("كلمة_المرور_السرية", hash);
console.log("هل كلمة المرور صحيحة؟", isMatch);
// التحقق من كلمة مرور خاطئة
const isWrongMatch = await password.verify("كلمة_مرور_خاطئة", hash);
console.log("هل كلمة المرور الخاطئة صحيحة؟", isWrongMatch);
التوقيعات الرقمية (HMAC)
إنشاء والتحقق من التوقيعات الرقمية:
import { crypto } from "bun";
// إنشاء مفتاح HMAC
const key = await crypto.subtle.generateKey(
{
name: "HMAC",
hash: "SHA-256"
},
true,
["sign", "verify"]
);
// البيانات المراد توقيعها
const data = new TextEncoder().encode("بيانات للتوقيع");
// إنشاء التوقيع
const signature = await crypto.subtle.sign(
"HMAC",
key,
data
);
console.log("التوقيع:", new Uint8Array(signature));
// التحقق من التوقيع
const isValid = await crypto.subtle.verify(
"HMAC",
key,
signature,
data
);
console.log("هل التوقيع صالح؟", isValid);
ملاحظات أمنية
- استخدم دائمًا خوارزميات تشفير قوية مثل AES-GCM للتشفير.
- تجنب استخدام MD5 أو SHA-1 للتطبيقات الأمنية حيث أنها تعتبر غير آمنة حاليًا.
- احرص على تخزين مفاتيح التشفير بأمان وعدم مشاركتها في التعليمات البرمجية.
- استخدم دائمًا متجه تهيئة (IV) جديد لكل عملية تشفير.
نظام البرمجيات الإضافية (Plugins)
يوفر Bun.js نظامًا للبرمجيات الإضافية (Plugins) يسمح بتوسيع وظائف المجمع (bundler) والإعدادات الأخرى. يمكن استخدام هذه البرمجيات الإضافية للتعامل مع أنواع ملفات مختلفة أو تحويل الكود أو إضافة وظائف مخصصة.
إنشاء برمجية إضافية بسيطة
إنشاء برمجية إضافية بسيطة لتحويل ملفات YAML إلى JSON:
// yaml-plugin.ts
import { BunPlugin } from "bun";
import yaml from "js-yaml"; // تحتاج إلى تثبيت هذه الحزمة
export default {
name: "YAML Loader",
setup(build) {
// استجابة لملفات بامتداد yaml أو yml
build.onLoad({ filter: /\.(yaml|yml)$/ }, async (args) => {
// قراءة ملف YAML
const text = await Bun.file(args.path).text();
// تحويل إلى JSON
const data = yaml.load(text);
// إعادة المحتوى كوحدة JavaScript
return {
exports: data,
loader: "object" // استخدام محمل الكائن (object loader)
};
});
}
} satisfies BunPlugin;
استخدام البرمجية الإضافية
لاستخدام البرمجية الإضافية التي أنشأناها، قم بتكوين Bun.js:
// bunfig.toml
[plugins]
paths = ["./yaml-plugin.ts"]
# أو يمكنك استيرادها مباشرة في ملف البناء
// build.ts
import { build } from "bun";
import yamlPlugin from "./yaml-plugin";
await build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
plugins: [yamlPlugin]
});
استيراد ملف YAML
الآن يمكنك استيراد ملفات YAML مباشرة في الكود:
// config.yaml
name: "My Application"
version: 1.0.0
settings:
debug: true
maxConnections: 100
database:
host: "localhost"
port: 5432
user: "admin"
// index.ts
import config from "./config.yaml";
console.log(`اسم التطبيق: ${config.name}`);
console.log(`إصدار: ${config.version}`);
console.log(`وضع التصحيح: ${config.settings.debug}`);
console.log(`مضيف قاعدة البيانات: ${config.database.host}`);
برمجية إضافية لتحويل الكود
يمكنك إنشاء برمجية إضافية لتحويل الكود، مثل استبدال النص في وقت البناء:
// replace-plugin.ts
import { BunPlugin } from "bun";
export interface ReplacePluginOptions {
replacements: [string, string][];
}
export default function replacePlugin(options: ReplacePluginOptions): BunPlugin {
return {
name: "Replace Plugin",
setup(build) {
build.onLoad({ filter: /\.(js|ts|jsx|tsx)$/ }, async (args) => {
// قراءة ملف المصدر
let source = await Bun.file(args.path).text();
// تطبيق الاستبدالات
for (const [find, replace] of options.replacements) {
source = source.replace(new RegExp(find, "g"), replace);
}
// إعادة الكود المعدل
return {
contents: source,
loader: args.path.endsWith(".ts") ? "ts" :
args.path.endsWith(".tsx") ? "tsx" :
args.path.endsWith(".jsx") ? "jsx" : "js"
};
});
}
};
}
استخدام برمجية التحويل:
// build.ts
import { build } from "bun";
import replacePlugin from "./replace-plugin";
// استخدام برمجية الاستبدال
await build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
plugins: [
replacePlugin({
replacements: [
["__VERSION__", "1.2.3"],
["__BUILD_DATE__", new Date().toISOString()],
["__DEBUG__", process.env.NODE_ENV === "development" ? "true" : "false"]
]
})
]
});
أنواع البرمجيات الإضافية الشائعة
- محمل الملفات: لاستيراد أنواع ملفات غير مدعومة افتراضيًا (CSV, Markdown, إلخ)
- محولات الكود: لتحويل الكود قبل أو بعد عملية البناء
- توليد الكود: لتوليد ملفات أو كود في وقت البناء
- تحسين الكود: لتحسين أو تصغير الكود
- إثراء البيانات الوصفية: لإضافة معلومات وصفية للتعريف
تتبع الأخطاء وتصحيحها
توفر Bun.js عدة طرق لتتبع الأخطاء وتصحيحها في التطبيقات، مما يساعد المطورين على تحديد وإصلاح المشكلات بسرعة.
وضع التصحيح الأساسي
يمكنك تشغيل Bun.js في وضع التصحيح باستخدام المتغير البيئي DEBUG
:
# تفعيل التصحيح العام
DEBUG=* bun run index.ts
# تفعيل التصحيح لمكونات محددة
DEBUG=http,server,db bun run index.ts
استخدام مصحح Node.js
يمكن استخدام مصحح Node.js المدمج مع Bun.js:
# تشغيل المصحح
bun --inspect index.ts
# تشغيل المصحح والانتظار للاتصال قبل التنفيذ
bun --inspect-brk index.ts
ثم يمكنك الاتصال بالمصحح باستخدام Chrome DevTools أو VS Code:
1. افتح Chrome وانتقل إلى chrome://inspect
2. انقر على "Open dedicated DevTools for Node"
3. سترى الجلسة المتاحة في قسم "Remote Target"
4. انقر على "inspect" للاتصال بالمصحح
إعداد نقاط التوقف في الكود
يمكنك إضافة كلمة debugger
في الكود لإنشاء نقطة توقف:
function processData(data) {
// سيتوقف المصحح هنا عند تنفيذ هذا السطر
debugger;
// التحقق من البيانات المدخلة
if (!data || !data.items || !Array.isArray(data.items)) {
throw new Error("بيانات غير صالحة");
}
// معالجة البيانات
const results = data.items.map(item => {
// يمكنك أيضًا وضع نقطة توقف أخرى هنا
debugger;
return {
id: item.id,
name: item.name,
value: item.value * 2
};
});
return results;
}
تتبع الأخطاء المتقدم
استخدام واجهة تتبع الأخطاء المدمجة لتحديد المشكلات:
try {
// كود قد يسبب خطأ
const result = complexOperation();
console.log("النتيجة:", result);
} catch (error) {
// طباعة رسالة الخطأ
console.error("حدث خطأ:", error.message);
// طباعة تتبع المكدس (stack trace)
console.error(error.stack);
// يمكن أيضًا طباعة معلومات إضافية
console.error("معلومات إضافية:", {
errorName: error.name,
errorCode: error.code,
timestamp: new Date().toISOString()
});
}
التحقق من الأداء
تتبع أداء التطبيق باستخدام console.time
و console.timeEnd
:
// قياس وقت التنفيذ
console.time("عملية_قاعدة_البيانات");
// تنفيذ العملية التي نريد قياسها
await database.query("SELECT * FROM users");
// طباعة الوقت المستغرق
console.timeEnd("عملية_قاعدة_البيانات");
// يمكن أيضًا استخدام performance API
const start = performance.now();
// تنفيذ عملية معقدة
processLargeData(data);
const end = performance.now();
console.log(`استغرقت العملية: ${end - start} مللي ثانية`);
تصحيح طلبات HTTP
تتبع طلبات HTTP في خادم Bun.js:
import { serve } from "bun";
// وسيط (middleware) لتسجيل الطلبات
function logRequest(req) {
const start = performance.now();
const url = new URL(req.url);
console.log(`[${new Date().toISOString()}] ${req.method} ${url.pathname}`);
// تسجيل رؤوس الطلب الهامة
console.log("User-Agent:", req.headers.get("User-Agent"));
console.log("Content-Type:", req.headers.get("Content-Type"));
return { start, url };
}
// وسيط لتسجيل الاستجابة
function logResponse(info, response) {
const end = performance.now();
console.log(`[${new Date().toISOString()}] استجابة ${info.url.pathname}: ${response.status} (${end - info.start}ms)`);
}
// الخادم مع تسجيل الطلبات
serve({
port: 3000,
fetch(req) {
// تسجيل الطلب
const requestInfo = logRequest(req);
// معالجة الطلب
let response;
try {
if (requestInfo.url.pathname === "/") {
response = new Response("مرحبًا بالعالم");
} else if (requestInfo.url.pathname === "/error") {
throw new Error("خطأ تجريبي");
} else {
response = new Response("صفحة غير موجودة", { status: 404 });
}
} catch (error) {
console.error("خطأ في معالجة الطلب:", error);
response = new Response("خطأ في الخادم", { status: 500 });
}
// تسجيل الاستجابة
logResponse(requestInfo, response);
return response;
}
});
نصائح للتصحيح الفعال
- استخدم رسائل تسجيل واضحة ومفهومة تصف بدقة ما يحدث.
- قسم التطبيق إلى وحدات صغيرة لتسهيل تحديد موقع المشكلة.
- راقب استهلاك الذاكرة باستخدام
process.memoryUsage()
لتحديد تسربات الذاكرة. - استخدم محرر متكامل مثل VS Code لتسهيل التصحيح مع تكامل Bun.js.
- أنشئ اختبارات وحدة لاكتشاف الأخطاء مبكرًا قبل وصولها إلى الإنتاج.
تكامل إطارات العمل
يمكن استخدام Bun.js مع العديد من إطارات العمل الشائعة، مما يوفر بديلاً أسرع لوقت التشغيل ومدير الحزم والمجمع.
استخدام Bun.js مع Next.js
لاستخدام Bun.js مع Next.js، يمكنك اتباع الخطوات التالية:
# إنشاء مشروع Next.js جديد باستخدام Bun
bun create next my-next-app
# أو استخدام create-next-app مع Bun
bunx create-next-app my-next-app
# الانتقال إلى المجلد
cd my-next-app
# تشغيل الخادم التطويري
bun dev
تكوين محدد لاستخدام Bun.js مع Next.js:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// إعدادات Next.js
reactStrictMode: true,
swcMinify: true,
// تعديلات خاصة بـ Bun.js
experimental: {
// إعدادات تجريبية متوافقة مع Bun
},
// استخدام Bun.js لبناء واجهة API
webpack: (config, { isServer }) => {
// إعدادات webpack مخصصة
return config;
},
};
module.exports = nextConfig;
مثال لواجهة برمجة API في Next.js مع Bun.js:
// pages/api/hello.js
import { readFile } from 'fs/promises';
import path from 'path';
export default async function handler(req, res) {
if (req.method === 'GET') {
try {
// استخدام ميزات Bun.js
const filePath = path.join(process.cwd(), 'data', 'example.json');
const data = JSON.parse(await readFile(filePath, 'utf8'));
res.status(200).json(data);
} catch (error) {
res.status(500).json({ error: 'فشل في جلب البيانات' });
}
} else {
res.setHeader('Allow', ['GET']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
استخدام Bun.js مع Remix
لاستخدام Bun.js مع إطار عمل Remix:
# إنشاء مشروع Remix جديد
bunx create-remix my-remix-app
# أو باستخدام قالب Bun.js محدد
bunx create-remix --template remix-run/bun-template
# الانتقال إلى المجلد
cd my-remix-app
# تثبيت الاعتمادات
bun install
# تشغيل الخادم التطويري
bun run dev
تكوين Remix للعمل مع Bun.js:
// remix.config.js
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
ignoredRouteFiles: ["**/.*"],
// عند استخدام Bun.js، يمكننا تكوين المحولات
serverModuleFormat: "esm",
serverPlatform: "node",
// إعدادات الخادم
server: "./server.js",
// تحسينات أداء الإنتاج
serverBuildPath: "build/index.js",
appDirectory: "app",
assetsBuildDirectory: "public/build",
publicPath: "/build/",
};
مثال لملف الخادم المخصص باستخدام Bun.js:
// server.js
import { serve } from "bun";
import { createRequestHandler } from "@remix-run/express";
import { broadcastDevReady } from "@remix-run/node";
// استيراد تطبيق Remix المبني
import * as build from "./build/index.js";
const port = process.env.PORT || 3000;
// إنشاء معالج الطلبات Remix
const remixHandler = createRequestHandler({
build,
mode: process.env.NODE_ENV
});
// إنشاء خادم Bun
serve({
port,
async fetch(request) {
// معالجة الطلب باستخدام Remix
try {
// تحويل طلب fetch إلى طلب Express
const url = new URL(request.url);
const expressReq = {
url: url.pathname + url.search,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
body: request.body
};
// إنشاء كائن استجابة وهمي
const expressRes = {
statusCode: 200,
headers: new Headers(),
body: null,
status(code) {
this.statusCode = code;
return this;
},
header(key, value) {
this.headers.set(key, value);
return this;
},
send(body) {
this.body = body;
return this;
}
};
// توجيه الطلب إلى Remix
await remixHandler(expressReq, expressRes);
// إنشاء استجابة Bun من استجابة Express
return new Response(expressRes.body, {
status: expressRes.statusCode,
headers: expressRes.headers
});
} catch (error) {
console.error("حدث خطأ في معالجة الطلب:", error);
return new Response("خطأ في الخادم", { status: 500 });
}
}
});
console.log(`الخادم يعمل على المنفذ ${port}`);
// إشعار حول جاهزية التطوير (في وضع التطوير فقط)
if (process.env.NODE_ENV === "development") {
broadcastDevReady(build);
}
استخدام Bun.js مع Astro
إعداد مشروع Astro باستخدام Bun.js:
# إنشاء مشروع Astro جديد
bunx create astro@latest my-astro-app
# الانتقال إلى المجلد
cd my-astro-app
# تثبيت الاعتمادات
bun install
# تشغيل الخادم التطويري
bun run dev
تكوين Astro لاستخدام Bun.js:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
// إعدادات المشروع الأساسية
output: 'hybrid',
// تحديد Bun.js كبيئة تشغيل الخادم
vite: {
// إعدادات Vite المخصصة لـ Bun.js
optimizeDeps: {
// تجاوز التبعيات التي قد تواجه مشاكل مع Bun
exclude: ['fsevents']
},
// استخدام محمل ESM
build: {
target: 'esnext'
}
},
// تكوين خادم التطوير
server: {
host: true
}
});
مثال لصفحة Astro مع واجهة API باستخدام Bun.js:
// src/pages/api/data.js
export async function get() {
// استخدام Bun.js لقراءة ملف بيانات
const dataFile = Bun.file('./data/info.json');
const exists = await dataFile.exists();
if (!exists) {
return new Response(JSON.stringify({
error: 'ملف البيانات غير موجود'
}), {
status: 404,
headers: {
'Content-Type': 'application/json'
}
});
}
try {
const data = await dataFile.json();
// إرجاع البيانات
return new Response(JSON.stringify({
success: true,
data
}), {
status: 200,
headers: {
'Content-Type': 'application/json'
}
});
} catch (error) {
return new Response(JSON.stringify({
error: 'فشل في تحليل ملف البيانات'
}), {
status: 500,
headers: {
'Content-Type': 'application/json'
}
});
}
}
ملاحظات عامة حول تكامل إطارات العمل
- تأكد من توافق إصدار إطار العمل مع Bun.js، حيث أن بعض الإصدارات القديمة قد لا تعمل بشكل صحيح.
- قد تحتاج لتعديل بعض الإعدادات أو البرمجيات الإضافية لتتوافق مع Bun.js.
- استخدم
bun install
بدلاً منnpm install
أوyarn
للاستفادة من سرعة Bun في تثبيت الحزم. - عند نشر التطبيق، تأكد من أن بيئة الإنتاج تدعم Bun.js أو قم بتكوين البناء لتوافق Node.js إذا لزم الأمر.
بيئات النشر
يمكن نشر تطبيقات Bun.js على العديد من منصات الاستضافة. تعرف على الطرق المختلفة لنشر تطبيقات Bun.js وأفضل الممارسات.
استخدام Docker
يمكن نشر تطبيقات Bun.js باستخدام Docker، وهي طريقة موثوقة وقابلة للتكرار:
# Dockerfile
FROM oven/bun:latest as base
WORKDIR /app
# نسخ ملفات التبعيات
COPY package.json bun.lockb ./
# تثبيت التبعيات
RUN bun install --frozen-lockfile
# نسخ جميع ملفات المشروع
COPY . .
# مرحلة الإنتاج
FROM oven/bun:latest
WORKDIR /app
# نسخ الملفات من المرحلة السابقة
COPY --from=base /app/node_modules ./node_modules
COPY --from=base /app/package.json ./
COPY --from=base /app/src ./src
COPY --from=base /app/public ./public
# تعريف المنفذ
EXPOSE 3000
# تشغيل التطبيق
CMD ["bun", "run", "src/index.ts"]
بناء وتشغيل حاوية Docker:
# بناء الصورة
docker build -t my-bun-app .
# تشغيل الحاوية
docker run -p 3000:3000 my-bun-app
استخدام خدمات السحابة
يمكن نشر تطبيقات Bun.js على خدمات السحابة مثل AWS و Google Cloud و Azure:
// مثال لملف إعداد AWS Elastic Beanstalk
// .ebextensions/bun.config
commands:
01_install_bun:
command: |
curl -fsSL https://bun.sh/install | bash
echo 'export BUN_INSTALL="$HOME/.bun"' >> /home/ec2-user/.bashrc
echo 'export PATH="$BUN_INSTALL/bin:$PATH"' >> /home/ec2-user/.bashrc
source /home/ec2-user/.bashrc
container_commands:
01_install_dependencies:
command: |
source /home/ec2-user/.bashrc
bun install
02_build:
command: |
source /home/ec2-user/.bashrc
bun run build
النشر على Vercel
تكوين مشروع Bun.js للنشر على Vercel:
// vercel.json
{
"version": 2,
"builds": [
{
"src": "src/index.ts",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "src/index.ts"
}
],
"env": {
"NODE_ENV": "production"
}
}
لاحظ أن هذا يستخدم وحدة Node.js للنشر. لاستخدام Bun.js مباشرة على Vercel، يمكنك إضافة ملف تكوين مخصص:
// api/index.js - نقطة الدخول لـ Vercel
export const config = {
runtime: 'edge'
};
export default async function handler(request) {
try {
// معالجة الطلب باستخدام تطبيق Bun.js
const url = new URL(request.url);
// مثال لاستجابة بسيطة
return new Response(JSON.stringify({
message: "مرحبًا من تطبيق Bun.js على Vercel!",
path: url.pathname,
timestamp: new Date().toISOString()
}), {
status: 200,
headers: {
'Content-Type': 'application/json'
}
});
} catch (error) {
return new Response(JSON.stringify({
error: error.message
}), {
status: 500,
headers: {
'Content-Type': 'application/json'
}
});
}
}
الملفات التنفيذية المستقلة
يمكن إنشاء ملفات تنفيذية مستقلة باستخدام Bun.js، مما يسهل النشر:
# إنشاء ملف تنفيذي مستقل
bun build ./src/index.ts --compile --outfile my-app
ثم يمكن نشر الملف التنفيذي المستقل مباشرة على الخادم:
# نقل الملف التنفيذي إلى الخادم
scp my-app user@server:/path/to/deployment/
# تنفيذ التطبيق على الخادم
ssh user@server "cd /path/to/deployment && chmod +x my-app && ./my-app"
النشر باستخدام PM2
يمكن استخدام PM2 لإدارة عمليات Bun.js في بيئة الإنتاج:
// ecosystem.config.js
module.exports = {
apps: [
{
name: "bun-app",
script: "bun",
args: "run src/index.ts",
instances: "max", // استخدام جميع النوى المتاحة
exec_mode: "cluster", // تشغيل في وضع التجميع
env: {
NODE_ENV: "production",
PORT: 3000
},
watch: false,
max_memory_restart: "1G"
}
]
};
تشغيل وإدارة التطبيق باستخدام PM2:
# تثبيت PM2
npm install -g pm2
# بدء التطبيق
pm2 start ecosystem.config.js
# عرض قائمة التطبيقات الجارية
pm2 list
# عرض سجلات التطبيق
pm2 logs bun-app
# إعادة تشغيل التطبيق بعد التحديثات
pm2 reload bun-app
نصائح لنشر تطبيقات Bun.js
- تأكد من التحقق الشامل لتطبيقك في بيئة مشابهة للإنتاج قبل النشر.
- قم بإضافة رصد الصحة والأداء لتطبيقك لمراقبة استخدام الموارد والأخطاء.
- استخدم متغيرات البيئة لفصل إعدادات التكوين عن الشيفرة المصدرية.
- قم بإعداد نظام للنسخ الاحتياطي وخطة للتعافي من الكوارث.
- استخدم نظام CI/CD لأتمتة عملية الاختبار والنشر.
أوامر CLI المتقدمة
يوفر Bun.js مجموعة واسعة من أوامر واجهة سطر الأوامر (CLI) المتقدمة لتلبية احتياجات المطورين المتنوعة. فيما يلي شرح تفصيلي للأوامر الأكثر فائدة.
أوامر التشغيل المتقدمة
# تشغيل مع تتبع الأداء
bun --trace run index.ts
# تشغيل مع التصحيح
bun --inspect run index.ts
# تشغيل مع بيئة محددة
bun --env-file=.env.production run index.ts
# تشغيل مع تعيين الحد الأقصى لحجم الذاكرة
bun --memory-limit=4000 run index.ts
# تشغيل مع إعادة التحميل عند تغيير الملفات
bun --watch run index.ts
# تشغيل بدون تخزين مؤقت
bun --no-cache run index.ts
أوامر البناء المتقدمة
# بناء مع تخريط المصدر (source maps)
bun build index.ts --sourcemap=external
# بناء للمتصفح
bun build index.ts --target=browser
# بناء كمكتبة بتنسيق معين
bun build index.ts --format=esm --outdir=./dist
# بناء مع شجرة هزازة (tree-shaking)
bun build index.ts --splitting --tree-shaking
# بناء بدون تجميع التبعيات
bun build index.ts --external=react,react-dom
# بناء مع الإدخال المتعدد
bun build ./src/app.ts ./src/worker.ts --outdir=./dist
أوامر اختبار متقدمة
# تشغيل اختبارات محددة
bun test --pattern "auth*"
# تشغيل اختبارات مع تغطية الكود
bun test --coverage
# تشغيل اختبارات مع إظهار الوقت المستغرق
bun test --timing
# تشغيل اختبارات متوازية
bun test --parallel
# تشغيل اختبارات وإعادة التشغيل عند التغيير
bun test --watch
# تشغيل اختبارات بترتيب عشوائي
bun test --random
أوامر إدارة الحزم المتقدمة
# تثبيت حزمة من مصدر محدد
bun install lodash@npm:4.17.21
# تثبيت من مستودع git
bun install github:username/repo#branch
# تثبيت من مجلد محلي
bun install ./local-package
# تثبيت الحزم حسب وقت الإنشاء (لتسريع CI)
bun install --frozen
# تحديث الاعتمادات إلى أحدث إصدار متوافق
bun update --latest
# حذف الحزم غير المستخدمة
bun pm prune
أوامر التطوير المتنوعة
# إنشاء مشروع من قالب محدد
bun create my-app --template=next
# إنشاء تطبيق باستخدام إطار عمل محدد
bun create react-app my-react-app
# التحقق من المعلومات
bun --info
# تشغيل أمر npm بدون تثبيت حزمة عالمياً
bunx cowsay "Hello from Bun"
# تنظيف ذاكرة التخزين المؤقت
bun pm cache rm
# تشخيص المشكلات
bun pm cache ls
# ترقية Bun.js نفسه
bun upgrade
أوامر التكوين
# إضافة إعدادات للمشروع
bun init
# إنشاء ملف tsconfig.json
bun init -y
# إنشاء ملف تكوين مخصص
bun init --template custom-template
ملاحظات حول أوامر CLI
- استخدم
bun --help
للحصول على قائمة كاملة بالأوامر المتاحة. - معظم أوامر Bun.js مصممة لتكون أسرع من أدوات Node.js المقابلة.
- يمكن دمج العديد من الخيارات معًا لتخصيص سلوك الأمر.
- للحصول على مساعدة حول أمر محدد، استخدم
bun [command] --help
.
استمر في التعلم والتطوير!
الآن بعد أن تعرفت على الميزات المتقدمة في Bun.js، يمكنك البدء في بناء تطبيقات أكثر قوة وكفاءة. استمر في استكشاف إمكانيات Bun.js من خلال التوثيق الرسمي والمجتمع النشط.