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