cry-synced-db-client 0.1.124 → 0.1.126
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 +14 -0
- package/dist/index.js +146 -11
- package/dist/src/db/SyncedDb.d.ts +22 -1
- package/dist/src/db/managers/CrossTabSyncManager.d.ts +1 -1
- package/dist/src/db/types/managers.d.ts +2 -0
- package/dist/src/types/I_SyncedDb.d.ts +48 -0
- package/dist/src/types/index.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 0.1.126 (2026-04-08)
|
|
4
|
+
|
|
5
|
+
- Eviction: remove out-of-scope records from Dexie and in-mem cache
|
|
6
|
+
- `evictOutOfScopeRecords(collection)` — evict records not matching `syncConfig.query`
|
|
7
|
+
- `evictOutOfScopeRecordsAll()` — evict across all collections, fires `onEviction` callback
|
|
8
|
+
- `evictStaleRecordsEveryHrs` config — auto-eviction after sync when interval elapsed
|
|
9
|
+
- `onEviction` callback with `EvictionInfo` (total, duration, trigger, per-collection breakdown)
|
|
10
|
+
- Dirty records always preserved; deleted/archived evicted; cross-tab reload broadcast emitted
|
|
11
|
+
- `dropDatabase()` clears `__lastEviction` timestamp
|
|
12
|
+
|
|
13
|
+
## 0.1.125 (2026-04-08)
|
|
14
|
+
|
|
15
|
+
- Detect stringified falsy `_id` values (`"undefined"`, `"null"`, `"0"`, `"false"`, `""`) across all DB operations
|
|
16
|
+
|
|
3
17
|
## 0.1.124 (2026-04-08)
|
|
4
18
|
|
|
5
19
|
- Fix `ensureId` not generating `_id` when key is absent (caused `"undefined"` string ids)
|
package/dist/index.js
CHANGED
|
@@ -3416,7 +3416,7 @@ var NetworkStatusManager = class {
|
|
|
3416
3416
|
};
|
|
3417
3417
|
|
|
3418
3418
|
// src/db/SyncedDb.ts
|
|
3419
|
-
var
|
|
3419
|
+
var _SyncedDb = class _SyncedDb {
|
|
3420
3420
|
constructor(config) {
|
|
3421
3421
|
this.collections = /* @__PURE__ */ new Map();
|
|
3422
3422
|
// State
|
|
@@ -3428,7 +3428,7 @@ var SyncedDb = class _SyncedDb {
|
|
|
3428
3428
|
this.syncOnlyCollections = null;
|
|
3429
3429
|
// Sync metadata cache
|
|
3430
3430
|
this.syncMetaCache = /* @__PURE__ */ new Map();
|
|
3431
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
3431
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
3432
3432
|
this.tenant = config.tenant;
|
|
3433
3433
|
this.dexieDb = config.dexieDb;
|
|
3434
3434
|
this.inMemDb = config.inMemDb;
|
|
@@ -3452,13 +3452,15 @@ var SyncedDb = class _SyncedDb {
|
|
|
3452
3452
|
this.onWsNotification = config.onWsNotification;
|
|
3453
3453
|
this.onCrossTabSync = config.onCrossTabSync;
|
|
3454
3454
|
this.onWakeSync = config.onWakeSync;
|
|
3455
|
+
this.onEviction = config.onEviction;
|
|
3456
|
+
this.evictStaleRecordsEveryHrs = (_e = config.evictStaleRecordsEveryHrs) != null ? _e : 0;
|
|
3455
3457
|
for (const col of config.collections) {
|
|
3456
3458
|
this.collections.set(col.name, col);
|
|
3457
3459
|
}
|
|
3458
3460
|
this.inMemManager = new InMemManager({
|
|
3459
3461
|
inMemDb: this.inMemDb,
|
|
3460
3462
|
collections: this.collections,
|
|
3461
|
-
useObjectMetadata: (
|
|
3463
|
+
useObjectMetadata: (_f = config.useObjectMetadata) != null ? _f : false
|
|
3462
3464
|
});
|
|
3463
3465
|
this.leaderElection = new LeaderElectionManager({
|
|
3464
3466
|
tenant: this.tenant,
|
|
@@ -3480,7 +3482,7 @@ var SyncedDb = class _SyncedDb {
|
|
|
3480
3482
|
tenant: this.tenant,
|
|
3481
3483
|
instanceId: this.syncedDbInstanceId,
|
|
3482
3484
|
windowId,
|
|
3483
|
-
debounceMs: (
|
|
3485
|
+
debounceMs: (_g = config.crossTabSyncDebounceMs) != null ? _g : 100,
|
|
3484
3486
|
callbacks: {
|
|
3485
3487
|
onCrossTabSync: config.onCrossTabSync,
|
|
3486
3488
|
onInfrastructureError: config.onInfrastructureError ? (type, message, error) => {
|
|
@@ -3506,8 +3508,8 @@ var SyncedDb = class _SyncedDb {
|
|
|
3506
3508
|
});
|
|
3507
3509
|
this.connectionManager = new ConnectionManager({
|
|
3508
3510
|
restInterface: this.restInterface,
|
|
3509
|
-
restTimeoutMs: (
|
|
3510
|
-
syncTimeoutMs: (
|
|
3511
|
+
restTimeoutMs: (_h = config.restTimeoutMs) != null ? _h : 9e4,
|
|
3512
|
+
syncTimeoutMs: (_i = config.syncTimeoutMs) != null ? _i : 12e4,
|
|
3511
3513
|
autoSyncIntervalMs: config.autoSyncIntervalMs,
|
|
3512
3514
|
callbacks: {
|
|
3513
3515
|
onOnlineStatusChange: config.onOnlineStatusChange,
|
|
@@ -3533,8 +3535,8 @@ var SyncedDb = class _SyncedDb {
|
|
|
3533
3535
|
});
|
|
3534
3536
|
this.pendingChanges = new PendingChangesManager({
|
|
3535
3537
|
tenant: this.tenant,
|
|
3536
|
-
debounceDexieWritesMs: (
|
|
3537
|
-
debounceRestWritesMs: (
|
|
3538
|
+
debounceDexieWritesMs: (_j = config.debounceDexieWritesMs) != null ? _j : 500,
|
|
3539
|
+
debounceRestWritesMs: (_k = config.debounceRestWritesMs) != null ? _k : 100,
|
|
3538
3540
|
callbacks: {
|
|
3539
3541
|
onDexieWriteRequest: config.onDexieWriteRequest,
|
|
3540
3542
|
onDexieWriteResult: config.onDexieWriteResult,
|
|
@@ -3611,8 +3613,8 @@ var SyncedDb = class _SyncedDb {
|
|
|
3611
3613
|
});
|
|
3612
3614
|
if (config.wakeSyncEnabled) {
|
|
3613
3615
|
this.wakeSync = new WakeSyncManager({
|
|
3614
|
-
gapThresholdMs: (
|
|
3615
|
-
debounceMs: (
|
|
3616
|
+
gapThresholdMs: (_l = config.wakeSyncGapThresholdMs) != null ? _l : 1e4,
|
|
3617
|
+
debounceMs: (_m = config.wakeSyncDebounceMs) != null ? _m : 2e3,
|
|
3616
3618
|
callbacks: {
|
|
3617
3619
|
onWakeSync: config.onWakeSync
|
|
3618
3620
|
},
|
|
@@ -3627,7 +3629,7 @@ var SyncedDb = class _SyncedDb {
|
|
|
3627
3629
|
}
|
|
3628
3630
|
if (config.networkStatusEnabled) {
|
|
3629
3631
|
this.networkStatus = new NetworkStatusManager({
|
|
3630
|
-
debounceMs: (
|
|
3632
|
+
debounceMs: (_n = config.networkStatusDebounceMs) != null ? _n : 100,
|
|
3631
3633
|
callbacks: {
|
|
3632
3634
|
onBrowserNetworkChange: config.onBrowserNetworkChange,
|
|
3633
3635
|
onBrowserOnline: config.onBrowserOnline,
|
|
@@ -4360,6 +4362,11 @@ var SyncedDb = class _SyncedDb {
|
|
|
4360
4362
|
this.syncLock = false;
|
|
4361
4363
|
this.crossTabSync.endServerSync();
|
|
4362
4364
|
await this.processQueuedWsUpdates();
|
|
4365
|
+
try {
|
|
4366
|
+
await this.maybeAutoEvict();
|
|
4367
|
+
} catch (err) {
|
|
4368
|
+
console.error("Auto-eviction failed:", err);
|
|
4369
|
+
}
|
|
4363
4370
|
}
|
|
4364
4371
|
}
|
|
4365
4372
|
async processQueuedWsUpdates() {
|
|
@@ -4500,6 +4507,8 @@ var SyncedDb = class _SyncedDb {
|
|
|
4500
4507
|
}
|
|
4501
4508
|
this.syncMetaCache.clear();
|
|
4502
4509
|
this._clearLastFullSync();
|
|
4510
|
+
this._lastEvictionDate = void 0;
|
|
4511
|
+
await this.dexieDb.deleteSyncMeta("__lastEviction");
|
|
4503
4512
|
}
|
|
4504
4513
|
/**
|
|
4505
4514
|
* Clear all collection data and sync metadata, then re-download
|
|
@@ -4526,6 +4535,108 @@ var SyncedDb = class _SyncedDb {
|
|
|
4526
4535
|
this.syncMetaCache.clear();
|
|
4527
4536
|
this._clearLastFullSync();
|
|
4528
4537
|
}
|
|
4538
|
+
// ==================== Eviction ====================
|
|
4539
|
+
/**
|
|
4540
|
+
* Remove records from Dexie and in-mem that no longer match the
|
|
4541
|
+
* collection's current syncConfig.query. Does NOT delete from server.
|
|
4542
|
+
* Skips records with dirty changes (pending local writes).
|
|
4543
|
+
*/
|
|
4544
|
+
async evictOutOfScopeRecords(collection) {
|
|
4545
|
+
var _a;
|
|
4546
|
+
this.assertCollection(collection);
|
|
4547
|
+
const config = this.collections.get(collection);
|
|
4548
|
+
const syncQuery = (_a = config.syncConfig) == null ? void 0 : _a.query;
|
|
4549
|
+
const query = typeof syncQuery === "function" ? syncQuery() : syncQuery;
|
|
4550
|
+
if (!query) {
|
|
4551
|
+
return { collection, evictedCount: 0, dirtySkipped: 0, scannedCount: 0 };
|
|
4552
|
+
}
|
|
4553
|
+
await this.pendingChanges.flushForCollection(collection);
|
|
4554
|
+
const dirtyItems = await this.dexieDb.getDirty(collection);
|
|
4555
|
+
const dirtyIds = new Set(dirtyItems.map((d) => String(d._id)));
|
|
4556
|
+
let scannedCount = 0;
|
|
4557
|
+
let dirtySkipped = 0;
|
|
4558
|
+
const evictIds = [];
|
|
4559
|
+
await this.dexieDb.forEachBatch(
|
|
4560
|
+
collection,
|
|
4561
|
+
2e3,
|
|
4562
|
+
async (items) => {
|
|
4563
|
+
for (const item of items) {
|
|
4564
|
+
scannedCount++;
|
|
4565
|
+
const id = String(item._id);
|
|
4566
|
+
if (dirtyIds.has(id)) {
|
|
4567
|
+
dirtySkipped++;
|
|
4568
|
+
continue;
|
|
4569
|
+
}
|
|
4570
|
+
if (!matchesQuery(item, query)) {
|
|
4571
|
+
evictIds.push(id);
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
);
|
|
4576
|
+
if (evictIds.length > 0) {
|
|
4577
|
+
await this.dexieDb.deleteMany(collection, evictIds);
|
|
4578
|
+
if (!config.writeOnly) {
|
|
4579
|
+
this.inMemManager.writeBatch(
|
|
4580
|
+
collection,
|
|
4581
|
+
evictIds.map((id) => ({ _id: id })),
|
|
4582
|
+
"delete"
|
|
4583
|
+
);
|
|
4584
|
+
}
|
|
4585
|
+
this.crossTabSync.broadcastReload([collection]);
|
|
4586
|
+
}
|
|
4587
|
+
return { collection, evictedCount: evictIds.length, dirtySkipped, scannedCount };
|
|
4588
|
+
}
|
|
4589
|
+
/**
|
|
4590
|
+
* Evict out-of-scope records for all collections.
|
|
4591
|
+
* Skips writeOnly and collections without syncConfig.query.
|
|
4592
|
+
* Fires onEviction callback.
|
|
4593
|
+
*/
|
|
4594
|
+
async evictOutOfScopeRecordsAll(trigger = "manual") {
|
|
4595
|
+
var _a;
|
|
4596
|
+
const startTime = Date.now();
|
|
4597
|
+
const collectionResults = [];
|
|
4598
|
+
let totalEvicted = 0;
|
|
4599
|
+
for (const [name, config] of this.collections) {
|
|
4600
|
+
if (config.writeOnly) continue;
|
|
4601
|
+
const syncQuery = (_a = config.syncConfig) == null ? void 0 : _a.query;
|
|
4602
|
+
const query = typeof syncQuery === "function" ? syncQuery() : syncQuery;
|
|
4603
|
+
if (!query) continue;
|
|
4604
|
+
const result = await this.evictOutOfScopeRecords(name);
|
|
4605
|
+
collectionResults.push(result);
|
|
4606
|
+
totalEvicted += result.evictedCount;
|
|
4607
|
+
}
|
|
4608
|
+
const info = {
|
|
4609
|
+
totalEvicted,
|
|
4610
|
+
durationMs: Date.now() - startTime,
|
|
4611
|
+
trigger,
|
|
4612
|
+
collections: collectionResults
|
|
4613
|
+
};
|
|
4614
|
+
this.safeCallback(this.onEviction, info);
|
|
4615
|
+
return info;
|
|
4616
|
+
}
|
|
4617
|
+
/**
|
|
4618
|
+
* Check if auto-eviction should run and execute if interval has elapsed.
|
|
4619
|
+
* Called after sync() and at end of init().
|
|
4620
|
+
*/
|
|
4621
|
+
async maybeAutoEvict() {
|
|
4622
|
+
if (this.evictStaleRecordsEveryHrs <= 0) return;
|
|
4623
|
+
const intervalMs = this.evictStaleRecordsEveryHrs * 36e5;
|
|
4624
|
+
if (!this._lastEvictionDate) {
|
|
4625
|
+
const meta = await this.dexieDb.getSyncMeta("__lastEviction");
|
|
4626
|
+
if (meta == null ? void 0 : meta.lastSyncTs) {
|
|
4627
|
+
this._lastEvictionDate = new Date(meta.lastSyncTs);
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
if (this._lastEvictionDate && Date.now() - this._lastEvictionDate.getTime() < intervalMs) {
|
|
4631
|
+
return;
|
|
4632
|
+
}
|
|
4633
|
+
await this.evictOutOfScopeRecordsAll("auto");
|
|
4634
|
+
this._lastEvictionDate = /* @__PURE__ */ new Date();
|
|
4635
|
+
await this.dexieDb.setSyncMeta(
|
|
4636
|
+
"__lastEviction",
|
|
4637
|
+
this._lastEvictionDate.toISOString()
|
|
4638
|
+
);
|
|
4639
|
+
}
|
|
4529
4640
|
// ==================== Object Metadata ====================
|
|
4530
4641
|
getObjectMetadata(collection, _id) {
|
|
4531
4642
|
return this.inMemManager.getObjectMetadata(collection, _id);
|
|
@@ -4669,6 +4780,11 @@ var SyncedDb = class _SyncedDb {
|
|
|
4669
4780
|
`SyncedDb.${method != null ? method : "?"}("${collection != null ? collection : "?"}"): id parameter is falsy (${JSON.stringify(id)}). This is a bug \u2014 the caller must provide a valid _id.`
|
|
4670
4781
|
);
|
|
4671
4782
|
}
|
|
4783
|
+
if (typeof id === "string" && _SyncedDb.STRINGIFIED_FALSY.has(id)) {
|
|
4784
|
+
console.error(
|
|
4785
|
+
`SyncedDb.${method != null ? method : "?"}("${collection != null ? collection : "?"}"): id is a stringified falsy value ("${id}"). This is a bug \u2014 a falsy value was coerced to string before being passed as _id.`
|
|
4786
|
+
);
|
|
4787
|
+
}
|
|
4672
4788
|
return typeof id === "object" && id !== null ? String(id) : id;
|
|
4673
4789
|
}
|
|
4674
4790
|
/**
|
|
@@ -4681,6 +4797,11 @@ var SyncedDb = class _SyncedDb {
|
|
|
4681
4797
|
`SyncedDb.${method}("${collection}"): _id is present in query/data but falsy (${JSON.stringify(data._id)}). This is a bug \u2014 _id must be valid when specified. Data keys: [${Object.keys(data).join(", ")}]`
|
|
4682
4798
|
);
|
|
4683
4799
|
}
|
|
4800
|
+
if ("_id" in data && typeof data._id === "string" && _SyncedDb.STRINGIFIED_FALSY.has(data._id)) {
|
|
4801
|
+
console.error(
|
|
4802
|
+
`SyncedDb.${method}("${collection}"): _id is a stringified falsy value ("${data._id}"). This is a bug \u2014 a falsy value was coerced to string before being passed as _id. Data keys: [${Object.keys(data).join(", ")}]`
|
|
4803
|
+
);
|
|
4804
|
+
}
|
|
4684
4805
|
}
|
|
4685
4806
|
/**
|
|
4686
4807
|
* Ensure `_id` on a data object is never falsy or absent.
|
|
@@ -4689,6 +4810,12 @@ var SyncedDb = class _SyncedDb {
|
|
|
4689
4810
|
* @mutates — modifies the object in-place and returns it.
|
|
4690
4811
|
*/
|
|
4691
4812
|
ensureId(data, method, collection) {
|
|
4813
|
+
if (typeof data._id === "string" && _SyncedDb.STRINGIFIED_FALSY.has(data._id)) {
|
|
4814
|
+
console.error(
|
|
4815
|
+
`SyncedDb.${method}("${collection}"): _id is a stringified falsy value ("${data._id}"). This is a bug \u2014 a falsy value was coerced to string before being passed as _id. Data keys: [${Object.keys(data).join(", ")}]`
|
|
4816
|
+
);
|
|
4817
|
+
data._id = null;
|
|
4818
|
+
}
|
|
4692
4819
|
if (!data._id) {
|
|
4693
4820
|
const newId = new ObjectId2().toHexString();
|
|
4694
4821
|
if ("_id" in data) {
|
|
@@ -4765,6 +4892,14 @@ var SyncedDb = class _SyncedDb {
|
|
|
4765
4892
|
);
|
|
4766
4893
|
}
|
|
4767
4894
|
};
|
|
4895
|
+
_SyncedDb.STRINGIFIED_FALSY = /* @__PURE__ */ new Set([
|
|
4896
|
+
"undefined",
|
|
4897
|
+
"null",
|
|
4898
|
+
"0",
|
|
4899
|
+
"false",
|
|
4900
|
+
""
|
|
4901
|
+
]);
|
|
4902
|
+
var SyncedDb = _SyncedDb;
|
|
4768
4903
|
|
|
4769
4904
|
// src/db/DexieDb.ts
|
|
4770
4905
|
import Dexie from "dexie";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AggregateOptions } from "mongodb";
|
|
2
|
-
import type { I_SyncedDb, SyncedDbConfig, WsNotificationInfo } from "../types/I_SyncedDb";
|
|
2
|
+
import type { I_SyncedDb, SyncedDbConfig, WsNotificationInfo, EvictionInfo, EvictionCollectionInfo } from "../types/I_SyncedDb";
|
|
3
3
|
import type { MetaUpdateBroadcast } from "../types/I_DexieDb";
|
|
4
4
|
import type { QuerySpec, QueryOpts, UpdateSpec, InsertSpec, BatchSpec } from "../types/I_RestInterface";
|
|
5
5
|
import type { Id, DbEntity } from "../types/DbEntity";
|
|
@@ -50,6 +50,9 @@ export declare class SyncedDb implements I_SyncedDb {
|
|
|
50
50
|
private readonly onWsNotification?;
|
|
51
51
|
private readonly onCrossTabSync?;
|
|
52
52
|
private readonly onWakeSync?;
|
|
53
|
+
private readonly onEviction?;
|
|
54
|
+
private readonly evictStaleRecordsEveryHrs;
|
|
55
|
+
private _lastEvictionDate?;
|
|
53
56
|
constructor(config: SyncedDbConfig);
|
|
54
57
|
getInstanceId(): string;
|
|
55
58
|
getCrossTabSyncDebounceMs(): number;
|
|
@@ -167,6 +170,23 @@ export declare class SyncedDb implements I_SyncedDb {
|
|
|
167
170
|
* The sync will also upload any remaining dirty items.
|
|
168
171
|
*/
|
|
169
172
|
refreshDatabaseFromServer(): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* Remove records from Dexie and in-mem that no longer match the
|
|
175
|
+
* collection's current syncConfig.query. Does NOT delete from server.
|
|
176
|
+
* Skips records with dirty changes (pending local writes).
|
|
177
|
+
*/
|
|
178
|
+
evictOutOfScopeRecords(collection: string): Promise<EvictionCollectionInfo>;
|
|
179
|
+
/**
|
|
180
|
+
* Evict out-of-scope records for all collections.
|
|
181
|
+
* Skips writeOnly and collections without syncConfig.query.
|
|
182
|
+
* Fires onEviction callback.
|
|
183
|
+
*/
|
|
184
|
+
evictOutOfScopeRecordsAll(trigger?: "auto" | "manual"): Promise<EvictionInfo>;
|
|
185
|
+
/**
|
|
186
|
+
* Check if auto-eviction should run and execute if interval has elapsed.
|
|
187
|
+
* Called after sync() and at end of init().
|
|
188
|
+
*/
|
|
189
|
+
private maybeAutoEvict;
|
|
170
190
|
getObjectMetadata<M>(collection: string, _id: Id): M | undefined;
|
|
171
191
|
getObjectsMetadata<M>(collection: string, _ids: Id[]): (M | undefined)[];
|
|
172
192
|
setObjectMetadata<M>(collection: string, _id: Id, metadata: M): void;
|
|
@@ -208,6 +228,7 @@ export declare class SyncedDb implements I_SyncedDb {
|
|
|
208
228
|
private loadCollectionsToInMem;
|
|
209
229
|
private loadCollectionToInMem;
|
|
210
230
|
private assertCollection;
|
|
231
|
+
private static readonly STRINGIFIED_FALSY;
|
|
211
232
|
/** Stringify an Id parameter (ObjectId → hex string). */
|
|
212
233
|
private normalizeId;
|
|
213
234
|
/**
|
|
@@ -62,6 +62,6 @@ export declare class CrossTabSyncManager implements I_CrossTabSyncManager {
|
|
|
62
62
|
private handleCrossTabMetaUpdate;
|
|
63
63
|
private handleReloadBroadcast;
|
|
64
64
|
private handleDeltaBroadcast;
|
|
65
|
-
|
|
65
|
+
broadcastReload(collections: string[]): void;
|
|
66
66
|
private callOnInfrastructureError;
|
|
67
67
|
}
|
|
@@ -82,6 +82,8 @@ export interface I_CrossTabSyncManager {
|
|
|
82
82
|
* Emits a single reload broadcast for all collections touched during the sync.
|
|
83
83
|
*/
|
|
84
84
|
endServerSync(): void;
|
|
85
|
+
/** Emit a reload broadcast for the given collections. */
|
|
86
|
+
broadcastReload(collections: string[]): void;
|
|
85
87
|
/** Cleanup resources. */
|
|
86
88
|
dispose(): void;
|
|
87
89
|
/** Handle external broadcast (for testing). */
|
|
@@ -228,6 +228,32 @@ export interface SyncInfo {
|
|
|
228
228
|
/** Per-collection sync statistics (collection name -> stats) */
|
|
229
229
|
collections?: Record<string, CollectionSyncStats>;
|
|
230
230
|
}
|
|
231
|
+
/** Per-collection eviction statistics. */
|
|
232
|
+
export interface EvictionCollectionInfo {
|
|
233
|
+
/** Collection name. */
|
|
234
|
+
collection: string;
|
|
235
|
+
/** Records removed from Dexie + in-mem. */
|
|
236
|
+
evictedCount: number;
|
|
237
|
+
/** Records skipped because they have pending dirty changes. */
|
|
238
|
+
dirtySkipped: number;
|
|
239
|
+
/** Total records scanned in this collection. */
|
|
240
|
+
scannedCount: number;
|
|
241
|
+
}
|
|
242
|
+
/** Eviction result reported to the onEviction callback. */
|
|
243
|
+
export interface EvictionInfo {
|
|
244
|
+
/** Total records evicted across all collections. */
|
|
245
|
+
totalEvicted: number;
|
|
246
|
+
/** Wall-clock duration of the eviction operation in ms. */
|
|
247
|
+
durationMs: number;
|
|
248
|
+
/** Whether this was triggered automatically or by manual call. */
|
|
249
|
+
trigger: "auto" | "manual";
|
|
250
|
+
/**
|
|
251
|
+
* Per-collection breakdown.
|
|
252
|
+
* Only collections with syncConfig.query are included.
|
|
253
|
+
* Collections where evictedCount === 0 ARE included.
|
|
254
|
+
*/
|
|
255
|
+
collections: EvictionCollectionInfo[];
|
|
256
|
+
}
|
|
231
257
|
/**
|
|
232
258
|
* Configuration for collection sync behavior (used in sync() method only, not uploadDirtyItems)
|
|
233
259
|
*/
|
|
@@ -469,6 +495,15 @@ export interface SyncedDbConfig {
|
|
|
469
495
|
* Note: This is different from onOnlineStatusChange which fires after SyncedDb processes the change.
|
|
470
496
|
*/
|
|
471
497
|
onBrowserOffline?: () => void;
|
|
498
|
+
/**
|
|
499
|
+
* Auto-eviction interval in hours. When set (> 0), eviction runs
|
|
500
|
+
* automatically after init and each sync cycle if the interval has elapsed.
|
|
501
|
+
* Stores last eviction timestamp in Dexie via syncMeta key `__lastEviction`.
|
|
502
|
+
* Default: 0 (disabled).
|
|
503
|
+
*/
|
|
504
|
+
evictStaleRecordsEveryHrs?: number;
|
|
505
|
+
/** Callback fired after each eviction run (manual or auto). Errors are swallowed. */
|
|
506
|
+
onEviction?: (info: EvictionInfo) => void;
|
|
472
507
|
}
|
|
473
508
|
/**
|
|
474
509
|
* Glavna logika za sinhronizirano bazo podatkov
|
|
@@ -670,6 +705,19 @@ export interface I_SyncedDb {
|
|
|
670
705
|
* Unlike dropDatabase(), this never discards unsynchronized edits.
|
|
671
706
|
*/
|
|
672
707
|
refreshDatabaseFromServer(): Promise<void>;
|
|
708
|
+
/**
|
|
709
|
+
* Remove records from Dexie and in-mem that no longer match the
|
|
710
|
+
* collection's current syncConfig.query. Does NOT delete from server.
|
|
711
|
+
* Skips records with dirty changes (pending local writes).
|
|
712
|
+
* Returns per-collection eviction stats.
|
|
713
|
+
*/
|
|
714
|
+
evictOutOfScopeRecords(collection: string): Promise<EvictionCollectionInfo>;
|
|
715
|
+
/**
|
|
716
|
+
* Same as evictOutOfScopeRecords but for all configured collections.
|
|
717
|
+
* Skips writeOnly collections and collections without syncConfig.query.
|
|
718
|
+
* Fires onEviction callback with full stats.
|
|
719
|
+
*/
|
|
720
|
+
evictOutOfScopeRecordsAll(): Promise<EvictionInfo>;
|
|
673
721
|
/**
|
|
674
722
|
* Check if this instance is the leader tab.
|
|
675
723
|
* Only the leader processes server notifications and writes to Dexie from server updates.
|
|
@@ -4,7 +4,7 @@ export type { Obj, QuerySpec, Projection, QueryOpts, KeyOf, InsertKeyOf, InsertS
|
|
|
4
4
|
export type { I_InMemDb as InMemDb } from "./I_InMemDb";
|
|
5
5
|
export type { I_DexieDb as DexieDb, SyncMeta } from "./I_DexieDb";
|
|
6
6
|
export type { I_ServerUpdateNotifier as ServerUpdateNotifier, ServerUpdateCallback, ServerUpdateNotifierCallbacks } from "./I_ServerUpdateNotifier";
|
|
7
|
-
export type { I_SyncedDb as SyncedDb, SyncedDbConfig, CollectionConfig, CollectionSyncConfig, SyncInfo, ServerWriteRequestInfo, ServerWriteResultInfo, FindNewerManyCallInfo, FindNewerManyResultInfo, DexieWriteRequestInfo, DexieWriteResultInfo, LocalstorageWriteResultInfo, WsNotificationInfo, InfrastructureErrorType, InfrastructureErrorInfo, ConflictSource, ConflictResolutionReport, CrossTabSyncInfo, } from "./I_SyncedDb";
|
|
7
|
+
export type { I_SyncedDb as SyncedDb, SyncedDbConfig, CollectionConfig, CollectionSyncConfig, SyncInfo, ServerWriteRequestInfo, ServerWriteResultInfo, FindNewerManyCallInfo, FindNewerManyResultInfo, DexieWriteRequestInfo, DexieWriteResultInfo, LocalstorageWriteResultInfo, WsNotificationInfo, InfrastructureErrorType, InfrastructureErrorInfo, ConflictSource, ConflictResolutionReport, CrossTabSyncInfo, EvictionInfo, EvictionCollectionInfo, } from "./I_SyncedDb";
|
|
8
8
|
export type { NetworkStatusChangeInfo } from "../db/types/managers";
|
|
9
9
|
export type { CollectionConfig as CollectionConfigFull, CollectionSyncConfig as CollectionSyncConfigFull } from "./CollectionConfig";
|
|
10
10
|
export type { Rdb2CallMap, Rdb2Operation } from "./Rdb2CallMap";
|