@sqlite-sync/core 0.12.0 → 0.12.2
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/{crdt-sync-remote-source-Q6P6OiL0.js → crdt-sync-remote-source-CPXzmSIS.js} +2 -2
- package/dist/{crdt-sync-remote-source-Q6P6OiL0.js.map → crdt-sync-remote-source-CPXzmSIS.js.map} +1 -1
- package/dist/{crdt-table-schema-2w_AIpsV.js → crdt-table-schema-BRm_WdcW.js} +19 -2
- package/dist/crdt-table-schema-BRm_WdcW.js.map +1 -0
- package/dist/{index-3zVvNWRd.d.ts → index-BHsKLqcK.d.ts} +6 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +212 -186
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +3 -3
- package/dist/{make-crdt-table-D8VQ-ptj.js → make-crdt-table-C6l0F-o_.js} +2 -2
- package/dist/{make-crdt-table-D8VQ-ptj.js.map → make-crdt-table-C6l0F-o_.js.map} +1 -1
- package/dist/{reset-state-BQi96NLn.js → reset-state-t0tTGqoh.js} +23 -342
- package/dist/reset-state-t0tTGqoh.js.map +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +2 -2
- package/dist/system-schema-Dvl_BLId.js +523 -0
- package/dist/system-schema-Dvl_BLId.js.map +1 -0
- package/dist/worker.d.ts +2 -1
- package/dist/worker.js +15 -10
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/crdt-table-schema-2w_AIpsV.js.map +0 -1
- package/dist/reset-state-BQi96NLn.js.map +0 -1
- package/dist/system-schema-C2JZDv6L.js +0 -178
- package/dist/system-schema-C2JZDv6L.js.map +0 -1
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
import { f as parseTableName, t as CRDT_EVENT_NO_OP_PAYLOAD } from "./crdt-table-schema-BRm_WdcW.js";
|
|
2
|
+
import { DummyDriver, Kysely, SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler, sql } from "kysely";
|
|
3
|
+
//#region src/dummy-kysely.ts
|
|
4
|
+
const dummyKysely = new Kysely({ dialect: {
|
|
5
|
+
createAdapter: () => new SqliteAdapter(),
|
|
6
|
+
createDriver: () => new DummyDriver(),
|
|
7
|
+
createQueryCompiler: () => new SqliteQueryCompiler(),
|
|
8
|
+
createIntrospector: (db) => new SqliteIntrospector(db)
|
|
9
|
+
} });
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/introspection.ts
|
|
12
|
+
function tablesQuery(qb) {
|
|
13
|
+
return qb.selectFrom("sqlite_master").where("type", "in", ["table", "view"]).where("name", "not like", "sqlite_%").select([
|
|
14
|
+
"name",
|
|
15
|
+
"sql",
|
|
16
|
+
"type"
|
|
17
|
+
]).orderBy("name");
|
|
18
|
+
}
|
|
19
|
+
function introspectDb(_db) {
|
|
20
|
+
const db = _db;
|
|
21
|
+
const tables = db.executeKysely((db) => tablesQuery(db), { loggerLevel: "system" }).rows;
|
|
22
|
+
const tablesMetadata = db.executeKysely((db) => db.with("table_list", (qb) => tablesQuery(qb)).selectFrom(["table_list as tl", sql`pragma_table_info(tl.name)`.as("p")]).select([
|
|
23
|
+
"tl.name as table",
|
|
24
|
+
"p.cid",
|
|
25
|
+
"p.name",
|
|
26
|
+
"p.type",
|
|
27
|
+
"p.notnull",
|
|
28
|
+
"p.dflt_value",
|
|
29
|
+
"p.pk"
|
|
30
|
+
]).orderBy("tl.name").orderBy("p.cid"), { loggerLevel: "system" }).rows;
|
|
31
|
+
const columnsByTable = {};
|
|
32
|
+
for (const row of tablesMetadata) {
|
|
33
|
+
columnsByTable[row.table] ??= [];
|
|
34
|
+
columnsByTable[row.table].push(row);
|
|
35
|
+
}
|
|
36
|
+
return Object.fromEntries(tables.map(({ name, sql, type }) => {
|
|
37
|
+
let autoIncrementCol = sql?.split(/[(),]/)?.find((it) => it.toLowerCase().includes("autoincrement"))?.trimStart()?.split(/\s+/)?.[0]?.replace(/["`]/g, "");
|
|
38
|
+
const columns = columnsByTable[name] ?? [];
|
|
39
|
+
if (!autoIncrementCol) {
|
|
40
|
+
const pkCols = columns.filter((r) => r.pk > 0);
|
|
41
|
+
if (pkCols.length === 1 && pkCols[0].type.toLowerCase() === "integer") autoIncrementCol = pkCols[0].name;
|
|
42
|
+
}
|
|
43
|
+
return [name, {
|
|
44
|
+
name,
|
|
45
|
+
isView: type === "view",
|
|
46
|
+
columns: columns.map((col) => ({
|
|
47
|
+
name: col.name,
|
|
48
|
+
dataType: col.type,
|
|
49
|
+
isNullable: !col.notnull,
|
|
50
|
+
isAutoIncrementing: col.name === autoIncrementCol,
|
|
51
|
+
hasDefaultValue: col.dflt_value != null,
|
|
52
|
+
defaultValueSql: col.dflt_value == null ? null : String(col.dflt_value),
|
|
53
|
+
isPrimaryKey: col.pk > 0,
|
|
54
|
+
comment: void 0
|
|
55
|
+
}))
|
|
56
|
+
}];
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/logger.ts
|
|
61
|
+
const noopPerformanceLogger = {
|
|
62
|
+
restart() {},
|
|
63
|
+
logEnd() {}
|
|
64
|
+
};
|
|
65
|
+
const startPerformanceLogger = (logger, level = "info") => {
|
|
66
|
+
if (!logger || level === "system") return noopPerformanceLogger;
|
|
67
|
+
let startTime = performance.now();
|
|
68
|
+
return {
|
|
69
|
+
restart: () => {
|
|
70
|
+
startTime = performance.now();
|
|
71
|
+
},
|
|
72
|
+
logEnd: (type, message, logLevel = level) => {
|
|
73
|
+
logger(type, `${(performance.now() - startTime).toFixed(2)}ms - ${message}`, logLevel);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/sqlite-db-wrapper.ts
|
|
79
|
+
var SQLiteDbWrapper = class {
|
|
80
|
+
db = null;
|
|
81
|
+
sqlite3;
|
|
82
|
+
logger;
|
|
83
|
+
loggerPrefix;
|
|
84
|
+
loadedDbSchema = null;
|
|
85
|
+
dataPointers = [];
|
|
86
|
+
preparedStatements = [];
|
|
87
|
+
preparedStatementsMap = /* @__PURE__ */ new Map();
|
|
88
|
+
preparedRawStatementsMap = /* @__PURE__ */ new Map();
|
|
89
|
+
afterMutatingStatement = null;
|
|
90
|
+
constructor(opts) {
|
|
91
|
+
this.db = opts.db();
|
|
92
|
+
this.sqlite3 = opts.sqlite3;
|
|
93
|
+
this.logger = opts.logger;
|
|
94
|
+
this.loggerPrefix = opts.loggerPrefix;
|
|
95
|
+
}
|
|
96
|
+
get ensureDb() {
|
|
97
|
+
if (!this.db) throw new Error("Database is already closed");
|
|
98
|
+
return this.db;
|
|
99
|
+
}
|
|
100
|
+
get dbSchema() {
|
|
101
|
+
if (!this.loadedDbSchema) this.loadedDbSchema = introspectDb(this);
|
|
102
|
+
return this.loadedDbSchema;
|
|
103
|
+
}
|
|
104
|
+
executeSql(sql, bind, loggerLevel, skipAfterMutatingStatement) {
|
|
105
|
+
const perf = startPerformanceLogger(this.logger, loggerLevel);
|
|
106
|
+
const statement = this.ensureDb.prepare(sql);
|
|
107
|
+
let transaction;
|
|
108
|
+
try {
|
|
109
|
+
const statementPointer = statement.pointer;
|
|
110
|
+
if (!statementPointer) throw new Error("Prepared statement is already finalized");
|
|
111
|
+
const afterMutatingStatement = !skipAfterMutatingStatement && this.afterMutatingStatement && this.sqlite3.capi.sqlite3_stmt_readonly(statementPointer) === 0 ? this.afterMutatingStatement : null;
|
|
112
|
+
let totalChangesBefore = 0;
|
|
113
|
+
if (afterMutatingStatement) {
|
|
114
|
+
transaction = this.beginStatementTransaction();
|
|
115
|
+
totalChangesBefore = this.sqlite3.capi.sqlite3_total_changes(this.ensureDb);
|
|
116
|
+
}
|
|
117
|
+
if (bind && bind.length > 0) statement.bind(bind);
|
|
118
|
+
const rows = [];
|
|
119
|
+
let columnNames;
|
|
120
|
+
while (statement.step()) {
|
|
121
|
+
columnNames ??= statement.getColumnNames([]);
|
|
122
|
+
const values = statement.get([]);
|
|
123
|
+
const row = Object.create(null);
|
|
124
|
+
for (let index = 0; index < columnNames.length; index++) row[columnNames[index]] = values[index];
|
|
125
|
+
rows.push(row);
|
|
126
|
+
}
|
|
127
|
+
statement.finalize();
|
|
128
|
+
perf.logEnd(`${this.loggerPrefix ?? ""}:query`, sql, loggerLevel);
|
|
129
|
+
if (transaction) {
|
|
130
|
+
if (this.sqlite3.capi.sqlite3_total_changes(this.ensureDb) !== totalChangesBefore) afterMutatingStatement?.(this);
|
|
131
|
+
transaction.commit();
|
|
132
|
+
}
|
|
133
|
+
return { rows };
|
|
134
|
+
} catch (error) {
|
|
135
|
+
statement.finalize();
|
|
136
|
+
transaction?.rollback();
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
execute(opts, options) {
|
|
141
|
+
const sql = typeof opts === "string" ? opts : opts.sql;
|
|
142
|
+
const bind = typeof opts === "string" ? void 0 : opts.parameters;
|
|
143
|
+
return this.executeSql(sql, bind, options?.loggerLevel, options?.skipAfterMutatingStatement ?? false);
|
|
144
|
+
}
|
|
145
|
+
setAfterMutatingStatement(callback) {
|
|
146
|
+
if (this.afterMutatingStatement) throw new Error("An afterMutatingStatement callback is already registered");
|
|
147
|
+
this.afterMutatingStatement = callback;
|
|
148
|
+
}
|
|
149
|
+
executeTransaction(callback) {
|
|
150
|
+
const transaction = this.beginTransaction();
|
|
151
|
+
try {
|
|
152
|
+
const result = callback(this);
|
|
153
|
+
transaction.commit();
|
|
154
|
+
return result;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
transaction.rollback();
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
isInTransaction() {
|
|
161
|
+
return this.sqlite3.capi.sqlite3_get_autocommit(this.ensureDb) === 0;
|
|
162
|
+
}
|
|
163
|
+
beginTransaction() {
|
|
164
|
+
this.executePreparedRaw({
|
|
165
|
+
key: "$begin-transaction",
|
|
166
|
+
sql: "begin",
|
|
167
|
+
meta: { loggerLevel: "system" }
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
commit: () => {
|
|
171
|
+
this.executePreparedRaw({
|
|
172
|
+
key: "$commit-transaction",
|
|
173
|
+
sql: "commit",
|
|
174
|
+
meta: { loggerLevel: "system" }
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
rollback: () => {
|
|
178
|
+
this.executePreparedRaw({
|
|
179
|
+
key: "$rollback-transaction",
|
|
180
|
+
sql: "rollback",
|
|
181
|
+
meta: { loggerLevel: "system" }
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
beginStatementTransaction() {
|
|
187
|
+
if (!this.isInTransaction()) return this.beginTransaction();
|
|
188
|
+
this.executePreparedRaw({
|
|
189
|
+
key: "$begin-statement-savepoint",
|
|
190
|
+
sql: "savepoint sqlite_sync_statement",
|
|
191
|
+
meta: { loggerLevel: "system" }
|
|
192
|
+
});
|
|
193
|
+
return {
|
|
194
|
+
commit: () => {
|
|
195
|
+
this.executePreparedRaw({
|
|
196
|
+
key: "$commit-statement-savepoint",
|
|
197
|
+
sql: "release savepoint sqlite_sync_statement",
|
|
198
|
+
meta: { loggerLevel: "system" }
|
|
199
|
+
});
|
|
200
|
+
},
|
|
201
|
+
rollback: () => {
|
|
202
|
+
this.executePreparedRaw({
|
|
203
|
+
key: "$rollback-statement-savepoint",
|
|
204
|
+
sql: "rollback to savepoint sqlite_sync_statement",
|
|
205
|
+
meta: { loggerLevel: "system" }
|
|
206
|
+
});
|
|
207
|
+
this.executePreparedRaw({
|
|
208
|
+
key: "$release-rolled-back-statement-savepoint",
|
|
209
|
+
sql: "release savepoint sqlite_sync_statement",
|
|
210
|
+
meta: { loggerLevel: "system" }
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
prepare(sql, opts) {
|
|
216
|
+
const perf = startPerformanceLogger(this.logger, opts?.loggerLevel);
|
|
217
|
+
const stmt = this.ensureDb.prepare(sql);
|
|
218
|
+
perf.logEnd(`${this.loggerPrefix ?? ""}:prepare`, sql, opts?.loggerLevel);
|
|
219
|
+
let isFinalized = false;
|
|
220
|
+
const execute = (params) => {
|
|
221
|
+
if (isFinalized) throw new Error("Statement is finalized");
|
|
222
|
+
const perf = startPerformanceLogger(this.logger, opts?.loggerLevel);
|
|
223
|
+
try {
|
|
224
|
+
if (params.length > 0) stmt.bind(params);
|
|
225
|
+
const results = [];
|
|
226
|
+
let columnNames;
|
|
227
|
+
while (stmt.step()) {
|
|
228
|
+
columnNames ??= stmt.getColumnNames([]);
|
|
229
|
+
const values = stmt.get([]);
|
|
230
|
+
const row = {};
|
|
231
|
+
for (let index = 0; index < columnNames.length; index++) row[columnNames[index]] = values[index];
|
|
232
|
+
results.push(row);
|
|
233
|
+
}
|
|
234
|
+
return results;
|
|
235
|
+
} finally {
|
|
236
|
+
stmt.reset(true);
|
|
237
|
+
perf.logEnd(`${this.loggerPrefix ?? ""}:prepare-execute`, sql, opts?.loggerLevel);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const finalize = () => {
|
|
241
|
+
isFinalized = true;
|
|
242
|
+
stmt.finalize();
|
|
243
|
+
};
|
|
244
|
+
const preparedStatement = {
|
|
245
|
+
execute,
|
|
246
|
+
finalize,
|
|
247
|
+
get isFinalized() {
|
|
248
|
+
return isFinalized;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
this.preparedStatements.push(preparedStatement);
|
|
252
|
+
return preparedStatement;
|
|
253
|
+
}
|
|
254
|
+
prepareKysely(opts) {
|
|
255
|
+
return (factory) => {
|
|
256
|
+
const query = compileKyselyStatement(factory);
|
|
257
|
+
const statement = this.prepare(query.sql, opts);
|
|
258
|
+
return { execute: (parameters) => statement.execute(query.bindParameters(parameters)) };
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
executeKysely(factory, options) {
|
|
262
|
+
const query = factory(dummyKysely).compile();
|
|
263
|
+
return this.execute(query, options);
|
|
264
|
+
}
|
|
265
|
+
executePrepared(key, params, factory, meta) {
|
|
266
|
+
let statement = this.preparedStatementsMap.get(key);
|
|
267
|
+
if (!statement) {
|
|
268
|
+
statement = this.prepareKysely(meta)(factory);
|
|
269
|
+
this.preparedStatementsMap.set(key, statement);
|
|
270
|
+
}
|
|
271
|
+
return statement.execute(params);
|
|
272
|
+
}
|
|
273
|
+
executePreparedRaw({ key, sql, params, meta }) {
|
|
274
|
+
let statement = this.preparedRawStatementsMap.get(key);
|
|
275
|
+
if (!statement) {
|
|
276
|
+
statement = this.prepare(sql, meta);
|
|
277
|
+
this.preparedRawStatementsMap.set(key, statement);
|
|
278
|
+
}
|
|
279
|
+
return statement.execute(params ?? []);
|
|
280
|
+
}
|
|
281
|
+
sql(templateOrString, ...parameters) {
|
|
282
|
+
if (typeof templateOrString === "string") return this.execute({
|
|
283
|
+
sql: templateOrString,
|
|
284
|
+
parameters
|
|
285
|
+
});
|
|
286
|
+
return this.execute({
|
|
287
|
+
sql: templateOrString.join("?"),
|
|
288
|
+
parameters
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
createScalarFunction({ name, callback, deterministic, directOnly, innocuous }) {
|
|
292
|
+
return this.ensureDb.createFunction({
|
|
293
|
+
name,
|
|
294
|
+
xFunc: (_, ...args) => {
|
|
295
|
+
return callback(...args);
|
|
296
|
+
},
|
|
297
|
+
arity: callback.length,
|
|
298
|
+
deterministic,
|
|
299
|
+
directOnly,
|
|
300
|
+
innocuous
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
useSnapshot(snapshot) {
|
|
304
|
+
const perf = startPerformanceLogger(this.logger, "system");
|
|
305
|
+
const dataPointer = this.sqlite3.wasm.allocFromTypedArray(snapshot);
|
|
306
|
+
this.dataPointers.push(dataPointer);
|
|
307
|
+
const resultCode = this.sqlite3.capi.sqlite3_deserialize(this.ensureDb, "main", dataPointer, snapshot.byteLength, snapshot.byteLength, this.sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE | this.sqlite3.capi.SQLITE_DESERIALIZE_RESIZEABLE);
|
|
308
|
+
this.ensureDb.checkRc(resultCode);
|
|
309
|
+
this.invalidateDbSchema();
|
|
310
|
+
perf.logEnd("useSnapshot", "success", "system");
|
|
311
|
+
}
|
|
312
|
+
createSnapshot() {
|
|
313
|
+
return this.sqlite3.capi.sqlite3_js_db_export(this.ensureDb);
|
|
314
|
+
}
|
|
315
|
+
invalidateDbSchema() {
|
|
316
|
+
this.loadedDbSchema = null;
|
|
317
|
+
}
|
|
318
|
+
cleanup() {
|
|
319
|
+
this.preparedStatements.forEach((stmt) => {
|
|
320
|
+
stmt.finalize();
|
|
321
|
+
});
|
|
322
|
+
this.preparedStatements.splice(0);
|
|
323
|
+
this.preparedStatementsMap.clear();
|
|
324
|
+
this.preparedRawStatementsMap.clear();
|
|
325
|
+
}
|
|
326
|
+
close() {
|
|
327
|
+
this.cleanup();
|
|
328
|
+
this.db?.close();
|
|
329
|
+
this.db = null;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
function compileKyselyStatement(factory) {
|
|
333
|
+
const parameterKeys = /* @__PURE__ */ new Map();
|
|
334
|
+
const query = factory(dummyKysely, (key) => {
|
|
335
|
+
const token = {};
|
|
336
|
+
parameterKeys.set(token, key);
|
|
337
|
+
return token;
|
|
338
|
+
}).compile();
|
|
339
|
+
return {
|
|
340
|
+
sql: query.sql,
|
|
341
|
+
bindParameters: (parameters) => query.parameters.map((value) => {
|
|
342
|
+
const key = parameterKeys.get(value);
|
|
343
|
+
return key === void 0 ? value : parameters[key];
|
|
344
|
+
})
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region src/sqlite-crdt/stored-value.ts
|
|
349
|
+
function createStoredValue({ initialValue, saveToStorage }) {
|
|
350
|
+
let currentValue = initialValue;
|
|
351
|
+
return {
|
|
352
|
+
get current() {
|
|
353
|
+
return currentValue;
|
|
354
|
+
},
|
|
355
|
+
set current(newValue) {
|
|
356
|
+
saveToStorage?.(newValue);
|
|
357
|
+
currentValue = newValue;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
//#endregion
|
|
362
|
+
//#region src/sqlite-kv-store.ts
|
|
363
|
+
function createKvStoreTableQuery(schema, tableName) {
|
|
364
|
+
return schema.createTable(tableName).ifNotExists().addColumn("key", "text", (col) => col.notNull().primaryKey()).addColumn("value", "text", (col) => col.notNull()).modifyEnd(sql`without rowid`);
|
|
365
|
+
}
|
|
366
|
+
function createSQLiteKvStore({ db, metaTableName }) {
|
|
367
|
+
const metaDb = db;
|
|
368
|
+
const get = (key) => {
|
|
369
|
+
const [result] = metaDb.executePrepared("get-meta-value", { key }, (db, params) => db.selectFrom(metaTableName).where("key", "=", params("key")).select("value").limit((eb) => eb.lit(1)), { loggerLevel: "system" });
|
|
370
|
+
return result?.value ?? null;
|
|
371
|
+
};
|
|
372
|
+
const set = (key, value) => {
|
|
373
|
+
metaDb.executePrepared("set-meta-value", {
|
|
374
|
+
key,
|
|
375
|
+
value
|
|
376
|
+
}, (db, params) => db.insertInto(metaTableName).values({
|
|
377
|
+
key: params("key"),
|
|
378
|
+
value: params("value")
|
|
379
|
+
}).onConflict((oc) => oc.doUpdateSet({ value: params("value") })), { loggerLevel: "system" });
|
|
380
|
+
};
|
|
381
|
+
const remove = (key) => {
|
|
382
|
+
metaDb.executePrepared("remove-meta-value", { key }, (db, params) => db.deleteFrom(metaTableName).where("key", "=", params("key")), { loggerLevel: "system" });
|
|
383
|
+
};
|
|
384
|
+
const getNumberOrDefault = (key, defaultValue) => {
|
|
385
|
+
const value = get(key);
|
|
386
|
+
if (!value) return defaultValue;
|
|
387
|
+
const parsedValue = Number.parseInt(value, 10);
|
|
388
|
+
return Number.isNaN(parsedValue) ? defaultValue : parsedValue;
|
|
389
|
+
};
|
|
390
|
+
return {
|
|
391
|
+
get,
|
|
392
|
+
set,
|
|
393
|
+
remove,
|
|
394
|
+
createStringStoredValue: (key, defaultValue) => createStoredValue({
|
|
395
|
+
initialValue: get(key) ?? defaultValue,
|
|
396
|
+
saveToStorage: (val) => set(key, val)
|
|
397
|
+
}),
|
|
398
|
+
createNumberStoredValue: (key, defaultValue) => createStoredValue({
|
|
399
|
+
initialValue: getNumberOrDefault(key, defaultValue),
|
|
400
|
+
saveToStorage: (val) => set(key, val.toString())
|
|
401
|
+
})
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/migrations/system-schema.ts
|
|
406
|
+
function createSystemDbConfig({ eventsTableName, updateLogTableName }) {
|
|
407
|
+
return {
|
|
408
|
+
eventsTable: parseTableName(eventsTableName),
|
|
409
|
+
updateLogTable: parseTableName(updateLogTableName)
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
const workerDbConfig = createSystemDbConfig({
|
|
413
|
+
eventsTableName: "worker.crdt_events",
|
|
414
|
+
updateLogTableName: "crdt_update_log"
|
|
415
|
+
});
|
|
416
|
+
const memoryDbConfig = createSystemDbConfig({
|
|
417
|
+
eventsTableName: "persisted_crdt_events",
|
|
418
|
+
updateLogTableName: "crdt_update_log"
|
|
419
|
+
});
|
|
420
|
+
const nonNoOpEventPredicate = `"payload" <> '${CRDT_EVENT_NO_OP_PAYLOAD}'`;
|
|
421
|
+
const baseSystemMigrations = [
|
|
422
|
+
{
|
|
423
|
+
version: 0,
|
|
424
|
+
up: (ctx) => {
|
|
425
|
+
ctx.execute(`CREATE TABLE IF NOT EXISTS ${ctx.eventsTable.fullIdentifier} (
|
|
426
|
+
"sync_id" integer NOT NULL PRIMARY KEY,
|
|
427
|
+
"schema_version" integer NOT NULL,
|
|
428
|
+
"status" text NOT NULL,
|
|
429
|
+
"type" text NOT NULL,
|
|
430
|
+
"timestamp" text NOT NULL,
|
|
431
|
+
"origin" text NOT NULL,
|
|
432
|
+
"dataset" text NOT NULL,
|
|
433
|
+
"item_id" text NOT NULL,
|
|
434
|
+
"payload" text NOT NULL
|
|
435
|
+
)`);
|
|
436
|
+
ctx.execute(`CREATE TABLE IF NOT EXISTS ${ctx.updateLogTable.fullIdentifier} (
|
|
437
|
+
"dataset" text NOT NULL,
|
|
438
|
+
"item_id" text NOT NULL,
|
|
439
|
+
"payload" text NOT NULL,
|
|
440
|
+
PRIMARY KEY ("item_id", "dataset")
|
|
441
|
+
)`);
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
version: 1,
|
|
446
|
+
up: (ctx) => {
|
|
447
|
+
ctx.execute(`ALTER TABLE ${ctx.eventsTable.fullIdentifier} ADD COLUMN "source_node_id" TEXT NOT NULL DEFAULT ''`);
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
version: 2,
|
|
452
|
+
up: (ctx) => {
|
|
453
|
+
const indexName = `${ctx.eventsTable.table}_status_sync_id_idx`;
|
|
454
|
+
ctx.execute(`CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} ("status", "sync_id")`);
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
version: 3,
|
|
459
|
+
up: (ctx) => {
|
|
460
|
+
const indexName = `${ctx.eventsTable.table}_timestamp_status_sync_id_idx`;
|
|
461
|
+
ctx.execute(`CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} ("timestamp", "status", "sync_id")`);
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
version: 4,
|
|
466
|
+
up: (ctx) => {
|
|
467
|
+
const indexName = `${ctx.eventsTable.table}_dataset_item_id_non_no_op_idx`;
|
|
468
|
+
ctx.execute(`CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} ("dataset", "item_id") WHERE ${nonNoOpEventPredicate}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
];
|
|
472
|
+
function runSystemMigrations(opts) {
|
|
473
|
+
const ctx = {
|
|
474
|
+
...opts.dbConfig,
|
|
475
|
+
execute: opts.execute
|
|
476
|
+
};
|
|
477
|
+
for (const migration of opts.migrations) if (migration.version > opts.version.current) opts.transaction(() => {
|
|
478
|
+
migration.up(ctx);
|
|
479
|
+
opts.version.current = migration.version;
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
function applyWorkerDbSchema(db) {
|
|
483
|
+
db.executeKysely((kysely) => createKvStoreTableQuery(kysely.schema, "worker.kv"), { loggerLevel: "system" });
|
|
484
|
+
const kvStore = createSQLiteKvStore({
|
|
485
|
+
db,
|
|
486
|
+
metaTableName: "worker.kv"
|
|
487
|
+
});
|
|
488
|
+
runSystemMigrations({
|
|
489
|
+
migrations: baseSystemMigrations,
|
|
490
|
+
version: kvStore.createNumberStoredValue("internal-schema-version", -1),
|
|
491
|
+
dbConfig: workerDbConfig,
|
|
492
|
+
execute: (sql) => db.execute(sql, { loggerLevel: "system" }),
|
|
493
|
+
transaction: (callback) => db.executeTransaction(callback)
|
|
494
|
+
});
|
|
495
|
+
return { kvStore };
|
|
496
|
+
}
|
|
497
|
+
function applyMemoryDbSchema(db) {
|
|
498
|
+
db.execute(`CREATE TABLE IF NOT EXISTS ${memoryDbConfig.eventsTable.fullIdentifier} (
|
|
499
|
+
"sync_id" integer NOT NULL PRIMARY KEY,
|
|
500
|
+
"schema_version" integer NOT NULL,
|
|
501
|
+
"status" text NOT NULL,
|
|
502
|
+
"type" text NOT NULL,
|
|
503
|
+
"timestamp" text NOT NULL,
|
|
504
|
+
"origin" text NOT NULL,
|
|
505
|
+
"source_node_id" text NOT NULL DEFAULT '',
|
|
506
|
+
"dataset" text NOT NULL,
|
|
507
|
+
"item_id" text NOT NULL,
|
|
508
|
+
"payload" text NOT NULL
|
|
509
|
+
)`, { loggerLevel: "system" });
|
|
510
|
+
db.execute(`CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_status_sync_id_idx ON ${memoryDbConfig.eventsTable.table} ("status", "sync_id")`, { loggerLevel: "system" });
|
|
511
|
+
db.execute(`CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_timestamp_status_sync_id_idx ON ${memoryDbConfig.eventsTable.table} ("timestamp", "status", "sync_id")`, { loggerLevel: "system" });
|
|
512
|
+
db.execute(`CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_dataset_item_id_non_no_op_idx ON ${memoryDbConfig.eventsTable.table} ("dataset", "item_id") WHERE ${nonNoOpEventPredicate}`, { loggerLevel: "system" });
|
|
513
|
+
db.execute(`CREATE TABLE IF NOT EXISTS ${memoryDbConfig.updateLogTable.fullIdentifier} (
|
|
514
|
+
"dataset" text NOT NULL,
|
|
515
|
+
"item_id" text NOT NULL,
|
|
516
|
+
"payload" text NOT NULL,
|
|
517
|
+
PRIMARY KEY ("item_id", "dataset")
|
|
518
|
+
)`, { loggerLevel: "system" });
|
|
519
|
+
}
|
|
520
|
+
//#endregion
|
|
521
|
+
export { memoryDbConfig as a, createKvStoreTableQuery as c, SQLiteDbWrapper as d, compileKyselyStatement as f, dummyKysely as h, createSystemDbConfig as i, createSQLiteKvStore as l, introspectDb as m, applyWorkerDbSchema as n, runSystemMigrations as o, startPerformanceLogger as p, baseSystemMigrations as r, workerDbConfig as s, applyMemoryDbSchema as t, createStoredValue as u };
|
|
522
|
+
|
|
523
|
+
//# sourceMappingURL=system-schema-Dvl_BLId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-schema-Dvl_BLId.js","names":[],"sources":["../src/dummy-kysely.ts","../src/introspection.ts","../src/logger.ts","../src/sqlite-db-wrapper.ts","../src/sqlite-crdt/stored-value.ts","../src/sqlite-kv-store.ts","../src/migrations/system-schema.ts"],"sourcesContent":["import { DummyDriver, Kysely, SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler } from \"kysely\";\n\nexport const dummyKysely: Kysely<any> = new Kysely({\n dialect: {\n createAdapter: () => new SqliteAdapter(),\n createDriver: () => new DummyDriver(),\n createQueryCompiler: () => new SqliteQueryCompiler(),\n createIntrospector: (db) => new SqliteIntrospector(db),\n },\n});\n","import type { Kysely, QueryCreator } from \"kysely\";\nimport { sql } from \"kysely\";\nimport type { SQLiteDbWrapper } from \"./sqlite-db-wrapper\";\n\ninterface SqliteSystemDatabase {\n // https://www.sqlite.org/schematab.html#alternative_names\n sqlite_master: SQliteMasterTable;\n}\n\n// https://www.sqlite.org/schematab.html#interpretation_of_the_schema_table\ninterface SQliteMasterTable {\n name: string;\n rootpage: number | null;\n sql: string;\n tbl_name: string;\n type: \"index\" | \"table\" | \"trigger\" | \"view\";\n}\n\n// https://www.sqlite.org/pragma.html#pragma_table_info\ninterface PragmaTableInfo {\n cid: number;\n dflt_value: unknown;\n name: string;\n notnull: 0 | 1;\n pk: number;\n type: string;\n}\n\nfunction tablesQuery(qb: QueryCreator<SqliteSystemDatabase> | Kysely<SqliteSystemDatabase>) {\n return qb\n .selectFrom(\"sqlite_master\")\n .where(\"type\", \"in\", [\"table\", \"view\"])\n .where(\"name\", \"not like\", \"sqlite_%\")\n .select([\"name\", \"sql\", \"type\"])\n .orderBy(\"name\");\n}\n\nexport type TableMetadata = {\n name: string;\n isView: boolean;\n columns: ColumnMetadata[];\n};\n\nexport type DatabaseIntrospection = Record<string, TableMetadata>;\n\nexport type ColumnMetadata = {\n name: string;\n dataType: string;\n isNullable: boolean;\n isAutoIncrementing: boolean;\n hasDefaultValue: boolean;\n defaultValueSql: string | null;\n isPrimaryKey: boolean;\n comment: undefined;\n};\n\nexport function introspectDb<BaseDatabase>(_db: SQLiteDbWrapper<BaseDatabase>): DatabaseIntrospection {\n const db = _db as unknown as SQLiteDbWrapper<SqliteSystemDatabase>;\n const tables = db.executeKysely((db) => tablesQuery(db as unknown as Kysely<SqliteSystemDatabase>), {\n loggerLevel: \"system\",\n }).rows;\n\n const tablesMetadata = db.executeKysely(\n (db) =>\n db\n .with(\"table_list\", (qb) => tablesQuery(qb as unknown as Kysely<SqliteSystemDatabase>))\n .selectFrom([\"table_list as tl\", sql<PragmaTableInfo>`pragma_table_info(tl.name)`.as(\"p\")])\n .select([\"tl.name as table\", \"p.cid\", \"p.name\", \"p.type\", \"p.notnull\", \"p.dflt_value\", \"p.pk\"])\n .orderBy(\"tl.name\")\n .orderBy(\"p.cid\"),\n { loggerLevel: \"system\" },\n ).rows;\n\n const columnsByTable: Record<string, typeof tablesMetadata> = {};\n for (const row of tablesMetadata) {\n columnsByTable[row.table] ??= [];\n columnsByTable[row.table].push(row);\n }\n\n return Object.fromEntries(\n tables.map(({ name, sql, type }) => {\n // Try to find the name of the column that has `autoincrement`\n let autoIncrementCol = sql\n ?.split(/[(),]/)\n ?.find((it) => it.toLowerCase().includes(\"autoincrement\"))\n ?.trimStart()\n ?.split(/\\s+/)?.[0]\n ?.replace(/[\"`]/g, \"\");\n\n const columns = columnsByTable[name] ?? [];\n\n // Otherwise, check for an INTEGER PRIMARY KEY\n // https://www.sqlite.org/autoinc.html\n if (!autoIncrementCol) {\n const pkCols = columns.filter((r) => r.pk > 0);\n if (pkCols.length === 1 && pkCols[0].type.toLowerCase() === \"integer\") {\n autoIncrementCol = pkCols[0].name;\n }\n }\n\n return [\n name,\n {\n name: name,\n isView: type === \"view\",\n columns: columns.map((col) => ({\n name: col.name,\n dataType: col.type,\n isNullable: !col.notnull,\n isAutoIncrementing: col.name === autoIncrementCol,\n hasDefaultValue: col.dflt_value != null,\n defaultValueSql: col.dflt_value == null ? null : String(col.dflt_value),\n isPrimaryKey: col.pk > 0,\n comment: undefined,\n })),\n },\n ];\n }),\n );\n}\n","export type LogLevel = \"info\" | \"warning\" | \"error\" | \"trace\" | \"system\";\n\nexport type Logger = (type: string, message: string, level?: LogLevel) => void;\n\nexport type PerformanceLogger = {\n restart: () => void;\n logEnd: (type: string, message: string, level?: LogLevel) => void;\n};\n\nconst noopPerformanceLogger: PerformanceLogger = {\n restart() {},\n logEnd() {},\n};\n\nexport const startPerformanceLogger = (logger: Logger | undefined, level: LogLevel = \"info\"): PerformanceLogger => {\n if (!logger || level === \"system\") {\n return noopPerformanceLogger;\n }\n\n let startTime = performance.now();\n\n return {\n restart: () => {\n startTime = performance.now();\n },\n logEnd: (type: string, message: string, logLevel: LogLevel = level) => {\n const elapsed = performance.now() - startTime;\n\n logger(type, `${elapsed.toFixed(2)}ms - ${message}`, logLevel);\n },\n };\n};\n","import type {\n BindableValue,\n FunctionOptions,\n Database as SQLiteDatabase,\n Sqlite3Static,\n SqlValue,\n} from \"@sqlite.org/sqlite-wasm\";\nimport type { Compilable, CompiledQuery, Kysely } from \"kysely\";\nimport { dummyKysely } from \"./dummy-kysely\";\nimport { type DatabaseIntrospection, introspectDb } from \"./introspection\";\nimport { type Logger, startPerformanceLogger } from \"./logger\";\n\nexport type ExecuteParams = {\n sql: string;\n parameters: readonly unknown[];\n};\n\nexport type ExecuteResult<T> = {\n rows: T[];\n};\n\nexport type PreparedStatement<TParams extends unknown[], TResult> = {\n execute: (parameters: TParams) => TResult[];\n finalize: () => void;\n isFinalized: boolean;\n};\n\ntype ScalarFunctionOptions<TArgs extends readonly SqlValue[], TResult extends SqlValue | undefined> = {\n name: string;\n callback: (...args: TArgs) => TResult;\n} & Pick<FunctionOptions, \"deterministic\" | \"directOnly\" | \"innocuous\">;\n\ntype SqliteWrapperOptions = {\n logger?: Logger;\n loggerPrefix?: string;\n sqlite3: Sqlite3Static;\n db: () => SQLiteDatabase;\n};\n\nexport type SQLiteTransactionWrapper<TDatabase = unknown> = Pick<\n SQLiteDbWrapper<TDatabase>,\n \"sql\" | \"execute\" | \"executePrepared\" | \"executePreparedRaw\" | \"executeKysely\"\n>;\n\nexport type InternalSQLiteTransactionWrapper<TDatabase = unknown> = Pick<\n SQLiteDbWrapper<TDatabase>,\n \"executePrepared\" | \"executePreparedRaw\"\n>;\n\nexport type InternalSQLiteWrapper<TDatabase = unknown> = InternalSQLiteTransactionWrapper<TDatabase> & {\n executeTransaction: (callback: (db: InternalSQLiteTransactionWrapper<TDatabase>) => void) => void;\n};\n\ntype QueryMetaOpts = {\n loggerLevel?: \"info\" | \"system\";\n};\n\n/** Runs after user SQL succeeds, before its transaction or savepoint is committed. */\ntype AfterMutatingStatement<TDatabase> = (db: InternalSQLiteTransactionWrapper<TDatabase>) => void;\n\ntype ExecuteOptions = QueryMetaOpts & {\n /** For internal mutations whose effects must not be processed by afterMutatingStatement. */\n skipAfterMutatingStatement?: boolean;\n};\n\nexport class SQLiteDbWrapper<TDatabase = unknown> {\n private db: SQLiteDatabase | null = null;\n private sqlite3: Sqlite3Static;\n private logger?: Logger;\n private loggerPrefix?: string;\n\n private loadedDbSchema: DatabaseIntrospection | null = null;\n\n private readonly dataPointers = [] as number[];\n\n private preparedStatements: PreparedStatement<SqlValue[], unknown>[] = [];\n private preparedStatementsMap = new Map<string, TypedStatement<Record<string, unknown>, unknown>>();\n private preparedRawStatementsMap = new Map<string, PreparedStatement<SqlValue[], unknown>>();\n private afterMutatingStatement: AfterMutatingStatement<TDatabase> | null = null;\n\n constructor(opts: SqliteWrapperOptions) {\n this.db = opts.db();\n this.sqlite3 = opts.sqlite3;\n this.logger = opts.logger;\n this.loggerPrefix = opts.loggerPrefix;\n }\n\n get ensureDb() {\n if (!this.db) {\n throw new Error(\"Database is already closed\");\n }\n return this.db;\n }\n\n get dbSchema() {\n if (!this.loadedDbSchema) {\n this.loadedDbSchema = introspectDb(this);\n }\n return this.loadedDbSchema;\n }\n\n private executeSql<T>(\n sql: string,\n bind: readonly unknown[] | undefined,\n loggerLevel: QueryMetaOpts[\"loggerLevel\"],\n skipAfterMutatingStatement: boolean,\n ): ExecuteResult<T> {\n const perf = startPerformanceLogger(this.logger, loggerLevel);\n const statement = this.ensureDb.prepare(sql);\n let transaction: { commit: () => void; rollback: () => void } | undefined;\n\n try {\n const statementPointer = statement.pointer;\n if (!statementPointer) {\n throw new Error(\"Prepared statement is already finalized\");\n }\n const afterMutatingStatement =\n !skipAfterMutatingStatement &&\n this.afterMutatingStatement &&\n this.sqlite3.capi.sqlite3_stmt_readonly(statementPointer) === 0\n ? this.afterMutatingStatement\n : null;\n\n let totalChangesBefore = 0;\n if (afterMutatingStatement) {\n transaction = this.beginStatementTransaction();\n totalChangesBefore = this.sqlite3.capi.sqlite3_total_changes(this.ensureDb);\n }\n\n if (bind && bind.length > 0) {\n statement.bind(bind as BindableValue[]);\n }\n\n const rows: T[] = [];\n let columnNames: string[] | undefined;\n while (statement.step()) {\n columnNames ??= statement.getColumnNames([]);\n const values = statement.get([]);\n const row = Object.create(null) as Record<string, unknown>;\n for (let index = 0; index < columnNames.length; index++) {\n row[columnNames[index]] = values[index];\n }\n rows.push(row as T);\n }\n statement.finalize();\n perf.logEnd(`${this.loggerPrefix ?? \"\"}:query`, sql, loggerLevel);\n\n if (transaction) {\n // Nothing was written, so the statement cannot have produced any work for the callback.\n if (this.sqlite3.capi.sqlite3_total_changes(this.ensureDb) !== totalChangesBefore) {\n afterMutatingStatement?.(this);\n }\n transaction.commit();\n }\n\n return { rows };\n } catch (error) {\n statement.finalize();\n transaction?.rollback();\n throw error;\n }\n }\n\n execute<T = unknown>(opts: ExecuteParams | string | CompiledQuery<T>, options?: ExecuteOptions): ExecuteResult<T> {\n const sql = typeof opts === \"string\" ? opts : opts.sql;\n const bind = typeof opts === \"string\" ? undefined : opts.parameters;\n\n return this.executeSql<T>(sql, bind, options?.loggerLevel, options?.skipAfterMutatingStatement ?? false);\n }\n\n setAfterMutatingStatement(callback: AfterMutatingStatement<TDatabase>) {\n if (this.afterMutatingStatement) {\n throw new Error(\"An afterMutatingStatement callback is already registered\");\n }\n this.afterMutatingStatement = callback;\n }\n\n executeTransaction<T>(callback: (db: SQLiteTransactionWrapper<TDatabase>) => T): T {\n const transaction = this.beginTransaction();\n try {\n const result = callback(this);\n transaction.commit();\n return result;\n } catch (error) {\n transaction.rollback();\n throw error;\n }\n }\n\n isInTransaction() {\n return this.sqlite3.capi.sqlite3_get_autocommit(this.ensureDb) === 0;\n }\n\n beginTransaction() {\n this.executePreparedRaw({\n key: \"$begin-transaction\",\n sql: \"begin\",\n meta: {\n loggerLevel: \"system\",\n },\n });\n\n return {\n commit: () => {\n this.executePreparedRaw({\n key: \"$commit-transaction\",\n sql: \"commit\",\n meta: {\n loggerLevel: \"system\",\n },\n });\n },\n rollback: () => {\n this.executePreparedRaw({\n key: \"$rollback-transaction\",\n sql: \"rollback\",\n meta: {\n loggerLevel: \"system\",\n },\n });\n },\n };\n }\n\n private beginStatementTransaction() {\n if (!this.isInTransaction()) {\n return this.beginTransaction();\n }\n\n this.executePreparedRaw({\n key: \"$begin-statement-savepoint\",\n sql: \"savepoint sqlite_sync_statement\",\n meta: { loggerLevel: \"system\" },\n });\n\n return {\n commit: () => {\n this.executePreparedRaw({\n key: \"$commit-statement-savepoint\",\n sql: \"release savepoint sqlite_sync_statement\",\n meta: { loggerLevel: \"system\" },\n });\n },\n rollback: () => {\n this.executePreparedRaw({\n key: \"$rollback-statement-savepoint\",\n sql: \"rollback to savepoint sqlite_sync_statement\",\n meta: { loggerLevel: \"system\" },\n });\n this.executePreparedRaw({\n key: \"$release-rolled-back-statement-savepoint\",\n sql: \"release savepoint sqlite_sync_statement\",\n meta: { loggerLevel: \"system\" },\n });\n },\n };\n }\n\n prepare<TParams extends unknown[], TResult>(sql: string, opts?: QueryMetaOpts) {\n const perf = startPerformanceLogger(this.logger, opts?.loggerLevel);\n const stmt = this.ensureDb.prepare(sql);\n perf.logEnd(`${this.loggerPrefix ?? \"\"}:prepare`, sql, opts?.loggerLevel);\n\n let isFinalized = false;\n\n const execute = (params: TParams) => {\n if (isFinalized) {\n throw new Error(\"Statement is finalized\");\n }\n\n const perf = startPerformanceLogger(this.logger, opts?.loggerLevel);\n try {\n if (params.length > 0) {\n stmt.bind(params as SqlValue[]);\n }\n\n const results = [] as TResult[];\n // Reading rows as arrays and naming them here keeps sqlite from looking up\n // every column name again for every row.\n let columnNames: string[] | undefined;\n while (stmt.step()) {\n columnNames ??= stmt.getColumnNames([]);\n const values = stmt.get([]);\n const row = {} as Record<string, unknown>;\n for (let index = 0; index < columnNames.length; index++) {\n row[columnNames[index]] = values[index];\n }\n results.push(row as TResult);\n }\n\n return results;\n } finally {\n stmt.reset(true);\n perf.logEnd(`${this.loggerPrefix ?? \"\"}:prepare-execute`, sql, opts?.loggerLevel);\n }\n };\n\n const finalize = () => {\n isFinalized = true;\n stmt.finalize();\n };\n\n const preparedStatement: PreparedStatement<TParams, TResult> = {\n execute,\n finalize,\n get isFinalized() {\n return isFinalized;\n },\n };\n\n this.preparedStatements.push(preparedStatement as PreparedStatement<SqlValue[], unknown>);\n\n return preparedStatement;\n }\n\n prepareKysely<TParams extends Record<string, unknown>>(opts?: QueryMetaOpts) {\n return <TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>>(\n factory: KyselyStatementFactory<TParams, TDatabase, TQuery, TResult>,\n ): TypedStatement<TParams, TResult> => {\n const query = compileKyselyStatement(factory);\n const statement = this.prepare<SqlValue[], TResult>(query.sql, opts);\n\n return {\n execute: (parameters) => statement.execute(query.bindParameters(parameters) as SqlValue[]),\n };\n };\n }\n\n executeKysely<TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>>(\n factory: KyselyQueryFactory<TDatabase, TQuery, TResult>,\n options?: ExecuteOptions,\n ) {\n const query = factory(dummyKysely).compile();\n return this.execute(query, options);\n }\n\n executePrepared<\n TParams extends Record<string, unknown>,\n TQuery extends Compilable<TResult>,\n TResult = QueryBuilderOutput<TQuery>,\n >(\n key: string,\n params: TParams,\n factory: KyselyStatementFactory<TParams, TDatabase, TQuery, TResult>,\n meta?: QueryMetaOpts,\n ) {\n let statement = this.preparedStatementsMap.get(key) as TypedStatement<TParams, TResult> | undefined;\n if (!statement) {\n statement = this.prepareKysely<TParams>(meta)(factory);\n this.preparedStatementsMap.set(key, statement as TypedStatement<Record<string, unknown>, unknown>);\n }\n\n return statement.execute(params);\n }\n\n executePreparedRaw<TParams extends unknown[], TResult>({\n key,\n sql,\n params,\n meta,\n }: {\n key: string;\n sql: string;\n params?: TParams;\n meta?: QueryMetaOpts;\n }) {\n let statement = this.preparedRawStatementsMap.get(key) as PreparedStatement<TParams, TResult> | undefined;\n if (!statement) {\n statement = this.prepare(sql, meta);\n this.preparedRawStatementsMap.set(key, statement as PreparedStatement<any[], unknown>);\n }\n\n return statement.execute((params ?? []) as TParams);\n }\n\n sql<T = unknown>(templateOrString: TemplateStringsArray | string, ...parameters: unknown[]) {\n if (typeof templateOrString === \"string\") {\n return this.execute<T>({\n sql: templateOrString,\n parameters,\n });\n }\n return this.execute<T>({\n sql: templateOrString.join(\"?\"),\n parameters,\n });\n }\n\n createScalarFunction<TArgs extends SqlValue[], TResult extends SqlValue | undefined>({\n name,\n callback,\n deterministic,\n directOnly,\n innocuous,\n }: ScalarFunctionOptions<TArgs, TResult>) {\n return this.ensureDb.createFunction({\n name,\n xFunc: (_, ...args) => {\n const result = callback(...(args as TArgs)) as SqlValue;\n return result;\n },\n arity: callback.length,\n deterministic,\n directOnly,\n innocuous,\n });\n }\n\n useSnapshot(snapshot: Uint8Array<ArrayBufferLike>) {\n const perf = startPerformanceLogger(this.logger, \"system\");\n const dataPointer = this.sqlite3.wasm.allocFromTypedArray(snapshot);\n this.dataPointers.push(dataPointer);\n\n const resultCode = this.sqlite3.capi.sqlite3_deserialize(\n this.ensureDb,\n \"main\",\n dataPointer,\n snapshot.byteLength,\n snapshot.byteLength,\n this.sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE | this.sqlite3.capi.SQLITE_DESERIALIZE_RESIZEABLE,\n );\n\n this.ensureDb.checkRc(resultCode);\n\n this.invalidateDbSchema();\n\n perf.logEnd(\"useSnapshot\", \"success\", \"system\");\n }\n\n createSnapshot() {\n return this.sqlite3.capi.sqlite3_js_db_export(this.ensureDb);\n }\n\n invalidateDbSchema() {\n this.loadedDbSchema = null;\n }\n\n cleanup() {\n this.preparedStatements.forEach((stmt) => {\n stmt.finalize();\n });\n this.preparedStatements.splice(0);\n this.preparedStatementsMap.clear();\n this.preparedRawStatementsMap.clear();\n }\n\n close() {\n this.cleanup();\n\n this.db?.close();\n this.db = null;\n }\n}\n\nexport type QueryBuilderOutput<QB> = QB extends Compilable<infer O> ? O : never;\ntype ParamsGetter<TParams> = <TKey extends keyof TParams>(key: TKey) => TParams[TKey];\n\ntype TypedStatement<TParams extends Record<string, unknown>, TResult> = {\n execute: (parameters: TParams) => TResult[];\n};\nexport type KyselyStatementFactory<\n TParams extends Record<string, unknown>,\n TDatabase,\n TQuery extends Compilable<TResult>,\n TResult = QueryBuilderOutput<TQuery>,\n> = (kysely: Kysely<TDatabase>, params: ParamsGetter<TParams>) => TQuery;\nexport type KyselyQueryFactory<TDatabase, TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>> = (\n kysely: Kysely<TDatabase>,\n) => TQuery;\n\nexport function compileKyselyStatement<TParams extends Record<string, unknown>, TDatabase>(\n factory: KyselyStatementFactory<TParams, TDatabase, Compilable<unknown>>,\n) {\n const parameterKeys = new Map<unknown, keyof TParams>();\n const query = factory(dummyKysely, (key) => {\n const token = {};\n parameterKeys.set(token, key);\n return token as TParams[typeof key];\n }).compile();\n\n return {\n sql: query.sql,\n bindParameters: (parameters: TParams) =>\n query.parameters.map((value) => {\n const key = parameterKeys.get(value);\n return key === undefined ? value : parameters[key];\n }),\n };\n}\n","export type StoredValue<T> = {\n get current(): T;\n set current(newValue: T);\n};\n\nexport function createStoredValue<T>({\n initialValue,\n saveToStorage,\n}: {\n initialValue: T;\n saveToStorage?: (value: T) => void;\n}): StoredValue<T> {\n let currentValue = initialValue;\n\n return {\n get current() {\n return currentValue;\n },\n set current(newValue: T) {\n saveToStorage?.(newValue);\n currentValue = newValue;\n },\n };\n}\n","import { type SchemaModule, sql } from \"kysely\";\nimport { createStoredValue } from \"./sqlite-crdt/stored-value\";\nimport type { InternalSQLiteWrapper } from \"./sqlite-db-wrapper\";\n\nexport type KvStoreItem = {\n key: string;\n value: string;\n};\n\nexport function createKvStoreTableQuery(schema: SchemaModule, tableName: string) {\n return schema\n .createTable(tableName)\n .ifNotExists()\n .addColumn(\"key\", \"text\", (col) => col.notNull().primaryKey())\n .addColumn(\"value\", \"text\", (col) => col.notNull())\n .modifyEnd(sql`without rowid`);\n}\n\nexport function createSQLiteKvStore({ db, metaTableName }: { db: InternalSQLiteWrapper<any>; metaTableName: string }) {\n const metaDb = db as InternalSQLiteWrapper<{\n meta: KvStoreItem;\n }>;\n\n const get = (key: string): string | null => {\n const [result] = metaDb.executePrepared(\n \"get-meta-value\",\n { key },\n (db, params) =>\n db\n .selectFrom(metaTableName as \"meta\")\n .where(\"key\", \"=\", params(\"key\"))\n .select(\"value\")\n .limit((eb) => eb.lit(1)),\n { loggerLevel: \"system\" },\n );\n\n return result?.value ?? null;\n };\n\n const set = (key: string, value: string) => {\n metaDb.executePrepared(\n \"set-meta-value\",\n { key, value },\n (db, params) =>\n db\n .insertInto(metaTableName as \"meta\")\n .values({ key: params(\"key\"), value: params(\"value\") })\n .onConflict((oc) => oc.doUpdateSet({ value: params(\"value\") })),\n { loggerLevel: \"system\" },\n );\n };\n\n const remove = (key: string) => {\n metaDb.executePrepared(\n \"remove-meta-value\",\n { key },\n (db, params) => db.deleteFrom(metaTableName as \"meta\").where(\"key\", \"=\", params(\"key\")),\n { loggerLevel: \"system\" },\n );\n };\n\n const getNumberOrDefault = <T>(key: string, defaultValue: T): T | number => {\n const value = get(key);\n if (!value) return defaultValue;\n const parsedValue = Number.parseInt(value, 10);\n return Number.isNaN(parsedValue) ? defaultValue : parsedValue;\n };\n\n return {\n get,\n set,\n remove,\n createStringStoredValue: (key: string, defaultValue: string) =>\n createStoredValue<string>({\n initialValue: get(key) ?? defaultValue,\n saveToStorage: (val) => set(key, val),\n }),\n createNumberStoredValue: (key: string, defaultValue: number) =>\n createStoredValue<number>({\n initialValue: getNumberOrDefault(key, defaultValue),\n saveToStorage: (val) => set(key, val.toString()),\n }),\n };\n}\n\nexport type KvStore = ReturnType<typeof createSQLiteKvStore>;\n","import {\n CRDT_EVENT_NO_OP_PAYLOAD,\n type CrdtUpdateLogItem,\n type PersistedCrdtEvent,\n} from \"../sqlite-crdt/crdt-table-schema\";\nimport type { StoredValue } from \"../sqlite-crdt/stored-value\";\nimport type { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport { createKvStoreTableQuery, createSQLiteKvStore, type KvStoreItem } from \"../sqlite-kv-store\";\nimport { type ParsedTableName, parseTableName } from \"../utils\";\n\nexport type SystemDbConfig = {\n eventsTable: ParsedTableName;\n updateLogTable: ParsedTableName;\n};\n\nexport function createSystemDbConfig({\n eventsTableName,\n updateLogTableName,\n}: {\n eventsTableName: string;\n updateLogTableName: string;\n}): SystemDbConfig {\n return {\n eventsTable: parseTableName(eventsTableName),\n updateLogTable: parseTableName(updateLogTableName),\n };\n}\n\nexport type WorkerDbSchema = {\n crdt_update_log: CrdtUpdateLogItem;\n \"worker.kv\": KvStoreItem;\n \"worker.crdt_events\": PersistedCrdtEvent;\n};\n\nexport const workerDbConfig = createSystemDbConfig({\n eventsTableName: \"worker.crdt_events\",\n updateLogTableName: \"crdt_update_log\",\n});\n\nexport type MemoryDbSchema = {\n crdt_update_log: CrdtUpdateLogItem;\n persisted_crdt_events: PersistedCrdtEvent;\n};\n\nexport const memoryDbConfig = createSystemDbConfig({\n eventsTableName: \"persisted_crdt_events\",\n updateLogTableName: \"crdt_update_log\",\n});\n\nexport type SystemMigrationContext = SystemDbConfig & {\n execute: (sql: string) => void;\n};\n\nexport type SystemMigration = {\n version: number;\n up: (ctx: SystemMigrationContext) => void;\n};\n\nconst nonNoOpEventPredicate = `\"payload\" <> '${CRDT_EVENT_NO_OP_PAYLOAD}'`;\n\nexport const baseSystemMigrations: SystemMigration[] = [\n {\n version: 0,\n up: (ctx: SystemMigrationContext) => {\n ctx.execute(`CREATE TABLE IF NOT EXISTS ${ctx.eventsTable.fullIdentifier} (\n \"sync_id\" integer NOT NULL PRIMARY KEY,\n \"schema_version\" integer NOT NULL,\n \"status\" text NOT NULL,\n \"type\" text NOT NULL,\n \"timestamp\" text NOT NULL,\n \"origin\" text NOT NULL,\n \"dataset\" text NOT NULL,\n \"item_id\" text NOT NULL,\n \"payload\" text NOT NULL\n )`);\n ctx.execute(`CREATE TABLE IF NOT EXISTS ${ctx.updateLogTable.fullIdentifier} (\n \"dataset\" text NOT NULL,\n \"item_id\" text NOT NULL,\n \"payload\" text NOT NULL,\n PRIMARY KEY (\"item_id\", \"dataset\")\n )`);\n },\n },\n {\n version: 1,\n up: (ctx: SystemMigrationContext) => {\n ctx.execute(`ALTER TABLE ${ctx.eventsTable.fullIdentifier} ADD COLUMN \"source_node_id\" TEXT NOT NULL DEFAULT ''`);\n },\n },\n {\n version: 2,\n up: (ctx: SystemMigrationContext) => {\n const indexName = `${ctx.eventsTable.table}_status_sync_id_idx`;\n ctx.execute(\n `CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} (\"status\", \"sync_id\")`,\n );\n },\n },\n {\n version: 3,\n up: (ctx: SystemMigrationContext) => {\n const indexName = `${ctx.eventsTable.table}_timestamp_status_sync_id_idx`;\n ctx.execute(\n `CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} (\"timestamp\", \"status\", \"sync_id\")`,\n );\n },\n },\n {\n version: 4,\n up: (ctx: SystemMigrationContext) => {\n const indexName = `${ctx.eventsTable.table}_dataset_item_id_non_no_op_idx`;\n ctx.execute(\n `CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} (\"dataset\", \"item_id\") WHERE ${nonNoOpEventPredicate}`,\n );\n },\n },\n];\n\nexport function runSystemMigrations(opts: {\n version: StoredValue<number>;\n migrations: SystemMigration[];\n dbConfig: SystemDbConfig;\n execute: (sql: string) => void;\n transaction: (callback: () => void) => void;\n}): void {\n const ctx: SystemMigrationContext = {\n ...opts.dbConfig,\n execute: opts.execute,\n };\n for (const migration of opts.migrations) {\n if (migration.version > opts.version.current) {\n opts.transaction(() => {\n migration.up(ctx);\n opts.version.current = migration.version;\n });\n }\n }\n}\n\nexport function applyWorkerDbSchema(db: SQLiteDbWrapper<any>) {\n // KV table stays separate — needed before system migrations for version tracking\n db.executeKysely((kysely) => createKvStoreTableQuery(kysely.schema, \"worker.kv\"), { loggerLevel: \"system\" });\n\n // System schema migrations (each in its own transaction)\n const kvStore = createSQLiteKvStore({ db, metaTableName: \"worker.kv\" });\n runSystemMigrations({\n migrations: baseSystemMigrations,\n version: kvStore.createNumberStoredValue(\"internal-schema-version\", -1),\n dbConfig: workerDbConfig,\n execute: (sql) => db.execute(sql, { loggerLevel: \"system\" }),\n transaction: (callback) => db.executeTransaction(callback),\n });\n\n return { kvStore };\n}\n\nexport function applyMemoryDbSchema(db: SQLiteDbWrapper<any>) {\n db.execute(\n `CREATE TABLE IF NOT EXISTS ${memoryDbConfig.eventsTable.fullIdentifier} (\n \"sync_id\" integer NOT NULL PRIMARY KEY,\n \"schema_version\" integer NOT NULL,\n \"status\" text NOT NULL,\n \"type\" text NOT NULL,\n \"timestamp\" text NOT NULL,\n \"origin\" text NOT NULL,\n \"source_node_id\" text NOT NULL DEFAULT '',\n \"dataset\" text NOT NULL,\n \"item_id\" text NOT NULL,\n \"payload\" text NOT NULL\n )`,\n { loggerLevel: \"system\" },\n );\n db.execute(\n `CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_status_sync_id_idx ON ${memoryDbConfig.eventsTable.table} (\"status\", \"sync_id\")`,\n {\n loggerLevel: \"system\",\n },\n );\n db.execute(\n `CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_timestamp_status_sync_id_idx ON ${memoryDbConfig.eventsTable.table} (\"timestamp\", \"status\", \"sync_id\")`,\n {\n loggerLevel: \"system\",\n },\n );\n db.execute(\n `CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_dataset_item_id_non_no_op_idx ON ${memoryDbConfig.eventsTable.table} (\"dataset\", \"item_id\") WHERE ${nonNoOpEventPredicate}`,\n {\n loggerLevel: \"system\",\n },\n );\n db.execute(\n `CREATE TABLE IF NOT EXISTS ${memoryDbConfig.updateLogTable.fullIdentifier} (\n \"dataset\" text NOT NULL,\n \"item_id\" text NOT NULL,\n \"payload\" text NOT NULL,\n PRIMARY KEY (\"item_id\", \"dataset\")\n)`,\n { loggerLevel: \"system\" },\n );\n}\n"],"mappings":"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"}
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as WorkerConfig, Vt as Logger, _ as CreateRemoteSourceFactory, b as EventsPushResponse, en as SyncDbSchema, r as ResetStore, rt as GetEventsBatch, v as EventsPullRequest, y as EventsPushRequest } from "./index-BHsKLqcK.js";
|
|
2
2
|
//#region src/web-socket/ws-remote-source.d.ts
|
|
3
3
|
type WsRemoteSourceConfig = {
|
|
4
4
|
createWebSocket: () => Pick<WebSocket, "onmessage" | "close" | "addEventListener" | "removeEventListener"> & {
|
|
@@ -34,6 +34,7 @@ type WorkerOptions = {
|
|
|
34
34
|
* Runs local worker event-log garbage collection on startup. Disabled by default;
|
|
35
35
|
* set `eventLogGc: true` to enable. GC keeps the latest event rows and never removes
|
|
36
36
|
* pending events or local rows that have not been pushed to the remote.
|
|
37
|
+
* Skipped while an existing tab holds an in-memory replica of this DB.
|
|
37
38
|
*/
|
|
38
39
|
eventLogGc?: boolean;
|
|
39
40
|
};
|
package/dist/worker.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { d as SQLiteDbWrapper, n as applyWorkerDbSchema, s as workerDbConfig } from "./system-schema-Dvl_BLId.js";
|
|
2
|
+
import { l as generateId, o as createDeferredPromise, u as jsonSafeParse } from "./crdt-table-schema-BRm_WdcW.js";
|
|
3
|
+
import { C as xxhash, S as createMigrator, T as createImportData, b as verifySyncSchema, d as isWorkerRequestMessage, g as createCrdtStorage, h as createCrdtSyncProducer, i as createResetStateStore, l as isWorkerInitMessage, m as syncDbWorkerLockName, n as createIdbResetStore, o as runWithNoActiveReplicas, p as syncDbClientLockName, r as createReloadRequestHandler, s as createBroadcastChannels, y as formatSchemaVerificationIssues } from "./reset-state-t0tTGqoh.js";
|
|
4
|
+
import { a as HLCCounter, t as createCrdtSyncRemoteSource } from "./crdt-sync-remote-source-CPXzmSIS.js";
|
|
5
5
|
import { t as HEARTBEAT_PING } from "./heartbeat-CJWQAoGP.js";
|
|
6
6
|
import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
|
|
7
7
|
//#region src/web-socket/ws-remote-source.ts
|
|
@@ -247,12 +247,17 @@ async function createDbWorker(config, opts) {
|
|
|
247
247
|
if (!storageGeneration.current) storageGeneration.current = generateId();
|
|
248
248
|
if (pendingReset) await resetState.markResetApplied(pendingReset.epoch);
|
|
249
249
|
if (isVersionMismatch) await storageVersion.markCurrentVersionApplied();
|
|
250
|
-
if (opts.eventLogGc === true)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
250
|
+
if (opts.eventLogGc === true) await runWithNoActiveReplicas({
|
|
251
|
+
dbId: config.dbId,
|
|
252
|
+
run: () => {
|
|
253
|
+
runWorkerEventLogGc({
|
|
254
|
+
db,
|
|
255
|
+
dbConfig: workerDbConfig,
|
|
256
|
+
pushSyncId,
|
|
257
|
+
eventHlcAccumulator,
|
|
258
|
+
logger
|
|
259
|
+
});
|
|
260
|
+
}
|
|
256
261
|
});
|
|
257
262
|
const crdtStorage = createCrdtStorage({
|
|
258
263
|
nodeId: config.clientId,
|