@anfenn/dync 1.0.1 → 1.0.3
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/README.md +2 -2
- package/dist/{chunk-LGHOZECP.js → chunk-66PSQW4D.js} +120 -120
- package/dist/chunk-66PSQW4D.js.map +1 -0
- package/dist/index.cjs +119 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{index.shared-YSn6c01d.d.cts → index.shared-BGwvMH8f.d.cts} +3 -3
- package/dist/{index.shared-CPIge2ZM.d.ts → index.shared-CkYsQYyn.d.ts} +3 -3
- package/dist/react/index.cjs +119 -119
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -1
- package/package.json +9 -4
- package/src/core/StateManager.ts +15 -15
- package/src/core/firstLoad.ts +27 -27
- package/src/core/pullOperations.ts +28 -28
- package/src/core/pushOperations.ts +41 -41
- package/src/core/tableEnhancers.ts +9 -9
- package/src/index.shared.ts +3 -3
- package/src/types.ts +3 -3
- package/dist/chunk-LGHOZECP.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,11 +10,11 @@ Start with a Website or PWA using IndexedDB, sync with your existing REST API, a
|
|
|
10
10
|
|
|
11
11
|
1. Frictionless upgrade path from an offline-first PWA targeting IndexedDB, to when:
|
|
12
12
|
|
|
13
|
-
**A)
|
|
13
|
+
**A) Full text search is required on many records** (IndexedDB doesn't support this so will do a full table scan in the JS VM, which is slow on many records)
|
|
14
14
|
|
|
15
15
|
**_and/or_**
|
|
16
16
|
|
|
17
|
-
**B)
|
|
17
|
+
**B) Encryption is required** (Browsers can't store the encryption key securely)
|
|
18
18
|
|
|
19
19
|
... you can simply add CapacitorJs or move to React Native which have sqlite & secure enclave storage, and only change the adapter Dync uses
|
|
20
20
|
|
|
@@ -152,7 +152,7 @@ var StateManager = class {
|
|
|
152
152
|
}
|
|
153
153
|
addPendingChange(change) {
|
|
154
154
|
const next = clonePersistedState(this.persistedState);
|
|
155
|
-
const queueItem = next.pendingChanges.find((p) => p.localId === change.localId && p.
|
|
155
|
+
const queueItem = next.pendingChanges.find((p) => p.localId === change.localId && p.tableName === change.tableName);
|
|
156
156
|
const omittedChanges = omitFields(change.changes, LOCAL_ONLY_SYNC_FIELDS);
|
|
157
157
|
const omittedBefore = omitFields(change.before, LOCAL_ONLY_SYNC_FIELDS);
|
|
158
158
|
const omittedAfter = omitFields(change.after, LOCAL_ONLY_SYNC_FIELDS);
|
|
@@ -173,7 +173,7 @@ var StateManager = class {
|
|
|
173
173
|
next.pendingChanges = [...next.pendingChanges];
|
|
174
174
|
next.pendingChanges.push({
|
|
175
175
|
action,
|
|
176
|
-
|
|
176
|
+
tableName: change.tableName,
|
|
177
177
|
localId: change.localId,
|
|
178
178
|
id: change.id,
|
|
179
179
|
version: 1,
|
|
@@ -185,18 +185,18 @@ var StateManager = class {
|
|
|
185
185
|
this.persistedState = next;
|
|
186
186
|
return this.persist();
|
|
187
187
|
}
|
|
188
|
-
samePendingVersion(
|
|
189
|
-
return this.persistedState.pendingChanges.find((p) => p.localId === localId && p.
|
|
188
|
+
samePendingVersion(tableName, localId, version) {
|
|
189
|
+
return this.persistedState.pendingChanges.find((p) => p.localId === localId && p.tableName === tableName)?.version === version;
|
|
190
190
|
}
|
|
191
|
-
removePendingChange(localId,
|
|
191
|
+
removePendingChange(localId, tableName) {
|
|
192
192
|
const next = clonePersistedState(this.persistedState);
|
|
193
|
-
next.pendingChanges = next.pendingChanges.filter((p) => !(p.localId === localId && p.
|
|
193
|
+
next.pendingChanges = next.pendingChanges.filter((p) => !(p.localId === localId && p.tableName === tableName));
|
|
194
194
|
this.persistedState = next;
|
|
195
195
|
return this.persist();
|
|
196
196
|
}
|
|
197
|
-
updatePendingChange(
|
|
197
|
+
updatePendingChange(tableName, localId, action, id) {
|
|
198
198
|
const next = clonePersistedState(this.persistedState);
|
|
199
|
-
const changeItem = next.pendingChanges.find((p) => p.
|
|
199
|
+
const changeItem = next.pendingChanges.find((p) => p.tableName === tableName && p.localId === localId);
|
|
200
200
|
if (changeItem) {
|
|
201
201
|
changeItem.action = action;
|
|
202
202
|
if (id) changeItem.id = id;
|
|
@@ -205,9 +205,9 @@ var StateManager = class {
|
|
|
205
205
|
}
|
|
206
206
|
return Promise.resolve();
|
|
207
207
|
}
|
|
208
|
-
setPendingChangeBefore(
|
|
208
|
+
setPendingChangeBefore(tableName, localId, before) {
|
|
209
209
|
const next = clonePersistedState(this.persistedState);
|
|
210
|
-
const changeItem = next.pendingChanges.find((p) => p.
|
|
210
|
+
const changeItem = next.pendingChanges.find((p) => p.tableName === tableName && p.localId === localId);
|
|
211
211
|
if (changeItem) {
|
|
212
212
|
changeItem.before = { ...changeItem.before ?? {}, ...before };
|
|
213
213
|
this.persistedState = next;
|
|
@@ -278,7 +278,7 @@ function cloneConflicts(conflicts) {
|
|
|
278
278
|
const next = {};
|
|
279
279
|
for (const [key, value] of Object.entries(conflicts)) {
|
|
280
280
|
next[key] = {
|
|
281
|
-
|
|
281
|
+
tableName: value.tableName,
|
|
282
282
|
fields: value.fields.map((field) => ({ ...field }))
|
|
283
283
|
};
|
|
284
284
|
}
|
|
@@ -389,7 +389,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
389
389
|
result = await rawAdd(syncedItem);
|
|
390
390
|
await state.addPendingChange({
|
|
391
391
|
action: "create" /* Create */,
|
|
392
|
-
|
|
392
|
+
tableName,
|
|
393
393
|
localId,
|
|
394
394
|
changes: syncedItem,
|
|
395
395
|
before: null,
|
|
@@ -417,7 +417,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
417
417
|
result = await rawPut(syncedItem);
|
|
418
418
|
await state.addPendingChange({
|
|
419
419
|
action: isUpdate ? "update" /* Update */ : "create" /* Create */,
|
|
420
|
-
|
|
420
|
+
tableName,
|
|
421
421
|
localId,
|
|
422
422
|
id: existingRecord?.id,
|
|
423
423
|
changes: syncedItem,
|
|
@@ -444,7 +444,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
444
444
|
if (result > 0) {
|
|
445
445
|
await state.addPendingChange({
|
|
446
446
|
action: "update" /* Update */,
|
|
447
|
-
|
|
447
|
+
tableName,
|
|
448
448
|
localId: key,
|
|
449
449
|
id: record.id,
|
|
450
450
|
changes: updatedChanges,
|
|
@@ -468,7 +468,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
468
468
|
deletedLocalId = record._localId;
|
|
469
469
|
await state.addPendingChange({
|
|
470
470
|
action: "remove" /* Remove */,
|
|
471
|
-
|
|
471
|
+
tableName,
|
|
472
472
|
localId: record._localId,
|
|
473
473
|
id: record.id,
|
|
474
474
|
changes: null,
|
|
@@ -497,7 +497,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
497
497
|
for (const syncedItem of syncedItems) {
|
|
498
498
|
await state.addPendingChange({
|
|
499
499
|
action: "create" /* Create */,
|
|
500
|
-
|
|
500
|
+
tableName,
|
|
501
501
|
localId: syncedItem._localId,
|
|
502
502
|
changes: syncedItem,
|
|
503
503
|
before: null,
|
|
@@ -535,7 +535,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
535
535
|
const existing = existingMap.get(syncedItem._localId);
|
|
536
536
|
await state.addPendingChange({
|
|
537
537
|
action: existing ? "update" /* Update */ : "create" /* Create */,
|
|
538
|
-
|
|
538
|
+
tableName,
|
|
539
539
|
localId: syncedItem._localId,
|
|
540
540
|
id: existing?.id,
|
|
541
541
|
changes: syncedItem,
|
|
@@ -576,7 +576,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
576
576
|
updatedKeys.push(record._localId);
|
|
577
577
|
await state.addPendingChange({
|
|
578
578
|
action: "update" /* Update */,
|
|
579
|
-
|
|
579
|
+
tableName,
|
|
580
580
|
localId: record._localId,
|
|
581
581
|
id: record.id,
|
|
582
582
|
changes,
|
|
@@ -603,7 +603,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
603
603
|
deletedLocalIds.push(record._localId);
|
|
604
604
|
await state.addPendingChange({
|
|
605
605
|
action: "remove" /* Remove */,
|
|
606
|
-
|
|
606
|
+
tableName,
|
|
607
607
|
localId: record._localId,
|
|
608
608
|
id: record.id,
|
|
609
609
|
changes: null,
|
|
@@ -627,7 +627,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
627
627
|
deletedLocalIds.push(record._localId);
|
|
628
628
|
await state.addPendingChange({
|
|
629
629
|
action: "remove" /* Remove */,
|
|
630
|
-
|
|
630
|
+
tableName,
|
|
631
631
|
localId: record._localId,
|
|
632
632
|
id: record.id,
|
|
633
633
|
changes: null,
|
|
@@ -656,33 +656,33 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
|
|
|
656
656
|
async function pullAll(ctx) {
|
|
657
657
|
let firstSyncError;
|
|
658
658
|
const changedTables = [];
|
|
659
|
-
for (const [
|
|
659
|
+
for (const [tableName, api] of Object.entries(ctx.syncApis)) {
|
|
660
660
|
try {
|
|
661
|
-
const lastPulled = ctx.state.getState().lastPulled[
|
|
661
|
+
const lastPulled = ctx.state.getState().lastPulled[tableName];
|
|
662
662
|
const since = lastPulled ? new Date(lastPulled) : /* @__PURE__ */ new Date(0);
|
|
663
|
-
ctx.logger.debug(`[dync] pull:start
|
|
663
|
+
ctx.logger.debug(`[dync] pull:start tableName=${tableName} since=${since.toISOString()}`);
|
|
664
664
|
const serverData = await api.list(since);
|
|
665
|
-
const changed = await processPullData(
|
|
666
|
-
if (changed) changedTables.push(
|
|
665
|
+
const changed = await processPullData(tableName, serverData, since, ctx);
|
|
666
|
+
if (changed) changedTables.push(tableName);
|
|
667
667
|
} catch (err) {
|
|
668
668
|
firstSyncError = firstSyncError ?? err;
|
|
669
|
-
ctx.logger.error(`[dync] pull:error
|
|
669
|
+
ctx.logger.error(`[dync] pull:error tableName=${tableName}`, err);
|
|
670
670
|
}
|
|
671
671
|
}
|
|
672
672
|
return { error: firstSyncError, changedTables };
|
|
673
673
|
}
|
|
674
|
-
async function handleRemoteItemUpdate(table,
|
|
675
|
-
const pendingChange = ctx.state.getState().pendingChanges.find((p) => p.
|
|
674
|
+
async function handleRemoteItemUpdate(table, tableName, localItem, remote, ctx) {
|
|
675
|
+
const pendingChange = ctx.state.getState().pendingChanges.find((p) => p.tableName === tableName && p.localId === localItem._localId);
|
|
676
676
|
const conflictStrategy = ctx.conflictResolutionStrategy;
|
|
677
677
|
if (pendingChange) {
|
|
678
|
-
ctx.logger.debug(`[dync] pull:conflict-strategy:${conflictStrategy}
|
|
678
|
+
ctx.logger.debug(`[dync] pull:conflict-strategy:${conflictStrategy} tableName=${tableName} id=${remote.id}`);
|
|
679
679
|
switch (conflictStrategy) {
|
|
680
680
|
case "local-wins":
|
|
681
681
|
break;
|
|
682
682
|
case "remote-wins": {
|
|
683
683
|
const merged = { ...remote, _localId: localItem._localId };
|
|
684
684
|
await table.raw.update(localItem._localId, merged);
|
|
685
|
-
await ctx.state.removePendingChange(localItem._localId,
|
|
685
|
+
await ctx.state.removePendingChange(localItem._localId, tableName);
|
|
686
686
|
break;
|
|
687
687
|
}
|
|
688
688
|
case "try-shallow-merge": {
|
|
@@ -695,7 +695,7 @@ async function handleRemoteItemUpdate(table, stateKey, localItem, remote, ctx) {
|
|
|
695
695
|
...syncState,
|
|
696
696
|
conflicts: {
|
|
697
697
|
...syncState.conflicts || {},
|
|
698
|
-
[localItem._localId]: {
|
|
698
|
+
[localItem._localId]: { tableName, fields }
|
|
699
699
|
}
|
|
700
700
|
}));
|
|
701
701
|
} else {
|
|
@@ -718,7 +718,7 @@ async function handleRemoteItemUpdate(table, stateKey, localItem, remote, ctx) {
|
|
|
718
718
|
} else {
|
|
719
719
|
const merged = { ...localItem, ...remote };
|
|
720
720
|
await table.raw.update(localItem._localId, merged);
|
|
721
|
-
ctx.logger.debug(`[dync] pull:merge-remote
|
|
721
|
+
ctx.logger.debug(`[dync] pull:merge-remote tableName=${tableName} id=${remote.id}`);
|
|
722
722
|
}
|
|
723
723
|
}
|
|
724
724
|
async function pullAllBatch(ctx) {
|
|
@@ -732,17 +732,17 @@ async function pullAllBatch(ctx) {
|
|
|
732
732
|
}
|
|
733
733
|
ctx.logger.debug(`[dync] pull:batch:start tables=${[...ctx.batchSync.syncTables].join(",")}`, sinceMap);
|
|
734
734
|
const serverDataByTable = await ctx.batchSync.pull(sinceMap);
|
|
735
|
-
for (const [
|
|
736
|
-
if (!ctx.batchSync.syncTables.includes(
|
|
737
|
-
ctx.logger.warn(`[dync] pull:batch:unknown-table
|
|
735
|
+
for (const [tableName, serverData] of Object.entries(serverDataByTable)) {
|
|
736
|
+
if (!ctx.batchSync.syncTables.includes(tableName)) {
|
|
737
|
+
ctx.logger.warn(`[dync] pull:batch:unknown-table tableName=${tableName}`);
|
|
738
738
|
continue;
|
|
739
739
|
}
|
|
740
740
|
try {
|
|
741
|
-
const changed = await processPullData(
|
|
742
|
-
if (changed) changedTables.push(
|
|
741
|
+
const changed = await processPullData(tableName, serverData, sinceMap[tableName], ctx);
|
|
742
|
+
if (changed) changedTables.push(tableName);
|
|
743
743
|
} catch (err) {
|
|
744
744
|
firstSyncError = firstSyncError ?? err;
|
|
745
|
-
ctx.logger.error(`[dync] pull:batch:error
|
|
745
|
+
ctx.logger.error(`[dync] pull:batch:error tableName=${tableName}`, err);
|
|
746
746
|
}
|
|
747
747
|
}
|
|
748
748
|
} catch (err) {
|
|
@@ -751,40 +751,40 @@ async function pullAllBatch(ctx) {
|
|
|
751
751
|
}
|
|
752
752
|
return { error: firstSyncError, changedTables };
|
|
753
753
|
}
|
|
754
|
-
async function processPullData(
|
|
754
|
+
async function processPullData(tableName, serverData, since, ctx) {
|
|
755
755
|
if (!serverData?.length) return false;
|
|
756
|
-
ctx.logger.debug(`[dync] pull:process
|
|
756
|
+
ctx.logger.debug(`[dync] pull:process tableName=${tableName} count=${serverData.length}`);
|
|
757
757
|
let newest = since;
|
|
758
758
|
let hasChanges = false;
|
|
759
|
-
await ctx.withTransaction("rw", [
|
|
760
|
-
const txTable = tables[
|
|
759
|
+
await ctx.withTransaction("rw", [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
760
|
+
const txTable = tables[tableName];
|
|
761
761
|
const pendingRemovalById = new Set(
|
|
762
|
-
ctx.state.getState().pendingChanges.filter((p) => p.
|
|
762
|
+
ctx.state.getState().pendingChanges.filter((p) => p.tableName === tableName && p.action === "remove" /* Remove */).map((p) => p.id)
|
|
763
763
|
);
|
|
764
764
|
for (const remote of serverData) {
|
|
765
765
|
const remoteUpdated = new Date(remote.updated_at);
|
|
766
766
|
if (remoteUpdated > newest) newest = remoteUpdated;
|
|
767
767
|
if (pendingRemovalById.has(remote.id)) {
|
|
768
|
-
ctx.logger.debug(`[dync] pull:skip-pending-remove
|
|
768
|
+
ctx.logger.debug(`[dync] pull:skip-pending-remove tableName=${tableName} id=${remote.id}`);
|
|
769
769
|
continue;
|
|
770
770
|
}
|
|
771
771
|
const localItem = await txTable.where("id").equals(remote.id).first();
|
|
772
772
|
if (remote.deleted) {
|
|
773
773
|
if (localItem) {
|
|
774
774
|
await txTable.raw.delete(localItem._localId);
|
|
775
|
-
ctx.logger.debug(`[dync] pull:remove
|
|
775
|
+
ctx.logger.debug(`[dync] pull:remove tableName=${tableName} id=${remote.id}`);
|
|
776
776
|
hasChanges = true;
|
|
777
777
|
}
|
|
778
778
|
continue;
|
|
779
779
|
}
|
|
780
780
|
delete remote.deleted;
|
|
781
781
|
if (localItem) {
|
|
782
|
-
await handleRemoteItemUpdate(txTable,
|
|
782
|
+
await handleRemoteItemUpdate(txTable, tableName, localItem, remote, ctx);
|
|
783
783
|
hasChanges = true;
|
|
784
784
|
} else {
|
|
785
785
|
const newLocalItem = { ...remote, _localId: createLocalId() };
|
|
786
786
|
await txTable.raw.add(newLocalItem);
|
|
787
|
-
ctx.logger.debug(`[dync] pull:add
|
|
787
|
+
ctx.logger.debug(`[dync] pull:add tableName=${tableName} id=${remote.id}`);
|
|
788
788
|
hasChanges = true;
|
|
789
789
|
}
|
|
790
790
|
}
|
|
@@ -792,7 +792,7 @@ async function processPullData(stateKey, serverData, since, ctx) {
|
|
|
792
792
|
...syncState,
|
|
793
793
|
lastPulled: {
|
|
794
794
|
...syncState.lastPulled,
|
|
795
|
-
[
|
|
795
|
+
[tableName]: newest.toISOString()
|
|
796
796
|
}
|
|
797
797
|
}));
|
|
798
798
|
});
|
|
@@ -801,35 +801,35 @@ async function processPullData(stateKey, serverData, since, ctx) {
|
|
|
801
801
|
|
|
802
802
|
// src/core/pushOperations.ts
|
|
803
803
|
async function handleRemoveSuccess(change, ctx) {
|
|
804
|
-
const {
|
|
805
|
-
ctx.logger.debug(`[dync] push:remove:success
|
|
806
|
-
await ctx.state.removePendingChange(localId,
|
|
804
|
+
const { tableName, localId, id } = change;
|
|
805
|
+
ctx.logger.debug(`[dync] push:remove:success tableName=${tableName} localId=${localId} id=${id}`);
|
|
806
|
+
await ctx.state.removePendingChange(localId, tableName);
|
|
807
807
|
}
|
|
808
808
|
async function handleUpdateSuccess(change, ctx) {
|
|
809
|
-
const {
|
|
810
|
-
ctx.logger.debug(`[dync] push:update:success
|
|
811
|
-
if (ctx.state.samePendingVersion(
|
|
812
|
-
await ctx.state.removePendingChange(localId,
|
|
809
|
+
const { tableName, localId, version, changes } = change;
|
|
810
|
+
ctx.logger.debug(`[dync] push:update:success tableName=${tableName} localId=${localId} id=${change.id}`);
|
|
811
|
+
if (ctx.state.samePendingVersion(tableName, localId, version)) {
|
|
812
|
+
await ctx.state.removePendingChange(localId, tableName);
|
|
813
813
|
} else {
|
|
814
|
-
await ctx.state.setPendingChangeBefore(
|
|
814
|
+
await ctx.state.setPendingChangeBefore(tableName, localId, changes);
|
|
815
815
|
}
|
|
816
816
|
}
|
|
817
817
|
async function handleCreateSuccess(change, serverResult, ctx) {
|
|
818
|
-
const {
|
|
819
|
-
ctx.logger.debug(`[dync] push:create:success
|
|
820
|
-
await ctx.withTransaction("rw", [
|
|
821
|
-
const txTable = tables[
|
|
818
|
+
const { tableName, localId, version, changes, id } = change;
|
|
819
|
+
ctx.logger.debug(`[dync] push:create:success tableName=${tableName} localId=${localId} id=${id ?? serverResult.id}`);
|
|
820
|
+
await ctx.withTransaction("rw", [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
821
|
+
const txTable = tables[tableName];
|
|
822
822
|
const wasChanged = await txTable.raw.update(localId, serverResult) ?? 0;
|
|
823
|
-
if (wasChanged && ctx.state.samePendingVersion(
|
|
824
|
-
await ctx.state.removePendingChange(localId,
|
|
823
|
+
if (wasChanged && ctx.state.samePendingVersion(tableName, localId, version)) {
|
|
824
|
+
await ctx.state.removePendingChange(localId, tableName);
|
|
825
825
|
} else {
|
|
826
826
|
const nextAction = wasChanged ? "update" /* Update */ : "remove" /* Remove */;
|
|
827
|
-
await ctx.state.updatePendingChange(
|
|
827
|
+
await ctx.state.updatePendingChange(tableName, localId, nextAction, serverResult.id);
|
|
828
828
|
if (nextAction === "remove" /* Remove */) return;
|
|
829
829
|
}
|
|
830
830
|
});
|
|
831
831
|
const finalItem = { ...changes, ...serverResult, _localId: localId };
|
|
832
|
-
ctx.syncOptions.onAfterRemoteAdd?.(
|
|
832
|
+
ctx.syncOptions.onAfterRemoteAdd?.(tableName, finalItem);
|
|
833
833
|
}
|
|
834
834
|
async function pushAll(ctx) {
|
|
835
835
|
let firstSyncError;
|
|
@@ -845,15 +845,15 @@ async function pushAll(ctx) {
|
|
|
845
845
|
return firstSyncError;
|
|
846
846
|
}
|
|
847
847
|
async function pushOne(change, ctx) {
|
|
848
|
-
const api = ctx.syncApis[change.
|
|
848
|
+
const api = ctx.syncApis[change.tableName];
|
|
849
849
|
if (!api) return;
|
|
850
|
-
ctx.logger.debug(`[dync] push:attempt action=${change.action}
|
|
851
|
-
const { action,
|
|
850
|
+
ctx.logger.debug(`[dync] push:attempt action=${change.action} tableName=${change.tableName} localId=${change.localId}`);
|
|
851
|
+
const { action, tableName, localId, id, changes, after } = change;
|
|
852
852
|
switch (action) {
|
|
853
853
|
case "remove" /* Remove */:
|
|
854
854
|
if (!id) {
|
|
855
|
-
ctx.logger.warn(`[dync] push:remove:no-id
|
|
856
|
-
await ctx.state.removePendingChange(localId,
|
|
855
|
+
ctx.logger.warn(`[dync] push:remove:no-id tableName=${tableName} localId=${localId}`);
|
|
856
|
+
await ctx.state.removePendingChange(localId, tableName);
|
|
857
857
|
return;
|
|
858
858
|
}
|
|
859
859
|
await api.remove(id);
|
|
@@ -861,7 +861,7 @@ async function pushOne(change, ctx) {
|
|
|
861
861
|
break;
|
|
862
862
|
case "update" /* Update */: {
|
|
863
863
|
if (ctx.state.hasConflicts(localId)) {
|
|
864
|
-
ctx.logger.warn(`[dync] push:update:skipping-with-conflicts
|
|
864
|
+
ctx.logger.warn(`[dync] push:update:skipping-with-conflicts tableName=${tableName} localId=${localId} id=${id}`);
|
|
865
865
|
return;
|
|
866
866
|
}
|
|
867
867
|
const exists = await api.update(id, changes, after);
|
|
@@ -877,9 +877,9 @@ async function pushOne(change, ctx) {
|
|
|
877
877
|
if (result) {
|
|
878
878
|
await handleCreateSuccess(change, result, ctx);
|
|
879
879
|
} else {
|
|
880
|
-
ctx.logger.warn(`[dync] push:create:no-result
|
|
881
|
-
if (ctx.state.samePendingVersion(
|
|
882
|
-
await ctx.state.removePendingChange(localId,
|
|
880
|
+
ctx.logger.warn(`[dync] push:create:no-result tableName=${tableName} localId=${localId} id=${id}`);
|
|
881
|
+
if (ctx.state.samePendingVersion(tableName, localId, change.version)) {
|
|
882
|
+
await ctx.state.removePendingChange(localId, tableName);
|
|
883
883
|
}
|
|
884
884
|
}
|
|
885
885
|
break;
|
|
@@ -887,21 +887,21 @@ async function pushOne(change, ctx) {
|
|
|
887
887
|
}
|
|
888
888
|
}
|
|
889
889
|
async function handleMissingRemoteRecord(change, ctx) {
|
|
890
|
-
const {
|
|
890
|
+
const { tableName, localId } = change;
|
|
891
891
|
const strategy = ctx.syncOptions.missingRemoteRecordDuringUpdateStrategy;
|
|
892
892
|
let localItem;
|
|
893
|
-
await ctx.withTransaction("rw", [
|
|
894
|
-
const txTable = tables[
|
|
893
|
+
await ctx.withTransaction("rw", [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
894
|
+
const txTable = tables[tableName];
|
|
895
895
|
localItem = await txTable.get(localId);
|
|
896
896
|
if (!localItem) {
|
|
897
|
-
ctx.logger.warn(`[dync] push:missing-remote:no-local-item
|
|
898
|
-
await ctx.state.removePendingChange(localId,
|
|
897
|
+
ctx.logger.warn(`[dync] push:missing-remote:no-local-item tableName=${tableName} localId=${localId}`);
|
|
898
|
+
await ctx.state.removePendingChange(localId, tableName);
|
|
899
899
|
return;
|
|
900
900
|
}
|
|
901
901
|
switch (strategy) {
|
|
902
902
|
case "delete-local-record":
|
|
903
903
|
await txTable.raw.delete(localId);
|
|
904
|
-
ctx.logger.debug(`[dync] push:missing-remote:${strategy}
|
|
904
|
+
ctx.logger.debug(`[dync] push:missing-remote:${strategy} tableName=${tableName} id=${localItem.id}`);
|
|
905
905
|
break;
|
|
906
906
|
case "insert-remote-record": {
|
|
907
907
|
const newItem = {
|
|
@@ -913,36 +913,36 @@ async function handleMissingRemoteRecord(change, ctx) {
|
|
|
913
913
|
await txTable.raw.delete(localId);
|
|
914
914
|
await ctx.state.addPendingChange({
|
|
915
915
|
action: "create" /* Create */,
|
|
916
|
-
|
|
916
|
+
tableName,
|
|
917
917
|
localId: newItem._localId,
|
|
918
918
|
changes: newItem,
|
|
919
919
|
before: null
|
|
920
920
|
});
|
|
921
|
-
ctx.logger.debug(`[dync] push:missing-remote:${strategy}
|
|
921
|
+
ctx.logger.debug(`[dync] push:missing-remote:${strategy} tableName=${tableName} id=${newItem.id}`);
|
|
922
922
|
break;
|
|
923
923
|
}
|
|
924
924
|
case "ignore":
|
|
925
|
-
ctx.logger.debug(`[dync] push:missing-remote:${strategy}
|
|
925
|
+
ctx.logger.debug(`[dync] push:missing-remote:${strategy} tableName=${tableName} id=${localItem.id}`);
|
|
926
926
|
break;
|
|
927
927
|
default:
|
|
928
|
-
ctx.logger.error(`[dync] push:missing-remote:unknown-strategy
|
|
928
|
+
ctx.logger.error(`[dync] push:missing-remote:unknown-strategy tableName=${tableName} id=${localItem.id} strategy=${strategy}`);
|
|
929
929
|
break;
|
|
930
930
|
}
|
|
931
|
-
await ctx.state.removePendingChange(localId,
|
|
931
|
+
await ctx.state.removePendingChange(localId, tableName);
|
|
932
932
|
});
|
|
933
933
|
ctx.syncOptions.onAfterMissingRemoteRecordDuringUpdate?.(strategy, localItem);
|
|
934
934
|
}
|
|
935
935
|
async function pushAllBatch(ctx) {
|
|
936
936
|
let firstSyncError;
|
|
937
937
|
try {
|
|
938
|
-
const changesSnapshot = [...ctx.state.getState().pendingChanges].filter((change) => ctx.batchSync.syncTables.includes(change.
|
|
938
|
+
const changesSnapshot = [...ctx.state.getState().pendingChanges].filter((change) => ctx.batchSync.syncTables.includes(change.tableName)).sort((a, b) => orderFor(a.action) - orderFor(b.action));
|
|
939
939
|
if (changesSnapshot.length === 0) {
|
|
940
940
|
ctx.logger.debug("[dync] push:batch:no-changes");
|
|
941
941
|
return void 0;
|
|
942
942
|
}
|
|
943
943
|
const changesToPush = changesSnapshot.filter((change) => {
|
|
944
944
|
if (change.action === "update" /* Update */ && ctx.state.hasConflicts(change.localId)) {
|
|
945
|
-
ctx.logger.warn(`[dync] push:batch:skipping-with-conflicts
|
|
945
|
+
ctx.logger.warn(`[dync] push:batch:skipping-with-conflicts tableName=${change.tableName} localId=${change.localId}`);
|
|
946
946
|
return false;
|
|
947
947
|
}
|
|
948
948
|
return true;
|
|
@@ -952,7 +952,7 @@ async function pushAllBatch(ctx) {
|
|
|
952
952
|
return void 0;
|
|
953
953
|
}
|
|
954
954
|
const payloads = changesToPush.map((change) => ({
|
|
955
|
-
table: change.
|
|
955
|
+
table: change.tableName,
|
|
956
956
|
action: change.action === "create" /* Create */ ? "add" : change.action === "update" /* Update */ ? "update" : "remove",
|
|
957
957
|
localId: change.localId,
|
|
958
958
|
id: change.id,
|
|
@@ -984,12 +984,12 @@ async function pushAllBatch(ctx) {
|
|
|
984
984
|
return firstSyncError;
|
|
985
985
|
}
|
|
986
986
|
async function processBatchPushResult(change, result, ctx) {
|
|
987
|
-
const { action,
|
|
987
|
+
const { action, tableName, localId } = change;
|
|
988
988
|
if (!result.success) {
|
|
989
989
|
if (action === "update" /* Update */) {
|
|
990
990
|
await handleMissingRemoteRecord(change, ctx);
|
|
991
991
|
} else {
|
|
992
|
-
ctx.logger.warn(`[dync] push:batch:failed
|
|
992
|
+
ctx.logger.warn(`[dync] push:batch:failed tableName=${tableName} localId=${localId} error=${result.error}`);
|
|
993
993
|
}
|
|
994
994
|
return;
|
|
995
995
|
}
|
|
@@ -1021,13 +1021,13 @@ async function startFirstLoad(ctx) {
|
|
|
1021
1021
|
return;
|
|
1022
1022
|
}
|
|
1023
1023
|
let error;
|
|
1024
|
-
for (const [
|
|
1024
|
+
for (const [tableName, api] of Object.entries(ctx.syncApis)) {
|
|
1025
1025
|
if (!api.firstLoad) {
|
|
1026
|
-
ctx.logger.error(`[dync] firstLoad:no-api-function
|
|
1026
|
+
ctx.logger.error(`[dync] firstLoad:no-api-function tableName=${tableName}`);
|
|
1027
1027
|
continue;
|
|
1028
1028
|
}
|
|
1029
1029
|
try {
|
|
1030
|
-
ctx.logger.info(`[dync] firstLoad:start
|
|
1030
|
+
ctx.logger.info(`[dync] firstLoad:start tableName=${tableName}`);
|
|
1031
1031
|
let lastId;
|
|
1032
1032
|
let isEmptyTable = true;
|
|
1033
1033
|
let batchCount = 0;
|
|
@@ -1037,19 +1037,19 @@ async function startFirstLoad(ctx) {
|
|
|
1037
1037
|
const batch = await api.firstLoad(lastId);
|
|
1038
1038
|
if (!batch?.length) break;
|
|
1039
1039
|
batchCount++;
|
|
1040
|
-
const { inserted, updated } = await processBatchInChunks(ctx,
|
|
1040
|
+
const { inserted, updated } = await processBatchInChunks(ctx, tableName, batch, isEmptyTable, lastId === void 0);
|
|
1041
1041
|
totalInserted += inserted;
|
|
1042
1042
|
totalUpdated += updated;
|
|
1043
1043
|
if (ctx.onProgress) {
|
|
1044
1044
|
ctx.onProgress({
|
|
1045
|
-
table:
|
|
1045
|
+
table: tableName,
|
|
1046
1046
|
inserted: totalInserted,
|
|
1047
1047
|
updated: totalUpdated,
|
|
1048
1048
|
total: totalInserted + totalUpdated
|
|
1049
1049
|
});
|
|
1050
1050
|
}
|
|
1051
1051
|
if (lastId === void 0) {
|
|
1052
|
-
isEmptyTable = await ctx.table(
|
|
1052
|
+
isEmptyTable = await ctx.table(tableName).count() === batch.length;
|
|
1053
1053
|
}
|
|
1054
1054
|
if (lastId !== void 0 && lastId === batch[batch.length - 1].id) {
|
|
1055
1055
|
throw new Error(`Duplicate records downloaded, stopping to prevent infinite loop`);
|
|
@@ -1059,10 +1059,10 @@ async function startFirstLoad(ctx) {
|
|
|
1059
1059
|
await yieldToEventLoop();
|
|
1060
1060
|
}
|
|
1061
1061
|
}
|
|
1062
|
-
ctx.logger.info(`[dync] firstLoad:done
|
|
1062
|
+
ctx.logger.info(`[dync] firstLoad:done tableName=${tableName} inserted=${totalInserted} updated=${totalUpdated}`);
|
|
1063
1063
|
} catch (err) {
|
|
1064
1064
|
error = error ?? err;
|
|
1065
|
-
ctx.logger.error(`[dync] firstLoad:error
|
|
1065
|
+
ctx.logger.error(`[dync] firstLoad:error tableName=${tableName}`, err);
|
|
1066
1066
|
}
|
|
1067
1067
|
}
|
|
1068
1068
|
await ctx.state.setState((syncState) => ({
|
|
@@ -1072,10 +1072,10 @@ async function startFirstLoad(ctx) {
|
|
|
1072
1072
|
}));
|
|
1073
1073
|
ctx.logger.debug("[dync] First load completed");
|
|
1074
1074
|
}
|
|
1075
|
-
async function processBatchInChunks(ctx,
|
|
1076
|
-
let newest = new Date(ctx.state.getState().lastPulled[
|
|
1077
|
-
return ctx.withTransaction("rw", [
|
|
1078
|
-
const txTable = tables[
|
|
1075
|
+
async function processBatchInChunks(ctx, tableName, batch, isEmptyTable, isFirstBatch) {
|
|
1076
|
+
let newest = new Date(ctx.state.getState().lastPulled[tableName] || 0);
|
|
1077
|
+
return ctx.withTransaction("rw", [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
1078
|
+
const txTable = tables[tableName];
|
|
1079
1079
|
let tableIsEmpty = isEmptyTable;
|
|
1080
1080
|
if (isFirstBatch) {
|
|
1081
1081
|
const count = await txTable.count();
|
|
@@ -1110,7 +1110,7 @@ async function processBatchInChunks(ctx, stateKey, batch, isEmptyTable, isFirstB
|
|
|
1110
1110
|
...syncState,
|
|
1111
1111
|
lastPulled: {
|
|
1112
1112
|
...syncState.lastPulled,
|
|
1113
|
-
[
|
|
1113
|
+
[tableName]: newest.toISOString()
|
|
1114
1114
|
}
|
|
1115
1115
|
}));
|
|
1116
1116
|
return { inserted, updated };
|
|
@@ -1175,23 +1175,23 @@ async function startFirstLoadBatch(ctx) {
|
|
|
1175
1175
|
break;
|
|
1176
1176
|
}
|
|
1177
1177
|
batchCount++;
|
|
1178
|
-
for (const [
|
|
1179
|
-
if (!ctx.batchSync.syncTables.includes(
|
|
1180
|
-
ctx.logger.warn(`[dync] firstLoad:batch:unknown-table
|
|
1178
|
+
for (const [tableName, batch] of Object.entries(result.data)) {
|
|
1179
|
+
if (!ctx.batchSync.syncTables.includes(tableName)) {
|
|
1180
|
+
ctx.logger.warn(`[dync] firstLoad:batch:unknown-table tableName=${tableName}`);
|
|
1181
1181
|
continue;
|
|
1182
1182
|
}
|
|
1183
1183
|
if (!batch?.length) continue;
|
|
1184
|
-
const isFirstBatch = progress[
|
|
1185
|
-
const isEmptyTable = isFirstBatch && await ctx.table(
|
|
1186
|
-
const { inserted, updated } = await processBatchInChunks(ctx,
|
|
1187
|
-
progress[
|
|
1188
|
-
progress[
|
|
1184
|
+
const isFirstBatch = progress[tableName].inserted === 0 && progress[tableName].updated === 0;
|
|
1185
|
+
const isEmptyTable = isFirstBatch && await ctx.table(tableName).count() === 0;
|
|
1186
|
+
const { inserted, updated } = await processBatchInChunks(ctx, tableName, batch, isEmptyTable, isFirstBatch);
|
|
1187
|
+
progress[tableName].inserted += inserted;
|
|
1188
|
+
progress[tableName].updated += updated;
|
|
1189
1189
|
if (ctx.onProgress) {
|
|
1190
1190
|
ctx.onProgress({
|
|
1191
|
-
table:
|
|
1192
|
-
inserted: progress[
|
|
1193
|
-
updated: progress[
|
|
1194
|
-
total: progress[
|
|
1191
|
+
table: tableName,
|
|
1192
|
+
inserted: progress[tableName].inserted,
|
|
1193
|
+
updated: progress[tableName].updated,
|
|
1194
|
+
total: progress[tableName].inserted + progress[tableName].updated
|
|
1195
1195
|
});
|
|
1196
1196
|
}
|
|
1197
1197
|
}
|
|
@@ -1203,8 +1203,8 @@ async function startFirstLoadBatch(ctx) {
|
|
|
1203
1203
|
break;
|
|
1204
1204
|
}
|
|
1205
1205
|
}
|
|
1206
|
-
for (const [
|
|
1207
|
-
ctx.logger.info(`[dync] firstLoad:batch:done
|
|
1206
|
+
for (const [tableName, p] of Object.entries(progress)) {
|
|
1207
|
+
ctx.logger.info(`[dync] firstLoad:batch:done tableName=${tableName} inserted=${p.inserted} updated=${p.updated}`);
|
|
1208
1208
|
}
|
|
1209
1209
|
} catch (err) {
|
|
1210
1210
|
error = err;
|
|
@@ -1574,8 +1574,8 @@ var DyncBase = class {
|
|
|
1574
1574
|
this.logger.warn(`[dync] No conflict found for localId: ${localId}`);
|
|
1575
1575
|
return;
|
|
1576
1576
|
}
|
|
1577
|
-
await this.withTransaction("rw", [conflict.
|
|
1578
|
-
const txTable = tables[conflict.
|
|
1577
|
+
await this.withTransaction("rw", [conflict.tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
1578
|
+
const txTable = tables[conflict.tableName];
|
|
1579
1579
|
if (!keepLocal) {
|
|
1580
1580
|
const item = await txTable.get(localId);
|
|
1581
1581
|
if (item) {
|
|
@@ -1588,7 +1588,7 @@ var DyncBase = class {
|
|
|
1588
1588
|
}
|
|
1589
1589
|
await this.state.setState((syncState) => ({
|
|
1590
1590
|
...syncState,
|
|
1591
|
-
pendingChanges: syncState.pendingChanges.filter((p) => !(p.localId === localId && p.
|
|
1591
|
+
pendingChanges: syncState.pendingChanges.filter((p) => !(p.localId === localId && p.tableName === conflict.tableName))
|
|
1592
1592
|
}));
|
|
1593
1593
|
}
|
|
1594
1594
|
await this.state.setState((syncState) => {
|
|
@@ -3881,4 +3881,4 @@ export {
|
|
|
3881
3881
|
SqliteQueryContext,
|
|
3882
3882
|
SQLiteAdapter2 as SQLiteAdapter
|
|
3883
3883
|
};
|
|
3884
|
-
//# sourceMappingURL=chunk-
|
|
3884
|
+
//# sourceMappingURL=chunk-66PSQW4D.js.map
|