@wuyuchentr/webassembly-runtime-embed 4.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wuyuchentr/webassembly-runtime-embed",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "Embed WebAssembly runtime in Node.js with WASI support, multi-instance isolation, resource limits, and remote loading with cache.",
5
5
  "bin": {
6
6
  "webassembly-runtime-embed": "bin/cli.js"
@@ -0,0 +1,728 @@
1
+ const COMPONENT_MAGIC = Buffer.from([0x00, 0x61, 0x73, 0x6d, 0x0a, 0x00, 0x01, 0x00]);
2
+ const CORE_MAGIC = Buffer.from([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]);
3
+
4
+ const SECTION_COMPONENT_TYPE_SORT = 0x50;
5
+ const SECTION_COMPONENT_IMPORT_SORT = 0x03;
6
+ const SECTION_COMPONENT_EXPORT_SORT = 0x07;
7
+ const SECTION_COMPONENT_INSTANCE = 0x01;
8
+ const SECTION_COMPONENT_ALIAS = 0x02;
9
+ const SECTION_COMPONENT_CANON = 0x0a;
10
+ const SECTION_COMPONENT_START = 0x0c;
11
+
12
+ const TYPE_STRING = 'string';
13
+ const TYPE_BOOL = 'bool';
14
+ const TYPE_U8 = 'u8';
15
+ const TYPE_U16 = 'u16';
16
+ const TYPE_U32 = 'u32';
17
+ const TYPE_U64 = 'u64';
18
+ const TYPE_S8 = 's8';
19
+ const TYPE_S16 = 's16';
20
+ const TYPE_S32 = 's32';
21
+ const TYPE_S64 = 's64';
22
+ const TYPE_F32 = 'float32';
23
+ const TYPE_F64 = 'float64';
24
+ const TYPE_CHAR = 'char';
25
+ const TYPE_LIST = 'list';
26
+ const TYPE_RECORD = 'record';
27
+ const TYPE_TUPLE = 'tuple';
28
+ const TYPE_VARIANT = 'variant';
29
+ const TYPE_OPTION = 'option';
30
+ const TYPE_RESULT = 'result';
31
+ const TYPE_ENUM = 'enum';
32
+ const TYPE_FLAGS = 'flags';
33
+
34
+ const PRIMITIVE_SIZES = {
35
+ [TYPE_BOOL]: 1,
36
+ [TYPE_U8]: 1, [TYPE_U16]: 2, [TYPE_U32]: 4, [TYPE_U64]: 8,
37
+ [TYPE_S8]: 1, [TYPE_S16]: 2, [TYPE_S32]: 4, [TYPE_S64]: 8,
38
+ [TYPE_F32]: 4, [TYPE_F64]: 8, [TYPE_CHAR]: 4,
39
+ };
40
+
41
+ class ComponentType {
42
+ constructor(def) {
43
+ this.kind = def.kind || def;
44
+ this.options = def.options || {};
45
+ this.fields = def.fields || [];
46
+ this.cases = def.cases || [];
47
+ this.element = def.element || null;
48
+ this.ok = def.ok || null;
49
+ this.err = def.err || null;
50
+ this.inner = def.inner || null;
51
+ this.align = def.align || 1;
52
+ }
53
+
54
+ size(memory) {
55
+ switch (this.kind) {
56
+ case TYPE_STRING: return 8;
57
+ case TYPE_LIST: return 8;
58
+ case TYPE_BOOL: case TYPE_U8: case TYPE_S8: return 1;
59
+ case TYPE_U16: case TYPE_S16: return 2;
60
+ case TYPE_U32: case TYPE_S32: case TYPE_F32: case TYPE_CHAR: return 4;
61
+ case TYPE_U64: case TYPE_S64: case TYPE_F64: return 8;
62
+ case TYPE_RECORD: return this.fields.reduce((s, f) => s + this._fieldType(f).size(memory), 0);
63
+ case TYPE_TUPLE: return this.inner.reduce((s, t) => s + t.size(memory), 0);
64
+ case TYPE_VARIANT: return 4 + Math.max(0, ...this.cases.map(c => { const t = this._caseType(c); return t ? t.size(memory) : 0; }));
65
+ case TYPE_OPTION: return 4 + (this.inner ? this.inner.size(memory) : 0);
66
+ case TYPE_RESULT: return 4 + Math.max(
67
+ this.ok ? this.ok.size(memory) : 0,
68
+ this.err ? this.err.size(memory) : 0
69
+ );
70
+ case TYPE_ENUM: return 1;
71
+ case TYPE_FLAGS: return Math.ceil(this.options.flagCount / 8);
72
+ default: return 4;
73
+ }
74
+ }
75
+
76
+ _fieldType(field) {
77
+ return field.type instanceof ComponentType ? field.type : new ComponentType(field.type);
78
+ }
79
+
80
+ _caseType(caseDef) {
81
+ if (!caseDef || !caseDef.type) return null;
82
+ return caseDef.type instanceof ComponentType ? caseDef.type : new ComponentType(caseDef.type);
83
+ }
84
+ }
85
+
86
+ function isComponent(bytes) {
87
+ if (bytes.length < 8) return false;
88
+ return bytes.slice(0, 8).equals(COMPONENT_MAGIC);
89
+ }
90
+
91
+ class ComponentInfo {
92
+ constructor(bytes) {
93
+ this.bytes = bytes;
94
+ this.isComponent = isComponent(bytes);
95
+ this.imports = [];
96
+ this.exports = [];
97
+ this.typeDefs = new Map();
98
+
99
+ if (this.isComponent) {
100
+ this._parse();
101
+ }
102
+ }
103
+
104
+ _parse() {
105
+ let offset = 8;
106
+ while (offset < this.bytes.length) {
107
+ const sectionId = this.bytes[offset++];
108
+ const sectionLen = this._leb128(offset);
109
+ offset += this._leb128Size(this.bytes, offset - 1);
110
+ const sectionEnd = offset + sectionLen;
111
+ if (sectionId === SECTION_COMPONENT_TYPE_SORT) {
112
+ this._parseTypes(offset, sectionEnd);
113
+ } else if (sectionId === SECTION_COMPONENT_IMPORT_SORT) {
114
+ this._parseImports(offset, sectionEnd);
115
+ } else if (sectionId === SECTION_COMPONENT_EXPORT_SORT) {
116
+ this._parseExports(offset, sectionEnd);
117
+ }
118
+ offset = sectionEnd;
119
+ }
120
+ }
121
+
122
+ _parseTypes(offset, end) {
123
+ const count = this._leb128(offset);
124
+ offset += this._leb128Size(this.bytes, offset);
125
+ for (let i = 0; i < count && offset < end; i++) {
126
+ const typeByte = this.bytes[offset++];
127
+ if (typeByte === 0x01) {
128
+ offset = this._parseFuncType(offset, i);
129
+ } else if (typeByte === 0x02) {
130
+ offset = this._parseComponentType(offset, i);
131
+ } else if (typeByte === 0x03) {
132
+ offset = this._parseInstanceType(offset, i);
133
+ }
134
+ }
135
+ }
136
+
137
+ _parseComponentType(offset, idx) {
138
+ const typeByte = this.bytes[offset++];
139
+ if (typeByte === 0x00) {
140
+ this.typeDefs.set(idx, 'defined');
141
+ return offset;
142
+ }
143
+ if (typeByte === 0x01) {
144
+ const count = this._leb128(offset);
145
+ offset += this._leb128Size(this.bytes, offset);
146
+ this.typeDefs.set(idx, { kind: TYPE_RECORD, fields: [] });
147
+ return offset;
148
+ }
149
+ if (typeByte === 0x02) {
150
+ this.typeDefs.set(idx, { kind: TYPE_FLAGS, options: { flagCount: 0 } });
151
+ return offset;
152
+ }
153
+ if (typeByte === 0x03) {
154
+ this.typeDefs.set(idx, { kind: TYPE_ENUM });
155
+ return offset;
156
+ }
157
+ if (typeByte === 0x04) {
158
+ const count = this._leb128(offset);
159
+ offset += this._leb128Size(this.bytes, offset);
160
+ this.typeDefs.set(idx, { kind: TYPE_VARIANT, cases: [] });
161
+ return offset;
162
+ }
163
+ if (typeByte === 0x05) {
164
+ const count = this._leb128(offset);
165
+ offset += this._leb128Size(this.bytes, offset);
166
+ this.typeDefs.set(idx, { kind: TYPE_TUPLE, inner: [] });
167
+ return offset;
168
+ }
169
+ if (typeByte === 0x06) {
170
+ this.typeDefs.set(idx, { kind: TYPE_LIST, element: null });
171
+ return offset;
172
+ }
173
+ if (typeByte === 0x07) {
174
+ this.typeDefs.set(idx, { kind: TYPE_OPTION, inner: null });
175
+ return offset;
176
+ }
177
+ if (typeByte === 0x08) {
178
+ this.typeDefs.set(idx, { kind: TYPE_RESULT, ok: null, err: null });
179
+ return offset;
180
+ }
181
+ if (typeByte === 0x09) {
182
+ this.typeDefs.set(idx, { kind: 'borrow' });
183
+ return offset;
184
+ }
185
+ if (typeByte === 0x10) {
186
+ this.typeDefs.set(idx, { kind: TYPE_STRING });
187
+ return offset;
188
+ }
189
+ if (typeByte === 0x11) {
190
+ this.typeDefs.set(idx, { kind: TYPE_BOOL });
191
+ return offset;
192
+ }
193
+ if (typeByte === 0x12) {
194
+ this.typeDefs.set(idx, { kind: TYPE_U8 });
195
+ return offset;
196
+ }
197
+ if (typeByte === 0x13) {
198
+ this.typeDefs.set(idx, { kind: TYPE_U16 });
199
+ return offset;
200
+ }
201
+ if (typeByte === 0x14) {
202
+ this.typeDefs.set(idx, { kind: TYPE_U32 });
203
+ return offset;
204
+ }
205
+ if (typeByte === 0x15) {
206
+ this.typeDefs.set(idx, { kind: TYPE_U64 });
207
+ return offset;
208
+ }
209
+ if (typeByte === 0x16) {
210
+ this.typeDefs.set(idx, { kind: TYPE_S8 });
211
+ return offset;
212
+ }
213
+ if (typeByte === 0x17) {
214
+ this.typeDefs.set(idx, { kind: TYPE_S16 });
215
+ return offset;
216
+ }
217
+ if (typeByte === 0x18) {
218
+ this.typeDefs.set(idx, { kind: TYPE_S32 });
219
+ return offset;
220
+ }
221
+ if (typeByte === 0x19) {
222
+ this.typeDefs.set(idx, { kind: TYPE_S64 });
223
+ return offset;
224
+ }
225
+ if (typeByte === 0x1a) {
226
+ this.typeDefs.set(idx, { kind: TYPE_F32 });
227
+ return offset;
228
+ }
229
+ if (typeByte === 0x1b) {
230
+ this.typeDefs.set(idx, { kind: TYPE_F64 });
231
+ return offset;
232
+ }
233
+ if (typeByte === 0x1c) {
234
+ this.typeDefs.set(idx, { kind: TYPE_CHAR });
235
+ return offset;
236
+ }
237
+ if (typeByte === 0x1d) {
238
+ this.typeDefs.set(idx, { kind: TYPE_STRING });
239
+ return offset;
240
+ }
241
+ return offset;
242
+ }
243
+
244
+ _parseImports(offset, end) {
245
+ const count = this._leb128(offset);
246
+ offset += this._leb128Size(this.bytes, offset);
247
+ for (let i = 0; i < count && offset < end; i++) {
248
+ const sort = this.bytes[offset++];
249
+ const nameLen = this._leb128(offset);
250
+ offset += this._leb128Size(this.bytes, offset);
251
+ let name = this.bytes.toString('utf8', offset, offset + nameLen);
252
+ offset += nameLen;
253
+ this.imports.push({ sort, name, typeIdx: 0 });
254
+ if (sort === 0x00) {
255
+ const typeIdx = this._leb128(offset);
256
+ offset += this._leb128Size(this.bytes, offset);
257
+ this.imports[this.imports.length - 1].typeIdx = typeIdx;
258
+ } else if (sort === 0x01) {
259
+ const typeIdx = this._leb128(offset);
260
+ offset += this._leb128Size(this.bytes, offset);
261
+ this.imports[this.imports.length - 1].typeIdx = typeIdx;
262
+ } else if (sort === 0x02) {
263
+ const instanceIdx = this._leb128(offset);
264
+ offset += this._leb128Size(this.bytes, offset);
265
+ this.imports[this.imports.length - 1].instanceIdx = instanceIdx;
266
+ }
267
+ }
268
+ }
269
+
270
+ _parseExports(offset, end) {
271
+ const count = this._leb128(offset);
272
+ offset += this._leb128Size(this.bytes, offset);
273
+ for (let i = 0; i < count && offset < end; i++) {
274
+ const sort = this.bytes[offset++];
275
+ const nameLen = this._leb128(offset);
276
+ offset += this._leb128Size(this.bytes, offset);
277
+ let name = this.bytes.toString('utf8', offset, offset + nameLen);
278
+ offset += nameLen;
279
+ if (sort === 0x00) {
280
+ const funcIdx = this._leb128(offset);
281
+ offset += this._leb128Size(this.bytes, offset);
282
+ this.exports.push({ sort, name, funcIdx });
283
+ } else if (sort === 0x01) {
284
+ const typeIdx = this._leb128(offset);
285
+ offset += this._leb128Size(this.bytes, offset);
286
+ this.exports.push({ sort, name, typeIdx });
287
+ } else if (sort === 0x03) {
288
+ const instanceIdx = this._leb128(offset);
289
+ offset += this._leb128Size(this.bytes, offset);
290
+ this.exports.push({ sort, name, instanceIdx });
291
+ }
292
+ }
293
+ }
294
+
295
+ _parseFuncType(offset, idx) {
296
+ const paramCount = this._leb128(offset);
297
+ offset += this._leb128Size(this.bytes, offset);
298
+ const params = [];
299
+ for (let i = 0; i < paramCount; i++) {
300
+ const paramNameLen = this._leb128(offset);
301
+ offset += this._leb128Size(this.bytes, offset);
302
+ const paramName = this.bytes.toString('utf8', offset, offset + paramNameLen);
303
+ offset += paramNameLen;
304
+ const valType = this.bytes[offset++];
305
+ params.push({ name: paramName, valType });
306
+ }
307
+ const resultCount = this._leb128(offset);
308
+ offset += this._leb128Size(this.bytes, offset);
309
+ if (resultCount === 1) {
310
+ offset++;
311
+ }
312
+ this.typeDefs.set(idx, { kind: 'func', params, results: resultCount });
313
+ return offset;
314
+ }
315
+
316
+ _parseInstanceType(offset, idx) {
317
+ const count = this._leb128(offset);
318
+ offset += this._leb128Size(this.bytes, offset);
319
+ for (let i = 0; i < count && offset < this.bytes.length; i++) {
320
+ const exportSort = this.bytes[offset++];
321
+ const nameLen = this._leb128(offset);
322
+ offset += this._leb128Size(this.bytes, offset);
323
+ offset += nameLen;
324
+ if (exportSort === 0x00) {
325
+ const typeIdx = this._leb128(offset);
326
+ offset += this._leb128Size(this.bytes, offset);
327
+ } else if (exportSort === 0x01) {
328
+ const typeIdx = this._leb128(offset);
329
+ offset += this._leb128Size(this.bytes, offset);
330
+ }
331
+ }
332
+ return offset;
333
+ }
334
+
335
+ _leb128(offset) {
336
+ let result = 0, shift = 0;
337
+ while (true) {
338
+ const byte = this.bytes[offset];
339
+ result |= (byte & 0x7f) << shift;
340
+ if (!(byte & 0x80)) break;
341
+ shift += 7;
342
+ offset++;
343
+ }
344
+ return result;
345
+ }
346
+
347
+ _leb128Size(buf, offset) {
348
+ let size = 0;
349
+ while (true) {
350
+ const byte = buf[offset + size];
351
+ size++;
352
+ if (!(byte & 0x80)) break;
353
+ }
354
+ return size;
355
+ }
356
+ }
357
+
358
+ class ComponentAdapter {
359
+ static lift(type, memoryView, ptr, options = {}) {
360
+ const t = type instanceof ComponentType ? type : new ComponentType(type);
361
+ switch (t.kind) {
362
+ case TYPE_STRING: return ComponentAdapter._liftString(memoryView, ptr);
363
+ case TYPE_BOOL: return memoryView.readU8(ptr) !== 0;
364
+ case TYPE_U8: return memoryView.readU8(ptr);
365
+ case TYPE_U16: return memoryView.readU16(ptr);
366
+ case TYPE_U32: case TYPE_CHAR: case TYPE_S32: return memoryView.readU32(ptr);
367
+ case TYPE_U64: case TYPE_S64: return Number(memoryView.readU32(ptr)) + (Number(memoryView.readU32(ptr + 4)) * 4294967296);
368
+ case TYPE_F32: return memoryView.readF32(ptr);
369
+ case TYPE_F64: return memoryView.readF64(ptr);
370
+ case TYPE_S8: return memoryView.readU8(ptr) << 24 >> 24;
371
+ case TYPE_S16: return memoryView.readU16(ptr) << 16 >> 16;
372
+ case TYPE_LIST: return ComponentAdapter._liftList(t.element, memoryView, ptr);
373
+ case TYPE_RECORD: return ComponentAdapter._liftRecord(t, memoryView, ptr);
374
+ case TYPE_TUPLE: return ComponentAdapter._liftTuple(t, memoryView, ptr);
375
+ case TYPE_VARIANT: return ComponentAdapter._liftVariant(t, memoryView, ptr);
376
+ case TYPE_OPTION: return ComponentAdapter._liftOption(t, memoryView, ptr);
377
+ case TYPE_RESULT: return ComponentAdapter._liftResult(t, memoryView, ptr);
378
+ case TYPE_ENUM: return memoryView.readU8(ptr);
379
+ case TYPE_FLAGS: {
380
+ const flagBytes = Math.ceil(t.options.flagCount / 8);
381
+ let val = 0;
382
+ for (let i = 0; i < flagBytes; i++) val |= memoryView.readU8(ptr + i) << (i * 8);
383
+ return val;
384
+ }
385
+ default: return memoryView.readU32(ptr);
386
+ }
387
+ }
388
+
389
+ static lower(type, memoryView, ptr, value) {
390
+ const t = type instanceof ComponentType ? type : new ComponentType(type);
391
+ switch (t.kind) {
392
+ case TYPE_STRING: return ComponentAdapter._lowerString(memoryView, ptr, value);
393
+ case TYPE_BOOL: memoryView.writeU8(ptr, value ? 1 : 0); return 1;
394
+ case TYPE_U8: case TYPE_S8: memoryView.writeU8(ptr, value); return 1;
395
+ case TYPE_U16: case TYPE_S16: memoryView.writeU16(ptr, value); return 2;
396
+ case TYPE_U32: case TYPE_S32: case TYPE_CHAR: memoryView.writeU32(ptr, value); return 4;
397
+ case TYPE_F32: memoryView.writeF32(ptr, value); return 4;
398
+ case TYPE_F64: memoryView.writeF64(ptr, value); return 8;
399
+ case TYPE_LIST: return ComponentAdapter._lowerList(t.element, memoryView, ptr, value);
400
+ case TYPE_RECORD: return ComponentAdapter._lowerRecord(t, memoryView, ptr, value);
401
+ case TYPE_TUPLE: return ComponentAdapter._lowerTuple(t, memoryView, ptr, value);
402
+ case TYPE_VARIANT: return ComponentAdapter._lowerVariant(t, memoryView, ptr, value);
403
+ case TYPE_OPTION: return ComponentAdapter._lowerOption(t, memoryView, ptr, value);
404
+ case TYPE_RESULT: return ComponentAdapter._lowerResult(t, memoryView, ptr, value);
405
+ case TYPE_ENUM: memoryView.writeU8(ptr, value); return 1;
406
+ default: memoryView.writeU32(ptr, value); return 4;
407
+ }
408
+ }
409
+
410
+ static _liftString(mem, ptr) {
411
+ const dataPtr = mem.readU32(ptr);
412
+ const len = mem.readU32(ptr + 4);
413
+ return mem.readString(dataPtr, len);
414
+ }
415
+
416
+ static _lowerString(mem, ptr, str) {
417
+ const bytes = new TextEncoder().encode(String(str));
418
+ const dataPtr = mem.alloc(bytes.length);
419
+ mem.writeBytes(dataPtr, bytes);
420
+ mem.writeU32(ptr, dataPtr);
421
+ mem.writeU32(ptr + 4, bytes.length);
422
+ return 8;
423
+ }
424
+
425
+ static _liftList(elemType, mem, ptr) {
426
+ const dataPtr = mem.readU32(ptr);
427
+ const len = mem.readU32(ptr + 4);
428
+ const result = [];
429
+ const elemT = elemType ? (elemType instanceof ComponentType ? elemType : new ComponentType(elemType)) : new ComponentType({ kind: TYPE_U32 });
430
+ const elemSize = elemT.size();
431
+ for (let i = 0; i < len; i++) {
432
+ result.push(ComponentAdapter.lift(elemT, mem, dataPtr + i * elemSize));
433
+ }
434
+ return result;
435
+ }
436
+
437
+ static _lowerList(elemType, mem, ptr, arr) {
438
+ arr = arr || [];
439
+ const elemT = elemType ? (elemType instanceof ComponentType ? elemType : new ComponentType(elemType)) : new ComponentType({ kind: TYPE_U32 });
440
+ const elemSize = elemT.size();
441
+ const dataPtr = mem.alloc(arr.length * elemSize);
442
+ for (let i = 0; i < arr.length; i++) {
443
+ ComponentAdapter.lower(elemT, mem, dataPtr + i * elemSize, arr[i]);
444
+ }
445
+ mem.writeU32(ptr, dataPtr);
446
+ mem.writeU32(ptr + 4, arr.length);
447
+ return 8;
448
+ }
449
+
450
+ static _liftRecord(type, mem, ptr) {
451
+ const result = {};
452
+ let offset = 0;
453
+ for (const field of type.fields) {
454
+ const fieldT = field.type instanceof ComponentType ? field.type : new ComponentType(field.type);
455
+ result[field.name] = ComponentAdapter.lift(fieldT, mem, ptr + offset);
456
+ offset += fieldT.size();
457
+ }
458
+ return result;
459
+ }
460
+
461
+ static _lowerRecord(type, mem, ptr, value) {
462
+ let written = 0;
463
+ for (const field of type.fields) {
464
+ const fieldT = field.type instanceof ComponentType ? field.type : new ComponentType(field.type);
465
+ ComponentAdapter.lower(fieldT, mem, ptr + written, value ? value[field.name] : undefined);
466
+ written += fieldT.size();
467
+ }
468
+ return written;
469
+ }
470
+
471
+ static _liftTuple(type, mem, ptr) {
472
+ const result = [];
473
+ let offset = 0;
474
+ for (const elemT of type.inner) {
475
+ const t = elemT instanceof ComponentType ? elemT : new ComponentType(elemT);
476
+ result.push(ComponentAdapter.lift(t, mem, ptr + offset));
477
+ offset += t.size();
478
+ }
479
+ return result;
480
+ }
481
+
482
+ static _lowerTuple(type, mem, ptr, value) {
483
+ value = value || [];
484
+ let written = 0;
485
+ for (let i = 0; i < type.inner.length; i++) {
486
+ const t = type.inner[i] instanceof ComponentType ? type.inner[i] : new ComponentType(type.inner[i]);
487
+ ComponentAdapter.lower(t, mem, ptr + written, value[i]);
488
+ written += t.size();
489
+ }
490
+ return written;
491
+ }
492
+
493
+ static _liftVariant(type, mem, ptr) {
494
+ const disc = mem.readU32(ptr);
495
+ const caseDef = type.cases[disc];
496
+ if (!caseDef) return { tag: disc };
497
+ if (!caseDef.type) return { tag: disc, value: undefined };
498
+ const caseT = caseDef.type instanceof ComponentType ? caseDef.type : new ComponentType(caseDef.type);
499
+ const value = ComponentAdapter.lift(caseT, mem, ptr + 4);
500
+ return { tag: disc, value };
501
+ }
502
+
503
+ static _lowerVariant(type, mem, ptr, value) {
504
+ const tag = value.tag !== undefined ? value.tag : 0;
505
+ mem.writeU32(ptr, tag);
506
+ const caseDef = type.cases[tag];
507
+ if (caseDef && caseDef.type && value.value !== undefined) {
508
+ const caseT = caseDef.type instanceof ComponentType ? caseDef.type : new ComponentType(caseDef.type);
509
+ ComponentAdapter.lower(caseT, mem, ptr + 4, value.value);
510
+ }
511
+ return 4 + (caseDef && caseDef.type ? caseDef.type.size() : 0);
512
+ }
513
+
514
+ static _liftOption(type, mem, ptr) {
515
+ const disc = mem.readU32(ptr);
516
+ if (disc === 0) return null;
517
+ if (type.inner) {
518
+ const innerT = type.inner instanceof ComponentType ? type.inner : new ComponentType(type.inner);
519
+ return ComponentAdapter.lift(innerT, mem, ptr + 4);
520
+ }
521
+ return true;
522
+ }
523
+
524
+ static _lowerOption(type, mem, ptr, value) {
525
+ if (value === null || value === undefined) {
526
+ mem.writeU32(ptr, 0);
527
+ return 4;
528
+ }
529
+ mem.writeU32(ptr, 1);
530
+ if (type.inner) {
531
+ const innerT = type.inner instanceof ComponentType ? type.inner : new ComponentType(type.inner);
532
+ ComponentAdapter.lower(innerT, mem, ptr + 4, value);
533
+ return 4 + innerT.size();
534
+ }
535
+ return 4;
536
+ }
537
+
538
+ static _liftResult(type, mem, ptr) {
539
+ const disc = mem.readU32(ptr);
540
+ if (disc === 0 && type.ok) {
541
+ const okT = type.ok instanceof ComponentType ? type.ok : new ComponentType(type.ok);
542
+ return { ok: ComponentAdapter.lift(okT, mem, ptr + 4) };
543
+ }
544
+ if (disc === 1 && type.err) {
545
+ const errT = type.err instanceof ComponentType ? type.err : new ComponentType(type.err);
546
+ return { err: ComponentAdapter.lift(errT, mem, ptr + 4) };
547
+ }
548
+ return disc === 0 ? { ok: null } : { err: null };
549
+ }
550
+
551
+ static _lowerResult(type, mem, ptr, value) {
552
+ if (value && value.err !== undefined) {
553
+ mem.writeU32(ptr, 1);
554
+ if (type.err && value.err !== null) {
555
+ const errT = type.err instanceof ComponentType ? type.err : new ComponentType(type.err);
556
+ ComponentAdapter.lower(errT, mem, ptr + 4, value.err);
557
+ return 4 + errT.size();
558
+ }
559
+ return 4;
560
+ }
561
+ mem.writeU32(ptr, 0);
562
+ if (type.ok && value && value.ok !== undefined && value.ok !== null) {
563
+ const okT = type.ok instanceof ComponentType ? type.ok : new ComponentType(type.ok);
564
+ ComponentAdapter.lower(okT, mem, ptr + 4, value.ok);
565
+ return 4 + okT.size();
566
+ }
567
+ return 4;
568
+ }
569
+ }
570
+
571
+ class ComponentInstance {
572
+ constructor(runtime, componentInfo, options = {}) {
573
+ this.runtime = runtime;
574
+ this.info = componentInfo;
575
+ this.options = options;
576
+ this._coreInstance = null;
577
+ this._exports = new Map();
578
+ this._loaded = false;
579
+ }
580
+
581
+ async instantiate(imports = {}) {
582
+ const bytes = this.info.bytes;
583
+ const module = await WebAssembly.compile(bytes);
584
+ const importObj = this._buildImportObject(imports);
585
+ this._coreInstance = await WebAssembly.instantiate(module, importObj);
586
+ if (this._coreInstance.exports.memory) {
587
+ this._memory = this._coreInstance.exports.memory;
588
+ const { MemoryView } = require('./memory.js');
589
+ this._memoryView = new MemoryView(this._memory);
590
+ }
591
+ this._resolveExports();
592
+ this._loaded = true;
593
+ return this;
594
+ }
595
+
596
+ _buildImportObject(imports) {
597
+ if (!this.info.imports || this.info.imports.length === 0) return imports;
598
+ const result = {};
599
+ for (const imp of this.info.imports) {
600
+ if (imp.sort === 0x00) {
601
+ const mod = imp.name;
602
+ if (!result[mod]) result[mod] = {};
603
+ const provided = imports[mod] || {};
604
+ result[mod][imp.name] = provided[imp.name] || (() => {});
605
+ }
606
+ }
607
+ return Object.keys(result).length > 0 ? result : imports;
608
+ }
609
+
610
+ _resolveExports() {
611
+ if (!this._coreInstance) return;
612
+ const exports = this._coreInstance.exports;
613
+ for (const exp of this.info.exports || []) {
614
+ if (exp.sort === 0x00 && typeof exports[exp.name] === 'function') {
615
+ this._exports.set(exp.name, {
616
+ func: exports[exp.name],
617
+ paramTypes: (exp.paramTypes || []).map(t => new ComponentType(t)),
618
+ resultType: exp.resultType ? new ComponentType(exp.resultType) : null,
619
+ });
620
+ }
621
+ }
622
+ }
623
+
624
+ async call(name, ...args) {
625
+ const exportDef = this._exports.get(name);
626
+ if (!exportDef) {
627
+ if (this._coreInstance && typeof this._coreInstance.exports[name] === 'function') {
628
+ return this._coreInstance.exports[name](...args);
629
+ }
630
+ throw new Error(`Component export '${name}' not found`);
631
+ }
632
+
633
+ if (!this._memoryView) {
634
+ return exportDef.func(...args);
635
+ }
636
+
637
+ const mem = this._memoryView;
638
+ const snapshot = mem.snapshot();
639
+ const paramTypes = exportDef.paramTypes || [];
640
+ const loweredArgs = [];
641
+ let argOffset = 0;
642
+
643
+ for (let i = 0; i < paramTypes.length; i++) {
644
+ const pt = paramTypes[i];
645
+ const size = pt.size();
646
+ if (pt.kind === TYPE_STRING || pt.kind === TYPE_LIST) {
647
+ ComponentAdapter.lower(pt, mem, argOffset, args[i]);
648
+ loweredArgs.push(mem.readU32(argOffset), mem.readU32(argOffset + 4));
649
+ argOffset += size;
650
+ } else {
651
+ loweredArgs.push(args[i]);
652
+ }
653
+ }
654
+
655
+ const result = exportDef.func(...loweredArgs);
656
+
657
+ const resultType = exportDef.resultType;
658
+ if (resultType && (resultType.kind === TYPE_STRING || resultType.kind === TYPE_LIST)) {
659
+ const lifted = ComponentAdapter.lift(resultType, mem, 0);
660
+ mem.restore(snapshot);
661
+ return lifted;
662
+ }
663
+
664
+ mem.restore(snapshot);
665
+ return result;
666
+ }
667
+
668
+ getMemory() {
669
+ return this._memoryView;
670
+ }
671
+
672
+ hasExport(name) {
673
+ return this._exports.has(name) || (this._coreInstance && name in this._coreInstance.exports);
674
+ }
675
+
676
+ exportNames() {
677
+ const names = Array.from(this._exports.keys());
678
+ if (this._coreInstance) {
679
+ names.push(...Object.keys(this._coreInstance.exports).filter(n => !names.includes(n)));
680
+ }
681
+ return names;
682
+ }
683
+ }
684
+
685
+ class ComponentLinker {
686
+ constructor(runtime, options = {}) {
687
+ this.runtime = runtime;
688
+ this.components = new Map();
689
+ this.instances = [];
690
+ }
691
+
692
+ register(name, componentInfo) {
693
+ this.components.set(name, componentInfo);
694
+ }
695
+
696
+ async instantiate(name, providedImports = {}) {
697
+ const info = this.components.get(name);
698
+ if (!info) throw new Error(`Component '${name}' not registered`);
699
+ const imports = { ...providedImports };
700
+ for (const imp of info.imports) {
701
+ if (imp.sort === 0x03) {
702
+ const depName = imp.name;
703
+ if (!imports[depName] && this.instances.length > 0) {
704
+ const depInst = this.instances.find(i => i.info === this.components.get(depName));
705
+ if (depInst) {
706
+ if (!imports[depName]) imports[depName] = {};
707
+ for (const exp of (this.components.get(depName) || {}).exports || []) {
708
+ imports[depName][depName] = depInst._coreInstance.exports[depName];
709
+ }
710
+ }
711
+ }
712
+ }
713
+ }
714
+ const inst = new ComponentInstance(this.runtime, info, {});
715
+ await inst.instantiate(imports);
716
+ this.instances.push(inst);
717
+ return inst;
718
+ }
719
+ }
720
+
721
+ module.exports = {
722
+ isComponent,
723
+ ComponentInfo,
724
+ ComponentType,
725
+ ComponentAdapter,
726
+ ComponentInstance,
727
+ ComponentLinker,
728
+ };
package/src/index.js CHANGED
@@ -6,6 +6,7 @@ const { WasiContext } = require('./wasi.js');
6
6
  const { WasmPool, InstanceLease } = require('./pool.js');
7
7
  const { HotReload } = require('./watcher.js');
8
8
  const { WasmWorker, WasmClient, WasmCluster } = require('./cluster.js');
9
+ const { ComponentInfo, ComponentInstance, ComponentLinker, ComponentAdapter, ComponentType, isComponent } = require('./component.js');
9
10
  const loader = require('./loader.js');
10
11
 
11
12
  function createRuntime(options = {}) {
@@ -33,5 +34,11 @@ module.exports = {
33
34
  WasmWorker,
34
35
  WasmClient,
35
36
  WasmCluster,
37
+ ComponentInfo,
38
+ ComponentInstance,
39
+ ComponentLinker,
40
+ ComponentAdapter,
41
+ ComponentType,
42
+ isComponent,
36
43
  loader,
37
44
  };
package/src/runtime.js CHANGED
@@ -4,6 +4,7 @@ const { ResourceLimits } = require('./limits.js');
4
4
  const { WasmPool } = require('./pool.js');
5
5
  const { HotReload } = require('./watcher.js');
6
6
  const { WasmWorker, WasmClient, WasmCluster } = require('./cluster.js');
7
+ const { ComponentInfo, ComponentInstance, ComponentLinker, isComponent } = require('./component.js');
7
8
  const loader = require('./loader.js');
8
9
 
9
10
  class WasmInstance {
@@ -162,6 +163,26 @@ class WasmRuntime {
162
163
  return new WasmCluster(options);
163
164
  }
164
165
 
166
+ async loadComponent(source, options = {}) {
167
+ let bytes;
168
+ if (Buffer.isBuffer(source)) {
169
+ bytes = source;
170
+ } else if (source.startsWith('http://') || source.startsWith('https://')) {
171
+ const result = await loader.loadFromUrl(source, options);
172
+ bytes = result.bytes;
173
+ } else {
174
+ const result = await loader.loadFromFile(source);
175
+ bytes = result.bytes;
176
+ }
177
+ const info = new ComponentInfo(bytes);
178
+ const inst = new ComponentInstance(this, info, options);
179
+ return inst;
180
+ }
181
+
182
+ createLinker(options = {}) {
183
+ return new ComponentLinker(this, options);
184
+ }
185
+
165
186
  getStats() {
166
187
  return {
167
188
  instances: this.instances.length,