@delali/sirannon-db 0.2.0 → 0.2.1
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/dist/backup-scheduler/index.d.ts +15 -1
- package/dist/backup-scheduler/index.mjs +2 -2
- package/dist/baseline-D93hcIEE.d.ts +17 -0
- package/dist/{change-tracker-DKRVUC3l.d.ts → change-tracker-DDmXB754.d.ts} +56 -8
- package/dist/{chunk-5NOIGN5Y.mjs → chunk-2QLXDHAP.mjs} +1 -1
- package/dist/{chunk-LNY2VVHE.mjs → chunk-7C36BCSN.mjs} +1 -1
- package/dist/{chunk-NVQS53NT.mjs → chunk-7FQRQH5Z.mjs} +53 -64
- package/dist/{chunk-D7LAYTKN.mjs → chunk-7R4ER4FB.mjs} +1 -1
- package/dist/{chunk-FHWTZFI4.mjs → chunk-BQFQ65OL.mjs} +1 -1
- package/dist/{chunk-O7SLN3GI.mjs → chunk-BTTFW4Z4.mjs} +1 -1
- package/dist/{chunk-H6PIVVDN.mjs → chunk-CCZK6LCB.mjs} +38 -25
- package/dist/{chunk-JZGINXTN.mjs → chunk-HCCGEIZ2.mjs} +2 -2
- package/dist/{chunk-67M7KAH6.mjs → chunk-IWGIYDMZ.mjs} +1 -1
- package/dist/{chunk-LFZ37BSX.mjs → chunk-OUSWVNWT.mjs} +1 -1
- package/dist/{chunk-HR5CWTLC.mjs → chunk-P2VJYRVY.mjs} +60 -7
- package/dist/{chunk-UC3SCMIN.mjs → chunk-PBRXXISQ.mjs} +3 -0
- package/dist/{chunk-JU64Y7HM.mjs → chunk-SBL6GN43.mjs} +1 -1
- package/dist/{chunk-EBJXPQQO.mjs → chunk-UPKKSUPA.mjs} +2 -2
- package/dist/{chunk-TJF5GZSV.mjs → chunk-VOSJBZ6Q.mjs} +1 -1
- package/dist/{chunk-PIKHN33N.mjs → chunk-VOYGMAU7.mjs} +9 -1
- package/dist/{chunk-H237TXZW.mjs → chunk-WJ67DTD6.mjs} +48 -6
- package/dist/{chunk-OQVZBEBY.mjs → chunk-XF2HH5E6.mjs} +4 -61
- package/dist/client/index.d.ts +211 -12
- package/dist/client/index.mjs +155 -67
- package/dist/client/topology.d.ts +55 -7
- package/dist/client/topology.mjs +20 -1
- package/dist/{client-base-CLWmH5Ln.d.ts → client-base-CmZO0v3m.d.ts} +133 -24
- package/dist/codegen/cli.mjs +3 -3
- package/dist/codegen/index.d.ts +92 -2
- package/dist/codegen/index.mjs +3 -3
- package/dist/core/index.d.ts +177 -15
- package/dist/core/index.mjs +2481 -2226
- package/dist/core/writer-worker.mjs +3 -3
- package/dist/database-B5Qv1-cU.d.ts +380 -0
- package/dist/driver/better-sqlite3.d.ts +18 -1
- package/dist/driver/better-sqlite3.mjs +5 -5
- package/dist/driver/bun.d.ts +28 -0
- package/dist/driver/expo.d.ts +17 -0
- package/dist/driver/node.d.ts +18 -1
- package/dist/driver/node.mjs +5 -5
- package/dist/driver/wa-sqlite.d.ts +18 -1
- package/dist/{errors-Bw5MdNCu.d.ts → errors-Dei4GdBb.d.ts} +80 -7
- package/dist/file-migrations/index.d.ts +54 -2
- package/dist/file-migrations/index.mjs +3 -3
- package/dist/{operation-registry-9DcvxcE5.d.ts → operation-registry-hlbhqu7q.d.ts} +50 -1
- package/dist/{primary-wins-DPAm2AKG.d.ts → primary-wins-B0np8JS3.d.ts} +25 -1
- package/dist/protocol-rqANt-9Q.d.ts +152 -0
- package/dist/query-types-DL3LtPvY.d.ts +95 -0
- package/dist/react/index.d.ts +58 -3
- package/dist/replication/coordinator/etcd.d.ts +63 -3
- package/dist/replication/coordinator/etcd.mjs +82 -46
- package/dist/replication/index.d.ts +329 -95
- package/dist/replication/index.mjs +256 -141
- package/dist/server/index.d.ts +230 -12
- package/dist/server/index.mjs +63 -50
- package/dist/{server-options-1JHu8pid.d.ts → server-options-Dab_Jvd_.d.ts} +96 -12
- package/dist/sirannon-CMhiJa5Y.d.ts +111 -0
- package/dist/transport/grpc.d.ts +93 -9
- package/dist/transport/grpc.mjs +63 -20
- package/dist/transport/memory.d.ts +50 -20
- package/dist/transport/memory.mjs +25 -0
- package/dist/{types-CL6piSnD.d.ts → types-BCejqzNA.d.ts} +20 -0
- package/dist/types-CMBcFPhb.d.ts +336 -0
- package/dist/types-CjhxcjhA.d.ts +123 -0
- package/dist/types-DyrCiWuc.d.ts +499 -0
- package/dist/types-rVZKnKN-.d.ts +591 -0
- package/package.json +7 -1
- package/dist/baseline-Br77Fnhb.d.ts +0 -6
- package/dist/database-BY0L5Q2n.d.ts +0 -172
- package/dist/protocol-6KrSq2Hy.d.ts +0 -66
- package/dist/sirannon-DaQSyhbJ.d.ts +0 -36
- package/dist/types-B7gmEsZW.d.ts +0 -221
- package/dist/types-BsVabqSI.d.ts +0 -139
- package/dist/types-C_D8IhpO.d.ts +0 -60
- package/dist/types-zhnRXrsb.d.ts +0 -384
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { e as SQLiteConnection, f as BackupScheduleOptions } from '../types-
|
|
1
|
+
import { e as SQLiteConnection, f as BackupScheduleOptions } from '../types-rVZKnKN-.js';
|
|
2
|
+
import '../query-types-DL3LtPvY.js';
|
|
2
3
|
|
|
3
4
|
declare class BackupManager {
|
|
4
5
|
backup(conn: SQLiteConnection, destPath: string): Promise<void>;
|
|
@@ -8,9 +9,22 @@ declare class BackupManager {
|
|
|
8
9
|
|
|
9
10
|
/** Runs a backup while the caller's writer lock is held, so it never shares the writer connection with another write. */
|
|
10
11
|
type RunExclusive = (op: () => Promise<void>) => Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* @public
|
|
14
|
+
*
|
|
15
|
+
* Repeats a database backup on a cron schedule and keeps a bounded number of files.
|
|
16
|
+
*/
|
|
11
17
|
declare class BackupScheduler {
|
|
12
18
|
private readonly manager;
|
|
13
19
|
constructor(manager?: BackupManager);
|
|
20
|
+
/**
|
|
21
|
+
* Starts repeating backups and returns a function that stops them.
|
|
22
|
+
*
|
|
23
|
+
* @param conn - Connection to the database being copied.
|
|
24
|
+
* @param options - Cron expression, destination directory, retention, time zone, and failure callback.
|
|
25
|
+
* @param runExclusive - Runs each copy with the database's writer held so that no write commits mid-copy.
|
|
26
|
+
* @returns A function that stops the schedule.
|
|
27
|
+
*/
|
|
14
28
|
schedule(conn: SQLiteConnection, options: BackupScheduleOptions, runExclusive?: RunExclusive): () => void;
|
|
15
29
|
private run;
|
|
16
30
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { BackupScheduler } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { BackupScheduler } from '../chunk-VOYGMAU7.mjs';
|
|
2
|
+
import '../chunk-PBRXXISQ.mjs';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marks the migration an existing database starts from, so earlier versions are recorded without running.
|
|
3
|
+
*
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
interface BaselineFileOption {
|
|
7
|
+
/**
|
|
8
|
+
* Version of the migration carrying the baseline.
|
|
9
|
+
*/
|
|
10
|
+
version: number;
|
|
11
|
+
/**
|
|
12
|
+
* Highest version this baseline covers.
|
|
13
|
+
*/
|
|
14
|
+
through: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type { BaselineFileOption as B };
|
|
@@ -1,11 +1,33 @@
|
|
|
1
|
-
import { e as SQLiteConnection
|
|
1
|
+
import { e as SQLiteConnection } from './types-rVZKnKN-.js';
|
|
2
|
+
import { C as ChangeEvent } from './query-types-DL3LtPvY.js';
|
|
2
3
|
|
|
4
|
+
/**
|
|
5
|
+
* How long a change tracker keeps changes, and how much it reads at a time.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
3
9
|
interface ChangeTrackerOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Milliseconds a change stays readable before it is pruned. Default: 3_600_000 (one hour).
|
|
12
|
+
*/
|
|
4
13
|
retention?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Name of the table changes are recorded in.
|
|
16
|
+
*/
|
|
5
17
|
changesTable?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Changes read in one poll. Default: 1000.
|
|
20
|
+
*/
|
|
6
21
|
pollBatchSize?: number;
|
|
7
22
|
}
|
|
8
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Records every change to the tables you watch so that subscribers and replication can read them in order.
|
|
26
|
+
*
|
|
27
|
+
* Construct one, watch the tables you want captured, and pass it to a replication engine or leave it to the database that owns it.
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
9
31
|
declare class ChangeTracker {
|
|
10
32
|
private readonly watched;
|
|
11
33
|
private lastSeq;
|
|
@@ -19,7 +41,19 @@ declare class ChangeTracker {
|
|
|
19
41
|
private pruneBoundary;
|
|
20
42
|
private lastPollAtTxBoundary;
|
|
21
43
|
constructor(options?: ChangeTrackerOptions);
|
|
44
|
+
/**
|
|
45
|
+
* Starts recording changes to a table by installing its change-capture triggers.
|
|
46
|
+
*
|
|
47
|
+
* @param conn - Writer connection the triggers are created on.
|
|
48
|
+
* @param table - Name of the table to watch.
|
|
49
|
+
*/
|
|
22
50
|
watch(conn: SQLiteConnection, table: string): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Stops recording changes to a table and drops its triggers.
|
|
53
|
+
*
|
|
54
|
+
* @param conn - Writer connection the triggers are dropped from.
|
|
55
|
+
* @param table - Name of the table to stop watching.
|
|
56
|
+
*/
|
|
23
57
|
unwatch(conn: SQLiteConnection, table: string): Promise<void>;
|
|
24
58
|
/**
|
|
25
59
|
* Rebuilds CDC triggers for every watched table directly on the supplied
|
|
@@ -45,6 +79,8 @@ declare class ChangeTracker {
|
|
|
45
79
|
* `conn`. The cached column list is updated on success.
|
|
46
80
|
* - Any other error (driver failure, identifier validation failure) is
|
|
47
81
|
* rethrown so the caller's transaction can roll back deterministically.
|
|
82
|
+
*
|
|
83
|
+
* @internal
|
|
48
84
|
*/
|
|
49
85
|
refreshAllTriggersUsingConnection(conn: SQLiteConnection): Promise<void>;
|
|
50
86
|
/**
|
|
@@ -66,25 +102,37 @@ declare class ChangeTracker {
|
|
|
66
102
|
* re-installed triggers compiled against the new schema. Those triggers
|
|
67
103
|
* are dropped here so the recreated table starts with a clean slate and
|
|
68
104
|
* the caller must explicitly `watch` it again.
|
|
105
|
+
*
|
|
106
|
+
* @internal
|
|
69
107
|
*/
|
|
70
108
|
pruneDroppedTables(conn: SQLiteConnection, tables: readonly string[]): Promise<void>;
|
|
109
|
+
/** @internal */
|
|
71
110
|
poll(conn: SQLiteConnection): Promise<ChangeEvent[]>;
|
|
111
|
+
/** @internal */
|
|
72
112
|
get pollEndedAtTxBoundary(): boolean;
|
|
73
|
-
/** The highest seq already polled; live subscribers receive events beyond it. */
|
|
74
|
-
get cursor(): bigint;
|
|
75
113
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
114
|
+
* The highest seq already polled; live subscribers receive events beyond it.
|
|
115
|
+
*
|
|
116
|
+
* @internal
|
|
79
117
|
*/
|
|
80
|
-
|
|
118
|
+
get cursor(): bigint;
|
|
119
|
+
/** @internal */
|
|
81
120
|
readSinceTables(conn: SQLiteConnection, tables: readonly string[], afterSeq: bigint, upToSeq: bigint, limit: number): Promise<ChangeEvent[]>;
|
|
82
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* The lowest retained seq, or `null` when the change log is empty.
|
|
123
|
+
*
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
83
126
|
getMinSeq(conn: SQLiteConnection): Promise<bigint | null>;
|
|
127
|
+
/** @internal */
|
|
84
128
|
advanceToLatest(conn: SQLiteConnection): Promise<void>;
|
|
129
|
+
/** @internal */
|
|
85
130
|
cleanup(conn: SQLiteConnection): Promise<number>;
|
|
131
|
+
/** @internal */
|
|
86
132
|
setPruneBoundary(seq: bigint): void;
|
|
133
|
+
/** @internal */
|
|
87
134
|
clearPruneBoundary(): void;
|
|
135
|
+
/** @internal */
|
|
88
136
|
get watchedTables(): ReadonlySet<string>;
|
|
89
137
|
private computeSeqBound;
|
|
90
138
|
private assertIdentifier;
|
|
@@ -1,56 +1,5 @@
|
|
|
1
|
-
import { META_TABLE, CHANGES_TABLE,
|
|
2
|
-
import {
|
|
3
|
-
import { SirannonError } from './chunk-UC3SCMIN.mjs';
|
|
4
|
-
|
|
5
|
-
// src/core/system-catalog/applied-changes-table.ts
|
|
6
|
-
function toSeq(value) {
|
|
7
|
-
if (value === null || value === void 0) return 0n;
|
|
8
|
-
return typeof value === "bigint" ? value : BigInt(String(value));
|
|
9
|
-
}
|
|
10
|
-
async function ensureAppliedChangesTable(conn) {
|
|
11
|
-
await conn.exec(`
|
|
12
|
-
CREATE TABLE IF NOT EXISTS "${APPLIED_CHANGES_TABLE}" (
|
|
13
|
-
source_node_id TEXT NOT NULL,
|
|
14
|
-
source_seq INTEGER NOT NULL,
|
|
15
|
-
applied_at REAL NOT NULL,
|
|
16
|
-
PRIMARY KEY (source_node_id, source_seq)
|
|
17
|
-
)`);
|
|
18
|
-
}
|
|
19
|
-
function prepareInsertAppliedChange(conn) {
|
|
20
|
-
return conn.prepare(
|
|
21
|
-
`INSERT OR IGNORE INTO ${APPLIED_CHANGES_TABLE} (source_node_id, source_seq, applied_at) VALUES (?, ?, ?)`
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
async function selectMaxAppliedSourceSeq(conn, sourceNodeId) {
|
|
25
|
-
const stmt = await conn.prepare(
|
|
26
|
-
`SELECT MAX(source_seq) AS max_seq FROM ${APPLIED_CHANGES_TABLE} WHERE source_node_id = ?`
|
|
27
|
-
);
|
|
28
|
-
const row = await stmt.get(sourceNodeId);
|
|
29
|
-
return toSeq(row?.max_seq);
|
|
30
|
-
}
|
|
31
|
-
async function selectMaxAppliedSourceSeqByNode(conn) {
|
|
32
|
-
const stmt = await conn.prepare(
|
|
33
|
-
`SELECT source_node_id, MAX(source_seq) AS max_seq FROM ${APPLIED_CHANGES_TABLE} GROUP BY source_node_id`
|
|
34
|
-
);
|
|
35
|
-
const rows = await stmt.all();
|
|
36
|
-
const byNode = /* @__PURE__ */ new Map();
|
|
37
|
-
for (const row of rows) {
|
|
38
|
-
if (row.max_seq === null || row.max_seq === void 0) continue;
|
|
39
|
-
byNode.set(row.source_node_id, toSeq(row.max_seq));
|
|
40
|
-
}
|
|
41
|
-
return byNode;
|
|
42
|
-
}
|
|
43
|
-
async function selectAppliedSourceSeqsInRange(conn, sourceNodeId, fromSeq, toSeqBound) {
|
|
44
|
-
const stmt = await conn.prepare(
|
|
45
|
-
`SELECT source_seq FROM ${APPLIED_CHANGES_TABLE} WHERE source_node_id = ? AND source_seq >= ? AND source_seq <= ?`
|
|
46
|
-
);
|
|
47
|
-
const rows = await stmt.all(sourceNodeId, fromSeq.toString(), toSeqBound.toString());
|
|
48
|
-
const seqs = /* @__PURE__ */ new Set();
|
|
49
|
-
for (const row of rows) {
|
|
50
|
-
seqs.add(String(row.source_seq));
|
|
51
|
-
}
|
|
52
|
-
return seqs;
|
|
53
|
-
}
|
|
1
|
+
import { META_TABLE, CHANGES_TABLE, MIGRATIONS_TABLE, APPLIED_CHANGES_TABLE, COLUMN_VERSIONS_TABLE, DEVICE_CURSORS_TABLE, PEER_STATE_TABLE, SYNC_STATE_TABLE, STAGED_CHANGES_TABLE } from './chunk-7R4ER4FB.mjs';
|
|
2
|
+
import { SirannonError } from './chunk-PBRXXISQ.mjs';
|
|
54
3
|
|
|
55
4
|
// src/core/system-catalog/sqlite-catalog.ts
|
|
56
5
|
async function selectTableExists(conn, table) {
|
|
@@ -167,11 +116,6 @@ var DDL_CHANGE_ROW_TABLE = "__ddl__";
|
|
|
167
116
|
function selectChangesAfterSeqSql(tableName) {
|
|
168
117
|
return `SELECT ${CHANGE_LOG_COLUMNS} FROM "${tableName}" WHERE seq > ? ORDER BY seq ASC LIMIT ?`;
|
|
169
118
|
}
|
|
170
|
-
function selectTableChangesInRangeSql(tableName) {
|
|
171
|
-
return `SELECT ${CHANGE_LOG_COLUMNS} FROM "${tableName}"
|
|
172
|
-
WHERE table_name = ? AND seq > ? AND seq <= ?
|
|
173
|
-
ORDER BY seq ASC LIMIT ?`;
|
|
174
|
-
}
|
|
175
119
|
function selectTablesChangesInRangeSql(tableName, tableCount) {
|
|
176
120
|
const placeholders = Array.from({ length: tableCount }, () => "?").join(", ");
|
|
177
121
|
return `SELECT ${CHANGE_LOG_COLUMNS} FROM "${tableName}"
|
|
@@ -312,6 +256,56 @@ function highestMigrationVersion(rows) {
|
|
|
312
256
|
return highest;
|
|
313
257
|
}
|
|
314
258
|
|
|
259
|
+
// src/core/system-catalog/applied-changes-table.ts
|
|
260
|
+
function toSeq(value) {
|
|
261
|
+
if (value === null || value === void 0) return 0n;
|
|
262
|
+
return typeof value === "bigint" ? value : BigInt(String(value));
|
|
263
|
+
}
|
|
264
|
+
async function ensureAppliedChangesTable(conn) {
|
|
265
|
+
await conn.exec(`
|
|
266
|
+
CREATE TABLE IF NOT EXISTS "${APPLIED_CHANGES_TABLE}" (
|
|
267
|
+
source_node_id TEXT NOT NULL,
|
|
268
|
+
source_seq INTEGER NOT NULL,
|
|
269
|
+
applied_at REAL NOT NULL,
|
|
270
|
+
PRIMARY KEY (source_node_id, source_seq)
|
|
271
|
+
)`);
|
|
272
|
+
}
|
|
273
|
+
function prepareInsertAppliedChange(conn) {
|
|
274
|
+
return conn.prepare(
|
|
275
|
+
`INSERT OR IGNORE INTO ${APPLIED_CHANGES_TABLE} (source_node_id, source_seq, applied_at) VALUES (?, ?, ?)`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
async function selectMaxAppliedSourceSeq(conn, sourceNodeId) {
|
|
279
|
+
const stmt = await conn.prepare(
|
|
280
|
+
`SELECT MAX(source_seq) AS max_seq FROM ${APPLIED_CHANGES_TABLE} WHERE source_node_id = ?`
|
|
281
|
+
);
|
|
282
|
+
const row = await stmt.get(sourceNodeId);
|
|
283
|
+
return toSeq(row?.max_seq);
|
|
284
|
+
}
|
|
285
|
+
async function selectMaxAppliedSourceSeqByNode(conn) {
|
|
286
|
+
const stmt = await conn.prepare(
|
|
287
|
+
`SELECT source_node_id, MAX(source_seq) AS max_seq FROM ${APPLIED_CHANGES_TABLE} GROUP BY source_node_id`
|
|
288
|
+
);
|
|
289
|
+
const rows = await stmt.all();
|
|
290
|
+
const byNode = /* @__PURE__ */ new Map();
|
|
291
|
+
for (const row of rows) {
|
|
292
|
+
if (row.max_seq === null || row.max_seq === void 0) continue;
|
|
293
|
+
byNode.set(row.source_node_id, toSeq(row.max_seq));
|
|
294
|
+
}
|
|
295
|
+
return byNode;
|
|
296
|
+
}
|
|
297
|
+
async function selectAppliedSourceSeqsInRange(conn, sourceNodeId, fromSeq, toSeqBound) {
|
|
298
|
+
const stmt = await conn.prepare(
|
|
299
|
+
`SELECT source_seq FROM ${APPLIED_CHANGES_TABLE} WHERE source_node_id = ? AND source_seq >= ? AND source_seq <= ?`
|
|
300
|
+
);
|
|
301
|
+
const rows = await stmt.all(sourceNodeId, fromSeq.toString(), toSeqBound.toString());
|
|
302
|
+
const seqs = /* @__PURE__ */ new Set();
|
|
303
|
+
for (const row of rows) {
|
|
304
|
+
seqs.add(String(row.source_seq));
|
|
305
|
+
}
|
|
306
|
+
return seqs;
|
|
307
|
+
}
|
|
308
|
+
|
|
315
309
|
// src/core/system-catalog/column-versions-table.ts
|
|
316
310
|
var UPSERT_COLUMN_VERSION = `INSERT INTO ${COLUMN_VERSIONS_TABLE} (table_name, row_id, column_name, hlc, node_id)
|
|
317
311
|
VALUES (?, ?, ?, ?, ?)
|
|
@@ -634,11 +628,6 @@ var PkResolver = class {
|
|
|
634
628
|
}
|
|
635
629
|
};
|
|
636
630
|
|
|
637
|
-
// src/core/sync/checksum.ts
|
|
638
|
-
function computeChecksum(changes) {
|
|
639
|
-
return sha256Hex(canonicaliseForChecksum(changes));
|
|
640
|
-
}
|
|
641
|
-
|
|
642
631
|
// src/core/sync/validators.ts
|
|
643
632
|
var IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
644
633
|
var SEQ_STRING_RE = /^\d{1,19}$/;
|
|
@@ -662,4 +651,4 @@ function extractDroppedTable(sql) {
|
|
|
662
651
|
return m?.[1] ?? null;
|
|
663
652
|
}
|
|
664
653
|
|
|
665
|
-
export { IDENTIFIER_RE, PkResolver, SEQ_STRING_RE, UPSERT_META_VALUE_SQL, assertSafeIdentifier, canonicaliseForChecksum,
|
|
654
|
+
export { IDENTIFIER_RE, PkResolver, SEQ_STRING_RE, UPSERT_META_VALUE_SQL, assertSafeIdentifier, canonicaliseForChecksum, deleteAllStagedChanges, deleteChangesBeforeSql, deleteChangesBeforeUpToSeqSql, deleteDeviceCursorsUpdatedBefore, deleteMetaValue, deleteStagedChangesUpToSeq, deleteSyncTableStates, ensureBatchApplyTables, ensureChangesTable, ensureDeviceCursorsTable, ensureMetaTable, ensureMigrationsTable, ensureReplicationStateTables, ensureStagedChangesTable, extractDroppedTable, highestMigrationVersion, insertDdlChange, insertMetaValueIfAbsent, prepareDeleteMigration, prepareDeleteRowColumnVersions, prepareDeleteRowColumnVersionsUpToHlc, prepareInsertAppliedChange, prepareInsertMigration, prepareUpdateMigrationChecksum, prepareUpsertColumnVersion, prepareUpsertNewerColumnVersion, referencedTables, replaceMigrationHistory, selectAppliedMigrations, selectAppliedMigrationsNewestFirst, selectAppliedSourceSeqsInRange, selectChangesAfterSeqSql, selectCompletedSyncTableNames, selectCountOutboundChanges, selectCountTableRows, selectDeviceCursors, selectFirstStagedTransactionEnd, selectMaxAppliedSourceSeq, selectMaxAppliedSourceSeqByNode, selectMaxChangeHlc, selectMaxChangeSeq, selectMaxColumnVersionHlc, selectMaxColumnVersionHlcForRow, selectMaxRowChangeHlc, selectMaxStagedSeq, selectMetaValue, selectMinChangeSeqSql, selectMinForeignChangeSeqSql, selectMinPeerAckedSeq, selectNodeChangesAfterSeqSql, selectOutboundChangesSql, selectPeerAckedSeq, selectStagedChangesInRange, selectSyncMetaRow, selectTableExists, selectTableSql, selectTablesChangesInRangeSql, selectUserSchemaObjects, selectUserTableNames, setForeignKeysEnabled, tableColumnNames, tableInfoRows, tablePkColumns, updateChangeStampsAfterSeqSql, updateUnstampedChangeStampsSql, upsertDeviceCursor, upsertMetaValue, upsertPeerAckedSeq, upsertStagedChangeSql, upsertSyncMeta, upsertSyncTableStatus, validateDdlSafety, validateIdentifier };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { tableColumnNames, tablePkColumns, selectMinChangeSeqSql, selectMaxChangeSeq, deleteChangesBeforeUpToSeqSql, deleteChangesBeforeSql, selectTableExists, ensureChangesTable, ensureMetaTable, insertMetaValueIfAbsent, selectMetaValue, selectChangesAfterSeqSql,
|
|
2
|
-
import { CHANGES_TABLE, isReservedIdentifier, CDC_TRIGGER_PREFIX, encodeTaggedValues, decodeTaggedValues, SAFE_INT_BOUND_TEXT } from './chunk-
|
|
3
|
-
import { synchronousPragmaValue } from './chunk-
|
|
4
|
-
import { SirannonError, ForbiddenSqlError, CDCError, MigrationError } from './chunk-
|
|
1
|
+
import { tableColumnNames, tablePkColumns, selectMinChangeSeqSql, selectMaxChangeSeq, deleteChangesBeforeUpToSeqSql, deleteChangesBeforeSql, selectTableExists, ensureChangesTable, ensureMetaTable, insertMetaValueIfAbsent, selectMetaValue, selectChangesAfterSeqSql, selectTablesChangesInRangeSql } from './chunk-7FQRQH5Z.mjs';
|
|
2
|
+
import { CHANGES_TABLE, isReservedIdentifier, CDC_TRIGGER_PREFIX, encodeTaggedValues, decodeTaggedValues, SAFE_INT_BOUND_TEXT } from './chunk-7R4ER4FB.mjs';
|
|
3
|
+
import { synchronousPragmaValue } from './chunk-OUSWVNWT.mjs';
|
|
4
|
+
import { SirannonError, ForbiddenSqlError, CDCError, MigrationError } from './chunk-PBRXXISQ.mjs';
|
|
5
5
|
|
|
6
6
|
// src/core/bulk-load.ts
|
|
7
7
|
var DEFAULT_LOAD_DURABILITY = "off";
|
|
@@ -107,11 +107,6 @@ async function pollChanges(conn, cache, changesTable, afterSeq, batchSize) {
|
|
|
107
107
|
}
|
|
108
108
|
return { events, lastSeq, atTxBoundary: false };
|
|
109
109
|
}
|
|
110
|
-
async function readSinceOneTable(conn, cache, changesTable, table, afterSeq, upToSeq, limit) {
|
|
111
|
-
const stmt = await cache.get(conn, "read_since", selectTableChangesInRangeSql(changesTable));
|
|
112
|
-
const rows = await stmt.all(table, afterSeq.toString(), upToSeq.toString(), limit);
|
|
113
|
-
return rows.map(rowToEvent);
|
|
114
|
-
}
|
|
115
110
|
async function readSinceTables(conn, cache, changesTable, tables, afterSeq, upToSeq, limit) {
|
|
116
111
|
if (tables.length === 0) return [];
|
|
117
112
|
const stmt = await cache.get(
|
|
@@ -228,6 +223,12 @@ var ChangeTracker = class {
|
|
|
228
223
|
this.pollBatchSize = options?.pollBatchSize ?? DEFAULT_POLL_BATCH_SIZE;
|
|
229
224
|
this.assertIdentifier(this.changesTable, "changes table name");
|
|
230
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Starts recording changes to a table by installing its change-capture triggers.
|
|
228
|
+
*
|
|
229
|
+
* @param conn - Writer connection the triggers are created on.
|
|
230
|
+
* @param table - Name of the table to watch.
|
|
231
|
+
*/
|
|
231
232
|
async watch(conn, table) {
|
|
232
233
|
this.assertIdentifier(table, "table name");
|
|
233
234
|
if (isReservedIdentifier(table)) {
|
|
@@ -258,6 +259,12 @@ var ChangeTracker = class {
|
|
|
258
259
|
this.watched.set(table, { table, columns, pkColumns });
|
|
259
260
|
this.watchedTablesCache = null;
|
|
260
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Stops recording changes to a table and drops its triggers.
|
|
264
|
+
*
|
|
265
|
+
* @param conn - Writer connection the triggers are dropped from.
|
|
266
|
+
* @param table - Name of the table to stop watching.
|
|
267
|
+
*/
|
|
261
268
|
async unwatch(conn, table) {
|
|
262
269
|
if (!this.watched.has(table)) {
|
|
263
270
|
return;
|
|
@@ -290,6 +297,8 @@ var ChangeTracker = class {
|
|
|
290
297
|
* `conn`. The cached column list is updated on success.
|
|
291
298
|
* - Any other error (driver failure, identifier validation failure) is
|
|
292
299
|
* rethrown so the caller's transaction can roll back deterministically.
|
|
300
|
+
*
|
|
301
|
+
* @internal
|
|
293
302
|
*/
|
|
294
303
|
async refreshAllTriggersUsingConnection(conn) {
|
|
295
304
|
const tables = Array.from(this.watched.keys());
|
|
@@ -337,6 +346,8 @@ var ChangeTracker = class {
|
|
|
337
346
|
* re-installed triggers compiled against the new schema. Those triggers
|
|
338
347
|
* are dropped here so the recreated table starts with a clean slate and
|
|
339
348
|
* the caller must explicitly `watch` it again.
|
|
349
|
+
*
|
|
350
|
+
* @internal
|
|
340
351
|
*/
|
|
341
352
|
async pruneDroppedTables(conn, tables) {
|
|
342
353
|
let mutated = false;
|
|
@@ -352,6 +363,7 @@ var ChangeTracker = class {
|
|
|
352
363
|
this.watchedTablesCache = null;
|
|
353
364
|
}
|
|
354
365
|
}
|
|
366
|
+
/** @internal */
|
|
355
367
|
async poll(conn) {
|
|
356
368
|
if (!this.changesTableReady) {
|
|
357
369
|
await this.detectChangesTable(conn);
|
|
@@ -368,27 +380,19 @@ var ChangeTracker = class {
|
|
|
368
380
|
this.lastSeq = result.lastSeq;
|
|
369
381
|
return result.events;
|
|
370
382
|
}
|
|
383
|
+
/** @internal */
|
|
371
384
|
get pollEndedAtTxBoundary() {
|
|
372
385
|
return this.lastPollAtTxBoundary;
|
|
373
386
|
}
|
|
374
|
-
/** The highest seq already polled; live subscribers receive events beyond it. */
|
|
375
|
-
get cursor() {
|
|
376
|
-
return this.lastSeq;
|
|
377
|
-
}
|
|
378
387
|
/**
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
388
|
+
* The highest seq already polled; live subscribers receive events beyond it.
|
|
389
|
+
*
|
|
390
|
+
* @internal
|
|
382
391
|
*/
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
await this.detectChangesTable(conn);
|
|
386
|
-
if (!this.changesTableReady) {
|
|
387
|
-
return [];
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
return readSinceOneTable(conn, this.stmtCache, this.changesTable, table, afterSeq, upToSeq, limit);
|
|
392
|
+
get cursor() {
|
|
393
|
+
return this.lastSeq;
|
|
391
394
|
}
|
|
395
|
+
/** @internal */
|
|
392
396
|
async readSinceTables(conn, tables, afterSeq, upToSeq, limit) {
|
|
393
397
|
if (tables.length === 0) return [];
|
|
394
398
|
if (!this.changesTableReady) {
|
|
@@ -399,7 +403,11 @@ var ChangeTracker = class {
|
|
|
399
403
|
}
|
|
400
404
|
return readSinceTables(conn, this.stmtCache, this.changesTable, tables, afterSeq, upToSeq, limit);
|
|
401
405
|
}
|
|
402
|
-
/**
|
|
406
|
+
/**
|
|
407
|
+
* The lowest retained seq, or `null` when the change log is empty.
|
|
408
|
+
*
|
|
409
|
+
* @internal
|
|
410
|
+
*/
|
|
403
411
|
async getMinSeq(conn) {
|
|
404
412
|
if (!this.changesTableReady) {
|
|
405
413
|
await this.detectChangesTable(conn);
|
|
@@ -415,6 +423,7 @@ var ChangeTracker = class {
|
|
|
415
423
|
}
|
|
416
424
|
return typeof seq === "bigint" ? seq : BigInt(String(seq));
|
|
417
425
|
}
|
|
426
|
+
/** @internal */
|
|
418
427
|
async advanceToLatest(conn) {
|
|
419
428
|
if (!this.changesTableReady) {
|
|
420
429
|
await this.detectChangesTable(conn);
|
|
@@ -427,6 +436,7 @@ var ChangeTracker = class {
|
|
|
427
436
|
this.lastSeq = latestSeq;
|
|
428
437
|
}
|
|
429
438
|
}
|
|
439
|
+
/** @internal */
|
|
430
440
|
async cleanup(conn) {
|
|
431
441
|
if (!this.changesTableReady) {
|
|
432
442
|
await this.detectChangesTable(conn);
|
|
@@ -449,12 +459,15 @@ var ChangeTracker = class {
|
|
|
449
459
|
const result = await stmt.run(cutoff);
|
|
450
460
|
return result.changes;
|
|
451
461
|
}
|
|
462
|
+
/** @internal */
|
|
452
463
|
setPruneBoundary(seq) {
|
|
453
464
|
this.pruneBoundary = seq;
|
|
454
465
|
}
|
|
466
|
+
/** @internal */
|
|
455
467
|
clearPruneBoundary() {
|
|
456
468
|
this.pruneBoundary = null;
|
|
457
469
|
}
|
|
470
|
+
/** @internal */
|
|
458
471
|
get watchedTables() {
|
|
459
472
|
if (!this.watchedTablesCache) {
|
|
460
473
|
this.watchedTablesCache = new Set(this.watched.keys());
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { assertSqlAllowed, encodeWireRowsInPlace } from './chunk-
|
|
2
|
-
import { SirannonError, QueryError } from './chunk-
|
|
1
|
+
import { assertSqlAllowed, encodeWireRowsInPlace } from './chunk-7R4ER4FB.mjs';
|
|
2
|
+
import { SirannonError, QueryError } from './chunk-PBRXXISQ.mjs';
|
|
3
3
|
|
|
4
4
|
// src/core/query-executor.ts
|
|
5
5
|
var STATEMENT_CACHE_CAPACITY = 128;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { query, execute, executeBatch } from './chunk-
|
|
2
|
-
import { ensureMetaTable, selectMetaValue,
|
|
3
|
-
import { ReplicationError, BatchValidationError } from './chunk-
|
|
4
|
-
import { CHANGES_TABLE, decodeTaggedValues } from './chunk-
|
|
1
|
+
import { query, execute, executeBatch } from './chunk-HCCGEIZ2.mjs';
|
|
2
|
+
import { ensureMetaTable, selectMetaValue, IDENTIFIER_RE, selectAppliedSourceSeqsInRange, prepareInsertAppliedChange, validateDdlSafety, extractDroppedTable, selectMaxChangeSeq, updateChangeStampsAfterSeqSql, selectOutboundChangesSql, selectNodeChangesAfterSeqSql, prepareDeleteRowColumnVersionsUpToHlc, canonicaliseForChecksum, prepareUpsertNewerColumnVersion, upsertMetaValue, selectMaxColumnVersionHlcForRow, selectMaxRowChangeHlc, validateIdentifier, prepareDeleteRowColumnVersions, prepareUpsertColumnVersion } from './chunk-7FQRQH5Z.mjs';
|
|
3
|
+
import { ReplicationError, BatchValidationError } from './chunk-2QLXDHAP.mjs';
|
|
4
|
+
import { CHANGES_TABLE, decodeTaggedValues } from './chunk-7R4ER4FB.mjs';
|
|
5
|
+
import { sha256Hex } from './chunk-GVCNMPOS.mjs';
|
|
5
6
|
|
|
6
7
|
// src/core/transaction.ts
|
|
7
8
|
var Transaction = class _Transaction {
|
|
@@ -9,14 +10,35 @@ var Transaction = class _Transaction {
|
|
|
9
10
|
this.conn = conn;
|
|
10
11
|
}
|
|
11
12
|
_lastInsertRowId = 0;
|
|
13
|
+
/**
|
|
14
|
+
* Runs a read inside this transaction.
|
|
15
|
+
*
|
|
16
|
+
* @param sql - The statement to run.
|
|
17
|
+
* @param params - Values bound to the statement, named or positional.
|
|
18
|
+
* @returns The rows the statement produced.
|
|
19
|
+
*/
|
|
12
20
|
async query(sql, params) {
|
|
13
21
|
return query(this.conn, sql, params);
|
|
14
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Runs one write inside this transaction.
|
|
25
|
+
*
|
|
26
|
+
* @param sql - The statement to run.
|
|
27
|
+
* @param params - Values bound to the statement, named or positional.
|
|
28
|
+
* @returns How many rows changed, and the last inserted row id.
|
|
29
|
+
*/
|
|
15
30
|
async execute(sql, params) {
|
|
16
31
|
const result = await execute(this.conn, sql, params);
|
|
17
32
|
this._lastInsertRowId = result.lastInsertRowId;
|
|
18
33
|
return result;
|
|
19
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Runs one statement over many parameter sets inside this transaction.
|
|
37
|
+
*
|
|
38
|
+
* @param sql - The statement to run for each parameter set.
|
|
39
|
+
* @param paramsBatch - One parameter set per run.
|
|
40
|
+
* @returns One result per parameter set, in order.
|
|
41
|
+
*/
|
|
20
42
|
async executeBatch(sql, paramsBatch) {
|
|
21
43
|
const results = await executeBatch(this.conn, sql, paramsBatch);
|
|
22
44
|
if (results.length > 0) {
|
|
@@ -24,9 +46,13 @@ var Transaction = class _Transaction {
|
|
|
24
46
|
}
|
|
25
47
|
return results;
|
|
26
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Row id SQLite assigned to the last row this transaction inserted.
|
|
51
|
+
*/
|
|
27
52
|
get lastInsertRowId() {
|
|
28
53
|
return this._lastInsertRowId;
|
|
29
54
|
}
|
|
55
|
+
/** @internal */
|
|
30
56
|
static async run(conn, fn) {
|
|
31
57
|
return conn.transaction(async (txConn) => {
|
|
32
58
|
const tx = new _Transaction(txConn);
|
|
@@ -46,7 +72,11 @@ var HLC = class _HLC {
|
|
|
46
72
|
this.wallMs = 0;
|
|
47
73
|
this.logical = 0;
|
|
48
74
|
}
|
|
49
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Generate a new HLC timestamp, advancing the clock.
|
|
77
|
+
*
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
50
80
|
now() {
|
|
51
81
|
const physicalMs = Date.now();
|
|
52
82
|
if (physicalMs > this.wallMs) {
|
|
@@ -60,7 +90,11 @@ var HLC = class _HLC {
|
|
|
60
90
|
}
|
|
61
91
|
return _HLC.encode(this.wallMs, this.logical, this.nodeId);
|
|
62
92
|
}
|
|
63
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* Merge a remote HLC timestamp into the local clock and return the updated value.
|
|
95
|
+
*
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
64
98
|
receive(remote) {
|
|
65
99
|
const r = _HLC.decode(remote);
|
|
66
100
|
const physicalMs = Date.now();
|
|
@@ -98,6 +132,14 @@ var HLC = class _HLC {
|
|
|
98
132
|
nodeId: parts.slice(2).join("-")
|
|
99
133
|
};
|
|
100
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Builds the string form of a clock reading, which sorts in the same order as the reading itself.
|
|
137
|
+
*
|
|
138
|
+
* @param wallMs - Wall-clock milliseconds since the Unix epoch.
|
|
139
|
+
* @param logical - Counter that orders events sharing one millisecond.
|
|
140
|
+
* @param nodeId - Identifier of the node taking the reading.
|
|
141
|
+
* @returns The encoded stamp.
|
|
142
|
+
*/
|
|
101
143
|
static encode(wallMs, logical, nodeId) {
|
|
102
144
|
const wallHex = wallMs.toString(16).padStart(12, "0");
|
|
103
145
|
const logicalHex = logical.toString(16).padStart(4, "0");
|
|
@@ -107,6 +149,12 @@ var HLC = class _HLC {
|
|
|
107
149
|
|
|
108
150
|
// src/core/sync/conflict/lww.ts
|
|
109
151
|
var LWWResolver = class {
|
|
152
|
+
/**
|
|
153
|
+
* Takes the incoming row when its stamp is higher, and keeps the local row otherwise.
|
|
154
|
+
*
|
|
155
|
+
* @param ctx - The local and incoming versions of one row.
|
|
156
|
+
* @returns Which version to write.
|
|
157
|
+
*/
|
|
110
158
|
resolve(ctx) {
|
|
111
159
|
if (ctx.remoteChange.operation === "delete") {
|
|
112
160
|
return { action: "accept_remote" };
|
|
@@ -128,6 +176,11 @@ var LWWResolver = class {
|
|
|
128
176
|
}
|
|
129
177
|
};
|
|
130
178
|
|
|
179
|
+
// src/core/sync/checksum.ts
|
|
180
|
+
function computeChecksum(changes) {
|
|
181
|
+
return sha256Hex(canonicaliseForChecksum(changes));
|
|
182
|
+
}
|
|
183
|
+
|
|
131
184
|
// src/core/sync/hlc-store.ts
|
|
132
185
|
var HLC_CLOCK_META_KEY = "hlc_clock";
|
|
133
186
|
function isWellFormedHlc(candidate) {
|
|
@@ -403,7 +456,7 @@ var BatchApplier = class {
|
|
|
403
456
|
}
|
|
404
457
|
/**
|
|
405
458
|
* Applies one pulled transaction whose changes are read from disk in
|
|
406
|
-
* batches
|
|
459
|
+
* batches so that a transaction of any size is applied with bounded memory.
|
|
407
460
|
* The whole group runs in one local transaction: the batches, the echo
|
|
408
461
|
* stamping, and `withinTx` commit together or not at all.
|
|
409
462
|
*/
|
|
@@ -54,6 +54,9 @@ var HookDeniedError = class extends SirannonError {
|
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
var RequestDeniedError = class extends SirannonError {
|
|
57
|
+
/**
|
|
58
|
+
* HTTP status the server answers the refused request with.
|
|
59
|
+
*/
|
|
57
60
|
status;
|
|
58
61
|
constructor(status, code, message) {
|
|
59
62
|
super(message, code);
|