@delali/sirannon-db 0.3.4-next.61 → 0.3.4-next.64

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 (108) hide show
  1. package/README.md +79 -56
  2. package/dist/backup/index.d.ts +44 -45
  3. package/dist/backup/index.mjs +7 -7
  4. package/dist/backup-scheduler/index.d.ts +42 -41
  5. package/dist/backup-scheduler/index.mjs +4 -4
  6. package/dist/baseline-Co3iIsN_.d.ts +19 -0
  7. package/dist/change-tracker-DqCdeEyx.d.ts +128 -0
  8. package/dist/{chunk-VOSJBZ6Q.mjs → chunk-2ES3DT4A.mjs} +71 -33
  9. package/dist/{chunk-6CMCO7S4.mjs → chunk-2O3PP4CF.mjs} +1 -1
  10. package/dist/{chunk-E7Q7HE46.mjs → chunk-3BZLZ47R.mjs} +26 -26
  11. package/dist/chunk-4KASJKKK.mjs +2501 -0
  12. package/dist/{chunk-MYNI2WFS.mjs → chunk-52XW3Z76.mjs} +40 -8
  13. package/dist/chunk-55YRLSM2.mjs +57 -0
  14. package/dist/{chunk-KDAHYFW7.mjs → chunk-6AOJCNRU.mjs} +5 -5
  15. package/dist/{chunk-KXTYR2OQ.mjs → chunk-A3PHCO6W.mjs} +39 -111
  16. package/dist/chunk-ADNWEHGZ.mjs +299 -0
  17. package/dist/{chunk-2QLXDHAP.mjs → chunk-BLMRNSUG.mjs} +11 -2
  18. package/dist/chunk-BVMZJOVM.mjs +5 -0
  19. package/dist/{chunk-IWGIYDMZ.mjs → chunk-BZWDPF4G.mjs} +1 -1
  20. package/dist/{chunk-YVF2ZJTE.mjs → chunk-CZL4D3BM.mjs} +1 -1
  21. package/dist/{chunk-7SC2TZAS.mjs → chunk-DX4WGJL7.mjs} +2 -2
  22. package/dist/{chunk-45MKELYA.mjs → chunk-ESTZZPT5.mjs} +132 -13
  23. package/dist/{chunk-QP5L5DFJ.mjs → chunk-FJPR2CJX.mjs} +3 -3
  24. package/dist/chunk-HR7K3GZW.mjs +36 -0
  25. package/dist/{chunk-LXCGPZPA.mjs → chunk-IEKIYO4V.mjs} +1 -1
  26. package/dist/{chunk-TUD5CJ76.mjs → chunk-KB4OLJAD.mjs} +26 -19
  27. package/dist/{chunk-PBRXXISQ.mjs → chunk-KINDFBQP.mjs} +3 -2
  28. package/dist/{chunk-E6IMX7RS.mjs → chunk-KJLNYOOL.mjs} +2 -2
  29. package/dist/{chunk-BTTFW4Z4.mjs → chunk-KU32OT4H.mjs} +2 -10
  30. package/dist/{chunk-JQWGHSEH.mjs → chunk-NVSQ2T5Q.mjs} +71 -70
  31. package/dist/{chunk-W4EUTJNT.mjs → chunk-NZGSDALX.mjs} +94 -96
  32. package/dist/{chunk-U445A3C4.mjs → chunk-QGVRRKB2.mjs} +1 -1
  33. package/dist/{chunk-GYCEJNU4.mjs → chunk-R7GBXNTO.mjs} +1 -1
  34. package/dist/{chunk-7C36BCSN.mjs → chunk-VDN6FHU4.mjs} +1 -1
  35. package/dist/{chunk-6IYS4WLH.mjs → chunk-VO3ZWA2G.mjs} +142 -115
  36. package/dist/chunk-WOMADPM4.mjs +12 -0
  37. package/dist/{chunk-OUSWVNWT.mjs → chunk-YCMB6GR4.mjs} +1 -1
  38. package/dist/{chunk-YNTDOFTN.mjs → chunk-YW7IKQ7T.mjs} +1 -1
  39. package/dist/{chunk-M2BEAH5I.mjs → chunk-ZN4HI5FK.mjs} +126 -72
  40. package/dist/{chunk-SBL6GN43.mjs → chunk-ZYWI45EN.mjs} +1 -1
  41. package/dist/client/index.d.ts +135 -137
  42. package/dist/client/index.mjs +177 -82
  43. package/dist/client/topology.d.ts +31 -31
  44. package/dist/client/topology.mjs +42 -23
  45. package/dist/client-base-BxTocU1x.d.ts +398 -0
  46. package/dist/client-streams-YKANXMWO.mjs +112 -0
  47. package/dist/codegen/cli.mjs +4 -4
  48. package/dist/codegen/index.d.ts +36 -29
  49. package/dist/codegen/index.mjs +4 -4
  50. package/dist/core/index.d.ts +130 -115
  51. package/dist/core/index.mjs +387 -297
  52. package/dist/core/writer-worker.mjs +3 -3
  53. package/dist/database-CGDsXW1K.d.ts +959 -0
  54. package/dist/database-backup-DcLhxoa1.d.ts +143 -0
  55. package/dist/driver/better-sqlite3.d.ts +11 -10
  56. package/dist/driver/better-sqlite3.mjs +11 -11
  57. package/dist/driver/bun.d.ts +9 -8
  58. package/dist/driver/expo.d.ts +6 -7
  59. package/dist/driver/node.d.ts +11 -10
  60. package/dist/driver/node.mjs +13 -11
  61. package/dist/driver/wa-sqlite.d.ts +6 -6
  62. package/dist/errors-CJUkQ-KL.d.ts +199 -0
  63. package/dist/file-migrations/index.d.ts +20 -18
  64. package/dist/file-migrations/index.mjs +3 -3
  65. package/dist/forward-rpc-VRWFZXHN.mjs +44 -0
  66. package/dist/operation-registry-CGhNT-Sn.d.ts +96 -0
  67. package/dist/primary-wins-CHNOYuN7.d.ts +81 -0
  68. package/dist/protocol-DFpNNsIT.d.ts +153 -0
  69. package/dist/query-types-5B02ixH0.d.ts +129 -0
  70. package/dist/react/index.d.ts +22 -22
  71. package/dist/replication/coordinator/etcd.d.ts +37 -31
  72. package/dist/replication/coordinator/etcd.mjs +296 -131
  73. package/dist/replication/index.d.ts +201 -161
  74. package/dist/replication/index.mjs +630 -529
  75. package/dist/restore-options-CH8mE-Tb.d.ts +84 -0
  76. package/dist/server/index.d.ts +201 -191
  77. package/dist/server/index.mjs +736 -619
  78. package/dist/server-options-aYtCkefX.d.ts +317 -0
  79. package/dist/server-streams-24XL4T43.mjs +254 -0
  80. package/dist/sirannon-J3EgPVD7.d.ts +179 -0
  81. package/dist/transport/grpc.d.ts +37 -24
  82. package/dist/transport/grpc.mjs +83 -3199
  83. package/dist/transport/memory.d.ts +25 -26
  84. package/dist/transport/memory.mjs +18 -9
  85. package/dist/types-BrAiDGOb.d.ts +505 -0
  86. package/dist/types-CEJG0eFS.d.ts +88 -0
  87. package/dist/types-Dc8oWlxj.d.ts +1580 -0
  88. package/dist/types-DqODKeOq.d.ts +123 -0
  89. package/dist/types-guEKSzMr.d.ts +367 -0
  90. package/package.json +7 -7
  91. package/dist/baseline-D93hcIEE.d.ts +0 -17
  92. package/dist/change-tracker-CjRFuZdB.d.ts +0 -144
  93. package/dist/client-base-Cmd9EVme.d.ts +0 -397
  94. package/dist/database-BLQvqjsw.d.ts +0 -917
  95. package/dist/database-backup-B7oszQ2n.d.ts +0 -141
  96. package/dist/errors-Dei4GdBb.d.ts +0 -196
  97. package/dist/operation-registry-oiPuSYI6.d.ts +0 -94
  98. package/dist/primary-wins-B0np8JS3.d.ts +0 -85
  99. package/dist/protocol-HI-nY8DD.d.ts +0 -152
  100. package/dist/query-types-p1liOP-b.d.ts +0 -127
  101. package/dist/restore-options-CksYsH1l.d.ts +0 -83
  102. package/dist/server-options-Bj5iJnjx.d.ts +0 -292
  103. package/dist/sirannon-BPxC5tC2.d.ts +0 -178
  104. package/dist/types-B0EhgDbr.d.ts +0 -340
  105. package/dist/types-CbjfRwrK.d.ts +0 -499
  106. package/dist/types-CjhxcjhA.d.ts +0 -123
  107. package/dist/types-DPSmjU54.d.ts +0 -1518
  108. package/dist/types-DTO4CYhW.d.ts +0 -88
package/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
  [![types](https://img.shields.io/badge/types-TypeScript-blue)](https://www.npmjs.com/package/@delali/sirannon-db)
7
7
  [![license](https://img.shields.io/npm/l/@delali/sirannon-db)](https://github.com/assetcorp/sirannon-db/blob/main/LICENSE)
8
8
 
9
- Build a networked SQLite service with connection pooling, change data capture, live queries, migrations, backups, device sync, and a client SDK. Applications reach Sirannon over HTTP or WebSocket, and Sirannon nodes replicate primary-owned changes over gRPC.
9
+ Build a networked SQLite service with connection pooling, change data capture, live queries, migrations, backups, device sync, and a client SDK. Applications reach Sirannon over HTTP or WebSocket, while a primary replicates its changes to other Sirannon nodes over gRPC.
10
10
 
11
- **Read the full documentation at [sirannon.sondelali.com/docs](https://sirannon.sondelali.com/docs).** This page gets you running, and the [guides](#documentation) hold the reference depth. The suite that measures Sirannon against Postgres 17 is a Python project under [`benchmarks/server`](../../benchmarks/server), and the write-up generator rewrites [BENCHMARKS.md](../../BENCHMARKS.md) from the latest committed run.
11
+ **Read the full documentation at [sirannon.sondelali.com/docs](https://sirannon.sondelali.com/docs).** Use this page to get started, and turn to the [guides](#documentation) for the detail. The benchmark harness that measures Sirannon against Postgres 17 is under [`benchmarks/server`](https://github.com/assetcorp/sirannon-db/tree/main/benchmarks/server), where a Node load generator drives both engines and a Python step joins their results. The write-up generator rewrites [BENCHMARKS.md](https://github.com/assetcorp/sirannon-db/blob/main/BENCHMARKS.md) from the latest committed run.
12
12
 
13
13
  The core engine, server, client, and primary-replica replication are stable. Coordinator-backed failover, device sync, and the Bun and Expo drivers are experimental.
14
14
 
@@ -25,13 +25,33 @@ Then add the SQLite driver for your runtime:
25
25
  | Driver | Import | Runtime | Install |
26
26
  | --- | --- | --- | --- |
27
27
  | better-sqlite3 | `@delali/sirannon-db/driver/better-sqlite3` | Node.js | `pnpm add -E better-sqlite3` |
28
- | Node built-in | `@delali/sirannon-db/driver/node` | Node.js >= 22 | None (flag-free from 22.13.0 and 23.4.0) |
28
+ | Node built-in | `@delali/sirannon-db/driver/node` | Node.js >= 22 | None (no flag needed from 22.13.0 and 23.4.0) |
29
29
  | wa-sqlite | `@delali/sirannon-db/driver/wa-sqlite` | Browser (IndexedDB) | `pnpm add -E wa-sqlite` |
30
- | Bun | `@delali/sirannon-db/driver/bun` | Bun | None (uses `bun:sqlite`) |
30
+ | Bun | `@delali/sirannon-db/driver/bun` | Bun | None (built on `bun:sqlite`) |
31
31
  | Expo | `@delali/sirannon-db/driver/expo` | React Native | `pnpm add -E expo-sqlite` |
32
32
 
33
33
  Write a custom driver by passing `capabilities` and an `open` function to `defineDriver`.
34
34
 
35
+ To serve a registry over HTTP and WebSocket through `@delali/sirannon-db/server`, add uWebSockets.js as well. The npm registry has no package named `uWebSockets.js`, so install the tagged GitHub release v20.69.0, which is the version in Sirannon's own development dependencies:
36
+
37
+ ```bash
38
+ pnpm add -E "uWebSockets.js@github:uNetworking/uWebSockets.js#v20.69.0"
39
+ ```
40
+
41
+ When the process cannot load uWebSockets.js, `server.listen()` fails with code `SERVER_DEPENDENCY_MISSING` and a message that gives this install command.
42
+
43
+ ## Add Sirannon with a coding agent
44
+
45
+ The [Sirannon skill](https://github.com/assetcorp/sirannon-db/tree/main/skills/sirannon) tells a coding agent which driver suits the app's runtime, which packages to install alongside it, and which server settings keep the data safe. Install the skill into the coding agents on your machine with the skills CLI:
46
+
47
+ ```bash
48
+ npx skills add assetcorp/sirannon-db
49
+ ```
50
+
51
+ The skill has the agent read the Sirannon version that your project installs, and it sends the agent to that version's types and documentation.
52
+
53
+ On the [agents page](https://sirannon.sondelali.com/agents), you can copy a prompt for one of six database tasks and paste it into your coding agent.
54
+
35
55
  ## Quick start
36
56
 
37
57
  ```ts
@@ -47,36 +67,36 @@ await db.execute('INSERT INTO users (name, email) VALUES (?, ?)', ['Ada', 'ada@e
47
67
  const users = await db.query<{ id: number; name: string }>('SELECT * FROM users')
48
68
  ```
49
69
 
50
- In the browser, open the database directly and use one read connection, because the `Sirannon` registry is built for server-side use:
70
+ In the browser, open the database through the same registry with the browser driver:
51
71
 
52
72
  ```ts
53
- import { Database } from '@delali/sirannon-db'
73
+ import { Sirannon } from '@delali/sirannon-db'
54
74
  import { waSqlite } from '@delali/sirannon-db/driver/wa-sqlite'
55
75
 
56
- const driver = waSqlite({ vfs: 'IDBBatchAtomicVFS' })
57
- const db = await Database.create('app', '/app.db', driver, { readPoolSize: 1, walMode: false })
76
+ const sirannon = new Sirannon({ driver: waSqlite({ vfs: 'IDBBatchAtomicVFS' }) })
77
+ const db = await sirannon.open('app', '/app.db', { walMode: false })
58
78
  ```
59
79
 
60
- React Native uses the same shape through `expoSqlite()` with `readPoolSize: 1`.
80
+ On React Native, open the database the same way with `expoSqlite()`.
61
81
 
62
82
  ## Package exports
63
83
 
64
84
  | Import | What you get |
65
85
  | --- | --- |
66
- | `@delali/sirannon-db` | Core library: queries, transactions, CDC, live queries, migrations, backups, hooks, metrics, lifecycle |
67
- | `@delali/sirannon-db/driver/*` | SQLite driver adapters (see the table above) |
68
- | `@delali/sirannon-db/file-migrations` | Load `.up.sql` and `.down.sql` files from a directory |
86
+ | `@delali/sirannon-db` | Core library: queries, transactions, CDC, live queries, migrations, backups, hooks, metrics, and lifecycle |
87
+ | `@delali/sirannon-db/driver/*` | SQLite driver adapters, listed in the table above |
88
+ | `@delali/sirannon-db/file-migrations` | A loader for `.up.sql` and `.down.sql` files in a directory |
69
89
  | `@delali/sirannon-db/backup` | Backup destination types, chain records, and `restoreBackup` |
70
- | `@delali/sirannon-db/backup-scheduler` | Cron-scheduled backup runner with file rotation |
71
- | `@delali/sirannon-db/server` | HTTP and WebSocket server powered by uWebSockets.js |
72
- | `@delali/sirannon-db/client` | Client SDK with auto-reconnect, subscription restore, and device sync |
73
- | `@delali/sirannon-db/client/topology` | Topology-aware client that routes across a replication group |
74
- | `@delali/sirannon-db/react` | `useLiveQuery` and `useCommand` hooks |
90
+ | `@delali/sirannon-db/backup-scheduler` | A cron-scheduled backup runner with file rotation |
91
+ | `@delali/sirannon-db/server` | An HTTP and WebSocket server built on uWebSockets.js |
92
+ | `@delali/sirannon-db/client` | A client SDK with automatic reconnection, subscription restore, and device sync |
93
+ | `@delali/sirannon-db/client/topology` | A topology-aware client that routes across a replication group |
94
+ | `@delali/sirannon-db/react` | The `useLiveQuery` and `useCommand` hooks |
75
95
  | `@delali/sirannon-db/codegen` | Typed operation references generated from your server's registry |
76
- | `@delali/sirannon-db/replication` | Replication engine, conflict resolvers, topologies, HLC |
77
- | `@delali/sirannon-db/replication/coordinator/etcd` | etcd-backed coordinator for primary authority and failover |
78
- | `@delali/sirannon-db/transport/grpc` | gRPC replication transport with TLS support |
79
- | `@delali/sirannon-db/transport/memory` | In-memory transport for testing |
96
+ | `@delali/sirannon-db/replication` | The replication engine, conflict resolvers, topologies, and HLC |
97
+ | `@delali/sirannon-db/replication/coordinator/etcd` | An etcd-backed coordinator for primary authority and failover |
98
+ | `@delali/sirannon-db/transport/grpc` | A gRPC replication transport with TLS support |
99
+ | `@delali/sirannon-db/transport/memory` | An in-memory transport for tests |
80
100
 
81
101
  ## Queries and transactions
82
102
 
@@ -94,11 +114,11 @@ const balance = await db.transaction(async tx => {
94
114
  })
95
115
  ```
96
116
 
97
- A large import runs faster through `bulkLoad`, which trades durability for speed inside one transaction and restores the configured level afterwards. The [core engine guide](../../docs/core.md) covers it, along with migrations, hooks, metrics, and the multi-tenant lifecycle. Backups have a [guide of their own](../../docs/backups.md).
117
+ `bulkLoad` writes a large import in one transaction under relaxed durability, and then it restores the durability level that you configured. Read the [bulk load guide](https://sirannon.sondelali.com/docs/bulk-load) for the details, and the guides on [migrations](https://sirannon.sondelali.com/docs/migrations) and on [hooks, metrics, and the multi-tenant lifecycle](https://sirannon.sondelali.com/docs/hooks-metrics-and-lifecycle) for the rest of the core engine. Backups have a [guide of their own](https://sirannon.sondelali.com/docs/backups).
98
118
 
99
119
  ## Change data capture and live queries
100
120
 
101
- A subscription reports the rows that changed:
121
+ Subscribe to a table to receive each row that changes:
102
122
 
103
123
  ```ts
104
124
  await db.watch('orders')
@@ -109,9 +129,9 @@ const subscription = db
109
129
  .subscribe(event => console.log(event.type, event.table, event.row, event.oldRow, event.seq))
110
130
  ```
111
131
 
112
- A filter reports membership of the set it describes. An order whose `status` changes from `pending` to `shipped` arrives as an insert, because the row enters the filter, and one that changes away from `shipped` arrives as a delete carrying the old row. Read `event.type` as the row's arrival or departure, since a real insert and a row entering the filter look the same.
132
+ With a filter, you receive each change in a row's membership of the set that the filter matches. When an order's `status` changes from `pending` to `shipped`, the handler receives an insert, because the row joins the set. When an order's `status` changes away from `shipped`, the handler receives a delete with the previous row in `oldRow`. Read `event.type` as the row joining or leaving the set, because the handler receives the same event for a real insert and for a row that joins the set.
113
133
 
114
- A live query reports the current answer, updating the rows it holds from those same events:
134
+ A live query holds the current result of a query and updates its rows from those same events:
115
135
 
116
136
  ```ts
117
137
  const pending = await db.live<{ id: number; total: number }>(
@@ -122,11 +142,11 @@ const pending = await db.live<{ id: number; total: number }>(
122
142
  pending.subscribe(() => render(pending.getState()))
123
143
  ```
124
144
 
125
- The [live queries guide](../../docs/live-queries.md) covers the update kinds, the statements a live query maintains, and the React hooks.
145
+ Read the [live queries guide](https://sirannon.sondelali.com/docs/live-queries) for the kinds of update, the statements that a live query can maintain, and the React hooks.
126
146
 
127
147
  ## Serve it over the network
128
148
 
129
- A server accepts no SQL from the network by default. Register the reads and writes it runs, and callers invoke them by name:
149
+ A server rejects SQL from the network by default. Callers call the reads and writes that you register on it by name:
130
150
 
131
151
  ```ts
132
152
  import { createServer } from '@delali/sirannon-db/server'
@@ -164,16 +184,16 @@ const client = new SirannonClient('http://localhost:9876', { transport: 'websock
164
184
  const db = client.database('app')
165
185
 
166
186
  const users = await db.query(activeUsers, {})
167
- const sub = await db.on('users').subscribe(event => console.log('User changed:', event))
187
+ const liveUsers = await db.live(activeUsers, {})
168
188
  ```
169
189
 
170
- Run `sirannon-codegen` to generate those references from the registry instead of writing them by hand, and set `acceptSql: true` when you want the server to run statements a client sends. The [registered operations guide](../../docs/operations.md) covers both, the [server guide](../../docs/server.md) lists the routes and messages, and the [client guide](../../docs/client.md) covers the transports.
190
+ The `sirannon-codegen` command generates those references from the registry, so you can skip writing them by hand. Set `acceptSql: true` when you want the server to execute the statements that a client sends. A client streams a table's changes through `db.on(table).subscribe` only from a server with `acceptSql: true` or an `onBeforeSubscribe` hook, which admits or refuses each table. Read the [registered operations guide](https://sirannon.sondelali.com/docs/registered-operations) and the [code generation guide](https://sirannon.sondelali.com/docs/code-generation) for operations and their references, the [server guide](https://sirannon.sondelali.com/docs/server) for `acceptSql`, the routes, and the messages, and the [client guide](https://sirannon.sondelali.com/docs/client-sdk) for the transports.
171
191
 
172
192
  ## Security
173
193
 
174
- Registered operations keep SQL on the server, so a caller reaches only the reads and writes you defined. Turning on `acceptSql` gives every client the run of the database, so put such a server behind an application layer, a private network boundary, or a `resolveExecutionTarget` that allows only known statements.
194
+ With registered operations, your SQL stays on the server, so a caller of a registered read or write supplies only the arguments that you declared for it. With `acceptSql: true`, the server executes the statements that an admitted caller sends, so put that server behind an application layer or a private network boundary, or give it a `resolveExecutionTarget` that accepts only the statements that you know.
175
195
 
176
- Authenticate every request through the `authenticate` hook. Return the caller's identity, which registered operations read through `fromIdentity`, and throw to refuse:
196
+ Authenticate every request through the `authenticate` hook. Return the caller's identity from the hook so that the server can fill each `fromIdentity` argument of a registered operation, and throw to reject the request:
177
197
 
178
198
  ```ts
179
199
  import { RequestDeniedError } from '@delali/sirannon-db'
@@ -192,7 +212,7 @@ const server = createServer<Identity>(sirannon, {
192
212
  })
193
213
  ```
194
214
 
195
- A Node client attaches `headers` to the WebSocket upgrade as well as to HTTP requests, so the hook reads `headers.authorization` on both transports:
215
+ A Node client attaches `headers` to the WebSocket upgrade as well as to HTTP requests, so your hook can read `headers.authorization` on both transports:
196
216
 
197
217
  ```ts
198
218
  const client = new SirannonClient('https://api.example.com', {
@@ -200,7 +220,7 @@ const client = new SirannonClient('https://api.example.com', {
200
220
  })
201
221
  ```
202
222
 
203
- A browser attaches no header to `new WebSocket(...)`, so a browser client carries a short-lived ticket in `webSocketProtocols` instead. A browser client built with `headers` alone on the WebSocket transport fails at construction with `INVALID_ARGUMENT`, because that credential would never reach the server:
223
+ A browser cannot attach a header to `new WebSocket(...)`, so give a browser client a short-lived ticket in `webSocketProtocols`:
204
224
 
205
225
  ```ts
206
226
  const client = new SirannonClient('https://api.example.com', {
@@ -208,45 +228,48 @@ const client = new SirannonClient('https://api.example.com', {
208
228
  })
209
229
  ```
210
230
 
211
- Pass both options when a browser client needs each of them, as the [entitlements example](examples/distributed-entitlements) does: the topology client sends `headers` on its coordinator discovery request to `GET /db/{id}/cluster` and the ticket on the socket handshake.
231
+ When you construct a browser client on the WebSocket transport with `headers` alone, the constructor throws `INVALID_ARGUMENT`, because the browser would leave that credential out of the handshake. Pass both options when a browser client needs each of them, as the [entitlements example](https://github.com/assetcorp/sirannon-db/tree/main/packages/ts/examples/distributed-entitlements) does. Its topology client sends `headers` with the discovery request to `GET /db/{id}/cluster`, while it sends the ticket with the socket handshake.
212
232
 
213
- The client offers the plain `sirannon.v1` identifier ahead of your values and the server selects that identifier, so the ticket never comes back in the handshake response. Check the `Origin` header in the same hook. When the hook refuses an upgrade with status 401 or 403, the server closes the connection with code 4401 or 4403, and the client raises `UNAUTHORIZED` or `FORBIDDEN` and leaves that connection closed.
233
+ Because the client offers the plain `sirannon.v1` identifier ahead of your values and the server selects it, the ticket stays out of the handshake response. Check the `Origin` header in the same hook. When the hook rejects an upgrade with status 401 or 403, the server closes the connection with code 4401 or 4403, and the client raises `UNAUTHORIZED` or `FORBIDDEN` and leaves that connection closed.
214
234
 
215
235
  - Bind to `127.0.0.1` or a private interface unless a proxy enforces TLS and access control.
216
- - Use HTTPS and WSS for non-local traffic, because the built-in server binds plain HTTP.
236
+ - Use HTTPS and WSS for traffic beyond the local machine, because the built-in server listens on plain HTTP.
217
237
  - Authenticate every HTTP database route and every WebSocket upgrade, and check `Origin` against an allowlist.
218
- - Keep user input in parameters, which the driver binds rather than splicing into the SQL text.
219
- - Restrict CORS to known origins; `cors: true` allows every origin and belongs in local development.
238
+ - Keep user input in parameters, which the driver binds separately from the SQL text.
239
+ - Restrict CORS to known origins, since `cors: true` allows every origin and is for local development only.
220
240
  - Keep long-lived secrets out of browser-visible configuration, and redact credentials from access logs.
221
241
  - Add rate limits, audit logs, and abuse monitoring at the application or edge layer.
222
242
 
223
- The [security guide](https://sirannon.sondelali.com/docs) covers each of these in full.
243
+ Read the [security guide](https://sirannon.sondelali.com/docs/security) for each of these in full.
224
244
 
225
245
  ## Documentation
226
246
 
227
- | Guide | What it covers |
247
+ | Guide | Topics |
228
248
  | --- | --- |
229
- | [Core engine](../../docs/core.md) | Bulk load, live queries, migrations, hooks, metrics, and the multi-tenant lifecycle |
230
- | [Backups](../../docs/backups.md) | Copies to a file or to storage you supply, the chain of changes after one, and restoring from a moment you name |
231
- | [Server](../../docs/server.md) | HTTP routes, WebSocket messages, authentication, write shapes, the writer worker, and value encoding |
232
- | [Registered operations](../../docs/operations.md) | Naming the statements a server runs, identity-filled arguments, capabilities, and code generation |
233
- | [Live queries](../../docs/live-queries.md) | Maintained query results locally, over the network, and in React |
234
- | [Client SDK](../../docs/client.md) | Transports, subscriptions, topology-aware routing, and read concern |
235
- | [Device sync](../../docs/device-sync.md) | Offline-first two-way sync between a device's local database and a server |
236
- | [Distributed replication](../../docs/replication.md) | Replication, first sync, write and read concerns, coordinator failover, resolvers, and transports |
237
- | [Configuration reference](../../docs/configuration.md) | Every option table, from `SirannonOptions` to `GrpcReplicationOptions` |
238
- | [Errors](../../docs/errors.md) | Every code, when it happens, whether the call is safe to retry, and its HTTP status |
239
-
240
- The [specification](../spec/) defines the wire formats, value encodings, and replication invariants every implementation follows.
249
+ | [Queries and transactions](https://sirannon.sondelali.com/docs/queries-and-transactions) | Parameterised SQL, batches, transactions, and the connection pool |
250
+ | [Bulk load](https://sirannon.sondelali.com/docs/bulk-load) | One-transaction imports under relaxed durability, over the server, and through the client SDK |
251
+ | [Migrations](https://sirannon.sondelali.com/docs/migrations) | File-based, programmatic, and bundled migrations, rollback, checksums, baselines, concurrency, and registry migrations |
252
+ | [Hooks, metrics, and lifecycle](https://sirannon.sondelali.com/docs/hooks-metrics-and-lifecycle) | Before and after hooks, metrics callbacks, and the multi-tenant lifecycle |
253
+ | [Backups](https://sirannon.sondelali.com/docs/backups) | Copies of an open database to a file and on a schedule, followed by the guides on [destinations](https://sirannon.sondelali.com/docs/backup-destinations), [continuous backups](https://sirannon.sondelali.com/docs/backup-chains), and [restores](https://sirannon.sondelali.com/docs/backup-restore) |
254
+ | [Server](https://sirannon.sondelali.com/docs/server) | Registered operations on the server, caller identity, size limits, write shapes, `acceptSql`, the backup routes, HTTP routes, and the WebSocket protocol |
255
+ | [Registered operations](https://sirannon.sondelali.com/docs/registered-operations) | Named reads and writes, identity-filled arguments, calls over HTTP and WebSocket, capabilities, and refusals |
256
+ | [Live queries](https://sirannon.sondelali.com/docs/live-queries) | Maintained query results locally, over the network, and in React |
257
+ | [Client SDK](https://sirannon.sondelali.com/docs/client-sdk) | Reads, writes, subscriptions, live queries, bulk imports, read concern, refused credentials, and client options |
258
+ | [Device sync](https://sirannon.sondelali.com/docs/device-sync) | Offline-first two-way sync between a device's local database and a server |
259
+ | [Distributed replication](https://sirannon.sondelali.com/docs/distributed-replication) | Certificates, first sync, write and read concerns, conflict resolution, coordinator failover, and schema changes |
260
+ | [Configuration reference](https://sirannon.sondelali.com/docs/configuration) | Every option for the registry, a database, backups, the server, the client, device sync, and replication |
261
+ | [Error codes](https://sirannon.sondelali.com/docs/error-codes) | Every error code, whether a retry is safe, its HTTP status, and the WebSocket close codes |
262
+
263
+ The wire formats, the value encodings, and the replication invariants that every implementation follows are in the [specification](https://github.com/assetcorp/sirannon-db/tree/main/packages/spec).
241
264
 
242
265
  ## Example projects
243
266
 
244
267
  | Example | Runtime | What it demonstrates |
245
268
  | --- | --- | --- |
246
- | [`node`](examples/node/) | Node.js >= 22 | Schema, migrations, CRUD, transactions, CDC, live queries, pools, metrics, multi-tenant lifecycle, hooks, backup, shutdown |
247
- | [`web-wa-sqlite`](examples/web-wa-sqlite/) | Browser and Node.js | Offline-first device sync: a local database in the browser, snapshot load, offline writes, conflict resolution, and a local live query |
248
- | [`web-client`](examples/web-client/) | Browser and Node.js | Registered operations, code generation, remote live queries, and the React hooks |
249
- | [`distributed-entitlements`](examples/distributed-entitlements/) | Node.js and browser | Three-node coordinator-backed replication over gRPC with etcd authority, mTLS, and Toxiproxy failure controls |
269
+ | [`node`](https://github.com/assetcorp/sirannon-db/tree/main/packages/ts/examples/node) | Node.js >= 22 | Schema, migrations, CRUD, transactions, CDC, live queries, pools, metrics, multi-tenant lifecycle, hooks, backup, and shutdown |
270
+ | [`web-wa-sqlite`](https://github.com/assetcorp/sirannon-db/tree/main/packages/ts/examples/web-wa-sqlite) | Browser and Node.js | Offline-first device sync with a local database in the browser, snapshot load, offline writes, conflict resolution, and a local live query |
271
+ | [`web-client`](https://github.com/assetcorp/sirannon-db/tree/main/packages/ts/examples/web-client) | Browser and Node.js | Registered operations, code generation, remote live queries, and the React hooks |
272
+ | [`distributed-entitlements`](https://github.com/assetcorp/sirannon-db/tree/main/packages/ts/examples/distributed-entitlements) | Node.js and browser | Three-node coordinator-backed replication over gRPC with etcd authority, mTLS, and Toxiproxy failure controls |
250
273
 
251
274
  ```bash
252
275
  pnpm install && pnpm --filter @delali/sirannon-db build
@@ -1,70 +1,69 @@
1
- import { B as BackupDestination, f as BackupRunReport } from '../types-DPSmjU54.js';
2
- export { g as BackupChain, h as BackupChainBase, i as BackupChainChange, j as BackupChainPosition, k as BackupChainRecord, l as BackupCycle, m as BackupCycleError, n as BackupCycleOptions, o as BackupCycleStatus, p as BackupFileReport, q as BackupGroupMembership, r as BackupGroupSource, s as BackupLogPosition, t as BackupNodePreference, u as BackupPiece, v as BackupProgress, w as BackupSkip, x as BackupSkipReason, y as BackupToDestinationOptions, z as BackupVerifyResult, C as DEFAULT_CHAIN_NAME, E as chainLogName, F as readBackupChains, G as verifyBackupRecord } from '../types-DPSmjU54.js';
3
- export { B as BackupCapabilities, a as BackupChainLocation, b as BackupRestorePlan, c as BackupSafeToDeleteOptions, d as backupPiecesSafeToDelete, p as planBackupRestore } from '../database-backup-B7oszQ2n.js';
4
- import { B as BackupRestoreOptions, a as BackupRestoreReport } from '../restore-options-CksYsH1l.js';
5
- export { b as BackupRestoreProgress } from '../restore-options-CksYsH1l.js';
6
- import '../query-types-p1liOP-b.js';
1
+ import { B as BackupDestination, f as BackupRunReport } from '../types-Dc8oWlxj.js';
2
+ export { g as BackupChain, h as BackupChainBase, i as BackupChainChange, j as BackupChainPosition, k as BackupChainRecord, l as BackupCycle, m as BackupCycleError, n as BackupCycleOptions, o as BackupCycleStatus, p as BackupFileReport, q as BackupGroupMembership, r as BackupGroupSource, s as BackupLogPosition, t as BackupNodePreference, u as BackupPiece, v as BackupProgress, w as BackupSkip, x as BackupSkipReason, y as BackupToDestinationOptions, z as BackupVerifyResult, C as DEFAULT_CHAIN_NAME, E as chainLogName, F as readBackupChains, G as verifyBackupRecord } from '../types-Dc8oWlxj.js';
3
+ export { B as BackupCapabilities, a as BackupChainLocation, b as BackupRestorePlan, c as BackupSafeToDeleteOptions, d as backupPiecesSafeToDelete, p as planBackupRestore } from '../database-backup-DcLhxoa1.js';
4
+ import { B as BackupRestoreOptions, a as BackupRestoreReport } from '../restore-options-CH8mE-Tb.js';
5
+ export { b as BackupRestoreProgress } from '../restore-options-CH8mE-Tb.js';
6
+ import '../query-types-5B02ixH0.js';
7
7
 
8
- /** What one assembled file took to build.
8
+ /** The byte and piece counts for one file that Sirannon assembles from a destination.
9
9
  * @public
10
10
  */
11
11
  interface AssembleResult {
12
- /** Bytes the assembly wrote. */
12
+ /** The number of bytes that Sirannon writes to the local file. */
13
13
  bytesWritten: number;
14
- /** Pieces the assembly read. */
14
+ /** The number of pieces that Sirannon reads from the destination. */
15
15
  pieceCount: number;
16
- /** SHA-256 of the assembled file, where the run recorded one to check it against. */
16
+ /** The SHA-256 of the assembled file, present only where the backup report holds a fingerprint to check it against. */
17
17
  fingerprint?: string;
18
18
  }
19
19
  /**
20
- * Builds a local file from the pieces a destination holds, checking the result
21
- * against what the run that wrote them reported.
20
+ * Rebuilds a local file from the pieces at a destination, and checks the result
21
+ * against the report of the backup that stored them.
22
22
  *
23
- * @param destination - Where the pieces are read from.
24
- * @param report - What the run that wrote those pieces recorded.
25
- * @param destPath - Path the assembled file is written to.
26
- * @returns The bytes and pieces the assembly wrote, and the fingerprint it computed.
23
+ * @param destination - The destination that holds the pieces.
24
+ * @param report - The report of the backup that stored those pieces.
25
+ * @param destPath - The path that Sirannon writes the assembled file to.
26
+ * @returns The bytes and pieces that Sirannon writes, and the fingerprint that it computes.
27
27
  *
28
28
  * @public
29
29
  */
30
30
  declare function assembleFromDestination(destination: BackupDestination, report: BackupRunReport, destPath: string): Promise<AssembleResult>;
31
31
 
32
32
  /**
33
- * Rebuilds a database from the moment you name and leaves it at a path of your
34
- * choosing.
33
+ * Rebuilds a database at the moment that you name, and writes it to a path
34
+ * that you choose.
35
35
  *
36
36
  * Sirannon reads the chain records at your destination and takes the newest
37
- * full copy finished at or before that moment. It then replays every change
38
- * piece captured from that copy up to the same moment, fetching one stored
39
- * piece and applying it before it asks for the next. One stored piece is
40
- * therefore all a restore holds, however large the database it rebuilds.
37
+ * full copy that finished at or before that moment. It then applies every
38
+ * change piece that it captured after that copy up to the same moment,
39
+ * fetching one stored piece and applying it before it requests the next, so a
40
+ * restore holds one stored piece in memory however large the database is.
41
41
  *
42
- * Two kinds of gap fail the call. A chain missing a change piece fails with
43
- * `BACKUP_CHAIN_BROKEN` naming the piece its sequence stops at, and a
44
- * destination missing one of the numbered pieces a file was stored in fails
45
- * with `BACKUP_DESTINATION_ERROR` naming that piece. Sirannon also checks each
46
- * file against the byte count and the fingerprint its backup recorded, both of
47
- * which cover the whole file.
42
+ * A gap fails the call in one of two ways. A missing change piece in the chain
43
+ * throws `BACKUP_CHAIN_BROKEN`, naming the sequence number where the chain
44
+ * stops, and a missing stored piece of a file throws
45
+ * `BACKUP_DESTINATION_ERROR`, naming that piece. Sirannon also checks each file
46
+ * against the byte count and the fingerprint in its record, both of which cover
47
+ * the whole file.
48
48
  *
49
- * Sirannon assembles the rebuilt database beside the path you named and renames
50
- * it onto that path once the last batch is folded in. A restore that fails, or
51
- * one the machine kills part-way, therefore leaves that path holding whatever
52
- * it held before. Where a database already sits at that path, Sirannon folds its
53
- * write-ahead log back into it before the rename, so a machine that stops the
54
- * restore between those two steps leaves that database whole. Where the fold
55
- * cannot empty that log, because another connection holds the database or
56
- * SQLite cannot open the file at all, Sirannon removes that database together
57
- * with its log, so a machine stopping there leaves the path plainly empty
58
- * rather than quietly short of its last commits. A database already there stops
59
- * the call unless you set `replaceExisting`, because the rename leaves the
60
- * rebuilt database at that path and nothing of the one it replaced.
49
+ * Sirannon builds the database next to the path that you name, and renames it
50
+ * onto that path after the checkpoint of the last batch. When the restore fails
51
+ * or the machine stops part-way, the path therefore keeps its previous
52
+ * contents. Where a database already exists at that path, Sirannon checkpoints
53
+ * its write-ahead log into it before the rename, so that a crash between those
54
+ * two steps leaves that database complete. Where the checkpoint cannot empty
55
+ * that log, because another connection holds the database or SQLite cannot open
56
+ * the file, Sirannon deletes that database and its log, so that a crash at that
57
+ * point leaves the path empty, with no database there that lacks its last
58
+ * commits. A database at that path stops the call unless you set
59
+ * `replaceExisting`, because the rename replaces that database entirely.
61
60
  *
62
- * The disk this needs is the finished database, plus one stored piece, plus the
63
- * log Sirannon writes for one batch of change pieces. `batchSize` sets that
64
- * last part.
61
+ * The restore needs disk space for the finished database, one stored piece,
62
+ * and the log that Sirannon writes for one batch of change pieces, whose size
63
+ * `batchSize` sets.
65
64
  *
66
- * @param options - Where to read from, what moment to reach, and where to put the result.
67
- * @returns The chain it read, the moment the result reflects, and what the restore fetched and replayed.
65
+ * @param options - The destination to read from, the moment to restore to, and the path for the result.
66
+ * @returns The chain that Sirannon reads, the moment that the rebuilt database reflects, and the counts of what the restore fetches and applies.
68
67
  *
69
68
  * @public
70
69
  */
@@ -1,8 +1,8 @@
1
- export { assembleFromDestination, restoreBackup } from '../chunk-E7Q7HE46.mjs';
2
- export { backupPiecesSafeToDelete, planBackupRestore } from '../chunk-YNTDOFTN.mjs';
3
- export { verifyBackupRecord } from '../chunk-7SC2TZAS.mjs';
4
- import '../chunk-U445A3C4.mjs';
5
- import '../chunk-GYCEJNU4.mjs';
6
- export { DEFAULT_CHAIN_NAME, chainLogName, readBackupChains } from '../chunk-YVF2ZJTE.mjs';
1
+ export { assembleFromDestination, restoreBackup } from '../chunk-3BZLZ47R.mjs';
2
+ export { backupPiecesSafeToDelete, planBackupRestore } from '../chunk-YW7IKQ7T.mjs';
3
+ export { verifyBackupRecord } from '../chunk-DX4WGJL7.mjs';
4
+ import '../chunk-QGVRRKB2.mjs';
5
+ import '../chunk-R7GBXNTO.mjs';
6
+ export { DEFAULT_CHAIN_NAME, chainLogName, readBackupChains } from '../chunk-CZL4D3BM.mjs';
7
7
  import '../chunk-QBBLSQHQ.mjs';
8
- import '../chunk-PBRXXISQ.mjs';
8
+ import '../chunk-KINDFBQP.mjs';
@@ -1,14 +1,14 @@
1
- import { I as SQLiteConnection, J as BackupFileCopy, K as BackupRunRequest, f as BackupRunReport, L as BackupScheduleRequest } from '../types-DPSmjU54.js';
2
- export { p as BackupFileReport, O as BackupScheduleOptions, R as RunExclusive } from '../types-DPSmjU54.js';
3
- import '../query-types-p1liOP-b.js';
1
+ import { I as SQLiteConnection, J as BackupFileCopy, K as BackupRunRequest, f as BackupRunReport, L as BackupScheduleRequest } from '../types-Dc8oWlxj.js';
2
+ export { p as BackupFileReport, O as BackupScheduleOptions, R as RunExclusive } from '../types-Dc8oWlxj.js';
3
+ import '../query-types-5B02ixH0.js';
4
4
 
5
- /** What one runtime needs before a copy can reach a destination without a local file.
5
+ /** What a runtime supplies so that Sirannon can send a copy to a destination without writing a local file.
6
6
  * @internal
7
7
  */
8
8
  interface BackupStreamingSupport {
9
- /** Absolute path of the compiled extension that carries the bytes. */
9
+ /** The absolute path of the compiled SQLite extension that queues the bytes of the copy for Sirannon to send. */
10
10
  extensionPath: string;
11
- /** Opens the connection the extension's statements run on. */
11
+ /** Opens the connection that Sirannon runs the statements of the extension on. */
12
12
  openConnection(): Promise<SQLiteConnection>;
13
13
  }
14
14
 
@@ -16,35 +16,34 @@ declare class BackupManager {
16
16
  private readonly streaming?;
17
17
  constructor(streaming?: BackupStreamingSupport | undefined);
18
18
  /**
19
- * Copies the database behind a connection to a file, while that database
20
- * stays open for reads and writes.
19
+ * Copies the database that a connection has open to a file, while other
20
+ * callers go on reading from and writing to that database.
21
21
  *
22
- * @param conn - Connection the copy runs on, which must be the connection that writes.
23
- * @param destPath - Path to write the copy to. A file already there stops the copy.
24
- * @param onFirstStep - Called once the copy's first step is done, so the caller can hand the writer back.
25
- * @returns What the copy moved, how long it took, and how often it restarted.
22
+ * @param conn - The writer connection, which SQLite runs the copy on.
23
+ * @param destPath - The path to write the copy to. Sirannon refuses a path where a file already exists.
24
+ * @param onFirstStep - Called once after the first step of the copy, so that the caller can release the writer.
25
+ * @returns The pages that SQLite copies, the time that the copy takes, and the number of times that SQLite restarts it from page one.
26
26
  */
27
27
  backup(conn: SQLiteConnection, destPath: string, onFirstStep?: () => void): Promise<BackupFileCopy>;
28
28
  /**
29
- * Reads the size of the file a copy wrote.
29
+ * Reads the size of the file that a copy writes.
30
30
  *
31
- * @param resolved - Absolute path of that file.
32
- * @param destPath - Path the caller named, which the error states.
33
- * @returns Bytes it holds.
31
+ * @param resolved - The absolute path of that file.
32
+ * @param destPath - The path that the caller names, which Sirannon quotes in any error.
33
+ * @returns The size of the file, in bytes.
34
34
  */
35
35
  private fileBytes;
36
36
  /**
37
- * Runs the copy, and removes the file it was writing where that copy fails, so
38
- * that a database missing its later pages never stays on disk as though the
39
- * copy had finished. Sirannon would otherwise count that half-written file
40
- * among the copies it keeps, and it would evict a whole copy to make room for
41
- * one nothing can restore.
37
+ * Runs the copy and deletes its file when the copy fails, so that a partly
38
+ * written database never stays on disk. Rotation would otherwise count that
39
+ * file among the copies it keeps, and it would delete a complete copy to make
40
+ * room for it.
42
41
  *
43
- * Sirannon stops waiting on a copy once the stall deadline passes, and SQLite
44
- * keeps writing that copy to the file. A removal that raced a live copy would
45
- * leave a truncated file in place, and on Windows it would fail outright.
46
- * Sirannon therefore removes the file only once the copy stops, while the
47
- * caller receives the failure at once and waits for none of that.
42
+ * When the stall deadline passes, Sirannon stops waiting on the copy while
43
+ * SQLite goes on writing to the file. Deleting the file under a live copy
44
+ * would leave a truncated file in place, and on Windows the delete would fail.
45
+ * Sirannon therefore deletes the file once the copy stops, although the caller
46
+ * receives the failure straight away.
48
47
  */
49
48
  private copyOrClearUp;
50
49
  copyToDestination(conn: SQLiteConnection, request: BackupRunRequest): Promise<BackupRunReport>;
@@ -54,7 +53,7 @@ declare class BackupManager {
54
53
  }
55
54
 
56
55
  /**
57
- * Repeats a database backup on a cron schedule and keeps a bounded number of files.
56
+ * Copies a database to a file on a cron schedule, and deletes the oldest copies beyond a set number.
58
57
  *
59
58
  * @public
60
59
  */
@@ -64,29 +63,31 @@ declare class BackupScheduler {
64
63
  /**
65
64
  * Starts repeating backups and returns a function that stops them.
66
65
  *
67
- * @param conn - Connection to the database being copied.
68
- * @param request - Cron expression, destination directory, retention, and time zone, along with the callbacks and the database the copies come from.
66
+ * @param conn - The connection to the database that Sirannon copies.
67
+ * @param request - The cron expression, destination directory, retention, and time zone, with the callbacks and the details of the source database.
69
68
  * @returns A function that stops the schedule.
70
69
  */
71
70
  schedule(conn: SQLiteConnection, request: BackupScheduleRequest): () => void;
72
71
  /**
73
- * Works out the database and the file every report names. A caller may supply
74
- * either of them, and Sirannon reads the file SQLite has open where the caller
75
- * named none. Sirannon puts that question to the connection that writes, so it
76
- * asks with nothing else holding the writer.
72
+ * Returns the database identifier and the source file that every report
73
+ * names. The caller can supply either of them. Where the caller names no
74
+ * source file, Sirannon reads the path of the file that SQLite has open, and
75
+ * it runs that query on the writer connection while no other operation holds
76
+ * the writer.
77
77
  *
78
- * @param conn - Connection the copies come from.
79
- * @param run - The schedule, which holds whichever of the two the caller named.
80
- * @param runExclusive - Runs the question with nothing else holding the writer.
81
- * @returns The database identifier and the path of the file the copies come from.
82
- * @throws A `BACKUP_ERROR` where the caller named no source file and SQLite has none open.
78
+ * @param conn - The connection that Sirannon copies from.
79
+ * @param run - The schedule, which holds whichever of the two the caller names.
80
+ * @param runExclusive - Runs the query while no other operation holds the writer.
81
+ * @returns The database identifier and the path of the source file.
82
+ * @throws A `BACKUP_ERROR` where the caller names no source file and SQLite reports no file open on the connection.
83
83
  */
84
84
  private namesFor;
85
85
  /**
86
- * Passes one finished copy to the caller's completion callback, and stops
87
- * waiting on that callback once its deadline passes.
86
+ * Passes the report of one finished copy to the completion callback of the
87
+ * caller, and fails with `BACKUP_ERROR` once the deadline of that callback
88
+ * passes. A deadline of zero waits for the callback without a limit.
88
89
  *
89
- * @param report - What the copy produced.
90
+ * @param report - The report of the finished copy.
90
91
  * @param run - The schedule, which holds the callback and its deadline.
91
92
  */
92
93
  private handToCaller;
@@ -1,6 +1,6 @@
1
- export { BackupScheduler } from '../chunk-JQWGHSEH.mjs';
1
+ export { BackupScheduler } from '../chunk-NVSQ2T5Q.mjs';
2
2
  import '../chunk-W67A4H2E.mjs';
3
- import '../chunk-E6IMX7RS.mjs';
4
- import '../chunk-GYCEJNU4.mjs';
3
+ import '../chunk-KJLNYOOL.mjs';
4
+ import '../chunk-R7GBXNTO.mjs';
5
5
  import '../chunk-QBBLSQHQ.mjs';
6
- import '../chunk-PBRXXISQ.mjs';
6
+ import '../chunk-KINDFBQP.mjs';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Marks one migration file as a baseline, which a new database runs in place of every version up to and including `through`.
3
+ *
4
+ * A database that already has migration history skips the baseline and applies only the versions that it lacks.
5
+ *
6
+ * @public
7
+ */
8
+ interface BaselineFileOption {
9
+ /**
10
+ * The version of the migration that acts as the baseline.
11
+ */
12
+ version: number;
13
+ /**
14
+ * The highest earlier version that a new database skips in favour of the baseline.
15
+ */
16
+ through: number;
17
+ }
18
+
19
+ export type { BaselineFileOption as B };