@bytecodealliance/preview2-shim 0.14.0 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/browser/filesystem.js +6 -5
- package/lib/browser/http.js +1 -3
- package/lib/browser/random.js +1 -1
- package/lib/io/calls.js +128 -1
- package/lib/io/worker-http.js +159 -65
- package/lib/io/worker-io.js +40 -43
- package/lib/io/worker-socket-tcp.js +131 -0
- package/lib/io/worker-socket-udp.js +219 -0
- package/lib/io/worker-thread.js +288 -82
- package/lib/nodejs/cli.js +27 -11
- package/lib/nodejs/filesystem.js +89 -38
- package/lib/nodejs/http.js +643 -522
- package/lib/nodejs/index.js +0 -1
- package/lib/nodejs/sockets/socket-common.js +15 -2
- package/lib/nodejs/sockets/tcp-socket-impl.js +279 -188
- package/lib/nodejs/sockets/udp-socket-impl.js +305 -165
- package/lib/nodejs/sockets/wasi-sockets.js +54 -33
- package/lib/nodejs/sockets.js +25 -11
- package/lib/synckit/index.js +22 -39
- package/package.json +1 -1
- package/types/interfaces/wasi-http-types.d.ts +53 -41
- package/types/interfaces/wasi-sockets-tcp.d.ts +5 -0
package/lib/io/worker-thread.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { resolve } from "node:dns/promises";
|
|
2
2
|
import { createReadStream, createWriteStream } from "node:fs";
|
|
3
|
-
import { hrtime } from "node:process";
|
|
3
|
+
import { _rawDebug, exit, hrtime, stderr, stdout } from "node:process";
|
|
4
|
+
import { PassThrough } from "node:stream";
|
|
4
5
|
import { runAsWorker } from "../synckit/index.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
6
|
+
import {
|
|
7
|
+
clearOutgoingResponse,
|
|
8
|
+
createHttpRequest,
|
|
9
|
+
setOutgoingResponse,
|
|
10
|
+
startHttpServer,
|
|
11
|
+
stopHttpServer,
|
|
12
|
+
} from "./worker-http.js";
|
|
13
|
+
|
|
14
|
+
const noop = () => {};
|
|
15
|
+
|
|
7
16
|
import {
|
|
8
17
|
CALL_MASK,
|
|
9
18
|
CALL_SHIFT,
|
|
@@ -11,10 +20,16 @@ import {
|
|
|
11
20
|
CLOCKS_DURATION_SUBSCRIBE,
|
|
12
21
|
CLOCKS_INSTANT_SUBSCRIBE,
|
|
13
22
|
CLOCKS_NOW,
|
|
23
|
+
FILE,
|
|
14
24
|
FUTURE_DISPOSE,
|
|
15
25
|
FUTURE_GET_VALUE_AND_DISPOSE,
|
|
26
|
+
HTTP,
|
|
16
27
|
HTTP_CREATE_REQUEST,
|
|
17
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,
|
|
18
33
|
INPUT_STREAM_BLOCKING_READ,
|
|
19
34
|
INPUT_STREAM_BLOCKING_SKIP,
|
|
20
35
|
INPUT_STREAM_CREATE,
|
|
@@ -32,21 +47,66 @@ import {
|
|
|
32
47
|
OUTPUT_STREAM_FLUSH,
|
|
33
48
|
OUTPUT_STREAM_SPLICE,
|
|
34
49
|
OUTPUT_STREAM_SUBSCRIBE,
|
|
35
|
-
OUTPUT_STREAM_WRITE_ZEROES,
|
|
36
50
|
OUTPUT_STREAM_WRITE,
|
|
37
|
-
|
|
51
|
+
OUTPUT_STREAM_WRITE_ZEROES,
|
|
38
52
|
POLL_POLLABLE_BLOCK,
|
|
39
53
|
POLL_POLLABLE_READY,
|
|
54
|
+
POLL_POLL_LIST,
|
|
40
55
|
SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST,
|
|
41
56
|
SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST,
|
|
42
57
|
SOCKET_RESOLVE_ADDRESS_GET_AND_DISPOSE_REQUEST,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
SOCKET_TCP_BIND,
|
|
59
|
+
SOCKET_TCP_CONNECT,
|
|
60
|
+
SOCKET_TCP_CREATE_HANDLE,
|
|
61
|
+
SOCKET_TCP_CREATE_INPUT_STREAM,
|
|
62
|
+
SOCKET_TCP_CREATE_OUTPUT_STREAM,
|
|
63
|
+
SOCKET_TCP_DISPOSE,
|
|
64
|
+
SOCKET_TCP_GET_LOCAL_ADDRESS,
|
|
65
|
+
SOCKET_TCP_GET_REMOTE_ADDRESS,
|
|
66
|
+
SOCKET_TCP_LISTEN,
|
|
67
|
+
SOCKET_TCP_SET_KEEP_ALIVE,
|
|
68
|
+
SOCKET_TCP_SHUTDOWN,
|
|
69
|
+
SOCKET_UDP_BIND,
|
|
70
|
+
SOCKET_UDP_CHECK_SEND,
|
|
71
|
+
SOCKET_UDP_CONNECT,
|
|
72
|
+
SOCKET_UDP_CREATE_HANDLE,
|
|
73
|
+
SOCKET_UDP_DISCONNECT,
|
|
74
|
+
SOCKET_UDP_DISPOSE,
|
|
75
|
+
SOCKET_UDP_GET_LOCAL_ADDRESS,
|
|
76
|
+
SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE,
|
|
77
|
+
SOCKET_UDP_GET_REMOTE_ADDRESS,
|
|
78
|
+
SOCKET_UDP_GET_SEND_BUFFER_SIZE,
|
|
79
|
+
SOCKET_UDP_RECEIVE,
|
|
80
|
+
SOCKET_UDP_SEND,
|
|
81
|
+
SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE,
|
|
82
|
+
SOCKET_UDP_SET_SEND_BUFFER_SIZE,
|
|
83
|
+
SOCKET_UDP_SET_UNICAST_HOP_LIMIT,
|
|
46
84
|
STDERR,
|
|
47
85
|
STDIN,
|
|
48
86
|
STDOUT,
|
|
49
87
|
} from "./calls.js";
|
|
88
|
+
import {
|
|
89
|
+
createTcpSocket,
|
|
90
|
+
socketTcpBind,
|
|
91
|
+
socketTcpConnect,
|
|
92
|
+
socketTcpDispose,
|
|
93
|
+
socketTcpGetLocalAddress,
|
|
94
|
+
socketTcpGetRemoteAddress,
|
|
95
|
+
socketTcpListen,
|
|
96
|
+
socketTcpSetKeepAlive,
|
|
97
|
+
socketTcpShutdown,
|
|
98
|
+
} from "./worker-socket-tcp.js";
|
|
99
|
+
import {
|
|
100
|
+
SocketUdpReceive,
|
|
101
|
+
createUdpSocket,
|
|
102
|
+
getSocketOrThrow,
|
|
103
|
+
socketUdpBind,
|
|
104
|
+
socketUdpCheckSend,
|
|
105
|
+
socketUdpConnect,
|
|
106
|
+
socketUdpDisconnect,
|
|
107
|
+
socketUdpDispose,
|
|
108
|
+
socketUdpSend,
|
|
109
|
+
} from "./worker-socket-udp.js";
|
|
50
110
|
|
|
51
111
|
let streamCnt = 0,
|
|
52
112
|
pollCnt = 0;
|
|
@@ -63,28 +123,28 @@ export const unfinishedFutures = new Map();
|
|
|
63
123
|
/**
|
|
64
124
|
* @param {NodeJS.ReadableStream | NodeJS.WritableStream} stream
|
|
65
125
|
*/
|
|
66
|
-
export function createStream(nodeStream
|
|
126
|
+
export function createStream(nodeStream) {
|
|
67
127
|
unfinishedStreams.set(++streamCnt, {
|
|
68
|
-
flushPromise,
|
|
128
|
+
flushPromise: null,
|
|
69
129
|
stream: nodeStream,
|
|
70
130
|
});
|
|
71
131
|
return streamCnt;
|
|
72
132
|
}
|
|
73
133
|
|
|
74
134
|
// Stdio
|
|
75
|
-
|
|
76
|
-
createStream(
|
|
77
|
-
createStream(
|
|
135
|
+
// Stdin created when used
|
|
136
|
+
createStream(stdout);
|
|
137
|
+
createStream(stderr);
|
|
78
138
|
|
|
79
139
|
/**
|
|
80
140
|
* @param {number} streamId
|
|
81
141
|
* @param {NodeJS.ReadableStream | NodeJS.WritableStream} stream
|
|
82
142
|
*/
|
|
83
143
|
function streamError(streamId, stream, err) {
|
|
84
|
-
if (stream.end) stream.end();
|
|
144
|
+
if (typeof stream.end === "function") stream.end();
|
|
85
145
|
// we delete the stream from unfinishedStreams as it is now "finished" (closed)
|
|
86
146
|
unfinishedStreams.delete(streamId);
|
|
87
|
-
return { tag: "last-operation-failed", val: err };
|
|
147
|
+
return { tag: "last-operation-failed", val: { code: err.code, message: err.message, stack: err.stack } };
|
|
88
148
|
}
|
|
89
149
|
|
|
90
150
|
/**
|
|
@@ -92,6 +152,7 @@ function streamError(streamId, stream, err) {
|
|
|
92
152
|
* @returns {{ stream: NodeJS.ReadableStream | NodeJS.WritableStream, flushPromise: Promise<void> | null }}
|
|
93
153
|
*/
|
|
94
154
|
export function getStreamOrThrow(streamId) {
|
|
155
|
+
if (!streamId) throw new Error("Internal error: no stream id provided");
|
|
95
156
|
const stream = unfinishedStreams.get(streamId);
|
|
96
157
|
// not in unfinished streams <=> closed
|
|
97
158
|
if (!stream) throw { tag: "closed" };
|
|
@@ -126,15 +187,36 @@ function handle(call, id, payload) {
|
|
|
126
187
|
switch (call) {
|
|
127
188
|
// Http
|
|
128
189
|
case HTTP_CREATE_REQUEST: {
|
|
129
|
-
const {
|
|
130
|
-
|
|
190
|
+
const {
|
|
191
|
+
method,
|
|
192
|
+
scheme,
|
|
193
|
+
authority,
|
|
194
|
+
pathWithQuery,
|
|
195
|
+
headers,
|
|
196
|
+
body,
|
|
197
|
+
connectTimeout,
|
|
198
|
+
betweenBytesTimeout,
|
|
199
|
+
firstByteTimeout,
|
|
200
|
+
} = payload;
|
|
201
|
+
return createFuture(
|
|
202
|
+
createHttpRequest(
|
|
203
|
+
method,
|
|
204
|
+
scheme,
|
|
205
|
+
authority,
|
|
206
|
+
pathWithQuery,
|
|
207
|
+
headers,
|
|
208
|
+
body,
|
|
209
|
+
connectTimeout,
|
|
210
|
+
betweenBytesTimeout,
|
|
211
|
+
firstByteTimeout
|
|
212
|
+
)
|
|
213
|
+
);
|
|
131
214
|
}
|
|
132
215
|
case OUTPUT_STREAM_CREATE | HTTP: {
|
|
133
|
-
const
|
|
134
|
-
const stream = Writable.fromWeb(webTransformStream.writable);
|
|
216
|
+
const stream = new PassThrough();
|
|
135
217
|
// content length is passed as payload
|
|
218
|
+
stream.contentLength = payload;
|
|
136
219
|
stream.bytesRemaining = payload;
|
|
137
|
-
stream.readableBodyStream = webTransformStream.readable;
|
|
138
220
|
return createStream(stream);
|
|
139
221
|
}
|
|
140
222
|
case OUTPUT_STREAM_SUBSCRIBE | HTTP:
|
|
@@ -142,32 +224,35 @@ function handle(call, id, payload) {
|
|
|
142
224
|
case OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP: {
|
|
143
225
|
// http flush is a noop
|
|
144
226
|
const { stream } = getStreamOrThrow(id);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
227
|
+
if (call === (OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP)) {
|
|
228
|
+
stream.bytesRemaining -= payload.byteLength;
|
|
229
|
+
if (stream.bytesRemaining < 0) {
|
|
230
|
+
throw {
|
|
231
|
+
tag: "last-operation-failed",
|
|
232
|
+
val: {
|
|
233
|
+
tag: "HTTP-request-body-size",
|
|
234
|
+
val: stream.contentLength - stream.bytesRemaining,
|
|
235
|
+
},
|
|
236
|
+
};
|
|
155
237
|
}
|
|
156
238
|
}
|
|
157
|
-
if (call === (OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH | HTTP))
|
|
158
|
-
stream.bytesRemaining -= payload.byteLength;
|
|
159
239
|
// otherwise fall through to generic implementation
|
|
160
240
|
return handle(call & ~HTTP, id, payload);
|
|
161
241
|
}
|
|
162
242
|
case OUTPUT_STREAM_DISPOSE | HTTP:
|
|
163
|
-
throw new Error(
|
|
243
|
+
throw new Error(
|
|
244
|
+
"Internal error: HTTP output stream dispose is bypassed for FINISH"
|
|
245
|
+
);
|
|
164
246
|
case OUTPUT_STREAM_WRITE | HTTP: {
|
|
165
247
|
const { stream } = getStreamOrThrow(id);
|
|
166
248
|
stream.bytesRemaining -= payload.byteLength;
|
|
167
249
|
if (stream.bytesRemaining < 0) {
|
|
168
250
|
throw {
|
|
169
|
-
tag:
|
|
170
|
-
val:
|
|
251
|
+
tag: "last-operation-failed",
|
|
252
|
+
val: {
|
|
253
|
+
tag: "HTTP-request-body-size",
|
|
254
|
+
val: stream.contentLength - stream.bytesRemaining,
|
|
255
|
+
},
|
|
171
256
|
};
|
|
172
257
|
}
|
|
173
258
|
const output = handle(OUTPUT_STREAM_WRITE, id, payload);
|
|
@@ -177,49 +262,178 @@ function handle(call, id, payload) {
|
|
|
177
262
|
const { stream } = getStreamOrThrow(id);
|
|
178
263
|
if (stream.bytesRemaining > 0) {
|
|
179
264
|
throw {
|
|
180
|
-
tag:
|
|
181
|
-
val:
|
|
265
|
+
tag: "HTTP-request-body-size",
|
|
266
|
+
val: stream.contentLength - stream.bytesRemaining,
|
|
182
267
|
};
|
|
183
268
|
}
|
|
184
269
|
if (stream.bytesRemaining < 0) {
|
|
185
270
|
throw {
|
|
186
|
-
tag:
|
|
187
|
-
val:
|
|
271
|
+
tag: "HTTP-request-body-size",
|
|
272
|
+
val: stream.contentLength - stream.bytesRemaining,
|
|
188
273
|
};
|
|
189
274
|
}
|
|
190
275
|
stream.end();
|
|
191
276
|
break;
|
|
192
277
|
}
|
|
278
|
+
case HTTP_SERVER_START:
|
|
279
|
+
return startHttpServer(id, payload);
|
|
280
|
+
case HTTP_SERVER_STOP:
|
|
281
|
+
return stopHttpServer(id);
|
|
282
|
+
case HTTP_SERVER_SET_OUTGOING_RESPONSE:
|
|
283
|
+
return setOutgoingResponse(id, payload);
|
|
284
|
+
case HTTP_SERVER_CLEAR_OUTGOING_RESPONSE:
|
|
285
|
+
return clearOutgoingResponse(id);
|
|
286
|
+
|
|
287
|
+
// Sockets TCP
|
|
288
|
+
case SOCKET_TCP_CREATE_HANDLE:
|
|
289
|
+
return createFuture(createTcpSocket());
|
|
290
|
+
|
|
291
|
+
case SOCKET_TCP_BIND:
|
|
292
|
+
return socketTcpBind(id, payload);
|
|
293
|
+
|
|
294
|
+
case SOCKET_TCP_CONNECT:
|
|
295
|
+
return socketTcpConnect(id, payload);
|
|
296
|
+
|
|
297
|
+
case SOCKET_TCP_LISTEN:
|
|
298
|
+
return socketTcpListen(id, payload);
|
|
299
|
+
|
|
300
|
+
case SOCKET_TCP_GET_LOCAL_ADDRESS:
|
|
301
|
+
return socketTcpGetLocalAddress(id);
|
|
302
|
+
|
|
303
|
+
case SOCKET_TCP_GET_REMOTE_ADDRESS:
|
|
304
|
+
return socketTcpGetRemoteAddress(id);
|
|
305
|
+
|
|
306
|
+
case SOCKET_TCP_SHUTDOWN:
|
|
307
|
+
return socketTcpShutdown(id, payload);
|
|
308
|
+
|
|
309
|
+
case SOCKET_TCP_SET_KEEP_ALIVE:
|
|
310
|
+
return socketTcpSetKeepAlive(id, payload);
|
|
311
|
+
|
|
312
|
+
case SOCKET_TCP_DISPOSE:
|
|
313
|
+
return socketTcpDispose(id);
|
|
314
|
+
|
|
315
|
+
case SOCKET_TCP_CREATE_INPUT_STREAM:
|
|
316
|
+
case SOCKET_TCP_CREATE_OUTPUT_STREAM:
|
|
317
|
+
return createStream(new PassThrough());
|
|
318
|
+
|
|
319
|
+
// Sockets UDP
|
|
320
|
+
case SOCKET_UDP_CREATE_HANDLE: {
|
|
321
|
+
const { addressFamily, reuseAddr } = payload;
|
|
322
|
+
return createFuture(createUdpSocket(addressFamily, reuseAddr));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
case SOCKET_UDP_BIND:
|
|
326
|
+
return socketUdpBind(id, payload);
|
|
327
|
+
|
|
328
|
+
case SOCKET_UDP_CHECK_SEND:
|
|
329
|
+
return socketUdpCheckSend(id);
|
|
330
|
+
|
|
331
|
+
case SOCKET_UDP_SEND:
|
|
332
|
+
return socketUdpSend(id, payload);
|
|
333
|
+
|
|
334
|
+
case SOCKET_UDP_RECEIVE:
|
|
335
|
+
return SocketUdpReceive(id, payload);
|
|
336
|
+
|
|
337
|
+
case SOCKET_UDP_CONNECT:
|
|
338
|
+
return socketUdpConnect(id, payload);
|
|
339
|
+
|
|
340
|
+
case SOCKET_UDP_DISCONNECT:
|
|
341
|
+
return socketUdpDisconnect(id);
|
|
342
|
+
|
|
343
|
+
case SOCKET_UDP_GET_LOCAL_ADDRESS: {
|
|
344
|
+
const socket = getSocketOrThrow(id);
|
|
345
|
+
return Promise.resolve(socket.address());
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
case SOCKET_UDP_GET_REMOTE_ADDRESS: {
|
|
349
|
+
const socket = getSocketOrThrow(id);
|
|
350
|
+
return Promise.resolve(socket.remoteAddress());
|
|
351
|
+
}
|
|
193
352
|
|
|
194
|
-
// Sockets
|
|
195
353
|
case SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST:
|
|
196
354
|
return createFuture(resolve(payload.hostname));
|
|
355
|
+
|
|
197
356
|
case SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST:
|
|
198
357
|
return void unfinishedFutures.delete(id);
|
|
358
|
+
|
|
199
359
|
case SOCKET_RESOLVE_ADDRESS_GET_AND_DISPOSE_REQUEST: {
|
|
200
360
|
const future = unfinishedFutures.get(id);
|
|
201
361
|
if (!future) {
|
|
202
362
|
// future not ready yet
|
|
203
363
|
if (unfinishedPolls.get(id)) {
|
|
204
|
-
throw
|
|
364
|
+
throw "would-block";
|
|
205
365
|
}
|
|
206
366
|
throw new Error("future already got and dropped");
|
|
207
367
|
}
|
|
208
368
|
unfinishedFutures.delete(id);
|
|
209
369
|
return future;
|
|
210
370
|
}
|
|
211
|
-
|
|
212
|
-
|
|
371
|
+
|
|
372
|
+
case SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE: {
|
|
373
|
+
const socket = getSocketOrThrow(id);
|
|
374
|
+
try {
|
|
375
|
+
return socket.getRecvBufferSize();
|
|
376
|
+
} catch (err) {
|
|
377
|
+
return err.errno;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
case SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE: {
|
|
382
|
+
const socket = getSocketOrThrow(id);
|
|
383
|
+
try {
|
|
384
|
+
return socket.setRecvBufferSize(65537);
|
|
385
|
+
} catch (err) {
|
|
386
|
+
return err.errno;
|
|
387
|
+
}
|
|
213
388
|
}
|
|
214
|
-
|
|
215
|
-
|
|
389
|
+
|
|
390
|
+
case SOCKET_UDP_GET_SEND_BUFFER_SIZE: {
|
|
391
|
+
const socket = getSocketOrThrow(id);
|
|
392
|
+
try {
|
|
393
|
+
return socket.getSendBufferSize();
|
|
394
|
+
} catch (err) {
|
|
395
|
+
return err.errno;
|
|
396
|
+
}
|
|
216
397
|
}
|
|
217
398
|
|
|
399
|
+
case SOCKET_UDP_SET_SEND_BUFFER_SIZE: {
|
|
400
|
+
const socket = getSocketOrThrow(id);
|
|
401
|
+
try {
|
|
402
|
+
return socket.setSendBufferSize(payload.value);
|
|
403
|
+
} catch (err) {
|
|
404
|
+
return err.errno;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
case SOCKET_UDP_SET_UNICAST_HOP_LIMIT: {
|
|
409
|
+
const socket = getSocketOrThrow(id);
|
|
410
|
+
try {
|
|
411
|
+
return socket.setTTL(payload.value);
|
|
412
|
+
} catch (err) {
|
|
413
|
+
return err.errno;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
case SOCKET_UDP_DISPOSE:
|
|
418
|
+
return socketUdpDispose(id);
|
|
419
|
+
|
|
218
420
|
// Stdio
|
|
421
|
+
case OUTPUT_STREAM_BLOCKING_FLUSH | STDOUT:
|
|
422
|
+
case OUTPUT_STREAM_BLOCKING_FLUSH | STDERR:
|
|
423
|
+
// no blocking flush for stdio in Node.js
|
|
424
|
+
return;
|
|
219
425
|
case OUTPUT_STREAM_DISPOSE | STDOUT:
|
|
220
426
|
case OUTPUT_STREAM_DISPOSE | STDERR:
|
|
221
|
-
case INPUT_STREAM_DISPOSE | STDIN:
|
|
222
427
|
return;
|
|
428
|
+
case INPUT_STREAM_CREATE | STDIN: {
|
|
429
|
+
const stream = createReadStream(null, {
|
|
430
|
+
fd: 0,
|
|
431
|
+
highWaterMark: 64 * 1024,
|
|
432
|
+
});
|
|
433
|
+
// for some reason fs streams dont emit readable on end
|
|
434
|
+
stream.on("end", () => void stream.emit("readable"));
|
|
435
|
+
return createStream(stream);
|
|
436
|
+
}
|
|
223
437
|
|
|
224
438
|
// Clocks
|
|
225
439
|
case CLOCKS_NOW:
|
|
@@ -258,7 +472,9 @@ function handle(call, id, payload) {
|
|
|
258
472
|
switch (call & CALL_MASK) {
|
|
259
473
|
case INPUT_STREAM_READ: {
|
|
260
474
|
const { stream } = getStreamOrThrow(id);
|
|
261
|
-
const res = stream.read(
|
|
475
|
+
const res = stream.read(
|
|
476
|
+
Math.min(stream.readableLength, Number(payload))
|
|
477
|
+
);
|
|
262
478
|
return res ?? new Uint8Array();
|
|
263
479
|
}
|
|
264
480
|
case INPUT_STREAM_BLOCKING_READ:
|
|
@@ -283,10 +499,6 @@ function handle(call, id, payload) {
|
|
|
283
499
|
);
|
|
284
500
|
case INPUT_STREAM_SUBSCRIBE: {
|
|
285
501
|
const stream = unfinishedStreams.get(id)?.stream;
|
|
286
|
-
if (id === 1) {
|
|
287
|
-
// TODO: stdin subscribe
|
|
288
|
-
return 0;
|
|
289
|
-
}
|
|
290
502
|
// already closed or errored -> immediately return poll
|
|
291
503
|
// (poll 0 is immediately resolved)
|
|
292
504
|
if (
|
|
@@ -304,10 +516,8 @@ function handle(call, id, payload) {
|
|
|
304
516
|
.once("error", (reject = _reject));
|
|
305
517
|
}).then(
|
|
306
518
|
() => void stream.off("error", reject),
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
throw streamError(id, stream.stream, err);
|
|
310
|
-
}
|
|
519
|
+
// error is read of stream itself when later accessed
|
|
520
|
+
(_err) => void stream.off("readable", resolve)
|
|
311
521
|
)
|
|
312
522
|
);
|
|
313
523
|
}
|
|
@@ -344,8 +554,10 @@ function handle(call, id, payload) {
|
|
|
344
554
|
);
|
|
345
555
|
}
|
|
346
556
|
return new Promise((resolve, reject) => {
|
|
557
|
+
stream.once("error", noop);
|
|
347
558
|
stream.write(payload, (err) => {
|
|
348
559
|
if (err) return void reject(streamError(id, stream, err));
|
|
560
|
+
stream.off("error", noop);
|
|
349
561
|
resolve(BigInt(payload.byteLength));
|
|
350
562
|
});
|
|
351
563
|
});
|
|
@@ -413,15 +625,12 @@ function handle(call, id, payload) {
|
|
|
413
625
|
}
|
|
414
626
|
case OUTPUT_STREAM_SUBSCRIBE: {
|
|
415
627
|
const { stream, flushPromise } = unfinishedStreams.get(id) ?? {};
|
|
416
|
-
if (flushPromise)
|
|
628
|
+
if (flushPromise)
|
|
629
|
+
return flushPromise.then(() => handle(call, id, payload));
|
|
417
630
|
// not added to unfinishedPolls => it's an immediately resolved poll
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
stream.errored ||
|
|
422
|
-
!stream.writableNeedDrain
|
|
423
|
-
)
|
|
424
|
-
return 0;
|
|
631
|
+
if (!stream || stream.closed || stream.errored) return 0;
|
|
632
|
+
if (!stream.writableNeedDrain)
|
|
633
|
+
return createPoll(new Promise((resolve) => setTimeout(resolve)));
|
|
425
634
|
let resolve, reject;
|
|
426
635
|
return createPoll(
|
|
427
636
|
new Promise((_resolve, _reject) => {
|
|
@@ -429,10 +638,8 @@ function handle(call, id, payload) {
|
|
|
429
638
|
.once("drain", (resolve = _resolve))
|
|
430
639
|
.once("error", (reject = _reject));
|
|
431
640
|
}).then(() => void stream.off("error", reject)),
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
throw streamError(id, stream, err);
|
|
435
|
-
}
|
|
641
|
+
// error is read off stream itself when later accessed
|
|
642
|
+
(_err) => void stream.off("drain", resolve)
|
|
436
643
|
);
|
|
437
644
|
}
|
|
438
645
|
case OUTPUT_STREAM_BLOCKING_SPLICE: {
|
|
@@ -489,18 +696,21 @@ function handle(call, id, payload) {
|
|
|
489
696
|
payload = [id];
|
|
490
697
|
// [intentional case fall-through]
|
|
491
698
|
case POLL_POLL_LIST: {
|
|
492
|
-
const
|
|
493
|
-
|
|
699
|
+
const doneList = [];
|
|
700
|
+
for (const [idx, id] of payload.entries()) {
|
|
701
|
+
if (!unfinishedPolls.has(id)) doneList.push(idx);
|
|
702
|
+
}
|
|
703
|
+
if (doneList.length > 0) return new Uint32Array(doneList);
|
|
494
704
|
// if all polls are promise type, we just race them
|
|
495
705
|
return Promise.race(
|
|
496
706
|
payload.map((id) => unfinishedPolls.get(id))
|
|
497
707
|
).then(() => {
|
|
498
|
-
const
|
|
499
|
-
(
|
|
500
|
-
|
|
501
|
-
if (
|
|
708
|
+
for (const [idx, id] of payload.entries()) {
|
|
709
|
+
if (!unfinishedPolls.has(id)) doneList.push(idx);
|
|
710
|
+
}
|
|
711
|
+
if (doneList.length === 0)
|
|
502
712
|
throw new Error("poll promise did not unregister poll");
|
|
503
|
-
return
|
|
713
|
+
return new Uint32Array(doneList);
|
|
504
714
|
});
|
|
505
715
|
}
|
|
506
716
|
|
|
@@ -508,11 +718,7 @@ function handle(call, id, payload) {
|
|
|
508
718
|
const future = unfinishedFutures.get(id);
|
|
509
719
|
if (!future) {
|
|
510
720
|
// future not ready yet
|
|
511
|
-
if (unfinishedPolls.get(id))
|
|
512
|
-
// if ((call & CALL_TYPE_MASK) ===
|
|
513
|
-
// http futures throw
|
|
514
|
-
throw undefined;
|
|
515
|
-
}
|
|
721
|
+
if (unfinishedPolls.get(id)) throw undefined;
|
|
516
722
|
throw new Error("future already got and dropped");
|
|
517
723
|
}
|
|
518
724
|
unfinishedFutures.delete(id);
|
|
@@ -539,9 +745,9 @@ export function createPoll(promise) {
|
|
|
539
745
|
promise.then(
|
|
540
746
|
() => void unfinishedPolls.delete(pollId),
|
|
541
747
|
(err) => {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
748
|
+
_rawDebug("Unexpected poll error");
|
|
749
|
+
_rawDebug(err);
|
|
750
|
+
exit(1);
|
|
545
751
|
}
|
|
546
752
|
)
|
|
547
753
|
);
|
package/lib/nodejs/cli.js
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
import { argv, env, cwd } from "node:process";
|
|
2
2
|
import {
|
|
3
|
+
ioCall,
|
|
3
4
|
streams,
|
|
4
5
|
inputStreamCreate,
|
|
5
6
|
outputStreamCreate,
|
|
6
7
|
} from "../io/worker-io.js";
|
|
7
|
-
import { STDIN, STDOUT
|
|
8
|
+
import { INPUT_STREAM_CREATE, STDERR, STDIN, STDOUT } from "../io/calls.js";
|
|
8
9
|
const { InputStream, OutputStream } = streams;
|
|
9
10
|
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export const
|
|
17
|
-
export const
|
|
18
|
-
export const
|
|
11
|
+
export const _appendEnv = (env) => {
|
|
12
|
+
void (_env = [
|
|
13
|
+
..._env.filter(([curKey]) => !(curKey in env)),
|
|
14
|
+
...Object.entries(env),
|
|
15
|
+
]);
|
|
16
|
+
}
|
|
17
|
+
export const _setEnv = (env) => void (_env = Object.entries(env));
|
|
18
|
+
export const _setArgs = (args) => void (_args = args);
|
|
19
|
+
export const _setCwd = (cwd) => void (_cwd = cwd);
|
|
20
|
+
export const _setStdin = (stdin) => void (stdinStream = stdin);
|
|
21
|
+
export const _setStdout = (stdout) => void (stdoutStream = stdout);
|
|
22
|
+
export const _setStderr = (stderr) => void (stderrStream = stderr);
|
|
23
|
+
export const _setTerminalStdin = (terminalStdin) =>
|
|
24
|
+
void (terminalStdinInstance = terminalStdin);
|
|
25
|
+
export const _setTerminalStdout = (terminalStdout) =>
|
|
26
|
+
void (terminalStdoutInstance = terminalStdout);
|
|
27
|
+
export const _setTerminalStderr = (terminalStderr) =>
|
|
28
|
+
void (terminalStderrInstance = terminalStderr);
|
|
19
29
|
|
|
20
30
|
let _env = Object.entries(env),
|
|
21
31
|
_args = argv.slice(1),
|
|
@@ -39,13 +49,19 @@ export const exit = {
|
|
|
39
49
|
},
|
|
40
50
|
};
|
|
41
51
|
|
|
42
|
-
|
|
52
|
+
// Stdin is created as a FILE descriptor
|
|
53
|
+
let stdinStream;
|
|
43
54
|
let stdoutStream = outputStreamCreate(STDOUT, 2);
|
|
44
55
|
let stderrStream = outputStreamCreate(STDERR, 3);
|
|
45
56
|
|
|
46
57
|
export const stdin = {
|
|
47
58
|
InputStream,
|
|
48
59
|
getStdin() {
|
|
60
|
+
if (!stdinStream)
|
|
61
|
+
stdinStream = inputStreamCreate(
|
|
62
|
+
STDIN,
|
|
63
|
+
ioCall(INPUT_STREAM_CREATE | STDIN, null, null)
|
|
64
|
+
);
|
|
49
65
|
return stdinStream;
|
|
50
66
|
},
|
|
51
67
|
};
|