@tanstack/db-sqlite-persistence-core 0.2.16 → 0.2.18

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,4 +1,4 @@
1
- import { safeRandomUUID, withCollectionConfigFactory, compileSingleRowExpression, toBooleanPredicate } from "@tanstack/db";
1
+ import { safeRandomUUID, withCollectionConfigFactory, SyncTransactionAbortedError, compileSingleRowExpression, toBooleanPredicate } from "@tanstack/db";
2
2
  import { InvalidPersistedCollectionCoordinatorError, InvalidPersistedCollectionConfigError, InvalidSyncConfigError, InvalidPersistedStorageKeyError, InvalidPersistedStorageKeyEncodingError, InvalidPersistenceAdapterError } from "./errors.js";
3
3
  const REQUIRED_COORDINATOR_METHODS = [
4
4
  `getNodeId`,
@@ -231,6 +231,8 @@ class PersistedCollectionRuntime {
231
231
  this.startupMetadataPromise = null;
232
232
  this.startPromise = null;
233
233
  this.internalApplyDepth = 0;
234
+ this.appliedReceiptSequence = 0;
235
+ this.pendingAppliedReceipts = /* @__PURE__ */ new Map();
234
236
  this.isHydrating = false;
235
237
  this.coordinatorUnsubscribe = null;
236
238
  this.indexAddedUnsubscribe = null;
@@ -245,7 +247,29 @@ class PersistedCollectionRuntime {
245
247
  this.localRowVersion = 0;
246
248
  }
247
249
  setSyncControls(syncControls) {
248
- this.syncControls = syncControls;
250
+ const commit = syncControls.commit;
251
+ this.syncControls = {
252
+ ...syncControls,
253
+ commit: commit ? (signal) => this.trackAppliedReceipt(commit(signal)) : null
254
+ };
255
+ }
256
+ trackAppliedReceipt(receipt) {
257
+ const sequence = ++this.appliedReceiptSequence;
258
+ if (receipt === true) {
259
+ return true;
260
+ }
261
+ this.pendingAppliedReceipts.set(sequence, receipt);
262
+ const removeReceipt = () => this.pendingAppliedReceipts.delete(sequence);
263
+ void receipt.then(removeReceipt, removeReceipt);
264
+ return receipt;
265
+ }
266
+ async waitForAppliedReceiptsAfter(cursor) {
267
+ await Promise.all(
268
+ Array.from(
269
+ this.pendingAppliedReceipts,
270
+ ([sequence, receipt]) => sequence > cursor ? receipt : void 0
271
+ )
272
+ );
249
273
  }
250
274
  clearSyncControls() {
251
275
  this.syncControls = {
@@ -300,9 +324,11 @@ class PersistedCollectionRuntime {
300
324
  await this.bootstrapPersistedIndexes(indexBootstrapSnapshot);
301
325
  if (this.syncMode !== `on-demand`) {
302
326
  this.activeSubsets.set(this.getSubsetKey({}), {});
327
+ const appliedCursor = this.appliedReceiptSequence;
303
328
  await this.applyMutex.run(
304
329
  () => this.hydrateSubsetUnsafe({}, { requestRemoteEnsure: false })
305
330
  );
331
+ await this.waitForAppliedReceiptsAfter(appliedCursor);
306
332
  }
307
333
  }
308
334
  async loadStartupMetadataInternal() {
@@ -351,16 +377,18 @@ class PersistedCollectionRuntime {
351
377
  }
352
378
  async loadSubset(options, upstreamLoadSubset) {
353
379
  this.activeSubsets.set(this.getSubsetKey(options), options);
380
+ const appliedCursor = this.appliedReceiptSequence;
354
381
  await this.applyMutex.run(
355
382
  () => this.hydrateSubsetUnsafe(options, {
356
383
  requestRemoteEnsure: this.mode === `sync-present`
357
384
  })
358
385
  );
386
+ await this.waitForAppliedReceiptsAfter(appliedCursor);
359
387
  if (upstreamLoadSubset) {
360
388
  try {
361
389
  const maybePromise = upstreamLoadSubset(options);
362
390
  if (maybePromise instanceof Promise) {
363
- maybePromise.catch((error) => {
391
+ await maybePromise.catch((error) => {
364
392
  console.warn(
365
393
  `Failed to load remote subset in persisted wrapper:`,
366
394
  error
@@ -483,6 +511,9 @@ class PersistedCollectionRuntime {
483
511
  }
484
512
  this.pendingRemoteSubsetEnsures.clear();
485
513
  this.activeSubsets.clear();
514
+ for (const transaction of this.queuedHydrationTransactions) {
515
+ transaction.rejectApplied?.(new SyncTransactionAbortedError());
516
+ }
486
517
  this.queuedHydrationTransactions.length = 0;
487
518
  this.queuedTxCommitted.length = 0;
488
519
  this.clearSyncControls();
@@ -490,7 +521,7 @@ class PersistedCollectionRuntime {
490
521
  withInternalApply(task) {
491
522
  this.internalApplyDepth++;
492
523
  try {
493
- task();
524
+ return task();
494
525
  } finally {
495
526
  this.internalApplyDepth--;
496
527
  }
@@ -586,26 +617,41 @@ class PersistedCollectionRuntime {
586
617
  if (!transaction) {
587
618
  continue;
588
619
  }
589
- await this.applyBufferedSyncTransactionUnsafe(transaction);
620
+ try {
621
+ await this.applyBufferedSyncTransactionUnsafe(transaction);
622
+ } catch (error) {
623
+ transaction.rejectApplied?.(error);
624
+ for (const abandoned of this.queuedHydrationTransactions) {
625
+ abandoned.rejectApplied?.(error);
626
+ }
627
+ this.queuedHydrationTransactions.length = 0;
628
+ throw error;
629
+ }
590
630
  }
591
631
  }
592
632
  async applyBufferedSyncTransactionUnsafe(transaction) {
593
- if (!this.syncControls.begin || !this.syncControls.write || !this.syncControls.commit) {
633
+ if (transaction.signal?.aborted) {
634
+ transaction.rejectApplied?.(new SyncTransactionAbortedError());
635
+ return;
636
+ }
637
+ const { begin, write, commit, truncate, metadata } = this.syncControls;
638
+ if (!begin || !write || !commit) {
639
+ transaction.rejectApplied?.(new SyncTransactionAbortedError());
594
640
  return;
595
641
  }
596
642
  const applyToCollection = () => {
597
- this.syncControls.begin?.();
643
+ begin();
598
644
  if (transaction.truncate) {
599
- this.syncControls.truncate?.();
645
+ truncate?.();
600
646
  }
601
647
  for (const operation of transaction.operations) {
602
648
  if (operation.type === `delete`) {
603
- this.syncControls.write?.({
649
+ write({
604
650
  type: `delete`,
605
651
  key: operation.key
606
652
  });
607
653
  } else {
608
- this.syncControls.write?.({
654
+ write({
609
655
  type: `update`,
610
656
  value: operation.value,
611
657
  metadata: operation.metadata
@@ -614,26 +660,33 @@ class PersistedCollectionRuntime {
614
660
  }
615
661
  for (const [key, metadataWrite] of transaction.rowMetadataWrites) {
616
662
  if (metadataWrite.type === `delete`) {
617
- this.syncControls.metadata?.row.delete(key);
663
+ metadata?.row.delete(key);
618
664
  } else {
619
- this.syncControls.metadata?.row.set(key, metadataWrite.value);
665
+ metadata?.row.set(key, metadataWrite.value);
620
666
  }
621
667
  }
622
668
  for (const [key, metadataWrite] of transaction.collectionMetadataWrites) {
623
669
  if (metadataWrite.type === `delete`) {
624
- this.syncControls.metadata?.collection.delete(key);
670
+ metadata?.collection.delete(key);
625
671
  } else {
626
- this.syncControls.metadata?.collection.set(key, metadataWrite.value);
672
+ metadata?.collection.set(key, metadataWrite.value);
627
673
  }
628
674
  }
629
- this.syncControls.commit?.();
675
+ return commit(transaction.signal);
630
676
  };
631
- if (transaction.internal) {
632
- this.withInternalApply(applyToCollection);
633
- return;
677
+ try {
678
+ const applied = transaction.internal ? this.withInternalApply(applyToCollection) : applyToCollection();
679
+ if (applied !== true) {
680
+ await applied;
681
+ }
682
+ if (!transaction.internal) {
683
+ await this.persistAndBroadcastExternalSyncTransactionUnsafe(transaction);
684
+ }
685
+ transaction.resolveApplied?.();
686
+ } catch (error) {
687
+ transaction.rejectApplied?.(error);
688
+ throw error;
634
689
  }
635
- applyToCollection();
636
- await this.persistAndBroadcastExternalSyncTransactionUnsafe(transaction);
637
690
  }
638
691
  async persistAndBroadcastExternalSyncTransactionUnsafe(transaction) {
639
692
  if (transaction.internal) {
@@ -1450,37 +1503,53 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime) {
1450
1503
  params.truncate();
1451
1504
  }
1452
1505
  },
1453
- commit: () => {
1506
+ commit: (signal) => {
1454
1507
  const openTransaction = transactionStack.pop();
1455
1508
  if (!openTransaction) {
1456
- params.commit();
1457
- return;
1509
+ return params.commit(signal);
1458
1510
  }
1459
1511
  if (openTransaction.queuedBecauseHydrating) {
1512
+ if (signal?.aborted) {
1513
+ const aborted = Promise.reject(new SyncTransactionAbortedError());
1514
+ void aborted.catch(() => void 0);
1515
+ return aborted;
1516
+ }
1517
+ let resolveApplied;
1518
+ let rejectApplied;
1519
+ const applied2 = new Promise((resolve, reject) => {
1520
+ resolveApplied = resolve;
1521
+ rejectApplied = reject;
1522
+ });
1523
+ void applied2.catch(() => void 0);
1460
1524
  runtime.queueHydrationBufferedTransaction({
1461
1525
  operations: openTransaction.operations,
1462
1526
  rowMetadataWrites: openTransaction.rowMetadataWrites,
1463
1527
  collectionMetadataWrites: openTransaction.collectionMetadataWrites,
1464
1528
  truncate: openTransaction.truncate,
1465
- internal: openTransaction.internal
1529
+ internal: openTransaction.internal,
1530
+ signal,
1531
+ resolveApplied,
1532
+ rejectApplied
1466
1533
  });
1467
- return;
1534
+ return applied2;
1468
1535
  }
1469
- params.commit();
1536
+ const applied = params.commit(signal);
1470
1537
  if (!openTransaction.internal) {
1471
- void runtime.persistAndBroadcastExternalSyncTransaction({
1472
- operations: openTransaction.operations,
1473
- rowMetadataWrites: openTransaction.rowMetadataWrites,
1474
- collectionMetadataWrites: openTransaction.collectionMetadataWrites,
1475
- truncate: openTransaction.truncate,
1476
- internal: false
1477
- }).catch((error) => {
1478
- console.warn(
1479
- `Failed to persist wrapped sync transaction:`,
1480
- error
1481
- );
1482
- });
1538
+ const persistAfterApplication = async () => {
1539
+ if (applied !== true) await applied;
1540
+ await runtime.persistAndBroadcastExternalSyncTransaction({
1541
+ operations: openTransaction.operations,
1542
+ rowMetadataWrites: openTransaction.rowMetadataWrites,
1543
+ collectionMetadataWrites: openTransaction.collectionMetadataWrites,
1544
+ truncate: openTransaction.truncate,
1545
+ internal: false
1546
+ });
1547
+ };
1548
+ const persisted = persistAfterApplication();
1549
+ void persisted.catch(() => void 0);
1550
+ return persisted;
1483
1551
  }
1552
+ return applied;
1484
1553
  }
1485
1554
  };
1486
1555
  let sourceResult = {};