@bytecodealliance/preview2-shim 0.0.20 → 0.14.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.
Files changed (43) hide show
  1. package/README.md +4 -14
  2. package/lib/browser/cli.js +77 -12
  3. package/lib/browser/clocks.js +15 -27
  4. package/lib/browser/filesystem.js +147 -205
  5. package/lib/browser/index.js +1 -3
  6. package/lib/{common → browser}/io.js +8 -4
  7. package/lib/common/assert.js +7 -0
  8. package/lib/io/calls.js +64 -0
  9. package/lib/io/worker-http.js +95 -0
  10. package/lib/io/worker-io.js +322 -0
  11. package/lib/io/worker-thread.js +569 -0
  12. package/lib/nodejs/cli.js +45 -58
  13. package/lib/nodejs/clocks.js +13 -27
  14. package/lib/nodejs/filesystem.js +540 -427
  15. package/lib/nodejs/http.js +441 -175
  16. package/lib/nodejs/index.js +4 -1
  17. package/lib/nodejs/io.js +1 -0
  18. package/lib/nodejs/sockets/socket-common.js +116 -0
  19. package/lib/nodejs/sockets/socketopts-bindings.js +94 -0
  20. package/lib/nodejs/sockets/tcp-socket-impl.js +794 -0
  21. package/lib/nodejs/sockets/udp-socket-impl.js +628 -0
  22. package/lib/nodejs/sockets/wasi-sockets.js +320 -0
  23. package/lib/nodejs/sockets.js +11 -200
  24. package/lib/synckit/index.js +4 -2
  25. package/package.json +1 -5
  26. package/types/interfaces/wasi-cli-terminal-input.d.ts +4 -0
  27. package/types/interfaces/wasi-cli-terminal-output.d.ts +4 -0
  28. package/types/interfaces/wasi-clocks-monotonic-clock.d.ts +19 -6
  29. package/types/interfaces/wasi-filesystem-types.d.ts +1 -178
  30. package/types/interfaces/wasi-http-outgoing-handler.d.ts +2 -2
  31. package/types/interfaces/wasi-http-types.d.ts +412 -82
  32. package/types/interfaces/wasi-io-error.d.ts +16 -0
  33. package/types/interfaces/wasi-io-poll.d.ts +19 -8
  34. package/types/interfaces/wasi-io-streams.d.ts +26 -46
  35. package/types/interfaces/wasi-sockets-ip-name-lookup.d.ts +9 -21
  36. package/types/interfaces/wasi-sockets-network.d.ts +4 -0
  37. package/types/interfaces/wasi-sockets-tcp.d.ts +75 -18
  38. package/types/interfaces/wasi-sockets-udp-create-socket.d.ts +1 -1
  39. package/types/interfaces/wasi-sockets-udp.d.ts +282 -193
  40. package/types/wasi-cli-command.d.ts +28 -28
  41. package/types/wasi-http-proxy.d.ts +12 -12
  42. package/lib/common/make-request.js +0 -30
  43. package/types/interfaces/wasi-clocks-timezone.d.ts +0 -56
@@ -1,6 +1,8 @@
1
1
  let id = 0;
2
2
 
3
- class Error {
3
+ const symbolDispose = Symbol.dispose || Symbol.for('dispose');
4
+
5
+ const IoError = class Error {
4
6
  constructor (msg) {
5
7
  this.msg = msg;
6
8
  }
@@ -73,7 +75,7 @@ class InputStream {
73
75
  subscribe() {
74
76
  console.log(`[streams] Subscribe to input stream ${this.id}`);
75
77
  }
76
- drop () {
78
+ [symbolDispose] () {
77
79
  if (this.handler.drop)
78
80
  this.handler.drop.call(this);
79
81
  }
@@ -158,11 +160,13 @@ class OutputStream {
158
160
  subscribe() {
159
161
  console.log(`[streams] Subscribe to output stream ${this.id}`);
160
162
  }
161
- drop() {
163
+ [symbolDispose]() {
162
164
  }
163
165
  }
164
166
 
165
- export const streams = { Error, InputStream, OutputStream };
167
+ export const error = { Error: IoError };
168
+
169
+ export const streams = { InputStream, OutputStream };
166
170
 
167
171
  class Pollable {}
168
172
 
@@ -0,0 +1,7 @@
1
+ export function assert(condition, tag, _val) {
2
+ if (condition) {
3
+ // TODO: throw meaningful errors
4
+ // NOTE: wasmtime conformance tests are expecting a string here (a tag)
5
+ throw tag;
6
+ }
7
+ }
@@ -0,0 +1,64 @@
1
+ let call_id = 0;
2
+
3
+ // Call is a 32 bit integer, leading 16 bits are call number, trailing 16 bits allow custom call types
4
+ export const CALL_MASK = 0xff000000;
5
+ export const CALL_TYPE_MASK = 0x00ffffff;
6
+ export const CALL_SHIFT = 24;
7
+
8
+ // Io Input Stream
9
+ export const INPUT_STREAM_CREATE = ++call_id << CALL_SHIFT;
10
+ export const INPUT_STREAM_READ = ++call_id << CALL_SHIFT;
11
+ export const INPUT_STREAM_BLOCKING_READ = ++call_id << CALL_SHIFT;
12
+ export const INPUT_STREAM_SKIP = ++call_id << CALL_SHIFT;
13
+ export const INPUT_STREAM_BLOCKING_SKIP = ++call_id << CALL_SHIFT;
14
+ export const INPUT_STREAM_SUBSCRIBE = ++call_id << CALL_SHIFT;
15
+ export const INPUT_STREAM_DISPOSE = ++call_id << CALL_SHIFT;
16
+
17
+ // Io Output Stream
18
+ export const OUTPUT_STREAM_CREATE = ++call_id << CALL_SHIFT;
19
+ export const OUTPUT_STREAM_CHECK_WRITE = ++call_id << CALL_SHIFT;
20
+ export const OUTPUT_STREAM_WRITE = ++call_id << CALL_SHIFT;
21
+ export const OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH = ++call_id << CALL_SHIFT;
22
+ export const OUTPUT_STREAM_FLUSH = ++call_id << CALL_SHIFT;
23
+ export const OUTPUT_STREAM_BLOCKING_FLUSH = ++call_id << CALL_SHIFT;
24
+ export const OUTPUT_STREAM_WRITE_ZEROES = ++call_id << CALL_SHIFT;
25
+ export const OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH =
26
+ ++call_id << CALL_SHIFT;
27
+ export const OUTPUT_STREAM_SPLICE = ++call_id << CALL_SHIFT;
28
+ export const OUTPUT_STREAM_BLOCKING_SPLICE = ++call_id << CALL_SHIFT;
29
+ export const OUTPUT_STREAM_SUBSCRIBE = ++call_id << CALL_SHIFT;
30
+ export const OUTPUT_STREAM_DISPOSE = ++call_id << CALL_SHIFT;
31
+
32
+ export const OUTPUT_STREAM_GET_TOTAL_BYTES = ++call_id << CALL_SHIFT;
33
+
34
+ // Io Poll
35
+ export const POLL_POLLABLE_READY = ++call_id << CALL_SHIFT;
36
+ export const POLL_POLLABLE_BLOCK = ++call_id << CALL_SHIFT;
37
+ export const POLL_POLL_LIST = ++call_id << CALL_SHIFT;
38
+
39
+ // Futures
40
+ export const FUTURE_GET_VALUE_AND_DISPOSE = ++call_id << CALL_SHIFT;
41
+ export const FUTURE_DISPOSE = ++call_id << CALL_SHIFT;
42
+
43
+ // Http
44
+ export const HTTP_CREATE_REQUEST = ++call_id << 24;
45
+ export const HTTP_OUTPUT_STREAM_FINISH = ++call_id << CALL_SHIFT;
46
+
47
+ // Clocks
48
+ export const CLOCKS_NOW = ++call_id << 24;
49
+ export const CLOCKS_DURATION_SUBSCRIBE = ++call_id << 24;
50
+ export const CLOCKS_INSTANT_SUBSCRIBE = ++call_id << 24;
51
+
52
+ // Sockets
53
+ export const SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST = ++call_id << 24;
54
+ export const SOCKET_RESOLVE_ADDRESS_GET_AND_DISPOSE_REQUEST = ++call_id << 24;
55
+ export const SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST = ++call_id << 24;
56
+
57
+ // Type indiciator for generic Stream, Future, and Poll calls
58
+ let cnt = 0;
59
+ export const STDIN = ++cnt;
60
+ export const STDOUT = ++cnt;
61
+ export const STDERR = ++cnt;
62
+ export const FILE = ++cnt;
63
+ export const HTTP = ++cnt;
64
+ export const SOCKET = ++cnt;
@@ -0,0 +1,95 @@
1
+ import { Readable } from "node:stream";
2
+ import { createStream, getStreamOrThrow } from "./worker-thread.js";
3
+
4
+ export async function createHttpRequest(method, url, headers, bodyId) {
5
+ let body = null;
6
+ if (bodyId) {
7
+ try {
8
+ const { stream } = getStreamOrThrow(bodyId);
9
+ body = stream.readableBodyStream;
10
+ // this indicates we're attached
11
+ stream.readableBodyStream = null;
12
+ } catch (e) {
13
+ if (e.tag === "closed")
14
+ throw { tag: "internal-error", val: "Unexpected closed body stream" };
15
+ // it should never be possible for the body stream to already
16
+ // be closed, or for there to be a write error
17
+ // we therefore just throw internal error here
18
+ if (e.tag === "last-operation-failed")
19
+ throw {
20
+ tag: "internal-error",
21
+ val: e.val,
22
+ };
23
+ // entirely unknown error -> trap
24
+ throw e;
25
+ }
26
+ }
27
+ try {
28
+ const res = await fetch(url, {
29
+ method,
30
+ headers: new Headers(headers),
31
+ body,
32
+ redirect: "manual",
33
+ duplex: "half",
34
+ });
35
+ const bodyStreamId = createStream(Readable.fromWeb(res.body));
36
+ return {
37
+ status: res.status,
38
+ headers: Array.from(res.headers),
39
+ bodyStreamId: bodyStreamId,
40
+ };
41
+ } catch (e) {
42
+ if (e?.cause) {
43
+ let err = e.cause;
44
+ if (e.cause instanceof AggregateError) err = e.cause.errors[0];
45
+ if (err.message === "unknown scheme")
46
+ throw {
47
+ tag: "HTTP-protocol-error",
48
+ };
49
+ switch (err.syscall) {
50
+ case "connect": {
51
+ if (err.code === "ECONNREFUSED")
52
+ throw {
53
+ tag: "connection-refused",
54
+ };
55
+ break;
56
+ }
57
+ case "getaddrinfo": {
58
+ const { errno, code } = err;
59
+ throw {
60
+ tag: "DNS-error",
61
+ val: {
62
+ rcode: code,
63
+ infoCode: errno,
64
+ },
65
+ };
66
+ }
67
+ }
68
+ }
69
+ if (e?.message?.includes("Failed to parse URL")) {
70
+ throw {
71
+ tag: "HTTP-request-URI-invalid",
72
+ val: undefined,
73
+ };
74
+ }
75
+ if (e?.message?.includes("HTTP")) {
76
+ switch (e?.message.replace(/'[^']+'/, "'{}'")) {
77
+ case "'{}' HTTP method is unsupported.":
78
+ throw {
79
+ tag: "HTTP-protocol-error",
80
+ val: undefined,
81
+ };
82
+ case "'{}' is not a valid HTTP method.":
83
+ throw {
84
+ tag: "HTTP-request-method-invalid",
85
+ val: undefined,
86
+ };
87
+ }
88
+ throw {
89
+ tag: "internal-error",
90
+ val: e.toString(),
91
+ };
92
+ }
93
+ throw e;
94
+ }
95
+ }
@@ -0,0 +1,322 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import { createSyncFn } from "../synckit/index.js";
3
+ import {
4
+ CALL_MASK,
5
+ CALL_SHIFT,
6
+ CALL_TYPE_MASK,
7
+ INPUT_STREAM_BLOCKING_READ,
8
+ INPUT_STREAM_BLOCKING_SKIP,
9
+ INPUT_STREAM_DISPOSE,
10
+ INPUT_STREAM_READ,
11
+ INPUT_STREAM_SKIP,
12
+ INPUT_STREAM_SUBSCRIBE,
13
+ OUTPUT_STREAM_BLOCKING_FLUSH,
14
+ OUTPUT_STREAM_BLOCKING_SPLICE,
15
+ OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH,
16
+ OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH,
17
+ OUTPUT_STREAM_CHECK_WRITE,
18
+ OUTPUT_STREAM_DISPOSE,
19
+ OUTPUT_STREAM_FLUSH,
20
+ OUTPUT_STREAM_SPLICE,
21
+ OUTPUT_STREAM_SUBSCRIBE,
22
+ OUTPUT_STREAM_WRITE_ZEROES,
23
+ OUTPUT_STREAM_WRITE,
24
+ POLL_POLL_LIST,
25
+ POLL_POLLABLE_BLOCK,
26
+ POLL_POLLABLE_READY,
27
+ } from "./calls.js";
28
+ import { STDERR } from "./calls.js";
29
+
30
+ const DEBUG = false;
31
+
32
+ const workerPath = fileURLToPath(
33
+ new URL("./worker-thread.js", import.meta.url)
34
+ );
35
+
36
+ /**
37
+ * @type {(call: number, id: number | null, payload: any) -> any}
38
+ */
39
+ export let ioCall = createSyncFn(workerPath);
40
+ if (DEBUG) {
41
+ const _ioCall = ioCall;
42
+ ioCall = function ioCall(num, id, payload) {
43
+ let ret;
44
+ try {
45
+ process._rawDebug(
46
+ (num & CALL_MASK) >> CALL_SHIFT,
47
+ num & CALL_TYPE_MASK,
48
+ id,
49
+ payload
50
+ );
51
+ ret = _ioCall(num, id, payload);
52
+ return ret;
53
+ } catch (e) {
54
+ ret = e;
55
+ throw ret;
56
+ } finally {
57
+ process._rawDebug("->", ret);
58
+ }
59
+ };
60
+ }
61
+
62
+ const symbolDispose = Symbol.dispose || Symbol.for("dispose");
63
+
64
+ const _Error = Error;
65
+ const IoError = class Error extends _Error {
66
+ toDebugString() {
67
+ return this.message;
68
+ }
69
+ };
70
+
71
+ function streamIoErrorCall(call, id, payload) {
72
+ try {
73
+ return ioCall(call, id, payload);
74
+ } catch (e) {
75
+ if (e.tag === 'closed')
76
+ throw e;
77
+ if (e.tag === "last-operation-failed") {
78
+ e.val = new IoError(e.val);
79
+ throw e;
80
+ }
81
+ // any invalid error is a trap
82
+ console.trace(e);
83
+ process.exit(1);
84
+ }
85
+ }
86
+
87
+ class InputStream {
88
+ #id;
89
+ #streamType;
90
+ get _id() {
91
+ return this.#id;
92
+ }
93
+ read(len) {
94
+ return streamIoErrorCall(
95
+ INPUT_STREAM_READ | this.#streamType,
96
+ this.#id,
97
+ len
98
+ );
99
+ }
100
+ blockingRead(len) {
101
+ return streamIoErrorCall(
102
+ INPUT_STREAM_BLOCKING_READ | this.#streamType,
103
+ this.#id,
104
+ len
105
+ );
106
+ }
107
+ skip(len) {
108
+ return streamIoErrorCall(
109
+ INPUT_STREAM_SKIP | this.#streamType,
110
+ this.#id,
111
+ len
112
+ );
113
+ }
114
+ blockingSkip(len) {
115
+ return streamIoErrorCall(
116
+ INPUT_STREAM_BLOCKING_SKIP | this.#streamType,
117
+ this.#id,
118
+ len
119
+ );
120
+ }
121
+ subscribe() {
122
+ return pollableCreate(
123
+ ioCall(INPUT_STREAM_SUBSCRIBE | this.#streamType, this.#id)
124
+ );
125
+ }
126
+ [symbolDispose]() {
127
+ ioCall(INPUT_STREAM_DISPOSE | this.#streamType, this.#id);
128
+ }
129
+ static _id(stream) {
130
+ return stream.#id;
131
+ }
132
+ /**
133
+ * @param {InputStreamType} streamType
134
+ */
135
+ static _create(streamType, id) {
136
+ const stream = new InputStream();
137
+ stream.#id = id;
138
+ stream.#streamType = streamType;
139
+ return stream;
140
+ }
141
+ }
142
+
143
+ export const inputStreamCreate = InputStream._create;
144
+ delete InputStream._create;
145
+
146
+ export const inputStreamId = InputStream._id;
147
+ delete InputStream._id;
148
+
149
+ class OutputStream {
150
+ #id;
151
+ #streamType;
152
+ get _id() {
153
+ return this.#id;
154
+ }
155
+ checkWrite(len) {
156
+ return streamIoErrorCall(
157
+ OUTPUT_STREAM_CHECK_WRITE | this.#streamType,
158
+ this.#id,
159
+ len
160
+ );
161
+ }
162
+ write(buf) {
163
+ if (this.#streamType <= STDERR) return this.blockingWriteAndFlush(buf);
164
+ return streamIoErrorCall(
165
+ OUTPUT_STREAM_WRITE | this.#streamType,
166
+ this.#id,
167
+ buf
168
+ );
169
+ }
170
+ blockingWriteAndFlush(buf) {
171
+ if (this.#streamType <= STDERR) {
172
+ const stream =
173
+ this.#streamType === STDERR ? process.stderr : process.stdout;
174
+ return void stream.write(buf);
175
+ }
176
+ return streamIoErrorCall(
177
+ OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | this.#streamType,
178
+ this.#id,
179
+ buf
180
+ );
181
+ }
182
+ flush() {
183
+ return streamIoErrorCall(OUTPUT_STREAM_FLUSH | this.#streamType, this.#id);
184
+ }
185
+ blockingFlush() {
186
+ return streamIoErrorCall(
187
+ OUTPUT_STREAM_BLOCKING_FLUSH | this.#streamType,
188
+ this.#id
189
+ );
190
+ }
191
+ writeZeroes(len) {
192
+ return streamIoErrorCall(
193
+ OUTPUT_STREAM_WRITE_ZEROES | this.#streamType,
194
+ this.#id,
195
+ len
196
+ );
197
+ }
198
+ blockingWriteZeroesAndFlush(len) {
199
+ return streamIoErrorCall(
200
+ OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH | this.#streamType,
201
+ this.#id,
202
+ len
203
+ );
204
+ }
205
+ splice(src, len) {
206
+ return streamIoErrorCall(
207
+ OUTPUT_STREAM_SPLICE | this.#streamType,
208
+ this.#id,
209
+ src.#id,
210
+ len
211
+ );
212
+ }
213
+ blockingSplice(src, len) {
214
+ return streamIoErrorCall(
215
+ OUTPUT_STREAM_BLOCKING_SPLICE | this.#streamType,
216
+ this.#id,
217
+ src.#id,
218
+ len
219
+ );
220
+ }
221
+ subscribe() {
222
+ return pollableCreate(
223
+ ioCall(OUTPUT_STREAM_SUBSCRIBE | this.#streamType, this.#id)
224
+ );
225
+ }
226
+ [symbolDispose]() {
227
+ ioCall(OUTPUT_STREAM_DISPOSE | this.#streamType, this.#id);
228
+ }
229
+
230
+ static _id(outputStream) {
231
+ return outputStream.#id;
232
+ }
233
+ /**
234
+ * @param {OutputStreamType} streamType
235
+ * @param {any} createPayload
236
+ */
237
+ static _create(streamType, id) {
238
+ const stream = new OutputStream();
239
+ stream.#id = id;
240
+ stream.#streamType = streamType;
241
+ return stream;
242
+ }
243
+ }
244
+
245
+ export const outputStreamCreate = OutputStream._create;
246
+ delete OutputStream._create;
247
+
248
+ export const outputStreamId = OutputStream._id;
249
+ delete OutputStream._id;
250
+
251
+ export const error = { Error: IoError };
252
+
253
+ export const streams = { InputStream, OutputStream };
254
+
255
+ class Pollable {
256
+ #id;
257
+ #ready = false;
258
+ get _id() {
259
+ return this.#id;
260
+ }
261
+ ready() {
262
+ if (this.#ready) return true;
263
+ const ready = ioCall(POLL_POLLABLE_READY, this.#id);
264
+ if (ready) this.#ready = true;
265
+ return ready;
266
+ }
267
+ block() {
268
+ if (!this.#ready) {
269
+ ioCall(POLL_POLLABLE_BLOCK, this.#id);
270
+ this.#ready = true;
271
+ }
272
+ }
273
+ static _getId(pollable) {
274
+ return pollable.#id;
275
+ }
276
+ static _create(id) {
277
+ const pollable = new Pollable();
278
+ pollable.#id = id;
279
+ if (id === 0) pollable.#ready = true;
280
+ return pollable;
281
+ }
282
+ static _listToIds(list) {
283
+ return list.map((pollable) => pollable.#id);
284
+ }
285
+ static _markReady(pollable) {
286
+ pollable.#ready = true;
287
+ }
288
+ }
289
+
290
+ export const pollableCreate = Pollable._create;
291
+ delete Pollable._create;
292
+
293
+ const pollableListToIds = Pollable._listToIds;
294
+ delete Pollable._listToIds;
295
+
296
+ const pollableMarkReady = Pollable._markReady;
297
+ delete Pollable._markReady;
298
+
299
+ const pollableGetId = Pollable._getId;
300
+ delete Pollable._getId;
301
+
302
+ export const poll = {
303
+ Pollable,
304
+ poll(list) {
305
+ const includeList = ioCall(POLL_POLL_LIST, null, pollableListToIds(list));
306
+ return list.filter((pollable) => {
307
+ if (includeList.includes(pollableGetId(pollable))) {
308
+ pollableMarkReady(pollable);
309
+ return true;
310
+ }
311
+ return false;
312
+ });
313
+ },
314
+ };
315
+
316
+ export function resolvedPoll() {
317
+ return pollableCreate(0);
318
+ }
319
+
320
+ export function createPoll(call, id, initPayload) {
321
+ return pollableCreate(ioCall(call, id, initPayload));
322
+ }