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 +197 -46
- package/dist/arrays.d.ts.map +1 -1
- package/dist/arrays.js +72 -72
- package/dist/arrays.js.map +1 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -2
- package/dist/index.js.map +1 -1
- package/dist/maps.d.ts.map +1 -1
- package/dist/maps.js +5 -7
- package/dist/maps.js.map +1 -1
- package/dist/optimizer.d.ts.map +1 -1
- package/dist/optimizer.js +109 -55
- package/dist/optimizer.js.map +1 -1
- package/dist/patches.d.ts +1 -1
- package/dist/patches.d.ts.map +1 -1
- package/dist/patches.js +26 -2
- package/dist/patches.js.map +1 -1
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +19 -3
- package/dist/proxy.js.map +1 -1
- package/dist/types.d.ts +37 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +11 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +48 -9
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/arrays.ts +78 -90
- package/src/index.ts +34 -2
- package/src/maps.ts +4 -6
- package/src/optimizer.ts +139 -57
- package/src/patches.ts +32 -4
- package/src/proxy.ts +17 -3
- package/src/types.ts +39 -4
- package/src/utils.ts +62 -10
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
|
-
- ✅ **
|
|
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
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
###
|
|
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
|
|
258
|
+
const patches = recordPatches(state, (draft) => {
|
|
218
259
|
draft.value = 3;
|
|
219
260
|
}, { pathAsArray: false });
|
|
220
|
-
console.log(
|
|
261
|
+
console.log(patches);
|
|
221
262
|
// [{ op: 'replace', path: '/value', value: 3 }]
|
|
222
263
|
|
|
223
|
-
//
|
|
224
|
-
const
|
|
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
|
-
}
|
|
229
|
-
|
|
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
|
|
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.
|
|
266
|
-
| Medium nested object | 0.
|
|
267
|
-
| Large nested object | 0.
|
|
268
|
-
| Array push (100k elements) | 3.
|
|
269
|
-
| Array index (100k elements) | 2.
|
|
270
|
-
| Map operations (100k entries) |
|
|
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** (
|
|
281
|
-
- **Array push** (
|
|
282
|
-
- **Array index assignment** (
|
|
283
|
-
- **Map operations** (
|
|
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
|
|
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 `
|
|
480
|
+
2. **Patch compression**: Reduces redundant patches via `compressPatches` option
|
|
330
481
|
|
|
331
482
|
## License
|
|
332
483
|
|
package/dist/arrays.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
9
|
-
if (mutatingMethods.includes(prop)) {
|
|
38
|
+
if (MUTATING_METHODS.has(prop)) {
|
|
10
39
|
return (...args) => {
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
79
|
+
// oldLength is the starting index before push
|
|
65
80
|
args.forEach((value, i) => {
|
|
66
|
-
const index =
|
|
81
|
+
const index = oldLength + i;
|
|
67
82
|
generateAddPatch(state, [...path, index], value);
|
|
68
83
|
});
|
|
69
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
//
|
|
121
|
-
addItems.
|
|
122
|
-
generateAddPatch(state, [...path,
|
|
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
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
137
|
+
// oldValue contains the array before the mutation
|
|
138
|
+
generateReplacePatch(state, path, [...obj], oldValue);
|
|
139
139
|
break;
|
|
140
140
|
}
|
|
141
141
|
}
|
package/dist/arrays.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arrays.js","sourceRoot":"","sources":["../src/arrays.ts"],"names":[],"mappings":"
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
|
|
39
|
-
if (options.
|
|
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
|