WebAssembly (مع WASI)
WebAssembly هي بيئة تشغيل آمنة ومعزولة (sandboxed) ومحمولة تعمل داخل متصفحات الويب وخارجها.
عمليًا:
- اللغات (مثل JavaScript) تُترجم إلى WebAssembly (
.wasmملفات) - بيئات تشغيل WebAssembly (مثل
wasmtimeأوjco) تتيح تشغيل ملفات WebAssembly الثنائية
بينما لا يملك WebAssembly الأساسي أي وصول إلى أشياء مثل نظام الملفات المحلي أو المقابس (sockets)، فإن WebAssembly System Interface تتدخل لتمكين تعريف منصة ضمن أعباء عمل (workloads) WebAssembly.
هذا يعني أنه مع WASI، يمكن لـ WebAssembly العمل على الملفات والمآخذ (sockets) وغير ذلك الكثير.
هل تريد إلقاء نظرة على واجهة WASI بنفسك؟ اطّلع على wasi:http
يعتمد دعم WebAssembly مع WASI في JS على StarlingMonkey، وبفضل التركيز على معايير الويب في كلاهما StarlingMonkey و Hono، يعمل Hono *مباشرة مع النظم البيئية لـ WebAssembly المفعّلة بـ WASI.
1. الإعداد
يوفر النظام البيئي JavaScript الخاص بـ WebAssembly أدوات تسهّل البدء في بناء مكونات WebAssembly المفعّلة بـ WASI:
- StarlingMonkey هو فرع (fork) من SpiderMonkey يُترجم إلى WebAssembly ويتيح للمكونات
componentize-js' يحوّل وحدات JavaScript ES إلى مكونات WebAssemblyjcoهو أداة متعددة الاستخدامات تبني المكونات، وتولّد الأنواع، وتشغّل المكونات في بيئات مثل Node.js أو المتصفح
يمتلك WebAssembly نظامًا بيئيًا مفتوحًا ومصدرًا مفتوحًا، حيث تُدار المشاريع الأساسية بشكل أساسي من قبل Bytecode Alliance وأعضائه.
الميزات الجديدة والقضايا وطلبات السحب (pull requests) وأنواع المساهمات الأخرى مرحب بها دائمًا.
على الرغم من أن قالب البدء (starter) لـ Hono على WebAssembly غير متوفر بعد، يمكنك بدء مشروع Hono على WebAssembly تمامًا مثل أي مشروع آخر:
mkdir my-app
cd my-app
npm init
npm i hono
npm i -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
npm i -D rolldown
mkdir my-app
cd my-app
npm init
yarn add hono
yarn add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
yarn add -D rolldown
mkdir my-app
cd my-app
pnpm init --init-type module
pnpm add hono
pnpm add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
pnpm add -D rolldown
mkdir my-app
cd my-app
npm init
bun add hono
bun add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
لضمان استخدام مشروعك لوحدات ES، تأكّد من type مضبوط على "module" في package.json
بعد إدخال my-app المجلد، ثبِّت التبعيات، وتهيِّئ TypeScript:
npm i
npx tsc --init
yarn
yarn tsc --init
pnpm i
pnpm exec tsc --init
bun i
بمجرد أن يكون لديك ملف تهيئة TypeScript أساسي (tsconfig.json)، يُرجى التأكد من أنه يحتوي على الإعدادات التالية:
compilerOptions.moduleتعيين إلى"nodenext"
منذ componentize-js (و jco الذي يعيد استخدامه) يدعم فقط ملفات JS المفردة،
التجميع ضروري، لذا rolldown يمكن استخدامه لإنشاء حزمة (bundle) من ملف واحد.
إعداد Rolldown (rolldown.config.mjs) كهذا يمكن استخدامه:
import { defineConfig } from 'rolldown'
export default defineConfig({
input: 'src/component.ts',
external: /wasi:.*/,
output: {
file: 'dist/component.js',
format: 'esm',
},
})
لا تتردد في استخدام أي أدوات تجميع (bundlers) أخرى تشعر براحة أكبر معها (rolldown, esbuild, rollup، إلخ)
2. إعداد واجهة WIT والتبعيات
WebAssembly Interface Types (WIT) هي لغة تعريف واجهات (Interface Definition Language) تحكم الوظائف التي يستخدمها مكون WebAssembly ("imports") وما يوفره ("exports").
من بين واجهات WIT الموحّدة، wasi:http مخصص للتعامل مع طلبات HTTP (سواء استقبالها أو إرسالها)، وبما أننا ننوي إنشاء خادم ويب، يجب أن يصرح مكوننا باستخدام wasi:http/incoming-handler في عالم WIT الخاص به:
أولاً، لنُعِدّ عالم WIT الخاص بالمكوّن في ملف يُسمّى wit/component.wit:
package example:hono;
world component {
export wasi:http/incoming-handler@0.2.6;
}
ببساطة، يعني ملف WIT أعلاه أن مكوننا "يوفر" وظيفة "استقبال"/"معالجة الطلبات الواردة" لطلبات HTTP.
الـ wasi:http/incoming-handler تعتمد الواجهة على واجهات WIT المعيارية من المنبع (مواصفات لكيفية تنظيم الطلبات، وما إلى ذلك).
لسحب واجهات WIT تلك من طرف ثالث (التي يحافظ عليها Bytecode Alliance)، فإن إحدى الأدوات التي يمكننا استخدامها هي wkg:
wkg wit fetch
بمجرد wkg انتهى التشغيل، ينبغي أن تجد wit مجلد مملوء بـ deps مجلد بجانب component.wit:
wit
├── component.wit
└── deps
├── wasi-cli-0.2.6
│ └── package.wit
├── wasi-clocks-0.2.6
│ └── package.wit
├── wasi-http-0.2.6
│ └── package.wit
├── wasi-io-0.2.6
│ └── package.wit
└── wasi-random-0.2.6
└── package.wit
3. مرحبًا Wasm
لبناء خادم HTTP في WebAssembly، يمكننا الاستفادة من [jco-std][jco-std] مشروع، يحتوي على أدوات مساعدة تجعل التجربة مشابهة جدًا لتجربة Hono القياسية.
لنحقِّق component مرحبًا بالعالم مع تطبيق Hono أساسي كمكوّن WebAssembly في ملف يُسمى src/component.ts:
import { Hono } from 'hono'
import { fire } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'
const app = new Hono()
app.get('/hello', (c) => {
return c.json({ message: 'Hello from WebAssembly!' })
})
fire(app)
// Although we've called `fire()` with wasi HTTP configured for use above,
// we still need to actually export the `wasi:http/incoming-handler` interface object,
// as jco and componentize-js will be looking for the ES module export that matches the WASI interface.
export { incomingHandler } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'
4. البناء
بما أننا نستخدم Rolldown (وهو مُهيأ للتعامل مع تجميع TypeScript)، يمكننا استخدامه للبناء والتجميع (bundle):
npx rolldown -c
yarn rolldown -c
pnpm exec rolldown -c
bun build --target=bun --outfile=dist/component.js ./src/component.ts
خطوة التجميع (bundling) ضرورية لأن أدوات النظام البيئي JavaScript الخاص بـ WebAssembly تدعم حاليًا ملف JS واحدًا فقط، ونود تضمين Hono مع المكتبات ذات الصلة.
بالنسبة للمكونات ذات المتطلبات الأبسط، لا تكون أدوات التجميع (bundlers) ضرورية.
لبناء مكوّن WebAssembly الخاص بك، استخدم jco (وبشكل غير مباشر componentize-js):
npx jco componentize -w wit -o dist/component.wasm dist/component.js
yarn jco componentize -w wit -o dist/component.wasm dist/component.js
pnpm exec jco componentize -w wit -o dist/component.wasm dist/component.js
bun run jco componentize -w wit -o dist/component.wasm dist/component.js
5. تشغيل
لتشغيل خادم Hono WebAssembly HTTP الخاص بك، يمكنك استخدام أي بيئة تشغيل WebAssembly تدعم WASI:
wasmtimejco(يعمل في Node.js)
في هذا الدليل، سنستخدم jco serve لأنه مثبّت بالفعل.
jco serve مخصص للتطوير (development)، ولا يُنصح باستخدامه في بيئة الإنتاج (production).
npx jco serve dist/component.wasm
yarn jco serve dist/component.wasm
pnpm exec jco serve dist/component.wasm
bun run jco serve dist/component.wasm
ينبغي أن ترى مخرجات كهذه:
$ npx jco serve dist/component.wasm
Server listening @ localhost:8000...
إرسال طلب إلى localhost:8000/hello سينتج مخرجات JSON التي حددتها في تطبيق Hono الخاص بك.
ينبغي أن ترى مخرجات كهذه:
{ "message": "Hello from WebAssembly!" }
jco serve يعمل عن طريق تحويل مكون WebAssembly إلى وحدة نواة (core module) أساسية من WebAssembly، بحيث يمكن تشغيله في أوقات تشغيل مثل Node.js والمتصفح.
تُشغَّل هذه العملية عادةً عبر jco transpile، وهي الطريقة التي يمكننا من خلالها استخدام محركات JS مثل Node.js والمتصفح (الذي قد يستخدم V8 أو محركات JavaScript أخرى) كأوقات تشغيل لمكونات WebAssembly.
كيف jco transpile خارج نطاق هذا الدليل، يمكنك قراءة المزيد عنه في كتاب Jco
مزيد من المعلومات
لمعرفة المزيد حول WASI ومكونات WebAssembly والمزيد، راجع الموارد التالية:
- كتاب نموذج مكوّنات BytecodeAlliance
jcocodebasejcoمكونات المثال (ولا سيما مثال Hono)
- Jco book
componentize-jscodebase- StarlingMonkey codebase
للتواصل مع مجتمع WebAssembly بأسئلة أو تعليقات أو مساهمات أو لتسجيل القضايا:
- Bytecode Alliance Zulip (فكّر في النشر في قناة
#jco) - مستودع
Jco - مستودع componentize-js