@bytecodealliance/preview3-shim 0.6.1 → 0.8.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.
- package/README.md +9 -2
- package/dist/browser/cli/environment.d.ts +1 -0
- package/dist/browser/cli/environment.js +9 -4
- package/dist/browser/cli/exit.js +4 -3
- package/dist/browser/cli/run.d.ts +2 -0
- package/dist/browser/cli/run.js +11 -3
- package/dist/browser/cli/stderr.js +10 -3
- package/dist/browser/cli/stdin.js +3 -2
- package/dist/browser/cli/stdout.js +10 -3
- package/dist/browser/cli/terminal-stderr.js +2 -2
- package/dist/browser/cli/terminal-stdin.js +2 -2
- package/dist/browser/cli/terminal-stdout.js +2 -2
- package/dist/browser/cli.d.ts +3 -0
- package/dist/browser/cli.js +3 -0
- package/dist/browser/clocks/monotonic-clock.js +7 -7
- package/dist/browser/clocks/system-clock.js +4 -3
- package/dist/browser/filesystem/preopens.js +3 -2
- package/dist/browser/filesystem/types.d.ts +44 -1
- package/dist/browser/filesystem/types.js +139 -50
- package/dist/browser/filesystem.d.ts +13 -0
- package/dist/browser/filesystem.js +20 -0
- package/dist/browser/http/client.d.ts +2 -2
- package/dist/browser/http/client.js +138 -3
- package/dist/browser/http/handler.d.ts +4 -0
- package/dist/browser/http/handler.js +11 -3
- package/dist/browser/http/types.d.ts +44 -22
- package/dist/browser/http/types.js +192 -50
- package/dist/browser/http.d.ts +3 -0
- package/dist/browser/http.js +3 -0
- package/dist/browser/sockets/ip-name-lookup.js +34 -3
- package/dist/browser/sockets/types.d.ts +15 -0
- package/dist/browser/sockets/types.js +241 -49
- package/dist/browser/sockets.d.ts +2 -0
- package/dist/browser/sockets.js +2 -0
- package/dist/browser/streams.d.ts +20 -0
- package/dist/browser/streams.js +106 -0
- package/dist/nodejs/filesystem/descriptor.js +14 -17
- package/package.json +3 -3
- package/dist/common/errors.d.ts +0 -3
- package/dist/common/errors.js +0 -5
|
@@ -1,126 +1,318 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as preview2Sockets from "@bytecodealliance/preview2-shim/sockets";
|
|
2
|
+
import { readableFromPreview2Input, writeToPreview2Output } from "../streams.js";
|
|
3
|
+
let tcpProvider = preview2Sockets;
|
|
4
|
+
let udpProvider = preview2Sockets;
|
|
5
|
+
class SocketError extends Error {
|
|
6
|
+
payload;
|
|
7
|
+
constructor(payload) {
|
|
8
|
+
super(payload.tag);
|
|
9
|
+
this.name = "SocketError";
|
|
10
|
+
this.payload = payload;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function socketErrorCode(error) {
|
|
14
|
+
let tag = typeof error === "string" ? error : error?.tag;
|
|
15
|
+
if (!tag && error instanceof Error) {
|
|
16
|
+
tag = error.message;
|
|
17
|
+
}
|
|
18
|
+
if (tag === "would-block" || tag === "not-in-progress") {
|
|
19
|
+
return { tag: "invalid-state" };
|
|
20
|
+
}
|
|
21
|
+
if (tag) {
|
|
22
|
+
return { tag };
|
|
23
|
+
}
|
|
24
|
+
return { tag: "other", val: String(error) };
|
|
25
|
+
}
|
|
26
|
+
function socketError(error) {
|
|
27
|
+
throw error instanceof SocketError ? error : new SocketError(socketErrorCode(error));
|
|
28
|
+
}
|
|
29
|
+
function socketCall(operation) {
|
|
30
|
+
try {
|
|
31
|
+
return operation();
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
socketError(error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function _setTcpProvider(provider) {
|
|
38
|
+
const previous = tcpProvider;
|
|
39
|
+
tcpProvider = provider;
|
|
40
|
+
return previous;
|
|
41
|
+
}
|
|
42
|
+
export function _setUdpProvider(provider) {
|
|
43
|
+
const previous = udpProvider;
|
|
44
|
+
udpProvider = provider;
|
|
45
|
+
return previous;
|
|
46
|
+
}
|
|
2
47
|
export class TcpSocket {
|
|
48
|
+
#socket;
|
|
49
|
+
#input;
|
|
50
|
+
#output;
|
|
3
51
|
static create(addressFamily) {
|
|
4
|
-
|
|
52
|
+
try {
|
|
53
|
+
return TcpSocket._wrap(tcpProvider.tcpCreateSocket.createTcpSocket(addressFamily));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
socketError(error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
static _wrap(socket, input, output) {
|
|
60
|
+
const wrapped = new TcpSocket();
|
|
61
|
+
wrapped.#socket = socket;
|
|
62
|
+
wrapped.#input = input;
|
|
63
|
+
wrapped.#output = output;
|
|
64
|
+
return wrapped;
|
|
5
65
|
}
|
|
6
66
|
bind(localAddress) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
67
|
+
try {
|
|
68
|
+
this.#socket.startBind({}, localAddress);
|
|
69
|
+
this.#socket.finishBind();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
socketError(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async connect(remoteAddress) {
|
|
76
|
+
try {
|
|
77
|
+
this.#socket.startConnect({}, remoteAddress);
|
|
78
|
+
const connected = this.#socket.finishConnect();
|
|
79
|
+
if (Array.isArray(connected)) {
|
|
80
|
+
[this.#input, this.#output] = connected;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
socketError(error);
|
|
85
|
+
}
|
|
11
86
|
}
|
|
12
87
|
listen() {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
88
|
+
try {
|
|
89
|
+
this.#socket.startListen();
|
|
90
|
+
this.#socket.finishListen();
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
socketError(error);
|
|
94
|
+
}
|
|
95
|
+
return new ReadableStream({
|
|
96
|
+
pull: async (controller) => {
|
|
97
|
+
while (true) {
|
|
98
|
+
try {
|
|
99
|
+
const [socket, input, output] = this.#socket.accept();
|
|
100
|
+
controller.enqueue(TcpSocket._wrap(socket, input, output));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error === "would-block") {
|
|
105
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
controller.error(new SocketError(socketErrorCode(error)));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async send(data) {
|
|
116
|
+
if (!this.#output) {
|
|
117
|
+
return { tag: "err", val: { tag: "invalid-state" } };
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
await writeToPreview2Output(data, this.#output);
|
|
121
|
+
return { tag: "ok", val: undefined };
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return { tag: "err", val: socketErrorCode(error) };
|
|
125
|
+
}
|
|
17
126
|
}
|
|
18
127
|
receive() {
|
|
19
|
-
|
|
128
|
+
if (!this.#input) {
|
|
129
|
+
const error = { tag: "invalid-state" };
|
|
130
|
+
const stream = new ReadableStream({
|
|
131
|
+
start(controller) {
|
|
132
|
+
controller.error(new SocketError(error));
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
return [stream, Promise.resolve({ tag: "err", val: error })];
|
|
136
|
+
}
|
|
137
|
+
return readableFromPreview2Input(this.#input, socketErrorCode);
|
|
20
138
|
}
|
|
21
139
|
getLocalAddress() {
|
|
22
|
-
|
|
140
|
+
try {
|
|
141
|
+
return this.#socket.localAddress();
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
socketError(error);
|
|
145
|
+
}
|
|
23
146
|
}
|
|
24
147
|
getRemoteAddress() {
|
|
25
|
-
|
|
148
|
+
try {
|
|
149
|
+
return this.#socket.remoteAddress();
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
socketError(error);
|
|
153
|
+
}
|
|
26
154
|
}
|
|
27
155
|
getIsListening() {
|
|
28
|
-
|
|
156
|
+
return socketCall(() => this.#socket.isListening());
|
|
29
157
|
}
|
|
30
158
|
getAddressFamily() {
|
|
31
|
-
|
|
159
|
+
return socketCall(() => this.#socket.addressFamily());
|
|
32
160
|
}
|
|
33
161
|
setListenBacklogSize(value) {
|
|
34
|
-
|
|
162
|
+
socketCall(() => this.#socket.setListenBacklogSize(value));
|
|
35
163
|
}
|
|
36
164
|
getKeepAliveEnabled() {
|
|
37
|
-
|
|
165
|
+
return socketCall(() => this.#socket.keepAliveEnabled());
|
|
38
166
|
}
|
|
39
167
|
setKeepAliveEnabled(value) {
|
|
40
|
-
|
|
168
|
+
socketCall(() => this.#socket.setKeepAliveEnabled(value));
|
|
41
169
|
}
|
|
42
170
|
getKeepAliveIdleTime() {
|
|
43
|
-
|
|
171
|
+
return socketCall(() => this.#socket.keepAliveIdleTime());
|
|
44
172
|
}
|
|
45
173
|
setKeepAliveIdleTime(value) {
|
|
46
|
-
|
|
174
|
+
socketCall(() => this.#socket.setKeepAliveIdleTime(value));
|
|
47
175
|
}
|
|
48
176
|
getKeepAliveInterval() {
|
|
49
|
-
|
|
177
|
+
return socketCall(() => this.#socket.keepAliveInterval());
|
|
50
178
|
}
|
|
51
179
|
setKeepAliveInterval(value) {
|
|
52
|
-
|
|
180
|
+
socketCall(() => this.#socket.setKeepAliveInterval(value));
|
|
53
181
|
}
|
|
54
182
|
getKeepAliveCount() {
|
|
55
|
-
|
|
183
|
+
return socketCall(() => this.#socket.keepAliveCount());
|
|
56
184
|
}
|
|
57
185
|
setKeepAliveCount(value) {
|
|
58
|
-
|
|
186
|
+
socketCall(() => this.#socket.setKeepAliveCount(value));
|
|
59
187
|
}
|
|
60
188
|
getHopLimit() {
|
|
61
|
-
|
|
189
|
+
return socketCall(() => this.#socket.hopLimit());
|
|
62
190
|
}
|
|
63
191
|
setHopLimit(value) {
|
|
64
|
-
|
|
192
|
+
socketCall(() => this.#socket.setHopLimit(value));
|
|
65
193
|
}
|
|
66
194
|
getReceiveBufferSize() {
|
|
67
|
-
|
|
195
|
+
return socketCall(() => this.#socket.receiveBufferSize());
|
|
68
196
|
}
|
|
69
197
|
setReceiveBufferSize(value) {
|
|
70
|
-
|
|
198
|
+
socketCall(() => this.#socket.setReceiveBufferSize(value));
|
|
71
199
|
}
|
|
72
200
|
getSendBufferSize() {
|
|
73
|
-
|
|
201
|
+
return socketCall(() => this.#socket.sendBufferSize());
|
|
74
202
|
}
|
|
75
203
|
setSendBufferSize(value) {
|
|
76
|
-
|
|
204
|
+
socketCall(() => this.#socket.setSendBufferSize(value));
|
|
77
205
|
}
|
|
78
206
|
}
|
|
79
207
|
export class UdpSocket {
|
|
208
|
+
#socket;
|
|
209
|
+
#incoming;
|
|
210
|
+
#outgoing;
|
|
80
211
|
static create(addressFamily) {
|
|
81
|
-
|
|
212
|
+
try {
|
|
213
|
+
const wrapped = new UdpSocket();
|
|
214
|
+
wrapped.#socket = udpProvider.udpCreateSocket.createUdpSocket(addressFamily);
|
|
215
|
+
return wrapped;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
socketError(error);
|
|
219
|
+
}
|
|
82
220
|
}
|
|
83
221
|
bind(localAddress) {
|
|
84
|
-
|
|
222
|
+
try {
|
|
223
|
+
this.#socket.startBind({}, localAddress);
|
|
224
|
+
this.#socket.finishBind();
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
socketError(error);
|
|
228
|
+
}
|
|
85
229
|
}
|
|
86
230
|
connect(remoteAddress) {
|
|
87
|
-
|
|
231
|
+
try {
|
|
232
|
+
[this.#incoming, this.#outgoing] = this.#socket.stream(remoteAddress);
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
socketError(error);
|
|
236
|
+
}
|
|
88
237
|
}
|
|
89
238
|
disconnect() {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
239
|
+
try {
|
|
240
|
+
[this.#incoming, this.#outgoing] = this.#socket.stream(undefined);
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
socketError(error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async send(data, remoteAddress) {
|
|
247
|
+
try {
|
|
248
|
+
if (!this.#outgoing) {
|
|
249
|
+
[this.#incoming, this.#outgoing] = this.#socket.stream(remoteAddress);
|
|
250
|
+
}
|
|
251
|
+
this.#outgoing.checkSend();
|
|
252
|
+
this.#outgoing.send([{ data, remoteAddress }]);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
socketError(error);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async receive() {
|
|
259
|
+
if (!this.#incoming) {
|
|
260
|
+
try {
|
|
261
|
+
[this.#incoming, this.#outgoing] = this.#socket.stream(undefined);
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
socketError(error);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
while (true) {
|
|
268
|
+
try {
|
|
269
|
+
const [datagram] = this.#incoming.receive(1n);
|
|
270
|
+
if (datagram) {
|
|
271
|
+
return [datagram.data, datagram.remoteAddress];
|
|
272
|
+
}
|
|
273
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
socketError(error);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
97
279
|
}
|
|
98
280
|
getLocalAddress() {
|
|
99
|
-
|
|
281
|
+
try {
|
|
282
|
+
return this.#socket.localAddress();
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
socketError(error);
|
|
286
|
+
}
|
|
100
287
|
}
|
|
101
288
|
getRemoteAddress() {
|
|
102
|
-
|
|
289
|
+
try {
|
|
290
|
+
return this.#socket.remoteAddress();
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
socketError(error);
|
|
294
|
+
}
|
|
103
295
|
}
|
|
104
296
|
getAddressFamily() {
|
|
105
|
-
|
|
297
|
+
return socketCall(() => this.#socket.addressFamily());
|
|
106
298
|
}
|
|
107
299
|
getUnicastHopLimit() {
|
|
108
|
-
|
|
300
|
+
return socketCall(() => this.#socket.unicastHopLimit());
|
|
109
301
|
}
|
|
110
302
|
setUnicastHopLimit(value) {
|
|
111
|
-
|
|
303
|
+
socketCall(() => this.#socket.setUnicastHopLimit(value));
|
|
112
304
|
}
|
|
113
305
|
getReceiveBufferSize() {
|
|
114
|
-
|
|
306
|
+
return socketCall(() => this.#socket.receiveBufferSize());
|
|
115
307
|
}
|
|
116
308
|
setReceiveBufferSize(value) {
|
|
117
|
-
|
|
309
|
+
socketCall(() => this.#socket.setReceiveBufferSize(value));
|
|
118
310
|
}
|
|
119
311
|
getSendBufferSize() {
|
|
120
|
-
|
|
312
|
+
return socketCall(() => this.#socket.sendBufferSize());
|
|
121
313
|
}
|
|
122
314
|
setSendBufferSize(value) {
|
|
123
|
-
|
|
315
|
+
socketCall(() => this.#socket.setSendBufferSize(value));
|
|
124
316
|
}
|
|
125
317
|
}
|
|
126
318
|
export default {
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { default as ipNameLookup } from "./sockets/ip-name-lookup.js";
|
|
2
2
|
export { default as types } from "./sockets/types.js";
|
|
3
|
+
export { _setTcpProvider, _setUdpProvider } from "./sockets/types.js";
|
|
4
|
+
export { InMemoryTcpClient, InMemoryTcpSockets, InMemoryUdpClient, InMemoryUdpSockets, } from "@bytecodealliance/preview2-shim/sockets";
|
package/dist/browser/sockets.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { default as ipNameLookup } from "./sockets/ip-name-lookup.js";
|
|
2
2
|
export { default as types } from "./sockets/types.js";
|
|
3
|
+
export { _setTcpProvider, _setUdpProvider } from "./sockets/types.js";
|
|
4
|
+
export { InMemoryTcpClient, InMemoryTcpSockets, InMemoryUdpClient, InMemoryUdpSockets, } from "@bytecodealliance/preview2-shim/sockets";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface Preview2InputStream {
|
|
2
|
+
blockingRead(length: bigint): Uint8Array | Promise<Uint8Array>;
|
|
3
|
+
subscribe(): {
|
|
4
|
+
block(): void | Promise<void>;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
interface Preview2OutputStream {
|
|
8
|
+
blockingWriteAndFlush(contents: Uint8Array): void;
|
|
9
|
+
blockingFlush(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function writeToPreview2Output(data: ReadableStream<number>, output: Preview2OutputStream): Promise<void>;
|
|
12
|
+
export declare function readableFromPreview2Input<E>(input: Preview2InputStream, errorCode: (error: unknown) => E): [ReadableStream<number>, Promise<{
|
|
13
|
+
tag: "ok";
|
|
14
|
+
val: void;
|
|
15
|
+
} | {
|
|
16
|
+
tag: "err";
|
|
17
|
+
val: E;
|
|
18
|
+
}>];
|
|
19
|
+
export declare function preview2StreamErrorCode(error: unknown): "io" | "illegal-byte-sequence" | "pipe";
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const symbolDispose = Symbol.dispose ?? Symbol.for("dispose");
|
|
2
|
+
const BYTE_CHUNK_SIZE = 64 * 1024;
|
|
3
|
+
const P2_WRITE_CHUNK_SIZE = 4096;
|
|
4
|
+
function byteChunk(value) {
|
|
5
|
+
if (value instanceof Uint8Array) {
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
if (value instanceof ArrayBuffer) {
|
|
9
|
+
return new Uint8Array(value);
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return Uint8Array.from(value);
|
|
13
|
+
}
|
|
14
|
+
if (typeof value === "number") {
|
|
15
|
+
if (!Number.isInteger(value) || value < 0 || value > 255) {
|
|
16
|
+
throw new RangeError(`invalid byte stream value: ${value}`);
|
|
17
|
+
}
|
|
18
|
+
return Uint8Array.of(value);
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === "string") {
|
|
21
|
+
return new TextEncoder().encode(value);
|
|
22
|
+
}
|
|
23
|
+
throw new TypeError("byte stream values must be bytes or byte arrays");
|
|
24
|
+
}
|
|
25
|
+
function isIteratorResult(value) {
|
|
26
|
+
return value !== null && typeof value === "object" && "done" in value;
|
|
27
|
+
}
|
|
28
|
+
async function* byteChunks(data) {
|
|
29
|
+
const stream = data;
|
|
30
|
+
if (typeof stream.read === "function") {
|
|
31
|
+
while (true) {
|
|
32
|
+
const value = await stream.read({ count: BYTE_CHUNK_SIZE });
|
|
33
|
+
if (isIteratorResult(value)) {
|
|
34
|
+
if (value.done) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
yield byteChunk(value.value);
|
|
38
|
+
}
|
|
39
|
+
else if (value === null) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
yield byteChunk(value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (typeof stream[Symbol.asyncIterator] === "function") {
|
|
48
|
+
for await (const value of stream) {
|
|
49
|
+
yield byteChunk(value);
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
throw new TypeError("byte stream must provide read() or [Symbol.asyncIterator]()");
|
|
54
|
+
}
|
|
55
|
+
export async function writeToPreview2Output(data, output) {
|
|
56
|
+
for await (const bytes of byteChunks(data)) {
|
|
57
|
+
for (let offset = 0; offset < bytes.byteLength; offset += P2_WRITE_CHUNK_SIZE) {
|
|
58
|
+
output.blockingWriteAndFlush(bytes.subarray(offset, offset + P2_WRITE_CHUNK_SIZE));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
output.blockingFlush();
|
|
62
|
+
}
|
|
63
|
+
export function readableFromPreview2Input(input, errorCode) {
|
|
64
|
+
let resolveResult = () => { };
|
|
65
|
+
const result = new Promise((resolve) => (resolveResult = resolve));
|
|
66
|
+
let settled = false;
|
|
67
|
+
const settle = (value) => {
|
|
68
|
+
if (!settled) {
|
|
69
|
+
settled = true;
|
|
70
|
+
resolveResult(value);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const readable = new ReadableStream({
|
|
74
|
+
async pull(controller) {
|
|
75
|
+
try {
|
|
76
|
+
const bytes = await input.blockingRead(BigInt(BYTE_CHUNK_SIZE));
|
|
77
|
+
for (const byte of bytes) {
|
|
78
|
+
controller.enqueue(byte);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (error?.tag === "closed") {
|
|
83
|
+
controller.close();
|
|
84
|
+
settle({ tag: "ok", val: undefined });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
settle({ tag: "err", val: errorCode(error) });
|
|
88
|
+
controller.error(error);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
cancel() {
|
|
92
|
+
settle({ tag: "ok", val: undefined });
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
return [readable, result];
|
|
96
|
+
}
|
|
97
|
+
export function preview2StreamErrorCode(error) {
|
|
98
|
+
const code = error?.code;
|
|
99
|
+
if (code === "EPIPE" || code === "ERR_STREAM_PREMATURE_CLOSE") {
|
|
100
|
+
return "pipe";
|
|
101
|
+
}
|
|
102
|
+
if (code === "ERR_ENCODING_INVALID_ENCODED_DATA") {
|
|
103
|
+
return "illegal-byte-sequence";
|
|
104
|
+
}
|
|
105
|
+
return "io";
|
|
106
|
+
}
|
|
@@ -872,6 +872,15 @@ class Descriptor {
|
|
|
872
872
|
* Resolve a relative subpath against this descriptor.
|
|
873
873
|
*/
|
|
874
874
|
#getFullPath(subpath, _followSymlinks) {
|
|
875
|
+
const segments = this.#normalizeSubpath(subpath);
|
|
876
|
+
const base = this.#hostPreopen ?? this.#fullPath;
|
|
877
|
+
if (segments.length === 0) {
|
|
878
|
+
return base;
|
|
879
|
+
}
|
|
880
|
+
const baseNormalized = stripTrailingSlash(base);
|
|
881
|
+
return `${baseNormalized}/${segments.join("/")}`;
|
|
882
|
+
}
|
|
883
|
+
#normalizeSubpath(subpath) {
|
|
875
884
|
subpath = subpath.replaceAll("\\", "/").replace(/\/\/+/g, "/");
|
|
876
885
|
if (subpath === "") {
|
|
877
886
|
throw new FSError("no-entry");
|
|
@@ -894,12 +903,7 @@ class Descriptor {
|
|
|
894
903
|
segments.push(seg);
|
|
895
904
|
}
|
|
896
905
|
}
|
|
897
|
-
|
|
898
|
-
if (segments.length === 0) {
|
|
899
|
-
return base;
|
|
900
|
-
}
|
|
901
|
-
const baseNormalized = stripTrailingSlash(base);
|
|
902
|
-
return `${baseNormalized}/${segments.join("/")}`;
|
|
906
|
+
return segments;
|
|
903
907
|
}
|
|
904
908
|
async #statForIdentity() {
|
|
905
909
|
if (this.#hostPreopen) {
|
|
@@ -916,19 +920,12 @@ class Descriptor {
|
|
|
916
920
|
}
|
|
917
921
|
const base = this.#hostPreopen ?? this.#fullPath;
|
|
918
922
|
const baseResolved = nodePath.resolve(base);
|
|
919
|
-
|
|
923
|
+
// Validate the same normalized path that the filesystem operation will use.
|
|
924
|
+
// Otherwise an ENOENT segment followed by `..` can stop validation before a
|
|
925
|
+
// surviving symlink that #getFullPath later exposes.
|
|
926
|
+
const segments = this.#normalizeSubpath(subpath);
|
|
920
927
|
let current = baseResolved;
|
|
921
928
|
for (const seg of segments) {
|
|
922
|
-
if (seg === "" || seg === ".") {
|
|
923
|
-
continue;
|
|
924
|
-
}
|
|
925
|
-
if (seg === "..") {
|
|
926
|
-
current = nodePath.dirname(current);
|
|
927
|
-
if (!isWithinPath(baseResolved, current)) {
|
|
928
|
-
throw new FSError("not-permitted");
|
|
929
|
-
}
|
|
930
|
-
continue;
|
|
931
|
-
}
|
|
932
929
|
current = nodePath.join(current, seg);
|
|
933
930
|
let stat;
|
|
934
931
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytecodealliance/preview3-shim",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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": {
|
|
@@ -49,9 +49,10 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@bytecodealliance/jco-node-fs": "0.3.2",
|
|
52
|
-
"@bytecodealliance/preview2-shim": "^0.
|
|
52
|
+
"@bytecodealliance/preview2-shim": "^0.27.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
+
"@bytecodealliance/jco": "1.25.2",
|
|
55
56
|
"@types/node": "^24.12.4",
|
|
56
57
|
"globals": "^17.6.0",
|
|
57
58
|
"rolldown": "^1.2.4",
|
|
@@ -67,7 +68,6 @@
|
|
|
67
68
|
"fmt:check": "oxfmt --check",
|
|
68
69
|
"lint": "oxlint",
|
|
69
70
|
"lint:fix": "oxlint --fix",
|
|
70
|
-
"pretest": "pnpm run build",
|
|
71
71
|
"test": "vitest --run -c test/vitest.ts",
|
|
72
72
|
"prebench": "pnpm run build",
|
|
73
73
|
"bench": "vitest bench --run"
|
package/dist/common/errors.d.ts
DELETED