patch-recorder 0.0.0 → 0.1.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.
package/README.md CHANGED
@@ -9,8 +9,9 @@
9
9
  - ✅ **Accurate patches** - JSON Patch (RFC 6902) compliant
10
10
  - ✅ **Type safety** - Full TypeScript support
11
11
  - ✅ **Immediate patch generation** - Patches generated as mutations occur
12
- - ✅ **Optional optimization** - Can compress/merge redundant patches
12
+ - ✅ **Optimization enabled by default** - Automatically compresses/merges redundant patches
13
13
  - ✅ **Full collection support** - Works with objects, arrays, Maps, and Sets
14
+ - ✅ **Item ID tracking** - Optionally include item IDs in remove/replace patches
14
15
 
15
16
  ## Installation
16
17
 
@@ -55,9 +56,9 @@ Unlike mutative or immer, **patch-recorder mutates the original object in place*
55
56
  - Perfect for scenarios where you need both mutation tracking AND direct object manipulation
56
57
 
57
58
  **Performance:**
58
- - Similar performance to mutative for most operations
59
- - Slightly faster for object and Map operations
60
- - Comparable performance for large array operations
59
+ - Substantially faster than mutative (1.1x to 650x depending on operation)
60
+ - Especially dramatic speedups for array index and Map operations
61
+ - Consistent performance improvements across all data types
61
62
 
62
63
  ```typescript
63
64
  // With patch-recorder
@@ -76,7 +77,15 @@ const patches = recordPatches(state, (draft) => {
76
77
 
77
78
  Records JSON patches from mutations applied to the state.
78
79
 
79
- #### Parameters
80
+ ### `create(state, mutate, options?)`
81
+
82
+ Mutative-compatible API for easy switching between mutative and patch-recorder. Returns `[state, patches]` tuple like mutative does.
83
+
84
+ **Key difference from mutative:** Unlike mutative which creates a new state copy, this mutates the original object in place. The returned `state` is the same reference as the input state.
85
+
86
+ **Note:** The `enablePatches` option is forced to `true` by default for full mutative compatibility (patches are always returned).
87
+
88
+ #### Parameters (both functions)
80
89
 
81
90
  - **`state`** (`T extends NonPrimitive`): The state object to mutate and record patches from
82
91
  - **`mutate`** `(state: Draft<T>) => void`: Callback function that performs mutations on the draft
@@ -84,32 +93,26 @@ Records JSON patches from mutations applied to the state.
84
93
 
85
94
  #### Options
86
95
 
87
- ```typescript
88
- interface RecordPatchesOptions {
89
- /**
90
- * Return paths as arrays (default: true) or strings
91
- */
92
- pathAsArray?: boolean;
93
-
94
- /**
95
- * Include array length in patches (default: true)
96
- */
97
- arrayLengthAssignment?: boolean;
98
-
99
- /**
100
- * Optimize patches by merging redundant operations (default: false)
101
- */
102
- optimize?: boolean;
103
- }
104
- ```
96
+ For `recordPatches`:
97
+
98
+ - **`pathAsArray`** (boolean, default: `true`) - Return paths as arrays or strings
99
+ - **`arrayLengthAssignment`** (boolean, default: `true`) - When `true`, includes length patches when array shrinks (pop, shift, splice delete). When `false`, omits length patches entirely. Aligned with mutative's behavior.
100
+ - **`compressPatches`** (boolean, default: `true`) - Compress patches by merging redundant operations
101
+ - **`getItemId`** (object, optional) - Configuration for extracting item IDs (see [Item ID Tracking](#item-id-tracking))
102
+
103
+ For `create` (additional options for mutative compatibility):
104
+ - **`enablePatches`** (boolean, default: `true`) - Always true, patches are always returned
105
105
 
106
106
  #### Returns
107
107
 
108
- `Patches<true>` - Array of JSON patches
108
+ - **`recordPatches`**: Returns `Patches<true>` - Array of JSON patches
109
+ - **`create`**: Returns `[T, Patches<true>]` - Tuple of mutated state and patches
109
110
 
110
111
  ## Usage Examples
111
112
 
112
- ### Basic Object Mutations
113
+ ### Using `recordPatches`
114
+
115
+ #### Basic Object Mutations
113
116
 
114
117
  ```typescript
115
118
  const state = { count: 0, name: 'test' };
@@ -167,11 +170,12 @@ console.log(patches);
167
170
  // { op: 'replace', path: ['items', 1], value: 10 },
168
171
  // { op: 'remove', path: ['items', 0] },
169
172
  // { op: 'replace', path: ['items', 0], value: 2 },
170
- // { op: 'replace', path: ['items', 1], value: 3 },
171
- // { op: 'replace', path: ['items', 'length'], value: 3 }
173
+ // { op: 'replace', path: ['items', 1], value: 3 }
172
174
  // ]
173
175
  ```
174
176
 
177
+ **Note:** Array length patches are included only when the array shrinks (pop, shift, splice delete operations) to optimize performance. This aligns with mutative's behavior. When the array grows (push, unshift, splice add operations), length patches are omitted as the length change is implied by the add operations themselves.
178
+
175
179
  ### Map Operations
176
180
 
177
181
  ```typescript
@@ -208,28 +212,156 @@ console.log(patches);
208
212
  // ]
209
213
  ```
210
214
 
215
+ ### Using `create` (Mutative-compatible API)
216
+
217
+ The `create` function provides the same API as mutative for easy switching:
218
+
219
+ ```typescript
220
+ import {create} from 'patch-recorder';
221
+
222
+ const state = { user: { name: 'John' } };
223
+
224
+ const [nextState, patches] = create(state, (draft) => {
225
+ draft.user.name = 'Jane';
226
+ });
227
+
228
+ console.log(nextState.user.name); // 'Jane' (mutated in place!)
229
+ console.log(nextState === state); // true (same reference - unlike mutative)
230
+ console.log(patches);
231
+ // [{ op: 'replace', path: ['user', 'name'], value: 'Jane' }]
232
+ ```
233
+
234
+ #### Easy Migration from Mutative
235
+
236
+ ```typescript
237
+ // Before (with mutative)
238
+ import {create} from 'mutative';
239
+ const [newState, patches] = create(state, mutate, {enablePatches: true});
240
+ // newState !== state (mutative creates a copy)
241
+
242
+ // After (with patch-recorder) - EXACT SAME CODE!
243
+ import {create} from 'patch-recorder';
244
+ const [nextState, patches] = create(state, mutate, {enablePatches: true});
245
+ // nextState === state (patch-recorder mutates in place)
246
+ ```
247
+
248
+ **No code changes needed** - just change the import! The `enablePatches` option is forced to `true` by default, so it's always enabled.
249
+
211
250
  ### Using Options
212
251
 
252
+ For `recordPatches`:
253
+
213
254
  ```typescript
214
255
  const state = { value: 1 };
215
256
 
216
257
  // Use string paths instead of arrays
217
- const patches2 = recordPatches(state, (draft) => {
258
+ const patches = recordPatches(state, (draft) => {
218
259
  draft.value = 3;
219
260
  }, { pathAsArray: false });
220
- console.log(patches2);
261
+ console.log(patches);
221
262
  // [{ op: 'replace', path: '/value', value: 3 }]
222
263
 
223
- // Optimize patches (merge redundant operations)
224
- const patches3 = recordPatches(state, (draft) => {
264
+ // Compress patches (merge redundant operations) - enabled by default
265
+ const patches = recordPatches(state, (draft) => {
225
266
  draft.value = 4;
226
267
  draft.value = 5;
227
268
  draft.value = 5; // no-op
228
- }, { optimize: true });
229
- console.log(patches3);
269
+ });
270
+ // To disable compression:
271
+ // const patches = recordPatches(state, (draft) => { ... }, { compressPatches: false });
272
+ console.log(patches);
230
273
  // [{ op: 'replace', path: ['value'], value: 5 }]
274
+
275
+ // For create function, you also have to pass enablePatches (it's always true)
276
+ const [nextState, patches] = create(state, (draft) => {
277
+ draft.value = 5;
278
+ }, { enablePatches: true, pathAsArray: false, compressPatches: true });
279
+ ```
280
+
281
+ ### Item ID Tracking
282
+
283
+ When working with arrays, the patch path only tells you the index, not which item was affected. The `getItemId` option allows you to include item IDs in `remove` and `replace` patches, making it easier to track which items changed.
284
+
285
+ ```typescript
286
+ const state = {
287
+ users: [
288
+ { id: 'user-1', name: 'Alice' },
289
+ { id: 'user-2', name: 'Bob' },
290
+ { id: 'user-3', name: 'Charlie' },
291
+ ]
292
+ };
293
+
294
+ const patches = recordPatches(state, (draft) => {
295
+ draft.users.splice(1, 1); // Remove Bob
296
+ }, {
297
+ getItemId: {
298
+ users: (user) => user.id // Extract ID from each user
299
+ }
300
+ });
301
+
302
+ console.log(patches);
303
+ // [{ op: 'remove', path: ['users', 1], id: 'user-2' }]
304
+ // Without getItemId, you'd only know index 1 was removed, not that it was Bob
305
+ ```
306
+
307
+ #### Configuration Structure
308
+
309
+ The `getItemId` option is an object that mirrors your data structure:
310
+
311
+ ```typescript
312
+ recordPatches(state, mutate, {
313
+ getItemId: {
314
+ // Top-level arrays
315
+ items: (item) => item.id,
316
+ users: (user) => user.userId,
317
+
318
+ // Nested paths - use nested objects
319
+ app: {
320
+ data: {
321
+ todos: (todo) => todo._id
322
+ }
323
+ },
324
+
325
+ // Maps - same as arrays
326
+ entityMap: (entity) => entity.internalId
327
+ }
328
+ });
231
329
  ```
232
330
 
331
+ #### Works with Maps and Sets too
332
+
333
+ ```typescript
334
+ const state = {
335
+ entityMap: new Map([
336
+ ['key1', { internalId: 'entity-1', data: 'value1' }],
337
+ ]),
338
+ itemSet: new Set([
339
+ { id: 'set-item-1', value: 1 }
340
+ ])
341
+ };
342
+
343
+ const patches = recordPatches(state, (draft) => {
344
+ draft.entityMap.delete('key1');
345
+ }, {
346
+ getItemId: {
347
+ entityMap: (entity) => entity.internalId
348
+ }
349
+ });
350
+
351
+ console.log(patches);
352
+ // [{ op: 'remove', path: ['entityMap', 'key1'], id: 'entity-1' }]
353
+ ```
354
+
355
+ #### When IDs are included
356
+
357
+ - **`remove`** patches always include `id` when configured
358
+ - **`replace`** patches include `id` (of the OLD value being replaced)
359
+ - **`add`** patches do NOT include `id` (the value already contains it)
360
+
361
+ #### ID can be undefined/null
362
+
363
+ If the `getItemId` function returns `undefined` or `null`, the `id` field is omitted from the patch. This is useful when some items might not have IDs.
364
+
233
365
  ## Comparison with Mutative
234
366
 
235
367
  | Feature | Mutative | patch-recorder |
@@ -238,8 +370,27 @@ console.log(patches3);
238
370
  | Memory overhead | ❌ Yes (copies) | ✅ No |
239
371
  | Patch accuracy | ✅ Excellent | ✅ Excellent |
240
372
  | Type safety | ✅ Excellent | ✅ Excellent |
241
- | API similarity | Similar | ✅ Similar |
373
+ | API compatibility | - | ✅ `create()` function provides same API |
242
374
  | Use case | Immutable state | Mutable with tracking |
375
+ | Performance | Fast | 1.1-650x faster |
376
+
377
+ ### Easy Migration
378
+
379
+ ```typescript
380
+ // Switching from mutative to patch-recorder is simple:
381
+ // Just change the import - no other changes needed!
382
+
383
+ // Before
384
+ import {create} from 'mutative';
385
+ const [nextState, patches] = create(state, mutate, {enablePatches: true});
386
+
387
+ // After - EXACT SAME CODE!
388
+ import {create} from 'patch-recorder';
389
+ const [nextState, patches] = create(state, mutate, {enablePatches: true});
390
+
391
+ // Note: patch-recorder mutates in place, so nextState === state
392
+ // If you rely on immutability, you may need to clone before mutation
393
+ ```
243
394
 
244
395
  ### When to Use patch-recorder
245
396
 
@@ -262,12 +413,12 @@ patch-recorder provides substantial performance improvements over mutative while
262
413
 
263
414
  | Operation | Mutative | patch-recorder | Speedup |
264
415
  |-----------|----------|----------------|---------|
265
- | Simple object mutation | 0.0272ms | 0.0110ms | **2.48x** |
266
- | Medium nested object | 0.0268ms | 0.0114ms | **2.35x** |
267
- | Large nested object | 0.0094ms | 0.0040ms | **2.38x** |
268
- | Array push (100k elements) | 3.277ms | 1.155ms | **2.84x** |
269
- | Array index (100k elements) | 2.966ms | 0.004ms | **826x** |
270
- | Map operations (100k entries) | 11.384ms | 0.011ms | **1,067x** |
416
+ | Simple object mutation | 0.0215ms | 0.0148ms | **1.45x** |
417
+ | Medium nested object | 0.0254ms | 0.0221ms | **1.15x** |
418
+ | Large nested object | 0.0088ms | 0.0078ms | **1.13x** |
419
+ | Array push (100k elements) | 3.0311ms | 0.6809ms | **4.45x** |
420
+ | Array index (100k elements) | 2.6097ms | 0.0069ms | **380x** |
421
+ | Map operations (100k entries) | 10.4033ms | 0.0160ms | **650x** |
271
422
 
272
423
  **Memory Usage:**
273
424
  - **Mutative**: Creates copies (memory overhead proportional to state size)
@@ -277,17 +428,17 @@ patch-recorder provides substantial performance improvements over mutative while
277
428
 
278
429
  The benchmark results reveal patch-recorder's massive advantage for operations that would require copying large data structures:
279
430
 
280
- - **Object mutations** (2.35-2.48x faster) - Consistent speedups due to simpler proxy overhead
281
- - **Array push** (2.84x faster) - Avoids copying entire arrays on mutation
282
- - **Array index assignment** (826x faster) - **Massive speedup** by not copying 100k-element arrays
283
- - **Map operations** (1,067x faster) - **Incredible speedup** by not copying 100k-entry Maps
431
+ - **Object mutations** (1.13-1.45x faster) - Consistent speedups due to simpler proxy overhead
432
+ - **Array push** (4.45x faster) - Avoids copying entire arrays on mutation
433
+ - **Array index assignment** (380x faster) - **Massive speedup** by not copying 100k-element arrays
434
+ - **Map operations** (650x faster) - **Incredible speedup** by not copying 100k-entry Maps
284
435
 
285
436
  **Why the dramatic differences?**
286
437
  - patch-recorder mutates in place, so array index assignment and Map operations don't require copying
287
438
  - mutative's copy-on-write approach is elegant but incurs significant overhead for large collections
288
439
  - The advantage scales with data size - the larger the collection, the bigger the speedup
289
440
 
290
- **Note on mutative's performance:** Mutative is impressively fast for object mutations and offers excellent immutability guarantees. Its speedups of 2-3x for objects are reasonable trade-offs for immutable state management.
441
+ **Note on mutative's performance:** Mutative is impressively fast for object mutations and offers excellent immutability guarantees. Its speedups of ~1.1-1.5x for objects are reasonable trade-offs for immutable state management.
291
442
 
292
443
  ### Run Benchmarks
293
444
 
@@ -326,7 +477,7 @@ npm run benchmark
326
477
  ### Optimization Tips
327
478
 
328
479
  1. **Lazy proxy creation**: Only creates proxies for accessed properties
329
- 2. **Patch compression**: Reduces redundant patches via `optimize` option
480
+ 2. **Patch compression**: Reduces redundant patches via `compressPatches` option
330
481
 
331
482
  ## License
332
483
 
@@ -1 +1 @@
1
- {"version":3,"file":"arrays.d.ts","sourceRoot":"","sources":["../src/arrays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,YAAY,CAAC;AAK9C;;GAEG;AACH,wBAAgB,cAAc,CAC7B,GAAG,EAAE,GAAG,EAAE,EACV,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EACzB,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,GACvB,GAAG,CA2DL"}
1
+ {"version":3,"file":"arrays.d.ts","sourceRoot":"","sources":["../src/arrays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,YAAY,CAAC;AAoC9C;;GAEG;AACH,wBAAgB,cAAc,CAC7B,GAAG,EAAE,GAAG,EAAE,EACV,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EACzB,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,GACvB,GAAG,CA4CL"}
package/dist/arrays.js CHANGED
@@ -1,42 +1,57 @@
1
1
  import { generateAddPatch, generateDeletePatch, generateReplacePatch } from './patches.js';
2
2
  import { createProxy } from './proxy.js';
3
+ // Module-level Sets for O(1) lookup instead of O(n) array includes
4
+ const MUTATING_METHODS = new Set([
5
+ 'push',
6
+ 'pop',
7
+ 'shift',
8
+ 'unshift',
9
+ 'splice',
10
+ 'sort',
11
+ 'reverse',
12
+ ]);
13
+ const NON_MUTATING_METHODS = new Set([
14
+ 'map',
15
+ 'filter',
16
+ 'reduce',
17
+ 'reduceRight',
18
+ 'forEach',
19
+ 'find',
20
+ 'findIndex',
21
+ 'some',
22
+ 'every',
23
+ 'includes',
24
+ 'indexOf',
25
+ 'lastIndexOf',
26
+ 'slice',
27
+ 'concat',
28
+ 'join',
29
+ 'flat',
30
+ 'flatMap',
31
+ 'at',
32
+ ]);
3
33
  /**
4
34
  * Handle array method calls and property access
5
35
  */
6
36
  export function handleArrayGet(obj, prop, path, state) {
7
37
  // Mutating methods
8
- const mutatingMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
9
- if (mutatingMethods.includes(prop)) {
38
+ if (MUTATING_METHODS.has(prop)) {
10
39
  return (...args) => {
11
- const oldValue = [...obj]; // Snapshot before mutation
40
+ // Optimized: only copy what's needed for each method
41
+ const oldLength = obj.length;
42
+ let oldValue = null;
43
+ // Only create full copy for sort/reverse which need the entire old array
44
+ if (prop === 'sort' || prop === 'reverse') {
45
+ oldValue = [...obj];
46
+ }
12
47
  const result = Array.prototype[prop].apply(obj, args);
13
48
  // Generate patches based on the method
14
- generateArrayPatches(state, obj, prop, args, result, path, oldValue);
49
+ generateArrayPatches(state, obj, prop, args, result, path, oldValue, oldLength);
15
50
  return result;
16
51
  };
17
52
  }
18
53
  // Non-mutating methods - just return them bound to the array
19
- const nonMutatingMethods = [
20
- 'map',
21
- 'filter',
22
- 'reduce',
23
- 'reduceRight',
24
- 'forEach',
25
- 'find',
26
- 'findIndex',
27
- 'some',
28
- 'every',
29
- 'includes',
30
- 'indexOf',
31
- 'lastIndexOf',
32
- 'slice',
33
- 'concat',
34
- 'join',
35
- 'flat',
36
- 'flatMap',
37
- 'at',
38
- ];
39
- if (nonMutatingMethods.includes(prop)) {
54
+ if (NON_MUTATING_METHODS.has(prop)) {
40
55
  return Array.prototype[prop].bind(obj);
41
56
  }
42
57
  // Property access
@@ -57,85 +72,70 @@ export function handleArrayGet(obj, prop, path, state) {
57
72
  /**
58
73
  * Generate patches for array mutations
59
74
  */
60
- function generateArrayPatches(state, obj, method, args, result, path, oldValue) {
75
+ function generateArrayPatches(state, obj, method, args, result, path, oldValue, oldLength) {
61
76
  switch (method) {
62
77
  case 'push': {
63
78
  // Generate add patches for each new element
64
- const startIndex = oldValue.length;
79
+ // oldLength is the starting index before push
65
80
  args.forEach((value, i) => {
66
- const index = startIndex + i;
81
+ const index = oldLength + i;
67
82
  generateAddPatch(state, [...path, index], value);
68
83
  });
69
- // Generate length patch if option is enabled
70
- if (state.options.arrayLengthAssignment !== false) {
71
- generateReplacePatch(state, [...path, 'length'], obj.length);
72
- }
84
+ // No length patch when array grows (aligned with mutative)
73
85
  break;
74
86
  }
75
87
  case 'pop': {
76
- // Generate remove patch for the removed element
77
- const removedIndex = oldValue.length - 1;
78
- generateDeletePatch(state, [...path, removedIndex], result);
79
- // Generate length patch if option is enabled
80
88
  if (state.options.arrayLengthAssignment !== false) {
81
- generateReplacePatch(state, [...path, 'length'], obj.length);
89
+ // Generate length replace patch (mutative uses this instead of remove)
90
+ generateReplacePatch(state, [...path, 'length'], obj.length, oldLength);
91
+ }
92
+ else {
93
+ // When arrayLengthAssignment is false, generate remove patch for last element
94
+ generateDeletePatch(state, [...path, oldLength - 1], result);
82
95
  }
83
96
  break;
84
97
  }
85
98
  case 'shift': {
86
- // Generate remove patch for the removed element
99
+ // Remove first element (shifted elements are handled automatically by JSON Patch spec)
100
+ // We don't have oldValue here, but the result of shift() is the removed element
87
101
  generateDeletePatch(state, [...path, 0], result);
88
- // Shift is complex - we need to update all remaining elements
89
- // Update all shifted elements (after the shift, each element moves to index - 1)
90
- for (let i = 0; i < obj.length; i++) {
91
- generateReplacePatch(state, [...path, i], oldValue[i + 1]);
92
- }
93
- // Generate length patch if option is enabled
94
- if (state.options.arrayLengthAssignment !== false) {
95
- generateReplacePatch(state, [...path, 'length'], obj.length);
96
- }
97
102
  break;
98
103
  }
99
104
  case 'unshift': {
100
- // Add new elements at the beginning
105
+ // Add new elements at the beginning (shifted elements are handled automatically by JSON Patch spec)
101
106
  args.forEach((value, i) => {
102
107
  generateAddPatch(state, [...path, i], value);
103
108
  });
104
- // Update all existing elements
105
- for (let i = 0; i < oldValue.length; i++) {
106
- generateReplacePatch(state, [...path, i + args.length], oldValue[i]);
107
- }
108
- // Generate length patch if option is enabled
109
- if (state.options.arrayLengthAssignment !== false) {
110
- generateReplacePatch(state, [...path, 'length'], obj.length);
111
- }
112
109
  break;
113
110
  }
114
111
  case 'splice': {
115
- const [start, deleteCount, ...addItems] = args;
116
- // Generate remove patches for deleted items
117
- for (let i = 0; i < deleteCount; i++) {
118
- generateDeletePatch(state, [...path, start], oldValue[start]);
112
+ const [start, deleteCount = 0, ...addItems] = args;
113
+ const actualStart = start < 0 ? Math.max(oldLength + start, 0) : Math.min(start, oldLength);
114
+ const actualDeleteCount = Math.min(deleteCount, oldLength - actualStart);
115
+ const minCount = Math.min(actualDeleteCount, addItems.length);
116
+ // For splice, we need the old values for delete operations
117
+ // Since we don't have oldValue, we need to track what was deleted
118
+ // The result of splice() is the array of deleted elements
119
+ const deletedElements = result;
120
+ // First minCount elements: replace (overlap between add and delete)
121
+ for (let i = 0; i < minCount; i++) {
122
+ generateReplacePatch(state, [...path, actualStart + i], addItems[i], deletedElements[i]);
119
123
  }
120
- // Generate add patches for new items
121
- addItems.forEach((item, i) => {
122
- generateAddPatch(state, [...path, start + i], item);
123
- });
124
- // If there are both deletions and additions, update the shifted elements
125
- const itemsToShift = oldValue.length - start - deleteCount;
126
- for (let i = 0; i < itemsToShift; i++) {
127
- generateReplacePatch(state, [...path, start + addItems.length + i], oldValue[start + deleteCount + i]);
124
+ // Remaining add items: add
125
+ for (let i = minCount; i < addItems.length; i++) {
126
+ generateAddPatch(state, [...path, actualStart + i], addItems[i]);
128
127
  }
129
- // Generate length patch if option is enabled
130
- if (state.options.arrayLengthAssignment !== false) {
131
- generateReplacePatch(state, [...path, 'length'], obj.length);
128
+ // Remaining delete items: remove (generate in reverse order)
129
+ for (let i = actualDeleteCount - 1; i >= minCount; i--) {
130
+ generateDeletePatch(state, [...path, actualStart + i], deletedElements[i]);
132
131
  }
133
132
  break;
134
133
  }
135
134
  case 'sort':
136
135
  case 'reverse': {
137
136
  // These reorder the entire array - generate full replace
138
- generateReplacePatch(state, path, [...obj]);
137
+ // oldValue contains the array before the mutation
138
+ generateReplacePatch(state, path, [...obj], oldValue);
139
139
  break;
140
140
  }
141
141
  }
@@ -1 +1 @@
1
- {"version":3,"file":"arrays.js","sourceRoot":"","sources":["../src/arrays.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"arrays.js","sourceRoot":"","sources":["../src/arrays.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -17,5 +17,28 @@ import type { NonPrimitive, Draft, RecordPatchesOptions, Patches } from './types
17
17
  * console.log(patches); // [{ op: 'replace', path: ['user', 'name'], value: 'Jane' }]
18
18
  */
19
19
  export declare function recordPatches<T extends NonPrimitive>(state: T, mutate: (state: Draft<T>) => void, options?: RecordPatchesOptions): Patches<true>;
20
+ /**
21
+ * Mutative-compatible API for easy switching between mutative and patch-recorder.
22
+ * Returns [state, patches] tuple like mutative does.
23
+ *
24
+ * Unlike mutative, this mutates the original object in place (state === originalState).
25
+ * The returned state is the same reference as the input state for API compatibility.
26
+ *
27
+ * @param state - The state to mutate and record patches from
28
+ * @param mutate - A function that receives a draft of the state and applies mutations
29
+ * @param options - Configuration options (enablePatches is forced but ignored - patches are always returned)
30
+ * @returns Tuple [state, patches] where state is the mutated state (same reference as input)
31
+ *
32
+ * @example
33
+ * const state = { user: { name: 'John' } };
34
+ * const [nextState, patches] = create(state, (draft) => {
35
+ * draft.user.name = 'Jane';
36
+ * }, {enabledPatches: true});
37
+ * console.log(nextState === state); // true (mutated in place!)
38
+ * console.log(patches); // [{ op: 'replace', path: ['user', 'name'], value: 'Jane' }]
39
+ */
40
+ export declare function create<T extends NonPrimitive>(state: T, mutate: (state: Draft<T>) => void, options?: RecordPatchesOptions & {
41
+ enablePatches: true;
42
+ }): [T, Patches<true>];
20
43
  export type { NonPrimitive, Draft, RecordPatchesOptions, Patches, Patch, Operation, } from './types.js';
21
44
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,YAAY,EACZ,KAAK,EACL,oBAAoB,EACpB,OAAO,EAGP,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,YAAY,EACnD,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,EACjC,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAGD,YAAY,EACX,YAAY,EACZ,KAAK,EACL,oBAAoB,EACpB,OAAO,EACP,KAAK,EACL,SAAS,GACT,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,YAAY,EACZ,KAAK,EACL,oBAAoB,EACpB,OAAO,EAGP,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,YAAY,EACnD,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,EACjC,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAGD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,YAAY,EAC5C,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,EACjC,OAAO,GAAE,oBAAoB,GAAG;IAAC,aAAa,EAAE,IAAI,CAAA;CAAyB,GAC3E,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAKpB;AAGD,YAAY,EACX,YAAY,EACZ,KAAK,EACL,oBAAoB,EACpB,OAAO,EACP,KAAK,EACL,SAAS,GACT,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -35,10 +35,36 @@ export function recordPatches(state, mutate, options = {}) {
35
35
  const proxy = createProxy(state, [], recorderState);
36
36
  // Apply mutations
37
37
  mutate(proxy);
38
- // Return patches (optionally optimized)
39
- if (options.optimize) {
38
+ // Return patches (optionally compressed)
39
+ if (options.compressPatches !== false) {
40
40
  return compressPatches(recorderState.patches);
41
41
  }
42
42
  return recorderState.patches;
43
43
  }
44
+ /**
45
+ * Mutative-compatible API for easy switching between mutative and patch-recorder.
46
+ * Returns [state, patches] tuple like mutative does.
47
+ *
48
+ * Unlike mutative, this mutates the original object in place (state === originalState).
49
+ * The returned state is the same reference as the input state for API compatibility.
50
+ *
51
+ * @param state - The state to mutate and record patches from
52
+ * @param mutate - A function that receives a draft of the state and applies mutations
53
+ * @param options - Configuration options (enablePatches is forced but ignored - patches are always returned)
54
+ * @returns Tuple [state, patches] where state is the mutated state (same reference as input)
55
+ *
56
+ * @example
57
+ * const state = { user: { name: 'John' } };
58
+ * const [nextState, patches] = create(state, (draft) => {
59
+ * draft.user.name = 'Jane';
60
+ * }, {enabledPatches: true});
61
+ * console.log(nextState === state); // true (mutated in place!)
62
+ * console.log(patches); // [{ op: 'replace', path: ['user', 'name'], value: 'Jane' }]
63
+ */
64
+ export function create(state, mutate, options = { enablePatches: true }) {
65
+ // Extract enablePatches but ignore it (patches are always returned)
66
+ const { enablePatches, ...recordPatchesOptions } = options;
67
+ const patches = recordPatches(state, mutate, recordPatchesOptions);
68
+ return [state, patches];
69
+ }
44
70
  //# sourceMappingURL=index.js.map