@delali/sirannon-db 0.3.6-next.77 → 0.3.6-next.79
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 +31 -3
- package/dist/backup/index.d.ts +5 -5
- package/dist/backup/index.mjs +5 -3
- package/dist/backup-scheduler/index.d.ts +2 -2
- package/dist/backup-scheduler/index.mjs +2 -2
- package/dist/chunk-22TYGH7J.mjs +55 -0
- package/dist/{chunk-SB3MIFLG.mjs → chunk-2ZQOZFX7.mjs} +29 -6
- package/dist/{chunk-UIDQGPJY.mjs → chunk-4O7WYZHN.mjs} +38 -3
- package/dist/{chunk-ZFFUVYYE.mjs → chunk-5DKWHT6K.mjs} +109 -1
- package/dist/chunk-5O4J6LQQ.mjs +35 -0
- package/dist/{chunk-CZL4D3BM.mjs → chunk-6CABU54O.mjs} +2 -2
- package/dist/{chunk-JAQ2I4OT.mjs → chunk-A5ULJVK7.mjs} +11 -11
- package/dist/{chunk-RLBKZZ24.mjs → chunk-AC7TKWNH.mjs} +1 -0
- package/dist/chunk-BKO4SCEC.mjs +99 -0
- package/dist/{chunk-KOTBP4AW.mjs → chunk-C7E4H6UN.mjs} +24 -4
- package/dist/{chunk-G4AG3ESO.mjs → chunk-FBA7P4ZA.mjs} +114 -149
- package/dist/{chunk-DKML5425.mjs → chunk-GBVB6WSC.mjs} +2 -2
- package/dist/chunk-I4JY5QPA.mjs +51 -0
- package/dist/{chunk-L6EX6K27.mjs → chunk-MO4APBYG.mjs} +54 -13
- package/dist/{chunk-RPWBYASV.mjs → chunk-MTOQ6QIS.mjs} +1 -1
- package/dist/chunk-MX77PGEN.mjs +60 -0
- package/dist/{chunk-BTUD6XQO.mjs → chunk-N3MHZV3B.mjs} +11 -29
- package/dist/{chunk-UHUDPQFI.mjs → chunk-OJXV4L3N.mjs} +1 -3
- package/dist/chunk-QF7WF7RJ.mjs +34 -0
- package/dist/chunk-RLBV4YTB.mjs +22 -0
- package/dist/{chunk-ISDT4RBD.mjs → chunk-ULUZUBON.mjs} +1 -1
- package/dist/{chunk-AP4367HK.mjs → chunk-VGRAAQX3.mjs} +4 -4
- package/dist/chunk-W74HFQFD.mjs +153 -0
- package/dist/chunk-Y4SP7CRF.mjs +9 -0
- package/dist/{chunk-JFVEGN7J.mjs → chunk-ZAUHAJ4G.mjs} +2 -1
- package/dist/{chunk-DDIHBUEN.mjs → chunk-ZWW53O5P.mjs} +19 -7
- package/dist/client/index.d.ts +8 -8
- package/dist/client/topology.d.ts +4 -4
- package/dist/{client-base-Doaqwrx3.d.ts → client-base-DJZbSY55.d.ts} +2 -2
- package/dist/{client-streams-MKPE4MXW.mjs → client-streams-F7IG3PTU.mjs} +13 -16
- package/dist/core/index.d.ts +48 -12
- package/dist/core/index.mjs +1812 -107
- package/dist/core/writer-worker.mjs +28 -4
- package/dist/{database-fz-8CJBP.d.ts → database-BZ96ZzAx.d.ts} +411 -184
- package/dist/{database-backup-BwTNiBm2.d.ts → database-backup-BhM8yJ1A.d.ts} +32 -2
- package/dist/driver/better-sqlite3.d.ts +11 -1
- package/dist/driver/better-sqlite3.mjs +212 -23
- package/dist/driver/bun.d.ts +1 -1
- package/dist/driver/bun.mjs +15 -22
- package/dist/driver/expo.d.ts +1 -1
- package/dist/driver/expo.mjs +185 -16
- package/dist/driver/node.d.ts +1 -1
- package/dist/driver/node.mjs +15 -62
- package/dist/driver/wa-sqlite.d.ts +1 -1
- package/dist/driver/wa-sqlite.mjs +14 -153
- package/dist/{change-tracker-Dpo99EI-.d.ts → errors-DLMAGQFb.d.ts} +200 -2
- package/dist/file-migrations/index.d.ts +1 -1
- package/dist/peer-refusal-RR37UOLV.mjs +1 -0
- package/dist/{protocol-DZSYSv0S.d.ts → protocol-DE10taas.d.ts} +1 -1
- package/dist/replication/coordinator/etcd.mjs +1 -1
- package/dist/replication/index.d.ts +7 -7
- package/dist/replication/index.mjs +66 -9
- package/dist/{restore-options-CcMukOLO.d.ts → restore-options-B6HV_5XT.d.ts} +6 -1
- package/dist/server/index.d.ts +10 -10
- package/dist/server/index.mjs +506 -350
- package/dist/{server-options-B9CsAZp5.d.ts → server-options-Bhdlvehe.d.ts} +14 -1
- package/dist/{server-streams-SBYCHWWM.mjs → server-streams-Q2AZAW3N.mjs} +6 -30
- package/dist/{sirannon-D0CjDULa.d.ts → sirannon-Ba1o2HUQ.d.ts} +5 -5
- package/dist/transport/grpc.d.ts +15 -4
- package/dist/transport/grpc.mjs +34 -37
- package/dist/transport/memory.d.ts +4 -3
- package/dist/transport/memory.mjs +8 -26
- package/dist/{types-DTmi7pXp.d.ts → types-BWyv_N18.d.ts} +189 -1
- package/dist/{types-BlZ9GG1a.d.ts → types-CDnlkVRK.d.ts} +8 -2
- package/package.json +17 -9
- package/dist/chunk-DBN57KVR.mjs +0 -171
- package/dist/chunk-W67A4H2E.mjs +0 -23
- package/dist/chunk-YW7IKQ7T.mjs +0 -62
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/@delali/sirannon-db)
|
|
7
7
|
[](https://github.com/assetcorp/sirannon-db/blob/main/LICENSE)
|
|
8
8
|
|
|
9
|
-
Build
|
|
9
|
+
Build an encrypted, 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
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
|
|
|
@@ -61,7 +61,9 @@ import { betterSqlite3 } from '@delali/sirannon-db/driver/better-sqlite3'
|
|
|
61
61
|
const sirannon = new Sirannon({ driver: betterSqlite3() })
|
|
62
62
|
const db = await sirannon.open('app', './data/app.db')
|
|
63
63
|
|
|
64
|
-
await db.execute(
|
|
64
|
+
await db.execute(
|
|
65
|
+
'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, active INTEGER NOT NULL DEFAULT 1)',
|
|
66
|
+
)
|
|
65
67
|
await db.execute('INSERT INTO users (name, email) VALUES (?, ?)', ['Ada', 'ada@example.com'])
|
|
66
68
|
|
|
67
69
|
const users = await db.query<{ id: number; name: string }>('SELECT * FROM users')
|
|
@@ -79,11 +81,36 @@ const db = await sirannon.open('app', '/app.db', { walMode: false })
|
|
|
79
81
|
|
|
80
82
|
On React Native, open the database the same way with `expoSqlite()`.
|
|
81
83
|
|
|
84
|
+
### Encrypt a database
|
|
85
|
+
|
|
86
|
+
On Node.js, install Sirannon's encrypting SQLite build beside the library:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pnpm add -E @delali/sirannon-db @delali/sirannon-sqlite
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Create the better-sqlite3 driver with encryption enabled. Return 32 raw key bytes when Sirannon opens the database:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { readFile } from 'node:fs/promises'
|
|
96
|
+
import { Sirannon } from '@delali/sirannon-db'
|
|
97
|
+
import { betterSqlite3 } from '@delali/sirannon-db/driver/better-sqlite3'
|
|
98
|
+
|
|
99
|
+
const loadLedgerKey = async () => new Uint8Array(await readFile('/run/secrets/ledger-key'))
|
|
100
|
+
|
|
101
|
+
const sirannon = new Sirannon({ driver: betterSqlite3({ encryption: true }) })
|
|
102
|
+
const db = await sirannon.open('ledger', './data/ledger.db', {
|
|
103
|
+
encryption: { key: async () => ({ key: await loadLedgerKey(), id: 'ledger-2026-11' }) },
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Store the key in a key service, a system keychain, or a secret file whose permissions restrict access to the application's operating-system account. Retain both keys until a key change finishes. Retain each backup chain's key for as long as you keep that chain. The [encryption guide](https://github.com/assetcorp/sirannon-db/blob/main/docs/encryption.md) covers key changes, encrypted backups, server routes, and Expo's SQLCipher setup.
|
|
108
|
+
|
|
82
109
|
## Package exports
|
|
83
110
|
|
|
84
111
|
| Import | What you get |
|
|
85
112
|
| --- | --- |
|
|
86
|
-
| `@delali/sirannon-db` | Core library: queries, transactions, CDC, live queries, migrations, backups, hooks, metrics, and lifecycle |
|
|
113
|
+
| `@delali/sirannon-db` | Core library: queries, transactions, encryption, CDC, live queries, migrations, backups, hooks, metrics, and lifecycle |
|
|
87
114
|
| `@delali/sirannon-db/driver/*` | SQLite driver adapters, listed in the table above |
|
|
88
115
|
| `@delali/sirannon-db/file-migrations` | A loader for `.up.sql` and `.down.sql` files in a directory |
|
|
89
116
|
| `@delali/sirannon-db/backup` | Backup destination types, chain records, and `restoreBackup` |
|
|
@@ -252,6 +279,7 @@ Read the [security guide](https://sirannon.sondelali.com/docs/security) for each
|
|
|
252
279
|
| [Bulk load](https://sirannon.sondelali.com/docs/bulk-load) | One-transaction imports under relaxed durability, over the server, and through the client SDK |
|
|
253
280
|
| [Migrations](https://sirannon.sondelali.com/docs/migrations) | File-based, programmatic, and bundled migrations, rollback, checksums, baselines, concurrency, and registry migrations |
|
|
254
281
|
| [Hooks, metrics, and lifecycle](https://sirannon.sondelali.com/docs/hooks-metrics-and-lifecycle) | Before and after hooks, metrics callbacks, and the multi-tenant lifecycle |
|
|
282
|
+
| [Encryption](https://github.com/assetcorp/sirannon-db/blob/main/docs/encryption.md) | Encrypted Node and Expo databases, key changes, encrypted backups, and operator routes |
|
|
255
283
|
| [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) |
|
|
256
284
|
| [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 |
|
|
257
285
|
| [Registered operations](https://sirannon.sondelali.com/docs/registered-operations) | Named reads and writes, identity-filled arguments, calls over HTTP and WebSocket, capabilities, and refusals |
|
package/dist/backup/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { B as BackupDestination, g as BackupRunReport } from '../types-
|
|
2
|
-
export { h as BackupChain, i as BackupChainBase, j as BackupChainChange, k as BackupChainPosition, l as BackupChainRecord, m as BackupCycle, n as BackupCycleError, o as BackupCycleOptions, p as BackupCycleStatus, q as BackupFileReport, r as BackupGroupMembership, s as BackupGroupSource, t as BackupLogPosition, u as BackupNodePreference, v as BackupPiece, w as BackupProgress, x as BackupSkip, y as BackupSkipReason, z as BackupToDestinationOptions, C as BackupVerifyResult,
|
|
3
|
-
export { B as BackupCapabilities, a as BackupChainLocation, b as BackupRestorePlan, c as BackupSafeToDeleteOptions, d as backupPiecesSafeToDelete, p as planBackupRestore } from '../database-backup-
|
|
4
|
-
import { B as BackupRestoreOptions, a as BackupRestoreReport } from '../restore-options-
|
|
5
|
-
export { b as BackupRestoreProgress } from '../restore-options-
|
|
1
|
+
import { B as BackupDestination, g as BackupRunReport } from '../types-BWyv_N18.js';
|
|
2
|
+
export { h as BackupChain, i as BackupChainBase, j as BackupChainChange, k as BackupChainPosition, l as BackupChainRecord, m as BackupCycle, n as BackupCycleError, o as BackupCycleOptions, p as BackupCycleStatus, q as BackupFileReport, r as BackupGroupMembership, s as BackupGroupSource, t as BackupLogPosition, u as BackupNodePreference, v as BackupPiece, w as BackupProgress, x as BackupSkip, y as BackupSkipReason, z as BackupToDestinationOptions, C as BackupVerifyResult, F as DEFAULT_CHAIN_NAME, G as chainLogName, H as readBackupChains, I as verifyBackupRecord } from '../types-BWyv_N18.js';
|
|
3
|
+
export { B as BackupCapabilities, a as BackupChainLocation, b as BackupRestorePlan, c as BackupSafeToDeleteOptions, d as backupPiecesSafeToDelete, p as planBackupRestore } from '../database-backup-BhM8yJ1A.js';
|
|
4
|
+
import { B as BackupRestoreOptions, a as BackupRestoreReport } from '../restore-options-B6HV_5XT.js';
|
|
5
|
+
export { b as BackupRestoreProgress } from '../restore-options-B6HV_5XT.js';
|
|
6
6
|
import '../query-types-5B02ixH0.js';
|
|
7
7
|
|
|
8
8
|
/** The byte and piece counts for one file that Sirannon assembles from a destination.
|
package/dist/backup/index.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export { assembleFromDestination, restoreBackup } from '../chunk-
|
|
2
|
-
export { backupPiecesSafeToDelete, planBackupRestore } from '../chunk-
|
|
1
|
+
export { assembleFromDestination, restoreBackup } from '../chunk-MO4APBYG.mjs';
|
|
2
|
+
export { backupPiecesSafeToDelete, planBackupRestore } from '../chunk-W74HFQFD.mjs';
|
|
3
|
+
import '../chunk-5DKWHT6K.mjs';
|
|
3
4
|
export { verifyBackupRecord } from '../chunk-DX4WGJL7.mjs';
|
|
4
5
|
import '../chunk-QGVRRKB2.mjs';
|
|
5
6
|
import '../chunk-BUWJH3D3.mjs';
|
|
6
|
-
export { DEFAULT_CHAIN_NAME, chainLogName, readBackupChains } from '../chunk-
|
|
7
|
+
export { DEFAULT_CHAIN_NAME, chainLogName, readBackupChains } from '../chunk-6CABU54O.mjs';
|
|
7
8
|
import '../chunk-BGJ5JVYL.mjs';
|
|
8
9
|
import '../chunk-HO2XTXVX.mjs';
|
|
10
|
+
import '../chunk-5O4J6LQQ.mjs';
|
|
9
11
|
import '../chunk-KINDFBQP.mjs';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { q as BackupFileReport,
|
|
1
|
+
import { L as SQLiteConnection, O as BackupFileCopy, P as BackupRunRequest, g as BackupRunReport, Q as BackupScheduleRequest } from '../types-BWyv_N18.js';
|
|
2
|
+
export { q as BackupFileReport, R as BackupScheduleOptions, U as RunExclusive } from '../types-BWyv_N18.js';
|
|
3
3
|
import '../query-types-5B02ixH0.js';
|
|
4
4
|
|
|
5
5
|
/** What a runtime supplies so that Sirannon can send a copy to a destination without writing a local file.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { BackupScheduler } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { BackupScheduler } from '../chunk-N3MHZV3B.mjs';
|
|
2
|
+
import '../chunk-I4JY5QPA.mjs';
|
|
3
3
|
import '../chunk-AHCMO4YL.mjs';
|
|
4
4
|
import '../chunk-BUWJH3D3.mjs';
|
|
5
5
|
import '../chunk-BGJ5JVYL.mjs';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/core/driver/statement-text.ts
|
|
2
|
+
var SPACE = 32;
|
|
3
|
+
var TAB = 9;
|
|
4
|
+
var LINE_FEED = 10;
|
|
5
|
+
var VERTICAL_TAB = 11;
|
|
6
|
+
var FORM_FEED = 12;
|
|
7
|
+
var CARRIAGE_RETURN = 13;
|
|
8
|
+
var SEMICOLON = 59;
|
|
9
|
+
var HYPHEN = 45;
|
|
10
|
+
var SLASH = 47;
|
|
11
|
+
var ASTERISK = 42;
|
|
12
|
+
var NO_STATEMENT_MESSAGE = "The supplied SQL string contains no statements";
|
|
13
|
+
function startsSqliteSpace(code) {
|
|
14
|
+
return code === SPACE || code === TAB || code === LINE_FEED || code === FORM_FEED || code === CARRIAGE_RETURN;
|
|
15
|
+
}
|
|
16
|
+
function continuesSqliteSpace(code) {
|
|
17
|
+
return startsSqliteSpace(code) || code === VERTICAL_TAB;
|
|
18
|
+
}
|
|
19
|
+
function holdsNoStatement(sql) {
|
|
20
|
+
const firstNul = sql.indexOf("\0");
|
|
21
|
+
const text = firstNul === -1 ? sql : sql.slice(0, firstNul);
|
|
22
|
+
let index = 0;
|
|
23
|
+
while (index < text.length) {
|
|
24
|
+
const code = text.charCodeAt(index);
|
|
25
|
+
if (startsSqliteSpace(code)) {
|
|
26
|
+
index++;
|
|
27
|
+
while (index < text.length && continuesSqliteSpace(text.charCodeAt(index))) index++;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (code === SEMICOLON) {
|
|
31
|
+
index++;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const next = text.charCodeAt(index + 1);
|
|
35
|
+
if (code === HYPHEN && next === HYPHEN) {
|
|
36
|
+
const lineEnd = text.indexOf("\n", index + 2);
|
|
37
|
+
if (lineEnd === -1) return true;
|
|
38
|
+
index = lineEnd;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (code === SLASH && next === ASTERISK && index + 2 < text.length) {
|
|
42
|
+
const commentEnd = text.indexOf("*/", index + 2);
|
|
43
|
+
if (commentEnd === -1) return true;
|
|
44
|
+
index = commentEnd + 2;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
function assertHoldsStatement(sql) {
|
|
52
|
+
if (holdsNoStatement(sql)) throw new RangeError(NO_STATEMENT_MESSAGE);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { assertHoldsStatement };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { selectOrdinaryTableName, deleteReplacedRowsOfTable, selectMinChangeSeqSql, selectMaxChangeSeq, deleteChangesBeforeUpToSeqSql, deleteAllReplacedRows, selectTableExists, ensureChangesTable, ensureReplacedRowsTable, ensureMetaTable, insertMetaValueIfAbsent, selectMetaValue, selectTriggerSql, selectChangesAfterSeqSql, selectTablesChangesInRangeSql, tableInfoRows, primaryKeyColumns, tableColumnsWithGenerated, tableIndexList, indexColumns, selectTableSql, TABLE_XINFO_HIDDEN, deleteExpiredDeviceCursors, selectMinDeviceCursorBoundary, SEQ_STRING_RE, selectIndexSql } from './chunk-
|
|
2
|
-
import { CDC_TRIGGER_PREFIX, CHANGES_TABLE, isReservedIdentifier, META_TABLE, encodeTaggedValues, REPLACED_ROWS_TABLE, changedAtToEventTimestamp, decodeTaggedValues, DEVICE_CURSORS_TABLE, SAFE_INT_BOUND_TEXT } from './chunk-
|
|
1
|
+
import { selectOrdinaryTableName, deleteReplacedRowsOfTable, selectMinChangeSeqSql, selectMaxChangeSeq, deleteChangesBeforeUpToSeqSql, deleteAllReplacedRows, selectTableExists, ensureChangesTable, ensureReplacedRowsTable, ensureMetaTable, insertMetaValueIfAbsent, selectMetaValue, selectTriggerSql, selectChangesAfterSeqSql, selectTablesChangesInRangeSql, tableInfoRows, primaryKeyColumns, tableColumnsWithGenerated, tableIndexList, indexColumns, selectTableSql, TABLE_XINFO_HIDDEN, deleteExpiredDeviceCursors, selectMinDeviceCursorBoundary, SEQ_STRING_RE, selectIndexSql } from './chunk-MTOQ6QIS.mjs';
|
|
2
|
+
import { CDC_TRIGGER_PREFIX, CHANGES_TABLE, isReservedIdentifier, META_TABLE, encodeTaggedValues, REPLACED_ROWS_TABLE, changedAtToEventTimestamp, decodeTaggedValues, DEVICE_CURSORS_TABLE, SAFE_INT_BOUND_TEXT } from './chunk-ZAUHAJ4G.mjs';
|
|
3
3
|
import { tokenizeSql, isKeyword } from './chunk-SEL4A4WT.mjs';
|
|
4
|
-
import { synchronousPragmaValue } from './chunk-YCMB6GR4.mjs';
|
|
5
4
|
import { randomHex } from './chunk-BGJ5JVYL.mjs';
|
|
6
5
|
import { invokeCallerCallback, reportCallerFailure, toError } from './chunk-HO2XTXVX.mjs';
|
|
6
|
+
import { synchronousPragmaValue } from './chunk-YCMB6GR4.mjs';
|
|
7
7
|
import { SirannonError, ForbiddenSqlError, CDCError, MigrationError } from './chunk-KINDFBQP.mjs';
|
|
8
8
|
|
|
9
9
|
// src/core/bulk-load.ts
|
|
@@ -666,6 +666,28 @@ function buildJsonObject(columns, ref) {
|
|
|
666
666
|
function escStr(name) {
|
|
667
667
|
return name.replace(/'/g, "''");
|
|
668
668
|
}
|
|
669
|
+
async function installKeyChangeCaptureTriggers(conn, triggers, table, log, keys) {
|
|
670
|
+
const logOf = (row) => `INSERT INTO "${escId(log)}" VALUES (${keys.map((key) => `${row}.${key}`).join(", ")});`;
|
|
671
|
+
const on = `main."${escId(table)}"`;
|
|
672
|
+
await conn.exec(`CREATE TEMP TRIGGER "${escId(triggers.insert)}" AFTER INSERT ON ${on} BEGIN ${logOf("NEW")} END`);
|
|
673
|
+
await conn.exec(
|
|
674
|
+
`CREATE TEMP TRIGGER "${escId(triggers.update)}" AFTER UPDATE ON ${on} BEGIN ${logOf("OLD")} ${logOf("NEW")} END`
|
|
675
|
+
);
|
|
676
|
+
await conn.exec(`CREATE TEMP TRIGGER "${escId(triggers.delete)}" AFTER DELETE ON ${on} BEGIN ${logOf("OLD")} END`);
|
|
677
|
+
}
|
|
678
|
+
async function dropKeyChangeCaptureTriggers(conn, triggers) {
|
|
679
|
+
for (const name of [triggers.insert, triggers.update, triggers.delete]) {
|
|
680
|
+
await conn.exec(`DROP TRIGGER IF EXISTS temp."${escId(name)}"`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
async function dropMainSchemaTriggers(conn) {
|
|
684
|
+
const triggers = await (await conn.prepare("SELECT name, sql FROM main.sqlite_schema WHERE type = 'trigger' AND sql IS NOT NULL")).all();
|
|
685
|
+
for (const trigger of triggers) await conn.exec(`DROP TRIGGER main."${escId(trigger.name)}"`);
|
|
686
|
+
return triggers.map((trigger) => trigger.sql);
|
|
687
|
+
}
|
|
688
|
+
async function createMainSchemaTriggers(conn, statements) {
|
|
689
|
+
for (const statement of statements) await conn.exec(statement);
|
|
690
|
+
}
|
|
669
691
|
|
|
670
692
|
// src/core/cdc/change-tracker.ts
|
|
671
693
|
var DEFAULT_RETENTION_MS = 36e5;
|
|
@@ -1155,7 +1177,7 @@ var SubscriptionBuilderImpl = class {
|
|
|
1155
1177
|
return this.manager.subscribe(this.table, this.conditions, callback, options);
|
|
1156
1178
|
}
|
|
1157
1179
|
};
|
|
1158
|
-
function startPolling(
|
|
1180
|
+
function startPolling(connection, tracker, manager, intervalMs, onError, runExclusive, deviceRetention = {
|
|
1159
1181
|
cursorRetentionMs: DEFAULT_DEVICE_CURSOR_RETENTION_MS,
|
|
1160
1182
|
maxChangesHeldForDevice: 0
|
|
1161
1183
|
}) {
|
|
@@ -1170,7 +1192,7 @@ function startPolling(conn, tracker, manager, intervalMs, onError, runExclusive,
|
|
|
1170
1192
|
if (polling) return;
|
|
1171
1193
|
polling = true;
|
|
1172
1194
|
try {
|
|
1173
|
-
const events = await exclusive(() => tracker.poll(
|
|
1195
|
+
const events = await exclusive(() => tracker.poll(connection()));
|
|
1174
1196
|
if (events.length > 0) {
|
|
1175
1197
|
manager.dispatch(events);
|
|
1176
1198
|
}
|
|
@@ -1180,6 +1202,7 @@ function startPolling(conn, tracker, manager, intervalMs, onError, runExclusive,
|
|
|
1180
1202
|
if (tickCount >= CLEANUP_INTERVAL_TICKS) {
|
|
1181
1203
|
tickCount = 0;
|
|
1182
1204
|
await exclusive(async () => {
|
|
1205
|
+
const conn = connection();
|
|
1183
1206
|
await applyDeviceCursorBoundary(conn, tracker, deviceRetention);
|
|
1184
1207
|
await tracker.cleanup(conn);
|
|
1185
1208
|
});
|
|
@@ -1371,4 +1394,4 @@ var TransactionGrouper = class {
|
|
|
1371
1394
|
}
|
|
1372
1395
|
};
|
|
1373
1396
|
|
|
1374
|
-
export { ChangeTracker, DEVICE_SYNC_PUSHED_SEQ_META_KEY, PrimedSubscription, ROWID_ALIASES, SubscriptionBuilderImpl, SubscriptionManager, TransactionGrouper, applyDeviceCursorBoundary, dropCdcTriggers, ensureCdcEpoch, filteredChange, isBulkLoadDurability, migrationChecksum, migrationContentChecksum, needsResync, parseColumnDefinitions, reconcileMigrationChecksums, resolveDeviceRetentionPolicy, runBulkLoad, startPolling };
|
|
1397
|
+
export { ChangeTracker, DEVICE_SYNC_PUSHED_SEQ_META_KEY, PrimedSubscription, ROWID_ALIASES, SubscriptionBuilderImpl, SubscriptionManager, TransactionGrouper, applyDeviceCursorBoundary, createMainSchemaTriggers, dropCdcTriggers, dropKeyChangeCaptureTriggers, dropMainSchemaTriggers, ensureCdcEpoch, escId, filteredChange, installKeyChangeCaptureTriggers, isBulkLoadDurability, migrationChecksum, migrationContentChecksum, needsResync, parseColumnDefinitions, reconcileMigrationChecksums, resolveDeviceRetentionPolicy, runBulkLoad, startPolling };
|
|
@@ -883,7 +883,7 @@ var AckPayload = {
|
|
|
883
883
|
}
|
|
884
884
|
};
|
|
885
885
|
function createBaseHello() {
|
|
886
|
-
return { nodeId: "", role: "", groupId: "", primaryTerm: 0n, protocolVersion: "" };
|
|
886
|
+
return { nodeId: "", role: "", groupId: "", primaryTerm: 0n, protocolVersion: "", encrypted: false };
|
|
887
887
|
}
|
|
888
888
|
var Hello = {
|
|
889
889
|
encode(message, writer = new BinaryWriter()) {
|
|
@@ -905,6 +905,9 @@ var Hello = {
|
|
|
905
905
|
if (message.protocolVersion !== "") {
|
|
906
906
|
writer.uint32(42).string(message.protocolVersion);
|
|
907
907
|
}
|
|
908
|
+
if (message.encrypted !== false) {
|
|
909
|
+
writer.uint32(48).bool(message.encrypted);
|
|
910
|
+
}
|
|
908
911
|
return writer;
|
|
909
912
|
},
|
|
910
913
|
decode(input, length) {
|
|
@@ -949,6 +952,13 @@ var Hello = {
|
|
|
949
952
|
message.protocolVersion = reader.string();
|
|
950
953
|
continue;
|
|
951
954
|
}
|
|
955
|
+
case 6: {
|
|
956
|
+
if (tag !== 48) {
|
|
957
|
+
break;
|
|
958
|
+
}
|
|
959
|
+
message.encrypted = reader.bool();
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
952
962
|
}
|
|
953
963
|
if ((tag & 7) === 4 || tag === 0) {
|
|
954
964
|
break;
|
|
@@ -963,7 +973,8 @@ var Hello = {
|
|
|
963
973
|
role: isSet(object.role) ? globalThis.String(object.role) : "",
|
|
964
974
|
groupId: isSet(object.groupId) ? globalThis.String(object.groupId) : isSet(object.group_id) ? globalThis.String(object.group_id) : "",
|
|
965
975
|
primaryTerm: isSet(object.primaryTerm) ? BigInt(object.primaryTerm) : isSet(object.primary_term) ? BigInt(object.primary_term) : 0n,
|
|
966
|
-
protocolVersion: isSet(object.protocolVersion) ? globalThis.String(object.protocolVersion) : isSet(object.protocol_version) ? globalThis.String(object.protocol_version) : ""
|
|
976
|
+
protocolVersion: isSet(object.protocolVersion) ? globalThis.String(object.protocolVersion) : isSet(object.protocol_version) ? globalThis.String(object.protocol_version) : "",
|
|
977
|
+
encrypted: isSet(object.encrypted) ? globalThis.Boolean(object.encrypted) : false
|
|
967
978
|
};
|
|
968
979
|
},
|
|
969
980
|
toJSON(message) {
|
|
@@ -983,6 +994,9 @@ var Hello = {
|
|
|
983
994
|
if (message.protocolVersion !== "") {
|
|
984
995
|
obj.protocolVersion = message.protocolVersion;
|
|
985
996
|
}
|
|
997
|
+
if (message.encrypted !== false) {
|
|
998
|
+
obj.encrypted = message.encrypted;
|
|
999
|
+
}
|
|
986
1000
|
return obj;
|
|
987
1001
|
},
|
|
988
1002
|
create(base) {
|
|
@@ -995,6 +1009,7 @@ var Hello = {
|
|
|
995
1009
|
message.groupId = object.groupId ?? "";
|
|
996
1010
|
message.primaryTerm = object.primaryTerm ?? 0n;
|
|
997
1011
|
message.protocolVersion = object.protocolVersion ?? "";
|
|
1012
|
+
message.encrypted = object.encrypted ?? false;
|
|
998
1013
|
return message;
|
|
999
1014
|
}
|
|
1000
1015
|
};
|
|
@@ -2639,4 +2654,24 @@ function buildChannelCreds(options) {
|
|
|
2639
2654
|
return credentials.createSsl(ca, key, cert);
|
|
2640
2655
|
}
|
|
2641
2656
|
|
|
2642
|
-
|
|
2657
|
+
// src/transport/grpc/hello.ts
|
|
2658
|
+
function localHello(t) {
|
|
2659
|
+
return {
|
|
2660
|
+
nodeId: t.localNodeId,
|
|
2661
|
+
role: t.localRole,
|
|
2662
|
+
groupId: t.localGroupId ?? "",
|
|
2663
|
+
primaryTerm: t.localPrimaryTerm ?? 0n,
|
|
2664
|
+
protocolVersion: t.localProtocolVersion ?? "",
|
|
2665
|
+
encrypted: t.localEncrypted
|
|
2666
|
+
};
|
|
2667
|
+
}
|
|
2668
|
+
function peerHelloFields(hello) {
|
|
2669
|
+
return {
|
|
2670
|
+
groupId: hello.groupId || void 0,
|
|
2671
|
+
primaryTerm: hello.primaryTerm === 0n ? void 0 : hello.primaryTerm,
|
|
2672
|
+
protocolVersion: hello.protocolVersion || void 0,
|
|
2673
|
+
encrypted: hello.encrypted === true
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
export { ReplicationClient, ReplicationService, buildChannelCreds, buildServerCreds, localHello, peerHelloFields };
|
|
@@ -94,6 +94,54 @@ function getDotPath(issue) {
|
|
|
94
94
|
return null;
|
|
95
95
|
}
|
|
96
96
|
// @__NO_SIDE_EFFECTS__
|
|
97
|
+
function check(requirement, message$1) {
|
|
98
|
+
return {
|
|
99
|
+
kind: "validation",
|
|
100
|
+
type: "check",
|
|
101
|
+
reference: check,
|
|
102
|
+
async: false,
|
|
103
|
+
expects: null,
|
|
104
|
+
requirement,
|
|
105
|
+
message: message$1,
|
|
106
|
+
"~run"(dataset, config$1) {
|
|
107
|
+
if (dataset.typed && !this.requirement(dataset.value)) _addIssue(this, "input", dataset, config$1);
|
|
108
|
+
return dataset;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// @__NO_SIDE_EFFECTS__
|
|
113
|
+
function maxLength(requirement, message$1) {
|
|
114
|
+
return {
|
|
115
|
+
kind: "validation",
|
|
116
|
+
type: "max_length",
|
|
117
|
+
reference: maxLength,
|
|
118
|
+
async: false,
|
|
119
|
+
expects: `<=${requirement}`,
|
|
120
|
+
requirement,
|
|
121
|
+
message: message$1,
|
|
122
|
+
"~run"(dataset, config$1) {
|
|
123
|
+
if (dataset.typed && dataset.value.length > this.requirement) _addIssue(this, "length", dataset, config$1, { received: `${dataset.value.length}` });
|
|
124
|
+
return dataset;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// @__NO_SIDE_EFFECTS__
|
|
129
|
+
function minLength(requirement, message$1) {
|
|
130
|
+
return {
|
|
131
|
+
kind: "validation",
|
|
132
|
+
type: "min_length",
|
|
133
|
+
reference: minLength,
|
|
134
|
+
async: false,
|
|
135
|
+
expects: `>=${requirement}`,
|
|
136
|
+
requirement,
|
|
137
|
+
message: message$1,
|
|
138
|
+
"~run"(dataset, config$1) {
|
|
139
|
+
if (dataset.typed && dataset.value.length < this.requirement) _addIssue(this, "length", dataset, config$1, { received: `${dataset.value.length}` });
|
|
140
|
+
return dataset;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// @__NO_SIDE_EFFECTS__
|
|
97
145
|
function regex(requirement, message$1) {
|
|
98
146
|
return {
|
|
99
147
|
kind: "validation",
|
|
@@ -163,6 +211,23 @@ function array(item, message$1) {
|
|
|
163
211
|
});
|
|
164
212
|
}
|
|
165
213
|
// @__NO_SIDE_EFFECTS__
|
|
214
|
+
function instance(class_, message$1) {
|
|
215
|
+
return _standardSchema({
|
|
216
|
+
kind: "schema",
|
|
217
|
+
type: "instance",
|
|
218
|
+
reference: instance,
|
|
219
|
+
expects: class_.name,
|
|
220
|
+
async: false,
|
|
221
|
+
class: class_,
|
|
222
|
+
message: message$1,
|
|
223
|
+
"~run"(dataset, config$1) {
|
|
224
|
+
if (dataset.value instanceof this.class) dataset.typed = true;
|
|
225
|
+
else _addIssue(this, "type", dataset, config$1);
|
|
226
|
+
return dataset;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// @__NO_SIDE_EFFECTS__
|
|
166
231
|
function nullable(wrapped, default_) {
|
|
167
232
|
return _standardSchema({
|
|
168
233
|
kind: "schema",
|
|
@@ -383,6 +448,49 @@ function string(message$1) {
|
|
|
383
448
|
});
|
|
384
449
|
}
|
|
385
450
|
// @__NO_SIDE_EFFECTS__
|
|
451
|
+
function _subIssues(datasets) {
|
|
452
|
+
let issues;
|
|
453
|
+
if (datasets) for (const dataset of datasets) if (issues) for (const issue of dataset.issues) issues.push(issue);
|
|
454
|
+
else issues = dataset.issues;
|
|
455
|
+
return issues;
|
|
456
|
+
}
|
|
457
|
+
// @__NO_SIDE_EFFECTS__
|
|
458
|
+
function union(options, message$1) {
|
|
459
|
+
return _standardSchema({
|
|
460
|
+
kind: "schema",
|
|
461
|
+
type: "union",
|
|
462
|
+
reference: union,
|
|
463
|
+
expects: /* @__PURE__ */ _joinExpects(options.map((option) => option.expects), "|"),
|
|
464
|
+
async: false,
|
|
465
|
+
options,
|
|
466
|
+
message: message$1,
|
|
467
|
+
"~run"(dataset, config$1) {
|
|
468
|
+
let validDataset;
|
|
469
|
+
let typedDatasets;
|
|
470
|
+
let untypedDatasets;
|
|
471
|
+
for (const schema of this.options) {
|
|
472
|
+
const optionDataset = schema["~run"]({ value: dataset.value }, config$1);
|
|
473
|
+
if (optionDataset.typed) if (optionDataset.issues) if (typedDatasets) typedDatasets.push(optionDataset);
|
|
474
|
+
else typedDatasets = [optionDataset];
|
|
475
|
+
else {
|
|
476
|
+
validDataset = optionDataset;
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
else if (untypedDatasets) untypedDatasets.push(optionDataset);
|
|
480
|
+
else untypedDatasets = [optionDataset];
|
|
481
|
+
}
|
|
482
|
+
if (validDataset) return validDataset;
|
|
483
|
+
if (typedDatasets) {
|
|
484
|
+
if (typedDatasets.length === 1) return typedDatasets[0];
|
|
485
|
+
_addIssue(this, "type", dataset, config$1, { issues: /* @__PURE__ */ _subIssues(typedDatasets) });
|
|
486
|
+
dataset.typed = true;
|
|
487
|
+
} else if (untypedDatasets?.length === 1) return untypedDatasets[0];
|
|
488
|
+
else _addIssue(this, "type", dataset, config$1, { issues: /* @__PURE__ */ _subIssues(untypedDatasets) });
|
|
489
|
+
return dataset;
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
// @__NO_SIDE_EFFECTS__
|
|
386
494
|
function unknown() {
|
|
387
495
|
return _standardSchema({
|
|
388
496
|
kind: "schema",
|
|
@@ -436,4 +544,4 @@ function summarize(issues) {
|
|
|
436
544
|
return summary;
|
|
437
545
|
}
|
|
438
546
|
|
|
439
|
-
export { array, getDotPath, nullable, number, object, optional, picklist, pipe, record, regex, safeParse, string, summarize, unknown };
|
|
547
|
+
export { array, check, getDotPath, instance, maxLength, minLength, nullable, number, object, optional, picklist, pipe, record, regex, safeParse, string, summarize, union, unknown };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
2
|
+
|
|
3
|
+
// src/core/encryption/errors.ts
|
|
4
|
+
var EncryptionError = class extends SirannonError {
|
|
5
|
+
/**
|
|
6
|
+
* Returns an error with an encryption code, and the failure that caused it when there is one.
|
|
7
|
+
*
|
|
8
|
+
* @param message - What failed, written without the key.
|
|
9
|
+
* @param code - The machine-readable code.
|
|
10
|
+
* @param cause - The failure that caused this error, such as the error that a key function threw.
|
|
11
|
+
*/
|
|
12
|
+
constructor(message, code, cause) {
|
|
13
|
+
super(message, code);
|
|
14
|
+
this.name = "EncryptionError";
|
|
15
|
+
if (cause !== void 0) {
|
|
16
|
+
Object.defineProperty(this, "cause", { value: cause, enumerable: false, writable: true, configurable: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function keyRejected(path, cause) {
|
|
21
|
+
return new EncryptionError(
|
|
22
|
+
`The key failed the authentication check of the first page of the database ${path}. Either the key is wrong, or the file comes from another encryption scheme: the better-sqlite3 driver encrypts with AEGIS-256 and the Expo driver encrypts with SQLCipher 4, and neither opens the other's files`,
|
|
23
|
+
"ENCRYPTION_KEY_REJECTED",
|
|
24
|
+
cause
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function refuseEncryptedOpen(driverName, options) {
|
|
28
|
+
if (options?.encryption === void 0) return;
|
|
29
|
+
throw new EncryptionError(
|
|
30
|
+
`The ${driverName} driver opens SQLite through a library that Sirannon cannot replace with an encrypting build, so it cannot open an encrypted database`,
|
|
31
|
+
"ENCRYPTION_UNSUPPORTED"
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { EncryptionError, keyRejected, refuseEncryptedOpen };
|
|
@@ -20,11 +20,11 @@ function isBackupChainHead(value) {
|
|
|
20
20
|
}
|
|
21
21
|
function isBackupChainBase(value) {
|
|
22
22
|
const base = value;
|
|
23
|
-
return base?.kind === "full" && typeof base.chainId === "string" && typeof base.name === "string" && typeof base.runId === "string" && isWholeNumber(base.finishedAt) && isWholeNumber(base.pieceCount) && isAtLeastOne(base.pieceBytes) && isWholeNumber(base.bytesWritten) && isTextOrAbsent(base.fingerprint);
|
|
23
|
+
return base?.kind === "full" && typeof base.chainId === "string" && typeof base.name === "string" && typeof base.runId === "string" && isWholeNumber(base.finishedAt) && isWholeNumber(base.pieceCount) && isAtLeastOne(base.pieceBytes) && isWholeNumber(base.bytesWritten) && isTextOrAbsent(base.fingerprint) && isTextOrAbsent(base.keyId);
|
|
24
24
|
}
|
|
25
25
|
function isBackupChainChange(value) {
|
|
26
26
|
const change = value;
|
|
27
|
-
return change?.kind === "change" && typeof change.chainId === "string" && typeof change.name === "string" && typeof change.runId === "string" && isAtLeastOne(change.sequence) && isBackupChainPosition(change.position) && isWholeNumber(change.capturedAt) && isAtLeastOne(change.frameCount) && isWholeNumber(change.pieceCount) && isAtLeastOne(change.pieceBytes) && isWholeNumber(change.bytesWritten) && typeof change.checkpointed === "boolean" && isTextOrAbsent(change.fingerprint);
|
|
27
|
+
return change?.kind === "change" && typeof change.chainId === "string" && typeof change.name === "string" && typeof change.runId === "string" && isAtLeastOne(change.sequence) && isBackupChainPosition(change.position) && isWholeNumber(change.capturedAt) && isAtLeastOne(change.frameCount) && isWholeNumber(change.pieceCount) && isAtLeastOne(change.pieceBytes) && isWholeNumber(change.bytesWritten) && typeof change.checkpointed === "boolean" && isTextOrAbsent(change.fingerprint) && isTextOrAbsent(change.keyId);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// src/core/backup/chain.ts
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
2
2
|
|
|
3
3
|
// src/core/driver/define.ts
|
|
4
|
+
function boundFileSystem(files) {
|
|
5
|
+
return Object.freeze({
|
|
6
|
+
readStart: files.readStart.bind(files),
|
|
7
|
+
remove: files.remove.bind(files),
|
|
8
|
+
rename: files.rename.bind(files)
|
|
9
|
+
});
|
|
10
|
+
}
|
|
4
11
|
function defineDriver(config) {
|
|
5
12
|
if (!config.capabilities || typeof config.open !== "function") {
|
|
6
13
|
throw new SirannonError("Driver must define capabilities and open()", "INVALID_DRIVER");
|
|
@@ -9,22 +16,15 @@ function defineDriver(config) {
|
|
|
9
16
|
throw new SirannonError("Driver worker entry must define a string specifier", "INVALID_DRIVER");
|
|
10
17
|
}
|
|
11
18
|
return Object.freeze({
|
|
12
|
-
capabilities: Object.freeze({ ...config.capabilities }),
|
|
19
|
+
capabilities: Object.freeze({ ...config.capabilities, encryption: config.capabilities.encryption === true }),
|
|
13
20
|
open: config.open.bind(config),
|
|
14
21
|
...config.worker ? { worker: Object.freeze({ ...config.worker }) } : {},
|
|
15
22
|
...config.startWriterHost ? { startWriterHost: config.startWriterHost.bind(config) } : {},
|
|
16
23
|
...config.createWriterContext ? { createWriterContext: config.createWriterContext.bind(config) } : {},
|
|
17
24
|
...config.createBackupEngine ? { createBackupEngine: config.createBackupEngine.bind(config) } : {},
|
|
18
|
-
...config.resolveExtensionPath ? { resolveExtensionPath: config.resolveExtensionPath.bind(config) } : {}
|
|
25
|
+
...config.resolveExtensionPath ? { resolveExtensionPath: config.resolveExtensionPath.bind(config) } : {},
|
|
26
|
+
...config.files ? { files: boundFileSystem(config.files) } : {}
|
|
19
27
|
});
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
var DEFAULT_CAPTURE_INTERVAL_MS = 6e4;
|
|
24
|
-
var DEFAULT_FULL_COPY_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
25
|
-
var DEFAULT_BACKUP_NAME_PREFIX = "sirannon-backup";
|
|
26
|
-
function defaultStagingDir(sourcePath) {
|
|
27
|
-
return `${sourcePath}-backup`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export { DEFAULT_BACKUP_NAME_PREFIX, DEFAULT_CAPTURE_INTERVAL_MS, DEFAULT_FULL_COPY_INTERVAL_MS, defaultStagingDir, defineDriver };
|
|
30
|
+
export { defineDriver };
|
|
@@ -19,6 +19,7 @@ function registerPeer(connectedPeers, peerConnectedHandler, nodeId, role, metada
|
|
|
19
19
|
role,
|
|
20
20
|
primaryTerm: metadata.primaryTerm,
|
|
21
21
|
protocolVersion: metadata.protocolVersion,
|
|
22
|
+
encrypted: metadata.encrypted === true,
|
|
22
23
|
joinedAt: Date.now(),
|
|
23
24
|
lastSeenAt: Date.now(),
|
|
24
25
|
lastAckedSeq: 0n
|