@bytecodealliance/preview2-shim 0.0.21 → 0.14.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.
Files changed (44) hide show
  1. package/README.md +4 -14
  2. package/lib/browser/cli.js +2 -4
  3. package/lib/browser/clocks.js +15 -27
  4. package/lib/browser/filesystem.js +2 -30
  5. package/lib/browser/http.js +1 -3
  6. package/lib/browser/io.js +4 -2
  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 -59
  13. package/lib/nodejs/clocks.js +13 -27
  14. package/lib/nodejs/filesystem.js +539 -459
  15. package/lib/nodejs/http.js +440 -173
  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/io.js +0 -183
  43. package/lib/common/make-request.js +0 -30
  44. package/types/interfaces/wasi-clocks-timezone.d.ts +0 -56
@@ -0,0 +1,569 @@
1
+ import { resolve } from "node:dns/promises";
2
+ import { createReadStream, createWriteStream } from "node:fs";
3
+ import { hrtime } from "node:process";
4
+ import { runAsWorker } from "../synckit/index.js";
5
+ import { createHttpRequest } from "./worker-http.js";
6
+ import { Writable } from "node:stream";
7
+ import {
8
+ CALL_MASK,
9
+ CALL_SHIFT,
10
+ CALL_TYPE_MASK,
11
+ CLOCKS_DURATION_SUBSCRIBE,
12
+ CLOCKS_INSTANT_SUBSCRIBE,
13
+ CLOCKS_NOW,
14
+ FUTURE_DISPOSE,
15
+ FUTURE_GET_VALUE_AND_DISPOSE,
16
+ HTTP_CREATE_REQUEST,
17
+ HTTP_OUTPUT_STREAM_FINISH,
18
+ INPUT_STREAM_BLOCKING_READ,
19
+ INPUT_STREAM_BLOCKING_SKIP,
20
+ INPUT_STREAM_CREATE,
21
+ INPUT_STREAM_DISPOSE,
22
+ INPUT_STREAM_READ,
23
+ INPUT_STREAM_SKIP,
24
+ INPUT_STREAM_SUBSCRIBE,
25
+ OUTPUT_STREAM_BLOCKING_FLUSH,
26
+ OUTPUT_STREAM_BLOCKING_SPLICE,
27
+ OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH,
28
+ OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH,
29
+ OUTPUT_STREAM_CHECK_WRITE,
30
+ OUTPUT_STREAM_CREATE,
31
+ OUTPUT_STREAM_DISPOSE,
32
+ OUTPUT_STREAM_FLUSH,
33
+ OUTPUT_STREAM_SPLICE,
34
+ OUTPUT_STREAM_SUBSCRIBE,
35
+ OUTPUT_STREAM_WRITE_ZEROES,
36
+ OUTPUT_STREAM_WRITE,
37
+ POLL_POLL_LIST,
38
+ POLL_POLLABLE_BLOCK,
39
+ POLL_POLLABLE_READY,
40
+ SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST,
41
+ SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST,
42
+ SOCKET_RESOLVE_ADDRESS_GET_AND_DISPOSE_REQUEST,
43
+ FILE,
44
+ HTTP,
45
+ SOCKET,
46
+ STDERR,
47
+ STDIN,
48
+ STDOUT,
49
+ } from "./calls.js";
50
+
51
+ let streamCnt = 0,
52
+ pollCnt = 0;
53
+
54
+ /** @type {Map<number, Promise<void>>} */
55
+ export const unfinishedPolls = new Map();
56
+
57
+ /** @type {Map<number, { flushPromise: Promise<void> | null, stream: NodeJS.ReadableStream | NodeJS.WritableStream }>} */
58
+ export const unfinishedStreams = new Map();
59
+
60
+ /** @type {Map<number, { value: any, error: bool }>} */
61
+ export const unfinishedFutures = new Map();
62
+
63
+ /**
64
+ * @param {NodeJS.ReadableStream | NodeJS.WritableStream} stream
65
+ */
66
+ export function createStream(nodeStream, flushPromise) {
67
+ unfinishedStreams.set(++streamCnt, {
68
+ flushPromise,
69
+ stream: nodeStream,
70
+ });
71
+ return streamCnt;
72
+ }
73
+
74
+ // Stdio
75
+ createStream(process.stdin, Promise.resolve());
76
+ createStream(process.stdout, Promise.resolve());
77
+ createStream(process.stderr, Promise.resolve());
78
+
79
+ /**
80
+ * @param {number} streamId
81
+ * @param {NodeJS.ReadableStream | NodeJS.WritableStream} stream
82
+ */
83
+ function streamError(streamId, stream, err) {
84
+ if (stream.end) stream.end();
85
+ // we delete the stream from unfinishedStreams as it is now "finished" (closed)
86
+ unfinishedStreams.delete(streamId);
87
+ return { tag: "last-operation-failed", val: err };
88
+ }
89
+
90
+ /**
91
+ * @param {number} streamId
92
+ * @returns {{ stream: NodeJS.ReadableStream | NodeJS.WritableStream, flushPromise: Promise<void> | null }}
93
+ */
94
+ export function getStreamOrThrow(streamId) {
95
+ const stream = unfinishedStreams.get(streamId);
96
+ // not in unfinished streams <=> closed
97
+ if (!stream) throw { tag: "closed" };
98
+ if (stream.stream.errored)
99
+ throw streamError(streamId, stream, stream.stream.errored);
100
+ if (stream.stream.closed) {
101
+ unfinishedStreams.delete(streamId);
102
+ throw { tag: "closed" };
103
+ }
104
+ return stream;
105
+ }
106
+
107
+ function subscribeInstant(instant) {
108
+ const duration = instant - hrtime.bigint();
109
+ if (duration <= 0) return Promise.resolve();
110
+ return new Promise((resolve) =>
111
+ duration < 10e6
112
+ ? setImmediate(resolve)
113
+ : setTimeout(resolve, Number(duration) / 1e6)
114
+ ).then(() => {
115
+ if (hrtime.bigint() < instant) return subscribeInstant(instant);
116
+ });
117
+ }
118
+
119
+ /**
120
+ * @param {number} call
121
+ * @param {number | null} id
122
+ * @param {any} payload
123
+ * @returns {Promise<any>}
124
+ */
125
+ function handle(call, id, payload) {
126
+ switch (call) {
127
+ // Http
128
+ case HTTP_CREATE_REQUEST: {
129
+ const { method, url, headers, body } = payload;
130
+ return createFuture(createHttpRequest(method, url, headers, body));
131
+ }
132
+ case OUTPUT_STREAM_CREATE | HTTP: {
133
+ const webTransformStream = new TransformStream();
134
+ const stream = Writable.fromWeb(webTransformStream.writable);
135
+ // content length is passed as payload
136
+ stream.bytesRemaining = payload;
137
+ stream.readableBodyStream = webTransformStream.readable;
138
+ return createStream(stream);
139
+ }
140
+ case OUTPUT_STREAM_SUBSCRIBE | HTTP:
141
+ case OUTPUT_STREAM_FLUSH | HTTP:
142
+ case OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP: {
143
+ // http flush is a noop
144
+ const { stream } = getStreamOrThrow(id);
145
+ // this existing indicates it's still unattached
146
+ // therefore there is no subscribe or backpressure
147
+ if (stream.readableBodyStream) {
148
+ switch (call) {
149
+ case OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP:
150
+ return handle(OUTPUT_STREAM_WRITE | HTTP, id, payload);
151
+ case OUTPUT_STREAM_FLUSH | HTTP:
152
+ return;
153
+ case OUTPUT_STREAM_SUBSCRIBE | HTTP:
154
+ return 0;
155
+ }
156
+ }
157
+ if (call === (OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP))
158
+ stream.bytesRemaining -= payload.byteLength;
159
+ // otherwise fall through to generic implementation
160
+ return handle(call & ~HTTP, id, payload);
161
+ }
162
+ case OUTPUT_STREAM_DISPOSE | HTTP:
163
+ throw new Error('Internal error: HTTP output stream dispose is bypassed for FINISH');
164
+ case OUTPUT_STREAM_WRITE | HTTP: {
165
+ const { stream } = getStreamOrThrow(id);
166
+ stream.bytesRemaining -= payload.byteLength;
167
+ if (stream.bytesRemaining < 0) {
168
+ throw {
169
+ tag: 'last-operation-failed',
170
+ val: 'too much written to output stream'
171
+ };
172
+ }
173
+ const output = handle(OUTPUT_STREAM_WRITE, id, payload);
174
+ return output;
175
+ }
176
+ case HTTP_OUTPUT_STREAM_FINISH: {
177
+ const { stream } = getStreamOrThrow(id);
178
+ if (stream.bytesRemaining > 0) {
179
+ throw {
180
+ tag: 'internal-error',
181
+ val: 'not enough written to body stream'
182
+ };
183
+ }
184
+ if (stream.bytesRemaining < 0) {
185
+ throw {
186
+ tag: 'internal-error',
187
+ val: 'too much written to body stream'
188
+ };
189
+ }
190
+ stream.end();
191
+ break;
192
+ }
193
+
194
+ // Sockets
195
+ case SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST:
196
+ return createFuture(resolve(payload.hostname));
197
+ case SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST:
198
+ return void unfinishedFutures.delete(id);
199
+ case SOCKET_RESOLVE_ADDRESS_GET_AND_DISPOSE_REQUEST: {
200
+ const future = unfinishedFutures.get(id);
201
+ if (!future) {
202
+ // future not ready yet
203
+ if (unfinishedPolls.get(id)) {
204
+ throw 'would-block';
205
+ }
206
+ throw new Error("future already got and dropped");
207
+ }
208
+ unfinishedFutures.delete(id);
209
+ return future;
210
+ }
211
+ case OUTPUT_STREAM_CREATE | SOCKET: {
212
+ // TODO: implement
213
+ }
214
+ case INPUT_STREAM_CREATE | SOCKET: {
215
+ // TODO: implement
216
+ }
217
+
218
+ // Stdio
219
+ case OUTPUT_STREAM_DISPOSE | STDOUT:
220
+ case OUTPUT_STREAM_DISPOSE | STDERR:
221
+ case INPUT_STREAM_DISPOSE | STDIN:
222
+ return;
223
+
224
+ // Clocks
225
+ case CLOCKS_NOW:
226
+ return hrtime.bigint();
227
+ case CLOCKS_DURATION_SUBSCRIBE:
228
+ return createPoll(subscribeInstant(hrtime.bigint() + payload));
229
+ case CLOCKS_INSTANT_SUBSCRIBE:
230
+ return createPoll(subscribeInstant(payload));
231
+
232
+ // Filesystem
233
+ case INPUT_STREAM_CREATE | FILE: {
234
+ const { fd, offset } = payload;
235
+ const stream = createReadStream(null, {
236
+ fd,
237
+ autoClose: false,
238
+ highWaterMark: 64 * 1024,
239
+ start: Number(offset),
240
+ });
241
+ // for some reason fs streams dont emit readable on end
242
+ stream.on("end", () => void stream.emit("readable"));
243
+ return createStream(stream);
244
+ }
245
+ case OUTPUT_STREAM_CREATE | FILE: {
246
+ const { fd, offset } = payload;
247
+ const stream = createWriteStream(null, {
248
+ fd,
249
+ autoClose: false,
250
+ emitClose: false,
251
+ highWaterMark: 64 * 1024,
252
+ start: Number(offset),
253
+ });
254
+ return createStream(stream);
255
+ }
256
+ // Generic call implementations (streams + polls)
257
+ default:
258
+ switch (call & CALL_MASK) {
259
+ case INPUT_STREAM_READ: {
260
+ const { stream } = getStreamOrThrow(id);
261
+ const res = stream.read(Number(payload));
262
+ return res ?? new Uint8Array();
263
+ }
264
+ case INPUT_STREAM_BLOCKING_READ:
265
+ return Promise.resolve(
266
+ unfinishedPolls.get(
267
+ handle(INPUT_STREAM_SUBSCRIBE | (call & CALL_TYPE_MASK), id)
268
+ )
269
+ ).then(() =>
270
+ handle(INPUT_STREAM_READ | (call & CALL_TYPE_MASK), id, payload)
271
+ );
272
+ case INPUT_STREAM_SKIP:
273
+ return handle(
274
+ INPUT_STREAM_READ | (call & CALL_TYPE_MASK),
275
+ id,
276
+ new Uint8Array(Number(payload))
277
+ );
278
+ case INPUT_STREAM_BLOCKING_SKIP:
279
+ return handle(
280
+ INPUT_STREAM_BLOCKING_READ | (call & CALL_TYPE_MASK),
281
+ id,
282
+ new Uint8Array(Number(payload))
283
+ );
284
+ case INPUT_STREAM_SUBSCRIBE: {
285
+ const stream = unfinishedStreams.get(id)?.stream;
286
+ if (id === 1) {
287
+ // TODO: stdin subscribe
288
+ return 0;
289
+ }
290
+ // already closed or errored -> immediately return poll
291
+ // (poll 0 is immediately resolved)
292
+ if (
293
+ !stream ||
294
+ stream.closed ||
295
+ stream.errored ||
296
+ stream.readableLength > 0
297
+ )
298
+ return 0;
299
+ let resolve, reject;
300
+ return createPoll(
301
+ new Promise((_resolve, _reject) => {
302
+ stream
303
+ .once("readable", (resolve = _resolve))
304
+ .once("error", (reject = _reject));
305
+ }).then(
306
+ () => void stream.off("error", reject),
307
+ (err) => {
308
+ stream.off("readable", resolve);
309
+ throw streamError(id, stream.stream, err);
310
+ }
311
+ )
312
+ );
313
+ }
314
+ case INPUT_STREAM_DISPOSE:
315
+ unfinishedStreams.delete(id);
316
+ return;
317
+
318
+ case OUTPUT_STREAM_CHECK_WRITE: {
319
+ const { stream } = getStreamOrThrow(id);
320
+ return BigInt(stream.writableHighWaterMark - stream.writableLength);
321
+ }
322
+ case OUTPUT_STREAM_WRITE: {
323
+ const { stream } = getStreamOrThrow(id);
324
+ if (
325
+ payload.byteLength >
326
+ stream.writableHighWaterMark - stream.writableLength
327
+ )
328
+ throw new Error("wasi-io error: attempt to write too many bytes");
329
+ return void stream.write(payload);
330
+ }
331
+ case OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH: {
332
+ const { stream, flushPromise } = getStreamOrThrow(id);
333
+ // if an existing flush, try again after that
334
+ if (flushPromise)
335
+ return flushPromise.then(() => handle(call, id, payload));
336
+ if (
337
+ payload.byteLength >
338
+ stream.writableHighWaterMark - stream.writableLength
339
+ ) {
340
+ throw streamError(
341
+ id,
342
+ stream,
343
+ new Error("Cannot write more than permitted writable length")
344
+ );
345
+ }
346
+ return new Promise((resolve, reject) => {
347
+ stream.write(payload, (err) => {
348
+ if (err) return void reject(streamError(id, stream, err));
349
+ resolve(BigInt(payload.byteLength));
350
+ });
351
+ });
352
+ }
353
+ case OUTPUT_STREAM_FLUSH: {
354
+ const stream = getStreamOrThrow(id);
355
+ if (stream.flushPromise) return;
356
+ return (stream.flushPromise = new Promise((resolve, reject) => {
357
+ stream.stream.write(new Uint8Array([]), (err) =>
358
+ err ? reject(streamError(id, stream.stream, err)) : resolve()
359
+ );
360
+ }).then(
361
+ () => void (stream.stream.flushPromise = null),
362
+ (err) => {
363
+ stream.stream.flushPromise = null;
364
+ throw streamError(id, stream.stream, err);
365
+ }
366
+ ));
367
+ }
368
+ case OUTPUT_STREAM_BLOCKING_FLUSH: {
369
+ const { stream, flushPromise } = getStreamOrThrow(id);
370
+ if (flushPromise) return flushPromise;
371
+ return new Promise((resolve, reject) => {
372
+ stream.write(new Uint8Array([]), (err) =>
373
+ err ? reject(streamError(id, stream, err)) : resolve()
374
+ );
375
+ });
376
+ }
377
+ case OUTPUT_STREAM_WRITE_ZEROES:
378
+ return handle(
379
+ OUTPUT_STREAM_WRITE | (call & CALL_TYPE_MASK),
380
+ id,
381
+ new Uint8Array(Number(payload))
382
+ );
383
+ case OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH:
384
+ return handle(
385
+ OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | (call & CALL_TYPE_MASK),
386
+ id,
387
+ new Uint8Array(Number(payload))
388
+ );
389
+ case OUTPUT_STREAM_SPLICE: {
390
+ const { stream: outputStream } = getStreamOrThrow(id);
391
+ const { stream: inputStream } = getStreamOrThrow(payload.src);
392
+ let bytesRemaining = Number(payload.len);
393
+ let chunk;
394
+ while (
395
+ bytesRemaining > 0 &&
396
+ (chunk = inputStream.read(
397
+ Math.min(
398
+ outputStream.writableHighWaterMark -
399
+ outputStream.writableLength,
400
+ bytesRemaining
401
+ )
402
+ ))
403
+ ) {
404
+ bytesRemaining -= chunk.byteLength;
405
+ outputStream.write(chunk);
406
+ }
407
+ // TODO: these error handlers should be attached, and only for the duration of the splice flush
408
+ if (inputStream.errored)
409
+ throw streamError(payload.src, inputStream, inputStream.errored);
410
+ if (outputStream.errored)
411
+ throw streamError(id, outputStream, outputStream.errored);
412
+ return payload.len - BigInt(bytesRemaining);
413
+ }
414
+ case OUTPUT_STREAM_SUBSCRIBE: {
415
+ const { stream, flushPromise } = unfinishedStreams.get(id) ?? {};
416
+ if (flushPromise) return flushPromise;
417
+ // not added to unfinishedPolls => it's an immediately resolved poll
418
+ if (
419
+ !stream ||
420
+ stream.closed ||
421
+ stream.errored ||
422
+ !stream.writableNeedDrain
423
+ )
424
+ return 0;
425
+ let resolve, reject;
426
+ return createPoll(
427
+ new Promise((_resolve, _reject) => {
428
+ stream
429
+ .once("drain", (resolve = _resolve))
430
+ .once("error", (reject = _reject));
431
+ }).then(() => void stream.off("error", reject)),
432
+ (err) => {
433
+ stream.off("drain", resolve);
434
+ throw streamError(id, stream, err);
435
+ }
436
+ );
437
+ }
438
+ case OUTPUT_STREAM_BLOCKING_SPLICE: {
439
+ const { stream: outputStream } = getStreamOrThrow(id);
440
+ let promise = Promise.resolve();
441
+ let resolve, reject;
442
+ if (outputStream.writableNeedDrain) {
443
+ promise = new Promise((_resolve, _reject) => {
444
+ outputStream
445
+ .once("drain", (resolve = _resolve))
446
+ .once("error", (reject = _reject));
447
+ }).then(
448
+ () => {
449
+ outputStream.off("error", reject);
450
+ },
451
+ (err) => {
452
+ outputStream.off("drain", resolve);
453
+ throw streamError(err);
454
+ }
455
+ );
456
+ }
457
+ const { stream: inputStream } = getStreamOrThrow(payload.src);
458
+ if (!inputStream.readable) {
459
+ promise = promise.then(() =>
460
+ new Promise((_resolve, _reject) => {
461
+ inputStream
462
+ .once("readable", (resolve = _resolve))
463
+ .once("error", (reject = _reject));
464
+ }).then(
465
+ () => {
466
+ inputStream.off("error", reject);
467
+ },
468
+ (err) => {
469
+ inputStream.off("readable", resolve);
470
+ throw streamError(err);
471
+ }
472
+ )
473
+ );
474
+ }
475
+ return promise.then(() => handle(OUTPUT_STREAM_SPLICE, id, payload));
476
+ }
477
+ case OUTPUT_STREAM_DISPOSE: {
478
+ const stream = unfinishedStreams.get(id);
479
+ if (stream) {
480
+ stream.stream.end();
481
+ unfinishedStreams.delete(id);
482
+ }
483
+ return;
484
+ }
485
+
486
+ case POLL_POLLABLE_READY:
487
+ return !unfinishedPolls.has(id);
488
+ case POLL_POLLABLE_BLOCK:
489
+ payload = [id];
490
+ // [intentional case fall-through]
491
+ case POLL_POLL_LIST: {
492
+ const resolvedList = payload.filter((id) => !unfinishedPolls.has(id));
493
+ if (resolvedList.length > 0) return resolvedList;
494
+ // if all polls are promise type, we just race them
495
+ return Promise.race(
496
+ payload.map((id) => unfinishedPolls.get(id))
497
+ ).then(() => {
498
+ const resolvedList = payload.filter(
499
+ (id) => !unfinishedPolls.has(id)
500
+ );
501
+ if (resolvedList.length === 0)
502
+ throw new Error("poll promise did not unregister poll");
503
+ return resolvedList;
504
+ });
505
+ }
506
+
507
+ case FUTURE_GET_VALUE_AND_DISPOSE: {
508
+ const future = unfinishedFutures.get(id);
509
+ if (!future) {
510
+ // future not ready yet
511
+ if (unfinishedPolls.get(id)) {
512
+ // if ((call & CALL_TYPE_MASK) ===
513
+ // http futures throw
514
+ throw undefined;
515
+ }
516
+ throw new Error("future already got and dropped");
517
+ }
518
+ unfinishedFutures.delete(id);
519
+ return future;
520
+ }
521
+ case FUTURE_DISPOSE:
522
+ return void unfinishedFutures.delete(id);
523
+
524
+ default:
525
+ throw new Error(
526
+ `Unknown call ${(call & CALL_MASK) >> CALL_SHIFT} with type ${
527
+ call & CALL_TYPE_MASK
528
+ }`
529
+ );
530
+ }
531
+ }
532
+ }
533
+
534
+ // poll promises must always resolve and never error
535
+ export function createPoll(promise) {
536
+ const pollId = ++pollCnt;
537
+ unfinishedPolls.set(
538
+ pollId,
539
+ promise.then(
540
+ () => void unfinishedPolls.delete(pollId),
541
+ (err) => {
542
+ process._rawDebug("Unexpected poll error");
543
+ process._rawDebug(err);
544
+ process.exit(1);
545
+ }
546
+ )
547
+ );
548
+ return pollId;
549
+ }
550
+
551
+ export function createFuture(promise) {
552
+ const pollId = ++pollCnt;
553
+ unfinishedPolls.set(
554
+ pollId,
555
+ promise.then(
556
+ (value) => {
557
+ unfinishedPolls.delete(pollId);
558
+ unfinishedFutures.set(pollId, { value, error: false });
559
+ },
560
+ (value) => {
561
+ unfinishedPolls.delete(pollId);
562
+ unfinishedFutures.set(pollId, { value, error: true });
563
+ }
564
+ )
565
+ );
566
+ return pollId;
567
+ }
568
+
569
+ runAsWorker(handle);