@earendil-works/djs 0.0.0-stage → 0.1.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.
@@ -0,0 +1,756 @@
1
+ /** A host function callable from the sandbox. Arguments and the result are host values. */
2
+ export type HostFunction = (...args: unknown[]) => unknown;
3
+ /**
4
+ * An async host call as an `AsyncHostFunction`'s hooks see it (D2771): what the script
5
+ * called, and the host's state of the call, which a dump keeps.
6
+ */
7
+ export interface HostCall<S = unknown> {
8
+ /** The VM's call id; a dump keeps it. */
9
+ readonly id: number;
10
+ /** The host function's name, as given in `asyncFunctions`. */
11
+ readonly fn: string;
12
+ readonly args: unknown[];
13
+ /** When this sandbox started (or, after a load, resumed) the call: ms, the host's `Date.now()`. */
14
+ readonly startedAt: number;
15
+ /**
16
+ * The host's state of the call: JSON data that a dump writes with the call (as
17
+ * `save`'s default) and a load gives back to `resume`. Set it at any time; a resumed
18
+ * call starts with the state it was dumped with.
19
+ */
20
+ state: S | undefined;
21
+ /**
22
+ * Aborted when this sandbox stops waiting for the call: the task was suspended into a
23
+ * dump (the call continues from there) or the sandbox was disposed.
24
+ */
25
+ readonly signal: AbortSignal;
26
+ }
27
+ /**
28
+ * An async host function with hooks for continuation dumps (D2771): a task the sandbox
29
+ * drives (`start`, `Sandbox.resume`) can be suspended while calls of it are open, and
30
+ * continued from the dump in another sandbox or process. `S` is the type of the calls'
31
+ * state.
32
+ */
33
+ export interface AsyncHostFunction<S = unknown> {
34
+ /** Starts the call: its result, or a promise of it. `this` is the call. */
35
+ call(this: HostCall<S>, ...args: unknown[]): unknown;
36
+ /** The call's state when a dump is taken: JSON data. Default: `call.state`. */
37
+ save?(call: HostCall<S>): S | undefined;
38
+ /**
39
+ * Continues the call after a load, from the `state` it was dumped with: its result, or
40
+ * a promise of it. Without `resume` the call is made again (`call`); a plain function
41
+ * (no hooks) is not continued: its call rejects.
42
+ */
43
+ resume?(state: S, call: HostCall<S>): unknown;
44
+ }
45
+ export interface ConsoleLike {
46
+ log(...args: unknown[]): void;
47
+ info(...args: unknown[]): void;
48
+ warn(...args: unknown[]): void;
49
+ error(...args: unknown[]): void;
50
+ }
51
+ export interface SandboxOptions {
52
+ /** The compiled djs-wasm module; default: `loadModule()`. */
53
+ module?: WebAssembly.Module;
54
+ /**
55
+ * The dump module (`djs-dump.wasm`: djs-wasm with the `dump` feature) for a `module`
56
+ * without the dumper (packaging B, D2635): `dump`, `Run.dump` and a task's `suspend`
57
+ * and `checkpoint` copy the VM's image to an instance of it. Not needed when `module`
58
+ * has the dumper itself, nor for loading (`load`, `loadRun`, `Sandbox.resume`): a dump
59
+ * is a Script.
60
+ */
61
+ dumpModule?: WebAssembly.Module;
62
+ /** Heap cap in bytes; default: the engine's (4 GiB). */
63
+ memoryLimit?: number;
64
+ /** Fuel per `vm_run` call: how often the host gets control back. Default 1,000,000. */
65
+ fuelSlice?: number;
66
+ /**
67
+ * Fuel budget of one `run`, after which it fails with "out of fuel" (the call is
68
+ * abandoned; the sandbox stays usable). Default: none.
69
+ */
70
+ maxFuel?: number;
71
+ /**
72
+ * Host functions, by identity (D2970): any string (`tools.read`, `@sdk/our-stuff#myTool`),
73
+ * which calls, journals and dumps name the function by. A call parks the VM until the
74
+ * function returns. An identity `ns.member` of two identifiers makes the function member
75
+ * `member` of the namespace object `ns`, which the sandbox makes for its first member.
76
+ * Where scripts find functions and namespaces is `globals` and `bindings`.
77
+ */
78
+ functions?: Record<string, HostFunction>;
79
+ /**
80
+ * Async host functions, by identity (D1374): a call returns a promise in the VM at
81
+ * once and the script goes on, so several calls can be outstanding (`Promise.all`
82
+ * over tools). The function may return a value or a promise. Identities as in `functions`.
83
+ * An `AsyncHostFunction` (`{ call, save, resume }`) adds the hooks that let a task
84
+ * suspended into a dump continue its open calls after a load.
85
+ */
86
+ asyncFunctions?: Record<string, HostFunction | AsyncHostFunction>;
87
+ /**
88
+ * The global bindings of host functions and namespaces: global name -> identity of a
89
+ * function or namespace (`{ tools: "tools", console: "console", myTool:
90
+ * "@sdk/our-stuff#myTool" }`, D2972). Without it, the one written rule: `console`, every
91
+ * namespace and every function whose identity is an identifier are globals of their
92
+ * own names (`tools.read` makes the global `tools`).
93
+ */
94
+ globals?: Record<string, string>;
95
+ /**
96
+ * The bindings every codemode body gets as parameters, after the run's `params`:
97
+ * parameter name -> identity of a function or namespace (`{ myTool:
98
+ * "@sdk/our-stuff#myTool", tools: "tools" }`, D2972). A stand-in for imports: a script
99
+ * that calls `myTool("x")` finds it there. Default: none.
100
+ */
101
+ bindings?: Record<string, string>;
102
+ /** Receives `console.*` calls from the sandbox; default: the host's `console`. */
103
+ console?: ConsoleLike;
104
+ /**
105
+ * The clock `Date.now()` and `new Date()` see, in ms since the epoch (the VM reads no
106
+ * clock itself, D1383): a fixed time (replays) or a function; default `Date.now`.
107
+ * Set before every fuel slice.
108
+ */
109
+ now?: number | (() => number);
110
+ /**
111
+ * The VM's local time zone, in minutes east of UTC (fixed, no DST); default: the
112
+ * host's current offset. Set before every fuel slice.
113
+ */
114
+ timeZoneOffset?: number;
115
+ /**
116
+ * Promises rejected with no handler once the VM's job queue ran empty (as qjs reports
117
+ * them, D1423; a rejected result promise does not count): `"warn"` (default) passes
118
+ * each reason (its display form, `TypeError: x`) to `console.warn`,
119
+ * `"throw"` fails the run with a `threw` SandboxError, `"collect"` keeps them for
120
+ * `takeUnhandledRejections()`.
121
+ */
122
+ unhandledRejections?: "warn" | "throw" | "collect";
123
+ /**
124
+ * Parse the code as TypeScript (types are erased, enums and namespaces run; 0048).
125
+ * Default false.
126
+ */
127
+ typescript?: boolean;
128
+ /**
129
+ * The 64-bit seed of `Math.random` for a new sandbox (a restored one continues its
130
+ * sequence). Default: from `crypto.getRandomValues`. Ignored with `deterministic`.
131
+ */
132
+ randomSeed?: number | bigint;
133
+ /**
134
+ * What `WeakRef` and `FinalizationRegistry` do (D1680): `"collected"` (weak, as the
135
+ * spec intends), `"strong"` (targets never die and cleanup callbacks never run, so no
136
+ * collection is observable) or `"absent"` (the globals are deleted). Default
137
+ * `"collected"`, with `deterministic` `"strong"`.
138
+ */
139
+ weakRefs?: "collected" | "strong" | "absent";
140
+ /**
141
+ * Frozen builtins (D2304): the builtin objects (prototypes, constructors, `Math`, ...)
142
+ * behave as frozen: writes to them fail (a TypeError in strict code), their properties
143
+ * read as non-writable and non-configurable, and `Object.isFrozen` is true. Assigning
144
+ * over an inherited builtin property (`obj.toString = f`, `this.name = ...` in an Error
145
+ * subclass) defines an own property instead of failing, as after SES's lockdown.
146
+ * `globalThis` stays ordinary. Default false (the spec's behavior); a restored sandbox
147
+ * keeps the mode it was created with.
148
+ */
149
+ frozenBuiltins?: boolean;
150
+ /**
151
+ * The scripts a loaded dump may refer to (D2834, D2882): a dump names the codemode body
152
+ * it continues by `$djs.source(name, hash)` instead of carrying its text a second time,
153
+ * and loading it needs that script again. The scripts this sandbox ran are known; for a
154
+ * dump from another process, return the code the script `name` (`BeginOptions.filename`,
155
+ * default `"sandbox.js"`) had. A script that is not the one the dump refers to (another
156
+ * text, parameters or options) is refused.
157
+ */
158
+ sources?: (name: string) => string | undefined;
159
+ /**
160
+ * Dumps write the codemode body's text instead of `$djs.source(name, hash)`: a dump
161
+ * that loads without the script (larger). Default false.
162
+ */
163
+ inlineSources?: boolean;
164
+ /**
165
+ * Run deterministically (D1680): with the same code, parameters and results in the same
166
+ * order, a run does the same thing every time. `Math.random` is seeded from `seed`, the
167
+ * time zone is UTC, weak references are strong (unless `weakRefs` says otherwise), and
168
+ * the clock starts at `startedAt` and moves only when an async host function's result
169
+ * is delivered: to `now` at that moment (never backwards). Sync host functions and
170
+ * fuel slices do not move it. A fixed `now` makes the clock fully reproducible.
171
+ */
172
+ deterministic?: {
173
+ startedAt: number;
174
+ seed: number | bigint;
175
+ };
176
+ /**
177
+ * With `typescript`: TypeScript's legacy `experimentalDecorators` (tsc's decorator
178
+ * semantics and parameter decorators) instead of the standard decorators (0065).
179
+ * Default false.
180
+ */
181
+ experimentalDecorators?: boolean;
182
+ /**
183
+ * With `typescript`: `emitDecoratorMetadata` (`design:type` and friends through
184
+ * `Reflect.metadata`); implies `experimentalDecorators`. Default false.
185
+ */
186
+ emitDecoratorMetadata?: boolean;
187
+ /**
188
+ * Makes `debugger;` a pause (D3050): in an async function, the statement awaits a call
189
+ * of this function, bound as the global `debugger` with the identity `"debugger"`. While
190
+ * it is open, the run is idle at the statement and can be dumped there (a task's
191
+ * `suspend`, `Run.dump`); a dump writes the pause as any open call, and a sandbox with
192
+ * this option that loads it pauses again at the next `debugger;`. Outside async
193
+ * functions the statement does nothing, as without the option. Needs a `module` with the
194
+ * dumper (the dump module), since a dump compiles the script again with the same options.
195
+ */
196
+ debugger?: HostFunction | AsyncHostFunction;
197
+ }
198
+ export type SandboxErrorKind = "threw" | "syntax" | "fuel" | "trap" | "memory" | "misuse" | "diverged" | "suspended";
199
+ /**
200
+ * One entry of a run's journal (D1730, docs/host.md "Journals"): everything a
201
+ * deterministic run took from outside, in order, plus checks. Values are host values
202
+ * (JSON-serializable when the host's values are); a host error is `{ name, message }`.
203
+ * - `run`: the start: the engine build, the code, the parameters and the canonical hash
204
+ * of the VM before the run.
205
+ * - `sync`: a sync host function's call and outcome (console output is not journaled).
206
+ * - `async`: an async host function's result, in delivery order, with the clock it
207
+ * carried.
208
+ * - `check`: the VM was idle (quiescent) after `delivered` results, with `issued` host
209
+ * calls made and this canonical hash.
210
+ * - `end`: the outcome (`ok` false: the error's display text), with the final counts and
211
+ * hash.
212
+ */
213
+ export type JournalEntry = {
214
+ type: "run";
215
+ engine: string;
216
+ code: string;
217
+ params: Record<string, unknown>;
218
+ hash: string;
219
+ } | {
220
+ type: "sync";
221
+ fn: string;
222
+ args: unknown[];
223
+ ok: boolean;
224
+ value: unknown;
225
+ } | {
226
+ type: "async";
227
+ callId: number;
228
+ fn: string;
229
+ args: unknown[];
230
+ ok: boolean;
231
+ value: unknown;
232
+ now: number;
233
+ } | {
234
+ type: "check";
235
+ delivered: number;
236
+ issued: number;
237
+ hash: string;
238
+ } | {
239
+ type: "end";
240
+ ok: boolean;
241
+ value: unknown;
242
+ delivered: number;
243
+ issued: number;
244
+ hash: string;
245
+ };
246
+ /** Options of one run. */
247
+ /** A source position (`Sandbox.callSite`): the script's name, line and column from 1. */
248
+ export interface CallSite {
249
+ file: string;
250
+ line: number;
251
+ column: number;
252
+ }
253
+ export interface RunOptions {
254
+ /** Record the run (deterministic sandboxes only): entries are appended to this array. */
255
+ journal?: JournalEntry[];
256
+ }
257
+ /** An async host call the script made that has no result yet (`Run.calls`). */
258
+ export interface OpenCall {
259
+ /** The VM's call id: `settle` takes it, and a dump keeps it. */
260
+ id: number;
261
+ /** The host function's name, as given in `asyncFunctions` (`sleep`, `tools.read`). */
262
+ fn: string;
263
+ args: unknown[];
264
+ /** A loaded call's state, as dumped (D2770); absent when the dump has none. */
265
+ state?: unknown;
266
+ }
267
+ /** A host call's outcome for `Run.settle`: a value, or (`ok` false) an error to reject with. */
268
+ export interface Settlement {
269
+ ok: boolean;
270
+ value: unknown;
271
+ }
272
+ /** What `Sandbox.load` recreated: the open host calls of a dump (0171, D2890). */
273
+ export interface LoadedDump {
274
+ calls: OpenCall[];
275
+ }
276
+ /**
277
+ * Options of `Sandbox.begin`. A `journal` records the run while it stays in this sandbox:
278
+ * the sync calls, every `settle` in order and a check at every idle point; `replay` runs
279
+ * it like a `runAsync` journal.
280
+ */
281
+ export interface BeginOptions extends RunOptions {
282
+ /**
283
+ * The script's name, as stack traces show it (`sleep.js:4:13`); default `sandbox.js`.
284
+ */
285
+ filename?: string;
286
+ }
287
+ /** Why a sandbox operation failed. `value` is the thrown VM value (kind "threw"). */
288
+ export declare class SandboxError extends Error {
289
+ readonly kind: SandboxErrorKind;
290
+ readonly value?: unknown;
291
+ constructor(kind: SandboxErrorKind, message: string, value?: unknown);
292
+ }
293
+ /**
294
+ * The hash of `$djs.source(name, hash, params)` (D2882, `vm::script_hash`): FNV-1a 64 of
295
+ * the UTF-8 of the parameter names (`,`-joined), the compile option bits in decimal and
296
+ * the source, `\0`-separated, folded to 32 bits (D2839), as 8 hex digits.
297
+ */
298
+ export declare function scriptHash(source: string, params: string, bits: number): string;
299
+ /** A plain dump (D2830): `"djs-dump";`, and whether its text is TypeScript. */
300
+ export declare function plainDump(text: string): {
301
+ typescript: boolean;
302
+ } | undefined;
303
+ /**
304
+ * The default location of djs-wasm: `$DJS_WASM`, else, running from the repository's
305
+ * sources (js/src/*.ts), the `wasm` profile build. The package's entry points have their
306
+ * own defaults (`Sandbox.defaultModule`), the modules shipped in the package (D3106).
307
+ */
308
+ export declare function defaultWasmPath(): string | URL;
309
+ /**
310
+ * Compiles djs-wasm once per location and caches the `WebAssembly.Module` (ARCH: the
311
+ * module is compiled once; every sandbox gets its own instance). A `file:` URL, or a
312
+ * string that is no URL in node, Bun or Deno, is read from the file system; anything else
313
+ * is fetched (in a browser, a relative string is relative to the page).
314
+ */
315
+ export declare function loadModule(location?: string | URL): Promise<WebAssembly.Module>;
316
+ /** What a run in progress needs from its sandbox. */
317
+ interface RunCore {
318
+ /** The open async calls, oldest first, until their results are delivered. */
319
+ open: Map<number, OpenCall>;
320
+ /**
321
+ * A resumed run's result promise, the handle its dump named `run` (freed at the end);
322
+ * 0 for a run this sandbox started, whose result is the entry call's outcome.
323
+ */
324
+ result: number;
325
+ /** Fuel used so far: `maxFuel` is per run, across all its steps. */
326
+ used: number;
327
+ /** A new async call (`runAsync`: call the host function now). */
328
+ onCall?(call: OpenCall, fnId: number): void;
329
+ /** Before every fuel slice (`runAsync`: pass back the results that came in). */
330
+ beforeSlice?(): void;
331
+ advance(): Promise<RunEnd>;
332
+ settle(id: number, s: Settlement): void;
333
+ /** Passes a result back; `at`: a replayed result's clock. */
334
+ deliver(id: number, ok: boolean, value: unknown, at?: number): void;
335
+ /** `states`: the host's state of open calls, by id, as JSON text (D2770). */
336
+ dump(states?: [number, string][]): string;
337
+ }
338
+ type RunEnd = {
339
+ state: "idle";
340
+ } | {
341
+ state: "done";
342
+ value: unknown;
343
+ } | {
344
+ state: "threw";
345
+ error: SandboxError;
346
+ };
347
+ /**
348
+ * A run the host drives itself (docs/host.md "Host-driven runs"; the part 0179
349
+ * needs of 0175): `Sandbox.begin` runs a body to its first idle point, the host settles
350
+ * the open async calls in any order and advances it, and while it is idle it can be
351
+ * dumped (`dump()`) and continued elsewhere (`Sandbox.resume`). The async host functions
352
+ * are never called by the run: `calls` lists them and the host answers, so that a call
353
+ * can outlive the process that issued it. Sync host functions are called as in `run`.
354
+ */
355
+ export declare class Run {
356
+ #private;
357
+ /** "idle": waiting on `calls`; "done": `value` is the result; "threw": `error`. */
358
+ state: "idle" | "done" | "threw";
359
+ value: unknown;
360
+ error: SandboxError | undefined;
361
+ /** Made by `Sandbox.begin` and `Sandbox.resume`. */
362
+ constructor(core: RunCore);
363
+ /** The async host calls waiting for a result, oldest first. */
364
+ get calls(): OpenCall[];
365
+ /**
366
+ * Delivers the result of open call `id`. The VM applies it at the next `advance`, after
367
+ * the results settled before it (D1670).
368
+ */
369
+ settle(id: number, s: Settlement): void;
370
+ /**
371
+ * Runs to the next idle point (or the end): applies the settled results, and calls the
372
+ * sync host functions the script calls meanwhile (awaiting them if they return
373
+ * promises). New async calls show up in `calls`.
374
+ */
375
+ advance(): Promise<void>;
376
+ /**
377
+ * The waiting run as a dump (a plain Script, D2830; docs/plain-js-dumps.md): its
378
+ * suspended activations, open calls and data as JavaScript, for `loadRun` in another
379
+ * sandbox or process. Only while idle, and only with `frozenBuiltins` (D2836). `states` are the host's states of open calls, by id (JSON
380
+ * data, D2770): the dump writes each with its call, and the load gives it back
381
+ * (`OpenCall.state`).
382
+ */
383
+ dump(states?: Record<number, unknown>): string;
384
+ /** Records how a step ended. */
385
+ end(e: RunEnd): void;
386
+ }
387
+ /** A call a task waits for: the hooks' view of it, and how to stop waiting. */
388
+ interface LiveCall {
389
+ call: HostCall;
390
+ abort: AbortController;
391
+ }
392
+ /** A dump a task was asked for: `suspend` (the task stops) or `checkpoint`. */
393
+ interface DumpRequest {
394
+ stop: boolean;
395
+ resolve(text: string): void;
396
+ reject(e: unknown): void;
397
+ }
398
+ /**
399
+ * A run the sandbox drives (D2771): `Sandbox.start` begins one, `Sandbox.resume`
400
+ * continues one from a dump. It calls the async host functions as the script calls them
401
+ * and passes their results back, like `runAsync`; `result` is the outcome. While it waits
402
+ * on host calls it can be suspended (`suspend()`): the sandbox writes it as a dump, with
403
+ * each open call's state from its function's `save` hook, and stops. `Sandbox.resume`
404
+ * continues the dump in a new sandbox, any time later and in any process: each open call
405
+ * is continued by its function's `resume` hook (or made again, or rejected).
406
+ */
407
+ export declare class Task {
408
+ #private;
409
+ /** The sandbox the task runs in (dispose it when done). */
410
+ readonly sandbox: Sandbox;
411
+ /**
412
+ * The run's result. Rejects with a SandboxError: `threw` for an exception, `suspended`
413
+ * when the task was suspended into a dump (this rejection counts as handled).
414
+ */
415
+ readonly result: Promise<unknown>;
416
+ /** Made by `Sandbox.start` and `Sandbox.resume`. */
417
+ constructor(sandbox: Sandbox, result: Promise<unknown>, calls: Map<number, LiveCall>, request: (r: DumpRequest) => void);
418
+ /** The async host calls the task waits for, oldest first. */
419
+ get calls(): HostCall[];
420
+ /**
421
+ * Suspends the task at its next idle point (no script work left, only host calls to
422
+ * wait for): calls `save` for every open call, dumps the run with those states and
423
+ * stops. The open calls' signals are aborted and their results ignored; `result`
424
+ * rejects with kind `suspended`. Resolves to the dump (a Script, for
425
+ * `Sandbox.resume`). Rejects, and the task goes on, when the run cannot be dumped (or a
426
+ * `save` throws); rejects when the run ends first.
427
+ */
428
+ suspend(): Promise<string>;
429
+ /** `suspend` without stopping: a dump at the next idle point, and the task goes on. */
430
+ checkpoint(): Promise<string>;
431
+ }
432
+ /**
433
+ * A VM image moving between the main module and the dump module (packaging B, D2635):
434
+ * the arena as is, with what the sandbox keeps beside it.
435
+ */
436
+ interface RawImage {
437
+ image: Uint8Array;
438
+ functions: string[];
439
+ clock?: number;
440
+ namespaces: [string, number][];
441
+ fnHandles: [number, number][];
442
+ globals: string[];
443
+ }
444
+ export declare class Sandbox {
445
+ private readonly x;
446
+ private readonly module;
447
+ /** The VM; replaced when the dump module's sandbox takes another image (`takeImage`). */
448
+ private vm;
449
+ private readonly opts;
450
+ /** Host function names by id - 1. */
451
+ private readonly fnNames;
452
+ private readonly fns;
453
+ /** Async host functions with hooks (D2771), by id. */
454
+ private readonly hooks;
455
+ private readonly atomCache;
456
+ private readonly rejections;
457
+ private readonly cons;
458
+ private dead;
459
+ /** Deterministic mode: the VM's clock, moved only by async results (never backwards). */
460
+ private clock;
461
+ /** The journal of the current run, being recorded or replayed (D1730). */
462
+ private journal;
463
+ /** The first hook a dump's load could not serve (D3001). */
464
+ private hookError;
465
+ /** Namespace objects this sandbox made for dotted function names (`tools`), as handles. */
466
+ private readonly namespaces;
467
+ /** The host function objects by id, as handles: one per identity (D2970). */
468
+ private readonly fnHandles;
469
+ private readonly fnAsync;
470
+ /** The globals the sandbox bound to host functions and namespaces (a dump's host globals). */
471
+ private readonly globalNames;
472
+ /** The host-driven run in progress (`begin`, `resume`), until it ends. */
473
+ private active;
474
+ /** The task in progress (`start`, `resume`): ends it when the sandbox is disposed. */
475
+ private cancelTask;
476
+ /**
477
+ * Where `create` and the constructor find the module when `opts.module` is not given:
478
+ * here `defaultWasmPath()`; each entry point of the package overrides it with its own
479
+ * module (D3106).
480
+ */
481
+ static defaultModule(): string | URL;
482
+ /** A new sandbox; loads (and caches) the default module unless `opts.module` is given. */
483
+ static create(opts?: SandboxOptions): Promise<Sandbox>;
484
+ /**
485
+ * Continues a task from its dump (`Task.suspend()`, or a host-driven `Run.dump()`) in
486
+ * a new sandbox made from `opts` (the same host functions, by name, as the one that
487
+ * dumped): `(await Sandbox.resume(dump, opts)).result`. See `resume`. A dump is code:
488
+ * resume only dumps you trust.
489
+ */
490
+ static resume(dump: string, opts?: SandboxOptions): Promise<Task>;
491
+ /**
492
+ * Loads a dumped run into this sandbox (a fresh one) and drives it (D2771): each open
493
+ * call is continued by its function's `resume` hook with the state it was dumped with,
494
+ * made again (`call`) when the function has no `resume`, and rejected when it is a
495
+ * plain function (the sandbox cannot know whether calling it again is safe).
496
+ */
497
+ resume(dump: string): Task;
498
+ /**
499
+ * Loads a dumped run into this sandbox (a fresh one) for the host to drive: the run is
500
+ * idle, waiting on the same open calls with the same ids (`run.calls`, with their
501
+ * states); the host settles them, issues them again or rejects them. No hooks run.
502
+ */
503
+ loadRun(dump: string): Run;
504
+ /** Loads a dump that holds a run: its open calls and the run's result promise. */
505
+ private loadRunDump;
506
+ /**
507
+ * A new sandbox (its own wasm instance and VM), synchronously: needs a compiled module,
508
+ * `opts.module` or one `loadModule()` has finished loading. (`raw`: internal, a VM image
509
+ * of this process moving to the dump module, D2635.)
510
+ */
511
+ constructor(opts?: SandboxOptions, raw?: RawImage);
512
+ /**
513
+ * Defines a host function with identity `name` and binds it as a global by the written
514
+ * rule (`SandboxOptions.globals`): `tools.read` as member `read` of the global namespace
515
+ * `tools`, an identifier as the global of that name, anything else nowhere.
516
+ */
517
+ defineFunction(name: string, f: HostFunction): void;
518
+ /** Defines an async host function (see `SandboxOptions.asyncFunctions`), bound as `defineFunction` does. */
519
+ defineAsyncFunction(name: string, f: HostFunction | AsyncHostFunction): void;
520
+ /** Makes host function `name` and, for `ns.member`, adds it to the namespace `ns`. */
521
+ private addFunction;
522
+ /** The namespace object `ns`, made on first use (D2632); bound nowhere by itself. */
523
+ private namespace;
524
+ /** The written rule: the globals the sandbox binds without `SandboxOptions.globals`. */
525
+ private defaultGlobals;
526
+ private bindDefault;
527
+ /** The object of a namespace or host function by identity (D2970), as a handle the sandbox owns. */
528
+ private hostValue;
529
+ private bindGlobal;
530
+ /** Defines a global variable holding a copy of `value` (sent through the wire format). */
531
+ setGlobal(name: string, value: unknown): void;
532
+ /**
533
+ * Runs `code` as a codemode function body (`return` gives the result) with the
534
+ * `params` as its parameters (sent through the wire format). `console` is a global. Host
535
+ * functions must return synchronously; use `runAsync` for ones that return promises. A
536
+ * promise as the result is unwrapped (fulfilled: its value; rejected: thrown).
537
+ */
538
+ run(code: string, params?: Record<string, unknown>, opts?: RunOptions): unknown;
539
+ /**
540
+ * Starts `code` as a codemode function body (as `run`) and runs it to its first idle
541
+ * point or its end, for the host to drive (`Run`): async host functions are not called
542
+ * but listed in `run.calls`. Sync host functions must return synchronously here (as in
543
+ * `run`); `advance` awaits them. A compile error throws; an exception in the script is
544
+ * `run.state === "threw"`. One run at a time: the sandbox runs nothing else until it
545
+ * ends.
546
+ */
547
+ begin(code: string, params?: Record<string, unknown>, opts?: BeginOptions): Run;
548
+ /**
549
+ * Starts `code` as a codemode function body (as `runAsync`) in a task the sandbox
550
+ * drives (D2771): it calls the async host functions as the script calls them, and
551
+ * `task.result` is the outcome. Unlike `runAsync`, the task can be suspended into a
552
+ * dump while it waits (`task.suspend()`) and continued later (`Sandbox.resume`). The
553
+ * first step runs now: a compile error throws. One task or run at a time.
554
+ */
555
+ start(code: string, params?: Record<string, unknown>, opts?: BeginOptions): Task;
556
+ /**
557
+ * Drives `core` as a task: continues the `loaded` calls of a resumed run through their
558
+ * hooks, calls new async host functions as the script calls them, delivers results at
559
+ * the next slice, and serves `suspend`/`checkpoint` at idle points.
560
+ */
561
+ private task;
562
+ /** Makes a run, waiting on `calls`, with its result promise `result` (0: the entry call's). */
563
+ private startRun;
564
+ /** One step of a host-driven run: `drive` until idle (recording a check) or the end. */
565
+ private step;
566
+ /**
567
+ * The run loop of `run`, `runAsync` and `replay`, on top of the host-driven run (`drive`
568
+ * to each idle point): calls the async host functions as soon as the script calls them
569
+ * (or, replaying, takes their results from the journal), passes their results back
570
+ * before the next fuel slice, and at an idle point waits for the next one. Yields a
571
+ * promise when the host must wait (only when `async`), returns the result.
572
+ */
573
+ private auto;
574
+ /** Records a `check` entry at an idle point, when recording. */
575
+ private recordCheck;
576
+ /** The run has ended: frees its result handle; the sandbox can run again. */
577
+ private endRun;
578
+ /**
579
+ * `Run.dump()`: the waiting run as a dump, its result promise named "run" (handle
580
+ * 0: the entry call's outcome, which takes no handle, so a dump leaves the heap as it
581
+ * was and a journal's later checks still replay).
582
+ */
583
+ private dumpRun;
584
+ /**
585
+ * The VM's global bindings (or the ones in `names`) as JavaScript that recreates them
586
+ * when `load`ed into another sandbox, also of a later djs (docs/functions-as-code.md):
587
+ * functions as their source text, closures and data as code, builtins by name. The
588
+ * host's functions and `console` are not written: the loading sandbox provides them
589
+ * under the same names. Needs `frozenBuiltins` (D2836). Throws a "misuse" error listing
590
+ * every value that cannot be written, by its path (a weak collection, a Proxy, ...).
591
+ */
592
+ dump(names?: string[]): string;
593
+ /**
594
+ * `vm_dump`. staging: the globals the host defines and the names to dump (comma
595
+ * separated, a line each), then this sandbox's host function names (tab separated, id
596
+ * order: an open call or a host function held as a value is written by name), then
597
+ * the values to name for the host (`handle:name`, tab separated), then the host roots
598
+ * (the namespace objects, `handle:name`, written as `$djs.host(name)`, D2971), then the open calls'
599
+ * states (`id:bytes`, tab separated) followed by their JSON texts (D2770).
600
+ */
601
+ private dumpText;
602
+ /**
603
+ * Runs `f` on a sandbox with the dumper: this one, or (with `opts.dumpModule`, D2635)
604
+ * a transient sandbox in the dump module made from this VM's image. The image travels
605
+ * as is, so a dump leaves this VM untouched (D2633).
606
+ */
607
+ private viaDumper;
608
+ /** The dump module's sandbox: takes the main sandbox's image into the same VM. */
609
+ private takeImage;
610
+ /** Replaces this sandbox's VM with an image (from the dump module). */
611
+ private replaceVm;
612
+ /**
613
+ * A plain dump's `$djs.source(name, hash, params)` (D2882): the codemode body `name`
614
+ * compiled again as this sandbox runs bodies, the one whose hash is the dump's (the
615
+ * `sources` option's code first, then the scripts this process ran, newest first).
616
+ */
617
+ private serveSource;
618
+ /**
619
+ * A plain dump's `$djs.host(name)` (D2971): the object this sandbox made for host
620
+ * function `name` (its identity) or for the namespace `name` (`tools`, `console`).
621
+ */
622
+ private serveHost;
623
+ /**
624
+ * A hook that cannot be served: its call throws, and the load fails once the Script ran
625
+ * (a hook the dump calls inside a continuation, D3001, throws into its activation, not
626
+ * the Script).
627
+ */
628
+ private failHook;
629
+ /** `vm_dump` in this sandbox's own module. */
630
+ private dumpHere;
631
+ /**
632
+ * Loads a dump (the text of `dump()`): runs it as a Script (D2890), so its bindings
633
+ * become globals of this sandbox. A dump is code: load only dumps you trust, into a
634
+ * fresh sandbox. Not recorded in a journal. Returns the open host calls the dump
635
+ * recreated (its open calls name this sandbox's host functions); `Sandbox.resume`
636
+ * continues a dumped run. The dumper is not needed.
637
+ */
638
+ load(dump: string): LoadedDump;
639
+ /** Loads a dump: the open calls it recreated and the handles it named (the caller frees them). */
640
+ private loadDump;
641
+ /**
642
+ * Loads a plain dump (D2830): runs it as a Script with the hooks in `$djs` (D2890), to
643
+ * its first stop that is not a sync host call (idle, or done); the hooks are served only
644
+ * meanwhile. Then the sandbox deletes `$djs` and the dump's `$dump` (D2916).
645
+ * `$djs.resume(id, name, args, state)` opens the dump's call `id` of host function
646
+ * `name` again under its id (`vm_open_call`): one of the calls returned, with its state;
647
+ * `$djs.seed(lo, hi, next)` sets `Math.random`'s state and, once the dump has loaded,
648
+ * the id of the next call (D2897). Other sync host functions are called
649
+ * (they must return synchronously); async calls the Script makes are new calls, which
650
+ * the run that follows reports. The Script's completion value, when a promise, is the
651
+ * handle "run". The dumper is not needed: a plain dump is code.
652
+ */
653
+ private loadPlain;
654
+ /**
655
+ * Like `run`, but awaits host functions that return promises: a sync one keeps the VM
656
+ * parked meanwhile, async ones run concurrently while the script goes on.
657
+ */
658
+ runAsync(code: string, params?: Record<string, unknown>, opts?: RunOptions): Promise<unknown>;
659
+ /**
660
+ * Replays a recorded run (D1730): runs the journal's code and parameters, answers every
661
+ * host call from the journal instead of calling the host functions (console output
662
+ * still goes to the console), delivers the async results in the recorded order with
663
+ * their clocks, and checks the calls the script makes and the canonical hashes at the
664
+ * start, at every recorded idle point and at the end. The sandbox must be deterministic
665
+ * and in the state the recording one was in (a fresh one created the same way, given the
666
+ * same runs before); the fuel slice may differ.
667
+ * Returns the run's result; a mismatch is a `diverged` SandboxError.
668
+ */
669
+ replay(journal: JournalEntry[]): Promise<unknown>;
670
+ /** The VM's canonical hash (D1730) as 16 hex digits: its state, independent of collections. */
671
+ canonicalHash(): string;
672
+ private buildHash;
673
+ /** Starts recording a run into `opts.journal`, if given. */
674
+ private beginJournal;
675
+ /**
676
+ * After a failed run: drops the VM's call if it is still in one (out of fuel, or a host
677
+ * side error escaped the loop), so the next run starts clean. Not after a trap: the
678
+ * instance is gone then.
679
+ */
680
+ private abandon;
681
+ /**
682
+ * The VM's arena: a view into wasm memory, valid until the next call into the sandbox
683
+ * (the arena may move when it grows). For inspecting the heap with `LAYOUT` (the docs'
684
+ * live examples walk it cell by cell).
685
+ */
686
+ heapView(): Uint8Array;
687
+ /**
688
+ * Where the run waits on open host call `id` (D3054): the `await` (or `yield`) of the
689
+ * innermost suspended activation waiting on the call, directly or through promise
690
+ * combinators. `file` is the name the code was compiled as: the run's `filename` for
691
+ * the code it started with, the dump loader's name (`dump.js`) for code a dump carried
692
+ * (a run continued from a dump runs the dump's text). Undefined when the call is not
693
+ * open or nothing waits on it at a known position, and in modules without the dumper
694
+ * (the `dump` feature), where it is not built.
695
+ */
696
+ callSite(id: number): CallSite | undefined;
697
+ /** Collects garbage now. False if the heap could not reserve the to-space. */
698
+ collect(): boolean;
699
+ /** The arena's current size in bytes. */
700
+ get heapSize(): number;
701
+ /** Releases the VM. The sandbox cannot be used afterwards. A task in progress ends (`misuse`). */
702
+ dispose(): void;
703
+ /** Panics inside the module on purpose (tests the trap path). */
704
+ debugTrap(): void;
705
+ private alive;
706
+ /** Runs `f` and turns a wasm trap into a SandboxError; the sandbox is dead afterwards. */
707
+ private guard;
708
+ private check;
709
+ /** Copies bytes into the staging buffer. */
710
+ private stage;
711
+ private stageText;
712
+ private hostFunction;
713
+ private defineOn;
714
+ /** A handle for a host value (0 for undefined). */
715
+ private toHandle;
716
+ private arena;
717
+ private read;
718
+ private readHandle;
719
+ private display;
720
+ /** Compiles `code` and starts the call; returns nothing (the VM holds the call). */
721
+ private enter;
722
+ /**
723
+ * The run loop to the next idle point or the end, as a generator: yields a promise when
724
+ * the host must wait for a sync host function (only when `async`), returns the result,
725
+ * or `IDLE` when only async host calls are left (they are in `run.open`).
726
+ */
727
+ private drive;
728
+ /**
729
+ * The unhandled rejections collected so far (`unhandledRejections: "collect"`), as host
730
+ * values; forgets them.
731
+ */
732
+ takeUnhandledRejections(): unknown[];
733
+ /** Reports the VM's unhandled rejections per `opts.unhandledRejections` (D1423). */
734
+ private checkRejections;
735
+ /**
736
+ * Passes the host clock and time zone to the VM (before every slice). Not in
737
+ * deterministic mode: there the clock moves only with async results (`complete`).
738
+ */
739
+ private setClock;
740
+ /** Calls host function `fnId` with arguments read from the VM; `call` is `this` for hooks. */
741
+ private invoke;
742
+ /** The async host calls made since the last check (`vm_new_host_calls`). */
743
+ private newHostCalls;
744
+ /** The function and arguments of pending host call `callId`. */
745
+ private callInfo;
746
+ /** Reads `[call_id, fn_id, this, args...]` from the out buffer. */
747
+ private readCall;
748
+ /**
749
+ * Passes a host call's outcome back: a value (through the wire format), or an error
750
+ * thrown at the call (sync) or rejecting its promise (async), as an Error of the same
751
+ * kind with the host error's message. A value the wire format cannot carry becomes
752
+ * such an error too.
753
+ */
754
+ private complete;
755
+ }
756
+ export {};