cry-synced-db-client 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db/SyncedDb.d.ts +1 -0
- package/dist/index.js +26 -9
- package/dist/types/I_DexieDb.d.ts +1 -1
- package/dist/types/I_SyncedDb.d.ts +33 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/db/SyncedDb.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare class SyncedDb implements I_SyncedDb {
|
|
|
18
18
|
private debounceRestWritesMs;
|
|
19
19
|
private onForcedOffline?;
|
|
20
20
|
private onSync?;
|
|
21
|
+
private onConflictResolved?;
|
|
21
22
|
private onServerWriteRequest?;
|
|
22
23
|
private onServerWriteResult?;
|
|
23
24
|
private onFindNewerManyCall?;
|
package/dist/index.js
CHANGED
|
@@ -271,6 +271,7 @@ class SyncedDb {
|
|
|
271
271
|
debounceRestWritesMs;
|
|
272
272
|
onForcedOffline;
|
|
273
273
|
onSync;
|
|
274
|
+
onConflictResolved;
|
|
274
275
|
onServerWriteRequest;
|
|
275
276
|
onServerWriteResult;
|
|
276
277
|
onFindNewerManyCall;
|
|
@@ -320,6 +321,7 @@ class SyncedDb {
|
|
|
320
321
|
this.debounceRestWritesMs = config.debounceRestWritesMs ?? DEFAULT_REST_DEBOUNCE_MS;
|
|
321
322
|
this.onForcedOffline = config.onForcedOffline;
|
|
322
323
|
this.onSync = config.onSync;
|
|
324
|
+
this.onConflictResolved = config.onConflictResolved;
|
|
323
325
|
this.onServerWriteRequest = config.onServerWriteRequest;
|
|
324
326
|
this.onServerWriteResult = config.onServerWriteResult;
|
|
325
327
|
this.onFindNewerManyCall = config.onFindNewerManyCall;
|
|
@@ -1312,7 +1314,7 @@ class SyncedDb {
|
|
|
1312
1314
|
if (localItem) {
|
|
1313
1315
|
if (localItem._dirty) {
|
|
1314
1316
|
conflictsResolved++;
|
|
1315
|
-
const resolved = this.resolveCollectionConflict(collectionName, config, localItem, serverItem);
|
|
1317
|
+
const resolved = this.resolveCollectionConflict(collectionName, config, localItem, serverItem, "sync");
|
|
1316
1318
|
dexieBatch.push({
|
|
1317
1319
|
...resolved,
|
|
1318
1320
|
_dirty: true,
|
|
@@ -1467,11 +1469,26 @@ class SyncedDb {
|
|
|
1467
1469
|
}
|
|
1468
1470
|
return { sentCount };
|
|
1469
1471
|
}
|
|
1470
|
-
resolveCollectionConflict(collectionName, config, local, external) {
|
|
1471
|
-
|
|
1472
|
-
|
|
1472
|
+
resolveCollectionConflict(collectionName, config, local, external, source) {
|
|
1473
|
+
const usedCustomResolver = !!config.resolveSyncConflict;
|
|
1474
|
+
const resolved = config.resolveSyncConflict ? config.resolveSyncConflict(local, external) : resolveConflict(local, external);
|
|
1475
|
+
if (this.onConflictResolved) {
|
|
1476
|
+
try {
|
|
1477
|
+
this.onConflictResolved({
|
|
1478
|
+
collection: collectionName,
|
|
1479
|
+
id: local._id,
|
|
1480
|
+
source,
|
|
1481
|
+
localItem: local,
|
|
1482
|
+
serverItem: external,
|
|
1483
|
+
resolvedItem: resolved,
|
|
1484
|
+
usedCustomResolver,
|
|
1485
|
+
timestamp: new Date
|
|
1486
|
+
});
|
|
1487
|
+
} catch (err) {
|
|
1488
|
+
console.error("onConflictResolved callback failed:", err);
|
|
1489
|
+
}
|
|
1473
1490
|
}
|
|
1474
|
-
return
|
|
1491
|
+
return resolved;
|
|
1475
1492
|
}
|
|
1476
1493
|
compareTimestamps(a, b) {
|
|
1477
1494
|
const aT = typeof a === "object" && "t" in a ? a.t : 0;
|
|
@@ -1598,7 +1615,7 @@ class SyncedDb {
|
|
|
1598
1615
|
const currentMemItem = this.inMemDb.getById(collectionName, serverItem._id);
|
|
1599
1616
|
if (currentMemItem) {
|
|
1600
1617
|
const config = this.collections.get(collectionName);
|
|
1601
|
-
const resolved = this.resolveCollectionConflict(collectionName, config, currentMemItem, serverItem);
|
|
1618
|
+
const resolved = this.resolveCollectionConflict(collectionName, config, currentMemItem, serverItem, "serverUpdate_pendingChanges");
|
|
1602
1619
|
if (!resolved._deleted) {
|
|
1603
1620
|
this.inMemDb.save(collectionName, serverItem._id, this.stripLocalFields(resolved));
|
|
1604
1621
|
}
|
|
@@ -1620,7 +1637,7 @@ class SyncedDb {
|
|
|
1620
1637
|
}
|
|
1621
1638
|
if (localItem._dirty) {
|
|
1622
1639
|
const config = this.collections.get(collectionName);
|
|
1623
|
-
const resolved = this.resolveCollectionConflict(collectionName, config, localItem, serverItem);
|
|
1640
|
+
const resolved = this.resolveCollectionConflict(collectionName, config, localItem, serverItem, "serverUpdate_dirtyLocal");
|
|
1624
1641
|
await this.dexieDb.save(collectionName, serverItem._id, {
|
|
1625
1642
|
...resolved,
|
|
1626
1643
|
_dirty: true
|
|
@@ -1695,7 +1712,7 @@ class DexieDb extends Dexie {
|
|
|
1695
1712
|
schema[SYNC_META_TABLE] = "[tenant+collection]";
|
|
1696
1713
|
for (const config of collectionConfigs) {
|
|
1697
1714
|
const additionalIndexes = config.indexes || [];
|
|
1698
|
-
const indexes = ["_id", "_dirty", "
|
|
1715
|
+
const indexes = ["_id", "_dirty", "_ts", ...additionalIndexes.map(String)];
|
|
1699
1716
|
schema[config.name] = indexes.join(", ");
|
|
1700
1717
|
}
|
|
1701
1718
|
this.version(1).stores(schema);
|
|
@@ -1860,7 +1877,7 @@ class DexieDb extends Dexie {
|
|
|
1860
1877
|
async getNewerThan(collection, timestamp) {
|
|
1861
1878
|
const table = this.getTable(collection);
|
|
1862
1879
|
const normalizedTs = this.normalizeTimestamp(timestamp);
|
|
1863
|
-
return await table.where("
|
|
1880
|
+
return await table.where("_ts").above(normalizedTs).toArray();
|
|
1864
1881
|
}
|
|
1865
1882
|
onMetaUpdated(callback) {
|
|
1866
1883
|
this.metaUpdateCallbacks.add(callback);
|
|
@@ -61,7 +61,7 @@ export interface I_DexieDb {
|
|
|
61
61
|
deleteSyncMeta(collection: string): Promise<void>;
|
|
62
62
|
/** Vrne tenant */
|
|
63
63
|
getTenant(): string;
|
|
64
|
-
/** Get records newer than timestamp (uses
|
|
64
|
+
/** Get records newer than timestamp (uses _ts index - server timestamp) */
|
|
65
65
|
getNewerThan<T extends LocalDbEntity>(collection: string, timestamp: any): Promise<T[]>;
|
|
66
66
|
/** Subscribe to metadata updates from other tabs. Returns unsubscribe function. */
|
|
67
67
|
onMetaUpdated(callback: (payload: MetaUpdateBroadcast) => void): () => void;
|
|
@@ -144,6 +144,37 @@ export interface WsNotificationInfo {
|
|
|
144
144
|
/** Timestamp when notification was received */
|
|
145
145
|
timestamp: Date;
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Source of the conflict - where the conflict was detected
|
|
149
|
+
*/
|
|
150
|
+
export type ConflictSource =
|
|
151
|
+
/** Conflict during batch sync (syncSingleCollection) */
|
|
152
|
+
"sync"
|
|
153
|
+
/** Conflict when processing server update with pending local changes not yet in Dexie */
|
|
154
|
+
| "serverUpdate_pendingChanges"
|
|
155
|
+
/** Conflict when processing server update with dirty local item in Dexie */
|
|
156
|
+
| "serverUpdate_dirtyLocal";
|
|
157
|
+
/**
|
|
158
|
+
* Callback payload for conflict resolution
|
|
159
|
+
*/
|
|
160
|
+
export interface ConflictResolutionReport {
|
|
161
|
+
/** Collection where conflict occurred */
|
|
162
|
+
collection: string;
|
|
163
|
+
/** ID of the conflicting item */
|
|
164
|
+
id: Id;
|
|
165
|
+
/** Source/context of the conflict */
|
|
166
|
+
source: ConflictSource;
|
|
167
|
+
/** Local item before resolution */
|
|
168
|
+
localItem: LocalDbEntity;
|
|
169
|
+
/** Server/external item */
|
|
170
|
+
serverItem: LocalDbEntity;
|
|
171
|
+
/** Resolved item (result of conflict resolution) */
|
|
172
|
+
resolvedItem: LocalDbEntity;
|
|
173
|
+
/** Whether a custom resolver was used (from CollectionConfig.resolveSyncConflict) */
|
|
174
|
+
usedCustomResolver: boolean;
|
|
175
|
+
/** Timestamp when conflict was resolved */
|
|
176
|
+
timestamp: Date;
|
|
177
|
+
}
|
|
147
178
|
/**
|
|
148
179
|
* Informacije o sinhronizaciji za debugging/logging
|
|
149
180
|
*/
|
|
@@ -204,6 +235,8 @@ export interface SyncedDbConfig {
|
|
|
204
235
|
onForcedOffline?: (reason: string) => void;
|
|
205
236
|
/** Callback za debugging/logging - pokliče se po vsaki sinhronizaciji */
|
|
206
237
|
onSync?: (info: SyncInfo) => void;
|
|
238
|
+
/** Callback when a sync conflict is resolved (local vs server data) */
|
|
239
|
+
onConflictResolved?: (report: ConflictResolutionReport) => void;
|
|
207
240
|
/** Callback before sending data to server (updateCollections) */
|
|
208
241
|
onServerWriteRequest?: (info: ServerWriteRequestInfo) => void;
|
|
209
242
|
/** Callback after receiving result from server (updateCollections) */
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ 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, SyncInfo, ServerWriteRequestInfo, ServerWriteResultInfo, FindNewerManyCallInfo, FindNewerCallInfo, DexieWriteRequestInfo, DexieWriteResultInfo, LocalstorageWriteResultInfo, WsNotificationInfo, InfrastructureErrorType, InfrastructureErrorInfo, } from "./I_SyncedDb";
|
|
7
|
+
export type { I_SyncedDb as SyncedDb, SyncedDbConfig, CollectionConfig, SyncInfo, ServerWriteRequestInfo, ServerWriteResultInfo, FindNewerManyCallInfo, FindNewerCallInfo, DexieWriteRequestInfo, DexieWriteResultInfo, LocalstorageWriteResultInfo, WsNotificationInfo, InfrastructureErrorType, InfrastructureErrorInfo, ConflictSource, ConflictResolutionReport, } from "./I_SyncedDb";
|
|
8
8
|
export type { CollectionConfig as CollectionConfigFull } from "./CollectionConfig";
|