@delali/sirannon-db 0.1.7 → 0.2.0
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 +117 -969
- package/dist/backup-scheduler/index.d.ts +1 -1
- package/dist/backup-scheduler/index.mjs +2 -2
- package/dist/baseline-Br77Fnhb.d.ts +6 -0
- package/dist/{change-tracker-CbmaMO-N.d.ts → change-tracker-DKRVUC3l.d.ts} +5 -8
- package/dist/{chunk-DJLX6CAE.mjs → chunk-4ISB7XMA.mjs} +2 -1
- package/dist/chunk-5NOIGN5Y.mjs +18 -0
- package/dist/chunk-67M7KAH6.mjs +90 -0
- package/dist/{chunk-GEZUUIKV.mjs → chunk-D7LAYTKN.mjs} +5 -2
- package/dist/{chunk-4IGMIJQK.mjs → chunk-EBJXPQQO.mjs} +59 -20
- package/dist/{chunk-BNUTBHHH.mjs → chunk-FHWTZFI4.mjs} +1 -1
- package/dist/chunk-GVCNMPOS.mjs +130 -0
- package/dist/chunk-H237TXZW.mjs +1407 -0
- package/dist/{chunk-HHRMRFFR.mjs → chunk-H6PIVVDN.mjs} +353 -142
- package/dist/chunk-HR5CWTLC.mjs +661 -0
- package/dist/chunk-JU64Y7HM.mjs +299 -0
- package/dist/{chunk-CW6S3WL5.mjs → chunk-JZGINXTN.mjs} +7 -5
- package/dist/{chunk-CJLYFDP5.mjs → chunk-LFZ37BSX.mjs} +1 -1
- package/dist/chunk-LNY2VVHE.mjs +18 -0
- package/dist/chunk-NVQS53NT.mjs +665 -0
- package/dist/{chunk-H5AB6NIR.mjs → chunk-O7SLN3GI.mjs} +2 -15
- package/dist/chunk-OQVZBEBY.mjs +283 -0
- package/dist/{chunk-VLTICJOD.mjs → chunk-PIKHN33N.mjs} +1 -1
- package/dist/chunk-TJF5GZSV.mjs +187 -0
- package/dist/chunk-TUD5CJ76.mjs +126 -0
- package/dist/{chunk-YPYVQJ4C.mjs → chunk-UC3SCMIN.mjs} +9 -1
- package/dist/client/index.d.ts +216 -321
- package/dist/client/index.mjs +1192 -1342
- package/dist/client/topology.d.ts +61 -0
- package/dist/client/topology.mjs +682 -0
- package/dist/client-base-CLWmH5Ln.d.ts +234 -0
- package/dist/codegen/cli.d.ts +2 -0
- package/dist/codegen/cli.mjs +11 -0
- package/dist/codegen/index.d.ts +30 -0
- package/dist/codegen/index.mjs +5 -0
- package/dist/core/index.d.ts +37 -16
- package/dist/core/index.mjs +2322 -298
- package/dist/core/writer-worker.mjs +28 -6
- package/dist/{database-DuGp0Rtr.d.ts → database-BY0L5Q2n.d.ts} +59 -14
- package/dist/driver/better-sqlite3.d.ts +1 -1
- package/dist/driver/better-sqlite3.mjs +6 -6
- package/dist/driver/node.d.ts +1 -1
- package/dist/driver/node.mjs +6 -6
- package/dist/driver/wa-sqlite.d.ts +1 -136
- package/dist/{errors-5Nf5ZAEC.d.ts → errors-Bw5MdNCu.d.ts} +5 -1
- package/dist/file-migrations/index.d.ts +7 -5
- package/dist/file-migrations/index.mjs +42 -37
- package/dist/operation-registry-9DcvxcE5.d.ts +45 -0
- package/dist/primary-wins-DPAm2AKG.d.ts +61 -0
- package/dist/protocol-6KrSq2Hy.d.ts +66 -0
- package/dist/react/index.d.ts +20 -0
- package/dist/react/index.mjs +137 -0
- package/dist/replication/coordinator/etcd.d.ts +5 -3
- package/dist/replication/coordinator/etcd.mjs +554 -462
- package/dist/replication/index.d.ts +51 -105
- package/dist/replication/index.mjs +1415 -2061
- package/dist/server/index.d.ts +111 -87
- package/dist/server/index.mjs +1917 -489
- package/dist/server-options-1JHu8pid.d.ts +191 -0
- package/dist/{sirannon-4SspRvP5.d.ts → sirannon-DaQSyhbJ.d.ts} +7 -2
- package/dist/transport/grpc.d.ts +7 -4
- package/dist/transport/grpc.mjs +191 -97
- package/dist/transport/memory.d.ts +5 -134
- package/dist/transport/memory.mjs +3 -1
- package/dist/{types-BsjobKbl.d.ts → types-B7gmEsZW.d.ts} +8 -61
- package/dist/{types-BEu1I_9_.d.ts → types-BsVabqSI.d.ts} +1 -1
- package/dist/types-CL6piSnD.d.ts +43 -0
- package/dist/types-C_D8IhpO.d.ts +60 -0
- package/dist/{types-D_hQW1hr.d.ts → types-zhnRXrsb.d.ts} +55 -165
- package/package.json +44 -6
- package/dist/chunk-TKGHYWQ6.mjs +0 -35
- package/dist/types-D4p4UyDK.d.ts +0 -26
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import '../chunk-
|
|
4
|
-
import { SirannonError } from '../chunk-
|
|
1
|
+
import { executeGroup } from '../chunk-JZGINXTN.mjs';
|
|
2
|
+
import '../chunk-D7LAYTKN.mjs';
|
|
3
|
+
import { WORKER_CANCELLED_CODE, serializeError } from '../chunk-4ISB7XMA.mjs';
|
|
4
|
+
import { SirannonError } from '../chunk-UC3SCMIN.mjs';
|
|
5
5
|
import { parentPort } from 'worker_threads';
|
|
6
6
|
|
|
7
7
|
var STATEMENT_CACHE_CAPACITY = 128;
|
|
@@ -93,7 +93,22 @@ async function dispatch(req) {
|
|
|
93
93
|
return void 0;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
+
var cancelledIds = /* @__PURE__ */ new Set();
|
|
97
|
+
var latestDispatchedId = 0;
|
|
96
98
|
async function handle(req) {
|
|
99
|
+
latestDispatchedId = req.id;
|
|
100
|
+
if (cancelledIds.delete(req.id)) {
|
|
101
|
+
port.postMessage({
|
|
102
|
+
id: req.id,
|
|
103
|
+
ok: false,
|
|
104
|
+
error: {
|
|
105
|
+
message: "The caller abandoned this operation before the worker reached it, so it was not run",
|
|
106
|
+
name: "SirannonError",
|
|
107
|
+
code: WORKER_CANCELLED_CODE
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
97
112
|
try {
|
|
98
113
|
const value = await dispatch(req);
|
|
99
114
|
port.postMessage({ id: req.id, ok: true, value });
|
|
@@ -101,7 +116,14 @@ async function handle(req) {
|
|
|
101
116
|
port.postMessage({ id: req.id, ok: false, error: serializeError(err) });
|
|
102
117
|
}
|
|
103
118
|
}
|
|
119
|
+
function settleDeliveredMessages() {
|
|
120
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
121
|
+
}
|
|
104
122
|
var tail = Promise.resolve();
|
|
105
|
-
port.on("message", (
|
|
106
|
-
|
|
123
|
+
port.on("message", (msg) => {
|
|
124
|
+
if (msg.kind === "cancel") {
|
|
125
|
+
if (msg.id > latestDispatchedId) cancelledIds.add(msg.id);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
tail = tail.then(settleDeliveredMessages).then(() => handle(msg));
|
|
107
129
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { i as QueryHookContext, j as ConnectionHookContext, k as BeforeSubscribeHook, H as HookConfig, l as MetricsConfig, m as QueryMetrics, n as ConnectionMetrics, o as CDCMetrics, h as ChangeEvent, a as SQLiteDriver, D as DatabaseOptions, P as Params, Q as QueryOptions, E as ExecuteResult, T as Transaction, e as SQLiteConnection, p as SubscriptionBuilder, M as Migration, q as MigrationResult, r as RollbackResult, f as BackupScheduleOptions, B as BeforeQueryHook, A as AfterQueryHook } from './types-zhnRXrsb.js';
|
|
2
|
+
import { c as ReplicationChange, C as ConflictResolver, A as ApplyResult, R as ReplicationBatch } from './types-C_D8IhpO.js';
|
|
3
|
+
import { A as AppliedMigrationRow, b as BulkLoadOptions, a as BulkLoadResult } from './server-options-1JHu8pid.js';
|
|
4
|
+
import { L as LiveQueryOptions, a as LiveQuery } from './types-CL6piSnD.js';
|
|
3
5
|
|
|
4
6
|
type HookEvent = 'beforeQuery' | 'afterQuery' | 'beforeConnect' | 'databaseOpen' | 'databaseClose' | 'beforeSubscribe';
|
|
5
7
|
type SubscribeHookContext = Parameters<BeforeSubscribeHook>[0];
|
|
@@ -38,10 +40,56 @@ declare class MetricsCollector {
|
|
|
38
40
|
get active(): boolean;
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
interface StagedRecovery {
|
|
44
|
+
resumeSeq: bigint | null;
|
|
45
|
+
appliedSeq: bigint | null;
|
|
46
|
+
/**
|
|
47
|
+
* The failure that stopped the recovery apply, or null. The staged rows
|
|
48
|
+
* are untouched by the failure and the resume watermark still covers
|
|
49
|
+
* them, so the caller opens the subscription anyway: a schema-gate refusal
|
|
50
|
+
* there is what tells a device it must migrate before this apply can
|
|
51
|
+
* succeed. The caller must then retry the recovery, because the resume
|
|
52
|
+
* watermark is past the transaction this apply left unapplied, so the
|
|
53
|
+
* server sends nothing that would prompt another attempt.
|
|
54
|
+
*/
|
|
55
|
+
applyError: unknown | null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface DeviceSyncPullState {
|
|
59
|
+
seq: bigint;
|
|
60
|
+
epoch: string | undefined;
|
|
61
|
+
}
|
|
62
|
+
interface DeviceSyncPort {
|
|
63
|
+
identity(): Promise<{
|
|
64
|
+
nodeId: string;
|
|
65
|
+
}>;
|
|
66
|
+
applyPulledTransaction(changes: readonly ReplicationChange[], pullSeq: bigint, resolver?: ConflictResolver | ((table: string) => ConflictResolver)): Promise<ApplyResult>;
|
|
67
|
+
stagePulledChanges(events: readonly ChangeEvent[]): Promise<bigint | null>;
|
|
68
|
+
applyStagedPull(resolver?: ConflictResolver | ((table: string) => ConflictResolver), onChange?: (event: ChangeEvent) => void): Promise<bigint | null>;
|
|
69
|
+
recoverStagedPull(resolver?: ConflictResolver | ((table: string) => ConflictResolver), onChange?: (event: ChangeEvent) => void): Promise<StagedRecovery>;
|
|
70
|
+
readOutboxBatch(afterSeq: bigint, limit: number): Promise<ReplicationBatch | null>;
|
|
71
|
+
countOutboxPending(afterSeq: bigint): Promise<number>;
|
|
72
|
+
getPushCursor(): Promise<bigint>;
|
|
73
|
+
setPushCursor(seq: bigint): Promise<void>;
|
|
74
|
+
getPullState(): Promise<DeviceSyncPullState | null>;
|
|
75
|
+
getResyncRequired(): Promise<boolean>;
|
|
76
|
+
setResyncRequired(required: boolean): Promise<void>;
|
|
77
|
+
setPullState(seq: bigint, epoch?: string): Promise<void>;
|
|
78
|
+
protectUnpushedChanges(pushedSeq: bigint): void;
|
|
79
|
+
snapshotLoadPending(): Promise<boolean>;
|
|
80
|
+
beginSnapshotLoad(tables: readonly string[]): Promise<void>;
|
|
81
|
+
applySnapshotSchema(schema: readonly string[]): Promise<void>;
|
|
82
|
+
loadSnapshotPage(table: string, rows: readonly Record<string, unknown>[]): Promise<void>;
|
|
83
|
+
replaceMigrationHistory(rows: readonly AppliedMigrationRow[]): Promise<void>;
|
|
84
|
+
endSnapshotLoad(tables: readonly string[]): Promise<void>;
|
|
85
|
+
abortSnapshotLoad(): Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
|
|
41
88
|
interface DatabaseInternals {
|
|
42
89
|
parentHooks?: HookRegistry;
|
|
43
90
|
metrics?: MetricsCollector;
|
|
44
91
|
}
|
|
92
|
+
|
|
45
93
|
declare class Database {
|
|
46
94
|
readonly id: string;
|
|
47
95
|
readonly path: string;
|
|
@@ -56,25 +104,18 @@ declare class Database {
|
|
|
56
104
|
private readonly closeListeners;
|
|
57
105
|
private _closed;
|
|
58
106
|
private readonly cdc;
|
|
107
|
+
private readonly sync;
|
|
59
108
|
private readonly hookRegistry;
|
|
60
109
|
private readonly observer;
|
|
61
110
|
private readonly backups;
|
|
111
|
+
private readonly reads;
|
|
62
112
|
private constructor();
|
|
63
113
|
static create(id: string, path: string, driver: SQLiteDriver, options?: DatabaseOptions, internals?: DatabaseInternals): Promise<Database>;
|
|
114
|
+
applyChanges(batch: ReplicationBatch, resolver?: ConflictResolver | ((table: string) => ConflictResolver)): Promise<ApplyResult>;
|
|
115
|
+
deviceSync(): DeviceSyncPort;
|
|
64
116
|
query<T = Record<string, unknown>>(sql: string, params?: Params, options?: QueryOptions): Promise<T[]>;
|
|
65
|
-
/**
|
|
66
|
-
* Returns query rows already encoded for the wire (safe-range integers as
|
|
67
|
-
* plain numbers, larger integers and BLOBs as tagged envelopes) in a single
|
|
68
|
-
* pass. The server response path uses this so a read walks its values once
|
|
69
|
-
* rather than narrowing on the driver and re-scanning to tag.
|
|
70
|
-
*/
|
|
71
117
|
queryForWire(sql: string, params?: Params, options?: QueryOptions): Promise<unknown[]>;
|
|
72
118
|
queryOne<T = Record<string, unknown>>(sql: string, params?: Params, options?: QueryOptions): Promise<T | undefined>;
|
|
73
|
-
/**
|
|
74
|
-
* On a single-connection driver the reader is the writer, so serialise the
|
|
75
|
-
* read through the writer lock to avoid a bulk load's uncommitted rows.
|
|
76
|
-
*/
|
|
77
|
-
private runRead;
|
|
78
119
|
execute(sql: string, params?: Params, options?: QueryOptions): Promise<ExecuteResult>;
|
|
79
120
|
executeBatch(sql: string, paramsBatch: Params[], options?: QueryOptions): Promise<ExecuteResult[]>;
|
|
80
121
|
private runInTransaction;
|
|
@@ -108,9 +149,12 @@ declare class Database {
|
|
|
108
149
|
* lock and stalls the event loop on `busy_timeout`.
|
|
109
150
|
*/
|
|
110
151
|
runCdcMaintenance(op: (writer: SQLiteConnection) => Promise<unknown>): Promise<void>;
|
|
152
|
+
ensureChangeStamping(): Promise<void>;
|
|
111
153
|
unwatch(table: string): Promise<void>;
|
|
112
154
|
on(table: string): SubscriptionBuilder;
|
|
155
|
+
live<T = Record<string, unknown>>(sql: string, params?: Params, options?: LiveQueryOptions): Promise<LiveQuery<T>>;
|
|
113
156
|
migrate(migrations: Migration[]): Promise<MigrationResult>;
|
|
157
|
+
appliedMigrations(): Promise<AppliedMigrationRow[]>;
|
|
114
158
|
rollback(migrations: Migration[], version?: number): Promise<RollbackResult>;
|
|
115
159
|
backup(destPath: string): Promise<void>;
|
|
116
160
|
scheduleBackup(options: BackupScheduleOptions): void;
|
|
@@ -122,6 +166,7 @@ declare class Database {
|
|
|
122
166
|
get closed(): boolean;
|
|
123
167
|
get readerCount(): number;
|
|
124
168
|
private ensureOpen;
|
|
169
|
+
private ensureNotClosed;
|
|
125
170
|
}
|
|
126
171
|
|
|
127
|
-
export { Database as D, type HookDispose as H, MetricsCollector as M, type SubscribeHookContext as S, type HookEvent as a, type HookEventContextMap as b, type HookHandler as c, HookRegistry as d };
|
|
172
|
+
export { Database as D, type HookDispose as H, MetricsCollector as M, type SubscribeHookContext as S, type HookEvent as a, type HookEventContextMap as b, type HookHandler as c, HookRegistry as d, type DeviceSyncPort as e };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { nodeResolveExtensionPath, nodeBackupEngine, nodeWriterContext, WriterWorker, createStatementCache, narrowSafeBigInt, narrowRowIntegers, narrowRowsIntegers } from '../chunk-
|
|
2
|
-
import
|
|
3
|
-
import '../chunk-
|
|
4
|
-
import
|
|
5
|
-
import '../chunk-
|
|
6
|
-
import '../chunk-
|
|
1
|
+
import { nodeResolveExtensionPath, nodeBackupEngine, nodeWriterContext, WriterWorker, createStatementCache, narrowSafeBigInt, narrowRowIntegers, narrowRowsIntegers } from '../chunk-EBJXPQQO.mjs';
|
|
2
|
+
import '../chunk-PIKHN33N.mjs';
|
|
3
|
+
import { defineDriver } from '../chunk-FHWTZFI4.mjs';
|
|
4
|
+
import '../chunk-4ISB7XMA.mjs';
|
|
5
|
+
import { synchronousPragmaValue } from '../chunk-LFZ37BSX.mjs';
|
|
6
|
+
import '../chunk-UC3SCMIN.mjs';
|
|
7
7
|
|
|
8
8
|
// src/drivers/better-sqlite3/index.ts
|
|
9
9
|
function createConnection(db) {
|
package/dist/driver/node.d.ts
CHANGED
package/dist/driver/node.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { nodeResolveExtensionPath, nodeBackupEngine, nodeWriterContext, createStatementCache, WriterWorker, narrowSafeBigInt, narrowRowIntegers, narrowRowsIntegers } from '../chunk-
|
|
2
|
-
import
|
|
3
|
-
import '../chunk-
|
|
4
|
-
import
|
|
5
|
-
import '../chunk-
|
|
6
|
-
import '../chunk-
|
|
1
|
+
import { nodeResolveExtensionPath, nodeBackupEngine, nodeWriterContext, createStatementCache, WriterWorker, narrowSafeBigInt, narrowRowIntegers, narrowRowsIntegers } from '../chunk-EBJXPQQO.mjs';
|
|
2
|
+
import '../chunk-PIKHN33N.mjs';
|
|
3
|
+
import { defineDriver } from '../chunk-FHWTZFI4.mjs';
|
|
4
|
+
import '../chunk-4ISB7XMA.mjs';
|
|
5
|
+
import { synchronousPragmaValue } from '../chunk-LFZ37BSX.mjs';
|
|
6
|
+
import '../chunk-UC3SCMIN.mjs';
|
|
7
7
|
|
|
8
8
|
// src/drivers/node/index.ts
|
|
9
9
|
function nodeSqlite(driverOptions) {
|
|
@@ -1,139 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
interface BackupScheduleOptions {
|
|
3
|
-
/** Cron expression (e.g., '0 * * * *' for hourly). */
|
|
4
|
-
cron: string;
|
|
5
|
-
/** Directory to store backup files. */
|
|
6
|
-
destDir: string;
|
|
7
|
-
/** Maximum number of backup files to keep. Default: 5. */
|
|
8
|
-
maxFiles?: number;
|
|
9
|
-
/**
|
|
10
|
-
* Sirannon evaluates the cron expression in this IANA time zone (e.g. 'America/New_York').
|
|
11
|
-
* When omitted, it uses the host's local time zone, which also sets the daylight saving rules that apply.
|
|
12
|
-
*/
|
|
13
|
-
timezone?: string;
|
|
14
|
-
/** Called when a scheduled backup fails. Without this, errors are silently discarded. */
|
|
15
|
-
onError?: (error: Error) => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface WorkerHostOptions {
|
|
19
|
-
writeTimeoutMs?: number;
|
|
20
|
-
maxRestarts?: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface RunResult {
|
|
24
|
-
changes: number;
|
|
25
|
-
lastInsertRowId: number | bigint;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Tells a caller running inside the operation that holds the writer from one
|
|
29
|
-
* merely waiting on it. A runtime without async context tracking cannot answer
|
|
30
|
-
* this, and answering it wrongly runs one caller's writes inside another
|
|
31
|
-
* caller's transaction.
|
|
32
|
-
*/
|
|
33
|
-
interface WriterContext {
|
|
34
|
-
run<T>(operation: () => T): T;
|
|
35
|
-
isActive(): boolean;
|
|
36
|
-
exit<T>(operation: () => T): T;
|
|
37
|
-
}
|
|
38
|
-
interface BackupEngine {
|
|
39
|
-
backup(conn: SQLiteConnection, destPath: string): Promise<void>;
|
|
40
|
-
schedule(conn: SQLiteConnection, options: BackupScheduleOptions, runExclusive: (op: () => Promise<void>) => Promise<void>): () => void;
|
|
41
|
-
}
|
|
42
|
-
interface BatchSummary {
|
|
43
|
-
rowsLoaded: number;
|
|
44
|
-
changes: number;
|
|
45
|
-
}
|
|
46
|
-
interface SQLiteStatement {
|
|
47
|
-
all<T = unknown>(...params: unknown[]): Promise<T[]>;
|
|
48
|
-
get<T = unknown>(...params: unknown[]): Promise<T | undefined>;
|
|
49
|
-
run(...params: unknown[]): Promise<RunResult>;
|
|
50
|
-
/**
|
|
51
|
-
* Like {@link all} but skips the safe-range BigInt narrowing, leaving every
|
|
52
|
-
* integer as a BigInt. The server wire path narrows and tags in one pass, so
|
|
53
|
-
* feeding it raw rows avoids a second walk. Optional: a driver that omits it
|
|
54
|
-
* falls back to {@link all}, still correct but with the extra narrowing walk.
|
|
55
|
-
*/
|
|
56
|
-
allRaw?<T = unknown>(...params: unknown[]): Promise<T[]>;
|
|
57
|
-
}
|
|
58
|
-
interface GroupRunError {
|
|
59
|
-
message: string;
|
|
60
|
-
name?: string;
|
|
61
|
-
code?: string;
|
|
62
|
-
}
|
|
63
|
-
type GroupRunOutcome = {
|
|
64
|
-
ok: true;
|
|
65
|
-
results: RunResult[];
|
|
66
|
-
} | {
|
|
67
|
-
ok: false;
|
|
68
|
-
error: GroupRunError;
|
|
69
|
-
};
|
|
70
|
-
interface SQLiteConnection {
|
|
71
|
-
exec(sql: string): Promise<void>;
|
|
72
|
-
prepare(sql: string): Promise<SQLiteStatement>;
|
|
73
|
-
transaction<T>(fn: (conn: SQLiteConnection) => Promise<T>): Promise<T>;
|
|
74
|
-
close(): Promise<void>;
|
|
75
|
-
runBatch?(sql: string, paramsBatch: readonly unknown[][]): Promise<RunResult[]>;
|
|
76
|
-
runBatchSummary?(sql: string, paramsBatch: readonly unknown[][]): Promise<BatchSummary>;
|
|
77
|
-
/**
|
|
78
|
-
* Runs several independent units in one transaction, one outcome per unit in
|
|
79
|
-
* order. A unit is one write or one whole transaction, and a unit that fails
|
|
80
|
-
* must not disturb the others.
|
|
81
|
-
*/
|
|
82
|
-
runGroup?(units: readonly {
|
|
83
|
-
statements: readonly {
|
|
84
|
-
sql: string;
|
|
85
|
-
params?: readonly unknown[];
|
|
86
|
-
}[];
|
|
87
|
-
}[]): Promise<GroupRunOutcome[]>;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* SQLite `PRAGMA synchronous` level applied to a connection. `normal` is safe
|
|
91
|
-
* from corruption in WAL mode but can lose the most recent commits on power
|
|
92
|
-
* loss; `full` fsyncs every commit; `extra` adds a directory sync after the
|
|
93
|
-
* rollback journal is unlinked in DELETE journal mode and equals `full` in
|
|
94
|
-
* WAL mode; `off` hands writes to the OS without syncing and is sanctioned
|
|
95
|
-
* only for re-runnable bulk loads.
|
|
96
|
-
*/
|
|
97
|
-
type SynchronousLevel = 'off' | 'normal' | 'full' | 'extra';
|
|
98
|
-
interface OpenOptions {
|
|
99
|
-
readonly?: boolean;
|
|
100
|
-
walMode?: boolean;
|
|
101
|
-
synchronous?: SynchronousLevel;
|
|
102
|
-
}
|
|
103
|
-
interface DriverCapabilities {
|
|
104
|
-
multipleConnections: boolean;
|
|
105
|
-
extensions: boolean;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Lets a worker thread rebuild the driver, since the driver's `open` function
|
|
109
|
-
* cannot cross the thread boundary. `specifier` must be importable from the
|
|
110
|
-
* worker and `config` must survive a structured clone; the worker imports the
|
|
111
|
-
* module and calls its `exportName` factory (default export otherwise) with it.
|
|
112
|
-
*/
|
|
113
|
-
interface DriverWorkerEntry {
|
|
114
|
-
specifier: string;
|
|
115
|
-
exportName?: string;
|
|
116
|
-
config?: unknown;
|
|
117
|
-
}
|
|
118
|
-
interface SQLiteDriver {
|
|
119
|
-
readonly capabilities: DriverCapabilities;
|
|
120
|
-
open(path: string, options?: OpenOptions): Promise<SQLiteConnection>;
|
|
121
|
-
readonly worker?: DriverWorkerEntry;
|
|
122
|
-
/**
|
|
123
|
-
* Offloads writes to a worker thread. Only a driver whose runtime has
|
|
124
|
-
* threads implements this, which is what keeps the thread machinery out of
|
|
125
|
-
* bundles built for runtimes that do not.
|
|
126
|
-
*/
|
|
127
|
-
startWriterHost?(path: string, options: OpenOptions, hostOptions?: WorkerHostOptions): Promise<SQLiteConnection>;
|
|
128
|
-
createWriterContext?(): WriterContext;
|
|
129
|
-
createBackupEngine?(): BackupEngine;
|
|
130
|
-
/**
|
|
131
|
-
* Makes an extension path absolute. Passing a bare name to `load_extension`
|
|
132
|
-
* would let the dynamic linker search its own paths and open a different
|
|
133
|
-
* library than the operator named.
|
|
134
|
-
*/
|
|
135
|
-
resolveExtensionPath?(extensionPath: string): string;
|
|
136
|
-
}
|
|
1
|
+
import { a as SQLiteDriver } from '../types-zhnRXrsb.js';
|
|
137
2
|
|
|
138
3
|
interface WaSqliteOptions {
|
|
139
4
|
vfs?: 'IDBBatchAtomicVFS' | 'AccessHandlePoolVFS';
|
|
@@ -60,6 +60,10 @@ declare class MigrationError extends SirannonError {
|
|
|
60
60
|
declare class HookDeniedError extends SirannonError {
|
|
61
61
|
constructor(hookName: string, reason?: string);
|
|
62
62
|
}
|
|
63
|
+
declare class RequestDeniedError extends SirannonError {
|
|
64
|
+
readonly status: number;
|
|
65
|
+
constructor(status: number, code: string, message: string);
|
|
66
|
+
}
|
|
63
67
|
/**
|
|
64
68
|
* Thrown when the change-data-capture pipeline encounters an unrecoverable
|
|
65
69
|
* error, such as a failed event dispatch or a corrupt change record.
|
|
@@ -116,4 +120,4 @@ declare class ExtensionError extends SirannonError {
|
|
|
116
120
|
constructor(path: string, cause?: string);
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
export { BackupError as B, CDCError as C, DatabaseAlreadyExistsError as D, ExtensionError as E, ForbiddenSqlError as F, HookDeniedError as H, MaxDatabasesError as M, QueryError as Q, ReadOnlyError as R, SirannonError as S, TransactionError as T, WriteOverloadError as W, ConnectionPoolError as a, DatabaseNotFoundError as b, MigrationError as c };
|
|
123
|
+
export { BackupError as B, CDCError as C, DatabaseAlreadyExistsError as D, ExtensionError as E, ForbiddenSqlError as F, HookDeniedError as H, MaxDatabasesError as M, QueryError as Q, ReadOnlyError as R, SirannonError as S, TransactionError as T, WriteOverloadError as W, ConnectionPoolError as a, DatabaseNotFoundError as b, MigrationError as c, RequestDeniedError as d };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import '../types-
|
|
1
|
+
import { B as BaselineFileOption } from '../baseline-Br77Fnhb.js';
|
|
2
|
+
import { M as Migration } from '../types-zhnRXrsb.js';
|
|
3
3
|
|
|
4
|
+
interface LoadMigrationsOptions {
|
|
5
|
+
baseline?: BaselineFileOption;
|
|
6
|
+
}
|
|
4
7
|
interface ScannedMigration {
|
|
5
8
|
version: number;
|
|
6
9
|
name: string;
|
|
@@ -9,7 +12,6 @@ interface ScannedMigration {
|
|
|
9
12
|
}
|
|
10
13
|
declare function scanDirectory(dirPath: string): ScannedMigration[];
|
|
11
14
|
declare function readUpMigrations(scanned: ScannedMigration[]): Migration[];
|
|
12
|
-
declare function
|
|
13
|
-
declare function loadMigrations(dirPath: string): Migration[];
|
|
15
|
+
declare function loadMigrations(dirPath: string, options?: LoadMigrationsOptions): Migration[];
|
|
14
16
|
|
|
15
|
-
export { type ScannedMigration, loadMigrations,
|
|
17
|
+
export { type LoadMigrationsOptions, type ScannedMigration, loadMigrations, readUpMigrations, scanDirectory };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseMigrationFilename, LAZY_DOWN_SQL } from '../chunk-LNY2VVHE.mjs';
|
|
2
|
+
import { applyBaselineOption } from '../chunk-67M7KAH6.mjs';
|
|
3
|
+
import { MigrationError } from '../chunk-UC3SCMIN.mjs';
|
|
2
4
|
import { statSync, readdirSync, readFileSync } from 'fs';
|
|
3
5
|
import { resolve, join } from 'path';
|
|
4
6
|
|
|
5
|
-
var MIGRATION_FILENAME_PATTERN = /^(\d+)_(\w+)\.(up|down)\.sql$/;
|
|
6
7
|
function hasControlCharacters(s) {
|
|
7
8
|
for (let i = 0; i < s.length; i++) {
|
|
8
9
|
if (s.charCodeAt(i) <= 31) return true;
|
|
@@ -35,14 +36,9 @@ function scanDirectory(dirPath) {
|
|
|
35
36
|
const grouped = /* @__PURE__ */ new Map();
|
|
36
37
|
for (const entry of entries) {
|
|
37
38
|
if (!entry.isFile()) continue;
|
|
38
|
-
const
|
|
39
|
-
if (!
|
|
40
|
-
const version =
|
|
41
|
-
const name = match[2];
|
|
42
|
-
const direction = match[3];
|
|
43
|
-
if (!Number.isFinite(version) || version <= 0 || !Number.isSafeInteger(version)) {
|
|
44
|
-
throw new MigrationError(`Invalid migration version: ${match[1]}`, version, "MIGRATION_VALIDATION_ERROR");
|
|
45
|
-
}
|
|
39
|
+
const parsed = parseMigrationFilename(entry.name);
|
|
40
|
+
if (!parsed) continue;
|
|
41
|
+
const { version, name, direction } = parsed;
|
|
46
42
|
const existing = grouped.get(version);
|
|
47
43
|
if (existing && existing.name !== name) {
|
|
48
44
|
throw new MigrationError(
|
|
@@ -94,35 +90,44 @@ function readUpMigrations(scanned) {
|
|
|
94
90
|
};
|
|
95
91
|
});
|
|
96
92
|
}
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
entry.version,
|
|
105
|
-
"MIGRATION_NO_DOWN"
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
const downSql = readFileSync(entry.downPath, "utf-8").trim();
|
|
109
|
-
if (downSql.length === 0) {
|
|
110
|
-
throw new MigrationError(
|
|
111
|
-
`Down migration file is empty: ${entry.downPath}`,
|
|
112
|
-
entry.version,
|
|
113
|
-
"MIGRATION_VALIDATION_ERROR"
|
|
114
|
-
);
|
|
93
|
+
function readDownSql(downPath, version) {
|
|
94
|
+
let contents;
|
|
95
|
+
try {
|
|
96
|
+
contents = readFileSync(downPath, "utf-8");
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
99
|
+
throw new MigrationError(`Down migration file no longer exists: ${downPath}`, version, "MIGRATION_NO_DOWN");
|
|
115
100
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
101
|
+
throw new MigrationError(
|
|
102
|
+
`Failed to read down migration file ${downPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
103
|
+
version,
|
|
104
|
+
"MIGRATION_ROLLBACK_ERROR"
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
const downSql = contents.trim();
|
|
108
|
+
if (downSql.length === 0) {
|
|
109
|
+
throw new MigrationError(`Down migration file is empty: ${downPath}`, version, "MIGRATION_VALIDATION_ERROR");
|
|
110
|
+
}
|
|
111
|
+
return downSql;
|
|
112
|
+
}
|
|
113
|
+
function attachLazyDown(migration, downPath) {
|
|
114
|
+
Object.defineProperty(migration, LAZY_DOWN_SQL, {
|
|
115
|
+
value: () => readDownSql(downPath, migration.version),
|
|
116
|
+
enumerable: false,
|
|
117
|
+
configurable: false,
|
|
118
|
+
writable: false
|
|
122
119
|
});
|
|
123
120
|
}
|
|
124
|
-
function loadMigrations(dirPath) {
|
|
125
|
-
|
|
121
|
+
function loadMigrations(dirPath, options) {
|
|
122
|
+
const scanned = scanDirectory(dirPath);
|
|
123
|
+
const migrations = readUpMigrations(scanned);
|
|
124
|
+
for (let i = 0; i < migrations.length; i++) {
|
|
125
|
+
const downPath = scanned[i].downPath;
|
|
126
|
+
if (downPath !== null) {
|
|
127
|
+
attachLazyDown(migrations[i], downPath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return applyBaselineOption(migrations, options?.baseline);
|
|
126
131
|
}
|
|
127
132
|
|
|
128
|
-
export { loadMigrations,
|
|
133
|
+
export { loadMigrations, readUpMigrations, scanDirectory };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { P as Params } from './types-zhnRXrsb.js';
|
|
2
|
+
|
|
3
|
+
interface OperationStatement {
|
|
4
|
+
sql: string;
|
|
5
|
+
params?: Params;
|
|
6
|
+
}
|
|
7
|
+
type OperationArguments = Record<string, unknown>;
|
|
8
|
+
interface ReadOperation<Identity = unknown> {
|
|
9
|
+
args?: readonly string[];
|
|
10
|
+
fromIdentity?: Readonly<Record<string, keyof Identity & string>>;
|
|
11
|
+
/**
|
|
12
|
+
* The columns every row of this read carries. Code generation emits a typed
|
|
13
|
+
* row from it, and reads the columns from the statement only when the
|
|
14
|
+
* operation takes no arguments, because arguments choose the statement.
|
|
15
|
+
*/
|
|
16
|
+
columns?: readonly string[];
|
|
17
|
+
statement(args: OperationArguments): OperationStatement;
|
|
18
|
+
}
|
|
19
|
+
interface WriteOperation<Identity = unknown> {
|
|
20
|
+
args?: readonly string[];
|
|
21
|
+
fromIdentity?: Readonly<Record<string, keyof Identity & string>>;
|
|
22
|
+
statements(args: OperationArguments): OperationStatement | readonly OperationStatement[];
|
|
23
|
+
}
|
|
24
|
+
interface DatabaseOperations<Identity = unknown> {
|
|
25
|
+
reads?: Readonly<Record<string, ReadOperation<Identity>>>;
|
|
26
|
+
writes?: Readonly<Record<string, WriteOperation<Identity>>>;
|
|
27
|
+
}
|
|
28
|
+
type OperationRegistry<Identity = unknown> = Readonly<Record<string, DatabaseOperations<Identity>>>;
|
|
29
|
+
/**
|
|
30
|
+
* A named operation a remote caller invokes, carrying the argument and row
|
|
31
|
+
* types of the registered operation. Only `name` exists at runtime; `types`
|
|
32
|
+
* is never assigned and is present so both type parameters are inferable at
|
|
33
|
+
* the call site. Code generation emits one reference per registered operation.
|
|
34
|
+
*/
|
|
35
|
+
interface OperationRef<Args = OperationArguments, Row = Record<string, unknown>> {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly types?: {
|
|
38
|
+
args: Args;
|
|
39
|
+
row: Row;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
declare function operationRef<Args = OperationArguments, Row = Record<string, unknown>>(name: string): OperationRef<Args, Row>;
|
|
43
|
+
declare function operationName(operation: string | OperationRef<never, never>): string;
|
|
44
|
+
|
|
45
|
+
export { type DatabaseOperations as D, type OperationRef as O, type ReadOperation as R, type WriteOperation as W, type OperationRegistry as a, type OperationArguments as b, type OperationStatement as c, operationRef as d, operationName as o };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { C as ConflictResolver, a as ConflictContext, b as ConflictResolution } from './types-C_D8IhpO.js';
|
|
2
|
+
|
|
3
|
+
type ColumnVersionGetter = (table: string, rowId: string) => Promise<Map<string, {
|
|
4
|
+
hlc: string;
|
|
5
|
+
nodeId: string;
|
|
6
|
+
}>>;
|
|
7
|
+
/**
|
|
8
|
+
* Per-column conflict resolver that merges non-overlapping field changes.
|
|
9
|
+
*
|
|
10
|
+
* Given a conflict between a local and remote write on the same row, this
|
|
11
|
+
* resolver computes the set of columns each side changed relative to the
|
|
12
|
+
* common ancestor (oldData). If the changed column sets don't overlap, both
|
|
13
|
+
* sides' changes are merged into a single row, avoiding unnecessary data
|
|
14
|
+
* loss. When columns do overlap, per-column HLC versions (retrieved via the
|
|
15
|
+
* injected `getColumnVersions` callback) determine which side's value wins
|
|
16
|
+
* for each contested column. If no column version metadata exists, the
|
|
17
|
+
* resolver falls back to whole-row LWW.
|
|
18
|
+
*/
|
|
19
|
+
declare class FieldMergeResolver implements ConflictResolver {
|
|
20
|
+
private readonly getColumnVersions;
|
|
21
|
+
private readonly lww;
|
|
22
|
+
constructor(getColumnVersions: ColumnVersionGetter);
|
|
23
|
+
resolve(ctx: ConflictContext): Promise<ConflictResolution>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Last-Writer-Wins conflict resolver.
|
|
28
|
+
*
|
|
29
|
+
* A remote delete is accepted whatever the timestamps say, so a delete wins over
|
|
30
|
+
* a concurrent update and a deleted row is never resurrected.
|
|
31
|
+
*
|
|
32
|
+
* Otherwise compares the remote HLC against the local HLC. The higher timestamp wins.
|
|
33
|
+
* When both timestamps are equal (concurrent writes within the same
|
|
34
|
+
* millisecond and logical tick), the tie is broken deterministically by
|
|
35
|
+
* comparing node IDs lexicographically, so that every node reaches the same
|
|
36
|
+
* resolution without coordination. This is the default resolver and the
|
|
37
|
+
* fallback used by PrimaryWinsResolver and FieldMergeResolver when they
|
|
38
|
+
* cannot make a more specific decision.
|
|
39
|
+
*/
|
|
40
|
+
declare class LWWResolver implements ConflictResolver {
|
|
41
|
+
resolve(ctx: ConflictContext): ConflictResolution;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Conflict resolver that unconditionally favors the designated primary node.
|
|
46
|
+
*
|
|
47
|
+
* If the remote change originated from the primary, it is accepted. If the
|
|
48
|
+
* local change originated from the primary, the remote is rejected. When
|
|
49
|
+
* neither side is the primary (e.g., two replicas syncing through a relay),
|
|
50
|
+
* the decision falls back to LWW ordering. This resolver is designed for
|
|
51
|
+
* primary-replica topologies where the primary is the authoritative source of
|
|
52
|
+
* truth and replica-side writes should never override it.
|
|
53
|
+
*/
|
|
54
|
+
declare class PrimaryWinsResolver implements ConflictResolver {
|
|
55
|
+
private readonly primaryNodeId;
|
|
56
|
+
private readonly lww;
|
|
57
|
+
constructor(primaryNodeId: string);
|
|
58
|
+
resolve(ctx: ConflictContext): ConflictResolution;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { FieldMergeResolver as F, LWWResolver as L, PrimaryWinsResolver as P };
|