@tanstack/powersync-db-collection 0.2.13 → 1.0.0-rc.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.
- package/dist/cjs/PowerSyncTransactor.cjs +2 -2
- package/dist/cjs/PowerSyncTransactor.cjs.map +1 -1
- package/dist/cjs/powersync.cjs +44 -16
- package/dist/cjs/powersync.cjs.map +1 -1
- package/dist/esm/PowerSyncTransactor.js +3 -3
- package/dist/esm/PowerSyncTransactor.js.map +1 -1
- package/dist/esm/powersync.js +44 -16
- package/dist/esm/powersync.js.map +1 -1
- package/package.json +4 -5
- package/src/PowerSyncTransactor.ts +3 -3
- package/src/powersync.ts +64 -25
package/src/powersync.ts
CHANGED
|
@@ -325,7 +325,22 @@ function createPowerSyncCollectionConfig<
|
|
|
325
325
|
*/
|
|
326
326
|
const sync: SyncConfig<OutputType, string> = {
|
|
327
327
|
sync: (params) => {
|
|
328
|
-
const {
|
|
328
|
+
const {
|
|
329
|
+
begin,
|
|
330
|
+
write,
|
|
331
|
+
collection,
|
|
332
|
+
commit,
|
|
333
|
+
fail,
|
|
334
|
+
reportError,
|
|
335
|
+
reportHealthy,
|
|
336
|
+
} = params
|
|
337
|
+
// Readiness of this sync run: a reload runs `sync` again while the
|
|
338
|
+
// Collection still shows the previous run's status.
|
|
339
|
+
let ready = false
|
|
340
|
+
const markReady = () => {
|
|
341
|
+
ready = true
|
|
342
|
+
params.markReady()
|
|
343
|
+
}
|
|
329
344
|
const abortController = new AbortController()
|
|
330
345
|
|
|
331
346
|
let disposeTracking:
|
|
@@ -409,6 +424,9 @@ function createPowerSyncCollectionConfig<
|
|
|
409
424
|
async function createDiffTrigger(
|
|
410
425
|
options: {
|
|
411
426
|
setupContext?: LockContext
|
|
427
|
+
// The load reads every row and replaces the rows the Collection
|
|
428
|
+
// holds.
|
|
429
|
+
snapshot?: boolean
|
|
412
430
|
when: Record<DiffTriggerOperation, string>
|
|
413
431
|
writeType: (rowId: string) => OperationType
|
|
414
432
|
batchQuery: (
|
|
@@ -419,7 +437,7 @@ function createPowerSyncCollectionConfig<
|
|
|
419
437
|
},
|
|
420
438
|
appliedReceipts: Array<SyncAppliedReceipt>,
|
|
421
439
|
) {
|
|
422
|
-
const { setupContext, when, writeType, batchQuery } = options
|
|
440
|
+
const { setupContext, snapshot, when, writeType, batchQuery } = options
|
|
423
441
|
|
|
424
442
|
return await database.triggers.createDiffTrigger({
|
|
425
443
|
source: viewName,
|
|
@@ -429,11 +447,25 @@ function createPowerSyncCollectionConfig<
|
|
|
429
447
|
when,
|
|
430
448
|
hooks: {
|
|
431
449
|
beforeCreate: async (context) => {
|
|
450
|
+
// A load of every row is one snapshot transaction: during a
|
|
451
|
+
// reload it replaces the kept rows, so a row the table no
|
|
452
|
+
// longer has goes. A subset load commits each batch. A
|
|
453
|
+
// transaction begins only once its rows are read, so a batch
|
|
454
|
+
// that fails leaves none open.
|
|
455
|
+
const batches: Array<Array<TableType>> = []
|
|
456
|
+
const apply = () => {
|
|
457
|
+
begin({ snapshot })
|
|
458
|
+
for (const row of batches.splice(0).flat()) {
|
|
459
|
+
write({
|
|
460
|
+
type: writeType(row.id),
|
|
461
|
+
value: deserializeSyncRow(row),
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
appliedReceipts.push(commit())
|
|
465
|
+
}
|
|
432
466
|
let currentBatchCount = syncBatchSize
|
|
433
467
|
let cursor = 0
|
|
434
468
|
while (currentBatchCount == syncBatchSize) {
|
|
435
|
-
begin()
|
|
436
|
-
|
|
437
469
|
const batchItems = await batchQuery(
|
|
438
470
|
context,
|
|
439
471
|
syncBatchSize,
|
|
@@ -441,14 +473,10 @@ function createPowerSyncCollectionConfig<
|
|
|
441
473
|
)
|
|
442
474
|
currentBatchCount = batchItems.length
|
|
443
475
|
cursor += currentBatchCount
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
type: writeType(row.id),
|
|
447
|
-
value: deserializeSyncRow(row),
|
|
448
|
-
})
|
|
449
|
-
}
|
|
450
|
-
appliedReceipts.push(commit())
|
|
476
|
+
batches.push(batchItems)
|
|
477
|
+
if (!snapshot) apply()
|
|
451
478
|
}
|
|
479
|
+
if (snapshot) apply()
|
|
452
480
|
database.logger.log({
|
|
453
481
|
level: LogLevels.info,
|
|
454
482
|
message: `Sync is ready for ${viewName} into ${trackedTableName}`,
|
|
@@ -597,6 +625,9 @@ function createPowerSyncCollectionConfig<
|
|
|
597
625
|
const appliedReceipts: Array<SyncAppliedReceipt> = []
|
|
598
626
|
await establishTracking(
|
|
599
627
|
{
|
|
628
|
+
// When the Collection holds rows, such as a reload's kept rows,
|
|
629
|
+
// the load of every row replaces them.
|
|
630
|
+
snapshot: !collection._state.acceptedSyncedEntries().next().done,
|
|
600
631
|
when: {
|
|
601
632
|
[DiffTriggerOperation.INSERT]: `TRUE`,
|
|
602
633
|
[DiffTriggerOperation.UPDATE]: `TRUE`,
|
|
@@ -625,9 +656,7 @@ function createPowerSyncCollectionConfig<
|
|
|
625
656
|
message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,
|
|
626
657
|
error,
|
|
627
658
|
})
|
|
628
|
-
if (
|
|
629
|
-
markError(error)
|
|
630
|
-
}
|
|
659
|
+
if (!ready) fail(error)
|
|
631
660
|
})
|
|
632
661
|
|
|
633
662
|
const invokeOnUnload = (): void | Promise<void> => {
|
|
@@ -733,6 +762,8 @@ function createPowerSyncCollectionConfig<
|
|
|
733
762
|
|
|
734
763
|
// One reconciliation owns every queued revision so callers cannot
|
|
735
764
|
// settle against a stale trigger configuration.
|
|
765
|
+
// Set while a failed rebuild may have left the source untracked.
|
|
766
|
+
let trackingOutage = false
|
|
736
767
|
const reconcileTracking = async (): Promise<void> => {
|
|
737
768
|
while (!stopped && reconciledTrackingRevision !== trackingRevision) {
|
|
738
769
|
const revision = trackingRevision
|
|
@@ -747,12 +778,11 @@ function createPowerSyncCollectionConfig<
|
|
|
747
778
|
if (!isCurrent()) return
|
|
748
779
|
|
|
749
780
|
const active = activeWhereExpressions()
|
|
750
|
-
// Tracking was absent during an
|
|
781
|
+
// Tracking was absent during an outage. Positive baseline rows
|
|
751
782
|
// alone cannot reveal deletes or predicate exits from that gap.
|
|
752
|
-
const missing =
|
|
753
|
-
collection.
|
|
754
|
-
|
|
755
|
-
: undefined
|
|
783
|
+
const missing = trackingOutage
|
|
784
|
+
? new Set(collection.keys())
|
|
785
|
+
: undefined
|
|
756
786
|
if (active.length > 0) {
|
|
757
787
|
const combinedWhere =
|
|
758
788
|
active.length === 1
|
|
@@ -808,9 +838,12 @@ function createPowerSyncCollectionConfig<
|
|
|
808
838
|
await Promise.all(appliedReceipts)
|
|
809
839
|
if (isCurrent()) {
|
|
810
840
|
reconciledTrackingRevision = revision
|
|
811
|
-
// Replacing the trigger alone
|
|
812
|
-
// writes must also
|
|
813
|
-
if (
|
|
841
|
+
// Replacing the trigger alone does not restore change tracking:
|
|
842
|
+
// its baseline writes must also apply before sync works again.
|
|
843
|
+
if (trackingOutage) {
|
|
844
|
+
trackingOutage = false
|
|
845
|
+
reportHealthy()
|
|
846
|
+
}
|
|
814
847
|
}
|
|
815
848
|
}
|
|
816
849
|
}
|
|
@@ -821,9 +854,15 @@ function createPowerSyncCollectionConfig<
|
|
|
821
854
|
while (!stopped && reconciledTrackingRevision !== trackingRevision) {
|
|
822
855
|
await (rebuildPromise ??= reconcileTracking()
|
|
823
856
|
.catch((error) => {
|
|
824
|
-
// A rebuild may already have removed every active diff trigger
|
|
825
|
-
//
|
|
826
|
-
|
|
857
|
+
// A rebuild may already have removed every active diff trigger,
|
|
858
|
+
// so rows can grow stale. Later demand rebuilds again, so after
|
|
859
|
+
// Collection readiness this is a failureReason, not fail().
|
|
860
|
+
if (stopped) {
|
|
861
|
+
// Cleanup owns the outcome.
|
|
862
|
+
} else if (ready) {
|
|
863
|
+
trackingOutage = true
|
|
864
|
+
reportError(error)
|
|
865
|
+
} else fail(error)
|
|
827
866
|
throw error
|
|
828
867
|
})
|
|
829
868
|
.finally(() => {
|