@bytecodealliance/jco 1.8.1 → 1.10.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": "@bytecodealliance/jco",
3
- "version": "1.8.1",
3
+ "version": "1.10.0",
4
4
  "description": "JavaScript tooling for working with WebAssembly Components",
5
5
  "author": "Guy Bedford",
6
6
  "bin": {
@@ -24,9 +24,9 @@
24
24
  },
25
25
  "type": "module",
26
26
  "dependencies": {
27
- "@bytecodealliance/componentize-js": "^0.14.0",
27
+ "@bytecodealliance/componentize-js": "^0.17.0",
28
28
  "@bytecodealliance/preview2-shim": "^0.17.1",
29
- "binaryen": "^120.0.0",
29
+ "binaryen": "^122.0.0",
30
30
  "chalk-template": "^1",
31
31
  "commander": "^12",
32
32
  "mkdirp": "^3",
@@ -40,7 +40,7 @@
40
40
  "eslint": "^9.9.0",
41
41
  "mime": "^4.0.4",
42
42
  "mocha": "^10.7.0",
43
- "puppeteer": "^23.4.0",
43
+ "puppeteer": "^24.0.1",
44
44
  "typescript": "^5.5.4"
45
45
  },
46
46
  "repository": {
@@ -62,8 +62,8 @@
62
62
  "build:release": "cargo xtask build release",
63
63
  "build:types:preview2-shim": "cargo xtask generate wasi-types",
64
64
  "lint": "eslint -c eslintrc.cjs src/**/*.js packages/*/lib/**/*.js",
65
- "test:lts": "mocha -u tdd test/test.js --timeout 120000",
66
- "test": "node --stack-trace-limit=100 node_modules/mocha/bin/mocha.js -u tdd test/test.js --timeout 120000",
65
+ "test:lts": "mocha -u tdd test/test.js --timeout 240000",
66
+ "test": "node --experimental-wasm-jspi --stack-trace-limit=100 node_modules/mocha/bin/mocha.js -u tdd test/test.js --timeout 240000",
67
67
  "prepublishOnly": "cargo xtask build release && npm run test"
68
68
  },
69
69
  "files": [
@@ -1 +1 @@
1
- {"version":3,"file":"componentize.d.ts","sourceRoot":"","sources":["componentize.js"],"names":[],"mappings":"AAIA,sEAiBC"}
1
+ {"version":3,"file":"componentize.d.ts","sourceRoot":"","sources":["componentize.js"],"names":[],"mappings":"AAIA,sEAkBC"}
@@ -10,6 +10,7 @@ export async function componentize (jsSource, opts) {
10
10
  const source = await readFile(jsSource, 'utf8');
11
11
  const { component } = await componentizeFn(source, {
12
12
  enableAot: opts.aot,
13
+ wevalBin: opts.wevalBin,
13
14
  sourceName: basename(jsSource),
14
15
  witPath: resolve(opts.wit),
15
16
  worldName: opts.worldName,
package/src/cmd/opt.d.ts CHANGED
@@ -2,10 +2,15 @@ export function opt(componentPath: any, opts: any, program: any): Promise<void>;
2
2
  /**
3
3
  *
4
4
  * @param {Uint8Array} componentBytes
5
- * @param {{ quiet: boolean, optArgs?: string[] }} options?
5
+ * @param {{ quiet: boolean, asyncify?: boolean, optArgs?: string[], noVerify?: boolean }} opts?
6
6
  * @returns {Promise<{ component: Uint8Array, compressionInfo: { beforeBytes: number, afterBytes: number }[] >}
7
7
  */
8
- export function optimizeComponent(componentBytes: Uint8Array, opts: any): Promise<{
8
+ export function optimizeComponent(componentBytes: Uint8Array, opts: {
9
+ quiet: boolean;
10
+ asyncify?: boolean;
11
+ optArgs?: string[];
12
+ noVerify?: boolean;
13
+ }): Promise<{
9
14
  component: Uint8Array;
10
15
  compressionInfo: {
11
16
  beforeBytes: number;
@@ -1 +1 @@
1
- {"version":3,"file":"opt.d.ts","sourceRoot":"","sources":["opt.js"],"names":[],"mappings":"AAQA,gFAoCC;AAED;;;;;GAKG;AACH,kDAJW,UAAU,cAER,OAAO,CAAC;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,eAAe,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAA,CAAE,CA8E7G"}
1
+ {"version":3,"file":"opt.d.ts","sourceRoot":"","sources":["opt.js"],"names":[],"mappings":"AAgBA,gFA2CC;AAgBD;;;;;GAKG;AACH,kDAJW,UAAU,QACV;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5E,OAAO,CAAC;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,eAAe,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAA,CAAE,CAkK7G"}
package/src/cmd/opt.js CHANGED
@@ -1,16 +1,23 @@
1
- import { $init, tools } from '../../obj/wasm-tools.js';
1
+ import { $init, tools } from "../../obj/wasm-tools.js";
2
2
  const { metadataShow, print } = tools;
3
- import { writeFile } from 'fs/promises';
4
- import { fileURLToPath } from 'url';
5
- import c from 'chalk-template';
6
- import { readFile, sizeStr, fixedDigitDisplay, table, spawnIOTmp, setShowSpinner, getShowSpinner } from '../common.js';
7
- import ora from '#ora';
3
+ import { writeFile } from "fs/promises";
4
+ import { fileURLToPath } from "url";
5
+ import c from "chalk-template";
6
+ import {
7
+ readFile,
8
+ sizeStr,
9
+ fixedDigitDisplay,
10
+ table,
11
+ spawnIOTmp,
12
+ setShowSpinner,
13
+ getShowSpinner,
14
+ } from "../common.js";
15
+ import ora from "#ora";
8
16
 
9
- export async function opt (componentPath, opts, program) {
17
+ export async function opt(componentPath, opts, program) {
10
18
  await $init;
11
- const varIdx = program.parent.rawArgs.indexOf('--');
12
- if (varIdx !== -1)
13
- opts.optArgs = program.parent.rawArgs.slice(varIdx + 1);
19
+ const varIdx = program.parent.rawArgs.indexOf("--");
20
+ if (varIdx !== -1) opts.optArgs = program.parent.rawArgs.slice(varIdx + 1);
14
21
  const componentBytes = await readFile(componentPath);
15
22
 
16
23
  if (!opts.quiet) setShowSpinner(true);
@@ -19,128 +26,235 @@ export async function opt (componentPath, opts, program) {
19
26
 
20
27
  await writeFile(opts.output, component);
21
28
 
22
- let totalBeforeBytes = 0, totalAfterBytes = 0;
29
+ let totalBeforeBytes = 0,
30
+ totalAfterBytes = 0;
23
31
 
24
32
  if (!opts.quiet)
25
33
  console.log(c`
26
34
  {bold Optimized WebAssembly Component Internal Core Modules:}
27
35
 
28
- ${table([...compressionInfo.map(({ beforeBytes, afterBytes }, i) => {
29
- totalBeforeBytes += beforeBytes;
30
- totalAfterBytes += afterBytes;
31
- return [
32
- ` - Core Module ${i + 1}: `,
33
- sizeStr(beforeBytes),
34
- ' -> ',
35
- c`{cyan ${sizeStr(afterBytes)}} `,
36
- `(${fixedDigitDisplay(afterBytes / beforeBytes * 100, 2)}%)`
37
- ];
38
- }), ['', '', '', '', ''], [
39
- ` = Total: `,
40
- `${sizeStr(totalBeforeBytes)}`,
41
- ` => `,
42
- c`{cyan ${sizeStr(totalAfterBytes)}} `,
43
- `(${fixedDigitDisplay(totalAfterBytes / totalBeforeBytes * 100, 2)}%)`
44
- ]], [,,,,'right'])}`);
36
+ ${table(
37
+ [
38
+ ...compressionInfo.map(({ beforeBytes, afterBytes }, i) => {
39
+ totalBeforeBytes += beforeBytes;
40
+ totalAfterBytes += afterBytes;
41
+ return [
42
+ ` - Core Module ${i + 1}: `,
43
+ sizeStr(beforeBytes),
44
+ " -> ",
45
+ c`{cyan ${sizeStr(afterBytes)}} `,
46
+ `(${fixedDigitDisplay((afterBytes / beforeBytes) * 100, 2)}%)`,
47
+ ];
48
+ }),
49
+ ["", "", "", "", ""],
50
+ [
51
+ ` = Total: `,
52
+ `${sizeStr(totalBeforeBytes)}`,
53
+ ` => `,
54
+ c`{cyan ${sizeStr(totalAfterBytes)}} `,
55
+ `(${fixedDigitDisplay((totalAfterBytes / totalBeforeBytes) * 100, 2)}%)`,
56
+ ],
57
+ ],
58
+ [, , , , "right"]
59
+ )}`);
45
60
  }
46
61
 
47
62
  /**
48
- *
49
- * @param {Uint8Array} componentBytes
50
- * @param {{ quiet: boolean, optArgs?: string[] }} options?
63
+ * Counts the byte length for the LEB128 encoding of a number.
64
+ * @param {number} val
65
+ * @returns {number}
66
+ */
67
+ function byteLengthLEB128(val) {
68
+ let len = 0;
69
+ do {
70
+ val >>>= 7;
71
+ len++;
72
+ } while (val !== 0);
73
+ return len;
74
+ }
75
+
76
+ /**
77
+ *
78
+ * @param {Uint8Array} componentBytes
79
+ * @param {{ quiet: boolean, asyncify?: boolean, optArgs?: string[], noVerify?: boolean }} opts?
51
80
  * @returns {Promise<{ component: Uint8Array, compressionInfo: { beforeBytes: number, afterBytes: number }[] >}
52
81
  */
53
- export async function optimizeComponent (componentBytes, opts) {
82
+ export async function optimizeComponent(componentBytes, opts) {
54
83
  await $init;
55
84
  const showSpinner = getShowSpinner();
56
85
  let spinner;
57
86
  try {
58
- const coreModules = metadataShow(componentBytes).slice(1, -1).map(({ range }) => range);
87
+ let componentMetadata = metadataShow(componentBytes);
88
+ componentMetadata.forEach((metadata, index) => {
89
+ // add index to the metadata object
90
+ metadata.index = index;
91
+ const size = metadata.range[1] - metadata.range[0];
92
+ // compute previous LEB128 encoding length
93
+ metadata.prevLEBLen = byteLengthLEB128(size);
94
+ });
95
+ const coreModules = componentMetadata
96
+ .filter(({ metaType }) => metaType.tag === "module");
59
97
 
98
+ // log number of core Wasm modules to be run with wasm-opt
60
99
  let completed = 0;
61
- const spinnerText = () => c`{cyan ${completed} / ${coreModules.length}} Running Binaryen on WebAssembly Component Internal Core Modules \n`;
100
+ const spinnerText = () =>
101
+ c`{cyan ${completed} / ${coreModules.length}} Running Binaryen on WebAssembly Component Internal Core Modules \n`;
62
102
  if (showSpinner) {
63
103
  spinner = ora({
64
- color: 'cyan',
65
- spinner: 'bouncingBar'
104
+ color: "cyan",
105
+ spinner: "bouncingBar",
66
106
  }).start();
67
107
  spinner.text = spinnerText();
68
108
  }
69
109
 
70
- const optimizedCoreModules = await Promise.all(coreModules.map(async ([coreModuleStart, coreModuleEnd]) => {
71
- const optimized = wasmOpt(componentBytes.subarray(coreModuleStart, coreModuleEnd), opts?.optArgs);
72
- if (spinner) {
73
- completed++;
74
- spinner.text = spinnerText();
110
+ // gather the options for wasm-opt. optionally, adding the asyncify flag
111
+ const args = opts?.optArgs ?
112
+ [...opts.optArgs] :
113
+ ['-Oz', '--low-memory-unused', '--enable-bulk-memory', '--strip-debug'];
114
+ if (opts?.asyncify) args.push('--asyncify');
115
+
116
+
117
+ // process core Wasm modules with wasm-opt
118
+ await Promise.all(
119
+ coreModules.map(async (metadata) => {
120
+ if (metadata.metaType.tag === "module") {
121
+ // store the wasm-opt processed module in the metadata
122
+ metadata.optimized = await wasmOpt(
123
+ componentBytes.subarray(metadata.range[0], metadata.range[1]),
124
+ args);
125
+
126
+ // compute the size change, including the change to
127
+ // the LEB128 encoding of the size change
128
+ const prevModuleSize = metadata.range[1] - metadata.range[0];
129
+ const newModuleSize = metadata.optimized.byteLength;
130
+ metadata.newLEBLen = byteLengthLEB128(newModuleSize);
131
+ metadata.sizeChange = newModuleSize - prevModuleSize;
132
+
133
+ if (spinner) {
134
+ completed++;
135
+ spinner.text = spinnerText();
136
+ }
137
+ }
138
+ }));
139
+
140
+ // organize components in modules into tree parent and children
141
+ const nodes = componentMetadata.slice(1);
142
+ const getChildren = (parentIndex) => {
143
+ const children = [];
144
+ for (let i = 0; i < nodes.length; i++) {
145
+ const metadata = nodes[i];
146
+ if (metadata.parentIndex === parentIndex) {
147
+ nodes.splice(i, 1); // remove from nodes
148
+ i--;
149
+ metadata.children = getChildren(metadata.index);
150
+ metadata.sizeChange = metadata.children
151
+ .reduce((total, {prevLEBLen, newLEBLen, sizeChange}) => {
152
+ return sizeChange ?
153
+ total + sizeChange + newLEBLen - prevLEBLen :
154
+ total;
155
+ },
156
+ metadata.sizeChange || 0);
157
+ const prevSize = metadata.range[1] - metadata.range[0];
158
+ metadata.newLEBLen = byteLengthLEB128(prevSize + metadata.sizeChange);
159
+ children.push(metadata);
160
+ }
75
161
  }
76
- return optimized;
77
- }));
162
+ return children;
163
+ };
164
+ const componentTree = getChildren(0);
78
165
 
79
- let outComponentBytes = new Uint8Array(componentBytes.byteLength);
80
- let nextReadPos = 0, nextWritePos = 0;
81
- for (let i = 0; i < coreModules.length; i++) {
82
- const [coreModuleStart, coreModuleEnd] = coreModules[i];
83
- const optimizedCoreModule = optimizedCoreModules[i];
166
+ // compute the total size change in the component binary
167
+ const sizeChange = componentTree
168
+ .reduce((total, {prevLEBLen, newLEBLen, sizeChange}) => {
169
+ return total + (sizeChange || 0) + newLEBLen - prevLEBLen;
170
+ }, 0);
84
171
 
85
- let lebByteLen = 1;
86
- while (componentBytes[coreModuleStart - 1 - lebByteLen] & 0x80) lebByteLen++;
172
+ let outComponentBytes = new Uint8Array(
173
+ componentBytes.byteLength + sizeChange);
174
+ let nextReadPos = 0, nextWritePos = 0;
87
175
 
88
- // Write from the last read to the LEB byte start of the core module
89
- outComponentBytes.set(componentBytes.subarray(nextReadPos, coreModuleStart - lebByteLen), nextWritePos);
90
- nextWritePos += coreModuleStart - lebByteLen - nextReadPos;
176
+ const write = ({prevLEBLen, range, optimized, children, sizeChange}) => {
177
+ // write from the last read to the LEB byte start
178
+ outComponentBytes.set(
179
+ componentBytes.subarray(nextReadPos, range[0] - prevLEBLen),
180
+ nextWritePos
181
+ );
182
+ nextWritePos += range[0] - prevLEBLen - nextReadPos;
91
183
 
92
- // Write the new LEB bytes
93
- let val = optimizedCoreModule.byteLength;
184
+ // write the new LEB bytes
185
+ let val = range[1] - range[0] + sizeChange;
94
186
  do {
95
- const byte = val & 0x7F;
187
+ const byte = val & 0x7f;
96
188
  val >>>= 7;
97
189
  outComponentBytes[nextWritePos++] = val === 0 ? byte : byte | 0x80;
98
190
  } while (val !== 0);
99
191
 
100
- // Write the core module
101
- outComponentBytes.set(optimizedCoreModule, nextWritePos);
102
- nextWritePos += optimizedCoreModule.byteLength;
103
-
104
- nextReadPos = coreModuleEnd;
105
- }
192
+ if (optimized) {
193
+ // write the core module
194
+ outComponentBytes.set(optimized, nextWritePos);
195
+ nextReadPos = range[1];
196
+ nextWritePos += optimized.byteLength;
197
+ } else if (children.length > 0) {
198
+ // write child components / modules
199
+ nextReadPos = range[0];
200
+ children.forEach(write);
201
+ } else {
202
+ // write component
203
+ outComponentBytes.set(
204
+ componentBytes.subarray(range[0], range[1]),
205
+ nextWritePos
206
+ );
207
+ nextReadPos = range[1];
208
+ nextWritePos += range[1] - range[0];
209
+ }
210
+ };
106
211
 
107
- outComponentBytes.set(componentBytes.subarray(nextReadPos, componentBytes.byteLength), nextWritePos);
108
- nextWritePos += componentBytes.byteLength - nextReadPos;
109
- nextReadPos += componentBytes.byteLength - nextReadPos;
212
+ // write each top-level component / module
213
+ componentTree.forEach(write);
110
214
 
111
- outComponentBytes = outComponentBytes.subarray(0, outComponentBytes.length + nextWritePos - nextReadPos);
215
+ // write remaining
216
+ outComponentBytes.set(
217
+ componentBytes.subarray(nextReadPos),
218
+ nextWritePos
219
+ );
112
220
 
113
221
  // verify it still parses ok
114
- try {
115
- await print(outComponentBytes);
116
- } catch (e) {
117
- throw new Error(`Internal error performing optimization.\n${e.message}`)
222
+ if (!opts?.noVerify) {
223
+ try {
224
+ await print(outComponentBytes);
225
+ } catch (e) {
226
+ throw new Error(
227
+ `Internal error performing optimization.\n${e.message}`
228
+ );
229
+ }
118
230
  }
119
231
 
120
232
  return {
121
233
  component: outComponentBytes,
122
- compressionInfo: coreModules.map(([s, e], i) => ({ beforeBytes: e - s, afterBytes: optimizedCoreModules[i].byteLength }))
234
+ compressionInfo: coreModules.map(({range, optimized}) => ({
235
+ beforeBytes: range[1] - range[0],
236
+ afterBytes: optimized?.byteLength,
237
+ })),
123
238
  };
124
- }
125
- finally {
126
- if (spinner)
127
- spinner.stop();
239
+ } finally {
240
+ if (spinner) spinner.stop();
128
241
  }
129
242
  }
130
243
 
131
244
  /**
132
- * @param {Uint8Array} source
245
+ * @param {Uint8Array} source
246
+ * @param {Array<string>} args
133
247
  * @returns {Promise<Uint8Array>}
134
248
  */
135
- async function wasmOpt(source, args = ['-O1', '--low-memory-unused', '--enable-bulk-memory']) {
136
- const wasmOptPath = fileURLToPath(import.meta.resolve('binaryen/bin/wasm-opt'));
249
+ async function wasmOpt(source, args) {
250
+ const wasmOptPath = fileURLToPath(
251
+ import.meta.resolve("binaryen/bin/wasm-opt")
252
+ );
137
253
 
138
254
  try {
139
- return await spawnIOTmp(wasmOptPath, source, [
140
- ...args, '-o'
141
- ]);
255
+ return await spawnIOTmp(wasmOptPath, source, [...args, "-o"]);
142
256
  } catch (e) {
143
- if (e.toString().includes('BasicBlock requested'))
257
+ if (e.toString().includes("BasicBlock requested"))
144
258
  return wasmOpt(source, args);
145
259
  throw e;
146
260
  }
package/src/cmd/run.js CHANGED
@@ -40,8 +40,8 @@ export async function serve (componentPath, args, opts) {
40
40
  return runComponent(componentPath, args, opts, `
41
41
  import { HTTPServer } from '@bytecodealliance/preview2-shim/http';
42
42
  const server = new HTTPServer(mod.incomingHandler);
43
- ${tryFindPort ? `
44
43
  let port = ${port};
44
+ ${tryFindPort ? `
45
45
  while (true) {
46
46
  try {
47
47
  server.listen(port, ${JSON.stringify(host)});
@@ -52,8 +52,8 @@ export async function serve (componentPath, args, opts) {
52
52
  }
53
53
  port++;
54
54
  }
55
- ` : `server.listen(${port}, ${JSON.stringify(host)})`}
56
- console.error(\`Server listening on \${port}...\`);
55
+ ` : `server.listen(port, ${JSON.stringify(host)})`}
56
+ console.error(\`Server listening on port...\`);
57
57
  `);
58
58
  }
59
59
 
@@ -1,4 +1,5 @@
1
1
  export function types(witPath: any, opts: any): Promise<void>;
2
+ export function guestTypes(witPath: any, opts: any): Promise<void>;
2
3
  /**
3
4
  * @param {string} witPath
4
5
  * @param {{
@@ -6,8 +7,12 @@ export function types(witPath: any, opts: any): Promise<void>;
6
7
  * worldName?: string,
7
8
  * instantiation?: 'async' | 'sync',
8
9
  * tlaCompat?: bool,
10
+ * asyncMode?: string,
11
+ * asyncImports?: string[],
12
+ * asyncExports?: string[],
9
13
  * outDir?: string,
10
14
  * features?: string[] | 'all',
15
+ * guest?: bool,
11
16
  * }} opts
12
17
  * @returns {Promise<{ [filename: string]: Uint8Array }>}
13
18
  */
@@ -16,8 +21,12 @@ export function typesComponent(witPath: string, opts: {
16
21
  worldName?: string;
17
22
  instantiation?: "async" | "sync";
18
23
  tlaCompat?: bool;
24
+ asyncMode?: string;
25
+ asyncImports?: string[];
26
+ asyncExports?: string[];
19
27
  outDir?: string;
20
28
  features?: string[] | "all";
29
+ guest?: bool;
21
30
  }): Promise<{
22
31
  [filename: string]: Uint8Array;
23
32
  }>;
@@ -30,6 +39,9 @@ export function transpile(componentPath: any, opts: any, program: any): Promise<
30
39
  * instantiation?: 'async' | 'sync',
31
40
  * importBindings?: 'js' | 'optimized' | 'hybrid' | 'direct-optimized',
32
41
  * map?: Record<string, string>,
42
+ * asyncMode?: string,
43
+ * asyncImports?: string[],
44
+ * asyncExports?: string[],
33
45
  * validLiftingOptimization?: bool,
34
46
  * tracing?: bool,
35
47
  * nodejsCompat?: bool,
@@ -51,6 +63,9 @@ export function transpileComponent(component: Uint8Array, opts?: {
51
63
  instantiation?: "async" | "sync";
52
64
  importBindings?: "js" | "optimized" | "hybrid" | "direct-optimized";
53
65
  map?: Record<string, string>;
66
+ asyncMode?: string;
67
+ asyncImports?: string[];
68
+ asyncExports?: string[];
54
69
  validLiftingOptimization?: bool;
55
70
  tracing?: bool;
56
71
  nodejsCompat?: bool;
@@ -1 +1 @@
1
- {"version":3,"file":"transpile.d.ts","sourceRoot":"","sources":["transpile.js"],"names":[],"mappings":"AAgBA,8DAGC;AAED;;;;;;;;;;;GAWG;AACH,wCAXW,MAAM,QACN;IACV,IAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAiB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACrC,SAAa,CAAC,EAAE,IAAI,CAAC;IACrB,MAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAY,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;CAC7B,GACS,OAAO,CAAC;IAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC,CA6BvD;AAkBD,sFAwBC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,8CAtBW,UAAU,SACV;IACV,IAAQ,EAAE,MAAM,CAAC;IACjB,aAAiB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACrC,cAAkB,CAAC,EAAE,IAAI,GAAG,WAAW,GAAG,QAAQ,GAAG,kBAAkB,CAAC;IACxE,GAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,wBAA4B,CAAC,EAAE,IAAI,CAAC;IACpC,OAAW,CAAC,EAAE,IAAI,CAAC;IACnB,YAAgB,CAAC,EAAE,IAAI,CAAC;IACxB,SAAa,CAAC,EAAE,IAAI,CAAC;IACrB,YAAgB,CAAC,EAAE,IAAI,CAAC;IACxB,EAAM,CAAC,EAAE,IAAI,CAAC;IACd,MAAU,CAAC,EAAE,IAAI,CAAC;IAClB,QAAY,CAAC,EAAE,IAAI,CAAC;IACpB,iBAAqB,CAAC,EAAE,IAAI,CAAC;IAC7B,MAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAe,CAAC,EAAE,IAAI,CAAC;IACvB,sBAA0B,CAAC,EAAE,IAAI,CAAC;IAClC,OAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,GACS,OAAO,CAAC;IAAE,KAAK,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC,EAAE,CAAA;CAAE,CAAC,CAoSnI"}
1
+ {"version":3,"file":"transpile.d.ts","sourceRoot":"","sources":["transpile.js"],"names":[],"mappings":"AAgCA,8DAGC;AAED,mEAGC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wCAfW,MAAM,QACN;IACN,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAC5B,KAAK,CAAC,EAAE,IAAI,CAAC;CACd,GACS,OAAO,CAAC;IAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC,CA8CvD;AAkBD,sFA8BC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,8CAzBW,UAAU,SACV;IACN,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACjC,cAAc,CAAC,EAAE,IAAI,GAAG,WAAW,GAAG,QAAQ,GAAG,kBAAkB,CAAC;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,wBAAwB,CAAC,EAAE,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,EAAE,CAAC,EAAE,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,iBAAiB,CAAC,EAAE,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,sBAAsB,CAAC,EAAE,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,GACS,OAAO,CAAC;IAAE,KAAK,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC,EAAE,CAAA;CAAE,CAAC,CAgTnI"}
@@ -14,11 +14,32 @@ import { platform } from 'node:process';
14
14
 
15
15
  const isWindows = platform === 'win32';
16
16
 
17
+ const ASYNC_WASI_IMPORTS = [
18
+ "wasi:io/poll#poll",
19
+ "wasi:io/poll#[method]pollable.block",
20
+ "wasi:io/streams#[method]input-stream.blocking-read",
21
+ "wasi:io/streams#[method]input-stream.blocking-skip",
22
+ "wasi:io/streams#[method]output-stream.blocking-flush",
23
+ "wasi:io/streams#[method]output-stream.blocking-write-and-flush",
24
+ "wasi:io/streams#[method]output-stream.blocking-write-zeroes-and-flush",
25
+ "wasi:io/streams#[method]output-stream.blocking-splice",
26
+ ];
27
+
28
+ const ASYNC_WASI_EXPORTS = [
29
+ "wasi:cli/run#run",
30
+ "wasi:http/incoming-handler#handle",
31
+ ];
32
+
17
33
  export async function types (witPath, opts) {
18
34
  const files = await typesComponent(witPath, opts);
19
35
  await writeFiles(files, opts.quiet ? false : 'Generated Type Files');
20
36
  }
21
37
 
38
+ export async function guestTypes (witPath, opts) {
39
+ const files = await typesComponent(witPath, { ...opts, guest: true });
40
+ await writeFiles(files, opts.quiet ? false : 'Generated Guest Typescript Definition Files (.d.ts)');
41
+ }
42
+
22
43
  /**
23
44
  * @param {string} witPath
24
45
  * @param {{
@@ -26,8 +47,12 @@ export async function types (witPath, opts) {
26
47
  * worldName?: string,
27
48
  * instantiation?: 'async' | 'sync',
28
49
  * tlaCompat?: bool,
50
+ * asyncMode?: string,
51
+ * asyncImports?: string[],
52
+ * asyncExports?: string[],
29
53
  * outDir?: string,
30
54
  * features?: string[] | 'all',
55
+ * guest?: bool,
31
56
  * }} opts
32
57
  * @returns {Promise<{ [filename: string]: Uint8Array }>}
33
58
  */
@@ -51,12 +76,29 @@ export async function typesComponent (witPath, opts) {
51
76
  features = { tag: 'list', val: opts.feature };
52
77
  }
53
78
 
79
+ if (opts.asyncWasiImports)
80
+ opts.asyncImports = ASYNC_WASI_IMPORTS.concat(opts.asyncImports || []);
81
+ if (opts.asyncWasiExports)
82
+ opts.asyncExports = ASYNC_WASI_EXPORTS.concat(opts.asyncExports || []);
83
+
84
+ const asyncMode = !opts.asyncMode || opts.asyncMode === 'sync' ?
85
+ null :
86
+ {
87
+ tag: opts.asyncMode,
88
+ val: {
89
+ imports: opts.asyncImports || [],
90
+ exports: opts.asyncExports || [],
91
+ },
92
+ };
93
+
54
94
  return Object.fromEntries(generateTypes(name, {
55
95
  wit: { tag: 'path', val: (isWindows ? '//?/' : '') + resolve(witPath) },
56
96
  instantiation,
57
97
  tlaCompat: opts.tlaCompat ?? false,
58
98
  world: opts.worldName,
59
99
  features,
100
+ guest: opts.guest ?? false,
101
+ asyncMode,
60
102
  }).map(([name, file]) => [`${outDir}${name}`, file]));
61
103
  }
62
104
 
@@ -98,6 +140,12 @@ export async function transpile (componentPath, opts, program) {
98
140
  opts.name = basename(componentPath.slice(0, -extname(componentPath).length || Infinity));
99
141
  if (opts.map)
100
142
  opts.map = Object.fromEntries(opts.map.map(mapping => mapping.split('=')));
143
+
144
+ if (opts.asyncWasiImports)
145
+ opts.asyncImports = ASYNC_WASI_IMPORTS.concat(opts.asyncImports || []);
146
+ if (opts.asyncWasiExports)
147
+ opts.asyncExports = ASYNC_WASI_EXPORTS.concat(opts.asyncExports || []);
148
+
101
149
  const { files } = await transpileComponent(component, opts);
102
150
  await writeFiles(files, opts.quiet ? false : 'Transpiled JS Component Files');
103
151
  }
@@ -126,6 +174,9 @@ async function wasm2Js (source) {
126
174
  * instantiation?: 'async' | 'sync',
127
175
  * importBindings?: 'js' | 'optimized' | 'hybrid' | 'direct-optimized',
128
176
  * map?: Record<string, string>,
177
+ * asyncMode?: string,
178
+ * asyncImports?: string[],
179
+ * asyncExports?: string[],
129
180
  * validLiftingOptimization?: bool,
130
181
  * tracing?: bool,
131
182
  * nodejsCompat?: bool,
@@ -148,6 +199,7 @@ export async function transpileComponent (component, opts = {}) {
148
199
 
149
200
  let spinner;
150
201
  const showSpinner = getShowSpinner();
202
+
151
203
  if (opts.optimize) {
152
204
  if (showSpinner) setShowSpinner(true);
153
205
  ({ component } = await optimizeComponent(component, opts));
@@ -176,10 +228,21 @@ export async function transpileComponent (component, opts = {}) {
176
228
  instantiation = { tag: 'async' };
177
229
  }
178
230
 
231
+ const asyncMode = !opts.asyncMode || opts.asyncMode === 'sync' ?
232
+ null :
233
+ {
234
+ tag: opts.asyncMode,
235
+ val: {
236
+ imports: opts.asyncImports || [],
237
+ exports: opts.asyncExports || [],
238
+ },
239
+ };
240
+
179
241
  let { files, imports, exports } = generate(component, {
180
242
  name: opts.name ?? 'component',
181
243
  map: Object.entries(opts.map ?? {}),
182
244
  instantiation,
245
+ asyncMode,
183
246
  importBindings: opts.importBindings ? { tag: opts.importBindings } : null,
184
247
  validLiftingOptimization: opts.validLiftingOptimization ?? false,
185
248
  tracing: opts.tracing ?? false,
package/src/common.d.ts CHANGED
@@ -9,8 +9,8 @@ export function table(data: any, align?: any[]): string;
9
9
  * The new directory is created using `fsPromises.mkdtemp()`.
10
10
  */
11
11
  export function getTmpDir(): Promise<string>;
12
- export function spawnIOTmp(cmd: any, input: any, args: any): Promise<Buffer>;
12
+ export function spawnIOTmp(cmd: any, input: any, args: any): Promise<Buffer<ArrayBufferLike>>;
13
13
  export const isWindows: boolean;
14
14
  export { readFileCli as readFile };
15
- declare function readFileCli(file: any, encoding: any): Promise<Buffer>;
15
+ declare function readFileCli(file: any, encoding: any): Promise<Buffer<ArrayBufferLike>>;
16
16
  //# sourceMappingURL=common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["common.js"],"names":[],"mappings":"AAWA,+CAEC;AACD,0CAIC;AAED,0CAOC;AAED,mEAaC;AAED,wDAcC;AAED;;;;GAIG;AACH,6CAEC;AAYD,6EA8BC;AArGD,gCAA8C;;AA6D9C,wEAOC"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["common.js"],"names":[],"mappings":"AAWA,+CAEC;AACD,0CAIC;AAED,0CAOC;AAED,mEAaC;AAED,wDAcC;AAED;;;;GAIG;AACH,6CAEC;AAYD,8FA8BC;AArGD,gCAA8C;;AA6D9C,yFAOC"}