@arcmantle/chronicle 1.0.2 → 1.0.4

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.
Files changed (41) hide show
  1. package/README.md +26 -12
  2. package/dist/api-methods.d.ts +10 -2
  3. package/dist/api-methods.d.ts.map +1 -1
  4. package/dist/api-methods.js +42 -20
  5. package/dist/api-methods.js.map +1 -1
  6. package/dist/batch-transaction.d.ts +6 -10
  7. package/dist/batch-transaction.d.ts.map +1 -1
  8. package/dist/batch-transaction.js +36 -42
  9. package/dist/batch-transaction.js.map +1 -1
  10. package/dist/chronicle.d.ts +3 -35
  11. package/dist/chronicle.d.ts.map +1 -1
  12. package/dist/chronicle.js +6 -8
  13. package/dist/chronicle.js.map +1 -1
  14. package/dist/history-recorder.d.ts.map +1 -1
  15. package/dist/history-recorder.js +5 -4
  16. package/dist/history-recorder.js.map +1 -1
  17. package/dist/history.d.ts +64 -0
  18. package/dist/history.d.ts.map +1 -1
  19. package/dist/history.js +16 -1
  20. package/dist/history.js.map +1 -1
  21. package/dist/nameof.d.ts +1 -2
  22. package/dist/nameof.d.ts.map +1 -1
  23. package/dist/nameof.js +38 -12
  24. package/dist/nameof.js.map +1 -1
  25. package/dist/path.js.map +1 -1
  26. package/dist/three-way-merge.d.ts +13 -0
  27. package/dist/three-way-merge.d.ts.map +1 -0
  28. package/dist/three-way-merge.js +152 -0
  29. package/dist/three-way-merge.js.map +1 -0
  30. package/dist/types.d.ts +19 -0
  31. package/dist/types.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/api-methods.ts +86 -53
  34. package/src/batch-transaction.ts +56 -55
  35. package/src/chronicle.ts +31 -84
  36. package/src/history-recorder.ts +5 -4
  37. package/src/history.ts +82 -1
  38. package/src/nameof.ts +51 -13
  39. package/src/path.ts +1 -1
  40. package/src/three-way-merge.ts +196 -0
  41. package/src/types.ts +25 -0
@@ -1,10 +1,12 @@
1
+ import { type ConfigureOptions, configureRoot } from './config.ts';
1
2
  import { clearLastUngrouped, historyDelete, historyGet } from './history.ts';
2
3
  import { addListenerToTrie, cleanupListenerBucket, ensureListenerBucket, removeListenerFromTrie } from './listener-trie.ts';
3
4
  import { nameofSegments } from './nameof.ts';
4
5
  import { clearProxyCache as pfClearProxyCache } from './proxy-factory.ts';
5
6
  import { buildEffectiveListener, flush as scheduleFlush, pause as schedulePause, resume as scheduleResume } from './schedule-queue.ts';
6
7
  import { cloneWithOptions, diffValues, originalSnapshotCache } from './snapshot-diff.ts';
7
- import type { ChangeListener, ChangeRecord, DiffRecord, ListenerOptions, PathMode, PathSelector } from './types.ts';
8
+ import { threeWayMerge } from './three-way-merge.ts';
9
+ import type { ChangeListener, ChangeRecord, ConflictResolutions, DiffRecord, ListenerOptions, MergeResult, PathMode, PathSelector } from './types.ts';
8
10
  import { canRedo as coreCanRedo, canUndo as coreCanUndo, clearRedoCache, redo as coreRedo, redoGroups as coreRedoGroups, resumeWrites, suspendWrites, undo as coreUndo, undoGroups as coreUndoGroups, undoSince as coreUndoSince } from './undo-redo.ts';
9
11
 
10
12
 
@@ -17,13 +19,13 @@ const isObject = (v: unknown): v is Record<string, unknown> => typeof v === 'obj
17
19
 
18
20
 
19
21
  export interface ChronicleApiMethods {
20
- listen: <T extends object>(
21
- object: T,
22
- selector: PathSelector<T>,
23
- listener: ChangeListener,
24
- modeOrOptions?: PathMode | ListenerOptions,
25
- maybeOptions?: ListenerOptions,
26
- ) => () => void;
22
+ listen<T extends object>(object: T, selector: PathSelector<T>, listener: ChangeListener): () => void;
23
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
24
+ listen<T extends object>(object: T, selector: PathSelector<T>, listener: ChangeListener, options: ListenerOptions): () => void;
25
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
26
+ listen<T extends object>(object: T, selector: PathSelector<T>, listener: ChangeListener, mode: PathMode): () => void;
27
+ // eslint-disable-next-line @stylistic/max-len, @typescript-eslint/unified-signatures
28
+ listen<T extends object>(object: T, selector: PathSelector<T>, listener: ChangeListener, mode: PathMode, options: ListenerOptions): () => void;
27
29
  onAny: (obj: object, listener: ChangeListener, options?: ListenerOptions) => () => void;
28
30
  pause: (obj: object) => void;
29
31
  resume: (obj: object) => void;
@@ -34,6 +36,9 @@ export interface ChronicleApiMethods {
34
36
  markPristine: (obj: object) => void;
35
37
  diff: (obj: object) => DiffRecord[];
36
38
  isPristine: (obj: object) => boolean;
39
+ snapshot: <T extends object>(obj: T) => T;
40
+ unwrap: <T extends object>(obj: T) => T;
41
+ merge: (obj: object, incomingObject: object, resolutions?: ConflictResolutions) => MergeResult;
37
42
  mark: (obj: object) => number;
38
43
  undo: (obj: object, steps?: number) => void;
39
44
  undoSince: (obj: object, historyLengthBefore: number) => void;
@@ -43,19 +48,20 @@ export interface ChronicleApiMethods {
43
48
  clearRedo: (obj: object) => void;
44
49
  redo: (obj: object, steps?: number) => void;
45
50
  redoGroups: (obj: object, groups?: number) => void;
51
+ configure: (obj: object, options: ConfigureOptions) => void;
46
52
  }
47
53
 
48
54
  export const createApiMethods = (deps: ApiDeps): ChronicleApiMethods => {
49
55
  // listen/onAny --------------------------------------------------------------
50
- const listen = <T extends object>(
51
- object: T,
52
- selector: PathSelector<T>,
53
- listener: ChangeListener,
56
+ const listen: ChronicleApiMethods['listen'] = (
57
+ object,
58
+ selector,
59
+ listener,
54
60
  modeOrOptions?: PathMode | ListenerOptions,
55
61
  maybeOptions?: ListenerOptions,
56
62
  ) => {
57
63
  const segs = nameofSegments(selector);
58
- const root = deps.getRoot(object as object);
64
+ const root = deps.getRoot(object);
59
65
  const bucket = ensureListenerBucket(root);
60
66
 
61
67
  let mode: PathMode = 'down';
@@ -96,34 +102,34 @@ export const createApiMethods = (deps: ApiDeps): ChronicleApiMethods => {
96
102
  return unsubscribe;
97
103
  };
98
104
 
99
- const onAny = (obj: object, listener: ChangeListener, options?: ListenerOptions): () => void => {
100
- return listen(obj as any, s => s as any, listener, options);
105
+ const onAny: ChronicleApiMethods['onAny'] = (obj, listener, options) => {
106
+ return listen(obj, s => s, listener, options!);
101
107
  };
102
108
 
103
109
  // pause/resume/flush --------------------------------------------------------
104
- const pause = (obj: object): void => {
110
+ const pause: ChronicleApiMethods['pause'] = (obj) => {
105
111
  const root = deps.getRoot(obj);
106
112
  schedulePause(root);
107
113
  };
108
114
 
109
- const resume = (obj: object): void => {
115
+ const resume: ChronicleApiMethods['resume'] = (obj) => {
110
116
  const root = deps.getRoot(obj);
111
117
  scheduleResume(root);
112
118
  };
113
119
 
114
- const flush = (obj: object): void => {
120
+ const flush: ChronicleApiMethods['flush'] = (obj) => {
115
121
  const root = deps.getRoot(obj);
116
122
  scheduleFlush(root);
117
123
  };
118
124
 
119
125
  // history ------------------------------------------------------------------
120
- const getHistory = (obj: object): ChangeRecord[] => {
126
+ const getHistory: ChronicleApiMethods['getHistory'] = (obj) => {
121
127
  const root = deps.getRoot(obj);
122
128
 
123
129
  return (historyGet(root) ?? []).slice();
124
130
  };
125
131
 
126
- const clearHistory = (obj: object): void => {
132
+ const clearHistory: ChronicleApiMethods['clearHistory'] = (obj) => {
127
133
  const root = deps.getRoot(obj);
128
134
  historyDelete(root);
129
135
  clearLastUngrouped(root);
@@ -131,7 +137,7 @@ export const createApiMethods = (deps: ApiDeps): ChronicleApiMethods => {
131
137
  };
132
138
 
133
139
  // reset/markPristine/diff/pristine ----------------------------------------
134
- const markPristine = (obj: object): void => {
140
+ const markPristine: ChronicleApiMethods['markPristine'] = (obj) => {
135
141
  const root = deps.getRoot(obj);
136
142
  originalSnapshotCache.set(root, cloneWithOptions(root, root));
137
143
  historyDelete(root);
@@ -140,7 +146,7 @@ export const createApiMethods = (deps: ApiDeps): ChronicleApiMethods => {
140
146
  pfClearProxyCache(root);
141
147
  };
142
148
 
143
- const reset = (obj: object): void => {
149
+ const reset: ChronicleApiMethods['reset'] = (obj) => {
144
150
  const root = deps.getRoot(obj);
145
151
  const snapshot = originalSnapshotCache.get(root);
146
152
  if (!snapshot) {
@@ -198,7 +204,7 @@ export const createApiMethods = (deps: ApiDeps): ChronicleApiMethods => {
198
204
  clearRedoCache(root);
199
205
  };
200
206
 
201
- const diff = (obj: object): DiffRecord[] => {
207
+ const diff: ChronicleApiMethods['diff'] = (obj) => {
202
208
  const root = deps.getRoot(obj);
203
209
  const original = originalSnapshotCache.get(root) ?? cloneWithOptions(root, root as any);
204
210
  const out: DiffRecord[] = [];
@@ -207,86 +213,113 @@ export const createApiMethods = (deps: ApiDeps): ChronicleApiMethods => {
207
213
  return out;
208
214
  };
209
215
 
210
- const isPristine = (obj: object): boolean => {
216
+ const isPristine: ChronicleApiMethods['isPristine'] = (obj) => {
211
217
  const diffs = diff(obj);
212
218
 
213
219
  return diffs.length === 0;
214
220
  };
215
221
 
222
+ const snapshot: ChronicleApiMethods['snapshot'] = (obj) => {
223
+ const root = deps.getRoot(obj);
224
+
225
+ return cloneWithOptions(root, root) as any;
226
+ };
227
+
228
+ const unwrap: ChronicleApiMethods['unwrap'] = (obj) => {
229
+ const root = deps.getRoot(obj);
230
+
231
+ return root as any;
232
+ };
233
+
234
+ const merge: ChronicleApiMethods['merge'] = (obj, incomingObject, resolutions?) => {
235
+ const root = deps.getRoot(obj);
236
+
237
+ return threeWayMerge(root, obj, incomingObject, resolutions);
238
+ };
239
+
216
240
  // marks/undo/redo -----------------------------------------------------------
217
- const mark = (obj: object): number => {
241
+ const mark: ChronicleApiMethods['mark'] = (obj) => {
218
242
  const root = deps.getRoot(obj);
219
243
  const history = historyGet(root);
220
244
 
221
245
  return history ? history.length : 0;
222
246
  };
223
247
 
224
- const undo = (obj: object, steps: number = Number.POSITIVE_INFINITY): void => {
248
+ const undo: ChronicleApiMethods['undo'] = (obj, steps = Number.POSITIVE_INFINITY) => {
225
249
  const root = deps.getRoot(obj);
226
250
  coreUndo(root, steps);
227
251
  };
228
252
 
229
- const undoSince = (obj: object, historyLengthBefore: number): void => {
253
+ const undoSince: ChronicleApiMethods['undoSince'] = (obj, historyLengthBefore) => {
230
254
  const root = deps.getRoot(obj);
231
255
  coreUndoSince(root, historyLengthBefore);
232
256
  clearLastUngrouped(root);
233
257
  };
234
258
 
235
- const undoGroups = (obj: object, groups: number = 1): void => {
259
+ const undoGroups: ChronicleApiMethods['undoGroups'] = (obj, groups = 1) => {
236
260
  const root = deps.getRoot(obj);
237
261
  coreUndoGroups(root, groups);
238
262
  clearLastUngrouped(root);
239
263
  };
240
264
 
241
- const canUndo = (obj: object): boolean => {
265
+ const canUndo: ChronicleApiMethods['canUndo'] = (obj) => {
242
266
  const root = deps.getRoot(obj);
243
267
 
244
268
  return coreCanUndo(root);
245
269
  };
246
270
 
247
- const canRedo = (obj: object): boolean => {
271
+ const canRedo: ChronicleApiMethods['canRedo'] = (obj) => {
248
272
  const root = deps.getRoot(obj);
249
273
 
250
274
  return coreCanRedo(root);
251
275
  };
252
276
 
253
- const clearRedo = (obj: object): void => {
277
+ const clearRedo: ChronicleApiMethods['clearRedo'] = (obj) => {
254
278
  const root = deps.getRoot(obj);
255
279
  clearRedoCache(root);
256
280
  };
257
281
 
258
- const redo = (obj: object, steps: number = Number.POSITIVE_INFINITY): void => {
282
+ const redo: ChronicleApiMethods['redo'] = (obj, steps = Number.POSITIVE_INFINITY) => {
259
283
  const root = deps.getRoot(obj);
260
284
  coreRedo(root, steps);
261
285
  clearLastUngrouped(root);
262
286
  };
263
287
 
264
- const redoGroups = (obj: object, groups: number = 1): void => {
288
+ const redoGroups: ChronicleApiMethods['redoGroups'] = (obj, groups = 1) => {
265
289
  const root = deps.getRoot(obj);
266
290
  coreRedoGroups(root, groups);
267
291
  clearLastUngrouped(root);
268
292
  };
269
293
 
294
+ const configure: ChronicleApiMethods['configure'] = (obj, options) => {
295
+ const root = deps.getRoot(obj);
296
+ configureRoot(root, options);
297
+ };
298
+
270
299
  return {
271
- listen: listen,
272
- onAny: onAny,
273
- pause: pause,
274
- resume: resume,
275
- flush: flush,
276
- getHistory: getHistory,
277
- clearHistory: clearHistory,
278
- reset: reset,
279
- markPristine: markPristine,
280
- diff: diff,
281
- isPristine: isPristine,
282
- mark: mark,
283
- undo: undo,
284
- undoSince: undoSince,
285
- undoGroups: undoGroups,
286
- canUndo: canUndo,
287
- canRedo: canRedo,
288
- clearRedo: clearRedo,
289
- redo: redo,
290
- redoGroups: redoGroups,
300
+ listen,
301
+ onAny,
302
+ pause,
303
+ resume,
304
+ flush,
305
+ getHistory,
306
+ clearHistory,
307
+ reset,
308
+ markPristine,
309
+ diff,
310
+ isPristine,
311
+ snapshot,
312
+ unwrap,
313
+ merge,
314
+ mark,
315
+ undo,
316
+ undoSince,
317
+ undoGroups,
318
+ canUndo,
319
+ canRedo,
320
+ clearRedo,
321
+ redo,
322
+ redoGroups,
323
+ configure,
291
324
  };
292
325
  };
@@ -8,6 +8,8 @@ const batchStack: WeakMap<object, BatchFrame[]> = new WeakMap();
8
8
 
9
9
  export type BatchDeps = Pick<ChronicleCore, 'chronicle' | 'getRoot'>;
10
10
 
11
+ export interface TransactionResult<R> { result: R; marker: number; undo: () => void; }
12
+
11
13
  export interface BatchFrame { marker: number; id: string; }
12
14
 
13
15
  export interface BatchAPI {
@@ -16,10 +18,11 @@ export interface BatchAPI {
16
18
  commitBatch: (obj: object) => void;
17
19
  rollbackBatch: (obj: object) => void;
18
20
  batch: <T extends object, R>(object: T, action: (observed: T) => R) => R;
19
- transaction: <T extends object, R>(object: T, action: (observed: T) => R) =>
20
- { result: R; marker: number; undo: () => void; };
21
- transactionAsync: <T extends object, R>(object: T, action: (observed: T) => Promise<R>) =>
22
- Promise<{ result: R; marker: number; undo: () => void; }>;
21
+ transaction<T extends object, R>(
22
+ object: T, action: (observed: T) => R,
23
+ ): R extends Promise<infer U>
24
+ ? Promise<TransactionResult<U>>
25
+ : TransactionResult<R>;
23
26
  }
24
27
 
25
28
 
@@ -80,7 +83,7 @@ export const createBatchTransaction = (deps: BatchDeps): BatchAPI => {
80
83
  };
81
84
 
82
85
  const transaction: BatchAPI['transaction'] = (object, action) => {
83
- const root = deps.getRoot(object as unknown as object);
86
+ const root = deps.getRoot(object);
84
87
  const marker = (historyGet(root) ?? []).length;
85
88
 
86
89
  const framesBefore = (batchStack.get(root) ?? []).length;
@@ -90,63 +93,62 @@ export const createBatchTransaction = (deps: BatchDeps): BatchAPI => {
90
93
 
91
94
  const observed = deps.chronicle(object);
92
95
  let groupId: string | undefined;
93
- try {
94
- const result = action(observed);
95
- const frames = (batchStack.get(root) ?? []);
96
- groupId = frames.length > 0 ? frames[frames.length - 1]!.id : undefined;
97
- if (isTopLevel)
98
- commitBatch(root);
99
-
100
- return {
101
- result,
102
- marker,
103
- undo: () => {
104
- const h = historyGet(root);
105
- if (groupId && h && h.length > 0) {
106
- const topGroup = h[h.length - 1]!.groupId ?? `__g#${ h.length - 1 }`;
107
- if (topGroup === groupId) {
108
- undoGroups(root, 1);
109
96
 
110
- return;
111
- }
112
- }
113
-
114
- undoSince(root, marker);
115
- },
116
- };
117
- }
118
- catch (err) {
119
- if (isTopLevel)
120
- rollbackBatch(root);
121
- else
122
- undoSince(root, marker);
123
-
124
- throw err;
125
- }
126
- };
127
-
128
- const transactionAsync: BatchAPI['transactionAsync'] = async (object, action) => {
129
- const root = deps.getRoot(object as unknown as object);
130
- const marker = (historyGet(root) ?? []).length;
131
-
132
- const framesBefore = (batchStack.get(root) ?? []).length;
133
- const isTopLevel = framesBefore === 0;
134
- if (isTopLevel)
135
- beginBatch(root);
136
-
137
- const observed = deps.chronicle(object);
138
- let groupId: string | undefined;
139
97
  try {
140
- const result = await action(observed);
98
+ const actionResult = action(observed);
99
+ const isObject = typeof actionResult === 'object' && actionResult !== null;
100
+ const isPromise = isObject && 'then' in actionResult && typeof actionResult.then === 'function';
101
+
102
+ // Check if result is a Promise
103
+ if (isPromise) {
104
+ const typedActionResult = actionResult as any as Promise<ReturnType<typeof action>>;
105
+
106
+ return typedActionResult.then(
107
+ resolvedResult => {
108
+ const frames = (batchStack.get(root) ?? []);
109
+ groupId = frames.length > 0 ? frames[frames.length - 1]!.id : undefined;
110
+ if (isTopLevel)
111
+ commitBatch(root);
112
+
113
+ return {
114
+ result: resolvedResult,
115
+ marker,
116
+ undo: () => {
117
+ const h = historyGet(root);
118
+ if (groupId && h && h.length > 0) {
119
+ const topGroup = h[h.length - 1]!.groupId ?? `__g#${ h.length - 1 }`;
120
+ if (topGroup === groupId) {
121
+ undoGroups(root, 1);
122
+
123
+ return;
124
+ }
125
+ }
126
+
127
+ undoSince(root, marker);
128
+ },
129
+ } satisfies TransactionResult<typeof resolvedResult> as any;
130
+ },
131
+ err => {
132
+ if (isTopLevel)
133
+ rollbackBatch(root);
134
+ else
135
+ undoSince(root, marker);
136
+
137
+ throw err;
138
+ },
139
+ );
140
+ }
141
+
142
+ // Synchronous result
141
143
  const frames = (batchStack.get(root) ?? []);
142
144
  groupId = frames.length > 0 ? frames[frames.length - 1]!.id : undefined;
143
145
  if (isTopLevel)
144
146
  commitBatch(root);
145
147
 
146
148
  return {
147
- result,
149
+ result: actionResult,
148
150
  marker,
149
- undo: () => {
151
+ undo: () => {
150
152
  const h = historyGet(root);
151
153
  if (groupId && h && h.length > 0) {
152
154
  const topGroup = h[h.length - 1]!.groupId ?? `__g#${ h.length - 1 }`;
@@ -159,7 +161,7 @@ export const createBatchTransaction = (deps: BatchDeps): BatchAPI => {
159
161
 
160
162
  undoSince(root, marker);
161
163
  },
162
- };
164
+ } satisfies TransactionResult<typeof actionResult> as any;
163
165
  }
164
166
  catch (err) {
165
167
  if (isTopLevel)
@@ -178,6 +180,5 @@ export const createBatchTransaction = (deps: BatchDeps): BatchAPI => {
178
180
  rollbackBatch,
179
181
  batch,
180
182
  transaction,
181
- transactionAsync,
182
183
  };
183
184
  };
package/src/chronicle.ts CHANGED
@@ -1,55 +1,10 @@
1
1
  import { createChronicleCore } from './api.ts';
2
- import { createApiMethods } from './api-methods.ts';
3
- import { createBatchTransaction } from './batch-transaction.ts';
4
- import { type ConfigureOptions, configureRoot } from './config.ts';
5
- import type { ChangeListener, ChangeRecord, DiffRecord, ListenerOptions, PathMode, PathSelector } from './types.ts';
2
+ import { type ChronicleApiMethods, createApiMethods } from './api-methods.ts';
3
+ import { type BatchAPI, createBatchTransaction } from './batch-transaction.ts';
6
4
 
7
5
 
8
- interface Chronicle {
6
+ interface Chronicle extends Omit<BatchAPI, 'getBatchFrames'>, ChronicleApiMethods {
9
7
  <T extends object>(object: T): T;
10
-
11
- listen<T extends object>(
12
- object: T,
13
- selector: PathSelector<T>,
14
- listener: ChangeListener,
15
- modeOrOptions?: PathMode | ListenerOptions,
16
- maybeOptions?: ListenerOptions
17
- ): () => void;
18
- onAny(obj: object, listener: ChangeListener, options?: ListenerOptions): () => void;
19
- pause(obj: object): void;
20
- resume(obj: object): void;
21
- flush(obj: object): void;
22
- getHistory(obj: object): ChangeRecord[];
23
- clearHistory(obj: object): void;
24
- reset(obj: object): void;
25
- markPristine(obj: object): void;
26
- undo(obj: object, steps?: number): void;
27
- undoSince(obj: object, historyLengthBefore: number): void;
28
- diff(obj: object): DiffRecord[];
29
- isPristine(obj: object): boolean;
30
- mark(obj: object): number;
31
- transaction<T extends object, R>(object: T, action: (observed: T) => R): TransactionResult<R>;
32
- transactionAsync<T extends object, R>(object: T, action: (observed: T) => Promise<R>): Promise<TransactionResult<R>>;
33
- beginBatch(obj: object): void;
34
- commitBatch(obj: object): void;
35
- rollbackBatch(obj: object): void;
36
- batch<T extends object, R>(object: T, action: (observed: T) => R): R;
37
- undoGroups(obj: object, groups?: number): void;
38
- canUndo(obj: object): boolean;
39
- canRedo(obj: object): boolean;
40
- clearRedo(obj: object): void;
41
- redo(obj: object, steps?: number): void;
42
- redoGroups(obj: object, groups?: number): void;
43
- configure(
44
- obj: object,
45
- options: ConfigureOptions,
46
- ): void;
47
- }
48
-
49
- interface TransactionResult<R> {
50
- result: R;
51
- marker: number;
52
- undo: () => void;
53
8
  }
54
9
 
55
10
  const core = createChronicleCore({ getBatchFrames: r => batchApi.getBatchFrames(r) });
@@ -58,43 +13,35 @@ const chronicle = core.chronicle as Chronicle;
58
13
  const batchApi = createBatchTransaction(core);
59
14
  const api = createApiMethods({ getRoot: core.getRoot });
60
15
 
61
- chronicle.listen = api.listen;
62
- chronicle.onAny = api.onAny;
63
- chronicle.pause = api.pause;
64
- chronicle.resume = api.resume;
65
- chronicle.flush = api.flush;
66
- chronicle.getHistory = api.getHistory;
67
- chronicle.clearHistory = api.clearHistory;
68
- chronicle.reset = api.reset;
69
- chronicle.markPristine = api.markPristine;
70
- chronicle.undo = api.undo;
71
- chronicle.undoSince = api.undoSince;
72
- chronicle.diff = api.diff;
73
- chronicle.isPristine = api.isPristine;
74
- chronicle.mark = api.mark;
75
- chronicle.undoGroups = api.undoGroups;
76
- chronicle.canUndo = api.canUndo;
77
- chronicle.canRedo = api.canRedo;
78
- chronicle.clearRedo = api.clearRedo;
79
- chronicle.redo = api.redo;
80
- chronicle.redoGroups = api.redoGroups;
81
-
82
-
83
- chronicle.transaction = <T extends object, R>(object: T, action: (observed: T) => R) =>
84
- batchApi.transaction(object, action);
85
- chronicle.transactionAsync = async <T extends object, R>(object: T, action: (observed: T) => Promise<R>) =>
86
- batchApi.transactionAsync(object, action);
87
- chronicle.beginBatch = (obj) => batchApi.beginBatch(core.getRoot(obj));
88
- chronicle.commitBatch = (obj) => batchApi.commitBatch(core.getRoot(obj));
16
+ chronicle.listen = api.listen;
17
+ chronicle.onAny = api.onAny;
18
+ chronicle.pause = api.pause;
19
+ chronicle.resume = api.resume;
20
+ chronicle.flush = api.flush;
21
+ chronicle.getHistory = api.getHistory;
22
+ chronicle.clearHistory = api.clearHistory;
23
+ chronicle.reset = api.reset;
24
+ chronicle.markPristine = api.markPristine;
25
+ chronicle.undo = api.undo;
26
+ chronicle.undoSince = api.undoSince;
27
+ chronicle.diff = api.diff;
28
+ chronicle.isPristine = api.isPristine;
29
+ chronicle.snapshot = api.snapshot;
30
+ chronicle.unwrap = api.unwrap;
31
+ chronicle.merge = api.merge;
32
+ chronicle.mark = api.mark;
33
+ chronicle.undoGroups = api.undoGroups;
34
+ chronicle.canUndo = api.canUndo;
35
+ chronicle.canRedo = api.canRedo;
36
+ chronicle.clearRedo = api.clearRedo;
37
+ chronicle.redo = api.redo;
38
+ chronicle.redoGroups = api.redoGroups;
39
+ chronicle.configure = api.configure;
40
+ chronicle.transaction = batchApi.transaction;
41
+ chronicle.batch = batchApi.batch;
42
+ chronicle.beginBatch = (obj) => batchApi.beginBatch(core.getRoot(obj));
43
+ chronicle.commitBatch = (obj) => batchApi.commitBatch(core.getRoot(obj));
89
44
  chronicle.rollbackBatch = (obj) => batchApi.rollbackBatch(core.getRoot(obj));
90
- chronicle.batch = <T extends object, R>(object: T, action: (observed: T) => R) =>
91
- batchApi.batch(object, action);
92
-
93
-
94
- chronicle.configure = (obj: object, options: ConfigureOptions) => {
95
- const root = core.getRoot(obj);
96
- configureRoot(root, options);
97
- };
98
45
 
99
46
  export { chronicle };
100
47
  export type { Chronicle };
@@ -1,4 +1,5 @@
1
1
  import { ensureHistory, getOptions, trimHistoryByGroups } from './history.ts';
2
+ import { cloneWithOptions } from './snapshot-diff.ts';
2
3
  import type { ChangeRecord } from './types.ts';
3
4
  import { clearRedoCache, isSuspended } from './undo-redo.ts';
4
5
 
@@ -34,8 +35,8 @@ export const recordSet = (
34
35
  const rec: ChangeRecord = {
35
36
  path: path.slice(),
36
37
  type: 'set',
37
- oldValue,
38
- newValue,
38
+ oldValue: cloneWithOptions(root, oldValue),
39
+ newValue: cloneWithOptions(root, newValue),
39
40
  timestamp: Date.now(),
40
41
  existedBefore,
41
42
  groupId,
@@ -92,7 +93,7 @@ export const recordDelete = (
92
93
  const rec: ChangeRecord = {
93
94
  path: path.slice(),
94
95
  type: 'delete',
95
- oldValue,
96
+ oldValue: cloneWithOptions(root, oldValue),
96
97
  newValue: undefined,
97
98
  timestamp: Date.now(),
98
99
  groupId,
@@ -133,7 +134,7 @@ export const recordArrayShrinkDeletes = (
133
134
  const rec: ChangeRecord = {
134
135
  path: [ ...basePath, String(index) ],
135
136
  type: 'delete',
136
- oldValue: oldVal,
137
+ oldValue: cloneWithOptions(root, oldVal),
137
138
  newValue: undefined,
138
139
  timestamp: Date.now(),
139
140
  groupId,