@statewalker/webrun-files-sqlite 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,179 +1,212 @@
1
1
  # @statewalker/webrun-files-sqlite
2
2
 
3
- Two `FilesApi` implementations over SQLite, sharing one small SQL port with drivers for
4
- `node:sqlite`, Cloudflare Durable Object storage and Cloudflare D1.
3
+ ## What it is
4
+
5
+ Two implementations of `FilesApi` from `@statewalker/webrun-files` that store files in a SQLite
6
+ database, through `@statewalker/db-api`'s `Db`: any `@statewalker/db-sqlite-*` driver — `node:sqlite`,
7
+ the browser (sqlite-wasm), or a Durable Object's storage:
8
+
9
+ - `SqliteFilesApi` streams content as fixed-size blocks (1 MiB by default, optionally deflated per
10
+ block), shares identical content between paths, and makes every metadata change one transaction.
11
+ Files of any size.
12
+ - `SqlarFilesApi` stores each file as one row of a [SQLite Archive](https://sqlite.org/sqlar.html),
13
+ readable by `sqlite3 -A`. Whole-file reads and writes.
14
+
15
+ ## Why it exists
16
+
17
+ Durable Objects offer SQLite and nothing like a file system, and a single SQLite file is a
18
+ convenient, transactional store elsewhere too. Storing a file as one blob breaks there: Durable
19
+ Objects cap a row at 2 MB, and a blob is read and written whole. `SqliteFilesApi` cuts
20
+ content into blocks that stay under the cap and can be streamed and range-read. `SqlarFilesApi`
21
+ exists for the opposite need: an archive other SQLAR tools can open, in runtimes without a row
22
+ limit.
5
23
 
6
24
  | | `SqliteFilesApi` | `SqlarFilesApi` |
7
25
  | --- | --- | --- |
8
- | Storage | paths → contents → blocks of ≤ 1 MiB | one row per file, [SQLite Archive](https://sqlite.org/sqlar.html) format |
26
+ | Storage | paths -> contents -> blocks of at most 1.5 MiB | one row per file, SQLite Archive format |
9
27
  | File size | unbounded; streamed block by block | one blob, read and written whole |
10
- | Durable Objects / D1 | yes — every row stays under the 2 MB limit | only files whose stored row is under 2 MB |
28
+ | Durable Objects | yes, every row stays under the 2 MB limit | only files whose stored row is under 2 MB |
11
29
  | Readable by `sqlite3 -A` | no | yes |
12
30
  | Identical content | stored once, shared by every path | stored per path |
13
31
 
14
- Use `SqliteFilesApi` by default, and `SqlarFilesApi` where the archive must be portable to other
15
- SQLAR tools, in a runtime without a row limit (Node, Deno, Bun, a browser SQLite).
32
+ Use `SqliteFilesApi` by default.
16
33
 
17
- ## Installation
34
+ ## How to use
18
35
 
19
36
  ```bash
20
- npm install @statewalker/webrun-files-sqlite @statewalker/webrun-files
37
+ pnpm add @statewalker/webrun-files-sqlite @statewalker/webrun-files @statewalker/db-sqlite-node
21
38
  ```
22
39
 
23
- `pako` is not a dependency: pass the module to `pakoCodec` / `pakoDeflateCodec` if you use them.
40
+ Pick the driver for your runtime: `@statewalker/db-sqlite-node` on Node, `@statewalker/db-sqlite-do`
41
+ in a Durable Object, `@statewalker/db-sqlite-browser` in the browser.
24
42
 
25
- ## Drivers
43
+ `pako` is not a dependency: pass the module to `pakoCodec` / `pakoDeflateCodec` if you use them.
26
44
 
27
- Both implementations take a `SqlDriver`. The package imports none of the handles below — they are
28
- passed in — so it bundles for any runtime.
45
+ One entry point, `@statewalker/webrun-files-sqlite`: ESM (`dist/esm/index.js`), CommonJS
46
+ (`dist/cjs/index.cjs`), types (`dist/index.d.ts`); sources in `src/`. The package imports no
47
+ database driver itself; you pass a `Db` in, so it bundles for any runtime.
29
48
 
30
49
  ```typescript
31
- import { D1SqlDriver, DoSqlDriver, NodeSqlDriver } from "@statewalker/webrun-files-sqlite";
50
+ import { newNodeSqliteDb } from '@statewalker/db-sqlite-node';
51
+ import { SqliteFilesApi } from '@statewalker/webrun-files-sqlite';
52
+
53
+ const db = await newNodeSqliteDb({ path: 'files.db' }); // node:sqlite
54
+ // or, in a Durable Object (ctx.storage, not ctx.storage.sql):
55
+ // import { newDoSqliteDb } from '@statewalker/db-sqlite-do';
56
+ // const db = newDoSqliteDb(this.ctx.storage);
32
57
 
33
- new NodeSqlDriver(new DatabaseSync("files.db")); // node:sqlite
34
- new DoSqlDriver(this.ctx.storage); // inside a Durable Object: ctx.storage, not ctx.storage.sql
35
- new D1SqlDriver(env.DB); // a D1 binding
58
+ const files = new SqliteFilesApi(db, { blockSize: 1024 * 1024 });
59
+ await files.init(); // creates tables and indexes if missing; await it before any other call
36
60
  ```
37
61
 
38
- Any other SQLite binding works by implementing the port:
62
+ ### `SqliteFilesApi` options
39
63
 
40
- ```typescript
41
- interface SqlStatement {
42
- sql: string;
43
- params?: unknown[];
44
- }
64
+ | Option | Default | |
65
+ | --- | --- | --- |
66
+ | `compression` | `webDeflateCodec()` where `CompressionStream` exists, else none | `null` stores uncompressed; `pakoDeflateCodec(pako)` for runtimes without Compression Streams |
67
+ | `tablePrefix` | `"fs_"` | several file systems in one database |
68
+ | `blockSize` | 1 MiB | uncompressed bytes per block for new writes, 1 byte to 1.5 MiB |
45
69
 
46
- interface SqlDriver {
47
- /** Rows as objects keyed by column name. */
48
- all<T>(sql: string, ...params: unknown[]): T[] | Promise<T[]>;
49
- /** One statement, for its effect. */
50
- run(sql: string, ...params: unknown[]): void | Promise<void>;
51
- /**
52
- * The statements in order, as one transaction: all apply or none do. Returns the number of rows
53
- * each statement changed. No statement may depend on another's result.
54
- */
55
- transaction(statements: SqlStatement[]): number[] | Promise<number[]>;
56
- }
57
- ```
70
+ ### `SqlarFilesApi` codecs (`codec` option)
58
71
 
59
- The built-in drivers implement `transaction` with `BEGIN IMMEDIATE … COMMIT` (node:sqlite),
60
- `ctx.storage.transactionSync` (Durable Objects, where `sql.exec` rejects `BEGIN`) and `batch()` (D1).
61
- Blobs come back as `Uint8Array`, `ArrayBuffer` or `number[]` depending on the runtime; both
62
- implementations accept all three.
72
+ | Codec | Uses | When |
73
+ | --- | --- | --- |
74
+ | `defaultCodec()` | `webCodec()` or `rawCodec()` | the default: `webCodec()` where `CompressionStream` exists, `rawCodec()` elsewhere |
75
+ | `webCodec({ minSize? })` | `CompressionStream("deflate")` | browsers, Workers, Node, Deno, Bun |
76
+ | `pakoCodec(pako, { minSize?, level? })` | an injected `pako` module | runtimes without Compression Streams |
77
+ | `rawCodec()` | nothing | never compresses, and refuses to read compressed rows |
63
78
 
64
- ## SqliteFilesApi
79
+ ## Examples
80
+
81
+ ### Stream a large file through `SqliteFilesApi`
65
82
 
66
83
  ```typescript
67
- import { DatabaseSync } from "node:sqlite";
68
- import { NodeSqlDriver, SqliteFilesApi } from "@statewalker/webrun-files-sqlite";
84
+ import { newNodeSqliteDb } from '@statewalker/db-sqlite-node';
85
+ import { SqliteFilesApi } from '@statewalker/webrun-files-sqlite';
69
86
 
70
- const files = new SqliteFilesApi(new NodeSqlDriver(new DatabaseSync("files.db")));
71
- await files.init(); // creates fs_paths, fs_files and fs_blocks if missing
87
+ const files = new SqliteFilesApi(await newNodeSqliteDb({ path: 'files.db' }));
88
+ await files.init();
72
89
 
73
- await files.write("/video.mp4", response.body); // any Iterable or AsyncIterable of Uint8Array
74
- for await (const chunk of files.read("/video.mp4", { start: 10_000_000, length: 65_536 })) {
90
+ await files.write('/video.mp4', chunks); // any Iterable or AsyncIterable of Uint8Array
91
+ for await (const chunk of files.read('/video.mp4', { start: 10_000_000, length: 65_536 })) {
75
92
  // only the block holding this range is fetched and inflated
76
93
  }
77
94
  ```
78
95
 
79
- `init()` creates the tables and indexes if they are missing; await it before any other call.
80
-
81
- ### How content is stored
82
-
83
- - `fs_paths(pid, path UNIQUE, fid, mtime)` — a path points at a content, or is a directory (`fid` NULL).
84
- - `fs_files(fid, size, compression, block_size, hash, updated)` — an immutable content: its
85
- uncompressed size, `"none"` or `"deflate"`, the block size it was written with, the SHA-256 of its
86
- bytes, and when it last changed (`size` and `hash` are NULL while it is being written).
87
- - `fs_blocks(fid, shift, block)`, keyed by `(fid, shift)` — the content cut into blocks of
88
- `block_size` uncompressed bytes (only the last one shorter); `shift` is the block's offset in the
89
- uncompressed content, always a multiple of `block_size`. Each block of a `deflate` content is its
90
- own zlib stream, so a range read fetches and inflates exactly the blocks the range touches, found
91
- by key.
96
+ ### Inside a Durable Object
92
97
 
93
- `copy` adds path rows pointing at the same content; a write whose bytes match an existing content
94
- (same SHA-256, size, compression and block size) shares it too. A content and its blocks are deleted when the
95
- last path pointing at it is removed or overwritten.
96
-
97
- ### Streaming
98
-
99
- `write` pulls the source only while it builds the current block and stores each block before
100
- pulling for the next; `read` fetches a block only when its consumer asks for bytes beyond the
101
- previous one. Memory is bounded by one block plus one source chunk, and the tests measure both
102
- bounds. An `AbortSignal` passed to `read` is checked before every block.
103
-
104
- `list` is streamed too: it reads `fs_paths` in pages of 256 by key, in the index's order — which is
105
- the path order every `FilesApi` lists in — and `{ after }` is simply where the first page starts.
98
+ ```typescript
99
+ import { newDoSqliteDb } from '@statewalker/db-sqlite-do';
100
+ import { SqliteFilesApi } from '@statewalker/webrun-files-sqlite';
106
101
 
107
- ### Options
102
+ const files = new SqliteFilesApi(newDoSqliteDb(this.ctx.storage));
103
+ await files.init();
104
+ ```
108
105
 
109
- | Option | Default | |
110
- | --- | --- | --- |
111
- | `compression` | `webDeflateCodec()` where `CompressionStream` exists, else none | `null` stores uncompressed; `pakoDeflateCodec(pako)` for runtimes without Compression Streams |
112
- | `tablePrefix` | `"fs_"` | several file systems in one database |
113
- | `blockSize` | 1 MiB | uncompressed bytes per block for new writes, 1 to 1.5 MiB (the cap keeps a deflated block under the 2 MB row limit). Larger means fewer rows — better where statements are costly, as on D1; smaller means cheaper small range reads. Each content records its own size, so changing this never affects existing files. |
106
+ ### Clean up interrupted writes
114
107
 
115
- ### Semantics worth knowing
108
+ ```typescript
109
+ const removed = await files.sweep({ olderThan: 60 * 60_000 }); // contents untouched for an hour
110
+ ```
116
111
 
117
- - `copy` and `move` replace the target's subtree rather than merging into it, and throw when one
118
- path contains the other.
119
- - A write is invisible until complete: if it fails for any reason, the path keeps its previous
120
- content and the partly written content is deleted.
121
- - A read whose content is removed underneath it throws `changed during read`. A block whose
122
- uncompressed length is not what its position implies throws `block at <shift> holds … bytes`,
123
- before any wrong byte reaches the reader.
112
+ ### A portable archive with `SqlarFilesApi`
124
113
 
125
- ### Interruptions and `sweep`
114
+ ```typescript
115
+ import { newNodeSqliteDb } from '@statewalker/db-sqlite-node';
116
+ import * as pako from 'pako';
117
+ import { readText, writeText } from '@statewalker/webrun-files';
118
+ import { pakoCodec, SqlarFilesApi } from '@statewalker/webrun-files-sqlite';
126
119
 
127
- Streaming happens outside transactions; every step that changes what a path shows is one
128
- transaction (`BEGIN IMMEDIATE` on node:sqlite, `transactionSync` on Durable Objects, `batch()` on D1):
129
- storing each block, finishing a write, `remove`, `copy` and `move`. A failure inside any of them
130
- leaves the database exactly as it was — `move` onto an existing target never loses the target.
120
+ const files = new SqlarFilesApi(await newNodeSqliteDb({ path: 'site.sqlar' }), {
121
+ codec: pakoCodec(pako, { level: 9 }),
122
+ });
123
+ await files.init(); // creates the sqlar table if missing
131
124
 
132
- A write stays invisible until it finishes. If the process dies mid-stream, the partly written
133
- content is left behind unreferenced; remove such leftovers with
125
+ await writeText(files, '/docs/index.md', '# Hello');
126
+ console.log(await readText(files, '/docs/index.md'));
134
127
 
135
- ```typescript
136
- const removed = await files.sweep({ olderThan: 60 * 60_000 }); // contents untouched for an hour
128
+ await files.symlink('/latest.md', './docs/index.md');
129
+ await files.readlink('/latest.md'); // "./docs/index.md"
137
130
  ```
138
131
 
139
- `sweep` deletes contents that no path references and that have not been updated for `olderThan`
140
- milliseconds, with their blocks. A write refreshes its content with every block, so only a write
141
- stalled longer than `olderThan` can be swept — and it then fails rather than creating a broken path.
142
- Choose a threshold above the longest pause a source may take. `sweep` never runs on its own.
132
+ ## Internals
143
133
 
144
- ## SqlarFilesApi
134
+ ### Why every step runs through `inTransaction`, and what breaks it
145
135
 
146
- ```typescript
147
- import { DatabaseSync } from "node:sqlite";
148
- import { readText, writeText } from "@statewalker/webrun-files";
149
- import { NodeSqlDriver, SqlarFilesApi } from "@statewalker/webrun-files-sqlite";
136
+ Each step that changes what a path shows runs as one SQLite transaction through `inTransaction`
137
+ from `@statewalker/db-sqlite`. It runs as `BEGIN IMMEDIATE … COMMIT` behind a mutex on Node and in
138
+ the browser, and through `ctx.storage.transaction()` in a Durable Object, where `sql.exec` rejects
139
+ `BEGIN`. The rows a statement changed come from `SELECT changes()` inside that transaction. Steps
140
+ never depend on reading another statement's result: conditions live in SQL (`WHERE EXISTS …`).
150
141
 
151
- const files = new SqlarFilesApi(new NodeSqlDriver(new DatabaseSync("site.sqlar")));
152
- await files.init(); // creates the sqlar table if missing — await before anything else
142
+ `inTransaction` finds the driver's transaction support through a hook on the `Db` it was given.
143
+ **A `Db` wrapped in your own proxy (logging, metrics) loses the hook.** The first `write`,
144
+ `remove`, `copy`, `move` or `sweep` then rejects with: `inTransaction: this Db has no SQLite
145
+ transaction hook. It is not a @statewalker/db-sqlite* Db, or it wraps one (a wrapper object loses
146
+ the hook - pass the driver's Db itself), or it is the handle of a running transaction (nesting is
147
+ not supported).` Pass the driver's `Db` itself, or forward `db[SQLITE_TRANSACTION]` (exported by
148
+ `@statewalker/db-sqlite`) in your wrapper.
153
149
 
154
- await writeText(files, "/docs/index.md", "# Hello");
155
- console.log(await readText(files, "/docs/index.md"));
150
+ ### How `SqliteFilesApi` stores content
151
+
152
+ ```
153
+ fs_paths(pid, path UNIQUE, fid, mtime) a path -> a content, or a directory (fid NULL)
154
+ |
155
+ v
156
+ fs_files(fid, size, compression, block_size, an immutable content: size, "none"/"deflate",
157
+ hash, updated) block size, SHA-256, last change
158
+ |
159
+ v
160
+ fs_blocks(fid, shift, block) the content in blocks of block_size bytes;
161
+ shift = offset in the uncompressed content
156
162
  ```
157
163
 
158
- It takes the same drivers (see *Drivers*), subject to the row limit below.
164
+ Each block of a `deflate` content is its own zlib stream, so a range read fetches and inflates
165
+ exactly the blocks it touches, found by key. `copy` adds path rows pointing at the same content; a
166
+ write whose bytes match an existing content (same SHA-256, size, compression and block size) shares
167
+ it too. A content and its blocks are deleted when the last path pointing at it goes.
159
168
 
160
- ### Compression codecs
169
+ ### Why the block size is capped at 1.5 MiB
161
170
 
162
- | Codec | Uses | When |
163
- | --- | --- | --- |
164
- | `webCodec()` | `CompressionStream("deflate")` | Default wherever `CompressionStream` exists |
165
- | `pakoCodec(pako, { level })` | an injected `pako` module | Runtimes without Compression Streams |
166
- | `rawCodec()` | nothing | Default elsewhere; never compresses, and refuses to read compressed rows |
171
+ A deflated block can be slightly larger than its input. 1.5 MiB keeps even incompressible input
172
+ under the 2 MB row limit of Durable Objects. Larger blocks mean fewer rows; smaller blocks make
173
+ small range reads cheaper. Each content records its own block size, so changing `blockSize` never
174
+ affects existing files.
167
175
 
168
- ```typescript
169
- import * as pako from "pako";
170
- const files = new SqlarFilesApi(driver, { codec: pakoCodec(pako) });
171
- ```
176
+ ### Streaming and memory
172
177
 
173
- All codecs read and write the same zlib format, so an archive written with one is read by another.
174
- Inputs shorter than `minSize` (64 bytes by default) are stored without attempting compression.
178
+ `write` pulls the source only while it builds the current block and stores each block before
179
+ pulling the next; `read` fetches a block only when its consumer asks for bytes beyond the previous
180
+ one. Memory is bounded by one block plus one source chunk; the tests measure both bounds. An
181
+ `AbortSignal` passed to `read` is checked before every block. `list` reads `fs_paths` in pages of
182
+ 256 by key, in index order, which is the `FilesApi` path order, and `{ after }` is where the first
183
+ page starts.
184
+
185
+ ### Atomic steps, and why `sweep` exists
186
+
187
+ Streaming happens outside transactions. Every step that changes what a path shows is one
188
+ transaction: storing each block, finishing a write, `remove`, `copy` and `move`. A failure inside
189
+ any of them leaves the database as it was; `move` onto an existing target never loses the target.
190
+ A write stays invisible until it finishes: if it fails, the path keeps its previous content and the
191
+ partial content is deleted.
192
+
193
+ If the process dies mid-stream, the partial content is left behind, unreferenced. `sweep` deletes
194
+ contents that no path references and that have not been updated for `olderThan` milliseconds, with
195
+ their blocks. A write refreshes its content with every block, so only a write stalled longer than
196
+ `olderThan` can be swept, and it then fails rather than creating a broken path. Choose a threshold
197
+ above the longest pause a source may take. `sweep` never runs on its own.
198
+
199
+ ### What `SqliteFilesApi` throws
200
+
201
+ - The constructor throws `SqliteFilesApi: blockSize must be an integer from 1 to 1572864, got ...`
202
+ and `SqliteFilesApi: tablePrefix must be a plain SQL identifier, got ...`.
203
+ - `copy` and `move` replace the target's subtree rather than merging into it, and throw when one
204
+ path contains the other.
205
+ - A read whose content is removed underneath it throws `changed during read`.
206
+ - A block whose uncompressed length does not match its position throws `block at <shift> holds ...
207
+ bytes`, before any wrong byte reaches the reader.
175
208
 
176
- ### How entries are stored
209
+ ### How `SqlarFilesApi` stores entries
177
210
 
178
211
  | Entry | `name` | `sz` | `data` |
179
212
  | --- | --- | --- | --- |
@@ -182,30 +215,39 @@ Inputs shorter than `minSize` (64 bytes by default) are stored without attemptin
182
215
  | Directory | | `0` | `NULL` |
183
216
  | Symlink | | `-1` | target as text |
184
217
 
185
- `mtime` is stored in seconds; `lastModified` reports it in milliseconds. Archives written by other
186
- tools without directory rows still behave as trees: a path that is only a prefix of other names is a
187
- directory.
218
+ `mtime` is stored in seconds; `lastModified` reports milliseconds. Inputs shorter than `minSize`
219
+ (64 bytes by default) are stored without trying to compress. All codecs read and write the same
220
+ zlib format. Archives written by other tools without directory rows still behave as trees: a path
221
+ that is only a prefix of other names is a directory.
188
222
 
189
- #### Symlinks
223
+ `FileKind` has no symlink, so `stats`, `exists`, `read` and `list` follow links as POSIX `stat()`
224
+ does. A dangling link, or a chain longer than 8, behaves as a missing path. `remove`, `copy` and
225
+ `move` act on the link itself. Only the final path component is resolved through symlinks.
190
226
 
191
- `FileKind` has no symlink, so `stats`, `exists`, `read` and `list` follow links the way POSIX
192
- `stat()` does. A dangling link, or a chain of more than 8, behaves as a missing path. `remove`,
193
- `copy` and `move` act on the link itself. Two extension methods give access to links:
227
+ ### `SqlarFilesApi` limits
194
228
 
195
- ```typescript
196
- await files.symlink("/latest.js", "./v2/index.js");
197
- await files.readlink("/latest.js"); // "./v2/index.js"
198
- ```
229
+ - A file is one blob: `write` buffers it, `read` inflates it whole and yields one chunk.
230
+ - On Durable Objects the stored (possibly compressed) size of one file must stay under 2 MB, since
231
+ SQLAR keeps each file in one row. A larger file fails the `write` with `string or blob too big`
232
+ — a value limit, not corruption. Store big files with `SqliteFilesApi`, whose blocks stay under
233
+ the limit.
234
+ - Multi-row mutations (`copy`, `move`, `remove` of a tree) are not wrapped in a transaction: a
235
+ failure part-way leaves the rows already changed in place, and a `move` can leave the entry under
236
+ both paths.
237
+ - `list` loads every row under the directory, then sorts the entries into path order before
238
+ applying `after`.
239
+
240
+ ### Dependencies
241
+
242
+ - `@statewalker/db-api` — the `Db` contract both implementations run on.
243
+ - `@statewalker/db-sqlite` — `inTransaction`, for `SqliteFilesApi`'s atomic steps.
244
+ - `@statewalker/webrun-files` — the `FilesApi` contract and path helpers.
199
245
 
200
- ### Limitations
246
+ The browser driver (`@statewalker/db-sqlite-browser`) is supported through the same `Db` contract, but
247
+ this package's tests run on `node:sqlite` and the Durable Object fake only.
201
248
 
202
- - A file is one blob: `write` buffers its content, `read` inflates it whole and yields one chunk.
203
- - On Durable Objects and D1 a row may not exceed 2 MB, so the *stored* (possibly compressed) size
204
- of one file is capped there. Use `SqliteFilesApi` in those runtimes.
205
- - Multi-row mutations (`copy`, `move`, `remove` of a tree) are not wrapped in a transaction.
206
- - Only the final path component is resolved through symlinks.
207
- - `list` loads every row under the directory, then sorts the entries into path order (implicit
208
- directories and resolved links do not sit where the rows do) before applying `after`.
249
+ The drivers (`@statewalker/db-sqlite-node`, `@statewalker/db-sqlite-do`, …) are the caller's
250
+ choice and are not dependencies; neither is `pako`, which is passed in.
209
251
 
210
252
  ## License
211
253
 
@@ -1,5 +1,5 @@
1
+ import type { Db } from "@statewalker/db-api";
1
2
  import type { FileInfo, FileStats, FilesApi, ListOptions, ReadOptions } from "@statewalker/webrun-files";
2
- import type { SqlDriver } from "../sql.types.js";
3
3
  import { type StreamCodec } from "./stream-codec.js";
4
4
  export interface SqliteFilesApiOptions {
5
5
  /** Prefix of the three table names. Defaults to "fs_". */
@@ -20,7 +20,7 @@ export interface SqliteFilesApiOptions {
20
20
  * A FilesApi over three SQLite tables: paths point at immutable contents, and
21
21
  * a content is a sequence of bounded, optionally compressed blocks. Reads and
22
22
  * writes stream one block at a time, so no file is ever held whole and no row
23
- * outgrows the 2 MB limit of Durable Objects and D1.
23
+ * outgrows the 2 MB row limit of Durable Objects.
24
24
  *
25
25
  * Streaming happens outside transactions; every step that changes what a path
26
26
  * shows is one transaction, so an interruption leaves at most an unreferenced
@@ -28,7 +28,7 @@ export interface SqliteFilesApiOptions {
28
28
  */
29
29
  export declare class SqliteFilesApi implements FilesApi {
30
30
  #private;
31
- constructor(sql: SqlDriver, opts?: SqliteFilesApiOptions);
31
+ constructor(db: Db, opts?: SqliteFilesApiOptions);
32
32
  /** Creates the tables and indexes if missing. Await it before any other call. */
33
33
  init(): Promise<void>;
34
34
  read(path: string, options?: ReadOptions): AsyncIterable<Uint8Array>;
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-files-api.d.ts","sourceRoot":"","sources":["../../src/blocks/sqlite-files-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,WAAW,EACZ,MAAM,2BAA2B,CAAC;AAGnC,OAAO,KAAK,EAAE,SAAS,EAAgB,MAAM,iBAAiB,CAAC;AAG/D,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAOtE,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;;;GASG;AACH,qBAAa,cAAe,YAAW,QAAQ;;IAS7C,YAAY,GAAG,EAAE,SAAS,EAAE,IAAI,GAAE,qBAA0B,EAqB3D;IAED,iFAAiF;IAC3E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA0B1B;IAIM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,aAAa,CAAC,UAAU,CAAC,CA8C9E;IAID;;;;OAIG;IACG,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,GACxD,OAAO,CAAC,IAAI,CAAC,CAmEf;IA8CK,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASvC;IAIK,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAKxD;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE3C;IAEM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,aAAa,CAAC,QAAQ,CAAC,CA4B5E;IAIK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG3C;IAgCK,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE3D;IAEK,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE3D;IA6CD;;;;;;;;OAQG;IACG,KAAK,CAAC,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqB3D;CAiEF"}
1
+ {"version":3,"file":"sqlite-files-api.d.ts","sourceRoot":"","sources":["../../src/blocks/sqlite-files-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAC;AAE9C,OAAO,KAAK,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,WAAW,EACZ,MAAM,2BAA2B,CAAC;AAMnC,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAOtE,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;;;GASG;AACH,qBAAa,cAAe,YAAW,QAAQ;;IAS7C,YAAY,EAAE,EAAE,EAAE,EAAE,IAAI,GAAE,qBAA0B,EAqBnD;IAED,iFAAiF;IAC3E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA0B1B;IAIM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,aAAa,CAAC,UAAU,CAAC,CA8C9E;IAID;;;;OAIG;IACG,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,GACxD,OAAO,CAAC,IAAI,CAAC,CAuEf;IA8CK,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQvC;IAIK,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAKxD;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE3C;IAEM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,aAAa,CAAC,QAAQ,CAAC,CA4B5E;IAIK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG3C;IAgCK,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE3D;IAEK,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE3D;IA6CD;;;;;;;;OAQG;IACG,KAAK,CAAC,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqB3D;CAqEF"}
package/dist/bytes.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- /** Blobs arrive as Uint8Array (node:sqlite), ArrayBuffer (Durable Objects) or number[] (D1). */
1
+ /** BLOBs arrive as Uint8Array from every @statewalker/db-sqlite driver; SQLAR may hold TEXT. */
2
2
  export declare function toBytes(value: unknown): Uint8Array;
3
3
  //# sourceMappingURL=bytes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../src/bytes.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAMlD"}
1
+ {"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../src/bytes.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAIlD"}