@bytecodealliance/preview2-shim 0.19.0 → 0.20.1
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/README.md +19 -15
- package/dist/browser/filesystem.js +31 -7
- package/dist/browser/http.js +1 -1
- package/dist/common/instantiation.js +2 -1
- package/dist/io/worker-io.js +11 -6
- package/dist/io/worker-socket-udp.js +2 -1
- package/dist/io/worker-thread.bundle.js +1725 -0
- package/dist/io/worker-thread.js +5 -2
- package/dist/nodejs/filesystem.js +6 -3
- package/dist/nodejs/http.js +4 -4
- package/dist/synckit/index.d.ts +1 -1
- package/dist/synckit/index.js +21 -25
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@ Node.js support is fully tested and conformant against the Wasmtime test suite.
|
|
|
6
6
|
|
|
7
7
|
Browser support is considered experimental, and not currently suitable for production applications.
|
|
8
8
|
|
|
9
|
+
The Node.js implementation owns its worker artifact. Direct package use and supported downstream
|
|
10
|
+
bundlers should resolve it through the public shim imports; applications do not need to import or
|
|
11
|
+
copy files from `dist/io`.
|
|
12
|
+
|
|
9
13
|
# Features
|
|
10
14
|
|
|
11
15
|
## WASI Shim object for easy instantiation
|
|
@@ -15,8 +19,8 @@ An default instantiation object can be used via the `WASIShim` class in `@byteco
|
|
|
15
19
|
```typescript
|
|
16
20
|
import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation";
|
|
17
21
|
import type {
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
VersionedWASIImportObject,
|
|
23
|
+
WASIImportObject,
|
|
20
24
|
} from "@bytecodealliance/preview2-shim/instantiation";
|
|
21
25
|
|
|
22
26
|
const shim = new WASIShim();
|
|
@@ -27,7 +31,7 @@ unversioned satisfies WASIImportObject;
|
|
|
27
31
|
unversioned satisfies VersionedWASIImportObject<"">;
|
|
28
32
|
|
|
29
33
|
const versioned: VersionedWASIImportObject<"0.2.3"> = shim.getImportObject({
|
|
30
|
-
|
|
34
|
+
asVersion: "0.2.3",
|
|
31
35
|
});
|
|
32
36
|
//console.log('versioned', versioned);
|
|
33
37
|
versioned satisfies VersionedWASIImportObject<"0.2.3">;
|
|
@@ -66,23 +70,23 @@ import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation";
|
|
|
66
70
|
|
|
67
71
|
// Fully sandboxed - no filesystem, network, or env access
|
|
68
72
|
const sandboxedShim = new WASIShim({
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
sandbox: {
|
|
74
|
+
preopens: {}, // No filesystem access
|
|
75
|
+
env: {}, // No environment variables
|
|
76
|
+
args: ["arg1"], // Custom arguments
|
|
77
|
+
enableNetwork: false, // Disable network access
|
|
78
|
+
},
|
|
75
79
|
});
|
|
76
80
|
|
|
77
81
|
// Limited filesystem access - map virtual paths to host paths
|
|
78
82
|
const limitedShim = new WASIShim({
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
sandbox: {
|
|
84
|
+
preopens: {
|
|
85
|
+
"/data": "/tmp/guest-data", // Guest sees /data, maps to /tmp/guest-data
|
|
86
|
+
"/config": "/etc/app", // Guest sees /config, maps to /etc/app
|
|
87
|
+
},
|
|
88
|
+
env: { ENV1: "42" }, // Only expose specific env vars
|
|
83
89
|
},
|
|
84
|
-
env: { ENV1: "42" }, // Only expose specific env vars
|
|
85
|
-
},
|
|
86
90
|
});
|
|
87
91
|
|
|
88
92
|
const component = await instantiate(loader, sandboxedShim.getImportObject());
|
|
@@ -74,6 +74,21 @@ function getSource(fileEntry) {
|
|
|
74
74
|
}
|
|
75
75
|
return fileEntry.source;
|
|
76
76
|
}
|
|
77
|
+
// Keep spare capacity separate so FileDataEntry.source always reflects the logical file size.
|
|
78
|
+
const fileWriteBuffers = new WeakMap();
|
|
79
|
+
function getFileWriteBuffer(entry, source, requiredLength) {
|
|
80
|
+
let buffer = fileWriteBuffers.get(entry);
|
|
81
|
+
if (!buffer || buffer.buffer !== source.buffer || buffer.byteOffset !== source.byteOffset) {
|
|
82
|
+
buffer = source;
|
|
83
|
+
}
|
|
84
|
+
if (requiredLength <= buffer.byteLength) {
|
|
85
|
+
return buffer;
|
|
86
|
+
}
|
|
87
|
+
const newBuffer = new Uint8Array(Math.max(requiredLength, source.byteLength * 2));
|
|
88
|
+
newBuffer.set(source);
|
|
89
|
+
fileWriteBuffers.set(entry, newBuffer);
|
|
90
|
+
return newBuffer;
|
|
91
|
+
}
|
|
77
92
|
class DirectoryEntryStream {
|
|
78
93
|
idx = 0;
|
|
79
94
|
entries = [];
|
|
@@ -130,15 +145,24 @@ class Descriptor {
|
|
|
130
145
|
}
|
|
131
146
|
writeViaStream(_offset) {
|
|
132
147
|
const entry = this.#entry;
|
|
133
|
-
let offset =
|
|
148
|
+
let offset = coerceToSafeIntegerNumber(_offset);
|
|
134
149
|
return outputStreamCreate({
|
|
135
150
|
write(buf) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
offset
|
|
141
|
-
|
|
151
|
+
if (buf.byteLength === 0) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const source = getSource(entry);
|
|
155
|
+
const end = offset + buf.byteLength;
|
|
156
|
+
if (!Number.isSafeInteger(end)) {
|
|
157
|
+
throw new TypeError(`excessively large number: ${end}`);
|
|
158
|
+
}
|
|
159
|
+
const buffer = getFileWriteBuffer(entry, source, end);
|
|
160
|
+
if (offset > source.byteLength) {
|
|
161
|
+
buffer.fill(0, source.byteLength, offset);
|
|
162
|
+
}
|
|
163
|
+
buffer.set(buf, offset);
|
|
164
|
+
entry.source = buffer.subarray(0, Math.max(source.byteLength, end));
|
|
165
|
+
offset = end;
|
|
142
166
|
},
|
|
143
167
|
});
|
|
144
168
|
}
|
package/dist/browser/http.js
CHANGED
|
@@ -164,7 +164,8 @@ export class WASIShim {
|
|
|
164
164
|
obj[`wasi:sockets/tcp-create-socket${versionSuffix}`] = this.#sockets.tcpCreateSocket;
|
|
165
165
|
obj[`wasi:sockets/udp${versionSuffix}`] = this.#sockets.udp;
|
|
166
166
|
obj[`wasi:sockets/udp-create-socket${versionSuffix}`] = this.#sockets.udpCreateSocket;
|
|
167
|
-
obj[`wasi:filesystem/preopens${versionSuffix}`] =
|
|
167
|
+
obj[`wasi:filesystem/preopens${versionSuffix}`] =
|
|
168
|
+
this.#preopens ?? this.#filesystem.preopens;
|
|
168
169
|
obj[`wasi:filesystem/types${versionSuffix}`] = this.#filesystem.types;
|
|
169
170
|
obj[`wasi:io/error${versionSuffix}`] = this.#io.error;
|
|
170
171
|
obj[`wasi:io/poll${versionSuffix}`] = this.#io.poll;
|
package/dist/io/worker-io.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Worker } from "node:worker_threads";
|
|
2
2
|
import { createSyncFn } from "../synckit/index.js";
|
|
3
3
|
import { CALL_MASK, CALL_TYPE_MASK, FILE, HTTP_SERVER_INCOMING_HANDLER, HTTP, INPUT_STREAM_BLOCKING_READ, INPUT_STREAM_BLOCKING_SKIP, INPUT_STREAM_DISPOSE, INPUT_STREAM_READ, INPUT_STREAM_SKIP, INPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_BLOCKING_FLUSH, OUTPUT_STREAM_BLOCKING_SPLICE, OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH, OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH, OUTPUT_STREAM_CHECK_WRITE, OUTPUT_STREAM_DISPOSE, OUTPUT_STREAM_FLUSH, OUTPUT_STREAM_SPLICE, OUTPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_WRITE_ZEROES, OUTPUT_STREAM_WRITE, POLL_POLL_LIST, POLL_POLLABLE_BLOCK, POLL_POLLABLE_DISPOSE, POLL_POLLABLE_READY, SOCKET_TCP, STDERR, STDIN, STDOUT, reverseMap, } from "./calls.js";
|
|
4
4
|
import nodeProcess, { exit, stderr, stdout, env } from "node:process";
|
|
@@ -6,18 +6,21 @@ const symbolDispose = Symbol.dispose || Symbol.for("dispose");
|
|
|
6
6
|
const _rawDebug = "_rawDebug" in nodeProcess
|
|
7
7
|
? nodeProcess._rawDebug
|
|
8
8
|
: console.error.bind(console);
|
|
9
|
-
const workerPath = fileURLToPath(new URL("./worker-thread.js", import.meta.url));
|
|
10
9
|
const httpIncomingHandlers = new Map();
|
|
11
10
|
export function registerIncomingHttpHandler(id, handler) {
|
|
12
11
|
httpIncomingHandlers.set(id, handler);
|
|
13
12
|
}
|
|
14
13
|
const instanceId = Math.round(Math.random() * 1000).toString();
|
|
15
14
|
const DEBUG_DEFAULT = false;
|
|
16
|
-
const DEBUG = env.PREVIEW2_SHIM_DEBUG === "0"
|
|
15
|
+
const DEBUG = env.PREVIEW2_SHIM_DEBUG === "0"
|
|
16
|
+
? false
|
|
17
|
+
: env.PREVIEW2_SHIM_DEBUG === "1"
|
|
18
|
+
? true
|
|
19
|
+
: DEBUG_DEFAULT;
|
|
17
20
|
/**
|
|
18
21
|
* @type {(call: number, id: number | null, payload: any) -> any}
|
|
19
22
|
*/
|
|
20
|
-
export let ioCall = createSyncFn(
|
|
23
|
+
export let ioCall = createSyncFn((options) => new Worker(new URL("./worker-thread.bundle.js", import.meta.url), options), DEBUG, (type, id, payload) => {
|
|
21
24
|
// 'callbacks' from the worker
|
|
22
25
|
// ONLY happens for an http server incoming handler, and NOTHING else (not even sockets, since accept is sync!)
|
|
23
26
|
if (type !== HTTP_SERVER_INCOMING_HANDLER) {
|
|
@@ -148,7 +151,8 @@ class InputStream {
|
|
|
148
151
|
disposeFn = httpInputStreamDispose;
|
|
149
152
|
break;
|
|
150
153
|
default:
|
|
151
|
-
throw new Error("wasi-io trap: Dispose function not created for stream type " +
|
|
154
|
+
throw new Error("wasi-io trap: Dispose function not created for stream type " +
|
|
155
|
+
reverseMap[streamType]);
|
|
152
156
|
}
|
|
153
157
|
stream.#finalizer = registerDispose(stream, null, id, disposeFn);
|
|
154
158
|
return stream;
|
|
@@ -253,7 +257,8 @@ class OutputStream {
|
|
|
253
257
|
case HTTP:
|
|
254
258
|
return stream;
|
|
255
259
|
default:
|
|
256
|
-
throw new Error("wasi-io trap: Dispose function not created for stream type " +
|
|
260
|
+
throw new Error("wasi-io trap: Dispose function not created for stream type " +
|
|
261
|
+
reverseMap[streamType]);
|
|
257
262
|
}
|
|
258
263
|
stream.#finalizer = registerDispose(stream, null, id, disposeFn);
|
|
259
264
|
return stream;
|
|
@@ -406,7 +406,8 @@ export async function socketOutgoingDatagramStreamSend(id, datagrams) {
|
|
|
406
406
|
function doSendBatch() {
|
|
407
407
|
return new Promise((resolve, reject) => {
|
|
408
408
|
if (socket.remoteAddress) {
|
|
409
|
-
if (sendQueueAddress !== socket.remoteAddress ||
|
|
409
|
+
if (sendQueueAddress !== socket.remoteAddress ||
|
|
410
|
+
sendQueuePort !== socket.remotePort) {
|
|
410
411
|
return void reject("invalid-argument");
|
|
411
412
|
}
|
|
412
413
|
udpSocket.send(sendQueue, handler);
|