@sqlite-sync/core 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,18 +1,10 @@
1
- import { c as generateId, f as quoteId, n as isNoOpCrdtEventPayload, o as createTypedEventTarget, r as TypedBroadcastChannel, s as ensureSingletonExecution, t as CRDT_EVENT_NO_OP_PAYLOAD } from "./crdt-table-schema-2w_AIpsV.js";
2
- import { c as serializeHLC, n as CrdtEventValidationError, r as validateNewCrdtEvent, s as deserializeHLC } from "./crdt-sync-remote-source-Q6P6OiL0.js";
3
- import { u as createStoredValue } from "./system-schema-C2JZDv6L.js";
4
- import { DummyDriver, Kysely, SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler, sql } from "kysely";
1
+ import { d as SQLiteDbWrapper, h as dummyKysely, m as introspectDb, u as createStoredValue } from "./system-schema-Dvl_BLId.js";
2
+ import { a as acquireLock, c as ensureSingletonExecution, l as generateId, n as isNoOpCrdtEventPayload, p as quoteId, r as TypedBroadcastChannel, s as createTypedEventTarget, t as CRDT_EVENT_NO_OP_PAYLOAD } from "./crdt-table-schema-BRm_WdcW.js";
3
+ import { c as serializeHLC, n as CrdtEventValidationError, r as validateNewCrdtEvent, s as deserializeHLC } from "./crdt-sync-remote-source-CPXzmSIS.js";
4
+ import { sql } from "kysely";
5
5
  import initXxhash from "xxhash-wasm";
6
6
  import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
7
7
  import { createStore, del, get, set } from "idb-keyval";
8
- //#region src/dummy-kysely.ts
9
- const dummyKysely = new Kysely({ dialect: {
10
- createAdapter: () => new SqliteAdapter(),
11
- createDriver: () => new DummyDriver(),
12
- createQueryCompiler: () => new SqliteQueryCompiler(),
13
- createIntrospector: (db) => new SqliteIntrospector(db)
14
- } });
15
- //#endregion
16
8
  //#region src/export-import.ts
17
9
  function createTableNameNormalizer(tablesConfig) {
18
10
  const tablesByAlias = /* @__PURE__ */ new Map();
@@ -133,331 +125,6 @@ const xxhash = {
133
125
  h64
134
126
  };
135
127
  //#endregion
136
- //#region src/introspection.ts
137
- function tablesQuery(qb) {
138
- return qb.selectFrom("sqlite_master").where("type", "in", ["table", "view"]).where("name", "not like", "sqlite_%").select([
139
- "name",
140
- "sql",
141
- "type"
142
- ]).orderBy("name");
143
- }
144
- function introspectDb(_db) {
145
- const db = _db;
146
- const tables = db.executeKysely((db) => tablesQuery(db), { loggerLevel: "system" }).rows;
147
- 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([
148
- "tl.name as table",
149
- "p.cid",
150
- "p.name",
151
- "p.type",
152
- "p.notnull",
153
- "p.dflt_value",
154
- "p.pk"
155
- ]).orderBy("tl.name").orderBy("p.cid"), { loggerLevel: "system" }).rows;
156
- const columnsByTable = {};
157
- for (const row of tablesMetadata) {
158
- columnsByTable[row.table] ??= [];
159
- columnsByTable[row.table].push(row);
160
- }
161
- return Object.fromEntries(tables.map(({ name, sql, type }) => {
162
- let autoIncrementCol = sql?.split(/[(),]/)?.find((it) => it.toLowerCase().includes("autoincrement"))?.trimStart()?.split(/\s+/)?.[0]?.replace(/["`]/g, "");
163
- const columns = columnsByTable[name] ?? [];
164
- if (!autoIncrementCol) {
165
- const pkCols = columns.filter((r) => r.pk > 0);
166
- if (pkCols.length === 1 && pkCols[0].type.toLowerCase() === "integer") autoIncrementCol = pkCols[0].name;
167
- }
168
- return [name, {
169
- name,
170
- isView: type === "view",
171
- columns: columns.map((col) => ({
172
- name: col.name,
173
- dataType: col.type,
174
- isNullable: !col.notnull,
175
- isAutoIncrementing: col.name === autoIncrementCol,
176
- hasDefaultValue: col.dflt_value != null,
177
- defaultValueSql: col.dflt_value == null ? null : String(col.dflt_value),
178
- isPrimaryKey: col.pk > 0,
179
- comment: void 0
180
- }))
181
- }];
182
- }));
183
- }
184
- //#endregion
185
- //#region src/logger.ts
186
- const noopPerformanceLogger = {
187
- restart() {},
188
- logEnd() {}
189
- };
190
- const startPerformanceLogger = (logger, level = "info") => {
191
- if (!logger || level === "system") return noopPerformanceLogger;
192
- let startTime = performance.now();
193
- return {
194
- restart: () => {
195
- startTime = performance.now();
196
- },
197
- logEnd: (type, message, logLevel = level) => {
198
- logger(type, `${(performance.now() - startTime).toFixed(2)}ms - ${message}`, logLevel);
199
- }
200
- };
201
- };
202
- //#endregion
203
- //#region src/sqlite-db-wrapper.ts
204
- var SQLiteDbWrapper = class {
205
- db = null;
206
- sqlite3;
207
- logger;
208
- loggerPrefix;
209
- loadedDbSchema = null;
210
- dataPointers = [];
211
- preparedStatements = [];
212
- preparedStatementsMap = /* @__PURE__ */ new Map();
213
- preparedRawStatementsMap = /* @__PURE__ */ new Map();
214
- afterMutatingStatement = null;
215
- constructor(opts) {
216
- this.db = opts.db();
217
- this.sqlite3 = opts.sqlite3;
218
- this.logger = opts.logger;
219
- this.loggerPrefix = opts.loggerPrefix;
220
- }
221
- get ensureDb() {
222
- if (!this.db) throw new Error("Database is already closed");
223
- return this.db;
224
- }
225
- get dbSchema() {
226
- if (!this.loadedDbSchema) this.loadedDbSchema = introspectDb(this);
227
- return this.loadedDbSchema;
228
- }
229
- executeSql(sql, bind, loggerLevel, skipAfterMutatingStatement) {
230
- const perf = startPerformanceLogger(this.logger, loggerLevel);
231
- const statement = this.ensureDb.prepare(sql);
232
- let transaction;
233
- try {
234
- const statementPointer = statement.pointer;
235
- if (!statementPointer) throw new Error("Prepared statement is already finalized");
236
- const afterMutatingStatement = !skipAfterMutatingStatement && this.afterMutatingStatement && this.sqlite3.capi.sqlite3_stmt_readonly(statementPointer) === 0 ? this.afterMutatingStatement : null;
237
- let totalChangesBefore = 0;
238
- if (afterMutatingStatement) {
239
- transaction = this.beginStatementTransaction();
240
- totalChangesBefore = this.sqlite3.capi.sqlite3_total_changes(this.ensureDb);
241
- }
242
- if (bind && bind.length > 0) statement.bind(bind);
243
- const rows = [];
244
- let columnNames;
245
- while (statement.step()) {
246
- columnNames ??= statement.getColumnNames([]);
247
- const values = statement.get([]);
248
- const row = Object.create(null);
249
- for (let index = 0; index < columnNames.length; index++) row[columnNames[index]] = values[index];
250
- rows.push(row);
251
- }
252
- statement.finalize();
253
- perf.logEnd(`${this.loggerPrefix ?? ""}:query`, sql, loggerLevel);
254
- if (transaction) {
255
- if (this.sqlite3.capi.sqlite3_total_changes(this.ensureDb) !== totalChangesBefore) afterMutatingStatement?.(this);
256
- transaction.commit();
257
- }
258
- return { rows };
259
- } catch (error) {
260
- statement.finalize();
261
- transaction?.rollback();
262
- throw error;
263
- }
264
- }
265
- execute(opts, options) {
266
- const sql = typeof opts === "string" ? opts : opts.sql;
267
- const bind = typeof opts === "string" ? void 0 : opts.parameters;
268
- return this.executeSql(sql, bind, options?.loggerLevel, options?.skipAfterMutatingStatement ?? false);
269
- }
270
- setAfterMutatingStatement(callback) {
271
- if (this.afterMutatingStatement) throw new Error("An afterMutatingStatement callback is already registered");
272
- this.afterMutatingStatement = callback;
273
- }
274
- executeTransaction(callback) {
275
- const transaction = this.beginTransaction();
276
- try {
277
- const result = callback(this);
278
- transaction.commit();
279
- return result;
280
- } catch (error) {
281
- transaction.rollback();
282
- throw error;
283
- }
284
- }
285
- isInTransaction() {
286
- return this.sqlite3.capi.sqlite3_get_autocommit(this.ensureDb) === 0;
287
- }
288
- beginTransaction() {
289
- this.executePreparedRaw({
290
- key: "$begin-transaction",
291
- sql: "begin",
292
- meta: { loggerLevel: "system" }
293
- });
294
- return {
295
- commit: () => {
296
- this.executePreparedRaw({
297
- key: "$commit-transaction",
298
- sql: "commit",
299
- meta: { loggerLevel: "system" }
300
- });
301
- },
302
- rollback: () => {
303
- this.executePreparedRaw({
304
- key: "$rollback-transaction",
305
- sql: "rollback",
306
- meta: { loggerLevel: "system" }
307
- });
308
- }
309
- };
310
- }
311
- beginStatementTransaction() {
312
- if (!this.isInTransaction()) return this.beginTransaction();
313
- this.executePreparedRaw({
314
- key: "$begin-statement-savepoint",
315
- sql: "savepoint sqlite_sync_statement",
316
- meta: { loggerLevel: "system" }
317
- });
318
- return {
319
- commit: () => {
320
- this.executePreparedRaw({
321
- key: "$commit-statement-savepoint",
322
- sql: "release savepoint sqlite_sync_statement",
323
- meta: { loggerLevel: "system" }
324
- });
325
- },
326
- rollback: () => {
327
- this.executePreparedRaw({
328
- key: "$rollback-statement-savepoint",
329
- sql: "rollback to savepoint sqlite_sync_statement",
330
- meta: { loggerLevel: "system" }
331
- });
332
- this.executePreparedRaw({
333
- key: "$release-rolled-back-statement-savepoint",
334
- sql: "release savepoint sqlite_sync_statement",
335
- meta: { loggerLevel: "system" }
336
- });
337
- }
338
- };
339
- }
340
- prepare(sql, opts) {
341
- const perf = startPerformanceLogger(this.logger, opts?.loggerLevel);
342
- const stmt = this.ensureDb.prepare(sql);
343
- perf.logEnd(`${this.loggerPrefix ?? ""}:prepare`, sql, opts?.loggerLevel);
344
- let isFinalized = false;
345
- const execute = (params) => {
346
- if (isFinalized) throw new Error("Statement is finalized");
347
- const perf = startPerformanceLogger(this.logger, opts?.loggerLevel);
348
- try {
349
- if (params.length > 0) stmt.bind(params);
350
- const results = [];
351
- let columnNames;
352
- while (stmt.step()) {
353
- columnNames ??= stmt.getColumnNames([]);
354
- const values = stmt.get([]);
355
- const row = {};
356
- for (let index = 0; index < columnNames.length; index++) row[columnNames[index]] = values[index];
357
- results.push(row);
358
- }
359
- return results;
360
- } finally {
361
- stmt.reset(true);
362
- perf.logEnd(`${this.loggerPrefix ?? ""}:prepare-execute`, sql, opts?.loggerLevel);
363
- }
364
- };
365
- const finalize = () => {
366
- isFinalized = true;
367
- stmt.finalize();
368
- };
369
- const preparedStatement = {
370
- execute,
371
- finalize,
372
- get isFinalized() {
373
- return isFinalized;
374
- }
375
- };
376
- this.preparedStatements.push(preparedStatement);
377
- return preparedStatement;
378
- }
379
- prepareKysely(opts) {
380
- return (factory) => {
381
- const query = factory(dummyKysely, (key) => key).compile();
382
- const statement = this.prepare(query.sql, opts);
383
- return { execute: (parameters) => {
384
- const params = query.parameters.map((param) => parameters[param]);
385
- return statement.execute(params);
386
- } };
387
- };
388
- }
389
- executeKysely(factory, options) {
390
- const query = factory(dummyKysely).compile();
391
- return this.execute(query, options);
392
- }
393
- executePrepared(key, params, factory, meta) {
394
- let statement = this.preparedStatementsMap.get(key);
395
- if (!statement) {
396
- statement = this.prepareKysely(meta)(factory);
397
- this.preparedStatementsMap.set(key, statement);
398
- }
399
- return statement.execute(params);
400
- }
401
- executePreparedRaw({ key, sql, params, meta }) {
402
- let statement = this.preparedRawStatementsMap.get(key);
403
- if (!statement) {
404
- statement = this.prepare(sql, meta);
405
- this.preparedRawStatementsMap.set(key, statement);
406
- }
407
- return statement.execute(params ?? []);
408
- }
409
- sql(templateOrString, ...parameters) {
410
- if (typeof templateOrString === "string") return this.execute({
411
- sql: templateOrString,
412
- parameters
413
- });
414
- return this.execute({
415
- sql: templateOrString.join("?"),
416
- parameters
417
- });
418
- }
419
- createScalarFunction({ name, callback, deterministic, directOnly, innocuous }) {
420
- return this.ensureDb.createFunction({
421
- name,
422
- xFunc: (_, ...args) => {
423
- return callback(...args);
424
- },
425
- arity: callback.length,
426
- deterministic,
427
- directOnly,
428
- innocuous
429
- });
430
- }
431
- useSnapshot(snapshot) {
432
- const perf = startPerformanceLogger(this.logger, "system");
433
- const dataPointer = this.sqlite3.wasm.allocFromTypedArray(snapshot);
434
- this.dataPointers.push(dataPointer);
435
- 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);
436
- this.ensureDb.checkRc(resultCode);
437
- this.invalidateDbSchema();
438
- perf.logEnd("useSnapshot", "success", "system");
439
- }
440
- createSnapshot() {
441
- return this.sqlite3.capi.sqlite3_js_db_export(this.ensureDb);
442
- }
443
- invalidateDbSchema() {
444
- this.loadedDbSchema = null;
445
- }
446
- cleanup() {
447
- this.preparedStatements.forEach((stmt) => {
448
- stmt.finalize();
449
- });
450
- this.preparedStatements.splice(0);
451
- this.preparedStatementsMap.clear();
452
- this.preparedRawStatementsMap.clear();
453
- }
454
- close() {
455
- this.cleanup();
456
- this.db?.close();
457
- this.db = null;
458
- }
459
- };
460
- //#endregion
461
128
  //#region src/migrations/migrator.ts
462
129
  const protectedColumns = ["id", "tombstone"];
463
130
  function assertColumnNotProtected(column, operation) {
@@ -902,9 +569,7 @@ const createSQLiteCrdtApplyFunction = ({ db, dbConfig }) => {
902
569
  },
903
570
  insertItem(opts) {
904
571
  const keys = Object.keys(opts.payload).sort();
905
- const insertPayload = {};
906
- for (const key of keys) insertPayload[key] = key;
907
- db.executePrepared(`crdt-insert-item-${opts.dataset}-${keys.join("-")}`, opts.payload, (db) => db.insertInto(opts.dataset).values(insertPayload), { loggerLevel: "system" });
572
+ db.executePrepared(`crdt-insert-item-${opts.dataset}-${keys.join("-")}`, opts.payload, (db, params) => db.insertInto(opts.dataset).values(Object.fromEntries(keys.map((key) => [key, params(key)]))), { loggerLevel: "system" });
908
573
  },
909
574
  updateItem(opts) {
910
575
  const keys = Array.from(Object.keys(opts.payload));
@@ -1550,6 +1215,22 @@ function isWorkerNotificationMessage(message) {
1550
1215
  return typeof message === "object" && message !== null && "notificationType" in message && !!message.notificationType;
1551
1216
  }
1552
1217
  //#endregion
1218
+ //#region src/worker-db/replica-lock.ts
1219
+ const replicaLockName = (dbId) => `sync-db-replica-lock-${dbId}`;
1220
+ function acquireReplicaLock({ dbId }) {
1221
+ return acquireLock(replicaLockName(dbId), { mode: "shared" });
1222
+ }
1223
+ function runWithNoActiveReplicas({ dbId, run }) {
1224
+ return navigator.locks.request(replicaLockName(dbId), {
1225
+ mode: "exclusive",
1226
+ ifAvailable: true
1227
+ }, async (lock) => {
1228
+ if (!lock) return false;
1229
+ await run();
1230
+ return true;
1231
+ });
1232
+ }
1233
+ //#endregion
1553
1234
  //#region src/worker-db/reset-state.ts
1554
1235
  /** Default IndexedDB-backed reset store (idb-keyval over a dedicated database). */
1555
1236
  function createIdbResetStore() {
@@ -1629,6 +1310,6 @@ function createReloadRequestHandler({ resetState, broadcast, generateEpoch = gen
1629
1310
  };
1630
1311
  }
1631
1312
  //#endregion
1632
- export { introspectDb as C, dummyKysely as D, createImportData as E, startPerformanceLogger as S, createExportData as T, formatSchemaVerificationIssues as _, createBroadcastChannels as a, createMigrator as b, isWorkerNotificationMessage as c, syncDbClientLockName as d, syncDbWorkerLockName as f, createSQLiteCrdtApplyFunction as g, createCrdtApplyFunction as h, createResetStateStore as i, isWorkerRequestMessage as l, createCrdtStorage as m, createIdbResetStore as n, isWorkerErrorResponseMessage as o, createCrdtSyncProducer as p, createReloadRequestHandler as r, isWorkerInitMessage as s, RESET_REQUEST_TTL_MS as t, isWorkerResponseMessage as u, verifySyncSchema as v, xxhash as w, SQLiteDbWrapper as x, createMigrations as y };
1313
+ export { xxhash as C, createMigrator as S, createImportData as T, createCrdtApplyFunction as _, acquireReplicaLock as a, verifySyncSchema as b, isWorkerErrorResponseMessage as c, isWorkerRequestMessage as d, isWorkerResponseMessage as f, createCrdtStorage as g, createCrdtSyncProducer as h, createResetStateStore as i, isWorkerInitMessage as l, syncDbWorkerLockName as m, createIdbResetStore as n, runWithNoActiveReplicas as o, syncDbClientLockName as p, createReloadRequestHandler as r, createBroadcastChannels as s, RESET_REQUEST_TTL_MS as t, isWorkerNotificationMessage as u, createSQLiteCrdtApplyFunction as v, createExportData as w, createMigrations as x, formatSchemaVerificationIssues as y };
1633
1314
 
1634
- //# sourceMappingURL=reset-state-BQi96NLn.js.map
1315
+ //# sourceMappingURL=reset-state-t0tTGqoh.js.map