@tanstack/db-sqlite-persistence-core 0.2.20 → 0.2.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/db-sqlite-persistence-core",
3
- "version": "0.2.20",
3
+ "version": "0.2.21",
4
4
  "description": "SQLite persisted collection core for TanStack DB",
5
5
  "author": "TanStack Team",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  ],
41
41
  "dependencies": {
42
42
  "@standard-schema/spec": "^1.1.0",
43
- "@tanstack/db": "0.8.7"
43
+ "@tanstack/db": "0.9.0"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "typescript": ">=4.7"
package/src/persisted.ts CHANGED
@@ -21,6 +21,7 @@ import type {
21
21
  CollectionIndexMetadata,
22
22
  DeleteMutationFnParams,
23
23
  InsertMutationFnParams,
24
+ LoadSubsetFn,
24
25
  LoadSubsetOptions,
25
26
  PendingMutation,
26
27
  SyncAppliedReceipt,
@@ -1015,7 +1016,7 @@ class PersistedCollectionRuntime<
1015
1016
 
1016
1017
  async loadSubset(
1017
1018
  options: LoadSubsetOptions,
1018
- upstreamLoadSubset?: (options: LoadSubsetOptions) => true | Promise<void>,
1019
+ upstreamLoadSubset?: LoadSubsetFn,
1019
1020
  ): Promise<void> {
1020
1021
  this.activeSubsets.set(this.getSubsetKey(options), options)
1021
1022
 
@@ -1029,17 +1030,18 @@ class PersistedCollectionRuntime<
1029
1030
 
1030
1031
  if (upstreamLoadSubset) {
1031
1032
  try {
1032
- const maybePromise = upstreamLoadSubset(options)
1033
- if (maybePromise instanceof Promise) {
1034
- await maybePromise.catch((error) => {
1035
- console.warn(
1036
- `Failed to load remote subset in persisted wrapper:`,
1037
- error,
1038
- )
1039
- this.queueRemoteSubsetEnsure(options)
1040
- })
1041
- }
1033
+ await upstreamLoadSubset(options)
1042
1034
  } catch (error) {
1035
+ if (
1036
+ options.signal?.aborted ||
1037
+ (typeof error === `object` &&
1038
+ error !== null &&
1039
+ `name` in error &&
1040
+ error.name === `AbortError`)
1041
+ ) {
1042
+ this.pendingRemoteSubsetEnsures.delete(this.getSubsetKey(options))
1043
+ throw error
1044
+ }
1043
1045
  console.warn(`Failed to trigger remote subset load:`, error)
1044
1046
  this.queueRemoteSubsetEnsure(options)
1045
1047
  }
@@ -1051,6 +1053,7 @@ class PersistedCollectionRuntime<
1051
1053
  upstreamUnloadSubset?: (options: LoadSubsetOptions) => void,
1052
1054
  ): void {
1053
1055
  this.activeSubsets.delete(this.getSubsetKey(options))
1056
+ this.pendingRemoteSubsetEnsures.delete(this.getSubsetKey(options))
1054
1057
  upstreamUnloadSubset?.(options)
1055
1058
  }
1056
1059
 
@@ -1824,7 +1827,8 @@ class PersistedCollectionRuntime<
1824
1827
  private queueRemoteSubsetEnsure(options: LoadSubsetOptions): void {
1825
1828
  if (
1826
1829
  this.mode !== `sync-present` ||
1827
- !this.persistence.coordinator.requestEnsureRemoteSubset
1830
+ !this.persistence.coordinator.requestEnsureRemoteSubset ||
1831
+ this.activeSubsets.get(this.getSubsetKey(options)) !== options
1828
1832
  ) {
1829
1833
  return
1830
1834
  }
@@ -2276,24 +2280,7 @@ function createWrappedSyncConfig<
2276
2280
  const getOpenTransaction = () =>
2277
2281
  transactionStack[transactionStack.length - 1]
2278
2282
  let fullStartPromise: Promise<void> | null = null
2279
- const cancelledLoadKeys = new Set<string>()
2280
- const loadSubscriptionIds = new WeakMap<object, string>()
2281
- let nextLoadSubscriptionId = 0
2282
- const getLoadKey = (options: LoadSubsetOptions) => {
2283
- const subscription = options.subscription as object | undefined
2284
- if (subscription && typeof subscription === `object`) {
2285
- const existingId = loadSubscriptionIds.get(subscription)
2286
- if (existingId) {
2287
- return `sub:${existingId}`
2288
- }
2289
- nextLoadSubscriptionId++
2290
- const nextId = String(nextLoadSubscriptionId)
2291
- loadSubscriptionIds.set(subscription, nextId)
2292
- return `sub:${nextId}`
2293
- }
2294
-
2295
- return `opts:${stableSerialize(normalizeSubsetOptionsForKey(options))}`
2296
- }
2283
+ const acquisitions = new Map<LoadSubsetOptions, { forwarded: boolean }>()
2297
2284
  runtime.setSyncControls({
2298
2285
  begin: params.begin,
2299
2286
  write: params.write as SyncControlFns<T, TKey>[`write`],
@@ -2590,23 +2577,48 @@ function createWrappedSyncConfig<
2590
2577
  return {
2591
2578
  cleanup: () => {
2592
2579
  startupState.cleanedUp = true
2580
+ acquisitions.clear()
2593
2581
  sourceResult.cleanup?.()
2594
2582
  runtime.cleanup()
2595
2583
  runtime.clearSyncControls()
2596
2584
  },
2597
2585
  loadSubset: async (options: LoadSubsetOptions) => {
2598
- const loadKey = getLoadKey(options)
2599
- cancelledLoadKeys.delete(loadKey)
2586
+ const acquisition = { forwarded: false }
2587
+ acquisitions.set(options, acquisition)
2600
2588
  await fullStartPromise
2601
2589
  const resolvedSourceResult = await sourceResultPromise
2602
- if (startupState.cleanedUp || cancelledLoadKeys.has(loadKey)) {
2590
+ if (
2591
+ startupState.cleanedUp ||
2592
+ acquisitions.get(options) !== acquisition
2593
+ ) {
2603
2594
  return
2604
2595
  }
2605
- await runtime.loadSubset(options, resolvedSourceResult.loadSubset)
2596
+ return runtime.loadSubset(options, (loadOptions) => {
2597
+ // Hydration is another async boundary. A release before this
2598
+ // point owns no upstream lease and must not start one later.
2599
+ if (
2600
+ startupState.cleanedUp ||
2601
+ acquisitions.get(options) !== acquisition
2602
+ ) {
2603
+ return true
2604
+ }
2605
+ if (!resolvedSourceResult.loadSubset) return true
2606
+ acquisition.forwarded = true
2607
+ try {
2608
+ return resolvedSourceResult.loadSubset(loadOptions)
2609
+ } catch (error) {
2610
+ acquisition.forwarded = false
2611
+ throw error
2612
+ }
2613
+ })
2606
2614
  },
2607
2615
  unloadSubset: (options: LoadSubsetOptions) => {
2608
- cancelledLoadKeys.add(getLoadKey(options))
2609
- runtime.unloadSubset(options, sourceResult.unloadSubset)
2616
+ const acquisition = acquisitions.get(options)
2617
+ acquisitions.delete(options)
2618
+ runtime.unloadSubset(
2619
+ options,
2620
+ acquisition?.forwarded ? sourceResult.unloadSubset : undefined,
2621
+ )
2610
2622
  },
2611
2623
  }
2612
2624
  },