@delali/sirannon-db 0.1.6 → 0.1.7
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 +134 -19
- package/dist/backup-scheduler/index.d.ts +18 -3
- package/dist/backup-scheduler/index.mjs +2 -2
- package/dist/{change-tracker-CFTQ9TSn.d.ts → change-tracker-CbmaMO-N.d.ts} +12 -2
- package/dist/chunk-4IGMIJQK.mjs +318 -0
- package/dist/chunk-BNUTBHHH.mjs +22 -0
- package/dist/chunk-CJLYFDP5.mjs +26 -0
- package/dist/chunk-CW6S3WL5.mjs +222 -0
- package/dist/chunk-DJLX6CAE.mjs +20 -0
- package/dist/chunk-DVWQD3GF.mjs +49 -0
- package/dist/chunk-GEZUUIKV.mjs +268 -0
- package/dist/{chunk-UVMVN3OT.mjs → chunk-H5AB6NIR.mjs} +1 -1
- package/dist/{chunk-UTO3ZAFS.mjs → chunk-HHRMRFFR.mjs} +148 -28
- package/dist/chunk-TKGHYWQ6.mjs +35 -0
- package/dist/chunk-VLTICJOD.mjs +470 -0
- package/dist/{chunk-O7BHI3CF.mjs → chunk-YPYVQJ4C.mjs} +15 -1
- package/dist/client/index.d.ts +107 -15
- package/dist/client/index.mjs +348 -37
- package/dist/core/index.d.ts +70 -13
- package/dist/core/index.mjs +635 -192
- package/dist/core/writer-worker.d.ts +2 -0
- package/dist/core/writer-worker.mjs +107 -0
- package/dist/{database-BVY1GqE7.d.ts → database-DuGp0Rtr.d.ts} +51 -19
- package/dist/driver/better-sqlite3.d.ts +1 -1
- package/dist/driver/better-sqlite3.mjs +44 -6
- package/dist/driver/bun.mjs +35 -6
- package/dist/driver/expo.mjs +2 -2
- package/dist/driver/node.d.ts +1 -1
- package/dist/driver/node.mjs +48 -5
- package/dist/driver/wa-sqlite.d.ts +109 -0
- package/dist/driver/wa-sqlite.mjs +30 -2
- package/dist/{errors-C00ed08Q.d.ts → errors-5Nf5ZAEC.d.ts} +19 -1
- package/dist/file-migrations/index.d.ts +2 -3
- package/dist/file-migrations/index.mjs +1 -1
- package/dist/replication/coordinator/etcd.mjs +2 -2
- package/dist/replication/index.d.ts +7 -8
- package/dist/replication/index.mjs +79 -78
- package/dist/server/index.d.ts +111 -28
- package/dist/server/index.mjs +1016 -384
- package/dist/{sirannon-Cd-lK6T0.d.ts → sirannon-4SspRvP5.d.ts} +3 -3
- package/dist/transport/grpc.d.ts +3 -4
- package/dist/transport/grpc.mjs +2 -2
- package/dist/{types-Lc7ywFx7.d.ts → types-BsjobKbl.d.ts} +2 -2
- package/dist/{types-BeozgNPr.d.ts → types-D4p4UyDK.d.ts} +1 -1
- package/dist/types-D_hQW1hr.d.ts +494 -0
- package/package.json +3 -23
- package/dist/chunk-3MCMONVP.mjs +0 -115
- package/dist/chunk-74UN4DIE.mjs +0 -14
- package/dist/chunk-FB2U2Q3Y.mjs +0 -21
- package/dist/chunk-GS7T5YMI.mjs +0 -51
- package/dist/chunk-PXKAKK2V.mjs +0 -124
- package/dist/index-CLdNrcPz.d.ts +0 -16
- package/dist/types-BFSsG77t.d.ts +0 -29
- package/dist/types-D-74JiXb.d.ts +0 -265
package/dist/client/index.mjs
CHANGED
|
@@ -9,13 +9,27 @@ var RemoteSubscriptionBuilderImpl = class {
|
|
|
9
9
|
this.conditions = { ...this.conditions, ...conditions };
|
|
10
10
|
return this;
|
|
11
11
|
}
|
|
12
|
-
subscribe(callback) {
|
|
12
|
+
subscribe(callback, options) {
|
|
13
13
|
const filter = Object.keys(this.conditions).length > 0 ? this.conditions : void 0;
|
|
14
|
-
return this.transport.subscribe(this.table, filter, callback);
|
|
14
|
+
return this.transport.subscribe(this.table, filter, callback, options);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/client/types.ts
|
|
19
|
+
var RemoteError = class extends Error {
|
|
20
|
+
code;
|
|
21
|
+
constructor(code, message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "RemoteError";
|
|
24
|
+
this.code = code;
|
|
15
25
|
}
|
|
16
26
|
};
|
|
17
27
|
|
|
18
28
|
// src/client/database-proxy.ts
|
|
29
|
+
var DEFAULT_LOAD_BATCH_SIZE = 1e3;
|
|
30
|
+
function isAsyncIterable(value) {
|
|
31
|
+
return typeof value[Symbol.asyncIterator] === "function";
|
|
32
|
+
}
|
|
19
33
|
var RemoteDatabase = class {
|
|
20
34
|
constructor(id, transport, onDispose) {
|
|
21
35
|
this.id = id;
|
|
@@ -47,13 +61,96 @@ var RemoteDatabase = class {
|
|
|
47
61
|
* Execute multiple statements as a single atomic transaction.
|
|
48
62
|
* Returns an array of results, one per statement.
|
|
49
63
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
64
|
+
* The whole list travels in one request and commits or rolls back as a
|
|
65
|
+
* unit, so the client is never in the loop between statements.
|
|
52
66
|
*/
|
|
53
67
|
async transaction(statements) {
|
|
54
68
|
const response = await this.transport.transaction(statements);
|
|
55
69
|
return response.results;
|
|
56
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Run the same statement once per parameter set as a single atomic
|
|
73
|
+
* transaction that commits with one fsync. Returns one result per
|
|
74
|
+
* parameter set, in order. Use this for a burst of same-shape writes
|
|
75
|
+
* (an import, a bulk insert) that must all commit or all roll back.
|
|
76
|
+
*/
|
|
77
|
+
async batch(sql, paramsBatch, writeConcern) {
|
|
78
|
+
const response = await this.transport.batch(sql, paramsBatch, writeConcern);
|
|
79
|
+
return response.results;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Load a whole dataset through the same statement, batching it into requests
|
|
83
|
+
* for you and paying the one fsyncing WAL checkpoint once, after the final
|
|
84
|
+
* batch. The configured durability is restored after every batch, so an
|
|
85
|
+
* import that stops partway never leaves the writer at the relaxed level.
|
|
86
|
+
* Prefer this over {@link load} for anything larger than a single request:
|
|
87
|
+
* it runs the finalize itself, so there is no checkpoint flag to forget.
|
|
88
|
+
*
|
|
89
|
+
* Accepts a synchronous or asynchronous iterable of parameter sets, so rows
|
|
90
|
+
* can stream from a file or the network without being held in memory at
|
|
91
|
+
* once. Returns the total rows loaded and changes applied.
|
|
92
|
+
*
|
|
93
|
+
* ```ts
|
|
94
|
+
* const summary = await db.loadAll(
|
|
95
|
+
* 'INSERT INTO events (id, payload) VALUES (?, ?)',
|
|
96
|
+
* rowStream,
|
|
97
|
+
* { batchSize: 5000, durability: 'off' },
|
|
98
|
+
* )
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
async loadAll(sql, rows, options) {
|
|
102
|
+
const batchSize = options?.batchSize ?? DEFAULT_LOAD_BATCH_SIZE;
|
|
103
|
+
if (!Number.isInteger(batchSize) || batchSize <= 0) {
|
|
104
|
+
throw new RemoteError("INVALID_ARGUMENT", "loadAll batchSize must be a positive integer");
|
|
105
|
+
}
|
|
106
|
+
const durability = options?.durability;
|
|
107
|
+
const total = { rowsLoaded: 0, changes: 0 };
|
|
108
|
+
const send = async (paramsBatch, checkpoint) => {
|
|
109
|
+
const summary = await this.transport.load(sql, paramsBatch, durability, checkpoint);
|
|
110
|
+
total.rowsLoaded += summary.rowsLoaded;
|
|
111
|
+
total.changes += summary.changes;
|
|
112
|
+
};
|
|
113
|
+
let batch = [];
|
|
114
|
+
let previous = null;
|
|
115
|
+
const rotate = async () => {
|
|
116
|
+
if (previous !== null) await send(previous, false);
|
|
117
|
+
previous = batch;
|
|
118
|
+
batch = [];
|
|
119
|
+
};
|
|
120
|
+
if (isAsyncIterable(rows)) {
|
|
121
|
+
for await (const row of rows) {
|
|
122
|
+
batch.push(row);
|
|
123
|
+
if (batch.length >= batchSize) await rotate();
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
for (const row of rows) {
|
|
127
|
+
batch.push(row);
|
|
128
|
+
if (batch.length >= batchSize) await rotate();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (batch.length > 0) {
|
|
132
|
+
if (previous !== null) await send(previous, false);
|
|
133
|
+
await send(batch, true);
|
|
134
|
+
} else if (previous !== null) {
|
|
135
|
+
await send(previous, true);
|
|
136
|
+
}
|
|
137
|
+
return total;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Load one batch of rows through the same statement with writer durability
|
|
141
|
+
* relaxed for the duration, then restored before this resolves. This is the
|
|
142
|
+
* low-level primitive; prefer {@link loadAll} for a dataset that spans more
|
|
143
|
+
* than one request, since it runs the finalize itself rather than relying on
|
|
144
|
+
* a `checkpoint` flag.
|
|
145
|
+
*
|
|
146
|
+
* Returns the total rows loaded and changes applied. When splitting a dataset
|
|
147
|
+
* across many `load` calls by hand, pass `checkpoint: false` on every call
|
|
148
|
+
* but the last so the one fsyncing WAL checkpoint runs once at the end; the
|
|
149
|
+
* configured durability is restored after each call regardless.
|
|
150
|
+
*/
|
|
151
|
+
async load(sql, paramsBatch, durability, checkpoint) {
|
|
152
|
+
return this.transport.load(sql, paramsBatch, durability, checkpoint);
|
|
153
|
+
}
|
|
57
154
|
/**
|
|
58
155
|
* Start building a CDC subscription for the given table.
|
|
59
156
|
* Chain `.filter()` to narrow the events, then call `.subscribe()`
|
|
@@ -82,15 +179,99 @@ var RemoteDatabase = class {
|
|
|
82
179
|
}
|
|
83
180
|
};
|
|
84
181
|
|
|
85
|
-
// src/
|
|
86
|
-
var
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
182
|
+
// src/core/cdc/encoding.ts
|
|
183
|
+
var BLOB_TAG = "__sirannon_blob";
|
|
184
|
+
var INT_TAG = "__sirannon_int";
|
|
185
|
+
var SAFE_INT_BOUND = 9007199254740991n;
|
|
186
|
+
var HEX_BYTES_RE = /^[0-9a-fA-F]*$/;
|
|
187
|
+
var INT_PAYLOAD_RE = /^-?\d{1,19}$/;
|
|
188
|
+
function encodeTaggedValues(value) {
|
|
189
|
+
if (typeof value === "bigint" || isBinaryValue(value)) {
|
|
190
|
+
return encodeTaggedLeaf(value);
|
|
191
|
+
}
|
|
192
|
+
if (value === null || typeof value !== "object") return value;
|
|
193
|
+
if (Array.isArray(value)) {
|
|
194
|
+
return value.map(encodeTaggedValues);
|
|
195
|
+
}
|
|
196
|
+
if (typeof value.toJSON === "function") {
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
const obj = value;
|
|
200
|
+
const out = {};
|
|
201
|
+
for (const k of Object.keys(obj)) {
|
|
202
|
+
out[k] = encodeTaggedValues(obj[k]);
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
function encodeTaggedLeaf(value) {
|
|
207
|
+
if (typeof value === "bigint") {
|
|
208
|
+
if (value >= -SAFE_INT_BOUND && value <= SAFE_INT_BOUND) {
|
|
209
|
+
return Number(value);
|
|
210
|
+
}
|
|
211
|
+
return { [INT_TAG]: value.toString() };
|
|
92
212
|
}
|
|
93
|
-
|
|
213
|
+
if (isBinaryValue(value)) {
|
|
214
|
+
return { [BLOB_TAG]: encodeHexBytes(value) };
|
|
215
|
+
}
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
function decodeTaggedValues(value) {
|
|
219
|
+
if (value === null || typeof value !== "object") return value;
|
|
220
|
+
if (isBinaryValue(value)) return value;
|
|
221
|
+
if (Array.isArray(value)) {
|
|
222
|
+
return value.map(decodeTaggedValues);
|
|
223
|
+
}
|
|
224
|
+
const obj = value;
|
|
225
|
+
const keys = Object.keys(obj);
|
|
226
|
+
if (keys.length === 1) {
|
|
227
|
+
const tag = keys[0];
|
|
228
|
+
const tagValue = obj[tag];
|
|
229
|
+
if (tag === BLOB_TAG && typeof tagValue === "string") {
|
|
230
|
+
return decodeHexBytes(tagValue);
|
|
231
|
+
}
|
|
232
|
+
if (tag === INT_TAG && typeof tagValue === "string") {
|
|
233
|
+
if (!INT_PAYLOAD_RE.test(tagValue)) {
|
|
234
|
+
throw new Error("Invalid tagged integer payload");
|
|
235
|
+
}
|
|
236
|
+
return BigInt(tagValue);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const out = {};
|
|
240
|
+
for (const k of keys) {
|
|
241
|
+
out[k] = decodeTaggedValues(obj[k]);
|
|
242
|
+
}
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
function isBinaryValue(value) {
|
|
246
|
+
return value instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(value);
|
|
247
|
+
}
|
|
248
|
+
function encodeHexBytes(bytes) {
|
|
249
|
+
if (typeof Buffer !== "undefined") {
|
|
250
|
+
return Buffer.from(bytes).toString("hex").toUpperCase();
|
|
251
|
+
}
|
|
252
|
+
let hex = "";
|
|
253
|
+
for (const byte of bytes) {
|
|
254
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
255
|
+
}
|
|
256
|
+
return hex.toUpperCase();
|
|
257
|
+
}
|
|
258
|
+
function decodeHexBytes(hex) {
|
|
259
|
+
if (hex.length % 2 !== 0 || !HEX_BYTES_RE.test(hex)) {
|
|
260
|
+
throw new Error("Invalid CDC BLOB hex payload");
|
|
261
|
+
}
|
|
262
|
+
if (typeof Buffer !== "undefined") {
|
|
263
|
+
return Buffer.from(hex, "hex");
|
|
264
|
+
}
|
|
265
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
266
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
267
|
+
const byte = Number.parseInt(hex.slice(i, i + 2), 16);
|
|
268
|
+
if (Number.isNaN(byte)) {
|
|
269
|
+
throw new Error("Invalid CDC BLOB hex payload");
|
|
270
|
+
}
|
|
271
|
+
bytes[i / 2] = byte;
|
|
272
|
+
}
|
|
273
|
+
return bytes;
|
|
274
|
+
}
|
|
94
275
|
|
|
95
276
|
// src/client/transport/http.ts
|
|
96
277
|
var HttpTransport = class {
|
|
@@ -105,13 +286,35 @@ var HttpTransport = class {
|
|
|
105
286
|
};
|
|
106
287
|
}
|
|
107
288
|
async query(sql, params, readConcern) {
|
|
108
|
-
|
|
289
|
+
const response = await this.post("/query", {
|
|
290
|
+
sql,
|
|
291
|
+
params: encodeTaggedValues(params),
|
|
292
|
+
readConcern
|
|
293
|
+
});
|
|
294
|
+
return { rows: decodeTaggedValues(response.rows ?? []) };
|
|
109
295
|
}
|
|
110
296
|
async execute(sql, params) {
|
|
111
|
-
return this.post("/execute", { sql, params });
|
|
297
|
+
return this.post("/execute", { sql, params: encodeTaggedValues(params) });
|
|
112
298
|
}
|
|
113
299
|
async transaction(statements) {
|
|
114
|
-
return this.post("/transaction", {
|
|
300
|
+
return this.post("/transaction", {
|
|
301
|
+
statements: statements.map((stmt) => ({ sql: stmt.sql, params: encodeTaggedValues(stmt.params) }))
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
async batch(sql, paramsBatch, writeConcern) {
|
|
305
|
+
return this.post("/batch", {
|
|
306
|
+
sql,
|
|
307
|
+
paramsBatch: paramsBatch.map((entry) => encodeTaggedValues(entry)),
|
|
308
|
+
writeConcern
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
async load(sql, paramsBatch, durability, checkpoint) {
|
|
312
|
+
return this.post("/load", {
|
|
313
|
+
sql,
|
|
314
|
+
paramsBatch: paramsBatch.map((entry) => encodeTaggedValues(entry)),
|
|
315
|
+
durability,
|
|
316
|
+
checkpoint
|
|
317
|
+
});
|
|
115
318
|
}
|
|
116
319
|
async subscribe(_table, _filter, _callback) {
|
|
117
320
|
throw new RemoteError(
|
|
@@ -182,29 +385,76 @@ var WebSocketTransport = class {
|
|
|
182
385
|
async query(sql, params) {
|
|
183
386
|
await this.ensureConnected();
|
|
184
387
|
const id = this.nextId();
|
|
185
|
-
|
|
388
|
+
const response = await this.request({
|
|
389
|
+
type: "query",
|
|
390
|
+
id,
|
|
391
|
+
sql,
|
|
392
|
+
params: encodeTaggedValues(params)
|
|
393
|
+
});
|
|
394
|
+
return { rows: decodeTaggedValues(response.rows ?? []) };
|
|
186
395
|
}
|
|
187
396
|
async execute(sql, params) {
|
|
188
397
|
await this.ensureConnected();
|
|
189
398
|
const id = this.nextId();
|
|
190
|
-
return this.request({
|
|
399
|
+
return this.request({
|
|
400
|
+
type: "execute",
|
|
401
|
+
id,
|
|
402
|
+
sql,
|
|
403
|
+
params: encodeTaggedValues(params)
|
|
404
|
+
});
|
|
191
405
|
}
|
|
192
|
-
async transaction(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
406
|
+
async transaction(statements) {
|
|
407
|
+
await this.ensureConnected();
|
|
408
|
+
const id = this.nextId();
|
|
409
|
+
return this.request({
|
|
410
|
+
type: "transaction",
|
|
411
|
+
id,
|
|
412
|
+
statements: statements.map((stmt) => ({
|
|
413
|
+
sql: stmt.sql,
|
|
414
|
+
params: encodeTaggedValues(stmt.params)
|
|
415
|
+
}))
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
async batch(sql, paramsBatch, writeConcern) {
|
|
419
|
+
await this.ensureConnected();
|
|
420
|
+
const id = this.nextId();
|
|
421
|
+
return this.request({
|
|
422
|
+
type: "batch",
|
|
423
|
+
id,
|
|
424
|
+
sql,
|
|
425
|
+
paramsBatch: paramsBatch.map((entry) => encodeTaggedValues(entry)),
|
|
426
|
+
...writeConcern ? { writeConcern } : {}
|
|
427
|
+
});
|
|
197
428
|
}
|
|
198
|
-
async
|
|
429
|
+
async load(sql, paramsBatch, durability, checkpoint) {
|
|
199
430
|
await this.ensureConnected();
|
|
200
431
|
const id = this.nextId();
|
|
201
|
-
this.
|
|
432
|
+
return this.request({
|
|
433
|
+
type: "load",
|
|
434
|
+
id,
|
|
435
|
+
sql,
|
|
436
|
+
paramsBatch: paramsBatch.map((entry) => encodeTaggedValues(entry)),
|
|
437
|
+
...durability ? { durability } : {},
|
|
438
|
+
...checkpoint !== void 0 ? { checkpoint } : {}
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
async subscribe(table, filter, callback, options) {
|
|
442
|
+
await this.ensureConnected();
|
|
443
|
+
const id = this.nextId();
|
|
444
|
+
this.activeSubscriptions.set(id, {
|
|
445
|
+
table,
|
|
446
|
+
filter,
|
|
447
|
+
callback,
|
|
448
|
+
onReset: options?.onReset,
|
|
449
|
+
lastSeq: void 0,
|
|
450
|
+
epoch: void 0
|
|
451
|
+
});
|
|
202
452
|
try {
|
|
203
453
|
const msg = {
|
|
204
454
|
type: "subscribe",
|
|
205
455
|
id,
|
|
206
456
|
table,
|
|
207
|
-
...filter ? { filter } : {}
|
|
457
|
+
...filter ? { filter: encodeTaggedValues(filter) } : {}
|
|
208
458
|
};
|
|
209
459
|
await this.request(msg);
|
|
210
460
|
} catch (err) {
|
|
@@ -303,6 +553,29 @@ var WebSocketTransport = class {
|
|
|
303
553
|
break;
|
|
304
554
|
}
|
|
305
555
|
case "subscribed": {
|
|
556
|
+
const sub = this.activeSubscriptions.get(msg.id);
|
|
557
|
+
if (sub) {
|
|
558
|
+
if (msg.epoch !== void 0) {
|
|
559
|
+
sub.epoch = msg.epoch;
|
|
560
|
+
}
|
|
561
|
+
let baseline;
|
|
562
|
+
if (msg.seq !== void 0) {
|
|
563
|
+
try {
|
|
564
|
+
baseline = BigInt(msg.seq);
|
|
565
|
+
} catch {
|
|
566
|
+
baseline = void 0;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (msg.resync) {
|
|
570
|
+
sub.lastSeq = baseline;
|
|
571
|
+
try {
|
|
572
|
+
sub.onReset?.();
|
|
573
|
+
} catch {
|
|
574
|
+
}
|
|
575
|
+
} else if (sub.lastSeq === void 0 && baseline !== void 0) {
|
|
576
|
+
sub.lastSeq = baseline;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
306
579
|
const pending = this.pendingRequests.get(msg.id);
|
|
307
580
|
if (pending) {
|
|
308
581
|
clearTimeout(pending.timer);
|
|
@@ -327,11 +600,14 @@ var WebSocketTransport = class {
|
|
|
327
600
|
const event = {
|
|
328
601
|
type: msg.event.type,
|
|
329
602
|
table: msg.event.table,
|
|
330
|
-
row: msg.event.row,
|
|
331
|
-
oldRow: msg.event.oldRow,
|
|
603
|
+
row: decodeTaggedValues(msg.event.row),
|
|
604
|
+
oldRow: msg.event.oldRow === void 0 ? void 0 : decodeTaggedValues(msg.event.oldRow),
|
|
332
605
|
seq: BigInt(msg.event.seq),
|
|
333
606
|
timestamp: msg.event.timestamp
|
|
334
607
|
};
|
|
608
|
+
if (sub.lastSeq === void 0 || event.seq > sub.lastSeq) {
|
|
609
|
+
sub.lastSeq = event.seq;
|
|
610
|
+
}
|
|
335
611
|
sub.callback(event);
|
|
336
612
|
} catch {
|
|
337
613
|
}
|
|
@@ -372,7 +648,9 @@ var WebSocketTransport = class {
|
|
|
372
648
|
type: "subscribe",
|
|
373
649
|
id,
|
|
374
650
|
table: sub.table,
|
|
375
|
-
...sub.filter ? { filter: sub.filter } : {}
|
|
651
|
+
...sub.filter ? { filter: encodeTaggedValues(sub.filter) } : {},
|
|
652
|
+
...sub.lastSeq !== void 0 ? { sinceSeq: sub.lastSeq.toString() } : {},
|
|
653
|
+
...sub.epoch !== void 0 ? { epoch: sub.epoch } : {}
|
|
376
654
|
};
|
|
377
655
|
await this.request(msg);
|
|
378
656
|
} catch {
|
|
@@ -387,10 +665,10 @@ var WebSocketTransport = class {
|
|
|
387
665
|
reject(new RemoteError("CONNECTION_ERROR", "WebSocket is not connected"));
|
|
388
666
|
return;
|
|
389
667
|
}
|
|
390
|
-
const timer = setTimeout(() => {
|
|
668
|
+
const timer = this.requestTimeout > 0 ? setTimeout(() => {
|
|
391
669
|
this.pendingRequests.delete(id);
|
|
392
670
|
reject(new RemoteError("TIMEOUT", `Request timed out after ${this.requestTimeout}ms`));
|
|
393
|
-
}, this.requestTimeout);
|
|
671
|
+
}, this.requestTimeout) : void 0;
|
|
394
672
|
this.pendingRequests.set(id, {
|
|
395
673
|
resolve,
|
|
396
674
|
reject,
|
|
@@ -551,6 +829,7 @@ var SirannonClient = class {
|
|
|
551
829
|
webSocketProtocols;
|
|
552
830
|
autoReconnect;
|
|
553
831
|
reconnectInterval;
|
|
832
|
+
requestTimeout;
|
|
554
833
|
databases = /* @__PURE__ */ new Map();
|
|
555
834
|
closed = false;
|
|
556
835
|
topologyEnabled;
|
|
@@ -584,6 +863,7 @@ var SirannonClient = class {
|
|
|
584
863
|
this.webSocketProtocols = topoOpts.webSocketProtocols;
|
|
585
864
|
this.autoReconnect = topoOpts.autoReconnect ?? true;
|
|
586
865
|
this.reconnectInterval = topoOpts.reconnectInterval ?? 1e3;
|
|
866
|
+
this.requestTimeout = topoOpts.requestTimeout;
|
|
587
867
|
} else {
|
|
588
868
|
this.topologyEnabled = false;
|
|
589
869
|
this.primaryUrl = void 0;
|
|
@@ -599,6 +879,7 @@ var SirannonClient = class {
|
|
|
599
879
|
this.webSocketProtocols = options?.webSocketProtocols;
|
|
600
880
|
this.autoReconnect = options?.autoReconnect ?? true;
|
|
601
881
|
this.reconnectInterval = options?.reconnectInterval ?? 1e3;
|
|
882
|
+
this.requestTimeout = options?.requestTimeout;
|
|
602
883
|
}
|
|
603
884
|
}
|
|
604
885
|
database(id) {
|
|
@@ -640,7 +921,8 @@ var SirannonClient = class {
|
|
|
640
921
|
return new WebSocketTransport(`${wsBaseUrl}/db/${encodedId}`, {
|
|
641
922
|
autoReconnect: this.autoReconnect,
|
|
642
923
|
reconnectInterval: this.reconnectInterval,
|
|
643
|
-
protocols: this.webSocketProtocols
|
|
924
|
+
protocols: this.webSocketProtocols,
|
|
925
|
+
requestTimeout: this.requestTimeout
|
|
644
926
|
});
|
|
645
927
|
}
|
|
646
928
|
_createTransportForEndpoint(url, databaseId) {
|
|
@@ -915,9 +1197,35 @@ var TopologyAwareTransport = class {
|
|
|
915
1197
|
throw err;
|
|
916
1198
|
}
|
|
917
1199
|
}
|
|
918
|
-
async
|
|
1200
|
+
async batch(sql, paramsBatch, writeConcern) {
|
|
1201
|
+
const transport = await this.getWriteTransport();
|
|
1202
|
+
try {
|
|
1203
|
+
return await transport.batch(sql, paramsBatch, writeConcern);
|
|
1204
|
+
} catch (err) {
|
|
1205
|
+
if (this.client._usesCoordinatorDiscovery() && shouldRefreshRouting(err)) {
|
|
1206
|
+
await this.client._refreshClusterRouting(this.databaseId);
|
|
1207
|
+
this.writeTransport = null;
|
|
1208
|
+
this.currentWriteUrl = "";
|
|
1209
|
+
}
|
|
1210
|
+
throw err;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
async load(sql, paramsBatch, durability, checkpoint) {
|
|
1214
|
+
const transport = await this.getWriteTransport();
|
|
1215
|
+
try {
|
|
1216
|
+
return await transport.load(sql, paramsBatch, durability, checkpoint);
|
|
1217
|
+
} catch (err) {
|
|
1218
|
+
if (this.client._usesCoordinatorDiscovery() && shouldRefreshRouting(err)) {
|
|
1219
|
+
await this.client._refreshClusterRouting(this.databaseId);
|
|
1220
|
+
this.writeTransport = null;
|
|
1221
|
+
this.currentWriteUrl = "";
|
|
1222
|
+
}
|
|
1223
|
+
throw err;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
async subscribe(table, filter, callback, options) {
|
|
919
1227
|
try {
|
|
920
|
-
return await this.subscribeOnCurrentEndpoint(table, filter, callback);
|
|
1228
|
+
return await this.subscribeOnCurrentEndpoint(table, filter, callback, options);
|
|
921
1229
|
} catch (err) {
|
|
922
1230
|
if (this.client._usesCoordinatorDiscovery() && shouldRefreshRouting(err)) {
|
|
923
1231
|
const hadActiveSubscriptions = this.activeSubscriptions.size > 0;
|
|
@@ -928,7 +1236,7 @@ var TopologyAwareTransport = class {
|
|
|
928
1236
|
if (!hadActiveSubscriptions) {
|
|
929
1237
|
this.closeSubscriptionTransport();
|
|
930
1238
|
}
|
|
931
|
-
return this.subscribeOnCurrentEndpoint(table, filter, callback);
|
|
1239
|
+
return this.subscribeOnCurrentEndpoint(table, filter, callback, options);
|
|
932
1240
|
}
|
|
933
1241
|
throw err;
|
|
934
1242
|
}
|
|
@@ -963,15 +1271,16 @@ var TopologyAwareTransport = class {
|
|
|
963
1271
|
this.currentSubscriptionUrl = "";
|
|
964
1272
|
this.client._unregisterTopologyTransport(this.databaseId, this);
|
|
965
1273
|
}
|
|
966
|
-
async subscribeOnCurrentEndpoint(table, filter, callback) {
|
|
1274
|
+
async subscribeOnCurrentEndpoint(table, filter, callback, options) {
|
|
967
1275
|
return this.withSubscriptionOperation(async () => {
|
|
968
1276
|
const transport = await this.getSubscriptionTransport(this.client._getReadConcern());
|
|
969
|
-
const remote = await transport.subscribe(table, filter, callback);
|
|
1277
|
+
const remote = await transport.subscribe(table, filter, callback, options);
|
|
970
1278
|
const subscription = {
|
|
971
1279
|
id: ++this.nextSubscriptionId,
|
|
972
1280
|
table,
|
|
973
1281
|
filter,
|
|
974
1282
|
callback,
|
|
1283
|
+
onReset: options?.onReset,
|
|
975
1284
|
remote,
|
|
976
1285
|
active: true
|
|
977
1286
|
};
|
|
@@ -1006,7 +1315,9 @@ var TopologyAwareTransport = class {
|
|
|
1006
1315
|
try {
|
|
1007
1316
|
for (const subscription of subscriptions) {
|
|
1008
1317
|
if (!subscription.active) continue;
|
|
1009
|
-
const remote = await nextTransport.subscribe(subscription.table, subscription.filter, subscription.callback
|
|
1318
|
+
const remote = await nextTransport.subscribe(subscription.table, subscription.filter, subscription.callback, {
|
|
1319
|
+
onReset: subscription.onReset
|
|
1320
|
+
});
|
|
1010
1321
|
if (!subscription.active) {
|
|
1011
1322
|
remote.unsubscribe();
|
|
1012
1323
|
continue;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,15 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
export { C as
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { g as BulkLoadDurability, e as SQLiteConnection, s as SynchronousLevel, h as BulkLoadResult, a as SQLiteDriver, t as WorkerHostOptions, D as DatabaseOptions, L as LifecycleConfig, P as Params, E as ExecuteResult } from '../types-D_hQW1hr.js';
|
|
2
|
+
export { A as AfterQueryHook, f as BackupScheduleOptions, u as BatchSummary, b as BeforeConnectHook, B as BeforeQueryHook, l as BeforeSubscribeHook, q as BulkLoadOptions, o as CDCMetrics, C as ChangeEvent, v as ChangeOperation, w as ClientOptions, x as ClusterReadEndpointInfo, y as ClusterStatusInfo, k as ConnectionHookContext, n as ConnectionMetrics, z as CorsOptions, d as DatabaseCloseHook, c as DatabaseOpenHook, F as DriverCapabilities, G as DriverWorkerEntry, H as HookConfig, M as MetricsConfig, O as OnRequestHook, I as OpenOptions, Q as QueryHookContext, m as QueryMetrics, p as QueryOptions, R as ReadConcern, J as ReadConcernLevel, K as ReplicationStatusInfo, N as RequestContext, U as RequestDenial, V as RunResult, X as SQLiteStatement, Y as ServerExecutionTarget, Z as ServerExecutionTargetResolver, i as ServerOptions, S as SirannonOptions, _ as Subscription, r as SubscriptionBuilder, T as Transaction, j as WSHandlerOptions, W as WriteConcern, $ as WriteConcernLevel, a0 as WriterWorkerOptions } from '../types-D_hQW1hr.js';
|
|
3
|
+
export { C as ChangeTracker, a as ChangeTrackerOptions } from '../change-tracker-CbmaMO-N.js';
|
|
4
|
+
import { D as Database } from '../database-DuGp0Rtr.js';
|
|
5
|
+
export { H as HookDispose, a as HookEvent, b as HookEventContextMap, c as HookHandler, d as HookRegistry, M as MetricsCollector, S as SubscribeHookContext } from '../database-DuGp0Rtr.js';
|
|
6
|
+
export { B as BackupError, C as CDCError, a as ConnectionPoolError, D as DatabaseAlreadyExistsError, b as DatabaseNotFoundError, E as ExtensionError, F as ForbiddenSqlError, H as HookDeniedError, M as MaxDatabasesError, c as MigrationError, Q as QueryError, R as ReadOnlyError, S as SirannonError, T as TransactionError, W as WriteOverloadError } from '../errors-5Nf5ZAEC.js';
|
|
7
|
+
import { M as Migration, a as MigrationResult, R as RollbackResult } from '../types-D4p4UyDK.js';
|
|
8
|
+
export { A as AppliedMigration, b as AppliedMigrationEntry } from '../types-D4p4UyDK.js';
|
|
9
|
+
export { S as Sirannon } from '../sirannon-4SspRvP5.js';
|
|
10
|
+
|
|
11
|
+
declare function isBulkLoadDurability(value: unknown): value is BulkLoadDurability;
|
|
12
|
+
interface BulkLoadRun {
|
|
13
|
+
writer: SQLiteConnection;
|
|
14
|
+
configuredSynchronous: SynchronousLevel;
|
|
15
|
+
walMode: boolean;
|
|
16
|
+
durability: BulkLoadDurability | undefined;
|
|
17
|
+
checkpoint?: boolean;
|
|
18
|
+
loadRows: () => Promise<BulkLoadResult>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Run a bulk load with relaxed writer durability, then restore the
|
|
22
|
+
* operator-configured level. The caller must hold the database's writer lock
|
|
23
|
+
* for the whole call, so no other write commits under the relaxed level.
|
|
24
|
+
*
|
|
25
|
+
* Interruption safety, by failure mode:
|
|
26
|
+
* - A load statement fails: the surrounding transaction rolls back, the
|
|
27
|
+
* configured level is restored, and the database is exactly as it was before
|
|
28
|
+
* the load; the operator recovers by re-running the load.
|
|
29
|
+
* - The process crashes mid-load: `PRAGMA synchronous` is connection state
|
|
30
|
+
* that SQLite never stores in the database file, so the next open re-applies
|
|
31
|
+
* the configured level, and the uncommitted transaction rolls back from the
|
|
32
|
+
* WAL on open.
|
|
33
|
+
* - Power loss or an OS crash during a load at 'off': SQLite documents that
|
|
34
|
+
* the file may be corrupted, which is why 'off' fits only a load the
|
|
35
|
+
* operator can re-run from scratch; 'normal' keeps WAL corruption safety.
|
|
36
|
+
*
|
|
37
|
+
* The restore runs on both the success and the failure path. A restore pragma
|
|
38
|
+
* only fails when the writer connection itself is gone, so on a committed load
|
|
39
|
+
* that failure is reported as 'DURABILITY_RESTORE_FAILED', which tells the
|
|
40
|
+
* operator the load committed and must not be re-run; on a failed load the
|
|
41
|
+
* original load error stays dominant.
|
|
42
|
+
*
|
|
43
|
+
* On success, and only when the load changed rows, the WAL is checkpointed
|
|
44
|
+
* after the restore so the loaded pages are written into the main database
|
|
45
|
+
* file and fsync'd at the restored synchronous level. A reader holding the WAL
|
|
46
|
+
* can defer that transfer: the checkpoint retries a few times and then leaves
|
|
47
|
+
* the remaining pages for a later checkpoint rather than failing a load that
|
|
48
|
+
* has already committed, so those pages stay in the WAL until then. The
|
|
49
|
+
* checkpoint runs synchronously in the engine and blocks the event loop for
|
|
50
|
+
* the flush, which grows with the size of the load.
|
|
51
|
+
*
|
|
52
|
+
* A multi-batch import passes `checkpoint: false` on every load but the last so
|
|
53
|
+
* the one fsyncing checkpoint is paid once at the end instead of once per
|
|
54
|
+
* batch; each intermediate load still restores the configured level, and
|
|
55
|
+
* SQLite's automatic checkpoint keeps the WAL bounded during the import at the
|
|
56
|
+
* relaxed level with no fsync of its own.
|
|
57
|
+
*/
|
|
58
|
+
declare function runBulkLoad(run: BulkLoadRun): Promise<BulkLoadResult>;
|
|
13
59
|
|
|
14
60
|
interface ConnectionPoolOptions {
|
|
15
61
|
driver: SQLiteDriver;
|
|
@@ -17,6 +63,9 @@ interface ConnectionPoolOptions {
|
|
|
17
63
|
readOnly?: boolean;
|
|
18
64
|
readPoolSize?: number;
|
|
19
65
|
walMode?: boolean;
|
|
66
|
+
synchronous?: SynchronousLevel;
|
|
67
|
+
useWriterWorker?: boolean;
|
|
68
|
+
workerHostOptions?: WorkerHostOptions;
|
|
20
69
|
}
|
|
21
70
|
declare class ConnectionPool {
|
|
22
71
|
private readonly writer;
|
|
@@ -34,6 +83,14 @@ declare class ConnectionPool {
|
|
|
34
83
|
|
|
35
84
|
declare function defineDriver(config: SQLiteDriver): SQLiteDriver;
|
|
36
85
|
|
|
86
|
+
declare const DEFAULT_SYNCHRONOUS: SynchronousLevel;
|
|
87
|
+
/**
|
|
88
|
+
* Map a synchronous level to its PRAGMA argument through an allowlist, so a
|
|
89
|
+
* value smuggled past the type system can never reach SQL interpolation.
|
|
90
|
+
*/
|
|
91
|
+
declare function synchronousPragmaValue(level: SynchronousLevel | undefined): string;
|
|
92
|
+
declare function isSynchronousLevel(value: unknown): value is SynchronousLevel;
|
|
93
|
+
|
|
37
94
|
interface LifecycleCallbacks {
|
|
38
95
|
open: (id: string, path: string, options?: DatabaseOptions) => Promise<Database>;
|
|
39
96
|
close: (id: string) => Promise<void>;
|
|
@@ -83,4 +140,4 @@ declare function queryOne<T = Record<string, unknown>>(conn: SQLiteConnection, s
|
|
|
83
140
|
declare function execute(conn: SQLiteConnection, sql: string, params?: Params): Promise<ExecuteResult>;
|
|
84
141
|
declare function executeBatch(conn: SQLiteConnection, sql: string, paramsBatch: Params[]): Promise<ExecuteResult[]>;
|
|
85
142
|
|
|
86
|
-
export { ConnectionPool, type ConnectionPoolOptions, Database, DatabaseOptions, ExecuteResult, type LifecycleCallbacks, LifecycleConfig, LifecycleManager, Migration, MigrationResult, MigrationRunner, Params, RollbackResult, SQLiteConnection, SQLiteDriver, type TenantResolverOptions, createTenantResolver, defineDriver, execute, executeBatch, query, queryOne, sanitizeTenantId, tenantPath };
|
|
143
|
+
export { BulkLoadDurability, BulkLoadResult, ConnectionPool, type ConnectionPoolOptions, DEFAULT_SYNCHRONOUS, Database, DatabaseOptions, ExecuteResult, type LifecycleCallbacks, LifecycleConfig, LifecycleManager, Migration, MigrationResult, MigrationRunner, Params, RollbackResult, SQLiteConnection, SQLiteDriver, SynchronousLevel, type TenantResolverOptions, createTenantResolver, defineDriver, execute, executeBatch, isBulkLoadDurability, isSynchronousLevel, query, queryOne, runBulkLoad, sanitizeTenantId, synchronousPragmaValue, tenantPath };
|