@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.
- package/dist/cjs/collection.cjs +113 -94
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +38 -11
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/proxy.cjs +87 -248
- package/dist/cjs/proxy.cjs.map +1 -1
- package/dist/cjs/proxy.d.cts +5 -5
- package/dist/cjs/query/compiled-query.cjs +23 -14
- package/dist/cjs/query/compiled-query.cjs.map +1 -1
- package/dist/cjs/query/compiled-query.d.cts +3 -1
- package/dist/cjs/query/evaluators.cjs +20 -20
- package/dist/cjs/query/evaluators.cjs.map +1 -1
- package/dist/cjs/query/evaluators.d.cts +3 -2
- package/dist/cjs/query/extractors.cjs +20 -20
- package/dist/cjs/query/extractors.cjs.map +1 -1
- package/dist/cjs/query/extractors.d.cts +3 -3
- package/dist/cjs/query/group-by.cjs +12 -15
- package/dist/cjs/query/group-by.cjs.map +1 -1
- package/dist/cjs/query/group-by.d.cts +7 -7
- package/dist/cjs/query/joins.cjs +41 -55
- package/dist/cjs/query/joins.cjs.map +1 -1
- package/dist/cjs/query/joins.d.cts +3 -3
- package/dist/cjs/query/order-by.cjs +37 -84
- package/dist/cjs/query/order-by.cjs.map +1 -1
- package/dist/cjs/query/order-by.d.cts +2 -2
- package/dist/cjs/query/pipeline-compiler.cjs +13 -18
- package/dist/cjs/query/pipeline-compiler.cjs.map +1 -1
- package/dist/cjs/query/pipeline-compiler.d.cts +2 -1
- package/dist/cjs/query/query-builder.cjs +0 -12
- package/dist/cjs/query/query-builder.cjs.map +1 -1
- package/dist/cjs/query/query-builder.d.cts +4 -8
- package/dist/cjs/query/schema.d.cts +1 -6
- package/dist/cjs/query/select.cjs +35 -24
- package/dist/cjs/query/select.cjs.map +1 -1
- package/dist/cjs/query/select.d.cts +2 -2
- package/dist/cjs/query/types.d.cts +1 -0
- package/dist/cjs/transactions.cjs +17 -8
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +41 -7
- package/dist/esm/collection.d.ts +38 -11
- package/dist/esm/collection.js +113 -94
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/proxy.d.ts +5 -5
- package/dist/esm/proxy.js +87 -248
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query/compiled-query.d.ts +3 -1
- package/dist/esm/query/compiled-query.js +23 -14
- package/dist/esm/query/compiled-query.js.map +1 -1
- package/dist/esm/query/evaluators.d.ts +3 -2
- package/dist/esm/query/evaluators.js +21 -21
- package/dist/esm/query/evaluators.js.map +1 -1
- package/dist/esm/query/extractors.d.ts +3 -3
- package/dist/esm/query/extractors.js +20 -20
- package/dist/esm/query/extractors.js.map +1 -1
- package/dist/esm/query/group-by.d.ts +7 -7
- package/dist/esm/query/group-by.js +14 -17
- package/dist/esm/query/group-by.js.map +1 -1
- package/dist/esm/query/joins.d.ts +3 -3
- package/dist/esm/query/joins.js +42 -56
- package/dist/esm/query/joins.js.map +1 -1
- package/dist/esm/query/order-by.d.ts +2 -2
- package/dist/esm/query/order-by.js +39 -86
- package/dist/esm/query/order-by.js.map +1 -1
- package/dist/esm/query/pipeline-compiler.d.ts +2 -1
- package/dist/esm/query/pipeline-compiler.js +14 -19
- package/dist/esm/query/pipeline-compiler.js.map +1 -1
- package/dist/esm/query/query-builder.d.ts +4 -8
- package/dist/esm/query/query-builder.js +0 -12
- package/dist/esm/query/query-builder.js.map +1 -1
- package/dist/esm/query/schema.d.ts +1 -6
- package/dist/esm/query/select.d.ts +2 -2
- package/dist/esm/query/select.js +36 -25
- package/dist/esm/query/select.js.map +1 -1
- package/dist/esm/query/types.d.ts +1 -0
- package/dist/esm/transactions.js +17 -8
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +41 -7
- package/package.json +2 -2
- package/src/collection.ts +174 -121
- package/src/proxy.ts +141 -358
- package/src/query/compiled-query.ts +30 -15
- package/src/query/evaluators.ts +22 -21
- package/src/query/extractors.ts +24 -21
- package/src/query/group-by.ts +24 -22
- package/src/query/joins.ts +88 -75
- package/src/query/order-by.ts +56 -106
- package/src/query/pipeline-compiler.ts +34 -37
- package/src/query/query-builder.ts +9 -23
- package/src/query/schema.ts +1 -10
- package/src/query/select.ts +44 -32
- package/src/query/types.ts +1 -0
- package/src/transactions.ts +22 -13
- package/src/types.ts +48 -7
- package/dist/cjs/query/key-by.cjs +0 -43
- package/dist/cjs/query/key-by.cjs.map +0 -1
- package/dist/cjs/query/key-by.d.cts +0 -3
- package/dist/esm/query/key-by.d.ts +0 -3
- package/dist/esm/query/key-by.js +0 -43
- package/dist/esm/query/key-by.js.map +0 -1
- package/src/query/key-by.ts +0 -61
package/src/proxy.ts
CHANGED
|
@@ -18,6 +18,7 @@ function debugLog(...args: Array<unknown>): void {
|
|
|
18
18
|
}
|
|
19
19
|
// In Node.js environment, check for environment variable (though this is primarily for browser)
|
|
20
20
|
else if (
|
|
21
|
+
// true
|
|
21
22
|
!isBrowser &&
|
|
22
23
|
typeof process !== `undefined` &&
|
|
23
24
|
process.env.DEBUG === `true`
|
|
@@ -34,13 +35,13 @@ interface TypedArray {
|
|
|
34
35
|
|
|
35
36
|
// Update type for ChangeTracker
|
|
36
37
|
interface ChangeTracker<T extends object> {
|
|
37
|
-
changes: Record<string | symbol, unknown>
|
|
38
38
|
originalObject: T
|
|
39
39
|
modified: boolean
|
|
40
|
-
copy_
|
|
40
|
+
copy_: T
|
|
41
|
+
proxyCount: number
|
|
41
42
|
assigned_: Record<string | symbol, boolean>
|
|
42
43
|
parent?: {
|
|
43
|
-
tracker: ChangeTracker<
|
|
44
|
+
tracker: ChangeTracker<Record<string | symbol, unknown>>
|
|
44
45
|
prop: string | symbol
|
|
45
46
|
}
|
|
46
47
|
target: T
|
|
@@ -245,6 +246,12 @@ function deepEqual<T>(a: T, b: T): boolean {
|
|
|
245
246
|
)
|
|
246
247
|
}
|
|
247
248
|
|
|
249
|
+
let count = 0
|
|
250
|
+
function getProxyCount() {
|
|
251
|
+
count += 1
|
|
252
|
+
return count
|
|
253
|
+
}
|
|
254
|
+
|
|
248
255
|
/**
|
|
249
256
|
* Creates a proxy that tracks changes to the target object
|
|
250
257
|
*
|
|
@@ -252,43 +259,90 @@ function deepEqual<T>(a: T, b: T): boolean {
|
|
|
252
259
|
* @param parent Optional parent information
|
|
253
260
|
* @returns An object containing the proxy and a function to get the changes
|
|
254
261
|
*/
|
|
255
|
-
export function createChangeProxy<
|
|
262
|
+
export function createChangeProxy<
|
|
263
|
+
T extends Record<string | symbol, any | undefined>,
|
|
264
|
+
>(
|
|
256
265
|
target: T,
|
|
257
|
-
parent?: {
|
|
266
|
+
parent?: {
|
|
267
|
+
tracker: ChangeTracker<Record<string | symbol, unknown>>
|
|
268
|
+
prop: string | symbol
|
|
269
|
+
}
|
|
258
270
|
): {
|
|
259
271
|
proxy: T
|
|
260
272
|
|
|
261
273
|
getChanges: () => Record<string | symbol, any>
|
|
262
274
|
} {
|
|
275
|
+
const changeProxyCache = new Map<object, object>()
|
|
276
|
+
|
|
277
|
+
function memoizedCreateChangeProxy<
|
|
278
|
+
TInner extends Record<string | symbol, any | undefined>,
|
|
279
|
+
>(
|
|
280
|
+
innerTarget: TInner,
|
|
281
|
+
innerParent?: {
|
|
282
|
+
tracker: ChangeTracker<Record<string | symbol, unknown>>
|
|
283
|
+
prop: string | symbol
|
|
284
|
+
}
|
|
285
|
+
): {
|
|
286
|
+
proxy: TInner
|
|
287
|
+
getChanges: () => Record<string | symbol, any>
|
|
288
|
+
} {
|
|
289
|
+
debugLog(`Object ID:`, innerTarget.constructor.name)
|
|
290
|
+
if (changeProxyCache.has(innerTarget)) {
|
|
291
|
+
return changeProxyCache.get(innerTarget) as {
|
|
292
|
+
proxy: TInner
|
|
293
|
+
getChanges: () => Record<string | symbol, any>
|
|
294
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
const changeProxy = createChangeProxy(innerTarget, innerParent)
|
|
297
|
+
changeProxyCache.set(innerTarget, changeProxy)
|
|
298
|
+
return changeProxy
|
|
299
|
+
}
|
|
300
|
+
}
|
|
263
301
|
// Create a WeakMap to cache proxies for nested objects
|
|
264
302
|
// This prevents creating multiple proxies for the same object
|
|
265
303
|
// and handles circular references
|
|
266
|
-
const proxyCache = new
|
|
304
|
+
const proxyCache = new Map<object, object>()
|
|
267
305
|
|
|
268
306
|
// Create a change tracker to track changes to the object
|
|
269
307
|
const changeTracker: ChangeTracker<T> = {
|
|
270
|
-
|
|
271
|
-
originalObject: deepClone(target),
|
|
308
|
+
copy_: deepClone(target),
|
|
309
|
+
originalObject: deepClone(target),
|
|
310
|
+
proxyCount: getProxyCount(),
|
|
272
311
|
modified: false,
|
|
273
312
|
assigned_: {},
|
|
274
313
|
parent,
|
|
275
314
|
target, // Store reference to the target object
|
|
276
315
|
}
|
|
277
316
|
|
|
317
|
+
debugLog(
|
|
318
|
+
`createChangeProxy called for target`,
|
|
319
|
+
target,
|
|
320
|
+
changeTracker.proxyCount
|
|
321
|
+
)
|
|
278
322
|
// Mark this object and all its ancestors as modified
|
|
323
|
+
// Also propagate the actual changes up the chain
|
|
279
324
|
function markChanged(state: ChangeTracker<object>) {
|
|
280
325
|
if (!state.modified) {
|
|
281
326
|
state.modified = true
|
|
327
|
+
}
|
|
282
328
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
329
|
+
// Propagate the change up the parent chain
|
|
330
|
+
if (state.parent) {
|
|
331
|
+
debugLog(`propagating change to parent`)
|
|
332
|
+
|
|
333
|
+
// Update parent's copy with this object's current state
|
|
334
|
+
state.parent.tracker.copy_[state.parent.prop] = state.copy_
|
|
335
|
+
state.parent.tracker.assigned_[state.parent.prop] = true
|
|
336
|
+
|
|
337
|
+
// Mark parent as changed
|
|
338
|
+
markChanged(state.parent.tracker)
|
|
287
339
|
}
|
|
288
340
|
}
|
|
289
341
|
|
|
290
342
|
// Check if all properties in the current state have reverted to original values
|
|
291
|
-
function checkIfReverted(
|
|
343
|
+
function checkIfReverted(
|
|
344
|
+
state: ChangeTracker<Record<string | symbol, unknown>>
|
|
345
|
+
): boolean {
|
|
292
346
|
debugLog(
|
|
293
347
|
`checkIfReverted called with assigned keys:`,
|
|
294
348
|
Object.keys(state.assigned_)
|
|
@@ -307,7 +361,7 @@ export function createChangeProxy<T extends object>(
|
|
|
307
361
|
for (const prop in state.assigned_) {
|
|
308
362
|
// If this property is marked as assigned
|
|
309
363
|
if (state.assigned_[prop] === true) {
|
|
310
|
-
const currentValue = state.copy_
|
|
364
|
+
const currentValue = state.copy_[prop]
|
|
311
365
|
const originalValue = (state.originalObject as any)[prop]
|
|
312
366
|
|
|
313
367
|
debugLog(
|
|
@@ -332,8 +386,8 @@ export function createChangeProxy<T extends object>(
|
|
|
332
386
|
// Check each assigned symbol property
|
|
333
387
|
const symbolProps = Object.getOwnPropertySymbols(state.assigned_)
|
|
334
388
|
for (const sym of symbolProps) {
|
|
335
|
-
if (state.assigned_[sym
|
|
336
|
-
const currentValue =
|
|
389
|
+
if (state.assigned_[sym] === true) {
|
|
390
|
+
const currentValue = (state.copy_ as any)[sym]
|
|
337
391
|
const originalValue = (state.originalObject as any)[sym]
|
|
338
392
|
|
|
339
393
|
// If the value is not equal to original, something is still changed
|
|
@@ -341,7 +395,7 @@ export function createChangeProxy<T extends object>(
|
|
|
341
395
|
debugLog(`Symbol property is different, returning false`)
|
|
342
396
|
return false
|
|
343
397
|
}
|
|
344
|
-
} else if (state.assigned_[sym
|
|
398
|
+
} else if (state.assigned_[sym] === false) {
|
|
345
399
|
// Property was deleted, so it's different from original
|
|
346
400
|
debugLog(`Symbol property was deleted, returning false`)
|
|
347
401
|
return false
|
|
@@ -353,50 +407,9 @@ export function createChangeProxy<T extends object>(
|
|
|
353
407
|
return true
|
|
354
408
|
}
|
|
355
409
|
|
|
356
|
-
// Recursively check and update modified status based on child objects
|
|
357
|
-
function updateModifiedStatus(state: ChangeTracker<object>): boolean {
|
|
358
|
-
debugLog(
|
|
359
|
-
`updateModifiedStatus called, assigned keys:`,
|
|
360
|
-
Object.keys(state.assigned_)
|
|
361
|
-
)
|
|
362
|
-
|
|
363
|
-
// Only check for reverts if we actually have changes
|
|
364
|
-
if (
|
|
365
|
-
Object.keys(state.assigned_).length === 0 &&
|
|
366
|
-
Object.getOwnPropertySymbols(state.assigned_).length === 0
|
|
367
|
-
) {
|
|
368
|
-
debugLog(`No assigned properties, returning false`)
|
|
369
|
-
return false
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// If this object has direct changes that aren't reverted, it's modified
|
|
373
|
-
const isReverted = checkIfReverted(state)
|
|
374
|
-
debugLog(`checkIfReverted returned:`, isReverted)
|
|
375
|
-
|
|
376
|
-
if (!isReverted) {
|
|
377
|
-
debugLog(`Object has changes that aren't reverted, returning true`)
|
|
378
|
-
return true
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
debugLog(`All changes reverted, clearing tracking`)
|
|
382
|
-
// All changes have been reverted, clear the tracking
|
|
383
|
-
state.modified = false
|
|
384
|
-
state.changes = {}
|
|
385
|
-
state.assigned_ = {}
|
|
386
|
-
|
|
387
|
-
// If we have a parent, update its status too
|
|
388
|
-
if (state.parent) {
|
|
389
|
-
debugLog(`Checking parent status for prop:`, state.parent.prop)
|
|
390
|
-
// Tell the parent this child has reverted
|
|
391
|
-
checkParentStatus(state.parent.tracker, state.parent.prop)
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return false
|
|
395
|
-
}
|
|
396
|
-
|
|
397
410
|
// Update parent status based on child changes
|
|
398
411
|
function checkParentStatus(
|
|
399
|
-
parentState: ChangeTracker<
|
|
412
|
+
parentState: ChangeTracker<Record<string | symbol, unknown>>,
|
|
400
413
|
childProp: string | symbol
|
|
401
414
|
) {
|
|
402
415
|
debugLog(`checkParentStatus called for child prop:`, childProp)
|
|
@@ -409,7 +422,6 @@ export function createChangeProxy<T extends object>(
|
|
|
409
422
|
debugLog(`Parent is fully reverted, clearing tracking`)
|
|
410
423
|
// If everything is reverted, clear the tracking
|
|
411
424
|
parentState.modified = false
|
|
412
|
-
parentState.changes = {}
|
|
413
425
|
parentState.assigned_ = {}
|
|
414
426
|
|
|
415
427
|
// Continue up the chain
|
|
@@ -422,15 +434,24 @@ export function createChangeProxy<T extends object>(
|
|
|
422
434
|
|
|
423
435
|
// Create a proxy for the target object
|
|
424
436
|
function createObjectProxy<TObj extends object>(obj: TObj): TObj {
|
|
437
|
+
debugLog(`createObjectProxy`, obj)
|
|
425
438
|
// If we've already created a proxy for this object, return it
|
|
426
439
|
if (proxyCache.has(obj)) {
|
|
440
|
+
debugLog(`proxyCache found match`)
|
|
427
441
|
return proxyCache.get(obj) as TObj
|
|
428
442
|
}
|
|
429
443
|
|
|
430
444
|
// Create a proxy for the object
|
|
431
445
|
const proxy = new Proxy(obj, {
|
|
432
446
|
get(ptarget, prop) {
|
|
433
|
-
|
|
447
|
+
debugLog(`get`, ptarget, prop)
|
|
448
|
+
const value =
|
|
449
|
+
changeTracker.copy_[prop as keyof T] ??
|
|
450
|
+
changeTracker.originalObject[prop as keyof T]
|
|
451
|
+
|
|
452
|
+
const originalValue = changeTracker.originalObject[prop as keyof T]
|
|
453
|
+
|
|
454
|
+
debugLog(`value (at top of proxy get)`, value)
|
|
434
455
|
|
|
435
456
|
// If it's a getter, return the value directly
|
|
436
457
|
const desc = Object.getOwnPropertyDescriptor(ptarget, prop)
|
|
@@ -459,7 +480,7 @@ export function createChangeProxy<T extends object>(
|
|
|
459
480
|
|
|
460
481
|
if (modifyingMethods.has(methodName)) {
|
|
461
482
|
return function (...args: Array<unknown>) {
|
|
462
|
-
const result = value.apply(
|
|
483
|
+
const result = value.apply(changeTracker.copy_, args)
|
|
463
484
|
markChanged(changeTracker)
|
|
464
485
|
return result
|
|
465
486
|
}
|
|
@@ -476,7 +497,7 @@ export function createChangeProxy<T extends object>(
|
|
|
476
497
|
|
|
477
498
|
if (iteratorMethods.has(methodName) || prop === Symbol.iterator) {
|
|
478
499
|
return function (this: unknown, ...args: Array<unknown>) {
|
|
479
|
-
const result = value.apply(
|
|
500
|
+
const result = value.apply(changeTracker.copy_, args)
|
|
480
501
|
|
|
481
502
|
// For forEach, we need to wrap the callback to track changes
|
|
482
503
|
if (methodName === `forEach`) {
|
|
@@ -544,16 +565,14 @@ export function createChangeProxy<T extends object>(
|
|
|
544
565
|
typeof nextResult.value[1] === `object`
|
|
545
566
|
) {
|
|
546
567
|
// Create a proxy for the value and replace it in the result
|
|
547
|
-
const { proxy: valueProxy } =
|
|
548
|
-
nextResult.value[1],
|
|
549
|
-
{
|
|
568
|
+
const { proxy: valueProxy } =
|
|
569
|
+
memoizedCreateChangeProxy(nextResult.value[1], {
|
|
550
570
|
tracker: changeTracker,
|
|
551
571
|
prop:
|
|
552
572
|
typeof nextResult.value[0] === `symbol`
|
|
553
573
|
? nextResult.value[0]
|
|
554
574
|
: String(nextResult.value[0]),
|
|
555
|
-
}
|
|
556
|
-
)
|
|
575
|
+
})
|
|
557
576
|
nextResult.value[1] = valueProxy
|
|
558
577
|
}
|
|
559
578
|
} else if (
|
|
@@ -570,13 +589,11 @@ export function createChangeProxy<T extends object>(
|
|
|
570
589
|
// For Map, we would need the key, but we don't have it here
|
|
571
590
|
// So we'll use a symbol as a placeholder
|
|
572
591
|
const tempKey = Symbol(`iterator-value`)
|
|
573
|
-
const { proxy: valueProxy } =
|
|
574
|
-
nextResult.value,
|
|
575
|
-
{
|
|
592
|
+
const { proxy: valueProxy } =
|
|
593
|
+
memoizedCreateChangeProxy(nextResult.value, {
|
|
576
594
|
tracker: changeTracker,
|
|
577
595
|
prop: tempKey,
|
|
578
|
-
}
|
|
579
|
-
)
|
|
596
|
+
})
|
|
580
597
|
nextResult.value = valueProxy
|
|
581
598
|
}
|
|
582
599
|
}
|
|
@@ -601,8 +618,8 @@ export function createChangeProxy<T extends object>(
|
|
|
601
618
|
if (
|
|
602
619
|
value &&
|
|
603
620
|
typeof value === `object` &&
|
|
604
|
-
!(value instanceof Date) &&
|
|
605
|
-
!(value instanceof RegExp)
|
|
621
|
+
!((value as any) instanceof Date) &&
|
|
622
|
+
!((value as any) instanceof RegExp)
|
|
606
623
|
) {
|
|
607
624
|
// Create a parent reference for the nested object
|
|
608
625
|
const nestedParent = {
|
|
@@ -611,7 +628,10 @@ export function createChangeProxy<T extends object>(
|
|
|
611
628
|
}
|
|
612
629
|
|
|
613
630
|
// Create a proxy for the nested object
|
|
614
|
-
const { proxy: nestedProxy } =
|
|
631
|
+
const { proxy: nestedProxy } = memoizedCreateChangeProxy(
|
|
632
|
+
originalValue,
|
|
633
|
+
nestedParent
|
|
634
|
+
)
|
|
615
635
|
|
|
616
636
|
// Cache the proxy
|
|
617
637
|
proxyCache.set(value, nestedProxy)
|
|
@@ -623,7 +643,7 @@ export function createChangeProxy<T extends object>(
|
|
|
623
643
|
},
|
|
624
644
|
|
|
625
645
|
set(sobj, prop, value) {
|
|
626
|
-
const currentValue =
|
|
646
|
+
const currentValue = changeTracker.copy_[prop as keyof T]
|
|
627
647
|
debugLog(
|
|
628
648
|
`set called for property ${String(prop)}, current:`,
|
|
629
649
|
currentValue,
|
|
@@ -631,28 +651,6 @@ export function createChangeProxy<T extends object>(
|
|
|
631
651
|
value
|
|
632
652
|
)
|
|
633
653
|
|
|
634
|
-
// Special handling for array length changes
|
|
635
|
-
if (Array.isArray(sobj) && prop === `length`) {
|
|
636
|
-
const newLength = Number(value)
|
|
637
|
-
const oldLength = sobj.length
|
|
638
|
-
|
|
639
|
-
// Create a new array with the desired length
|
|
640
|
-
const newArray = Array.from({ length: newLength }, (_, i) =>
|
|
641
|
-
i < oldLength ? sobj[i] : undefined
|
|
642
|
-
)
|
|
643
|
-
|
|
644
|
-
// Track the change in the parent object since 'arr' is the property name
|
|
645
|
-
if (parent) {
|
|
646
|
-
parent.tracker.changes[parent.prop] = newArray
|
|
647
|
-
parent.tracker.assigned_[parent.prop] = true
|
|
648
|
-
markChanged(parent.tracker)
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// Update the original array
|
|
652
|
-
sobj.length = newLength
|
|
653
|
-
return true
|
|
654
|
-
}
|
|
655
|
-
|
|
656
654
|
// Only track the change if the value is actually different
|
|
657
655
|
if (!deepEqual(currentValue, value)) {
|
|
658
656
|
// Check if the new value is equal to the original value
|
|
@@ -660,7 +658,9 @@ export function createChangeProxy<T extends object>(
|
|
|
660
658
|
const originalValue = changeTracker.originalObject[prop as keyof T]
|
|
661
659
|
const isRevertToOriginal = deepEqual(value, originalValue)
|
|
662
660
|
debugLog(
|
|
663
|
-
`
|
|
661
|
+
`value:`,
|
|
662
|
+
value,
|
|
663
|
+
`original:`,
|
|
664
664
|
originalValue,
|
|
665
665
|
`isRevertToOriginal:`,
|
|
666
666
|
isRevertToOriginal
|
|
@@ -669,14 +669,11 @@ export function createChangeProxy<T extends object>(
|
|
|
669
669
|
if (isRevertToOriginal) {
|
|
670
670
|
debugLog(`Reverting property ${String(prop)} to original value`)
|
|
671
671
|
// If the value is reverted to its original state, remove it from changes
|
|
672
|
-
delete changeTracker.changes[prop.toString()]
|
|
673
672
|
delete changeTracker.assigned_[prop.toString()]
|
|
674
673
|
|
|
675
674
|
// Make sure the copy is updated with the original value
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
changeTracker.copy_[prop as keyof T] = deepClone(originalValue)
|
|
679
|
-
}
|
|
675
|
+
debugLog(`Updating copy with original value for ${String(prop)}`)
|
|
676
|
+
changeTracker.copy_[prop as keyof T] = deepClone(originalValue)
|
|
680
677
|
|
|
681
678
|
// Check if all properties in this object have been reverted
|
|
682
679
|
debugLog(`Checking if all properties reverted`)
|
|
@@ -687,7 +684,6 @@ export function createChangeProxy<T extends object>(
|
|
|
687
684
|
debugLog(`All properties reverted, clearing tracking`)
|
|
688
685
|
// If all have been reverted, clear tracking
|
|
689
686
|
changeTracker.modified = false
|
|
690
|
-
changeTracker.changes = {}
|
|
691
687
|
changeTracker.assigned_ = {}
|
|
692
688
|
|
|
693
689
|
// If we're a nested object, check if the parent needs updating
|
|
@@ -702,25 +698,15 @@ export function createChangeProxy<T extends object>(
|
|
|
702
698
|
}
|
|
703
699
|
} else {
|
|
704
700
|
debugLog(`Setting new value for property ${String(prop)}`)
|
|
705
|
-
// Create a copy of the object if it doesn't exist
|
|
706
|
-
prepareCopy(changeTracker)
|
|
707
701
|
|
|
708
702
|
// Set the value on the copy
|
|
709
|
-
|
|
710
|
-
changeTracker.copy_[prop as keyof T] = value
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// Set the value on the original object
|
|
714
|
-
obj[prop as keyof TObj] = value
|
|
703
|
+
changeTracker.copy_[prop as keyof T] = value
|
|
715
704
|
|
|
716
705
|
// Track that this property was assigned - store using the actual property (symbol or string)
|
|
717
706
|
changeTracker.assigned_[prop.toString()] = true
|
|
718
707
|
|
|
719
|
-
// Track the change directly with the property as the key
|
|
720
|
-
changeTracker.changes[prop.toString()] = deepClone(value)
|
|
721
|
-
|
|
722
708
|
// Mark this object and its ancestors as modified
|
|
723
|
-
debugLog(`Marking object and ancestors as modified
|
|
709
|
+
debugLog(`Marking object and ancestors as modified`, changeTracker)
|
|
724
710
|
markChanged(changeTracker)
|
|
725
711
|
}
|
|
726
712
|
} else {
|
|
@@ -731,24 +717,24 @@ export function createChangeProxy<T extends object>(
|
|
|
731
717
|
},
|
|
732
718
|
|
|
733
719
|
defineProperty(ptarget, prop, descriptor) {
|
|
734
|
-
const result = Reflect.defineProperty(
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
720
|
+
// const result = Reflect.defineProperty(
|
|
721
|
+
// changeTracker.copy_,
|
|
722
|
+
// prop,
|
|
723
|
+
// descriptor
|
|
724
|
+
// )
|
|
725
|
+
// if (result) {
|
|
726
|
+
if (`value` in descriptor) {
|
|
727
|
+
changeTracker.copy_[prop as keyof T] = deepClone(descriptor.value)
|
|
728
|
+
changeTracker.assigned_[prop.toString()] = true
|
|
729
|
+
markChanged(changeTracker)
|
|
742
730
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
setPrototypeOf(ptarget, proto) {
|
|
747
|
-
// Allow setting prototype but don't track it as a change
|
|
748
|
-
return Object.setPrototypeOf(ptarget, proto)
|
|
731
|
+
// }
|
|
732
|
+
// return result
|
|
733
|
+
return true
|
|
749
734
|
},
|
|
750
735
|
|
|
751
736
|
deleteProperty(dobj, prop) {
|
|
737
|
+
debugLog(`deleteProperty`, dobj, prop)
|
|
752
738
|
const stringProp = typeof prop === `symbol` ? prop.toString() : prop
|
|
753
739
|
|
|
754
740
|
if (stringProp in dobj) {
|
|
@@ -756,25 +742,14 @@ export function createChangeProxy<T extends object>(
|
|
|
756
742
|
const hadPropertyInOriginal =
|
|
757
743
|
stringProp in changeTracker.originalObject
|
|
758
744
|
|
|
759
|
-
// Create a copy of the object if it doesn't exist
|
|
760
|
-
prepareCopy(changeTracker)
|
|
761
|
-
|
|
762
745
|
// Delete the property from the copy
|
|
763
|
-
if (changeTracker.copy_) {
|
|
764
|
-
// Use type assertion to tell TypeScript this is allowed
|
|
765
|
-
delete (changeTracker.copy_ as Record<string | symbol, unknown>)[
|
|
766
|
-
prop
|
|
767
|
-
]
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
// Delete the property from the original object
|
|
771
746
|
// Use type assertion to tell TypeScript this is allowed
|
|
772
|
-
delete (
|
|
747
|
+
delete (changeTracker.copy_ as Record<string | symbol, unknown>)[prop]
|
|
773
748
|
|
|
774
749
|
// If the property didn't exist in the original object, removing it
|
|
775
750
|
// should revert to the original state
|
|
776
751
|
if (!hadPropertyInOriginal) {
|
|
777
|
-
delete changeTracker.
|
|
752
|
+
delete changeTracker.copy_[stringProp]
|
|
778
753
|
delete changeTracker.assigned_[stringProp]
|
|
779
754
|
|
|
780
755
|
// If this is the last change and we're not a nested object,
|
|
@@ -791,7 +766,7 @@ export function createChangeProxy<T extends object>(
|
|
|
791
766
|
} else {
|
|
792
767
|
// Mark this property as deleted
|
|
793
768
|
changeTracker.assigned_[stringProp] = false
|
|
794
|
-
changeTracker.
|
|
769
|
+
changeTracker.copy_[stringProp as keyof T] = undefined as T[keyof T]
|
|
795
770
|
markChanged(changeTracker)
|
|
796
771
|
}
|
|
797
772
|
}
|
|
@@ -813,12 +788,8 @@ export function createChangeProxy<T extends object>(
|
|
|
813
788
|
return {
|
|
814
789
|
proxy,
|
|
815
790
|
getChanges: () => {
|
|
816
|
-
debugLog(
|
|
817
|
-
|
|
818
|
-
changeTracker.modified,
|
|
819
|
-
`assigned keys:`,
|
|
820
|
-
Object.keys(changeTracker.assigned_)
|
|
821
|
-
)
|
|
791
|
+
debugLog(`getChanges called, modified:`, changeTracker.modified)
|
|
792
|
+
debugLog(changeTracker)
|
|
822
793
|
|
|
823
794
|
// First, check if the object is still considered modified
|
|
824
795
|
if (!changeTracker.modified) {
|
|
@@ -826,181 +797,33 @@ export function createChangeProxy<T extends object>(
|
|
|
826
797
|
return {}
|
|
827
798
|
}
|
|
828
799
|
|
|
829
|
-
//
|
|
800
|
+
// If we have a copy, return it directly
|
|
801
|
+
// Check if valueObj is actually an object
|
|
830
802
|
if (
|
|
831
|
-
|
|
832
|
-
|
|
803
|
+
typeof changeTracker.copy_ !== `object` ||
|
|
804
|
+
Array.isArray(changeTracker.copy_)
|
|
833
805
|
) {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
// If there are no assigned properties but the object is still marked as modified,
|
|
837
|
-
// we should check deep equality with the original object
|
|
838
|
-
if (changeTracker.copy_) {
|
|
839
|
-
debugLog(`Comparing copy with original`)
|
|
840
|
-
if (deepEqual(changeTracker.copy_, changeTracker.originalObject)) {
|
|
841
|
-
debugLog(`Copy equals original, returning empty object`)
|
|
842
|
-
changeTracker.modified = false
|
|
843
|
-
return {}
|
|
844
|
-
}
|
|
845
|
-
} else if (deepEqual(target, changeTracker.originalObject)) {
|
|
846
|
-
debugLog(`Target equals original, returning empty object`)
|
|
847
|
-
changeTracker.modified = false
|
|
848
|
-
changeTracker.changes = {}
|
|
849
|
-
changeTracker.assigned_ = {}
|
|
850
|
-
return {}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
debugLog(`Forcing full check for reverted state`)
|
|
855
|
-
// Force a full check for reverted state, which will update the modified flag accordingly
|
|
856
|
-
updateModifiedStatus(changeTracker)
|
|
857
|
-
|
|
858
|
-
// If we're no longer modified after the check, return empty changes
|
|
859
|
-
// eslint-disable-next-line
|
|
860
|
-
if (!changeTracker.modified) {
|
|
861
|
-
debugLog(`No longer modified after check, returning empty object`)
|
|
862
|
-
return {}
|
|
806
|
+
return changeTracker.copy_
|
|
863
807
|
}
|
|
864
808
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
if (changeTracker.modified) {
|
|
868
|
-
const objToCheck = changeTracker.copy_ || target
|
|
869
|
-
debugLog(
|
|
870
|
-
`Checking if object is equal to original:`,
|
|
871
|
-
objToCheck,
|
|
872
|
-
changeTracker.originalObject
|
|
873
|
-
)
|
|
874
|
-
if (deepEqual(objToCheck, changeTracker.originalObject)) {
|
|
875
|
-
debugLog(`Object equals original, returning empty object`)
|
|
876
|
-
changeTracker.modified = false
|
|
877
|
-
changeTracker.changes = {}
|
|
878
|
-
changeTracker.assigned_ = {}
|
|
879
|
-
return {}
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
// If there are assigned properties, return the changes
|
|
884
|
-
if (
|
|
885
|
-
Object.keys(changeTracker.assigned_).length > 0 ||
|
|
886
|
-
Object.getOwnPropertySymbols(changeTracker.assigned_).length > 0
|
|
887
|
-
) {
|
|
888
|
-
// If we have a copy, use it to construct the changes
|
|
889
|
-
if (changeTracker.copy_) {
|
|
890
|
-
const changes: Record<string | symbol, unknown> = {}
|
|
891
|
-
|
|
892
|
-
// Add all assigned properties
|
|
893
|
-
for (const key in changeTracker.assigned_) {
|
|
894
|
-
if (changeTracker.assigned_[key] === true) {
|
|
895
|
-
// Property was assigned
|
|
896
|
-
changes[key] = deepClone(changeTracker.copy_[key as keyof T])
|
|
897
|
-
} else if (changeTracker.assigned_[key] === false) {
|
|
898
|
-
// Property was deleted
|
|
899
|
-
changes[key] = undefined
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
// Handle symbol properties - this needs special handling
|
|
904
|
-
const symbolProps = Object.getOwnPropertySymbols(
|
|
905
|
-
changeTracker.assigned_
|
|
906
|
-
)
|
|
907
|
-
for (const sym of symbolProps) {
|
|
908
|
-
if (changeTracker.assigned_[sym.toString()] === true) {
|
|
909
|
-
// Use the symbol directly instead of its string representation
|
|
910
|
-
|
|
911
|
-
const value = (changeTracker.copy_ as any)[sym]
|
|
912
|
-
|
|
913
|
-
changes[sym.toString()] = deepClone(value)
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
return changes
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
// Fall back to the existing changes object if no copy exists
|
|
921
|
-
return changeTracker.changes
|
|
809
|
+
if (Object.keys(changeTracker.assigned_).length === 0) {
|
|
810
|
+
return changeTracker.copy_
|
|
922
811
|
}
|
|
923
812
|
|
|
924
|
-
|
|
925
|
-
// but we're the root object, recursively check if unknown changes exist
|
|
926
|
-
// eslint-disable-next-line
|
|
927
|
-
if (changeTracker.modified && !parent) {
|
|
928
|
-
debugLog(`Root object with nested changes, checking deep equality`)
|
|
929
|
-
|
|
930
|
-
const currentState = changeTracker.copy_ || (target as any)
|
|
813
|
+
const result: Record<string, any | undefined> = {}
|
|
931
814
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
changeTracker.modified = false
|
|
941
|
-
return {}
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
// One more deep check - compare the actual values
|
|
945
|
-
// This is needed for the case where nested properties are modified and then reverted
|
|
946
|
-
debugLog(
|
|
947
|
-
`Comparing target with original:`,
|
|
948
|
-
target,
|
|
949
|
-
changeTracker.originalObject
|
|
950
|
-
)
|
|
951
|
-
if (deepEqual(target, changeTracker.originalObject)) {
|
|
952
|
-
debugLog(`Target equals original, returning empty object`)
|
|
953
|
-
changeTracker.modified = false
|
|
954
|
-
changeTracker.changes = {}
|
|
955
|
-
changeTracker.assigned_ = {}
|
|
956
|
-
return {}
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// Special case for nested object reverts
|
|
960
|
-
// If we're here, we need to check if the nested objects have been reverted
|
|
961
|
-
// even if the parent object still shows as modified
|
|
962
|
-
// eslint-disable-next-line
|
|
963
|
-
if (typeof target === `object` && target !== null) {
|
|
964
|
-
let allNestedReverted = true
|
|
965
|
-
|
|
966
|
-
// Check each property to see if it's been reverted to original
|
|
967
|
-
for (const key in target) {
|
|
968
|
-
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
|
969
|
-
const currentValue = target[key]
|
|
970
|
-
const originalValue = changeTracker.originalObject[key as keyof T]
|
|
971
|
-
|
|
972
|
-
// If this property is different from original, not all are reverted
|
|
973
|
-
if (!deepEqual(currentValue, originalValue)) {
|
|
974
|
-
allNestedReverted = false
|
|
975
|
-
break
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
// If all nested properties match original values, return empty changes
|
|
981
|
-
if (allNestedReverted) {
|
|
982
|
-
debugLog(
|
|
983
|
-
`All nested properties match original values, returning empty object`
|
|
984
|
-
)
|
|
985
|
-
changeTracker.modified = false
|
|
986
|
-
changeTracker.changes = {}
|
|
987
|
-
changeTracker.assigned_ = {}
|
|
988
|
-
return {}
|
|
989
|
-
}
|
|
815
|
+
// Iterate through keys in keyObj
|
|
816
|
+
for (const key in changeTracker.copy_) {
|
|
817
|
+
// If the key's value is true and the key exists in valueObj
|
|
818
|
+
if (
|
|
819
|
+
changeTracker.assigned_[key] === true &&
|
|
820
|
+
key in changeTracker.copy_
|
|
821
|
+
) {
|
|
822
|
+
result[key] = changeTracker.copy_[key]
|
|
990
823
|
}
|
|
991
|
-
|
|
992
|
-
debugLog(
|
|
993
|
-
`Changes detected, returning full object:`,
|
|
994
|
-
changeTracker.copy_ || target
|
|
995
|
-
)
|
|
996
|
-
// Convert the copy or target to a Record type before returning
|
|
997
|
-
const result = changeTracker.copy_ || target
|
|
998
|
-
return result as unknown as Record<string | symbol, unknown>
|
|
999
824
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
debugLog(`No changes detected, returning empty object`)
|
|
1003
|
-
return {}
|
|
825
|
+
debugLog(`Returning copy:`, result)
|
|
826
|
+
return result as unknown as Record<string | symbol, unknown>
|
|
1004
827
|
},
|
|
1005
828
|
}
|
|
1006
829
|
}
|
|
@@ -1062,43 +885,3 @@ export function withArrayChangeTracking<T extends object>(
|
|
|
1062
885
|
|
|
1063
886
|
return getChanges()
|
|
1064
887
|
}
|
|
1065
|
-
|
|
1066
|
-
/**
|
|
1067
|
-
* Creates a shallow copy of the target object if it doesn't already exist
|
|
1068
|
-
*/
|
|
1069
|
-
function prepareCopy<T extends object>(state: ChangeTracker<T>) {
|
|
1070
|
-
if (!state.copy_) {
|
|
1071
|
-
state.copy_ = shallowCopy(state.originalObject)
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
/**
|
|
1076
|
-
* Creates a shallow copy of an object
|
|
1077
|
-
*/
|
|
1078
|
-
function shallowCopy<T>(obj: T): T {
|
|
1079
|
-
if (Array.isArray(obj)) {
|
|
1080
|
-
return [...obj] as unknown as T
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
if (obj instanceof Map) {
|
|
1084
|
-
return new Map(obj) as unknown as T
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
if (obj instanceof Set) {
|
|
1088
|
-
return new Set(obj) as unknown as T
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
if (obj instanceof Date) {
|
|
1092
|
-
return new Date(obj.getTime()) as unknown as T
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
if (obj instanceof RegExp) {
|
|
1096
|
-
return new RegExp(obj.source, obj.flags) as unknown as T
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
if (obj !== null && typeof obj === `object`) {
|
|
1100
|
-
return { ...obj } as T
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
return obj
|
|
1104
|
-
}
|