@tanstack/electric-db-collection 0.4.4 → 0.4.5
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/electric.cjs +50 -8
- package/dist/cjs/electric.cjs.map +1 -1
- package/dist/esm/electric.js +50 -8
- package/dist/esm/electric.js.map +1 -1
- package/package.json +2 -2
- package/src/electric.ts +58 -9
package/src/electric.ts
CHANGED
|
@@ -47,6 +47,7 @@ import type {
|
|
|
47
47
|
DeleteMutationFnParams,
|
|
48
48
|
InsertMutationFnParams,
|
|
49
49
|
LoadSubsetOptions,
|
|
50
|
+
SyncAppliedReceipt,
|
|
50
51
|
SyncConfig,
|
|
51
52
|
SyncMode,
|
|
52
53
|
UpdateMutationFnParams,
|
|
@@ -526,6 +527,8 @@ function createLoadSubsetDedupe<T extends Row<unknown>>({
|
|
|
526
527
|
begin,
|
|
527
528
|
write,
|
|
528
529
|
commit,
|
|
530
|
+
getCommitCursor,
|
|
531
|
+
waitForCommitsAfter,
|
|
529
532
|
collectionId,
|
|
530
533
|
encodeColumnName,
|
|
531
534
|
signal,
|
|
@@ -539,7 +542,9 @@ function createLoadSubsetDedupe<T extends Row<unknown>>({
|
|
|
539
542
|
value: T
|
|
540
543
|
metadata: Record<string, unknown>
|
|
541
544
|
}) => void
|
|
542
|
-
commit: () =>
|
|
545
|
+
commit: (signal?: AbortSignal) => SyncAppliedReceipt
|
|
546
|
+
getCommitCursor: () => number
|
|
547
|
+
waitForCommitsAfter: (cursor: number) => Promise<void>
|
|
543
548
|
collectionId?: string
|
|
544
549
|
/**
|
|
545
550
|
* Optional function to encode column names (e.g., camelCase to snake_case).
|
|
@@ -573,6 +578,7 @@ function createLoadSubsetDedupe<T extends Row<unknown>>({
|
|
|
573
578
|
}
|
|
574
579
|
|
|
575
580
|
const loadSubset = async (opts: LoadSubsetOptions) => {
|
|
581
|
+
const commitCursor = getCommitCursor()
|
|
576
582
|
if (opts.signal?.aborted) return
|
|
577
583
|
|
|
578
584
|
if (isBufferingInitialSync()) {
|
|
@@ -593,7 +599,7 @@ function createLoadSubsetDedupe<T extends Row<unknown>>({
|
|
|
593
599
|
metadata: { ...row.headers },
|
|
594
600
|
})
|
|
595
601
|
}
|
|
596
|
-
commit()
|
|
602
|
+
await commit(opts.signal)
|
|
597
603
|
debug(`${logPrefix}Applied snapshot with ${rows.length} rows`)
|
|
598
604
|
}
|
|
599
605
|
} catch (error) {
|
|
@@ -691,6 +697,7 @@ function createLoadSubsetDedupe<T extends Row<unknown>>({
|
|
|
691
697
|
}
|
|
692
698
|
throw error
|
|
693
699
|
}
|
|
700
|
+
await waitForCommitsAfter(commitCursor)
|
|
694
701
|
}
|
|
695
702
|
|
|
696
703
|
return new DeduplicatedLoadSubset({ loadSubset })
|
|
@@ -1487,13 +1494,33 @@ function createElectricSync<T extends Row<unknown>>(
|
|
|
1487
1494
|
const {
|
|
1488
1495
|
begin,
|
|
1489
1496
|
write,
|
|
1490
|
-
commit,
|
|
1497
|
+
commit: commitSyncTransaction,
|
|
1491
1498
|
markReady,
|
|
1492
1499
|
markError,
|
|
1493
1500
|
truncate,
|
|
1494
1501
|
collection,
|
|
1495
1502
|
metadata,
|
|
1496
1503
|
} = params
|
|
1504
|
+
let commitSequence = 0
|
|
1505
|
+
const pendingAppliedReceipts = new Map<number, Promise<void>>()
|
|
1506
|
+
const commit = (signal?: AbortSignal): SyncAppliedReceipt => {
|
|
1507
|
+
const sequence = ++commitSequence
|
|
1508
|
+
const applied = commitSyncTransaction(signal)
|
|
1509
|
+
if (applied === true) {
|
|
1510
|
+
return true
|
|
1511
|
+
}
|
|
1512
|
+
pendingAppliedReceipts.set(sequence, applied)
|
|
1513
|
+
const removeReceipt = () => pendingAppliedReceipts.delete(sequence)
|
|
1514
|
+
void applied.then(removeReceipt, removeReceipt)
|
|
1515
|
+
return applied
|
|
1516
|
+
}
|
|
1517
|
+
const waitForCommitsAfter = async (cursor: number): Promise<void> => {
|
|
1518
|
+
await Promise.all(
|
|
1519
|
+
Array.from(pendingAppliedReceipts, ([sequence, applied]) =>
|
|
1520
|
+
sequence > cursor ? applied : undefined,
|
|
1521
|
+
),
|
|
1522
|
+
)
|
|
1523
|
+
}
|
|
1497
1524
|
const readPersistedResumeState = (): ElectricResumeState | undefined => {
|
|
1498
1525
|
const persistedResumeState = metadata?.collection.get(`electric:resume`)
|
|
1499
1526
|
return parseElectricResumeState(persistedResumeState)
|
|
@@ -1518,7 +1545,13 @@ function createElectricSync<T extends Row<unknown>>(
|
|
|
1518
1545
|
|
|
1519
1546
|
// Wrap markReady to wait for test hook in progressive mode
|
|
1520
1547
|
let progressiveReadyGate: Promise<void> | null = null
|
|
1521
|
-
|
|
1548
|
+
let streamErrorVersion = 0
|
|
1549
|
+
const wrappedMarkReady = (
|
|
1550
|
+
isBuffering: boolean,
|
|
1551
|
+
expectedErrorVersion = streamErrorVersion,
|
|
1552
|
+
) => {
|
|
1553
|
+
if (streamErrorVersion !== expectedErrorVersion) return
|
|
1554
|
+
|
|
1522
1555
|
// Only create gate if we're in buffering phase (first up-to-date)
|
|
1523
1556
|
if (
|
|
1524
1557
|
isBuffering &&
|
|
@@ -1528,7 +1561,9 @@ function createElectricSync<T extends Row<unknown>>(
|
|
|
1528
1561
|
// Create a new gate promise for this sync cycle
|
|
1529
1562
|
progressiveReadyGate = testHooks.beforeMarkingReady()
|
|
1530
1563
|
progressiveReadyGate.then(() => {
|
|
1531
|
-
|
|
1564
|
+
if (streamErrorVersion === expectedErrorVersion) {
|
|
1565
|
+
markReady()
|
|
1566
|
+
}
|
|
1532
1567
|
})
|
|
1533
1568
|
} else {
|
|
1534
1569
|
// No hook, not buffering, or already past first up-to-date
|
|
@@ -1583,6 +1618,7 @@ function createElectricSync<T extends Row<unknown>>(
|
|
|
1583
1618
|
(canUsePersistedResume ? persistedResumeState.handle : undefined),
|
|
1584
1619
|
signal: abortController.signal,
|
|
1585
1620
|
onError: (errorParams) => {
|
|
1621
|
+
streamErrorVersion++
|
|
1586
1622
|
// Note that Electric sends a 409 error on a `must-refetch` message, but the
|
|
1587
1623
|
// ShapeStream handled this and it will not reach this handler, therefor
|
|
1588
1624
|
// this handler will not run for a `must-refetch`.
|
|
@@ -1730,6 +1766,8 @@ function createElectricSync<T extends Row<unknown>>(
|
|
|
1730
1766
|
begin,
|
|
1731
1767
|
write,
|
|
1732
1768
|
commit,
|
|
1769
|
+
getCommitCursor: () => commitSequence,
|
|
1770
|
+
waitForCommitsAfter,
|
|
1733
1771
|
collectionId,
|
|
1734
1772
|
// Pass the columnMapper's encode function to transform column names
|
|
1735
1773
|
// (e.g., camelCase to snake_case) when compiling SQL for subset queries
|
|
@@ -1892,6 +1930,8 @@ function createElectricSync<T extends Row<unknown>>(
|
|
|
1892
1930
|
}
|
|
1893
1931
|
|
|
1894
1932
|
if (commitPoint !== null) {
|
|
1933
|
+
let applied: SyncAppliedReceipt = true
|
|
1934
|
+
const wasBufferingInitialSync = isBufferingInitialSync()
|
|
1895
1935
|
// PROGRESSIVE MODE: Atomic swap on first up-to-date (not subset-end)
|
|
1896
1936
|
// EXCEPTION: Skip atomic swap if a transaction is already started (e.g., from must-refetch).
|
|
1897
1937
|
// In that case, do a normal commit to properly close the existing transaction.
|
|
@@ -1946,7 +1986,7 @@ function createElectricSync<T extends Row<unknown>>(
|
|
|
1946
1986
|
|
|
1947
1987
|
// Commit the atomic swap
|
|
1948
1988
|
stageResumeMetadata()
|
|
1949
|
-
commit()
|
|
1989
|
+
applied = commit()
|
|
1950
1990
|
|
|
1951
1991
|
// Exit buffering phase by marking that we've received up-to-date
|
|
1952
1992
|
// isBufferingInitialSync() will now return false
|
|
@@ -1960,15 +2000,24 @@ function createElectricSync<T extends Row<unknown>>(
|
|
|
1960
2000
|
// Both up-to-date and subset-end trigger a commit
|
|
1961
2001
|
if (transactionStarted) {
|
|
1962
2002
|
stageResumeMetadata()
|
|
1963
|
-
commit()
|
|
2003
|
+
applied = commit()
|
|
1964
2004
|
transactionStarted = false
|
|
1965
2005
|
} else if (commitPoint === `up-to-date` && metadata) {
|
|
1966
2006
|
begin()
|
|
1967
2007
|
stageResumeMetadata()
|
|
1968
|
-
commit()
|
|
2008
|
+
applied = commit()
|
|
1969
2009
|
}
|
|
1970
2010
|
}
|
|
1971
|
-
|
|
2011
|
+
const readyErrorVersion = streamErrorVersion
|
|
2012
|
+
if (applied === true) {
|
|
2013
|
+
wrappedMarkReady(wasBufferingInitialSync, readyErrorVersion)
|
|
2014
|
+
} else {
|
|
2015
|
+
void applied.then(
|
|
2016
|
+
() =>
|
|
2017
|
+
wrappedMarkReady(wasBufferingInitialSync, readyErrorVersion),
|
|
2018
|
+
() => undefined,
|
|
2019
|
+
)
|
|
2020
|
+
}
|
|
1972
2021
|
|
|
1973
2022
|
// Track that we've received the first up-to-date for progressive mode
|
|
1974
2023
|
if (commitPoint === `up-to-date`) {
|