@daugasauron/dolly 0.1.5 → 0.1.6

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.
Files changed (76) hide show
  1. package/README.md +51 -78
  2. package/coi-serviceworker.js +23 -8
  3. package/dist/dolly-build-id.mjs +1 -1
  4. package/dist/dolly-image-build-id.mjs +1 -1
  5. package/dist/dolly-process-0.wasm +0 -0
  6. package/dist/dolly-process-abi.mjs +1 -1
  7. package/dist/dolly-process-dso.mjs +19 -11
  8. package/dist/dolly-process-worker.mjs +88 -33
  9. package/dist/dolly-runtime-worker.mjs +1544 -0
  10. package/dist/dolly.wasm +0 -0
  11. package/host/dso/dso.mjs +2 -2
  12. package/host/dso/module.json +4 -1
  13. package/host/http/abi.mjs +3 -1
  14. package/host/http/broker.mjs +17 -7
  15. package/host/http/http.mjs +19 -5
  16. package/host/http/module.json +3 -1
  17. package/host/http/policy.mjs +13 -6
  18. package/host/http/refusals.mjs +77 -0
  19. package/host/input/abi.mjs +1 -1
  20. package/host/modules.mjs +64 -23
  21. package/host/nena/module.json +2 -1
  22. package/host/nena/nena.mjs +2 -0
  23. package/host/packages/abi.mjs +27 -0
  24. package/host/packages/module.json +6 -4
  25. package/host/packages/packages.mjs +23 -8
  26. package/host/packages/service.mjs +46 -20
  27. package/host/packages/transport.mjs +123 -0
  28. package/host/packages/worker.mjs +13 -9
  29. package/host/requirements.mjs +1 -1
  30. package/host/runtime/abi.mjs +32 -0
  31. package/host/runtime/lease-bridge.mjs +13 -8
  32. package/host/runtime/module.json +3 -3
  33. package/host/runtime/runtime.mjs +3 -3
  34. package/host/snapshot/abi.mjs +2 -1
  35. package/host/snapshot/module.json +1 -1
  36. package/host/snapshot/snapshot.mjs +80 -57
  37. package/host/snapshot/transport.mjs +10 -3
  38. package/host/snapshot/ui.mjs +64 -39
  39. package/host/stack/module.json +1 -0
  40. package/host/stack/stack.mjs +1 -1
  41. package/host/threads/module.json +6 -1
  42. package/host/threads/threads.mjs +32 -13
  43. package/package.json +2 -2
  44. package/{src → page}/browser.mjs +37 -23
  45. package/page/generated.mjs +8 -0
  46. package/{src → page}/image-entry.mjs +10 -6
  47. package/{src → page}/image-inputs.mjs +11 -0
  48. package/{src → page}/image-names.mjs +2 -2
  49. package/{src → page}/kernel-plugin.mjs +1 -1
  50. package/{src → page}/pack-store.mjs +1 -1
  51. package/{src → page}/packaged-snapshot.mjs +56 -46
  52. package/{src → page}/process-constants.mjs +72 -15
  53. package/{src → page}/process-supervisor.mjs +155 -227
  54. package/{src → page}/snapshot-records.mjs +63 -44
  55. package/{src → page}/static-asset.mjs +36 -20
  56. package/{src → page}/version.mjs +1 -1
  57. package/{src → page}/worker-image.mjs +20 -9
  58. package/{src → site}/build-worker.mjs +36 -25
  59. package/{src → site}/custom-image.mjs +2 -10
  60. package/{src → site}/dollyfile-graph.mjs +1 -1
  61. package/{src → site}/dollyfile-view.mjs +1 -1
  62. package/{src → site}/image-artifact.mjs +9 -8
  63. package/{src → site}/image-build.mjs +4 -4
  64. package/{src → site}/image-builder.mjs +5 -4
  65. package/site/offline.mjs +53 -0
  66. package/{src → site}/session-store.mjs +47 -38
  67. package/src/runtime-worker.mjs +0 -57
  68. /package/{src → page}/build-log.mjs +0 -0
  69. /package/{src → page}/images.mjs +0 -0
  70. /package/{src → page}/page-chords.mjs +0 -0
  71. /package/{src → page}/page-indicators.mjs +0 -0
  72. /package/{src → page}/process-abi.mjs +0 -0
  73. /package/{src → page}/sha256.mjs +0 -0
  74. /package/{src → page}/terminal-text.mjs +0 -0
  75. /package/{src → page}/wasm-interface.mjs +0 -0
  76. /package/{src → site}/alphabetical.mjs +0 -0
package/README.md CHANGED
@@ -2,16 +2,17 @@
2
2
 
3
3
  Dolly is a POSIX-like userspace for wasm64 that runs in a browser tab: a Wasm
4
4
  kernel, programs in private Wasm memories, and host modules that bridge it to
5
- the browser. These npm packages put it in a page of your own. Their first use is
6
- an agent in place of a web frontend (`docs/agent-frontend.md` in the repository).
5
+ the browser. These npm packages put it in a page of your own: this document is
6
+ `boot()` and the page around it. [Dolly from npm](npm.md) is the guide to the
7
+ packages and to `dolly-build`, which builds an image of your own: a coding agent
8
+ with your tools and skills, as a static site.
7
9
 
8
10
  | Package | What it holds |
9
11
  | --- | --- |
10
12
  | `@daugasauron/dolly` | The runtime: `boot()`, the kernel, the process runtime, its image builder, and the host modules that are its own machinery (`runtime`, `http`, `threads`, `dso`, `stack`, `nena`, `snapshot`, `packages`) |
11
13
  | `@daugasauron/dolly-host-NAME` | One device host module each: `display` (Ghostty's terminal and programs' frames), `input`, `gpu`, `audio`, `microphone`, `touch`, `download` (as `dolly-host-save`: npm takes no new name with the word), `upload`, `html`, `sockets`, `pty` |
12
- | `@daugasauron/dolly-image-NAME` | An image's directory for `boot()`: `default` (a shell, its text tools, the terminal, curl, `amy`, `nena`, `ps`, `html`), `pi-minimal` (the Pi coding agent with JavaScript, a shell, POSIX tools, curl, ripgrep and fd, on `display`, `input`, `download` and `html` alone: the README's "Pi with npm"), `python-data` (`default` with Python 3.14, pip, the C compiler, NumPy, pandas, Matplotlib and seaborn). Its README gives the install line and the `FROM` row of a recipe built on it. Its `amy-index.txt` lists the packages the image holds, so `amy list` and `amy info` describe them |
13
- | `@daugasauron/dolly-package-NAME` | A package for an image of your own: `git` (with diff and patch), `cc` (the C and C++ compiler with Make), `python` (3.14 with pip; it brings `libffi` and `cc`), `numpy`, `pandas`, `matplotlib`, `seaborn` and `python-dateutil`. Its directory holds its catalog, metadata, packs and recipes, as an image's does, and nothing to boot; it depends on the packages it installs, so npm installs them with it |
14
- | `@daugasauron/dolly-build` | The `dolly-build` command: a project with an image of your own, `FROM` `pi-minimal` with the packages it `INSTALL`s, built in a browser into a site a static host serves ("Your own image" below) |
14
+ | `@daugasauron/dolly-image-NAME` | An image's directory for `boot()`: `default` (a shell, its text tools, the terminal, curl, `amy`, `nena`, `ps`), `pi-minimal` (the Pi coding agent with JavaScript, a shell, POSIX tools, curl, ripgrep and fd, on `display`, `input`, `download` and `html` alone: the README's "Pi with npm"), `python-data` (`default` with Python 3.14, pip, the C compiler, NumPy, pandas, Matplotlib and seaborn). Its README gives the install line and the `FROM` row of a recipe built on it. Its `amy-index.txt` lists the packages the image holds, so `amy list` and `amy info` describe them |
15
+ | `@daugasauron/dolly-package-NAME`, `@daugasauron/dolly-build` | Packages for an image of your own, and the command that builds it ([Dolly from npm](npm.md)) |
15
16
 
16
17
  ```sh
17
18
  npm install @daugasauron/dolly @daugasauron/dolly-image-default \
@@ -33,7 +34,7 @@ in `.yarnrc.yml`: Plug'n'Play leaves no files to serve.
33
34
  <meta charset="utf-8">
34
35
  <main id="dolly" style="position:fixed;inset:0"></main>
35
36
  <script type="module">
36
- import { boot } from "./node_modules/@daugasauron/dolly/src/browser.mjs";
37
+ import { boot } from "./node_modules/@daugasauron/dolly/page/browser.mjs";
37
38
  import { images, image, modules } from "./node_modules/@daugasauron/dolly-image-default/index.mjs";
38
39
  const dolly = await boot(document.querySelector("#dolly"), { images, image, modules });
39
40
  </script>
@@ -50,7 +51,7 @@ the page's.
50
51
 
51
52
  | Option | Meaning |
52
53
  | --- | --- |
53
- | `images` | URL of an image directory: `dist/dolly-images.json`, the images' metadata and packs, their recipes, all data. `@daugasauron/dolly-image-default`'s `index.mjs` exports its own. A page has one image directory. The packs an image reads stay in the origin's private storage, so it starts again without downloading them (`src/pack-store.mjs`) |
54
+ | `images` | URL of an image directory: `dist/dolly-images.json`, the images' metadata and packs, their recipes, all data. `@daugasauron/dolly-image-default`'s `index.mjs` exports its own. A page has one image directory, which is also `amy`'s package server unless a session names another (`amy server URL`, [Dollyfile](dollyfile.md)). The packs an image reads stay in the origin's private storage, so it starts again without downloading them (`page/pack-store.mjs`) |
54
55
  | `image` | The image to run, by name |
55
56
  | `modules` | URLs of the `module.json` of each host module the page has. The registry loads these and no other; an image's `REQUIRES HOST` must be among them |
56
57
  | `site` | Optional: the URL of a site that serves Dolly's own pages (`session/`, `sessions/`, `docs/`), as Dolly's site does. Given, a saved session names itself in the tab's URL as the site's session route and modules link to those pages |
@@ -80,9 +81,10 @@ WebGPU adapter, a device that fails to open with `ENODEV`.
80
81
 
81
82
  ## Colours
82
83
 
83
- The terminal's colours and the popups' are the page's, not the image's. Set
84
- these on the element (or above it) in your style sheet to match your page;
85
- unset, they are Dolly's:
84
+ The terminal's colours, the popups' and those of the rest of Dolly's own UI in
85
+ the element (the boot log, the status line, the dialogs, the notices and the
86
+ indicators) are the page's, not the image's. Set these on the element (or above
87
+ it) in your style sheet to match your page; unset, they are Dolly's:
86
88
 
87
89
  ```css
88
90
  #dolly {
@@ -98,7 +100,9 @@ and bright yellow (`-3`, `-11`) the accent, bright black (`-8`) `#77736c` and
98
100
  the others the text's colour, so a program's red shows as its text does. A
99
101
  value may be any CSS colour; one that is not counts as unset. The terminal
100
102
  takes them at boot and whenever they change, by any rule or media query; a
101
- popup as it opens.
103
+ popup as it opens. Dolly's UI draws its quieter lines in bright black (`-8`),
104
+ its raised and sunken parts in mixes of the background and the text, and an
105
+ alert in `#e98773`.
102
106
 
103
107
  ## Popups
104
108
 
@@ -121,6 +125,31 @@ the popup its bytes; without one, name fonts the system has. The page reads the
121
125
  properties each time a popup opens. Dolly's site sets
122
126
  `"Dolly IosevkaTerm SemiBold", monospace` and its font file.
123
127
 
128
+ ## A terminal into the session
129
+
130
+ An image that declares `console@0` (`@daugasauron/dolly-host-console`) gives
131
+ boot()'s handle `openConsole({ columns, rows, argv })`: a terminal the page
132
+ holds, with `argv`'s program started on it, for something other than the
133
+ page's own terminal to use, such as a socket to another machine. It resolves
134
+ to `{ output, write(bytes), resize(columns, rows), close(), exited }`:
135
+ `output` is a `ReadableStream` of what the programs show, which ends when the
136
+ last of them lets go of the terminal; `write` takes bytes or text as typed,
137
+ Ctrl+C included, and resolves to how many it took, fewer while the input is
138
+ full; `exited` is the program's status. The terminal is a pseudo-terminal's
139
+ ([process model](process-model.md#pseudo-terminals)). With `pipes: true` in
140
+ place of the size, the program's standard streams are pipes: `write` is its
141
+ input byte for byte, `endInput()` ends it, `interrupt()` is its Ctrl+C (SIGINT
142
+ to the program, as a terminal's interrupt key), and `output` and `errors` are
143
+ its standard output and error apart.
144
+
145
+ ```js
146
+ const shell = await dolly.openConsole({ columns: 100, rows: 30, argv: ["/bin/slop"] });
147
+ await shell.write("echo hello\r");
148
+ const sort = await dolly.openConsole({ pipes: true, argv: ["/bin/sort"] });
149
+ await sort.write("b\na\n");
150
+ await sort.endInput();
151
+ ```
152
+
124
153
  ## Serving
125
154
 
126
155
  - Serve the installed packages as they lie: the runtime finds its own files
@@ -133,76 +162,20 @@ properties each time a popup opens. Dolly's site sets
133
162
  as `application/json`: a browser imports a host module's manifest and an image
134
163
  directory's catalog and metadata only with that type.
135
164
  - No bundler is needed: these are ES modules, Workers and Wasm files.
165
+ - A Content-Security-Policy that Dolly runs under, with no `'unsafe-inline'` or
166
+ `'unsafe-eval'`: `default-src 'self'; script-src 'self' 'wasm-unsafe-eval' blob:;
167
+ worker-src 'self' blob:; img-src 'self' blob:; object-src 'none'; base-uri 'none'`,
168
+ with `connect-src` naming the origins the HTTP policy allows (or their relay).
169
+ WebAssembly needs `'wasm-unsafe-eval'`; processes start in Workers from `blob:` URLs
170
+ of code fetched once, so a program starts without a request. Neither boot() nor a
171
+ host module writes a script or style element (the popup is a frame in which no
172
+ script runs), so the page's own script and style, as files, are all the policy
173
+ admits ([test](../test/csp-browser.mjs)).
136
174
 
137
175
  ## Your own image
138
176
 
139
- `dolly-build` builds a Dollyfile (`docs/dollyfile.md` in the repository)
140
- `FROM` the installed `pi-minimal`, with the packages whose directories are
141
- installed beside it, into a site: a directory that any static host serves as it
142
- lies. The build runs in a headless browser, with the installed runtime's own
143
- builder, as the catalog's images are built; the command serves the packages to
144
- it on 127.0.0.1 and writes what it hands back. Nothing of it, nor of the site,
145
- the page or the image, fetches from the network: after `npm install`, the
146
- packages hold every file. The build's browser reaches that server and the URLs
147
- of the recipe's own `SOURCE` rows alone; its own requests (its vendor's updates,
148
- clock and autofill) go to a proxy of the command's that refuses them.
149
-
150
- ```sh
151
- npm install @daugasauron/dolly-build @daugasauron/dolly-package-git
152
- npx dolly-build init my-pi git
153
- npx dolly-build Dollyfile-my-pi my-pi
154
- ```
155
-
156
- - `dolly-build init NAME [PACKAGE…]` writes a project where the packages are
157
- installed, and refuses to write over a file: `Dollyfile-NAME` (the host modules
158
- of `pi-minimal` and of each package, `FROM` `pi-minimal` and `INSTALL` of each
159
- package at the installed pins, rows for Pi's instructions, a skill, an extension,
160
- a tool and the script that starts Pi, and `pi-minimal`'s `ENTRY`) and `public/`,
161
- the page's files: `index.html` (the policy, the popups' font, and the files of
162
- `workspace/` and `theme.json` for `boot()`'s `files`, listed by `workspace.json`),
163
- `theme.json` (the sandbox's colours: Pi's theme, written over the image's at
164
- boot, whose background, text, accent and muted the page sets as the terminal's
165
- and the popups') and `workspace/README.md`; and, unless the project has one, a
166
- `.gitignore` for `node_modules/` and the site. It names the host module
167
- package to install when a package needs one the page lacks (`python`'s `sockets`).
168
- The repository README's "Pi with npm" says what each file is for.
169
- - The recipe is the one place that declares the sandbox. Add rows before `ENTRY`:
170
- `FILE` with a body (indented four spaces) for a file, `INSTALL` of a package
171
- whose directory is installed (every package it needs comes with it, so `python`
172
- brings `cc`), `SLOP` to run the image's tools at build time. `FROM` carries the
173
- base's files, environment and exports but not its host modules or `ENTRY`, so the
174
- recipe repeats those; a package's `REQUIRES HOST` rows must be among the recipe's,
175
- and the build names a missing one. A recipe is at most 128 KiB; `/workspace` is
176
- never part of an image (`boot()`'s `files` writes the project at each boot).
177
- - `dolly-build Dollyfile-NAME OUT` writes the site `OUT`, replacing the one it
178
- wrote there before: the files of `public/`, `workspace.json` (the files under
179
- `public/workspace/`), the runtime (`dolly/`), the device host modules the recipe
180
- declares (`dolly-host-NAME/`), `coi-serviceworker.js` and `image/`, the image's
181
- directory as an image package lays it out (its catalog, metadata and packs, the
182
- recipes it was built from, its `amy-index.txt` and an `index.mjs`). The page
183
- imports `./dolly/src/browser.mjs` and `./image/index.mjs`. An image this recipe
184
- built on this runtime is kept, so a change to `public/` alone takes seconds.
185
- `--browser chrome` (the default: your Chrome), `chromium` or `firefox`
186
- (Playwright's, after `npx playwright-core install chromium` or `firefox`).
187
- Both commands run where the packages are installed: the directory whose
188
- `node_modules/@daugasauron/` holds them (npm, pnpm and Yarn 1 alike). A row
189
- that names an image no installed package holds at that pin fails naming the row.
190
- - Serve `OUT` with any static file server, `python3 -m http.server -d OUT` on
191
- your machine. Where the host sends the isolation headers ("Serving" above) the
192
- page is isolated; where it does not, `init`'s page registers
193
- `coi-serviceworker.js`, which adds them to the site's own responses, and loads
194
- again under it.
195
- - A recipe may start `FROM` another installed image instead, `python-data` say: its
196
- package's README gives the `FROM` row, and the recipe repeats the image's `REQUIRES HOST`
197
- rows (a FILE of the project's own script and a SLOP that runs it at build time, for
198
- example). `init` writes Pi's project only; the page in `public/index.html` is then one
199
- as in "A page" above that imports `./dolly/src/browser.mjs` and `./image/index.mjs`,
200
- with the lines of `init`'s page that register `coi-serviceworker.js` for a host
201
- without the headers.
202
- - An image is built for the installed versions: after the packages update, the
203
- rows' pins are others (`init` in an empty directory writes the new ones), and it
204
- is built again. Packages of two versions in one `node_modules` fail the build,
205
- naming both.
177
+ `dolly-build` (`@daugasauron/dolly-build`) builds a recipe of your own from
178
+ installed packages into a static site: [Dolly from npm](npm.md).
206
179
 
207
180
  ## Host modules
208
181
 
@@ -1,15 +1,30 @@
1
- // GitHub Pages cannot configure COOP/COEP response headers. This worker adds
2
- // them only to Dolly's same-origin static responses so shared wasm64 memory is
3
- // available there. Cross-origin requests, including the explicit HTTP broker,
4
- // pass through unchanged.
5
- self.addEventListener("install", () => self.skipWaiting());
6
- self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
1
+ // The site's service worker, for its own same-origin GET and HEAD responses; it
2
+ // holds no policy and reaches nothing a page's fetch does not. Cross-origin
3
+ // requests, including the explicit HTTP broker's, pass through unchanged.
4
+ // - GitHub Pages cannot configure COOP/COEP response headers: the worker adds
5
+ // them where a response lacks them, so shared wasm64 memory is available there.
6
+ // - Where the network fails, a GET is answered from this site's kept runtime: the
7
+ // files a saved session opens with, which the page stores when a session is
8
+ // saved, each checked against the site's list (site/offline.mjs). A response the
9
+ // server gives, an error among them, is never replaced.
10
+ const kept = `dolly-offline ${self.registration.scope} `;
11
+ async function keptResponse(request, failure) {
12
+ for (const name of request.method === "GET" ? await caches.keys() : []) {
13
+ const response = name.startsWith(kept) && await (await caches.open(name)).match(request, { ignoreSearch: true });
14
+ if (response) return response;
15
+ }
16
+ throw failure;
17
+ }
18
+ // It controls the pages loaded after it and never takes over a running one
19
+ // (no clients.claim, no skipWaiting): a page whose Workers wait in Atomics.wait
20
+ // stalls its fetches in Firefox while its controller changes.
7
21
  self.addEventListener("fetch", (event) => {
8
22
  const target = new URL(event.request.url);
9
23
  if (target.origin !== self.location.origin) return;
10
24
  event.respondWith((async () => {
11
- const response = await fetch(event.request);
12
- if (response.type === "opaque" || response.type === "opaqueredirect") return response;
25
+ const response = await fetch(event.request).catch(failure => keptResponse(event.request, failure));
26
+ if (response.type === "opaque" || response.type === "opaqueredirect" ||
27
+ response.headers.has("Cross-Origin-Embedder-Policy")) return response;
13
28
  const headers = new Headers(response.headers);
14
29
  // Fetch exposes a decoded body while origin transport headers can still
15
30
  // describe its compressed representation. A reconstructed Response must
@@ -1 +1 @@
1
- export const DOLLY_BUILD_ID = "sha256:4dab85fcfe184d0632f281d1ff0a7839f9ac8176e6f1656819881e8ca38c3c27";
1
+ export const DOLLY_BUILD_ID = "sha256:d8f91a7526e31cfd010f30ba48cea8912e4aa9cdd6b1497fd9a96d46ea2eee52";
@@ -1 +1 @@
1
- export const DOLLY_IMAGE_BUILD_ID = "sha256:b9e16e76c04b07f3e0a20b6a399a1b736c1c8d9e103554634abb910a34d6c341";
1
+ export const DOLLY_IMAGE_BUILD_ID = "sha256:87c272432c019ccd35bc35fd5576682681a35c1abdcd36f18e456f608d23b758";
Binary file
@@ -1 +1 @@
1
- export const DOLLY_PROCESS_ABI_DIGEST = "36d9bdf963bcc094b53c3b7e44f244aeb2e9213d99a729a90fd01b2f9a368c5f";
1
+ export const DOLLY_PROCESS_ABI_DIGEST = "a193b87b75700fc2617daafe583c8693499f2c408124e6d8473d518cdf32c67b";
@@ -1,5 +1,8 @@
1
1
  // dist/dolly-process-abi.mjs
2
- var DOLLY_PROCESS_ABI_DIGEST = "36d9bdf963bcc094b53c3b7e44f244aeb2e9213d99a729a90fd01b2f9a368c5f";
2
+ var DOLLY_PROCESS_ABI_DIGEST = "a193b87b75700fc2617daafe583c8693499f2c408124e6d8473d518cdf32c67b";
3
+
4
+ // dist/dolly-kernel-plugin-abi.mjs
5
+ var DOLLY_KERNEL_PLUGIN_IMPORTS = Object.freeze(["fopen", "fseek", "fclose", "ftell", "malloc", "fread", "free", "dolly_assert_fail", "realloc", "strcmp", "strncmp", "strlen", "memcmp", "bcmp", "write", "__errno_location", "lseek", "preadv", "close", "fstat", "unlinkat", "openat", "realpath", "readv", "dolly_input_decoder_install"]);
3
6
 
4
7
  // host/dso/abi.mjs
5
8
  var DOLLY_DSO_OPEN = 112;
@@ -16,7 +19,7 @@ var DOLLY_FFI_CLOSURE_FREE = 122;
16
19
  var DOLLY_FFI_CLOSURE_PREP = 123;
17
20
  var DOLLY_ERRNO = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
18
21
 
19
- // src/wasm-interface.mjs
22
+ // page/wasm-interface.mjs
20
23
  var decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
21
24
  var valueTypes = /* @__PURE__ */ new Map([
22
25
  [127, "i32"],
@@ -386,15 +389,20 @@ var DOLLY_HOST_NAME_BYTES = 32;
386
389
  var DOLLY_HOST_DIGEST_BYTES = 32;
387
390
  var DOLLY_HOST_MAX_RECORDS = 64;
388
391
 
389
- // src/snapshot-records.mjs
392
+ // host/runtime/abi.mjs
393
+ var DOLLY_SNAPSHOT_MAX_MEBIBYTES = 2048;
394
+ var DOLLY_ERRNO2 = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
395
+
396
+ // page/snapshot-records.mjs
390
397
  var decoder2 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
391
- var MAX_SNAPSHOT_BYTES = 2 * 1024 * 1024 * 1024;
398
+ var encoder = new TextEncoder();
399
+ var MAX_SNAPSHOT_BYTES = DOLLY_SNAPSHOT_MAX_MEBIBYTES * 1024 * 1024;
392
400
  var MAX_SNAPSHOT_PACKS = MAX_SNAPSHOT_BYTES / (1024 * 1024);
393
401
 
394
- // src/version.mjs
395
- var DOLLY_VERSION = "0.1.5";
402
+ // page/version.mjs
403
+ var DOLLY_VERSION = "0.1.6";
396
404
 
397
- // src/static-asset.mjs
405
+ // page/static-asset.mjs
398
406
  var STATIC_PART_BYTES = 20 * 1024 * 1024;
399
407
  var hex = (bytes) => Array.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, "0")).join("");
400
408
  var sitePrefix = `/v${DOLLY_VERSION}`;
@@ -1176,7 +1184,7 @@ function createProcessFfi({
1176
1184
  });
1177
1185
  }
1178
1186
 
1179
- // src/process-abi.mjs
1187
+ // page/process-abi.mjs
1180
1188
  function unique(entries, key) {
1181
1189
  const result = /* @__PURE__ */ new Map();
1182
1190
  for (const entry of entries) {
@@ -1406,7 +1414,7 @@ function createThreadRecords({ memory, address, allocate, apply }) {
1406
1414
  // host/dso/process.mjs
1407
1415
  var DSO_RESPONSE_SIZE = 16 + DOLLY_DSO_ERROR_CAPACITY;
1408
1416
  var LOAD_FAILED = 2147483648;
1409
- var encoder = new TextEncoder();
1417
+ var encoder2 = new TextEncoder();
1410
1418
  var decoder3 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
1411
1419
  var configuration;
1412
1420
  var instance;
@@ -1704,7 +1712,7 @@ function symbolIndex(handle, name, value) {
1704
1712
  return threadRecords.change((publish) => {
1705
1713
  const indexed = functionIndices.get(value.value);
1706
1714
  if (indexed !== void 0) return indexed;
1707
- const bytes = encoder.encode(name);
1715
+ const bytes = encoder2.encode(name);
1708
1716
  const copy = keep(bytes);
1709
1717
  const index = functionIndex(value.value);
1710
1718
  publish({ kind: DOLLY_DSO_SYMBOL, values: [handle, copy, BigInt(bytes.length), index, 0n] }, true);
@@ -1756,7 +1764,7 @@ function futexWait(request) {
1756
1764
  }
1757
1765
  function writeDsoResponse(response, value = 0n, error = 0, message = "") {
1758
1766
  if (response.size < DSO_RESPONSE_SIZE) return -BigInt(DOLLY_ERRNO.ENOBUFS);
1759
- const bytes = encoder.encode(message);
1767
+ const bytes = encoder2.encode(message);
1760
1768
  const messageSize = Math.min(bytes.length, DOLLY_DSO_ERROR_CAPACITY);
1761
1769
  const output = new Uint8Array(
1762
1770
  configuration.memory.buffer,
@@ -1,4 +1,4 @@
1
- // src/process-constants.mjs
1
+ // page/process-constants.mjs
2
2
  var DOLLY_PROCESS_PACKET_LIMIT = 1048576;
3
3
  var DOLLY_PROCESS_EXIT = 5;
4
4
  var DOLLY_PROCESS_CLOCK_TIME = 48;
@@ -85,51 +85,105 @@ var DOLLY_ERRNO = Object.freeze({
85
85
  });
86
86
  var DOLLY_PROCESS_SIZEOF = Object.freeze({
87
87
  dolly_process_vector_sizes: 16,
88
+ dolly_process_exit_request: 8,
88
89
  dolly_process_fd_io_request: 16,
89
90
  dolly_process_fd_pread_request: 24,
90
91
  dolly_process_fd_truncate_request: 16,
91
92
  dolly_process_fd_request: 8,
92
93
  dolly_process_timestamp: 16,
93
94
  dolly_process_fd_times_request: 40,
95
+ dolly_process_io_result: 8,
94
96
  dolly_process_fd_dup_request: 16,
97
+ dolly_process_fd_dup_response: 8,
95
98
  dolly_process_fd_flags: 8,
96
99
  dolly_process_fd_lock_request: 32,
97
100
  dolly_process_fd_lock_response: 24,
101
+ dolly_process_pipe_request: 8,
102
+ dolly_process_pipe_response: 8,
98
103
  dolly_process_poll_request: 16,
99
104
  dolly_process_poll_query: 8,
100
105
  dolly_process_poll_response: 16,
101
106
  dolly_process_poll_result: 8,
102
- dolly_process_directory_entry: 24,
103
107
  dolly_process_directory_request: 24,
104
- dolly_process_stat_response: 88,
105
- dolly_process_filesystem_stat_response: 96,
106
- dolly_process_spawn_request: 72,
107
- dolly_process_fd_mapping: 8,
108
+ dolly_process_directory_entry: 24,
109
+ dolly_process_fd_seek_request: 16,
110
+ dolly_process_fd_seek_response: 8,
111
+ dolly_process_clock_request: 16,
112
+ dolly_process_clock_response: 8,
113
+ dolly_process_clock_sleep_request: 16,
108
114
  dolly_process_exec_state: 16,
115
+ dolly_process_fd_mapping: 8,
116
+ dolly_process_spawn_request: 72,
117
+ dolly_process_spawn_response: 8,
109
118
  dolly_process_exec_request: 48,
110
- dolly_process_pipe_request: 8,
111
- dolly_process_two_path_request: 16,
112
- dolly_process_path_times_request: 48,
113
- dolly_process_terminal_request: 24,
114
- dolly_process_terminal_response: 16,
115
- dolly_process_clock_sleep_request: 16,
116
- dolly_process_exit_request: 8,
117
119
  dolly_process_wait_request: 8,
118
120
  dolly_process_wait_response: 16,
121
+ dolly_process_info_response: 8,
119
122
  dolly_process_signal_request: 8,
120
- dolly_process_alarm: 16,
121
123
  dolly_process_signal_dispositions: 8,
122
- dolly_process_fd_seek_request: 16,
123
- dolly_process_clock_request: 16,
124
- dolly_process_path_request: 16
124
+ dolly_process_alarm: 16,
125
+ dolly_process_terminal_request: 24,
126
+ dolly_process_terminal_response: 16,
127
+ dolly_process_path_request: 16,
128
+ dolly_process_path_times_request: 48,
129
+ dolly_process_path_open_response: 8,
130
+ dolly_process_stat_response: 88,
131
+ dolly_process_filesystem_stat_response: 96,
132
+ dolly_process_two_path_request: 16
133
+ });
134
+ var DOLLY_PROCESS_OFFSETOF = Object.freeze({
135
+ dolly_process_vector_sizes: Object.freeze({ "count": 0, "reserved": 4, "bytes": 8 }),
136
+ dolly_process_exit_request: Object.freeze({ "status": 0, "signal_number": 4 }),
137
+ dolly_process_fd_io_request: Object.freeze({ "descriptor": 0, "reserved": 4, "size": 8 }),
138
+ dolly_process_fd_pread_request: Object.freeze({ "descriptor": 0, "reserved": 4, "offset": 8, "size": 16 }),
139
+ dolly_process_fd_truncate_request: Object.freeze({ "descriptor": 0, "reserved": 4, "size": 8 }),
140
+ dolly_process_fd_request: Object.freeze({ "descriptor": 0, "reserved": 4 }),
141
+ dolly_process_timestamp: Object.freeze({ "seconds": 0, "nanoseconds": 8, "flags": 12 }),
142
+ dolly_process_fd_times_request: Object.freeze({ "descriptor": 0, "reserved": 4, "access": 8, "modification": 24 }),
143
+ dolly_process_io_result: Object.freeze({ "size": 0 }),
144
+ dolly_process_fd_dup_request: Object.freeze({ "source_descriptor": 0, "target_descriptor": 4, "flags": 8, "reserved": 12 }),
145
+ dolly_process_fd_dup_response: Object.freeze({ "descriptor": 0, "reserved": 4 }),
146
+ dolly_process_fd_flags: Object.freeze({ "descriptor": 0, "flags": 4 }),
147
+ dolly_process_fd_lock_request: Object.freeze({ "descriptor": 0, "flags": 4, "type": 8, "whence": 12, "start": 16, "length": 24 }),
148
+ dolly_process_fd_lock_response: Object.freeze({ "type": 0, "pid": 4, "start": 8, "length": 16 }),
149
+ dolly_process_pipe_request: Object.freeze({ "flags": 0, "reserved": 4 }),
150
+ dolly_process_pipe_response: Object.freeze({ "read_descriptor": 0, "write_descriptor": 4 }),
151
+ dolly_process_poll_request: Object.freeze({ "deadline_nanoseconds": 0, "count": 8, "reserved": 12 }),
152
+ dolly_process_poll_query: Object.freeze({ "descriptor": 0, "events": 4, "reserved": 6 }),
153
+ dolly_process_poll_response: Object.freeze({ "ready": 0, "count": 4, "reserved": 8 }),
154
+ dolly_process_poll_result: Object.freeze({ "events": 0, "reserved": 2, "reserved2": 4 }),
155
+ dolly_process_directory_request: Object.freeze({ "descriptor": 0, "maximum_entries": 4, "cookie": 8, "reserved": 16 }),
156
+ dolly_process_directory_entry: Object.freeze({ "inode": 0, "next_cookie": 8, "file_type": 16, "name_size": 20 }),
157
+ dolly_process_fd_seek_request: Object.freeze({ "descriptor": 0, "whence": 4, "offset": 8 }),
158
+ dolly_process_fd_seek_response: Object.freeze({ "offset": 0 }),
159
+ dolly_process_clock_request: Object.freeze({ "clock_id": 0, "reserved": 4, "precision_nanoseconds": 8 }),
160
+ dolly_process_clock_response: Object.freeze({ "nanoseconds": 0 }),
161
+ dolly_process_clock_sleep_request: Object.freeze({ "clock_id": 0, "flags": 4, "deadline_nanoseconds": 8 }),
162
+ dolly_process_exec_state: Object.freeze({ "blocked_signals": 0, "ignored_signals": 4, "pending_signals": 8, "reserved": 12 }),
163
+ dolly_process_fd_mapping: Object.freeze({ "source_descriptor": 0, "target_descriptor": 4 }),
164
+ dolly_process_spawn_request: Object.freeze({ "flags": 0, "argument_count": 4, "environment_count": 8, "cwd_size": 12, "mapping_count": 16, "descriptor_inheritance": 20, "reserved": 24, "path_size": 28, "argument_bytes": 32, "environment_bytes": 40, "deadline_nanoseconds": 48, "signals": 56 }),
165
+ dolly_process_spawn_response: Object.freeze({ "pid": 0, "reserved": 4 }),
166
+ dolly_process_exec_request: Object.freeze({ "signals": 0, "argument_count": 16, "environment_count": 20, "path_size": 24, "reserved": 28, "argument_bytes": 32, "environment_bytes": 40 }),
167
+ dolly_process_wait_request: Object.freeze({ "pid": 0, "flags": 4 }),
168
+ dolly_process_wait_response: Object.freeze({ "pid": 0, "status": 4, "signal_number": 8, "reserved": 12 }),
169
+ dolly_process_info_response: Object.freeze({ "pid": 0, "parent_pid": 4 }),
170
+ dolly_process_signal_request: Object.freeze({ "pid": 0, "signal_number": 4 }),
171
+ dolly_process_signal_dispositions: Object.freeze({ "ignored_signals": 0, "handled_signals": 4 }),
172
+ dolly_process_alarm: Object.freeze({ "value_nanoseconds": 0, "interval_nanoseconds": 8 }),
173
+ dolly_process_terminal_request: Object.freeze({ "operation": 0, "descriptor": 4, "flags": 8, "reserved": 12, "deadline_nanoseconds": 16 }),
174
+ dolly_process_terminal_response: Object.freeze({ "value": 0, "columns": 8, "rows": 12 }),
175
+ dolly_process_path_request: Object.freeze({ "directory_descriptor": 0, "flags": 4, "reserved": 8, "path_size": 12 }),
176
+ dolly_process_path_times_request: Object.freeze({ "directory_descriptor": 0, "flags": 4, "reserved": 8, "path_size": 12, "access": 16, "modification": 32 }),
177
+ dolly_process_path_open_response: Object.freeze({ "descriptor": 0, "reserved": 4 }),
178
+ dolly_process_stat_response: Object.freeze({ "device": 0, "inode": 8, "size": 16, "access_nanoseconds": 24, "modification_nanoseconds": 32, "change_nanoseconds": 40, "blocks": 48, "mode": 56, "link_count": 60, "user": 64, "group": 68, "block_size": 72, "file_type": 76, "reserved": 80 }),
179
+ dolly_process_filesystem_stat_response: Object.freeze({ "type": 0, "block_size": 8, "blocks": 16, "blocks_free": 24, "blocks_available": 32, "files": 40, "files_free": 48, "maximum_name_length": 56, "fragment_size": 64, "flags": 72, "filesystem_id": 80, "reserved": 88 }),
180
+ dolly_process_two_path_request: Object.freeze({ "old_directory_descriptor": 0, "new_directory_descriptor": 4, "old_path_size": 8, "new_path_size": 12 })
125
181
  });
126
182
 
127
- // host/threads/abi.mjs
128
- var DOLLY_THREAD_SPAWN = 144;
129
- var DOLLY_THREAD_EXIT = 146;
130
- var DOLLY_ERRNO2 = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
131
-
132
- // src/process-worker.mjs
183
+ // page/process-worker.mjs
184
+ var clockField = DOLLY_PROCESS_OFFSETOF.dolly_process_clock_request;
185
+ var clockRequestSize = DOLLY_PROCESS_SIZEOF.dolly_process_clock_request;
186
+ var clockResponseSize = DOLLY_PROCESS_SIZEOF.dolly_process_clock_response;
133
187
  var PROCESS_EXIT = Symbol("Dolly process exit");
134
188
  var THREAD_EXIT = Symbol("Dolly thread exit");
135
189
  var UNSTARTED = Symbol("Dolly thread ended before it started");
@@ -148,7 +202,8 @@ var configuration = await new Promise((resolve, reject) => {
148
202
  resolve(event.data);
149
203
  });
150
204
  });
151
- if (!(configuration.module instanceof WebAssembly.Module || configuration.module instanceof MessagePort) || !(configuration.gateModule instanceof WebAssembly.Module) || !(configuration.kernelMemory instanceof WebAssembly.Memory) || !(configuration.control instanceof SharedArrayBuffer) || !Array.isArray(configuration.local) || configuration.control.byteLength !== 32 || !Number.isFinite(configuration.clockOrigin) || !Number.isSafeInteger(configuration.mailbox) || configuration.mailbox <= 0 || typeof configuration.memory?.initial !== "bigint" || typeof configuration.memory?.maximum !== "bigint" || configuration.port !== void 0 && !(configuration.port instanceof MessagePort) || !Number.isInteger(configuration.pid) || configuration.pid <= 0) {
205
+ var threadOperations = configuration.threaded ? configuration.threadOperations : {};
206
+ if (!(configuration.module instanceof WebAssembly.Module || configuration.module instanceof MessagePort) || !(configuration.gateModule instanceof WebAssembly.Module) || !(configuration.kernelMemory instanceof WebAssembly.Memory) || !(configuration.control instanceof SharedArrayBuffer) || !Array.isArray(configuration.local) || configuration.control.byteLength !== 32 || !Number.isFinite(configuration.clockOrigin) || !Number.isSafeInteger(configuration.mailbox) || configuration.mailbox <= 0 || typeof configuration.memory?.initial !== "bigint" || typeof configuration.memory?.maximum !== "bigint" || configuration.port !== void 0 && !(configuration.port instanceof MessagePort) || !Number.isInteger(configuration.pid) || configuration.pid <= 0 || configuration.threaded && !(Number.isInteger(configuration.threadOperations?.spawn) && Number.isInteger(configuration.threadOperations?.exit))) {
152
207
  throw new Error("Dolly process received an invalid configuration");
153
208
  }
154
209
  var control = new Int32Array(configuration.control);
@@ -245,8 +300,8 @@ function clockResponse(clock, response, now) {
245
300
  const nanoseconds = Math.round(
246
301
  (clock === DOLLY_PROCESS_CLOCK_REALTIME ? Date.now() : now + clockOffset) * 1e6
247
302
  );
248
- new DataView(memory.buffer, response.address, 8).setBigUint64(0, BigInt(nanoseconds), true);
249
- return 8n;
303
+ new DataView(memory.buffer, response.address, clockResponseSize).setBigUint64(DOLLY_PROCESS_OFFSETOF.dolly_process_clock_response.nanoseconds, BigInt(nanoseconds), true);
304
+ return BigInt(clockResponseSize);
250
305
  }
251
306
  function copyRequest(request) {
252
307
  if (Atomics.compareExchange(control, 4, 0, 1) !== 0) return false;
@@ -276,12 +331,12 @@ function call(operation, requestAddressValue, requestSizeValue, responseAddressV
276
331
  for (const module of localModules) {
277
332
  if (module.handles(operation)) return module.call(operation, request, response);
278
333
  }
279
- const exitingResult = operation === DOLLY_THREAD_EXIT && request.size === 8 ? new DataView(memory.buffer, request.address, 8).getBigUint64(0, true) : void 0;
334
+ const exitingResult = operation === threadOperations.exit && request.size === 8 ? new DataView(memory.buffer, request.address, 8).getBigUint64(0, true) : void 0;
280
335
  let clock;
281
- if (operation === DOLLY_PROCESS_CLOCK_TIME && request.size === 16 && response.size >= 8 && response.size <= DOLLY_PROCESS_PACKET_LIMIT) {
282
- const packet = new DataView(memory.buffer, request.address, 16);
283
- const id = packet.getUint32(0, true);
284
- if ((id === DOLLY_PROCESS_CLOCK_REALTIME || id === DOLLY_PROCESS_CLOCK_MONOTONIC) && packet.getUint32(4, true) === 0) clock = id;
336
+ if (operation === DOLLY_PROCESS_CLOCK_TIME && request.size === clockRequestSize && response.size >= clockResponseSize && response.size <= DOLLY_PROCESS_PACKET_LIMIT) {
337
+ const packet = new DataView(memory.buffer, request.address, clockRequestSize);
338
+ const id = packet.getUint32(clockField.clock_id, true);
339
+ if ((id === DOLLY_PROCESS_CLOCK_REALTIME || id === DOLLY_PROCESS_CLOCK_MONOTONIC) && packet.getUint32(clockField.reserved, true) === 0) clock = id;
285
340
  const now = performance.now();
286
341
  if (clock !== void 0 && now - lastSignalCheck < 1) return clockResponse(clock, response, now);
287
342
  }
@@ -295,7 +350,7 @@ function call(operation, requestAddressValue, requestSizeValue, responseAddressV
295
350
  const memoryBytes = memory.buffer.byteLength;
296
351
  let channel;
297
352
  if (openChannels.length) closeReceivedChannels();
298
- if (operation === DOLLY_THREAD_SPAWN && configuration.threaded) {
353
+ if (operation === threadOperations.spawn && configuration.threaded) {
299
354
  if (!reserveMaximum()) return -BigInt(DOLLY_ERRNO.EAGAIN);
300
355
  channel = new MessageChannel();
301
356
  }
@@ -334,7 +389,7 @@ function call(operation, requestAddressValue, requestSizeValue, responseAddressV
334
389
  if (result === deferredResult) return ended(PROCESS_EXIT);
335
390
  if (result > 0n) gate.exports.response(mailbox, BigInt(response.address), result);
336
391
  lastSignalCheck = result === -BigInt(DOLLY_ERRNO.EINTR) ? -Infinity : performance.now();
337
- if (operation === DOLLY_THREAD_EXIT && result >= 0n) {
392
+ if (operation === threadOperations.exit && result >= 0n) {
338
393
  threadResult = exitingResult;
339
394
  for (const module of localModules) module.leaving?.();
340
395
  return ended(THREAD_EXIT);