cry-synced-db-client 0.1.200 → 0.1.202
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 +20 -0
- package/dist/index.js +302 -87
- package/dist/src/types/I_SyncedDb.d.ts +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 0.1.201 (2026-06-14)
|
|
4
|
+
|
|
5
|
+
### `onBeforeDirtyClearAll` callback zdaj prejme `DirtyChange[]` (ne samo `DirtyMeta[]`)
|
|
6
|
+
|
|
7
|
+
`BeforeDirtyClearAllInfo.items` je nadgrajen iz `DirtyMeta[]` v `DirtyChange[]` —
|
|
8
|
+
callback dobi poleg metapodatkov (`id`, `collection`, `stuckSince`, ...) tudi
|
|
9
|
+
`changes` payload z dejanskimi field-level spremembami.
|
|
10
|
+
|
|
11
|
+
**Affected paths:**
|
|
12
|
+
- `clearDirty()` clear-all path: fetcha `DirtyChange[]` prek `getDirtyChangesBatch`
|
|
13
|
+
- `discardStuckItems()`: zamenja `getDirty` + `find` (ki je vračal
|
|
14
|
+
`Partial<LocalDbEntity>[]`) z `getDirtyChangesBatch` za pravilne `DirtyChange[]`
|
|
15
|
+
|
|
16
|
+
**Type change (backward-compatible):**
|
|
17
|
+
- `items` v `BeforeDirtyClearAllInfo`: `DirtyMeta[]` → `DirtyChange[]`
|
|
18
|
+
- `DirtyChange` je superset of `DirtyMeta`; vsa obstoječa polja (`id`,
|
|
19
|
+
`collection`, `createdAt`, `updatedAt`, `stuckSince`, ...) so še vedno na voljo
|
|
20
|
+
|
|
21
|
+
876 pass, 0 fail.
|
|
22
|
+
|
|
3
23
|
## 0.1.200 (2026-06-14)
|
|
4
24
|
|
|
5
25
|
### `preprocessDirtyItem` skip/throw zdaj incrementa `numUploadAttempts`
|
package/dist/index.js
CHANGED
|
@@ -4902,7 +4902,8 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4902
4902
|
this.evictOnWake = (_g = config.evictOnWake) != null ? _g : false;
|
|
4903
4903
|
for (const col of config.collections) {
|
|
4904
4904
|
this.collections.set(col.name, col);
|
|
4905
|
-
if (!col.writeOnly)
|
|
4905
|
+
if (!col.writeOnly)
|
|
4906
|
+
this.preloadStatusMap.set(col.name, { state: "pending", itemCount: 0 });
|
|
4906
4907
|
}
|
|
4907
4908
|
this.inMemManager = new InMemManager({
|
|
4908
4909
|
inMemDb: this.inMemDb,
|
|
@@ -4916,14 +4917,20 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4916
4917
|
onBecameLeader: () => {
|
|
4917
4918
|
if (this.initialized && !this.connectionManager.isOnline() && !this.connectionManager.isForcedOffline()) {
|
|
4918
4919
|
this.connectionManager.tryGoOnline().catch((err) => {
|
|
4919
|
-
networkError(
|
|
4920
|
+
networkError(
|
|
4921
|
+
`[SyncedDb] tryGoOnline on becameLeader failed: ${err}`,
|
|
4922
|
+
err
|
|
4923
|
+
);
|
|
4920
4924
|
});
|
|
4921
4925
|
}
|
|
4922
4926
|
if (config.onBecameLeader) {
|
|
4923
4927
|
try {
|
|
4924
4928
|
config.onBecameLeader();
|
|
4925
4929
|
} catch (err) {
|
|
4926
|
-
console.error(
|
|
4930
|
+
console.error(
|
|
4931
|
+
`[SyncedDb] onBecameLeader callback failed: ${err}`,
|
|
4932
|
+
err
|
|
4933
|
+
);
|
|
4927
4934
|
}
|
|
4928
4935
|
}
|
|
4929
4936
|
},
|
|
@@ -5026,7 +5033,9 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5026
5033
|
restInterface: this.restInterface,
|
|
5027
5034
|
preprocessDirtyItem: config.preprocessDirtyItem,
|
|
5028
5035
|
callbacks: {
|
|
5029
|
-
onSyncStart: config.onSyncStart ? (info) => config.onSyncStart(__spreadProps(__spreadValues({}, info), {
|
|
5036
|
+
onSyncStart: config.onSyncStart ? (info) => config.onSyncStart(__spreadProps(__spreadValues({}, info), {
|
|
5037
|
+
initialSync: !this._lastFullSyncDate
|
|
5038
|
+
})) : void 0,
|
|
5030
5039
|
onSyncEnd: config.onSyncEnd,
|
|
5031
5040
|
onSyncProgress: config.onSyncProgress,
|
|
5032
5041
|
onServerSyncStart: config.onServerSyncStart,
|
|
@@ -5168,7 +5177,12 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5168
5177
|
"[SyncedDb] callWorker: no serverUpdateNotifier or notifier does not support callWorker"
|
|
5169
5178
|
);
|
|
5170
5179
|
}
|
|
5171
|
-
return fn.call(
|
|
5180
|
+
return fn.call(
|
|
5181
|
+
this.serverUpdateNotifier,
|
|
5182
|
+
service,
|
|
5183
|
+
payload,
|
|
5184
|
+
options
|
|
5185
|
+
);
|
|
5172
5186
|
}
|
|
5173
5187
|
/**
|
|
5174
5188
|
* Register a collection for sync at runtime. See `I_SyncedDb.addCollectionToSync`.
|
|
@@ -5205,7 +5219,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5205
5219
|
if (existing && !existing.temporaryConfig) continue;
|
|
5206
5220
|
this.collections.set(spec.name, spec);
|
|
5207
5221
|
if (!spec.writeOnly && !this.preloadStatusMap.has(spec.name)) {
|
|
5208
|
-
this.preloadStatusMap.set(spec.name, {
|
|
5222
|
+
this.preloadStatusMap.set(spec.name, {
|
|
5223
|
+
state: "pending",
|
|
5224
|
+
itemCount: 0
|
|
5225
|
+
});
|
|
5209
5226
|
}
|
|
5210
5227
|
if (this.syncOnlyCollections) {
|
|
5211
5228
|
this.syncOnlyCollections.add(spec.name);
|
|
@@ -5267,7 +5284,11 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5267
5284
|
if (!data || data.length === 0) continue;
|
|
5268
5285
|
const source = plan.wasFirstTime ? "initial" : "incremental";
|
|
5269
5286
|
try {
|
|
5270
|
-
await this.syncEngine.processCollectionServerData(
|
|
5287
|
+
await this.syncEngine.processCollectionServerData(
|
|
5288
|
+
plan.spec.name,
|
|
5289
|
+
data,
|
|
5290
|
+
{ source }
|
|
5291
|
+
);
|
|
5271
5292
|
} catch (err) {
|
|
5272
5293
|
console.error(
|
|
5273
5294
|
`[SyncedDb] addCollectionsToSync: processCollectionServerData failed for "${plan.spec.name}":`,
|
|
@@ -5306,7 +5327,9 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5306
5327
|
if (!spec.writeOnly && this.connectionManager.canSync()) {
|
|
5307
5328
|
const rawQuery = (_a = spec.syncConfig) == null ? void 0 : _a.query;
|
|
5308
5329
|
const query = typeof rawQuery === "function" ? rawQuery() : rawQuery;
|
|
5309
|
-
await this.syncCollectionForFind(spec.name, query, {
|
|
5330
|
+
await this.syncCollectionForFind(spec.name, query, {
|
|
5331
|
+
returnDeleted: true
|
|
5332
|
+
});
|
|
5310
5333
|
}
|
|
5311
5334
|
}
|
|
5312
5335
|
/**
|
|
@@ -5332,7 +5355,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5332
5355
|
}
|
|
5333
5356
|
this.emitPreloadStatusChange();
|
|
5334
5357
|
if (newlyAllowed.length > 0) {
|
|
5335
|
-
await this.loadCollectionsToInMem(
|
|
5358
|
+
await this.loadCollectionsToInMem(
|
|
5359
|
+
newlyAllowed,
|
|
5360
|
+
"setSyncOnlyTheseCollections"
|
|
5361
|
+
);
|
|
5336
5362
|
}
|
|
5337
5363
|
if (newlyAllowed.length > 0 && this.connectionManager.canSync()) {
|
|
5338
5364
|
this.sync("setSyncOnlyTheseCollections").catch(() => {
|
|
@@ -5374,12 +5400,17 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5374
5400
|
try {
|
|
5375
5401
|
this.onDatabaseCreated();
|
|
5376
5402
|
} catch (err) {
|
|
5377
|
-
console.error(
|
|
5403
|
+
console.error(
|
|
5404
|
+
`[SyncedDb] onDatabaseCreated callback failed: ${err}`,
|
|
5405
|
+
err
|
|
5406
|
+
);
|
|
5378
5407
|
}
|
|
5379
5408
|
}
|
|
5380
5409
|
await this.pendingChanges.recoverPendingWrites();
|
|
5381
5410
|
await this.preloadAllSyncMetas();
|
|
5382
|
-
const allowedColls = [...this.collections.keys()].filter(
|
|
5411
|
+
const allowedColls = [...this.collections.keys()].filter(
|
|
5412
|
+
(n) => this.isSyncAllowed(n)
|
|
5413
|
+
);
|
|
5383
5414
|
await this.loadCollectionsToInMem(allowedColls, "init");
|
|
5384
5415
|
this.leaderElection.init();
|
|
5385
5416
|
this.crossTabSync.init();
|
|
@@ -5410,7 +5441,9 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5410
5441
|
console.log(`[SyncedDb] SyncedDb: ebus-proxy connected to ${ep}`);
|
|
5411
5442
|
} catch (err) {
|
|
5412
5443
|
const ep = (_d = this.serverUpdateNotifier.endpoint) != null ? _d : "unknown";
|
|
5413
|
-
console.warn(
|
|
5444
|
+
console.warn(
|
|
5445
|
+
`[SyncedDb] SyncedDb: ebus-proxy connection to ${ep} failed`
|
|
5446
|
+
);
|
|
5414
5447
|
this.connectionManager.reportInfrastructureError(
|
|
5415
5448
|
"WEBSOCKET_CONNECTION_FAILED",
|
|
5416
5449
|
`WebSocket connection to ${ep} failed during initialization`,
|
|
@@ -5432,10 +5465,16 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5432
5465
|
this.visibilityFlushHandler = () => {
|
|
5433
5466
|
if (document.visibilityState !== "hidden") return;
|
|
5434
5467
|
this.flushToServer("visibility-hidden").catch((err) => {
|
|
5435
|
-
console.warn(
|
|
5468
|
+
console.warn(
|
|
5469
|
+
`[SyncedDb] flushToServer on visibility-hidden failed: ${err == null ? void 0 : err.message}`,
|
|
5470
|
+
err
|
|
5471
|
+
);
|
|
5436
5472
|
});
|
|
5437
5473
|
};
|
|
5438
|
-
document.addEventListener(
|
|
5474
|
+
document.addEventListener(
|
|
5475
|
+
"visibilitychange",
|
|
5476
|
+
this.visibilityFlushHandler
|
|
5477
|
+
);
|
|
5439
5478
|
}
|
|
5440
5479
|
this._checkForceFullResyncThreshold("init");
|
|
5441
5480
|
this._startForceResyncCheckTimer();
|
|
@@ -5548,7 +5587,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5548
5587
|
});
|
|
5549
5588
|
if (this.connectionManager.canSync()) {
|
|
5550
5589
|
this.sync(`force-full-resync:${reason}`).catch((err) => {
|
|
5551
|
-
console.error(
|
|
5590
|
+
console.error(
|
|
5591
|
+
`[SyncedDb] force full resync sync() failed: ${err}`,
|
|
5592
|
+
err
|
|
5593
|
+
);
|
|
5552
5594
|
});
|
|
5553
5595
|
}
|
|
5554
5596
|
}
|
|
@@ -5622,7 +5664,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5622
5664
|
this.beforeUnloadHandler = void 0;
|
|
5623
5665
|
}
|
|
5624
5666
|
if (typeof document !== "undefined" && this.visibilityFlushHandler) {
|
|
5625
|
-
document.removeEventListener(
|
|
5667
|
+
document.removeEventListener(
|
|
5668
|
+
"visibilitychange",
|
|
5669
|
+
this.visibilityFlushHandler
|
|
5670
|
+
);
|
|
5626
5671
|
this.visibilityFlushHandler = void 0;
|
|
5627
5672
|
}
|
|
5628
5673
|
this.syncMetaCache.clear();
|
|
@@ -5702,7 +5747,12 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5702
5747
|
if (serverItem) {
|
|
5703
5748
|
await this.dexieDb.saveMany(collection, [serverItem]);
|
|
5704
5749
|
if (!serverItem._deleted && !serverItem._archived) {
|
|
5705
|
-
this.inMemManager.writeBatch(
|
|
5750
|
+
this.inMemManager.writeBatch(
|
|
5751
|
+
collection,
|
|
5752
|
+
[serverItem],
|
|
5753
|
+
"upsert",
|
|
5754
|
+
{ source: "incremental" }
|
|
5755
|
+
);
|
|
5706
5756
|
}
|
|
5707
5757
|
}
|
|
5708
5758
|
} catch (e) {
|
|
@@ -5775,9 +5825,16 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5775
5825
|
);
|
|
5776
5826
|
if (serverItems && serverItems.length > 0) {
|
|
5777
5827
|
await this.dexieDb.saveMany(collection, serverItems);
|
|
5778
|
-
const toInMem = serverItems.filter(
|
|
5828
|
+
const toInMem = serverItems.filter(
|
|
5829
|
+
(s) => !s._deleted && !s._archived
|
|
5830
|
+
);
|
|
5779
5831
|
if (toInMem.length > 0) {
|
|
5780
|
-
this.inMemManager.writeBatch(
|
|
5832
|
+
this.inMemManager.writeBatch(
|
|
5833
|
+
collection,
|
|
5834
|
+
toInMem,
|
|
5835
|
+
"upsert",
|
|
5836
|
+
{ source: "incremental" }
|
|
5837
|
+
);
|
|
5781
5838
|
}
|
|
5782
5839
|
}
|
|
5783
5840
|
} catch (e) {
|
|
@@ -5818,11 +5875,16 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5818
5875
|
filtered.push(item);
|
|
5819
5876
|
}
|
|
5820
5877
|
if (filtered.length === 0) {
|
|
5821
|
-
if ((opts == null ? void 0 : opts.referToServer) && this.isOnline())
|
|
5878
|
+
if ((opts == null ? void 0 : opts.referToServer) && this.isOnline())
|
|
5879
|
+
this.referToServerSync(collection, query);
|
|
5822
5880
|
return null;
|
|
5823
5881
|
}
|
|
5824
|
-
const sorted = applyQueryOpts(filtered, {
|
|
5825
|
-
|
|
5882
|
+
const sorted = applyQueryOpts(filtered, {
|
|
5883
|
+
sort: opts.sort,
|
|
5884
|
+
project: opts == null ? void 0 : opts.project
|
|
5885
|
+
});
|
|
5886
|
+
if ((opts == null ? void 0 : opts.referToServer) && this.isOnline())
|
|
5887
|
+
this.referToServerSync(collection, query);
|
|
5826
5888
|
return (_b = sorted[0]) != null ? _b : null;
|
|
5827
5889
|
} else {
|
|
5828
5890
|
let result = null;
|
|
@@ -5836,7 +5898,8 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5836
5898
|
if (result && (opts == null ? void 0 : opts.project)) {
|
|
5837
5899
|
result = projectItem(result, opts.project);
|
|
5838
5900
|
}
|
|
5839
|
-
if ((opts == null ? void 0 : opts.referToServer) && this.isOnline())
|
|
5901
|
+
if ((opts == null ? void 0 : opts.referToServer) && this.isOnline())
|
|
5902
|
+
this.referToServerSync(collection, query);
|
|
5840
5903
|
return result;
|
|
5841
5904
|
}
|
|
5842
5905
|
}
|
|
@@ -5889,15 +5952,24 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5889
5952
|
const timestamp = (meta == null ? void 0 : meta.lastSyncTs) || 0;
|
|
5890
5953
|
try {
|
|
5891
5954
|
const serverData = await this.connectionManager.withRestTimeout(
|
|
5892
|
-
this.restInterface.findNewer(
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5955
|
+
this.restInterface.findNewer(
|
|
5956
|
+
collection,
|
|
5957
|
+
timestamp,
|
|
5958
|
+
query,
|
|
5959
|
+
{
|
|
5960
|
+
returnDeleted: (opts == null ? void 0 : opts.returnDeleted) || false,
|
|
5961
|
+
returnArchived: (opts == null ? void 0 : opts.returnArchived) || false
|
|
5962
|
+
}
|
|
5963
|
+
),
|
|
5896
5964
|
"syncCollectionForFind"
|
|
5897
5965
|
);
|
|
5898
5966
|
if (serverData.length > 0) {
|
|
5899
5967
|
const source = timestamp === 0 ? "initial" : "incremental";
|
|
5900
|
-
await this.syncEngine.processCollectionServerData(
|
|
5968
|
+
await this.syncEngine.processCollectionServerData(
|
|
5969
|
+
collection,
|
|
5970
|
+
serverData,
|
|
5971
|
+
{ source }
|
|
5972
|
+
);
|
|
5901
5973
|
}
|
|
5902
5974
|
} catch (e) {
|
|
5903
5975
|
}
|
|
@@ -5911,11 +5983,17 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5911
5983
|
*/
|
|
5912
5984
|
referToServerSync(collection, query) {
|
|
5913
5985
|
this.connectionManager.withRestTimeout(
|
|
5914
|
-
this.restInterface.findNewer(collection, 0, query, {
|
|
5986
|
+
this.restInterface.findNewer(collection, 0, query, {
|
|
5987
|
+
returnDeleted: true
|
|
5988
|
+
}),
|
|
5915
5989
|
"referToServer"
|
|
5916
5990
|
).then(async (serverData) => {
|
|
5917
5991
|
if (serverData.length > 0) {
|
|
5918
|
-
await this.syncEngine.processCollectionServerData(
|
|
5992
|
+
await this.syncEngine.processCollectionServerData(
|
|
5993
|
+
collection,
|
|
5994
|
+
serverData,
|
|
5995
|
+
{ source: "refresh" }
|
|
5996
|
+
);
|
|
5919
5997
|
}
|
|
5920
5998
|
}).catch((err) => {
|
|
5921
5999
|
networkError(`[SyncedDb] referToServer failed for ${collection}:`, err);
|
|
@@ -5943,7 +6021,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5943
6021
|
for (const item of serverItems) {
|
|
5944
6022
|
serverById.set(String(item._id), item);
|
|
5945
6023
|
}
|
|
5946
|
-
const localItems = await this.dexieDb.getByIds(
|
|
6024
|
+
const localItems = await this.dexieDb.getByIds(
|
|
6025
|
+
collection,
|
|
6026
|
+
ids
|
|
6027
|
+
);
|
|
5947
6028
|
const dirtyMap = await this.dexieDb.getDirtyChangesBatch(collection, ids);
|
|
5948
6029
|
const toSaveDexie = [];
|
|
5949
6030
|
const toSaveInMem = [];
|
|
@@ -5971,10 +6052,16 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5971
6052
|
await this.dexieDb.saveMany(collection, toSaveDexie);
|
|
5972
6053
|
}
|
|
5973
6054
|
if (toSaveInMem.length > 0) {
|
|
5974
|
-
this.inMemManager.writeBatch(collection, toSaveInMem, "upsert", {
|
|
6055
|
+
this.inMemManager.writeBatch(collection, toSaveInMem, "upsert", {
|
|
6056
|
+
source: "incremental"
|
|
6057
|
+
});
|
|
5975
6058
|
}
|
|
5976
6059
|
if (dirtyServerItems.length > 0) {
|
|
5977
|
-
await this.syncEngine.processCollectionServerData(
|
|
6060
|
+
await this.syncEngine.processCollectionServerData(
|
|
6061
|
+
collection,
|
|
6062
|
+
dirtyServerItems,
|
|
6063
|
+
{ source: "incremental" }
|
|
6064
|
+
);
|
|
5978
6065
|
}
|
|
5979
6066
|
}
|
|
5980
6067
|
/**
|
|
@@ -6002,9 +6089,16 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6002
6089
|
"refreshInBackground"
|
|
6003
6090
|
).then(async (serverItems) => {
|
|
6004
6091
|
if (!serverItems || serverItems.length === 0) return;
|
|
6005
|
-
await this.syncEngine.processCollectionServerData(
|
|
6092
|
+
await this.syncEngine.processCollectionServerData(
|
|
6093
|
+
collection,
|
|
6094
|
+
serverItems,
|
|
6095
|
+
{ source: "incremental" }
|
|
6096
|
+
);
|
|
6006
6097
|
}).catch((err) => {
|
|
6007
|
-
networkError(
|
|
6098
|
+
networkError(
|
|
6099
|
+
`[SyncedDb] refreshInBackground failed for ${collection}:`,
|
|
6100
|
+
err
|
|
6101
|
+
);
|
|
6008
6102
|
});
|
|
6009
6103
|
}
|
|
6010
6104
|
async ensureItemsAreLoaded(collection, ids, withDeleted) {
|
|
@@ -6032,7 +6126,9 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6032
6126
|
await this.dexieDb.saveMany(collection, toSaveDexie);
|
|
6033
6127
|
}
|
|
6034
6128
|
if (toSaveInMem.length > 0) {
|
|
6035
|
-
this.inMemManager.writeBatch(collection, toSaveInMem, "upsert", {
|
|
6129
|
+
this.inMemManager.writeBatch(collection, toSaveInMem, "upsert", {
|
|
6130
|
+
source: "incremental"
|
|
6131
|
+
});
|
|
6036
6132
|
}
|
|
6037
6133
|
}
|
|
6038
6134
|
// ==================== Write Operations ====================
|
|
@@ -6077,12 +6173,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6077
6173
|
}
|
|
6078
6174
|
const fullChanges = __spreadProps(__spreadValues({}, update), { _lastUpdaterId: this.updaterId });
|
|
6079
6175
|
const diff = computeObjDiff(existing, fullChanges);
|
|
6080
|
-
await this.dexieDb.addDirtyChange(
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
{ _ts: existing == null ? void 0 : existing._ts, _rev: existing == null ? void 0 : existing._rev }
|
|
6085
|
-
);
|
|
6176
|
+
await this.dexieDb.addDirtyChange(collection, id, diff, {
|
|
6177
|
+
_ts: existing == null ? void 0 : existing._ts,
|
|
6178
|
+
_rev: existing == null ? void 0 : existing._rev
|
|
6179
|
+
});
|
|
6086
6180
|
const isWriteOnly = (_b = this.collections.get(collection)) == null ? void 0 : _b.writeOnly;
|
|
6087
6181
|
const currentMem = isWriteOnly ? null : this.inMemDb.getById(collection, id);
|
|
6088
6182
|
const merged = applyObjDiff(
|
|
@@ -6091,9 +6185,17 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6091
6185
|
id,
|
|
6092
6186
|
collection
|
|
6093
6187
|
);
|
|
6094
|
-
this.pendingChanges.schedule(
|
|
6188
|
+
this.pendingChanges.schedule(
|
|
6189
|
+
collection,
|
|
6190
|
+
id,
|
|
6191
|
+
merged,
|
|
6192
|
+
0,
|
|
6193
|
+
"save"
|
|
6194
|
+
);
|
|
6095
6195
|
if (!isWriteOnly && !(existing == null ? void 0 : existing._deleted) && !(existing == null ? void 0 : existing._archived)) {
|
|
6096
|
-
this.inMemManager.writeBatch(collection, [merged], "upsert", {
|
|
6196
|
+
this.inMemManager.writeBatch(collection, [merged], "upsert", {
|
|
6197
|
+
source: "incremental"
|
|
6198
|
+
});
|
|
6097
6199
|
}
|
|
6098
6200
|
return merged;
|
|
6099
6201
|
}
|
|
@@ -6138,7 +6240,12 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6138
6240
|
for (const path of unsetPaths) deleteByPath(newData, path);
|
|
6139
6241
|
this.pendingChanges.schedule(collection, id, newData, 0, "insert");
|
|
6140
6242
|
if (!((_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly)) {
|
|
6141
|
-
this.inMemManager.writeBatch(
|
|
6243
|
+
this.inMemManager.writeBatch(
|
|
6244
|
+
collection,
|
|
6245
|
+
[newData],
|
|
6246
|
+
"upsert",
|
|
6247
|
+
{ source: "incremental" }
|
|
6248
|
+
);
|
|
6142
6249
|
}
|
|
6143
6250
|
return newData;
|
|
6144
6251
|
}
|
|
@@ -6162,7 +6269,12 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6162
6269
|
};
|
|
6163
6270
|
this.pendingChanges.schedule(collection, id, deleteUpdate, 0, "deleteOne");
|
|
6164
6271
|
if (!((_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly)) {
|
|
6165
|
-
this.inMemManager.writeBatch(
|
|
6272
|
+
this.inMemManager.writeBatch(
|
|
6273
|
+
collection,
|
|
6274
|
+
[{ _id: id }],
|
|
6275
|
+
"delete",
|
|
6276
|
+
{ source: "incremental" }
|
|
6277
|
+
);
|
|
6166
6278
|
}
|
|
6167
6279
|
return existing;
|
|
6168
6280
|
}
|
|
@@ -6172,7 +6284,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6172
6284
|
const items = await this.find(collection, query);
|
|
6173
6285
|
if (items.length === 0) return 0;
|
|
6174
6286
|
const ids = items.map((item) => item._id);
|
|
6175
|
-
const existingItems = await this.dexieDb.getByIds(
|
|
6287
|
+
const existingItems = await this.dexieDb.getByIds(
|
|
6288
|
+
collection,
|
|
6289
|
+
ids
|
|
6290
|
+
);
|
|
6176
6291
|
const now = /* @__PURE__ */ new Date();
|
|
6177
6292
|
const dirtyChangesBatch = [];
|
|
6178
6293
|
const idsToDelete = [];
|
|
@@ -6189,7 +6304,13 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6189
6304
|
_deleted: now,
|
|
6190
6305
|
_lastUpdaterId: this.updaterId
|
|
6191
6306
|
};
|
|
6192
|
-
this.pendingChanges.schedule(
|
|
6307
|
+
this.pendingChanges.schedule(
|
|
6308
|
+
collection,
|
|
6309
|
+
item._id,
|
|
6310
|
+
deleteUpdate,
|
|
6311
|
+
0,
|
|
6312
|
+
"deleteMany"
|
|
6313
|
+
);
|
|
6193
6314
|
idsToDelete.push(item._id);
|
|
6194
6315
|
}
|
|
6195
6316
|
if (dirtyChangesBatch.length > 0) {
|
|
@@ -6217,11 +6338,19 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6217
6338
|
return null;
|
|
6218
6339
|
}
|
|
6219
6340
|
await this.connectionManager.withRestTimeout(
|
|
6220
|
-
this.restInterface.call("hardDeleteOne", {
|
|
6341
|
+
this.restInterface.call("hardDeleteOne", {
|
|
6342
|
+
collection,
|
|
6343
|
+
query: { _id: id }
|
|
6344
|
+
}),
|
|
6221
6345
|
"hardDeleteOne"
|
|
6222
6346
|
);
|
|
6223
6347
|
await this.dexieDb.deleteOne(collection, id);
|
|
6224
|
-
this.inMemManager.writeBatch(
|
|
6348
|
+
this.inMemManager.writeBatch(
|
|
6349
|
+
collection,
|
|
6350
|
+
[{ _id: id }],
|
|
6351
|
+
"delete",
|
|
6352
|
+
{ source: "incremental" }
|
|
6353
|
+
);
|
|
6225
6354
|
return existing;
|
|
6226
6355
|
}
|
|
6227
6356
|
async hardDelete(collection, query) {
|
|
@@ -6233,7 +6362,9 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6233
6362
|
const items = await this.find(collection, query);
|
|
6234
6363
|
if (items.length === 0) return 0;
|
|
6235
6364
|
const existingItems = await Promise.all(
|
|
6236
|
-
items.map(
|
|
6365
|
+
items.map(
|
|
6366
|
+
(item) => this.dexieDb.getById(collection, item._id)
|
|
6367
|
+
)
|
|
6237
6368
|
);
|
|
6238
6369
|
const toDelete = [];
|
|
6239
6370
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -6248,24 +6379,38 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6248
6379
|
const queue = [...toDelete];
|
|
6249
6380
|
const results = [];
|
|
6250
6381
|
await Promise.all(
|
|
6251
|
-
Array.from(
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6382
|
+
Array.from(
|
|
6383
|
+
{ length: Math.min(maxConcurrency, queue.length) },
|
|
6384
|
+
async () => {
|
|
6385
|
+
while (queue.length > 0) {
|
|
6386
|
+
const item = queue.pop();
|
|
6387
|
+
if (!item) break;
|
|
6388
|
+
try {
|
|
6389
|
+
await this.connectionManager.withRestTimeout(
|
|
6390
|
+
this.restInterface.call("hardDeleteOne", {
|
|
6391
|
+
collection,
|
|
6392
|
+
query: { _id: item.id }
|
|
6393
|
+
}),
|
|
6394
|
+
"hardDelete"
|
|
6395
|
+
);
|
|
6396
|
+
await this.dexieDb.deleteOne(collection, item.id);
|
|
6397
|
+
this.inMemManager.writeBatch(
|
|
6398
|
+
collection,
|
|
6399
|
+
[{ _id: item.id }],
|
|
6400
|
+
"delete",
|
|
6401
|
+
{ source: "incremental" }
|
|
6402
|
+
);
|
|
6403
|
+
results.push(true);
|
|
6404
|
+
} catch (err) {
|
|
6405
|
+
networkError(
|
|
6406
|
+
`[SyncedDb] Failed to hard delete ${String(item.id)}:`,
|
|
6407
|
+
err
|
|
6408
|
+
);
|
|
6409
|
+
results.push(false);
|
|
6410
|
+
}
|
|
6266
6411
|
}
|
|
6267
6412
|
}
|
|
6268
|
-
|
|
6413
|
+
)
|
|
6269
6414
|
);
|
|
6270
6415
|
return results.filter(Boolean).length;
|
|
6271
6416
|
}
|
|
@@ -6317,11 +6462,17 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6317
6462
|
const now = /* @__PURE__ */ new Date();
|
|
6318
6463
|
if (!this._lastFullSyncDate) {
|
|
6319
6464
|
this._setLastInitialSync(now).catch((err) => {
|
|
6320
|
-
console.error(
|
|
6465
|
+
console.error(
|
|
6466
|
+
`[SyncedDb] Failed to persist lastInitialSync: ${err}`,
|
|
6467
|
+
err
|
|
6468
|
+
);
|
|
6321
6469
|
});
|
|
6322
6470
|
}
|
|
6323
6471
|
this._setLastFullSync(now).catch((err) => {
|
|
6324
|
-
console.error(
|
|
6472
|
+
console.error(
|
|
6473
|
+
`[SyncedDb] Failed to persist lastFullSync: ${err}`,
|
|
6474
|
+
err
|
|
6475
|
+
);
|
|
6325
6476
|
});
|
|
6326
6477
|
if (consumingPendingFullResync) this._pendingFullResync = false;
|
|
6327
6478
|
}
|
|
@@ -6475,10 +6626,18 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6475
6626
|
for (const [name] of this.collections) {
|
|
6476
6627
|
const metas2 = await this.dexieDb.getDirtyMeta(name);
|
|
6477
6628
|
if (metas2.length === 0) continue;
|
|
6629
|
+
const ids2 = metas2.map((m) => m.id);
|
|
6630
|
+
const changesMap = await this.dexieDb.getDirtyChangesBatch(name, ids2);
|
|
6631
|
+
const items = metas2.map(
|
|
6632
|
+
(m) => {
|
|
6633
|
+
var _a;
|
|
6634
|
+
return (_a = changesMap.get(m.id)) != null ? _a : __spreadProps(__spreadValues({}, m), { changes: {} });
|
|
6635
|
+
}
|
|
6636
|
+
);
|
|
6478
6637
|
this.safeCallback(this.onBeforeDirtyClearAll, {
|
|
6479
6638
|
reason: calledFrom != null ? calledFrom : "manual",
|
|
6480
6639
|
collection: name,
|
|
6481
|
-
items
|
|
6640
|
+
items,
|
|
6482
6641
|
calledFrom,
|
|
6483
6642
|
timestamp: /* @__PURE__ */ new Date()
|
|
6484
6643
|
});
|
|
@@ -6541,10 +6700,34 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6541
6700
|
const metas = await this.dexieDb.getDirtyMeta(collectionName);
|
|
6542
6701
|
const stuck = metas.filter((m) => m.stuckSince !== void 0);
|
|
6543
6702
|
if (stuck.length === 0) continue;
|
|
6703
|
+
const allDirty = await this.dexieDb.getDirty(collectionName);
|
|
6704
|
+
const content = stuck.map((m) => {
|
|
6705
|
+
const dirtyId = String(m.id);
|
|
6706
|
+
const found = allDirty.find(
|
|
6707
|
+
(d) => String(d._id) === dirtyId
|
|
6708
|
+
);
|
|
6709
|
+
if (found) {
|
|
6710
|
+
const _a = found, { _id, _ts, _rev } = _a, changedFields = __objRest(_a, ["_id", "_ts", "_rev"]);
|
|
6711
|
+
return {
|
|
6712
|
+
collection: collectionName,
|
|
6713
|
+
id: dirtyId,
|
|
6714
|
+
changes: changedFields,
|
|
6715
|
+
baseTs: _ts,
|
|
6716
|
+
baseRev: _rev,
|
|
6717
|
+
createdAt: m.createdAt,
|
|
6718
|
+
updatedAt: m.updatedAt,
|
|
6719
|
+
firstUploadAttempt: m.firstUploadAttempt,
|
|
6720
|
+
lastUploadAttempt: m.lastUploadAttempt,
|
|
6721
|
+
numUploadAttempts: m.numUploadAttempts,
|
|
6722
|
+
stuckSince: m.stuckSince
|
|
6723
|
+
};
|
|
6724
|
+
}
|
|
6725
|
+
return __spreadProps(__spreadValues({}, m), { changes: {} });
|
|
6726
|
+
});
|
|
6544
6727
|
this.safeCallback(this.onBeforeDirtyClearAll, {
|
|
6545
6728
|
reason: "discard-stuck",
|
|
6546
6729
|
collection: collectionName,
|
|
6547
|
-
items:
|
|
6730
|
+
items: content,
|
|
6548
6731
|
calledFrom,
|
|
6549
6732
|
timestamp: /* @__PURE__ */ new Date()
|
|
6550
6733
|
});
|
|
@@ -6590,7 +6773,9 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6590
6773
|
}
|
|
6591
6774
|
async dropDatabase(force = false) {
|
|
6592
6775
|
if (!force && (this.connectionManager.isForcedOffline() || !this.connectionManager.isOnline())) {
|
|
6593
|
-
throw new Error(
|
|
6776
|
+
throw new Error(
|
|
6777
|
+
"Cannot drop database: database is offline. Use force=true to drop anyway."
|
|
6778
|
+
);
|
|
6594
6779
|
}
|
|
6595
6780
|
await this.pendingChanges.flushAll();
|
|
6596
6781
|
const collectionNames = Array.from(this.collections.keys());
|
|
@@ -6598,7 +6783,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6598
6783
|
for (const collectionName of collectionNames) {
|
|
6599
6784
|
const dirtyItems = await this.dexieDb.getDirty(collectionName);
|
|
6600
6785
|
if (dirtyItems.length > 0) {
|
|
6601
|
-
dirtyCollections.push({
|
|
6786
|
+
dirtyCollections.push({
|
|
6787
|
+
name: collectionName,
|
|
6788
|
+
count: dirtyItems.length
|
|
6789
|
+
});
|
|
6602
6790
|
}
|
|
6603
6791
|
}
|
|
6604
6792
|
if (dirtyCollections.length > 0) {
|
|
@@ -6876,7 +7064,11 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6876
7064
|
);
|
|
6877
7065
|
serverRounds = 1;
|
|
6878
7066
|
for (const [specId, items] of Object.entries(results)) {
|
|
6879
|
-
this._applyScopeExitChunkToPlan(
|
|
7067
|
+
this._applyScopeExitChunkToPlan(
|
|
7068
|
+
plan,
|
|
7069
|
+
specId,
|
|
7070
|
+
items
|
|
7071
|
+
);
|
|
6880
7072
|
}
|
|
6881
7073
|
} catch (err) {
|
|
6882
7074
|
serverFailed = true;
|
|
@@ -7179,7 +7371,8 @@ var _SyncedDb = class _SyncedDb {
|
|
|
7179
7371
|
const config = this.collections.get(collection);
|
|
7180
7372
|
if (!config) return false;
|
|
7181
7373
|
if (config.writeOnly) return false;
|
|
7182
|
-
if (this.syncOnlyCollections && !this.syncOnlyCollections.has(collection))
|
|
7374
|
+
if (this.syncOnlyCollections && !this.syncOnlyCollections.has(collection))
|
|
7375
|
+
return false;
|
|
7183
7376
|
return true;
|
|
7184
7377
|
}
|
|
7185
7378
|
/**
|
|
@@ -7254,7 +7447,11 @@ var _SyncedDb = class _SyncedDb {
|
|
|
7254
7447
|
var _a;
|
|
7255
7448
|
try {
|
|
7256
7449
|
const count = await this._hydrateCollectionFromDexie(name);
|
|
7257
|
-
this.preloadStatusMap.set(name, {
|
|
7450
|
+
this.preloadStatusMap.set(name, {
|
|
7451
|
+
state: "hydrated",
|
|
7452
|
+
itemCount: count,
|
|
7453
|
+
hydratedAt: /* @__PURE__ */ new Date()
|
|
7454
|
+
});
|
|
7258
7455
|
this.emitPreloadStatusChange();
|
|
7259
7456
|
return count;
|
|
7260
7457
|
} catch (err) {
|
|
@@ -7281,7 +7478,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
7281
7478
|
let pendingCount = 0;
|
|
7282
7479
|
for (const name of this.collections.keys()) {
|
|
7283
7480
|
if (!this.isSyncAllowed(name)) continue;
|
|
7284
|
-
const rec = (_a = this.preloadStatusMap.get(name)) != null ? _a : {
|
|
7481
|
+
const rec = (_a = this.preloadStatusMap.get(name)) != null ? _a : {
|
|
7482
|
+
state: "pending",
|
|
7483
|
+
itemCount: 0
|
|
7484
|
+
};
|
|
7285
7485
|
const everDownloaded = !!((_b = this.syncMetaCache.get(name)) == null ? void 0 : _b.lastSyncTs);
|
|
7286
7486
|
const ready = rec.state === "hydrated";
|
|
7287
7487
|
if (rec.state === "failed") failedCount++;
|
|
@@ -7303,22 +7503,34 @@ var _SyncedDb = class _SyncedDb {
|
|
|
7303
7503
|
else if (readyCount === expectedCount) aggregate = "full";
|
|
7304
7504
|
else if (failedCount === expectedCount) aggregate = "failed";
|
|
7305
7505
|
else aggregate = "partial";
|
|
7306
|
-
return {
|
|
7506
|
+
return {
|
|
7507
|
+
aggregate,
|
|
7508
|
+
collections,
|
|
7509
|
+
expectedCount,
|
|
7510
|
+
readyCount,
|
|
7511
|
+
failedCount,
|
|
7512
|
+
pendingCount
|
|
7513
|
+
};
|
|
7307
7514
|
}
|
|
7308
7515
|
/** Emit onPreloadStatusChange with a fresh snapshot (skips computation when no listener). */
|
|
7309
7516
|
emitPreloadStatusChange() {
|
|
7310
|
-
if (this.onPreloadStatusChange)
|
|
7517
|
+
if (this.onPreloadStatusChange)
|
|
7518
|
+
this.safeCallback(this.onPreloadStatusChange, this.getPreloadStatus());
|
|
7311
7519
|
}
|
|
7312
7520
|
async _hydrateCollectionFromDexie(name) {
|
|
7313
7521
|
const allItems = [];
|
|
7314
|
-
await this.dexieDb.forEachBatch(
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7522
|
+
await this.dexieDb.forEachBatch(
|
|
7523
|
+
name,
|
|
7524
|
+
2e3,
|
|
7525
|
+
async (chunk) => {
|
|
7526
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
7527
|
+
const item = chunk[i];
|
|
7528
|
+
if (!item._deleted && !item._archived) {
|
|
7529
|
+
allItems.push(item);
|
|
7530
|
+
}
|
|
7319
7531
|
}
|
|
7320
7532
|
}
|
|
7321
|
-
|
|
7533
|
+
);
|
|
7322
7534
|
const dirty = await this.dexieDb.getDirty(name);
|
|
7323
7535
|
if (dirty.length > 0) {
|
|
7324
7536
|
const itemById = /* @__PURE__ */ new Map();
|
|
@@ -7387,7 +7599,9 @@ var _SyncedDb = class _SyncedDb {
|
|
|
7387
7599
|
}
|
|
7388
7600
|
assertCollection(name) {
|
|
7389
7601
|
if (!this.collections.has(name)) {
|
|
7390
|
-
throw new Error(
|
|
7602
|
+
throw new Error(
|
|
7603
|
+
`SyncedDb: Collection "${(name == null ? void 0 : name.toString()) || "?"}" not configured`
|
|
7604
|
+
);
|
|
7391
7605
|
}
|
|
7392
7606
|
}
|
|
7393
7607
|
/**
|
|
@@ -7517,7 +7731,8 @@ var _SyncedDb = class _SyncedDb {
|
|
|
7517
7731
|
if (_SyncedDb.isObjectIdLike(data)) return String(data);
|
|
7518
7732
|
if (typeof data !== "object") return data;
|
|
7519
7733
|
if (data instanceof Date) return data;
|
|
7520
|
-
if (Array.isArray(data))
|
|
7734
|
+
if (Array.isArray(data))
|
|
7735
|
+
return data.map((v) => _SyncedDb.stringifyObjectIds(v));
|
|
7521
7736
|
const result = {};
|
|
7522
7737
|
for (const key of Object.keys(data)) {
|
|
7523
7738
|
result[key] = _SyncedDb.stringifyObjectIds(data[key]);
|
|
@@ -121,8 +121,8 @@ export interface BeforeDirtyClearAllInfo {
|
|
|
121
121
|
reason: string;
|
|
122
122
|
/** Collection whose dirty entries are about to be deleted. */
|
|
123
123
|
collection: string;
|
|
124
|
-
/**
|
|
125
|
-
items: import("./I_DexieDb").
|
|
124
|
+
/** Every dirty entry in this collection — includes both metadata and the `changes` payload. */
|
|
125
|
+
items: import("./I_DexieDb").DirtyChange[];
|
|
126
126
|
/** Optional caller tag passed through `clearDirty(undefined, undefined, calledFrom)`. */
|
|
127
127
|
calledFrom?: string;
|
|
128
128
|
/** Timestamp when the callback fires. */
|
|
@@ -648,7 +648,7 @@ export interface SyncedDbConfig {
|
|
|
648
648
|
* `loaded`/`total` are scoped to the phase that emitted the event.
|
|
649
649
|
*/
|
|
650
650
|
onSyncProgress?: (info: {
|
|
651
|
-
phase:
|
|
651
|
+
phase: "dexie" | "server";
|
|
652
652
|
collection: string;
|
|
653
653
|
loaded: number;
|
|
654
654
|
total: number;
|