@eryx-org/eryx 0.0.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +281 -4
- package/eryx-sandbox.conditional-host-import-0.wasm +0 -0
- package/eryx-sandbox.conditional-host-import-1.wasm +0 -0
- package/eryx-sandbox.conditional-suspending-1-i32-to-i32.wasm +0 -0
- package/eryx-sandbox.core.wasm +0 -0
- package/eryx-sandbox.core10.wasm +0 -0
- package/eryx-sandbox.core11.wasm +0 -0
- package/eryx-sandbox.core12.wasm +0 -0
- package/eryx-sandbox.core13.wasm +0 -0
- package/eryx-sandbox.core2.wasm +0 -0
- package/eryx-sandbox.core3.wasm +0 -0
- package/eryx-sandbox.core4.wasm +0 -0
- package/eryx-sandbox.core5.wasm +0 -0
- package/eryx-sandbox.core6.wasm +0 -0
- package/eryx-sandbox.core7.wasm +0 -0
- package/eryx-sandbox.core8.wasm +0 -0
- package/eryx-sandbox.core9.wasm +0 -0
- package/eryx-sandbox.d.ts +185 -0
- package/eryx-sandbox.js +55329 -0
- package/index.d.ts +331 -0
- package/index.js +698 -0
- package/instance.js +174 -0
- package/interfaces/eryx-net-tcp.d.ts +39 -0
- package/interfaces/eryx-net-tls.d.ts +33 -0
- package/interfaces/wasi-cli-environment.d.ts +3 -0
- package/interfaces/wasi-cli-exit.d.ts +3 -0
- package/interfaces/wasi-cli-stderr.d.ts +3 -0
- package/interfaces/wasi-cli-stdin.d.ts +3 -0
- package/interfaces/wasi-cli-stdout.d.ts +3 -0
- package/interfaces/wasi-cli-terminal-input.d.ts +8 -0
- package/interfaces/wasi-cli-terminal-output.d.ts +8 -0
- package/interfaces/wasi-cli-terminal-stderr.d.ts +3 -0
- package/interfaces/wasi-cli-terminal-stdin.d.ts +3 -0
- package/interfaces/wasi-cli-terminal-stdout.d.ts +3 -0
- package/interfaces/wasi-clocks-monotonic-clock.d.ts +8 -0
- package/interfaces/wasi-clocks-wall-clock.d.ts +7 -0
- package/interfaces/wasi-filesystem-preopens.d.ts +3 -0
- package/interfaces/wasi-filesystem-types.d.ts +288 -0
- package/interfaces/wasi-io-error.d.ts +8 -0
- package/interfaces/wasi-io-poll.d.ts +10 -0
- package/interfaces/wasi-io-streams.d.ts +51 -0
- package/interfaces/wasi-random-random.d.ts +2 -0
- package/interfaces/wasi-sockets-instance-network.d.ts +3 -0
- package/interfaces/wasi-sockets-ip-name-lookup.d.ts +21 -0
- package/interfaces/wasi-sockets-network.d.ts +92 -0
- package/interfaces/wasi-sockets-tcp-create-socket.d.ts +8 -0
- package/interfaces/wasi-sockets-tcp.d.ts +127 -0
- package/interfaces/wasi-sockets-udp-create-socket.d.ts +8 -0
- package/interfaces/wasi-sockets-udp.d.ts +93 -0
- package/package.json +69 -3
- package/python-stdlib.tar.gz +0 -0
- package/shims/callbacks.d.ts +45 -0
- package/shims/callbacks.js +469 -0
- package/shims/ip.js +46 -0
- package/shims/net-fetch.js +264 -0
- package/shims/net-node.js +236 -0
- package/shims/net.d.ts +84 -0
- package/shims/net.js +260 -0
- package/shims/preview2-node.js +35 -0
- package/shims/raw-json.js +111 -0
- package/shims/sockets.js +53 -0
- package/stdlib-loader.js +174 -0
- package/worker.js +100 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eryx - JavaScript API
|
|
3
|
+
*
|
|
4
|
+
* A Python sandbox powered by WebAssembly, for browser and Node.js.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
setTraceHandler,
|
|
9
|
+
setOutputHandler,
|
|
10
|
+
SuspendCallback,
|
|
11
|
+
} from "./shims/callbacks.js";
|
|
12
|
+
|
|
13
|
+
export { DEFAULT_BLOCKED_HOSTS, DEFAULT_BLOCKED_NETWORKS } from "./shims/net.js";
|
|
14
|
+
export type { NetConfig } from "./shims/net.js";
|
|
15
|
+
|
|
16
|
+
import type { NetConfig } from "./shims/net.js";
|
|
17
|
+
import type { Callbacks, ExecuteResult } from "@eryx-org/eryx-run/shared";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The errors a run throws, shared with eryx.run's client
|
|
21
|
+
* (`@eryx-org/eryx-run`), so one `instanceof` handles both. Code that raises
|
|
22
|
+
* rejects with a `PythonExceptionError` (its traceback as `message`, the
|
|
23
|
+
* output before it in `stdout` and `stderr`); a run past `timeoutMs` with a
|
|
24
|
+
* `TimeoutError`.
|
|
25
|
+
*/
|
|
26
|
+
export {
|
|
27
|
+
EryxError,
|
|
28
|
+
ExecutionError,
|
|
29
|
+
PythonExceptionError,
|
|
30
|
+
ResourceLimitError,
|
|
31
|
+
TimeoutError,
|
|
32
|
+
} from "@eryx-org/eryx-run/shared";
|
|
33
|
+
export type { CallbackHandler, Callbacks, ExecuteResult } from "@eryx-org/eryx-run/shared";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set the default network policy, or disable networking again with `null`
|
|
37
|
+
* (the initial default). Each {@link Sandbox} copies it when it is created,
|
|
38
|
+
* unless given its own `netConfig`; sandboxes that already exist keep their
|
|
39
|
+
* policy. It also applies to the default sandbox behind the module-level
|
|
40
|
+
* helpers. Throws a TypeError if a network isn't a valid CIDR or
|
|
41
|
+
* `maxConnections` isn't a non-negative integer.
|
|
42
|
+
*/
|
|
43
|
+
export function setNetConfig(config: NetConfig | null): void;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A captured Python session state, from {@link Sandbox.snapshotState}.
|
|
47
|
+
*/
|
|
48
|
+
export interface StateSnapshot {
|
|
49
|
+
/** The serialized state, accepted by `restoreState()`. */
|
|
50
|
+
data: Uint8Array;
|
|
51
|
+
/**
|
|
52
|
+
* Names of the globals left out because they could not be serialized (e.g.
|
|
53
|
+
* open sockets, generators). They raise `NameError` after a restore.
|
|
54
|
+
*/
|
|
55
|
+
skippedGlobals: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/** The callback that suspended execution. */
|
|
60
|
+
export interface SuspendedCallback {
|
|
61
|
+
/** Name of the callback that suspended. */
|
|
62
|
+
name: string;
|
|
63
|
+
/** Canonical arguments JSON it was invoked with. */
|
|
64
|
+
argsJson: string;
|
|
65
|
+
/** The reason passed to `SuspendCallback`. */
|
|
66
|
+
reason: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Options for {@link Sandbox.executeWithJournal}. */
|
|
70
|
+
export interface ExecuteWithJournalOptions {
|
|
71
|
+
/**
|
|
72
|
+
* The `journal` string from a previous run's outcome, whose results should be
|
|
73
|
+
* replayed. Pass it back unmodified (don't round-trip it through
|
|
74
|
+
* `JSON.parse`/`JSON.stringify`, which would lose number precision in results).
|
|
75
|
+
*/
|
|
76
|
+
journal?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The outcome of {@link Sandbox.executeWithJournal}. */
|
|
80
|
+
export interface ReplayOutcome {
|
|
81
|
+
/** The execution result, or `undefined` if execution failed. */
|
|
82
|
+
result?: ExecuteResult;
|
|
83
|
+
/**
|
|
84
|
+
* Why execution failed — a Python exception, or the `SuspendCallback` that
|
|
85
|
+
* halted it — or `undefined` on success. Check `suspended` first.
|
|
86
|
+
*/
|
|
87
|
+
error?: Error;
|
|
88
|
+
/**
|
|
89
|
+
* Callbacks completed during this run, serialized as JSON in the same shape as
|
|
90
|
+
* the Rust `CallbackJournal`
|
|
91
|
+
* (`{"code", "entries": [{"index", "name", "args_hash", "args_json", "result": {"Ok": value} | {"Err": message}}]}`).
|
|
92
|
+
* Always present, even on error. Store it as-is.
|
|
93
|
+
*/
|
|
94
|
+
journal: string;
|
|
95
|
+
/** How many callbacks were served from the supplied journal. */
|
|
96
|
+
replayedCallbacks: number;
|
|
97
|
+
/** Set if a callback threw `SuspendCallback`. */
|
|
98
|
+
suspended?: SuspendedCallback;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Options for a {@link Sandbox}.
|
|
103
|
+
*
|
|
104
|
+
* `traceHandler` and `outputHandler` override, for this sandbox only, the
|
|
105
|
+
* module-wide default set by the setter of the same name. An omitted one
|
|
106
|
+
* follows that default, including later changes to it.
|
|
107
|
+
*/
|
|
108
|
+
export interface SandboxOptions {
|
|
109
|
+
/**
|
|
110
|
+
* Functions the code can `await`, by name, as for eryx.run's client: each
|
|
111
|
+
* a function of the code's keyword arguments, or `{ handler, description }`
|
|
112
|
+
* (the description is what `list_callbacks()` shows). What it returns must
|
|
113
|
+
* be JSON; if it throws, the call fails in Python. A handler can throw
|
|
114
|
+
* {@link SuspendCallback} to halt the run.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* new Sandbox({ callbacks: { get_user: async ({ id }) => ({ id, name: "Ada" }) } });
|
|
118
|
+
*/
|
|
119
|
+
callbacks?: Callbacks;
|
|
120
|
+
/** Handler for trace events from the Python runtime. */
|
|
121
|
+
traceHandler?:
|
|
122
|
+
| ((lineno: number, eventJson: string, contextJson: string) => void)
|
|
123
|
+
| null;
|
|
124
|
+
/** Handler for streaming output: stream 0 = stdout, 1 = stderr. */
|
|
125
|
+
outputHandler?: ((stream: number, data: string) => void) | null;
|
|
126
|
+
/**
|
|
127
|
+
* This sandbox's network policy (`null` disables networking). Omitted, the
|
|
128
|
+
* sandbox copies the default set with {@link setNetConfig} when it is
|
|
129
|
+
* created; later {@link setNetConfig} calls don't change it.
|
|
130
|
+
*/
|
|
131
|
+
netConfig?: NetConfig | null;
|
|
132
|
+
/**
|
|
133
|
+
* Run the sandbox in its own Worker (a `worker_threads` Worker in Node.js)
|
|
134
|
+
* and reject any call that takes longer than this many milliseconds with a
|
|
135
|
+
* {@link TimeoutError}. The Worker is then terminated and replaced, so the
|
|
136
|
+
* sandbox's Python state is lost. The deadline is wall-clock time and
|
|
137
|
+
* includes time spent in callbacks; instantiation doesn't count.
|
|
138
|
+
*
|
|
139
|
+
* Each such sandbox also holds a Worker with its own JavaScript heap and its
|
|
140
|
+
* own copy of the Python stdlib tree, about 70 MB on top of the instance's
|
|
141
|
+
* memory.
|
|
142
|
+
*
|
|
143
|
+
* Opt-in: there is no default timeout, unlike the Rust crate and the Python
|
|
144
|
+
* bindings (30 seconds). Without `timeoutMs`, nothing interrupts a call that
|
|
145
|
+
* never finishes, such as `while True: pass`. There is no memory limit
|
|
146
|
+
* either way: an instance's memory grows as its code allocates, up to the
|
|
147
|
+
* WebAssembly 4 GiB maximum or what the JavaScript engine allows.
|
|
148
|
+
*/
|
|
149
|
+
timeoutMs?: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* A Python sandbox powered by WebAssembly.
|
|
155
|
+
*
|
|
156
|
+
* Each Sandbox is its own WebAssembly instance, with its own Python
|
|
157
|
+
* interpreter, filesystem, network connections, callback handlers and
|
|
158
|
+
* journal, so code run in one can't see or change another's state. Python
|
|
159
|
+
* state (variables, imports, etc.) persists across execute() calls on the same
|
|
160
|
+
* Sandbox; connections are closed when each call finishes.
|
|
161
|
+
*
|
|
162
|
+
* The compiled code is shared, but every instance has its own linear memory
|
|
163
|
+
* (the pre-initialized interpreter image plus whatever the code allocates), so
|
|
164
|
+
* each live Sandbox costs tens of megabytes (about 20 MB idle on Node.js 24).
|
|
165
|
+
* One created with `timeoutMs` also runs in its own Worker, which adds about
|
|
166
|
+
* 70 MB. Call close() (or use `using`) when you're done with a sandbox: it
|
|
167
|
+
* stops the Worker at once and closes the sandbox's connections. A sandbox
|
|
168
|
+
* that is only dropped is freed whenever the garbage collector gets to it.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* const sandbox = new Sandbox();
|
|
172
|
+
*
|
|
173
|
+
* // Variables persist across calls
|
|
174
|
+
* await sandbox.execute('x = 42');
|
|
175
|
+
* const result = await sandbox.execute('print(x)');
|
|
176
|
+
* console.log(result.stdout); // "42"
|
|
177
|
+
*
|
|
178
|
+
* // Reset state
|
|
179
|
+
* await sandbox.clearState();
|
|
180
|
+
*/
|
|
181
|
+
export class Sandbox {
|
|
182
|
+
/**
|
|
183
|
+
* Create a sandbox. Instantiation starts right away and finishes in the
|
|
184
|
+
* background; the first call waits for it, and rejects if it failed.
|
|
185
|
+
*/
|
|
186
|
+
constructor(options?: SandboxOptions);
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* This sandbox's own file tree (a copy of the module's `_fileTree`). Changes
|
|
190
|
+
* to it are visible to the sandbox's next filesystem access. Undefined for a
|
|
191
|
+
* sandbox with `timeoutMs`, whose files live in its Worker.
|
|
192
|
+
*
|
|
193
|
+
* @internal For the demo app and advanced integrations.
|
|
194
|
+
*/
|
|
195
|
+
readonly _fileTree: { dir: Record<string, unknown> } | undefined;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Execute Python code in the sandbox.
|
|
199
|
+
*
|
|
200
|
+
* The code runs in the sandboxed Python interpreter. Output to stdout
|
|
201
|
+
* and stderr is captured and returned. Variables and imports persist
|
|
202
|
+
* across calls on the same Sandbox instance.
|
|
203
|
+
*
|
|
204
|
+
* Calls on one Sandbox run one at a time, in call order; an execute() made
|
|
205
|
+
* while another call on it is running waits for it to finish. Don't await
|
|
206
|
+
* execute() on the same Sandbox from inside its callback handler: it would
|
|
207
|
+
* wait for the execution that invoked the handler, which never finishes.
|
|
208
|
+
*
|
|
209
|
+
* @param code - Python source code to execute
|
|
210
|
+
* @returns Captured stdout and stderr
|
|
211
|
+
* @throws If the Python code raises an unhandled exception
|
|
212
|
+
*/
|
|
213
|
+
execute(code: string): Promise<ExecuteResult>;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Execute Python code, journaling callback results so a later run can replay
|
|
217
|
+
* them instead of re-invoking the callbacks.
|
|
218
|
+
*
|
|
219
|
+
* Callbacks matching an entry of `options.journal` (by name and canonical
|
|
220
|
+
* arguments, FIFO) return the recorded result without calling the handler;
|
|
221
|
+
* the first miss switches the rest of the run to live calls. A handler can
|
|
222
|
+
* throw {@link SuspendCallback} to halt execution.
|
|
223
|
+
*
|
|
224
|
+
* Never rejects for script failures: check `suspended`, then `error`. Like
|
|
225
|
+
* every call on this Sandbox, it waits for earlier calls to finish first.
|
|
226
|
+
*/
|
|
227
|
+
executeWithJournal(
|
|
228
|
+
code: string,
|
|
229
|
+
options?: ExecuteWithJournalOptions,
|
|
230
|
+
): Promise<ReplayOutcome>;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Set the name of the variable captured as the structured result.
|
|
234
|
+
*
|
|
235
|
+
* After each `execute()`, the variable with this name is read from the
|
|
236
|
+
* script's namespace, JSON-serialized, and returned as
|
|
237
|
+
* `ExecuteResult.result`. Defaults to `"result"`. Takes effect for calls made
|
|
238
|
+
* after it.
|
|
239
|
+
*
|
|
240
|
+
* @param name - The variable name to capture
|
|
241
|
+
*/
|
|
242
|
+
setResultVariable(name: string): Promise<void>;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Replace this sandbox's network policy (`null` disables networking). It
|
|
246
|
+
* applies to connections opened after the call, and doesn't change the
|
|
247
|
+
* default set with the module-level {@link setNetConfig} or other
|
|
248
|
+
* sandboxes. Throws a TypeError if a network isn't a valid CIDR or
|
|
249
|
+
* `maxConnections` isn't a non-negative integer.
|
|
250
|
+
*/
|
|
251
|
+
setNetConfig(config: NetConfig | null): void;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Stop using this sandbox. Its open connections are closed, and every later
|
|
255
|
+
* call rejects. For a `timeoutMs` sandbox the Worker is terminated at once,
|
|
256
|
+
* so a call in flight rejects too; without `timeoutMs` a call in flight runs
|
|
257
|
+
* to the end. Calling it again does nothing. `using` and `await using` call
|
|
258
|
+
* it at the end of their scope.
|
|
259
|
+
*/
|
|
260
|
+
close(): void;
|
|
261
|
+
[Symbol.dispose](): void;
|
|
262
|
+
[Symbol.asyncDispose](): void;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Capture a snapshot of the current Python session state.
|
|
266
|
+
*
|
|
267
|
+
* Returns serialized state (via pickle) that can be restored later
|
|
268
|
+
* with restoreState(). This captures all user-defined variables that can
|
|
269
|
+
* be serialized, and names the others in `skippedGlobals`.
|
|
270
|
+
*
|
|
271
|
+
* @returns Serialized Python state
|
|
272
|
+
* @throws If serialization fails
|
|
273
|
+
*/
|
|
274
|
+
snapshotState(): Promise<StateSnapshot>;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Restore Python session state from a previously captured snapshot.
|
|
278
|
+
*
|
|
279
|
+
* @param snapshot - A snapshot from snapshotState(), or its `data`
|
|
280
|
+
* @throws If deserialization fails
|
|
281
|
+
*/
|
|
282
|
+
restoreState(snapshot: StateSnapshot | Uint8Array): Promise<void>;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Clear all persistent state from the session.
|
|
286
|
+
*/
|
|
287
|
+
clearState(): Promise<void>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Execute Python code in the default sandbox.
|
|
292
|
+
*
|
|
293
|
+
* This is a convenience function backed by one module-wide Sandbox, created on
|
|
294
|
+
* first use, so state from previous calls persists. It shares nothing with
|
|
295
|
+
* Sandbox instances you create yourself; use one of those for isolated
|
|
296
|
+
* execution.
|
|
297
|
+
*
|
|
298
|
+
* @param code - Python source code to execute
|
|
299
|
+
* @returns Captured stdout and stderr
|
|
300
|
+
* @throws If the Python code raises an unhandled exception
|
|
301
|
+
*/
|
|
302
|
+
export function execute(code: string): Promise<ExecuteResult>;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Execute Python code in the default sandbox, journaling callback results.
|
|
306
|
+
* See {@link Sandbox.executeWithJournal}.
|
|
307
|
+
*/
|
|
308
|
+
export function executeWithJournal(
|
|
309
|
+
code: string,
|
|
310
|
+
options?: ExecuteWithJournalOptions,
|
|
311
|
+
): Promise<ReplayOutcome>;
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Set the name of the variable captured as the structured result in the
|
|
315
|
+
* default sandbox (the one behind {@link execute}). See
|
|
316
|
+
* {@link Sandbox.setResultVariable}.
|
|
317
|
+
*
|
|
318
|
+
* @param name - The variable name to capture
|
|
319
|
+
*/
|
|
320
|
+
export function setResultVariable(name: string): Promise<void>;
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The template file tree each new Sandbox copies: `python-stdlib` and an empty
|
|
324
|
+
* `site-packages` directory. Files added here are visible to sandboxes created
|
|
325
|
+
* afterwards; change {@link Sandbox._fileTree} to reach one that exists.
|
|
326
|
+
*
|
|
327
|
+
* @internal This is primarily for use by the demo app and advanced integrations.
|
|
328
|
+
*/
|
|
329
|
+
export const _fileTree: {
|
|
330
|
+
dir: Record<string, unknown>;
|
|
331
|
+
};
|