@delali/sirannon-db 0.3.5 → 0.3.6-next.78

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/dist/backup/index.d.ts +5 -5
  2. package/dist/backup/index.mjs +5 -3
  3. package/dist/backup-scheduler/index.d.ts +2 -2
  4. package/dist/backup-scheduler/index.mjs +2 -2
  5. package/dist/chunk-22TYGH7J.mjs +55 -0
  6. package/dist/{chunk-SB3MIFLG.mjs → chunk-2ZQOZFX7.mjs} +29 -6
  7. package/dist/{chunk-UIDQGPJY.mjs → chunk-4O7WYZHN.mjs} +38 -3
  8. package/dist/{chunk-ZFFUVYYE.mjs → chunk-5DKWHT6K.mjs} +109 -1
  9. package/dist/chunk-5O4J6LQQ.mjs +35 -0
  10. package/dist/{chunk-CZL4D3BM.mjs → chunk-6CABU54O.mjs} +2 -2
  11. package/dist/{chunk-JAQ2I4OT.mjs → chunk-A5ULJVK7.mjs} +11 -11
  12. package/dist/{chunk-RLBKZZ24.mjs → chunk-AC7TKWNH.mjs} +1 -0
  13. package/dist/chunk-BKO4SCEC.mjs +99 -0
  14. package/dist/{chunk-KOTBP4AW.mjs → chunk-C7E4H6UN.mjs} +24 -4
  15. package/dist/{chunk-G4AG3ESO.mjs → chunk-FBA7P4ZA.mjs} +114 -149
  16. package/dist/{chunk-DKML5425.mjs → chunk-GBVB6WSC.mjs} +2 -2
  17. package/dist/chunk-I4JY5QPA.mjs +51 -0
  18. package/dist/{chunk-L6EX6K27.mjs → chunk-MO4APBYG.mjs} +54 -13
  19. package/dist/{chunk-RPWBYASV.mjs → chunk-MTOQ6QIS.mjs} +1 -1
  20. package/dist/chunk-MX77PGEN.mjs +60 -0
  21. package/dist/{chunk-BTUD6XQO.mjs → chunk-N3MHZV3B.mjs} +11 -29
  22. package/dist/{chunk-UHUDPQFI.mjs → chunk-OJXV4L3N.mjs} +1 -3
  23. package/dist/chunk-QF7WF7RJ.mjs +34 -0
  24. package/dist/chunk-RLBV4YTB.mjs +22 -0
  25. package/dist/{chunk-ISDT4RBD.mjs → chunk-ULUZUBON.mjs} +1 -1
  26. package/dist/{chunk-AP4367HK.mjs → chunk-VGRAAQX3.mjs} +4 -4
  27. package/dist/chunk-W74HFQFD.mjs +153 -0
  28. package/dist/chunk-Y4SP7CRF.mjs +9 -0
  29. package/dist/{chunk-JFVEGN7J.mjs → chunk-ZAUHAJ4G.mjs} +2 -1
  30. package/dist/{chunk-DDIHBUEN.mjs → chunk-ZWW53O5P.mjs} +19 -7
  31. package/dist/client/index.d.ts +8 -8
  32. package/dist/client/topology.d.ts +4 -4
  33. package/dist/{client-base-Doaqwrx3.d.ts → client-base-DJZbSY55.d.ts} +2 -2
  34. package/dist/{client-streams-MKPE4MXW.mjs → client-streams-F7IG3PTU.mjs} +13 -16
  35. package/dist/core/index.d.ts +48 -12
  36. package/dist/core/index.mjs +1812 -107
  37. package/dist/core/writer-worker.mjs +28 -4
  38. package/dist/{database-fz-8CJBP.d.ts → database-BZ96ZzAx.d.ts} +411 -184
  39. package/dist/{database-backup-BwTNiBm2.d.ts → database-backup-BhM8yJ1A.d.ts} +32 -2
  40. package/dist/driver/better-sqlite3.d.ts +11 -1
  41. package/dist/driver/better-sqlite3.mjs +212 -23
  42. package/dist/driver/bun.d.ts +1 -1
  43. package/dist/driver/bun.mjs +15 -22
  44. package/dist/driver/expo.d.ts +1 -1
  45. package/dist/driver/expo.mjs +185 -16
  46. package/dist/driver/node.d.ts +1 -1
  47. package/dist/driver/node.mjs +15 -62
  48. package/dist/driver/wa-sqlite.d.ts +1 -1
  49. package/dist/driver/wa-sqlite.mjs +14 -153
  50. package/dist/{change-tracker-Dpo99EI-.d.ts → errors-DLMAGQFb.d.ts} +200 -2
  51. package/dist/file-migrations/index.d.ts +1 -1
  52. package/dist/peer-refusal-RR37UOLV.mjs +1 -0
  53. package/dist/{protocol-DZSYSv0S.d.ts → protocol-DE10taas.d.ts} +1 -1
  54. package/dist/replication/coordinator/etcd.mjs +1 -1
  55. package/dist/replication/index.d.ts +7 -7
  56. package/dist/replication/index.mjs +66 -9
  57. package/dist/{restore-options-CcMukOLO.d.ts → restore-options-B6HV_5XT.d.ts} +6 -1
  58. package/dist/server/index.d.ts +10 -10
  59. package/dist/server/index.mjs +506 -350
  60. package/dist/{server-options-B9CsAZp5.d.ts → server-options-Bhdlvehe.d.ts} +14 -1
  61. package/dist/{server-streams-SBYCHWWM.mjs → server-streams-Q2AZAW3N.mjs} +6 -30
  62. package/dist/{sirannon-D0CjDULa.d.ts → sirannon-Ba1o2HUQ.d.ts} +5 -5
  63. package/dist/transport/grpc.d.ts +15 -4
  64. package/dist/transport/grpc.mjs +34 -37
  65. package/dist/transport/memory.d.ts +4 -3
  66. package/dist/transport/memory.mjs +8 -26
  67. package/dist/{types-DTmi7pXp.d.ts → types-BWyv_N18.d.ts} +189 -1
  68. package/dist/{types-BlZ9GG1a.d.ts → types-CDnlkVRK.d.ts} +8 -2
  69. package/package.json +17 -9
  70. package/dist/chunk-DBN57KVR.mjs +0 -171
  71. package/dist/chunk-W67A4H2E.mjs +0 -23
  72. package/dist/chunk-YW7IKQ7T.mjs +0 -62
@@ -1,6 +1,6 @@
1
+ import { N as NodeHealth, T as Transaction, J as EncryptionKey, K as ClusterStatusInfo } from './types-BWyv_N18.js';
1
2
  import { a as OperationRegistry, D as DatabaseOperations } from './operation-registry-CGhNT-Sn.js';
2
3
  import { R as ReplicationBatch, C as ConflictResolver, A as ApplyResult } from './types-BG31TtrQ.js';
3
- import { N as NodeHealth, T as Transaction, I as ClusterStatusInfo } from './types-DTmi7pXp.js';
4
4
  import { P as Params, Q as QueryOptions, E as ExecuteResult } from './query-types-5B02ixH0.js';
5
5
 
6
6
  /**
@@ -202,6 +202,19 @@ interface ServerOptions<Identity = unknown> {
202
202
  * hook.
203
203
  */
204
204
  acceptBackupRestore?: boolean;
205
+ /**
206
+ * Enables `POST /db/{id}/encryption`, which encrypts a database, changes its key, or removes encryption while the
207
+ * server serves it; the default is false. The server constructor throws `INVALID_ENCRYPTION_CHANGE` when you enable
208
+ * it without an `authenticate` hook or without {@link ServerOptions.encryptionKeys}.
209
+ */
210
+ acceptEncryptionChange?: boolean;
211
+ /**
212
+ * Returns the key that a `keyRef` in an encryption request refers to, so that the caller sends the server a name and
213
+ * the key stays on the server. Return 32 random bytes, or `{ key, id }` to store an identifier with the key. The
214
+ * server calls it once to check the key before it answers, and again each time the database opens its connections
215
+ * under that key.
216
+ */
217
+ encryptionKeys?: (databaseId: string, keyRef: string) => EncryptionKey | Promise<EncryptionKey>;
205
218
  /** Enables device sync, which covers the push, snapshot, and migration-list routes, device subscriptions, and acknowledgements; the default is false. The server constructor throws `INVALID_DEVICE_SYNC` when you enable it without an `authenticate` hook. */
206
219
  acceptDeviceSync?: boolean;
207
220
  /** Returns the target that the server executes a database's statements against; without it, the server uses the databases in its own registry. */
@@ -1,6 +1,6 @@
1
+ import { ReplicationService, buildServerCreds, localHello, peerHelloFields } from './chunk-4O7WYZHN.mjs';
1
2
  import { SERVICE_NAME } from './chunk-BVMZJOVM.mjs';
2
- import { ReplicationService, buildServerCreds } from './chunk-UIDQGPJY.mjs';
3
- import { restartPeerConnection, registerPeer, removePeer } from './chunk-RLBKZZ24.mjs';
3
+ import { restartPeerConnection, registerPeer, removePeer } from './chunk-AC7TKWNH.mjs';
4
4
  import { fromForwardRequest, fromBatchPayload, fromAckPayload, fromSyncRequestPayload, fromSyncBatchPayload, fromSyncCompletePayload, fromSyncAckPayload } from './chunk-3VTT3VU6.mjs';
5
5
  import { TransportError } from './chunk-KU32OT4H.mjs';
6
6
  import './chunk-BLMRNSUG.mjs';
@@ -77,22 +77,10 @@ function handleServerReplicateStream(t, call) {
77
77
  t.serverPeerStreams.set(peerId, ps);
78
78
  if (replaced) restartPeerConnection(t.connectedPeers, t.peerDisconnectedHandler, peerId);
79
79
  if (!helloSent) {
80
- call.write({
81
- hello: {
82
- nodeId: t.localNodeId,
83
- role: t.localRole,
84
- groupId: t.localGroupId ?? "",
85
- primaryTerm: t.localPrimaryTerm ?? 0n,
86
- protocolVersion: t.localProtocolVersion ?? ""
87
- }
88
- });
80
+ call.write({ hello: localHello(t) });
89
81
  helloSent = true;
90
82
  }
91
- registerPeer(t.connectedPeers, t.peerConnectedHandler, peerId, peerRole, {
92
- groupId: msg.hello.groupId || void 0,
93
- primaryTerm: msg.hello.primaryTerm === 0n ? void 0 : msg.hello.primaryTerm,
94
- protocolVersion: msg.hello.protocolVersion || void 0
95
- });
83
+ registerPeer(t.connectedPeers, t.peerConnectedHandler, peerId, peerRole, peerHelloFields(msg.hello));
96
84
  return;
97
85
  }
98
86
  if (msg.batch) {
@@ -139,22 +127,10 @@ function handleServerSyncStream(t, call) {
139
127
  ps.syncStream = call;
140
128
  t.serverPeerStreams.set(peerId, ps);
141
129
  if (!helloSent) {
142
- call.write({
143
- hello: {
144
- nodeId: t.localNodeId,
145
- role: t.localRole,
146
- groupId: t.localGroupId ?? "",
147
- primaryTerm: t.localPrimaryTerm ?? 0n,
148
- protocolVersion: t.localProtocolVersion ?? ""
149
- }
150
- });
130
+ call.write({ hello: localHello(t) });
151
131
  helloSent = true;
152
132
  }
153
- registerPeer(t.connectedPeers, t.peerConnectedHandler, peerId, peerRole, {
154
- groupId: msg.hello.groupId || void 0,
155
- primaryTerm: msg.hello.primaryTerm === 0n ? void 0 : msg.hello.primaryTerm,
156
- protocolVersion: msg.hello.protocolVersion || void 0
157
- });
133
+ registerPeer(t.connectedPeers, t.peerConnectedHandler, peerId, peerRole, peerHelloFields(msg.hello));
158
134
  return;
159
135
  }
160
136
  if (msg.syncRequest) {
@@ -1,5 +1,5 @@
1
- import { H as HookRegistry, D as Database, a as HookDispose } from './database-fz-8CJBP.js';
2
- import { a as SirannonOptions, S as SQLiteDriver, b as MetricsCollector, D as DatabaseOptions, M as Migration, c as BeforeQueryHook, A as AfterQueryHook, d as BeforeConnectHook, e as DatabaseOpenHook, f as DatabaseCloseHook } from './types-DTmi7pXp.js';
1
+ import { H as HookRegistry, D as Database, a as HookDispose } from './database-BZ96ZzAx.js';
2
+ import { a as SirannonOptions, S as SQLiteDriver, b as MetricsCollector, D as DatabaseOptions, E as EncryptionOptions, M as Migration, c as BeforeQueryHook, A as AfterQueryHook, d as BeforeConnectHook, e as DatabaseOpenHook, f as DatabaseCloseHook } from './types-BWyv_N18.js';
3
3
 
4
4
  /** The outcome of taking one database offline.
5
5
  * @internal
@@ -80,13 +80,13 @@ declare class Sirannon {
80
80
  * Sirannon reopens the database even after a failed action.
81
81
  *
82
82
  * @param id - The identifier of the database to take offline.
83
- * @param action - The function that Sirannon calls with the database file path once the database is closed.
83
+ * @param action - The function that Sirannon calls with the database file path and its key once the database is closed.
84
84
  * @returns Whether the action returned, the value that it returned or the error that it threw, and any error that the reopen threw.
85
- * @throws A `DatabaseNotFoundError` when no database is open under the identifier, a `ReadOnlyError` when the database is read-only, or the close error when the close fails.
85
+ * @throws A `DatabaseNotFoundError` when no database is open under the identifier, a `ReadOnlyError` when the database is read-only, a `ReplicationError` with `REPLICATION_ACTIVE` while a replication engine writes to the database, or the close error when the close fails.
86
86
  *
87
87
  * @internal
88
88
  */
89
- withDatabaseOffline<T>(id: string, action: (path: string) => Promise<T>): Promise<OfflineOutcome<T>>;
89
+ withDatabaseOffline<T>(id: string, action: (path: string, encryption?: EncryptionOptions) => Promise<T>): Promise<OfflineOutcome<T>>;
90
90
  /**
91
91
  * Returns an already-open database.
92
92
  *
@@ -1,10 +1,10 @@
1
1
  import { BinaryWriter, BinaryReader } from '@bufbuild/protobuf/wire';
2
2
  import { Client, ClientDuplexStream, CallOptions, Metadata, ServiceError, ClientUnaryCall, ChannelCredentials, ClientOptions, ServerDuplexStream, Server } from '@grpc/grpc-js';
3
3
  import { HealthImplementation } from 'grpc-health-check';
4
- import { R as ReplicationAck, F as ForwardedTransaction, a as ForwardedTransactionResult, N as NodeInfo, S as SyncRequest, b as SyncBatch, c as SyncComplete, d as SyncAck, e as ReplicationTransport, f as TopologyRole, T as TransportConfig } from '../types-BlZ9GG1a.js';
4
+ import { S as SirannonError } from '../errors-DLMAGQFb.js';
5
+ import { R as ReplicationAck, F as ForwardedTransaction, a as ForwardedTransactionResult, N as NodeInfo, S as SyncRequest, b as SyncBatch, c as SyncComplete, d as SyncAck, e as ReplicationTransport, f as TopologyRole, T as TransportConfig } from '../types-CDnlkVRK.js';
5
6
  import { R as ReplicationBatch } from '../types-BG31TtrQ.js';
6
- import '../change-tracker-Dpo99EI-.js';
7
- import '../types-DTmi7pXp.js';
7
+ import '../types-BWyv_N18.js';
8
8
  import '../query-types-5B02ixH0.js';
9
9
  import '../types-MczwKtul.js';
10
10
 
@@ -68,6 +68,7 @@ interface Hello {
68
68
  groupId: string;
69
69
  primaryTerm: bigint;
70
70
  protocolVersion: string;
71
+ encrypted: boolean;
71
72
  }
72
73
  declare const Hello: MessageFns<Hello>;
73
74
  interface ReplicationMessage {
@@ -324,6 +325,8 @@ declare class GrpcReplicationTransport implements ReplicationTransport {
324
325
  /** @internal */
325
326
  localProtocolVersion: string | undefined;
326
327
  /** @internal */
328
+ localEncrypted: boolean;
329
+ /** @internal */
327
330
  connected: boolean;
328
331
  /** @internal */
329
332
  server: Server | null;
@@ -398,6 +401,14 @@ declare class GrpcReplicationTransport implements ReplicationTransport {
398
401
  sendSyncComplete(peerId: string, complete: SyncComplete): Promise<void>;
399
402
  /** Tells the source whether this joining node stored one first-sync page. */
400
403
  sendSyncAck(peerId: string, ack: SyncAck): Promise<void>;
404
+ /**
405
+ * Ends every stream with one peer, and carries the error's code to the peer in the `sirannon-error-code` trailer of
406
+ * each stream that the peer opened.
407
+ *
408
+ * @param peerId - The peer to refuse.
409
+ * @param error - The error whose code the streams carry, such as `ENCRYPTION_REQUIRED`.
410
+ */
411
+ refusePeer(peerId: string, error: SirannonError): Promise<void>;
401
412
  /** Registers the handler that applies incoming change batches. */
402
413
  onBatchReceived(handler: BatchHandler): void;
403
414
  /** Registers the handler that records incoming acknowledgements. */
@@ -429,7 +440,7 @@ declare class GrpcReplicationTransport implements ReplicationTransport {
429
440
  }): string | null;
430
441
  private ensureConnected;
431
442
  private getReplicateWriteStream;
432
- private getSyncWriteStream;
443
+ private writeSync;
433
444
  }
434
445
 
435
446
  export { type GrpcReplicationOptions, GrpcReplicationTransport, fromColumnValue, toColumnValue };
@@ -1,6 +1,6 @@
1
1
  import { SERVICE_NAME, DEFAULT_FORWARD_DEADLINE_MS } from '../chunk-BVMZJOVM.mjs';
2
- import { replaceWantedEndpoints, stopEndpointDialling } from '../chunk-KOTBP4AW.mjs';
3
- import { peerIdForServerStream, replicateWriteStream, syncWriteStream } from '../chunk-RLBKZZ24.mjs';
2
+ import { replaceWantedEndpoints, stopEndpointDialling } from '../chunk-C7E4H6UN.mjs';
3
+ import { peerIdForServerStream, replicateWriteStream, syncWriteStream } from '../chunk-AC7TKWNH.mjs';
4
4
  import { toBatchPayload, toAckPayload, toSyncRequestPayload, toSyncBatchPayload, toSyncCompletePayload, toSyncAckPayload } from '../chunk-3VTT3VU6.mjs';
5
5
  export { fromColumnValue, toColumnValue } from '../chunk-3VTT3VU6.mjs';
6
6
  import { TransportError } from '../chunk-KU32OT4H.mjs';
@@ -29,15 +29,17 @@ async function loadGrpcRuntime() {
29
29
  await requirePackage("@grpc/grpc-js", () => import('@grpc/grpc-js'));
30
30
  await requirePackage("@bufbuild/protobuf", () => import('@bufbuild/protobuf/wire'));
31
31
  await requirePackage("grpc-health-check", () => import('grpc-health-check'));
32
- const [serverStreams, clientStreams, forwardRpc] = await Promise.all([
33
- import('../server-streams-SBYCHWWM.mjs'),
34
- import('../client-streams-MKPE4MXW.mjs'),
35
- import('../forward-rpc-S7NMLBX7.mjs')
32
+ const [serverStreams, clientStreams, forwardRpc, peerRefusal] = await Promise.all([
33
+ import('../server-streams-Q2AZAW3N.mjs'),
34
+ import('../client-streams-F7IG3PTU.mjs'),
35
+ import('../forward-rpc-S7NMLBX7.mjs'),
36
+ import('../peer-refusal-RR37UOLV.mjs')
36
37
  ]);
37
38
  return {
38
39
  startServer: serverStreams.startServer,
39
40
  connectToEndpoint: clientStreams.connectToEndpoint,
40
- forwardOverRpc: forwardRpc.forwardOverRpc
41
+ forwardOverRpc: forwardRpc.forwardOverRpc,
42
+ refusePeerStreams: peerRefusal.refusePeerStreams
41
43
  };
42
44
  }
43
45
 
@@ -116,6 +118,8 @@ var GrpcReplicationTransport = class {
116
118
  /** @internal */
117
119
  localProtocolVersion;
118
120
  /** @internal */
121
+ localEncrypted = false;
122
+ /** @internal */
119
123
  connected = false;
120
124
  /** @internal */
121
125
  server = null;
@@ -181,6 +185,7 @@ var GrpcReplicationTransport = class {
181
185
  this.localGroupId = config.groupId;
182
186
  this.localPrimaryTerm = config.primaryTerm;
183
187
  this.localProtocolVersion = config.protocolVersion;
188
+ this.localEncrypted = config.encrypted === true;
184
189
  this.connected = true;
185
190
  if (this.localRole === "primary" || config.groupId) {
186
191
  await runtime.startServer(this);
@@ -285,43 +290,30 @@ var GrpcReplicationTransport = class {
285
290
  }
286
291
  /** Asks a peer to stream a full copy of the database. */
287
292
  async requestSync(peerId, request) {
288
- this.ensureConnected();
289
- const stream = this.getSyncWriteStream(peerId);
290
- if (!stream) {
291
- throw new TransportError(`Peer '${peerId}' is not connected`);
292
- }
293
- const msg = { syncRequest: toSyncRequestPayload(request) };
294
- await writeWithBackpressure(stream, msg);
293
+ await this.writeSync(peerId, () => ({ syncRequest: toSyncRequestPayload(request) }));
295
294
  }
296
295
  /** Sends one page of first-sync table data. */
297
296
  async sendSyncBatch(peerId, batch) {
298
- this.ensureConnected();
299
- const stream = this.getSyncWriteStream(peerId);
300
- if (!stream) {
301
- throw new TransportError(`Peer '${peerId}' is not connected`);
302
- }
303
- const msg = { syncBatch: toSyncBatchPayload(batch) };
304
- await writeWithBackpressure(stream, msg);
297
+ await this.writeSync(peerId, () => ({ syncBatch: toSyncBatchPayload(batch) }));
305
298
  }
306
299
  /** Tells a joining node that first sync is complete, and sends the manifests that the node checks its copy against. */
307
300
  async sendSyncComplete(peerId, complete) {
308
- this.ensureConnected();
309
- const stream = this.getSyncWriteStream(peerId);
310
- if (!stream) {
311
- throw new TransportError(`Peer '${peerId}' is not connected`);
312
- }
313
- const msg = { syncComplete: toSyncCompletePayload(complete) };
314
- await writeWithBackpressure(stream, msg);
301
+ await this.writeSync(peerId, () => ({ syncComplete: toSyncCompletePayload(complete) }));
315
302
  }
316
303
  /** Tells the source whether this joining node stored one first-sync page. */
317
304
  async sendSyncAck(peerId, ack) {
318
- this.ensureConnected();
319
- const stream = this.getSyncWriteStream(peerId);
320
- if (!stream) {
321
- throw new TransportError(`Peer '${peerId}' is not connected`);
322
- }
323
- const msg = { syncAck: toSyncAckPayload(ack) };
324
- await writeWithBackpressure(stream, msg);
305
+ await this.writeSync(peerId, () => ({ syncAck: toSyncAckPayload(ack) }));
306
+ }
307
+ /**
308
+ * Ends every stream with one peer, and carries the error's code to the peer in the `sirannon-error-code` trailer of
309
+ * each stream that the peer opened.
310
+ *
311
+ * @param peerId - The peer to refuse.
312
+ * @param error - The error whose code the streams carry, such as `ENCRYPTION_REQUIRED`.
313
+ */
314
+ async refusePeer(peerId, error) {
315
+ if (!this.connected || !this.runtime) return;
316
+ this.runtime.refusePeerStreams(this, peerId, error);
325
317
  }
326
318
  /** Registers the handler that applies incoming change batches. */
327
319
  onBatchReceived(handler) {
@@ -394,8 +386,13 @@ var GrpcReplicationTransport = class {
394
386
  getReplicateWriteStream(peerId) {
395
387
  return replicateWriteStream(this.serverPeerStreams, this.clientPeerStreams, peerId);
396
388
  }
397
- getSyncWriteStream(peerId) {
398
- return syncWriteStream(this.serverPeerStreams, this.clientPeerStreams, peerId);
389
+ async writeSync(peerId, message) {
390
+ this.ensureConnected();
391
+ const stream = syncWriteStream(this.serverPeerStreams, this.clientPeerStreams, peerId);
392
+ if (!stream) {
393
+ throw new TransportError(`Peer '${peerId}' is not connected`);
394
+ }
395
+ await writeWithBackpressure(stream, message());
399
396
  }
400
397
  };
401
398
 
@@ -1,7 +1,7 @@
1
- import { R as ReplicationAck, F as ForwardedTransaction, a as ForwardedTransactionResult, S as SyncRequest, b as SyncBatch, c as SyncComplete, d as SyncAck, N as NodeInfo, e as ReplicationTransport, T as TransportConfig } from '../types-BlZ9GG1a.js';
1
+ import { R as ReplicationAck, F as ForwardedTransaction, a as ForwardedTransactionResult, S as SyncRequest, b as SyncBatch, c as SyncComplete, d as SyncAck, N as NodeInfo, e as ReplicationTransport, T as TransportConfig } from '../types-CDnlkVRK.js';
2
2
  import { R as ReplicationBatch } from '../types-BG31TtrQ.js';
3
- import '../change-tracker-Dpo99EI-.js';
4
- import '../types-DTmi7pXp.js';
3
+ import '../errors-DLMAGQFb.js';
4
+ import '../types-BWyv_N18.js';
5
5
  import '../query-types-5B02ixH0.js';
6
6
  import '../types-MczwKtul.js';
7
7
 
@@ -34,6 +34,7 @@ declare class InMemoryTransport implements ReplicationTransport {
34
34
  private localGroupId;
35
35
  private localPrimaryTerm;
36
36
  private localProtocolVersion;
37
+ private localEncrypted;
37
38
  private connected;
38
39
  private readonly bus;
39
40
  private readonly connectedPeers;
@@ -1,3 +1,7 @@
1
+ import { TransportError } from '../chunk-KU32OT4H.mjs';
2
+ import '../chunk-BLMRNSUG.mjs';
3
+ import '../chunk-KINDFBQP.mjs';
4
+
1
5
  // src/transport/memory/bus.ts
2
6
  var MemoryBus = class {
3
7
  transports = /* @__PURE__ */ new Map();
@@ -23,32 +27,6 @@ var MemoryBus = class {
23
27
  }
24
28
  };
25
29
 
26
- // src/core/errors.ts
27
- var SirannonError = class extends Error {
28
- constructor(message, code) {
29
- super(message);
30
- this.code = code;
31
- this.name = "SirannonError";
32
- }
33
- };
34
-
35
- // src/core/sync/errors.ts
36
- var ReplicationError = class extends SirannonError {
37
- constructor(message, code = "REPLICATION_ERROR", details) {
38
- super(message, code);
39
- this.details = details;
40
- this.name = "ReplicationError";
41
- }
42
- };
43
-
44
- // src/replication/errors.ts
45
- var TransportError = class extends ReplicationError {
46
- constructor(message) {
47
- super(message, "TRANSPORT_ERROR");
48
- this.name = "TransportError";
49
- }
50
- };
51
-
52
30
  // src/transport/validators.ts
53
31
  function isValidBatch(batch) {
54
32
  if (typeof batch !== "object" || batch === null) return false;
@@ -117,6 +95,7 @@ var InMemoryTransport = class {
117
95
  localGroupId;
118
96
  localPrimaryTerm;
119
97
  localProtocolVersion;
98
+ localEncrypted = false;
120
99
  connected = false;
121
100
  bus;
122
101
  connectedPeers = /* @__PURE__ */ new Map();
@@ -145,6 +124,7 @@ var InMemoryTransport = class {
145
124
  this.localGroupId = config.groupId;
146
125
  this.localPrimaryTerm = config.primaryTerm;
147
126
  this.localProtocolVersion = config.protocolVersion;
127
+ this.localEncrypted = config.encrypted === true;
148
128
  this.connected = true;
149
129
  this.bus.join(localNodeId, this);
150
130
  for (const peerId of this.bus.peerIds()) {
@@ -157,6 +137,7 @@ var InMemoryTransport = class {
157
137
  role: peerTransport.role,
158
138
  primaryTerm: peerTransport.localPrimaryTerm,
159
139
  protocolVersion: peerTransport.localProtocolVersion,
140
+ encrypted: peerTransport.localEncrypted,
160
141
  joinedAt: Date.now(),
161
142
  lastSeenAt: Date.now(),
162
143
  lastAckedSeq: 0n
@@ -168,6 +149,7 @@ var InMemoryTransport = class {
168
149
  role: this.localRole,
169
150
  primaryTerm: this.localPrimaryTerm,
170
151
  protocolVersion: this.localProtocolVersion,
152
+ encrypted: this.localEncrypted,
171
153
  joinedAt: Date.now(),
172
154
  lastSeenAt: Date.now(),
173
155
  lastAckedSeq: 0n
@@ -88,6 +88,8 @@ interface BackupChainBase {
88
88
  bytesWritten: number;
89
89
  /** The SHA-256 of the copy, present while fingerprinting is on. */
90
90
  fingerprint?: string;
91
+ /** The identifier that came with the key that this copy holds ciphertext under, where the key came with one. */
92
+ keyId?: string;
91
93
  }
92
94
  /**
93
95
  * One change piece, which holds the log frames that a database writes between
@@ -122,6 +124,8 @@ interface BackupChainChange {
122
124
  checkpointed: boolean;
123
125
  /** The SHA-256 of the frames, present while fingerprinting is on. */
124
126
  fingerprint?: string;
127
+ /** The identifier that came with the key that these frames hold ciphertext under, where the key came with one. */
128
+ keyId?: string;
125
129
  }
126
130
  /** Either the full copy at the head of a chain or one change piece along it.
127
131
  * @public
@@ -557,6 +561,8 @@ interface BackupCycleRequest extends BackupCycleOptions {
557
561
  acquireWriter: () => SQLiteConnection;
558
562
  /** Copies the whole database to the destination as the full copy that starts each chain. */
559
563
  fullCopy: (options: BackupToDestinationOptions) => Promise<BackupRunReport>;
564
+ /** Returns the identifier that came with the database's current key, which each chain record stores. */
565
+ keyId?: () => string | undefined;
560
566
  }
561
567
 
562
568
  /** A failure of one turn of the cycle.
@@ -753,6 +759,8 @@ declare class BackupCycle {
753
759
  private reportSkip;
754
760
  private tick;
755
761
  private verifyChain;
762
+ /** Drops the chain that this stopped cycle extended, so that the next cycle starts a new chain with a full copy. */
763
+ forgetChain(): Promise<void>;
756
764
  private discardState;
757
765
  /**
758
766
  * Runs one turn. When the turn fails with `BACKUP_LOG_REWOUND` or
@@ -784,6 +792,101 @@ declare class BackupCycle {
784
792
  private sendWaitingCapture;
785
793
  }
786
794
 
795
+ /**
796
+ * The key that an {@link EncryptionOptions.key} function returns: 32 raw bytes, or those bytes with an identifier that
797
+ * Sirannon stores in the database and reports through {@link EncryptionStatus.keyId}.
798
+ *
799
+ * @public
800
+ */
801
+ type EncryptionKey = Uint8Array | {
802
+ key: Uint8Array;
803
+ id?: string;
804
+ };
805
+ /**
806
+ * Settings that encrypt every page of a database file, its write-ahead log, and every backup that Sirannon writes for
807
+ * it.
808
+ *
809
+ * @public
810
+ */
811
+ interface EncryptionOptions {
812
+ /**
813
+ * Returns the 32-byte key. Sirannon calls it each time it opens the database's connections, which includes a
814
+ * restarted writer worker and the reopen after a restore or a key change, so the key can come from a key service or
815
+ * a keychain. Sirannon copies the bytes and wipes its copy once those connections are open, so the function can
816
+ * return a buffer that it keeps.
817
+ */
818
+ key: () => EncryptionKey | Promise<EncryptionKey>;
819
+ }
820
+ /**
821
+ * The key change that {@link DatabaseEncryption.changeEncryption} makes: a `key` function encrypts a plaintext database or
822
+ * changes the key of an encrypted one, and `null` removes encryption.
823
+ *
824
+ * @public
825
+ */
826
+ interface EncryptionChange {
827
+ /** Returns the 32-byte key that the database takes, or `null` to store the database as plaintext. */
828
+ key: (() => EncryptionKey | Promise<EncryptionKey>) | null;
829
+ }
830
+ /**
831
+ * The progress of the current key change, or the outcome of the last one.
832
+ *
833
+ * @public
834
+ */
835
+ interface EncryptionChangeStatus {
836
+ /**
837
+ * `idle` before any change, `copying` while Sirannon copies a snapshot to the new file, `catching-up` while it
838
+ * applies the writes committed since that snapshot, `swapping` while it holds writes and replaces the file, and
839
+ * `done` or `failed` once the change ends.
840
+ */
841
+ state: 'idle' | 'copying' | 'catching-up' | 'swapping' | 'done' | 'failed';
842
+ /** Whether the change encrypts the file or stores it as plaintext. */
843
+ target?: 'encrypted' | 'plaintext';
844
+ /** The time at which the change started, in milliseconds since the Unix epoch. */
845
+ startedAt?: number;
846
+ /** The time at which the change ended, in milliseconds since the Unix epoch. */
847
+ finishedAt?: number;
848
+ /** The number of rows that the snapshot copy wrote to the new file. */
849
+ rowsCopied?: number;
850
+ /** The number of rounds in which Sirannon applied the writes committed since the snapshot. */
851
+ rounds?: number;
852
+ /** The code and message of the error that stopped the change. */
853
+ error?: {
854
+ code: string;
855
+ message: string;
856
+ };
857
+ /**
858
+ * The code and message of an error that came after the new file was in place, such as a key function that failed
859
+ * when the connections reopened. The change is `done` and the file holds ciphertext under the new key, so keep that
860
+ * key, and close and open the database again with it.
861
+ */
862
+ reopenError?: {
863
+ code: string;
864
+ message: string;
865
+ };
866
+ }
867
+ /**
868
+ * Reports whether a database is encrypted, under which key, and how its last key change went.
869
+ *
870
+ * @public
871
+ */
872
+ interface EncryptionStatus {
873
+ /** Whether the database file holds ciphertext. */
874
+ encrypted: boolean;
875
+ /**
876
+ * The scheme that an encrypted file uses: `aegis-256` from the better-sqlite3 driver, or `sqlcipher-4` from the
877
+ * Expo driver.
878
+ */
879
+ cipher?: 'aegis-256' | 'sqlcipher-4';
880
+ /** The version of the encryption engine, which an encrypted database reports. */
881
+ cipherVersion?: string;
882
+ /** The identifier that came with the current key. */
883
+ keyId?: string;
884
+ /** The time at which the last key change finished, in milliseconds since the Unix epoch. */
885
+ changedAt?: number;
886
+ /** The current key change, or the last one. */
887
+ change: EncryptionChangeStatus;
888
+ }
889
+
787
890
  /** The context that Sirannon passes to each query hook.
788
891
  * @public
789
892
  */
@@ -1318,6 +1421,14 @@ interface DatabaseOptions {
1318
1421
  * off.
1319
1422
  */
1320
1423
  backups?: BackupCycleOptions;
1424
+ /**
1425
+ * Encrypts every page of the database file, its write-ahead log, and every
1426
+ * backup that Sirannon writes for it, under a 32-byte key that your function
1427
+ * returns. The driver has to report `encryption` in its capabilities, and with
1428
+ * any other driver the open throws `ENCRYPTION_UNSUPPORTED`. The default is
1429
+ * off, which keeps the file as plaintext.
1430
+ */
1431
+ encryption?: EncryptionOptions;
1321
1432
  }
1322
1433
  /** The limits and recovery settings for the writer worker thread.
1323
1434
  * @public
@@ -1422,6 +1533,45 @@ interface WorkerHostOptions {
1422
1533
  writeTimeoutMs?: number;
1423
1534
  maxRestarts?: number;
1424
1535
  countsRows?: boolean;
1536
+ encryptionKey?: () => Promise<Uint8Array>;
1537
+ }
1538
+
1539
+ /**
1540
+ * The file operations that a key change and its recovery perform on a database's files, which a driver for a runtime
1541
+ * with a file system supplies.
1542
+ *
1543
+ * When the runtime lacks a module that the operations need, each one can throw an `EncryptionError` with
1544
+ * `ENCRYPTION_UNSUPPORTED`. A key change then stops before it writes any file, and an open finds no key change to
1545
+ * finish.
1546
+ *
1547
+ * @public
1548
+ */
1549
+ interface DatabaseFileSystem {
1550
+ /**
1551
+ * Returns the first bytes of a file, fewer when the file is shorter.
1552
+ *
1553
+ * @param path - The file to read.
1554
+ * @param length - The most bytes to return.
1555
+ * @returns The bytes, or `null` when the file does not exist.
1556
+ */
1557
+ readStart(path: string, length: number): Promise<Uint8Array | null>;
1558
+ /**
1559
+ * Deletes a file, and resolves without an error when the file does not exist.
1560
+ *
1561
+ * @param path - The file to delete.
1562
+ */
1563
+ remove(path: string): Promise<void>;
1564
+ /**
1565
+ * Moves a file to a new path in the same directory, replacing any file at that path.
1566
+ *
1567
+ * Where the file system replaces a file in one rename, the move takes that one step. Where it cannot, the move
1568
+ * deletes the file at the new path and then renames the file, so a crash between the two steps can leave neither
1569
+ * file at the new path. Sirannon recovers from that state on the next open.
1570
+ *
1571
+ * @param from - The file to move.
1572
+ * @param to - The path that the file takes.
1573
+ */
1574
+ rename(from: string, to: string): Promise<void>;
1425
1575
  }
1426
1576
 
1427
1577
  /** Describes the result of one write through the driver.
@@ -1659,6 +1809,28 @@ interface SQLiteConnection {
1659
1809
  runGroup?(units: readonly {
1660
1810
  statements: readonly GroupRunStatement[];
1661
1811
  }[], opening?: GroupRunStatement): Promise<GroupRunOutcome[]>;
1812
+ /**
1813
+ * Starts recording every row that later writes on this connection insert,
1814
+ * update, or delete in the main database, through SQLite's session extension.
1815
+ * Sirannon calls it on the writer while it holds the writer, at the instant
1816
+ * that a key change copies its snapshot, so the snapshot and the recording
1817
+ * start from the same committed state.
1818
+ */
1819
+ startChangeCapture?(): Promise<void>;
1820
+ /**
1821
+ * Returns the rows that changed since the recording started or since the
1822
+ * last call, as a SQLite changeset, and restarts the recording in the same
1823
+ * step. Call it only while no transaction is open on the connection.
1824
+ */
1825
+ takeCapturedChanges?(): Promise<Uint8Array>;
1826
+ /** Stops the recording that {@link SQLiteConnection.startChangeCapture} started, and drops what it holds. */
1827
+ stopChangeCapture?(): Promise<void>;
1828
+ /**
1829
+ * Applies a changeset from {@link SQLiteConnection.takeCapturedChanges} to
1830
+ * this connection's database with triggers off, and fails without applying
1831
+ * any of it when a change does not match the rows that it expects.
1832
+ */
1833
+ applyCapturedChanges?(changes: Uint8Array): Promise<void>;
1662
1834
  }
1663
1835
  /**
1664
1836
  * The SQLite `PRAGMA synchronous` level for a connection. In WAL mode,
@@ -1692,6 +1864,15 @@ interface OpenOptions {
1692
1864
  * open, including after a writer worker restarts.
1693
1865
  */
1694
1866
  walAutoCheckpoint?: number;
1867
+ /**
1868
+ * The 32-byte key of an encrypted database. Sirannon sets it on every open
1869
+ * of an encrypted database, and the driver keeps no reference to the key once
1870
+ * `open` returns. A driver whose capabilities report `encryption` as false
1871
+ * fails such an open with `ENCRYPTION_UNSUPPORTED`.
1872
+ */
1873
+ encryption?: {
1874
+ key: Uint8Array;
1875
+ };
1695
1876
  }
1696
1877
  /** Describes what a driver's runtime supports.
1697
1878
  * @public
@@ -1710,6 +1891,11 @@ interface DriverCapabilities {
1710
1891
  * {@link SQLiteStatement.allCounted}.
1711
1892
  */
1712
1893
  rowCounts: boolean;
1894
+ /**
1895
+ * `true` when the driver encrypts every page of the database file and its
1896
+ * log under the 32-byte key that {@link OpenOptions.encryption} carries.
1897
+ */
1898
+ encryption: boolean;
1713
1899
  }
1714
1900
  /**
1715
1901
  * Describes how a worker thread rebuilds the driver, since a function such as
@@ -1758,6 +1944,8 @@ interface SQLiteDriver {
1758
1944
  * different library from the one that the operator named.
1759
1945
  */
1760
1946
  resolveExtensionPath?(extensionPath: string): string;
1947
+ /** The file operations that a key change and its recovery perform, which a driver for a runtime with a file system supplies. */
1948
+ readonly files?: DatabaseFileSystem;
1761
1949
  }
1762
1950
 
1763
- export { type BatchSummary as $, type AfterQueryHook as A, type BackupDestination as B, type BackupVerifyResult as C, type DatabaseOptions as D, DEFAULT_CHAIN_NAME as E, chainLogName as F, readBackupChains as G, verifyBackupRecord as H, type ClusterStatusInfo as I, type SQLiteConnection as J, type BackupFileCopy as K, type BackupRunRequest as L, type Migration as M, type NodeHealth as N, type BackupScheduleRequest as O, type BackupScheduleOptions as P, type DriverCapabilities as Q, type RunExclusive as R, type SQLiteDriver as S, Transaction as T, type BackupEngine as U, type SynchronousLevel as V, type LifecycleConfig as W, type MigrationResult as X, type RollbackResult as Y, type AfterQueryHookContext as Z, type AppliedMigrationEntry as _, type SirannonOptions as a, type BeforePushHook as a0, type BeforeSnapshotHook as a1, type BeforeSubscribeHook as a2, type CDCMetrics as a3, type ClusterReadEndpointInfo as a4, type ConnectionHookContext as a5, type ConnectionMetrics as a6, type Counted as a7, type DatabaseCopyRequest as a8, type DatabaseCopyStep as a9, type DriverWorkerEntry as aa, type HookConfig as ab, MIGRATION_NAME_RE as ac, type MetricsConfig as ad, type MigrationBaseline as ae, type MigrationSource as af, NODE_HEALTH_REASONS as ag, NODE_HEALTH_STATES as ah, type NodeHealthReason as ai, type NodeHealthState as aj, type OpenOptions as ak, type QueryHookContext as al, type QueryMetrics as am, type RunResult as an, type SQLiteStatement as ao, type WriterWorkerOptions as ap, type WorkerHostOptions as aq, type ChangeDispatchObserver as ar, SubscriptionManager as as, type WriterTransactionRunner as at, type StatementMeter as au, type WriterContext as av, type QueryOutcomeMeasure as aw, type QueryFailureMeasure as ax, type QueryOutcome as ay, type RowCounts as az, MetricsCollector as b, type BeforeQueryHook as c, type BeforeConnectHook as d, type DatabaseOpenHook as e, type DatabaseCloseHook as f, type BackupRunReport as g, type BackupChain as h, type BackupChainBase as i, type BackupChainChange as j, type BackupChainPosition as k, type BackupChainRecord as l, BackupCycle as m, type BackupCycleError as n, type BackupCycleOptions as o, type BackupCycleStatus as p, type BackupFileReport as q, type BackupGroupMembership as r, type BackupGroupSource as s, type BackupLogPosition as t, type BackupNodePreference as u, type BackupPiece as v, type BackupProgress as w, type BackupSkip as x, type BackupSkipReason as y, type BackupToDestinationOptions as z };
1951
+ export { type AfterQueryHookContext as $, type AfterQueryHook as A, type BackupDestination as B, type BackupVerifyResult as C, type DatabaseOptions as D, type EncryptionOptions as E, DEFAULT_CHAIN_NAME as F, chainLogName as G, readBackupChains as H, verifyBackupRecord as I, type EncryptionKey as J, type ClusterStatusInfo as K, type SQLiteConnection as L, type Migration as M, type NodeHealth as N, type BackupFileCopy as O, type BackupRunRequest as P, type BackupScheduleRequest as Q, type BackupScheduleOptions as R, type SQLiteDriver as S, Transaction as T, type RunExclusive as U, type DriverCapabilities as V, type BackupEngine as W, type SynchronousLevel as X, type LifecycleConfig as Y, type MigrationResult as Z, type RollbackResult as _, type SirannonOptions as a, type AppliedMigrationEntry as a0, type BatchSummary as a1, type BeforePushHook as a2, type BeforeSnapshotHook as a3, type BeforeSubscribeHook as a4, type CDCMetrics as a5, type ClusterReadEndpointInfo as a6, type ConnectionHookContext as a7, type ConnectionMetrics as a8, type Counted as a9, type StatementMeter as aA, type WriterContext as aB, type QueryOutcomeMeasure as aC, type QueryFailureMeasure as aD, type QueryOutcome as aE, type RowCounts as aF, type DatabaseCopyRequest as aa, type DatabaseCopyStep as ab, type DatabaseFileSystem as ac, type DriverWorkerEntry as ad, type EncryptionChange as ae, type EncryptionChangeStatus as af, type EncryptionStatus as ag, type HookConfig as ah, MIGRATION_NAME_RE as ai, type MetricsConfig as aj, type MigrationBaseline as ak, type MigrationSource as al, NODE_HEALTH_REASONS as am, NODE_HEALTH_STATES as an, type NodeHealthReason as ao, type NodeHealthState as ap, type OpenOptions as aq, type QueryHookContext as ar, type QueryMetrics as as, type RunResult as at, type SQLiteStatement as au, type WriterWorkerOptions as av, type WorkerHostOptions as aw, type ChangeDispatchObserver as ax, SubscriptionManager as ay, type WriterTransactionRunner as az, MetricsCollector as b, type BeforeQueryHook as c, type BeforeConnectHook as d, type DatabaseOpenHook as e, type DatabaseCloseHook as f, type BackupRunReport as g, type BackupChain as h, type BackupChainBase as i, type BackupChainChange as j, type BackupChainPosition as k, type BackupChainRecord as l, BackupCycle as m, type BackupCycleError as n, type BackupCycleOptions as o, type BackupCycleStatus as p, type BackupFileReport as q, type BackupGroupMembership as r, type BackupGroupSource as s, type BackupLogPosition as t, type BackupNodePreference as u, type BackupPiece as v, type BackupProgress as w, type BackupSkip as x, type BackupSkipReason as y, type BackupToDestinationOptions as z };