cry-synced-db-client 0.1.166 → 0.1.167

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
@@ -4963,7 +4963,8 @@ var _SyncedDb = class _SyncedDb {
4963
4963
  const merged = _SyncedDb.applyDiffLocally(
4964
4964
  currentMem != null ? currentMem : existing,
4965
4965
  diff,
4966
- id
4966
+ id,
4967
+ collection
4967
4968
  );
4968
4969
  this.pendingChanges.schedule(collection, id, merged, 0, "save");
4969
4970
  if (!isWriteOnly && !(existing == null ? void 0 : existing._deleted) && !(existing == null ? void 0 : existing._archived)) {
@@ -6122,7 +6123,7 @@ var _SyncedDb = class _SyncedDb {
6122
6123
  * Returns a new object (the cloned-and-mutated `base`); never mutates
6123
6124
  * the input `base` reference.
6124
6125
  */
6125
- static applyDiffLocally(base, diff, fallbackId) {
6126
+ static applyDiffLocally(base, diff, fallbackId, collection) {
6126
6127
  const seed = base ? _SyncedDb.safeDeepClone(base) : { _id: fallbackId };
6127
6128
  if (seed._id == null) seed._id = fallbackId;
6128
6129
  for (const path of Object.keys(diff)) {
@@ -6136,10 +6137,98 @@ var _SyncedDb = class _SyncedDb {
6136
6137
  continue;
6137
6138
  }
6138
6139
  const ok = setByPath(seed, path, value);
6139
- if (!ok) seed[path] = value;
6140
+ if (!ok) {
6141
+ _SyncedDb.materializeBracketPath(seed, path, value, collection, fallbackId);
6142
+ }
6140
6143
  }
6141
6144
  return seed;
6142
6145
  }
6146
+ /**
6147
+ * Fallback for `setByPath` failures inside `applyDiffLocally`. Materializes
6148
+ * the parent array when it's missing from `seed`. Two patterns covered, per
6149
+ * production-spec (mozirje 2026-05-10 — literal bracket-keyed sibling
6150
+ * properties polluting Dexie/in-mem):
6151
+ *
6152
+ * 1. `polje[<id>] = <obj>` → seed.polje = [<obj>]
6153
+ * 2. `polje[<id>].<field> = <v>` → seed.polje = [{_id: <id>, <field>: <v>}]
6154
+ *
6155
+ * Everything else (nested-via-dots before bracket, multi-bracket paths
6156
+ * like `_redundanca.terapije[<id>].postavke[<id2>]`, deeper sub-fields)
6157
+ * is dropped silently — materializing those locally would risk corrupting
6158
+ * unrelated invariants on the nested objects. Dirty-change still carries
6159
+ * the path, so server applies it; next sync brings the canonical state
6160
+ * back to local.
6161
+ *
6162
+ * Replaces the pre-fix blind `seed[path] = value` fallback that stamped
6163
+ * literal bracket-keyed top-level properties (e.g. `"postavke[<id>]": [<el>]`)
6164
+ * onto Dexie rows and in-mem state, persisting forever through subsequent
6165
+ * `safeDeepClone`-based save cycles.
6166
+ */
6167
+ static materializeBracketPath(seed, path, value, collection, id) {
6168
+ const tokens = tokenizePath(path);
6169
+ const firstToken = tokens[0];
6170
+ const dropSilently = typeof firstToken === "string" && firstToken.startsWith("_");
6171
+ const drop = (reason) => {
6172
+ if (dropSilently) return;
6173
+ console.error(
6174
+ `SyncedDb.applyDiffLocally: dropping bracket-path diff entry (${reason})`,
6175
+ { collection, _id: String(id), path, value }
6176
+ );
6177
+ };
6178
+ if (tokens.length < 2 || tokens.length > 3) {
6179
+ drop(`unsupported token count ${tokens.length}`);
6180
+ return;
6181
+ }
6182
+ if (firstToken === void 0 || firstToken.startsWith("[")) {
6183
+ drop("first segment is not a plain field");
6184
+ return;
6185
+ }
6186
+ const secondToken = tokens[1];
6187
+ if (!secondToken.startsWith("[") || !secondToken.endsWith("]")) {
6188
+ drop("second segment is not a bracket-by-id");
6189
+ return;
6190
+ }
6191
+ if (tokens.length === 3 && tokens[2].startsWith("[")) {
6192
+ drop("nested bracket path");
6193
+ return;
6194
+ }
6195
+ const bracketId = secondToken.slice(1, -1);
6196
+ if (bracketId.length === 0) {
6197
+ drop("empty bracket id");
6198
+ return;
6199
+ }
6200
+ const existing = seed[firstToken];
6201
+ if (existing != null && !Array.isArray(existing)) {
6202
+ drop(`existing "${firstToken}" is not an array`);
6203
+ return;
6204
+ }
6205
+ if (tokens.length === 2) {
6206
+ let element = value;
6207
+ if (Array.isArray(value) && value.length === 1 && value[0] != null && typeof value[0] === "object") {
6208
+ element = value[0];
6209
+ }
6210
+ if (element == null || typeof element !== "object" || Array.isArray(element)) {
6211
+ drop("value is not a single element or wire-form wrapper");
6212
+ return;
6213
+ }
6214
+ if (element._id == null) {
6215
+ element._id = bracketId;
6216
+ }
6217
+ if (existing == null) {
6218
+ seed[firstToken] = [element];
6219
+ } else {
6220
+ existing.push(element);
6221
+ }
6222
+ return;
6223
+ }
6224
+ const fieldName = tokens[2];
6225
+ const newElement = { _id: bracketId, [fieldName]: value };
6226
+ if (existing == null) {
6227
+ seed[firstToken] = [newElement];
6228
+ } else {
6229
+ existing.push(newElement);
6230
+ }
6231
+ }
6143
6232
  /**
6144
6233
  * Deep clone for `applyDiffLocally`. Recurses into plain objects and
6145
6234
  * arrays; preserves `Date` (cloned to avoid shared reference) and
@@ -435,6 +435,28 @@ export declare class SyncedDb implements I_SyncedDb {
435
435
  * the input `base` reference.
436
436
  */
437
437
  private static applyDiffLocally;
438
+ /**
439
+ * Fallback for `setByPath` failures inside `applyDiffLocally`. Materializes
440
+ * the parent array when it's missing from `seed`. Two patterns covered, per
441
+ * production-spec (mozirje 2026-05-10 — literal bracket-keyed sibling
442
+ * properties polluting Dexie/in-mem):
443
+ *
444
+ * 1. `polje[<id>] = <obj>` → seed.polje = [<obj>]
445
+ * 2. `polje[<id>].<field> = <v>` → seed.polje = [{_id: <id>, <field>: <v>}]
446
+ *
447
+ * Everything else (nested-via-dots before bracket, multi-bracket paths
448
+ * like `_redundanca.terapije[<id>].postavke[<id2>]`, deeper sub-fields)
449
+ * is dropped silently — materializing those locally would risk corrupting
450
+ * unrelated invariants on the nested objects. Dirty-change still carries
451
+ * the path, so server applies it; next sync brings the canonical state
452
+ * back to local.
453
+ *
454
+ * Replaces the pre-fix blind `seed[path] = value` fallback that stamped
455
+ * literal bracket-keyed top-level properties (e.g. `"postavke[<id>]": [<el>]`)
456
+ * onto Dexie rows and in-mem state, persisting forever through subsequent
457
+ * `safeDeepClone`-based save cycles.
458
+ */
459
+ private static materializeBracketPath;
438
460
  /**
439
461
  * Deep clone for `applyDiffLocally`. Recurses into plain objects and
440
462
  * arrays; preserves `Date` (cloned to avoid shared reference) and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cry-synced-db-client",
3
- "version": "0.1.166",
3
+ "version": "0.1.167",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",