@bytecodealliance/preview3-shim 0.1.2 → 0.2.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 (112) hide show
  1. package/dist/nodejs/cli.d.ts +51 -0
  2. package/dist/nodejs/cli.js +106 -0
  3. package/dist/nodejs/clocks.d.ts +55 -0
  4. package/dist/nodejs/clocks.js +83 -0
  5. package/dist/nodejs/filesystem/descriptor.d.ts +443 -0
  6. package/dist/nodejs/filesystem/descriptor.js +1010 -0
  7. package/dist/nodejs/filesystem/error.d.ts +61 -0
  8. package/dist/nodejs/filesystem/error.js +86 -0
  9. package/dist/nodejs/filesystem/utils.d.ts +8 -0
  10. package/dist/nodejs/filesystem/utils.js +25 -0
  11. package/dist/nodejs/filesystem.js +2 -0
  12. package/dist/nodejs/finalization.d.ts +2 -0
  13. package/{lib → dist}/nodejs/finalization.js +15 -21
  14. package/dist/nodejs/future.d.ts +98 -0
  15. package/dist/nodejs/future.js +176 -0
  16. package/dist/nodejs/http/client.d.ts +22 -0
  17. package/dist/nodejs/http/client.js +138 -0
  18. package/dist/nodejs/http/error.d.ts +18 -0
  19. package/dist/nodejs/http/error.js +62 -0
  20. package/dist/nodejs/http/fields.d.ts +144 -0
  21. package/dist/nodejs/http/fields.js +332 -0
  22. package/dist/nodejs/http/request.d.ts +256 -0
  23. package/dist/nodejs/http/request.js +606 -0
  24. package/dist/nodejs/http/response.d.ts +82 -0
  25. package/dist/nodejs/http/response.js +202 -0
  26. package/dist/nodejs/http/server.d.ts +13 -0
  27. package/dist/nodejs/http/server.js +127 -0
  28. package/dist/nodejs/http.d.ts +19 -0
  29. package/{lib → dist}/nodejs/http.js +13 -18
  30. package/dist/nodejs/index.js +8 -0
  31. package/dist/nodejs/random.d.ts +5 -0
  32. package/{lib → dist}/nodejs/random.js +2 -8
  33. package/dist/nodejs/sockets/address.d.ts +78 -0
  34. package/dist/nodejs/sockets/address.js +120 -0
  35. package/dist/nodejs/sockets/error.d.ts +83 -0
  36. package/dist/nodejs/sockets/error.js +120 -0
  37. package/dist/nodejs/sockets/ip-name-lookup.d.ts +18 -0
  38. package/dist/nodejs/sockets/ip-name-lookup.js +84 -0
  39. package/dist/nodejs/sockets/tcp.d.ts +333 -0
  40. package/dist/nodejs/sockets/tcp.js +723 -0
  41. package/dist/nodejs/sockets/udp.d.ts +224 -0
  42. package/dist/nodejs/sockets/udp.js +475 -0
  43. package/dist/nodejs/sockets.d.ts +11 -0
  44. package/{lib → dist}/nodejs/sockets.js +2 -5
  45. package/dist/nodejs/stream.d.ts +161 -0
  46. package/dist/nodejs/stream.js +388 -0
  47. package/dist/nodejs/workers/cli-worker.d.ts +1 -0
  48. package/dist/nodejs/workers/cli-worker.js +41 -0
  49. package/dist/nodejs/workers/filesystem-worker.d.ts +1 -0
  50. package/dist/nodejs/workers/filesystem-worker.js +109 -0
  51. package/dist/nodejs/workers/http-worker.d.ts +1 -0
  52. package/dist/nodejs/workers/http-worker.js +355 -0
  53. package/dist/nodejs/workers/resource-worker.d.ts +13 -0
  54. package/dist/nodejs/workers/resource-worker.js +152 -0
  55. package/dist/nodejs/workers/tcp-worker.d.ts +2 -0
  56. package/dist/nodejs/workers/tcp-worker.js +318 -0
  57. package/dist/nodejs/workers/udp-worker.d.ts +1 -0
  58. package/dist/nodejs/workers/udp-worker.js +156 -0
  59. package/package.json +20 -12
  60. package/types/interfaces/wasi-cli-environment.d.ts +3 -3
  61. package/types/interfaces/wasi-cli-exit.d.ts +12 -1
  62. package/types/interfaces/wasi-cli-run.d.ts +1 -1
  63. package/types/interfaces/wasi-cli-stderr.d.ts +3 -3
  64. package/types/interfaces/wasi-cli-stdin.d.ts +5 -5
  65. package/types/interfaces/wasi-cli-stdout.d.ts +3 -3
  66. package/types/interfaces/wasi-cli-terminal-input.d.ts +1 -1
  67. package/types/interfaces/wasi-cli-terminal-output.d.ts +1 -1
  68. package/types/interfaces/wasi-cli-terminal-stderr.d.ts +1 -1
  69. package/types/interfaces/wasi-cli-terminal-stdin.d.ts +1 -1
  70. package/types/interfaces/wasi-cli-terminal-stdout.d.ts +1 -1
  71. package/types/interfaces/wasi-cli-types.d.ts +5 -5
  72. package/types/interfaces/wasi-clocks-monotonic-clock.d.ts +3 -3
  73. package/types/interfaces/wasi-clocks-system-clock.d.ts +6 -6
  74. package/types/interfaces/wasi-clocks-types.d.ts +1 -1
  75. package/types/interfaces/wasi-filesystem-preopens.d.ts +1 -1
  76. package/types/interfaces/wasi-filesystem-types.d.ts +74 -74
  77. package/types/interfaces/wasi-http-client.d.ts +1 -1
  78. package/types/interfaces/wasi-http-handler.d.ts +1 -1
  79. package/types/interfaces/wasi-http-types.d.ts +34 -34
  80. package/types/interfaces/wasi-random-insecure-seed.d.ts +5 -5
  81. package/types/interfaces/wasi-random-insecure.d.ts +5 -5
  82. package/types/interfaces/wasi-random-random.d.ts +5 -5
  83. package/types/interfaces/wasi-sockets-ip-name-lookup.d.ts +11 -11
  84. package/types/interfaces/wasi-sockets-types.d.ts +121 -121
  85. package/types/wasi-cli-command.d.ts +23 -23
  86. package/types/wasi-http-service.d.ts +14 -14
  87. package/lib/nodejs/cli.js +0 -127
  88. package/lib/nodejs/clocks.js +0 -98
  89. package/lib/nodejs/filesystem/descriptor.js +0 -1093
  90. package/lib/nodejs/filesystem/error.js +0 -92
  91. package/lib/nodejs/filesystem/utils.js +0 -19
  92. package/lib/nodejs/future.js +0 -190
  93. package/lib/nodejs/http/client.js +0 -161
  94. package/lib/nodejs/http/error.js +0 -71
  95. package/lib/nodejs/http/fields.js +0 -373
  96. package/lib/nodejs/http/request.js +0 -691
  97. package/lib/nodejs/http/response.js +0 -227
  98. package/lib/nodejs/http/server.js +0 -157
  99. package/lib/nodejs/sockets/address.js +0 -146
  100. package/lib/nodejs/sockets/error.js +0 -123
  101. package/lib/nodejs/sockets/ip-name-lookup.js +0 -93
  102. package/lib/nodejs/sockets/tcp.js +0 -801
  103. package/lib/nodejs/sockets/udp.js +0 -516
  104. package/lib/nodejs/stream.js +0 -437
  105. package/lib/nodejs/workers/cli-worker.js +0 -49
  106. package/lib/nodejs/workers/filesystem-worker.js +0 -119
  107. package/lib/nodejs/workers/http-worker.js +0 -417
  108. package/lib/nodejs/workers/resource-worker.js +0 -195
  109. package/lib/nodejs/workers/tcp-worker.js +0 -370
  110. package/lib/nodejs/workers/udp-worker.js +0 -194
  111. /package/{lib/nodejs/filesystem.js → dist/nodejs/filesystem.d.ts} +0 -0
  112. /package/{lib/nodejs/index.js → dist/nodejs/index.d.ts} +0 -0
@@ -0,0 +1,318 @@
1
+ import { Socket, Server } from "node:net";
2
+ import { Readable } from "stream";
3
+ import { pipeline } from "stream/promises";
4
+ import { once } from "node:events";
5
+ import { Router } from "../workers/resource-worker.js";
6
+ import { serializeIpAddress, makeIpAddress, ipAddressConflict } from "../sockets/address.js";
7
+ import { SocketError } from "../sockets/error.js";
8
+ import process from "node:process";
9
+ const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
10
+ // Socket instances stored by ID
11
+ const sockets = new Map();
12
+ // Unique IDs for sockets
13
+ let NEXT_SOCKET_ID = 0n;
14
+ // Handle worker messages
15
+ Router()
16
+ .beforeAll((msg) => {
17
+ if (msg.op !== "tcp-create" && !sockets.has(msg.socketId)) {
18
+ throw new Error("Invalid socket ID");
19
+ }
20
+ })
21
+ .op("tcp-create", handleTcpCreate)
22
+ .op("tcp-bind", handleTcpBind)
23
+ .op("tcp-connect", handleTcpConnect)
24
+ .op("tcp-listen", handleTcpListen)
25
+ .op("tcp-send", handleTcpSend)
26
+ .op("tcp-receive", handleTcpReceive)
27
+ .op("tcp-get-local-address", handleGetLocalAddress)
28
+ .op("tcp-get-remote-address", handleGetRemoteAddress)
29
+ .op("tcp-set-listen-backlog-size", handleTcpSetBacklogSize)
30
+ .op("tcp-set-keep-alive", handleTcpSetKeepAlive)
31
+ .op("tcp-recv-buffer-size", handleRecvBufferSize)
32
+ .op("tcp-send-buffer-size", handleSendBufferSize)
33
+ .op("tcp-dispose", handleTcpDispose);
34
+ // Create a new TCP socket
35
+ function handleTcpCreate({ family }) {
36
+ const socketId = NEXT_SOCKET_ID++;
37
+ const handle = new TCP(TCPConstants.SOCKET);
38
+ sockets.set(socketId, {
39
+ handle,
40
+ family,
41
+ tcp: null,
42
+ server: null,
43
+ backlog: 128,
44
+ localAddress: null,
45
+ disposed: false,
46
+ activeStreams: 0,
47
+ });
48
+ return { socketId };
49
+ }
50
+ // Bind a socket to local address
51
+ async function handleTcpBind({ socketId, localAddress }) {
52
+ const socket = sockets.get(socketId);
53
+ const address = serializeIpAddress(localAddress);
54
+ const port = localAddress.val.port;
55
+ const { handle, family } = socket;
56
+ const hasConflict = [...sockets].some(([id, { localAddress: boundAddress }]) => id !== socketId && boundAddress && ipAddressConflict(boundAddress, localAddress));
57
+ if (hasConflict) {
58
+ const err = new Error("EADDRINUSE");
59
+ err.code = "EADDRINUSE";
60
+ throw err;
61
+ }
62
+ await new Promise((resolve, reject) => {
63
+ let code;
64
+ if (family === "ipv6") {
65
+ code = handle.bind6(address, port, TCPConstants.UV_TCP_IPV6ONLY);
66
+ }
67
+ else {
68
+ code = handle.bind(address, port);
69
+ }
70
+ if (code !== 0) {
71
+ reject(SocketError.from(-code));
72
+ }
73
+ else {
74
+ resolve();
75
+ }
76
+ });
77
+ const out = {};
78
+ const code = handle.getsockname(out);
79
+ if (code !== 0) {
80
+ throw SocketError.from(-code);
81
+ }
82
+ socket.localAddress = makeIpAddress(out.family.toLowerCase(), out.address, out.port);
83
+ }
84
+ // Connect a socket to remote address
85
+ async function handleTcpConnect({ socketId, remoteAddress }) {
86
+ const socket = sockets.get(socketId);
87
+ const host = serializeIpAddress(remoteAddress);
88
+ const port = remoteAddress.val.port;
89
+ const tcp = (socket.tcp = new Socket({
90
+ handle: socket.handle,
91
+ pauseOnCreate: true,
92
+ allowHalfOpen: true,
93
+ }));
94
+ const onConnect = once(tcp, "connect");
95
+ const onError = once(tcp, "error").then(([err]) => {
96
+ throw err;
97
+ });
98
+ // TODO(tandr): Add lookup
99
+ tcp.connect({ port, host });
100
+ await Promise.race([onConnect, onError]);
101
+ }
102
+ async function handleTcpListen({ socketId, stream }) {
103
+ const writer = stream.getWriter();
104
+ const socket = sockets.get(socketId);
105
+ const { handle, backlog, family } = socket;
106
+ const server = new Server({
107
+ pauseOnConnect: true,
108
+ allowHalfOpen: true,
109
+ });
110
+ const onListening = once(server, "listening");
111
+ const onError = once(server, "error").then(([err]) => {
112
+ throw err;
113
+ });
114
+ server.listen(handle, backlog);
115
+ await Promise.race([onListening, onError]);
116
+ const addr = server.address();
117
+ if (addr && typeof addr === "object") {
118
+ socket.localAddress = makeIpAddress(family, addr.address, addr.port);
119
+ }
120
+ server.on("connection", (conn) => {
121
+ conn.allowHalfOpen = true;
122
+ const id = NEXT_SOCKET_ID++;
123
+ sockets.set(id, {
124
+ handle: conn._handle,
125
+ family,
126
+ backlog,
127
+ tcp: conn,
128
+ server: null,
129
+ localAddress: makeIpAddress(family, conn.localAddress, conn.localPort),
130
+ disposed: false,
131
+ activeStreams: 0,
132
+ });
133
+ writer.write({ family, socketId: id });
134
+ });
135
+ socket.server = server;
136
+ server.on("error", (err) => stream.abort(err));
137
+ server.on("close", () => writer.close());
138
+ }
139
+ async function handleTcpSend({ socketId, stream }) {
140
+ const socket = sockets.get(socketId);
141
+ socket.activeStreams++;
142
+ const { tcp } = socket;
143
+ const readable = Readable.fromWeb(stream);
144
+ try {
145
+ await pipeline(readable, tcp);
146
+ }
147
+ finally {
148
+ socket.activeStreams--;
149
+ cleanupDisposedSocket(socketId, socket);
150
+ }
151
+ }
152
+ async function handleTcpReceive({ socketId, stream }) {
153
+ const socket = sockets.get(socketId);
154
+ const writer = stream.getWriter();
155
+ socket.activeStreams++;
156
+ const { tcp } = socket;
157
+ try {
158
+ await new Promise((resolve, reject) => {
159
+ let settled = false;
160
+ let pending = Promise.resolve();
161
+ const cleanup = () => {
162
+ tcp.off("data", onData);
163
+ tcp.off("end", onEnd);
164
+ tcp.off("error", onError);
165
+ };
166
+ const settle = (err = null) => {
167
+ if (settled) {
168
+ return;
169
+ }
170
+ settled = true;
171
+ cleanup();
172
+ if (err) {
173
+ reject(err);
174
+ }
175
+ else {
176
+ resolve();
177
+ }
178
+ };
179
+ const onData = (chunk) => {
180
+ tcp.pause();
181
+ pending = pending.then(async () => {
182
+ try {
183
+ await writer.write(chunk);
184
+ }
185
+ catch {
186
+ settle();
187
+ return;
188
+ }
189
+ finally {
190
+ // Always undo the pause, even if the guest dropped or cancelled the
191
+ // receive stream. Otherwise the send path can remain backpressured
192
+ // forever.
193
+ tcp.resume();
194
+ }
195
+ }, settle);
196
+ };
197
+ const onEnd = () => {
198
+ pending = pending.then(async () => {
199
+ try {
200
+ await writer.close();
201
+ }
202
+ catch {
203
+ // The guest can drop the receive stream before remote EOF.
204
+ }
205
+ settle();
206
+ }, settle);
207
+ };
208
+ const onError = (err) => {
209
+ pending = pending.finally(() => settle(err));
210
+ };
211
+ writer.closed.then(() => settle(), () => settle());
212
+ tcp.on("data", onData);
213
+ tcp.once("end", onEnd);
214
+ tcp.once("error", onError);
215
+ tcp.resume();
216
+ });
217
+ }
218
+ finally {
219
+ writer.releaseLock();
220
+ socket.activeStreams--;
221
+ cleanupDisposedSocket(socketId, socket);
222
+ }
223
+ }
224
+ function cleanupDisposedSocket(socketId, socket) {
225
+ if (!socket.disposed || socket.activeStreams > 0) {
226
+ return;
227
+ }
228
+ if (socket.server) {
229
+ socket.server.close();
230
+ }
231
+ if (socket.tcp) {
232
+ socket.tcp.destroy();
233
+ }
234
+ if (socket.handle) {
235
+ socket.handle.close();
236
+ }
237
+ sockets.delete(socketId);
238
+ }
239
+ async function handleGetLocalAddress({ socketId }) {
240
+ const socket = sockets.get(socketId);
241
+ const out = {};
242
+ const code = socket.handle.getsockname(out);
243
+ if (code !== 0) {
244
+ throw SocketError.from(-code);
245
+ }
246
+ return makeIpAddress(out.family.toLowerCase(), out.address, out.port);
247
+ }
248
+ async function handleGetRemoteAddress({ socketId }) {
249
+ const socket = sockets.get(socketId);
250
+ const out = {};
251
+ const code = socket.handle.getpeername(out);
252
+ if (code !== 0) {
253
+ throw SocketError.from(-code);
254
+ }
255
+ return makeIpAddress(out.family.toLowerCase(), out.address, out.port);
256
+ }
257
+ function handleTcpSetBacklogSize({ socketId, value }) {
258
+ const socket = sockets.get(socketId);
259
+ socket.backlog = Number(value);
260
+ }
261
+ async function handleTcpSetKeepAlive({ socketId, keepAliveEnabled, keepAliveIdleTime }) {
262
+ const socket = sockets.get(socketId);
263
+ const time = Number(keepAliveIdleTime / 1000000000n);
264
+ const code = socket.handle.setKeepAlive(keepAliveEnabled, time);
265
+ if (code !== 0) {
266
+ throw SocketError.from(-code);
267
+ }
268
+ }
269
+ async function handleRecvBufferSize({ socketId }) {
270
+ const socket = sockets.get(socketId);
271
+ if (socket.tcp) {
272
+ return BigInt(socket.tcp.getRecvBufferSize());
273
+ }
274
+ else {
275
+ return await getDefaultReceiveBufferSize();
276
+ }
277
+ }
278
+ async function handleSendBufferSize({ socketId }) {
279
+ const socket = sockets.get(socketId);
280
+ if (socket.tcp) {
281
+ return BigInt(socket.tcp.getSendBufferSize());
282
+ }
283
+ else {
284
+ return await getDefaultSendBufferSize();
285
+ }
286
+ }
287
+ function handleTcpDispose({ socketId }) {
288
+ const socket = sockets.get(socketId);
289
+ if (!socket) {
290
+ return;
291
+ }
292
+ socket.disposed = true;
293
+ cleanupDisposedSocket(socketId, socket);
294
+ }
295
+ let _recvBufferSize, _sendBufferSize;
296
+ async function getDefaultBufferSizes() {
297
+ var s = new Socket();
298
+ s.bind(0);
299
+ await new Promise((resolve, reject) => {
300
+ s.once("error", reject);
301
+ s.once("listening", resolve);
302
+ });
303
+ _recvBufferSize = BigInt(s.getRecvBufferSize());
304
+ _sendBufferSize = BigInt(s.getSendBufferSize());
305
+ s.close();
306
+ }
307
+ export async function getDefaultSendBufferSize() {
308
+ if (!_sendBufferSize) {
309
+ await getDefaultBufferSizes();
310
+ }
311
+ return _sendBufferSize;
312
+ }
313
+ export async function getDefaultReceiveBufferSize() {
314
+ if (!_recvBufferSize) {
315
+ await getDefaultBufferSizes();
316
+ }
317
+ return _recvBufferSize;
318
+ }
@@ -0,0 +1 @@
1
+ export declare function noLookup(ip: any, _opts: any, cb: any): void;
@@ -0,0 +1,156 @@
1
+ import { once } from "node:events";
2
+ import { Router } from "../workers/resource-worker.js";
3
+ import { serializeIpAddress, makeIpAddress, ipAddressConflict, isLoopbackIpAddress, } from "../sockets/address.js";
4
+ import dgram from "node:dgram";
5
+ const sockets = new Map();
6
+ // Unique IDs for sockets
7
+ let NEXT_SOCKET_ID = 0n;
8
+ export function noLookup(ip, _opts, cb) {
9
+ cb(null, ip);
10
+ }
11
+ Router()
12
+ .beforeAll((msg) => {
13
+ if (msg.op !== "udp-create" && !sockets.has(msg.socketId)) {
14
+ throw new Error("Invalid socket ID");
15
+ }
16
+ })
17
+ .op("udp-create", handleCreate)
18
+ .op("udp-bind", handleBind)
19
+ .op("udp-connect", handleConnect)
20
+ .op("udp-disconnect", handleDisconnect)
21
+ .op("udp-send", handleSend)
22
+ .op("udp-receive", handleReceive)
23
+ .op("udp-get-local-address", handleGetLocal)
24
+ .op("udp-set-unicast-hop-limit", handleSetHop)
25
+ .op("udp-recv-buffer-size", handleRecvBuffer)
26
+ .op("udp-send-buffer-size", handleSendBuffer)
27
+ .op("udp-dispose", handleDispose);
28
+ function handleCreate({ family }) {
29
+ const socketId = NEXT_SOCKET_ID++;
30
+ const socket = {
31
+ family,
32
+ connected: null,
33
+ localAddress: null,
34
+ receiveQueue: [],
35
+ };
36
+ socket.udp = createSocket(socket);
37
+ sockets.set(socketId, socket);
38
+ return { socketId };
39
+ }
40
+ function createSocket(socket) {
41
+ const type = socket.family === "ipv6" ? "udp6" : "udp4";
42
+ const ipv6Only = socket.family === "ipv6";
43
+ const udp = dgram.createSocket({
44
+ type,
45
+ ipv6Only,
46
+ reuseAddr: false,
47
+ lookup: noLookup,
48
+ });
49
+ udp.on("message", (msg, rinfo) => socket.receiveQueue.push({ msg, rinfo }));
50
+ udp.on("error", () => { });
51
+ return udp;
52
+ }
53
+ async function handleBind({ socketId, localAddress }) {
54
+ const socket = sockets.get(socketId);
55
+ const hasConflict = [...sockets].some(([id, { localAddress: boundAddress }]) => id !== socketId && boundAddress && ipAddressConflict(boundAddress, localAddress));
56
+ if (hasConflict) {
57
+ const err = new Error("EADDRINUSE");
58
+ err.code = "EADDRINUSE";
59
+ throw err;
60
+ }
61
+ const addr = serializeIpAddress(localAddress);
62
+ const port = localAddress.val.port;
63
+ const onListening = once(socket.udp, "listening");
64
+ const onError = once(socket.udp, "error").then(([err]) => {
65
+ throw err;
66
+ });
67
+ socket.udp.bind(port, addr);
68
+ await Promise.race([onListening, onError]);
69
+ const boundAddr = socket.udp.address();
70
+ socket.localAddress = makeIpAddress(socket.family, boundAddr.address, boundAddr.port);
71
+ }
72
+ async function handleConnect({ socketId, remoteAddress }) {
73
+ const socket = sockets.get(socketId);
74
+ const preserveLocalAddress = socket.connected &&
75
+ socket.localAddress &&
76
+ isLoopbackIpAddress(socket.connected) &&
77
+ isLoopbackIpAddress(remoteAddress);
78
+ if (socket.connected) {
79
+ const localAddress = preserveLocalAddress ? socket.localAddress : null;
80
+ await new Promise((resolve) => socket.udp.close(resolve));
81
+ socket.udp = createSocket(socket);
82
+ if (localAddress) {
83
+ await handleBind({ socketId, localAddress });
84
+ }
85
+ else {
86
+ socket.localAddress = null;
87
+ }
88
+ }
89
+ const addr = serializeIpAddress(remoteAddress);
90
+ const port = remoteAddress.val.port;
91
+ await new Promise((resolve, reject) => {
92
+ socket.udp.connect(port, addr, (err) => (err ? reject(err) : resolve()));
93
+ });
94
+ const boundAddr = socket.udp.address();
95
+ socket.localAddress = makeIpAddress(socket.family, boundAddr.address, boundAddr.port);
96
+ socket.connected = remoteAddress;
97
+ }
98
+ function handleDisconnect({ socketId }) {
99
+ const socket = sockets.get(socketId);
100
+ socket.udp.disconnect();
101
+ socket.connected = null;
102
+ }
103
+ async function handleSend({ socketId, data, remoteAddress }) {
104
+ const socket = sockets.get(socketId);
105
+ const isConnected = socket.connected != null;
106
+ await new Promise((resolve, reject) => {
107
+ if (isConnected) {
108
+ socket.udp.send(data, (err) => (err ? reject(err) : resolve()));
109
+ }
110
+ else {
111
+ const addr = serializeIpAddress(remoteAddress);
112
+ const port = remoteAddress.val.port;
113
+ socket.udp.send(data, port, addr, (err) => (err ? reject(err) : resolve()));
114
+ }
115
+ });
116
+ const boundAddr = socket.udp.address();
117
+ socket.localAddress = makeIpAddress(socket.family, boundAddr.address, boundAddr.port);
118
+ }
119
+ async function handleReceive({ socketId }) {
120
+ const socket = sockets.get(socketId);
121
+ if (socket.receiveQueue.length === 0) {
122
+ await Promise.race([
123
+ once(socket.udp, "message"),
124
+ once(socket.udp, "error").then(([err]) => {
125
+ throw err;
126
+ }),
127
+ ]);
128
+ }
129
+ const { msg, rinfo } = socket.receiveQueue.shift();
130
+ return {
131
+ data: msg,
132
+ remoteAddress: makeIpAddress(socket.family, rinfo.address, rinfo.port),
133
+ };
134
+ }
135
+ function handleGetLocal({ socketId }) {
136
+ const socket = sockets.get(socketId);
137
+ const addr = socket.udp.address();
138
+ return makeIpAddress(socket.family, addr.address, addr.port);
139
+ }
140
+ function handleSetHop({ socketId, value }) {
141
+ const socket = sockets.get(socketId);
142
+ socket.udp.setTTL(value);
143
+ }
144
+ function handleRecvBuffer({ socketId }) {
145
+ const socket = sockets.get(socketId);
146
+ return BigInt(socket.udp.getRecvBufferSize());
147
+ }
148
+ function handleSendBuffer({ socketId }) {
149
+ const socket = sockets.get(socketId);
150
+ return BigInt(socket.udp.getSendBufferSize());
151
+ }
152
+ function handleDispose({ socketId }) {
153
+ const socket = sockets.get(socketId);
154
+ socket.udp.close();
155
+ sockets.delete(socketId);
156
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytecodealliance/preview3-shim",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "WASI Preview3 shim for JS environments",
5
5
  "homepage": "https://github.com/bytecodealliance/jco/blob/main/packages/preview3-shim/#readme",
6
6
  "bugs": {
@@ -23,35 +23,43 @@
23
23
  },
24
24
  "files": [
25
25
  "types",
26
- "lib"
26
+ "dist"
27
27
  ],
28
28
  "type": "module",
29
- "types": "./types/index.d.ts",
30
29
  "exports": {
31
30
  ".": {
32
- "types": "./types/index.d.ts",
33
- "node": "./lib/nodejs/index.js"
31
+ "types": "./dist/nodejs/index.d.ts",
32
+ "node": "./dist/nodejs/index.js"
34
33
  },
35
34
  "./*": {
36
- "types": "./types/*.d.ts",
37
- "node": "./lib/nodejs/*.js"
35
+ "types": "./dist/nodejs/*.d.ts",
36
+ "node": "./dist/nodejs/*.js"
37
+ },
38
+ "./interfaces/*": {
39
+ "types": "./types/interfaces/*.d.ts"
38
40
  }
39
41
  },
40
42
  "dependencies": {
41
- "@bytecodealliance/preview2-shim": "^0.17.3"
43
+ "@bytecodealliance/preview2-shim": "^0.19.0"
42
44
  },
43
45
  "devDependencies": {
44
- "globals": "^16.3.0",
45
- "vite": "^7.1.5",
46
- "vitest": "^4.1.0"
46
+ "@types/node": "^24.12.4",
47
+ "globals": "^17.6.0",
48
+ "typescript": "^6.0.3",
49
+ "vite": "^8.0.16",
50
+ "vitest": "^4.0.8"
47
51
  },
48
52
  "scripts": {
49
- "compile:check": "tsc --noEmit types/index.d.ts",
53
+ "build": "tsc --project tsconfig.build.json",
54
+ "build:watch": "tsc --watch",
55
+ "types:check": "tsc --noEmit",
50
56
  "fmt": "oxfmt",
51
57
  "fmt:check": "oxfmt --check",
52
58
  "lint": "oxlint",
53
59
  "lint:fix": "oxlint --fix",
60
+ "pretest": "pnpm run build",
54
61
  "test": "vitest --run",
62
+ "prebench": "pnpm run build",
55
63
  "bench": "vitest bench --run"
56
64
  }
57
65
  }
@@ -1,10 +1,10 @@
1
- /** @module Interface wasi:cli/environment@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/environment@0.3.0 **/
2
2
  /**
3
3
  * Get the POSIX-style environment variables.
4
- *
4
+ *
5
5
  * Each environment variable is provided as a pair of string variable names
6
6
  * and string value.
7
- *
7
+ *
8
8
  * Morally, these are a value import, but until value imports are available
9
9
  * in the component model, this import function should return the same
10
10
  * values each time it is called.
@@ -1,6 +1,17 @@
1
- /** @module Interface wasi:cli/exit@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/exit@0.3.0 **/
2
2
  /**
3
3
  * Exit the current instance and any linked instances.
4
4
  */
5
5
  export function exit(status: Result<void, void>): void;
6
+ /**
7
+ * Exit the current instance and any linked instances, reporting the
8
+ * specified status code to the host.
9
+ *
10
+ * The meaning of the code depends on the context, with 0 usually meaning
11
+ * "success", and other values indicating various types of failure.
12
+ *
13
+ * This function does not return; the effect is analogous to a trap, but
14
+ * without the connotation that something bad has happened.
15
+ */
16
+ export function exitWithCode(statusCode: number): void;
6
17
  export type Result<T, E> = { tag: 'ok', val: T } | { tag: 'err', val: E };
@@ -1,4 +1,4 @@
1
- /** @module Interface wasi:cli/run@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/run@0.3.0 **/
2
2
  /**
3
3
  * Run the program.
4
4
  */
@@ -1,11 +1,11 @@
1
- /** @module Interface wasi:cli/stderr@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/stderr@0.3.0 **/
2
2
  /**
3
3
  * Write the given stream to stderr.
4
- *
4
+ *
5
5
  * If the stream's writable end is dropped this function will either return
6
6
  * success once the entire contents of the stream have been written or an
7
7
  * error-code representing a failure.
8
- *
8
+ *
9
9
  * Otherwise if there is an error the readable end of the stream will be
10
10
  * dropped and this function will return an error-code.
11
11
  */
@@ -1,16 +1,16 @@
1
- /** @module Interface wasi:cli/stdin@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/stdin@0.3.0 **/
2
2
  /**
3
3
  * Return a stream for reading from stdin.
4
- *
4
+ *
5
5
  * This function returns a stream which provides data read from stdin,
6
6
  * and a future to signal read results.
7
- *
7
+ *
8
8
  * If the stream's readable end is dropped the future will resolve to success.
9
- *
9
+ *
10
10
  * If the stream's writable end is dropped the future will either resolve to
11
11
  * success if stdin was closed by the writer or to an error-code if reading
12
12
  * failed for some other reason.
13
- *
13
+ *
14
14
  * Multiple streams may be active at the same time. The behavior of concurrent
15
15
  * reads is implementation-specific.
16
16
  */
@@ -1,11 +1,11 @@
1
- /** @module Interface wasi:cli/stdout@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/stdout@0.3.0 **/
2
2
  /**
3
3
  * Write the given stream to stdout.
4
- *
4
+ *
5
5
  * If the stream's writable end is dropped this function will either return
6
6
  * success once the entire contents of the stream have been written or an
7
7
  * error-code representing a failure.
8
- *
8
+ *
9
9
  * Otherwise if there is an error the readable end of the stream will be
10
10
  * dropped and this function will return an error-code.
11
11
  */
@@ -1,4 +1,4 @@
1
- /** @module Interface wasi:cli/terminal-input@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/terminal-input@0.3.0 **/
2
2
 
3
3
  export class TerminalInput {
4
4
  /**
@@ -1,4 +1,4 @@
1
- /** @module Interface wasi:cli/terminal-output@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/terminal-output@0.3.0 **/
2
2
 
3
3
  export class TerminalOutput {
4
4
  /**
@@ -1,4 +1,4 @@
1
- /** @module Interface wasi:cli/terminal-stderr@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/terminal-stderr@0.3.0 **/
2
2
  /**
3
3
  * If stderr is connected to a terminal, return a `terminal-output` handle
4
4
  * allowing further interaction with it.
@@ -1,4 +1,4 @@
1
- /** @module Interface wasi:cli/terminal-stdin@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/terminal-stdin@0.3.0 **/
2
2
  /**
3
3
  * If stdin is connected to a terminal, return a `terminal-input` handle
4
4
  * allowing further interaction with it.
@@ -1,4 +1,4 @@
1
- /** @module Interface wasi:cli/terminal-stdout@0.3.0-rc-2026-03-15 **/
1
+ /** @module Interface wasi:cli/terminal-stdout@0.3.0 **/
2
2
  /**
3
3
  * If stdout is connected to a terminal, return a `terminal-output` handle
4
4
  * allowing further interaction with it.