cry-synced-db-client 0.1.73 → 0.1.74
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/index.js
CHANGED
|
@@ -125,7 +125,7 @@ function sortItems(items, sort) {
|
|
|
125
125
|
const sortEntries = Object.entries(sort);
|
|
126
126
|
if (sortEntries.length === 0)
|
|
127
127
|
return items;
|
|
128
|
-
return
|
|
128
|
+
return items.slice().sort((a, b) => {
|
|
129
129
|
for (const [field, direction] of sortEntries) {
|
|
130
130
|
const aVal = getNestedValue(a, field);
|
|
131
131
|
const bVal = getNestedValue(b, field);
|
|
@@ -194,7 +194,7 @@ function projectItem(item, project) {
|
|
|
194
194
|
}
|
|
195
195
|
return result;
|
|
196
196
|
} else {
|
|
197
|
-
const result = {
|
|
197
|
+
const result = Object.assign({}, item);
|
|
198
198
|
for (const [field, value] of entries) {
|
|
199
199
|
if (value === false || value === 0) {
|
|
200
200
|
delete result[field];
|
|
@@ -237,7 +237,9 @@ class InMemManager {
|
|
|
237
237
|
if (operation === "upsert") {
|
|
238
238
|
this.inMemDb.saveMany(collection, items);
|
|
239
239
|
if (this.useObjectMetadata && config?.hasMetadata) {
|
|
240
|
-
const ids =
|
|
240
|
+
const ids = [];
|
|
241
|
+
for (const item of items)
|
|
242
|
+
ids.push(item._id);
|
|
241
243
|
let metadatas;
|
|
242
244
|
if (config.onObjectsUpdated) {
|
|
243
245
|
try {
|
|
@@ -259,7 +261,9 @@ class InMemManager {
|
|
|
259
261
|
this.setObjectsMetadataInternal(collection, ids, metadatas);
|
|
260
262
|
}
|
|
261
263
|
} else if (operation === "delete") {
|
|
262
|
-
const ids =
|
|
264
|
+
const ids = [];
|
|
265
|
+
for (const item of items)
|
|
266
|
+
ids.push(item._id);
|
|
263
267
|
this.inMemDb.deleteManyByIds(collection, ids);
|
|
264
268
|
if (this.useObjectMetadata && config?.hasMetadata) {
|
|
265
269
|
this.deleteObjectsMetadataInternal(collection, ids);
|
|
@@ -271,7 +275,9 @@ class InMemManager {
|
|
|
271
275
|
if (this.useObjectMetadata && items.length > 0) {
|
|
272
276
|
const config = this.collections.get(collection);
|
|
273
277
|
if (config?.hasMetadata) {
|
|
274
|
-
const ids =
|
|
278
|
+
const ids = [];
|
|
279
|
+
for (const item of items)
|
|
280
|
+
ids.push(item._id);
|
|
275
281
|
let metadatas;
|
|
276
282
|
if (config.onObjectsUpdated) {
|
|
277
283
|
try {
|
|
@@ -1722,11 +1728,12 @@ function savePendingWrite(tenant, collection, id, delta) {
|
|
|
1722
1728
|
let pending;
|
|
1723
1729
|
if (existingRaw) {
|
|
1724
1730
|
const existing = dist_default.parse(existingRaw);
|
|
1731
|
+
Object.assign(existing.data, delta);
|
|
1725
1732
|
pending = {
|
|
1726
1733
|
tenant,
|
|
1727
1734
|
collection,
|
|
1728
1735
|
id: String(id),
|
|
1729
|
-
data:
|
|
1736
|
+
data: existing.data,
|
|
1730
1737
|
timestamp: now
|
|
1731
1738
|
};
|
|
1732
1739
|
} else {
|
|
@@ -1808,7 +1815,7 @@ class PendingChangesManager {
|
|
|
1808
1815
|
}
|
|
1809
1816
|
const deltaWithId = existing ? data : { _id: id, ...data };
|
|
1810
1817
|
savePendingWrite(this.tenant, collection, id, deltaWithId);
|
|
1811
|
-
const fullData = existing ?
|
|
1818
|
+
const fullData = existing ? Object.assign(existing.data, data) : { _id: id, ...data };
|
|
1812
1819
|
const timer = setTimeout(() => {
|
|
1813
1820
|
this.executePendingChange(key);
|
|
1814
1821
|
}, this.debounceDexieWritesMs);
|
|
@@ -1879,7 +1886,8 @@ class PendingChangesManager {
|
|
|
1879
1886
|
baseMeta: { _ts: existing?._ts, _rev: existing?._rev }
|
|
1880
1887
|
});
|
|
1881
1888
|
if (existing) {
|
|
1882
|
-
|
|
1889
|
+
Object.assign(existing, write.data);
|
|
1890
|
+
saveBatch.push(existing);
|
|
1883
1891
|
} else {
|
|
1884
1892
|
insertBatch.push(write.data);
|
|
1885
1893
|
}
|
|
@@ -1887,7 +1895,7 @@ class PendingChangesManager {
|
|
|
1887
1895
|
if (dirtyChangesBatch.length > 0) {
|
|
1888
1896
|
await this.deps.dexieDb.addDirtyChangesBatch(collection, dirtyChangesBatch);
|
|
1889
1897
|
}
|
|
1890
|
-
const allToSave =
|
|
1898
|
+
const allToSave = saveBatch.concat(insertBatch);
|
|
1891
1899
|
if (allToSave.length > 0) {
|
|
1892
1900
|
await this.deps.dexieDb.saveMany(collection, allToSave);
|
|
1893
1901
|
}
|
|
@@ -2063,20 +2071,26 @@ function mergeObjects(local, external) {
|
|
|
2063
2071
|
}
|
|
2064
2072
|
function mergeArrays(local, external) {
|
|
2065
2073
|
if (local.length === 0) {
|
|
2066
|
-
return
|
|
2074
|
+
return external.slice();
|
|
2067
2075
|
}
|
|
2068
2076
|
const firstLocal = local[0];
|
|
2069
2077
|
const firstExternal = external[0];
|
|
2070
2078
|
if (typeof firstLocal === "string" || typeof firstExternal === "string") {
|
|
2071
|
-
|
|
2079
|
+
const set2 = new Set(local);
|
|
2080
|
+
for (const item of external)
|
|
2081
|
+
set2.add(item);
|
|
2082
|
+
return Array.from(set2);
|
|
2072
2083
|
}
|
|
2073
2084
|
if (isPlainObject3(firstLocal) || isPlainObject3(firstExternal)) {
|
|
2074
2085
|
return mergeObjectArrays(local, external);
|
|
2075
2086
|
}
|
|
2076
|
-
|
|
2087
|
+
const set = new Set(local);
|
|
2088
|
+
for (const item of external)
|
|
2089
|
+
set.add(item);
|
|
2090
|
+
return Array.from(set);
|
|
2077
2091
|
}
|
|
2078
2092
|
function mergeObjectArrays(local, external) {
|
|
2079
|
-
const result =
|
|
2093
|
+
const result = local.slice();
|
|
2080
2094
|
const localIds = new Map;
|
|
2081
2095
|
for (let i = 0;i < local.length; i++) {
|
|
2082
2096
|
const item = local[i];
|
|
@@ -2155,13 +2169,11 @@ class SyncEngine {
|
|
|
2155
2169
|
}
|
|
2156
2170
|
this.callOnFindNewerManyCall(syncSpecs, calledFrom);
|
|
2157
2171
|
const findNewerManyStartTime = Date.now();
|
|
2158
|
-
const allUpdatedIds = {};
|
|
2159
2172
|
const collectionState = new Map;
|
|
2160
2173
|
for (const [name] of configMap) {
|
|
2161
2174
|
collectionState.set(name, {
|
|
2162
2175
|
maxTs: undefined,
|
|
2163
2176
|
conflicts: 0,
|
|
2164
|
-
updatedIds: [],
|
|
2165
2177
|
receivedCount: 0
|
|
2166
2178
|
});
|
|
2167
2179
|
}
|
|
@@ -2179,28 +2191,23 @@ class SyncEngine {
|
|
|
2179
2191
|
state.maxTs = stats.maxTs;
|
|
2180
2192
|
}
|
|
2181
2193
|
}
|
|
2182
|
-
|
|
2194
|
+
if (stats.updatedIds.length > 0) {
|
|
2195
|
+
this.deps.broadcastUpdates({ [collection]: stats.updatedIds });
|
|
2196
|
+
}
|
|
2183
2197
|
}), "findNewerManyStream");
|
|
2184
2198
|
for (const [name, state] of collectionState) {
|
|
2185
2199
|
receivedCount += state.receivedCount;
|
|
2186
2200
|
conflictsResolved += state.conflicts;
|
|
2187
2201
|
collectionStats[name] = {
|
|
2188
2202
|
receivedCount: state.receivedCount,
|
|
2189
|
-
sentCount: 0
|
|
2190
|
-
receivedItems: []
|
|
2203
|
+
sentCount: 0
|
|
2191
2204
|
};
|
|
2192
|
-
if (state.updatedIds.length > 0) {
|
|
2193
|
-
allUpdatedIds[name] = state.updatedIds;
|
|
2194
|
-
}
|
|
2195
2205
|
}
|
|
2196
2206
|
this.callOnFindNewerManyResult(syncSpecs, {}, findNewerManyStartTime, true, calledFrom);
|
|
2197
2207
|
} catch (err) {
|
|
2198
2208
|
this.callOnFindNewerManyResult(syncSpecs, {}, findNewerManyStartTime, false, calledFrom, err);
|
|
2199
2209
|
throw err;
|
|
2200
2210
|
}
|
|
2201
|
-
if (Object.keys(allUpdatedIds).length > 0) {
|
|
2202
|
-
this.deps.broadcastUpdates(allUpdatedIds);
|
|
2203
|
-
}
|
|
2204
2211
|
const uploadStats = await this.uploadDirtyItems(calledFrom);
|
|
2205
2212
|
sentCount = uploadStats.sentCount;
|
|
2206
2213
|
for (const [collectionName, stats] of Object.entries(uploadStats.collectionSentCounts || {})) {
|
|
@@ -2209,8 +2216,7 @@ class SyncEngine {
|
|
|
2209
2216
|
} else {
|
|
2210
2217
|
collectionStats[collectionName] = {
|
|
2211
2218
|
receivedCount: 0,
|
|
2212
|
-
sentCount: stats
|
|
2213
|
-
receivedItems: []
|
|
2219
|
+
sentCount: stats
|
|
2214
2220
|
};
|
|
2215
2221
|
}
|
|
2216
2222
|
}
|
|
@@ -2297,18 +2303,22 @@ class SyncEngine {
|
|
|
2297
2303
|
const collectionSentCounts = {};
|
|
2298
2304
|
for (const result of results) {
|
|
2299
2305
|
const { collection, results: { inserted, updated, deleted, errors } } = result;
|
|
2300
|
-
const allSuccessIds = [
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2306
|
+
const allSuccessIds = [];
|
|
2307
|
+
for (const e of inserted)
|
|
2308
|
+
allSuccessIds.push(e._id);
|
|
2309
|
+
for (const e of updated)
|
|
2310
|
+
allSuccessIds.push(e._id);
|
|
2311
|
+
for (const e of deleted)
|
|
2312
|
+
allSuccessIds.push(e._id);
|
|
2305
2313
|
if (allSuccessIds.length > 0) {
|
|
2306
2314
|
await this.dexieDb.clearDirtyChangesBatch(collection, allSuccessIds);
|
|
2307
2315
|
}
|
|
2308
2316
|
let collectionSentCount = 0;
|
|
2309
|
-
const insertedAndUpdated =
|
|
2317
|
+
const insertedAndUpdated = inserted.concat(updated);
|
|
2310
2318
|
if (insertedAndUpdated.length > 0) {
|
|
2311
|
-
const idsToCheck =
|
|
2319
|
+
const idsToCheck = [];
|
|
2320
|
+
for (const e of insertedAndUpdated)
|
|
2321
|
+
idsToCheck.push(e._id);
|
|
2312
2322
|
const dexieItems = await this.dexieDb.getByIds(collection, idsToCheck);
|
|
2313
2323
|
const dexieSaveBatch = [];
|
|
2314
2324
|
const inMemUpdateBatch = [];
|
|
@@ -2316,11 +2326,9 @@ class SyncEngine {
|
|
|
2316
2326
|
const entity = insertedAndUpdated[i];
|
|
2317
2327
|
const dexieItem = dexieItems[i];
|
|
2318
2328
|
if (dexieItem) {
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
_ts: entity._ts
|
|
2323
|
-
});
|
|
2329
|
+
dexieItem._rev = entity._rev;
|
|
2330
|
+
dexieItem._ts = entity._ts;
|
|
2331
|
+
dexieSaveBatch.push(dexieItem);
|
|
2324
2332
|
if (!dexieItem._deleted) {
|
|
2325
2333
|
const inMemItem = this.deps.getInMemById(collection, entity._id);
|
|
2326
2334
|
if (inMemItem) {
|
|
@@ -2343,21 +2351,27 @@ class SyncEngine {
|
|
|
2343
2351
|
collectionSentCount += insertedAndUpdated.length;
|
|
2344
2352
|
}
|
|
2345
2353
|
if (deleted.length > 0) {
|
|
2346
|
-
const deleteIds =
|
|
2354
|
+
const deleteIds = [];
|
|
2355
|
+
const deleteDbEntities = [];
|
|
2356
|
+
for (const e of deleted) {
|
|
2357
|
+
deleteIds.push(e._id);
|
|
2358
|
+
deleteDbEntities.push({ _id: e._id });
|
|
2359
|
+
}
|
|
2347
2360
|
await this.dexieDb.deleteMany(collection, deleteIds);
|
|
2348
|
-
this.deps.writeToInMemBatch(collection,
|
|
2361
|
+
this.deps.writeToInMemBatch(collection, deleteDbEntities, "delete");
|
|
2349
2362
|
sentCount += deleted.length;
|
|
2350
2363
|
collectionSentCount += deleted.length;
|
|
2351
2364
|
}
|
|
2352
2365
|
if (collectionSentCount > 0) {
|
|
2353
2366
|
collectionSentCounts[collection] = collectionSentCount;
|
|
2354
2367
|
}
|
|
2355
|
-
const allItems = [...inserted, ...updated, ...deleted];
|
|
2356
2368
|
let maxTs = undefined;
|
|
2357
|
-
for (const
|
|
2358
|
-
|
|
2359
|
-
if (
|
|
2360
|
-
maxTs
|
|
2369
|
+
for (const arr of [inserted, updated, deleted]) {
|
|
2370
|
+
for (const item of arr) {
|
|
2371
|
+
if (item._ts) {
|
|
2372
|
+
if (!maxTs || this.compareTimestamps(item._ts, maxTs) > 0) {
|
|
2373
|
+
maxTs = item._ts;
|
|
2374
|
+
}
|
|
2361
2375
|
}
|
|
2362
2376
|
}
|
|
2363
2377
|
}
|
|
@@ -2418,11 +2432,13 @@ class SyncEngine {
|
|
|
2418
2432
|
let sentCount = 0;
|
|
2419
2433
|
for (const result of results) {
|
|
2420
2434
|
const { results: { inserted, updated, deleted } } = result;
|
|
2421
|
-
const allSuccessIds = [
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2435
|
+
const allSuccessIds = [];
|
|
2436
|
+
for (const e of inserted)
|
|
2437
|
+
allSuccessIds.push(e._id);
|
|
2438
|
+
for (const e of updated)
|
|
2439
|
+
allSuccessIds.push(e._id);
|
|
2440
|
+
for (const e of deleted)
|
|
2441
|
+
allSuccessIds.push(e._id);
|
|
2426
2442
|
if (allSuccessIds.length > 0) {
|
|
2427
2443
|
await this.dexieDb.clearDirtyChangesBatch(collection, allSuccessIds);
|
|
2428
2444
|
}
|
|
@@ -2451,7 +2467,9 @@ class SyncEngine {
|
|
|
2451
2467
|
const BATCH = SyncEngine.SYNC_BATCH_SIZE;
|
|
2452
2468
|
for (let offset = 0;offset < serverData.length; offset += BATCH) {
|
|
2453
2469
|
const chunk = serverData.slice(offset, offset + BATCH);
|
|
2454
|
-
const chunkIds =
|
|
2470
|
+
const chunkIds = [];
|
|
2471
|
+
for (const item of chunk)
|
|
2472
|
+
chunkIds.push(item._id);
|
|
2455
2473
|
const localItems = await this.dexieDb.getByIds(collectionName, chunkIds);
|
|
2456
2474
|
const dirtyChangesMap = await this.dexieDb.getDirtyChangesBatch(collectionName, chunkIds);
|
|
2457
2475
|
const dexieBatch = [];
|
|
@@ -2461,6 +2479,7 @@ class SyncEngine {
|
|
|
2461
2479
|
const serverItem = chunk[i];
|
|
2462
2480
|
const localItem = localItems[i];
|
|
2463
2481
|
const dirtyChange = dirtyChangesMap.get(String(serverItem._id));
|
|
2482
|
+
allUpdatedIds.push(String(serverItem._id));
|
|
2464
2483
|
if (serverItem._ts) {
|
|
2465
2484
|
if (!maxTs || this.compareTimestamps(serverItem._ts, maxTs) > 0) {
|
|
2466
2485
|
maxTs = serverItem._ts;
|
|
@@ -2500,9 +2519,6 @@ class SyncEngine {
|
|
|
2500
2519
|
if (inMemDeleteIds.length > 0) {
|
|
2501
2520
|
this.deps.writeToInMemBatch(collectionName, inMemDeleteIds.map((id) => ({ _id: id })), "delete");
|
|
2502
2521
|
}
|
|
2503
|
-
for (const id of chunkIds) {
|
|
2504
|
-
allUpdatedIds.push(String(id));
|
|
2505
|
-
}
|
|
2506
2522
|
}
|
|
2507
2523
|
if (maxTs) {
|
|
2508
2524
|
await this.dexieDb.setSyncMeta(collectionName, maxTs);
|
|
@@ -2759,9 +2775,9 @@ class ServerUpdateHandler {
|
|
|
2759
2775
|
if (pendingChange) {
|
|
2760
2776
|
const newFields = this.getNewFieldsFromServer(localItem, serverDelta);
|
|
2761
2777
|
if (Object.keys(newFields).length > 0) {
|
|
2762
|
-
|
|
2778
|
+
Object.assign(pendingChange.data, newFields);
|
|
2763
2779
|
}
|
|
2764
|
-
const currentInMemState = {
|
|
2780
|
+
const currentInMemState = Object.assign({}, localItem, pendingChange.data);
|
|
2765
2781
|
const merged = this.mergeLocalWithDelta(currentInMemState, serverDelta);
|
|
2766
2782
|
if (!merged._deleted && !merged._archived) {
|
|
2767
2783
|
this.deps.writeToInMemBatch(collection, [this.stripLocalFields(merged)], "upsert");
|
|
@@ -2820,13 +2836,12 @@ class ServerUpdateHandler {
|
|
|
2820
2836
|
return false;
|
|
2821
2837
|
}
|
|
2822
2838
|
mergeLocalWithDelta(local, delta) {
|
|
2823
|
-
const result = { ...local };
|
|
2824
2839
|
for (const key of Object.keys(delta)) {
|
|
2825
2840
|
if (key === "_id" || key === "_dirty")
|
|
2826
2841
|
continue;
|
|
2827
|
-
|
|
2842
|
+
local[key] = delta[key];
|
|
2828
2843
|
}
|
|
2829
|
-
return
|
|
2844
|
+
return local;
|
|
2830
2845
|
}
|
|
2831
2846
|
getNewFieldsFromServer(local, server) {
|
|
2832
2847
|
const newFields = {};
|
|
@@ -3278,16 +3293,20 @@ class SyncedDb {
|
|
|
3278
3293
|
}
|
|
3279
3294
|
await this.pendingChanges.recoverPendingWrites();
|
|
3280
3295
|
for (const [name] of this.collections) {
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3296
|
+
this.inMemManager.clearCollection(name);
|
|
3297
|
+
await this.dexieDb.forEachBatch(name, 2000, async (chunk) => {
|
|
3298
|
+
let writeIdx = 0;
|
|
3299
|
+
for (let i = 0;i < chunk.length; i++) {
|
|
3300
|
+
const item = chunk[i];
|
|
3301
|
+
if (!item._deleted && !item._archived) {
|
|
3302
|
+
chunk[writeIdx++] = item;
|
|
3303
|
+
}
|
|
3287
3304
|
}
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3305
|
+
chunk.length = writeIdx;
|
|
3306
|
+
if (chunk.length > 0) {
|
|
3307
|
+
this.inMemManager.writeBatch(name, chunk, "upsert");
|
|
3308
|
+
}
|
|
3309
|
+
});
|
|
3291
3310
|
const meta = await this.dexieDb.getSyncMeta(name);
|
|
3292
3311
|
if (meta) {
|
|
3293
3312
|
this.syncMetaCache.set(name, meta);
|
|
@@ -3449,26 +3468,48 @@ class SyncedDb {
|
|
|
3449
3468
|
await this.syncCollectionForFind(collection, query, opts);
|
|
3450
3469
|
}
|
|
3451
3470
|
const all = await this.dexieDb.getAll(collection);
|
|
3452
|
-
const
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3471
|
+
const returnDeleted = opts?.returnDeleted;
|
|
3472
|
+
const returnArchived = opts?.returnArchived;
|
|
3473
|
+
const hasSort = opts?.sort && Object.keys(opts.sort).length > 0;
|
|
3474
|
+
if (hasSort) {
|
|
3475
|
+
const filtered = [];
|
|
3476
|
+
for (const item of all) {
|
|
3477
|
+
if (!returnDeleted && item._deleted)
|
|
3478
|
+
continue;
|
|
3479
|
+
if (!returnArchived && item._archived)
|
|
3480
|
+
continue;
|
|
3481
|
+
if (!matchesQuery(item, query))
|
|
3482
|
+
continue;
|
|
3483
|
+
filtered.push(item);
|
|
3484
|
+
}
|
|
3485
|
+
if (filtered.length === 0) {
|
|
3486
|
+
if (opts?.referToServer && this.isOnline())
|
|
3487
|
+
this.referToServerSync(collection, query);
|
|
3488
|
+
return null;
|
|
3489
|
+
}
|
|
3490
|
+
const sorted = applyQueryOpts(filtered, { sort: opts.sort, project: opts?.project });
|
|
3491
|
+
if (opts?.referToServer && this.isOnline())
|
|
3462
3492
|
this.referToServerSync(collection, query);
|
|
3493
|
+
return sorted[0] ?? null;
|
|
3494
|
+
} else {
|
|
3495
|
+
let result = null;
|
|
3496
|
+
for (const item of all) {
|
|
3497
|
+
if (!returnDeleted && item._deleted)
|
|
3498
|
+
continue;
|
|
3499
|
+
if (!returnArchived && item._archived)
|
|
3500
|
+
continue;
|
|
3501
|
+
if (!matchesQuery(item, query))
|
|
3502
|
+
continue;
|
|
3503
|
+
result = item;
|
|
3504
|
+
break;
|
|
3463
3505
|
}
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3506
|
+
if (result && opts?.project) {
|
|
3507
|
+
result = projectItem(result, opts.project);
|
|
3508
|
+
}
|
|
3509
|
+
if (opts?.referToServer && this.isOnline())
|
|
3510
|
+
this.referToServerSync(collection, query);
|
|
3511
|
+
return result;
|
|
3470
3512
|
}
|
|
3471
|
-
return result;
|
|
3472
3513
|
}
|
|
3473
3514
|
async find(collection, query, opts) {
|
|
3474
3515
|
this.assertCollection(collection);
|
|
@@ -3477,14 +3518,19 @@ class SyncedDb {
|
|
|
3477
3518
|
await this.syncCollectionForFind(collection, query, opts);
|
|
3478
3519
|
}
|
|
3479
3520
|
const all = await this.dexieDb.getAll(collection);
|
|
3480
|
-
const
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3521
|
+
const returnDeleted = opts?.returnDeleted;
|
|
3522
|
+
const returnArchived = opts?.returnArchived;
|
|
3523
|
+
const hasQuery = query && Object.keys(query).length > 0;
|
|
3524
|
+
const filtered = [];
|
|
3525
|
+
for (const item of all) {
|
|
3526
|
+
if (!returnDeleted && item._deleted)
|
|
3527
|
+
continue;
|
|
3528
|
+
if (!returnArchived && item._archived)
|
|
3529
|
+
continue;
|
|
3530
|
+
if (hasQuery && !matchesQuery(item, query))
|
|
3531
|
+
continue;
|
|
3532
|
+
filtered.push(item);
|
|
3533
|
+
}
|
|
3488
3534
|
const result = applyQueryOpts(filtered, opts);
|
|
3489
3535
|
if (opts?.referToServer && this.isOnline()) {
|
|
3490
3536
|
this.referToServerSync(collection, query);
|
|
@@ -3957,7 +4003,9 @@ class DexieDb extends Dexie {
|
|
|
3957
4003
|
if (ids.length === 0)
|
|
3958
4004
|
return;
|
|
3959
4005
|
const table = this.getTable(collection);
|
|
3960
|
-
const keys =
|
|
4006
|
+
const keys = [];
|
|
4007
|
+
for (const id of ids)
|
|
4008
|
+
keys.push(this.idToString(id));
|
|
3961
4009
|
await table.bulkDelete(keys);
|
|
3962
4010
|
}
|
|
3963
4011
|
async saveCollection(collection, data) {
|
|
@@ -3980,36 +4028,50 @@ class DexieDb extends Dexie {
|
|
|
3980
4028
|
if (ids.length === 0)
|
|
3981
4029
|
return [];
|
|
3982
4030
|
const table = this.getTable(collection);
|
|
3983
|
-
const keys =
|
|
4031
|
+
const keys = [];
|
|
4032
|
+
for (const id of ids)
|
|
4033
|
+
keys.push(this.idToString(id));
|
|
3984
4034
|
return await table.bulkGet(keys);
|
|
3985
4035
|
}
|
|
3986
4036
|
async getAll(collection) {
|
|
3987
4037
|
const table = this.getTable(collection);
|
|
3988
4038
|
return await table.toArray();
|
|
3989
4039
|
}
|
|
4040
|
+
async forEachBatch(collection, batchSize, callback) {
|
|
4041
|
+
const table = this.getTable(collection);
|
|
4042
|
+
let offset = 0;
|
|
4043
|
+
while (true) {
|
|
4044
|
+
const items = await table.offset(offset).limit(batchSize).toArray();
|
|
4045
|
+
if (items.length === 0)
|
|
4046
|
+
break;
|
|
4047
|
+
await callback(items);
|
|
4048
|
+
if (items.length < batchSize)
|
|
4049
|
+
break;
|
|
4050
|
+
offset += items.length;
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
3990
4053
|
async count(collection) {
|
|
3991
4054
|
const table = this.getTable(collection);
|
|
3992
4055
|
return await table.count();
|
|
3993
4056
|
}
|
|
3994
4057
|
async getDirty(collection) {
|
|
3995
4058
|
const dirtyEntries = await this.dirtyChanges.where("[collection+id]").between([collection, Dexie.minKey], [collection, Dexie.maxKey]).toArray();
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
}
|
|
4059
|
+
const result = [];
|
|
4060
|
+
for (const entry of dirtyEntries) {
|
|
4061
|
+
const obj = { _id: entry.id, _ts: entry.baseTs, _rev: entry.baseRev };
|
|
4062
|
+
Object.assign(obj, entry.changes);
|
|
4063
|
+
result.push(obj);
|
|
4064
|
+
}
|
|
4065
|
+
return result;
|
|
4002
4066
|
}
|
|
4003
4067
|
async addDirtyChange(collection, id, changes, baseMeta) {
|
|
4004
4068
|
const stringId = this.idToString(id);
|
|
4005
4069
|
const existing = await this.dirtyChanges.get([collection, stringId]);
|
|
4006
4070
|
const now = Date.now();
|
|
4007
4071
|
if (existing) {
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
updatedAt: now
|
|
4012
|
-
});
|
|
4072
|
+
Object.assign(existing.changes, changes);
|
|
4073
|
+
existing.updatedAt = now;
|
|
4074
|
+
await this.dirtyChanges.put(existing);
|
|
4013
4075
|
} else {
|
|
4014
4076
|
await this.dirtyChanges.put({
|
|
4015
4077
|
collection,
|
|
@@ -4026,7 +4088,9 @@ class DexieDb extends Dexie {
|
|
|
4026
4088
|
if (changesList.length === 0)
|
|
4027
4089
|
return;
|
|
4028
4090
|
const now = Date.now();
|
|
4029
|
-
const keys =
|
|
4091
|
+
const keys = [];
|
|
4092
|
+
for (const c of changesList)
|
|
4093
|
+
keys.push([collection, this.idToString(c.id)]);
|
|
4030
4094
|
const existingEntries = await this.dirtyChanges.bulkGet(keys);
|
|
4031
4095
|
const toWrite = [];
|
|
4032
4096
|
for (let i = 0;i < changesList.length; i++) {
|
|
@@ -4034,11 +4098,9 @@ class DexieDb extends Dexie {
|
|
|
4034
4098
|
const stringId = this.idToString(changeItem.id);
|
|
4035
4099
|
const existing = existingEntries[i];
|
|
4036
4100
|
if (existing) {
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
updatedAt: now
|
|
4041
|
-
});
|
|
4101
|
+
Object.assign(existing.changes, changeItem.changes);
|
|
4102
|
+
existing.updatedAt = now;
|
|
4103
|
+
toWrite.push(existing);
|
|
4042
4104
|
} else {
|
|
4043
4105
|
toWrite.push({
|
|
4044
4106
|
collection,
|
|
@@ -4061,7 +4123,9 @@ class DexieDb extends Dexie {
|
|
|
4061
4123
|
const result = new Map;
|
|
4062
4124
|
if (ids.length === 0)
|
|
4063
4125
|
return result;
|
|
4064
|
-
const keys =
|
|
4126
|
+
const keys = [];
|
|
4127
|
+
for (const id of ids)
|
|
4128
|
+
keys.push([collection, this.idToString(id)]);
|
|
4065
4129
|
const entries = await this.dirtyChanges.bulkGet(keys);
|
|
4066
4130
|
for (let i = 0;i < ids.length; i++) {
|
|
4067
4131
|
const entry = entries[i];
|
|
@@ -4078,7 +4142,9 @@ class DexieDb extends Dexie {
|
|
|
4078
4142
|
async clearDirtyChangesBatch(collection, ids) {
|
|
4079
4143
|
if (ids.length === 0)
|
|
4080
4144
|
return;
|
|
4081
|
-
const keys =
|
|
4145
|
+
const keys = [];
|
|
4146
|
+
for (const id of ids)
|
|
4147
|
+
keys.push([collection, this.idToString(id)]);
|
|
4082
4148
|
await this.dirtyChanges.bulkDelete(keys);
|
|
4083
4149
|
}
|
|
4084
4150
|
async clearDirtyChanges(collection) {
|
|
@@ -6245,11 +6311,46 @@ var pack2 = (x) => packr.pack(preprocessForPack(x));
|
|
|
6245
6311
|
var unpack2 = (x) => unpackr.unpack(x);
|
|
6246
6312
|
var DEFAULT_TIMEOUT = 5000;
|
|
6247
6313
|
var DEFAULT_PROGRESS_CHUNK_SIZE = 16 * 1024;
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6314
|
+
|
|
6315
|
+
class StreamBuffer {
|
|
6316
|
+
buf;
|
|
6317
|
+
rPos = 0;
|
|
6318
|
+
wPos = 0;
|
|
6319
|
+
constructor(initialCapacity = 64 * 1024) {
|
|
6320
|
+
this.buf = new Uint8Array(initialCapacity);
|
|
6321
|
+
}
|
|
6322
|
+
get length() {
|
|
6323
|
+
return this.wPos - this.rPos;
|
|
6324
|
+
}
|
|
6325
|
+
append(data) {
|
|
6326
|
+
if (this.rPos > 0 && this.wPos + data.length > this.buf.length) {
|
|
6327
|
+
this.buf.copyWithin(0, this.rPos, this.wPos);
|
|
6328
|
+
this.wPos -= this.rPos;
|
|
6329
|
+
this.rPos = 0;
|
|
6330
|
+
}
|
|
6331
|
+
if (this.wPos + data.length > this.buf.length) {
|
|
6332
|
+
const newSize = Math.max(this.buf.length * 2, this.wPos + data.length);
|
|
6333
|
+
const newBuf = new Uint8Array(newSize);
|
|
6334
|
+
newBuf.set(this.buf.subarray(0, this.wPos));
|
|
6335
|
+
this.buf = newBuf;
|
|
6336
|
+
}
|
|
6337
|
+
this.buf.set(data, this.wPos);
|
|
6338
|
+
this.wPos += data.length;
|
|
6339
|
+
}
|
|
6340
|
+
subarray(start, end) {
|
|
6341
|
+
return this.buf.subarray(this.rPos + start, this.rPos + end);
|
|
6342
|
+
}
|
|
6343
|
+
at(offset) {
|
|
6344
|
+
return this.buf[this.rPos + offset];
|
|
6345
|
+
}
|
|
6346
|
+
consume(n) {
|
|
6347
|
+
this.rPos += n;
|
|
6348
|
+
if (this.rPos > this.buf.length >>> 1) {
|
|
6349
|
+
this.buf.copyWithin(0, this.rPos, this.wPos);
|
|
6350
|
+
this.wPos -= this.rPos;
|
|
6351
|
+
this.rPos = 0;
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6253
6354
|
}
|
|
6254
6355
|
|
|
6255
6356
|
class RestProxy {
|
|
@@ -6479,23 +6580,24 @@ class RestProxy {
|
|
|
6479
6580
|
}
|
|
6480
6581
|
async parseStreamingResponse(response, onChunk, onActivity) {
|
|
6481
6582
|
const reader = response.body.getReader();
|
|
6482
|
-
|
|
6583
|
+
const buffer = new StreamBuffer;
|
|
6584
|
+
const decoder2 = new TextDecoder;
|
|
6483
6585
|
const readMore = async () => {
|
|
6484
6586
|
const { done, value } = await reader.read();
|
|
6485
6587
|
if (done)
|
|
6486
6588
|
return false;
|
|
6487
6589
|
onActivity();
|
|
6488
|
-
buffer
|
|
6590
|
+
buffer.append(value);
|
|
6489
6591
|
return true;
|
|
6490
6592
|
};
|
|
6491
6593
|
while (buffer.length < 1) {
|
|
6492
6594
|
if (!await readMore())
|
|
6493
6595
|
return;
|
|
6494
6596
|
}
|
|
6495
|
-
const firstByte = buffer
|
|
6597
|
+
const firstByte = buffer.at(0);
|
|
6496
6598
|
if (firstByte !== 0 && firstByte !== 1) {
|
|
6497
6599
|
while (await readMore()) {}
|
|
6498
|
-
const result = unpack2(buffer);
|
|
6600
|
+
const result = unpack2(buffer.subarray(0, buffer.length));
|
|
6499
6601
|
for (const [collection, items] of Object.entries(result)) {
|
|
6500
6602
|
if (items.length > 0) {
|
|
6501
6603
|
await onChunk(collection, items);
|
|
@@ -6508,29 +6610,28 @@ class RestProxy {
|
|
|
6508
6610
|
if (!await readMore())
|
|
6509
6611
|
return;
|
|
6510
6612
|
}
|
|
6511
|
-
if (buffer
|
|
6613
|
+
if (buffer.at(0) === 0)
|
|
6512
6614
|
return;
|
|
6513
6615
|
while (buffer.length < 3) {
|
|
6514
6616
|
if (!await readMore())
|
|
6515
6617
|
throw new Error("Unexpected end of stream in chunk header");
|
|
6516
6618
|
}
|
|
6517
|
-
const nameLen = buffer
|
|
6619
|
+
const nameLen = buffer.at(1) << 8 | buffer.at(2);
|
|
6518
6620
|
const headerSize = 1 + 2 + nameLen + 4;
|
|
6519
6621
|
while (buffer.length < headerSize) {
|
|
6520
6622
|
if (!await readMore())
|
|
6521
6623
|
throw new Error("Unexpected end of stream in chunk header");
|
|
6522
6624
|
}
|
|
6523
|
-
const collection =
|
|
6625
|
+
const collection = decoder2.decode(buffer.subarray(3, 3 + nameLen));
|
|
6524
6626
|
const dataOffset = 3 + nameLen;
|
|
6525
|
-
const dataLen = buffer
|
|
6627
|
+
const dataLen = buffer.at(dataOffset) << 24 | buffer.at(dataOffset + 1) << 16 | buffer.at(dataOffset + 2) << 8 | buffer.at(dataOffset + 3);
|
|
6526
6628
|
const totalChunkSize = headerSize + dataLen;
|
|
6527
6629
|
while (buffer.length < totalChunkSize) {
|
|
6528
6630
|
if (!await readMore())
|
|
6529
6631
|
throw new Error("Unexpected end of stream in chunk data");
|
|
6530
6632
|
}
|
|
6531
|
-
const
|
|
6532
|
-
|
|
6533
|
-
buffer = buffer.slice(totalChunkSize);
|
|
6633
|
+
const items = unpack2(buffer.subarray(headerSize, totalChunkSize));
|
|
6634
|
+
buffer.consume(totalChunkSize);
|
|
6534
6635
|
await onChunk(collection, items);
|
|
6535
6636
|
}
|
|
6536
6637
|
}
|
package/dist/src/db/DexieDb.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare class DexieDb extends Dexie implements I_DexieDb {
|
|
|
25
25
|
getById<T extends LocalDbEntity>(collection: string, id: Id): Promise<T | undefined>;
|
|
26
26
|
getByIds<T extends LocalDbEntity>(collection: string, ids: Id[]): Promise<(T | undefined)[]>;
|
|
27
27
|
getAll<T extends LocalDbEntity>(collection: string): Promise<T[]>;
|
|
28
|
+
forEachBatch<T extends LocalDbEntity>(collection: string, batchSize: number, callback: (items: T[]) => Promise<void>): Promise<void>;
|
|
28
29
|
count(collection: string): Promise<number>;
|
|
29
30
|
getDirty<T extends LocalDbEntity>(collection: string): Promise<Partial<T>[]>;
|
|
30
31
|
addDirtyChange(collection: string, id: Id, changes: Record<string, any>, baseMeta?: {
|
|
@@ -38,6 +38,7 @@ export declare class ServerUpdateHandler implements I_ServerUpdateHandler {
|
|
|
38
38
|
handleServerItemDelete(collection: string, id: Id): Promise<void>;
|
|
39
39
|
private stripLocalFields;
|
|
40
40
|
private timestampsEqual;
|
|
41
|
+
/** @mutates local — mutira vhodni objekt namesto kopiranja */
|
|
41
42
|
private mergeLocalWithDelta;
|
|
42
43
|
private getNewFieldsFromServer;
|
|
43
44
|
private callOnWsNotification;
|
|
@@ -64,6 +64,8 @@ export interface I_DexieDb {
|
|
|
64
64
|
getByIds<T extends LocalDbEntity>(collection: string, ids: Id[]): Promise<(T | undefined)[]>;
|
|
65
65
|
/** Vrne vse objekte v kolekciji */
|
|
66
66
|
getAll<T extends LocalDbEntity>(collection: string): Promise<T[]>;
|
|
67
|
+
/** Iterira po kolekciji v chunkih brez nalaganja vseh zapisov naenkrat */
|
|
68
|
+
forEachBatch<T extends LocalDbEntity>(collection: string, batchSize: number, callback: (items: T[]) => Promise<void>): Promise<void>;
|
|
67
69
|
/** Vrne vse dirty objekte (z lokalnimi spremembami) - returns only changed fields + _id + metadata */
|
|
68
70
|
getDirty<T extends LocalDbEntity>(collection: string): Promise<Partial<T>[]>;
|
|
69
71
|
/** Add or accumulate changes for a record */
|
|
@@ -206,8 +206,6 @@ export interface CollectionSyncStats {
|
|
|
206
206
|
receivedCount: number;
|
|
207
207
|
/** Number of dirty items sent to server for this collection */
|
|
208
208
|
sentCount: number;
|
|
209
|
-
/** @deprecated Use receivedCount instead. Will be empty array in future streaming mode. */
|
|
210
|
-
receivedItems: LocalDbEntity[];
|
|
211
209
|
}
|
|
212
210
|
/**
|
|
213
211
|
* Informacije o sinhronizaciji za debugging/logging
|