cry-synced-db-client 0.1.167 → 0.1.170

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
@@ -329,6 +329,22 @@ function sameIdSequence(a, b) {
329
329
  }
330
330
  return true;
331
331
  }
332
+ function containsIdArrayDescendant(value) {
333
+ if (value === null || typeof value !== "object") return false;
334
+ if (value instanceof Date || isObjectIdLike(value)) return false;
335
+ if (Array.isArray(value)) {
336
+ if (value.length > 0 && allElementsHaveId(value)) return true;
337
+ for (const v of value) {
338
+ if (containsIdArrayDescendant(v)) return true;
339
+ }
340
+ return false;
341
+ }
342
+ if (!isPlainObject(value)) return false;
343
+ for (const key of Object.keys(value)) {
344
+ if (containsIdArrayDescendant(value[key])) return true;
345
+ }
346
+ return false;
347
+ }
332
348
  function computeArrayDiff(existingArr, updateArr, basePath, diff) {
333
349
  if (existingArr.length === 0 && updateArr.length === 0) return;
334
350
  if (!allElementsHaveId(updateArr) || existingArr.length > 0 && !allElementsHaveId(existingArr)) {
@@ -354,12 +370,14 @@ function computeArrayDiff(existingArr, updateArr, basePath, diff) {
354
370
  if (sameIdSequence(existingArr, updateArr)) {
355
371
  for (let i = 0; i < updateArr.length; i++) {
356
372
  const elementId = String(updateArr[i]._id);
357
- computeDiffInto(
358
- existingArr[i],
359
- updateArr[i],
360
- `${basePath}[${elementId}]`,
361
- diff
362
- );
373
+ const elementPath = `${basePath}[${elementId}]`;
374
+ if (containsIdArrayDescendant(updateArr[i])) {
375
+ if (!deepEquals(existingArr[i], updateArr[i])) {
376
+ diff[elementPath] = updateArr[i];
377
+ }
378
+ } else {
379
+ computeDiffInto(existingArr[i], updateArr[i], elementPath, diff);
380
+ }
363
381
  }
364
382
  } else {
365
383
  diff[basePath] = updateArr;
@@ -383,12 +401,19 @@ function computeArrayDiff(existingArr, updateArr, basePath, diff) {
383
401
  for (const updateEl of updateArr) {
384
402
  const id = String(updateEl._id);
385
403
  if (existingIds.has(id)) {
386
- computeDiffInto(
387
- existingById.get(id),
388
- updateEl,
389
- `${basePath}[${id}]`,
390
- diff
391
- );
404
+ const elementPath = `${basePath}[${id}]`;
405
+ if (containsIdArrayDescendant(updateEl)) {
406
+ if (!deepEquals(existingById.get(id), updateEl)) {
407
+ diff[elementPath] = updateEl;
408
+ }
409
+ } else {
410
+ computeDiffInto(
411
+ existingById.get(id),
412
+ updateEl,
413
+ elementPath,
414
+ diff
415
+ );
416
+ }
392
417
  }
393
418
  }
394
419
  }
@@ -3271,11 +3296,45 @@ var _SyncEngine = class _SyncEngine {
3271
3296
  let sentCount = 0;
3272
3297
  const collectionSentCounts = {};
3273
3298
  for (const result of results) {
3274
- const { collection, results: { inserted, updated, deleted, errors: errors2 } } = result;
3299
+ const {
3300
+ collection,
3301
+ results: { inserted, updated, deleted, errors: errors2, warnings }
3302
+ } = result;
3303
+ const erroredIds = /* @__PURE__ */ new Set();
3304
+ if (errors2 && errors2.length > 0) {
3305
+ for (const e of errors2) erroredIds.add(String(e._id));
3306
+ }
3275
3307
  const allSuccessIds = [];
3276
- for (const e of inserted) allSuccessIds.push(e._id);
3277
- for (const e of updated) allSuccessIds.push(e._id);
3278
- for (const e of deleted) allSuccessIds.push(e._id);
3308
+ const ambiguous = [];
3309
+ for (const e of inserted) {
3310
+ const sid = String(e._id);
3311
+ if (erroredIds.has(sid)) {
3312
+ ambiguous.push(sid);
3313
+ continue;
3314
+ }
3315
+ allSuccessIds.push(e._id);
3316
+ }
3317
+ for (const e of updated) {
3318
+ const sid = String(e._id);
3319
+ if (erroredIds.has(sid)) {
3320
+ ambiguous.push(sid);
3321
+ continue;
3322
+ }
3323
+ allSuccessIds.push(e._id);
3324
+ }
3325
+ for (const e of deleted) {
3326
+ const sid = String(e._id);
3327
+ if (erroredIds.has(sid)) {
3328
+ ambiguous.push(sid);
3329
+ continue;
3330
+ }
3331
+ allSuccessIds.push(e._id);
3332
+ }
3333
+ if (ambiguous.length > 0) {
3334
+ console.error(
3335
+ `Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
3336
+ );
3337
+ }
3279
3338
  if (allSuccessIds.length > 0) {
3280
3339
  await this.dexieDb.clearDirtyChangesBatch(collection, allSuccessIds);
3281
3340
  }
@@ -3368,12 +3427,19 @@ var _SyncEngine = class _SyncEngine {
3368
3427
  });
3369
3428
  }
3370
3429
  }
3371
- if (errors2) {
3372
- console.error(
3373
- `Sync upload errors for ${collection}:`,
3374
- errors2,
3375
- "\u2014 dirty entries for failed items will persist until retry"
3376
- );
3430
+ if (errors2 && errors2.length > 0) {
3431
+ for (const e of errors2) {
3432
+ console.error(
3433
+ `Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
3434
+ );
3435
+ }
3436
+ }
3437
+ if (warnings && warnings.length > 0) {
3438
+ for (const w of warnings) {
3439
+ console.warn(
3440
+ `Sync upload warning [${collection}] _id=${w._id}: ${w.error}`
3441
+ );
3442
+ }
3377
3443
  }
3378
3444
  const sentIds = /* @__PURE__ */ new Set([
3379
3445
  ...collectionBatches.flat().filter((b) => b.collection === collection).flatMap((b) => [
@@ -3433,15 +3499,60 @@ var _SyncEngine = class _SyncEngine {
3433
3499
  );
3434
3500
  let sentCount = 0;
3435
3501
  for (const result of results) {
3436
- const { results: { inserted, updated, deleted } } = result;
3502
+ const {
3503
+ results: { inserted, updated, deleted, errors: errors2, warnings }
3504
+ } = result;
3505
+ const erroredIds = /* @__PURE__ */ new Set();
3506
+ if (errors2 && errors2.length > 0) {
3507
+ for (const e of errors2) {
3508
+ erroredIds.add(String(e._id));
3509
+ console.error(
3510
+ `Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
3511
+ );
3512
+ }
3513
+ }
3514
+ if (warnings && warnings.length > 0) {
3515
+ for (const w of warnings) {
3516
+ console.warn(
3517
+ `Sync upload warning [${collection}] _id=${w._id}: ${w.error}`
3518
+ );
3519
+ }
3520
+ }
3437
3521
  const allSuccessIds = [];
3438
- for (const e of inserted) allSuccessIds.push(e._id);
3439
- for (const e of updated) allSuccessIds.push(e._id);
3440
- for (const e of deleted) allSuccessIds.push(e._id);
3522
+ const ambiguous = [];
3523
+ for (const e of inserted) {
3524
+ const sid = String(e._id);
3525
+ if (erroredIds.has(sid)) {
3526
+ ambiguous.push(sid);
3527
+ continue;
3528
+ }
3529
+ allSuccessIds.push(e._id);
3530
+ }
3531
+ for (const e of updated) {
3532
+ const sid = String(e._id);
3533
+ if (erroredIds.has(sid)) {
3534
+ ambiguous.push(sid);
3535
+ continue;
3536
+ }
3537
+ allSuccessIds.push(e._id);
3538
+ }
3539
+ for (const e of deleted) {
3540
+ const sid = String(e._id);
3541
+ if (erroredIds.has(sid)) {
3542
+ ambiguous.push(sid);
3543
+ continue;
3544
+ }
3545
+ allSuccessIds.push(e._id);
3546
+ }
3547
+ if (ambiguous.length > 0) {
3548
+ console.error(
3549
+ `Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
3550
+ );
3551
+ }
3441
3552
  if (allSuccessIds.length > 0) {
3442
3553
  await this.dexieDb.clearDirtyChangesBatch(collection, allSuccessIds);
3443
3554
  }
3444
- sentCount += inserted.length + updated.length + deleted.length;
3555
+ sentCount += allSuccessIds.length;
3445
3556
  }
3446
3557
  return { sentCount };
3447
3558
  }
@@ -4270,8 +4381,8 @@ var _SyncedDb = class _SyncedDb {
4270
4381
  });
4271
4382
  this.pendingChanges = new PendingChangesManager({
4272
4383
  tenant: this.tenant,
4273
- debounceDexieWritesMs: (_l = config.debounceDexieWritesMs) != null ? _l : 500,
4274
- debounceRestWritesMs: (_m = config.debounceRestWritesMs) != null ? _m : 100,
4384
+ debounceDexieWritesMs: (_l = config.debounceDexieWritesMs) != null ? _l : 200,
4385
+ debounceRestWritesMs: (_m = config.debounceRestWritesMs) != null ? _m : 1e3,
4275
4386
  callbacks: {
4276
4387
  onDexieWriteRequest: config.onDexieWriteRequest,
4277
4388
  onDexieWriteResult: config.onDexieWriteResult,
@@ -6312,6 +6423,18 @@ var SyncedDb = _SyncedDb;
6312
6423
  import Dexie from "dexie";
6313
6424
  var SYNC_META_TABLE = "_sync_meta";
6314
6425
  var DIRTY_CHANGES_TABLE = "_dirty_changes";
6426
+ var META_ONLY_DIRTY_KEYS = /* @__PURE__ */ new Set([
6427
+ "_id",
6428
+ "_ts",
6429
+ "_rev",
6430
+ "_lastUpdaterId"
6431
+ ]);
6432
+ function isMetaOnlyChanges(changes) {
6433
+ for (const k of Object.keys(changes)) {
6434
+ if (!META_ONLY_DIRTY_KEYS.has(k)) return false;
6435
+ }
6436
+ return true;
6437
+ }
6315
6438
  var DexieDb = class extends Dexie {
6316
6439
  constructor(tenant, collectionConfigs) {
6317
6440
  super(`synced-db-${tenant}`);
@@ -6476,6 +6599,7 @@ var DexieDb = class extends Dexie {
6476
6599
  return result;
6477
6600
  }
6478
6601
  async addDirtyChange(collection, id, changes, baseMeta) {
6602
+ if (isMetaOnlyChanges(changes)) return;
6479
6603
  const stringId = this.idToString(id);
6480
6604
  const existing = await this.dirtyChanges.get([collection, stringId]);
6481
6605
  const now = Date.now();
@@ -6498,13 +6622,15 @@ var DexieDb = class extends Dexie {
6498
6622
  async addDirtyChangesBatch(collection, changesList) {
6499
6623
  var _a, _b;
6500
6624
  if (changesList.length === 0) return;
6625
+ const filtered = changesList.filter((c) => !isMetaOnlyChanges(c.changes));
6626
+ if (filtered.length === 0) return;
6501
6627
  const now = Date.now();
6502
6628
  const keys = [];
6503
- for (const c of changesList) keys.push([collection, this.idToString(c.id)]);
6629
+ for (const c of filtered) keys.push([collection, this.idToString(c.id)]);
6504
6630
  const existingEntries = await this.dirtyChanges.bulkGet(keys);
6505
6631
  const toWrite = [];
6506
- for (let i = 0; i < changesList.length; i++) {
6507
- const changeItem = changesList[i];
6632
+ for (let i = 0; i < filtered.length; i++) {
6633
+ const changeItem = filtered[i];
6508
6634
  const stringId = this.idToString(changeItem.id);
6509
6635
  const existing = existingEntries[i];
6510
6636
  if (existing) {
@@ -43,7 +43,6 @@ export interface RestProxyConfig {
43
43
  * ## Performance
44
44
  *
45
45
  * Benchmarked at ~5ms per request (after warmup) on localhost.
46
- * Native fetch performs comparably to undici with connection pooling.
47
46
  *
48
47
  * ## Progress Callback Limitations
49
48
  *
@@ -429,9 +429,9 @@ export interface SyncedDbConfig {
429
429
  restTimeoutMs?: number;
430
430
  /** Timeout za sync REST klice v ms (default: 120000) - daljši ker sync prenaša več podatkov */
431
431
  syncTimeoutMs?: number;
432
- /** Debounce čas za zapis v Dexie v ms (default: 500) */
432
+ /** Debounce čas za zapis v Dexie v ms (default: 200) */
433
433
  debounceDexieWritesMs?: number;
434
- /** Debounce čas za pošiljanje na REST v ms (default: 100) - po uspešnem zapisu v Dexie */
434
+ /** Debounce čas za pošiljanje na REST v ms (default: 1000) - po uspešnem zapisu v Dexie */
435
435
  debounceRestWritesMs?: number;
436
436
  /**
437
437
  * Callback fired on each sync failure. Unlike the removed `onForcedOffline`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cry-synced-db-client",
3
- "version": "0.1.167",
3
+ "version": "0.1.170",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "scripts": {
19
19
  "clean": "rm -rf dist",
20
20
  "build": "bun run clean && bun run build:js && bun run build:types",
21
- "build:js": "esbuild ./src/index.ts --bundle --outdir=./dist --target=es2017 --format=esm --platform=browser --external:dexie --external:bson --external:cry-helpers --external:notepack.io",
21
+ "build:js": "esbuild ./src/index.ts --bundle --outdir=./dist --target=es2017 --format=esm --platform=browser --external:dexie --external:bson --external:cry-helpers",
22
22
  "build:types": "tsc --emitDeclarationOnly --outDir dist",
23
23
  "test": "bun test test/*.test.ts test/restProxy/*.test.ts && vitest run",
24
24
  "test:bun": "bun test test/*.test.ts test/restProxy/*.test.ts",
@@ -39,10 +39,7 @@
39
39
  "dependencies": {
40
40
  "cry-helpers": "^2.1.194",
41
41
  "msgpackr": "^2.0.1",
42
- "notepack": "^0.0.2",
43
- "notepack.io": "^3.0.1",
44
- "superjson": "^2.2.6",
45
- "undici": "^8.2.0"
42
+ "superjson": "^2.2.6"
46
43
  },
47
44
  "peerDependencies": {
48
45
  "bson": "^6.0.0 || ^7.0.0",