patch-recorder 0.3.0 → 0.4.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS;;;;CAIZ,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;AAEnF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,GAAG,eAAe,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;AAE9D,MAAM,MAAM,KAAK,GAAG;IACnB,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;AAE9B,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;AAEnD;;GAEG;AACH,UAAU,wBAAwB;IACjC;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,UAAU,8BAA+B,SAAQ,wBAAwB;IACxE;;;OAGG;IACH,qBAAqB,EAAE,KAAK,CAAC;IAC7B;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAS,EAAE,eAAe,CAAC;CAC3B;AAED;;GAEG;AACH,UAAU,iCAAkC,SAAQ,wBAAwB;IAC3E;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAC7B,8BAA8B,GAC9B,iCAAiC,CAAC;AAErC,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,YAAY;IACpD,KAAK,EAAE,CAAC,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,oBAAoB,CAAC;IAC9B;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACjC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS;;;;CAIZ,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;AAEnF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,GAAG,eAAe,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;AAE9D;;;;GAIG;AACH,UAAU,eAAe;IACxB;;;OAGG;IACH,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,UAAU,kBAAkB;IAC3B,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,SAAS,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,UAAU,SAAS;IAClB,IAAI,EAAE,SAAS,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG;IAClC,EAAE,EAAE,OAAO,SAAS,CAAC,GAAG,CAAC;IACzB,KAAK,EAAE,GAAG,CAAC;CACX,GAAG,CAAC,eAAe,GAAG,kBAAkB,CAAC,CAAC;AAE3C;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG;IACrC,EAAE,EAAE,OAAO,SAAS,CAAC,MAAM,CAAC;CAC5B,GAAG,CAAC,eAAe,GAAG,kBAAkB,CAAC,CAAC;AAE3C;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG;IACtC,EAAE,EAAE,OAAO,SAAS,CAAC,OAAO,CAAC;IAC7B,KAAK,EAAE,GAAG,CAAC;IACX;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,CAAC,eAAe,GAAG,kBAAkB,CAAC,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,CAAC;AAE1D,MAAM,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;AAE9B,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,YAAY;IACpD,KAAK,EAAE,CAAC,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,oBAAoB,CAAC;IAC9B;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAE,iBAAiB,EAAE,SAAS,EAAC,MAAM,YAAY,CAAC;AAE9E;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,EAAE,CAE1D;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAEpE;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAE3D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAMlD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CA2CvD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAmC5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,SAAS,EACf,eAAe,EAAE,eAAe,GAAG,SAAS,GAC1C,iBAAiB,GAAG,SAAS,CA6C/B;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CA8BjD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAc1D"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAE,iBAAiB,EAAE,SAAS,EAAC,MAAM,YAAY,CAAC;AAE9E;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,EAAE,CAE1D;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAEpE;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAE3D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAMlD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CA2CvD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAmC5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,SAAS,EACf,eAAe,EAAE,eAAe,GAAG,SAAS,GAC1C,iBAAiB,GAAG,SAAS,CAoD/B;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CA8BjD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAc1D"}
package/dist/utils.js CHANGED
@@ -150,6 +150,12 @@ export function findGetItemIdFn(path, getItemIdConfig) {
150
150
  if (typeof key === 'number') {
151
151
  continue;
152
152
  }
153
+ // If we already found a function, return it immediately
154
+ // This handles nested paths like ['items', 0, 'data', 'nested', 'value']
155
+ // where the config is { items: (item) => item.id }
156
+ if (typeof current === 'function') {
157
+ return current;
158
+ }
153
159
  if (current === undefined || typeof current !== 'object') {
154
160
  return undefined;
155
161
  }
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patch-recorder",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Record JSON patches (RFC 6902) from mutations applied to objects, arrays, Maps, and Sets via a proxy interface.",
5
5
  "keywords": [
6
6
  "patch",
package/src/arrays.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type {NonPrimitive, PatchPath, RecorderState} from './types.js';
2
- import {generateAddPatch, generateDeletePatch, generateReplacePatch} from './patches.js';
3
- import {createProxy} from './proxy.js';
2
+ import {generateAddPatch, generateDeletePatch, generateReplacePatch, generateSetPatch} from './patches.js';
3
+ import {createProxy, findArrayItemContext} from './proxy.js';
4
4
 
5
5
  // Module-level Sets for O(1) lookup instead of O(n) array includes
6
6
  const MUTATING_METHODS = new Set(['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']);
@@ -93,13 +93,19 @@ function generateArrayPatches(
93
93
  oldArray: unknown[] | null,
94
94
  oldLength: number,
95
95
  ) {
96
+ // Check if this array is nested inside a tracked item
97
+ // If so, all patches should include the parent item's id
98
+ const itemContext = findArrayItemContext(path, state);
99
+ const parentItem = itemContext?.item;
100
+ const itemPathIndex = itemContext?.pathIndex;
101
+
96
102
  switch (method) {
97
103
  case 'push': {
98
104
  // Generate add patches for each new element
99
105
  // oldLength is the starting index before push
100
106
  args.forEach((value, i) => {
101
107
  const index = oldLength + i;
102
- generateAddPatch(state, [...path, index], value);
108
+ generateAddPatch(state, [...path, index], value, parentItem, itemPathIndex);
103
109
  });
104
110
  // No length patch when array grows (aligned with mutative)
105
111
  break;
@@ -113,7 +119,7 @@ function generateArrayPatches(
113
119
  generateReplacePatch(state, [...path, 'length'], array.length, oldLength);
114
120
  } else {
115
121
  // When arrayLengthAssignment is false, generate remove patch for last element
116
- generateDeletePatch(state, [...path, oldLength - 1], result);
122
+ generateDeletePatch(state, [...path, oldLength - 1], result, parentItem, itemPathIndex);
117
123
  }
118
124
  }
119
125
  break;
@@ -122,14 +128,14 @@ function generateArrayPatches(
122
128
  case 'shift': {
123
129
  // Remove first element (shifted elements are handled automatically by JSON Patch spec)
124
130
  // We don't have oldValue here, but the result of shift() is the removed element
125
- generateDeletePatch(state, [...path, 0], result);
131
+ generateDeletePatch(state, [...path, 0], result, parentItem, itemPathIndex);
126
132
  break;
127
133
  }
128
134
 
129
135
  case 'unshift': {
130
136
  // Add new elements at the beginning (shifted elements are handled automatically by JSON Patch spec)
131
137
  args.forEach((value, i) => {
132
- generateAddPatch(state, [...path, i], value);
138
+ generateAddPatch(state, [...path, i], value, parentItem, itemPathIndex);
133
139
  });
134
140
  break;
135
141
  }
@@ -146,18 +152,19 @@ function generateArrayPatches(
146
152
  const deletedElements = result as any[];
147
153
 
148
154
  // First minCount elements: replace (overlap between add and delete)
155
+ // These are element replacements in a nested array - include parent item context
149
156
  for (let i = 0; i < minCount; i++) {
150
- generateReplacePatch(state, [...path, actualStart + i], addItems[i], deletedElements[i]);
157
+ generateSetPatch(state, [...path, actualStart + i], addItems[i], parentItem, itemPathIndex);
151
158
  }
152
159
 
153
160
  // Remaining add items: add
154
161
  for (let i = minCount; i < addItems.length; i++) {
155
- generateAddPatch(state, [...path, actualStart + i], addItems[i]);
162
+ generateAddPatch(state, [...path, actualStart + i], addItems[i], parentItem, itemPathIndex);
156
163
  }
157
164
 
158
165
  // Remaining delete items: remove (generate in reverse order)
159
166
  for (let i = actualDeleteCount - 1; i >= minCount; i--) {
160
- generateDeletePatch(state, [...path, actualStart + i], deletedElements[i]);
167
+ generateDeletePatch(state, [...path, actualStart + i], deletedElements[i], parentItem, itemPathIndex);
161
168
  }
162
169
 
163
170
  break;
@@ -167,7 +174,12 @@ function generateArrayPatches(
167
174
  case 'reverse': {
168
175
  // These reorder the entire array - generate full replace
169
176
  // oldValue contains the array before the mutation
170
- generateReplacePatch(state, path, array, oldArray);
177
+ // For nested arrays, include parent item context
178
+ if (itemContext) {
179
+ generateSetPatch(state, path, array, parentItem, itemPathIndex);
180
+ } else {
181
+ generateReplacePatch(state, path, array, oldArray);
182
+ }
171
183
  break;
172
184
  }
173
185
  }
package/src/index.ts CHANGED
@@ -23,14 +23,6 @@ export function recordPatches<
23
23
  T extends NonPrimitive,
24
24
  PatchesOption extends RecordPatchesOptions = {},
25
25
  >(state: T, mutate: (state: T) => void, options?: PatchesOption): Patches {
26
- // Runtime validation: getItemId requires arrayLengthAssignment: false
27
- if (options?.getItemId && options?.arrayLengthAssignment !== false) {
28
- throw new Error(
29
- 'getItemId requires arrayLengthAssignment: false. ' +
30
- 'Length patches cannot include individual item IDs.',
31
- );
32
- }
33
-
34
26
  const recorderState = {
35
27
  state,
36
28
  patches: [],
package/src/maps.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type {PatchPath, RecorderState} from './types.js';
2
- import {createProxy} from './proxy.js';
3
- import {generateAddPatch, generateDeletePatch, generateReplacePatch} from './patches.js';
2
+ import {createProxy, findArrayItemContext} from './proxy.js';
3
+ import {generateAddPatch, generateDeletePatch, generateSetPatch} from './patches.js';
4
4
  import {cloneIfNeeded} from './utils.js';
5
5
 
6
6
  /**
@@ -29,13 +29,18 @@ export function handleMapGet(
29
29
 
30
30
  // Generate patch
31
31
  const itemPath = [...path, key as any];
32
+
33
+ // Find parent item context if this Map is inside a tracked array item
34
+ const itemContext = findArrayItemContext(path, state);
32
35
 
33
36
  if (existed) {
34
- // Key exists - replace (pass oldValue for getItemId)
35
- generateReplacePatch(state, itemPath, cloneIfNeeded(value), oldValue);
37
+ // Key exists - replace
38
+ // Pass 'map' to skip getItemId for the Map's own keys
39
+ // but still include parent item id if nested inside a tracked array item
40
+ generateSetPatch(state, itemPath, cloneIfNeeded(value), itemContext?.item, itemContext?.pathIndex, 'map');
36
41
  } else {
37
42
  // Key doesn't exist - add
38
- generateAddPatch(state, itemPath, cloneIfNeeded(value));
43
+ generateAddPatch(state, itemPath, cloneIfNeeded(value), itemContext?.item, itemContext?.pathIndex);
39
44
  }
40
45
 
41
46
  return result;
@@ -49,7 +54,9 @@ export function handleMapGet(
49
54
 
50
55
  if (result) {
51
56
  const itemPath = [...path, key as any];
52
- generateDeletePatch(state, itemPath, cloneIfNeeded(oldValue));
57
+ // Find parent item context if this Map is inside a tracked array item
58
+ const itemContext = findArrayItemContext(path, state);
59
+ generateDeletePatch(state, itemPath, cloneIfNeeded(oldValue), itemContext?.item, itemContext?.pathIndex);
53
60
  }
54
61
 
55
62
  return result;
@@ -61,10 +68,13 @@ export function handleMapGet(
61
68
  const entries = Array.from(obj.entries());
62
69
  obj.clear();
63
70
 
71
+ // Find parent item context if this Map is inside a tracked array item
72
+ const itemContext = findArrayItemContext(path, state);
73
+
64
74
  // Generate remove patches for all items
65
75
  entries.forEach(([key, value]) => {
66
76
  const itemPath = [...path, key as any];
67
- generateDeletePatch(state, itemPath, cloneIfNeeded(value));
77
+ generateDeletePatch(state, itemPath, cloneIfNeeded(value), itemContext?.item, itemContext?.pathIndex);
68
78
  });
69
79
  };
70
80
  }
package/src/optimizer.ts CHANGED
@@ -203,9 +203,9 @@ function cancelArrayPushPop(patches: Patches): Patches {
203
203
  (a, b) => (b.path[b.path.length - 1] as number) - (a.path[a.path.length - 1] as number),
204
204
  );
205
205
 
206
- // Find pop patches (length reduction)
206
+ // Find pop patches (length reduction) - these are replace patches with a value
207
207
  const popPatches = groupPatches.filter(
208
- (p) => p.op === 'replace' && p.path[p.path.length - 1] === 'length',
208
+ (p) => p.op === 'replace' && p.path[p.path.length - 1] === 'length' && 'value' in p,
209
209
  );
210
210
 
211
211
  // Cancel pushes and pops that match (push at highest index, pop reduces length)
@@ -216,7 +216,7 @@ function cancelArrayPushPop(patches: Patches): Patches {
216
216
 
217
217
  // Check if the push index matches the pop target
218
218
  const pushIndex = pushPatch.path[pushPatch.path.length - 1] as number;
219
- const popLength = popPatch.value as number;
219
+ const popLength = (popPatch as {value: number}).value;
220
220
 
221
221
  // If push added at index pushIndex and pop reduced to popLength, they cancel
222
222
  // This is a heuristic: push adds at end, pop removes from end
@@ -241,7 +241,8 @@ function cancelOutOfBoundsPatches(patches: Patches): Patches {
241
241
  if (
242
242
  Array.isArray(patch.path) &&
243
243
  patch.path.length >= 2 &&
244
- patch.path[patch.path.length - 1] === 'length'
244
+ patch.path[patch.path.length - 1] === 'length' &&
245
+ 'value' in patch
245
246
  ) {
246
247
  const parentPath = pathToKey(patch.path.slice(0, -1));
247
248
  arrayLengths.set(parentPath, patch.value as number);
@@ -284,7 +285,7 @@ function mergePatches(patch1: Patch, patch2: Patch): Patch | null | undefined {
284
285
  // Same operations - keep the latest one
285
286
  if (op1 === op2) {
286
287
  // For replace operations, keep the latest value
287
- if (op1 === 'replace') {
288
+ if (op1 === 'replace' && 'value' in patch1 && 'value' in patch2) {
288
289
  // Skip if same reference (no-op)
289
290
  if (patch1.value === patch2.value) {
290
291
  return patch1;
@@ -292,7 +293,7 @@ function mergePatches(patch1: Patch, patch2: Patch): Patch | null | undefined {
292
293
  return patch2;
293
294
  }
294
295
  // For add operations, if adding same reference, it's a no-op
295
- if (op1 === 'add' && patch1.value === patch2.value) {
296
+ if (op1 === 'add' && 'value' in patch1 && 'value' in patch2 && patch1.value === patch2.value) {
296
297
  return patch1;
297
298
  }
298
299
  // For remove operations, don't merge (sequential removes should never be merged)
@@ -324,6 +325,19 @@ function mergePatches(patch1: Patch, patch2: Patch): Patch | null | undefined {
324
325
 
325
326
  if (op1 === 'remove' && op2 === 'add') {
326
327
  // Remove then add - this is a replace operation
328
+ // Preserve id and pathIndex from either patch (they should be the same if present)
329
+ const id = patch1.id ?? patch2.id;
330
+ const pathIndex = patch1.pathIndex ?? patch2.pathIndex;
331
+
332
+ if (id !== undefined && pathIndex !== undefined) {
333
+ return {
334
+ op: 'replace',
335
+ path: patch1.path,
336
+ value: patch2.value,
337
+ id,
338
+ pathIndex,
339
+ };
340
+ }
327
341
  return {
328
342
  op: 'replace',
329
343
  path: patch1.path,
package/src/patches.ts CHANGED
@@ -1,73 +1,165 @@
1
- import type {NonPrimitive, Patch, PatchPath, RecorderState} from './types.js';
1
+ import type {NonPrimitive, PatchPath, RecorderState, ReplacePatch, RemovePatch, AddPatch} from './types.js';
2
2
  import {Operation} from './types.js';
3
3
  import {cloneIfNeeded, findGetItemIdFn} from './utils.js';
4
4
 
5
5
  /**
6
- * Generate a replace patch for property changes
6
+ * Generate a replace patch for property changes.
7
+ * Used for field modifications inside items (e.g., state.items[0].name = 'new')
8
+ *
9
+ * @param parentItem - Optional parent item when modifying a field inside an array item.
10
+ * Used to extract the item's ID for the patch.
11
+ * @param itemPathIndex - Optional index indicating where the item path ends in the full path.
12
+ * Used for pathIndex in the patch.
13
+ * @param isMapOrSet - Deprecated/unused - Maps nested in tracked items now include parent item ID.
7
14
  */
8
15
  export function generateSetPatch(
9
16
  state: RecorderState<NonPrimitive>,
10
17
  path: PatchPath,
11
- oldValue: unknown,
12
18
  newValue: unknown,
19
+ parentItem?: unknown,
20
+ itemPathIndex?: number,
21
+ isMapOrSet?: 'map' | 'set',
13
22
  ) {
14
- const patch: any = {
15
- op: Operation.Replace,
16
- path,
17
- value: cloneIfNeeded(newValue),
18
- };
19
-
20
- // Add id if getItemId is configured for this path
21
- const getItemIdFn = findGetItemIdFn(path, state.options.getItemId);
22
- if (getItemIdFn && oldValue !== undefined) {
23
- const id = getItemIdFn(oldValue);
24
- if (id !== undefined && id !== null) {
25
- patch.id = id;
23
+ // Try to extract item id if parent item is provided
24
+ // Note: The isMapOrSet flag is no longer checked here because:
25
+ // - When a Map is nested inside a tracked array item, we want the parent item's id
26
+ // - The parentItem passed in is the array item, not the Map entry
27
+ let id: string | number | undefined;
28
+ if (parentItem !== undefined) {
29
+ const getItemIdFn = findGetItemIdFn(path, state.options.getItemId);
30
+ if (getItemIdFn) {
31
+ const extractedId = getItemIdFn(parentItem);
32
+ if (extractedId !== undefined && extractedId !== null) {
33
+ id = extractedId;
34
+ }
26
35
  }
27
36
  }
28
37
 
38
+ // Construct patch with proper type based on whether id is present
39
+ const patch: ReplacePatch =
40
+ id !== undefined && itemPathIndex !== undefined
41
+ ? {
42
+ op: Operation.Replace,
43
+ path,
44
+ value: cloneIfNeeded(newValue),
45
+ id,
46
+ pathIndex: itemPathIndex,
47
+ }
48
+ : {
49
+ op: Operation.Replace,
50
+ path,
51
+ value: cloneIfNeeded(newValue),
52
+ };
53
+
29
54
  state.patches.push(patch);
30
55
  }
31
56
 
32
57
  /**
33
- * Generate a remove patch for property deletions
58
+ * Generate a remove patch for property deletions.
59
+ *
60
+ * For ITEM removal (e.g., state.items.splice(1, 1)):
61
+ * - No id is included - the item is being removed, not modified.
62
+ *
63
+ * For FIELD removal from an item (e.g., delete state.items[0].optional):
64
+ * - The id of the parent item is included - the item is being modified.
65
+ *
66
+ * @param parentItem - Optional parent item when deleting a field from inside an array item.
67
+ * Used to extract the item's ID for the patch.
68
+ * @param itemPathIndex - Optional index indicating where the item path ends in the full path.
34
69
  */
35
70
  export function generateDeletePatch(
36
71
  state: RecorderState<NonPrimitive>,
37
72
  path: PatchPath,
38
- oldValue: unknown,
73
+ _oldValue: unknown,
74
+ parentItem?: unknown,
75
+ itemPathIndex?: number,
39
76
  ) {
40
- const patch: Patch = {
41
- op: Operation.Remove,
42
- path: path,
43
- };
44
-
45
- // Add id if getItemId is configured for this path
46
- const getItemIdFn = findGetItemIdFn(path, state.options.getItemId);
47
- if (getItemIdFn && oldValue !== undefined) {
48
- const id = getItemIdFn(oldValue);
49
- if (id !== undefined && id !== null) {
50
- patch.id = id;
77
+ // Try to extract item id if parent item is provided
78
+ let id: string | number | undefined;
79
+ if (parentItem !== undefined) {
80
+ const getItemIdFn = findGetItemIdFn(path, state.options.getItemId);
81
+ if (getItemIdFn) {
82
+ const extractedId = getItemIdFn(parentItem);
83
+ if (extractedId !== undefined && extractedId !== null) {
84
+ id = extractedId;
85
+ }
51
86
  }
52
87
  }
53
88
 
89
+ // Construct patch with proper type based on whether id is present
90
+ const patch: RemovePatch =
91
+ id !== undefined && itemPathIndex !== undefined
92
+ ? {
93
+ op: Operation.Remove,
94
+ path,
95
+ id,
96
+ pathIndex: itemPathIndex,
97
+ }
98
+ : {
99
+ op: Operation.Remove,
100
+ path,
101
+ };
102
+
54
103
  state.patches.push(patch);
55
104
  }
56
105
 
57
106
  /**
58
- * Generate an add patch for new properties
107
+ * Generate an add patch for new properties.
108
+ *
109
+ * For adding a FIELD to an existing item (e.g., state.items[0].newField = 'value'):
110
+ * - The id of the parent item is included - the item is being modified.
111
+ *
112
+ * For adding a NEW ITEM to an array (e.g., state.items.push(newItem)):
113
+ * - No id is included - we're not modifying an existing item.
114
+ *
115
+ * @param parentItem - Optional parent item when adding a field to an array item.
116
+ * Used to extract the item's ID for the patch.
117
+ * @param itemPathIndex - Optional index indicating where the item path ends in the full path.
59
118
  */
60
- export function generateAddPatch(state: RecorderState<any>, path: PatchPath, value: any) {
61
- const patch: Patch = {
62
- op: Operation.Add,
63
- path,
64
- value: cloneIfNeeded(value),
65
- };
119
+ export function generateAddPatch(
120
+ state: RecorderState<any>,
121
+ path: PatchPath,
122
+ value: any,
123
+ parentItem?: unknown,
124
+ itemPathIndex?: number,
125
+ ) {
126
+ // Try to extract item id if parent item is provided
127
+ let id: string | number | undefined;
128
+ if (parentItem !== undefined) {
129
+ const getItemIdFn = findGetItemIdFn(path, state.options.getItemId);
130
+ if (getItemIdFn) {
131
+ const extractedId = getItemIdFn(parentItem);
132
+ if (extractedId !== undefined && extractedId !== null) {
133
+ id = extractedId;
134
+ }
135
+ }
136
+ }
137
+
138
+ // Construct patch with proper type based on whether id is present
139
+ const patch: AddPatch =
140
+ id !== undefined && itemPathIndex !== undefined
141
+ ? {
142
+ op: Operation.Add,
143
+ path,
144
+ value: cloneIfNeeded(value),
145
+ id,
146
+ pathIndex: itemPathIndex,
147
+ }
148
+ : {
149
+ op: Operation.Add,
150
+ path,
151
+ value: cloneIfNeeded(value),
152
+ };
153
+
66
154
  state.patches.push(patch);
67
155
  }
68
156
 
69
157
  /**
70
- * Generate a replace patch for full object/array replacement
158
+ * Generate a replace patch for full item replacement or array length changes.
159
+ * Used for array length changes that need oldValue.
160
+ *
161
+ * Note: This function is used for array length changes only.
162
+ * For item replacements without id, use generateSetPatch instead.
71
163
  */
72
164
  export function generateReplacePatch(
73
165
  state: RecorderState<any>,
@@ -75,25 +167,20 @@ export function generateReplacePatch(
75
167
  value: unknown,
76
168
  oldValue?: unknown,
77
169
  ) {
78
- const patch: Patch = {
79
- op: Operation.Replace,
80
- path: path,
81
- value: cloneIfNeeded(value),
82
- };
83
-
84
- // Include oldValue for array length changes to enable consumers to detect element removal
85
- if (path.length > 0 && path[path.length - 1] === 'length' && oldValue !== undefined) {
86
- patch.oldValue = oldValue;
87
- }
88
-
89
- // Add id if getItemId is configured for this path
90
- const getItemIdFn = findGetItemIdFn(path, state.options.getItemId);
91
- if (getItemIdFn && oldValue !== undefined) {
92
- const id = getItemIdFn(oldValue);
93
- if (id !== undefined && id !== null) {
94
- patch.id = id;
95
- }
96
- }
170
+ // This function is now only used for array length changes
171
+ const patch: ReplacePatch =
172
+ path.length > 0 && path[path.length - 1] === 'length' && oldValue !== undefined
173
+ ? {
174
+ op: Operation.Replace,
175
+ path,
176
+ value: cloneIfNeeded(value),
177
+ oldValue,
178
+ }
179
+ : {
180
+ op: Operation.Replace,
181
+ path,
182
+ value: cloneIfNeeded(value),
183
+ };
97
184
 
98
185
  state.patches.push(patch);
99
186
  }