@bytecodealliance/preview2-shim 0.18.1 → 0.19.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/dist/browser/cli.d.ts +16 -0
- package/dist/browser/cli.js +109 -0
- package/dist/browser/clocks.d.ts +3 -0
- package/dist/browser/clocks.js +36 -0
- package/dist/browser/config.d.ts +2 -0
- package/{lib → dist}/browser/config.js +2 -4
- package/dist/browser/environment.d.ts +5 -0
- package/dist/browser/environment.js +24 -0
- package/dist/browser/filesystem.d.ts +132 -0
- package/dist/browser/filesystem.js +464 -0
- package/dist/browser/http.d.ts +4 -0
- package/dist/browser/http.js +643 -0
- package/dist/browser/index.d.ts +7 -0
- package/dist/browser/index.js +7 -0
- package/dist/browser/io.d.ts +58 -0
- package/dist/browser/io.js +205 -0
- package/dist/browser/random.d.ts +4 -0
- package/dist/browser/random.js +49 -0
- package/dist/browser/sockets.d.ts +8 -0
- package/dist/browser/sockets.js +66 -0
- package/dist/common/instantiation.d.ts +97 -0
- package/dist/common/instantiation.js +227 -0
- package/dist/io/calls.d.ts +91 -0
- package/{lib → dist}/io/calls.js +4 -19
- package/dist/io/worker-http.d.ts +17 -0
- package/dist/io/worker-http.js +192 -0
- package/dist/io/worker-io.d.ts +84 -0
- package/dist/io/worker-io.js +375 -0
- package/dist/io/worker-socket-tcp.d.ts +34 -0
- package/dist/io/worker-socket-tcp.js +251 -0
- package/dist/io/worker-socket-udp.d.ts +54 -0
- package/dist/io/worker-socket-udp.js +492 -0
- package/dist/io/worker-sockets.d.ts +32 -0
- package/dist/io/worker-sockets.js +265 -0
- package/dist/io/worker-thread.d.ts +73 -0
- package/dist/io/worker-thread.js +791 -0
- package/dist/nodejs/cli.d.ts +30 -0
- package/dist/nodejs/cli.js +86 -0
- package/dist/nodejs/clocks.d.ts +3 -0
- package/dist/nodejs/clocks.js +61 -0
- package/dist/nodejs/filesystem.d.ts +113 -0
- package/dist/nodejs/filesystem.js +758 -0
- package/dist/nodejs/http.d.ts +82 -0
- package/dist/nodejs/http.js +674 -0
- package/dist/nodejs/index.d.ts +7 -0
- package/dist/nodejs/index.js +7 -0
- package/dist/nodejs/io.d.ts +1 -0
- package/dist/nodejs/io.js +1 -0
- package/dist/nodejs/random.d.ts +4 -0
- package/dist/nodejs/random.js +42 -0
- package/dist/nodejs/sockets.d.ts +21 -0
- package/dist/nodejs/sockets.js +482 -0
- package/dist/synckit/index.d.ts +2 -0
- package/dist/synckit/index.js +117 -0
- package/package.json +26 -13
- package/types/instantiation.d.ts +7 -7
- package/lib/browser/cli.js +0 -135
- package/lib/browser/clocks.js +0 -39
- package/lib/browser/environment.js +0 -29
- package/lib/browser/filesystem.js +0 -503
- package/lib/browser/http.js +0 -707
- package/lib/browser/index.js +0 -9
- package/lib/browser/io.js +0 -262
- package/lib/browser/random.js +0 -56
- package/lib/browser/sockets.js +0 -88
- package/lib/common/instantiation.js +0 -253
- package/lib/io/worker-http.js +0 -206
- package/lib/io/worker-io.js +0 -468
- package/lib/io/worker-socket-tcp.js +0 -331
- package/lib/io/worker-socket-udp.js +0 -587
- package/lib/io/worker-sockets.js +0 -364
- package/lib/io/worker-thread.js +0 -1051
- package/lib/nodejs/cli.js +0 -109
- package/lib/nodejs/clocks.js +0 -67
- package/lib/nodejs/filesystem.js +0 -829
- package/lib/nodejs/http.js +0 -764
- package/lib/nodejs/index.js +0 -9
- package/lib/nodejs/io.js +0 -1
- package/lib/nodejs/random.js +0 -49
- package/lib/nodejs/sockets.js +0 -565
- package/lib/synckit/index.js +0 -127
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import * as wasi from "@bytecodealliance/preview2-shim";
|
|
2
|
+
import { types, _createPreopenDescriptor } from "@bytecodealliance/preview2-shim/filesystem";
|
|
3
|
+
/**
|
|
4
|
+
* (EXPERIMENTAL) A class that holds WASI shims and can be used to configure
|
|
5
|
+
* an instantiation of a WebAssembly component transpiled with jco
|
|
6
|
+
* (i.e. via `jco transpile`).
|
|
7
|
+
*
|
|
8
|
+
* Normally, transpiled components contain mapping for WASI interfaces
|
|
9
|
+
* and/or imports that import the relevant packages (ex. `@bytecodealliance/preview2-shim/clocks`)
|
|
10
|
+
* from the right sources.
|
|
11
|
+
*
|
|
12
|
+
* This function makes use of the `WASIShim` object to provide an object that can be easily
|
|
13
|
+
* fed to the `instantiate` function produced by a transpiled component:
|
|
14
|
+
*
|
|
15
|
+
* ```js
|
|
16
|
+
* import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation"
|
|
17
|
+
* // ...
|
|
18
|
+
* import { instantiate } from "path/to/transpiled/component.js"
|
|
19
|
+
* // ...
|
|
20
|
+
* const component = await instantiate(null, new WASIShim().getImportObject())
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* You can also replace imports that you'd like to override with custom implementations,
|
|
24
|
+
* by using the `WASIShim` object directly:
|
|
25
|
+
*
|
|
26
|
+
* ```js
|
|
27
|
+
* import { random } from "@bytecodealliance/preview2-shim"
|
|
28
|
+
* import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation"
|
|
29
|
+
* // ...
|
|
30
|
+
* import { instantiate } from "path/to/transpiled/component.js"
|
|
31
|
+
* // ...
|
|
32
|
+
* const customWASIShim = new WASIShim({
|
|
33
|
+
* random: {
|
|
34
|
+
* // For these two interfaces we re-use the default provided shim
|
|
35
|
+
* random: random.random,
|
|
36
|
+
* insecure-seed: random.insecureSeed,
|
|
37
|
+
* // For insecure, we can supply our own custom implementation
|
|
38
|
+
* insecure: {
|
|
39
|
+
* ...
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* const component = await instantiate(null, customWASIShim.getImportObject())
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* For sandboxing, you can configure preopens, environment variables, and other
|
|
48
|
+
* capabilities via the `sandbox` option:
|
|
49
|
+
*
|
|
50
|
+
* ```js
|
|
51
|
+
* import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation"
|
|
52
|
+
*
|
|
53
|
+
* // Fully sandboxed - no filesystem, network, or env access
|
|
54
|
+
* const sandboxedShim = new WASIShim({
|
|
55
|
+
* sandbox: {
|
|
56
|
+
* preopens: {}, // No filesystem access
|
|
57
|
+
* env: {}, // No environment variables
|
|
58
|
+
* args: ['program'], // Custom arguments
|
|
59
|
+
* enableNetwork: false, // Disable network (default: true for backward compat)
|
|
60
|
+
* }
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* // Limited filesystem access
|
|
64
|
+
* const limitedShim = new WASIShim({
|
|
65
|
+
* sandbox: {
|
|
66
|
+
* preopens: {
|
|
67
|
+
* '/data': '/tmp/guest-data', // Guest sees /data, maps to /tmp/guest-data
|
|
68
|
+
* '/config': '/etc/app' // Guest sees /config, maps to /etc/app
|
|
69
|
+
* }
|
|
70
|
+
* }
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* Note that this object is similar but not identical to the Node `WASI` object --
|
|
75
|
+
* it is solely concerned with shimming of preview2 when dealing with a WebAssembly
|
|
76
|
+
* component transpiled by Jco. While this object *does* work with Node (and the browser)
|
|
77
|
+
* semantics are not the same as Node's `WASI` object.
|
|
78
|
+
*
|
|
79
|
+
* @class WASIShim
|
|
80
|
+
*/
|
|
81
|
+
export class WASIShim {
|
|
82
|
+
/** Object that confirms to the shim interface for `wasi:cli` */
|
|
83
|
+
#cli;
|
|
84
|
+
/** Object that confirms to the shim interface for `wasi:filesystem` */
|
|
85
|
+
#filesystem;
|
|
86
|
+
/** Object that confirms to the shim interface for `wasi:io` */
|
|
87
|
+
#io;
|
|
88
|
+
/** Object that confirms to the shim interface for `wasi:random` */
|
|
89
|
+
#random;
|
|
90
|
+
/** Object that confirms to the shim interface for `wasi:clocks` */
|
|
91
|
+
#clocks;
|
|
92
|
+
/** Object that confirms to the shim interface for `wasi:sockets` */
|
|
93
|
+
#sockets;
|
|
94
|
+
/** Object that confirms to the shim interface for `wasi:http` */
|
|
95
|
+
#http;
|
|
96
|
+
/** Isolated preopens for this instance */
|
|
97
|
+
#preopens;
|
|
98
|
+
/** Isolated environment for this instance */
|
|
99
|
+
#environment;
|
|
100
|
+
/**
|
|
101
|
+
* Create a new WASIShim instance.
|
|
102
|
+
*
|
|
103
|
+
* @param config - Configuration options
|
|
104
|
+
*/
|
|
105
|
+
constructor(config) {
|
|
106
|
+
// Support both old 'shims' parameter name and new 'config' style
|
|
107
|
+
const shims = config;
|
|
108
|
+
this.#cli = shims?.cli ?? wasi.cli;
|
|
109
|
+
this.#filesystem = shims?.filesystem ?? wasi.filesystem;
|
|
110
|
+
this.#io = shims?.io ?? wasi.io;
|
|
111
|
+
this.#random = shims?.random ?? wasi.random;
|
|
112
|
+
this.#clocks = shims?.clocks ?? wasi.clocks;
|
|
113
|
+
this.#sockets = shims?.sockets ?? wasi.sockets;
|
|
114
|
+
this.#http = shims?.http ?? wasi.http;
|
|
115
|
+
// Extract sandbox options
|
|
116
|
+
const sandbox = shims?.sandbox;
|
|
117
|
+
// Create isolated preopens if configured
|
|
118
|
+
if (sandbox?.preopens !== undefined) {
|
|
119
|
+
this.#preopens = createIsolatedPreopens(sandbox.preopens);
|
|
120
|
+
}
|
|
121
|
+
// Create isolated environment if env or args are configured
|
|
122
|
+
if (sandbox?.env !== undefined || sandbox?.args !== undefined) {
|
|
123
|
+
this.#environment = createIsolatedEnvironment(sandbox?.env, sandbox?.args, this.#cli);
|
|
124
|
+
}
|
|
125
|
+
// Apply network restrictions if disabled
|
|
126
|
+
if (sandbox?.enableNetwork === false) {
|
|
127
|
+
// Use the sockets module's built-in deny functions
|
|
128
|
+
if (this.#sockets._denyTcp) {
|
|
129
|
+
this.#sockets._denyTcp();
|
|
130
|
+
}
|
|
131
|
+
if (this.#sockets._denyUdp) {
|
|
132
|
+
this.#sockets._denyUdp();
|
|
133
|
+
}
|
|
134
|
+
if (this.#sockets._denyDnsLookup) {
|
|
135
|
+
this.#sockets._denyDnsLookup();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Generate an import object for the shim that can be used with
|
|
141
|
+
* functions like `instantiate` that are exposed from a transpiled
|
|
142
|
+
* WebAssembly component.
|
|
143
|
+
*
|
|
144
|
+
* @param opts - options for import object generation
|
|
145
|
+
* @returns WASIImportObject
|
|
146
|
+
*/
|
|
147
|
+
getImportObject(opts) {
|
|
148
|
+
const versionSuffix = opts?.asVersion ? `@${opts.asVersion}` : "";
|
|
149
|
+
const obj = {};
|
|
150
|
+
obj[`wasi:cli/environment${versionSuffix}`] = this.#environment ?? this.#cli.environment;
|
|
151
|
+
obj[`wasi:cli/exit${versionSuffix}`] = this.#cli.exit;
|
|
152
|
+
obj[`wasi:cli/stderr${versionSuffix}`] = this.#cli.stderr;
|
|
153
|
+
obj[`wasi:cli/stdin${versionSuffix}`] = this.#cli.stdin;
|
|
154
|
+
obj[`wasi:cli/stdout${versionSuffix}`] = this.#cli.stdout;
|
|
155
|
+
obj[`wasi:cli/terminal-input${versionSuffix}`] = this.#cli.terminalInput;
|
|
156
|
+
obj[`wasi:cli/terminal-output${versionSuffix}`] = this.#cli.terminalOutput;
|
|
157
|
+
obj[`wasi:cli/terminal-stderr${versionSuffix}`] = this.#cli.terminalStderr;
|
|
158
|
+
obj[`wasi:cli/terminal-stdin${versionSuffix}`] = this.#cli.terminalStdin;
|
|
159
|
+
obj[`wasi:cli/terminal-stdout${versionSuffix}`] = this.#cli.terminalStdout;
|
|
160
|
+
obj[`wasi:sockets/instance-network${versionSuffix}`] = this.#sockets.instanceNetwork;
|
|
161
|
+
obj[`wasi:sockets/ip-name-lookup${versionSuffix}`] = this.#sockets.ipNameLookup;
|
|
162
|
+
obj[`wasi:sockets/network${versionSuffix}`] = this.#sockets.network;
|
|
163
|
+
obj[`wasi:sockets/tcp${versionSuffix}`] = this.#sockets.tcp;
|
|
164
|
+
obj[`wasi:sockets/tcp-create-socket${versionSuffix}`] = this.#sockets.tcpCreateSocket;
|
|
165
|
+
obj[`wasi:sockets/udp${versionSuffix}`] = this.#sockets.udp;
|
|
166
|
+
obj[`wasi:sockets/udp-create-socket${versionSuffix}`] = this.#sockets.udpCreateSocket;
|
|
167
|
+
obj[`wasi:filesystem/preopens${versionSuffix}`] = this.#preopens ?? this.#filesystem.preopens;
|
|
168
|
+
obj[`wasi:filesystem/types${versionSuffix}`] = this.#filesystem.types;
|
|
169
|
+
obj[`wasi:io/error${versionSuffix}`] = this.#io.error;
|
|
170
|
+
obj[`wasi:io/poll${versionSuffix}`] = this.#io.poll;
|
|
171
|
+
obj[`wasi:io/streams${versionSuffix}`] = this.#io.streams;
|
|
172
|
+
obj[`wasi:random/random${versionSuffix}`] = this.#random.random;
|
|
173
|
+
obj[`wasi:random/insecure${versionSuffix}`] = this.#random.insecure;
|
|
174
|
+
obj[`wasi:random/insecure-seed${versionSuffix}`] = this.#random.insecureSeed;
|
|
175
|
+
obj[`wasi:clocks/monotonic-clock${versionSuffix}`] = this.#clocks.monotonicClock;
|
|
176
|
+
obj[`wasi:clocks/wall-clock${versionSuffix}`] = this.#clocks.wallClock;
|
|
177
|
+
obj[`wasi:http/types${versionSuffix}`] = this.#http.types;
|
|
178
|
+
obj[`wasi:http/outgoing-handler${versionSuffix}`] = this.#http.outgoingHandler;
|
|
179
|
+
return obj;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Create an isolated preopens object with its own preopen entries.
|
|
184
|
+
*
|
|
185
|
+
* @param preopensConfig - Map of virtual paths to host paths
|
|
186
|
+
* @returns A preopens object with Descriptor and getDirectories()
|
|
187
|
+
*/
|
|
188
|
+
function createIsolatedPreopens(preopensConfig) {
|
|
189
|
+
const entries = [];
|
|
190
|
+
// Populate entries using the filesystem's descriptor creation
|
|
191
|
+
if (_createPreopenDescriptor) {
|
|
192
|
+
for (const [virtualPath, hostPath] of Object.entries(preopensConfig)) {
|
|
193
|
+
const descriptor = _createPreopenDescriptor(hostPath);
|
|
194
|
+
entries.push([descriptor, virtualPath]);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
Descriptor: types.Descriptor,
|
|
199
|
+
getDirectories() {
|
|
200
|
+
return entries;
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Create an isolated CLI environment with its own env and args.
|
|
206
|
+
*
|
|
207
|
+
* @param env - Environment variables
|
|
208
|
+
* @param args - Command-line arguments
|
|
209
|
+
* @param baseCli - The base CLI module to extend
|
|
210
|
+
* @returns An isolated CLI environment object
|
|
211
|
+
*/
|
|
212
|
+
function createIsolatedEnvironment(env, args, baseCli) {
|
|
213
|
+
const envEntries = env ? Object.entries(env) : null;
|
|
214
|
+
const argsArray = args || null;
|
|
215
|
+
return {
|
|
216
|
+
...baseCli.environment,
|
|
217
|
+
getEnvironment() {
|
|
218
|
+
return envEntries ?? baseCli.environment.getEnvironment();
|
|
219
|
+
},
|
|
220
|
+
getArguments() {
|
|
221
|
+
return argsArray ?? baseCli.environment.getArguments();
|
|
222
|
+
},
|
|
223
|
+
initialCwd() {
|
|
224
|
+
return baseCli.environment.initialCwd();
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export declare const CALL_MASK = 4278190080;
|
|
2
|
+
export declare const CALL_TYPE_MASK = 16777215;
|
|
3
|
+
export declare const CALL_SHIFT = 24;
|
|
4
|
+
export declare const STDIN: number;
|
|
5
|
+
export declare const STDOUT: number;
|
|
6
|
+
export declare const STDERR: number;
|
|
7
|
+
export declare const FILE: number;
|
|
8
|
+
export declare const HTTP: number;
|
|
9
|
+
export declare const SOCKET_TCP: number;
|
|
10
|
+
export declare const SOCKET_UDP: number;
|
|
11
|
+
export declare const CLOCKS: number;
|
|
12
|
+
export declare const INPUT_STREAM_CREATE: number;
|
|
13
|
+
export declare const INPUT_STREAM_READ: number;
|
|
14
|
+
export declare const INPUT_STREAM_BLOCKING_READ: number;
|
|
15
|
+
export declare const INPUT_STREAM_SKIP: number;
|
|
16
|
+
export declare const INPUT_STREAM_BLOCKING_SKIP: number;
|
|
17
|
+
export declare const INPUT_STREAM_SUBSCRIBE: number;
|
|
18
|
+
export declare const INPUT_STREAM_DISPOSE: number;
|
|
19
|
+
export declare const OUTPUT_STREAM_CREATE: number;
|
|
20
|
+
export declare const OUTPUT_STREAM_CHECK_WRITE: number;
|
|
21
|
+
export declare const OUTPUT_STREAM_WRITE: number;
|
|
22
|
+
export declare const OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH: number;
|
|
23
|
+
export declare const OUTPUT_STREAM_FLUSH: number;
|
|
24
|
+
export declare const OUTPUT_STREAM_BLOCKING_FLUSH: number;
|
|
25
|
+
export declare const OUTPUT_STREAM_WRITE_ZEROES: number;
|
|
26
|
+
export declare const OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH: number;
|
|
27
|
+
export declare const OUTPUT_STREAM_SPLICE: number;
|
|
28
|
+
export declare const OUTPUT_STREAM_BLOCKING_SPLICE: number;
|
|
29
|
+
export declare const OUTPUT_STREAM_SUBSCRIBE: number;
|
|
30
|
+
export declare const OUTPUT_STREAM_DISPOSE: number;
|
|
31
|
+
export declare const OUTPUT_STREAM_GET_TOTAL_BYTES: number;
|
|
32
|
+
export declare const POLL_POLLABLE_READY: number;
|
|
33
|
+
export declare const POLL_POLLABLE_BLOCK: number;
|
|
34
|
+
export declare const POLL_POLLABLE_DISPOSE: number;
|
|
35
|
+
export declare const POLL_POLL_LIST: number;
|
|
36
|
+
export declare const FUTURE_DISPOSE: number;
|
|
37
|
+
export declare const FUTURE_TAKE_VALUE: number;
|
|
38
|
+
export declare const FUTURE_SUBSCRIBE: number;
|
|
39
|
+
export declare const HTTP_CREATE_REQUEST: number;
|
|
40
|
+
export declare const HTTP_OUTPUT_STREAM_FINISH: number;
|
|
41
|
+
export declare const HTTP_SERVER_START: number;
|
|
42
|
+
export declare const HTTP_SERVER_STOP: number;
|
|
43
|
+
export declare const HTTP_SERVER_INCOMING_HANDLER: number;
|
|
44
|
+
export declare const HTTP_SERVER_SET_OUTGOING_RESPONSE: number;
|
|
45
|
+
export declare const HTTP_SERVER_CLEAR_OUTGOING_RESPONSE: number;
|
|
46
|
+
export declare const HTTP_SERVER_GET_ADDRESS: number;
|
|
47
|
+
export declare const HTTP_OUTGOING_BODY_DISPOSE: number;
|
|
48
|
+
export declare const CLOCKS_DURATION_SUBSCRIBE: number;
|
|
49
|
+
export declare const CLOCKS_INSTANT_SUBSCRIBE: number;
|
|
50
|
+
export declare const SOCKET_TCP_CREATE_HANDLE: number;
|
|
51
|
+
export declare const SOCKET_TCP_BIND_START: number;
|
|
52
|
+
export declare const SOCKET_TCP_BIND_FINISH: number;
|
|
53
|
+
export declare const SOCKET_TCP_CONNECT_START: number;
|
|
54
|
+
export declare const SOCKET_TCP_CONNECT_FINISH: number;
|
|
55
|
+
export declare const SOCKET_TCP_SUBSCRIBE: number;
|
|
56
|
+
export declare const SOCKET_TCP_LISTEN_START: number;
|
|
57
|
+
export declare const SOCKET_TCP_LISTEN_FINISH: number;
|
|
58
|
+
export declare const SOCKET_TCP_IS_LISTENING: number;
|
|
59
|
+
export declare const SOCKET_TCP_ACCEPT: number;
|
|
60
|
+
export declare const SOCKET_TCP_GET_LOCAL_ADDRESS: number;
|
|
61
|
+
export declare const SOCKET_TCP_GET_REMOTE_ADDRESS: number;
|
|
62
|
+
export declare const SOCKET_TCP_SET_KEEP_ALIVE: number;
|
|
63
|
+
export declare const SOCKET_TCP_SET_LISTEN_BACKLOG_SIZE: number;
|
|
64
|
+
export declare const SOCKET_TCP_SHUTDOWN: number;
|
|
65
|
+
export declare const SOCKET_TCP_DISPOSE: number;
|
|
66
|
+
export declare const SOCKET_UDP_CREATE_HANDLE: number;
|
|
67
|
+
export declare const SOCKET_UDP_BIND_START: number;
|
|
68
|
+
export declare const SOCKET_UDP_BIND_FINISH: number;
|
|
69
|
+
export declare const SOCKET_UDP_STREAM: number;
|
|
70
|
+
export declare const SOCKET_UDP_SUBSCRIBE: number;
|
|
71
|
+
export declare const SOCKET_UDP_DISPOSE: number;
|
|
72
|
+
export declare const SOCKET_UDP_GET_LOCAL_ADDRESS: number;
|
|
73
|
+
export declare const SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE: number;
|
|
74
|
+
export declare const SOCKET_UDP_GET_REMOTE_ADDRESS: number;
|
|
75
|
+
export declare const SOCKET_UDP_GET_SEND_BUFFER_SIZE: number;
|
|
76
|
+
export declare const SOCKET_UDP_GET_UNICAST_HOP_LIMIT: number;
|
|
77
|
+
export declare const SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE: number;
|
|
78
|
+
export declare const SOCKET_UDP_SET_SEND_BUFFER_SIZE: number;
|
|
79
|
+
export declare const SOCKET_UDP_SET_UNICAST_HOP_LIMIT: number;
|
|
80
|
+
export declare const SOCKET_INCOMING_DATAGRAM_STREAM_RECEIVE: number;
|
|
81
|
+
export declare const SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND: number;
|
|
82
|
+
export declare const SOCKET_OUTGOING_DATAGRAM_STREAM_SEND: number;
|
|
83
|
+
export declare const SOCKET_DATAGRAM_STREAM_SUBSCRIBE: number;
|
|
84
|
+
export declare const SOCKET_DATAGRAM_STREAM_DISPOSE: number;
|
|
85
|
+
export declare const SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE: number;
|
|
86
|
+
export declare const SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE: number;
|
|
87
|
+
export declare const SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST: number;
|
|
88
|
+
export declare const SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST: number;
|
|
89
|
+
export declare const SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST: number;
|
|
90
|
+
export declare const SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST: number;
|
|
91
|
+
export declare const reverseMap: {};
|
package/{lib → dist}/io/calls.js
RENAMED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
let call_id = 0;
|
|
2
|
-
|
|
3
2
|
// Call is a 32 bit integer, leading 8 bits are call number, trailing 24 bits allow custom call types
|
|
4
3
|
export const CALL_MASK = 0xff000000;
|
|
5
4
|
export const CALL_TYPE_MASK = 0x00ffffff;
|
|
6
5
|
export const CALL_SHIFT = 24;
|
|
7
|
-
|
|
8
6
|
// Type indiciator for generic Stream, Future, and Poll calls
|
|
9
7
|
let cnt = 0;
|
|
10
8
|
export const STDIN = ++cnt;
|
|
@@ -15,7 +13,6 @@ export const HTTP = ++cnt;
|
|
|
15
13
|
export const SOCKET_TCP = ++cnt;
|
|
16
14
|
export const SOCKET_UDP = ++cnt;
|
|
17
15
|
export const CLOCKS = ++cnt;
|
|
18
|
-
|
|
19
16
|
// Io Input Stream
|
|
20
17
|
export const INPUT_STREAM_CREATE = ++call_id << CALL_SHIFT;
|
|
21
18
|
export const INPUT_STREAM_READ = ++call_id << CALL_SHIFT;
|
|
@@ -24,7 +21,6 @@ export const INPUT_STREAM_SKIP = ++call_id << CALL_SHIFT;
|
|
|
24
21
|
export const INPUT_STREAM_BLOCKING_SKIP = ++call_id << CALL_SHIFT;
|
|
25
22
|
export const INPUT_STREAM_SUBSCRIBE = ++call_id << CALL_SHIFT;
|
|
26
23
|
export const INPUT_STREAM_DISPOSE = ++call_id << CALL_SHIFT;
|
|
27
|
-
|
|
28
24
|
// Io Output Stream
|
|
29
25
|
export const OUTPUT_STREAM_CREATE = ++call_id << CALL_SHIFT;
|
|
30
26
|
export const OUTPUT_STREAM_CHECK_WRITE = ++call_id << CALL_SHIFT;
|
|
@@ -38,20 +34,16 @@ export const OUTPUT_STREAM_SPLICE = ++call_id << CALL_SHIFT;
|
|
|
38
34
|
export const OUTPUT_STREAM_BLOCKING_SPLICE = ++call_id << CALL_SHIFT;
|
|
39
35
|
export const OUTPUT_STREAM_SUBSCRIBE = ++call_id << CALL_SHIFT;
|
|
40
36
|
export const OUTPUT_STREAM_DISPOSE = ++call_id << CALL_SHIFT;
|
|
41
|
-
|
|
42
37
|
export const OUTPUT_STREAM_GET_TOTAL_BYTES = ++call_id << CALL_SHIFT;
|
|
43
|
-
|
|
44
38
|
// Io Poll
|
|
45
39
|
export const POLL_POLLABLE_READY = ++call_id << CALL_SHIFT;
|
|
46
40
|
export const POLL_POLLABLE_BLOCK = ++call_id << CALL_SHIFT;
|
|
47
41
|
export const POLL_POLLABLE_DISPOSE = ++call_id << CALL_SHIFT;
|
|
48
42
|
export const POLL_POLL_LIST = ++call_id << CALL_SHIFT;
|
|
49
|
-
|
|
50
43
|
// Futures
|
|
51
44
|
export const FUTURE_DISPOSE = ++call_id << CALL_SHIFT;
|
|
52
45
|
export const FUTURE_TAKE_VALUE = ++call_id << CALL_SHIFT;
|
|
53
46
|
export const FUTURE_SUBSCRIBE = ++call_id << CALL_SHIFT;
|
|
54
|
-
|
|
55
47
|
// Http
|
|
56
48
|
export const HTTP_CREATE_REQUEST = ++call_id << 24;
|
|
57
49
|
export const HTTP_OUTPUT_STREAM_FINISH = ++call_id << CALL_SHIFT;
|
|
@@ -63,11 +55,9 @@ export const HTTP_SERVER_SET_OUTGOING_RESPONSE = ++call_id << CALL_SHIFT;
|
|
|
63
55
|
export const HTTP_SERVER_CLEAR_OUTGOING_RESPONSE = ++call_id << CALL_SHIFT;
|
|
64
56
|
export const HTTP_SERVER_GET_ADDRESS = ++call_id << CALL_SHIFT;
|
|
65
57
|
export const HTTP_OUTGOING_BODY_DISPOSE = ++call_id << CALL_SHIFT;
|
|
66
|
-
|
|
67
58
|
// Clocks
|
|
68
59
|
export const CLOCKS_DURATION_SUBSCRIBE = ++call_id << CALL_SHIFT;
|
|
69
60
|
export const CLOCKS_INSTANT_SUBSCRIBE = ++call_id << CALL_SHIFT;
|
|
70
|
-
|
|
71
61
|
// Sockets
|
|
72
62
|
// Tcp
|
|
73
63
|
export const SOCKET_TCP_CREATE_HANDLE = ++call_id << CALL_SHIFT;
|
|
@@ -106,23 +96,18 @@ export const SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND = ++call_id << CALL_SHIF
|
|
|
106
96
|
export const SOCKET_OUTGOING_DATAGRAM_STREAM_SEND = ++call_id << CALL_SHIFT;
|
|
107
97
|
export const SOCKET_DATAGRAM_STREAM_SUBSCRIBE = ++call_id << CALL_SHIFT;
|
|
108
98
|
export const SOCKET_DATAGRAM_STREAM_DISPOSE = ++call_id << CALL_SHIFT;
|
|
109
|
-
|
|
110
99
|
export const SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE = ++call_id << CALL_SHIFT;
|
|
111
100
|
export const SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE = ++call_id << CALL_SHIFT;
|
|
112
|
-
|
|
113
101
|
// Name lookup
|
|
114
102
|
export const SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST = ++call_id << CALL_SHIFT;
|
|
115
103
|
export const SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST = ++call_id << CALL_SHIFT;
|
|
116
104
|
export const SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST = ++call_id << CALL_SHIFT;
|
|
117
105
|
export const SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST = ++call_id << CALL_SHIFT;
|
|
118
|
-
|
|
119
106
|
export const reverseMap = {};
|
|
120
|
-
|
|
121
107
|
import * as calls from "./calls.js";
|
|
122
|
-
|
|
123
108
|
for (const name of Object.getOwnPropertyNames(calls)) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
109
|
+
if (name === "reverseMap") {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
reverseMap[calls[name]] = name;
|
|
128
113
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function stopHttpServer(id: any): Promise<void>;
|
|
2
|
+
export declare function clearOutgoingResponse(id: any): void;
|
|
3
|
+
export declare function getHttpServerAddress(id: any): Promise<any>;
|
|
4
|
+
export declare function setOutgoingResponse(id: any, { statusCode, headers, streamId }: {
|
|
5
|
+
statusCode: any;
|
|
6
|
+
headers: any;
|
|
7
|
+
streamId: any;
|
|
8
|
+
}): Promise<void>;
|
|
9
|
+
export declare function startHttpServer(id: number, { port, host }: {
|
|
10
|
+
port: any;
|
|
11
|
+
host: any;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
export declare function createHttpRequest(method: any, scheme: any, authority: any, pathWithQuery: any, headers: any, bodyId: any, connectTimeout: any, betweenBytesTimeout: any, firstByteTimeout: any): Promise<{
|
|
14
|
+
status: number | undefined;
|
|
15
|
+
headers: [string, string | string[] | undefined][];
|
|
16
|
+
bodyStreamId: number;
|
|
17
|
+
}>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { createReadableStream, getStreamOrThrow } from "./worker-thread.js";
|
|
2
|
+
import { createServer, request as httpRequest, Agent as HttpAgent, } from "node:http";
|
|
3
|
+
import { request as httpsRequest, Agent as HttpsAgent } from "node:https";
|
|
4
|
+
import { parentPort } from "node:worker_threads";
|
|
5
|
+
import { HTTP_SERVER_INCOMING_HANDLER } from "./calls.js";
|
|
6
|
+
const agentOptions = {
|
|
7
|
+
keepAlive: true,
|
|
8
|
+
};
|
|
9
|
+
const httpAgent = new HttpAgent(agentOptions);
|
|
10
|
+
const httpsAgent = new HttpsAgent(agentOptions);
|
|
11
|
+
const servers = new Map();
|
|
12
|
+
let responseCnt = 0;
|
|
13
|
+
const responses = new Map();
|
|
14
|
+
export async function stopHttpServer(id) {
|
|
15
|
+
await new Promise((resolve) => servers.get(id).close(resolve));
|
|
16
|
+
}
|
|
17
|
+
export function clearOutgoingResponse(id) {
|
|
18
|
+
responses.delete(id);
|
|
19
|
+
}
|
|
20
|
+
export async function getHttpServerAddress(id) {
|
|
21
|
+
return servers.get(id).address();
|
|
22
|
+
}
|
|
23
|
+
export async function setOutgoingResponse(id, { statusCode, headers, streamId }) {
|
|
24
|
+
const response = responses.get(id);
|
|
25
|
+
const textDecoder = new TextDecoder();
|
|
26
|
+
response.writeHead(statusCode, Object.fromEntries(headers.map(([key, val]) => [key, textDecoder.decode(val)])));
|
|
27
|
+
response.flushHeaders();
|
|
28
|
+
const { stream } = getStreamOrThrow(streamId);
|
|
29
|
+
stream.pipe(response);
|
|
30
|
+
responses.delete(id);
|
|
31
|
+
}
|
|
32
|
+
export async function startHttpServer(id, { port, host }) {
|
|
33
|
+
const server = createServer((req, res) => {
|
|
34
|
+
// MessagePort is only available when running in worker thread.
|
|
35
|
+
if (!parentPort) {
|
|
36
|
+
throw {
|
|
37
|
+
tag: "internal-error",
|
|
38
|
+
val: "Unable to post message. Likely running in main thread.",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// create the streams and their ids
|
|
42
|
+
const streamId = createReadableStream(req);
|
|
43
|
+
const responseId = ++responseCnt;
|
|
44
|
+
parentPort.postMessage({
|
|
45
|
+
type: HTTP_SERVER_INCOMING_HANDLER,
|
|
46
|
+
id,
|
|
47
|
+
payload: {
|
|
48
|
+
responseId,
|
|
49
|
+
method: req.method,
|
|
50
|
+
host: req.headers.host || host || "localhost",
|
|
51
|
+
pathWithQuery: req.url,
|
|
52
|
+
headers: Object.entries(req.headersDistinct).flatMap(([key, val]) => val?.map((val) => [key, val])),
|
|
53
|
+
streamId,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
responses.set(responseId, res);
|
|
57
|
+
});
|
|
58
|
+
await new Promise((resolve, reject) => {
|
|
59
|
+
// @ts-expect-error Resolve has different signature
|
|
60
|
+
server.listen(port, host, resolve);
|
|
61
|
+
server.on("error", reject);
|
|
62
|
+
});
|
|
63
|
+
servers.set(id, server);
|
|
64
|
+
}
|
|
65
|
+
export async function createHttpRequest(method, scheme, authority, pathWithQuery, headers, bodyId, connectTimeout, betweenBytesTimeout, firstByteTimeout) {
|
|
66
|
+
let stream = null;
|
|
67
|
+
if (bodyId) {
|
|
68
|
+
try {
|
|
69
|
+
({ stream } = getStreamOrThrow(bodyId));
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
if (e.tag === "closed") {
|
|
73
|
+
throw {
|
|
74
|
+
tag: "internal-error",
|
|
75
|
+
val: "Unexpected closed body stream",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// it should never be possible for the body stream to already
|
|
79
|
+
// be closed, or for there to be a write error
|
|
80
|
+
// we therefore just throw internal error here
|
|
81
|
+
if (e.tag === "last-operation-failed") {
|
|
82
|
+
throw {
|
|
83
|
+
tag: "internal-error",
|
|
84
|
+
val: e.val,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// entirely unknown error -> trap
|
|
88
|
+
throw e;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
// Make a request
|
|
93
|
+
let req;
|
|
94
|
+
switch (scheme) {
|
|
95
|
+
case "http:":
|
|
96
|
+
req = httpRequest({
|
|
97
|
+
agent: httpAgent,
|
|
98
|
+
method,
|
|
99
|
+
host: authority.split(":")[0],
|
|
100
|
+
port: authority.split(":")[1],
|
|
101
|
+
path: pathWithQuery,
|
|
102
|
+
timeout: connectTimeout && Number(connectTimeout / 1000000n),
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
case "https:":
|
|
106
|
+
req = httpsRequest({
|
|
107
|
+
agent: httpsAgent,
|
|
108
|
+
method,
|
|
109
|
+
host: authority.split(":")[0],
|
|
110
|
+
port: authority.split(":")[1],
|
|
111
|
+
path: pathWithQuery,
|
|
112
|
+
timeout: connectTimeout && Number(connectTimeout / 1000000n),
|
|
113
|
+
});
|
|
114
|
+
break;
|
|
115
|
+
default:
|
|
116
|
+
throw { tag: "HTTP-protocol-error" };
|
|
117
|
+
}
|
|
118
|
+
for (const [key, value] of headers) {
|
|
119
|
+
req.appendHeader(key, value);
|
|
120
|
+
}
|
|
121
|
+
req.flushHeaders();
|
|
122
|
+
if (stream) {
|
|
123
|
+
stream.pipe(req);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
req.end();
|
|
127
|
+
}
|
|
128
|
+
const res = await new Promise((resolve, reject) => {
|
|
129
|
+
req.once("timeout", () => {
|
|
130
|
+
reject({
|
|
131
|
+
tag: "connection-timeout",
|
|
132
|
+
});
|
|
133
|
+
req.destroy();
|
|
134
|
+
});
|
|
135
|
+
req.once("response", resolve);
|
|
136
|
+
req.once("close", () => reject);
|
|
137
|
+
req.once("error", reject);
|
|
138
|
+
});
|
|
139
|
+
if (firstByteTimeout) {
|
|
140
|
+
res.setTimeout(Number(firstByteTimeout / 1000000n));
|
|
141
|
+
}
|
|
142
|
+
if (betweenBytesTimeout) {
|
|
143
|
+
res.once("readable", () => {
|
|
144
|
+
res.setTimeout(Number(betweenBytesTimeout / 1000000n));
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const bodyStreamId = createReadableStream(res);
|
|
148
|
+
return {
|
|
149
|
+
status: res.statusCode,
|
|
150
|
+
headers: Array.from(Object.entries(res.headers)),
|
|
151
|
+
bodyStreamId,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
if (e?.tag) {
|
|
156
|
+
throw e;
|
|
157
|
+
}
|
|
158
|
+
const err = getFirstError(e);
|
|
159
|
+
switch (err.code) {
|
|
160
|
+
case "ECONNRESET":
|
|
161
|
+
throw { tag: "HTTP-protocol-error" };
|
|
162
|
+
case "ENOTFOUND":
|
|
163
|
+
throw {
|
|
164
|
+
tag: "DNS-error",
|
|
165
|
+
val: {
|
|
166
|
+
rcode: err.code,
|
|
167
|
+
infoCode: err.errno < 0 ? -err.errno : err.errno,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
case "ECONNREFUSED":
|
|
171
|
+
throw {
|
|
172
|
+
tag: "connection-refused",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
throw {
|
|
176
|
+
tag: "internal-error",
|
|
177
|
+
val: err.toString(),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function getFirstError(e) {
|
|
182
|
+
if (typeof e !== "object" || e === null) {
|
|
183
|
+
return e;
|
|
184
|
+
}
|
|
185
|
+
if (e.cause) {
|
|
186
|
+
return getFirstError(e.cause);
|
|
187
|
+
}
|
|
188
|
+
if (e instanceof AggregateError) {
|
|
189
|
+
return getFirstError(e.errors[0]);
|
|
190
|
+
}
|
|
191
|
+
return e;
|
|
192
|
+
}
|