@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.
@@ -1,160 +1,161 @@
1
- import { createReadStream, createWriteStream } from "node:fs";
2
- import { hrtime, stderr, stdout } from "node:process";
3
- import { PassThrough } from "node:stream";
4
- import { format } from "node:util";
5
- import { runAsWorker } from "../synckit/index.js";
1
+ import { createReadStream, createWriteStream } from 'node:fs';
2
+ import { hrtime, stderr, stdout } from 'node:process';
3
+ import { PassThrough } from 'node:stream';
4
+ import { runAsWorker } from '../synckit/index.js';
6
5
  import {
7
- clearOutgoingResponse,
8
- createHttpRequest,
9
- setOutgoingResponse,
10
- startHttpServer,
11
- stopHttpServer,
12
- } from "./worker-http.js";
13
- import { Readable } from "node:stream";
14
- import { read } from "node:fs";
15
- import { nextTick } from "node:process";
6
+ clearOutgoingResponse,
7
+ createHttpRequest,
8
+ setOutgoingResponse,
9
+ startHttpServer,
10
+ stopHttpServer,
11
+ } from './worker-http.js';
12
+ import { Readable } from 'node:stream';
13
+ import { read } from 'node:fs';
14
+ import { nextTick } from 'node:process';
16
15
  import {
17
- CALL_MASK,
18
- CALL_TYPE_MASK,
19
- CLOCKS_DURATION_SUBSCRIBE,
20
- CLOCKS_INSTANT_SUBSCRIBE,
21
- FILE,
22
- FUTURE_DISPOSE,
23
- FUTURE_SUBSCRIBE,
24
- FUTURE_TAKE_VALUE,
25
- HTTP,
26
- HTTP_CREATE_REQUEST,
27
- HTTP_OUTGOING_BODY_DISPOSE,
28
- HTTP_OUTPUT_STREAM_FINISH,
29
- HTTP_SERVER_CLEAR_OUTGOING_RESPONSE,
30
- HTTP_SERVER_SET_OUTGOING_RESPONSE,
31
- HTTP_SERVER_START,
32
- HTTP_SERVER_STOP,
33
- INPUT_STREAM_BLOCKING_READ,
34
- INPUT_STREAM_BLOCKING_SKIP,
35
- INPUT_STREAM_CREATE,
36
- INPUT_STREAM_DISPOSE,
37
- INPUT_STREAM_READ,
38
- INPUT_STREAM_SKIP,
39
- INPUT_STREAM_SUBSCRIBE,
40
- OUTPUT_STREAM_BLOCKING_FLUSH,
41
- OUTPUT_STREAM_BLOCKING_SPLICE,
42
- OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH,
43
- OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH,
44
- OUTPUT_STREAM_CHECK_WRITE,
45
- OUTPUT_STREAM_CREATE,
46
- OUTPUT_STREAM_DISPOSE,
47
- OUTPUT_STREAM_FLUSH,
48
- OUTPUT_STREAM_SPLICE,
49
- OUTPUT_STREAM_SUBSCRIBE,
50
- OUTPUT_STREAM_WRITE,
51
- OUTPUT_STREAM_WRITE_ZEROES,
52
- POLL_POLLABLE_BLOCK,
53
- POLL_POLLABLE_DISPOSE,
54
- POLL_POLLABLE_READY,
55
- POLL_POLL_LIST,
56
- SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST,
57
- SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST,
58
- SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST,
59
- SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST,
60
- SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE,
61
- SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE,
62
- SOCKET_TCP_ACCEPT,
63
- SOCKET_TCP_BIND_FINISH,
64
- SOCKET_TCP_BIND_START,
65
- SOCKET_TCP_CONNECT_FINISH,
66
- SOCKET_TCP_CONNECT_START,
67
- SOCKET_TCP_CREATE_HANDLE,
68
- SOCKET_TCP_DISPOSE,
69
- SOCKET_TCP_GET_LOCAL_ADDRESS,
70
- SOCKET_TCP_GET_REMOTE_ADDRESS,
71
- SOCKET_TCP_IS_LISTENING,
72
- SOCKET_TCP_LISTEN_FINISH,
73
- SOCKET_TCP_LISTEN_START,
74
- SOCKET_TCP_SET_KEEP_ALIVE,
75
- SOCKET_TCP_SET_LISTEN_BACKLOG_SIZE,
76
- SOCKET_TCP_SHUTDOWN,
77
- SOCKET_TCP_SUBSCRIBE,
78
- SOCKET_UDP_BIND_FINISH,
79
- SOCKET_UDP_BIND_START,
80
- SOCKET_UDP_CREATE_HANDLE,
81
- SOCKET_UDP_DISPOSE,
82
- SOCKET_UDP_GET_LOCAL_ADDRESS,
83
- SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE,
84
- SOCKET_UDP_GET_REMOTE_ADDRESS,
85
- SOCKET_UDP_GET_SEND_BUFFER_SIZE,
86
- SOCKET_UDP_GET_UNICAST_HOP_LIMIT,
87
- SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE,
88
- SOCKET_UDP_SET_SEND_BUFFER_SIZE,
89
- SOCKET_UDP_SET_UNICAST_HOP_LIMIT,
90
- SOCKET_UDP_STREAM,
91
- SOCKET_UDP_SUBSCRIBE,
92
- SOCKET_INCOMING_DATAGRAM_STREAM_RECEIVE,
93
- SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND,
94
- SOCKET_OUTGOING_DATAGRAM_STREAM_SEND,
95
- SOCKET_DATAGRAM_STREAM_SUBSCRIBE,
96
- SOCKET_DATAGRAM_STREAM_DISPOSE,
97
- STDERR,
98
- STDIN,
99
- STDOUT,
100
- reverseMap,
101
- } from "./calls.js";
16
+ CALL_MASK,
17
+ CALL_TYPE_MASK,
18
+ CLOCKS_DURATION_SUBSCRIBE,
19
+ CLOCKS_INSTANT_SUBSCRIBE,
20
+ FILE,
21
+ FUTURE_DISPOSE,
22
+ FUTURE_SUBSCRIBE,
23
+ FUTURE_TAKE_VALUE,
24
+ HTTP,
25
+ HTTP_CREATE_REQUEST,
26
+ HTTP_OUTGOING_BODY_DISPOSE,
27
+ HTTP_OUTPUT_STREAM_FINISH,
28
+ HTTP_SERVER_CLEAR_OUTGOING_RESPONSE,
29
+ HTTP_SERVER_SET_OUTGOING_RESPONSE,
30
+ HTTP_SERVER_START,
31
+ HTTP_SERVER_STOP,
32
+ INPUT_STREAM_BLOCKING_READ,
33
+ INPUT_STREAM_BLOCKING_SKIP,
34
+ INPUT_STREAM_CREATE,
35
+ INPUT_STREAM_DISPOSE,
36
+ INPUT_STREAM_READ,
37
+ INPUT_STREAM_SKIP,
38
+ INPUT_STREAM_SUBSCRIBE,
39
+ OUTPUT_STREAM_BLOCKING_FLUSH,
40
+ OUTPUT_STREAM_BLOCKING_SPLICE,
41
+ OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH,
42
+ OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH,
43
+ OUTPUT_STREAM_CHECK_WRITE,
44
+ OUTPUT_STREAM_CREATE,
45
+ OUTPUT_STREAM_DISPOSE,
46
+ OUTPUT_STREAM_FLUSH,
47
+ OUTPUT_STREAM_SPLICE,
48
+ OUTPUT_STREAM_SUBSCRIBE,
49
+ OUTPUT_STREAM_WRITE,
50
+ OUTPUT_STREAM_WRITE_ZEROES,
51
+ POLL_POLLABLE_BLOCK,
52
+ POLL_POLLABLE_DISPOSE,
53
+ POLL_POLLABLE_READY,
54
+ POLL_POLL_LIST,
55
+ SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST,
56
+ SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST,
57
+ SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST,
58
+ SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST,
59
+ SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE,
60
+ SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE,
61
+ SOCKET_TCP_ACCEPT,
62
+ SOCKET_TCP_BIND_FINISH,
63
+ SOCKET_TCP_BIND_START,
64
+ SOCKET_TCP_CONNECT_FINISH,
65
+ SOCKET_TCP_CONNECT_START,
66
+ SOCKET_TCP_CREATE_HANDLE,
67
+ SOCKET_TCP_DISPOSE,
68
+ SOCKET_TCP_GET_LOCAL_ADDRESS,
69
+ SOCKET_TCP_GET_REMOTE_ADDRESS,
70
+ SOCKET_TCP_IS_LISTENING,
71
+ SOCKET_TCP_LISTEN_FINISH,
72
+ SOCKET_TCP_LISTEN_START,
73
+ SOCKET_TCP_SET_KEEP_ALIVE,
74
+ SOCKET_TCP_SET_LISTEN_BACKLOG_SIZE,
75
+ SOCKET_TCP_SHUTDOWN,
76
+ SOCKET_TCP_SUBSCRIBE,
77
+ SOCKET_UDP_BIND_FINISH,
78
+ SOCKET_UDP_BIND_START,
79
+ SOCKET_UDP_CREATE_HANDLE,
80
+ SOCKET_UDP_DISPOSE,
81
+ SOCKET_UDP_GET_LOCAL_ADDRESS,
82
+ SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE,
83
+ SOCKET_UDP_GET_REMOTE_ADDRESS,
84
+ SOCKET_UDP_GET_SEND_BUFFER_SIZE,
85
+ SOCKET_UDP_GET_UNICAST_HOP_LIMIT,
86
+ SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE,
87
+ SOCKET_UDP_SET_SEND_BUFFER_SIZE,
88
+ SOCKET_UDP_SET_UNICAST_HOP_LIMIT,
89
+ SOCKET_UDP_STREAM,
90
+ SOCKET_UDP_SUBSCRIBE,
91
+ SOCKET_INCOMING_DATAGRAM_STREAM_RECEIVE,
92
+ SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND,
93
+ SOCKET_OUTGOING_DATAGRAM_STREAM_SEND,
94
+ SOCKET_DATAGRAM_STREAM_SUBSCRIBE,
95
+ SOCKET_DATAGRAM_STREAM_DISPOSE,
96
+ STDERR,
97
+ STDIN,
98
+ STDOUT,
99
+ reverseMap,
100
+ } from './calls.js';
102
101
  import {
103
- SOCKET_STATE_BIND,
104
- SOCKET_STATE_BOUND,
105
- SOCKET_STATE_CONNECT,
106
- SOCKET_STATE_CONNECTION,
107
- SOCKET_STATE_LISTEN,
108
- SOCKET_STATE_LISTENER,
109
- socketResolveAddress,
110
- getDefaultSendBufferSize,
111
- getDefaultReceiveBufferSize,
112
- } from "./worker-sockets.js";
102
+ SOCKET_STATE_BIND,
103
+ SOCKET_STATE_BOUND,
104
+ SOCKET_STATE_CONNECT,
105
+ SOCKET_STATE_CONNECTION,
106
+ SOCKET_STATE_LISTEN,
107
+ SOCKET_STATE_LISTENER,
108
+ socketResolveAddress,
109
+ getDefaultSendBufferSize,
110
+ getDefaultReceiveBufferSize,
111
+ } from './worker-sockets.js';
113
112
  import {
114
- createTcpSocket,
115
- socketTcpAccept,
116
- socketTcpBindStart,
117
- socketTcpConnectStart,
118
- socketTcpDispose,
119
- socketTcpFinish,
120
- socketTcpGetLocalAddress,
121
- socketTcpGetRemoteAddress,
122
- socketTcpListenStart,
123
- socketTcpSetKeepAlive,
124
- socketTcpSetListenBacklogSize,
125
- socketTcpShutdown,
126
- tcpSockets,
127
- } from "./worker-socket-tcp.js";
113
+ createTcpSocket,
114
+ socketTcpAccept,
115
+ socketTcpBindStart,
116
+ socketTcpConnectStart,
117
+ socketTcpDispose,
118
+ socketTcpFinish,
119
+ socketTcpGetLocalAddress,
120
+ socketTcpGetRemoteAddress,
121
+ socketTcpListenStart,
122
+ socketTcpSetKeepAlive,
123
+ socketTcpSetListenBacklogSize,
124
+ socketTcpShutdown,
125
+ tcpSockets,
126
+ } from './worker-socket-tcp.js';
128
127
  import {
129
- createUdpSocket,
130
- datagramStreams,
131
- socketDatagramStreamDispose,
132
- socketIncomingDatagramStreamReceive,
133
- socketOutgoingDatagramStreamCheckSend,
134
- socketOutgoingDatagramStreamSend,
135
- socketUdpBindFinish,
136
- socketUdpBindStart,
137
- socketUdpDispose,
138
- socketUdpGetLocalAddress,
139
- socketUdpGetReceiveBufferSize,
140
- socketUdpGetRemoteAddress,
141
- socketUdpGetSendBufferSize,
142
- socketUdpGetUnicastHopLimit,
143
- socketUdpSetReceiveBufferSize,
144
- socketUdpSetSendBufferSize,
145
- socketUdpSetUnicastHopLimit,
146
- socketUdpStream,
147
- udpSockets,
148
- } from "./worker-socket-udp.js";
149
- import process from "node:process";
150
-
151
- function log(msg) {
152
- if (debug) process._rawDebug(msg);
128
+ createUdpSocket,
129
+ datagramStreams,
130
+ socketDatagramStreamDispose,
131
+ socketIncomingDatagramStreamReceive,
132
+ socketOutgoingDatagramStreamCheckSend,
133
+ socketOutgoingDatagramStreamSend,
134
+ socketUdpBindFinish,
135
+ socketUdpBindStart,
136
+ socketUdpDispose,
137
+ socketUdpGetLocalAddress,
138
+ socketUdpGetReceiveBufferSize,
139
+ socketUdpGetRemoteAddress,
140
+ socketUdpGetSendBufferSize,
141
+ socketUdpGetUnicastHopLimit,
142
+ socketUdpSetReceiveBufferSize,
143
+ socketUdpSetSendBufferSize,
144
+ socketUdpSetUnicastHopLimit,
145
+ socketUdpStream,
146
+ udpSockets,
147
+ } from './worker-socket-udp.js';
148
+ import process from 'node:process';
149
+
150
+ export function log(msg) {
151
+ if (debug) {
152
+ process._rawDebug(msg);
153
+ }
153
154
  }
154
155
 
155
156
  let pollCnt = 0,
156
- streamCnt = 0,
157
- futureCnt = 0;
157
+ streamCnt = 0,
158
+ futureCnt = 0;
158
159
 
159
160
  /**
160
161
  * @typedef {{
@@ -189,68 +190,68 @@ export const streams = new Map();
189
190
  export const futures = new Map();
190
191
 
191
192
  export function createReadableStreamPollState(nodeStream) {
192
- const pollState = {
193
- ready: true,
194
- listener: null,
195
- polls: [],
196
- parentStream: nodeStream,
197
- };
198
- function pollDone() {
199
- pollStateReady(pollState);
200
- nodeStream.off("end", pollDone);
201
- nodeStream.off("close", pollDone);
202
- nodeStream.off("error", pollDone);
203
- }
204
- nodeStream.on("end", pollDone);
205
- nodeStream.on("close", pollDone);
206
- nodeStream.on("error", pollDone);
207
- return pollState;
193
+ const pollState = {
194
+ ready: true,
195
+ listener: null,
196
+ polls: [],
197
+ parentStream: nodeStream,
198
+ };
199
+ function pollDone() {
200
+ pollStateReady(pollState);
201
+ nodeStream.off('end', pollDone);
202
+ nodeStream.off('close', pollDone);
203
+ nodeStream.off('error', pollDone);
204
+ }
205
+ nodeStream.on('end', pollDone);
206
+ nodeStream.on('close', pollDone);
207
+ nodeStream.on('error', pollDone);
208
+ return pollState;
208
209
  }
209
210
 
210
211
  /**
211
212
  * @param {NodeJS.ReadableStream | NodeJS.WritableStream} stream
212
213
  */
213
214
  export function createReadableStream(
214
- nodeStream,
215
- pollState = createReadableStreamPollState(nodeStream)
215
+ nodeStream,
216
+ pollState = createReadableStreamPollState(nodeStream)
216
217
  ) {
217
- const stream = {
218
- stream: nodeStream,
219
- flushPromise: null,
220
- pollState,
221
- };
222
- streams.set(++streamCnt, stream);
223
- return streamCnt;
218
+ const stream = {
219
+ stream: nodeStream,
220
+ flushPromise: null,
221
+ pollState,
222
+ };
223
+ streams.set(++streamCnt, stream);
224
+ return streamCnt;
224
225
  }
225
226
 
226
227
  export function createWritableStream(nodeStream) {
227
- const pollState = {
228
- ready: true,
229
- listener: null,
230
- polls: [],
231
- parentStream: null,
232
- };
233
- const stream = {
234
- stream: nodeStream,
235
- flushPromise: null,
236
- pollState,
237
- };
238
- streams.set(++streamCnt, stream);
239
- function pollReady() {
240
- pollStateReady(pollState);
241
- }
242
- function pollDone() {
243
- pollStateReady(pollState);
244
- nodeStream.off("drain", pollReady);
245
- nodeStream.off("finish", pollDone);
246
- nodeStream.off("error", pollDone);
247
- nodeStream.off("close", pollDone);
248
- }
249
- nodeStream.on("drain", pollReady);
250
- nodeStream.on("finish", pollDone);
251
- nodeStream.on("error", pollDone);
252
- nodeStream.on("close", pollDone);
253
- return streamCnt;
228
+ const pollState = {
229
+ ready: true,
230
+ listener: null,
231
+ polls: [],
232
+ parentStream: null,
233
+ };
234
+ const stream = {
235
+ stream: nodeStream,
236
+ flushPromise: null,
237
+ pollState,
238
+ };
239
+ streams.set(++streamCnt, stream);
240
+ function pollReady() {
241
+ pollStateReady(pollState);
242
+ }
243
+ function pollDone() {
244
+ pollStateReady(pollState);
245
+ nodeStream.off('drain', pollReady);
246
+ nodeStream.off('finish', pollDone);
247
+ nodeStream.off('error', pollDone);
248
+ nodeStream.off('close', pollDone);
249
+ }
250
+ nodeStream.on('drain', pollReady);
251
+ nodeStream.on('finish', pollDone);
252
+ nodeStream.on('error', pollDone);
253
+ nodeStream.on('close', pollDone);
254
+ return streamCnt;
254
255
  }
255
256
 
256
257
  // Stdio
@@ -263,10 +264,10 @@ createWritableStream(stderr);
263
264
  * @param {NodeJS.ReadableStream | NodeJS.WritableStream} stream
264
265
  */
265
266
  function streamError(err) {
266
- return {
267
- tag: "last-operation-failed",
268
- val: { code: err.code, message: err.message, stack: err.stack },
269
- };
267
+ return {
268
+ tag: 'last-operation-failed',
269
+ val: { code: err.code, message: err.message, stack: err.stack },
270
+ };
270
271
  }
271
272
 
272
273
  /**
@@ -274,15 +275,21 @@ function streamError(err) {
274
275
  * @returns {{ stream: NodeJS.ReadableStream | NodeJS.WritableStream, polls: number[] }}
275
276
  */
276
277
  export function getStreamOrThrow(streamId) {
277
- if (!streamId) throw new Error("wasi-io trap: no stream id provided");
278
- const stream = streams.get(streamId);
279
- // not in unfinished streams <=> closed
280
- if (!stream) throw { tag: "closed" };
281
- if (stream.stream.errored) throw streamError(stream.stream.errored);
282
- if (stream.stream.closed) {
283
- throw { tag: "closed" };
284
- }
285
- return stream;
278
+ if (!streamId) {
279
+ throw new Error('wasi-io trap: no stream id provided');
280
+ }
281
+ const stream = streams.get(streamId);
282
+ // not in unfinished streams <=> closed
283
+ if (!stream) {
284
+ throw { tag: 'closed' };
285
+ }
286
+ if (stream.stream.errored) {
287
+ throw streamError(stream.stream.errored);
288
+ }
289
+ if (stream.stream.closed) {
290
+ throw { tag: 'closed' };
291
+ }
292
+ return stream;
286
293
  }
287
294
 
288
295
  /**
@@ -292,604 +299,675 @@ export function getStreamOrThrow(streamId) {
292
299
  * @returns {Promise<any>}
293
300
  */
294
301
  function handle(call, id, payload) {
295
- if (uncaughtException) throw uncaughtException;
296
- switch (call) {
297
- // Http
298
- case HTTP_CREATE_REQUEST: {
299
- const {
300
- method,
301
- scheme,
302
- authority,
303
- pathWithQuery,
304
- headers,
305
- body,
306
- connectTimeout,
307
- betweenBytesTimeout,
308
- firstByteTimeout,
309
- } = payload;
310
- return createFuture(
311
- createHttpRequest(
312
- method,
313
- scheme,
314
- authority,
315
- pathWithQuery,
316
- headers,
317
- body,
318
- connectTimeout,
319
- betweenBytesTimeout,
320
- firstByteTimeout
321
- )
322
- );
323
- }
324
- case OUTPUT_STREAM_CREATE | HTTP: {
325
- const stream = new PassThrough();
326
- // content length is passed as payload
327
- stream.contentLength = payload;
328
- stream.bytesRemaining = payload;
329
- return createWritableStream(stream);
302
+ if (uncaughtException) {
303
+ throw uncaughtException;
330
304
  }
331
- case OUTPUT_STREAM_SUBSCRIBE | HTTP:
332
- case OUTPUT_STREAM_FLUSH | HTTP:
333
- case OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP: {
334
- // http flush is a noop
335
- const { stream } = getStreamOrThrow(id);
336
- if (call === (OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP)) {
337
- stream.bytesRemaining -= payload.byteLength;
338
- if (stream.bytesRemaining < 0) {
339
- throw {
340
- tag: "last-operation-failed",
341
- val: {
342
- tag: "HTTP-request-body-size",
343
- val: stream.contentLength - stream.bytesRemaining,
344
- },
345
- };
305
+ switch (call) {
306
+ // Http
307
+ case HTTP_CREATE_REQUEST: {
308
+ const {
309
+ method,
310
+ scheme,
311
+ authority,
312
+ pathWithQuery,
313
+ headers,
314
+ body,
315
+ connectTimeout,
316
+ betweenBytesTimeout,
317
+ firstByteTimeout,
318
+ } = payload;
319
+ return createFuture(
320
+ createHttpRequest(
321
+ method,
322
+ scheme,
323
+ authority,
324
+ pathWithQuery,
325
+ headers,
326
+ body,
327
+ connectTimeout,
328
+ betweenBytesTimeout,
329
+ firstByteTimeout
330
+ )
331
+ );
346
332
  }
347
- }
348
- // otherwise fall through to generic implementation
349
- return handle(call & ~HTTP, id, payload);
350
- }
351
- case OUTPUT_STREAM_WRITE | HTTP: {
352
- const { stream } = getStreamOrThrow(id);
353
- stream.bytesRemaining -= payload.byteLength;
354
- if (stream.bytesRemaining < 0) {
355
- throw {
356
- tag: "last-operation-failed",
357
- val: {
358
- tag: "HTTP-request-body-size",
359
- val: stream.contentLength - stream.bytesRemaining,
360
- },
361
- };
362
- }
363
- const output = handle(OUTPUT_STREAM_WRITE, id, payload);
364
- return output;
365
- }
366
- case OUTPUT_STREAM_DISPOSE | HTTP:
367
- throw new Error(
368
- "wasi-io trap: Output stream dispose not implemented as an IO-call for HTTP"
369
- );
370
- case HTTP_OUTPUT_STREAM_FINISH: {
371
- let stream;
372
- try {
373
- ({ stream } = getStreamOrThrow(id));
374
- } catch (e) {
375
- if (e.tag === "closed")
376
- throw { tag: "internal-error", val: "stream closed" };
377
- if (e.tag === "last-operation-failed")
378
- throw { tag: "internal-error", val: e.val.message };
379
- }
380
- if (stream.bytesRemaining > 0) {
381
- throw {
382
- tag: "HTTP-request-body-size",
383
- val: stream.contentLength - stream.bytesRemaining,
384
- };
385
- }
386
- if (stream.bytesRemaining < 0) {
387
- throw {
388
- tag: "HTTP-request-body-size",
389
- val: stream.contentLength - stream.bytesRemaining,
390
- };
391
- }
392
- stream.end();
393
- return;
394
- }
395
- case HTTP_OUTGOING_BODY_DISPOSE:
396
- if (debug && !streams.has(id))
397
- console.warn(`wasi-io: stream ${id} not found to dispose`);
398
- streams.delete(id);
399
- return;
400
- case HTTP_SERVER_START:
401
- return startHttpServer(id, payload);
402
- case HTTP_SERVER_STOP:
403
- return stopHttpServer(id);
404
- case HTTP_SERVER_SET_OUTGOING_RESPONSE:
405
- return setOutgoingResponse(id, payload);
406
- case HTTP_SERVER_CLEAR_OUTGOING_RESPONSE:
407
- return clearOutgoingResponse(id);
408
-
409
- // Sockets name resolution
410
- case SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST:
411
- return createFuture(socketResolveAddress(payload));
412
- case SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST:
413
- return createPoll(futures.get(id).pollState);
414
- case SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST:
415
- return void futureDispose(id, true);
416
- case SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST: {
417
- const val = futureTakeValue(id);
418
- if (val === undefined) throw "would-block";
419
- // double take avoidance is ensured
420
- return val.val;
421
- }
422
-
423
- // Sockets TCP
424
- case SOCKET_TCP_ACCEPT:
425
- return socketTcpAccept(id);
426
- case SOCKET_TCP_CREATE_HANDLE:
427
- return createTcpSocket();
428
- case SOCKET_TCP_BIND_START:
429
- return socketTcpBindStart(id, payload.localAddress, payload.family);
430
- case SOCKET_TCP_BIND_FINISH:
431
- return socketTcpFinish(id, SOCKET_STATE_BIND, SOCKET_STATE_BOUND);
432
- case SOCKET_TCP_CONNECT_START:
433
- return socketTcpConnectStart(id, payload.remoteAddress, payload.family);
434
- case SOCKET_TCP_CONNECT_FINISH:
435
- return socketTcpFinish(id, SOCKET_STATE_CONNECT, SOCKET_STATE_CONNECTION);
436
- case SOCKET_TCP_LISTEN_START:
437
- return socketTcpListenStart(id);
438
- case SOCKET_TCP_LISTEN_FINISH:
439
- return socketTcpFinish(id, SOCKET_STATE_LISTEN, SOCKET_STATE_LISTENER);
440
- case SOCKET_TCP_IS_LISTENING:
441
- return tcpSockets.get(id).state === SOCKET_STATE_LISTENER;
442
- case SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE:
443
- return getDefaultSendBufferSize(id);
444
- case SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE:
445
- return getDefaultReceiveBufferSize(id);
446
- case SOCKET_TCP_SET_LISTEN_BACKLOG_SIZE:
447
- return socketTcpSetListenBacklogSize(id);
448
- case SOCKET_TCP_GET_LOCAL_ADDRESS:
449
- return socketTcpGetLocalAddress(id);
450
- case SOCKET_TCP_GET_REMOTE_ADDRESS:
451
- return socketTcpGetRemoteAddress(id);
452
- case SOCKET_TCP_SHUTDOWN:
453
- return socketTcpShutdown(id, payload);
454
- case SOCKET_TCP_SUBSCRIBE:
455
- return createPoll(tcpSockets.get(id).pollState);
456
- case SOCKET_TCP_SET_KEEP_ALIVE:
457
- return socketTcpSetKeepAlive(id, payload);
458
- case SOCKET_TCP_DISPOSE:
459
- return socketTcpDispose(id);
460
-
461
- // Sockets UDP
462
- case SOCKET_UDP_CREATE_HANDLE:
463
- return createUdpSocket(payload);
464
- case SOCKET_UDP_BIND_START:
465
- return socketUdpBindStart(id, payload.localAddress, payload.family);
466
- case SOCKET_UDP_BIND_FINISH:
467
- return socketUdpBindFinish(id);
468
- case SOCKET_UDP_STREAM:
469
- return socketUdpStream(id, payload);
470
- case SOCKET_UDP_SUBSCRIBE:
471
- return createPoll(udpSockets.get(id).pollState);
472
- case SOCKET_UDP_GET_LOCAL_ADDRESS:
473
- return socketUdpGetLocalAddress(id);
474
- case SOCKET_UDP_GET_REMOTE_ADDRESS:
475
- return socketUdpGetRemoteAddress(id);
476
- case SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE:
477
- return socketUdpSetReceiveBufferSize(id, payload);
478
- case SOCKET_UDP_SET_SEND_BUFFER_SIZE:
479
- return socketUdpSetSendBufferSize(id, payload);
480
- case SOCKET_UDP_SET_UNICAST_HOP_LIMIT:
481
- return socketUdpSetUnicastHopLimit(id, payload);
482
- case SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE:
483
- return socketUdpGetReceiveBufferSize(id);
484
- case SOCKET_UDP_GET_SEND_BUFFER_SIZE:
485
- return socketUdpGetSendBufferSize(id);
486
- case SOCKET_UDP_GET_UNICAST_HOP_LIMIT:
487
- return socketUdpGetUnicastHopLimit(id);
488
- case SOCKET_UDP_DISPOSE:
489
- return socketUdpDispose(id);
490
-
491
- case SOCKET_INCOMING_DATAGRAM_STREAM_RECEIVE:
492
- return socketIncomingDatagramStreamReceive(id, payload);
493
- case SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND:
494
- return socketOutgoingDatagramStreamCheckSend(id);
495
- case SOCKET_OUTGOING_DATAGRAM_STREAM_SEND:
496
- return socketOutgoingDatagramStreamSend(id, payload);
497
- case SOCKET_DATAGRAM_STREAM_SUBSCRIBE:
498
- return createPoll(datagramStreams.get(id).pollState);
499
- case SOCKET_DATAGRAM_STREAM_DISPOSE:
500
- return socketDatagramStreamDispose(id);
501
-
502
- // Stdio
503
- case OUTPUT_STREAM_BLOCKING_FLUSH | STDOUT:
504
- case OUTPUT_STREAM_BLOCKING_FLUSH | STDERR:
505
- // no blocking flush for stdio in Node.js
506
- return;
507
- case OUTPUT_STREAM_DISPOSE | STDOUT:
508
- case OUTPUT_STREAM_DISPOSE | STDERR:
509
- return;
510
- case INPUT_STREAM_CREATE | STDIN: {
511
- return createReadableStream(
512
- new Readable({
513
- read(n) {
514
- if (n <= 0) return void this.push(null);
515
- let buf = Buffer.allocUnsafeSlow(n);
516
- read(0, buf, 0, n, null, (err, bytesRead) => {
517
- if (err) {
518
- if (err.code === "EAGAIN") {
519
- nextTick(() => void this._read(n));
520
- return;
333
+ case OUTPUT_STREAM_CREATE | HTTP: {
334
+ const stream = new PassThrough();
335
+ // content length is passed as payload
336
+ stream.contentLength = payload;
337
+ stream.bytesRemaining = payload;
338
+ return createWritableStream(stream);
339
+ }
340
+ case OUTPUT_STREAM_SUBSCRIBE | HTTP:
341
+ case OUTPUT_STREAM_FLUSH | HTTP:
342
+ case OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP: {
343
+ // http flush is a noop
344
+ const { stream } = getStreamOrThrow(id);
345
+ if (call === (OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP)) {
346
+ stream.bytesRemaining -= payload.byteLength;
347
+ if (stream.bytesRemaining < 0) {
348
+ throw {
349
+ tag: 'last-operation-failed',
350
+ val: {
351
+ tag: 'HTTP-request-body-size',
352
+ val: stream.contentLength - stream.bytesRemaining,
353
+ },
354
+ };
355
+ }
356
+ }
357
+ // otherwise fall through to generic implementation
358
+ return handle(call & ~HTTP, id, payload);
359
+ }
360
+ case OUTPUT_STREAM_WRITE | HTTP: {
361
+ const { stream } = getStreamOrThrow(id);
362
+ stream.bytesRemaining -= payload.byteLength;
363
+ if (stream.bytesRemaining < 0) {
364
+ throw {
365
+ tag: 'last-operation-failed',
366
+ val: {
367
+ tag: 'HTTP-request-body-size',
368
+ val: stream.contentLength - stream.bytesRemaining,
369
+ },
370
+ };
371
+ }
372
+ const output = handle(OUTPUT_STREAM_WRITE, id, payload);
373
+ return output;
374
+ }
375
+ case OUTPUT_STREAM_DISPOSE | HTTP:
376
+ throw new Error(
377
+ 'wasi-io trap: Output stream dispose not implemented as an IO-call for HTTP'
378
+ );
379
+ case HTTP_OUTPUT_STREAM_FINISH: {
380
+ let stream;
381
+ try {
382
+ ({ stream } = getStreamOrThrow(id));
383
+ } catch (e) {
384
+ if (e.tag === 'closed') {
385
+ throw { tag: 'internal-error', val: 'stream closed' };
521
386
  }
522
- this.destroy(err);
523
- } else if (bytesRead > 0) {
524
- if (bytesRead !== buf.length) {
525
- const dst = Buffer.allocUnsafeSlow(bytesRead);
526
- buf.copy(dst, 0, 0, bytesRead);
527
- buf = dst;
387
+ if (e.tag === 'last-operation-failed') {
388
+ throw { tag: 'internal-error', val: e.val.message };
528
389
  }
529
- this.push(buf);
530
- } else {
531
- this.push(null);
532
- }
533
- });
534
- },
535
- })
536
- );
537
- }
390
+ }
391
+ if (stream.bytesRemaining > 0) {
392
+ throw {
393
+ tag: 'HTTP-request-body-size',
394
+ val: stream.contentLength - stream.bytesRemaining,
395
+ };
396
+ }
397
+ if (stream.bytesRemaining < 0) {
398
+ throw {
399
+ tag: 'HTTP-request-body-size',
400
+ val: stream.contentLength - stream.bytesRemaining,
401
+ };
402
+ }
403
+ stream.end();
404
+ return;
405
+ }
406
+ case HTTP_OUTGOING_BODY_DISPOSE:
407
+ if (debug && !streams.has(id)) {
408
+ console.warn(`wasi-io: stream ${id} not found to dispose`);
409
+ }
410
+ streams.delete(id);
411
+ return;
412
+ case HTTP_SERVER_START:
413
+ return startHttpServer(id, payload);
414
+ case HTTP_SERVER_STOP:
415
+ return stopHttpServer(id);
416
+ case HTTP_SERVER_SET_OUTGOING_RESPONSE:
417
+ return setOutgoingResponse(id, payload);
418
+ case HTTP_SERVER_CLEAR_OUTGOING_RESPONSE:
419
+ return clearOutgoingResponse(id);
538
420
 
539
- // Clocks
540
- case CLOCKS_DURATION_SUBSCRIBE:
541
- payload = hrtime.bigint() + payload;
542
- // fallthrough
543
- case CLOCKS_INSTANT_SUBSCRIBE: {
544
- const pollState = {
545
- ready: false,
546
- listener: null,
547
- polls: [],
548
- parentStream: null,
549
- };
550
- subscribeInstant(pollState, payload);
551
- return createPoll(pollState);
552
- }
421
+ // Sockets name resolution
422
+ case SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST:
423
+ return createFuture(socketResolveAddress(payload));
424
+ case SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST:
425
+ return createPoll(futures.get(id).pollState);
426
+ case SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST:
427
+ return void futureDispose(id, true);
428
+ case SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST: {
429
+ const val = futureTakeValue(id);
430
+ if (val === undefined) {
431
+ throw 'would-block';
432
+ }
433
+ // double take avoidance is ensured
434
+ return val.val;
435
+ }
553
436
 
554
- // Filesystem
555
- case INPUT_STREAM_CREATE | FILE: {
556
- const { fd, offset } = payload;
557
- const stream = createReadStream(null, {
558
- fd,
559
- autoClose: false,
560
- highWaterMark: 64 * 1024,
561
- start: Number(offset),
562
- });
563
- return createReadableStream(stream);
564
- }
565
- case OUTPUT_STREAM_CREATE | FILE: {
566
- const { fd, offset } = payload;
567
- const stream = createWriteStream(null, {
568
- fd,
569
- autoClose: false,
570
- emitClose: false,
571
- highWaterMark: 64 * 1024,
572
- start: Number(offset),
573
- });
574
- return createWritableStream(stream);
575
- }
576
- }
577
-
578
- // Generic call implementations (streams + polls)
579
- switch (call & CALL_MASK) {
580
- case INPUT_STREAM_READ: {
581
- const stream = getStreamOrThrow(id);
582
- if (!stream.pollState.ready) return new Uint8Array();
583
- const res = stream.stream.read(
584
- Math.min(stream.stream.readableLength, Number(payload))
585
- );
586
- if (res) return res;
587
- if (stream.stream.readableEnded) throw { tag: "closed" };
588
- return new Uint8Array();
589
- }
590
- case INPUT_STREAM_BLOCKING_READ: {
591
- const { pollState } = streams.get(id);
592
- pollStateCheck(pollState);
593
- if (pollState.ready)
594
- return handle(INPUT_STREAM_READ | (call & CALL_TYPE_MASK), id, payload);
595
- return new Promise((resolve) => void (pollState.listener = resolve)).then(
596
- () => handle(INPUT_STREAM_READ | (call & CALL_TYPE_MASK), id, payload)
597
- );
598
- }
599
- case INPUT_STREAM_SKIP:
600
- return handle(
601
- INPUT_STREAM_READ | (call & CALL_TYPE_MASK),
602
- id,
603
- new Uint8Array(Number(payload))
604
- );
605
- case INPUT_STREAM_BLOCKING_SKIP:
606
- return handle(
607
- INPUT_STREAM_BLOCKING_READ | (call & CALL_TYPE_MASK),
608
- id,
609
- new Uint8Array(Number(payload))
610
- );
611
- case INPUT_STREAM_SUBSCRIBE:
612
- return createPoll(streams.get(id).pollState);
613
- case INPUT_STREAM_DISPOSE: {
614
- const stream = streams.get(id);
615
- verifyPollsDroppedForDrop(stream.pollState, "input stream");
616
- streams.delete(id);
617
- return;
618
- }
619
- case OUTPUT_STREAM_CHECK_WRITE: {
620
- const { stream, pollState } = getStreamOrThrow(id);
621
- const bytes = stream.writableHighWaterMark - stream.writableLength;
622
- if (bytes === 0) pollState.ready = false;
623
- return BigInt(bytes);
624
- }
625
- case OUTPUT_STREAM_WRITE: {
626
- const { stream } = getStreamOrThrow(id);
627
- if (
628
- payload.byteLength >
629
- stream.writableHighWaterMark - stream.writableLength
630
- )
631
- throw new Error("wasi-io trap: attempt to write too many bytes");
632
- return void stream.write(payload);
633
- }
634
- case OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH: {
635
- const stream = getStreamOrThrow(id);
636
- // if an existing flush, try again after that
637
- if (stream.flushPromise)
638
- return stream.flushPromise.then(() => handle(call, id, payload));
639
- if (
640
- payload.byteLength >
641
- stream.stream.writableHighWaterMark - stream.stream.writableLength
642
- ) {
643
- new Error(
644
- "wasi-io trap: Cannot write more than permitted writable length"
645
- );
646
- }
647
- stream.pollState.ready = false;
648
- return (stream.flushPromise = new Promise((resolve, reject) => {
649
- if (stream.stream === stdout || stream.stream === stderr) {
650
- // Inside workers, NodeJS actually queues writes destined for
651
- // stdout/stderr in a port that is only flushed on exit of the worker.
652
- //
653
- // In this case, we cannot attempt to wait for the promise.
654
- //
655
- // This code may have to be reworked for browsers.
656
- //
657
- // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker/io.js#L288
658
- // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker.js#L303
659
- // see: https://github.com/nodejs/node/blob/v22.12.0/typings/internalBinding/messaging.d.ts#L27
660
- stream.stream.write(payload);
661
- stream.flushPromise = null;
662
- pollStateReady(stream.pollState);
663
- resolve(BigInt(payload.byteLength));
664
- } else {
665
- stream.stream.write(payload, (err) => {
666
- stream.flushPromise = null;
667
- pollStateReady(stream.pollState);
668
- if (err) return void reject(streamError(err));
669
- resolve(BigInt(payload.byteLength));
670
- });
437
+ // Sockets TCP
438
+ case SOCKET_TCP_ACCEPT:
439
+ return socketTcpAccept(id);
440
+ case SOCKET_TCP_CREATE_HANDLE:
441
+ return createTcpSocket();
442
+ case SOCKET_TCP_BIND_START:
443
+ return socketTcpBindStart(id, payload.localAddress, payload.family);
444
+ case SOCKET_TCP_BIND_FINISH:
445
+ return socketTcpFinish(id, SOCKET_STATE_BIND, SOCKET_STATE_BOUND);
446
+ case SOCKET_TCP_CONNECT_START:
447
+ return socketTcpConnectStart(
448
+ id,
449
+ payload.remoteAddress,
450
+ payload.family
451
+ );
452
+ case SOCKET_TCP_CONNECT_FINISH:
453
+ return socketTcpFinish(
454
+ id,
455
+ SOCKET_STATE_CONNECT,
456
+ SOCKET_STATE_CONNECTION
457
+ );
458
+ case SOCKET_TCP_LISTEN_START:
459
+ return socketTcpListenStart(id);
460
+ case SOCKET_TCP_LISTEN_FINISH:
461
+ return socketTcpFinish(
462
+ id,
463
+ SOCKET_STATE_LISTEN,
464
+ SOCKET_STATE_LISTENER
465
+ );
466
+ case SOCKET_TCP_IS_LISTENING:
467
+ return tcpSockets.get(id).state === SOCKET_STATE_LISTENER;
468
+ case SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE:
469
+ return getDefaultSendBufferSize(id);
470
+ case SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE:
471
+ return getDefaultReceiveBufferSize(id);
472
+ case SOCKET_TCP_SET_LISTEN_BACKLOG_SIZE:
473
+ return socketTcpSetListenBacklogSize(id);
474
+ case SOCKET_TCP_GET_LOCAL_ADDRESS:
475
+ return socketTcpGetLocalAddress(id);
476
+ case SOCKET_TCP_GET_REMOTE_ADDRESS:
477
+ return socketTcpGetRemoteAddress(id);
478
+ case SOCKET_TCP_SHUTDOWN:
479
+ return socketTcpShutdown(id, payload);
480
+ case SOCKET_TCP_SUBSCRIBE:
481
+ return createPoll(tcpSockets.get(id).pollState);
482
+ case SOCKET_TCP_SET_KEEP_ALIVE:
483
+ return socketTcpSetKeepAlive(id, payload);
484
+ case SOCKET_TCP_DISPOSE:
485
+ return socketTcpDispose(id);
486
+
487
+ // Sockets UDP
488
+ case SOCKET_UDP_CREATE_HANDLE:
489
+ return createUdpSocket(payload);
490
+ case SOCKET_UDP_BIND_START:
491
+ return socketUdpBindStart(id, payload.localAddress, payload.family);
492
+ case SOCKET_UDP_BIND_FINISH:
493
+ return socketUdpBindFinish(id);
494
+ case SOCKET_UDP_STREAM:
495
+ return socketUdpStream(id, payload);
496
+ case SOCKET_UDP_SUBSCRIBE:
497
+ return createPoll(udpSockets.get(id).pollState);
498
+ case SOCKET_UDP_GET_LOCAL_ADDRESS:
499
+ return socketUdpGetLocalAddress(id);
500
+ case SOCKET_UDP_GET_REMOTE_ADDRESS:
501
+ return socketUdpGetRemoteAddress(id);
502
+ case SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE:
503
+ return socketUdpSetReceiveBufferSize(id, payload);
504
+ case SOCKET_UDP_SET_SEND_BUFFER_SIZE:
505
+ return socketUdpSetSendBufferSize(id, payload);
506
+ case SOCKET_UDP_SET_UNICAST_HOP_LIMIT:
507
+ return socketUdpSetUnicastHopLimit(id, payload);
508
+ case SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE:
509
+ return socketUdpGetReceiveBufferSize(id);
510
+ case SOCKET_UDP_GET_SEND_BUFFER_SIZE:
511
+ return socketUdpGetSendBufferSize(id);
512
+ case SOCKET_UDP_GET_UNICAST_HOP_LIMIT:
513
+ return socketUdpGetUnicastHopLimit(id);
514
+ case SOCKET_UDP_DISPOSE:
515
+ return socketUdpDispose(id);
516
+
517
+ case SOCKET_INCOMING_DATAGRAM_STREAM_RECEIVE:
518
+ return socketIncomingDatagramStreamReceive(id, payload);
519
+ case SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND:
520
+ return socketOutgoingDatagramStreamCheckSend(id);
521
+ case SOCKET_OUTGOING_DATAGRAM_STREAM_SEND:
522
+ return socketOutgoingDatagramStreamSend(id, payload);
523
+ case SOCKET_DATAGRAM_STREAM_SUBSCRIBE:
524
+ return createPoll(datagramStreams.get(id).pollState);
525
+ case SOCKET_DATAGRAM_STREAM_DISPOSE:
526
+ return socketDatagramStreamDispose(id);
527
+
528
+ // Stdio
529
+ case OUTPUT_STREAM_BLOCKING_FLUSH | STDOUT:
530
+ case OUTPUT_STREAM_BLOCKING_FLUSH | STDERR:
531
+ // no blocking flush for stdio in Node.js
532
+ return;
533
+ case OUTPUT_STREAM_DISPOSE | STDOUT:
534
+ case OUTPUT_STREAM_DISPOSE | STDERR:
535
+ return;
536
+ case INPUT_STREAM_CREATE | STDIN: {
537
+ return createReadableStream(
538
+ new Readable({
539
+ read(n) {
540
+ if (n <= 0) {
541
+ return void this.push(null);
542
+ }
543
+ let buf = Buffer.allocUnsafeSlow(n);
544
+ read(0, buf, 0, n, null, (err, bytesRead) => {
545
+ if (err) {
546
+ if (err.code === 'EAGAIN') {
547
+ nextTick(() => void this._read(n));
548
+ return;
549
+ }
550
+ this.destroy(err);
551
+ } else if (bytesRead > 0) {
552
+ if (bytesRead !== buf.length) {
553
+ const dst =
554
+ Buffer.allocUnsafeSlow(bytesRead);
555
+ buf.copy(dst, 0, 0, bytesRead);
556
+ buf = dst;
557
+ }
558
+ this.push(buf);
559
+ } else {
560
+ this.push(null);
561
+ }
562
+ });
563
+ },
564
+ })
565
+ );
671
566
  }
672
- }));
673
- }
674
- case OUTPUT_STREAM_FLUSH: {
675
- const stream = getStreamOrThrow(id);
676
- if (stream.flushPromise) return;
677
- stream.pollState.ready = false;
678
- stream.flushPromise = new Promise((resolve, reject) => {
679
- if (stream.stream === stdout || stream.stream === stderr) {
680
- // Inside workers, NodeJS actually queues writes destined for
681
- // stdout/stderr in a port that is only flushed on exit of the worker.
682
- //
683
- // In this case, we cannot attempt to wait for the promise.
684
- //
685
- // This code may have to be reworked for browsers.
686
- //
687
- // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker/io.js#L288
688
- // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker.js#L303
689
- // see: https://github.com/nodejs/node/blob/v22.12.0/typings/internalBinding/messaging.d.ts#L27
690
- stream.flushPromise = null;
691
- pollStateReady(stream.pollState);
692
- resolve();
693
- } else {
694
- // For all other writes, we can perform the actual write and expect the write to complete
695
- // and trigger the relevant callback
696
- stream.stream.write(new Uint8Array([]), (err) => {
697
- stream.flushPromise = null;
698
- pollStateReady(stream.pollState);
699
- if (err) return void reject(streamError(err));
700
- resolve();
701
- });
567
+
568
+ // Clocks
569
+ case CLOCKS_DURATION_SUBSCRIBE:
570
+ payload = hrtime.bigint() + payload;
571
+ // fallthrough
572
+ case CLOCKS_INSTANT_SUBSCRIBE: {
573
+ const pollState = {
574
+ ready: false,
575
+ listener: null,
576
+ polls: [],
577
+ parentStream: null,
578
+ };
579
+ subscribeInstant(pollState, payload);
580
+ return createPoll(pollState);
702
581
  }
703
- });
704
- return stream.flushPromise;
705
- }
706
- case OUTPUT_STREAM_BLOCKING_FLUSH: {
707
- const stream = getStreamOrThrow(id);
708
- if (stream.flushPromise) return stream.flushPromise;
709
- return new Promise((resolve, reject) => {
710
- if (stream.stream === stdout || stream.stream === stderr) {
711
- // Inside workers, NodeJS actually queues writes destined for
712
- // stdout/stderr in a port that is only flushed on exit of the worker.
713
- //
714
- // In this case, we cannot attempt to wait for the promise.
715
- //
716
- // This code may have to be reworked for browsers.
717
- //
718
- // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker/io.js#L288
719
- // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker.js#L303
720
- // see: https://github.com/nodejs/node/blob/v22.12.0/typings/internalBinding/messaging.d.ts#L27
721
- resolve();
722
- } else {
723
- // For all other writes, we can perform the actual write and expect the write to complete
724
- // and trigger the relevant callback
725
- stream.stream.write(new Uint8Array([]), (err) =>
726
- err ? reject(streamError(err)) : resolve()
727
- );
582
+
583
+ // Filesystem
584
+ case INPUT_STREAM_CREATE | FILE: {
585
+ const { fd, offset } = payload;
586
+ const stream = createReadStream(null, {
587
+ fd,
588
+ autoClose: false,
589
+ highWaterMark: 64 * 1024,
590
+ start: Number(offset),
591
+ });
592
+ return createReadableStream(stream);
593
+ }
594
+ case OUTPUT_STREAM_CREATE | FILE: {
595
+ const { fd, offset } = payload;
596
+ const stream = createWriteStream(null, {
597
+ fd,
598
+ autoClose: false,
599
+ emitClose: false,
600
+ highWaterMark: 64 * 1024,
601
+ start: Number(offset),
602
+ });
603
+ return createWritableStream(stream);
728
604
  }
729
- });
730
- }
731
- case OUTPUT_STREAM_WRITE_ZEROES:
732
- return handle(
733
- OUTPUT_STREAM_WRITE | (call & CALL_TYPE_MASK),
734
- id,
735
- new Uint8Array(Number(payload))
736
- );
737
- case OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH:
738
- return handle(
739
- OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | (call & CALL_TYPE_MASK),
740
- id,
741
- new Uint8Array(Number(payload))
742
- );
743
- case OUTPUT_STREAM_SPLICE: {
744
- const outputStream = getStreamOrThrow(id);
745
- const inputStream = getStreamOrThrow(payload.src);
746
- let bytesRemaining = Number(payload.len);
747
- let chunk;
748
- while (
749
- bytesRemaining > 0 &&
750
- (chunk = inputStream.stream.read(
751
- Math.min(
752
- outputStream.writableHighWaterMark - outputStream.writableLength,
753
- bytesRemaining
754
- )
755
- ))
756
- ) {
757
- bytesRemaining -= chunk.byteLength;
758
- outputStream.stream.write(chunk);
759
- }
760
- if (inputStream.stream.errored)
761
- throw streamError(inputStream.stream.errored);
762
- if (outputStream.stream.errored)
763
- throw streamError(outputStream.stream.errored);
764
- return payload.len - BigInt(bytesRemaining);
765
605
  }
766
- case OUTPUT_STREAM_SUBSCRIBE:
767
- return createPoll(streams.get(id).pollState);
768
- case OUTPUT_STREAM_BLOCKING_SPLICE: {
769
- const outputStream = getStreamOrThrow(id);
770
- let promise = Promise.resolve();
771
- let resolve, reject;
772
- if (outputStream.stream.writableNeedDrain) {
773
- promise = new Promise((_resolve, _reject) => {
774
- outputStream.stream
775
- .once("drain", (resolve = _resolve))
776
- .once("error", (reject = _reject));
777
- }).then(
778
- () => {
779
- outputStream.stream.off("error", reject);
780
- },
781
- (err) => {
782
- outputStream.stream.off("drain", resolve);
783
- throw streamError(err);
784
- }
785
- );
786
- }
787
- const inputStream = getStreamOrThrow(payload.src);
788
- if (!inputStream.stream.readable) {
789
- promise = promise.then(() =>
790
- new Promise((_resolve, _reject) => {
791
- inputStream.stream
792
- .once("readable", (resolve = _resolve))
793
- .once("error", (reject = _reject));
794
- }).then(
795
- () => {
796
- inputStream.stream.off("error", reject);
797
- },
798
- (err) => {
799
- inputStream.stream.off("readable", resolve);
800
- throw streamError(err);
606
+
607
+ // Generic call implementations (streams + polls)
608
+ switch (call & CALL_MASK) {
609
+ case INPUT_STREAM_READ: {
610
+ const stream = getStreamOrThrow(id);
611
+ if (!stream.pollState.ready) {
612
+ return new Uint8Array();
801
613
  }
802
- )
803
- );
804
- }
805
- return promise.then(() => handle(OUTPUT_STREAM_SPLICE, id, payload));
806
- }
807
- case OUTPUT_STREAM_DISPOSE: {
808
- const stream = streams.get(id);
809
- verifyPollsDroppedForDrop(stream.pollState, "output stream");
810
- stream.stream.end();
811
- streams.delete(id);
812
- return;
813
- }
614
+ const res = stream.stream.read(
615
+ Math.min(stream.stream.readableLength, Number(payload))
616
+ );
617
+ if (res) {
618
+ return res;
619
+ }
620
+ if (stream.stream.readableEnded) {
621
+ throw { tag: 'closed' };
622
+ }
623
+ return new Uint8Array();
624
+ }
625
+ case INPUT_STREAM_BLOCKING_READ: {
626
+ const { pollState } = streams.get(id);
627
+ pollStateCheck(pollState);
628
+ if (pollState.ready) {
629
+ return handle(
630
+ INPUT_STREAM_READ | (call & CALL_TYPE_MASK),
631
+ id,
632
+ payload
633
+ );
634
+ }
635
+ return new Promise(
636
+ (resolve) => void (pollState.listener = resolve)
637
+ ).then(() =>
638
+ handle(INPUT_STREAM_READ | (call & CALL_TYPE_MASK), id, payload)
639
+ );
640
+ }
641
+ case INPUT_STREAM_SKIP:
642
+ return handle(
643
+ INPUT_STREAM_READ | (call & CALL_TYPE_MASK),
644
+ id,
645
+ new Uint8Array(Number(payload))
646
+ );
647
+ case INPUT_STREAM_BLOCKING_SKIP:
648
+ return handle(
649
+ INPUT_STREAM_BLOCKING_READ | (call & CALL_TYPE_MASK),
650
+ id,
651
+ new Uint8Array(Number(payload))
652
+ );
653
+ case INPUT_STREAM_SUBSCRIBE:
654
+ return createPoll(streams.get(id).pollState);
655
+ case INPUT_STREAM_DISPOSE: {
656
+ const stream = streams.get(id);
657
+ verifyPollsDroppedForDrop(stream.pollState, 'input stream');
658
+ streams.delete(id);
659
+ return;
660
+ }
661
+ case OUTPUT_STREAM_CHECK_WRITE: {
662
+ const { stream, pollState } = getStreamOrThrow(id);
663
+ const bytes = stream.writableHighWaterMark - stream.writableLength;
664
+ if (bytes === 0) {
665
+ pollState.ready = false;
666
+ }
667
+ return BigInt(bytes);
668
+ }
669
+ case OUTPUT_STREAM_WRITE: {
670
+ const { stream } = getStreamOrThrow(id);
671
+ if (
672
+ payload.byteLength >
673
+ stream.writableHighWaterMark - stream.writableLength
674
+ ) {
675
+ throw new Error(
676
+ 'wasi-io trap: attempt to write too many bytes'
677
+ );
678
+ }
679
+ return void stream.write(payload);
680
+ }
681
+ case OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH: {
682
+ const stream = getStreamOrThrow(id);
683
+ // if an existing flush, try again after that
684
+ if (stream.flushPromise) {
685
+ return stream.flushPromise.then(() =>
686
+ handle(call, id, payload)
687
+ );
688
+ }
689
+ if (
690
+ payload.byteLength >
691
+ stream.stream.writableHighWaterMark -
692
+ stream.stream.writableLength
693
+ ) {
694
+ throw new Error(
695
+ 'wasi-io trap: Cannot write more than permitted writable length'
696
+ );
697
+ }
698
+ stream.pollState.ready = false;
699
+ return (stream.flushPromise = new Promise((resolve, reject) => {
700
+ if (stream.stream === stdout || stream.stream === stderr) {
701
+ // Inside workers, NodeJS actually queues writes destined for
702
+ // stdout/stderr in a port that is only flushed on exit of the worker.
703
+ //
704
+ // In this case, we cannot attempt to wait for the promise.
705
+ //
706
+ // This code may have to be reworked for browsers.
707
+ //
708
+ // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker/io.js#L288
709
+ // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker.js#L303
710
+ // see: https://github.com/nodejs/node/blob/v22.12.0/typings/internalBinding/messaging.d.ts#L27
711
+ stream.stream.write(payload);
712
+ stream.flushPromise = null;
713
+ pollStateReady(stream.pollState);
714
+ resolve(BigInt(payload.byteLength));
715
+ } else {
716
+ stream.stream.write(payload, (err) => {
717
+ stream.flushPromise = null;
718
+ pollStateReady(stream.pollState);
719
+ if (err) {
720
+ return void reject(streamError(err));
721
+ }
722
+ resolve(BigInt(payload.byteLength));
723
+ });
724
+ }
725
+ }));
726
+ }
727
+ case OUTPUT_STREAM_FLUSH: {
728
+ const stream = getStreamOrThrow(id);
729
+ if (stream.flushPromise) {
730
+ return;
731
+ }
732
+ stream.pollState.ready = false;
733
+ stream.flushPromise = new Promise((resolve, reject) => {
734
+ if (stream.stream === stdout || stream.stream === stderr) {
735
+ // Inside workers, NodeJS actually queues writes destined for
736
+ // stdout/stderr in a port that is only flushed on exit of the worker.
737
+ //
738
+ // In this case, we cannot attempt to wait for the promise.
739
+ //
740
+ // This code may have to be reworked for browsers.
741
+ //
742
+ // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker/io.js#L288
743
+ // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker.js#L303
744
+ // see: https://github.com/nodejs/node/blob/v22.12.0/typings/internalBinding/messaging.d.ts#L27
745
+ stream.flushPromise = null;
746
+ pollStateReady(stream.pollState);
747
+ resolve();
748
+ } else {
749
+ // For all other writes, we can perform the actual write and expect the write to complete
750
+ // and trigger the relevant callback
751
+ stream.stream.write(new Uint8Array([]), (err) => {
752
+ stream.flushPromise = null;
753
+ pollStateReady(stream.pollState);
754
+ if (err) {
755
+ return void reject(streamError(err));
756
+ }
757
+ resolve();
758
+ });
759
+ }
760
+ });
761
+ return stream.flushPromise;
762
+ }
763
+ case OUTPUT_STREAM_BLOCKING_FLUSH: {
764
+ const stream = getStreamOrThrow(id);
765
+ if (stream.flushPromise) {
766
+ return stream.flushPromise;
767
+ }
768
+ return new Promise((resolve, reject) => {
769
+ if (stream.stream === stdout || stream.stream === stderr) {
770
+ // Inside workers, NodeJS actually queues writes destined for
771
+ // stdout/stderr in a port that is only flushed on exit of the worker.
772
+ //
773
+ // In this case, we cannot attempt to wait for the promise.
774
+ //
775
+ // This code may have to be reworked for browsers.
776
+ //
777
+ // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker/io.js#L288
778
+ // see: https://github.com/nodejs/node/blob/v22.12.0/lib/internal/worker.js#L303
779
+ // see: https://github.com/nodejs/node/blob/v22.12.0/typings/internalBinding/messaging.d.ts#L27
780
+ resolve();
781
+ } else {
782
+ // For all other writes, we can perform the actual write and expect the write to complete
783
+ // and trigger the relevant callback
784
+ stream.stream.write(new Uint8Array([]), (err) =>
785
+ err ? reject(streamError(err)) : resolve()
786
+ );
787
+ }
788
+ });
789
+ }
790
+ case OUTPUT_STREAM_WRITE_ZEROES:
791
+ return handle(
792
+ OUTPUT_STREAM_WRITE | (call & CALL_TYPE_MASK),
793
+ id,
794
+ new Uint8Array(Number(payload))
795
+ );
796
+ case OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH:
797
+ return handle(
798
+ OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH |
799
+ (call & CALL_TYPE_MASK),
800
+ id,
801
+ new Uint8Array(Number(payload))
802
+ );
803
+ case OUTPUT_STREAM_SPLICE: {
804
+ const outputStream = getStreamOrThrow(id);
805
+ const inputStream = getStreamOrThrow(payload.src);
806
+ let bytesRemaining = Number(payload.len);
807
+ let chunk;
808
+ while (
809
+ bytesRemaining > 0 &&
810
+ (chunk = inputStream.stream.read(
811
+ Math.min(
812
+ outputStream.writableHighWaterMark -
813
+ outputStream.writableLength,
814
+ bytesRemaining
815
+ )
816
+ ))
817
+ ) {
818
+ bytesRemaining -= chunk.byteLength;
819
+ outputStream.stream.write(chunk);
820
+ }
821
+ if (inputStream.stream.errored) {
822
+ throw streamError(inputStream.stream.errored);
823
+ }
824
+ if (outputStream.stream.errored) {
825
+ throw streamError(outputStream.stream.errored);
826
+ }
827
+ return payload.len - BigInt(bytesRemaining);
828
+ }
829
+ case OUTPUT_STREAM_SUBSCRIBE:
830
+ return createPoll(streams.get(id).pollState);
831
+ case OUTPUT_STREAM_BLOCKING_SPLICE: {
832
+ const outputStream = getStreamOrThrow(id);
833
+ let promise = Promise.resolve();
834
+ let resolve, reject;
835
+ if (outputStream.stream.writableNeedDrain) {
836
+ promise = new Promise((_resolve, _reject) => {
837
+ outputStream.stream
838
+ .once('drain', (resolve = _resolve))
839
+ .once('error', (reject = _reject));
840
+ }).then(
841
+ () => {
842
+ outputStream.stream.off('error', reject);
843
+ },
844
+ (err) => {
845
+ outputStream.stream.off('drain', resolve);
846
+ throw streamError(err);
847
+ }
848
+ );
849
+ }
850
+ const inputStream = getStreamOrThrow(payload.src);
851
+ if (!inputStream.stream.readable) {
852
+ promise = promise.then(() =>
853
+ new Promise((_resolve, _reject) => {
854
+ inputStream.stream
855
+ .once('readable', (resolve = _resolve))
856
+ .once('error', (reject = _reject));
857
+ }).then(
858
+ () => {
859
+ inputStream.stream.off('error', reject);
860
+ },
861
+ (err) => {
862
+ inputStream.stream.off('readable', resolve);
863
+ throw streamError(err);
864
+ }
865
+ )
866
+ );
867
+ }
868
+ return promise.then(() =>
869
+ handle(OUTPUT_STREAM_SPLICE, id, payload)
870
+ );
871
+ }
872
+ case OUTPUT_STREAM_DISPOSE: {
873
+ const stream = streams.get(id);
874
+ verifyPollsDroppedForDrop(stream.pollState, 'output stream');
875
+ stream.stream.end();
876
+ streams.delete(id);
877
+ return;
878
+ }
814
879
 
815
- case POLL_POLLABLE_READY:
816
- return polls.get(id).ready;
817
- case POLL_POLLABLE_BLOCK:
818
- payload = [id];
819
- // [intentional case fall-through]
820
- case POLL_POLL_LIST: {
821
- if (payload.length === 0)
822
- throw new Error("wasi-io trap: attempt to poll on empty list");
823
- const doneList = [];
824
- const pollList = payload.map((pollId) => polls.get(pollId));
825
- for (const [idx, pollState] of pollList.entries()) {
826
- pollStateCheck(pollState);
827
- if (pollState.ready) doneList.push(idx);
828
- }
829
- if (doneList.length > 0) {
830
- return new Uint32Array(doneList);
831
- }
832
- let readyPromiseResolve;
833
- const readyPromise = new Promise(
834
- (resolve) => void (readyPromiseResolve = resolve)
835
- );
836
- for (const poll of pollList) {
837
- poll.listener = readyPromiseResolve;
838
- }
839
- return readyPromise.then(() => {
840
- for (const [idx, pollState] of pollList.entries()) {
841
- pollState.listener = null;
842
- if (pollState.ready) doneList.push(idx);
880
+ case POLL_POLLABLE_READY:
881
+ return polls.get(id).ready;
882
+ case POLL_POLLABLE_BLOCK:
883
+ payload = [id];
884
+ // [intentional case fall-through]
885
+ case POLL_POLL_LIST: {
886
+ if (payload.length === 0) {
887
+ throw new Error('wasi-io trap: attempt to poll on empty list');
888
+ }
889
+ const doneList = [];
890
+ const pollList = payload.map((pollId) => polls.get(pollId));
891
+ for (const [idx, pollState] of pollList.entries()) {
892
+ pollStateCheck(pollState);
893
+ if (pollState.ready) {
894
+ doneList.push(idx);
895
+ }
896
+ }
897
+ if (doneList.length > 0) {
898
+ return new Uint32Array(doneList);
899
+ }
900
+ let readyPromiseResolve;
901
+ const readyPromise = new Promise(
902
+ (resolve) => void (readyPromiseResolve = resolve)
903
+ );
904
+ for (const poll of pollList) {
905
+ poll.listener = readyPromiseResolve;
906
+ }
907
+ return readyPromise.then(() => {
908
+ for (const [idx, pollState] of pollList.entries()) {
909
+ pollState.listener = null;
910
+ if (pollState.ready) {
911
+ doneList.push(idx);
912
+ }
913
+ }
914
+ return new Uint32Array(doneList);
915
+ });
843
916
  }
844
- return new Uint32Array(doneList);
845
- });
846
- }
847
- case POLL_POLLABLE_DISPOSE:
848
- if (!polls.delete(id))
849
- throw new Error(
850
- `wasi-io trap: Disposed a poll ${id} that does not exist`
851
- );
852
- return;
917
+ case POLL_POLLABLE_DISPOSE:
918
+ if (!polls.delete(id)) {
919
+ throw new Error(
920
+ `wasi-io trap: Disposed a poll ${id} that does not exist`
921
+ );
922
+ }
923
+ return;
853
924
 
854
- case FUTURE_TAKE_VALUE:
855
- return futureTakeValue(id);
925
+ case FUTURE_TAKE_VALUE:
926
+ return futureTakeValue(id);
856
927
 
857
- case FUTURE_SUBSCRIBE: {
858
- const { pollState } = futures.get(id);
859
- const pollId = ++pollCnt;
860
- polls.set(pollId, pollState);
861
- return pollId;
928
+ case FUTURE_SUBSCRIBE: {
929
+ const { pollState } = futures.get(id);
930
+ const pollId = ++pollCnt;
931
+ polls.set(pollId, pollState);
932
+ return pollId;
933
+ }
934
+ case FUTURE_DISPOSE:
935
+ return void futureDispose(id, true);
936
+ default:
937
+ throw new Error(
938
+ `wasi-io trap: Unknown call ${call} (${reverseMap[call]}) with type ${
939
+ reverseMap[call & CALL_TYPE_MASK]
940
+ }`
941
+ );
862
942
  }
863
- case FUTURE_DISPOSE:
864
- return void futureDispose(id, true);
865
- default:
866
- throw new Error(
867
- `wasi-io trap: Unknown call ${call} (${reverseMap[call]}) with type ${
868
- reverseMap[call & CALL_TYPE_MASK]
869
- }`
870
- );
871
- }
872
943
  }
873
944
 
874
945
  /**
875
946
  * @param {PollState} pollState
876
947
  */
877
948
  function createPoll(pollState) {
878
- const pollId = ++pollCnt;
879
- pollState.polls.push(pollId);
880
- polls.set(pollId, pollState);
881
- return pollId;
949
+ const pollId = ++pollCnt;
950
+ pollState.polls.push(pollId);
951
+ polls.set(pollId, pollState);
952
+ return pollId;
882
953
  }
883
954
 
884
955
  function subscribeInstant(pollState, instant) {
885
- const duration = instant - hrtime.bigint();
886
- if (duration <= 0) return pollStateReady(pollState);
887
- function cb() {
888
- if (hrtime.bigint() < instant) return subscribeInstant(pollState, instant);
889
- pollStateReady(pollState);
890
- }
891
- if (duration < 10e6) setImmediate(cb);
892
- else setTimeout(cb, Number(duration) / 1e6);
956
+ const duration = instant - hrtime.bigint();
957
+ if (duration <= 0) {
958
+ return pollStateReady(pollState);
959
+ }
960
+ function cb() {
961
+ if (hrtime.bigint() < instant) {
962
+ return subscribeInstant(pollState, instant);
963
+ }
964
+ pollStateReady(pollState);
965
+ }
966
+ if (duration < 10e6) {
967
+ setImmediate(cb);
968
+ } else {
969
+ setTimeout(cb, Number(duration) / 1e6);
970
+ }
893
971
  }
894
972
 
895
973
  /**
@@ -897,13 +975,14 @@ function subscribeInstant(pollState, instant) {
897
975
  * @param {string} polledResourceDebugName
898
976
  */
899
977
  export function verifyPollsDroppedForDrop(pollState, polledResourceDebugName) {
900
- for (const pollId of pollState.polls) {
901
- const poll = polls.get(pollId);
902
- if (poll)
903
- throw new Error(
904
- `wasi-io trap: Cannot drop ${polledResourceDebugName} as it has a child poll resource which has not yet been dropped`
905
- );
906
- }
978
+ for (const pollId of pollState.polls) {
979
+ const poll = polls.get(pollId);
980
+ if (poll) {
981
+ throw new Error(
982
+ `wasi-io trap: Cannot drop ${polledResourceDebugName} as it has a child poll resource which has not yet been dropped`
983
+ );
984
+ }
985
+ }
907
986
  }
908
987
 
909
988
  /**
@@ -911,41 +990,41 @@ export function verifyPollsDroppedForDrop(pollState, polledResourceDebugName) {
911
990
  * @param {bool} finished
912
991
  */
913
992
  export function pollStateReady(pollState) {
914
- if (pollState.ready && pollState.listener) {
915
- uncaughtException = new Error(
916
- "wasi-io trap: poll already ready with listener attached"
917
- );
918
- }
919
- pollState.ready = true;
920
- if (pollState.listener) {
921
- pollState.listener();
922
- pollState.listener = null;
923
- }
993
+ if (pollState.ready && pollState.listener) {
994
+ uncaughtException = new Error(
995
+ 'wasi-io trap: poll already ready with listener attached'
996
+ );
997
+ }
998
+ pollState.ready = true;
999
+ if (pollState.listener) {
1000
+ pollState.listener();
1001
+ pollState.listener = null;
1002
+ }
924
1003
  }
925
1004
 
926
1005
  /**
927
1006
  * @param {PollState} pollState
928
1007
  */
929
1008
  function pollStateCheck(pollState) {
930
- if (pollState.ready && pollState.parentStream) {
931
- // stream ONLY applies to readable streams here
932
- const stream = pollState.parentStream;
933
- const res = stream.read(0);
934
- if (res !== null) {
935
- throw new Error("wasi-io trap: got data for a null read");
936
- }
937
- if (
938
- pollState.ready &&
939
- stream.readableLength === 0 &&
940
- !stream.readableEnded &&
941
- !stream.errored
942
- ) {
943
- pollState.ready = false;
944
- stream.once("readable", () => {
945
- pollStateReady(pollState);
946
- });
1009
+ if (pollState.ready && pollState.parentStream) {
1010
+ // stream ONLY applies to readable streams here
1011
+ const stream = pollState.parentStream;
1012
+ const res = stream.read(0);
1013
+ if (res !== null) {
1014
+ throw new Error('wasi-io trap: got data for a null read');
1015
+ }
1016
+ if (
1017
+ pollState.ready &&
1018
+ stream.readableLength === 0 &&
1019
+ !stream.readableEnded &&
1020
+ !stream.errored
1021
+ ) {
1022
+ pollState.ready = false;
1023
+ stream.once('readable', () => {
1024
+ pollStateReady(pollState);
1025
+ });
1026
+ }
947
1027
  }
948
- }
949
1028
  }
950
1029
 
951
1030
  /**
@@ -955,31 +1034,31 @@ function pollStateCheck(pollState) {
955
1034
  * @returns {number}
956
1035
  */
957
1036
  export function createFuture(promise, pollState) {
958
- const futureId = ++futureCnt;
959
- if (pollState) {
960
- pollState.ready = false;
961
- } else {
962
- pollState = {
963
- ready: false,
964
- listener: null,
965
- polls: [],
966
- parent: null,
967
- };
968
- }
969
- const future = { tag: "ok", val: null };
970
- futures.set(futureId, { future, pollState });
971
- promise.then(
972
- (value) => {
973
- pollStateReady(pollState);
974
- future.val = value;
975
- },
976
- (value) => {
977
- pollStateReady(pollState);
978
- future.tag = "err";
979
- future.val = value;
1037
+ const futureId = ++futureCnt;
1038
+ if (pollState) {
1039
+ pollState.ready = false;
1040
+ } else {
1041
+ pollState = {
1042
+ ready: false,
1043
+ listener: null,
1044
+ polls: [],
1045
+ parent: null,
1046
+ };
980
1047
  }
981
- );
982
- return futureId;
1048
+ const future = { tag: 'ok', val: null };
1049
+ futures.set(futureId, { future, pollState });
1050
+ promise.then(
1051
+ (value) => {
1052
+ pollStateReady(pollState);
1053
+ future.val = value;
1054
+ },
1055
+ (value) => {
1056
+ pollStateReady(pollState);
1057
+ future.tag = 'err';
1058
+ future.val = value;
1059
+ }
1060
+ );
1061
+ return futureId;
983
1062
  }
984
1063
 
985
1064
  /**
@@ -988,29 +1067,29 @@ export function createFuture(promise, pollState) {
988
1067
  * @throws {undefined}
989
1068
  */
990
1069
  export function futureTakeValue(id) {
991
- const future = futures.get(id);
992
- // Not ready = return undefined
993
- if (!future.pollState.ready) return undefined;
994
- // Ready but already taken = return { tag: 'err', val: undefined }
995
- if (!future.future) return { tag: "err", val: undefined };
996
- const out = { tag: "ok", val: future.future };
997
- future.future = null;
998
- return out;
1070
+ const future = futures.get(id);
1071
+ // Not ready = return undefined
1072
+ if (!future.pollState.ready) {
1073
+ return undefined;
1074
+ }
1075
+ // Ready but already taken = return { tag: 'err', val: undefined }
1076
+ if (!future.future) {
1077
+ return { tag: 'err', val: undefined };
1078
+ }
1079
+ const out = { tag: 'ok', val: future.future };
1080
+ future.future = null;
1081
+ return out;
999
1082
  }
1000
1083
 
1001
1084
  export function futureDispose(id, ownsState) {
1002
- const { pollState } = futures.get(id);
1003
- if (ownsState) verifyPollsDroppedForDrop(pollState, "future");
1004
- return void futures.delete(id);
1085
+ const { pollState } = futures.get(id);
1086
+ if (ownsState) {
1087
+ verifyPollsDroppedForDrop(pollState, 'future');
1088
+ }
1089
+ return void futures.delete(id);
1005
1090
  }
1006
1091
 
1007
1092
  let uncaughtException;
1008
- process.on("uncaughtException", (err) => (uncaughtException = err));
1009
-
1010
- // eslint-disable-next-line no-unused-vars
1011
- function trace(msg) {
1012
- const tmpErr = new Error(format(msg));
1013
- log(tmpErr.stack);
1014
- }
1093
+ process.on('uncaughtException', (err) => (uncaughtException = err));
1015
1094
 
1016
1095
  const debug = runAsWorker(handle);