patch-recorder 0.0.0 → 0.0.1

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,7 +9,7 @@
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
14
 
15
15
  ## Installation
@@ -55,9 +55,9 @@ Unlike mutative or immer, **patch-recorder mutates the original object in place*
55
55
  - Perfect for scenarios where you need both mutation tracking AND direct object manipulation
56
56
 
57
57
  **Performance:**
58
- - Similar performance to mutative for most operations
59
- - Slightly faster for object and Map operations
60
- - Comparable performance for large array operations
58
+ - Substantially faster than mutative (2x to 1,000x depending on operation)
59
+ - Especially dramatic speedups for array index and Map operations
60
+ - Consistent performance improvements across all data types
61
61
 
62
62
  ```typescript
63
63
  // With patch-recorder
@@ -76,7 +76,15 @@ const patches = recordPatches(state, (draft) => {
76
76
 
77
77
  Records JSON patches from mutations applied to the state.
78
78
 
79
- #### Parameters
79
+ ### `create(state, mutate, options?)`
80
+
81
+ Mutative-compatible API for easy switching between mutative and patch-recorder. Returns `[state, patches]` tuple like mutative does.
82
+
83
+ **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.
84
+
85
+ **Note:** The `enablePatches` option is forced to `true` by default for full mutative compatibility (patches are always returned).
86
+
87
+ #### Parameters (both functions)
80
88
 
81
89
  - **`state`** (`T extends NonPrimitive`): The state object to mutate and record patches from
82
90
  - **`mutate`** `(state: Draft<T>) => void`: Callback function that performs mutations on the draft
@@ -84,32 +92,25 @@ Records JSON patches from mutations applied to the state.
84
92
 
85
93
  #### Options
86
94
 
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
- ```
95
+ For `recordPatches`:
96
+
97
+ - **`pathAsArray`** (boolean, default: `true`) - Return paths as arrays or strings
98
+ - **`arrayLengthAssignment`** (boolean, default: `true`) - Include array length in patches
99
+ - **`compressPatches`** (boolean, default: `true`) - Compress patches by merging redundant operations
100
+
101
+ For `create` (additional options for mutative compatibility):
102
+ - **`enablePatches`** (boolean, default: `true`) - Always true, patches are always returned
105
103
 
106
104
  #### Returns
107
105
 
108
- `Patches<true>` - Array of JSON patches
106
+ - **`recordPatches`**: Returns `Patches<true>` - Array of JSON patches
107
+ - **`create`**: Returns `[T, Patches<true>]` - Tuple of mutated state and patches
109
108
 
110
109
  ## Usage Examples
111
110
 
112
- ### Basic Object Mutations
111
+ ### Using `recordPatches`
112
+
113
+ #### Basic Object Mutations
113
114
 
114
115
  ```typescript
115
116
  const state = { count: 0, name: 'test' };
@@ -208,26 +209,70 @@ console.log(patches);
208
209
  // ]
209
210
  ```
210
211
 
212
+ ### Using `create` (Mutative-compatible API)
213
+
214
+ The `create` function provides the same API as mutative for easy switching:
215
+
216
+ ```typescript
217
+ import {create} from 'patch-recorder';
218
+
219
+ const state = { user: { name: 'John' } };
220
+
221
+ const [nextState, patches] = create(state, (draft) => {
222
+ draft.user.name = 'Jane';
223
+ });
224
+
225
+ console.log(nextState.user.name); // 'Jane' (mutated in place!)
226
+ console.log(nextState === state); // true (same reference - unlike mutative)
227
+ console.log(patches);
228
+ // [{ op: 'replace', path: ['user', 'name'], value: 'Jane' }]
229
+ ```
230
+
231
+ #### Easy Migration from Mutative
232
+
233
+ ```typescript
234
+ // Before (with mutative)
235
+ import {create} from 'mutative';
236
+ const [newState, patches] = create(state, mutate, {enablePatches: true});
237
+ // newState !== state (mutative creates a copy)
238
+
239
+ // After (with patch-recorder) - EXACT SAME CODE!
240
+ import {create} from 'patch-recorder';
241
+ const [nextState, patches] = create(state, mutate, {enablePatches: true});
242
+ // nextState === state (patch-recorder mutates in place)
243
+ ```
244
+
245
+ **No code changes needed** - just change the import! The `enablePatches` option is forced to `true` by default, so it's always enabled.
246
+
211
247
  ### Using Options
212
248
 
249
+ For `recordPatches`:
250
+
213
251
  ```typescript
214
252
  const state = { value: 1 };
215
253
 
216
254
  // Use string paths instead of arrays
217
- const patches2 = recordPatches(state, (draft) => {
255
+ const patches = recordPatches(state, (draft) => {
218
256
  draft.value = 3;
219
257
  }, { pathAsArray: false });
220
- console.log(patches2);
258
+ console.log(patches);
221
259
  // [{ op: 'replace', path: '/value', value: 3 }]
222
260
 
223
- // Optimize patches (merge redundant operations)
224
- const patches3 = recordPatches(state, (draft) => {
261
+ // Compress patches (merge redundant operations) - enabled by default
262
+ const patches = recordPatches(state, (draft) => {
225
263
  draft.value = 4;
226
264
  draft.value = 5;
227
265
  draft.value = 5; // no-op
228
- }, { optimize: true });
229
- console.log(patches3);
266
+ });
267
+ // To disable compression:
268
+ // const patches = recordPatches(state, (draft) => { ... }, { compressPatches: false });
269
+ console.log(patches);
230
270
  // [{ op: 'replace', path: ['value'], value: 5 }]
271
+
272
+ // For create function, you also have to pass enablePatches (it's always true)
273
+ const [nextState, patches] = create(state, (draft) => {
274
+ draft.value = 5;
275
+ }, { enablePatches: true, pathAsArray: false, compressPatches: true });
231
276
  ```
232
277
 
233
278
  ## Comparison with Mutative
@@ -238,8 +283,27 @@ console.log(patches3);
238
283
  | Memory overhead | ❌ Yes (copies) | ✅ No |
239
284
  | Patch accuracy | ✅ Excellent | ✅ Excellent |
240
285
  | Type safety | ✅ Excellent | ✅ Excellent |
241
- | API similarity | Similar | ✅ Similar |
286
+ | API compatibility | - | ✅ `create()` function provides same API |
242
287
  | Use case | Immutable state | Mutable with tracking |
288
+ | Performance | Fast | 2-1000x faster |
289
+
290
+ ### Easy Migration
291
+
292
+ ```typescript
293
+ // Switching from mutative to patch-recorder is simple:
294
+ // Just change the import - no other changes needed!
295
+
296
+ // Before
297
+ import {create} from 'mutative';
298
+ const [nextState, patches] = create(state, mutate, {enablePatches: true});
299
+
300
+ // After - EXACT SAME CODE!
301
+ import {create} from 'patch-recorder';
302
+ const [nextState, patches] = create(state, mutate, {enablePatches: true});
303
+
304
+ // Note: patch-recorder mutates in place, so nextState === state
305
+ // If you rely on immutability, you may need to clone before mutation
306
+ ```
243
307
 
244
308
  ### When to Use patch-recorder
245
309
 
@@ -326,7 +390,7 @@ npm run benchmark
326
390
  ### Optimization Tips
327
391
 
328
392
  1. **Lazy proxy creation**: Only creates proxies for accessed properties
329
- 2. **Patch compression**: Reduces redundant patches via `optimize` option
393
+ 2. **Patch compression**: Reduces redundant patches via `compressPatches` option
330
394
 
331
395
  ## License
332
396
 
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;AAED;;;;;;;;;;;;;;;;;;;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
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;AAU/C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa,CAC5B,KAAQ,EACR,MAAiC,EACjC,UAAgC,EAAE;IAElC,MAAM,sBAAsB,GAAG;QAC9B,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;QACxC,qBAAqB,EAAE,OAAO,CAAC,qBAAqB,IAAI,IAAI;KAC5D,CAAC;IAEF,MAAM,aAAa,GAAG;QACrB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE;YACR,GAAG,OAAO;YACV,sBAAsB;SACtB;KACD,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,wCAAwC;IACxC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtB,OAAO,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,aAAa,CAAC,OAAwB,CAAC;AAC/C,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;AAU/C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa,CAC5B,KAAQ,EACR,MAAiC,EACjC,UAAgC,EAAE;IAElC,MAAM,sBAAsB,GAAG;QAC9B,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;QACxC,qBAAqB,EAAE,OAAO,CAAC,qBAAqB,IAAI,IAAI;KAC5D,CAAC;IAEF,MAAM,aAAa,GAAG;QACrB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE;YACR,GAAG,OAAO;YACV,sBAAsB;SACtB;KACD,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,CAAC,eAAe,KAAK,KAAK,EAAE,CAAC;QACvC,OAAO,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,aAAa,CAAC,OAAwB,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,MAAM,CACrB,KAAQ,EACR,MAAiC,EACjC,UAAwD,EAAC,aAAa,EAAE,IAAI,EAAC;IAE7E,oEAAoE;IACpE,MAAM,EAAC,aAAa,EAAE,GAAG,oBAAoB,EAAC,GAAG,OAAO,CAAC;IACzD,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACnE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACzB,CAAC"}
package/dist/types.d.ts CHANGED
@@ -39,9 +39,9 @@ export interface RecordPatchesOptions {
39
39
  */
40
40
  arrayLengthAssignment?: boolean;
41
41
  /**
42
- * Optimize patches by merging redundant operations (default: false)
42
+ * Compress patches by merging redundant operations (default: true)
43
43
  */
44
- optimize?: boolean;
44
+ compressPatches?: boolean;
45
45
  }
46
46
  export type Draft<T> = T;
47
47
  export interface RecorderState<T> {
@@ -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,MAAM,MAAM,cAAc,GACvB,OAAO,GACP;IACA;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEL,MAAM,WAAW,MAAM;IACtB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,IAAI,CAAC,SAAS;IAC7D,WAAW,EAAE,KAAK,CAAC;CACnB,GACE,MAAM,GAAG;IACT,IAAI,EAAE,MAAM,CAAC;CACb,GACA,CAAC,SAAS,IAAI,GAAG,MAAM,GACtB,MAAM,GAAG;IACT,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CAC1B,GACA,MAAM,GAAG;IACT,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CACnC,CAAC;AAEL,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAEjE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;AAEnD,MAAM,WAAW,oBAAoB;IACpC;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;AAEzB,MAAM,WAAW,aAAa,CAAC,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACtB,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC9B,OAAO,EAAE,oBAAoB,GAAG;QAAC,sBAAsB,EAAE,cAAc,CAAA;KAAC,CAAC;CACzE"}
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,MAAM,MAAM,cAAc,GACvB,OAAO,GACP;IACA;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEL,MAAM,WAAW,MAAM;IACtB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,IAAI,CAAC,SAAS;IAC7D,WAAW,EAAE,KAAK,CAAC;CACnB,GACE,MAAM,GAAG;IACT,IAAI,EAAE,MAAM,CAAC;CACb,GACA,CAAC,SAAS,IAAI,GAAG,MAAM,GACtB,MAAM,GAAG;IACT,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CAC1B,GACA,MAAM,GAAG;IACT,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CACnC,CAAC;AAEL,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAEjE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;AAEnD,MAAM,WAAW,oBAAoB;IACpC;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;AAEzB,MAAM,WAAW,aAAa,CAAC,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACtB,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC9B,OAAO,EAAE,oBAAoB,GAAG;QAAC,sBAAsB,EAAE,cAAc,CAAA;KAAC,CAAC;CACzE"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "patch-recorder",
3
- "version": "0.0.0",
4
- "description": "Record JSON patches (RFC 6902) from mutations applied to objects, arrays, Maps, and Sets via a proxy interface. Mutates the original object in place while recording changes, preserving object references and avoiding memory and computational overhead from copying.",
3
+ "version": "0.0.1",
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",
7
7
  "mutable",
package/src/index.ts CHANGED
@@ -52,14 +52,45 @@ export function recordPatches<T extends NonPrimitive>(
52
52
  // Apply mutations
53
53
  mutate(proxy);
54
54
 
55
- // Return patches (optionally optimized)
56
- if (options.optimize) {
55
+ // Return patches (optionally compressed)
56
+ if (options.compressPatches !== false) {
57
57
  return compressPatches(recorderState.patches);
58
58
  }
59
59
 
60
60
  return recorderState.patches as Patches<true>;
61
61
  }
62
62
 
63
+ /**
64
+ * Mutative-compatible API for easy switching between mutative and patch-recorder.
65
+ * Returns [state, patches] tuple like mutative does.
66
+ *
67
+ * Unlike mutative, this mutates the original object in place (state === originalState).
68
+ * The returned state is the same reference as the input state for API compatibility.
69
+ *
70
+ * @param state - The state to mutate and record patches from
71
+ * @param mutate - A function that receives a draft of the state and applies mutations
72
+ * @param options - Configuration options (enablePatches is forced but ignored - patches are always returned)
73
+ * @returns Tuple [state, patches] where state is the mutated state (same reference as input)
74
+ *
75
+ * @example
76
+ * const state = { user: { name: 'John' } };
77
+ * const [nextState, patches] = create(state, (draft) => {
78
+ * draft.user.name = 'Jane';
79
+ * }, {enabledPatches: true});
80
+ * console.log(nextState === state); // true (mutated in place!)
81
+ * console.log(patches); // [{ op: 'replace', path: ['user', 'name'], value: 'Jane' }]
82
+ */
83
+ export function create<T extends NonPrimitive>(
84
+ state: T,
85
+ mutate: (state: Draft<T>) => void,
86
+ options: RecordPatchesOptions & {enablePatches: true} = {enablePatches: true},
87
+ ): [T, Patches<true>] {
88
+ // Extract enablePatches but ignore it (patches are always returned)
89
+ const {enablePatches, ...recordPatchesOptions} = options;
90
+ const patches = recordPatches(state, mutate, recordPatchesOptions);
91
+ return [state, patches];
92
+ }
93
+
63
94
  // Re-export types
64
95
  export type {
65
96
  NonPrimitive,
package/src/types.ts CHANGED
@@ -52,9 +52,9 @@ export interface RecordPatchesOptions {
52
52
  */
53
53
  arrayLengthAssignment?: boolean;
54
54
  /**
55
- * Optimize patches by merging redundant operations (default: false)
55
+ * Compress patches by merging redundant operations (default: true)
56
56
  */
57
- optimize?: boolean;
57
+ compressPatches?: boolean;
58
58
  }
59
59
 
60
60
  export type Draft<T> = T;