@tanstack/db 0.0.4 → 0.0.5

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 (102) hide show
  1. package/dist/cjs/collection.cjs +113 -94
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +38 -11
  4. package/dist/cjs/index.cjs +1 -0
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/proxy.cjs +87 -248
  7. package/dist/cjs/proxy.cjs.map +1 -1
  8. package/dist/cjs/proxy.d.cts +5 -5
  9. package/dist/cjs/query/compiled-query.cjs +23 -14
  10. package/dist/cjs/query/compiled-query.cjs.map +1 -1
  11. package/dist/cjs/query/compiled-query.d.cts +3 -1
  12. package/dist/cjs/query/evaluators.cjs +20 -20
  13. package/dist/cjs/query/evaluators.cjs.map +1 -1
  14. package/dist/cjs/query/evaluators.d.cts +3 -2
  15. package/dist/cjs/query/extractors.cjs +20 -20
  16. package/dist/cjs/query/extractors.cjs.map +1 -1
  17. package/dist/cjs/query/extractors.d.cts +3 -3
  18. package/dist/cjs/query/group-by.cjs +12 -15
  19. package/dist/cjs/query/group-by.cjs.map +1 -1
  20. package/dist/cjs/query/group-by.d.cts +7 -7
  21. package/dist/cjs/query/joins.cjs +41 -55
  22. package/dist/cjs/query/joins.cjs.map +1 -1
  23. package/dist/cjs/query/joins.d.cts +3 -3
  24. package/dist/cjs/query/order-by.cjs +37 -84
  25. package/dist/cjs/query/order-by.cjs.map +1 -1
  26. package/dist/cjs/query/order-by.d.cts +2 -2
  27. package/dist/cjs/query/pipeline-compiler.cjs +13 -18
  28. package/dist/cjs/query/pipeline-compiler.cjs.map +1 -1
  29. package/dist/cjs/query/pipeline-compiler.d.cts +2 -1
  30. package/dist/cjs/query/query-builder.cjs +0 -12
  31. package/dist/cjs/query/query-builder.cjs.map +1 -1
  32. package/dist/cjs/query/query-builder.d.cts +4 -8
  33. package/dist/cjs/query/schema.d.cts +1 -6
  34. package/dist/cjs/query/select.cjs +35 -24
  35. package/dist/cjs/query/select.cjs.map +1 -1
  36. package/dist/cjs/query/select.d.cts +2 -2
  37. package/dist/cjs/query/types.d.cts +1 -0
  38. package/dist/cjs/transactions.cjs +17 -8
  39. package/dist/cjs/transactions.cjs.map +1 -1
  40. package/dist/cjs/types.d.cts +41 -7
  41. package/dist/esm/collection.d.ts +38 -11
  42. package/dist/esm/collection.js +113 -94
  43. package/dist/esm/collection.js.map +1 -1
  44. package/dist/esm/index.js +2 -1
  45. package/dist/esm/proxy.d.ts +5 -5
  46. package/dist/esm/proxy.js +87 -248
  47. package/dist/esm/proxy.js.map +1 -1
  48. package/dist/esm/query/compiled-query.d.ts +3 -1
  49. package/dist/esm/query/compiled-query.js +23 -14
  50. package/dist/esm/query/compiled-query.js.map +1 -1
  51. package/dist/esm/query/evaluators.d.ts +3 -2
  52. package/dist/esm/query/evaluators.js +21 -21
  53. package/dist/esm/query/evaluators.js.map +1 -1
  54. package/dist/esm/query/extractors.d.ts +3 -3
  55. package/dist/esm/query/extractors.js +20 -20
  56. package/dist/esm/query/extractors.js.map +1 -1
  57. package/dist/esm/query/group-by.d.ts +7 -7
  58. package/dist/esm/query/group-by.js +14 -17
  59. package/dist/esm/query/group-by.js.map +1 -1
  60. package/dist/esm/query/joins.d.ts +3 -3
  61. package/dist/esm/query/joins.js +42 -56
  62. package/dist/esm/query/joins.js.map +1 -1
  63. package/dist/esm/query/order-by.d.ts +2 -2
  64. package/dist/esm/query/order-by.js +39 -86
  65. package/dist/esm/query/order-by.js.map +1 -1
  66. package/dist/esm/query/pipeline-compiler.d.ts +2 -1
  67. package/dist/esm/query/pipeline-compiler.js +14 -19
  68. package/dist/esm/query/pipeline-compiler.js.map +1 -1
  69. package/dist/esm/query/query-builder.d.ts +4 -8
  70. package/dist/esm/query/query-builder.js +0 -12
  71. package/dist/esm/query/query-builder.js.map +1 -1
  72. package/dist/esm/query/schema.d.ts +1 -6
  73. package/dist/esm/query/select.d.ts +2 -2
  74. package/dist/esm/query/select.js +36 -25
  75. package/dist/esm/query/select.js.map +1 -1
  76. package/dist/esm/query/types.d.ts +1 -0
  77. package/dist/esm/transactions.js +17 -8
  78. package/dist/esm/transactions.js.map +1 -1
  79. package/dist/esm/types.d.ts +41 -7
  80. package/package.json +2 -2
  81. package/src/collection.ts +174 -121
  82. package/src/proxy.ts +141 -358
  83. package/src/query/compiled-query.ts +30 -15
  84. package/src/query/evaluators.ts +22 -21
  85. package/src/query/extractors.ts +24 -21
  86. package/src/query/group-by.ts +24 -22
  87. package/src/query/joins.ts +88 -75
  88. package/src/query/order-by.ts +56 -106
  89. package/src/query/pipeline-compiler.ts +34 -37
  90. package/src/query/query-builder.ts +9 -23
  91. package/src/query/schema.ts +1 -10
  92. package/src/query/select.ts +44 -32
  93. package/src/query/types.ts +1 -0
  94. package/src/transactions.ts +22 -13
  95. package/src/types.ts +48 -7
  96. package/dist/cjs/query/key-by.cjs +0 -43
  97. package/dist/cjs/query/key-by.cjs.map +0 -1
  98. package/dist/cjs/query/key-by.d.cts +0 -3
  99. package/dist/esm/query/key-by.d.ts +0 -3
  100. package/dist/esm/query/key-by.js +0 -43
  101. package/dist/esm/query/key-by.js.map +0 -1
  102. package/src/query/key-by.ts +0 -61
@@ -2,6 +2,14 @@ import { Derived, Store } from '@tanstack/store';
2
2
  import { SortedMap } from './SortedMap.cjs';
3
3
  import { ChangeMessage, CollectionConfig, InsertConfig, OperationConfig, OptimisticChangeMessage, Transaction } from './types.cjs';
4
4
  export declare const collectionsStore: Store<Map<string, Collection<any>>, (cb: Map<string, Collection<any>>) => Map<string, Collection<any>>>;
5
+ /**
6
+ * Creates a new Collection instance with the given configuration
7
+ *
8
+ * @template T - The type of items in the collection
9
+ * @param config - Configuration for the collection, including id and sync
10
+ * @returns A new Collection instance
11
+ */
12
+ export declare function createCollection<T extends object = Record<string, unknown>>(config: CollectionConfig<T>): Collection<T>;
5
13
  /**
6
14
  * Preloads a collection with the given configuration
7
15
  * Returns a promise that resolves once the sync tool has done its first commit (initial sync is finished)
@@ -55,7 +63,6 @@ export declare class Collection<T extends object = Record<string, unknown>> {
55
63
  private syncedKeys;
56
64
  config: CollectionConfig<T>;
57
65
  private hasReceivedFirstCommit;
58
- objectKeyMap: WeakMap<object, string>;
59
66
  private onFirstCommitCallbacks;
60
67
  /**
61
68
  * Register a callback to be executed on the next commit
@@ -63,22 +70,23 @@ export declare class Collection<T extends object = Record<string, unknown>> {
63
70
  * @param callback Function to call after the next commit
64
71
  */
65
72
  onFirstCommit(callback: () => void): void;
66
- id: `${string}-${string}-${string}-${string}-${string}`;
73
+ id: string;
67
74
  /**
68
75
  * Creates a new Collection instance
69
76
  *
70
77
  * @param config - Configuration object for the collection
71
78
  * @throws Error if sync config is missing
72
79
  */
73
- constructor(config?: CollectionConfig<T>);
80
+ constructor(config: CollectionConfig<T>);
74
81
  /**
75
82
  * Attempts to commit pending synced transactions if there are no active transactions
76
83
  * This method processes operations from pending transactions and applies them to the synced data
77
84
  */
78
85
  commitPendingTransactions: () => void;
79
86
  private ensureStandardSchema;
87
+ private getKeyFromId;
88
+ generateObjectKey(id: any, item: any): string;
80
89
  private validateData;
81
- private generateKey;
82
90
  /**
83
91
  * Inserts one or more items into the collection
84
92
  * @param items - Single item or array of items to insert
@@ -118,24 +126,43 @@ export declare class Collection<T extends object = Record<string, unknown>> {
118
126
  * // Update with metadata
119
127
  * update(todo, { metadata: { reason: "user update" } }, (draft) => { draft.text = "Updated text" })
120
128
  */
121
- update<TItem extends object = T>(item: TItem, configOrCallback: ((draft: TItem) => void) | OperationConfig, maybeCallback?: (draft: TItem) => void): Transaction;
122
- update<TItem extends object = T>(items: Array<TItem>, configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig, maybeCallback?: (draft: Array<TItem>) => void): Transaction;
129
+ /**
130
+ * Updates one or more items in the collection using a callback function
131
+ * @param ids - Single ID or array of IDs to update
132
+ * @param configOrCallback - Either update configuration or update callback
133
+ * @param maybeCallback - Update callback if config was provided
134
+ * @returns A Transaction object representing the update operation(s)
135
+ * @throws {SchemaValidationError} If the updated data fails schema validation
136
+ * @example
137
+ * // Update a single item
138
+ * update("todo-1", (draft) => { draft.completed = true })
139
+ *
140
+ * // Update multiple items
141
+ * update(["todo-1", "todo-2"], (drafts) => {
142
+ * drafts.forEach(draft => { draft.completed = true })
143
+ * })
144
+ *
145
+ * // Update with metadata
146
+ * update("todo-1", { metadata: { reason: "user update" } }, (draft) => { draft.text = "Updated text" })
147
+ */
148
+ update<TItem extends object = T>(id: unknown, configOrCallback: ((draft: TItem) => void) | OperationConfig, maybeCallback?: (draft: TItem) => void): Transaction;
149
+ update<TItem extends object = T>(ids: Array<unknown>, configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig, maybeCallback?: (draft: Array<TItem>) => void): Transaction;
123
150
  /**
124
151
  * Deletes one or more items from the collection
125
- * @param items - Single item/key or array of items/keys to delete
152
+ * @param ids - Single ID or array of IDs to delete
126
153
  * @param config - Optional configuration including metadata
127
154
  * @returns A Transaction object representing the delete operation(s)
128
155
  * @example
129
156
  * // Delete a single item
130
- * delete(todo)
157
+ * delete("todo-1")
131
158
  *
132
159
  * // Delete multiple items
133
- * delete([todo1, todo2])
160
+ * delete(["todo-1", "todo-2"])
134
161
  *
135
162
  * // Delete with metadata
136
- * delete(todo, { metadata: { reason: "completed" } })
163
+ * delete("todo-1", { metadata: { reason: "completed" } })
137
164
  */
138
- delete: (items: Array<T | string> | T | string, config?: OperationConfig) => Transaction;
165
+ delete: (ids: Array<string> | string, config?: OperationConfig) => Transaction;
139
166
  /**
140
167
  * Gets the current state of the collection as a Map
141
168
  *
@@ -12,6 +12,7 @@ const pipelineCompiler = require("./query/pipeline-compiler.cjs");
12
12
  exports.Collection = collection.Collection;
13
13
  exports.SchemaValidationError = collection.SchemaValidationError;
14
14
  exports.collectionsStore = collection.collectionsStore;
15
+ exports.createCollection = collection.createCollection;
15
16
  exports.preloadCollection = collection.preloadCollection;
16
17
  exports.SortedMap = SortedMap.SortedMap;
17
18
  exports.Transaction = transactions.Transaction;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -4,7 +4,10 @@ function debugLog(...args) {
4
4
  const isBrowser = typeof window !== `undefined` && typeof localStorage !== `undefined`;
5
5
  if (isBrowser && localStorage.getItem(`DEBUG`) === `true`) {
6
6
  console.log(`[proxy]`, ...args);
7
- } else if (!isBrowser && typeof process !== `undefined` && process.env.DEBUG === `true`) {
7
+ } else if (
8
+ // true
9
+ !isBrowser && typeof process !== `undefined` && process.env.DEBUG === `true`
10
+ ) {
8
11
  console.log(`[proxy]`, ...args);
9
12
  }
10
13
  }
@@ -131,24 +134,48 @@ function deepEqual(a, b) {
131
134
  (key) => Object.prototype.hasOwnProperty.call(b, key) && deepEqual(a[key], b[key])
132
135
  );
133
136
  }
137
+ let count = 0;
138
+ function getProxyCount() {
139
+ count += 1;
140
+ return count;
141
+ }
134
142
  function createChangeProxy(target, parent) {
135
- const proxyCache = /* @__PURE__ */ new WeakMap();
143
+ const changeProxyCache = /* @__PURE__ */ new Map();
144
+ function memoizedCreateChangeProxy(innerTarget, innerParent) {
145
+ debugLog(`Object ID:`, innerTarget.constructor.name);
146
+ if (changeProxyCache.has(innerTarget)) {
147
+ return changeProxyCache.get(innerTarget);
148
+ } else {
149
+ const changeProxy = createChangeProxy(innerTarget, innerParent);
150
+ changeProxyCache.set(innerTarget, changeProxy);
151
+ return changeProxy;
152
+ }
153
+ }
154
+ const proxyCache = /* @__PURE__ */ new Map();
136
155
  const changeTracker = {
137
- changes: {},
156
+ copy_: deepClone(target),
138
157
  originalObject: deepClone(target),
139
- // Create a deep clone to preserve the original state
158
+ proxyCount: getProxyCount(),
140
159
  modified: false,
141
160
  assigned_: {},
142
161
  parent,
143
162
  target
144
163
  // Store reference to the target object
145
164
  };
165
+ debugLog(
166
+ `createChangeProxy called for target`,
167
+ target,
168
+ changeTracker.proxyCount
169
+ );
146
170
  function markChanged(state) {
147
171
  if (!state.modified) {
148
172
  state.modified = true;
149
- if (state.parent) {
150
- markChanged(state.parent.tracker);
151
- }
173
+ }
174
+ if (state.parent) {
175
+ debugLog(`propagating change to parent`);
176
+ state.parent.tracker.copy_[state.parent.prop] = state.copy_;
177
+ state.parent.tracker.assigned_[state.parent.prop] = true;
178
+ markChanged(state.parent.tracker);
152
179
  }
153
180
  }
154
181
  function checkIfReverted(state) {
@@ -162,7 +189,7 @@ function createChangeProxy(target, parent) {
162
189
  }
163
190
  for (const prop in state.assigned_) {
164
191
  if (state.assigned_[prop] === true) {
165
- const currentValue = state.copy_ ? state.copy_[prop] : null;
192
+ const currentValue = state.copy_[prop];
166
193
  const originalValue = state.originalObject[prop];
167
194
  debugLog(
168
195
  `Checking property ${String(prop)}, current:`,
@@ -181,14 +208,14 @@ function createChangeProxy(target, parent) {
181
208
  }
182
209
  const symbolProps = Object.getOwnPropertySymbols(state.assigned_);
183
210
  for (const sym of symbolProps) {
184
- if (state.assigned_[sym.toString()] === true) {
185
- const currentValue = state.copy_ ? state.copy_[sym] : null;
211
+ if (state.assigned_[sym] === true) {
212
+ const currentValue = state.copy_[sym];
186
213
  const originalValue = state.originalObject[sym];
187
214
  if (!deepEqual(currentValue, originalValue)) {
188
215
  debugLog(`Symbol property is different, returning false`);
189
216
  return false;
190
217
  }
191
- } else if (state.assigned_[sym.toString()] === false) {
218
+ } else if (state.assigned_[sym] === false) {
192
219
  debugLog(`Symbol property was deleted, returning false`);
193
220
  return false;
194
221
  }
@@ -196,31 +223,6 @@ function createChangeProxy(target, parent) {
196
223
  debugLog(`All properties match original values, returning true`);
197
224
  return true;
198
225
  }
199
- function updateModifiedStatus(state) {
200
- debugLog(
201
- `updateModifiedStatus called, assigned keys:`,
202
- Object.keys(state.assigned_)
203
- );
204
- if (Object.keys(state.assigned_).length === 0 && Object.getOwnPropertySymbols(state.assigned_).length === 0) {
205
- debugLog(`No assigned properties, returning false`);
206
- return false;
207
- }
208
- const isReverted = checkIfReverted(state);
209
- debugLog(`checkIfReverted returned:`, isReverted);
210
- if (!isReverted) {
211
- debugLog(`Object has changes that aren't reverted, returning true`);
212
- return true;
213
- }
214
- debugLog(`All changes reverted, clearing tracking`);
215
- state.modified = false;
216
- state.changes = {};
217
- state.assigned_ = {};
218
- if (state.parent) {
219
- debugLog(`Checking parent status for prop:`, state.parent.prop);
220
- checkParentStatus(state.parent.tracker, state.parent.prop);
221
- }
222
- return false;
223
- }
224
226
  function checkParentStatus(parentState, childProp) {
225
227
  debugLog(`checkParentStatus called for child prop:`, childProp);
226
228
  const isReverted = checkIfReverted(parentState);
@@ -228,7 +230,6 @@ function createChangeProxy(target, parent) {
228
230
  if (isReverted) {
229
231
  debugLog(`Parent is fully reverted, clearing tracking`);
230
232
  parentState.modified = false;
231
- parentState.changes = {};
232
233
  parentState.assigned_ = {};
233
234
  if (parentState.parent) {
234
235
  debugLog(`Continuing up the parent chain`);
@@ -237,12 +238,17 @@ function createChangeProxy(target, parent) {
237
238
  }
238
239
  }
239
240
  function createObjectProxy(obj) {
241
+ debugLog(`createObjectProxy`, obj);
240
242
  if (proxyCache.has(obj)) {
243
+ debugLog(`proxyCache found match`);
241
244
  return proxyCache.get(obj);
242
245
  }
243
246
  const proxy2 = new Proxy(obj, {
244
247
  get(ptarget, prop) {
245
- const value = ptarget[prop];
248
+ debugLog(`get`, ptarget, prop);
249
+ const value = changeTracker.copy_[prop] ?? changeTracker.originalObject[prop];
250
+ const originalValue = changeTracker.originalObject[prop];
251
+ debugLog(`value (at top of proxy get)`, value);
246
252
  const desc = Object.getOwnPropertyDescriptor(ptarget, prop);
247
253
  if (desc == null ? void 0 : desc.get) {
248
254
  return value;
@@ -265,7 +271,7 @@ function createChangeProxy(target, parent) {
265
271
  ]);
266
272
  if (modifyingMethods.has(methodName)) {
267
273
  return function(...args) {
268
- const result = value.apply(ptarget, args);
274
+ const result = value.apply(changeTracker.copy_, args);
269
275
  markChanged(changeTracker);
270
276
  return result;
271
277
  };
@@ -279,7 +285,7 @@ function createChangeProxy(target, parent) {
279
285
  ]);
280
286
  if (iteratorMethods.has(methodName) || prop === Symbol.iterator) {
281
287
  return function(...args) {
282
- const result = value.apply(ptarget, args);
288
+ const result = value.apply(changeTracker.copy_, args);
283
289
  if (methodName === `forEach`) {
284
290
  const callback = args[0];
285
291
  if (typeof callback === `function`) {
@@ -307,25 +313,19 @@ function createChangeProxy(target, parent) {
307
313
  if (!nextResult.done && nextResult.value && typeof nextResult.value === `object`) {
308
314
  if (methodName === `entries` && Array.isArray(nextResult.value) && nextResult.value.length === 2) {
309
315
  if (nextResult.value[1] && typeof nextResult.value[1] === `object`) {
310
- const { proxy: valueProxy } = createChangeProxy(
311
- nextResult.value[1],
312
- {
313
- tracker: changeTracker,
314
- prop: typeof nextResult.value[0] === `symbol` ? nextResult.value[0] : String(nextResult.value[0])
315
- }
316
- );
316
+ const { proxy: valueProxy } = memoizedCreateChangeProxy(nextResult.value[1], {
317
+ tracker: changeTracker,
318
+ prop: typeof nextResult.value[0] === `symbol` ? nextResult.value[0] : String(nextResult.value[0])
319
+ });
317
320
  nextResult.value[1] = valueProxy;
318
321
  }
319
322
  } else if (methodName === `values` || methodName === Symbol.iterator.toString() || prop === Symbol.iterator) {
320
323
  if (typeof nextResult.value === `object` && nextResult.value !== null) {
321
324
  const tempKey = Symbol(`iterator-value`);
322
- const { proxy: valueProxy } = createChangeProxy(
323
- nextResult.value,
324
- {
325
- tracker: changeTracker,
326
- prop: tempKey
327
- }
328
- );
325
+ const { proxy: valueProxy } = memoizedCreateChangeProxy(nextResult.value, {
326
+ tracker: changeTracker,
327
+ prop: tempKey
328
+ });
329
329
  nextResult.value = valueProxy;
330
330
  }
331
331
  }
@@ -348,59 +348,45 @@ function createChangeProxy(target, parent) {
348
348
  tracker: changeTracker,
349
349
  prop: String(prop)
350
350
  };
351
- const { proxy: nestedProxy } = createChangeProxy(value, nestedParent);
351
+ const { proxy: nestedProxy } = memoizedCreateChangeProxy(
352
+ originalValue,
353
+ nestedParent
354
+ );
352
355
  proxyCache.set(value, nestedProxy);
353
356
  return nestedProxy;
354
357
  }
355
358
  return value;
356
359
  },
357
360
  set(sobj, prop, value) {
358
- const currentValue = sobj[prop];
361
+ const currentValue = changeTracker.copy_[prop];
359
362
  debugLog(
360
363
  `set called for property ${String(prop)}, current:`,
361
364
  currentValue,
362
365
  `new:`,
363
366
  value
364
367
  );
365
- if (Array.isArray(sobj) && prop === `length`) {
366
- const newLength = Number(value);
367
- const oldLength = sobj.length;
368
- const newArray = Array.from(
369
- { length: newLength },
370
- (_, i) => i < oldLength ? sobj[i] : void 0
371
- );
372
- if (parent) {
373
- parent.tracker.changes[parent.prop] = newArray;
374
- parent.tracker.assigned_[parent.prop] = true;
375
- markChanged(parent.tracker);
376
- }
377
- sobj.length = newLength;
378
- return true;
379
- }
380
368
  if (!deepEqual(currentValue, value)) {
381
369
  const originalValue = changeTracker.originalObject[prop];
382
370
  const isRevertToOriginal = deepEqual(value, originalValue);
383
371
  debugLog(
384
- `Value different, original:`,
372
+ `value:`,
373
+ value,
374
+ `original:`,
385
375
  originalValue,
386
376
  `isRevertToOriginal:`,
387
377
  isRevertToOriginal
388
378
  );
389
379
  if (isRevertToOriginal) {
390
380
  debugLog(`Reverting property ${String(prop)} to original value`);
391
- delete changeTracker.changes[prop.toString()];
392
381
  delete changeTracker.assigned_[prop.toString()];
393
- if (changeTracker.copy_) {
394
- debugLog(`Updating copy with original value for ${String(prop)}`);
395
- changeTracker.copy_[prop] = deepClone(originalValue);
396
- }
382
+ debugLog(`Updating copy with original value for ${String(prop)}`);
383
+ changeTracker.copy_[prop] = deepClone(originalValue);
397
384
  debugLog(`Checking if all properties reverted`);
398
385
  const allReverted = checkIfReverted(changeTracker);
399
386
  debugLog(`All reverted:`, allReverted);
400
387
  if (allReverted) {
401
388
  debugLog(`All properties reverted, clearing tracking`);
402
389
  changeTracker.modified = false;
403
- changeTracker.changes = {};
404
390
  changeTracker.assigned_ = {};
405
391
  if (parent) {
406
392
  debugLog(`Updating parent for property:`, parent.prop);
@@ -412,14 +398,9 @@ function createChangeProxy(target, parent) {
412
398
  }
413
399
  } else {
414
400
  debugLog(`Setting new value for property ${String(prop)}`);
415
- prepareCopy(changeTracker);
416
- if (changeTracker.copy_) {
417
- changeTracker.copy_[prop] = value;
418
- }
419
- obj[prop] = value;
401
+ changeTracker.copy_[prop] = value;
420
402
  changeTracker.assigned_[prop.toString()] = true;
421
- changeTracker.changes[prop.toString()] = deepClone(value);
422
- debugLog(`Marking object and ancestors as modified`);
403
+ debugLog(`Marking object and ancestors as modified`, changeTracker);
423
404
  markChanged(changeTracker);
424
405
  }
425
406
  } else {
@@ -428,30 +409,21 @@ function createChangeProxy(target, parent) {
428
409
  return true;
429
410
  },
430
411
  defineProperty(ptarget, prop, descriptor) {
431
- const result = Reflect.defineProperty(ptarget, prop, descriptor);
432
- if (result) {
433
- if (`value` in descriptor) {
434
- changeTracker.changes[prop.toString()] = deepClone(descriptor.value);
435
- changeTracker.assigned_[prop.toString()] = true;
436
- markChanged(changeTracker);
437
- }
412
+ if (`value` in descriptor) {
413
+ changeTracker.copy_[prop] = deepClone(descriptor.value);
414
+ changeTracker.assigned_[prop.toString()] = true;
415
+ markChanged(changeTracker);
438
416
  }
439
- return result;
440
- },
441
- setPrototypeOf(ptarget, proto) {
442
- return Object.setPrototypeOf(ptarget, proto);
417
+ return true;
443
418
  },
444
419
  deleteProperty(dobj, prop) {
420
+ debugLog(`deleteProperty`, dobj, prop);
445
421
  const stringProp = typeof prop === `symbol` ? prop.toString() : prop;
446
422
  if (stringProp in dobj) {
447
423
  const hadPropertyInOriginal = stringProp in changeTracker.originalObject;
448
- prepareCopy(changeTracker);
449
- if (changeTracker.copy_) {
450
- delete changeTracker.copy_[prop];
451
- }
452
- delete dobj[prop];
424
+ delete changeTracker.copy_[prop];
453
425
  if (!hadPropertyInOriginal) {
454
- delete changeTracker.changes[stringProp];
426
+ delete changeTracker.copy_[stringProp];
455
427
  delete changeTracker.assigned_[stringProp];
456
428
  if (Object.keys(changeTracker.assigned_).length === 0 && Object.getOwnPropertySymbols(changeTracker.assigned_).length === 0) {
457
429
  changeTracker.modified = false;
@@ -460,7 +432,7 @@ function createChangeProxy(target, parent) {
460
432
  }
461
433
  } else {
462
434
  changeTracker.assigned_[stringProp] = false;
463
- changeTracker.changes[stringProp] = void 0;
435
+ changeTracker.copy_[stringProp] = void 0;
464
436
  markChanged(changeTracker);
465
437
  }
466
438
  }
@@ -474,133 +446,26 @@ function createChangeProxy(target, parent) {
474
446
  return {
475
447
  proxy,
476
448
  getChanges: () => {
477
- debugLog(
478
- `getChanges called, modified:`,
479
- changeTracker.modified,
480
- `assigned keys:`,
481
- Object.keys(changeTracker.assigned_)
482
- );
449
+ debugLog(`getChanges called, modified:`, changeTracker.modified);
450
+ debugLog(changeTracker);
483
451
  if (!changeTracker.modified) {
484
452
  debugLog(`Object not modified, returning empty object`);
485
453
  return {};
486
454
  }
487
- if (Object.keys(changeTracker.assigned_).length === 0 && Object.getOwnPropertySymbols(changeTracker.assigned_).length === 0) {
488
- debugLog(`No assigned properties, checking deep equality`);
489
- if (changeTracker.copy_) {
490
- debugLog(`Comparing copy with original`);
491
- if (deepEqual(changeTracker.copy_, changeTracker.originalObject)) {
492
- debugLog(`Copy equals original, returning empty object`);
493
- changeTracker.modified = false;
494
- return {};
495
- }
496
- } else if (deepEqual(target, changeTracker.originalObject)) {
497
- debugLog(`Target equals original, returning empty object`);
498
- changeTracker.modified = false;
499
- changeTracker.changes = {};
500
- changeTracker.assigned_ = {};
501
- return {};
502
- }
455
+ if (typeof changeTracker.copy_ !== `object` || Array.isArray(changeTracker.copy_)) {
456
+ return changeTracker.copy_;
503
457
  }
504
- debugLog(`Forcing full check for reverted state`);
505
- updateModifiedStatus(changeTracker);
506
- if (!changeTracker.modified) {
507
- debugLog(`No longer modified after check, returning empty object`);
508
- return {};
458
+ if (Object.keys(changeTracker.assigned_).length === 0) {
459
+ return changeTracker.copy_;
509
460
  }
510
- if (changeTracker.modified) {
511
- const objToCheck = changeTracker.copy_ || target;
512
- debugLog(
513
- `Checking if object is equal to original:`,
514
- objToCheck,
515
- changeTracker.originalObject
516
- );
517
- if (deepEqual(objToCheck, changeTracker.originalObject)) {
518
- debugLog(`Object equals original, returning empty object`);
519
- changeTracker.modified = false;
520
- changeTracker.changes = {};
521
- changeTracker.assigned_ = {};
522
- return {};
461
+ const result = {};
462
+ for (const key in changeTracker.copy_) {
463
+ if (changeTracker.assigned_[key] === true && key in changeTracker.copy_) {
464
+ result[key] = changeTracker.copy_[key];
523
465
  }
524
466
  }
525
- if (Object.keys(changeTracker.assigned_).length > 0 || Object.getOwnPropertySymbols(changeTracker.assigned_).length > 0) {
526
- if (changeTracker.copy_) {
527
- const changes = {};
528
- for (const key in changeTracker.assigned_) {
529
- if (changeTracker.assigned_[key] === true) {
530
- changes[key] = deepClone(changeTracker.copy_[key]);
531
- } else if (changeTracker.assigned_[key] === false) {
532
- changes[key] = void 0;
533
- }
534
- }
535
- const symbolProps = Object.getOwnPropertySymbols(
536
- changeTracker.assigned_
537
- );
538
- for (const sym of symbolProps) {
539
- if (changeTracker.assigned_[sym.toString()] === true) {
540
- const value = changeTracker.copy_[sym];
541
- changes[sym.toString()] = deepClone(value);
542
- }
543
- }
544
- return changes;
545
- }
546
- return changeTracker.changes;
547
- }
548
- if (changeTracker.modified && !parent) {
549
- debugLog(`Root object with nested changes, checking deep equality`);
550
- const currentState = changeTracker.copy_ || target;
551
- debugLog(
552
- `Comparing current state with original:`,
553
- currentState,
554
- changeTracker.originalObject
555
- );
556
- if (deepEqual(currentState, changeTracker.originalObject)) {
557
- debugLog(`Current state equals original, returning empty object`);
558
- changeTracker.modified = false;
559
- return {};
560
- }
561
- debugLog(
562
- `Comparing target with original:`,
563
- target,
564
- changeTracker.originalObject
565
- );
566
- if (deepEqual(target, changeTracker.originalObject)) {
567
- debugLog(`Target equals original, returning empty object`);
568
- changeTracker.modified = false;
569
- changeTracker.changes = {};
570
- changeTracker.assigned_ = {};
571
- return {};
572
- }
573
- if (typeof target === `object` && target !== null) {
574
- let allNestedReverted = true;
575
- for (const key in target) {
576
- if (Object.prototype.hasOwnProperty.call(target, key)) {
577
- const currentValue = target[key];
578
- const originalValue = changeTracker.originalObject[key];
579
- if (!deepEqual(currentValue, originalValue)) {
580
- allNestedReverted = false;
581
- break;
582
- }
583
- }
584
- }
585
- if (allNestedReverted) {
586
- debugLog(
587
- `All nested properties match original values, returning empty object`
588
- );
589
- changeTracker.modified = false;
590
- changeTracker.changes = {};
591
- changeTracker.assigned_ = {};
592
- return {};
593
- }
594
- }
595
- debugLog(
596
- `Changes detected, returning full object:`,
597
- changeTracker.copy_ || target
598
- );
599
- const result = changeTracker.copy_ || target;
600
- return result;
601
- }
602
- debugLog(`No changes detected, returning empty object`);
603
- return {};
467
+ debugLog(`Returning copy:`, result);
468
+ return result;
604
469
  }
605
470
  };
606
471
  }
@@ -621,32 +486,6 @@ function withArrayChangeTracking(targets, callback) {
621
486
  callback(proxies);
622
487
  return getChanges();
623
488
  }
624
- function prepareCopy(state) {
625
- if (!state.copy_) {
626
- state.copy_ = shallowCopy(state.originalObject);
627
- }
628
- }
629
- function shallowCopy(obj) {
630
- if (Array.isArray(obj)) {
631
- return [...obj];
632
- }
633
- if (obj instanceof Map) {
634
- return new Map(obj);
635
- }
636
- if (obj instanceof Set) {
637
- return new Set(obj);
638
- }
639
- if (obj instanceof Date) {
640
- return new Date(obj.getTime());
641
- }
642
- if (obj instanceof RegExp) {
643
- return new RegExp(obj.source, obj.flags);
644
- }
645
- if (obj !== null && typeof obj === `object`) {
646
- return { ...obj };
647
- }
648
- return obj;
649
- }
650
489
  exports.createArrayChangeProxy = createArrayChangeProxy;
651
490
  exports.createChangeProxy = createChangeProxy;
652
491
  exports.withArrayChangeTracking = withArrayChangeTracking;