patch-recorder 0.1.0 → 0.2.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 +1 -75
- package/dist/arrays.d.ts +2 -2
- package/dist/arrays.d.ts.map +1 -1
- package/dist/arrays.js +12 -20
- package/dist/arrays.js.map +1 -1
- package/dist/index.d.ts +1 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -34
- package/dist/index.js.map +1 -1
- package/dist/maps.d.ts +2 -2
- package/dist/maps.d.ts.map +1 -1
- package/dist/maps.js +4 -20
- package/dist/maps.js.map +1 -1
- package/dist/optimizer.d.ts +16 -1
- package/dist/optimizer.d.ts.map +1 -1
- package/dist/optimizer.js +114 -15
- package/dist/optimizer.js.map +1 -1
- package/dist/patches.d.ts +5 -5
- package/dist/patches.d.ts.map +1 -1
- package/dist/patches.js +5 -5
- package/dist/patches.js.map +1 -1
- package/dist/proxy.d.ts +2 -2
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +24 -52
- package/dist/proxy.js.map +1 -1
- package/dist/sets.d.ts +2 -2
- package/dist/sets.d.ts.map +1 -1
- package/dist/sets.js +4 -20
- package/dist/sets.js.map +1 -1
- package/dist/types.d.ts +14 -33
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +28 -13
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +76 -20
- package/dist/utils.js.map +1 -1
- package/package.json +2 -1
- package/src/arrays.ts +22 -30
- package/src/index.ts +9 -54
- package/src/maps.ts +12 -30
- package/src/optimizer.ts +146 -28
- package/src/patches.ts +20 -21
- package/src/proxy.ts +31 -60
- package/src/sets.ts +11 -30
- package/src/types.ts +16 -40
- package/src/utils.ts +81 -28
package/README.md
CHANGED
|
@@ -77,10 +77,6 @@ const patches = recordPatches(state, (draft) => {
|
|
|
77
77
|
|
|
78
78
|
Records JSON patches from mutations applied to the state.
|
|
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
80
|
**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
81
|
|
|
86
82
|
**Note:** The `enablePatches` option is forced to `true` by default for full mutative compatibility (patches are always returned).
|
|
@@ -93,20 +89,15 @@ Mutative-compatible API for easy switching between mutative and patch-recorder.
|
|
|
93
89
|
|
|
94
90
|
#### Options
|
|
95
91
|
|
|
96
|
-
For `recordPatches`:
|
|
97
92
|
|
|
98
|
-
- **`pathAsArray`** (boolean, default: `true`) - Return paths as arrays or strings
|
|
99
93
|
- **`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
94
|
- **`compressPatches`** (boolean, default: `true`) - Compress patches by merging redundant operations
|
|
101
95
|
- **`getItemId`** (object, optional) - Configuration for extracting item IDs (see [Item ID Tracking](#item-id-tracking))
|
|
102
96
|
|
|
103
|
-
For `create` (additional options for mutative compatibility):
|
|
104
|
-
- **`enablePatches`** (boolean, default: `true`) - Always true, patches are always returned
|
|
105
97
|
|
|
106
98
|
#### Returns
|
|
107
99
|
|
|
108
|
-
-
|
|
109
|
-
- **`create`**: Returns `[T, Patches<true>]` - Tuple of mutated state and patches
|
|
100
|
+
- `Patches` - Array of JSON patches
|
|
110
101
|
|
|
111
102
|
## Usage Examples
|
|
112
103
|
|
|
@@ -212,41 +203,6 @@ console.log(patches);
|
|
|
212
203
|
// ]
|
|
213
204
|
```
|
|
214
205
|
|
|
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
|
-
|
|
250
206
|
### Using Options
|
|
251
207
|
|
|
252
208
|
For `recordPatches`:
|
|
@@ -254,12 +210,6 @@ For `recordPatches`:
|
|
|
254
210
|
```typescript
|
|
255
211
|
const state = { value: 1 };
|
|
256
212
|
|
|
257
|
-
// Use string paths instead of arrays
|
|
258
|
-
const patches = recordPatches(state, (draft) => {
|
|
259
|
-
draft.value = 3;
|
|
260
|
-
}, { pathAsArray: false });
|
|
261
|
-
console.log(patches);
|
|
262
|
-
// [{ op: 'replace', path: '/value', value: 3 }]
|
|
263
213
|
|
|
264
214
|
// Compress patches (merge redundant operations) - enabled by default
|
|
265
215
|
const patches = recordPatches(state, (draft) => {
|
|
@@ -272,11 +222,6 @@ const patches = recordPatches(state, (draft) => {
|
|
|
272
222
|
console.log(patches);
|
|
273
223
|
// [{ op: 'replace', path: ['value'], value: 5 }]
|
|
274
224
|
|
|
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
225
|
|
|
281
226
|
### Item ID Tracking
|
|
282
227
|
|
|
@@ -370,28 +315,9 @@ If the `getItemId` function returns `undefined` or `null`, the `id` field is omi
|
|
|
370
315
|
| Memory overhead | ❌ Yes (copies) | ✅ No |
|
|
371
316
|
| Patch accuracy | ✅ Excellent | ✅ Excellent |
|
|
372
317
|
| Type safety | ✅ Excellent | ✅ Excellent |
|
|
373
|
-
| API compatibility | - | ✅ `create()` function provides same API |
|
|
374
318
|
| Use case | Immutable state | Mutable with tracking |
|
|
375
319
|
| Performance | Fast | 1.1-650x faster |
|
|
376
320
|
|
|
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
|
-
```
|
|
394
|
-
|
|
395
321
|
### When to Use patch-recorder
|
|
396
322
|
|
|
397
323
|
Use **patch-recorder** when you need:
|
package/dist/arrays.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { RecorderState } from './types.js';
|
|
1
|
+
import type { NonPrimitive, PatchPath, RecorderState } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Handle array method calls and property access
|
|
4
4
|
*/
|
|
5
|
-
export declare function handleArrayGet(
|
|
5
|
+
export declare function handleArrayGet(array: unknown[], prop: string, path: PatchPath, state: RecorderState<NonPrimitive>): any;
|
|
6
6
|
//# sourceMappingURL=arrays.d.ts.map
|
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,YAAY,EAAE,SAAS,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AA4BvE;;GAEG;AACH,wBAAgB,cAAc,CAC7B,KAAK,EAAE,OAAO,EAAE,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAChC,GAAG,CA4CL"}
|
package/dist/arrays.js
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { generateAddPatch, generateDeletePatch, generateReplacePatch } from './patches.js';
|
|
2
2
|
import { createProxy } from './proxy.js';
|
|
3
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
|
-
]);
|
|
4
|
+
const MUTATING_METHODS = new Set(['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']);
|
|
13
5
|
const NON_MUTATING_METHODS = new Set([
|
|
14
6
|
'map',
|
|
15
7
|
'filter',
|
|
@@ -33,32 +25,32 @@ const NON_MUTATING_METHODS = new Set([
|
|
|
33
25
|
/**
|
|
34
26
|
* Handle array method calls and property access
|
|
35
27
|
*/
|
|
36
|
-
export function handleArrayGet(
|
|
28
|
+
export function handleArrayGet(array, prop, path, state) {
|
|
37
29
|
// Mutating methods
|
|
38
30
|
if (MUTATING_METHODS.has(prop)) {
|
|
39
31
|
return (...args) => {
|
|
40
32
|
// Optimized: only copy what's needed for each method
|
|
41
|
-
const oldLength =
|
|
33
|
+
const oldLength = array.length;
|
|
42
34
|
let oldValue = null;
|
|
43
35
|
// Only create full copy for sort/reverse which need the entire old array
|
|
44
36
|
if (prop === 'sort' || prop === 'reverse') {
|
|
45
|
-
oldValue = [...
|
|
37
|
+
oldValue = [...array];
|
|
46
38
|
}
|
|
47
|
-
const result = Array.prototype[prop].apply(
|
|
39
|
+
const result = Array.prototype[prop].apply(array, args);
|
|
48
40
|
// Generate patches based on the method
|
|
49
|
-
generateArrayPatches(state,
|
|
41
|
+
generateArrayPatches(state, array, prop, args, result, path, oldValue, oldLength);
|
|
50
42
|
return result;
|
|
51
43
|
};
|
|
52
44
|
}
|
|
53
45
|
// Non-mutating methods - just return them bound to the array
|
|
54
46
|
if (NON_MUTATING_METHODS.has(prop)) {
|
|
55
|
-
return Array.prototype[prop].bind(
|
|
47
|
+
return Array.prototype[prop].bind(array);
|
|
56
48
|
}
|
|
57
49
|
// Property access
|
|
58
50
|
if (prop === 'length') {
|
|
59
|
-
return
|
|
51
|
+
return array.length;
|
|
60
52
|
}
|
|
61
|
-
const value =
|
|
53
|
+
const value = array[prop];
|
|
62
54
|
// For numeric properties (array indices), check if the value is an object/array
|
|
63
55
|
// If so, return a proxy to enable nested mutation tracking
|
|
64
56
|
if (!isNaN(Number(prop)) && typeof value === 'object' && value !== null) {
|
|
@@ -72,7 +64,7 @@ export function handleArrayGet(obj, prop, path, state) {
|
|
|
72
64
|
/**
|
|
73
65
|
* Generate patches for array mutations
|
|
74
66
|
*/
|
|
75
|
-
function generateArrayPatches(state,
|
|
67
|
+
function generateArrayPatches(state, array, method, args, result, path, oldArray, oldLength) {
|
|
76
68
|
switch (method) {
|
|
77
69
|
case 'push': {
|
|
78
70
|
// Generate add patches for each new element
|
|
@@ -87,7 +79,7 @@ function generateArrayPatches(state, obj, method, args, result, path, oldValue,
|
|
|
87
79
|
case 'pop': {
|
|
88
80
|
if (state.options.arrayLengthAssignment !== false) {
|
|
89
81
|
// Generate length replace patch (mutative uses this instead of remove)
|
|
90
|
-
generateReplacePatch(state, [...path, 'length'],
|
|
82
|
+
generateReplacePatch(state, [...path, 'length'], array.length, oldLength);
|
|
91
83
|
}
|
|
92
84
|
else {
|
|
93
85
|
// When arrayLengthAssignment is false, generate remove patch for last element
|
|
@@ -135,7 +127,7 @@ function generateArrayPatches(state, obj, method, args, result, path, oldValue,
|
|
|
135
127
|
case 'reverse': {
|
|
136
128
|
// These reorder the entire array - generate full replace
|
|
137
129
|
// oldValue contains the array before the mutation
|
|
138
|
-
generateReplacePatch(state, path,
|
|
130
|
+
generateReplacePatch(state, path, array, oldArray);
|
|
139
131
|
break;
|
|
140
132
|
}
|
|
141
133
|
}
|
package/dist/arrays.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arrays.js","sourceRoot":"","sources":["../src/arrays.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,gBAAgB,EAAE,mBAAmB,EAAE,oBAAoB,EAAC,MAAM,cAAc,CAAC;AACzF,OAAO,EAAC,WAAW,EAAC,MAAM,YAAY,CAAC;AAEvC,mEAAmE;AACnE,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC
|
|
1
|
+
{"version":3,"file":"arrays.js","sourceRoot":"","sources":["../src/arrays.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,29 +16,6 @@ import type { NonPrimitive, Draft, RecordPatchesOptions, Patches } from './types
|
|
|
16
16
|
* console.log(state.user.name); // 'Jane' (mutated in place!)
|
|
17
17
|
* console.log(patches); // [{ op: 'replace', path: ['user', 'name'], value: 'Jane' }]
|
|
18
18
|
*/
|
|
19
|
-
export declare function recordPatches<T extends NonPrimitive>(state: T, mutate: (state: Draft<T>) => void, options?:
|
|
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>];
|
|
19
|
+
export declare function recordPatches<T extends NonPrimitive, PatchesOption extends RecordPatchesOptions = {}>(state: T, mutate: (state: Draft<T>) => void, options?: PatchesOption): Patches;
|
|
43
20
|
export type { NonPrimitive, Draft, RecordPatchesOptions, Patches, Patch, Operation, } from './types.js';
|
|
44
21
|
//# 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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,YAAY,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAC,MAAM,YAAY,CAAC;AAEnF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC5B,CAAC,SAAS,YAAY,EACtB,aAAa,SAAS,oBAAoB,GAAG,EAAE,EAC9C,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAuB/E;AAGD,YAAY,EACX,YAAY,EACZ,KAAK,EACL,oBAAoB,EACpB,OAAO,EACP,KAAK,EACL,SAAS,GACT,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,54 +17,24 @@ import { compressPatches } from './optimizer.js';
|
|
|
17
17
|
* console.log(state.user.name); // 'Jane' (mutated in place!)
|
|
18
18
|
* console.log(patches); // [{ op: 'replace', path: ['user', 'name'], value: 'Jane' }]
|
|
19
19
|
*/
|
|
20
|
-
export function recordPatches(state, mutate, options
|
|
21
|
-
const internalPatchesOptions = {
|
|
22
|
-
pathAsArray: options.pathAsArray ?? true,
|
|
23
|
-
arrayLengthAssignment: options.arrayLengthAssignment ?? true,
|
|
24
|
-
};
|
|
20
|
+
export function recordPatches(state, mutate, options) {
|
|
25
21
|
const recorderState = {
|
|
26
|
-
|
|
22
|
+
state,
|
|
27
23
|
patches: [],
|
|
28
24
|
basePath: [],
|
|
29
25
|
options: {
|
|
30
26
|
...options,
|
|
31
|
-
internalPatchesOptions,
|
|
32
27
|
},
|
|
28
|
+
proxyCache: new WeakMap(),
|
|
33
29
|
};
|
|
34
30
|
// Create proxy
|
|
35
31
|
const proxy = createProxy(state, [], recorderState);
|
|
36
32
|
// Apply mutations
|
|
37
33
|
mutate(proxy);
|
|
38
34
|
// Return patches (optionally compressed)
|
|
39
|
-
if (options
|
|
35
|
+
if (options?.compressPatches !== false) {
|
|
40
36
|
return compressPatches(recorderState.patches);
|
|
41
37
|
}
|
|
42
38
|
return recorderState.patches;
|
|
43
39
|
}
|
|
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
|
-
}
|
|
70
40
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,YAAY,CAAC;AACvC,OAAO,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,YAAY,CAAC;AACvC,OAAO,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAG/C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa,CAG3B,KAAQ,EAAE,MAAiC,EAAE,OAAuB;IACrE,MAAM,aAAa,GAAG;QACrB,KAAK;QACL,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE;YACR,GAAG,OAAO;SACV;QACD,UAAU,EAAE,IAAI,OAAO,EAAE;KACzB,CAAC;IAEF,eAAe;IACf,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,aAAa,CAAa,CAAC;IAEhE,kBAAkB;IAClB,MAAM,CAAC,KAAK,CAAC,CAAC;IAEd,yCAAyC;IACzC,IAAI,OAAO,EAAE,eAAe,KAAK,KAAK,EAAE,CAAC;QACxC,OAAO,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,aAAa,CAAC,OAAkB,CAAC;AACzC,CAAC"}
|
package/dist/maps.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { RecorderState } from './types.js';
|
|
1
|
+
import type { PatchPath, RecorderState } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Handle property access on Map objects
|
|
4
4
|
* Wraps mutating methods (set, delete, clear) to generate patches
|
|
5
5
|
*/
|
|
6
|
-
export declare function handleMapGet
|
|
6
|
+
export declare function handleMapGet(obj: Map<any, any>, prop: string | symbol, path: PatchPath, state: RecorderState<any>): any;
|
|
7
7
|
//# sourceMappingURL=maps.d.ts.map
|
package/dist/maps.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maps.d.ts","sourceRoot":"","sources":["../src/maps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"maps.d.ts","sourceRoot":"","sources":["../src/maps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AAKzD;;;GAGG;AACH,wBAAgB,YAAY,CAC3B,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,EAClB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,GACvB,GAAG,CAoFL"}
|
package/dist/maps.js
CHANGED
|
@@ -6,15 +6,16 @@ import { cloneIfNeeded } from './utils.js';
|
|
|
6
6
|
* Wraps mutating methods (set, delete, clear) to generate patches
|
|
7
7
|
*/
|
|
8
8
|
export function handleMapGet(obj, prop, path, state) {
|
|
9
|
-
//
|
|
9
|
+
// Handle symbol properties - return the property value directly
|
|
10
|
+
// Symbol methods like Symbol.iterator should work normally
|
|
10
11
|
if (typeof prop === 'symbol') {
|
|
11
12
|
return obj[prop];
|
|
12
13
|
}
|
|
13
14
|
// Mutating methods
|
|
14
15
|
if (prop === 'set') {
|
|
15
16
|
return (key, value) => {
|
|
16
|
-
// Check if key
|
|
17
|
-
const existed =
|
|
17
|
+
// Check if key exists BEFORE mutation (current state, not original)
|
|
18
|
+
const existed = obj.has(key);
|
|
18
19
|
const oldValue = obj.get(key);
|
|
19
20
|
const result = obj.set(key, value);
|
|
20
21
|
// Generate patch
|
|
@@ -74,21 +75,4 @@ export function handleMapGet(obj, prop, path, state) {
|
|
|
74
75
|
// Return any other property
|
|
75
76
|
return obj[prop];
|
|
76
77
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Navigate to the original Map at the given path and check if a key exists
|
|
79
|
-
* This is needed to check if a key existed before mutations
|
|
80
|
-
*/
|
|
81
|
-
function keyExistsInOriginal(original, path, key) {
|
|
82
|
-
let current = original;
|
|
83
|
-
for (const part of path) {
|
|
84
|
-
if (current == null)
|
|
85
|
-
return false;
|
|
86
|
-
current = current[part];
|
|
87
|
-
}
|
|
88
|
-
// If we reached a Map, check if the key exists
|
|
89
|
-
if (current instanceof Map) {
|
|
90
|
-
return current.has(key);
|
|
91
|
-
}
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
78
|
//# sourceMappingURL=maps.js.map
|
package/dist/maps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maps.js","sourceRoot":"","sources":["../src/maps.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"maps.js","sourceRoot":"","sources":["../src/maps.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,YAAY,CAAC;AACvC,OAAO,EAAC,gBAAgB,EAAE,mBAAmB,EAAE,oBAAoB,EAAC,MAAM,cAAc,CAAC;AACzF,OAAO,EAAC,aAAa,EAAC,MAAM,YAAY,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC3B,GAAkB,EAClB,IAAqB,EACrB,IAAe,EACf,KAAyB;IAEzB,gEAAgE;IAChE,2DAA2D;IAC3D,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAQ,GAAW,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,mBAAmB;IACnB,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACpB,OAAO,CAAC,GAAQ,EAAE,KAAU,EAAE,EAAE;YAC/B,oEAAoE;YACpE,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAEnC,iBAAiB;YACjB,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,GAAU,CAAC,CAAC;YAEvC,IAAI,OAAO,EAAE,CAAC;gBACb,qDAAqD;gBACrD,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;YACvE,CAAC;iBAAM,CAAC;gBACP,0BAA0B;gBAC1B,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;YACzD,CAAC;YAED,OAAO,MAAM,CAAC;QACf,CAAC,CAAC;IACH,CAAC;IAED,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,CAAC,GAAQ,EAAE,EAAE;YACnB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAE/B,IAAI,MAAM,EAAE,CAAC;gBACZ,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,GAAU,CAAC,CAAC;gBACvC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC/D,CAAC;YAED,OAAO,MAAM,CAAC;QACf,CAAC,CAAC;IACH,CAAC;IAED,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACtB,OAAO,GAAG,EAAE;YACX,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC1C,GAAG,CAAC,KAAK,EAAE,CAAC;YAEZ,wCAAwC;YACxC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;gBAChC,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,GAAU,CAAC,CAAC;gBACvC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;YAC5D,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACpB,OAAO,CAAC,GAAQ,EAAE,EAAE;YACnB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAE3B,yEAAyE;YACzE,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAChD,OAAO,WAAW,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAU,CAAC,EAAE,KAAK,CAAC,CAAC;YACzD,CAAC;YAED,OAAO,KAAK,CAAC;QACd,CAAC,CAAC;IACH,CAAC;IAED,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAE3E,IAAI,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,OAAQ,GAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,gBAAgB;IAChB,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACrB,OAAO,GAAG,CAAC,IAAI,CAAC;IACjB,CAAC;IAED,4BAA4B;IAC5B,OAAQ,GAAW,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC"}
|
package/dist/optimizer.d.ts
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import type { Patches } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compress patches by merging redundant operations using nested Maps
|
|
4
|
+
* This is faster than the string-key version because:
|
|
5
|
+
* - No string allocation for path keys
|
|
6
|
+
* - Preserves symbol and object identity
|
|
7
|
+
* - 2.5-5x faster in benchmarks
|
|
8
|
+
*/
|
|
9
|
+
export declare function compressPatchesWithNestedMaps(patches: Patches): Patches;
|
|
10
|
+
/**
|
|
11
|
+
* Compress patches by merging redundant operations using string keys
|
|
12
|
+
* This is the original implementation that uses pathToKey for path lookup.
|
|
13
|
+
*/
|
|
14
|
+
export declare function compressPatchesWithStringKeys(patches: Patches): Patches;
|
|
2
15
|
/**
|
|
3
16
|
* Compress patches by merging redundant operations
|
|
4
17
|
* This handles both consecutive and interleaved operations on the same path
|
|
18
|
+
*
|
|
19
|
+
* Uses the nested Map implementation for better performance (2.5-5x faster)
|
|
5
20
|
*/
|
|
6
|
-
export declare
|
|
21
|
+
export declare const compressPatches: typeof compressPatchesWithNestedMaps;
|
|
7
22
|
//# sourceMappingURL=optimizer.d.ts.map
|
package/dist/optimizer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"optimizer.d.ts","sourceRoot":"","sources":["../src/optimizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"optimizer.d.ts","sourceRoot":"","sources":["../src/optimizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,OAAO,EAAY,MAAM,YAAY,CAAC;AAiD1D;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CA6CvE;AAID;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CA8CvE;AAID;;;;;GAKG;AACH,eAAO,MAAM,eAAe,sCAAgC,CAAC"}
|