cry-synced-db-client 0.1.182 → 0.1.183
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 +43 -0
- package/dist/index.js +51 -19
- package/dist/src/db/SyncedDb.d.ts +19 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 0.1.183 (2026-05-14)
|
|
4
|
+
|
|
5
|
+
### Fix: `applyDiffLocally` materializes deep sub-field paths under bracket-by-id
|
|
6
|
+
|
|
7
|
+
`materializeBracketPath` (the fallback when `setByPath` can't reach the
|
|
8
|
+
target) capped at `tokens.length ≤ 3`. Any deeper path was dropped with
|
|
9
|
+
a `dropping bracket-path diff entry (unsupported token count N)` log
|
|
10
|
+
(silent when first segment started with `_`, visible otherwise).
|
|
11
|
+
|
|
12
|
+
Real-world obisk records carry nested objects on every `zaracunaj`
|
|
13
|
+
element — `karence.karence.meso`, `uporabljeneSerijskeNaNapravi.serijske`,
|
|
14
|
+
etc. Saves like
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
syncedDb.save("obiski", id, {
|
|
18
|
+
"zaracunaj[<newId>].karence.karence.meso": 5,
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
dropped the data even though the intent is unambiguous and the data
|
|
23
|
+
shape is valid.
|
|
24
|
+
|
|
25
|
+
**New behavior** in `materializeBracketPath`:
|
|
26
|
+
|
|
27
|
+
| Shape | Result |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `polje[<id>] = <obj>` | `seed.polje = [<obj>]` (unchanged) |
|
|
30
|
+
| `polje[<id>].field = v` | `seed.polje = [{_id: <id>, field: v}]` (unchanged) |
|
|
31
|
+
| `polje[<id>].a.b.c = v` (new) | `seed.polje = [{_id: <id>, a: {b: {c: v}}}]` |
|
|
32
|
+
| Matching `_id` exists, intermediate missing (new) | Walk into the element, create missing intermediates on the way down, set the leaf. No duplicate element. |
|
|
33
|
+
|
|
34
|
+
Multi-bracket paths (e.g. `polje[a].sub[b]`, `polje[a].b.sub[c]`) still
|
|
35
|
+
drop — they require shape knowledge the fallback can't reconstruct;
|
|
36
|
+
server applies them and the next sync hydrates local.
|
|
37
|
+
|
|
38
|
+
Tests: `test/applyDiffLocallyObiskShape.test.ts` (18 cases) — uses a
|
|
39
|
+
trimmed real-shape obisk fixture with the two _id-arrays (top-level
|
|
40
|
+
`zaracunaj` and `_dobavnica_saved.postavke` nested under a plain
|
|
41
|
+
container), all the composition-change scenarios, the deep-bracket-path
|
|
42
|
+
edge cases that used to drop, and a "no drop-log during typical
|
|
43
|
+
roundtrip" guard. 3 cases were red before the fix; all pass after.
|
|
44
|
+
Full suite: 762 bun + 18 vitest pass.
|
|
45
|
+
|
|
3
46
|
## 0.1.182 (2026-05-14)
|
|
4
47
|
|
|
5
48
|
### `networkError` utility — quiet log noise during sustained offline
|
package/dist/index.js
CHANGED
|
@@ -6653,19 +6653,25 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6653
6653
|
}
|
|
6654
6654
|
/**
|
|
6655
6655
|
* Fallback for `setByPath` failures inside `applyDiffLocally`. Materializes
|
|
6656
|
-
* the parent array when it's missing from `seed
|
|
6657
|
-
*
|
|
6658
|
-
*
|
|
6656
|
+
* the parent array when it's missing from `seed`, OR fills in missing
|
|
6657
|
+
* intermediate objects under an existing array element when the diff
|
|
6658
|
+
* path reaches deeper than the element currently holds.
|
|
6659
6659
|
*
|
|
6660
|
-
*
|
|
6661
|
-
* 2. `polje[<id>].<field> = <v>` → seed.polje = [{_id: <id>, <field>: <v>}]
|
|
6660
|
+
* Supported shapes (path tokenizes to `[firstField, [<id>], …rest]`):
|
|
6662
6661
|
*
|
|
6663
|
-
*
|
|
6664
|
-
*
|
|
6665
|
-
*
|
|
6666
|
-
*
|
|
6667
|
-
*
|
|
6668
|
-
*
|
|
6662
|
+
* 1. `polje[<id>] = <obj>` → seed.polje = [<obj>]
|
|
6663
|
+
* 2. `polje[<id>].field = <v>` → seed.polje = [{_id: <id>, field: <v>}]
|
|
6664
|
+
* 3. `polje[<id>].a.b = <v>` → seed.polje = [{_id: <id>, a: {b: <v>}}]
|
|
6665
|
+
* 4. `polje[<id>].a.b.c.d = <v>` → seed.polje = [{_id: <id>, a: {b: {c: {d: <v>}}}}]
|
|
6666
|
+
* 5. Existing element with `_id` matches `<id>` but missing intermediate
|
|
6667
|
+
* → walk down the element, create missing intermediate plain objects,
|
|
6668
|
+
* set the leaf. No duplicate element pushed.
|
|
6669
|
+
*
|
|
6670
|
+
* Dropped (multi-bracket / nested-via-dots-before-bracket / non-plain
|
|
6671
|
+
* intermediates) — materializing those locally would risk corrupting
|
|
6672
|
+
* unrelated invariants on the nested objects. Dirty-change still
|
|
6673
|
+
* carries the path, so the server applies it; next sync brings the
|
|
6674
|
+
* canonical state back to local.
|
|
6669
6675
|
*
|
|
6670
6676
|
* Replaces the pre-fix blind `seed[path] = value` fallback that stamped
|
|
6671
6677
|
* literal bracket-keyed top-level properties (e.g. `"postavke[<id>]": [<el>]`)
|
|
@@ -6683,7 +6689,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6683
6689
|
{ collection, _id: String(id), path, value }
|
|
6684
6690
|
);
|
|
6685
6691
|
};
|
|
6686
|
-
if (tokens.length < 2
|
|
6692
|
+
if (tokens.length < 2) {
|
|
6687
6693
|
drop(`unsupported token count ${tokens.length}`);
|
|
6688
6694
|
return;
|
|
6689
6695
|
}
|
|
@@ -6696,9 +6702,11 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6696
6702
|
drop("second segment is not a bracket-by-id");
|
|
6697
6703
|
return;
|
|
6698
6704
|
}
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6705
|
+
for (let i = 2; i < tokens.length; i++) {
|
|
6706
|
+
if (tokens[i].startsWith("[")) {
|
|
6707
|
+
drop("nested bracket path");
|
|
6708
|
+
return;
|
|
6709
|
+
}
|
|
6702
6710
|
}
|
|
6703
6711
|
const bracketId = secondToken.slice(1, -1);
|
|
6704
6712
|
if (bracketId.length === 0) {
|
|
@@ -6729,12 +6737,36 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6729
6737
|
}
|
|
6730
6738
|
return;
|
|
6731
6739
|
}
|
|
6732
|
-
const
|
|
6733
|
-
const
|
|
6740
|
+
const subFieldPath = tokens.slice(2);
|
|
6741
|
+
const buildNestedSubTree = (segs, leaf) => {
|
|
6742
|
+
let acc = leaf;
|
|
6743
|
+
for (let i = segs.length - 1; i >= 0; i--) {
|
|
6744
|
+
acc = { [segs[i]]: acc };
|
|
6745
|
+
}
|
|
6746
|
+
return acc;
|
|
6747
|
+
};
|
|
6734
6748
|
if (existing == null) {
|
|
6735
|
-
|
|
6749
|
+
const subTree = buildNestedSubTree(subFieldPath, value);
|
|
6750
|
+
seed[firstToken] = [__spreadValues({ _id: bracketId }, subTree)];
|
|
6751
|
+
return;
|
|
6752
|
+
}
|
|
6753
|
+
const existingIdx = existing.findIndex(
|
|
6754
|
+
(it) => it != null && typeof it === "object" && String(it._id) === bracketId
|
|
6755
|
+
);
|
|
6756
|
+
if (existingIdx >= 0) {
|
|
6757
|
+
let cur = existing[existingIdx];
|
|
6758
|
+
for (let i = 0; i < subFieldPath.length - 1; i++) {
|
|
6759
|
+
const seg = subFieldPath[i];
|
|
6760
|
+
const next = cur[seg];
|
|
6761
|
+
if (next == null || typeof next !== "object" || Array.isArray(next)) {
|
|
6762
|
+
cur[seg] = {};
|
|
6763
|
+
}
|
|
6764
|
+
cur = cur[seg];
|
|
6765
|
+
}
|
|
6766
|
+
cur[subFieldPath[subFieldPath.length - 1]] = value;
|
|
6736
6767
|
} else {
|
|
6737
|
-
|
|
6768
|
+
const subTree = buildNestedSubTree(subFieldPath, value);
|
|
6769
|
+
existing.push(__spreadValues({ _id: bracketId }, subTree));
|
|
6738
6770
|
}
|
|
6739
6771
|
}
|
|
6740
6772
|
/**
|
|
@@ -521,19 +521,25 @@ export declare class SyncedDb implements I_SyncedDb {
|
|
|
521
521
|
private static applyDiffLocally;
|
|
522
522
|
/**
|
|
523
523
|
* Fallback for `setByPath` failures inside `applyDiffLocally`. Materializes
|
|
524
|
-
* the parent array when it's missing from `seed
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
535
|
-
* the
|
|
536
|
-
*
|
|
524
|
+
* the parent array when it's missing from `seed`, OR fills in missing
|
|
525
|
+
* intermediate objects under an existing array element when the diff
|
|
526
|
+
* path reaches deeper than the element currently holds.
|
|
527
|
+
*
|
|
528
|
+
* Supported shapes (path tokenizes to `[firstField, [<id>], …rest]`):
|
|
529
|
+
*
|
|
530
|
+
* 1. `polje[<id>] = <obj>` → seed.polje = [<obj>]
|
|
531
|
+
* 2. `polje[<id>].field = <v>` → seed.polje = [{_id: <id>, field: <v>}]
|
|
532
|
+
* 3. `polje[<id>].a.b = <v>` → seed.polje = [{_id: <id>, a: {b: <v>}}]
|
|
533
|
+
* 4. `polje[<id>].a.b.c.d = <v>` → seed.polje = [{_id: <id>, a: {b: {c: {d: <v>}}}}]
|
|
534
|
+
* 5. Existing element with `_id` matches `<id>` but missing intermediate
|
|
535
|
+
* → walk down the element, create missing intermediate plain objects,
|
|
536
|
+
* set the leaf. No duplicate element pushed.
|
|
537
|
+
*
|
|
538
|
+
* Dropped (multi-bracket / nested-via-dots-before-bracket / non-plain
|
|
539
|
+
* intermediates) — materializing those locally would risk corrupting
|
|
540
|
+
* unrelated invariants on the nested objects. Dirty-change still
|
|
541
|
+
* carries the path, so the server applies it; next sync brings the
|
|
542
|
+
* canonical state back to local.
|
|
537
543
|
*
|
|
538
544
|
* Replaces the pre-fix blind `seed[path] = value` fallback that stamped
|
|
539
545
|
* literal bracket-keyed top-level properties (e.g. `"postavke[<id>]": [<el>]`)
|