@bytecodealliance/preview2-shim 0.17.2 → 0.17.3
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/lib/browser/cli.js +91 -94
- package/lib/browser/clocks.js +30 -29
- package/lib/browser/filesystem.js +298 -251
- package/lib/browser/http.js +129 -128
- package/lib/browser/index.js +8 -16
- package/lib/browser/io.js +143 -135
- package/lib/browser/random.js +44 -42
- package/lib/browser/sockets.js +68 -166
- package/lib/common/instantiation.js +127 -0
- package/lib/io/calls.js +7 -5
- package/lib/io/worker-http.js +175 -157
- package/lib/io/worker-io.js +402 -386
- package/lib/io/worker-socket-tcp.js +271 -219
- package/lib/io/worker-socket-udp.js +494 -429
- package/lib/io/worker-sockets.js +276 -262
- package/lib/io/worker-thread.js +943 -864
- package/lib/nodejs/cli.js +64 -63
- package/lib/nodejs/clocks.js +51 -45
- package/lib/nodejs/filesystem.js +788 -654
- package/lib/nodejs/http.js +693 -617
- package/lib/nodejs/index.js +8 -16
- package/lib/nodejs/random.js +32 -28
- package/lib/nodejs/sockets.js +538 -474
- package/lib/synckit/index.js +94 -85
- package/package.json +9 -5
- package/types/index.d.ts +0 -1
- package/types/instantiation.d.ts +112 -0
package/lib/synckit/index.js
CHANGED
|
@@ -23,105 +23,114 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
23
23
|
SOFTWARE.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import path from
|
|
26
|
+
import path from 'node:path';
|
|
27
27
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} from
|
|
28
|
+
MessageChannel,
|
|
29
|
+
Worker,
|
|
30
|
+
receiveMessageOnPort,
|
|
31
|
+
workerData,
|
|
32
|
+
parentPort,
|
|
33
|
+
} from 'node:worker_threads';
|
|
34
34
|
|
|
35
35
|
const DEFAULT_WORKER_BUFFER_SIZE = 1024;
|
|
36
36
|
|
|
37
37
|
function extractProperties(object) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
if (object && typeof object === 'object') {
|
|
39
|
+
const properties = {};
|
|
40
|
+
for (const key in object) {
|
|
41
|
+
properties[key] = object[key];
|
|
42
|
+
}
|
|
43
|
+
return properties;
|
|
42
44
|
}
|
|
43
|
-
return properties;
|
|
44
|
-
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const CALL_TIMEOUT = undefined;
|
|
48
48
|
|
|
49
49
|
export function createSyncFn(workerPath, debug, callbackHandler) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
const { port1: mainPort, port2: workerPort } = new MessageChannel();
|
|
54
|
-
const worker = new Worker(workerPath, {
|
|
55
|
-
workerData: { workerPort, debug },
|
|
56
|
-
transferList: [workerPort],
|
|
57
|
-
execArgv: []
|
|
58
|
-
});
|
|
59
|
-
worker.on('message', ({ type, id, payload }) => {
|
|
60
|
-
if (!type)
|
|
61
|
-
throw new Error('Internal error: Expected a type of a worker callback');
|
|
62
|
-
callbackHandler(type, id, payload);
|
|
63
|
-
});
|
|
64
|
-
let nextID = 0;
|
|
65
|
-
const syncFn = (...args) => {
|
|
66
|
-
const cid = nextID++;
|
|
67
|
-
const sharedBuffer = new SharedArrayBuffer(DEFAULT_WORKER_BUFFER_SIZE);
|
|
68
|
-
const sharedBufferView = new Int32Array(sharedBuffer);
|
|
69
|
-
const msg = { sharedBuffer, cid, args };
|
|
70
|
-
worker.postMessage(msg);
|
|
71
|
-
const status = Atomics.wait(sharedBufferView, 0, 0, CALL_TIMEOUT);
|
|
72
|
-
if (!["ok", "not-equal"].includes(status)) {
|
|
73
|
-
throw new Error("Internal error: Atomics.wait() failed: " + status);
|
|
74
|
-
}
|
|
75
|
-
const {
|
|
76
|
-
cid: cid2,
|
|
77
|
-
result,
|
|
78
|
-
error,
|
|
79
|
-
properties,
|
|
80
|
-
} = receiveMessageOnPort(mainPort).message;
|
|
81
|
-
if (cid !== cid2) {
|
|
82
|
-
throw new Error(`Internal error: Expected id ${cid} but got id ${cid2}`);
|
|
50
|
+
if (!path.isAbsolute(workerPath)) {
|
|
51
|
+
throw new Error('`workerPath` must be absolute');
|
|
83
52
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
53
|
+
const { port1: mainPort, port2: workerPort } = new MessageChannel();
|
|
54
|
+
const worker = new Worker(workerPath, {
|
|
55
|
+
workerData: { workerPort, debug },
|
|
56
|
+
transferList: [workerPort],
|
|
57
|
+
execArgv: [],
|
|
58
|
+
});
|
|
59
|
+
worker.on('message', ({ type, id, payload }) => {
|
|
60
|
+
if (!type) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
'Internal error: Expected a type of a worker callback'
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
callbackHandler(type, id, payload);
|
|
66
|
+
});
|
|
67
|
+
let nextID = 0;
|
|
68
|
+
const syncFn = (...args) => {
|
|
69
|
+
const cid = nextID++;
|
|
70
|
+
const sharedBuffer = new SharedArrayBuffer(DEFAULT_WORKER_BUFFER_SIZE);
|
|
71
|
+
const sharedBufferView = new Int32Array(sharedBuffer);
|
|
72
|
+
const msg = { sharedBuffer, cid, args };
|
|
73
|
+
worker.postMessage(msg);
|
|
74
|
+
const status = Atomics.wait(sharedBufferView, 0, 0, CALL_TIMEOUT);
|
|
75
|
+
if (!['ok', 'not-equal'].includes(status)) {
|
|
76
|
+
throw new Error('Internal error: Atomics.wait() failed: ' + status);
|
|
77
|
+
}
|
|
78
|
+
const {
|
|
79
|
+
cid: cid2,
|
|
80
|
+
result,
|
|
81
|
+
error,
|
|
82
|
+
properties,
|
|
83
|
+
} = receiveMessageOnPort(mainPort).message;
|
|
84
|
+
if (cid !== cid2) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Internal error: Expected id ${cid} but got id ${cid2}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (error) {
|
|
90
|
+
if (error instanceof Error) {
|
|
91
|
+
throw Object.assign(error, properties);
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
};
|
|
97
|
+
if (worker.unref) {
|
|
98
|
+
worker.unref();
|
|
87
99
|
}
|
|
88
|
-
return
|
|
89
|
-
};
|
|
90
|
-
if (worker.unref) worker.unref();
|
|
91
|
-
return syncFn;
|
|
100
|
+
return syncFn;
|
|
92
101
|
}
|
|
93
102
|
|
|
94
103
|
export function runAsWorker(fn) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
104
|
+
if (!workerData) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const { workerPort, debug } = workerData;
|
|
108
|
+
try {
|
|
109
|
+
parentPort.on('message', ({ sharedBuffer, cid, args }) => {
|
|
110
|
+
(async () => {
|
|
111
|
+
const sharedBufferView = new Int32Array(sharedBuffer);
|
|
112
|
+
let msg;
|
|
113
|
+
try {
|
|
114
|
+
msg = { cid, result: await fn(...args) };
|
|
115
|
+
} catch (error) {
|
|
116
|
+
msg = { cid, error, properties: extractProperties(error) };
|
|
117
|
+
}
|
|
118
|
+
workerPort.postMessage(msg);
|
|
119
|
+
Atomics.add(sharedBufferView, 0, 1);
|
|
120
|
+
Atomics.notify(sharedBufferView, 0);
|
|
121
|
+
})();
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
parentPort.on('message', ({ sharedBuffer, cid }) => {
|
|
125
|
+
const sharedBufferView = new Int32Array(sharedBuffer);
|
|
126
|
+
workerPort.postMessage({
|
|
127
|
+
cid,
|
|
128
|
+
error,
|
|
129
|
+
properties: extractProperties(error),
|
|
130
|
+
});
|
|
131
|
+
Atomics.add(sharedBufferView, 0, 1);
|
|
132
|
+
Atomics.notify(sharedBufferView, 0);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return debug;
|
|
127
136
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytecodealliance/preview2-shim",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
4
4
|
"description": "WASI Preview2 shim for JS environments",
|
|
5
5
|
"author": "Guy Bedford, Eduardo Rodrigues<16357187+eduardomourar@users.noreply.github.com>",
|
|
6
6
|
"type": "module",
|
|
@@ -15,18 +15,22 @@
|
|
|
15
15
|
"types": "./types/*.d.ts",
|
|
16
16
|
"node": "./lib/nodejs/*.js",
|
|
17
17
|
"default": "./lib/browser/*.js"
|
|
18
|
+
},
|
|
19
|
+
"./instantiation": {
|
|
20
|
+
"types": "./types/instantiation.d.ts",
|
|
21
|
+
"node": "./lib/common/instantiation.js",
|
|
22
|
+
"default": "./lib/common/instantiation.js"
|
|
18
23
|
}
|
|
19
24
|
},
|
|
20
25
|
"scripts": {
|
|
21
|
-
"
|
|
26
|
+
"lint": "eslint -c ../../eslint.config.mjs ./lib/**/*.js",
|
|
27
|
+
"lint:fix": "npm run lint -- --fix",
|
|
28
|
+
"test": "vitest run -c test/vitest.ts"
|
|
22
29
|
},
|
|
23
30
|
"files": [
|
|
24
31
|
"types",
|
|
25
32
|
"lib"
|
|
26
33
|
],
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"mocha": "^10.2.0"
|
|
29
|
-
},
|
|
30
34
|
"repository": {
|
|
31
35
|
"type": "git",
|
|
32
36
|
"url": "git+https://github.com/bytecodealliance/jco.git"
|
package/types/index.d.ts
CHANGED
|
@@ -2,6 +2,5 @@ export type * as cli from "./cli.d.ts";
|
|
|
2
2
|
export type * as clocks from './clocks.d.ts';
|
|
3
3
|
export type * as filesystem from './filesystem.d.ts';
|
|
4
4
|
export type * as http from "./http.d.ts";
|
|
5
|
-
export type * as io from "./io.d.ts";
|
|
6
5
|
export type * as random from "./random.d.ts";
|
|
7
6
|
export type * as sockets from "./sockets.d.ts";
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type alias that represents the kind of imports that the average
|
|
3
|
+
* transpiled Jco component will require.
|
|
4
|
+
*
|
|
5
|
+
* For example, `wasi:http/types` and `wasi:http/outgoing-handler` are present due to
|
|
6
|
+
* the ability for a component to call `fetch()` at any point in time.
|
|
7
|
+
*
|
|
8
|
+
* While the feature can be disabled when building with `jco componentize` or `componentize-js`,
|
|
9
|
+
* by default it is enabled, and as such included here (practically, the implementations can be no-ops).
|
|
10
|
+
*/
|
|
11
|
+
type WASIImportObject = {
|
|
12
|
+
'wasi:cli/environment': typeof import('./interfaces/wasi-cli-environment.d.ts');
|
|
13
|
+
'wasi:cli/exit': typeof import('./interfaces/wasi-cli-exit.d.ts');
|
|
14
|
+
'wasi:cli/stderr': typeof import('./interfaces/wasi-cli-stderr.d.ts');
|
|
15
|
+
'wasi:cli/stdin': typeof import('./interfaces/wasi-cli-stdin.d.ts');
|
|
16
|
+
'wasi:cli/stdout': typeof import('./interfaces/wasi-cli-stdout.d.ts');
|
|
17
|
+
'wasi:cli/terminal-input': typeof import('./interfaces/wasi-cli-terminal-input.d.ts');
|
|
18
|
+
'wasi:cli/terminal-output': typeof import('./interfaces/wasi-cli-terminal-output.d.ts');
|
|
19
|
+
'wasi:cli/terminal-stderr': typeof import('./interfaces/wasi-cli-terminal-stderr.d.ts');
|
|
20
|
+
'wasi:cli/terminal-stdin': typeof import('./interfaces/wasi-cli-terminal-stdin.d.ts');
|
|
21
|
+
'wasi:cli/terminal-stdout': typeof import('./interfaces/wasi-cli-terminal-stdout.d.ts');
|
|
22
|
+
|
|
23
|
+
'wasi:sockets/instance-network': typeof import('./interfaces/wasi-sockets-instance-network.d.ts');
|
|
24
|
+
'wasi:sockets/ip-name-lookup': typeof import('./interfaces/wasi-sockets-ip-name-lookup.d.ts');
|
|
25
|
+
'wasi:sockets/network': typeof import('./interfaces/wasi-sockets-network.d.ts');
|
|
26
|
+
'wasi:sockets/tcp': typeof import('./interfaces/wasi-sockets-tcp.d.ts');
|
|
27
|
+
'wasi:sockets/tcp-create-socket': typeof import('./interfaces/wasi-sockets-tcp-create-socket.d.ts');
|
|
28
|
+
'wasi:sockets/udp': typeof import('./interfaces/wasi-sockets-udp.d.ts');
|
|
29
|
+
'wasi:sockets/udp-create-socket': typeof import('./interfaces/wasi-sockets-udp-create-socket.d.ts');
|
|
30
|
+
|
|
31
|
+
'wasi:filesystem/preopens': typeof import('./interfaces/wasi-filesystem-preopens.d.ts');
|
|
32
|
+
'wasi:filesystem/types': typeof import('./interfaces/wasi-filesystem-types.d.ts');
|
|
33
|
+
|
|
34
|
+
'wasi:io/error': typeof import('./interfaces/wasi-io-error.d.ts');
|
|
35
|
+
'wasi:io/poll': typeof import('./interfaces/wasi-io-poll.d.ts');
|
|
36
|
+
'wasi:io/streams': typeof import('./interfaces/wasi-io-streams.d.ts');
|
|
37
|
+
|
|
38
|
+
'wasi:random/random': typeof import('./interfaces/wasi-random-random.d.ts');
|
|
39
|
+
'wasi:random/insecure': typeof import('./interfaces/wasi-random-insecure.d.ts');
|
|
40
|
+
'wasi:random/insecure-seed': typeof import('./interfaces/wasi-random-insecure-seed.d.ts');
|
|
41
|
+
|
|
42
|
+
'wasi:clocks/monotonic-clock': typeof import('./interfaces/wasi-clocks-monotonic-clock.d.ts');
|
|
43
|
+
'wasi:clocks/timezone': typeof import('./interfaces/wasi-clocks-timezone.d.ts');
|
|
44
|
+
'wasi:clocks/wall-clock': typeof import('./interfaces/wasi-clocks-wall-clock.d.ts');
|
|
45
|
+
|
|
46
|
+
'wasi:http/types': typeof import('./interfaces/wasi-http-types.d.ts');
|
|
47
|
+
'wasi:http/outgoing-handler': typeof import('./interfaces/wasi-http-outgoing-handler.d.ts');
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* (EXPERIMENTAL) A class that holds WASI shims and can be used to configure
|
|
52
|
+
* an instantiation of a WebAssembly component transpiled with jco
|
|
53
|
+
* (i.e. via `jco transpile`).
|
|
54
|
+
*
|
|
55
|
+
* Normally, transpiled components contain mapping for WASI interfaces
|
|
56
|
+
* and/or imports that import the relevant packages (ex. `@bytecodealliance/preview2-shim/clocks`)
|
|
57
|
+
* from the right sources.
|
|
58
|
+
*
|
|
59
|
+
* This function makes use of the `WASIShim` object to provide an object that can be easily
|
|
60
|
+
* fed to the `instantiate` function produced by a transpiled component:
|
|
61
|
+
*
|
|
62
|
+
* ```js
|
|
63
|
+
* import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation"
|
|
64
|
+
* // ...
|
|
65
|
+
* import { instantiate } from "path/to/transpiled/component.js"
|
|
66
|
+
* // ...
|
|
67
|
+
* const component = await instantiate(null, new WASIShim().getImportObject())
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* You can also replace imports that you'd like to override with custom implementations,
|
|
71
|
+
* by using the `WASIShim` object directly:
|
|
72
|
+
*
|
|
73
|
+
* ```js
|
|
74
|
+
* import { random } from "@bytecodealliance/preview2-shim"
|
|
75
|
+
* import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation"
|
|
76
|
+
* // ...
|
|
77
|
+
* import { instantiate } from "path/to/transpiled/component.js"
|
|
78
|
+
* // ...
|
|
79
|
+
* const customWASIShim = new WASIShim({
|
|
80
|
+
* random: {
|
|
81
|
+
* // For these two interfaces we re-use the default provided shim
|
|
82
|
+
* random: random.random,
|
|
83
|
+
* insecure-seed: random.insecureSeed,
|
|
84
|
+
* // For insecure, we can supply our own custom implementation
|
|
85
|
+
* insecure: {
|
|
86
|
+
* ...
|
|
87
|
+
* }
|
|
88
|
+
* }
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* const component = await instantiate(null, customWASIShim.getImportObject())
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* Note that this object is similar but not identical to the Node `WASI` object --
|
|
95
|
+
* it is solely concerned with shimming of preview2 when dealing with a WebAssembly
|
|
96
|
+
* component transpiled by Jco. While this object *does* work with Node (and the browser)
|
|
97
|
+
* semantics are not the same as Node's `WASI` object.
|
|
98
|
+
*
|
|
99
|
+
* @class WASIShim
|
|
100
|
+
*/
|
|
101
|
+
export class WASIShim {
|
|
102
|
+
constructor(shims?: Partial<WASIImportObject>);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Generate an import object for the shim that can be used with
|
|
106
|
+
* functions like `instantiate` that are exposed from a transpiled
|
|
107
|
+
* WebAssembly component.
|
|
108
|
+
*
|
|
109
|
+
* @returns {object}
|
|
110
|
+
*/
|
|
111
|
+
getImportObject(): WASIImportObject;
|
|
112
|
+
}
|