@bytecodealliance/preview2-shim 0.20.2 → 0.22.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.
@@ -31,6 +31,7 @@ export declare const OUTPUT_STREAM_DISPOSE: number;
31
31
  export declare const OUTPUT_STREAM_GET_TOTAL_BYTES: number;
32
32
  export declare const FILESYSTEM_DESCRIPTOR_OPEN: number;
33
33
  export declare const FILESYSTEM_DESCRIPTOR_CLOSE: number;
34
+ export declare const FILESYSTEM_DESCRIPTOR_ADVISE: number;
34
35
  export declare const FILESYSTEM_DESCRIPTOR_SYNC_DATA: number;
35
36
  export declare const FILESYSTEM_DESCRIPTOR_GET_TYPE: number;
36
37
  export declare const FILESYSTEM_DESCRIPTOR_SET_SIZE: number;
package/dist/io/calls.js CHANGED
@@ -38,6 +38,7 @@ export const OUTPUT_STREAM_GET_TOTAL_BYTES = ++call_id << CALL_SHIFT;
38
38
  // Filesystem descriptors
39
39
  export const FILESYSTEM_DESCRIPTOR_OPEN = ++call_id << CALL_SHIFT;
40
40
  export const FILESYSTEM_DESCRIPTOR_CLOSE = ++call_id << CALL_SHIFT;
41
+ export const FILESYSTEM_DESCRIPTOR_ADVISE = ++call_id << CALL_SHIFT;
41
42
  export const FILESYSTEM_DESCRIPTOR_SYNC_DATA = ++call_id << CALL_SHIFT;
42
43
  export const FILESYSTEM_DESCRIPTOR_GET_TYPE = ++call_id << CALL_SHIFT;
43
44
  export const FILESYSTEM_DESCRIPTOR_SET_SIZE = ++call_id << CALL_SHIFT;
@@ -1,3 +1,4 @@
1
+ import type { Advice } from "@bytecodealliance/jco-node-fs";
1
2
  export declare function openFileResource(path: string, flags: number): Promise<{
2
3
  id: number;
3
4
  type: any;
@@ -5,6 +6,7 @@ export declare function openFileResource(path: string, flags: number): Promise<{
5
6
  export declare function retainFileResource(id: number): number;
6
7
  export declare function releaseFileResource(id: number): Promise<void>;
7
8
  export declare function fileResourceFd(id: number): number;
9
+ export declare function adviseFileResource(id: number, offset: bigint, length: bigint, advice: Advice): void;
8
10
  export declare function syncFileResourceData(id: number): Promise<void>;
9
11
  export declare function getFileResourceType(id: number): Promise<"block-device" | "character-device" | "directory" | "fifo" | "regular-file" | "socket" | "symbolic-link" | "unknown">;
10
12
  export declare function setFileResourceSize(id: number, size: number): Promise<void>;
@@ -1,5 +1,8 @@
1
1
  import fs from "node:fs";
2
+ import { createRequire } from "node:module";
2
3
  import { promisify } from "node:util";
4
+ const require = createRequire(import.meta.url);
5
+ let nativeFadvise;
3
6
  const closeAsync = promisify(fs.close);
4
7
  const fdatasyncAsync = promisify(fs.fdatasync);
5
8
  const fstatAsync = promisify(fs.fstat);
@@ -72,6 +75,10 @@ export async function releaseFileResource(id) {
72
75
  export function fileResourceFd(id) {
73
76
  return getFileResource(id).fd;
74
77
  }
78
+ export function adviseFileResource(id, offset, length, advice) {
79
+ const fadvise = (nativeFadvise ??= require("@bytecodealliance/jco-node-fs").fadvise);
80
+ fadvise(fileResourceFd(id), offset, length, advice);
81
+ }
75
82
  export async function syncFileResourceData(id) {
76
83
  await fdatasyncAsync(fileResourceFd(id));
77
84
  }
@@ -1,5 +1,5 @@
1
1
  import { PollState } from "./worker-thread.js";
2
- import { Socket as TcpSocket } from "node:net";
2
+ import { Server, Socket as TcpSocket } from "node:net";
3
3
  import { IpSocketAddress } from "../../types/interfaces/wasi-sockets-network.js";
4
4
  interface PendingAccept {
5
5
  tcpSocket: TcpSocket | null;
@@ -10,8 +10,11 @@ interface TcpSocketRecord {
10
10
  state: number;
11
11
  future: number | null;
12
12
  tcpSocket: TcpSocket | null;
13
+ server: Server | null;
14
+ localAddress: IpSocketAddress | null;
13
15
  listenBacklogSize: number;
14
- handle: TCP;
16
+ keepAlive: boolean;
17
+ keepAliveIdleTime: number;
15
18
  pendingAccepts: PendingAccept[];
16
19
  pollState: PollState | null;
17
20
  }
@@ -1,20 +1,20 @@
1
1
  import { createFuture, createReadableStream, createReadableStreamPollState, createWritableStream, futureDispose, futureTakeValue, pollStateReady, verifyPollsDroppedForDrop, } from "./worker-thread.js";
2
2
  import process from "node:process";
3
- // TODO: tcp_wrap is no longer exposed in newer versions of Node.js
4
- const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
5
- import { convertSocketError, convertSocketErrorCode, ipSocketAddress, isIPv4MappedAddress, isMulticastIpAddress, isUnicastIpAddress, isWildcardAddress, noLookup, serializeIpAddress, SOCKET_STATE_BIND, SOCKET_STATE_BOUND, SOCKET_STATE_CLOSED, SOCKET_STATE_CONNECT, SOCKET_STATE_CONNECTION, SOCKET_STATE_INIT, SOCKET_STATE_LISTEN, SOCKET_STATE_LISTENER, } from "./worker-sockets.js";
3
+ import { convertSocketError, ipSocketAddress, isIPv4MappedAddress, isMulticastIpAddress, isUnicastIpAddress, isWildcardAddress, noLookup, serializeIpAddress, SOCKET_STATE_BIND, SOCKET_STATE_BOUND, SOCKET_STATE_CLOSED, SOCKET_STATE_CONNECT, SOCKET_STATE_CONNECTION, SOCKET_STATE_INIT, SOCKET_STATE_LISTEN, SOCKET_STATE_LISTENER, } from "./worker-sockets.js";
6
4
  import { Server, Socket as TcpSocket } from "node:net";
7
5
  const win = process.platform === "win32";
8
6
  export const tcpSockets = new Map();
9
7
  let tcpSocketCnt = 0;
10
8
  export function createTcpSocket() {
11
- const handle = new TCP(TCPConstants.SOCKET);
12
9
  tcpSockets.set(++tcpSocketCnt, {
13
10
  state: SOCKET_STATE_INIT,
14
11
  future: null,
15
12
  tcpSocket: null,
13
+ server: null,
14
+ localAddress: null,
16
15
  listenBacklogSize: 128,
17
- handle,
16
+ keepAlive: false,
17
+ keepAliveIdleTime: 0,
18
18
  pendingAccepts: [],
19
19
  pollState: {
20
20
  ready: true,
@@ -44,7 +44,7 @@ export function socketTcpFinish(id, fromState, toState) {
44
44
  socket.state = toState;
45
45
  // for the listener, we must immediately transition back to unresolved
46
46
  if (toState === SOCKET_STATE_LISTENER) {
47
- socket.pollState.ready = false;
47
+ socket.pollState.ready = socket.pendingAccepts.length > 0;
48
48
  }
49
49
  return val;
50
50
  }
@@ -60,26 +60,77 @@ export function socketTcpBindStart(id, localAddress, family) {
60
60
  throw "invalid-argument";
61
61
  }
62
62
  socket.state = SOCKET_STATE_BIND;
63
- const { handle } = socket;
64
- socket.future = createFuture((async () => {
63
+ socket.future = createFuture(new Promise((resolve, reject) => {
65
64
  const address = serializeIpAddress(localAddress);
66
65
  const port = localAddress.val.port;
67
- const code = localAddress.tag === "ipv6"
68
- ? handle.bind6(address, port, TCPConstants.UV_TCP_IPV6ONLY)
69
- : handle.bind(address, port);
70
- if (code !== 0) {
71
- throw convertSocketErrorCode(-code);
66
+ // node:net has no public standalone TCP bind API. Keep a server open
67
+ // to reserve the endpoint, then reuse it for listen or close it for connect.
68
+ const server = (socket.server = createTcpServer(socket));
69
+ function handleErr(err) {
70
+ server.off("listening", handleListen);
71
+ reject(convertSocketError(err));
72
72
  }
73
- // This is a Node.js / libuv quirk to force the bind error to be thrown
74
- // (specifically address-in-use).
75
- {
76
- const out = {};
77
- const code = handle.getsockname(out);
78
- if (code !== 0) {
79
- throw convertSocketErrorCode(-code);
73
+ function handleListen() {
74
+ server.off("error", handleErr);
75
+ const boundAddress = server.address();
76
+ if (!boundAddress || typeof boundAddress === "string") {
77
+ reject("invalid-state");
78
+ return;
80
79
  }
80
+ socket.localAddress = ipSocketAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
81
+ resolve();
81
82
  }
82
- })(), socket.pollState);
83
+ server.once("listening", handleListen);
84
+ server.once("error", handleErr);
85
+ server.listen({
86
+ host: address ?? undefined,
87
+ port,
88
+ backlog: socket.listenBacklogSize,
89
+ ipv6Only: family === "ipv6",
90
+ });
91
+ }), socket.pollState);
92
+ }
93
+ function createTcpServer(socket) {
94
+ const server = new Server({
95
+ pauseOnConnect: true,
96
+ allowHalfOpen: true,
97
+ });
98
+ server.on("connection", (tcpSocket) => {
99
+ // The reservation is already listening at the OS level, so reject any
100
+ // connections that arrive before the WASI socket enters its listen state.
101
+ if (socket.state !== SOCKET_STATE_LISTEN && socket.state !== SOCKET_STATE_LISTENER) {
102
+ tcpSocket.destroy();
103
+ return;
104
+ }
105
+ pollStateReady(socket.pollState);
106
+ const pollState = createReadableStreamPollState(tcpSocket);
107
+ socket.pendingAccepts.push({
108
+ tcpSocket,
109
+ err: null,
110
+ pollState,
111
+ });
112
+ });
113
+ server.on("error", (err) => {
114
+ if (socket.state === SOCKET_STATE_LISTEN || socket.state === SOCKET_STATE_LISTENER) {
115
+ pollStateReady(socket.pollState);
116
+ socket.pendingAccepts.push({
117
+ tcpSocket: null,
118
+ err,
119
+ pollState: null,
120
+ });
121
+ }
122
+ });
123
+ return server;
124
+ }
125
+ function closeTcpServer(socket) {
126
+ const server = socket.server;
127
+ socket.server = null;
128
+ if (!server) {
129
+ return Promise.resolve();
130
+ }
131
+ return new Promise((resolve, reject) => {
132
+ server.close((err) => (err ? reject(err) : resolve()));
133
+ });
83
134
  }
84
135
  export function socketTcpConnectStart(id, remoteAddress, family) {
85
136
  const socket = tcpSockets.get(id);
@@ -95,71 +146,44 @@ export function socketTcpConnectStart(id, remoteAddress, family) {
95
146
  throw "invalid-argument";
96
147
  }
97
148
  socket.state = SOCKET_STATE_CONNECT;
98
- socket.future = createFuture(new Promise((resolve, reject) => {
149
+ socket.future = createFuture((async () => {
150
+ const localAddress = socket.localAddress;
151
+ await closeTcpServer(socket);
99
152
  const tcpSocket = (socket.tcpSocket = new TcpSocket({
100
- // @ts-expect-error Not present in type definition
101
- handle: socket.handle,
102
- pauseOnCreate: true,
103
153
  allowHalfOpen: true,
104
154
  }));
105
- function handleErr(err) {
106
- tcpSocket.off("connect", handleConnect);
107
- reject(convertSocketError(err));
108
- }
109
- function handleConnect() {
110
- tcpSocket.off("error", handleErr);
111
- resolve([createReadableStream(tcpSocket), createWritableStream(tcpSocket)]);
112
- }
113
- tcpSocket.once("connect", handleConnect);
114
- tcpSocket.once("error", handleErr);
115
- tcpSocket.connect({
116
- port: remoteAddress.val.port,
117
- host: serializeIpAddress(remoteAddress) ?? undefined,
118
- lookup: noLookup,
155
+ tcpSocket.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
156
+ await new Promise((resolve, reject) => {
157
+ function handleErr(err) {
158
+ tcpSocket.off("connect", handleConnect);
159
+ reject(convertSocketError(err));
160
+ }
161
+ function handleConnect() {
162
+ tcpSocket.off("error", handleErr);
163
+ resolve();
164
+ }
165
+ tcpSocket.once("connect", handleConnect);
166
+ tcpSocket.once("error", handleErr);
167
+ tcpSocket.connect({
168
+ port: remoteAddress.val.port,
169
+ host: serializeIpAddress(remoteAddress) ?? undefined,
170
+ lookup: noLookup,
171
+ localAddress: localAddress
172
+ ? (serializeIpAddress(localAddress) ?? undefined)
173
+ : undefined,
174
+ localPort: localAddress?.val.port,
175
+ });
119
176
  });
120
- }), socket.pollState);
177
+ return [createReadableStream(tcpSocket), createWritableStream(tcpSocket)];
178
+ })(), socket.pollState);
121
179
  }
122
180
  export function socketTcpListenStart(id) {
123
181
  const socket = tcpSockets.get(id);
124
182
  if (socket.state !== SOCKET_STATE_BOUND) {
125
183
  throw "invalid-state";
126
184
  }
127
- const { handle } = socket;
128
185
  socket.state = SOCKET_STATE_LISTEN;
129
- socket.future = createFuture(new Promise((resolve, reject) => {
130
- const server = new Server({
131
- pauseOnConnect: true,
132
- allowHalfOpen: true,
133
- });
134
- function handleErr(err) {
135
- server.off("listening", handleListen);
136
- reject(convertSocketError(err));
137
- }
138
- function handleListen() {
139
- server.off("error", handleErr);
140
- server.on("connection", (tcpSocket) => {
141
- pollStateReady(socket.pollState);
142
- const pollState = createReadableStreamPollState(tcpSocket);
143
- socket.pendingAccepts.push({
144
- tcpSocket,
145
- err: null,
146
- pollState,
147
- });
148
- });
149
- server.on("error", (err) => {
150
- pollStateReady(socket.pollState);
151
- socket.pendingAccepts.push({
152
- tcpSocket: null,
153
- err,
154
- pollState: null,
155
- });
156
- });
157
- resolve();
158
- }
159
- server.once("listening", handleListen);
160
- server.once("error", handleErr);
161
- server.listen(handle, socket.listenBacklogSize);
162
- }), socket.pollState);
186
+ socket.future = createFuture(Promise.resolve(), socket.pollState);
163
187
  }
164
188
  export function socketTcpAccept(id) {
165
189
  const socket = tcpSockets.get(id);
@@ -181,9 +205,11 @@ export function socketTcpAccept(id) {
181
205
  state: SOCKET_STATE_CONNECTION,
182
206
  future: null,
183
207
  tcpSocket: accept.tcpSocket,
208
+ server: null,
209
+ localAddress: null,
184
210
  listenBacklogSize: 128,
185
- // @ts-expect-error
186
- handle: accept.tcpSocket?._handle,
211
+ keepAlive: false,
212
+ keepAliveIdleTime: 0,
187
213
  pendingAccepts: [],
188
214
  pollState: accept.pollState,
189
215
  });
@@ -207,22 +233,22 @@ export function socketTcpSetListenBacklogSize(id, backlogSize) {
207
233
  socket.listenBacklogSize = Number(backlogSize);
208
234
  }
209
235
  export function socketTcpGetLocalAddress(id) {
210
- const { handle } = tcpSockets.get(id);
211
- const out = {};
212
- const code = handle.getsockname(out);
213
- if (code !== 0) {
214
- throw convertSocketErrorCode(-code);
236
+ const socket = tcpSockets.get(id);
237
+ const address = socket.tcpSocket?.address();
238
+ if (address && typeof address !== "string" && "family" in address) {
239
+ return ipSocketAddress(address.family.toLowerCase(), address.address, address.port);
240
+ }
241
+ if (socket.localAddress) {
242
+ return socket.localAddress;
215
243
  }
216
- return ipSocketAddress(out.family.toLowerCase(), out.address, out.port);
244
+ throw "invalid-state";
217
245
  }
218
246
  export function socketTcpGetRemoteAddress(id) {
219
- const { handle } = tcpSockets.get(id);
220
- const out = {};
221
- const code = handle.getpeername(out);
222
- if (code !== 0) {
223
- throw convertSocketErrorCode(-code);
247
+ const { tcpSocket } = tcpSockets.get(id);
248
+ if (!tcpSocket?.remoteFamily || !tcpSocket.remoteAddress || !tcpSocket.remotePort) {
249
+ throw "invalid-state";
224
250
  }
225
- return ipSocketAddress(out.family.toLowerCase(), out.address, out.port);
251
+ return ipSocketAddress(tcpSocket.remoteFamily.toLowerCase(), tcpSocket.remoteAddress, tcpSocket.remotePort);
226
252
  }
227
253
  export function socketTcpShutdown(id, _shutdownType) {
228
254
  const socket = tcpSockets.get(id);
@@ -237,15 +263,15 @@ export function socketTcpShutdown(id, _shutdownType) {
237
263
  }
238
264
  }
239
265
  export function socketTcpSetKeepAlive(id, { keepAlive, keepAliveIdleTime }) {
240
- const { handle } = tcpSockets.get(id);
241
- const code = handle.setKeepAlive(keepAlive, Number(keepAliveIdleTime / 1000000000n));
242
- if (code !== 0) {
243
- throw convertSocketErrorCode(-code);
244
- }
266
+ const socket = tcpSockets.get(id);
267
+ socket.keepAlive = keepAlive;
268
+ socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
269
+ socket.tcpSocket?.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
245
270
  }
246
271
  export function socketTcpDispose(id) {
247
272
  const socket = tcpSockets.get(id);
248
273
  verifyPollsDroppedForDrop(socket.pollState, "tcp socket");
249
- socket.handle.close();
274
+ socket.server?.close();
275
+ socket.tcpSocket?.destroy();
250
276
  tcpSockets.delete(id);
251
277
  }
@@ -1,3 +1,4 @@
1
+ import { createRequire } from "node:module";
1
2
  import fs, { createReadStream, createWriteStream, read } from "node:fs";
2
3
  import process, { hrtime, nextTick, stderr, stdout } from "node:process";
3
4
  import { PassThrough, Readable } from "node:stream";
@@ -79,6 +80,7 @@ var calls_exports = /* @__PURE__ */ __exportAll({
79
80
  CLOCKS_DURATION_SUBSCRIBE: () => CLOCKS_DURATION_SUBSCRIBE,
80
81
  CLOCKS_INSTANT_SUBSCRIBE: () => CLOCKS_INSTANT_SUBSCRIBE,
81
82
  FILE: () => FILE,
83
+ FILESYSTEM_DESCRIPTOR_ADVISE: () => FILESYSTEM_DESCRIPTOR_ADVISE,
82
84
  FILESYSTEM_DESCRIPTOR_CLOSE: () => FILESYSTEM_DESCRIPTOR_CLOSE,
83
85
  FILESYSTEM_DESCRIPTOR_GET_TYPE: () => FILESYSTEM_DESCRIPTOR_GET_TYPE,
84
86
  FILESYSTEM_DESCRIPTOR_METADATA_HASH: () => FILESYSTEM_DESCRIPTOR_METADATA_HASH,
@@ -209,6 +211,7 @@ const OUTPUT_STREAM_DISPOSE = ++call_id << 24;
209
211
  const OUTPUT_STREAM_GET_TOTAL_BYTES = ++call_id << 24;
210
212
  const FILESYSTEM_DESCRIPTOR_OPEN = ++call_id << 24;
211
213
  const FILESYSTEM_DESCRIPTOR_CLOSE = ++call_id << 24;
214
+ const FILESYSTEM_DESCRIPTOR_ADVISE = ++call_id << 24;
212
215
  const FILESYSTEM_DESCRIPTOR_SYNC_DATA = ++call_id << 24;
213
216
  const FILESYSTEM_DESCRIPTOR_GET_TYPE = ++call_id << 24;
214
217
  const FILESYSTEM_DESCRIPTOR_SET_SIZE = ++call_id << 24;
@@ -427,6 +430,8 @@ function getFirstError(e) {
427
430
  }
428
431
  //#endregion
429
432
  //#region dist/io/worker-filesystem.js
433
+ const require = createRequire(import.meta.url);
434
+ let nativeFadvise;
430
435
  const closeAsync = promisify(fs.close);
431
436
  const fdatasyncAsync = promisify(fs.fdatasync);
432
437
  const fstatAsync = promisify(fs.fstat);
@@ -490,6 +495,9 @@ async function releaseFileResource(id) {
490
495
  function fileResourceFd(id) {
491
496
  return getFileResource(id).fd;
492
497
  }
498
+ function adviseFileResource(id, offset, length, advice) {
499
+ (nativeFadvise ??= require("@bytecodealliance/jco-node-fs").fadvise)(fileResourceFd(id), offset, length, advice);
500
+ }
493
501
  async function syncFileResourceData(id) {
494
502
  await fdatasyncAsync(fileResourceFd(id));
495
503
  }
@@ -719,18 +727,19 @@ async function getDefaultReceiveBufferSize() {
719
727
  }
720
728
  //#endregion
721
729
  //#region dist/io/worker-socket-tcp.js
722
- const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
723
730
  const win = process.platform === "win32";
724
731
  const tcpSockets = /* @__PURE__ */ new Map();
725
732
  let tcpSocketCnt = 0;
726
733
  function createTcpSocket() {
727
- const handle = new TCP(TCPConstants.SOCKET);
728
734
  tcpSockets.set(++tcpSocketCnt, {
729
735
  state: SOCKET_STATE_INIT,
730
736
  future: null,
731
737
  tcpSocket: null,
738
+ server: null,
739
+ localAddress: null,
732
740
  listenBacklogSize: 128,
733
- handle,
741
+ keepAlive: false,
742
+ keepAliveIdleTime: 0,
734
743
  pendingAccepts: [],
735
744
  pollState: {
736
745
  ready: true,
@@ -753,7 +762,7 @@ function socketTcpFinish(id, fromState, toState) {
753
762
  throw val;
754
763
  } else {
755
764
  socket.state = toState;
756
- if (toState === SOCKET_STATE_LISTENER) socket.pollState.ready = false;
765
+ if (toState === SOCKET_STATE_LISTENER) socket.pollState.ready = socket.pendingAccepts.length > 0;
757
766
  return val;
758
767
  }
759
768
  }
@@ -762,85 +771,109 @@ function socketTcpBindStart(id, localAddress, family) {
762
771
  if (socket.state !== SOCKET_STATE_INIT) throw "invalid-state";
763
772
  if (family !== localAddress.tag || !isUnicastIpAddress(localAddress) || isIPv4MappedAddress(localAddress)) throw "invalid-argument";
764
773
  socket.state = SOCKET_STATE_BIND;
765
- const { handle } = socket;
766
- socket.future = createFuture((async () => {
774
+ socket.future = createFuture(new Promise((resolve, reject) => {
767
775
  const address = serializeIpAddress(localAddress);
768
776
  const port = localAddress.val.port;
769
- const code = localAddress.tag === "ipv6" ? handle.bind6(address, port, TCPConstants.UV_TCP_IPV6ONLY) : handle.bind(address, port);
770
- if (code !== 0) throw convertSocketErrorCode(-code);
771
- {
772
- const code = handle.getsockname({});
773
- if (code !== 0) throw convertSocketErrorCode(-code);
777
+ const server = socket.server = createTcpServer(socket);
778
+ function handleErr(err) {
779
+ server.off("listening", handleListen);
780
+ reject(convertSocketError(err));
774
781
  }
775
- })(), socket.pollState);
782
+ function handleListen() {
783
+ server.off("error", handleErr);
784
+ const boundAddress = server.address();
785
+ if (!boundAddress || typeof boundAddress === "string") {
786
+ reject("invalid-state");
787
+ return;
788
+ }
789
+ socket.localAddress = ipSocketAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
790
+ resolve();
791
+ }
792
+ server.once("listening", handleListen);
793
+ server.once("error", handleErr);
794
+ server.listen({
795
+ host: address ?? void 0,
796
+ port,
797
+ backlog: socket.listenBacklogSize,
798
+ ipv6Only: family === "ipv6"
799
+ });
800
+ }), socket.pollState);
801
+ }
802
+ function createTcpServer(socket) {
803
+ const server = new Server({
804
+ pauseOnConnect: true,
805
+ allowHalfOpen: true
806
+ });
807
+ server.on("connection", (tcpSocket) => {
808
+ if (socket.state !== SOCKET_STATE_LISTEN && socket.state !== SOCKET_STATE_LISTENER) {
809
+ tcpSocket.destroy();
810
+ return;
811
+ }
812
+ pollStateReady(socket.pollState);
813
+ const pollState = createReadableStreamPollState(tcpSocket);
814
+ socket.pendingAccepts.push({
815
+ tcpSocket,
816
+ err: null,
817
+ pollState
818
+ });
819
+ });
820
+ server.on("error", (err) => {
821
+ if (socket.state === SOCKET_STATE_LISTEN || socket.state === SOCKET_STATE_LISTENER) {
822
+ pollStateReady(socket.pollState);
823
+ socket.pendingAccepts.push({
824
+ tcpSocket: null,
825
+ err,
826
+ pollState: null
827
+ });
828
+ }
829
+ });
830
+ return server;
831
+ }
832
+ function closeTcpServer(socket) {
833
+ const server = socket.server;
834
+ socket.server = null;
835
+ if (!server) return Promise.resolve();
836
+ return new Promise((resolve, reject) => {
837
+ server.close((err) => err ? reject(err) : resolve());
838
+ });
776
839
  }
777
840
  function socketTcpConnectStart(id, remoteAddress, family) {
778
841
  const socket = tcpSockets.get(id);
779
842
  if (socket.state !== SOCKET_STATE_INIT && socket.state !== SOCKET_STATE_BOUND) throw "invalid-state";
780
843
  if (isWildcardAddress(remoteAddress) || family !== remoteAddress.tag || !isUnicastIpAddress(remoteAddress) || isMulticastIpAddress(remoteAddress) || remoteAddress.val.port === 0 || isIPv4MappedAddress(remoteAddress)) throw "invalid-argument";
781
844
  socket.state = SOCKET_STATE_CONNECT;
782
- socket.future = createFuture(new Promise((resolve, reject) => {
783
- const tcpSocket = socket.tcpSocket = new Socket({
784
- handle: socket.handle,
785
- pauseOnCreate: true,
786
- allowHalfOpen: true
787
- });
788
- function handleErr(err) {
789
- tcpSocket.off("connect", handleConnect);
790
- reject(convertSocketError(err));
791
- }
792
- function handleConnect() {
793
- tcpSocket.off("error", handleErr);
794
- resolve([createReadableStream(tcpSocket), createWritableStream(tcpSocket)]);
795
- }
796
- tcpSocket.once("connect", handleConnect);
797
- tcpSocket.once("error", handleErr);
798
- tcpSocket.connect({
799
- port: remoteAddress.val.port,
800
- host: serializeIpAddress(remoteAddress) ?? void 0,
801
- lookup: noLookup
845
+ socket.future = createFuture((async () => {
846
+ const localAddress = socket.localAddress;
847
+ await closeTcpServer(socket);
848
+ const tcpSocket = socket.tcpSocket = new Socket({ allowHalfOpen: true });
849
+ tcpSocket.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
850
+ await new Promise((resolve, reject) => {
851
+ function handleErr(err) {
852
+ tcpSocket.off("connect", handleConnect);
853
+ reject(convertSocketError(err));
854
+ }
855
+ function handleConnect() {
856
+ tcpSocket.off("error", handleErr);
857
+ resolve();
858
+ }
859
+ tcpSocket.once("connect", handleConnect);
860
+ tcpSocket.once("error", handleErr);
861
+ tcpSocket.connect({
862
+ port: remoteAddress.val.port,
863
+ host: serializeIpAddress(remoteAddress) ?? void 0,
864
+ lookup: noLookup,
865
+ localAddress: localAddress ? serializeIpAddress(localAddress) ?? void 0 : void 0,
866
+ localPort: localAddress?.val.port
867
+ });
802
868
  });
803
- }), socket.pollState);
869
+ return [createReadableStream(tcpSocket), createWritableStream(tcpSocket)];
870
+ })(), socket.pollState);
804
871
  }
805
872
  function socketTcpListenStart(id) {
806
873
  const socket = tcpSockets.get(id);
807
874
  if (socket.state !== SOCKET_STATE_BOUND) throw "invalid-state";
808
- const { handle } = socket;
809
875
  socket.state = SOCKET_STATE_LISTEN;
810
- socket.future = createFuture(new Promise((resolve, reject) => {
811
- const server = new Server({
812
- pauseOnConnect: true,
813
- allowHalfOpen: true
814
- });
815
- function handleErr(err) {
816
- server.off("listening", handleListen);
817
- reject(convertSocketError(err));
818
- }
819
- function handleListen() {
820
- server.off("error", handleErr);
821
- server.on("connection", (tcpSocket) => {
822
- pollStateReady(socket.pollState);
823
- const pollState = createReadableStreamPollState(tcpSocket);
824
- socket.pendingAccepts.push({
825
- tcpSocket,
826
- err: null,
827
- pollState
828
- });
829
- });
830
- server.on("error", (err) => {
831
- pollStateReady(socket.pollState);
832
- socket.pendingAccepts.push({
833
- tcpSocket: null,
834
- err,
835
- pollState: null
836
- });
837
- });
838
- resolve();
839
- }
840
- server.once("listening", handleListen);
841
- server.once("error", handleErr);
842
- server.listen(handle, socket.listenBacklogSize);
843
- }), socket.pollState);
876
+ socket.future = createFuture(Promise.resolve(), socket.pollState);
844
877
  }
845
878
  function socketTcpAccept(id) {
846
879
  const socket = tcpSockets.get(id);
@@ -856,8 +889,11 @@ function socketTcpAccept(id) {
856
889
  state: SOCKET_STATE_CONNECTION,
857
890
  future: null,
858
891
  tcpSocket: accept.tcpSocket,
892
+ server: null,
893
+ localAddress: null,
859
894
  listenBacklogSize: 128,
860
- handle: accept.tcpSocket?._handle,
895
+ keepAlive: false,
896
+ keepAliveIdleTime: 0,
861
897
  pendingAccepts: [],
862
898
  pollState: accept.pollState
863
899
  });
@@ -874,18 +910,16 @@ function socketTcpSetListenBacklogSize(id, backlogSize) {
874
910
  socket.listenBacklogSize = Number(backlogSize);
875
911
  }
876
912
  function socketTcpGetLocalAddress(id) {
877
- const { handle } = tcpSockets.get(id);
878
- const out = {};
879
- const code = handle.getsockname(out);
880
- if (code !== 0) throw convertSocketErrorCode(-code);
881
- return ipSocketAddress(out.family.toLowerCase(), out.address, out.port);
913
+ const socket = tcpSockets.get(id);
914
+ const address = socket.tcpSocket?.address();
915
+ if (address && typeof address !== "string" && "family" in address) return ipSocketAddress(address.family.toLowerCase(), address.address, address.port);
916
+ if (socket.localAddress) return socket.localAddress;
917
+ throw "invalid-state";
882
918
  }
883
919
  function socketTcpGetRemoteAddress(id) {
884
- const { handle } = tcpSockets.get(id);
885
- const out = {};
886
- const code = handle.getpeername(out);
887
- if (code !== 0) throw convertSocketErrorCode(-code);
888
- return ipSocketAddress(out.family.toLowerCase(), out.address, out.port);
920
+ const { tcpSocket } = tcpSockets.get(id);
921
+ if (!tcpSocket?.remoteFamily || !tcpSocket.remoteAddress || !tcpSocket.remotePort) throw "invalid-state";
922
+ return ipSocketAddress(tcpSocket.remoteFamily.toLowerCase(), tcpSocket.remoteAddress, tcpSocket.remotePort);
889
923
  }
890
924
  function socketTcpShutdown(id, _shutdownType) {
891
925
  const socket = tcpSockets.get(id);
@@ -894,14 +928,16 @@ function socketTcpShutdown(id, _shutdownType) {
894
928
  else socket.tcpSocket?.destroy();
895
929
  }
896
930
  function socketTcpSetKeepAlive(id, { keepAlive, keepAliveIdleTime }) {
897
- const { handle } = tcpSockets.get(id);
898
- const code = handle.setKeepAlive(keepAlive, Number(keepAliveIdleTime / 1000000000n));
899
- if (code !== 0) throw convertSocketErrorCode(-code);
931
+ const socket = tcpSockets.get(id);
932
+ socket.keepAlive = keepAlive;
933
+ socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
934
+ socket.tcpSocket?.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
900
935
  }
901
936
  function socketTcpDispose(id) {
902
937
  const socket = tcpSockets.get(id);
903
938
  verifyPollsDroppedForDrop(socket.pollState, "tcp socket");
904
- socket.handle.close();
939
+ socket.server?.close();
940
+ socket.tcpSocket?.destroy();
905
941
  tcpSockets.delete(id);
906
942
  }
907
943
  //#endregion
@@ -1556,6 +1592,7 @@ function handle(call, id, payload) {
1556
1592
  }
1557
1593
  case FILESYSTEM_DESCRIPTOR_OPEN: return openFileResource(payload.path, payload.flags);
1558
1594
  case FILESYSTEM_DESCRIPTOR_CLOSE: return releaseFileResource(id);
1595
+ case FILESYSTEM_DESCRIPTOR_ADVISE: return adviseFileResource(id, payload.offset, payload.length, payload.advice);
1559
1596
  case FILESYSTEM_DESCRIPTOR_SYNC_DATA: return syncFileResourceData(id);
1560
1597
  case FILESYSTEM_DESCRIPTOR_GET_TYPE: return getFileResourceType(id);
1561
1598
  case FILESYSTEM_DESCRIPTOR_SET_SIZE: return setFileResourceSize(id, payload);
@@ -6,8 +6,8 @@ import { clearOutgoingResponse, createHttpRequest, setOutgoingResponse, startHtt
6
6
  import { Readable } from "node:stream";
7
7
  import { read } from "node:fs";
8
8
  import { nextTick } from "node:process";
9
- import { CALL_MASK, CALL_TYPE_MASK, CLOCKS_DURATION_SUBSCRIBE, CLOCKS_INSTANT_SUBSCRIBE, FILE, FILESYSTEM_DESCRIPTOR_CLOSE, FILESYSTEM_DESCRIPTOR_GET_TYPE, FILESYSTEM_DESCRIPTOR_METADATA_HASH, FILESYSTEM_DESCRIPTOR_OPEN, FILESYSTEM_DESCRIPTOR_READ, FILESYSTEM_DESCRIPTOR_SET_SIZE, FILESYSTEM_DESCRIPTOR_SET_TIMES, FILESYSTEM_DESCRIPTOR_STAT, FILESYSTEM_DESCRIPTOR_SYNC, FILESYSTEM_DESCRIPTOR_SYNC_DATA, FILESYSTEM_DESCRIPTOR_WRITE, FUTURE_DISPOSE, FUTURE_SUBSCRIBE, FUTURE_TAKE_VALUE, HTTP, HTTP_CREATE_REQUEST, HTTP_OUTGOING_BODY_DISPOSE, HTTP_OUTPUT_STREAM_FINISH, HTTP_SERVER_CLEAR_OUTGOING_RESPONSE, HTTP_SERVER_SET_OUTGOING_RESPONSE, HTTP_SERVER_START, HTTP_SERVER_STOP, HTTP_SERVER_GET_ADDRESS, INPUT_STREAM_BLOCKING_READ, INPUT_STREAM_BLOCKING_SKIP, INPUT_STREAM_CREATE, INPUT_STREAM_DISPOSE, INPUT_STREAM_READ, INPUT_STREAM_SKIP, INPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_BLOCKING_FLUSH, OUTPUT_STREAM_BLOCKING_SPLICE, OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH, OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH, OUTPUT_STREAM_CHECK_WRITE, OUTPUT_STREAM_CREATE, OUTPUT_STREAM_DISPOSE, OUTPUT_STREAM_FLUSH, OUTPUT_STREAM_SPLICE, OUTPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_WRITE, OUTPUT_STREAM_WRITE_ZEROES, POLL_POLLABLE_BLOCK, POLL_POLLABLE_DISPOSE, POLL_POLLABLE_READY, POLL_POLL_LIST, SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST, SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST, SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST, SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST, SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE, SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE, SOCKET_TCP_ACCEPT, SOCKET_TCP_BIND_FINISH, SOCKET_TCP_BIND_START, SOCKET_TCP_CONNECT_FINISH, SOCKET_TCP_CONNECT_START, SOCKET_TCP_CREATE_HANDLE, SOCKET_TCP_DISPOSE, SOCKET_TCP_GET_LOCAL_ADDRESS, SOCKET_TCP_GET_REMOTE_ADDRESS, SOCKET_TCP_IS_LISTENING, SOCKET_TCP_LISTEN_FINISH, SOCKET_TCP_LISTEN_START, SOCKET_TCP_SET_KEEP_ALIVE, SOCKET_TCP_SET_LISTEN_BACKLOG_SIZE, SOCKET_TCP_SHUTDOWN, SOCKET_TCP_SUBSCRIBE, SOCKET_UDP_BIND_FINISH, SOCKET_UDP_BIND_START, SOCKET_UDP_CREATE_HANDLE, SOCKET_UDP_DISPOSE, SOCKET_UDP_GET_LOCAL_ADDRESS, SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE, SOCKET_UDP_GET_REMOTE_ADDRESS, SOCKET_UDP_GET_SEND_BUFFER_SIZE, SOCKET_UDP_GET_UNICAST_HOP_LIMIT, SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE, SOCKET_UDP_SET_SEND_BUFFER_SIZE, SOCKET_UDP_SET_UNICAST_HOP_LIMIT, SOCKET_UDP_STREAM, SOCKET_UDP_SUBSCRIBE, SOCKET_INCOMING_DATAGRAM_STREAM_RECEIVE, SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND, SOCKET_OUTGOING_DATAGRAM_STREAM_SEND, SOCKET_DATAGRAM_STREAM_SUBSCRIBE, SOCKET_DATAGRAM_STREAM_DISPOSE, STDERR, STDIN, STDOUT, reverseMap, } from "./calls.js";
10
- import { fileResourceFd, getFileResourceType, metadataHashFileResource, openFileResource, readFileResource, releaseFileResource, retainFileResource, setFileResourceSize, setFileResourceTimes, statFileResource, syncFileResource, syncFileResourceData, writeFileResource, } from "./worker-filesystem.js";
9
+ import { CALL_MASK, CALL_TYPE_MASK, CLOCKS_DURATION_SUBSCRIBE, CLOCKS_INSTANT_SUBSCRIBE, FILE, FILESYSTEM_DESCRIPTOR_ADVISE, FILESYSTEM_DESCRIPTOR_CLOSE, FILESYSTEM_DESCRIPTOR_GET_TYPE, FILESYSTEM_DESCRIPTOR_METADATA_HASH, FILESYSTEM_DESCRIPTOR_OPEN, FILESYSTEM_DESCRIPTOR_READ, FILESYSTEM_DESCRIPTOR_SET_SIZE, FILESYSTEM_DESCRIPTOR_SET_TIMES, FILESYSTEM_DESCRIPTOR_STAT, FILESYSTEM_DESCRIPTOR_SYNC, FILESYSTEM_DESCRIPTOR_SYNC_DATA, FILESYSTEM_DESCRIPTOR_WRITE, FUTURE_DISPOSE, FUTURE_SUBSCRIBE, FUTURE_TAKE_VALUE, HTTP, HTTP_CREATE_REQUEST, HTTP_OUTGOING_BODY_DISPOSE, HTTP_OUTPUT_STREAM_FINISH, HTTP_SERVER_CLEAR_OUTGOING_RESPONSE, HTTP_SERVER_SET_OUTGOING_RESPONSE, HTTP_SERVER_START, HTTP_SERVER_STOP, HTTP_SERVER_GET_ADDRESS, INPUT_STREAM_BLOCKING_READ, INPUT_STREAM_BLOCKING_SKIP, INPUT_STREAM_CREATE, INPUT_STREAM_DISPOSE, INPUT_STREAM_READ, INPUT_STREAM_SKIP, INPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_BLOCKING_FLUSH, OUTPUT_STREAM_BLOCKING_SPLICE, OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH, OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH, OUTPUT_STREAM_CHECK_WRITE, OUTPUT_STREAM_CREATE, OUTPUT_STREAM_DISPOSE, OUTPUT_STREAM_FLUSH, OUTPUT_STREAM_SPLICE, OUTPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_WRITE, OUTPUT_STREAM_WRITE_ZEROES, POLL_POLLABLE_BLOCK, POLL_POLLABLE_DISPOSE, POLL_POLLABLE_READY, POLL_POLL_LIST, SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST, SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST, SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST, SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST, SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE, SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE, SOCKET_TCP_ACCEPT, SOCKET_TCP_BIND_FINISH, SOCKET_TCP_BIND_START, SOCKET_TCP_CONNECT_FINISH, SOCKET_TCP_CONNECT_START, SOCKET_TCP_CREATE_HANDLE, SOCKET_TCP_DISPOSE, SOCKET_TCP_GET_LOCAL_ADDRESS, SOCKET_TCP_GET_REMOTE_ADDRESS, SOCKET_TCP_IS_LISTENING, SOCKET_TCP_LISTEN_FINISH, SOCKET_TCP_LISTEN_START, SOCKET_TCP_SET_KEEP_ALIVE, SOCKET_TCP_SET_LISTEN_BACKLOG_SIZE, SOCKET_TCP_SHUTDOWN, SOCKET_TCP_SUBSCRIBE, SOCKET_UDP_BIND_FINISH, SOCKET_UDP_BIND_START, SOCKET_UDP_CREATE_HANDLE, SOCKET_UDP_DISPOSE, SOCKET_UDP_GET_LOCAL_ADDRESS, SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE, SOCKET_UDP_GET_REMOTE_ADDRESS, SOCKET_UDP_GET_SEND_BUFFER_SIZE, SOCKET_UDP_GET_UNICAST_HOP_LIMIT, SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE, SOCKET_UDP_SET_SEND_BUFFER_SIZE, SOCKET_UDP_SET_UNICAST_HOP_LIMIT, SOCKET_UDP_STREAM, SOCKET_UDP_SUBSCRIBE, SOCKET_INCOMING_DATAGRAM_STREAM_RECEIVE, SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND, SOCKET_OUTGOING_DATAGRAM_STREAM_SEND, SOCKET_DATAGRAM_STREAM_SUBSCRIBE, SOCKET_DATAGRAM_STREAM_DISPOSE, STDERR, STDIN, STDOUT, reverseMap, } from "./calls.js";
10
+ import { adviseFileResource, fileResourceFd, getFileResourceType, metadataHashFileResource, openFileResource, readFileResource, releaseFileResource, retainFileResource, setFileResourceSize, setFileResourceTimes, statFileResource, syncFileResource, syncFileResourceData, writeFileResource, } from "./worker-filesystem.js";
11
11
  import { SOCKET_STATE_BIND, SOCKET_STATE_BOUND, SOCKET_STATE_CONNECT, SOCKET_STATE_CONNECTION, SOCKET_STATE_LISTEN, SOCKET_STATE_LISTENER, socketResolveAddress, getDefaultSendBufferSize, getDefaultReceiveBufferSize, } from "./worker-sockets.js";
12
12
  import { createTcpSocket, socketTcpAccept, socketTcpBindStart, socketTcpConnectStart, socketTcpDispose, socketTcpFinish, socketTcpGetLocalAddress, socketTcpGetRemoteAddress, socketTcpListenStart, socketTcpSetKeepAlive, socketTcpSetListenBacklogSize, socketTcpShutdown, tcpSockets, } from "./worker-socket-tcp.js";
13
13
  import { createUdpSocket, datagramStreams, socketDatagramStreamDispose, socketIncomingDatagramStreamReceive, socketOutgoingDatagramStreamCheckSend, socketOutgoingDatagramStreamSend, socketUdpBindFinish, socketUdpBindStart, socketUdpDispose, socketUdpGetLocalAddress, socketUdpGetReceiveBufferSize, socketUdpGetRemoteAddress, socketUdpGetSendBufferSize, socketUdpGetUnicastHopLimit, socketUdpSetReceiveBufferSize, socketUdpSetSendBufferSize, socketUdpSetUnicastHopLimit, socketUdpStream, udpSockets, } from "./worker-socket-udp.js";
@@ -370,6 +370,8 @@ function handle(call, id, payload) {
370
370
  return openFileResource(payload.path, payload.flags);
371
371
  case FILESYSTEM_DESCRIPTOR_CLOSE:
372
372
  return releaseFileResource(id);
373
+ case FILESYSTEM_DESCRIPTOR_ADVISE:
374
+ return adviseFileResource(id, payload.offset, payload.length, payload.advice);
373
375
  case FILESYSTEM_DESCRIPTOR_SYNC_DATA:
374
376
  return syncFileResourceData(id);
375
377
  case FILESYSTEM_DESCRIPTOR_GET_TYPE:
@@ -13,7 +13,7 @@ declare class Descriptor implements IDescriptor {
13
13
  readViaStream(offset: any): any;
14
14
  writeViaStream(offset: any): any;
15
15
  appendViaStream(): any;
16
- advise(_offset: any, _length: any, _advice: any): void;
16
+ advise(offset: any, length: any, advice: any): void;
17
17
  syncData(): void;
18
18
  getFlags(): any;
19
19
  getType(): any;
@@ -1,5 +1,5 @@
1
1
  import { earlyDispose, inputStreamCreate, ioCall, outputStreamCreate, registerDispose, } from "../io/worker-io.js";
2
- import { FILE, FILESYSTEM_DESCRIPTOR_CLOSE, FILESYSTEM_DESCRIPTOR_GET_TYPE, FILESYSTEM_DESCRIPTOR_METADATA_HASH, FILESYSTEM_DESCRIPTOR_OPEN, FILESYSTEM_DESCRIPTOR_READ, FILESYSTEM_DESCRIPTOR_SET_SIZE, FILESYSTEM_DESCRIPTOR_SET_TIMES, FILESYSTEM_DESCRIPTOR_STAT, FILESYSTEM_DESCRIPTOR_SYNC, FILESYSTEM_DESCRIPTOR_SYNC_DATA, FILESYSTEM_DESCRIPTOR_WRITE, INPUT_STREAM_CREATE, OUTPUT_STREAM_CREATE, } from "../io/calls.js";
2
+ import { FILE, FILESYSTEM_DESCRIPTOR_ADVISE, FILESYSTEM_DESCRIPTOR_CLOSE, FILESYSTEM_DESCRIPTOR_GET_TYPE, FILESYSTEM_DESCRIPTOR_METADATA_HASH, FILESYSTEM_DESCRIPTOR_OPEN, FILESYSTEM_DESCRIPTOR_READ, FILESYSTEM_DESCRIPTOR_SET_SIZE, FILESYSTEM_DESCRIPTOR_SET_TIMES, FILESYSTEM_DESCRIPTOR_STAT, FILESYSTEM_DESCRIPTOR_SYNC, FILESYSTEM_DESCRIPTOR_SYNC_DATA, FILESYSTEM_DESCRIPTOR_WRITE, INPUT_STREAM_CREATE, OUTPUT_STREAM_CREATE, } from "../io/calls.js";
3
3
  import { constants, linkSync, lstatSync, lutimesSync, mkdirSync, opendirSync, readlinkSync, renameSync, rmdirSync, statSync, symlinkSync, unlinkSync, utimesSync, } from "node:fs";
4
4
  import { platform } from "node:process";
5
5
  const symbolDispose = Symbol.dispose || Symbol.for("dispose");
@@ -100,10 +100,20 @@ class Descriptor {
100
100
  appendViaStream() {
101
101
  return this.writeViaStream(this.stat().size);
102
102
  }
103
- advise(_offset, _length, _advice) {
103
+ advise(offset, length, advice) {
104
104
  if (this.getType() === "directory") {
105
105
  throw "bad-descriptor";
106
106
  }
107
+ try {
108
+ ioCall(FILESYSTEM_DESCRIPTOR_ADVISE, this.#fileResourceId, {
109
+ offset,
110
+ length,
111
+ advice,
112
+ });
113
+ }
114
+ catch (e) {
115
+ throw convertFsError(e);
116
+ }
107
117
  }
108
118
  syncData() {
109
119
  if (this.#hostPreopen) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytecodealliance/preview2-shim",
3
- "version": "0.20.2",
3
+ "version": "0.22.0",
4
4
  "description": "WASI Preview2 shim for JS environments",
5
5
  "homepage": "https://github.com/bytecodealliance/jco#readme",
6
6
  "bugs": {
@@ -56,6 +56,9 @@
56
56
  "types": "./types/interfaces/*.d.ts"
57
57
  }
58
58
  },
59
+ "dependencies": {
60
+ "@bytecodealliance/jco-node-fs": "0.2.0"
61
+ },
59
62
  "devDependencies": {
60
63
  "@bytecodealliance/componentize-js": "0.22.0",
61
64
  "@bytecodealliance/jco": "1.25.2",