@bytecodealliance/preview3-shim 0.3.2 → 0.4.0

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.
@@ -255,7 +255,13 @@ export class TcpSocket {
255
255
  }
256
256
  let stream;
257
257
  try {
258
- stream = readableByteStreamFromReader(data, { name: "tcp send data" });
258
+ // A remote error arrives from the TCP worker through a MessagePort. Yield
259
+ // after each chunk so a guest producer that completes writes immediately
260
+ // cannot starve that error (and its stream cancellation) indefinitely.
261
+ stream = readableByteStreamFromReader(data, {
262
+ name: "tcp send data",
263
+ yieldAfterChunk: true,
264
+ });
259
265
  }
260
266
  catch (error) {
261
267
  if (error instanceof TypeError) {
@@ -1,5 +1,10 @@
1
1
  export declare const DEFAULT_BYTE_STREAM_CHUNK_SIZE: number;
2
2
  declare const symbolDispose: symbol;
3
+ interface ReadableByteStreamOptions {
4
+ chunkSize?: number;
5
+ name?: string;
6
+ yieldAfterChunk?: boolean;
7
+ }
3
8
  /**
4
9
  * Creates a transferable ReadableStream from an async iterator.
5
10
  *
@@ -22,9 +27,10 @@ export declare function readableStreamFromIterator(iterator: any, name?: string)
22
27
  * @param {object} [opts={}] - Optional adapter settings.
23
28
  * @param {number} [opts.chunkSize=DEFAULT_BYTE_STREAM_CHUNK_SIZE] - Maximum bytes to request per generated `read()` call.
24
29
  * @param {string} [opts.name="stream reader"] - Reader name used in error messages.
30
+ * @param {boolean} [opts.yieldAfterChunk=false] - Yield one event-loop turn after each chunk.
25
31
  * @returns {ReadableStream<Uint8Array>} A transferable stream of byte chunks.
26
32
  */
27
- export declare function readableByteStreamFromReader(reader: any, opts?: {}): ReadableStream<any>;
33
+ export declare function readableByteStreamFromReader(reader: any, opts?: ReadableByteStreamOptions): ReadableStream<any>;
28
34
  export declare function _byteChunk(value: any): any;
29
35
  /**
30
36
  * Creates a bidirectional stream with separate reader and writer interfaces.
@@ -46,6 +46,7 @@ export function readableStreamFromIterator(iterator, name = "iterator") {
46
46
  * @param {object} [opts={}] - Optional adapter settings.
47
47
  * @param {number} [opts.chunkSize=DEFAULT_BYTE_STREAM_CHUNK_SIZE] - Maximum bytes to request per generated `read()` call.
48
48
  * @param {string} [opts.name="stream reader"] - Reader name used in error messages.
49
+ * @param {boolean} [opts.yieldAfterChunk=false] - Yield one event-loop turn after each chunk.
49
50
  * @returns {ReadableStream<Uint8Array>} A transferable stream of byte chunks.
50
51
  */
51
52
  export function readableByteStreamFromReader(reader, opts = {}) {
@@ -58,6 +59,9 @@ export function readableByteStreamFromReader(reader, opts = {}) {
58
59
  return;
59
60
  }
60
61
  controller.enqueue(_byteChunk(value));
62
+ if (opts.yieldAfterChunk) {
63
+ await new Promise((resolve) => setTimeout(resolve, 0));
64
+ }
61
65
  },
62
66
  cancel(reason) {
63
67
  return source.cancel?.(reason);
@@ -3,7 +3,6 @@ import { Readable } from "stream";
3
3
  import { pipeline } from "stream/promises";
4
4
  import { once } from "node:events";
5
5
  import { isMainThread, parentPort } from "worker_threads";
6
- import process from "node:process";
7
6
  //#region dist/nodejs/workers/resource-worker.js
8
7
  function Router() {
9
8
  const _hooks = {
@@ -255,7 +254,6 @@ var SocketError = class SocketError extends Error {
255
254
  };
256
255
  //#endregion
257
256
  //#region dist/nodejs/workers/tcp-worker.js
258
- const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
259
257
  const sockets = /* @__PURE__ */ new Map();
260
258
  let NEXT_SOCKET_ID = 0n;
261
259
  Router().beforeAll((msg) => {
@@ -263,14 +261,15 @@ Router().beforeAll((msg) => {
263
261
  }).op("tcp-create", handleTcpCreate).op("tcp-bind", handleTcpBind).op("tcp-connect", handleTcpConnect).op("tcp-listen", handleTcpListen).op("tcp-send", handleTcpSend).op("tcp-receive", handleTcpReceive).op("tcp-get-local-address", handleGetLocalAddress).op("tcp-get-remote-address", handleGetRemoteAddress).op("tcp-set-listen-backlog-size", handleTcpSetBacklogSize).op("tcp-set-keep-alive", handleTcpSetKeepAlive).op("tcp-recv-buffer-size", handleRecvBufferSize).op("tcp-send-buffer-size", handleSendBufferSize).op("tcp-dispose", handleTcpDispose);
264
262
  function handleTcpCreate({ family }) {
265
263
  const socketId = NEXT_SOCKET_ID++;
266
- const handle = new TCP(TCPConstants.SOCKET);
267
264
  sockets.set(socketId, {
268
- handle,
269
265
  family,
270
266
  tcp: null,
271
267
  server: null,
268
+ acceptWriter: null,
272
269
  backlog: 128,
273
270
  localAddress: null,
271
+ keepAliveEnabled: false,
272
+ keepAliveIdleTime: 0,
274
273
  disposed: false,
275
274
  activeStreams: 0
276
275
  });
@@ -280,74 +279,104 @@ async function handleTcpBind({ socketId, localAddress }) {
280
279
  const socket = sockets.get(socketId);
281
280
  const address = serializeIpAddress(localAddress);
282
281
  const port = localAddress.val.port;
283
- const { handle, family } = socket;
282
+ const { family } = socket;
284
283
  if ([...sockets].some(([id, { localAddress: boundAddress }]) => id !== socketId && boundAddress && ipAddressConflict(boundAddress, localAddress))) {
285
284
  const err = /* @__PURE__ */ new Error("EADDRINUSE");
286
285
  err.code = "EADDRINUSE";
287
286
  throw err;
288
287
  }
289
- await new Promise((resolve, reject) => {
290
- let code;
291
- if (family === "ipv6") code = handle.bind6(address, port, TCPConstants.UV_TCP_IPV6ONLY);
292
- else code = handle.bind(address, port);
293
- if (code !== 0) reject(SocketError.from(-code));
294
- else resolve();
288
+ const server = socket.server = createTcpServer(socket);
289
+ const onListening = once(server, "listening");
290
+ server.listen({
291
+ host: address,
292
+ port,
293
+ backlog: socket.backlog,
294
+ ipv6Only: family === "ipv6"
295
295
  });
296
- const out = {};
297
- const code = handle.getsockname(out);
298
- if (code !== 0) throw SocketError.from(-code);
299
- socket.localAddress = makeIpAddress(out.family.toLowerCase(), out.address, out.port);
296
+ await onListening;
297
+ const boundAddress = server.address();
298
+ if (!boundAddress || typeof boundAddress === "string") throw new SocketError("invalid-state");
299
+ socket.localAddress = makeIpAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
300
300
  }
301
301
  async function handleTcpConnect({ socketId, remoteAddress }) {
302
302
  const socket = sockets.get(socketId);
303
303
  const host = serializeIpAddress(remoteAddress);
304
304
  const port = remoteAddress.val.port;
305
- const tcp = socket.tcp = new Socket({
306
- handle: socket.handle,
307
- pauseOnCreate: true,
308
- allowHalfOpen: true
309
- });
305
+ const localAddress = socket.localAddress;
306
+ await closeTcpServer(socket);
307
+ const tcp = socket.tcp = new Socket({ allowHalfOpen: true });
308
+ tcp.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
310
309
  const onConnect = once(tcp, "connect");
311
310
  tcp.connect({
312
311
  port,
313
- host
312
+ host,
313
+ localAddress: localAddress ? serializeIpAddress(localAddress) : void 0,
314
+ localPort: localAddress?.val.port
314
315
  });
315
316
  await onConnect;
316
317
  }
317
318
  async function handleTcpListen({ socketId, stream }) {
318
319
  const writer = stream.getWriter();
319
320
  const socket = sockets.get(socketId);
320
- const { handle, backlog, family } = socket;
321
+ const { backlog, family } = socket;
322
+ socket.acceptWriter = writer;
323
+ let server = socket.server;
324
+ if (!server) {
325
+ server = socket.server = createTcpServer(socket);
326
+ const onListening = once(server, "listening");
327
+ server.listen({
328
+ host: family === "ipv6" ? "::" : "0.0.0.0",
329
+ port: 0,
330
+ backlog,
331
+ ipv6Only: family === "ipv6"
332
+ });
333
+ await onListening;
334
+ }
335
+ const addr = server.address();
336
+ if (addr && typeof addr === "object") socket.localAddress = makeIpAddress(family, addr.address, addr.port);
337
+ }
338
+ function createTcpServer(socket) {
321
339
  const server = new Server({
322
340
  pauseOnConnect: true,
323
341
  allowHalfOpen: true
324
342
  });
325
- const onListening = once(server, "listening");
326
- server.listen(handle, backlog);
327
- await onListening;
328
- const addr = server.address();
329
- if (addr && typeof addr === "object") socket.localAddress = makeIpAddress(family, addr.address, addr.port);
330
343
  server.on("connection", (conn) => {
344
+ const writer = socket.acceptWriter;
345
+ if (!writer) {
346
+ conn.destroy();
347
+ return;
348
+ }
331
349
  conn.allowHalfOpen = true;
350
+ conn.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
332
351
  const id = NEXT_SOCKET_ID++;
333
352
  sockets.set(id, {
334
- handle: conn._handle,
335
- family,
336
- backlog,
353
+ family: socket.family,
354
+ backlog: socket.backlog,
337
355
  tcp: conn,
338
356
  server: null,
339
- localAddress: makeIpAddress(family, conn.localAddress, conn.localPort),
357
+ acceptWriter: null,
358
+ localAddress: makeIpAddress(socket.family, conn.localAddress, conn.localPort),
359
+ keepAliveEnabled: socket.keepAliveEnabled,
360
+ keepAliveIdleTime: socket.keepAliveIdleTime,
340
361
  disposed: false,
341
362
  activeStreams: 0
342
363
  });
343
364
  writer.write({
344
- family,
365
+ family: socket.family,
345
366
  socketId: id
346
367
  });
347
368
  });
348
- socket.server = server;
349
- server.on("error", (err) => stream.abort(err));
350
- server.on("close", () => writer.close());
369
+ server.on("error", (err) => socket.acceptWriter?.abort(err));
370
+ server.on("close", () => socket.acceptWriter?.close());
371
+ return server;
372
+ }
373
+ function closeTcpServer(socket) {
374
+ const server = socket.server;
375
+ socket.server = null;
376
+ if (!server) return Promise.resolve();
377
+ return new Promise((resolve, reject) => {
378
+ server.close((err) => err ? reject(err) : resolve());
379
+ });
351
380
  }
352
381
  async function handleTcpSend({ socketId, stream }) {
353
382
  const socket = sockets.get(socketId);
@@ -422,22 +451,19 @@ function cleanupDisposedSocket(socketId, socket) {
422
451
  if (!socket.disposed || socket.activeStreams > 0) return;
423
452
  if (socket.server) socket.server.close();
424
453
  if (socket.tcp) socket.tcp.destroy();
425
- if (socket.handle) socket.handle.close();
426
454
  sockets.delete(socketId);
427
455
  }
428
456
  async function handleGetLocalAddress({ socketId }) {
429
457
  const socket = sockets.get(socketId);
430
- const out = {};
431
- const code = socket.handle.getsockname(out);
432
- if (code !== 0) throw SocketError.from(-code);
433
- return makeIpAddress(out.family.toLowerCase(), out.address, out.port);
458
+ const address = socket.tcp?.address();
459
+ if (address && typeof address !== "string" && "family" in address) return makeIpAddress(address.family.toLowerCase(), address.address, address.port);
460
+ if (socket.localAddress) return socket.localAddress;
461
+ throw new SocketError("invalid-state");
434
462
  }
435
463
  async function handleGetRemoteAddress({ socketId }) {
436
464
  const socket = sockets.get(socketId);
437
- const out = {};
438
- const code = socket.handle.getpeername(out);
439
- if (code !== 0) throw SocketError.from(-code);
440
- return makeIpAddress(out.family.toLowerCase(), out.address, out.port);
465
+ if (!socket.tcp?.remoteFamily || !socket.tcp.remoteAddress || !socket.tcp.remotePort) throw new SocketError("invalid-state");
466
+ return makeIpAddress(socket.tcp.remoteFamily.toLowerCase(), socket.tcp.remoteAddress, socket.tcp.remotePort);
441
467
  }
442
468
  function handleTcpSetBacklogSize({ socketId, value }) {
443
469
  const socket = sockets.get(socketId);
@@ -445,9 +471,9 @@ function handleTcpSetBacklogSize({ socketId, value }) {
445
471
  }
446
472
  async function handleTcpSetKeepAlive({ socketId, keepAliveEnabled, keepAliveIdleTime }) {
447
473
  const socket = sockets.get(socketId);
448
- const time = Number(keepAliveIdleTime / 1000000000n);
449
- const code = socket.handle.setKeepAlive(keepAliveEnabled, time);
450
- if (code !== 0) throw SocketError.from(-code);
474
+ socket.keepAliveEnabled = keepAliveEnabled;
475
+ socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
476
+ socket.tcp?.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
451
477
  }
452
478
  async function handleRecvBufferSize({ socketId }) {
453
479
  const socket = sockets.get(socketId);
@@ -5,8 +5,6 @@ import { once } from "node:events";
5
5
  import { Router } from "../workers/resource-worker.js";
6
6
  import { serializeIpAddress, makeIpAddress, ipAddressConflict } from "../sockets/address.js";
7
7
  import { SocketError } from "../sockets/error.js";
8
- import process from "node:process";
9
- const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
10
8
  // Socket instances stored by ID
11
9
  const sockets = new Map();
12
10
  // Unique IDs for sockets
@@ -34,14 +32,15 @@ Router()
34
32
  // Create a new TCP socket
35
33
  function handleTcpCreate({ family }) {
36
34
  const socketId = NEXT_SOCKET_ID++;
37
- const handle = new TCP(TCPConstants.SOCKET);
38
35
  sockets.set(socketId, {
39
- handle,
40
36
  family,
41
37
  tcp: null,
42
38
  server: null,
39
+ acceptWriter: null,
43
40
  backlog: 128,
44
41
  localAddress: null,
42
+ keepAliveEnabled: false,
43
+ keepAliveIdleTime: 0,
45
44
  disposed: false,
46
45
  activeStreams: 0,
47
46
  });
@@ -52,48 +51,49 @@ async function handleTcpBind({ socketId, localAddress }) {
52
51
  const socket = sockets.get(socketId);
53
52
  const address = serializeIpAddress(localAddress);
54
53
  const port = localAddress.val.port;
55
- const { handle, family } = socket;
54
+ const { family } = socket;
56
55
  const hasConflict = [...sockets].some(([id, { localAddress: boundAddress }]) => id !== socketId && boundAddress && ipAddressConflict(boundAddress, localAddress));
57
56
  if (hasConflict) {
58
57
  const err = new Error("EADDRINUSE");
59
58
  err.code = "EADDRINUSE";
60
59
  throw err;
61
60
  }
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
- }
61
+ // node:net has no public standalone TCP bind API. Keep a server open
62
+ // to reserve the endpoint, then reuse it for listen or close it for connect.
63
+ const server = (socket.server = createTcpServer(socket));
64
+ const onListening = once(server, "listening");
65
+ server.listen({
66
+ host: address,
67
+ port,
68
+ backlog: socket.backlog,
69
+ ipv6Only: family === "ipv6",
76
70
  });
77
- const out = {};
78
- const code = handle.getsockname(out);
79
- if (code !== 0) {
80
- throw SocketError.from(-code);
71
+ await onListening;
72
+ const boundAddress = server.address();
73
+ if (!boundAddress || typeof boundAddress === "string") {
74
+ throw new SocketError("invalid-state");
81
75
  }
82
- socket.localAddress = makeIpAddress(out.family.toLowerCase(), out.address, out.port);
76
+ socket.localAddress = makeIpAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
83
77
  }
84
78
  // Connect a socket to remote address
85
79
  async function handleTcpConnect({ socketId, remoteAddress }) {
86
80
  const socket = sockets.get(socketId);
87
81
  const host = serializeIpAddress(remoteAddress);
88
82
  const port = remoteAddress.val.port;
83
+ const localAddress = socket.localAddress;
84
+ await closeTcpServer(socket);
89
85
  const tcp = (socket.tcp = new Socket({
90
- handle: socket.handle,
91
- pauseOnCreate: true,
92
86
  allowHalfOpen: true,
93
87
  }));
88
+ tcp.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
94
89
  // TODO(tandr): Add lookup
95
90
  const onConnect = once(tcp, "connect");
96
- tcp.connect({ port, host });
91
+ tcp.connect({
92
+ port,
93
+ host,
94
+ localAddress: localAddress ? serializeIpAddress(localAddress) : undefined,
95
+ localPort: localAddress?.val.port,
96
+ });
97
97
  // events.once rejects when the emitter produces an error while waiting and
98
98
  // removes its temporary listeners after settling. A separate error promise
99
99
  // would remain attached after a successful connect and could later reject
@@ -103,36 +103,68 @@ async function handleTcpConnect({ socketId, remoteAddress }) {
103
103
  async function handleTcpListen({ socketId, stream }) {
104
104
  const writer = stream.getWriter();
105
105
  const socket = sockets.get(socketId);
106
- const { handle, backlog, family } = socket;
107
- const server = new Server({
108
- pauseOnConnect: true,
109
- allowHalfOpen: true,
110
- });
111
- const onListening = once(server, "listening");
112
- server.listen(handle, backlog);
113
- await onListening;
106
+ const { backlog, family } = socket;
107
+ socket.acceptWriter = writer;
108
+ let server = socket.server;
109
+ if (!server) {
110
+ server = socket.server = createTcpServer(socket);
111
+ const onListening = once(server, "listening");
112
+ server.listen({
113
+ host: family === "ipv6" ? "::" : "0.0.0.0",
114
+ port: 0,
115
+ backlog,
116
+ ipv6Only: family === "ipv6",
117
+ });
118
+ await onListening;
119
+ }
114
120
  const addr = server.address();
115
121
  if (addr && typeof addr === "object") {
116
122
  socket.localAddress = makeIpAddress(family, addr.address, addr.port);
117
123
  }
124
+ }
125
+ function createTcpServer(socket) {
126
+ const server = new Server({
127
+ pauseOnConnect: true,
128
+ allowHalfOpen: true,
129
+ });
118
130
  server.on("connection", (conn) => {
131
+ const writer = socket.acceptWriter;
132
+ // The reservation is already listening at the OS level, so reject any
133
+ // connections that arrive before the WASI socket enters its listen state.
134
+ if (!writer) {
135
+ conn.destroy();
136
+ return;
137
+ }
119
138
  conn.allowHalfOpen = true;
139
+ conn.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
120
140
  const id = NEXT_SOCKET_ID++;
121
141
  sockets.set(id, {
122
- handle: conn._handle,
123
- family,
124
- backlog,
142
+ family: socket.family,
143
+ backlog: socket.backlog,
125
144
  tcp: conn,
126
145
  server: null,
127
- localAddress: makeIpAddress(family, conn.localAddress, conn.localPort),
146
+ acceptWriter: null,
147
+ localAddress: makeIpAddress(socket.family, conn.localAddress, conn.localPort),
148
+ keepAliveEnabled: socket.keepAliveEnabled,
149
+ keepAliveIdleTime: socket.keepAliveIdleTime,
128
150
  disposed: false,
129
151
  activeStreams: 0,
130
152
  });
131
- writer.write({ family, socketId: id });
153
+ writer.write({ family: socket.family, socketId: id });
154
+ });
155
+ server.on("error", (err) => socket.acceptWriter?.abort(err));
156
+ server.on("close", () => socket.acceptWriter?.close());
157
+ return server;
158
+ }
159
+ function closeTcpServer(socket) {
160
+ const server = socket.server;
161
+ socket.server = null;
162
+ if (!server) {
163
+ return Promise.resolve();
164
+ }
165
+ return new Promise((resolve, reject) => {
166
+ server.close((err) => (err ? reject(err) : resolve()));
132
167
  });
133
- socket.server = server;
134
- server.on("error", (err) => stream.abort(err));
135
- server.on("close", () => writer.close());
136
168
  }
137
169
  async function handleTcpSend({ socketId, stream }) {
138
170
  const socket = sockets.get(socketId);
@@ -229,28 +261,25 @@ function cleanupDisposedSocket(socketId, socket) {
229
261
  if (socket.tcp) {
230
262
  socket.tcp.destroy();
231
263
  }
232
- if (socket.handle) {
233
- socket.handle.close();
234
- }
235
264
  sockets.delete(socketId);
236
265
  }
237
266
  async function handleGetLocalAddress({ socketId }) {
238
267
  const socket = sockets.get(socketId);
239
- const out = {};
240
- const code = socket.handle.getsockname(out);
241
- if (code !== 0) {
242
- throw SocketError.from(-code);
268
+ const address = socket.tcp?.address();
269
+ if (address && typeof address !== "string" && "family" in address) {
270
+ return makeIpAddress(address.family.toLowerCase(), address.address, address.port);
271
+ }
272
+ if (socket.localAddress) {
273
+ return socket.localAddress;
243
274
  }
244
- return makeIpAddress(out.family.toLowerCase(), out.address, out.port);
275
+ throw new SocketError("invalid-state");
245
276
  }
246
277
  async function handleGetRemoteAddress({ socketId }) {
247
278
  const socket = sockets.get(socketId);
248
- const out = {};
249
- const code = socket.handle.getpeername(out);
250
- if (code !== 0) {
251
- throw SocketError.from(-code);
279
+ if (!socket.tcp?.remoteFamily || !socket.tcp.remoteAddress || !socket.tcp.remotePort) {
280
+ throw new SocketError("invalid-state");
252
281
  }
253
- return makeIpAddress(out.family.toLowerCase(), out.address, out.port);
282
+ return makeIpAddress(socket.tcp.remoteFamily.toLowerCase(), socket.tcp.remoteAddress, socket.tcp.remotePort);
254
283
  }
255
284
  function handleTcpSetBacklogSize({ socketId, value }) {
256
285
  const socket = sockets.get(socketId);
@@ -258,11 +287,9 @@ function handleTcpSetBacklogSize({ socketId, value }) {
258
287
  }
259
288
  async function handleTcpSetKeepAlive({ socketId, keepAliveEnabled, keepAliveIdleTime }) {
260
289
  const socket = sockets.get(socketId);
261
- const time = Number(keepAliveIdleTime / 1000000000n);
262
- const code = socket.handle.setKeepAlive(keepAliveEnabled, time);
263
- if (code !== 0) {
264
- throw SocketError.from(-code);
265
- }
290
+ socket.keepAliveEnabled = keepAliveEnabled;
291
+ socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
292
+ socket.tcp?.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
266
293
  }
267
294
  async function handleRecvBufferSize({ socketId }) {
268
295
  const socket = sockets.get(socketId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytecodealliance/preview3-shim",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
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": {
@@ -48,7 +48,7 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@bytecodealliance/preview2-shim": "^0.19.0"
51
+ "@bytecodealliance/preview2-shim": "^0.21.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/node": "^24.12.4",