cry-synced-db-client 0.1.203 → 0.1.204
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/CHANGELOG.md +35 -2
- package/dist/index.js +131 -121
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 0.1.204 (2026-06-17)
|
|
4
|
+
|
|
5
|
+
### Per-collection error isolation in `uploadDirtyItems`
|
|
6
|
+
|
|
7
|
+
Celoten per-collection cikel (`getDirty`, `getByIds`, `saveMany`,
|
|
8
|
+
`incrementDirtyUploadAttempts`, ...) je zdaj zavit v `try/catch`. Če Dexie
|
|
9
|
+
operacija za eno kolekcijo vrže (npr. corrupted transaction, IndexedDB error),
|
|
10
|
+
odpade samo tista kolekcija — ostale se normalno pošljejo naprej.
|
|
11
|
+
|
|
12
|
+
**Before:** ena kolekcija pade → celoten upload crasne, vse ostale collection
|
|
13
|
+
dirty entry-ji ostanejo neposlani do naslednjega sync cycle-a.
|
|
14
|
+
**After:** napaka se logira (`console.error`), zanka gre na naslednjo kolekcijo.
|
|
15
|
+
|
|
16
|
+
876 pass, 0 fail.
|
|
17
|
+
|
|
3
18
|
## 0.1.203 (2026-06-15)
|
|
4
19
|
|
|
5
20
|
### `onDirtyItemStuck` callback zdaj prejme tudi `itemsContent: DirtyChange[]`
|
|
@@ -8,6 +23,7 @@
|
|
|
8
23
|
entry-ji (vključno s `changes` payload-om), ne samo `DirtyMeta[]` z metapodatki.
|
|
9
24
|
|
|
10
25
|
**Affected paths:**
|
|
26
|
+
|
|
11
27
|
- `callOnDirtyItemStuck` v `SyncEngine.ts`: async, fetcha polne entry-je prek
|
|
12
28
|
`dexieDb.getDirtyChangesBatch()` in jih posreduje kot `itemsContent`
|
|
13
29
|
|
|
@@ -30,11 +46,13 @@ callback dobi poleg metapodatkov (`id`, `collection`, `stuckSince`, ...) tudi
|
|
|
30
46
|
`changes` payload z dejanskimi field-level spremembami.
|
|
31
47
|
|
|
32
48
|
**Affected paths:**
|
|
49
|
+
|
|
33
50
|
- `clearDirty()` clear-all path: fetcha `DirtyChange[]` prek `getDirtyChangesBatch`
|
|
34
51
|
- `discardStuckItems()`: zamenja `getDirty` + `find` (ki je vračal
|
|
35
52
|
`Partial<LocalDbEntity>[]`) z `getDirtyChangesBatch` za pravilne `DirtyChange[]`
|
|
36
53
|
|
|
37
54
|
**Type change (backward-compatible):**
|
|
55
|
+
|
|
38
56
|
- `items` v `BeforeDirtyClearAllInfo`: `DirtyMeta[]` → `DirtyChange[]`
|
|
39
57
|
- `DirtyChange` je superset of `DirtyMeta`; vsa obstoječa polja (`id`,
|
|
40
58
|
`collection`, `createdAt`, `updatedAt`, `stuckSince`, ...) so še vedno na voljo
|
|
@@ -54,11 +72,13 @@ bi postali stuck — zdaj se po 3 skipih (vsak sync da 2 incrementa = main + fol
|
|
|
54
72
|
dobijo `stuckSince`.
|
|
55
73
|
|
|
56
74
|
**Internal:**
|
|
75
|
+
|
|
57
76
|
- V `uploadDirtyItems` zbira `preprocessSkippedIds` v obeh poteh, batch-write v
|
|
58
77
|
`incrementDirtyUploadAttempts` takoj za `for` zanko (en DB write za vse skipane
|
|
59
78
|
iteme namesto N write-ov)
|
|
60
79
|
|
|
61
80
|
**Tests:** 5 novih testov v `test/preprocessDirtyItem.test.ts`:
|
|
81
|
+
|
|
62
82
|
- `undefined/throw: numUploadAttempts increments across sync cycles, eventually stuck`
|
|
63
83
|
- `undefined/throw: onDirtyItemStuck fires when item becomes stuck`
|
|
64
84
|
- `mixed: stuck item via getStuckItems while non-stuck dirty still active`
|
|
@@ -72,17 +92,21 @@ dobijo `stuckSince`.
|
|
|
72
92
|
Nov mehanizem za detekcijo dirty itemov, ki jih server vztrajno zavrača.
|
|
73
93
|
|
|
74
94
|
**Fields na `DirtyChange` / `DirtyMeta`:**
|
|
95
|
+
|
|
75
96
|
- `firstUploadAttempt?`, `lastUploadAttempt?`, `numUploadAttempts?`, `stuckSince?`
|
|
76
97
|
- `stuckSince` se nastavi ko `numUploadAttempts > DIRTY_STUCK_AFTER_UPLOAD_ATTEMPTS` (2)
|
|
77
98
|
|
|
78
99
|
**Nove metode na `SyncedDb`:**
|
|
100
|
+
|
|
79
101
|
- `getStuckItems()` — vrne samo stuck dirty iteme po kolekcijah (`Record<collection, DirtyMeta[]>`)
|
|
80
102
|
- `discardStuckItems(calledFrom?)` — zbriše vse stuck dirty iteme, sproži `onBeforeDirtyClearAll` z `reason: "discard-stuck"`
|
|
81
103
|
|
|
82
104
|
**Nov callback:**
|
|
105
|
+
|
|
83
106
|
- `onDirtyItemStuck(info: DirtyItemStuckInfo)` — sproži se ko item prvič postane stuck (po 3. neuspelem uploadu). Vsebuje `collection`, `items: DirtyMeta[]`, `calledFrom`, `timestamp`.
|
|
84
107
|
|
|
85
108
|
**Internal:**
|
|
109
|
+
|
|
86
110
|
- `I_DexieDb.incrementDirtyUploadAttempts(collection, ids)` — nova metoda, vrača `DirtyMeta[]` na novo stuck itemov
|
|
87
111
|
- Vgrajena v `SyncEngine.uploadDirtyItems` in `uploadDirtyItemsForCollection` — per-collection result (errored ids) in catch block (network/timeout)
|
|
88
112
|
- `DexieDb.getDirtyMeta` sedaj vključuje nova polja v izhod
|
|
@@ -105,6 +129,7 @@ Razlika od `refreshInBackground`: `refreshImmediately` je blokiren — počaka
|
|
|
105
129
|
na odgovor serverja in vrne ažuriran podatek.
|
|
106
130
|
|
|
107
131
|
Internal:
|
|
132
|
+
|
|
108
133
|
- `_refreshImmediately()` — nova zasebna metoda: fetča s serverja, primerja
|
|
109
134
|
`_rev`, posodobi Dexie samo če server novejši; za iteme z dirty spremembami
|
|
110
135
|
delegira `processCollectionServerData`
|
|
@@ -129,7 +154,6 @@ in-mem. `referToServer` zdaj resnično awaita sveže podatke s serverja.
|
|
|
129
154
|
Internal: `ensureItemsAreLoaded` — odstranjena Dexie pre-check (`getByIds`
|
|
130
155
|
→ `missingIds`).
|
|
131
156
|
|
|
132
|
-
|
|
133
157
|
## 0.1.194 (2026-06-09)
|
|
134
158
|
|
|
135
159
|
### Preload status `ready` fix
|
|
@@ -139,7 +163,6 @@ je `state === "hydrated"`, ne glede na `itemCount` ali `everDownloaded`.
|
|
|
139
163
|
Prazen (še nikoli sinhroniziran) direktorij je še vedno ready — svežina
|
|
140
164
|
podatkov je globalni koncept (`lastSuccessfulServerSync`).
|
|
141
165
|
|
|
142
|
-
|
|
143
166
|
## 0.1.193 (2026-05-25)
|
|
144
167
|
|
|
145
168
|
Adds per-collection preload status reporting (non-breaking, additive).
|
|
@@ -277,6 +300,7 @@ batched download leaves collections registered; next auto-sync tick
|
|
|
277
300
|
retries via the normal sync flow.
|
|
278
301
|
|
|
279
302
|
Compared to looping `addCollectionToSync(spec)` per item:
|
|
303
|
+
|
|
280
304
|
- N collections × per-collection RTT (sequential or `Promise.all`)
|
|
281
305
|
→ 1 RTT for the whole batch.
|
|
282
306
|
- Hydration also parallelized.
|
|
@@ -668,6 +692,7 @@ paths are preserved. After the prune, `entry.baseTs` / `entry.baseRev`
|
|
|
668
692
|
advance to the new floor.
|
|
669
693
|
|
|
670
694
|
Implemented in:
|
|
695
|
+
|
|
671
696
|
- `src/db/DexieDb.ts` — new module-level `rebaseDirtyOnServerAdvance`
|
|
672
697
|
called from both `addDirtyChange` and `addDirtyChangesBatch` before
|
|
673
698
|
`mergeDirtyChanges`.
|
|
@@ -737,6 +762,7 @@ drop silently. These fields are server-mirrored — local materialization
|
|
|
737
762
|
with partial data could diverge from the canonical state.
|
|
738
763
|
|
|
739
764
|
Tests:
|
|
765
|
+
|
|
740
766
|
- `test/applyDiffLocallyObiskShape.test.ts` — 5 new cases covering
|
|
741
767
|
multi-segment prefix shapes plus the `_`-prefix silent-drop contract.
|
|
742
768
|
- `test/applyDiffLocallyMaterialize.test.ts` — flipped the
|
|
@@ -836,6 +862,7 @@ sentry/log dashboards.
|
|
|
836
862
|
|
|
837
863
|
What's NOT routed through `networkError` (intentional — these are real
|
|
838
864
|
bugs regardless of network state):
|
|
865
|
+
|
|
839
866
|
- Caller bugs (falsy `_id`, missing `_id`, id mismatch, no id provided)
|
|
840
867
|
- Dexie failures (`bulkPut failed`, `Failed to write to Dexie`, etc.)
|
|
841
868
|
- Consumer-supplied callback throws (`onSyncEnd callback failed`, etc.)
|
|
@@ -1015,6 +1042,7 @@ in the same scannable line as the tag while the full error object remains
|
|
|
1015
1042
|
attached for devtools inspection.
|
|
1016
1043
|
|
|
1017
1044
|
Also fixed:
|
|
1045
|
+
|
|
1018
1046
|
- `SyncedDb.findById`: bare `console.error(err)` → tag line first, Error
|
|
1019
1047
|
object as second arg.
|
|
1020
1048
|
- `Ebus2ProxyNotifier`: WebSocket error event and server-error payload now
|
|
@@ -1236,6 +1264,7 @@ Behavior on subsequent calls (inspected against the existing config's
|
|
|
1236
1264
|
spec; the new config (whatever shape) drives future syncs alone.
|
|
1237
1265
|
|
|
1238
1266
|
Constraints:
|
|
1267
|
+
|
|
1239
1268
|
- Dexie schema must already declare the table (Dexie does not support
|
|
1240
1269
|
adding tables to an open database). The auto-register handles only the
|
|
1241
1270
|
runtime SyncedDb-level config.
|
|
@@ -1549,6 +1578,7 @@ spread that would replace top-level array/object fields wholesale and
|
|
|
1549
1578
|
drop nested fields the caller's `update` didn't mention.
|
|
1550
1579
|
|
|
1551
1580
|
Replaced with `applyDiffLocally(base, diff, id)`:
|
|
1581
|
+
|
|
1552
1582
|
1. Deep-clone `base` (currentMem ?? existing) via `safeDeepClone`
|
|
1553
1583
|
(handles Date and `ObjectId`-like values; avoids `structuredClone`
|
|
1554
1584
|
throwing on bson class instances)
|
|
@@ -1565,6 +1595,7 @@ Replaced with `applyDiffLocally(base, diff, id)`:
|
|
|
1565
1595
|
Reverted automatic `_id` stamping for objects appearing as array elements.
|
|
1566
1596
|
If an array of objects lacks `_id`, the caller's element shape is now
|
|
1567
1597
|
preserved. This allows callers to mix:
|
|
1598
|
+
|
|
1568
1599
|
- Whole-element bracket replace: `update.postavke = [{...}]`
|
|
1569
1600
|
- Bracket-by-_id sub-field path: `update["postavke[<id>].field"] = value`
|
|
1570
1601
|
in the same payload without the client mutating element identity.
|
|
@@ -1583,6 +1614,7 @@ in the same payload without the client mutating element identity.
|
|
|
1583
1614
|
| Different `_id` set | mixed: `$pull` + `$push` + sub-field |
|
|
1584
1615
|
|
|
1585
1616
|
For composition changes, `computeDiff` now emits:
|
|
1617
|
+
|
|
1586
1618
|
- **Removed `_id`**: `arr[<id>] = undefined` (server: `$pull`)
|
|
1587
1619
|
- **Added `_id`**: `arr[<id>] = [element]` (server: `$concatArrays + $filter`)
|
|
1588
1620
|
- **Retained `_id`**: element-wise sub-field via `arr[<id>].field`
|
|
@@ -2090,6 +2122,7 @@ streams separately. **Non-breaking**: consumers that destructure
|
|
|
2090
2122
|
`{ collection, loaded, total }` and ignore `phase` keep working unchanged.
|
|
2091
2123
|
|
|
2092
2124
|
Type change in `I_SyncedDb.SyncedDbConfig` and internal `SyncEngineCallbacks`:
|
|
2125
|
+
|
|
2093
2126
|
```ts
|
|
2094
2127
|
onSyncProgress?: (info: {
|
|
2095
2128
|
phase: 'dexie' | 'server';
|
package/dist/index.js
CHANGED
|
@@ -3528,59 +3528,80 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3528
3528
|
async uploadDirtyItems(calledFrom) {
|
|
3529
3529
|
var _a, _b;
|
|
3530
3530
|
const collectionBatches = [];
|
|
3531
|
-
for (const [collectionName] of this.collections)
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3531
|
+
for (const [collectionName] of this.collections)
|
|
3532
|
+
try {
|
|
3533
|
+
const dirtyChanges = await this.dexieDb.getDirty(collectionName);
|
|
3534
|
+
if (dirtyChanges.length === 0) continue;
|
|
3535
|
+
const dirtyChangesMap = /* @__PURE__ */ new Map();
|
|
3536
|
+
for (const dirtyItem of dirtyChanges) {
|
|
3537
|
+
dirtyChangesMap.set(String(dirtyItem._id), dirtyItem);
|
|
3538
|
+
}
|
|
3539
|
+
const updates = [];
|
|
3540
|
+
const skipped = [];
|
|
3541
|
+
const ids = dirtyChanges.map((dc) => dc._id);
|
|
3542
|
+
const fullItems = await this.dexieDb.getByIds(
|
|
3543
|
+
collectionName,
|
|
3544
|
+
ids
|
|
3545
|
+
);
|
|
3546
|
+
const orphanReconstructed = [];
|
|
3547
|
+
for (let i = 0; i < fullItems.length; i++) {
|
|
3548
|
+
const fullItem = fullItems[i];
|
|
3549
|
+
const id = ids[i];
|
|
3550
|
+
if (fullItem) {
|
|
3551
|
+
const delta = dirtyChangesMap.get(String(fullItem._id));
|
|
3552
|
+
if (delta) {
|
|
3553
|
+
const currentServerRev = typeof fullItem._rev === "number" ? fullItem._rev : void 0;
|
|
3554
|
+
updates.push({ _id: fullItem._id, delta, currentServerRev });
|
|
3555
|
+
} else {
|
|
3556
|
+
skipped.push({
|
|
3557
|
+
_id: String(fullItem._id),
|
|
3558
|
+
reason: "no-delta-for-fullitem"
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
} else if (id != null) {
|
|
3562
|
+
const delta = dirtyChangesMap.get(String(id));
|
|
3563
|
+
if (delta) {
|
|
3564
|
+
const reconstructed = __spreadProps(__spreadValues({}, delta), { _id: id });
|
|
3565
|
+
orphanReconstructed.push(reconstructed);
|
|
3566
|
+
updates.push({ _id: id, delta });
|
|
3567
|
+
} else {
|
|
3568
|
+
skipped.push({ _id: String(id), reason: "no-delta-for-orphan" });
|
|
3569
|
+
}
|
|
3554
3570
|
} else {
|
|
3555
|
-
skipped.push({
|
|
3556
|
-
_id: String(fullItem._id),
|
|
3557
|
-
reason: "no-delta-for-fullitem"
|
|
3558
|
-
});
|
|
3571
|
+
skipped.push({ _id: "<null>", reason: "no-fullitem-no-id" });
|
|
3559
3572
|
}
|
|
3560
|
-
}
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3573
|
+
}
|
|
3574
|
+
if (orphanReconstructed.length > 0) {
|
|
3575
|
+
await this.dexieDb.saveMany(collectionName, orphanReconstructed);
|
|
3576
|
+
}
|
|
3577
|
+
if (updates.length === 0) {
|
|
3578
|
+
console.warn(
|
|
3579
|
+
`[SyncEngine] uploadDirtyItems: ${collectionName} has`,
|
|
3580
|
+
dirtyChanges.length,
|
|
3581
|
+
"dirty entries but 0 resolvable items",
|
|
3582
|
+
skipped
|
|
3583
|
+
);
|
|
3584
|
+
if (this.callbacks.onUploadSkip) {
|
|
3585
|
+
try {
|
|
3586
|
+
this.callbacks.onUploadSkip({
|
|
3587
|
+
collection: collectionName,
|
|
3588
|
+
reason: "no-resolvable-items",
|
|
3589
|
+
dirtyCount: dirtyChanges.length,
|
|
3590
|
+
skippedIds: skipped.slice(0, 20).map((s) => s._id),
|
|
3591
|
+
skipReasons: skipped.slice(0, 20),
|
|
3592
|
+
calledFrom,
|
|
3593
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3594
|
+
});
|
|
3595
|
+
} catch (err) {
|
|
3596
|
+
console.error(
|
|
3597
|
+
`[SyncEngine] onUploadSkip callback failed: ${err}`,
|
|
3598
|
+
err
|
|
3599
|
+
);
|
|
3600
|
+
}
|
|
3568
3601
|
}
|
|
3569
|
-
|
|
3570
|
-
skipped.push({ _id: "<null>", reason: "no-fullitem-no-id" });
|
|
3602
|
+
continue;
|
|
3571
3603
|
}
|
|
3572
|
-
|
|
3573
|
-
if (orphanReconstructed.length > 0) {
|
|
3574
|
-
await this.dexieDb.saveMany(collectionName, orphanReconstructed);
|
|
3575
|
-
}
|
|
3576
|
-
if (updates.length === 0) {
|
|
3577
|
-
console.warn(
|
|
3578
|
-
`[SyncEngine] uploadDirtyItems: ${collectionName} has`,
|
|
3579
|
-
dirtyChanges.length,
|
|
3580
|
-
"dirty entries but 0 resolvable items",
|
|
3581
|
-
skipped
|
|
3582
|
-
);
|
|
3583
|
-
if (this.callbacks.onUploadSkip) {
|
|
3604
|
+
if (skipped.length > 0 && this.callbacks.onUploadSkip) {
|
|
3584
3605
|
try {
|
|
3585
3606
|
this.callbacks.onUploadSkip({
|
|
3586
3607
|
collection: collectionName,
|
|
@@ -3598,86 +3619,75 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3598
3619
|
);
|
|
3599
3620
|
}
|
|
3600
3621
|
}
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
reason: "no-resolvable-items",
|
|
3608
|
-
dirtyCount: dirtyChanges.length,
|
|
3609
|
-
skippedIds: skipped.slice(0, 20).map((s) => s._id),
|
|
3610
|
-
skipReasons: skipped.slice(0, 20),
|
|
3611
|
-
calledFrom,
|
|
3612
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
3613
|
-
});
|
|
3614
|
-
} catch (err) {
|
|
3615
|
-
console.error(
|
|
3616
|
-
`[SyncEngine] onUploadSkip callback failed: ${err}`,
|
|
3617
|
-
err
|
|
3622
|
+
const mappedUpdates = [];
|
|
3623
|
+
const preprocessSkippedIds = [];
|
|
3624
|
+
for (const item of updates) {
|
|
3625
|
+
const dirtyBaseRev = typeof item.delta._rev === "number" ? item.delta._rev : void 0;
|
|
3626
|
+
const stripped = stripServerManagedFromChanges(
|
|
3627
|
+
item.delta
|
|
3618
3628
|
);
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3629
|
+
const fixed = fixDotnetArrays(
|
|
3630
|
+
stripped,
|
|
3631
|
+
item.currentServerRev,
|
|
3632
|
+
dirtyBaseRev
|
|
3633
|
+
);
|
|
3634
|
+
let candidate = {
|
|
3635
|
+
_id: item._id,
|
|
3636
|
+
update: fixed
|
|
3637
|
+
};
|
|
3638
|
+
if (this.preprocessDirtyItem) {
|
|
3639
|
+
try {
|
|
3640
|
+
const processed = this.preprocessDirtyItem(
|
|
3641
|
+
candidate,
|
|
3642
|
+
collectionName
|
|
3643
|
+
);
|
|
3644
|
+
if (processed === void 0) {
|
|
3645
|
+
preprocessSkippedIds.push(item._id);
|
|
3646
|
+
continue;
|
|
3647
|
+
}
|
|
3648
|
+
candidate = processed;
|
|
3649
|
+
} catch (err) {
|
|
3650
|
+
console.error(
|
|
3651
|
+
`[SyncEngine] preprocessDirtyItem(${collectionName}) failed for _id=${String(item._id)}; keeping dirty for retry:`,
|
|
3652
|
+
err
|
|
3653
|
+
);
|
|
3644
3654
|
preprocessSkippedIds.push(item._id);
|
|
3645
3655
|
continue;
|
|
3646
3656
|
}
|
|
3647
|
-
candidate = processed;
|
|
3648
|
-
} catch (err) {
|
|
3649
|
-
console.error(
|
|
3650
|
-
`[SyncEngine] preprocessDirtyItem(${collectionName}) failed for _id=${String(item._id)}; keeping dirty for retry:`,
|
|
3651
|
-
err
|
|
3652
|
-
);
|
|
3653
|
-
preprocessSkippedIds.push(item._id);
|
|
3654
|
-
continue;
|
|
3655
3657
|
}
|
|
3658
|
+
mappedUpdates.push({
|
|
3659
|
+
_id: candidate._id,
|
|
3660
|
+
_rev: dirtyBaseRev != null ? dirtyBaseRev : 0,
|
|
3661
|
+
update: candidate.update
|
|
3662
|
+
});
|
|
3656
3663
|
}
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3664
|
+
if (preprocessSkippedIds.length > 0) {
|
|
3665
|
+
const newlyStuck = await this.dexieDb.incrementDirtyUploadAttempts(
|
|
3666
|
+
collectionName,
|
|
3667
|
+
preprocessSkippedIds
|
|
3668
|
+
);
|
|
3669
|
+
await this.callOnDirtyItemStuck(
|
|
3670
|
+
collectionName,
|
|
3671
|
+
newlyStuck,
|
|
3672
|
+
calledFrom
|
|
3673
|
+
);
|
|
3674
|
+
}
|
|
3675
|
+
if (mappedUpdates.length === 0) continue;
|
|
3676
|
+
collectionBatches.push([
|
|
3677
|
+
{
|
|
3678
|
+
collection: collectionName,
|
|
3679
|
+
batch: {
|
|
3680
|
+
updates: mappedUpdates,
|
|
3681
|
+
deletes: []
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
]);
|
|
3685
|
+
} catch (err) {
|
|
3686
|
+
console.error(
|
|
3687
|
+
`[SyncEngine] uploadDirtyItems: failed for collection "${collectionName}":`,
|
|
3688
|
+
err
|
|
3667
3689
|
);
|
|
3668
|
-
await this.callOnDirtyItemStuck(collectionName, newlyStuck, calledFrom);
|
|
3669
3690
|
}
|
|
3670
|
-
if (mappedUpdates.length === 0) continue;
|
|
3671
|
-
collectionBatches.push([
|
|
3672
|
-
{
|
|
3673
|
-
collection: collectionName,
|
|
3674
|
-
batch: {
|
|
3675
|
-
updates: mappedUpdates,
|
|
3676
|
-
deletes: []
|
|
3677
|
-
}
|
|
3678
|
-
}
|
|
3679
|
-
]);
|
|
3680
|
-
}
|
|
3681
3691
|
if (collectionBatches.length === 0) {
|
|
3682
3692
|
return { sentCount: 0 };
|
|
3683
3693
|
}
|