@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.
- 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
|
@@ -1,34 +1,24 @@
|
|
|
1
1
|
import { verifyBackupRecord } from './chunk-DX4WGJL7.mjs';
|
|
2
|
-
import { BackupManager, BackupScheduler,
|
|
3
|
-
import { DEFAULT_BACKUP_NAME_PREFIX, defaultStagingDir, DEFAULT_CAPTURE_INTERVAL_MS, DEFAULT_FULL_COPY_INTERVAL_MS } from './chunk-
|
|
2
|
+
import { BackupManager, BackupScheduler, sendFileInPieces } from './chunk-N3MHZV3B.mjs';
|
|
3
|
+
import { DEFAULT_BACKUP_NAME_PREFIX, defaultStagingDir, DEFAULT_CAPTURE_INTERVAL_MS, DEFAULT_FULL_COPY_INTERVAL_MS } from './chunk-Y4SP7CRF.mjs';
|
|
4
|
+
import { DEFAULT_PIECE_BYTES } from './chunk-I4JY5QPA.mjs';
|
|
4
5
|
import { withLateGroupOutcomes, withLateRowCounts } from './chunk-K43WJORM.mjs';
|
|
5
|
-
import { leftTransactionUnbegun, canCarryBegin,
|
|
6
|
+
import { leftTransactionUnbegun, canCarryBegin, WORKER_CANCELLED_CODE, WORKER_COPY_STOPPED_CODE, deserializeError, toWorkerGroupStatement } from './chunk-75IBRE73.mjs';
|
|
6
7
|
import { rowCountsOf, totalRowCounts } from './chunk-E2GRX4ZD.mjs';
|
|
7
|
-
import { readCycleState, writeCycleState, removeCycleState, stagedCapturePath, captureAndCheckpointTurn } from './chunk-
|
|
8
|
+
import { readCycleState, writeCycleState, removeCycleState, stagedCapturePath, captureAndCheckpointTurn } from './chunk-OJXV4L3N.mjs';
|
|
8
9
|
import { checkpointLog } from './chunk-QGVRRKB2.mjs';
|
|
9
10
|
import { logPathFor } from './chunk-AHCMO4YL.mjs';
|
|
10
11
|
import { reportQuietly } from './chunk-BUWJH3D3.mjs';
|
|
11
|
-
import { DEFAULT_CHAIN_NAME, readBackupChains, appendChainRecord, appendChainHead, readChainHeadAt, readChainHeads } from './chunk-
|
|
12
|
+
import { DEFAULT_CHAIN_NAME, readBackupChains, appendChainRecord, appendChainHead, readChainHeadAt, readChainHeads } from './chunk-6CABU54O.mjs';
|
|
12
13
|
import { destinationWithDeadline, DEFAULT_DESTINATION_TIMEOUT_MS, randomHex } from './chunk-BGJ5JVYL.mjs';
|
|
13
|
-
import { SirannonError
|
|
14
|
+
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
14
15
|
import { Worker } from 'worker_threads';
|
|
15
16
|
import { existsSync } from 'fs';
|
|
16
17
|
import { resolve, dirname, join, sep } from 'path';
|
|
17
18
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
18
|
-
import { AsyncLocalStorage } from 'async_hooks';
|
|
19
19
|
import { mkdir, rm, stat } from 'fs/promises';
|
|
20
20
|
import { createRequire } from 'module';
|
|
21
21
|
|
|
22
|
-
// src/core/driver/extension.ts
|
|
23
|
-
async function loadThroughRuntime(extensionPath, load) {
|
|
24
|
-
try {
|
|
25
|
-
load();
|
|
26
|
-
} catch (err) {
|
|
27
|
-
if (err instanceof SirannonError) throw err;
|
|
28
|
-
throw new ExtensionError(extensionPath, err instanceof Error ? err.message : String(err));
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
22
|
// src/core/driver/statement-cache.ts
|
|
33
23
|
var STATEMENT_CACHE_CAPACITY = 128;
|
|
34
24
|
function createStatementCache(prepare) {
|
|
@@ -50,63 +40,50 @@ function createStatementCache(prepare) {
|
|
|
50
40
|
};
|
|
51
41
|
}
|
|
52
42
|
|
|
53
|
-
// src/core/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
function narrowRowIntegers(row) {
|
|
62
|
-
if (row === null || typeof row !== "object") return row;
|
|
63
|
-
const obj = row;
|
|
64
|
-
for (const key of Object.keys(obj)) {
|
|
65
|
-
const value = obj[key];
|
|
66
|
-
if (typeof value === "bigint" && value >= -SAFE_INT_BOUND && value <= SAFE_INT_BOUND) {
|
|
67
|
-
obj[key] = Number(value);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return row;
|
|
71
|
-
}
|
|
72
|
-
function narrowRowsIntegers(rows) {
|
|
73
|
-
for (const row of rows) {
|
|
74
|
-
narrowRowIntegers(row);
|
|
75
|
-
}
|
|
76
|
-
return rows;
|
|
77
|
-
}
|
|
78
|
-
var EMPTY_BLOB_WITH_ALLOCATED_STORAGE = new Uint8Array(new ArrayBuffer(1), 0, 0);
|
|
79
|
-
function bindableValue(value) {
|
|
80
|
-
if (typeof value === "boolean") return value ? 1n : 0n;
|
|
81
|
-
if (ArrayBuffer.isView(value) && value.byteLength === 0) return EMPTY_BLOB_WITH_ALLOCATED_STORAGE;
|
|
82
|
-
return value;
|
|
83
|
-
}
|
|
84
|
-
function isNamedParams(value) {
|
|
85
|
-
if (value === null || typeof value !== "object") return false;
|
|
86
|
-
const prototype = Object.getPrototypeOf(value);
|
|
87
|
-
return prototype === Object.prototype || prototype === null;
|
|
88
|
-
}
|
|
89
|
-
function bindableNamedParams(params) {
|
|
90
|
-
let bound;
|
|
91
|
-
for (const key of Object.keys(params)) {
|
|
92
|
-
const value = params[key];
|
|
93
|
-
const replacement = bindableValue(value);
|
|
94
|
-
if (replacement === value) continue;
|
|
95
|
-
bound ??= { ...params };
|
|
96
|
-
bound[key] = replacement;
|
|
97
|
-
}
|
|
98
|
-
return bound ?? params;
|
|
43
|
+
// src/core/worker/host-connection.ts
|
|
44
|
+
function countedCalls(request, sql) {
|
|
45
|
+
return {
|
|
46
|
+
allCounted: (...params) => request({ kind: "allCounted", sql, params }),
|
|
47
|
+
getCounted: (...params) => request({ kind: "getCounted", sql, params }),
|
|
48
|
+
allRawCounted: (...params) => request({ kind: "allRawCounted", sql, params })
|
|
49
|
+
};
|
|
99
50
|
}
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
51
|
+
function workerHostConnection(request, countsRows, ownCalls) {
|
|
52
|
+
return {
|
|
53
|
+
exec: (sql) => request({ kind: "exec", sql }),
|
|
54
|
+
prepare: async (sql) => ({
|
|
55
|
+
all: (...params) => request({ kind: "all", sql, params }),
|
|
56
|
+
get: (...params) => request({ kind: "get", sql, params }),
|
|
57
|
+
run: (...params) => request({ kind: "run", sql, params }),
|
|
58
|
+
allRaw: (...params) => request({ kind: "allRaw", sql, params }),
|
|
59
|
+
...countsRows ? countedCalls(request, sql) : {}
|
|
60
|
+
}),
|
|
61
|
+
runBatch: (sql, paramsBatch) => request({ kind: "runBatch", sql, paramsBatch }),
|
|
62
|
+
runBatchSummary: (sql, paramsBatch) => request({ kind: "runBatchSummary", sql, paramsBatch }),
|
|
63
|
+
runGroup: (units, opening) => request({
|
|
64
|
+
kind: "runGroup",
|
|
65
|
+
units: units.map((unit) => ({ statements: unit.statements.map(toWorkerGroupStatement) })),
|
|
66
|
+
...opening === void 0 ? {} : { opening: toWorkerGroupStatement(opening) }
|
|
67
|
+
}),
|
|
68
|
+
copyRunsOffCallerThread: true,
|
|
69
|
+
copyDatabase: (copyRequest) => request(
|
|
70
|
+
{
|
|
71
|
+
kind: "copyDatabase",
|
|
72
|
+
destPath: copyRequest.destPath,
|
|
73
|
+
pagesPerStep: copyRequest.pagesPerStep,
|
|
74
|
+
...copyRequest.stallTimeoutMs === void 0 ? {} : { stallTimeoutMs: copyRequest.stallTimeoutMs }
|
|
75
|
+
},
|
|
76
|
+
copyRequest.onStep
|
|
77
|
+
),
|
|
78
|
+
startChangeCapture: async () => {
|
|
79
|
+
await request({ kind: "startChangeCapture" });
|
|
80
|
+
},
|
|
81
|
+
takeCapturedChanges: () => request({ kind: "takeCapturedChanges" }),
|
|
82
|
+
stopChangeCapture: async () => {
|
|
83
|
+
await request({ kind: "stopChangeCapture" });
|
|
84
|
+
},
|
|
85
|
+
...ownCalls
|
|
86
|
+
};
|
|
110
87
|
}
|
|
111
88
|
|
|
112
89
|
// src/core/worker/late-replies.ts
|
|
@@ -233,11 +210,12 @@ var DEFAULT_WRITE_TIMEOUT_MS = 3e4;
|
|
|
233
210
|
var DEFAULT_MAX_RESTARTS = 5;
|
|
234
211
|
var CLOSE_GRACE_MS = 1e3;
|
|
235
212
|
var WriterWorker = class _WriterWorker {
|
|
236
|
-
constructor(openRequest, timeoutMs, maxRestarts, countsRows) {
|
|
213
|
+
constructor(openRequest, timeoutMs, maxRestarts, countsRows, encryptionKey) {
|
|
237
214
|
this.openRequest = openRequest;
|
|
238
215
|
this.timeoutMs = timeoutMs;
|
|
239
216
|
this.maxRestarts = maxRestarts;
|
|
240
217
|
this.countsRows = countsRows;
|
|
218
|
+
this.encryptionKey = encryptionKey;
|
|
241
219
|
this.connection = this.makeConnection();
|
|
242
220
|
}
|
|
243
221
|
worker = null;
|
|
@@ -260,7 +238,8 @@ var WriterWorker = class _WriterWorker {
|
|
|
260
238
|
{ kind: "open", entry, path, options },
|
|
261
239
|
workerOptions?.writeTimeoutMs ?? DEFAULT_WRITE_TIMEOUT_MS,
|
|
262
240
|
workerOptions?.maxRestarts ?? DEFAULT_MAX_RESTARTS,
|
|
263
|
-
workerOptions?.countsRows === true
|
|
241
|
+
workerOptions?.countsRows === true,
|
|
242
|
+
workerOptions?.encryptionKey
|
|
264
243
|
);
|
|
265
244
|
worker.spawn();
|
|
266
245
|
try {
|
|
@@ -287,10 +266,23 @@ var WriterWorker = class _WriterWorker {
|
|
|
287
266
|
this.fault(new SirannonError(`Writer worker exited with code ${code}`, "WRITER_WORKER_EXIT"));
|
|
288
267
|
}
|
|
289
268
|
});
|
|
290
|
-
this.ready = this.
|
|
269
|
+
this.ready = this.openOnWorker().then(() => this.reloadExtensionsOntoRestartedConnection());
|
|
291
270
|
this.ready.catch(() => {
|
|
292
271
|
});
|
|
293
272
|
}
|
|
273
|
+
async openOnWorker() {
|
|
274
|
+
if (!this.encryptionKey) {
|
|
275
|
+
await this.send(this.openRequest);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const key = await this.encryptionKey();
|
|
279
|
+
try {
|
|
280
|
+
const options = { ...this.openRequest.options, encryption: { key } };
|
|
281
|
+
await this.send({ ...this.openRequest, options }, void 0, [key.buffer]);
|
|
282
|
+
} finally {
|
|
283
|
+
if (key.buffer.byteLength > 0) key.fill(0);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
294
286
|
async reloadExtensionsOntoRestartedConnection() {
|
|
295
287
|
for (const path of this.loadedExtensions) {
|
|
296
288
|
await this.send({ kind: "loadExtension", path });
|
|
@@ -413,7 +405,7 @@ var WriterWorker = class _WriterWorker {
|
|
|
413
405
|
}
|
|
414
406
|
this.spawn();
|
|
415
407
|
}
|
|
416
|
-
send(request, onStep) {
|
|
408
|
+
send(request, onStep, transfer = []) {
|
|
417
409
|
const worker = this.worker;
|
|
418
410
|
if (!worker) {
|
|
419
411
|
return Promise.reject(
|
|
@@ -446,7 +438,7 @@ var WriterWorker = class _WriterWorker {
|
|
|
446
438
|
...onStep ? { onStep } : {}
|
|
447
439
|
});
|
|
448
440
|
try {
|
|
449
|
-
worker.postMessage(message);
|
|
441
|
+
worker.postMessage(message, transfer);
|
|
450
442
|
} catch (err) {
|
|
451
443
|
this.pending.delete(id);
|
|
452
444
|
if (timer) clearTimeout(timer);
|
|
@@ -468,70 +460,38 @@ var WriterWorker = class _WriterWorker {
|
|
|
468
460
|
return value;
|
|
469
461
|
});
|
|
470
462
|
}
|
|
471
|
-
countedCalls(sql) {
|
|
472
|
-
return {
|
|
473
|
-
allCounted: (...params) => this.request({ kind: "allCounted", sql, params }),
|
|
474
|
-
getCounted: (...params) => this.request({ kind: "getCounted", sql, params }),
|
|
475
|
-
allRawCounted: (...params) => this.request({ kind: "allRawCounted", sql, params })
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
463
|
makeConnection() {
|
|
479
|
-
const conn =
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
...this.countsRows ? this.countedCalls(sql) : {}
|
|
487
|
-
}),
|
|
488
|
-
runBatch: (sql, paramsBatch) => this.request({ kind: "runBatch", sql, paramsBatch }),
|
|
489
|
-
runBatchSummary: (sql, paramsBatch) => this.request({
|
|
490
|
-
kind: "runBatchSummary",
|
|
491
|
-
sql,
|
|
492
|
-
paramsBatch
|
|
493
|
-
}),
|
|
494
|
-
runGroup: (units, opening) => this.request({
|
|
495
|
-
kind: "runGroup",
|
|
496
|
-
units: units.map((unit) => ({ statements: unit.statements.map(toWorkerGroupStatement) })),
|
|
497
|
-
...opening === void 0 ? {} : { opening: toWorkerGroupStatement(opening) }
|
|
498
|
-
}),
|
|
499
|
-
copyRunsOffCallerThread: true,
|
|
500
|
-
copyDatabase: (request) => this.request(
|
|
501
|
-
{
|
|
502
|
-
kind: "copyDatabase",
|
|
503
|
-
destPath: request.destPath,
|
|
504
|
-
pagesPerStep: request.pagesPerStep,
|
|
505
|
-
...request.stallTimeoutMs === void 0 ? {} : { stallTimeoutMs: request.stallTimeoutMs }
|
|
464
|
+
const conn = workerHostConnection(
|
|
465
|
+
(request, onStep) => this.request(request, onStep),
|
|
466
|
+
this.countsRows,
|
|
467
|
+
{
|
|
468
|
+
loadExtension: async (extensionPath) => {
|
|
469
|
+
await this.request({ kind: "loadExtension", path: extensionPath });
|
|
470
|
+
if (!this.loadedExtensions.includes(extensionPath)) this.loadedExtensions.push(extensionPath);
|
|
506
471
|
},
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
await this.request({ kind: "loadExtension", path: extensionPath });
|
|
511
|
-
if (!this.loadedExtensions.includes(extensionPath)) this.loadedExtensions.push(extensionPath);
|
|
512
|
-
},
|
|
513
|
-
transaction: async (fn) => {
|
|
514
|
-
this.transactionOpen = true;
|
|
515
|
-
this.beginPending = true;
|
|
516
|
-
try {
|
|
517
|
-
const result = await fn(conn);
|
|
518
|
-
if (!this.beginPending) await conn.exec("COMMIT");
|
|
519
|
-
return result;
|
|
520
|
-
} catch (err) {
|
|
472
|
+
transaction: async (fn) => {
|
|
473
|
+
this.transactionOpen = true;
|
|
474
|
+
this.beginPending = true;
|
|
521
475
|
try {
|
|
522
|
-
|
|
523
|
-
|
|
476
|
+
const result = await fn(conn);
|
|
477
|
+
if (!this.beginPending) await conn.exec("COMMIT");
|
|
478
|
+
return result;
|
|
479
|
+
} catch (err) {
|
|
480
|
+
try {
|
|
481
|
+
if (!this.beginPending) await conn.exec("ROLLBACK");
|
|
482
|
+
} catch {
|
|
483
|
+
}
|
|
484
|
+
throw err;
|
|
485
|
+
} finally {
|
|
486
|
+
this.transactionOpen = false;
|
|
487
|
+
this.beginPending = false;
|
|
488
|
+
this.beginCarrier = null;
|
|
489
|
+
this.lostTransaction = null;
|
|
524
490
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
this.beginCarrier = null;
|
|
530
|
-
this.lostTransaction = null;
|
|
531
|
-
}
|
|
532
|
-
},
|
|
533
|
-
close: () => this.close()
|
|
534
|
-
};
|
|
491
|
+
},
|
|
492
|
+
close: () => this.close()
|
|
493
|
+
}
|
|
494
|
+
);
|
|
535
495
|
return conn;
|
|
536
496
|
}
|
|
537
497
|
async close() {
|
|
@@ -561,14 +521,6 @@ var WriterWorker = class _WriterWorker {
|
|
|
561
521
|
this.pending.clear();
|
|
562
522
|
}
|
|
563
523
|
};
|
|
564
|
-
function asyncLocalWriterContext() {
|
|
565
|
-
const held = new AsyncLocalStorage();
|
|
566
|
-
return {
|
|
567
|
-
run: (operation) => held.run(true, operation),
|
|
568
|
-
isActive: () => held.getStore() === true,
|
|
569
|
-
exit: (operation) => held.exit(operation)
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
524
|
|
|
573
525
|
// src/core/backup/preferred-node.ts
|
|
574
526
|
function preferredBackupNode(membership, preference) {
|
|
@@ -928,6 +880,10 @@ async function confirmChainStillListed(grip) {
|
|
|
928
880
|
grip.report(lost);
|
|
929
881
|
return false;
|
|
930
882
|
}
|
|
883
|
+
function keyIdField(request) {
|
|
884
|
+
const keyId = request.keyId?.();
|
|
885
|
+
return keyId === void 0 ? {} : { keyId };
|
|
886
|
+
}
|
|
931
887
|
async function startChain(request, chainName, namePrefix, previousChainId) {
|
|
932
888
|
const chainId = randomHex(8);
|
|
933
889
|
const startedAt = Date.now();
|
|
@@ -954,7 +910,8 @@ async function startChain(request, chainName, namePrefix, previousChainId) {
|
|
|
954
910
|
pieceCount: report.pieceCount,
|
|
955
911
|
pieceBytes: report.pieceBytes,
|
|
956
912
|
bytesWritten: report.bytesWritten,
|
|
957
|
-
...report.fingerprint ? { fingerprint: report.fingerprint } : {}
|
|
913
|
+
...report.fingerprint ? { fingerprint: report.fingerprint } : {},
|
|
914
|
+
...keyIdField(request)
|
|
958
915
|
};
|
|
959
916
|
await appendChainRecord(request.destination, chainName, base, 0);
|
|
960
917
|
const head = {
|
|
@@ -998,7 +955,8 @@ async function transferCapture(request, chainName, chainId, pending, recordIndex
|
|
|
998
955
|
pieceBytes,
|
|
999
956
|
bytesWritten: sent.bytesWritten,
|
|
1000
957
|
checkpointed: pending.cursor.checkpointed,
|
|
1001
|
-
...sent.fingerprint ? { fingerprint: sent.fingerprint } : {}
|
|
958
|
+
...sent.fingerprint ? { fingerprint: sent.fingerprint } : {},
|
|
959
|
+
...keyIdField(request)
|
|
1002
960
|
};
|
|
1003
961
|
await appendChainRecord(request.destination, chainName, record, recordIndex);
|
|
1004
962
|
return {
|
|
@@ -1286,6 +1244,13 @@ var BackupCycle = class {
|
|
|
1286
1244
|
report: (err) => this.report(err)
|
|
1287
1245
|
});
|
|
1288
1246
|
}
|
|
1247
|
+
/** Drops the chain that this stopped cycle extended, so that the next cycle starts a new chain with a full copy. */
|
|
1248
|
+
forgetChain() {
|
|
1249
|
+
return this.runTurn(async () => {
|
|
1250
|
+
await this.discardState();
|
|
1251
|
+
await removeCycleState(this.stagingDir);
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1289
1254
|
async discardState() {
|
|
1290
1255
|
const pending = this.state?.pending;
|
|
1291
1256
|
if (pending) await rm(stagedCapturePath(this.stagingDir, pending.sequence), { force: true });
|
|
@@ -1466,4 +1431,4 @@ function nodeBackupEngine(streaming) {
|
|
|
1466
1431
|
};
|
|
1467
1432
|
}
|
|
1468
1433
|
|
|
1469
|
-
export { WriterWorker,
|
|
1434
|
+
export { WriterWorker, createStatementCache, nodeBackupEngine, nodeResolveExtensionPath, nodeStreamingSupport };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { IDENTIFIER_RE, selectCountTableRows, canonicaliseForChecksum, selectUserSchemaObjects, validateDdlSafety, selectUserTableNames, referencedTables } from './chunk-
|
|
1
|
+
import { IDENTIFIER_RE, selectCountTableRows, canonicaliseForChecksum, selectUserSchemaObjects, validateDdlSafety, selectUserTableNames, referencedTables } from './chunk-MTOQ6QIS.mjs';
|
|
2
2
|
import { ReplicationError } from './chunk-BLMRNSUG.mjs';
|
|
3
|
-
import { INTERNAL_TABLE_PREFIX } from './chunk-
|
|
3
|
+
import { INTERNAL_TABLE_PREFIX } from './chunk-ZAUHAJ4G.mjs';
|
|
4
4
|
import { createHash } from 'crypto';
|
|
5
5
|
|
|
6
6
|
var DumpOps = class {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
2
|
+
|
|
3
|
+
// src/core/backup/start-guard.ts
|
|
4
|
+
async function startCopyWithoutHoldingWriter(runExclusive, start) {
|
|
5
|
+
let begin;
|
|
6
|
+
let release;
|
|
7
|
+
const writerHeld = new Promise((resolve) => {
|
|
8
|
+
begin = resolve;
|
|
9
|
+
});
|
|
10
|
+
const firstStepDone = new Promise((resolve) => {
|
|
11
|
+
release = resolve;
|
|
12
|
+
});
|
|
13
|
+
const run = (async () => {
|
|
14
|
+
await writerHeld;
|
|
15
|
+
return start(release);
|
|
16
|
+
})();
|
|
17
|
+
run.then(release, release);
|
|
18
|
+
await runExclusive(async () => {
|
|
19
|
+
begin();
|
|
20
|
+
await firstStepDone;
|
|
21
|
+
});
|
|
22
|
+
return run;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/core/backup/report.ts
|
|
26
|
+
var DEFAULT_PIECE_BYTES = 16 * 1024 * 1024;
|
|
27
|
+
function defaultDestinationName() {
|
|
28
|
+
return `backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.db`;
|
|
29
|
+
}
|
|
30
|
+
function assertPieceBytes(pieceBytes) {
|
|
31
|
+
if (!Number.isInteger(pieceBytes) || pieceBytes <= 0) {
|
|
32
|
+
throw new SirannonError(
|
|
33
|
+
`Piece size must be a positive whole number of bytes, and it was ${pieceBytes}`,
|
|
34
|
+
"BACKUP_ERROR"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function readPageSize(conn) {
|
|
39
|
+
const stmt = await conn.prepare("PRAGMA page_size");
|
|
40
|
+
const row = await stmt.get();
|
|
41
|
+
const [pageSize] = row === void 0 ? [] : Object.values(row);
|
|
42
|
+
return pageSize === void 0 ? 0 : Number(pageSize);
|
|
43
|
+
}
|
|
44
|
+
async function readMainDatabasePath(conn) {
|
|
45
|
+
const stmt = await conn.prepare("PRAGMA database_list");
|
|
46
|
+
const rows = await stmt.all();
|
|
47
|
+
const main = rows.find((row) => row.name === "main");
|
|
48
|
+
return main?.file ?? "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { DEFAULT_PIECE_BYTES, assertPieceBytes, defaultDestinationName, readMainDatabasePath, readPageSize, startCopyWithoutHoldingWriter };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { planBackupRestore } from './chunk-
|
|
1
|
+
import { planBackupRestore, EncryptionKeyring } from './chunk-W74HFQFD.mjs';
|
|
2
2
|
import { listStoredFilePieces, fetchStoredFile, checkpointLog } from './chunk-QGVRRKB2.mjs';
|
|
3
3
|
import { reportQuietly, LOG_HEADER_BYTES, LOG_FRAME_HEADER_BYTES, readLogHeader, foldLogChecksum, MAGIC_WITH_LITTLE_ENDIAN_CONTENT, LOG_FORMAT_VERSION } from './chunk-BUWJH3D3.mjs';
|
|
4
|
-
import { readBackupChains, DEFAULT_CHAIN_NAME } from './chunk-
|
|
4
|
+
import { readBackupChains, DEFAULT_CHAIN_NAME } from './chunk-6CABU54O.mjs';
|
|
5
5
|
import { destinationWithDeadline, DEFAULT_DESTINATION_TIMEOUT_MS, randomHex } from './chunk-BGJ5JVYL.mjs';
|
|
6
|
+
import { EncryptionError } from './chunk-5O4J6LQQ.mjs';
|
|
6
7
|
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
7
8
|
import { rename, open, rm, stat } from 'fs/promises';
|
|
8
9
|
|
|
@@ -53,6 +54,7 @@ function assembleFromDestination(destination, report, destPath) {
|
|
|
53
54
|
return assembleStoredFile(destination, { ...report, name: report.destinationName }, destPath);
|
|
54
55
|
}
|
|
55
56
|
var DATABASE_HEADER_BYTES = 100;
|
|
57
|
+
var PLAINTEXT_HEADER = "SQLite format 3\0";
|
|
56
58
|
var PAGE_SIZE_OFFSET = 16;
|
|
57
59
|
var PAGE_SIZE_FOR_65536 = 1;
|
|
58
60
|
var SMALLEST_PAGE_BYTES = 512;
|
|
@@ -71,6 +73,7 @@ async function readDatabaseHeader(path) {
|
|
|
71
73
|
if (bytesRead < DATABASE_HEADER_BYTES) {
|
|
72
74
|
throw restoreError(`The full copy assembled into '${path}' is too short to be a SQLite database`);
|
|
73
75
|
}
|
|
76
|
+
if (buffer.toString("latin1", 0, PLAINTEXT_HEADER.length) !== PLAINTEXT_HEADER) return null;
|
|
74
77
|
const stated = buffer.readUInt16BE(PAGE_SIZE_OFFSET);
|
|
75
78
|
const pageSize = stated === PAGE_SIZE_FOR_65536 ? LARGEST_PAGE_BYTES : stated;
|
|
76
79
|
if (pageSize < SMALLEST_PAGE_BYTES || pageSize > LARGEST_PAGE_BYTES || (pageSize & pageSize - 1) !== 0) {
|
|
@@ -332,16 +335,16 @@ async function readPageCount(conn) {
|
|
|
332
335
|
const row = await stmt.get();
|
|
333
336
|
return row ? Number(row.page_count) : 0;
|
|
334
337
|
}
|
|
335
|
-
async function countDatabasePages(
|
|
336
|
-
const conn = await
|
|
338
|
+
async function countDatabasePages(openRebuilt, destPath) {
|
|
339
|
+
const conn = await openRebuilt(destPath);
|
|
337
340
|
try {
|
|
338
341
|
return await readPageCount(conn);
|
|
339
342
|
} finally {
|
|
340
343
|
await conn.close();
|
|
341
344
|
}
|
|
342
345
|
}
|
|
343
|
-
async function foldLogIntoDatabase(
|
|
344
|
-
const conn = await
|
|
346
|
+
async function foldLogIntoDatabase(openRebuilt, destPath, databasePages) {
|
|
347
|
+
const conn = await openRebuilt(destPath);
|
|
345
348
|
try {
|
|
346
349
|
const reached = await readPageCount(conn);
|
|
347
350
|
if (reached !== databasePages) {
|
|
@@ -375,7 +378,7 @@ async function applyChangeBatch(request) {
|
|
|
375
378
|
});
|
|
376
379
|
}
|
|
377
380
|
const written = await writer.finish();
|
|
378
|
-
await foldLogIntoDatabase(request.
|
|
381
|
+
await foldLogIntoDatabase(request.openRebuilt, request.destPath, written.databasePages);
|
|
379
382
|
return written.frameCount;
|
|
380
383
|
} catch (err) {
|
|
381
384
|
await writer.abandon();
|
|
@@ -385,6 +388,43 @@ async function applyChangeBatch(request) {
|
|
|
385
388
|
}
|
|
386
389
|
}
|
|
387
390
|
|
|
391
|
+
// src/core/backup/restore-keyed.ts
|
|
392
|
+
var KEY_FAILURE_CODES = /* @__PURE__ */ new Set(["ENCRYPTION_KEY_REJECTED", "ENCRYPTION_KEY_MISSING"]);
|
|
393
|
+
function rebuiltDatabaseOpener(driver, encryption) {
|
|
394
|
+
const keyring = new EncryptionKeyring(encryption?.key ?? null);
|
|
395
|
+
return (path) => keyring.openWith((keyed) => driver.open(path, { walMode: false, walAutoCheckpoint: 0, ...keyed }));
|
|
396
|
+
}
|
|
397
|
+
function explainKeyFailure(err, base) {
|
|
398
|
+
if (!(err instanceof EncryptionError) || !KEY_FAILURE_CODES.has(err.code)) return err;
|
|
399
|
+
const heldUnder = base.keyId === void 0 ? "a key that came with no identifier" : `the key '${base.keyId}'`;
|
|
400
|
+
return new EncryptionError(
|
|
401
|
+
`The key that the restore opened with failed on the full copy '${base.name}', which holds ciphertext under ${heldUnder}`,
|
|
402
|
+
err.code === "ENCRYPTION_KEY_MISSING" ? "ENCRYPTION_KEY_MISSING" : "ENCRYPTION_KEY_REJECTED",
|
|
403
|
+
err
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
async function readText(conn, sql) {
|
|
407
|
+
const row = await (await conn.prepare(sql)).get();
|
|
408
|
+
return row === void 0 ? void 0 : Object.values(row)[0];
|
|
409
|
+
}
|
|
410
|
+
async function readRebuiltHeader(path, openRebuilt, base) {
|
|
411
|
+
const plaintext = await readDatabaseHeader(path);
|
|
412
|
+
if (plaintext !== null) return plaintext;
|
|
413
|
+
let conn;
|
|
414
|
+
try {
|
|
415
|
+
conn = await openRebuilt(path);
|
|
416
|
+
} catch (err) {
|
|
417
|
+
throw explainKeyFailure(err, base);
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
const pageSize = Number(await readText(conn, "PRAGMA page_size"));
|
|
421
|
+
const journalMode = await readText(conn, "PRAGMA journal_mode");
|
|
422
|
+
return { pageSize, walMode: typeof journalMode === "string" && journalMode.toLowerCase() === "wal" };
|
|
423
|
+
} finally {
|
|
424
|
+
await conn.close();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
388
428
|
// src/core/backup/restore-options.ts
|
|
389
429
|
var DEFAULT_RESTORE_BATCH_SIZE = 16;
|
|
390
430
|
var MAX_RESTORE_BATCH_SIZE = 4096;
|
|
@@ -410,12 +450,12 @@ async function removeDatabaseAndLog(path) {
|
|
|
410
450
|
});
|
|
411
451
|
await removeLogBeside(path);
|
|
412
452
|
}
|
|
413
|
-
async function foldLogIntoDatabase2(
|
|
453
|
+
async function foldLogIntoDatabase2(openRebuilt, path) {
|
|
414
454
|
const existing = await stat(path).catch(() => void 0);
|
|
415
455
|
if (!existing) return true;
|
|
416
456
|
const log = await stat(`${path}-wal`).catch(() => void 0);
|
|
417
457
|
if (!log || log.size === 0) return true;
|
|
418
|
-
const conn = await
|
|
458
|
+
const conn = await openRebuilt(path).catch(() => void 0);
|
|
419
459
|
if (!conn) return false;
|
|
420
460
|
const folded = await checkpointLog(conn).catch(() => void 0);
|
|
421
461
|
await conn.close().catch(() => void 0);
|
|
@@ -452,6 +492,7 @@ async function restoreBackup(options) {
|
|
|
452
492
|
await assertPathIsFree(destPath, options.replaceExisting === true);
|
|
453
493
|
const buildPath = `${destPath}${BUILD_SUFFIX}`;
|
|
454
494
|
await removeDatabaseAndLog(buildPath);
|
|
495
|
+
const openRebuilt = rebuiltDatabaseOpener(options.driver, options.encryption);
|
|
455
496
|
let piecesFetched = 0;
|
|
456
497
|
let bytesFetched = 0;
|
|
457
498
|
let changesApplied = 0;
|
|
@@ -470,7 +511,7 @@ async function restoreBackup(options) {
|
|
|
470
511
|
bytesFetched = bytes;
|
|
471
512
|
reportProgress("full-copy");
|
|
472
513
|
});
|
|
473
|
-
const header = await
|
|
514
|
+
const header = await readRebuiltHeader(buildPath, openRebuilt, plan.base);
|
|
474
515
|
if (!header.walMode && plan.changes.length > 0) {
|
|
475
516
|
throw new SirannonError(
|
|
476
517
|
`The full copy assembled into '${buildPath}' records no write-ahead log, so SQLite would ignore the change pieces rather than replay them`,
|
|
@@ -482,7 +523,7 @@ async function restoreBackup(options) {
|
|
|
482
523
|
logSequence++;
|
|
483
524
|
framesApplied += await applyChangeBatch({
|
|
484
525
|
destination,
|
|
485
|
-
|
|
526
|
+
openRebuilt,
|
|
486
527
|
destPath: buildPath,
|
|
487
528
|
pageSize: header.pageSize,
|
|
488
529
|
logSequence,
|
|
@@ -496,8 +537,8 @@ async function restoreBackup(options) {
|
|
|
496
537
|
changesApplied += batch.length;
|
|
497
538
|
reportProgress("changes");
|
|
498
539
|
}
|
|
499
|
-
await countDatabasePages(
|
|
500
|
-
const logFoldedIn = await foldLogIntoDatabase2(
|
|
540
|
+
await countDatabasePages(openRebuilt, buildPath);
|
|
541
|
+
const logFoldedIn = await foldLogIntoDatabase2(openRebuilt, destPath);
|
|
501
542
|
if (logFoldedIn) await removeLogBeside(destPath);
|
|
502
543
|
else await removeDatabaseAndLog(destPath);
|
|
503
544
|
await rename(buildPath, destPath);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReplicationError, BatchValidationError } from './chunk-BLMRNSUG.mjs';
|
|
2
|
-
import { CHANGES_TABLE, META_TABLE, MIGRATIONS_TABLE, REPLACED_ROWS_TABLE, APPLIED_CHANGES_TABLE, STAGED_PUSH_CHANGES_TABLE, COLUMN_VERSIONS_TABLE, DEVICE_CURSORS_TABLE, PEER_STATE_TABLE, SYNC_STATE_TABLE, STAGED_CHANGES_TABLE, isReservedIdentifier } from './chunk-
|
|
2
|
+
import { CHANGES_TABLE, META_TABLE, MIGRATIONS_TABLE, REPLACED_ROWS_TABLE, APPLIED_CHANGES_TABLE, STAGED_PUSH_CHANGES_TABLE, COLUMN_VERSIONS_TABLE, DEVICE_CURSORS_TABLE, PEER_STATE_TABLE, SYNC_STATE_TABLE, STAGED_CHANGES_TABLE, isReservedIdentifier } from './chunk-ZAUHAJ4G.mjs';
|
|
3
3
|
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
4
4
|
|
|
5
5
|
// src/core/sync/hlc.ts
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/core/driver/values.ts
|
|
2
|
+
var SAFE_INT_BOUND = 9007199254740991n;
|
|
3
|
+
function narrowSafeBigInt(value) {
|
|
4
|
+
if (typeof value === "bigint" && value >= -SAFE_INT_BOUND && value <= SAFE_INT_BOUND) {
|
|
5
|
+
return Number(value);
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
function narrowRowIntegers(row) {
|
|
10
|
+
if (row === null || typeof row !== "object") return row;
|
|
11
|
+
const obj = row;
|
|
12
|
+
for (const key of Object.keys(obj)) {
|
|
13
|
+
const value = obj[key];
|
|
14
|
+
if (typeof value === "bigint" && value >= -SAFE_INT_BOUND && value <= SAFE_INT_BOUND) {
|
|
15
|
+
obj[key] = Number(value);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return row;
|
|
19
|
+
}
|
|
20
|
+
function narrowRowsIntegers(rows) {
|
|
21
|
+
for (const row of rows) {
|
|
22
|
+
narrowRowIntegers(row);
|
|
23
|
+
}
|
|
24
|
+
return rows;
|
|
25
|
+
}
|
|
26
|
+
var EMPTY_BLOB_WITH_ALLOCATED_STORAGE = new Uint8Array(new ArrayBuffer(1), 0, 0);
|
|
27
|
+
function bindableValue(value) {
|
|
28
|
+
if (typeof value === "boolean") return value ? 1n : 0n;
|
|
29
|
+
if (ArrayBuffer.isView(value) && value.byteLength === 0) return EMPTY_BLOB_WITH_ALLOCATED_STORAGE;
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function isNamedParams(value) {
|
|
33
|
+
if (value === null || typeof value !== "object") return false;
|
|
34
|
+
const prototype = Object.getPrototypeOf(value);
|
|
35
|
+
return prototype === Object.prototype || prototype === null;
|
|
36
|
+
}
|
|
37
|
+
function bindableNamedParams(params) {
|
|
38
|
+
let bound;
|
|
39
|
+
for (const key of Object.keys(params)) {
|
|
40
|
+
const value = params[key];
|
|
41
|
+
const replacement = bindableValue(value);
|
|
42
|
+
if (replacement === value) continue;
|
|
43
|
+
bound ??= { ...params };
|
|
44
|
+
bound[key] = replacement;
|
|
45
|
+
}
|
|
46
|
+
return bound ?? params;
|
|
47
|
+
}
|
|
48
|
+
function bindableParams(params) {
|
|
49
|
+
let bound;
|
|
50
|
+
for (let index = 0; index < params.length; index++) {
|
|
51
|
+
const value = params[index];
|
|
52
|
+
const replacement = isNamedParams(value) ? bindableNamedParams(value) : bindableValue(value);
|
|
53
|
+
if (replacement === value) continue;
|
|
54
|
+
bound ??= [...params];
|
|
55
|
+
bound[index] = replacement;
|
|
56
|
+
}
|
|
57
|
+
return bound ?? params;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { bindableParams, isNamedParams, narrowRowIntegers, narrowRowsIntegers, narrowSafeBigInt };
|