cry-synced-db-client 0.1.158 → 0.1.160
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
|
@@ -330,28 +330,67 @@ function sameIdSequence(a, b) {
|
|
|
330
330
|
return true;
|
|
331
331
|
}
|
|
332
332
|
function computeArrayDiff(existingArr, updateArr, basePath, diff) {
|
|
333
|
-
if (existingArr.length === 0)
|
|
334
|
-
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
if (!allElementsHaveId(existingArr) || !allElementsHaveId(updateArr)) {
|
|
333
|
+
if (existingArr.length === 0 && updateArr.length === 0) return;
|
|
334
|
+
if (!allElementsHaveId(updateArr) || existingArr.length > 0 && !allElementsHaveId(existingArr)) {
|
|
338
335
|
if (!deepEquals(existingArr, updateArr)) {
|
|
339
336
|
diff[basePath] = updateArr;
|
|
340
337
|
}
|
|
341
338
|
return;
|
|
342
339
|
}
|
|
343
|
-
|
|
344
|
-
|
|
340
|
+
const existingIds = /* @__PURE__ */ new Set();
|
|
341
|
+
for (const e of existingArr) existingIds.add(String(e._id));
|
|
342
|
+
const updateIds = /* @__PURE__ */ new Set();
|
|
343
|
+
for (const u of updateArr) updateIds.add(String(u._id));
|
|
344
|
+
let sameSet = existingIds.size === updateIds.size;
|
|
345
|
+
if (sameSet) {
|
|
346
|
+
for (const id of existingIds) {
|
|
347
|
+
if (!updateIds.has(id)) {
|
|
348
|
+
sameSet = false;
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (sameSet) {
|
|
354
|
+
if (sameIdSequence(existingArr, updateArr)) {
|
|
355
|
+
for (let i = 0; i < updateArr.length; i++) {
|
|
356
|
+
const elementId = String(updateArr[i]._id);
|
|
357
|
+
computeDiffInto(
|
|
358
|
+
existingArr[i],
|
|
359
|
+
updateArr[i],
|
|
360
|
+
`${basePath}[${elementId}]`,
|
|
361
|
+
diff
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
diff[basePath] = updateArr;
|
|
366
|
+
}
|
|
345
367
|
return;
|
|
346
368
|
}
|
|
347
|
-
for (
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
)
|
|
369
|
+
for (const id of existingIds) {
|
|
370
|
+
if (!updateIds.has(id)) {
|
|
371
|
+
diff[`${basePath}[${id}]`] = void 0;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
for (const updateEl of updateArr) {
|
|
375
|
+
const id = String(updateEl._id);
|
|
376
|
+
if (!existingIds.has(id)) {
|
|
377
|
+
diff[`${basePath}[${id}]`] = [updateEl];
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (existingArr.length > 0) {
|
|
381
|
+
const existingById = /* @__PURE__ */ new Map();
|
|
382
|
+
for (const e of existingArr) existingById.set(String(e._id), e);
|
|
383
|
+
for (const updateEl of updateArr) {
|
|
384
|
+
const id = String(updateEl._id);
|
|
385
|
+
if (existingIds.has(id)) {
|
|
386
|
+
computeDiffInto(
|
|
387
|
+
existingById.get(id),
|
|
388
|
+
updateEl,
|
|
389
|
+
`${basePath}[${id}]`,
|
|
390
|
+
diff
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
355
394
|
}
|
|
356
395
|
}
|
|
357
396
|
function computeDiffInto(existing, update, basePath, diff) {
|
|
@@ -482,12 +521,62 @@ function setSegment(current, part, value) {
|
|
|
482
521
|
}
|
|
483
522
|
return false;
|
|
484
523
|
}
|
|
524
|
+
function deleteByPath(target2, path) {
|
|
525
|
+
if (target2 === null || target2 === void 0) return false;
|
|
526
|
+
const parts = tokenizePath(path);
|
|
527
|
+
if (parts.length === 0) return false;
|
|
528
|
+
let current = target2;
|
|
529
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
530
|
+
const next = navigateSegment(current, parts[i]);
|
|
531
|
+
if (next === void 0 || next === null) return false;
|
|
532
|
+
current = next;
|
|
533
|
+
}
|
|
534
|
+
const last = parts[parts.length - 1];
|
|
535
|
+
return deleteSegment(current, last);
|
|
536
|
+
}
|
|
537
|
+
function deleteSegment(current, part) {
|
|
538
|
+
if (current === null || current === void 0) return false;
|
|
539
|
+
if (part.startsWith("[") && part.endsWith("]")) {
|
|
540
|
+
const idStr = part.slice(1, -1);
|
|
541
|
+
if (!Array.isArray(current)) return false;
|
|
542
|
+
const idx = current.findIndex((item) => item && String(item._id) === idStr);
|
|
543
|
+
if (idx < 0) return false;
|
|
544
|
+
current.splice(idx, 1);
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
if (/^\d+$/.test(part) && Array.isArray(current)) {
|
|
548
|
+
const idx = Number(part);
|
|
549
|
+
if (idx < 0 || idx >= current.length) return false;
|
|
550
|
+
current.splice(idx, 1);
|
|
551
|
+
return true;
|
|
552
|
+
}
|
|
553
|
+
if (typeof current === "object") {
|
|
554
|
+
if (!Object.prototype.hasOwnProperty.call(current, part)) return false;
|
|
555
|
+
delete current[part];
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
function isTerminalBracketKey(path) {
|
|
561
|
+
const tokens = tokenizePath(path);
|
|
562
|
+
const last = tokens[tokens.length - 1];
|
|
563
|
+
return !!(last && last.length >= 2 && last.charCodeAt(0) === 91 && last.charCodeAt(last.length - 1) === 93);
|
|
564
|
+
}
|
|
485
565
|
function mergeDirtyPath(accumulated, newPath, newValue) {
|
|
486
566
|
for (const existingKey of Object.keys(accumulated)) {
|
|
487
567
|
if (existingKey === newPath) continue;
|
|
488
568
|
if (isDescendantOrEqual(newPath, existingKey)) {
|
|
569
|
+
const existingValue = accumulated[existingKey];
|
|
570
|
+
const existingIsTerminal = isTerminalBracketKey(existingKey);
|
|
571
|
+
if (existingIsTerminal && existingValue === void 0) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
let mutationTarget = existingValue;
|
|
575
|
+
if (existingIsTerminal && Array.isArray(existingValue) && existingValue.length === 1) {
|
|
576
|
+
mutationTarget = existingValue[0];
|
|
577
|
+
}
|
|
489
578
|
const relativePath = newPath.substring(existingKey.length + 1);
|
|
490
|
-
const ok = setByPath(
|
|
579
|
+
const ok = setByPath(mutationTarget, relativePath, newValue);
|
|
491
580
|
if (ok) return;
|
|
492
581
|
break;
|
|
493
582
|
}
|
|
@@ -508,6 +597,51 @@ function mergeDirtyChanges(accumulated, newChanges) {
|
|
|
508
597
|
}
|
|
509
598
|
}
|
|
510
599
|
|
|
600
|
+
// src/utils/normalizeUndefined.ts
|
|
601
|
+
var SERVER_MANAGED_KEYS = /* @__PURE__ */ new Set(["_ts", "_rev", "_csq"]);
|
|
602
|
+
function isObjectIdLike2(v) {
|
|
603
|
+
return !!(v && typeof v === "object" && (v._bsontype === "ObjectId" || v._bsontype === "ObjectID"));
|
|
604
|
+
}
|
|
605
|
+
function collectUnsetPaths(value) {
|
|
606
|
+
const paths = [];
|
|
607
|
+
walk(value, "", paths);
|
|
608
|
+
return paths;
|
|
609
|
+
}
|
|
610
|
+
function walk(node, prefix, paths) {
|
|
611
|
+
if (node === null || node === void 0) return;
|
|
612
|
+
if (typeof node !== "object") return;
|
|
613
|
+
if (node instanceof Date) return;
|
|
614
|
+
if (isObjectIdLike2(node)) return;
|
|
615
|
+
if (Array.isArray(node)) {
|
|
616
|
+
for (let i = 0; i < node.length; i++) {
|
|
617
|
+
const element = node[i];
|
|
618
|
+
const childPath = childPathForArrayElement(prefix, element, i);
|
|
619
|
+
if (element === void 0) {
|
|
620
|
+
paths.push(childPath);
|
|
621
|
+
} else {
|
|
622
|
+
walk(element, childPath, paths);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
for (const key of Object.keys(node)) {
|
|
628
|
+
if (SERVER_MANAGED_KEYS.has(key)) continue;
|
|
629
|
+
const child = node[key];
|
|
630
|
+
const childPath = prefix ? `${prefix}.${key}` : key;
|
|
631
|
+
if (child === void 0) {
|
|
632
|
+
paths.push(childPath);
|
|
633
|
+
} else {
|
|
634
|
+
walk(child, childPath, paths);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function childPathForArrayElement(prefix, element, index) {
|
|
639
|
+
if (element !== null && element !== void 0 && typeof element === "object" && !Array.isArray(element) && !(element instanceof Date) && !isObjectIdLike2(element) && element._id != null) {
|
|
640
|
+
return `${prefix}[${String(element._id)}]`;
|
|
641
|
+
}
|
|
642
|
+
return prefix ? `${prefix}.${index}` : String(index);
|
|
643
|
+
}
|
|
644
|
+
|
|
511
645
|
// src/db/managers/InMemManager.ts
|
|
512
646
|
var InMemManager = class {
|
|
513
647
|
constructor(config) {
|
|
@@ -2755,62 +2889,19 @@ function fixDotnetArrays(changes, serverRev, baseRev) {
|
|
|
2755
2889
|
}
|
|
2756
2890
|
|
|
2757
2891
|
// src/utils/translateBracketPaths.ts
|
|
2758
|
-
function translateBracketPathsToIndex(changes,
|
|
2759
|
-
|
|
2760
|
-
for (const [key, value] of Object.entries(changes)) {
|
|
2761
|
-
const translated = translateKey(key, entity);
|
|
2762
|
-
if (translated !== null) {
|
|
2763
|
-
result[translated] = value;
|
|
2764
|
-
}
|
|
2765
|
-
}
|
|
2766
|
-
return result;
|
|
2767
|
-
}
|
|
2768
|
-
function translateKey(key, entity) {
|
|
2769
|
-
const parts = tokenizePath(key);
|
|
2770
|
-
const out = [];
|
|
2771
|
-
let cursor = entity;
|
|
2772
|
-
for (let i = 0; i < parts.length; i++) {
|
|
2773
|
-
const part = parts[i];
|
|
2774
|
-
if (part.startsWith("[") && part.endsWith("]")) {
|
|
2775
|
-
const idStr = part.slice(1, -1);
|
|
2776
|
-
if (!Array.isArray(cursor)) return null;
|
|
2777
|
-
const idx = cursor.findIndex(
|
|
2778
|
-
(item) => item && String(item._id) === idStr
|
|
2779
|
-
);
|
|
2780
|
-
if (idx < 0) return null;
|
|
2781
|
-
out.push(String(idx));
|
|
2782
|
-
cursor = cursor[idx];
|
|
2783
|
-
continue;
|
|
2784
|
-
}
|
|
2785
|
-
out.push(part);
|
|
2786
|
-
if (cursor === null || cursor === void 0) {
|
|
2787
|
-
for (let j = i + 1; j < parts.length; j++) {
|
|
2788
|
-
const p = parts[j];
|
|
2789
|
-
if (p.startsWith("[") && p.endsWith("]")) return null;
|
|
2790
|
-
out.push(p);
|
|
2791
|
-
}
|
|
2792
|
-
return out.join(".");
|
|
2793
|
-
}
|
|
2794
|
-
if (/^\d+$/.test(part) && Array.isArray(cursor)) {
|
|
2795
|
-
cursor = cursor[Number(part)];
|
|
2796
|
-
} else if (typeof cursor === "object") {
|
|
2797
|
-
cursor = cursor[part];
|
|
2798
|
-
} else {
|
|
2799
|
-
cursor = void 0;
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
return out.join(".");
|
|
2892
|
+
function translateBracketPathsToIndex(changes, _entity) {
|
|
2893
|
+
return changes;
|
|
2803
2894
|
}
|
|
2804
2895
|
|
|
2805
2896
|
// src/utils/stripServerManaged.ts
|
|
2806
|
-
var
|
|
2807
|
-
function
|
|
2897
|
+
var SERVER_MANAGED_KEYS2 = /* @__PURE__ */ new Set(["_ts", "_rev", "_csq"]);
|
|
2898
|
+
function isObjectIdLike3(v) {
|
|
2808
2899
|
return !!(v && typeof v === "object" && (v._bsontype === "ObjectId" || v._bsontype === "ObjectID"));
|
|
2809
2900
|
}
|
|
2810
2901
|
function isServerManagedPath(key) {
|
|
2811
2902
|
for (const part of tokenizePath(key)) {
|
|
2812
2903
|
if (part.startsWith("[")) continue;
|
|
2813
|
-
if (
|
|
2904
|
+
if (SERVER_MANAGED_KEYS2.has(part)) return true;
|
|
2814
2905
|
}
|
|
2815
2906
|
return false;
|
|
2816
2907
|
}
|
|
@@ -2821,10 +2912,10 @@ function scrubServerManagedDeep(value) {
|
|
|
2821
2912
|
}
|
|
2822
2913
|
if (typeof value !== "object") return value;
|
|
2823
2914
|
if (value instanceof Date) return value;
|
|
2824
|
-
if (
|
|
2915
|
+
if (isObjectIdLike3(value)) return value;
|
|
2825
2916
|
const out = {};
|
|
2826
2917
|
for (const k of Object.keys(value)) {
|
|
2827
|
-
if (
|
|
2918
|
+
if (SERVER_MANAGED_KEYS2.has(k)) continue;
|
|
2828
2919
|
out[k] = scrubServerManagedDeep(value[k]);
|
|
2829
2920
|
}
|
|
2830
2921
|
return out;
|
|
@@ -3671,6 +3762,11 @@ var ServerUpdateHandler = class {
|
|
|
3671
3762
|
async handleServerItemInsert(collection, serverItem) {
|
|
3672
3763
|
const localItem = await this.dexieDb.getById(collection, serverItem._id);
|
|
3673
3764
|
if (localItem) {
|
|
3765
|
+
const isStaleSelfEcho = serverItem._lastUpdaterId === this.updaterId && typeof serverItem._rev === "number" && typeof localItem._rev === "number" && serverItem._rev <= localItem._rev;
|
|
3766
|
+
if (isStaleSelfEcho) {
|
|
3767
|
+
await this.dexieDb.clearDirtyChange(collection, serverItem._id);
|
|
3768
|
+
return;
|
|
3769
|
+
}
|
|
3674
3770
|
const dirtyChange = await this.dexieDb.getDirtyChange(collection, serverItem._id);
|
|
3675
3771
|
const metaChanged = localItem._rev !== serverItem._rev || !this.timestampsEqual(localItem._ts, serverItem._ts);
|
|
3676
3772
|
if (metaChanged) {
|
|
@@ -3696,12 +3792,13 @@ var ServerUpdateHandler = class {
|
|
|
3696
3792
|
* Handle server item update (delta).
|
|
3697
3793
|
*/
|
|
3698
3794
|
async handleServerItemUpdate(collection, localItem, serverDelta) {
|
|
3699
|
-
const
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3795
|
+
const serverRev = serverDelta._rev;
|
|
3796
|
+
const localRev = localItem._rev;
|
|
3797
|
+
const isSelfEcho = serverDelta._lastUpdaterId === this.updaterId && typeof serverRev === "number" && typeof localRev === "number" && serverRev <= localRev + 1;
|
|
3798
|
+
if (isSelfEcho) {
|
|
3799
|
+
if (serverRev > localRev) {
|
|
3703
3800
|
await this.dexieDb.save(collection, serverDelta._id, {
|
|
3704
|
-
_rev:
|
|
3801
|
+
_rev: serverRev,
|
|
3705
3802
|
_ts: serverDelta._ts
|
|
3706
3803
|
});
|
|
3707
3804
|
}
|
|
@@ -4814,7 +4911,11 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4814
4911
|
this.pendingChanges.schedule(collection, id, newData, 0, "save");
|
|
4815
4912
|
const isWriteOnly = (_b = this.collections.get(collection)) == null ? void 0 : _b.writeOnly;
|
|
4816
4913
|
const currentMem = isWriteOnly ? null : this.inMemDb.getById(collection, id);
|
|
4817
|
-
const merged =
|
|
4914
|
+
const merged = _SyncedDb.applyDiffLocally(
|
|
4915
|
+
currentMem != null ? currentMem : existing,
|
|
4916
|
+
diff,
|
|
4917
|
+
id
|
|
4918
|
+
);
|
|
4818
4919
|
if (!isWriteOnly && !(existing == null ? void 0 : existing._deleted) && !(existing == null ? void 0 : existing._archived)) {
|
|
4819
4920
|
this.inMemManager.writeBatch(collection, [merged], "upsert", { source: "incremental" });
|
|
4820
4921
|
}
|
|
@@ -4839,6 +4940,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4839
4940
|
this.ensureId(data, "insert", collection);
|
|
4840
4941
|
_SyncedDb.ensureNestedIds(data);
|
|
4841
4942
|
data = _SyncedDb.stringifyObjectIds(data);
|
|
4943
|
+
const unsetPaths = collectUnsetPaths(data);
|
|
4842
4944
|
const id = String(data._id);
|
|
4843
4945
|
const existing = await this.dexieDb.getById(collection, id);
|
|
4844
4946
|
if (existing && !existing._deleted && !existing._archived) {
|
|
@@ -4856,6 +4958,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4856
4958
|
_id: id,
|
|
4857
4959
|
_lastUpdaterId: this.updaterId
|
|
4858
4960
|
});
|
|
4961
|
+
for (const path of unsetPaths) deleteByPath(newData, path);
|
|
4859
4962
|
this.pendingChanges.schedule(collection, id, newData, 0, "insert");
|
|
4860
4963
|
if (!((_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly)) {
|
|
4861
4964
|
this.inMemManager.writeBatch(collection, [newData], "upsert", { source: "incremental" });
|
|
@@ -5939,6 +6042,64 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5939
6042
|
static isObjectIdLike(v) {
|
|
5940
6043
|
return !!(v && typeof v === "object" && (v._bsontype === "ObjectId" || typeof v.toHexString === "function"));
|
|
5941
6044
|
}
|
|
6045
|
+
/**
|
|
6046
|
+
* Mongo-symmetric local apply: starting from `base` (a safe deep clone of
|
|
6047
|
+
* `currentMem` or `existing`), walk each `(path, value)` entry of `diff`:
|
|
6048
|
+
*
|
|
6049
|
+
* - `value === undefined` → `deleteByPath` (mongo `$unset` symmetric)
|
|
6050
|
+
* - otherwise → `setByPath` (mongo `$set` symmetric)
|
|
6051
|
+
*
|
|
6052
|
+
* The result is what an equivalent server-side `$set` + `$unset` would
|
|
6053
|
+
* have produced. Replaces the previous shallow `{ ...currentMem, ...update }`
|
|
6054
|
+
* merge which dropped nested fields the caller's `update` didn't mention.
|
|
6055
|
+
*
|
|
6056
|
+
* Returns a new object (the cloned-and-mutated `base`); never mutates
|
|
6057
|
+
* the input `base` reference.
|
|
6058
|
+
*/
|
|
6059
|
+
static applyDiffLocally(base, diff, fallbackId) {
|
|
6060
|
+
const seed = base ? _SyncedDb.safeDeepClone(base) : { _id: fallbackId };
|
|
6061
|
+
if (seed._id == null) seed._id = fallbackId;
|
|
6062
|
+
for (const path of Object.keys(diff)) {
|
|
6063
|
+
const value = diff[path];
|
|
6064
|
+
if (value === void 0) {
|
|
6065
|
+
deleteByPath(seed, path);
|
|
6066
|
+
continue;
|
|
6067
|
+
}
|
|
6068
|
+
if (!path.includes(".") && !path.includes("[")) {
|
|
6069
|
+
seed[path] = value;
|
|
6070
|
+
continue;
|
|
6071
|
+
}
|
|
6072
|
+
const ok = setByPath(seed, path, value);
|
|
6073
|
+
if (!ok) seed[path] = value;
|
|
6074
|
+
}
|
|
6075
|
+
return seed;
|
|
6076
|
+
}
|
|
6077
|
+
/**
|
|
6078
|
+
* Deep clone for `applyDiffLocally`. Recurses into plain objects and
|
|
6079
|
+
* arrays; preserves `Date` (cloned to avoid shared reference) and
|
|
6080
|
+
* `ObjectId`-like values by reference (their internal Buffer state is
|
|
6081
|
+
* immutable from our perspective). Other class instances pass through
|
|
6082
|
+
* by reference. Avoids `structuredClone` because it throws on class
|
|
6083
|
+
* instances like bson `ObjectId`.
|
|
6084
|
+
*/
|
|
6085
|
+
static safeDeepClone(value) {
|
|
6086
|
+
if (value === null || value === void 0) return value;
|
|
6087
|
+
if (typeof value !== "object") return value;
|
|
6088
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
6089
|
+
if (_SyncedDb.isObjectIdLike(value)) return value;
|
|
6090
|
+
if (Array.isArray(value)) {
|
|
6091
|
+
const out2 = new Array(value.length);
|
|
6092
|
+
for (let i = 0; i < value.length; i++) out2[i] = _SyncedDb.safeDeepClone(value[i]);
|
|
6093
|
+
return out2;
|
|
6094
|
+
}
|
|
6095
|
+
const proto = Object.getPrototypeOf(value);
|
|
6096
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
6097
|
+
const out = {};
|
|
6098
|
+
for (const key of Object.keys(value)) {
|
|
6099
|
+
out[key] = _SyncedDb.safeDeepClone(value[key]);
|
|
6100
|
+
}
|
|
6101
|
+
return out;
|
|
6102
|
+
}
|
|
5942
6103
|
/**
|
|
5943
6104
|
* Recursively walk `value` and ensure every plain object that appears
|
|
5944
6105
|
* as an element of an array carries an `_id`. Missing `_id`s are
|
|
@@ -6339,14 +6500,6 @@ var C1 = new C1Type();
|
|
|
6339
6500
|
C1.name = "MessagePack 0xC1";
|
|
6340
6501
|
var sequentialMode = false;
|
|
6341
6502
|
var inlineObjectReadThreshold = 2;
|
|
6342
|
-
var readStruct;
|
|
6343
|
-
var onLoadedStructures;
|
|
6344
|
-
var onSaveState;
|
|
6345
|
-
try {
|
|
6346
|
-
new Function("");
|
|
6347
|
-
} catch (error) {
|
|
6348
|
-
inlineObjectReadThreshold = Infinity;
|
|
6349
|
-
}
|
|
6350
6503
|
var Unpackr = class _Unpackr {
|
|
6351
6504
|
constructor(options) {
|
|
6352
6505
|
if (options) {
|
|
@@ -6449,8 +6602,8 @@ var Unpackr = class _Unpackr {
|
|
|
6449
6602
|
}
|
|
6450
6603
|
}
|
|
6451
6604
|
_mergeStructures(loadedStructures, existingStructures) {
|
|
6452
|
-
if (
|
|
6453
|
-
loadedStructures =
|
|
6605
|
+
if (this._onLoadedStructures)
|
|
6606
|
+
loadedStructures = this._onLoadedStructures(loadedStructures);
|
|
6454
6607
|
loadedStructures = loadedStructures || [];
|
|
6455
6608
|
if (Object.isFrozen(loadedStructures))
|
|
6456
6609
|
loadedStructures = loadedStructures.map((structure) => structure.slice(0));
|
|
@@ -6488,8 +6641,8 @@ function checkedRead(options) {
|
|
|
6488
6641
|
currentStructures.length = sharedLength;
|
|
6489
6642
|
}
|
|
6490
6643
|
let result;
|
|
6491
|
-
if (currentUnpackr.
|
|
6492
|
-
result =
|
|
6644
|
+
if (currentUnpackr._readStruct && src[position] < 64 && src[position] >= 32) {
|
|
6645
|
+
result = currentUnpackr._readStruct(src, position, srcEnd);
|
|
6493
6646
|
src = null;
|
|
6494
6647
|
if (!(options && options.lazy) && result)
|
|
6495
6648
|
result = result.toJSON();
|
|
@@ -6778,10 +6931,16 @@ var validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
|
|
|
6778
6931
|
function createStructureReader(structure, firstId) {
|
|
6779
6932
|
function readObject() {
|
|
6780
6933
|
if (readObject.count++ > inlineObjectReadThreshold) {
|
|
6781
|
-
let
|
|
6934
|
+
let optimizedReadObject;
|
|
6935
|
+
try {
|
|
6936
|
+
optimizedReadObject = structure.read = new Function("r", "return function(){return " + (currentUnpackr.freezeData ? "Object.freeze" : "") + "({" + structure.map((key) => key === "__proto__" ? "__proto_:r()" : validName.test(key) ? key + ":r()" : "[" + JSON.stringify(key) + "]:r()").join(",") + "})}")(read);
|
|
6937
|
+
} catch (error) {
|
|
6938
|
+
inlineObjectReadThreshold = Infinity;
|
|
6939
|
+
return readObject();
|
|
6940
|
+
}
|
|
6782
6941
|
if (structure.highByte === 0)
|
|
6783
6942
|
structure.read = createSecondByteReader(firstId, structure.read);
|
|
6784
|
-
return
|
|
6943
|
+
return optimizedReadObject();
|
|
6785
6944
|
}
|
|
6786
6945
|
let object = {};
|
|
6787
6946
|
for (let i = 0, l = structure.length; i < l; i++) {
|
|
@@ -7213,7 +7372,7 @@ currentExtensions[66] = (data) => {
|
|
|
7213
7372
|
if (length <= 40) {
|
|
7214
7373
|
let out = view.getBigUint64(start);
|
|
7215
7374
|
for (let i = start + 8; i < end; i += 8) {
|
|
7216
|
-
out <<= BigInt(
|
|
7375
|
+
out <<= BigInt(64);
|
|
7217
7376
|
out |= view.getBigUint64(i);
|
|
7218
7377
|
}
|
|
7219
7378
|
return out;
|
|
@@ -7328,8 +7487,8 @@ currentExtensions[255] = (data) => {
|
|
|
7328
7487
|
return /* @__PURE__ */ new Date("invalid");
|
|
7329
7488
|
};
|
|
7330
7489
|
function saveState(callback) {
|
|
7331
|
-
if (
|
|
7332
|
-
|
|
7490
|
+
if (currentUnpackr && currentUnpackr._onSaveState)
|
|
7491
|
+
currentUnpackr._onSaveState();
|
|
7333
7492
|
let savedSrcEnd = srcEnd;
|
|
7334
7493
|
let savedPosition = position;
|
|
7335
7494
|
let savedStringPosition = stringPosition;
|
|
@@ -7383,6 +7542,7 @@ var FLOAT32_OPTIONS = {
|
|
|
7383
7542
|
};
|
|
7384
7543
|
var f32Array = new Float32Array(1);
|
|
7385
7544
|
var u8Array = new Uint8Array(f32Array.buffer, 0, 4);
|
|
7545
|
+
Unpackr.SUPPORTS_STRUCT_HOOKS = true;
|
|
7386
7546
|
|
|
7387
7547
|
// node_modules/msgpackr/pack.js
|
|
7388
7548
|
var textEncoder;
|
|
@@ -7404,7 +7564,6 @@ var targetView;
|
|
|
7404
7564
|
var position2 = 0;
|
|
7405
7565
|
var safeEnd;
|
|
7406
7566
|
var bundledStrings2 = null;
|
|
7407
|
-
var writeStructSlots;
|
|
7408
7567
|
var MAX_BUNDLE_SIZE = 21760;
|
|
7409
7568
|
var hasNonLatin = /[\u0080-\uFFFF]/;
|
|
7410
7569
|
var RECORD_SYMBOL = /* @__PURE__ */ Symbol("record-id");
|
|
@@ -7506,7 +7665,7 @@ var Packr = class extends Unpackr {
|
|
|
7506
7665
|
hasSharedUpdate = false;
|
|
7507
7666
|
let encodingError;
|
|
7508
7667
|
try {
|
|
7509
|
-
if (packr3.
|
|
7668
|
+
if (packr3._writeStruct && value && typeof value === "object") {
|
|
7510
7669
|
if (value.constructor === Object) writeStruct(value);
|
|
7511
7670
|
else if (value.constructor !== Map && !Array.isArray(value) && !extensionClasses.some((extClass) => value instanceof extClass)) {
|
|
7512
7671
|
writeStruct(value.toJSON ? value.toJSON() : value);
|
|
@@ -7569,7 +7728,7 @@ var Packr = class extends Unpackr {
|
|
|
7569
7728
|
if (hasSharedUpdate && packr3.saveStructures) {
|
|
7570
7729
|
let sharedLength = structures.sharedLength || 0;
|
|
7571
7730
|
let returnBuffer = target.subarray(start, position2);
|
|
7572
|
-
let newSharedData = prepareStructures(structures, packr3);
|
|
7731
|
+
let newSharedData = (packr3._prepareStructures || prepareStructures)(structures, packr3);
|
|
7573
7732
|
if (!encodingError) {
|
|
7574
7733
|
if (packr3.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
|
|
7575
7734
|
return packr3.pack(value, encodeOptions);
|
|
@@ -7726,7 +7885,7 @@ var Packr = class extends Unpackr {
|
|
|
7726
7885
|
position2 += length;
|
|
7727
7886
|
} else if (type === "number") {
|
|
7728
7887
|
if (value >>> 0 === value) {
|
|
7729
|
-
if (value < 32 || value < 128 && this.useRecords === false || value < 64 && !this.
|
|
7888
|
+
if (value < 32 || value < 128 && this.useRecords === false || value < 64 && !this._writeStruct) {
|
|
7730
7889
|
target[position2++] = value;
|
|
7731
7890
|
} else if (value < 256) {
|
|
7732
7891
|
target[position2++] = 204;
|
|
@@ -8085,6 +8244,23 @@ var Packr = class extends Unpackr {
|
|
|
8085
8244
|
const writeObject = checkUseRecords ? (object) => {
|
|
8086
8245
|
checkUseRecords(object) ? writeRecord(object) : writePlainObject(object);
|
|
8087
8246
|
} : writeRecord;
|
|
8247
|
+
const writeStruct = (object) => {
|
|
8248
|
+
let newPosition = packr3._writeStruct(object, target, start, position2, structures, makeRoom, (value, newPosition2, notifySharedUpdate) => {
|
|
8249
|
+
if (notifySharedUpdate)
|
|
8250
|
+
return hasSharedUpdate = true;
|
|
8251
|
+
position2 = newPosition2;
|
|
8252
|
+
let startTarget = target;
|
|
8253
|
+
pack3(value);
|
|
8254
|
+
resetStructures();
|
|
8255
|
+
if (startTarget !== target) {
|
|
8256
|
+
return { position: position2, targetView, target };
|
|
8257
|
+
}
|
|
8258
|
+
return position2;
|
|
8259
|
+
});
|
|
8260
|
+
if (newPosition === 0)
|
|
8261
|
+
return writeObject(object);
|
|
8262
|
+
position2 = newPosition;
|
|
8263
|
+
};
|
|
8088
8264
|
const makeRoom = (end) => {
|
|
8089
8265
|
let newSize;
|
|
8090
8266
|
if (end > 16777216) {
|
|
@@ -8185,23 +8361,6 @@ var Packr = class extends Unpackr {
|
|
|
8185
8361
|
target[insertionOffset + start] = keysTarget[0];
|
|
8186
8362
|
}
|
|
8187
8363
|
};
|
|
8188
|
-
const writeStruct = (object) => {
|
|
8189
|
-
let newPosition = writeStructSlots(object, target, start, position2, structures, makeRoom, (value, newPosition2, notifySharedUpdate) => {
|
|
8190
|
-
if (notifySharedUpdate)
|
|
8191
|
-
return hasSharedUpdate = true;
|
|
8192
|
-
position2 = newPosition2;
|
|
8193
|
-
let startTarget = target;
|
|
8194
|
-
pack3(value);
|
|
8195
|
-
resetStructures();
|
|
8196
|
-
if (startTarget !== target) {
|
|
8197
|
-
return { position: position2, targetView, target };
|
|
8198
|
-
}
|
|
8199
|
-
return position2;
|
|
8200
|
-
}, this);
|
|
8201
|
-
if (newPosition === 0)
|
|
8202
|
-
return writeObject(object);
|
|
8203
|
-
position2 = newPosition;
|
|
8204
|
-
};
|
|
8205
8364
|
}
|
|
8206
8365
|
useBuffer(buffer) {
|
|
8207
8366
|
target = buffer;
|
|
@@ -8438,6 +8597,7 @@ function prepareStructures(structures, packr3) {
|
|
|
8438
8597
|
};
|
|
8439
8598
|
return structures;
|
|
8440
8599
|
}
|
|
8600
|
+
Packr.SUPPORTS_STRUCT_HOOKS = true;
|
|
8441
8601
|
var defaultPackr = new Packr({ useRecords: false });
|
|
8442
8602
|
var pack = defaultPackr.pack;
|
|
8443
8603
|
var encode = defaultPackr.pack;
|
|
@@ -403,6 +403,30 @@ export declare class SyncedDb implements I_SyncedDb {
|
|
|
403
403
|
*/
|
|
404
404
|
private static stringifyObjectIds;
|
|
405
405
|
private static isObjectIdLike;
|
|
406
|
+
/**
|
|
407
|
+
* Mongo-symmetric local apply: starting from `base` (a safe deep clone of
|
|
408
|
+
* `currentMem` or `existing`), walk each `(path, value)` entry of `diff`:
|
|
409
|
+
*
|
|
410
|
+
* - `value === undefined` → `deleteByPath` (mongo `$unset` symmetric)
|
|
411
|
+
* - otherwise → `setByPath` (mongo `$set` symmetric)
|
|
412
|
+
*
|
|
413
|
+
* The result is what an equivalent server-side `$set` + `$unset` would
|
|
414
|
+
* have produced. Replaces the previous shallow `{ ...currentMem, ...update }`
|
|
415
|
+
* merge which dropped nested fields the caller's `update` didn't mention.
|
|
416
|
+
*
|
|
417
|
+
* Returns a new object (the cloned-and-mutated `base`); never mutates
|
|
418
|
+
* the input `base` reference.
|
|
419
|
+
*/
|
|
420
|
+
private static applyDiffLocally;
|
|
421
|
+
/**
|
|
422
|
+
* Deep clone for `applyDiffLocally`. Recurses into plain objects and
|
|
423
|
+
* arrays; preserves `Date` (cloned to avoid shared reference) and
|
|
424
|
+
* `ObjectId`-like values by reference (their internal Buffer state is
|
|
425
|
+
* immutable from our perspective). Other class instances pass through
|
|
426
|
+
* by reference. Avoids `structuredClone` because it throws on class
|
|
427
|
+
* instances like bson `ObjectId`.
|
|
428
|
+
*/
|
|
429
|
+
private static safeDeepClone;
|
|
406
430
|
/**
|
|
407
431
|
* Recursively walk `value` and ensure every plain object that appears
|
|
408
432
|
* as an element of an array carries an `_id`. Missing `_id`s are
|
|
@@ -41,6 +41,34 @@ export declare function hasDotNotationPaths(changes: Record<string, any>): boole
|
|
|
41
41
|
* "arr[A].sub[B].field" → ["arr", "[A]", "sub", "[B]", "field"]
|
|
42
42
|
*/
|
|
43
43
|
export declare function tokenizePath(path: string): string[];
|
|
44
|
+
/**
|
|
45
|
+
* Set a value at a path within a nested target. Supports three segment forms:
|
|
46
|
+
* - Numeric ("0", "1") → array index
|
|
47
|
+
* - Bracket ("[<_id>]") → array element matched by `_id` field
|
|
48
|
+
* - Plain ("field") → object key
|
|
49
|
+
*
|
|
50
|
+
* @returns true if the value was successfully set, false if path traversal failed.
|
|
51
|
+
*/
|
|
52
|
+
export declare function setByPath(target: any, path: string, value: any): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Delete the value at `path` within `target`. Sibling of `setByPath` —
|
|
55
|
+
* navigates the same tokenized path forms (numeric, bracket-by-_id, plain),
|
|
56
|
+
* but the final segment removes the property/element instead of setting it.
|
|
57
|
+
*
|
|
58
|
+
* Used by `save()`/`insert()`/`upsert()` to honor the convention that an
|
|
59
|
+
* `undefined` value in the caller's `update` means "delete this field".
|
|
60
|
+
*
|
|
61
|
+
* Behavior on the last segment:
|
|
62
|
+
* - plain key: `delete obj[key]`
|
|
63
|
+
* - numeric `"<idx>"` on array: `arr.splice(idx, 1)` (removes element)
|
|
64
|
+
* - bracket `"[<_id>]"` on array of `_id`-keyed objects: splice the
|
|
65
|
+
* matching element
|
|
66
|
+
*
|
|
67
|
+
* Returns `true` if a delete actually occurred. `false` if traversal failed
|
|
68
|
+
* (path leads through a missing/non-object value) or the target segment
|
|
69
|
+
* doesn't exist — caller can ignore.
|
|
70
|
+
*/
|
|
71
|
+
export declare function deleteByPath(target: any, path: string): boolean;
|
|
44
72
|
/**
|
|
45
73
|
* Smart-merge a single (path, value) entry into an accumulated dirty changes
|
|
46
74
|
* object. Resolves three relationships between the new path and existing keys:
|
|
@@ -49,6 +77,13 @@ export declare function tokenizePath(path: string): string[];
|
|
|
49
77
|
* new is "koraki.0.diag"): mutate the value inside the existing parent
|
|
50
78
|
* rather than adding a conflicting child path.
|
|
51
79
|
*
|
|
80
|
+
* SPECIAL CASES for terminal-bracket existing keys (whole-element ops):
|
|
81
|
+
* a) existing value is `undefined` (REMOVE marker) → drop new sub-field
|
|
82
|
+
* silently. Element is gone, sub-field write is moot.
|
|
83
|
+
* b) existing value is `[element]` (INSERT wrapper) → navigate INTO the
|
|
84
|
+
* wrapped element[0], not into the array itself. The pending insert
|
|
85
|
+
* absorbs the sub-field edit.
|
|
86
|
+
*
|
|
52
87
|
* 2. New path is an ANCESTOR of existing keys (e.g. existing has "koraki.0.diag",
|
|
53
88
|
* new is "koraki" with full array): remove the now-redundant descendants and
|
|
54
89
|
* set the parent path. The new full value supersedes any field-level deltas.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk `value` recursively without mutation. Return the dot/bracket
|
|
3
|
+
* notation paths of every property whose value is `undefined`.
|
|
4
|
+
*
|
|
5
|
+
* The original `value` (and all nested objects/arrays) is left intact —
|
|
6
|
+
* `undefined` values stay in place so that downstream code (computeDiff,
|
|
7
|
+
* dirty-change storage, REST upload via msgpackr) can carry them through
|
|
8
|
+
* to cry-db, which natively routes `key: undefined` → `$unset` server-side.
|
|
9
|
+
*/
|
|
10
|
+
export declare function collectUnsetPaths(value: any): string[];
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* `
|
|
5
|
-
*
|
|
2
|
+
* Pass-through: returns changes unchanged. Server-side cry-db v2.4.33+
|
|
3
|
+
* `_applyBracketProcessing` handles all bracket forms atomically:
|
|
4
|
+
* - terminal `arr[<id>]` with array value → idempotent `$concatArrays + $filter`
|
|
5
|
+
* - terminal `arr[<id>]` with `undefined` → `$pull`
|
|
6
|
+
* - terminal `arr[<id>]` with object → arrayFilter `$set`
|
|
7
|
+
* - sub-field `arr[<id>].field` → arrayFilter `$set` (position-independent)
|
|
8
|
+
*
|
|
9
|
+
* Signature kept (with `_entity` unused) for backward-compat with callers.
|
|
6
10
|
*/
|
|
7
|
-
export declare function translateBracketPathsToIndex(changes: Record<string, any>,
|
|
11
|
+
export declare function translateBracketPathsToIndex(changes: Record<string, any>, _entity: any): Record<string, any>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cry-synced-db-client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.160",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -30,19 +30,19 @@
|
|
|
30
30
|
"bson": "^7.2.0",
|
|
31
31
|
"cry-ebus-proxy": "^1.0.3",
|
|
32
32
|
"dexie": "^4.4.2",
|
|
33
|
-
"esbuild": "^0.
|
|
33
|
+
"esbuild": "^0.28.0",
|
|
34
34
|
"fake-indexeddb": "^6.2.5",
|
|
35
35
|
"typescript": "^6",
|
|
36
|
-
"vitest": "^4.1.
|
|
36
|
+
"vitest": "^4.1.5"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"cry-db": "^2.4.
|
|
40
|
-
"cry-helpers": "^2.1.
|
|
41
|
-
"msgpackr": "^
|
|
39
|
+
"cry-db": "^2.4.33",
|
|
40
|
+
"cry-helpers": "^2.1.194",
|
|
41
|
+
"msgpackr": "^2.0.1",
|
|
42
42
|
"notepack": "^0.0.2",
|
|
43
43
|
"notepack.io": "^3.0.1",
|
|
44
44
|
"superjson": "^2.2.6",
|
|
45
|
-
"undici": "^
|
|
45
|
+
"undici": "^8.2.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"bson": "^6.0.0 || ^7.0.0",
|