@statewalker/cellb 0.1.1 → 0.2.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.
Files changed (72) hide show
  1. package/README.md +45 -21
  2. package/dist/adapters/wasm-sqlite.d.ts +11 -0
  3. package/dist/adapters/wasm-sqlite.js +26 -3
  4. package/dist/adapters/wasm-sqlite.js.map +1 -1
  5. package/dist/bindings/assets.js +2 -2
  6. package/dist/bindings/assets.js.map +1 -1
  7. package/dist/bindings/r2.js +2 -2
  8. package/dist/bindings/r2.js.map +1 -1
  9. package/dist/browser.d.ts +0 -1
  10. package/dist/browser.js +0 -1
  11. package/dist/browser.js.map +1 -1
  12. package/dist/bucket/celld-cells.js +1 -1
  13. package/dist/bucket/celld-cells.js.map +1 -1
  14. package/dist/bucket/s3-object-store.js +1 -1
  15. package/dist/bucket/s3-object-store.js.map +1 -1
  16. package/dist/core/handles.d.ts +5 -2
  17. package/dist/core/handles.js +12 -1
  18. package/dist/core/handles.js.map +1 -1
  19. package/dist/core/id.js +3 -3
  20. package/dist/core/id.js.map +1 -1
  21. package/dist/core/namespace.d.ts +1 -1
  22. package/dist/core/namespace.js +2 -1
  23. package/dist/core/namespace.js.map +1 -1
  24. package/dist/core/object-host.d.ts +23 -0
  25. package/dist/core/object-host.js +87 -6
  26. package/dist/core/object-host.js.map +1 -1
  27. package/dist/core/runtime.d.ts +10 -0
  28. package/dist/core/runtime.js +43 -2
  29. package/dist/core/runtime.js.map +1 -1
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.js +2 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/isolate/browser.js +1 -1
  34. package/dist/isolate/browser.js.map +1 -1
  35. package/dist/isolate/host.d.ts +5 -1
  36. package/dist/isolate/host.js +19 -3
  37. package/dist/isolate/host.js.map +1 -1
  38. package/dist/isolate/isolate-worker.js +11 -1
  39. package/dist/isolate/isolate-worker.js.map +1 -1
  40. package/dist/isolate/isolate.js +12 -0
  41. package/dist/isolate/isolate.js.map +1 -1
  42. package/dist/isolate/node-entry.js +11 -0
  43. package/dist/isolate/node-entry.js.map +1 -1
  44. package/dist/isolate/protocol.d.ts +4 -0
  45. package/dist/isolate/protocol.js +118 -2
  46. package/dist/isolate/protocol.js.map +1 -1
  47. package/dist/node-compat/crypto.js +3 -3
  48. package/dist/node-compat/crypto.js.map +1 -1
  49. package/dist/project/browser-host.js +3 -2
  50. package/dist/project/browser-host.js.map +1 -1
  51. package/dist/project/node-project.js +2 -1
  52. package/dist/project/node-project.js.map +1 -1
  53. package/dist/project/site-loader.d.ts +45 -0
  54. package/dist/project/site-loader.js +97 -0
  55. package/dist/project/site-loader.js.map +1 -0
  56. package/dist/project/site.d.ts +0 -36
  57. package/dist/project/site.js +11 -99
  58. package/dist/project/site.js.map +1 -1
  59. package/dist/project/wrangler.js +10 -0
  60. package/dist/project/wrangler.js.map +1 -1
  61. package/dist/router/cell-router.js +1 -1
  62. package/dist/router/cell-router.js.map +1 -1
  63. package/dist/site.d.ts +1 -0
  64. package/dist/site.js +6 -0
  65. package/dist/site.js.map +1 -0
  66. package/dist/ws/frames.d.ts +14 -0
  67. package/dist/ws/frames.js +51 -0
  68. package/dist/ws/frames.js.map +1 -1
  69. package/dist/ws/server.d.ts +4 -0
  70. package/dist/ws/server.js +17 -2
  71. package/dist/ws/server.js.map +1 -1
  72. package/package.json +25 -5
package/README.md CHANGED
@@ -29,7 +29,8 @@ npm install esbuild # for loadNodeProject and bundleWorkerForBrowser (an optio
29
29
 
30
30
  The package has three entry points, one per platform, and two more for bundling. Published, it is
31
31
  JavaScript with type declarations (`dist/`, built by `scripts/build.mjs`); in this repository the
32
- entry points are the TypeScript sources, which the workspace's examples and tests import directly.
32
+ entry points are the TypeScript sources, which cellb's tests and browser pages import directly
33
+ (the examples, in a workspace of their own, use the published package).
33
34
  In a browser app, a bundler must handle `new Worker(new URL("…", import.meta.url))`: the LTX store
34
35
  worker is started that way (checked with a Vite build, see `scripts/pack-check.mjs`).
35
36
 
@@ -37,7 +38,8 @@ worker is started that way (checked with a Vite build, see `scripts/pack-check.m
37
38
  |---|---|
38
39
  | `@statewalker/cellb` | the runtime (`createCellRuntime`, `createProjectRuntime`), `DurableObject` and `WorkerEntrypoint`, the ports and `CellStorage`, `celldBucket`, the object stores (`s3ObjectStore`, `memoryObjectStore`, `filesApiObjectStore`), `filesApiLtxLog`, `filesApiChangeLog`, `filesApiBackup`, `wasmMemoryOpener` (in-memory SQLite WASM), links over ports, `CellRouter`, `createShardWorker`, the WebSocket classes, and the errors embedders catch |
39
40
  | `@statewalker/cellb/node` | `nodeSqliteOpener`, `sqliteObjectStore` (celld dev's local bucket), `loadNodeProject`, `applyNodeD1Migrations`, and celld's peer protocol (`startCelldPeerServer`, `celldPeers`) |
40
- | `@statewalker/cellb/browser` | `browserProjectRuntime`, `startProjectShards`, `joinProjectShards` and `serveProjectShard` (shards in workers and tabs), `installProjectRouter` (the ServiceWorker side), `sahpoolOpener`, `idbObjectStore`, `createProjectSite`, `serviceWorkerConnector` |
41
+ | `@statewalker/cellb/browser` | `browserProjectRuntime`, `startProjectShards`, `joinProjectShards` and `serveProjectShard` (shards in workers and tabs), `installProjectRouter` (the ServiceWorker side), `sahpoolOpener`, `idbObjectStore`, `serviceWorkerConnector` |
42
+ | `@statewalker/cellb/site` | `createProjectSite`: a project's sources compiled in the browser and served by an in-browser HTTP server (`browserProjectRuntime({ site })`). Needs the optional peer dependencies `@statewalker/webrun-modules` and `@statewalker/webrun-site-builder`; the site is hosted in a ServiceWorker with `@statewalker/webrun-site-host`, also an optional peer |
41
43
  | `@statewalker/cellb/host-modules` | for bundlers other than cellb's own (webrun-bundler in a browser, say): the sources of `cloudflare:workers`, `node:*` and the Workers globals as cellb's realms provide them (`cellbHostModule`, `CELLS_GLOBALS_MODULE`) |
42
44
  | `@statewalker/cellb/isolate-worker` | the Dedicated Worker that runs a Worker-Loader Worker in a realm of its own (`isolateScript`) |
43
45
 
@@ -45,8 +47,12 @@ Node 22.13 or later is needed for `node:sqlite`. `loadNodeProject` bundles a pro
45
47
  `esbuild`, which the embedder must install.
46
48
 
47
49
  A runtime is a `(Request) ⇒ Response` handler: mount `runtime.fetch` on `node:http`, a
48
- ServiceWorker or a test, and call `runtime.close()` when done. `close()` takes final backups,
49
- closes every database, then closes the storage.
50
+ ServiceWorker or a test, and call `runtime.close()` when done. `close()` aborts the signal of the
51
+ requests in flight (the Worker's and its objects', as when a client goes away on Cloudflare: a long
52
+ poll or an SSE stream ends), gives events still running and `waitUntil` work `closeGraceMs` (2 s) to
53
+ finish, takes final backups, closes every database, then closes the storage. After that, the
54
+ runtime answers requests with 503, and an object's code still running fails with
55
+ `ObjectClosedError` when it uses its storage or another object.
50
56
 
51
57
  ## Examples
52
58
 
@@ -199,7 +205,8 @@ conformance/ celld examples, our projects, workerd's tests, recorded gold
199
205
  test/ unit tests (vitest, Node)
200
206
  e2e/ tests against a real celld and RustFS (vitest; skipped without CELLD_BIN)
201
207
  e2e-browser/ Playwright specs; pages/ holds the demo pages, the host app, ServiceWorker and
202
- shard workers they drive; probes/ the exploratory browser probes (ServiceWorker idling)
208
+ shard workers they drive, and labs/ (storage internals, measured; cross-origin isolated); probes/
209
+ the exploratory browser probes (ServiceWorker idling, the ltx and sqlite labs)
203
210
  scripts/ vendoring, the interop environment, mutation harnesses
204
211
  probes/ Node hypothesis probes (SQLite locks, webrun-modules globals)
205
212
  docs/ topic pages
@@ -238,8 +245,9 @@ docs/ topic pages
238
245
  stays open until they are.
239
246
 
240
247
  What cellb does not do yet is in [docs/gaps.md](docs/gaps.md); how it matches celld is in
241
- [docs/celld-compatibility.md](docs/celld-compatibility.md). The vocabulary is in
242
- [CONTEXT.md](CONTEXT.md).
248
+ [docs/celld-compatibility.md](docs/celld-compatibility.md), and service by service (KV, D1, R2,
249
+ Queues, Workflows, …) in [docs/services/](docs/services/). What is isolated, and what is not, is in
250
+ [docs/security.md](docs/security.md). The vocabulary is in [CONTEXT.md](CONTEXT.md).
243
251
 
244
252
  ### Dependencies, and why
245
253
 
@@ -249,8 +257,12 @@ What cellb does not do yet is in [docs/gaps.md](docs/gaps.md); how it matches ce
249
257
  - `@statewalker/webrun-rpc`, `webrun-http-streams`, `webrun-streams` — HTTP with streaming bodies
250
258
  over MessagePorts.
251
259
  - `@statewalker/webrun-files` — the FilesApi for logs, backups and KV blobs;
252
- `webrun-http-browser`, `webrun-site-builder`, `webrun-site-host` — ServiceWorker ingress and the
253
- in-browser HTTP server.
260
+ `webrun-http-browser` — ServiceWorker ingress.
261
+ - Optional peers, for `@statewalker/cellb/site` only: `webrun-modules` (compiles a project's
262
+ sources in the browser; it brings lightningcss-wasm and @jspm/core, about 30 MB),
263
+ `webrun-site-builder` (the in-browser HTTP server), and `webrun-site-host`, which the
264
+ embedder's own ServiceWorker code uses to host it (cellb does not import it). Projects that run a
265
+ bundle (`{ bundle }`) need none of them, and their builds emit none of their assets.
254
266
  - `worker-timers` — unthrottled heartbeat timers on a page's main thread.
255
267
  - `buffer`, `events`, `util`, `assert`, `path-browserify` — `nodejs_compat` in browsers.
256
268
 
@@ -298,23 +310,35 @@ Run from `packages/cellb` after `pnpm install` at the repository root.
298
310
  | `pnpm e2e:firefox`, `pnpm e2e:webkit` | the same suite in Playwright's Firefox or WebKit (`npx playwright install firefox webkit`; `PW_BROWSERS` takes a comma-separated list). What differs in Firefox: [docs/browser-host.md](docs/browser-host.md#firefox-and-webkit). |
299
311
  | `pnpm interop` | `e2e/` and the celld conformance in Node: `scripts/interop-env.sh` downloads the pinned celld and RustFS into `~/.cache/cellb-interop`, starts a shared RustFS on `127.0.0.1:9200` (`RUSTFS_PORT`), and sets `CELLD_BIN`, `ESBUILD_DIR`, `RUSTFS_ENDPOINT` |
300
312
  | `pnpm interop:browser` | the same for Chromium |
313
+ | `pnpm probes:browser` | the exploratory probes in Chromium (`e2e-browser/probes`), measurements printed, not part of `pnpm e2e`; among them the storage labs: `ltx-lab` (SQLite over a cell's LTX files; `LTX_STORE=worker`, `LTX_SYNC=1`) and `sqlite-lab` (SQLite in N workers, six storage modes). Add a spec name to run one |
314
+ | `pnpm bench`, `pnpm bench:browser` | the numbers celld.dev reports, measured for cellb: durability (one writer, acknowledged writes after a `SIGKILL`, durable write latency, failover), speed (stateless requests, waking a hibernated cell) and density (memory per resident cell), on Node and in Chromium. See [bench/README.md](bench/README.md); `bench/results/` holds the last recorded runs |
301
315
  | `pnpm mutate`, `pnpm mutate:browser` | mutation harnesses in a throw-away copy, after a baseline run; `MUTANTS=0,3` runs a subset |
302
316
  | `pnpm browser:dev` | the demo pages in `e2e-browser/pages`: `/` (one cell worker), `/sharded/`, `/backup/`, and `/host/`, the host app that runs a project folder |
303
317
 
304
318
  ### Releasing
305
319
 
306
- Releases are made by hand from a local checkout, with the maintainer's npm login and 2FA; CI
307
- never publishes (the statewalker convention). A change to the package carries a changeset
308
- (`pnpm changeset` at the repository root). To release:
309
-
310
- ```sh
311
- pnpm changeset version # versions and changelog from the pending changesets
312
- git commit -am "chore: version packages"
313
- pnpm changeset publish # publishes what npm lacks (prepack builds dist/), tags it
314
- git push --follow-tags
315
- ```
316
-
317
- Run `node scripts/pack-check.mjs` first: it checks exactly what `publish` uploads.
320
+ Releases are made by CI, the same pipeline as the other statewalker monorepos
321
+ (`.github/workflows/ci.yml`). A change to the package carries a changeset (`pnpm changeset` at
322
+ the repository root); without one, the release planner (statewalker/.github's
323
+ `scripts/release-changesets.mjs`) writes a patch for a package whose published contents changed.
324
+ On every push to `main`, after the `cellb` checks pass:
325
+
326
+ 1. `version` applies the pending changesets in a "chore: version packages" pull request
327
+ (versions, `CHANGELOG.md`). It never publishes.
328
+ 2. Once that pull request is merged, `pack` packs every public package whose version npm lacks
329
+ (`pnpm pack`: `prepack` builds `dist/`, and `catalog:` ranges are resolved).
330
+ 3. `publish` waits for a maintainer's approval (the `npm` GitHub environment), then publishes
331
+ the tarballs with npm trusted publishing (OIDC, with provenance) and creates a GitHub release
332
+ per package. No npm token is stored in GitHub.
333
+
334
+ One-time setup on npm: `@statewalker/cellb` → Settings → Trusted publishing → GitHub Actions,
335
+ repository `statewalker/cellb.dev`, workflow `ci.yml`, environment `npm`. On GitHub: an
336
+ environment named `npm` with required reviewers. A new package's first version is published by
337
+ hand (`pnpm publish` in its directory, with the maintainer's 2FA), since a trusted publisher can
338
+ only be added to a package that exists.
339
+
340
+ The `cellb` job runs `node scripts/pack-check.mjs` before any of these: it checks exactly what
341
+ `publish` uploads. Run it locally to see the same.
318
342
 
319
343
  ## License
320
344
 
@@ -56,6 +56,17 @@ export declare class WasmSqliteDriver implements SyncSqlDriver {
56
56
  /** In-memory WASM databases (tests, ephemeral cells). `sqlite3` = await sqlite3InitModule(). */
57
57
  /** Image hooks for an in-memory DB: `sqlite3_js_db_export` / `sqlite3_deserialize`. */
58
58
  export declare function memoryImageHooks(sqlite3: any): WasmImageHooks;
59
+ /**
60
+ * Sets up a new SQLite WASM connection for a cell. One worker holds many small cells, and SQLite
61
+ * WASM's heap is capped at 2 GiB and never shrinks, so each connection's fixed memory decides how
62
+ * many cells can be open at once:
63
+ * - no lookaside: SQLite's per-connection allocator of small blocks, about 45 KiB each;
64
+ * - 4 KiB pages for a database that is still empty (celld's page size: SQLite's native default;
65
+ * the WASM build's is 8 KiB). The page cache starts with a block of pages, so the page size
66
+ * sets most of a connection's memory. A database that already holds data keeps its page size.
67
+ * Together they bring a connection with one small table from about 258 KiB to about 120 KiB.
68
+ */
69
+ export declare function tuneCellConnection(sqlite3: unknown, db: Oo1Db): void;
59
70
  export declare function wasmMemoryOpener(sqlite3: {
60
71
  oo1: {
61
72
  DB: new (path: string, mode?: string) => Oo1Db;
@@ -167,11 +167,33 @@ export function memoryImageHooks(sqlite3) {
167
167
  },
168
168
  };
169
169
  }
170
+ /**
171
+ * Sets up a new SQLite WASM connection for a cell. One worker holds many small cells, and SQLite
172
+ * WASM's heap is capped at 2 GiB and never shrinks, so each connection's fixed memory decides how
173
+ * many cells can be open at once:
174
+ * - no lookaside: SQLite's per-connection allocator of small blocks, about 45 KiB each;
175
+ * - 4 KiB pages for a database that is still empty (celld's page size: SQLite's native default;
176
+ * the WASM build's is 8 KiB). The page cache starts with a block of pages, so the page size
177
+ * sets most of a connection's memory. A database that already holds data keeps its page size.
178
+ * Together they bring a connection with one small table from about 258 KiB to about 120 KiB.
179
+ */
180
+ export function tuneCellConnection(sqlite3, db) {
181
+ const capi = sqlite3.capi;
182
+ const pointer = db.pointer;
183
+ // Before the connection runs any statement: SQLite refuses (SQLITE_BUSY) while lookaside is used.
184
+ if (capi?.sqlite3_db_config && pointer) {
185
+ capi.sqlite3_db_config(pointer, capi.SQLITE_DBCONFIG_LOOKASIDE, 0, 0, 0);
186
+ }
187
+ db.exec("PRAGMA page_size=4096");
188
+ }
170
189
  let memdbCounter = 0;
171
190
  export function wasmMemoryOpener(sqlite3) {
172
- return () => new WasmSqliteDriver(
173
- // memdb (not ":memory:"): an in-memory database that IS a VFS file, so the page hook works.
174
- new sqlite3.oo1.DB(`file:/cells-mem-${++memdbCounter}?vfs=memdb`, "c"), memoryImageHooks(sqlite3), sqlite3);
191
+ return () => {
192
+ // memdb (not ":memory:"): an in-memory database that IS a VFS file, so the page hook works.
193
+ const db = new sqlite3.oo1.DB(`file:/cells-mem-${++memdbCounter}?vfs=memdb`, "c");
194
+ tuneCellConnection(sqlite3, db);
195
+ return new WasmSqliteDriver(db, memoryImageHooks(sqlite3), sqlite3);
196
+ };
175
197
  }
176
198
  /**
177
199
  * Browser persistence: one OPFS "SyncAccessHandle pool" database per cell.
@@ -228,6 +250,7 @@ export async function sahpoolOpener(sqlite3, opts = {}) {
228
250
  // database file is the same in both modes (only WAL marks it).
229
251
  const openDb = (path) => {
230
252
  const db = new pool.OpfsSAHPoolDb(path);
253
+ tuneCellConnection(sqlite3, db);
231
254
  db.exec("PRAGMA journal_mode=PERSIST");
232
255
  return db;
233
256
  };
@@ -1 +1 @@
1
- {"version":3,"file":"wasm-sqlite.js","sourceRoot":"","sources":["../../src/adapters/wasm-sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { splitSqlStatements } from \"../core/sql-split.js\";\nimport type { OpenCellSql, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { type DirtyPages, installPageHook } from \"./wasm-page-hook.js\";\nimport { type WasmSqlite3, wasmSessionCapture } from \"./wasm-session.js\";\n\n/** Minimal slice of the @sqlite.org/sqlite-wasm `oo1.DB` API we rely on. */\nexport interface Oo1Db {\n prepare(sql: string): Oo1Stmt;\n exec(sql: string): unknown;\n changes(): number;\n close(): void;\n}\ninterface Oo1Stmt {\n bind(values: unknown[]): Oo1Stmt;\n getColumnNames(): string[];\n columnCount: number;\n step(): boolean;\n get(target: unknown[]): unknown[];\n finalize(): void;\n}\n\n/**\n * SyncSqlDriver over the official SQLite WASM build (oo1 API). Synchronous,\n * so it satisfies `ctx.storage.sql`. Works in Node (in-memory, for tests) and\n * in browsers; persistence comes from the VFS behind the DB (see sahpoolOpener).\n */\n/** Backup hooks: how to export an image of the DB and how to replace it (returns the reopened DB). */\nexport interface WasmImageHooks {\n image(db: Oo1Db): Uint8Array;\n restore(db: Oo1Db, image: Uint8Array): Oo1Db;\n}\n\nexport class WasmSqliteDriver implements SyncSqlDriver {\n captureStart?: () => void;\n captureTake?: () => Uint8Array;\n captureDiscard?: () => void;\n applyChangeset?: (changeset: Uint8Array) => void;\n captureCoversRowidTables?: boolean;\n #db: Oo1Db;\n readonly #hooks?: WasmImageHooks;\n readonly #sqlite3?: WasmSqlite3;\n readonly #capi?: { sqlite3_get_autocommit(db: number): number };\n #pageHook?: ReturnType<typeof installPageHook>;\n constructor(\n db: Oo1Db,\n hooks?: WasmImageHooks,\n sqlite3?: WasmSqlite3,\n /** SQLite's C API, for `inTransaction()` alone (when `sqlite3` is not given). */\n capi?: { sqlite3_get_autocommit(db: number): number },\n ) {\n this.#db = db;\n this.#hooks = hooks;\n this.#sqlite3 = sqlite3;\n this.#capi = capi ?? sqlite3?.capi;\n if (!this.#capi?.sqlite3_get_autocommit)\n (this as { inTransaction?: unknown }).inTransaction = undefined;\n this.#enableDqs();\n this.#hookPages();\n if (sqlite3?.capi?.sqlite3session_create) {\n // Transaction-log capture (session extension), following the current DB handle.\n Object.assign(\n this,\n wasmSessionCapture(sqlite3, () => (this.#db as unknown as { pointer: number }).pointer),\n );\n }\n if (!hooks) {\n // No image support for this driver: hide the optional port methods.\n (this as { image?: unknown }).image = undefined;\n (this as { restore?: unknown }).restore = undefined;\n }\n }\n image(): Uint8Array {\n return this.#hooks!.image(this.#db);\n }\n\n /**\n * As celld and workerd: a double-quoted name that is no column is a string literal (SQLite's\n * DQS misfeature, off in this build).\n */\n #enableDqs(): void {\n const capi = this.#sqlite3?.capi;\n if (!capi?.sqlite3_db_config) return;\n const db = (this.#db as unknown as { pointer: number }).pointer;\n capi.sqlite3_db_config(db, capi.SQLITE_DBCONFIG_DQS_DML, 1, 0);\n capi.sqlite3_db_config(db, capi.SQLITE_DBCONFIG_DQS_DDL, 1, 0);\n }\n\n /** Installs the page hook on the current main database file (see wasm-page-hook.ts). */\n #hookPages(): void {\n this.#pageHook = undefined;\n if (!this.#sqlite3?.capi?.sqlite3_file_control) return;\n try {\n this.#pageHook = installPageHook(\n this.#sqlite3,\n (this.#db as unknown as { pointer: number }).pointer,\n );\n } catch {\n this.#pageHook = undefined;\n }\n }\n /** Pages written since the previous call — a committed transaction's changed pages. */\n takeDirtyPages(): DirtyPages | undefined {\n if (!this.#pageHook) return undefined;\n const pageSize = Number(this.exec(\"PRAGMA page_size\", []).rows[0][0]);\n const pageCount = Number(this.exec(\"PRAGMA page_count\", []).rows[0][0]);\n return this.#pageHook.take(pageSize, pageCount);\n }\n discardDirtyPages(): void {\n this.#pageHook?.discard();\n }\n /**\n * WASM builds cannot load extensions at run time; an extension must be compiled into the\n * build (e.g. the `sqlite-vec-wasm-demo` package for \"sqlite_vec\"). This only checks it is.\n */\n loadExtension(name: string): void {\n const probe: Record<string, string> = { sqlite_vec: \"SELECT vec_version()\" };\n if (!probe[name]) throw new Error(`Unknown SQLite extension \"${name}\"`);\n try {\n this.exec(probe[name], []);\n } catch {\n throw new Error(`SQLite extension \"${name}\" is not compiled into this SQLite WASM build`);\n }\n }\n restore(image: Uint8Array): void {\n // celld's databases are in WAL mode (header bytes 18–19 = 2); the WASM VFSes (memdb,\n // opfs-sahpool) have no shared memory for WAL and refuse to open such a file. The pages\n // are the same in rollback-journal mode: switch the header to 1 (celld sets WAL again\n // when it opens the database).\n if (image.length > 19 && image[18] === 2 && image[19] === 2) {\n image = image.slice();\n image[18] = 1;\n image[19] = 1;\n }\n this.#db = this.#hooks!.restore(this.#db, image);\n this.#enableDqs();\n this.#hookPages(); // a new database file behind the connection\n }\n exec(sql: string, params: unknown[]): SqlResult {\n const statements = params.length === 0 ? splitSqlStatements(sql) : [sql];\n // Several statements: the result of the last one, with the rows written by all of them.\n let last: SqlResult = { columnNames: [], rows: [], rowsWritten: 0 };\n let written = 0;\n for (const text of statements) {\n last = this.#one(text, params);\n written += last.rowsWritten;\n }\n return { ...last, rowsWritten: written };\n }\n #one(sql: string, params: unknown[]): SqlResult {\n const st = this.#db.prepare(sql);\n try {\n if (params.length) st.bind(params.map(toSqlite));\n const columnNames = st.columnCount ? st.getColumnNames() : [];\n const rows: unknown[][] = [];\n while (st.step()) if (columnNames.length) rows.push(st.get([]).map(fromSqlite));\n return { columnNames, rows, rowsWritten: columnNames.length ? 0 : this.#db.changes() };\n } finally {\n st.finalize();\n }\n }\n inTransaction(): boolean {\n const pointer = (this.#db as unknown as { pointer: number }).pointer;\n return this.#capi!.sqlite3_get_autocommit(pointer) === 0;\n }\n databaseSize(): number {\n const pc = this.exec(\"PRAGMA page_count\", []).rows[0][0];\n const ps = this.exec(\"PRAGMA page_size\", []).rows[0][0];\n return Number(pc) * Number(ps);\n }\n close(): void {\n this.#db.close();\n }\n}\n\nfunction toSqlite(v: unknown): unknown {\n if (v instanceof ArrayBuffer) return new Uint8Array(v);\n if (typeof v === \"boolean\") return v ? 1 : 0;\n return v === undefined ? null : v;\n}\nfunction fromSqlite(v: unknown): unknown {\n if (v instanceof Uint8Array) return v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength);\n return v;\n}\n\n/** In-memory WASM databases (tests, ephemeral cells). `sqlite3` = await sqlite3InitModule(). */\n/** Image hooks for an in-memory DB: `sqlite3_js_db_export` / `sqlite3_deserialize`. */\nexport function memoryImageHooks(sqlite3: any): WasmImageHooks {\n const { capi, wasm } = sqlite3;\n return {\n image: (db) => sqlite3.capi.sqlite3_js_db_export(db),\n restore(db, image) {\n const p = wasm.allocFromTypedArray(image);\n const rc = capi.sqlite3_deserialize(\n (db as any).pointer,\n \"main\",\n p,\n image.byteLength,\n image.byteLength,\n capi.SQLITE_DESERIALIZE_FREEONCLOSE | capi.SQLITE_DESERIALIZE_RESIZEABLE,\n );\n if (rc) throw new Error(`sqlite3_deserialize failed: ${capi.sqlite3_js_rc_str?.(rc) ?? rc}`);\n return db;\n },\n };\n}\n\nlet memdbCounter = 0;\nexport function wasmMemoryOpener(sqlite3: {\n oo1: { DB: new (path: string, mode?: string) => Oo1Db };\n}): OpenCellSql {\n return () =>\n new WasmSqliteDriver(\n // memdb (not \":memory:\"): an in-memory database that IS a VFS file, so the page hook works.\n new sqlite3.oo1.DB(`file:/cells-mem-${++memdbCounter}?vfs=memdb`, \"c\"),\n memoryImageHooks(sqlite3),\n sqlite3 as never,\n );\n}\n\n/**\n * Browser persistence: one OPFS \"SyncAccessHandle pool\" database per cell.\n * Must run in a Dedicated Worker; needs neither COOP/COEP nor SharedArrayBuffer.\n * Not runnable in Node (installOpfsSAHPoolVfs is browser-only) — covered by P04's Playwright spec.\n */\nexport async function sahpoolOpener(\n sqlite3: {\n installOpfsSAHPoolVfs(opts: { name?: string; initialCapacity?: number }): Promise<{\n OpfsSAHPoolDb: new (path: string) => Oo1Db;\n reserveMinimumCapacity(n: number): Promise<number>;\n getCapacity(): number;\n getFileCount(): number;\n // Not in older SQLite WASM builds (sqlite-vec's): there `release()` does nothing.\n pauseVfs?(): unknown;\n isPaused?(): boolean;\n unpauseVfs?(): Promise<unknown>;\n }>;\n },\n opts: { name?: string; initialCapacity?: number } = {},\n): Promise<OpenCellSql & { reserve(n: number): Promise<number>; release(): void }> {\n const pool = await sqlite3.installOpfsSAHPoolVfs({\n name: opts.name ?? \"cellb\",\n initialCapacity: opts.initialCapacity ?? 16,\n });\n // The same pool again in this worker (SQLite WASM keeps one per name), after a `release()`.\n if (pool.isPaused?.()) await pool.unpauseVfs?.();\n // A full pool fails the open (`SAH pool is full`), and opening is synchronous while growing\n // the pool is not. A cell about to open reserves room first (`room`, awaited by the runtime\n // before the cell's first event); the pool grows when the reservations would not fit, and in\n // the background whenever less than half of it is free.\n const SLOTS_PER_CELL = 3; // the database, its journal, and one spare\n const reserved = new Set<string>();\n let growing: Promise<void> | undefined;\n const grow = (min: number): Promise<void> => {\n growing ??= pool\n .reserveMinimumCapacity(Math.max(min, pool.getCapacity() * 2))\n .then(() => {})\n .finally(() => {\n growing = undefined;\n });\n return growing;\n };\n const needed = () => pool.getFileCount() + SLOTS_PER_CELL * reserved.size;\n const room = (cellKey: string): Promise<void> | undefined => {\n reserved.add(cellKey);\n if (needed() <= pool.getCapacity()) return undefined;\n // Grow until this reservation (and every other one) fits; the open fails visibly if not.\n const fit = async (): Promise<void> => {\n while (needed() > pool.getCapacity()) {\n const before = pool.getCapacity();\n await grow(needed());\n if (pool.getCapacity() <= before) return; // it did not grow: the open will say why\n }\n };\n return fit();\n };\n const keepRoom = () => {\n const capacity = pool.getCapacity();\n if (!growing && capacity - needed() < capacity / 2) grow(capacity * 2).catch(() => {});\n };\n // The rollback journal stays in the pool between transactions (PERSIST): DELETE gives its pool\n // slot back and takes it again on every commit, which costs a header write each time. The\n // database file is the same in both modes (only WAL marks it).\n const openDb = (path: string) => {\n const db = new pool.OpfsSAHPoolDb(path);\n db.exec(\"PRAGMA journal_mode=PERSIST\");\n return db;\n };\n const open: OpenCellSql = (cellKey) => {\n const path = `/${cellKey.replace(/[^\\w.-]/g, \"_\")}.sqlite`;\n reserved.delete(cellKey);\n queueMicrotask(keepRoom); // after this open (and its first slot) is counted\n return new WasmSqliteDriver(\n openDb(path),\n {\n image: (db) => (sqlite3 as any).capi.sqlite3_js_db_export(db),\n // Replace the file inside the pool, then reopen it.\n restore(db, image) {\n db.close();\n (pool as any).importDb(path, image);\n return openDb(path);\n },\n },\n sqlite3 as never,\n );\n };\n return Object.assign(open, {\n room,\n reserve: (n: number) => pool.reserveMinimumCapacity(n),\n /**\n * Gives the pool's OPFS files back once every database is closed, so that another worker\n * (another tab's) can open the pool at once instead of when this worker is gone.\n */\n release: () => void pool.pauseVfs?.(),\n });\n}\n"]}
1
+ {"version":3,"file":"wasm-sqlite.js","sourceRoot":"","sources":["../../src/adapters/wasm-sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { splitSqlStatements } from \"../core/sql-split.js\";\nimport type { OpenCellSql, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { type DirtyPages, installPageHook } from \"./wasm-page-hook.js\";\nimport { type WasmSqlite3, wasmSessionCapture } from \"./wasm-session.js\";\n\n/** Minimal slice of the @sqlite.org/sqlite-wasm `oo1.DB` API we rely on. */\nexport interface Oo1Db {\n prepare(sql: string): Oo1Stmt;\n exec(sql: string): unknown;\n changes(): number;\n close(): void;\n}\ninterface Oo1Stmt {\n bind(values: unknown[]): Oo1Stmt;\n getColumnNames(): string[];\n columnCount: number;\n step(): boolean;\n get(target: unknown[]): unknown[];\n finalize(): void;\n}\n\n/**\n * SyncSqlDriver over the official SQLite WASM build (oo1 API). Synchronous,\n * so it satisfies `ctx.storage.sql`. Works in Node (in-memory, for tests) and\n * in browsers; persistence comes from the VFS behind the DB (see sahpoolOpener).\n */\n/** Backup hooks: how to export an image of the DB and how to replace it (returns the reopened DB). */\nexport interface WasmImageHooks {\n image(db: Oo1Db): Uint8Array;\n restore(db: Oo1Db, image: Uint8Array): Oo1Db;\n}\n\nexport class WasmSqliteDriver implements SyncSqlDriver {\n captureStart?: () => void;\n captureTake?: () => Uint8Array;\n captureDiscard?: () => void;\n applyChangeset?: (changeset: Uint8Array) => void;\n captureCoversRowidTables?: boolean;\n #db: Oo1Db;\n readonly #hooks?: WasmImageHooks;\n readonly #sqlite3?: WasmSqlite3;\n readonly #capi?: { sqlite3_get_autocommit(db: number): number };\n #pageHook?: ReturnType<typeof installPageHook>;\n constructor(\n db: Oo1Db,\n hooks?: WasmImageHooks,\n sqlite3?: WasmSqlite3,\n /** SQLite's C API, for `inTransaction()` alone (when `sqlite3` is not given). */\n capi?: { sqlite3_get_autocommit(db: number): number },\n ) {\n this.#db = db;\n this.#hooks = hooks;\n this.#sqlite3 = sqlite3;\n this.#capi = capi ?? sqlite3?.capi;\n if (!this.#capi?.sqlite3_get_autocommit)\n (this as { inTransaction?: unknown }).inTransaction = undefined;\n this.#enableDqs();\n this.#hookPages();\n if (sqlite3?.capi?.sqlite3session_create) {\n // Transaction-log capture (session extension), following the current DB handle.\n Object.assign(\n this,\n wasmSessionCapture(sqlite3, () => (this.#db as unknown as { pointer: number }).pointer),\n );\n }\n if (!hooks) {\n // No image support for this driver: hide the optional port methods.\n (this as { image?: unknown }).image = undefined;\n (this as { restore?: unknown }).restore = undefined;\n }\n }\n image(): Uint8Array {\n return this.#hooks!.image(this.#db);\n }\n\n /**\n * As celld and workerd: a double-quoted name that is no column is a string literal (SQLite's\n * DQS misfeature, off in this build).\n */\n #enableDqs(): void {\n const capi = this.#sqlite3?.capi;\n if (!capi?.sqlite3_db_config) return;\n const db = (this.#db as unknown as { pointer: number }).pointer;\n capi.sqlite3_db_config(db, capi.SQLITE_DBCONFIG_DQS_DML, 1, 0);\n capi.sqlite3_db_config(db, capi.SQLITE_DBCONFIG_DQS_DDL, 1, 0);\n }\n\n /** Installs the page hook on the current main database file (see wasm-page-hook.ts). */\n #hookPages(): void {\n this.#pageHook = undefined;\n if (!this.#sqlite3?.capi?.sqlite3_file_control) return;\n try {\n this.#pageHook = installPageHook(\n this.#sqlite3,\n (this.#db as unknown as { pointer: number }).pointer,\n );\n } catch {\n this.#pageHook = undefined;\n }\n }\n /** Pages written since the previous call — a committed transaction's changed pages. */\n takeDirtyPages(): DirtyPages | undefined {\n if (!this.#pageHook) return undefined;\n const pageSize = Number(this.exec(\"PRAGMA page_size\", []).rows[0][0]);\n const pageCount = Number(this.exec(\"PRAGMA page_count\", []).rows[0][0]);\n return this.#pageHook.take(pageSize, pageCount);\n }\n discardDirtyPages(): void {\n this.#pageHook?.discard();\n }\n /**\n * WASM builds cannot load extensions at run time; an extension must be compiled into the\n * build (e.g. the `sqlite-vec-wasm-demo` package for \"sqlite_vec\"). This only checks it is.\n */\n loadExtension(name: string): void {\n const probe: Record<string, string> = { sqlite_vec: \"SELECT vec_version()\" };\n if (!probe[name]) throw new Error(`Unknown SQLite extension \"${name}\"`);\n try {\n this.exec(probe[name], []);\n } catch {\n throw new Error(`SQLite extension \"${name}\" is not compiled into this SQLite WASM build`);\n }\n }\n restore(image: Uint8Array): void {\n // celld's databases are in WAL mode (header bytes 18–19 = 2); the WASM VFSes (memdb,\n // opfs-sahpool) have no shared memory for WAL and refuse to open such a file. The pages\n // are the same in rollback-journal mode: switch the header to 1 (celld sets WAL again\n // when it opens the database).\n if (image.length > 19 && image[18] === 2 && image[19] === 2) {\n image = image.slice();\n image[18] = 1;\n image[19] = 1;\n }\n this.#db = this.#hooks!.restore(this.#db, image);\n this.#enableDqs();\n this.#hookPages(); // a new database file behind the connection\n }\n exec(sql: string, params: unknown[]): SqlResult {\n const statements = params.length === 0 ? splitSqlStatements(sql) : [sql];\n // Several statements: the result of the last one, with the rows written by all of them.\n let last: SqlResult = { columnNames: [], rows: [], rowsWritten: 0 };\n let written = 0;\n for (const text of statements) {\n last = this.#one(text, params);\n written += last.rowsWritten;\n }\n return { ...last, rowsWritten: written };\n }\n #one(sql: string, params: unknown[]): SqlResult {\n const st = this.#db.prepare(sql);\n try {\n if (params.length) st.bind(params.map(toSqlite));\n const columnNames = st.columnCount ? st.getColumnNames() : [];\n const rows: unknown[][] = [];\n while (st.step()) if (columnNames.length) rows.push(st.get([]).map(fromSqlite));\n return { columnNames, rows, rowsWritten: columnNames.length ? 0 : this.#db.changes() };\n } finally {\n st.finalize();\n }\n }\n inTransaction(): boolean {\n const pointer = (this.#db as unknown as { pointer: number }).pointer;\n return this.#capi!.sqlite3_get_autocommit(pointer) === 0;\n }\n databaseSize(): number {\n const pc = this.exec(\"PRAGMA page_count\", []).rows[0][0];\n const ps = this.exec(\"PRAGMA page_size\", []).rows[0][0];\n return Number(pc) * Number(ps);\n }\n close(): void {\n this.#db.close();\n }\n}\n\nfunction toSqlite(v: unknown): unknown {\n if (v instanceof ArrayBuffer) return new Uint8Array(v);\n if (typeof v === \"boolean\") return v ? 1 : 0;\n return v === undefined ? null : v;\n}\nfunction fromSqlite(v: unknown): unknown {\n if (v instanceof Uint8Array) return v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength);\n return v;\n}\n\n/** In-memory WASM databases (tests, ephemeral cells). `sqlite3` = await sqlite3InitModule(). */\n/** Image hooks for an in-memory DB: `sqlite3_js_db_export` / `sqlite3_deserialize`. */\nexport function memoryImageHooks(sqlite3: any): WasmImageHooks {\n const { capi, wasm } = sqlite3;\n return {\n image: (db) => sqlite3.capi.sqlite3_js_db_export(db),\n restore(db, image) {\n const p = wasm.allocFromTypedArray(image);\n const rc = capi.sqlite3_deserialize(\n (db as any).pointer,\n \"main\",\n p,\n image.byteLength,\n image.byteLength,\n capi.SQLITE_DESERIALIZE_FREEONCLOSE | capi.SQLITE_DESERIALIZE_RESIZEABLE,\n );\n if (rc) throw new Error(`sqlite3_deserialize failed: ${capi.sqlite3_js_rc_str?.(rc) ?? rc}`);\n return db;\n },\n };\n}\n\n/**\n * Sets up a new SQLite WASM connection for a cell. One worker holds many small cells, and SQLite\n * WASM's heap is capped at 2 GiB and never shrinks, so each connection's fixed memory decides how\n * many cells can be open at once:\n * - no lookaside: SQLite's per-connection allocator of small blocks, about 45 KiB each;\n * - 4 KiB pages for a database that is still empty (celld's page size: SQLite's native default;\n * the WASM build's is 8 KiB). The page cache starts with a block of pages, so the page size\n * sets most of a connection's memory. A database that already holds data keeps its page size.\n * Together they bring a connection with one small table from about 258 KiB to about 120 KiB.\n */\nexport function tuneCellConnection(sqlite3: unknown, db: Oo1Db): void {\n const capi = (sqlite3 as { capi?: Record<string, any> }).capi;\n const pointer = (db as unknown as { pointer?: number }).pointer;\n // Before the connection runs any statement: SQLite refuses (SQLITE_BUSY) while lookaside is used.\n if (capi?.sqlite3_db_config && pointer) {\n capi.sqlite3_db_config(pointer, capi.SQLITE_DBCONFIG_LOOKASIDE, 0, 0, 0);\n }\n db.exec(\"PRAGMA page_size=4096\");\n}\n\nlet memdbCounter = 0;\nexport function wasmMemoryOpener(sqlite3: {\n oo1: { DB: new (path: string, mode?: string) => Oo1Db };\n}): OpenCellSql {\n return () => {\n // memdb (not \":memory:\"): an in-memory database that IS a VFS file, so the page hook works.\n const db = new sqlite3.oo1.DB(`file:/cells-mem-${++memdbCounter}?vfs=memdb`, \"c\");\n tuneCellConnection(sqlite3, db);\n return new WasmSqliteDriver(db, memoryImageHooks(sqlite3), sqlite3 as never);\n };\n}\n\n/**\n * Browser persistence: one OPFS \"SyncAccessHandle pool\" database per cell.\n * Must run in a Dedicated Worker; needs neither COOP/COEP nor SharedArrayBuffer.\n * Not runnable in Node (installOpfsSAHPoolVfs is browser-only) — covered by P04's Playwright spec.\n */\nexport async function sahpoolOpener(\n sqlite3: {\n installOpfsSAHPoolVfs(opts: { name?: string; initialCapacity?: number }): Promise<{\n OpfsSAHPoolDb: new (path: string) => Oo1Db;\n reserveMinimumCapacity(n: number): Promise<number>;\n getCapacity(): number;\n getFileCount(): number;\n // Not in older SQLite WASM builds (sqlite-vec's): there `release()` does nothing.\n pauseVfs?(): unknown;\n isPaused?(): boolean;\n unpauseVfs?(): Promise<unknown>;\n }>;\n },\n opts: { name?: string; initialCapacity?: number } = {},\n): Promise<OpenCellSql & { reserve(n: number): Promise<number>; release(): void }> {\n const pool = await sqlite3.installOpfsSAHPoolVfs({\n name: opts.name ?? \"cellb\",\n initialCapacity: opts.initialCapacity ?? 16,\n });\n // The same pool again in this worker (SQLite WASM keeps one per name), after a `release()`.\n if (pool.isPaused?.()) await pool.unpauseVfs?.();\n // A full pool fails the open (`SAH pool is full`), and opening is synchronous while growing\n // the pool is not. A cell about to open reserves room first (`room`, awaited by the runtime\n // before the cell's first event); the pool grows when the reservations would not fit, and in\n // the background whenever less than half of it is free.\n const SLOTS_PER_CELL = 3; // the database, its journal, and one spare\n const reserved = new Set<string>();\n let growing: Promise<void> | undefined;\n const grow = (min: number): Promise<void> => {\n growing ??= pool\n .reserveMinimumCapacity(Math.max(min, pool.getCapacity() * 2))\n .then(() => {})\n .finally(() => {\n growing = undefined;\n });\n return growing;\n };\n const needed = () => pool.getFileCount() + SLOTS_PER_CELL * reserved.size;\n const room = (cellKey: string): Promise<void> | undefined => {\n reserved.add(cellKey);\n if (needed() <= pool.getCapacity()) return undefined;\n // Grow until this reservation (and every other one) fits; the open fails visibly if not.\n const fit = async (): Promise<void> => {\n while (needed() > pool.getCapacity()) {\n const before = pool.getCapacity();\n await grow(needed());\n if (pool.getCapacity() <= before) return; // it did not grow: the open will say why\n }\n };\n return fit();\n };\n const keepRoom = () => {\n const capacity = pool.getCapacity();\n if (!growing && capacity - needed() < capacity / 2) grow(capacity * 2).catch(() => {});\n };\n // The rollback journal stays in the pool between transactions (PERSIST): DELETE gives its pool\n // slot back and takes it again on every commit, which costs a header write each time. The\n // database file is the same in both modes (only WAL marks it).\n const openDb = (path: string) => {\n const db = new pool.OpfsSAHPoolDb(path);\n tuneCellConnection(sqlite3, db);\n db.exec(\"PRAGMA journal_mode=PERSIST\");\n return db;\n };\n const open: OpenCellSql = (cellKey) => {\n const path = `/${cellKey.replace(/[^\\w.-]/g, \"_\")}.sqlite`;\n reserved.delete(cellKey);\n queueMicrotask(keepRoom); // after this open (and its first slot) is counted\n return new WasmSqliteDriver(\n openDb(path),\n {\n image: (db) => (sqlite3 as any).capi.sqlite3_js_db_export(db),\n // Replace the file inside the pool, then reopen it.\n restore(db, image) {\n db.close();\n (pool as any).importDb(path, image);\n return openDb(path);\n },\n },\n sqlite3 as never,\n );\n };\n return Object.assign(open, {\n room,\n reserve: (n: number) => pool.reserveMinimumCapacity(n),\n /**\n * Gives the pool's OPFS files back once every database is closed, so that another worker\n * (another tab's) can open the pool at once instead of when this worker is gone.\n */\n release: () => void pool.pauseVfs?.(),\n });\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { sha256 } from "@noble/hashes/sha256";
2
- import { bytesToHex } from "@noble/hashes/utils";
1
+ import { sha256 } from "@noble/hashes/sha2.js";
2
+ import { bytesToHex } from "@noble/hashes/utils.js";
3
3
  const TYPES = {
4
4
  html: "text/html; charset=utf-8",
5
5
  htm: "text/html; charset=utf-8",
@@ -1 +1 @@
1
- {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/bindings/assets.ts"],"names":[],"mappings":"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","sourcesContent":["import { sha256 } from \"@noble/hashes/sha256\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\n\n/**\n * Static assets as celld serves them (docs/services/static-assets.md), checked against\n * celld 0.6.2 by conformance/projects/assets-api:\n * GET/HEAD only; `run_worker_first`; `_redirects` first (301/302/303/307/308 → Location,\n * 200 → rewrite, `*` → `:splat`); `html_handling` auto-trailing-slash with 307 to canonical\n * paths; `not_found_handling` (with a Worker, only for navigation requests); strong\n * SHA-256 ETag, If-None-Match → 304, Range → 206/416, `_headers` rules,\n * `Cache-Control: public, max-age=0, must-revalidate` by default; unknown extension → no\n * Content-Type.\n */\nexport interface AssetsConfig {\n directory?: string;\n binding?: string;\n html_handling?: \"auto-trailing-slash\" | \"force-trailing-slash\" | \"drop-trailing-slash\" | \"none\";\n not_found_handling?: \"single-page-application\" | \"404-page\" | \"none\";\n run_worker_first?: boolean | string[];\n}\n\n/** File path (\"/about.html\") → bytes. */\nexport type AssetFiles = Map<string, Uint8Array>;\n\nconst TYPES: Record<string, string> = {\n html: \"text/html; charset=utf-8\",\n htm: \"text/html; charset=utf-8\",\n css: \"text/css; charset=utf-8\",\n js: \"text/javascript; charset=utf-8\",\n mjs: \"text/javascript; charset=utf-8\",\n json: \"application/json; charset=utf-8\",\n txt: \"text/plain; charset=utf-8\",\n xml: \"application/xml; charset=utf-8\",\n svg: \"image/svg+xml\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n ico: \"image/x-icon\",\n wasm: \"application/wasm\",\n pdf: \"application/pdf\",\n woff2: \"font/woff2\",\n woff: \"font/woff\",\n};\n\ninterface Redirect {\n from: RegExp;\n names: string[];\n to: string;\n status: number;\n}\n\nfunction globToRegExp(pattern: string): { re: RegExp; names: string[] } {\n const names: string[] = [];\n const src = pattern\n .split(/(\\*|:[A-Za-z_]\\w*)/)\n .map((part) => {\n if (part === \"*\") {\n names.push(\"splat\");\n return \"(.*)\";\n }\n if (part.startsWith(\":\")) {\n names.push(part.slice(1));\n return \"([^/]+)\";\n }\n return part.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n })\n .join(\"\");\n return { re: new RegExp(`^${src}$`), names };\n}\n\nfunction parseRedirects(text: string | undefined): Redirect[] {\n const out: Redirect[] = [];\n for (const line of (text ?? \"\").split(\"\\n\")) {\n const t = line.trim();\n if (!t || t.startsWith(\"#\")) continue;\n const [from, to, status] = t.split(/\\s+/);\n const { re, names } = globToRegExp(from);\n out.push({ from: re, names, to, status: Number(status ?? 302) });\n }\n return out;\n}\n\nfunction parseHeaderRules(text: string | undefined): { re: RegExp; headers: [string, string][] }[] {\n const rules: { re: RegExp; headers: [string, string][] }[] = [];\n for (const line of (text ?? \"\").split(\"\\n\")) {\n if (!line.trim() || line.trim().startsWith(\"#\")) continue;\n if (!/^\\s/.test(line)) rules.push({ re: globToRegExp(line.trim()).re, headers: [] });\n else {\n const i = line.indexOf(\":\");\n if (i > 0 && rules.length)\n rules.at(-1)!.headers.push([line.slice(0, i).trim(), line.slice(i + 1).trim()]);\n }\n }\n return rules;\n}\n\nconst decoder = new TextDecoder();\n\nexport function createAssets(files: AssetFiles, config: AssetsConfig, hasWorker: boolean) {\n const redirects = parseRedirects(\n files.has(\"/_redirects\") ? decoder.decode(files.get(\"/_redirects\")) : undefined,\n );\n const headerRules = parseHeaderRules(\n files.has(\"/_headers\") ? decoder.decode(files.get(\"/_headers\")) : undefined,\n );\n const etags = new Map<string, string>();\n const etagOf = (path: string) => {\n let e = etags.get(path);\n if (!e) {\n e = `\"${bytesToHex(sha256(files.get(path)!))}\"`;\n etags.set(path, e);\n }\n return e;\n };\n const exists = (p: string) => files.has(p) && p !== \"/_headers\" && p !== \"/_redirects\";\n\n const workerFirst = (path: string) => {\n const r = config.run_worker_first;\n if (r === true) return true;\n if (!Array.isArray(r)) return false;\n let hit = false;\n for (const p of r) {\n const neg = p.startsWith(\"!\");\n if (globToRegExp(neg ? p.slice(1) : p).re.test(path)) hit = !neg;\n }\n return hit;\n };\n\n /** Resolves a URL path to a file, or a canonical redirect (auto-trailing-slash). */\n const lookup = (path: string): { file: string } | { redirect: string } | undefined => {\n const mode = config.html_handling ?? \"auto-trailing-slash\";\n if (mode === \"none\") return exists(path) ? { file: path } : undefined;\n if (mode === \"force-trailing-slash\" || mode === \"drop-trailing-slash\")\n return lookupSlash(path, mode);\n if (path.endsWith(\"/index.html\")) {\n if (exists(path)) return { redirect: path.slice(0, -\"index.html\".length) };\n } else if (path.endsWith(\".html\")) {\n if (exists(path)) return { redirect: path.slice(0, -\".html\".length) };\n }\n if (path.endsWith(\"/\")) {\n // `/` and `/dir/` → index.html. (celld 0.6.2 serves only the root index here and\n // falls through for nested ones — a celld bug; we follow Cloudflare, see docs/celld-compatibility.md.)\n return exists(`${path}index.html`) ? { file: `${path}index.html` } : undefined;\n }\n if (exists(path)) return { file: path };\n if (exists(`${path}.html`)) return { file: `${path}.html` };\n if (exists(`${path}/index.html`)) return { redirect: `${path}/` };\n return undefined;\n };\n\n /**\n * force-trailing-slash: HTML pages live at `/x/` (x.html or x/index.html); `/x`, `/x.html`\n * and `/x/index.html` redirect there. drop-trailing-slash: they live at `/x`; `/x/`,\n * `/x.html` and `/x/index.html` redirect there. The root is `/` in both.\n * (celld 0.6.2 answers 404 for any path ending in \"/\" other than the root, so in force\n * mode it redirects to pages it then refuses — a celld bug; we follow Cloudflare.)\n */\n const lookupSlash = (path: string, mode: \"force-trailing-slash\" | \"drop-trailing-slash\") => {\n if (path === \"/\") return exists(\"/index.html\") ? { file: \"/index.html\" } : undefined;\n const page = (base: string) =>\n exists(`${base}.html`)\n ? `${base}.html`\n : exists(`${base}/index.html`)\n ? `${base}/index.html`\n : undefined;\n let base: string | undefined;\n if (path.endsWith(\"/index.html\")) base = path.slice(0, -\"/index.html\".length);\n else if (path.endsWith(\".html\")) base = path.slice(0, -\".html\".length);\n else if (path.endsWith(\"/\")) base = path.slice(0, -1);\n if (base === \"\") return exists(path) ? { redirect: \"/\" } : undefined;\n const canonical = (b: string) => (mode === \"force-trailing-slash\" ? `${b}/` : b);\n if (base !== undefined) {\n const file = page(base);\n if (!file) return undefined;\n return path === canonical(base) ? { file } : { redirect: canonical(base) };\n }\n if (exists(path)) return { file: path }; // non-HTML file\n const file = page(path);\n if (!file) return undefined;\n return mode === \"drop-trailing-slash\" ? { file } : { redirect: `${path}/` };\n };\n\n const serve = (req: Request, file: string, status = 200): Response => {\n const bytes = files.get(file)!;\n const etag = etagOf(file);\n const headers = new Headers();\n const ext = file.split(\".\").pop()?.toLowerCase() ?? \"\";\n for (const rule of headerRules)\n if (rule.re.test(new URL(req.url).pathname))\n for (const [k, v] of rule.headers) headers.set(k, v);\n headers.set(\"etag\", etag);\n if (req.headers.get(\"if-none-match\") === etag) {\n headers.delete(\"cache-control\");\n return new Response(null, { status: 304, headers });\n }\n const range = /^bytes=(\\d*)-(\\d*)$/.exec(req.headers.get(\"range\") ?? \"\");\n if (range && status === 200) {\n const size = bytes.byteLength;\n let start = range[1] === \"\" ? size - Number(range[2]) : Number(range[1]);\n let end = range[1] === \"\" ? size - 1 : range[2] === \"\" ? size - 1 : Number(range[2]);\n if (start >= size || start < 0 || start > end) {\n headers.set(\"content-range\", `bytes */${size}`);\n headers.set(\"content-length\", \"0\");\n headers.delete(\"cache-control\");\n return new Response(null, { status: 416, headers });\n }\n end = Math.min(end, size - 1);\n start = Math.max(0, start);\n if (TYPES[ext]) headers.set(\"content-type\", TYPES[ext]);\n headers.set(\"content-range\", `bytes ${start}-${end}/${size}`);\n headers.set(\"content-length\", String(end - start + 1));\n headers.delete(\"cache-control\");\n return new Response(\n req.method === \"HEAD\" ? null : (bytes.slice(start, end + 1) as BodyInit),\n { status: 206, headers },\n );\n }\n if (TYPES[ext]) headers.set(\"content-type\", TYPES[ext]);\n if (!headers.has(\"cache-control\"))\n headers.set(\"cache-control\", \"public, max-age=0, must-revalidate\");\n headers.set(\"content-length\", String(bytes.byteLength));\n return new Response(req.method === \"HEAD\" ? null : (bytes as BodyInit), { status, headers });\n };\n\n const redirectTo = (location: string, status: number) =>\n new Response(null, { status, headers: { location, \"content-length\": \"0\" } });\n\n /** The asset router. `undefined` = not an asset: hand the request to the Worker. */\n const route = (req: Request, viaBinding = false): Response | undefined => {\n if (req.method !== \"GET\" && req.method !== \"HEAD\") return undefined;\n const url = new URL(req.url);\n let path = decodeURIComponent(url.pathname);\n if (!viaBinding && hasWorker && workerFirst(path)) return undefined;\n for (const r of redirects) {\n const m = r.from.exec(path);\n if (!m) continue;\n let to = r.to;\n for (const [i, n] of r.names.entries()) to = to.replace(`:${n}`, m[i + 1]);\n if (r.status === 200) {\n path = to;\n break;\n }\n return redirectTo(to, r.status);\n }\n const hit = lookup(path);\n if (hit && \"redirect\" in hit) return redirectTo(`${hit.redirect}${url.search}`, 307);\n if (hit) return serve(req, hit.file);\n // Miss: not_found_handling (with a Worker, only for navigation requests).\n const nav = req.headers.get(\"sec-fetch-mode\") === \"navigate\";\n if (!hasWorker || nav || viaBinding) {\n if (config.not_found_handling === \"single-page-application\" && exists(\"/index.html\"))\n return serve(req, \"/index.html\");\n if (config.not_found_handling === \"404-page\") {\n for (\n let dir = path.slice(0, path.lastIndexOf(\"/\") + 1);\n dir;\n dir = dir.slice(0, dir.lastIndexOf(\"/\", dir.length - 2) + 1)\n ) {\n if (exists(`${dir}404.html`)) return serve(req, `${dir}404.html`, 404);\n if (dir === \"/\") break;\n }\n }\n }\n if (hasWorker && !viaBinding) return undefined;\n return new Response(\"Not found\", {\n status: 404,\n headers: { \"content-type\": \"application/json\" },\n });\n };\n\n return {\n route,\n /** The `env.ASSETS` binding. */\n binding: {\n fetch: async (input: RequestInfo | URL, init?: RequestInit) =>\n route(new Request(input, init), true)!,\n },\n };\n}\n"]}
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/bindings/assets.ts"],"names":[],"mappings":"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","sourcesContent":["import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bytesToHex } from \"@noble/hashes/utils.js\";\n\n/**\n * Static assets as celld serves them (docs/services/static-assets.md), checked against\n * celld 0.6.2 by conformance/projects/assets-api:\n * GET/HEAD only; `run_worker_first`; `_redirects` first (301/302/303/307/308 → Location,\n * 200 → rewrite, `*` → `:splat`); `html_handling` auto-trailing-slash with 307 to canonical\n * paths; `not_found_handling` (with a Worker, only for navigation requests); strong\n * SHA-256 ETag, If-None-Match → 304, Range → 206/416, `_headers` rules,\n * `Cache-Control: public, max-age=0, must-revalidate` by default; unknown extension → no\n * Content-Type.\n */\nexport interface AssetsConfig {\n directory?: string;\n binding?: string;\n html_handling?: \"auto-trailing-slash\" | \"force-trailing-slash\" | \"drop-trailing-slash\" | \"none\";\n not_found_handling?: \"single-page-application\" | \"404-page\" | \"none\";\n run_worker_first?: boolean | string[];\n}\n\n/** File path (\"/about.html\") → bytes. */\nexport type AssetFiles = Map<string, Uint8Array>;\n\nconst TYPES: Record<string, string> = {\n html: \"text/html; charset=utf-8\",\n htm: \"text/html; charset=utf-8\",\n css: \"text/css; charset=utf-8\",\n js: \"text/javascript; charset=utf-8\",\n mjs: \"text/javascript; charset=utf-8\",\n json: \"application/json; charset=utf-8\",\n txt: \"text/plain; charset=utf-8\",\n xml: \"application/xml; charset=utf-8\",\n svg: \"image/svg+xml\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n ico: \"image/x-icon\",\n wasm: \"application/wasm\",\n pdf: \"application/pdf\",\n woff2: \"font/woff2\",\n woff: \"font/woff\",\n};\n\ninterface Redirect {\n from: RegExp;\n names: string[];\n to: string;\n status: number;\n}\n\nfunction globToRegExp(pattern: string): { re: RegExp; names: string[] } {\n const names: string[] = [];\n const src = pattern\n .split(/(\\*|:[A-Za-z_]\\w*)/)\n .map((part) => {\n if (part === \"*\") {\n names.push(\"splat\");\n return \"(.*)\";\n }\n if (part.startsWith(\":\")) {\n names.push(part.slice(1));\n return \"([^/]+)\";\n }\n return part.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n })\n .join(\"\");\n return { re: new RegExp(`^${src}$`), names };\n}\n\nfunction parseRedirects(text: string | undefined): Redirect[] {\n const out: Redirect[] = [];\n for (const line of (text ?? \"\").split(\"\\n\")) {\n const t = line.trim();\n if (!t || t.startsWith(\"#\")) continue;\n const [from, to, status] = t.split(/\\s+/);\n const { re, names } = globToRegExp(from);\n out.push({ from: re, names, to, status: Number(status ?? 302) });\n }\n return out;\n}\n\nfunction parseHeaderRules(text: string | undefined): { re: RegExp; headers: [string, string][] }[] {\n const rules: { re: RegExp; headers: [string, string][] }[] = [];\n for (const line of (text ?? \"\").split(\"\\n\")) {\n if (!line.trim() || line.trim().startsWith(\"#\")) continue;\n if (!/^\\s/.test(line)) rules.push({ re: globToRegExp(line.trim()).re, headers: [] });\n else {\n const i = line.indexOf(\":\");\n if (i > 0 && rules.length)\n rules.at(-1)!.headers.push([line.slice(0, i).trim(), line.slice(i + 1).trim()]);\n }\n }\n return rules;\n}\n\nconst decoder = new TextDecoder();\n\nexport function createAssets(files: AssetFiles, config: AssetsConfig, hasWorker: boolean) {\n const redirects = parseRedirects(\n files.has(\"/_redirects\") ? decoder.decode(files.get(\"/_redirects\")) : undefined,\n );\n const headerRules = parseHeaderRules(\n files.has(\"/_headers\") ? decoder.decode(files.get(\"/_headers\")) : undefined,\n );\n const etags = new Map<string, string>();\n const etagOf = (path: string) => {\n let e = etags.get(path);\n if (!e) {\n e = `\"${bytesToHex(sha256(files.get(path)!))}\"`;\n etags.set(path, e);\n }\n return e;\n };\n const exists = (p: string) => files.has(p) && p !== \"/_headers\" && p !== \"/_redirects\";\n\n const workerFirst = (path: string) => {\n const r = config.run_worker_first;\n if (r === true) return true;\n if (!Array.isArray(r)) return false;\n let hit = false;\n for (const p of r) {\n const neg = p.startsWith(\"!\");\n if (globToRegExp(neg ? p.slice(1) : p).re.test(path)) hit = !neg;\n }\n return hit;\n };\n\n /** Resolves a URL path to a file, or a canonical redirect (auto-trailing-slash). */\n const lookup = (path: string): { file: string } | { redirect: string } | undefined => {\n const mode = config.html_handling ?? \"auto-trailing-slash\";\n if (mode === \"none\") return exists(path) ? { file: path } : undefined;\n if (mode === \"force-trailing-slash\" || mode === \"drop-trailing-slash\")\n return lookupSlash(path, mode);\n if (path.endsWith(\"/index.html\")) {\n if (exists(path)) return { redirect: path.slice(0, -\"index.html\".length) };\n } else if (path.endsWith(\".html\")) {\n if (exists(path)) return { redirect: path.slice(0, -\".html\".length) };\n }\n if (path.endsWith(\"/\")) {\n // `/` and `/dir/` → index.html. (celld 0.6.2 serves only the root index here and\n // falls through for nested ones — a celld bug; we follow Cloudflare, see docs/celld-compatibility.md.)\n return exists(`${path}index.html`) ? { file: `${path}index.html` } : undefined;\n }\n if (exists(path)) return { file: path };\n if (exists(`${path}.html`)) return { file: `${path}.html` };\n if (exists(`${path}/index.html`)) return { redirect: `${path}/` };\n return undefined;\n };\n\n /**\n * force-trailing-slash: HTML pages live at `/x/` (x.html or x/index.html); `/x`, `/x.html`\n * and `/x/index.html` redirect there. drop-trailing-slash: they live at `/x`; `/x/`,\n * `/x.html` and `/x/index.html` redirect there. The root is `/` in both.\n * (celld 0.6.2 answers 404 for any path ending in \"/\" other than the root, so in force\n * mode it redirects to pages it then refuses — a celld bug; we follow Cloudflare.)\n */\n const lookupSlash = (path: string, mode: \"force-trailing-slash\" | \"drop-trailing-slash\") => {\n if (path === \"/\") return exists(\"/index.html\") ? { file: \"/index.html\" } : undefined;\n const page = (base: string) =>\n exists(`${base}.html`)\n ? `${base}.html`\n : exists(`${base}/index.html`)\n ? `${base}/index.html`\n : undefined;\n let base: string | undefined;\n if (path.endsWith(\"/index.html\")) base = path.slice(0, -\"/index.html\".length);\n else if (path.endsWith(\".html\")) base = path.slice(0, -\".html\".length);\n else if (path.endsWith(\"/\")) base = path.slice(0, -1);\n if (base === \"\") return exists(path) ? { redirect: \"/\" } : undefined;\n const canonical = (b: string) => (mode === \"force-trailing-slash\" ? `${b}/` : b);\n if (base !== undefined) {\n const file = page(base);\n if (!file) return undefined;\n return path === canonical(base) ? { file } : { redirect: canonical(base) };\n }\n if (exists(path)) return { file: path }; // non-HTML file\n const file = page(path);\n if (!file) return undefined;\n return mode === \"drop-trailing-slash\" ? { file } : { redirect: `${path}/` };\n };\n\n const serve = (req: Request, file: string, status = 200): Response => {\n const bytes = files.get(file)!;\n const etag = etagOf(file);\n const headers = new Headers();\n const ext = file.split(\".\").pop()?.toLowerCase() ?? \"\";\n for (const rule of headerRules)\n if (rule.re.test(new URL(req.url).pathname))\n for (const [k, v] of rule.headers) headers.set(k, v);\n headers.set(\"etag\", etag);\n if (req.headers.get(\"if-none-match\") === etag) {\n headers.delete(\"cache-control\");\n return new Response(null, { status: 304, headers });\n }\n const range = /^bytes=(\\d*)-(\\d*)$/.exec(req.headers.get(\"range\") ?? \"\");\n if (range && status === 200) {\n const size = bytes.byteLength;\n let start = range[1] === \"\" ? size - Number(range[2]) : Number(range[1]);\n let end = range[1] === \"\" ? size - 1 : range[2] === \"\" ? size - 1 : Number(range[2]);\n if (start >= size || start < 0 || start > end) {\n headers.set(\"content-range\", `bytes */${size}`);\n headers.set(\"content-length\", \"0\");\n headers.delete(\"cache-control\");\n return new Response(null, { status: 416, headers });\n }\n end = Math.min(end, size - 1);\n start = Math.max(0, start);\n if (TYPES[ext]) headers.set(\"content-type\", TYPES[ext]);\n headers.set(\"content-range\", `bytes ${start}-${end}/${size}`);\n headers.set(\"content-length\", String(end - start + 1));\n headers.delete(\"cache-control\");\n return new Response(\n req.method === \"HEAD\" ? null : (bytes.slice(start, end + 1) as BodyInit),\n { status: 206, headers },\n );\n }\n if (TYPES[ext]) headers.set(\"content-type\", TYPES[ext]);\n if (!headers.has(\"cache-control\"))\n headers.set(\"cache-control\", \"public, max-age=0, must-revalidate\");\n headers.set(\"content-length\", String(bytes.byteLength));\n return new Response(req.method === \"HEAD\" ? null : (bytes as BodyInit), { status, headers });\n };\n\n const redirectTo = (location: string, status: number) =>\n new Response(null, { status, headers: { location, \"content-length\": \"0\" } });\n\n /** The asset router. `undefined` = not an asset: hand the request to the Worker. */\n const route = (req: Request, viaBinding = false): Response | undefined => {\n if (req.method !== \"GET\" && req.method !== \"HEAD\") return undefined;\n const url = new URL(req.url);\n let path = decodeURIComponent(url.pathname);\n if (!viaBinding && hasWorker && workerFirst(path)) return undefined;\n for (const r of redirects) {\n const m = r.from.exec(path);\n if (!m) continue;\n let to = r.to;\n for (const [i, n] of r.names.entries()) to = to.replace(`:${n}`, m[i + 1]);\n if (r.status === 200) {\n path = to;\n break;\n }\n return redirectTo(to, r.status);\n }\n const hit = lookup(path);\n if (hit && \"redirect\" in hit) return redirectTo(`${hit.redirect}${url.search}`, 307);\n if (hit) return serve(req, hit.file);\n // Miss: not_found_handling (with a Worker, only for navigation requests).\n const nav = req.headers.get(\"sec-fetch-mode\") === \"navigate\";\n if (!hasWorker || nav || viaBinding) {\n if (config.not_found_handling === \"single-page-application\" && exists(\"/index.html\"))\n return serve(req, \"/index.html\");\n if (config.not_found_handling === \"404-page\") {\n for (\n let dir = path.slice(0, path.lastIndexOf(\"/\") + 1);\n dir;\n dir = dir.slice(0, dir.lastIndexOf(\"/\", dir.length - 2) + 1)\n ) {\n if (exists(`${dir}404.html`)) return serve(req, `${dir}404.html`, 404);\n if (dir === \"/\") break;\n }\n }\n }\n if (hasWorker && !viaBinding) return undefined;\n return new Response(\"Not found\", {\n status: 404,\n headers: { \"content-type\": \"application/json\" },\n });\n };\n\n return {\n route,\n /** The `env.ASSETS` binding. */\n binding: {\n fetch: async (input: RequestInfo | URL, init?: RequestInit) =>\n route(new Request(input, init), true)!,\n },\n };\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { md5 } from "@noble/hashes/legacy";
2
- import { bytesToHex } from "@noble/hashes/utils";
1
+ import { md5 } from "@noble/hashes/legacy.js";
2
+ import { bytesToHex } from "@noble/hashes/utils.js";
3
3
  export class R2Object {
4
4
  key;
5
5
  version;