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