@tanstack/db 0.5.17 → 0.5.19

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 (83) hide show
  1. package/dist/cjs/collection/changes.cjs +3 -0
  2. package/dist/cjs/collection/changes.cjs.map +1 -1
  3. package/dist/cjs/collection/sync.cjs +2 -1
  4. package/dist/cjs/collection/sync.cjs.map +1 -1
  5. package/dist/cjs/proxy.cjs +1 -1
  6. package/dist/cjs/proxy.cjs.map +1 -1
  7. package/dist/cjs/query/builder/index.cjs +5 -4
  8. package/dist/cjs/query/builder/index.cjs.map +1 -1
  9. package/dist/cjs/query/builder/index.d.cts +5 -4
  10. package/dist/cjs/query/builder/ref-proxy.cjs +63 -0
  11. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
  12. package/dist/cjs/query/builder/ref-proxy.d.cts +13 -0
  13. package/dist/cjs/query/builder/types.d.cts +26 -1
  14. package/dist/cjs/query/compiler/evaluators.cjs +29 -2
  15. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  16. package/dist/cjs/query/compiler/group-by.cjs +29 -12
  17. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  18. package/dist/cjs/query/compiler/group-by.d.cts +18 -2
  19. package/dist/cjs/query/compiler/index.cjs +5 -5
  20. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  21. package/dist/cjs/query/compiler/order-by.cjs +1 -1
  22. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  23. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  24. package/dist/cjs/query/compiler/select.cjs +1 -1
  25. package/dist/cjs/query/compiler/select.cjs.map +1 -1
  26. package/dist/cjs/query/compiler/select.d.cts +1 -1
  27. package/dist/cjs/query/live/collection-config-builder.cjs +26 -10
  28. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  29. package/dist/cjs/query/live/collection-subscriber.cjs +42 -33
  30. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  31. package/dist/cjs/query/live/internal.cjs +1 -1
  32. package/dist/cjs/query/live/internal.cjs.map +1 -1
  33. package/dist/cjs/query/live/types.d.cts +2 -1
  34. package/dist/cjs/types.d.cts +6 -0
  35. package/dist/esm/collection/changes.js +3 -0
  36. package/dist/esm/collection/changes.js.map +1 -1
  37. package/dist/esm/collection/sync.js +2 -1
  38. package/dist/esm/collection/sync.js.map +1 -1
  39. package/dist/esm/proxy.js +1 -1
  40. package/dist/esm/proxy.js.map +1 -1
  41. package/dist/esm/query/builder/index.d.ts +5 -4
  42. package/dist/esm/query/builder/index.js +6 -5
  43. package/dist/esm/query/builder/index.js.map +1 -1
  44. package/dist/esm/query/builder/ref-proxy.d.ts +13 -0
  45. package/dist/esm/query/builder/ref-proxy.js +63 -0
  46. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  47. package/dist/esm/query/builder/types.d.ts +26 -1
  48. package/dist/esm/query/compiler/evaluators.js +29 -2
  49. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  50. package/dist/esm/query/compiler/group-by.d.ts +18 -2
  51. package/dist/esm/query/compiler/group-by.js +30 -13
  52. package/dist/esm/query/compiler/group-by.js.map +1 -1
  53. package/dist/esm/query/compiler/index.js +5 -5
  54. package/dist/esm/query/compiler/index.js.map +1 -1
  55. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  56. package/dist/esm/query/compiler/order-by.js +1 -1
  57. package/dist/esm/query/compiler/order-by.js.map +1 -1
  58. package/dist/esm/query/compiler/select.d.ts +1 -1
  59. package/dist/esm/query/compiler/select.js +1 -1
  60. package/dist/esm/query/compiler/select.js.map +1 -1
  61. package/dist/esm/query/live/collection-config-builder.js +26 -10
  62. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  63. package/dist/esm/query/live/collection-subscriber.js +42 -33
  64. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  65. package/dist/esm/query/live/internal.js +1 -1
  66. package/dist/esm/query/live/internal.js.map +1 -1
  67. package/dist/esm/query/live/types.d.ts +2 -1
  68. package/dist/esm/types.d.ts +6 -0
  69. package/package.json +2 -2
  70. package/src/collection/changes.ts +7 -0
  71. package/src/collection/sync.ts +2 -2
  72. package/src/query/builder/index.ts +22 -6
  73. package/src/query/builder/ref-proxy.ts +90 -0
  74. package/src/query/builder/types.ts +26 -1
  75. package/src/query/compiler/evaluators.ts +38 -2
  76. package/src/query/compiler/group-by.ts +76 -22
  77. package/src/query/compiler/index.ts +13 -13
  78. package/src/query/compiler/order-by.ts +7 -6
  79. package/src/query/compiler/select.ts +5 -8
  80. package/src/query/live/collection-config-builder.ts +66 -16
  81. package/src/query/live/collection-subscriber.ts +60 -41
  82. package/src/query/live/types.ts +3 -1
  83. package/src/types.ts +6 -0
@@ -1 +1 @@
1
- {"version":3,"file":"collection-subscriber.cjs","sources":["../../../../src/query/live/collection-subscriber.ts"],"sourcesContent":["import { MultiSet } from '@tanstack/db-ivm'\nimport {\n normalizeExpressionPaths,\n normalizeOrderByPaths,\n} from '../compiler/expressions.js'\nimport type { MultiSetArray, RootStreamBuilder } from '@tanstack/db-ivm'\nimport type { Collection } from '../../collection/index.js'\nimport type { ChangeMessage } from '../../types.js'\nimport type { Context, GetResult } from '../builder/types.js'\nimport type { BasicExpression } from '../ir.js'\nimport type { OrderByOptimizationInfo } from '../compiler/order-by.js'\nimport type { CollectionConfigBuilder } from './collection-config-builder.js'\nimport type { CollectionSubscription } from '../../collection/subscription.js'\n\nconst loadMoreCallbackSymbol = Symbol.for(\n `@tanstack/db.collection-config-builder`,\n)\n\nexport class CollectionSubscriber<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n> {\n // Keep track of the biggest value we've sent so far (needed for orderBy optimization)\n private biggest: any = undefined\n\n // Track deferred promises for subscription loading states\n private subscriptionLoadingPromises = new Map<\n CollectionSubscription,\n { resolve: () => void }\n >()\n\n // Track keys that have been sent to the D2 pipeline to prevent duplicate inserts\n // This is necessary because different code paths (initial load, change events)\n // can potentially send the same item to D2 multiple times.\n private sentToD2Keys = new Set<string | number>()\n\n constructor(\n private alias: string,\n private collectionId: string,\n private collection: Collection,\n private collectionConfigBuilder: CollectionConfigBuilder<TContext, TResult>,\n ) {}\n\n subscribe(): CollectionSubscription {\n const whereClause = this.getWhereClauseForAlias()\n\n if (whereClause) {\n const whereExpression = normalizeExpressionPaths(whereClause, this.alias)\n return this.subscribeToChanges(whereExpression)\n }\n\n return this.subscribeToChanges()\n }\n\n private subscribeToChanges(whereExpression?: BasicExpression<boolean>) {\n let subscription: CollectionSubscription\n const orderByInfo = this.getOrderByInfo()\n if (orderByInfo) {\n subscription = this.subscribeToOrderedChanges(\n whereExpression,\n orderByInfo,\n )\n } else {\n // If the source alias is lazy then we should not include the initial state\n const includeInitialState = !this.collectionConfigBuilder.isLazyAlias(\n this.alias,\n )\n\n subscription = this.subscribeToMatchingChanges(\n whereExpression,\n includeInitialState,\n )\n }\n\n const trackLoadPromise = () => {\n // Guard against duplicate transitions\n if (!this.subscriptionLoadingPromises.has(subscription)) {\n let resolve: () => void\n const promise = new Promise<void>((res) => {\n resolve = res\n })\n\n this.subscriptionLoadingPromises.set(subscription, {\n resolve: resolve!,\n })\n this.collectionConfigBuilder.liveQueryCollection!._sync.trackLoadPromise(\n promise,\n )\n }\n }\n\n // It can be that we are not yet subscribed when the first `loadSubset` call happens (i.e. the initial query).\n // So we also check the status here and if it's `loadingSubset` then we track the load promise\n if (subscription.status === `loadingSubset`) {\n trackLoadPromise()\n }\n\n // Subscribe to subscription status changes to propagate loading state\n const statusUnsubscribe = subscription.on(`status:change`, (event) => {\n if (event.status === `loadingSubset`) {\n trackLoadPromise()\n } else {\n // status is 'ready'\n const deferred = this.subscriptionLoadingPromises.get(subscription)\n if (deferred) {\n // Clear the map entry FIRST (before resolving)\n this.subscriptionLoadingPromises.delete(subscription)\n deferred.resolve()\n }\n }\n })\n\n const unsubscribe = () => {\n // If subscription has a pending promise, resolve it before unsubscribing\n const deferred = this.subscriptionLoadingPromises.get(subscription)\n if (deferred) {\n // Clear the map entry FIRST (before resolving)\n this.subscriptionLoadingPromises.delete(subscription)\n deferred.resolve()\n }\n\n statusUnsubscribe()\n subscription.unsubscribe()\n }\n // currentSyncState is always defined when subscribe() is called\n // (called during sync session setup)\n this.collectionConfigBuilder.currentSyncState!.unsubscribeCallbacks.add(\n unsubscribe,\n )\n return subscription\n }\n\n private sendChangesToPipeline(\n changes: Iterable<ChangeMessage<any, string | number>>,\n callback?: () => boolean,\n ) {\n // Filter changes to prevent duplicate inserts to D2 pipeline.\n // This ensures D2 multiplicity stays at 1 for visible items, so deletes\n // properly reduce multiplicity to 0 (triggering DELETE output).\n const changesArray = Array.isArray(changes) ? changes : [...changes]\n const filteredChanges: Array<ChangeMessage<any, string | number>> = []\n for (const change of changesArray) {\n if (change.type === `insert`) {\n if (this.sentToD2Keys.has(change.key)) {\n // Skip duplicate insert - already sent to D2\n continue\n }\n this.sentToD2Keys.add(change.key)\n } else if (change.type === `delete`) {\n // Remove from tracking so future re-inserts are allowed\n this.sentToD2Keys.delete(change.key)\n }\n // Updates are handled as delete+insert by splitUpdates, so no special handling needed\n filteredChanges.push(change)\n }\n\n // currentSyncState and input are always defined when this method is called\n // (only called from active subscriptions during a sync session)\n const input =\n this.collectionConfigBuilder.currentSyncState!.inputs[this.alias]!\n const sentChanges = sendChangesToInput(\n input,\n filteredChanges,\n this.collection.config.getKey,\n )\n\n // Do not provide the callback that loads more data\n // if there's no more data to load\n // otherwise we end up in an infinite loop trying to load more data\n const dataLoader = sentChanges > 0 ? callback : undefined\n\n // We need to schedule a graph run even if there's no data to load\n // because we need to mark the collection as ready if it's not already\n // and that's only done in `scheduleGraphRun`\n this.collectionConfigBuilder.scheduleGraphRun(dataLoader, {\n alias: this.alias,\n })\n }\n\n private subscribeToMatchingChanges(\n whereExpression: BasicExpression<boolean> | undefined,\n includeInitialState: boolean = false,\n ) {\n const sendChanges = (\n changes: Array<ChangeMessage<any, string | number>>,\n ) => {\n this.sendChangesToPipeline(changes)\n }\n\n // Only pass includeInitialState when true. When it's false, we leave it\n // undefined so that user subscriptions with explicit `includeInitialState: false`\n // can be distinguished from internal lazy-loading subscriptions.\n // If we pass `false`, changes.ts would call markAllStateAsSeen() which\n // disables filtering - but internal subscriptions still need filtering.\n const subscription = this.collection.subscribeChanges(sendChanges, {\n ...(includeInitialState && { includeInitialState }),\n whereExpression,\n })\n\n return subscription\n }\n\n private subscribeToOrderedChanges(\n whereExpression: BasicExpression<boolean> | undefined,\n orderByInfo: OrderByOptimizationInfo,\n ) {\n const { orderBy, offset, limit, index } = orderByInfo\n\n const sendChangesInRange = (\n changes: Iterable<ChangeMessage<any, string | number>>,\n ) => {\n // Split live updates into a delete of the old value and an insert of the new value\n const splittedChanges = splitUpdates(changes)\n this.sendChangesToPipelineWithTracking(splittedChanges, subscription)\n }\n\n // Subscribe to changes and only send changes that are smaller than the biggest value we've sent so far\n // values that are bigger don't need to be sent because they can't affect the topK\n const subscription = this.collection.subscribeChanges(sendChangesInRange, {\n whereExpression,\n })\n\n // Listen for truncate events to reset cursor tracking state and sentToD2Keys\n // This ensures that after a must-refetch/truncate, we don't use stale cursor data\n // and allow re-inserts of previously sent keys\n const truncateUnsubscribe = this.collection.on(`truncate`, () => {\n this.biggest = undefined\n this.sentToD2Keys.clear()\n })\n\n // Clean up truncate listener when subscription is unsubscribed\n subscription.on(`unsubscribed`, () => {\n truncateUnsubscribe()\n })\n\n // Normalize the orderBy clauses such that the references are relative to the collection\n const normalizedOrderBy = normalizeOrderByPaths(orderBy, this.alias)\n\n if (index) {\n // We have an index on the first orderBy column - use lazy loading optimization\n // This works for both single-column and multi-column orderBy:\n // - Single-column: index provides exact ordering\n // - Multi-column: index provides ordering on first column, secondary sort in memory\n subscription.setOrderByIndex(index)\n\n // Load the first `offset + limit` values from the index\n // i.e. the K items from the collection that fall into the requested range: [offset, offset + limit[\n subscription.requestLimitedSnapshot({\n limit: offset + limit,\n orderBy: normalizedOrderBy,\n })\n } else {\n // No index available (e.g., non-ref expression): pass orderBy/limit to loadSubset\n // so the sync layer can optimize if the backend supports it\n subscription.requestSnapshot({\n orderBy: normalizedOrderBy,\n limit: offset + limit,\n })\n }\n\n return subscription\n }\n\n // This function is called by maybeRunGraph\n // after each iteration of the query pipeline\n // to ensure that the orderBy operator has enough data to work with\n loadMoreIfNeeded(subscription: CollectionSubscription) {\n const orderByInfo = this.getOrderByInfo()\n\n if (!orderByInfo) {\n // This query has no orderBy operator\n // so there's no data to load\n return true\n }\n\n const { dataNeeded } = orderByInfo\n\n if (!dataNeeded) {\n // dataNeeded is not set when there's no index (e.g., non-ref expression).\n // In this case, we've already loaded all data via requestSnapshot\n // and don't need to lazily load more.\n return true\n }\n\n // `dataNeeded` probes the orderBy operator to see if it needs more data\n // if it needs more data, it returns the number of items it needs\n const n = dataNeeded()\n if (n > 0) {\n this.loadNextItems(n, subscription)\n }\n return true\n }\n\n private sendChangesToPipelineWithTracking(\n changes: Iterable<ChangeMessage<any, string | number>>,\n subscription: CollectionSubscription,\n ) {\n const orderByInfo = this.getOrderByInfo()\n if (!orderByInfo) {\n this.sendChangesToPipeline(changes)\n return\n }\n\n const trackedChanges = this.trackSentValues(changes, orderByInfo.comparator)\n\n // Cache the loadMoreIfNeeded callback on the subscription using a symbol property.\n // This ensures we pass the same function instance to the scheduler each time,\n // allowing it to deduplicate callbacks when multiple changes arrive during a transaction.\n type SubscriptionWithLoader = CollectionSubscription & {\n [loadMoreCallbackSymbol]?: () => boolean\n }\n\n const subscriptionWithLoader = subscription as SubscriptionWithLoader\n\n subscriptionWithLoader[loadMoreCallbackSymbol] ??=\n this.loadMoreIfNeeded.bind(this, subscription)\n\n this.sendChangesToPipeline(\n trackedChanges,\n subscriptionWithLoader[loadMoreCallbackSymbol],\n )\n }\n\n // Loads the next `n` items from the collection\n // starting from the biggest item it has sent\n private loadNextItems(n: number, subscription: CollectionSubscription) {\n const orderByInfo = this.getOrderByInfo()\n if (!orderByInfo) {\n return\n }\n const { orderBy, valueExtractorForRawRow, offset } = orderByInfo\n const biggestSentRow = this.biggest\n\n // Extract all orderBy column values from the biggest sent row\n // For single-column: returns single value, for multi-column: returns array\n const extractedValues = biggestSentRow\n ? valueExtractorForRawRow(biggestSentRow)\n : undefined\n\n // Normalize to array format for minValues\n const minValues =\n extractedValues !== undefined\n ? Array.isArray(extractedValues)\n ? extractedValues\n : [extractedValues]\n : undefined\n\n // Normalize the orderBy clauses such that the references are relative to the collection\n const normalizedOrderBy = normalizeOrderByPaths(orderBy, this.alias)\n\n // Take the `n` items after the biggest sent value\n // Pass the current window offset to ensure proper deduplication\n subscription.requestLimitedSnapshot({\n orderBy: normalizedOrderBy,\n limit: n,\n minValues,\n offset,\n })\n }\n\n private getWhereClauseForAlias(): BasicExpression<boolean> | undefined {\n const sourceWhereClausesCache =\n this.collectionConfigBuilder.sourceWhereClausesCache\n if (!sourceWhereClausesCache) {\n return undefined\n }\n return sourceWhereClausesCache.get(this.alias)\n }\n\n private getOrderByInfo(): OrderByOptimizationInfo | undefined {\n const info =\n this.collectionConfigBuilder.optimizableOrderByCollections[\n this.collectionId\n ]\n if (info && info.alias === this.alias) {\n return info\n }\n return undefined\n }\n\n private *trackSentValues(\n changes: Iterable<ChangeMessage<any, string | number>>,\n comparator: (a: any, b: any) => number,\n ) {\n for (const change of changes) {\n // Only track inserts/updates for cursor positioning, not deletes\n if (change.type !== `delete`) {\n if (!this.biggest) {\n this.biggest = change.value\n } else if (comparator(this.biggest, change.value) < 0) {\n this.biggest = change.value\n }\n }\n\n yield change\n }\n }\n}\n\n/**\n * Helper function to send changes to a D2 input stream\n */\nfunction sendChangesToInput(\n input: RootStreamBuilder<unknown>,\n changes: Iterable<ChangeMessage>,\n getKey: (item: ChangeMessage[`value`]) => any,\n): number {\n const multiSetArray: MultiSetArray<unknown> = []\n for (const change of changes) {\n const key = getKey(change.value)\n if (change.type === `insert`) {\n multiSetArray.push([[key, change.value], 1])\n } else if (change.type === `update`) {\n multiSetArray.push([[key, change.previousValue], -1])\n multiSetArray.push([[key, change.value], 1])\n } else {\n // change.type === `delete`\n multiSetArray.push([[key, change.value], -1])\n }\n }\n\n if (multiSetArray.length !== 0) {\n input.sendData(new MultiSet(multiSetArray))\n }\n\n return multiSetArray.length\n}\n\n/** Splits updates into a delete of the old value and an insert of the new value */\nfunction* splitUpdates<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n>(\n changes: Iterable<ChangeMessage<T, TKey>>,\n): Generator<ChangeMessage<T, TKey>> {\n for (const change of changes) {\n if (change.type === `update`) {\n yield { type: `delete`, key: change.key, value: change.previousValue! }\n yield { type: `insert`, key: change.key, value: change.value }\n } else {\n yield change\n }\n }\n}\n"],"names":["normalizeExpressionPaths","normalizeOrderByPaths","MultiSet"],"mappings":"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"}
1
+ {"version":3,"file":"collection-subscriber.cjs","sources":["../../../../src/query/live/collection-subscriber.ts"],"sourcesContent":["import { MultiSet } from '@tanstack/db-ivm'\nimport {\n normalizeExpressionPaths,\n normalizeOrderByPaths,\n} from '../compiler/expressions.js'\nimport type { MultiSetArray, RootStreamBuilder } from '@tanstack/db-ivm'\nimport type { Collection } from '../../collection/index.js'\nimport type {\n ChangeMessage,\n SubscriptionStatusChangeEvent,\n} from '../../types.js'\nimport type { Context, GetResult } from '../builder/types.js'\nimport type { BasicExpression } from '../ir.js'\nimport type { OrderByOptimizationInfo } from '../compiler/order-by.js'\nimport type { CollectionConfigBuilder } from './collection-config-builder.js'\nimport type { CollectionSubscription } from '../../collection/subscription.js'\n\nconst loadMoreCallbackSymbol = Symbol.for(\n `@tanstack/db.collection-config-builder`,\n)\n\nexport class CollectionSubscriber<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n> {\n // Keep track of the biggest value we've sent so far (needed for orderBy optimization)\n private biggest: any = undefined\n\n // Track deferred promises for subscription loading states\n private subscriptionLoadingPromises = new Map<\n CollectionSubscription,\n { resolve: () => void }\n >()\n\n // Track keys that have been sent to the D2 pipeline to prevent duplicate inserts\n // This is necessary because different code paths (initial load, change events)\n // can potentially send the same item to D2 multiple times.\n private sentToD2Keys = new Set<string | number>()\n\n constructor(\n private alias: string,\n private collectionId: string,\n private collection: Collection,\n private collectionConfigBuilder: CollectionConfigBuilder<TContext, TResult>,\n ) {}\n\n subscribe(): CollectionSubscription {\n const whereClause = this.getWhereClauseForAlias()\n\n if (whereClause) {\n const whereExpression = normalizeExpressionPaths(whereClause, this.alias)\n return this.subscribeToChanges(whereExpression)\n }\n\n return this.subscribeToChanges()\n }\n\n private subscribeToChanges(whereExpression?: BasicExpression<boolean>) {\n const orderByInfo = this.getOrderByInfo()\n\n // Track load promises using subscription from the event (avoids circular dependency)\n const trackLoadPromise = (subscription: CollectionSubscription) => {\n // Guard against duplicate transitions\n if (!this.subscriptionLoadingPromises.has(subscription)) {\n let resolve: () => void\n const promise = new Promise<void>((res) => {\n resolve = res\n })\n\n this.subscriptionLoadingPromises.set(subscription, {\n resolve: resolve!,\n })\n this.collectionConfigBuilder.liveQueryCollection!._sync.trackLoadPromise(\n promise,\n )\n }\n }\n\n // Status change handler - passed to subscribeChanges so it's registered\n // BEFORE any snapshot is requested, preventing race conditions\n const onStatusChange = (event: SubscriptionStatusChangeEvent) => {\n const subscription = event.subscription as CollectionSubscription\n if (event.status === `loadingSubset`) {\n trackLoadPromise(subscription)\n } else {\n // status is 'ready'\n const deferred = this.subscriptionLoadingPromises.get(subscription)\n if (deferred) {\n // Clear the map entry FIRST (before resolving)\n this.subscriptionLoadingPromises.delete(subscription)\n deferred.resolve()\n }\n }\n }\n\n // Create subscription with onStatusChange - listener is registered before any async work\n let subscription: CollectionSubscription\n if (orderByInfo) {\n subscription = this.subscribeToOrderedChanges(\n whereExpression,\n orderByInfo,\n onStatusChange,\n )\n } else {\n // If the source alias is lazy then we should not include the initial state\n const includeInitialState = !this.collectionConfigBuilder.isLazyAlias(\n this.alias,\n )\n\n subscription = this.subscribeToMatchingChanges(\n whereExpression,\n includeInitialState,\n onStatusChange,\n )\n }\n\n // Check current status after subscribing - if status is 'loadingSubset', track it.\n // The onStatusChange listener will catch the transition to 'ready'.\n if (subscription.status === `loadingSubset`) {\n trackLoadPromise(subscription)\n }\n\n const unsubscribe = () => {\n // If subscription has a pending promise, resolve it before unsubscribing\n const deferred = this.subscriptionLoadingPromises.get(subscription)\n if (deferred) {\n // Clear the map entry FIRST (before resolving)\n this.subscriptionLoadingPromises.delete(subscription)\n deferred.resolve()\n }\n\n subscription.unsubscribe()\n }\n // currentSyncState is always defined when subscribe() is called\n // (called during sync session setup)\n this.collectionConfigBuilder.currentSyncState!.unsubscribeCallbacks.add(\n unsubscribe,\n )\n return subscription\n }\n\n private sendChangesToPipeline(\n changes: Iterable<ChangeMessage<any, string | number>>,\n callback?: () => boolean,\n ) {\n // Filter changes to prevent duplicate inserts to D2 pipeline.\n // This ensures D2 multiplicity stays at 1 for visible items, so deletes\n // properly reduce multiplicity to 0 (triggering DELETE output).\n const changesArray = Array.isArray(changes) ? changes : [...changes]\n const filteredChanges: Array<ChangeMessage<any, string | number>> = []\n for (const change of changesArray) {\n if (change.type === `insert`) {\n if (this.sentToD2Keys.has(change.key)) {\n // Skip duplicate insert - already sent to D2\n continue\n }\n this.sentToD2Keys.add(change.key)\n } else if (change.type === `delete`) {\n // Remove from tracking so future re-inserts are allowed\n this.sentToD2Keys.delete(change.key)\n }\n // Updates are handled as delete+insert by splitUpdates, so no special handling needed\n filteredChanges.push(change)\n }\n\n // currentSyncState and input are always defined when this method is called\n // (only called from active subscriptions during a sync session)\n const input =\n this.collectionConfigBuilder.currentSyncState!.inputs[this.alias]!\n const sentChanges = sendChangesToInput(\n input,\n filteredChanges,\n this.collection.config.getKey,\n )\n\n // Do not provide the callback that loads more data\n // if there's no more data to load\n // otherwise we end up in an infinite loop trying to load more data\n const dataLoader = sentChanges > 0 ? callback : undefined\n\n // We need to schedule a graph run even if there's no data to load\n // because we need to mark the collection as ready if it's not already\n // and that's only done in `scheduleGraphRun`\n this.collectionConfigBuilder.scheduleGraphRun(dataLoader, {\n alias: this.alias,\n })\n }\n\n private subscribeToMatchingChanges(\n whereExpression: BasicExpression<boolean> | undefined,\n includeInitialState: boolean,\n onStatusChange: (event: SubscriptionStatusChangeEvent) => void,\n ): CollectionSubscription {\n const sendChanges = (\n changes: Array<ChangeMessage<any, string | number>>,\n ) => {\n this.sendChangesToPipeline(changes)\n }\n\n // Create subscription with onStatusChange - listener is registered before snapshot\n // Note: For non-ordered queries (no limit/offset), we use trackLoadSubsetPromise: false\n // which is the default behavior in subscribeChanges\n const subscription = this.collection.subscribeChanges(sendChanges, {\n ...(includeInitialState && { includeInitialState }),\n whereExpression,\n onStatusChange,\n })\n\n return subscription\n }\n\n private subscribeToOrderedChanges(\n whereExpression: BasicExpression<boolean> | undefined,\n orderByInfo: OrderByOptimizationInfo,\n onStatusChange: (event: SubscriptionStatusChangeEvent) => void,\n ): CollectionSubscription {\n const { orderBy, offset, limit, index } = orderByInfo\n\n // Use a holder to forward-reference subscription in the callback\n const subscriptionHolder: { current?: CollectionSubscription } = {}\n\n const sendChangesInRange = (\n changes: Iterable<ChangeMessage<any, string | number>>,\n ) => {\n // Split live updates into a delete of the old value and an insert of the new value\n const splittedChanges = splitUpdates(changes)\n this.sendChangesToPipelineWithTracking(\n splittedChanges,\n subscriptionHolder.current!,\n )\n }\n\n // Subscribe to changes with onStatusChange - listener is registered before any snapshot\n // values bigger than what we've sent don't need to be sent because they can't affect the topK\n const subscription = this.collection.subscribeChanges(sendChangesInRange, {\n whereExpression,\n onStatusChange,\n })\n subscriptionHolder.current = subscription\n\n // Listen for truncate events to reset cursor tracking state and sentToD2Keys\n // This ensures that after a must-refetch/truncate, we don't use stale cursor data\n // and allow re-inserts of previously sent keys\n const truncateUnsubscribe = this.collection.on(`truncate`, () => {\n this.biggest = undefined\n this.sentToD2Keys.clear()\n })\n\n // Clean up truncate listener when subscription is unsubscribed\n subscription.on(`unsubscribed`, () => {\n truncateUnsubscribe()\n })\n\n // Normalize the orderBy clauses such that the references are relative to the collection\n const normalizedOrderBy = normalizeOrderByPaths(orderBy, this.alias)\n\n // Trigger the snapshot request - onStatusChange listener is already registered\n if (index) {\n // We have an index on the first orderBy column - use lazy loading optimization\n // This works for both single-column and multi-column orderBy:\n // - Single-column: index provides exact ordering\n // - Multi-column: index provides ordering on first column, secondary sort in memory\n subscription.setOrderByIndex(index)\n\n // Load the first `offset + limit` values from the index\n // i.e. the K items from the collection that fall into the requested range: [offset, offset + limit[\n subscription.requestLimitedSnapshot({\n limit: offset + limit,\n orderBy: normalizedOrderBy,\n })\n } else {\n // No index available (e.g., non-ref expression): pass orderBy/limit to loadSubset\n // so the sync layer can optimize if the backend supports it\n subscription.requestSnapshot({\n orderBy: normalizedOrderBy,\n limit: offset + limit,\n })\n }\n\n return subscription\n }\n\n // This function is called by maybeRunGraph\n // after each iteration of the query pipeline\n // to ensure that the orderBy operator has enough data to work with\n loadMoreIfNeeded(subscription: CollectionSubscription) {\n const orderByInfo = this.getOrderByInfo()\n\n if (!orderByInfo) {\n // This query has no orderBy operator\n // so there's no data to load\n return true\n }\n\n const { dataNeeded } = orderByInfo\n\n if (!dataNeeded) {\n // dataNeeded is not set when there's no index (e.g., non-ref expression).\n // In this case, we've already loaded all data via requestSnapshot\n // and don't need to lazily load more.\n return true\n }\n\n // `dataNeeded` probes the orderBy operator to see if it needs more data\n // if it needs more data, it returns the number of items it needs\n const n = dataNeeded()\n if (n > 0) {\n this.loadNextItems(n, subscription)\n }\n return true\n }\n\n private sendChangesToPipelineWithTracking(\n changes: Iterable<ChangeMessage<any, string | number>>,\n subscription: CollectionSubscription,\n ) {\n const orderByInfo = this.getOrderByInfo()\n if (!orderByInfo) {\n this.sendChangesToPipeline(changes)\n return\n }\n\n const trackedChanges = this.trackSentValues(changes, orderByInfo.comparator)\n\n // Cache the loadMoreIfNeeded callback on the subscription using a symbol property.\n // This ensures we pass the same function instance to the scheduler each time,\n // allowing it to deduplicate callbacks when multiple changes arrive during a transaction.\n type SubscriptionWithLoader = CollectionSubscription & {\n [loadMoreCallbackSymbol]?: () => boolean\n }\n\n const subscriptionWithLoader = subscription as SubscriptionWithLoader\n\n subscriptionWithLoader[loadMoreCallbackSymbol] ??=\n this.loadMoreIfNeeded.bind(this, subscription)\n\n this.sendChangesToPipeline(\n trackedChanges,\n subscriptionWithLoader[loadMoreCallbackSymbol],\n )\n }\n\n // Loads the next `n` items from the collection\n // starting from the biggest item it has sent\n private loadNextItems(n: number, subscription: CollectionSubscription) {\n const orderByInfo = this.getOrderByInfo()\n if (!orderByInfo) {\n return\n }\n const { orderBy, valueExtractorForRawRow, offset } = orderByInfo\n const biggestSentRow = this.biggest\n\n // Extract all orderBy column values from the biggest sent row\n // For single-column: returns single value, for multi-column: returns array\n const extractedValues = biggestSentRow\n ? valueExtractorForRawRow(biggestSentRow)\n : undefined\n\n // Normalize to array format for minValues\n const minValues =\n extractedValues !== undefined\n ? Array.isArray(extractedValues)\n ? extractedValues\n : [extractedValues]\n : undefined\n\n // Normalize the orderBy clauses such that the references are relative to the collection\n const normalizedOrderBy = normalizeOrderByPaths(orderBy, this.alias)\n\n // Take the `n` items after the biggest sent value\n // Pass the current window offset to ensure proper deduplication\n subscription.requestLimitedSnapshot({\n orderBy: normalizedOrderBy,\n limit: n,\n minValues,\n offset,\n })\n }\n\n private getWhereClauseForAlias(): BasicExpression<boolean> | undefined {\n const sourceWhereClausesCache =\n this.collectionConfigBuilder.sourceWhereClausesCache\n if (!sourceWhereClausesCache) {\n return undefined\n }\n return sourceWhereClausesCache.get(this.alias)\n }\n\n private getOrderByInfo(): OrderByOptimizationInfo | undefined {\n const info =\n this.collectionConfigBuilder.optimizableOrderByCollections[\n this.collectionId\n ]\n if (info && info.alias === this.alias) {\n return info\n }\n return undefined\n }\n\n private *trackSentValues(\n changes: Iterable<ChangeMessage<any, string | number>>,\n comparator: (a: any, b: any) => number,\n ) {\n for (const change of changes) {\n // Only track inserts/updates for cursor positioning, not deletes\n if (change.type !== `delete`) {\n if (!this.biggest) {\n this.biggest = change.value\n } else if (comparator(this.biggest, change.value) < 0) {\n this.biggest = change.value\n }\n }\n\n yield change\n }\n }\n}\n\n/**\n * Helper function to send changes to a D2 input stream\n */\nfunction sendChangesToInput(\n input: RootStreamBuilder<unknown>,\n changes: Iterable<ChangeMessage>,\n getKey: (item: ChangeMessage[`value`]) => any,\n): number {\n const multiSetArray: MultiSetArray<unknown> = []\n for (const change of changes) {\n const key = getKey(change.value)\n if (change.type === `insert`) {\n multiSetArray.push([[key, change.value], 1])\n } else if (change.type === `update`) {\n multiSetArray.push([[key, change.previousValue], -1])\n multiSetArray.push([[key, change.value], 1])\n } else {\n // change.type === `delete`\n multiSetArray.push([[key, change.value], -1])\n }\n }\n\n if (multiSetArray.length !== 0) {\n input.sendData(new MultiSet(multiSetArray))\n }\n\n return multiSetArray.length\n}\n\n/** Splits updates into a delete of the old value and an insert of the new value */\nfunction* splitUpdates<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n>(\n changes: Iterable<ChangeMessage<T, TKey>>,\n): Generator<ChangeMessage<T, TKey>> {\n for (const change of changes) {\n if (change.type === `update`) {\n yield { type: `delete`, key: change.key, value: change.previousValue! }\n yield { type: `insert`, key: change.key, value: change.value }\n } else {\n yield change\n }\n }\n}\n"],"names":["normalizeExpressionPaths","subscription","normalizeOrderByPaths","MultiSet"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const LIVE_QUERY_INTERNAL = Symbol(`liveQueryInternal`);
3
+ const LIVE_QUERY_INTERNAL = /* @__PURE__ */ Symbol(`liveQueryInternal`);
4
4
  exports.LIVE_QUERY_INTERNAL = LIVE_QUERY_INTERNAL;
5
5
  //# sourceMappingURL=internal.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.cjs","sources":["../../../../src/query/live/internal.ts"],"sourcesContent":["import type { CollectionConfigBuilder } from './collection-config-builder.js'\n\n/**\n * Symbol for accessing internal utilities that should not be part of the public API\n */\nexport const LIVE_QUERY_INTERNAL = Symbol(`liveQueryInternal`)\n\n/**\n * Internal utilities for live queries, accessible via Symbol\n */\nexport type LiveQueryInternalUtils = {\n getBuilder: () => CollectionConfigBuilder<any, any>\n hasCustomGetKey: boolean\n hasJoins: boolean\n}\n"],"names":[],"mappings":";;AAKO,MAAM,sBAAsB,OAAO,mBAAmB;;"}
1
+ {"version":3,"file":"internal.cjs","sources":["../../../../src/query/live/internal.ts"],"sourcesContent":["import type { CollectionConfigBuilder } from './collection-config-builder.js'\n\n/**\n * Symbol for accessing internal utilities that should not be part of the public API\n */\nexport const LIVE_QUERY_INTERNAL = Symbol(`liveQueryInternal`)\n\n/**\n * Internal utilities for live queries, accessible via Symbol\n */\nexport type LiveQueryInternalUtils = {\n getBuilder: () => CollectionConfigBuilder<any, any>\n hasCustomGetKey: boolean\n hasJoins: boolean\n}\n"],"names":[],"mappings":";;AAKO,MAAM,6CAA6B,mBAAmB;;"}
@@ -15,8 +15,9 @@ export type SyncState = {
15
15
  graph?: D2;
16
16
  inputs?: Record<string, RootStreamBuilder<unknown>>;
17
17
  pipeline?: ResultStream;
18
+ flushPendingChanges?: () => void;
18
19
  };
19
- export type FullSyncState = Required<SyncState>;
20
+ export type FullSyncState = Required<Omit<SyncState, `flushPendingChanges`>> & Pick<SyncState, `flushPendingChanges`>;
20
21
  /**
21
22
  * Configuration interface for live query collection options
22
23
  *
@@ -615,6 +615,12 @@ export interface SubscribeChangesOptions<T extends object = Record<string, unkno
615
615
  where?: (row: SingleRowRefProxy<T>) => any;
616
616
  /** Pre-compiled expression for filtering changes */
617
617
  whereExpression?: BasicExpression<boolean>;
618
+ /**
619
+ * Listener for subscription status changes.
620
+ * Registered BEFORE any snapshot is requested, ensuring no status transitions are missed.
621
+ * @internal
622
+ */
623
+ onStatusChange?: (event: SubscriptionStatusChangeEvent) => void;
618
624
  }
619
625
  export interface SubscribeChangesSnapshotOptions<T extends object = Record<string, unknown>> extends Omit<SubscribeChangesOptions<T>, `includeInitialState`> {
620
626
  orderBy?: OrderBy;
@@ -74,6 +74,9 @@ class CollectionChangesManager {
74
74
  this.changeSubscriptions.delete(subscription);
75
75
  }
76
76
  });
77
+ if (options.onStatusChange) {
78
+ subscription.on(`status:change`, options.onStatusChange);
79
+ }
77
80
  if (options.includeInitialState) {
78
81
  subscription.requestSnapshot({ trackLoadSubsetPromise: false });
79
82
  } else if (options.includeInitialState === false) {
@@ -1 +1 @@
1
- {"version":3,"file":"changes.js","sources":["../../../src/collection/changes.ts"],"sourcesContent":["import { NegativeActiveSubscribersError } from '../errors'\nimport {\n createSingleRowRefProxy,\n toExpression,\n} from '../query/builder/ref-proxy.js'\nimport { CollectionSubscription } from './subscription.js'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { ChangeMessage, SubscribeChangesOptions } from '../types'\nimport type { CollectionLifecycleManager } from './lifecycle.js'\nimport type { CollectionSyncManager } from './sync.js'\nimport type { CollectionEventsManager } from './events.js'\nimport type { CollectionImpl } from './index.js'\n\nexport class CollectionChangesManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private sync!: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n private events!: CollectionEventsManager\n private collection!: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n\n public activeSubscribersCount = 0\n public changeSubscriptions = new Set<CollectionSubscription>()\n public batchedEvents: Array<ChangeMessage<TOutput, TKey>> = []\n public shouldBatchEvents = false\n\n /**\n * Creates a new CollectionChangesManager instance\n */\n constructor() {}\n\n public setDeps(deps: {\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n events: CollectionEventsManager\n collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n }) {\n this.lifecycle = deps.lifecycle\n this.sync = deps.sync\n this.events = deps.events\n this.collection = deps.collection\n }\n\n /**\n * Emit an empty ready event to notify subscribers that the collection is ready\n * This bypasses the normal empty array check in emitEvents\n */\n public emitEmptyReadyEvent(): void {\n // Emit empty array directly to all subscribers\n for (const subscription of this.changeSubscriptions) {\n subscription.emitEvents([])\n }\n }\n\n /**\n * Emit events either immediately or batch them for later emission\n */\n public emitEvents(\n changes: Array<ChangeMessage<TOutput, TKey>>,\n forceEmit = false,\n ): void {\n // Skip batching for user actions (forceEmit=true) to keep UI responsive\n if (this.shouldBatchEvents && !forceEmit) {\n // Add events to the batch\n this.batchedEvents.push(...changes)\n return\n }\n\n // Either we're not batching, or we're forcing emission (user action or ending batch cycle)\n let eventsToEmit = changes\n\n if (forceEmit) {\n // Force emit is used to end a batch (e.g. after a sync commit). Combine any\n // buffered optimistic events with the final changes so subscribers see the\n // whole picture, even if the sync diff is empty.\n if (this.batchedEvents.length > 0) {\n eventsToEmit = [...this.batchedEvents, ...changes]\n }\n this.batchedEvents = []\n this.shouldBatchEvents = false\n }\n\n if (eventsToEmit.length === 0) {\n return\n }\n\n // Emit to all listeners\n for (const subscription of this.changeSubscriptions) {\n subscription.emitEvents(eventsToEmit)\n }\n }\n\n /**\n * Subscribe to changes in the collection\n */\n public subscribeChanges(\n callback: (changes: Array<ChangeMessage<TOutput>>) => void,\n options: SubscribeChangesOptions<TOutput> = {},\n ): CollectionSubscription {\n // Start sync and track subscriber\n this.addSubscriber()\n\n // Compile where callback to whereExpression if provided\n if (options.where && options.whereExpression) {\n throw new Error(\n `Cannot specify both 'where' and 'whereExpression' options. Use one or the other.`,\n )\n }\n\n const { where, ...opts } = options\n let whereExpression = opts.whereExpression\n if (where) {\n const proxy = createSingleRowRefProxy<TOutput>()\n const result = where(proxy)\n whereExpression = toExpression(result)\n }\n\n const subscription = new CollectionSubscription(this.collection, callback, {\n ...opts,\n whereExpression,\n onUnsubscribe: () => {\n this.removeSubscriber()\n this.changeSubscriptions.delete(subscription)\n },\n })\n\n if (options.includeInitialState) {\n subscription.requestSnapshot({ trackLoadSubsetPromise: false })\n } else if (options.includeInitialState === false) {\n // When explicitly set to false (not just undefined), mark all state as \"seen\"\n // so that all future changes (including deletes) pass through unfiltered.\n subscription.markAllStateAsSeen()\n }\n\n // Add to batched listeners\n this.changeSubscriptions.add(subscription)\n\n return subscription\n }\n\n /**\n * Increment the active subscribers count and start sync if needed\n */\n private addSubscriber(): void {\n const previousSubscriberCount = this.activeSubscribersCount\n this.activeSubscribersCount++\n this.lifecycle.cancelGCTimer()\n\n // Start sync if collection was cleaned up\n if (\n this.lifecycle.status === `cleaned-up` ||\n this.lifecycle.status === `idle`\n ) {\n this.sync.startSync()\n }\n\n this.events.emitSubscribersChange(\n this.activeSubscribersCount,\n previousSubscriberCount,\n )\n }\n\n /**\n * Decrement the active subscribers count and start GC timer if needed\n */\n private removeSubscriber(): void {\n const previousSubscriberCount = this.activeSubscribersCount\n this.activeSubscribersCount--\n\n if (this.activeSubscribersCount === 0) {\n this.lifecycle.startGCTimer()\n } else if (this.activeSubscribersCount < 0) {\n throw new NegativeActiveSubscribersError()\n }\n\n this.events.emitSubscribersChange(\n this.activeSubscribersCount,\n previousSubscriberCount,\n )\n }\n\n /**\n * Clean up the collection by stopping sync and clearing data\n * This can be called manually or automatically by garbage collection\n */\n public cleanup(): void {\n this.batchedEvents = []\n this.shouldBatchEvents = false\n }\n}\n"],"names":[],"mappings":";;;AAaO,MAAM,yBAKX;AAAA;AAAA;AAAA;AAAA,EAcA,cAAc;AARd,SAAO,yBAAyB;AAChC,SAAO,0CAA0B,IAAA;AACjC,SAAO,gBAAqD,CAAA;AAC5D,SAAO,oBAAoB;AAAA,EAKZ;AAAA,EAER,QAAQ,MAKZ;AACD,SAAK,YAAY,KAAK;AACtB,SAAK,OAAO,KAAK;AACjB,SAAK,SAAS,KAAK;AACnB,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,sBAA4B;AAEjC,eAAW,gBAAgB,KAAK,qBAAqB;AACnD,mBAAa,WAAW,EAAE;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,WACL,SACA,YAAY,OACN;AAEN,QAAI,KAAK,qBAAqB,CAAC,WAAW;AAExC,WAAK,cAAc,KAAK,GAAG,OAAO;AAClC;AAAA,IACF;AAGA,QAAI,eAAe;AAEnB,QAAI,WAAW;AAIb,UAAI,KAAK,cAAc,SAAS,GAAG;AACjC,uBAAe,CAAC,GAAG,KAAK,eAAe,GAAG,OAAO;AAAA,MACnD;AACA,WAAK,gBAAgB,CAAA;AACrB,WAAK,oBAAoB;AAAA,IAC3B;AAEA,QAAI,aAAa,WAAW,GAAG;AAC7B;AAAA,IACF;AAGA,eAAW,gBAAgB,KAAK,qBAAqB;AACnD,mBAAa,WAAW,YAAY;AAAA,IACtC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,iBACL,UACA,UAA4C,IACpB;AAExB,SAAK,cAAA;AAGL,QAAI,QAAQ,SAAS,QAAQ,iBAAiB;AAC5C,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAEA,UAAM,EAAE,OAAO,GAAG,KAAA,IAAS;AAC3B,QAAI,kBAAkB,KAAK;AAC3B,QAAI,OAAO;AACT,YAAM,QAAQ,wBAAA;AACd,YAAM,SAAS,MAAM,KAAK;AAC1B,wBAAkB,aAAa,MAAM;AAAA,IACvC;AAEA,UAAM,eAAe,IAAI,uBAAuB,KAAK,YAAY,UAAU;AAAA,MACzE,GAAG;AAAA,MACH;AAAA,MACA,eAAe,MAAM;AACnB,aAAK,iBAAA;AACL,aAAK,oBAAoB,OAAO,YAAY;AAAA,MAC9C;AAAA,IAAA,CACD;AAED,QAAI,QAAQ,qBAAqB;AAC/B,mBAAa,gBAAgB,EAAE,wBAAwB,MAAA,CAAO;AAAA,IAChE,WAAW,QAAQ,wBAAwB,OAAO;AAGhD,mBAAa,mBAAA;AAAA,IACf;AAGA,SAAK,oBAAoB,IAAI,YAAY;AAEzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,UAAM,0BAA0B,KAAK;AACrC,SAAK;AACL,SAAK,UAAU,cAAA;AAGf,QACE,KAAK,UAAU,WAAW,gBAC1B,KAAK,UAAU,WAAW,QAC1B;AACA,WAAK,KAAK,UAAA;AAAA,IACZ;AAEA,SAAK,OAAO;AAAA,MACV,KAAK;AAAA,MACL;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,UAAM,0BAA0B,KAAK;AACrC,SAAK;AAEL,QAAI,KAAK,2BAA2B,GAAG;AACrC,WAAK,UAAU,aAAA;AAAA,IACjB,WAAW,KAAK,yBAAyB,GAAG;AAC1C,YAAM,IAAI,+BAAA;AAAA,IACZ;AAEA,SAAK,OAAO;AAAA,MACV,KAAK;AAAA,MACL;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAgB;AACrB,SAAK,gBAAgB,CAAA;AACrB,SAAK,oBAAoB;AAAA,EAC3B;AACF;"}
1
+ {"version":3,"file":"changes.js","sources":["../../../src/collection/changes.ts"],"sourcesContent":["import { NegativeActiveSubscribersError } from '../errors'\nimport {\n createSingleRowRefProxy,\n toExpression,\n} from '../query/builder/ref-proxy.js'\nimport { CollectionSubscription } from './subscription.js'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { ChangeMessage, SubscribeChangesOptions } from '../types'\nimport type { CollectionLifecycleManager } from './lifecycle.js'\nimport type { CollectionSyncManager } from './sync.js'\nimport type { CollectionEventsManager } from './events.js'\nimport type { CollectionImpl } from './index.js'\n\nexport class CollectionChangesManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private sync!: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n private events!: CollectionEventsManager\n private collection!: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n\n public activeSubscribersCount = 0\n public changeSubscriptions = new Set<CollectionSubscription>()\n public batchedEvents: Array<ChangeMessage<TOutput, TKey>> = []\n public shouldBatchEvents = false\n\n /**\n * Creates a new CollectionChangesManager instance\n */\n constructor() {}\n\n public setDeps(deps: {\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n events: CollectionEventsManager\n collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n }) {\n this.lifecycle = deps.lifecycle\n this.sync = deps.sync\n this.events = deps.events\n this.collection = deps.collection\n }\n\n /**\n * Emit an empty ready event to notify subscribers that the collection is ready\n * This bypasses the normal empty array check in emitEvents\n */\n public emitEmptyReadyEvent(): void {\n // Emit empty array directly to all subscribers\n for (const subscription of this.changeSubscriptions) {\n subscription.emitEvents([])\n }\n }\n\n /**\n * Emit events either immediately or batch them for later emission\n */\n public emitEvents(\n changes: Array<ChangeMessage<TOutput, TKey>>,\n forceEmit = false,\n ): void {\n // Skip batching for user actions (forceEmit=true) to keep UI responsive\n if (this.shouldBatchEvents && !forceEmit) {\n // Add events to the batch\n this.batchedEvents.push(...changes)\n return\n }\n\n // Either we're not batching, or we're forcing emission (user action or ending batch cycle)\n let eventsToEmit = changes\n\n if (forceEmit) {\n // Force emit is used to end a batch (e.g. after a sync commit). Combine any\n // buffered optimistic events with the final changes so subscribers see the\n // whole picture, even if the sync diff is empty.\n if (this.batchedEvents.length > 0) {\n eventsToEmit = [...this.batchedEvents, ...changes]\n }\n this.batchedEvents = []\n this.shouldBatchEvents = false\n }\n\n if (eventsToEmit.length === 0) {\n return\n }\n\n // Emit to all listeners\n for (const subscription of this.changeSubscriptions) {\n subscription.emitEvents(eventsToEmit)\n }\n }\n\n /**\n * Subscribe to changes in the collection\n */\n public subscribeChanges(\n callback: (changes: Array<ChangeMessage<TOutput>>) => void,\n options: SubscribeChangesOptions<TOutput> = {},\n ): CollectionSubscription {\n // Start sync and track subscriber\n this.addSubscriber()\n\n // Compile where callback to whereExpression if provided\n if (options.where && options.whereExpression) {\n throw new Error(\n `Cannot specify both 'where' and 'whereExpression' options. Use one or the other.`,\n )\n }\n\n const { where, ...opts } = options\n let whereExpression = opts.whereExpression\n if (where) {\n const proxy = createSingleRowRefProxy<TOutput>()\n const result = where(proxy)\n whereExpression = toExpression(result)\n }\n\n const subscription = new CollectionSubscription(this.collection, callback, {\n ...opts,\n whereExpression,\n onUnsubscribe: () => {\n this.removeSubscriber()\n this.changeSubscriptions.delete(subscription)\n },\n })\n\n // Register status listener BEFORE requesting snapshot to avoid race condition.\n // This ensures the listener catches all status transitions, even if the\n // loadSubset promise resolves synchronously or very quickly.\n if (options.onStatusChange) {\n subscription.on(`status:change`, options.onStatusChange)\n }\n\n if (options.includeInitialState) {\n subscription.requestSnapshot({ trackLoadSubsetPromise: false })\n } else if (options.includeInitialState === false) {\n // When explicitly set to false (not just undefined), mark all state as \"seen\"\n // so that all future changes (including deletes) pass through unfiltered.\n subscription.markAllStateAsSeen()\n }\n\n // Add to batched listeners\n this.changeSubscriptions.add(subscription)\n\n return subscription\n }\n\n /**\n * Increment the active subscribers count and start sync if needed\n */\n private addSubscriber(): void {\n const previousSubscriberCount = this.activeSubscribersCount\n this.activeSubscribersCount++\n this.lifecycle.cancelGCTimer()\n\n // Start sync if collection was cleaned up\n if (\n this.lifecycle.status === `cleaned-up` ||\n this.lifecycle.status === `idle`\n ) {\n this.sync.startSync()\n }\n\n this.events.emitSubscribersChange(\n this.activeSubscribersCount,\n previousSubscriberCount,\n )\n }\n\n /**\n * Decrement the active subscribers count and start GC timer if needed\n */\n private removeSubscriber(): void {\n const previousSubscriberCount = this.activeSubscribersCount\n this.activeSubscribersCount--\n\n if (this.activeSubscribersCount === 0) {\n this.lifecycle.startGCTimer()\n } else if (this.activeSubscribersCount < 0) {\n throw new NegativeActiveSubscribersError()\n }\n\n this.events.emitSubscribersChange(\n this.activeSubscribersCount,\n previousSubscriberCount,\n )\n }\n\n /**\n * Clean up the collection by stopping sync and clearing data\n * This can be called manually or automatically by garbage collection\n */\n public cleanup(): void {\n this.batchedEvents = []\n this.shouldBatchEvents = false\n }\n}\n"],"names":[],"mappings":";;;AAaO,MAAM,yBAKX;AAAA;AAAA;AAAA;AAAA,EAcA,cAAc;AARd,SAAO,yBAAyB;AAChC,SAAO,0CAA0B,IAAA;AACjC,SAAO,gBAAqD,CAAA;AAC5D,SAAO,oBAAoB;AAAA,EAKZ;AAAA,EAER,QAAQ,MAKZ;AACD,SAAK,YAAY,KAAK;AACtB,SAAK,OAAO,KAAK;AACjB,SAAK,SAAS,KAAK;AACnB,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,sBAA4B;AAEjC,eAAW,gBAAgB,KAAK,qBAAqB;AACnD,mBAAa,WAAW,EAAE;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,WACL,SACA,YAAY,OACN;AAEN,QAAI,KAAK,qBAAqB,CAAC,WAAW;AAExC,WAAK,cAAc,KAAK,GAAG,OAAO;AAClC;AAAA,IACF;AAGA,QAAI,eAAe;AAEnB,QAAI,WAAW;AAIb,UAAI,KAAK,cAAc,SAAS,GAAG;AACjC,uBAAe,CAAC,GAAG,KAAK,eAAe,GAAG,OAAO;AAAA,MACnD;AACA,WAAK,gBAAgB,CAAA;AACrB,WAAK,oBAAoB;AAAA,IAC3B;AAEA,QAAI,aAAa,WAAW,GAAG;AAC7B;AAAA,IACF;AAGA,eAAW,gBAAgB,KAAK,qBAAqB;AACnD,mBAAa,WAAW,YAAY;AAAA,IACtC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,iBACL,UACA,UAA4C,IACpB;AAExB,SAAK,cAAA;AAGL,QAAI,QAAQ,SAAS,QAAQ,iBAAiB;AAC5C,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAEA,UAAM,EAAE,OAAO,GAAG,KAAA,IAAS;AAC3B,QAAI,kBAAkB,KAAK;AAC3B,QAAI,OAAO;AACT,YAAM,QAAQ,wBAAA;AACd,YAAM,SAAS,MAAM,KAAK;AAC1B,wBAAkB,aAAa,MAAM;AAAA,IACvC;AAEA,UAAM,eAAe,IAAI,uBAAuB,KAAK,YAAY,UAAU;AAAA,MACzE,GAAG;AAAA,MACH;AAAA,MACA,eAAe,MAAM;AACnB,aAAK,iBAAA;AACL,aAAK,oBAAoB,OAAO,YAAY;AAAA,MAC9C;AAAA,IAAA,CACD;AAKD,QAAI,QAAQ,gBAAgB;AAC1B,mBAAa,GAAG,iBAAiB,QAAQ,cAAc;AAAA,IACzD;AAEA,QAAI,QAAQ,qBAAqB;AAC/B,mBAAa,gBAAgB,EAAE,wBAAwB,MAAA,CAAO;AAAA,IAChE,WAAW,QAAQ,wBAAwB,OAAO;AAGhD,mBAAa,mBAAA;AAAA,IACf;AAGA,SAAK,oBAAoB,IAAI,YAAY;AAEzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,UAAM,0BAA0B,KAAK;AACrC,SAAK;AACL,SAAK,UAAU,cAAA;AAGf,QACE,KAAK,UAAU,WAAW,gBAC1B,KAAK,UAAU,WAAW,QAC1B;AACA,WAAK,KAAK,UAAA;AAAA,IACZ;AAEA,SAAK,OAAO;AAAA,MACV,KAAK;AAAA,MACL;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,UAAM,0BAA0B,KAAK;AACrC,SAAK;AAEL,QAAI,KAAK,2BAA2B,GAAG;AACrC,WAAK,UAAU,aAAA;AAAA,IACjB,WAAW,KAAK,yBAAyB,GAAG;AAC1C,YAAM,IAAI,+BAAA;AAAA,IACZ;AAEA,SAAK,OAAO;AAAA,MACV,KAAK;AAAA,MACL;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAgB;AACrB,SAAK,gBAAgB,CAAA;AACrB,SAAK,oBAAoB;AAAA,EAC3B;AACF;"}
@@ -62,7 +62,8 @@ class CollectionSyncManager {
62
62
  const isTruncateTransaction = pendingTransaction.truncate === true;
63
63
  if (insertingIntoExistingSynced && !hasPendingDeleteForKey && !isTruncateTransaction) {
64
64
  const existingValue = this.state.syncedData.get(key);
65
- if (existingValue !== void 0 && deepEquals(existingValue, messageWithOptionalKey.value)) {
65
+ const valuesEqual = existingValue !== void 0 && deepEquals(existingValue, messageWithOptionalKey.value);
66
+ if (valuesEqual) {
66
67
  messageType = `update`;
67
68
  } else {
68
69
  const utils = this.config.utils;
@@ -1 +1 @@
1
- {"version":3,"file":"sync.js","sources":["../../../src/collection/sync.ts"],"sourcesContent":["import {\n CollectionConfigurationError,\n CollectionIsInErrorStateError,\n DuplicateKeySyncError,\n NoPendingSyncTransactionCommitError,\n NoPendingSyncTransactionWriteError,\n SyncCleanupError,\n SyncTransactionAlreadyCommittedError,\n SyncTransactionAlreadyCommittedWriteError,\n} from '../errors'\nimport { deepEquals } from '../utils'\nimport { LIVE_QUERY_INTERNAL } from '../query/live/internal.js'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type {\n ChangeMessageOrDeleteKeyMessage,\n CleanupFn,\n CollectionConfig,\n LoadSubsetOptions,\n OptimisticChangeMessage,\n SyncConfigRes,\n} from '../types'\nimport type { CollectionImpl } from './index.js'\nimport type { CollectionStateManager } from './state'\nimport type { CollectionLifecycleManager } from './lifecycle'\nimport type { CollectionEventsManager } from './events.js'\nimport type { LiveQueryCollectionUtils } from '../query/live/collection-config-builder.js'\n\nexport class CollectionSyncManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n private collection!: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n private state!: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private _events!: CollectionEventsManager\n private config!: CollectionConfig<TOutput, TKey, TSchema>\n private id: string\n private syncMode: `eager` | `on-demand`\n\n public preloadPromise: Promise<void> | null = null\n public syncCleanupFn: (() => void) | null = null\n public syncLoadSubsetFn:\n | ((options: LoadSubsetOptions) => true | Promise<void>)\n | null = null\n public syncUnloadSubsetFn: ((options: LoadSubsetOptions) => void) | null =\n null\n\n private pendingLoadSubsetPromises: Set<Promise<void>> = new Set()\n\n /**\n * Creates a new CollectionSyncManager instance\n */\n constructor(config: CollectionConfig<TOutput, TKey, TSchema>, id: string) {\n this.config = config\n this.id = id\n this.syncMode = config.syncMode ?? `eager`\n }\n\n setDeps(deps: {\n collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n events: CollectionEventsManager\n }) {\n this.collection = deps.collection\n this.state = deps.state\n this.lifecycle = deps.lifecycle\n this._events = deps.events\n }\n\n /**\n * Start the sync process for this collection\n * This is called when the collection is first accessed or preloaded\n */\n public startSync(): void {\n if (\n this.lifecycle.status !== `idle` &&\n this.lifecycle.status !== `cleaned-up`\n ) {\n return // Already started or in progress\n }\n\n this.lifecycle.setStatus(`loading`)\n\n try {\n const syncRes = normalizeSyncFnResult(\n this.config.sync.sync({\n collection: this.collection,\n begin: () => {\n this.state.pendingSyncedTransactions.push({\n committed: false,\n operations: [],\n deletedKeys: new Set(),\n })\n },\n write: (\n messageWithOptionalKey: ChangeMessageOrDeleteKeyMessage<\n TOutput,\n TKey\n >,\n ) => {\n const pendingTransaction =\n this.state.pendingSyncedTransactions[\n this.state.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new NoPendingSyncTransactionWriteError()\n }\n if (pendingTransaction.committed) {\n throw new SyncTransactionAlreadyCommittedWriteError()\n }\n\n let key: TKey | undefined = undefined\n if (`key` in messageWithOptionalKey) {\n key = messageWithOptionalKey.key\n } else {\n key = this.config.getKey(messageWithOptionalKey.value)\n }\n\n let messageType = messageWithOptionalKey.type\n\n // Check if an item with this key already exists when inserting\n if (messageWithOptionalKey.type === `insert`) {\n const insertingIntoExistingSynced = this.state.syncedData.has(key)\n const hasPendingDeleteForKey =\n pendingTransaction.deletedKeys.has(key)\n const isTruncateTransaction = pendingTransaction.truncate === true\n // Allow insert after truncate in the same transaction even if it existed in syncedData\n if (\n insertingIntoExistingSynced &&\n !hasPendingDeleteForKey &&\n !isTruncateTransaction\n ) {\n const existingValue = this.state.syncedData.get(key)\n if (\n existingValue !== undefined &&\n deepEquals(existingValue, messageWithOptionalKey.value)\n ) {\n // The \"insert\" is an echo of a value we already have locally.\n // Treat it as an update so we preserve optimistic intent without\n // throwing a duplicate-key error during reconciliation.\n messageType = `update`\n } else {\n const utils = this.config\n .utils as Partial<LiveQueryCollectionUtils>\n const internal = utils[LIVE_QUERY_INTERNAL]\n throw new DuplicateKeySyncError(key, this.id, {\n hasCustomGetKey: internal?.hasCustomGetKey ?? false,\n hasJoins: internal?.hasJoins ?? false,\n })\n }\n }\n }\n\n const message = {\n ...messageWithOptionalKey,\n type: messageType,\n key,\n } as OptimisticChangeMessage<TOutput, TKey>\n pendingTransaction.operations.push(message)\n\n if (messageType === `delete`) {\n pendingTransaction.deletedKeys.add(key)\n }\n },\n commit: () => {\n const pendingTransaction =\n this.state.pendingSyncedTransactions[\n this.state.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new NoPendingSyncTransactionCommitError()\n }\n if (pendingTransaction.committed) {\n throw new SyncTransactionAlreadyCommittedError()\n }\n\n pendingTransaction.committed = true\n\n this.state.commitPendingTransactions()\n },\n markReady: () => {\n this.lifecycle.markReady()\n },\n truncate: () => {\n const pendingTransaction =\n this.state.pendingSyncedTransactions[\n this.state.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new NoPendingSyncTransactionWriteError()\n }\n if (pendingTransaction.committed) {\n throw new SyncTransactionAlreadyCommittedWriteError()\n }\n\n // Clear all operations from the current transaction\n pendingTransaction.operations = []\n pendingTransaction.deletedKeys.clear()\n\n // Mark the transaction as a truncate operation. During commit, this triggers:\n // - Delete events for all previously synced keys (excluding optimistic-deleted keys)\n // - Clearing of syncedData/syncedMetadata\n // - Subsequent synced ops applied on the fresh base\n // - Finally, optimistic mutations re-applied on top (single batch)\n pendingTransaction.truncate = true\n\n // Capture optimistic state NOW to preserve it even if transactions complete\n // before this truncate transaction is committed\n pendingTransaction.optimisticSnapshot = {\n upserts: new Map(this.state.optimisticUpserts),\n deletes: new Set(this.state.optimisticDeletes),\n }\n },\n }),\n )\n\n // Store cleanup function if provided\n this.syncCleanupFn = syncRes?.cleanup ?? null\n\n // Store loadSubset function if provided\n this.syncLoadSubsetFn = syncRes?.loadSubset ?? null\n\n // Store unloadSubset function if provided\n this.syncUnloadSubsetFn = syncRes?.unloadSubset ?? null\n\n // Validate: on-demand mode requires a loadSubset function\n if (this.syncMode === `on-demand` && !this.syncLoadSubsetFn) {\n throw new CollectionConfigurationError(\n `Collection \"${this.id}\" is configured with syncMode \"on-demand\" but the sync function did not return a loadSubset handler. ` +\n `Either provide a loadSubset handler or use syncMode \"eager\".`,\n )\n }\n } catch (error) {\n this.lifecycle.setStatus(`error`)\n throw error\n }\n }\n\n /**\n * Preload the collection data by starting sync if not already started\n * Multiple concurrent calls will share the same promise\n */\n public preload(): Promise<void> {\n if (this.preloadPromise) {\n return this.preloadPromise\n }\n\n // Warn when calling preload on an on-demand collection\n if (this.syncMode === `on-demand`) {\n console.warn(\n `${this.id ? `[${this.id}] ` : ``}Calling .preload() on a collection with syncMode \"on-demand\" is a no-op. ` +\n `In on-demand mode, data is only loaded when queries request it. ` +\n `Instead, create a live query and call .preload() on that to load the specific data you need. ` +\n `See https://tanstack.com/blog/tanstack-db-0.5-query-driven-sync for more details.`,\n )\n }\n\n this.preloadPromise = new Promise<void>((resolve, reject) => {\n if (this.lifecycle.status === `ready`) {\n resolve()\n return\n }\n\n if (this.lifecycle.status === `error`) {\n reject(new CollectionIsInErrorStateError())\n return\n }\n\n // Register callback BEFORE starting sync to avoid race condition\n this.lifecycle.onFirstReady(() => {\n resolve()\n })\n\n // Start sync if collection hasn't started yet or was cleaned up\n if (\n this.lifecycle.status === `idle` ||\n this.lifecycle.status === `cleaned-up`\n ) {\n try {\n this.startSync()\n } catch (error) {\n reject(error)\n return\n }\n }\n })\n\n return this.preloadPromise\n }\n\n /**\n * Gets whether the collection is currently loading more data\n */\n public get isLoadingSubset(): boolean {\n return this.pendingLoadSubsetPromises.size > 0\n }\n\n /**\n * Tracks a load promise for isLoadingSubset state.\n * @internal This is for internal coordination (e.g., live-query glue code), not for general use.\n */\n public trackLoadPromise(promise: Promise<void>): void {\n const loadingStarting = !this.isLoadingSubset\n this.pendingLoadSubsetPromises.add(promise)\n\n if (loadingStarting) {\n this._events.emit(`loadingSubset:change`, {\n type: `loadingSubset:change`,\n collection: this.collection,\n isLoadingSubset: true,\n previousIsLoadingSubset: false,\n loadingSubsetTransition: `start`,\n })\n }\n\n promise.finally(() => {\n const loadingEnding =\n this.pendingLoadSubsetPromises.size === 1 &&\n this.pendingLoadSubsetPromises.has(promise)\n this.pendingLoadSubsetPromises.delete(promise)\n\n if (loadingEnding) {\n this._events.emit(`loadingSubset:change`, {\n type: `loadingSubset:change`,\n collection: this.collection,\n isLoadingSubset: false,\n previousIsLoadingSubset: true,\n loadingSubsetTransition: `end`,\n })\n }\n })\n }\n\n /**\n * Requests the sync layer to load more data.\n * @param options Options to control what data is being loaded\n * @returns If data loading is asynchronous, this method returns a promise that resolves when the data is loaded.\n * Returns true if no sync function is configured, if syncMode is 'eager', or if there is no work to do.\n */\n public loadSubset(options: LoadSubsetOptions): Promise<void> | true {\n // Bypass loadSubset when syncMode is 'eager'\n if (this.syncMode === `eager`) {\n return true\n }\n\n if (this.syncLoadSubsetFn) {\n const result = this.syncLoadSubsetFn(options)\n // If the result is a promise, track it\n if (result instanceof Promise) {\n this.trackLoadPromise(result)\n return result\n }\n }\n\n return true\n }\n\n /**\n * Notifies the sync layer that a subset is no longer needed.\n * @param options Options that identify what data is being unloaded\n */\n public unloadSubset(options: LoadSubsetOptions): void {\n if (this.syncUnloadSubsetFn) {\n this.syncUnloadSubsetFn(options)\n }\n }\n\n public cleanup(): void {\n try {\n if (this.syncCleanupFn) {\n this.syncCleanupFn()\n this.syncCleanupFn = null\n }\n } catch (error) {\n // Re-throw in a microtask to surface the error after cleanup completes\n queueMicrotask(() => {\n if (error instanceof Error) {\n // Preserve the original error and stack trace\n const wrappedError = new SyncCleanupError(this.id, error)\n wrappedError.cause = error\n wrappedError.stack = error.stack\n throw wrappedError\n } else {\n throw new SyncCleanupError(this.id, error as Error | string)\n }\n })\n }\n this.preloadPromise = null\n }\n}\n\nfunction normalizeSyncFnResult(result: void | CleanupFn | SyncConfigRes) {\n if (typeof result === `function`) {\n return { cleanup: result }\n }\n\n if (typeof result === `object`) {\n return result\n }\n\n return undefined\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync.js","sources":["../../../src/collection/sync.ts"],"sourcesContent":["import {\n CollectionConfigurationError,\n CollectionIsInErrorStateError,\n DuplicateKeySyncError,\n NoPendingSyncTransactionCommitError,\n NoPendingSyncTransactionWriteError,\n SyncCleanupError,\n SyncTransactionAlreadyCommittedError,\n SyncTransactionAlreadyCommittedWriteError,\n} from '../errors'\nimport { deepEquals } from '../utils'\nimport { LIVE_QUERY_INTERNAL } from '../query/live/internal.js'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type {\n ChangeMessageOrDeleteKeyMessage,\n CleanupFn,\n CollectionConfig,\n LoadSubsetOptions,\n OptimisticChangeMessage,\n SyncConfigRes,\n} from '../types'\nimport type { CollectionImpl } from './index.js'\nimport type { CollectionStateManager } from './state'\nimport type { CollectionLifecycleManager } from './lifecycle'\nimport type { CollectionEventsManager } from './events.js'\nimport type { LiveQueryCollectionUtils } from '../query/live/collection-config-builder.js'\n\nexport class CollectionSyncManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n private collection!: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n private state!: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private _events!: CollectionEventsManager\n private config!: CollectionConfig<TOutput, TKey, TSchema>\n private id: string\n private syncMode: `eager` | `on-demand`\n\n public preloadPromise: Promise<void> | null = null\n public syncCleanupFn: (() => void) | null = null\n public syncLoadSubsetFn:\n | ((options: LoadSubsetOptions) => true | Promise<void>)\n | null = null\n public syncUnloadSubsetFn: ((options: LoadSubsetOptions) => void) | null =\n null\n\n private pendingLoadSubsetPromises: Set<Promise<void>> = new Set()\n\n /**\n * Creates a new CollectionSyncManager instance\n */\n constructor(config: CollectionConfig<TOutput, TKey, TSchema>, id: string) {\n this.config = config\n this.id = id\n this.syncMode = config.syncMode ?? `eager`\n }\n\n setDeps(deps: {\n collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n events: CollectionEventsManager\n }) {\n this.collection = deps.collection\n this.state = deps.state\n this.lifecycle = deps.lifecycle\n this._events = deps.events\n }\n\n /**\n * Start the sync process for this collection\n * This is called when the collection is first accessed or preloaded\n */\n public startSync(): void {\n if (\n this.lifecycle.status !== `idle` &&\n this.lifecycle.status !== `cleaned-up`\n ) {\n return // Already started or in progress\n }\n\n this.lifecycle.setStatus(`loading`)\n\n try {\n const syncRes = normalizeSyncFnResult(\n this.config.sync.sync({\n collection: this.collection,\n begin: () => {\n this.state.pendingSyncedTransactions.push({\n committed: false,\n operations: [],\n deletedKeys: new Set(),\n })\n },\n write: (\n messageWithOptionalKey: ChangeMessageOrDeleteKeyMessage<\n TOutput,\n TKey\n >,\n ) => {\n const pendingTransaction =\n this.state.pendingSyncedTransactions[\n this.state.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new NoPendingSyncTransactionWriteError()\n }\n if (pendingTransaction.committed) {\n throw new SyncTransactionAlreadyCommittedWriteError()\n }\n\n let key: TKey | undefined = undefined\n if (`key` in messageWithOptionalKey) {\n key = messageWithOptionalKey.key\n } else {\n key = this.config.getKey(messageWithOptionalKey.value)\n }\n\n let messageType = messageWithOptionalKey.type\n\n // Check if an item with this key already exists when inserting\n if (messageWithOptionalKey.type === `insert`) {\n const insertingIntoExistingSynced = this.state.syncedData.has(key)\n const hasPendingDeleteForKey =\n pendingTransaction.deletedKeys.has(key)\n const isTruncateTransaction = pendingTransaction.truncate === true\n // Allow insert after truncate in the same transaction even if it existed in syncedData\n if (\n insertingIntoExistingSynced &&\n !hasPendingDeleteForKey &&\n !isTruncateTransaction\n ) {\n const existingValue = this.state.syncedData.get(key)\n const valuesEqual =\n existingValue !== undefined &&\n deepEquals(existingValue, messageWithOptionalKey.value)\n if (valuesEqual) {\n // The \"insert\" is an echo of a value we already have locally.\n // Treat it as an update so we preserve optimistic intent without\n // throwing a duplicate-key error during reconciliation.\n messageType = `update`\n } else {\n const utils = this.config\n .utils as Partial<LiveQueryCollectionUtils>\n const internal = utils[LIVE_QUERY_INTERNAL]\n throw new DuplicateKeySyncError(key, this.id, {\n hasCustomGetKey: internal?.hasCustomGetKey ?? false,\n hasJoins: internal?.hasJoins ?? false,\n })\n }\n }\n }\n\n const message = {\n ...messageWithOptionalKey,\n type: messageType,\n key,\n } as OptimisticChangeMessage<TOutput, TKey>\n pendingTransaction.operations.push(message)\n\n if (messageType === `delete`) {\n pendingTransaction.deletedKeys.add(key)\n }\n },\n commit: () => {\n const pendingTransaction =\n this.state.pendingSyncedTransactions[\n this.state.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new NoPendingSyncTransactionCommitError()\n }\n if (pendingTransaction.committed) {\n throw new SyncTransactionAlreadyCommittedError()\n }\n\n pendingTransaction.committed = true\n\n this.state.commitPendingTransactions()\n },\n markReady: () => {\n this.lifecycle.markReady()\n },\n truncate: () => {\n const pendingTransaction =\n this.state.pendingSyncedTransactions[\n this.state.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new NoPendingSyncTransactionWriteError()\n }\n if (pendingTransaction.committed) {\n throw new SyncTransactionAlreadyCommittedWriteError()\n }\n\n // Clear all operations from the current transaction\n pendingTransaction.operations = []\n pendingTransaction.deletedKeys.clear()\n\n // Mark the transaction as a truncate operation. During commit, this triggers:\n // - Delete events for all previously synced keys (excluding optimistic-deleted keys)\n // - Clearing of syncedData/syncedMetadata\n // - Subsequent synced ops applied on the fresh base\n // - Finally, optimistic mutations re-applied on top (single batch)\n pendingTransaction.truncate = true\n\n // Capture optimistic state NOW to preserve it even if transactions complete\n // before this truncate transaction is committed\n pendingTransaction.optimisticSnapshot = {\n upserts: new Map(this.state.optimisticUpserts),\n deletes: new Set(this.state.optimisticDeletes),\n }\n },\n }),\n )\n\n // Store cleanup function if provided\n this.syncCleanupFn = syncRes?.cleanup ?? null\n\n // Store loadSubset function if provided\n this.syncLoadSubsetFn = syncRes?.loadSubset ?? null\n\n // Store unloadSubset function if provided\n this.syncUnloadSubsetFn = syncRes?.unloadSubset ?? null\n\n // Validate: on-demand mode requires a loadSubset function\n if (this.syncMode === `on-demand` && !this.syncLoadSubsetFn) {\n throw new CollectionConfigurationError(\n `Collection \"${this.id}\" is configured with syncMode \"on-demand\" but the sync function did not return a loadSubset handler. ` +\n `Either provide a loadSubset handler or use syncMode \"eager\".`,\n )\n }\n } catch (error) {\n this.lifecycle.setStatus(`error`)\n throw error\n }\n }\n\n /**\n * Preload the collection data by starting sync if not already started\n * Multiple concurrent calls will share the same promise\n */\n public preload(): Promise<void> {\n if (this.preloadPromise) {\n return this.preloadPromise\n }\n\n // Warn when calling preload on an on-demand collection\n if (this.syncMode === `on-demand`) {\n console.warn(\n `${this.id ? `[${this.id}] ` : ``}Calling .preload() on a collection with syncMode \"on-demand\" is a no-op. ` +\n `In on-demand mode, data is only loaded when queries request it. ` +\n `Instead, create a live query and call .preload() on that to load the specific data you need. ` +\n `See https://tanstack.com/blog/tanstack-db-0.5-query-driven-sync for more details.`,\n )\n }\n\n this.preloadPromise = new Promise<void>((resolve, reject) => {\n if (this.lifecycle.status === `ready`) {\n resolve()\n return\n }\n\n if (this.lifecycle.status === `error`) {\n reject(new CollectionIsInErrorStateError())\n return\n }\n\n // Register callback BEFORE starting sync to avoid race condition\n this.lifecycle.onFirstReady(() => {\n resolve()\n })\n\n // Start sync if collection hasn't started yet or was cleaned up\n if (\n this.lifecycle.status === `idle` ||\n this.lifecycle.status === `cleaned-up`\n ) {\n try {\n this.startSync()\n } catch (error) {\n reject(error)\n return\n }\n }\n })\n\n return this.preloadPromise\n }\n\n /**\n * Gets whether the collection is currently loading more data\n */\n public get isLoadingSubset(): boolean {\n return this.pendingLoadSubsetPromises.size > 0\n }\n\n /**\n * Tracks a load promise for isLoadingSubset state.\n * @internal This is for internal coordination (e.g., live-query glue code), not for general use.\n */\n public trackLoadPromise(promise: Promise<void>): void {\n const loadingStarting = !this.isLoadingSubset\n this.pendingLoadSubsetPromises.add(promise)\n\n if (loadingStarting) {\n this._events.emit(`loadingSubset:change`, {\n type: `loadingSubset:change`,\n collection: this.collection,\n isLoadingSubset: true,\n previousIsLoadingSubset: false,\n loadingSubsetTransition: `start`,\n })\n }\n\n promise.finally(() => {\n const loadingEnding =\n this.pendingLoadSubsetPromises.size === 1 &&\n this.pendingLoadSubsetPromises.has(promise)\n this.pendingLoadSubsetPromises.delete(promise)\n\n if (loadingEnding) {\n this._events.emit(`loadingSubset:change`, {\n type: `loadingSubset:change`,\n collection: this.collection,\n isLoadingSubset: false,\n previousIsLoadingSubset: true,\n loadingSubsetTransition: `end`,\n })\n }\n })\n }\n\n /**\n * Requests the sync layer to load more data.\n * @param options Options to control what data is being loaded\n * @returns If data loading is asynchronous, this method returns a promise that resolves when the data is loaded.\n * Returns true if no sync function is configured, if syncMode is 'eager', or if there is no work to do.\n */\n public loadSubset(options: LoadSubsetOptions): Promise<void> | true {\n // Bypass loadSubset when syncMode is 'eager'\n if (this.syncMode === `eager`) {\n return true\n }\n\n if (this.syncLoadSubsetFn) {\n const result = this.syncLoadSubsetFn(options)\n // If the result is a promise, track it\n if (result instanceof Promise) {\n this.trackLoadPromise(result)\n return result\n }\n }\n\n return true\n }\n\n /**\n * Notifies the sync layer that a subset is no longer needed.\n * @param options Options that identify what data is being unloaded\n */\n public unloadSubset(options: LoadSubsetOptions): void {\n if (this.syncUnloadSubsetFn) {\n this.syncUnloadSubsetFn(options)\n }\n }\n\n public cleanup(): void {\n try {\n if (this.syncCleanupFn) {\n this.syncCleanupFn()\n this.syncCleanupFn = null\n }\n } catch (error) {\n // Re-throw in a microtask to surface the error after cleanup completes\n queueMicrotask(() => {\n if (error instanceof Error) {\n // Preserve the original error and stack trace\n const wrappedError = new SyncCleanupError(this.id, error)\n wrappedError.cause = error\n wrappedError.stack = error.stack\n throw wrappedError\n } else {\n throw new SyncCleanupError(this.id, error as Error | string)\n }\n })\n }\n this.preloadPromise = null\n }\n}\n\nfunction normalizeSyncFnResult(result: void | CleanupFn | SyncConfigRes) {\n if (typeof result === `function`) {\n return { cleanup: result }\n }\n\n if (typeof result === `object`) {\n return result\n }\n\n return undefined\n}\n"],"names":[],"mappings":"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"}
package/dist/esm/proxy.js CHANGED
@@ -232,7 +232,7 @@ function createMapSetIteratorHandler(methodName, prop, methodFn, target, changeT
232
232
  );
233
233
  nextResult.value = valueProxy;
234
234
  } else {
235
- const tempKey = Symbol(`iterator-value`);
235
+ const tempKey = /* @__PURE__ */ Symbol(`iterator-value`);
236
236
  const { proxy: valueProxy } = memoizedCreateChangeProxy(
237
237
  nextResult.value,
238
238
  {
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.js","sources":["../../src/proxy.ts"],"sourcesContent":["/**\n * A utility for creating a proxy that captures changes to an object\n * and provides a way to retrieve those changes.\n */\n\nimport { deepEquals, isTemporal } from './utils'\n\n/**\n * Set of array methods that iterate with callbacks and may return elements.\n * Hoisted to module scope to avoid creating a new Set on every property access.\n */\nconst CALLBACK_ITERATION_METHODS = new Set([\n `find`,\n `findLast`,\n `findIndex`,\n `findLastIndex`,\n `filter`,\n `map`,\n `flatMap`,\n `forEach`,\n `some`,\n `every`,\n `reduce`,\n `reduceRight`,\n])\n\n/**\n * Set of array methods that modify the array in place.\n */\nconst ARRAY_MODIFYING_METHODS = new Set([\n `pop`,\n `push`,\n `shift`,\n `unshift`,\n `splice`,\n `sort`,\n `reverse`,\n `fill`,\n `copyWithin`,\n])\n\n/**\n * Set of Map/Set methods that modify the collection in place.\n */\nconst MAP_SET_MODIFYING_METHODS = new Set([`set`, `delete`, `clear`, `add`])\n\n/**\n * Set of Map/Set iterator methods.\n */\nconst MAP_SET_ITERATOR_METHODS = new Set([\n `entries`,\n `keys`,\n `values`,\n `forEach`,\n])\n\n/**\n * Check if a value is a proxiable object (not Date, RegExp, or Temporal)\n */\nfunction isProxiableObject(\n value: unknown,\n): value is Record<string | symbol, unknown> {\n return (\n value !== null &&\n typeof value === `object` &&\n !((value as any) instanceof Date) &&\n !((value as any) instanceof RegExp) &&\n !isTemporal(value)\n )\n}\n\n/**\n * Creates a handler for array iteration methods that ensures proxied elements\n * are passed to callbacks and returned from methods like find/filter.\n */\nfunction createArrayIterationHandler<T extends object>(\n methodName: string,\n methodFn: (...args: Array<unknown>) => unknown,\n changeTracker: ChangeTracker<T>,\n memoizedCreateChangeProxy: (\n obj: Record<string | symbol, unknown>,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n ) => { proxy: Record<string | symbol, unknown> },\n): ((...args: Array<unknown>) => unknown) | undefined {\n if (!CALLBACK_ITERATION_METHODS.has(methodName)) {\n return undefined\n }\n\n return function (...args: Array<unknown>) {\n const callback = args[0]\n if (typeof callback !== `function`) {\n return methodFn.apply(changeTracker.copy_, args)\n }\n\n // Create a helper to get proxied version of an array element\n const getProxiedElement = (element: unknown, index: number): unknown => {\n if (isProxiableObject(element)) {\n const nestedParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: String(index),\n }\n const { proxy: elementProxy } = memoizedCreateChangeProxy(\n element,\n nestedParent,\n )\n return elementProxy\n }\n return element\n }\n\n // Wrap the callback to pass proxied elements\n const wrappedCallback = function (\n this: unknown,\n element: unknown,\n index: number,\n array: unknown,\n ) {\n const proxiedElement = getProxiedElement(element, index)\n return callback.call(this, proxiedElement, index, array)\n }\n\n // For reduce/reduceRight, the callback signature is different\n if (methodName === `reduce` || methodName === `reduceRight`) {\n const reduceCallback = function (\n this: unknown,\n accumulator: unknown,\n element: unknown,\n index: number,\n array: unknown,\n ) {\n const proxiedElement = getProxiedElement(element, index)\n return callback.call(this, accumulator, proxiedElement, index, array)\n }\n return methodFn.apply(changeTracker.copy_, [\n reduceCallback,\n ...args.slice(1),\n ])\n }\n\n const result = methodFn.apply(changeTracker.copy_, [\n wrappedCallback,\n ...args.slice(1),\n ])\n\n // For find/findLast, proxy the returned element if it's an object\n if (\n (methodName === `find` || methodName === `findLast`) &&\n result &&\n typeof result === `object`\n ) {\n const foundIndex = (\n changeTracker.copy_ as unknown as Array<unknown>\n ).indexOf(result)\n if (foundIndex !== -1) {\n return getProxiedElement(result, foundIndex)\n }\n }\n\n // For filter, proxy each element in the result array\n if (methodName === `filter` && Array.isArray(result)) {\n return result.map((element) => {\n const originalIndex = (\n changeTracker.copy_ as unknown as Array<unknown>\n ).indexOf(element)\n if (originalIndex !== -1) {\n return getProxiedElement(element, originalIndex)\n }\n return element\n })\n }\n\n return result\n }\n}\n\n/**\n * Creates a Symbol.iterator handler for arrays that yields proxied elements.\n */\nfunction createArrayIteratorHandler<T extends object>(\n changeTracker: ChangeTracker<T>,\n memoizedCreateChangeProxy: (\n obj: Record<string | symbol, unknown>,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n ) => { proxy: Record<string | symbol, unknown> },\n): () => Iterator<unknown> {\n return function () {\n const array = changeTracker.copy_ as unknown as Array<unknown>\n let index = 0\n\n return {\n next() {\n if (index >= array.length) {\n return { done: true, value: undefined }\n }\n\n const element = array[index]\n let proxiedElement = element\n\n if (isProxiableObject(element)) {\n const nestedParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: String(index),\n }\n const { proxy: elementProxy } = memoizedCreateChangeProxy(\n element,\n nestedParent,\n )\n proxiedElement = elementProxy\n }\n\n index++\n return { done: false, value: proxiedElement }\n },\n [Symbol.iterator]() {\n return this\n },\n }\n }\n}\n\n/**\n * Creates a wrapper for methods that modify a collection (array, Map, Set).\n * The wrapper calls the method and marks the change tracker as modified.\n */\nfunction createModifyingMethodHandler<T extends object>(\n methodFn: (...args: Array<unknown>) => unknown,\n changeTracker: ChangeTracker<T>,\n markChanged: (tracker: ChangeTracker<T>) => void,\n): (...args: Array<unknown>) => unknown {\n return function (...args: Array<unknown>) {\n const result = methodFn.apply(changeTracker.copy_, args)\n markChanged(changeTracker)\n return result\n }\n}\n\n/**\n * Creates handlers for Map/Set iterator methods (entries, keys, values, forEach).\n * Returns proxied values for iteration to enable change tracking.\n */\nfunction createMapSetIteratorHandler<T extends object>(\n methodName: string,\n prop: string | symbol,\n methodFn: (...args: Array<unknown>) => unknown,\n target: Map<unknown, unknown> | Set<unknown>,\n changeTracker: ChangeTracker<T>,\n memoizedCreateChangeProxy: (\n obj: Record<string | symbol, unknown>,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n ) => { proxy: Record<string | symbol, unknown> },\n markChanged: (tracker: ChangeTracker<T>) => void,\n): ((...args: Array<unknown>) => unknown) | undefined {\n const isIteratorMethod =\n MAP_SET_ITERATOR_METHODS.has(methodName) || prop === Symbol.iterator\n\n if (!isIteratorMethod) {\n return undefined\n }\n\n return function (this: unknown, ...args: Array<unknown>) {\n const result = methodFn.apply(changeTracker.copy_, args)\n\n // For forEach, wrap the callback to track changes\n if (methodName === `forEach`) {\n const callback = args[0]\n if (typeof callback === `function`) {\n const wrappedCallback = function (\n this: unknown,\n value: unknown,\n key: unknown,\n collection: unknown,\n ) {\n const cbresult = callback.call(this, value, key, collection)\n markChanged(changeTracker)\n return cbresult\n }\n return methodFn.apply(target, [wrappedCallback, ...args.slice(1)])\n }\n }\n\n // For iterators (entries, keys, values, Symbol.iterator)\n const isValueIterator =\n methodName === `entries` ||\n methodName === `values` ||\n methodName === Symbol.iterator.toString() ||\n prop === Symbol.iterator\n\n if (isValueIterator) {\n const originalIterator = result as Iterator<unknown>\n\n // For values() iterator on Maps, create a value-to-key mapping\n const valueToKeyMap = new Map()\n if (methodName === `values` && target instanceof Map) {\n for (const [key, mapValue] of (\n changeTracker.copy_ as unknown as Map<unknown, unknown>\n ).entries()) {\n valueToKeyMap.set(mapValue, key)\n }\n }\n\n // For Set iterators, create an original-to-modified mapping\n const originalToModifiedMap = new Map()\n if (target instanceof Set) {\n for (const setValue of (\n changeTracker.copy_ as unknown as Set<unknown>\n ).values()) {\n originalToModifiedMap.set(setValue, setValue)\n }\n }\n\n // Return a wrapped iterator that proxies values\n return {\n next() {\n const nextResult = originalIterator.next()\n\n if (\n !nextResult.done &&\n nextResult.value &&\n typeof nextResult.value === `object`\n ) {\n // For entries, the value is a [key, value] pair\n if (\n methodName === `entries` &&\n Array.isArray(nextResult.value) &&\n nextResult.value.length === 2\n ) {\n if (\n nextResult.value[1] &&\n typeof nextResult.value[1] === `object`\n ) {\n const mapKey = nextResult.value[0]\n const mapParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: mapKey as string | symbol,\n updateMap: (newValue: unknown) => {\n if (changeTracker.copy_ instanceof Map) {\n ;(changeTracker.copy_ as Map<unknown, unknown>).set(\n mapKey,\n newValue,\n )\n }\n },\n }\n const { proxy: valueProxy } = memoizedCreateChangeProxy(\n nextResult.value[1] as Record<string | symbol, unknown>,\n mapParent as unknown as {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n )\n nextResult.value[1] = valueProxy\n }\n } else if (\n methodName === `values` ||\n methodName === Symbol.iterator.toString() ||\n prop === Symbol.iterator\n ) {\n // For Map values(), use the key mapping\n if (methodName === `values` && target instanceof Map) {\n const mapKey = valueToKeyMap.get(nextResult.value)\n if (mapKey !== undefined) {\n const mapParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: mapKey as string | symbol,\n updateMap: (newValue: unknown) => {\n if (changeTracker.copy_ instanceof Map) {\n ;(changeTracker.copy_ as Map<unknown, unknown>).set(\n mapKey,\n newValue,\n )\n }\n },\n }\n const { proxy: valueProxy } = memoizedCreateChangeProxy(\n nextResult.value as Record<string | symbol, unknown>,\n mapParent as unknown as {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n )\n nextResult.value = valueProxy\n }\n } else if (target instanceof Set) {\n // For Set, track modifications\n const setOriginalValue = nextResult.value\n const setParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: setOriginalValue as unknown as string | symbol,\n updateSet: (newValue: unknown) => {\n if (changeTracker.copy_ instanceof Set) {\n ;(changeTracker.copy_ as Set<unknown>).delete(\n setOriginalValue,\n )\n ;(changeTracker.copy_ as Set<unknown>).add(newValue)\n originalToModifiedMap.set(setOriginalValue, newValue)\n }\n },\n }\n const { proxy: valueProxy } = memoizedCreateChangeProxy(\n nextResult.value as Record<string | symbol, unknown>,\n setParent as unknown as {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n )\n nextResult.value = valueProxy\n } else {\n // For other cases, use a symbol placeholder\n const tempKey = Symbol(`iterator-value`)\n const { proxy: valueProxy } = memoizedCreateChangeProxy(\n nextResult.value as Record<string | symbol, unknown>,\n {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: tempKey,\n },\n )\n nextResult.value = valueProxy\n }\n }\n }\n\n return nextResult\n },\n [Symbol.iterator]() {\n return this\n },\n }\n }\n\n return result\n }\n}\n\n/**\n * Simple debug utility that only logs when debug mode is enabled\n * Set DEBUG to true in localStorage to enable debug logging\n */\nfunction debugLog(...args: Array<unknown>): void {\n // Check if we're in a browser environment\n const isBrowser =\n typeof window !== `undefined` && typeof localStorage !== `undefined`\n\n // In browser, check localStorage for debug flag\n if (isBrowser && localStorage.getItem(`DEBUG`) === `true`) {\n console.log(`[proxy]`, ...args)\n }\n // In Node.js environment, check for environment variable (though this is primarily for browser)\n else if (\n // true\n !isBrowser &&\n typeof process !== `undefined` &&\n process.env.DEBUG === `true`\n ) {\n console.log(`[proxy]`, ...args)\n }\n}\n\n// Add TypedArray interface with proper type\ninterface TypedArray {\n length: number\n [index: number]: number\n}\n\n// Update type for ChangeTracker\ninterface ChangeTracker<T extends object> {\n originalObject: T\n modified: boolean\n copy_: T\n proxyCount: number\n assigned_: Record<string | symbol, boolean>\n parent?:\n | {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n }\n | {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n updateMap: (newValue: unknown) => void\n }\n | {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: unknown\n updateSet: (newValue: unknown) => void\n }\n target: T\n}\n\n/**\n * Deep clones an object while preserving special types like Date and RegExp\n */\n\nfunction deepClone<T extends unknown>(\n obj: T,\n visited = new WeakMap<object, unknown>(),\n): T {\n // Handle null and undefined\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle primitive types\n if (typeof obj !== `object`) {\n return obj\n }\n\n // If we've already cloned this object, return the cached clone\n if (visited.has(obj as object)) {\n return visited.get(obj as object) as T\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as unknown as T\n }\n\n if (obj instanceof RegExp) {\n return new RegExp(obj.source, obj.flags) as unknown as T\n }\n\n if (Array.isArray(obj)) {\n const arrayClone = [] as Array<unknown>\n visited.set(obj as object, arrayClone)\n obj.forEach((item, index) => {\n arrayClone[index] = deepClone(item, visited)\n })\n return arrayClone as unknown as T\n }\n\n // Handle TypedArrays\n if (ArrayBuffer.isView(obj) && !(obj instanceof DataView)) {\n // Get the constructor to create a new instance of the same type\n const TypedArrayConstructor = Object.getPrototypeOf(obj).constructor\n const clone = new TypedArrayConstructor(\n (obj as unknown as TypedArray).length,\n ) as unknown as TypedArray\n visited.set(obj as object, clone)\n\n // Copy the values\n for (let i = 0; i < (obj as unknown as TypedArray).length; i++) {\n clone[i] = (obj as unknown as TypedArray)[i]!\n }\n\n return clone as unknown as T\n }\n\n if (obj instanceof Map) {\n const clone = new Map() as Map<unknown, unknown>\n visited.set(obj as object, clone)\n obj.forEach((value, key) => {\n clone.set(key, deepClone(value, visited))\n })\n return clone as unknown as T\n }\n\n if (obj instanceof Set) {\n const clone = new Set()\n visited.set(obj as object, clone)\n obj.forEach((value) => {\n clone.add(deepClone(value, visited))\n })\n return clone as unknown as T\n }\n\n // Handle Temporal objects\n if (isTemporal(obj)) {\n // Temporal objects are immutable, so we can return them directly\n // This preserves all their internal state correctly\n return obj\n }\n\n const clone = {} as Record<string | symbol, unknown>\n visited.set(obj as object, clone)\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n clone[key] = deepClone(\n (obj as Record<string | symbol, unknown>)[key],\n visited,\n )\n }\n }\n\n const symbolProps = Object.getOwnPropertySymbols(obj)\n for (const sym of symbolProps) {\n clone[sym] = deepClone(\n (obj as Record<string | symbol, unknown>)[sym],\n visited,\n )\n }\n\n return clone as T\n}\n\nlet count = 0\nfunction getProxyCount() {\n count += 1\n return count\n}\n\n/**\n * Creates a proxy that tracks changes to the target object\n *\n * @param target The object to proxy\n * @param parent Optional parent information\n * @returns An object containing the proxy and a function to get the changes\n */\nexport function createChangeProxy<\n T extends Record<string | symbol, any | undefined>,\n>(\n target: T,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n): {\n proxy: T\n\n getChanges: () => Record<string | symbol, any>\n} {\n const changeProxyCache = new Map<object, object>()\n\n function memoizedCreateChangeProxy<\n TInner extends Record<string | symbol, any | undefined>,\n >(\n innerTarget: TInner,\n innerParent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n ): {\n proxy: TInner\n getChanges: () => Record<string | symbol, any>\n } {\n debugLog(`Object ID:`, innerTarget.constructor.name)\n if (changeProxyCache.has(innerTarget)) {\n return changeProxyCache.get(innerTarget) as {\n proxy: TInner\n getChanges: () => Record<string | symbol, any>\n }\n } else {\n const changeProxy = createChangeProxy(innerTarget, innerParent)\n changeProxyCache.set(innerTarget, changeProxy)\n return changeProxy\n }\n }\n // Create a WeakMap to cache proxies for nested objects\n // This prevents creating multiple proxies for the same object\n // and handles circular references\n const proxyCache = new Map<object, object>()\n\n // Create a change tracker to track changes to the object\n const changeTracker: ChangeTracker<T> = {\n copy_: deepClone(target),\n originalObject: deepClone(target),\n proxyCount: getProxyCount(),\n modified: false,\n assigned_: {},\n parent,\n target, // Store reference to the target object\n }\n\n debugLog(\n `createChangeProxy called for target`,\n target,\n changeTracker.proxyCount,\n )\n // Mark this object and all its ancestors as modified\n // Also propagate the actual changes up the chain\n function markChanged(state: ChangeTracker<object>) {\n if (!state.modified) {\n state.modified = true\n }\n\n // Propagate the change up the parent chain\n if (state.parent) {\n debugLog(`propagating change to parent`)\n\n // Check if this is a special Map parent with updateMap function\n if (`updateMap` in state.parent) {\n // Use the special updateMap function for Maps\n state.parent.updateMap(state.copy_)\n } else if (`updateSet` in state.parent) {\n // Use the special updateSet function for Sets\n state.parent.updateSet(state.copy_)\n } else {\n // Update parent's copy with this object's current state\n state.parent.tracker.copy_[state.parent.prop] = state.copy_\n state.parent.tracker.assigned_[state.parent.prop] = true\n }\n\n // Mark parent as changed\n markChanged(state.parent.tracker)\n }\n }\n\n // Check if all properties in the current state have reverted to original values\n function checkIfReverted(\n state: ChangeTracker<Record<string | symbol, unknown>>,\n ): boolean {\n debugLog(\n `checkIfReverted called with assigned keys:`,\n Object.keys(state.assigned_),\n )\n\n // If there are no assigned properties, object is unchanged\n if (\n Object.keys(state.assigned_).length === 0 &&\n Object.getOwnPropertySymbols(state.assigned_).length === 0\n ) {\n debugLog(`No assigned properties, returning true`)\n return true\n }\n\n // Check each assigned regular property\n for (const prop in state.assigned_) {\n // If this property is marked as assigned\n if (state.assigned_[prop] === true) {\n const currentValue = state.copy_[prop]\n const originalValue = (state.originalObject as any)[prop]\n\n debugLog(\n `Checking property ${String(prop)}, current:`,\n currentValue,\n `original:`,\n originalValue,\n )\n\n // If the value is not equal to original, something is still changed\n if (!deepEquals(currentValue, originalValue)) {\n debugLog(`Property ${String(prop)} is different, returning false`)\n return false\n }\n } else if (state.assigned_[prop] === false) {\n // Property was deleted, so it's different from original\n debugLog(`Property ${String(prop)} was deleted, returning false`)\n return false\n }\n }\n\n // Check each assigned symbol property\n const symbolProps = Object.getOwnPropertySymbols(state.assigned_)\n for (const sym of symbolProps) {\n if (state.assigned_[sym] === true) {\n const currentValue = (state.copy_ as any)[sym]\n const originalValue = (state.originalObject as any)[sym]\n\n // If the value is not equal to original, something is still changed\n if (!deepEquals(currentValue, originalValue)) {\n debugLog(`Symbol property is different, returning false`)\n return false\n }\n } else if (state.assigned_[sym] === false) {\n // Property was deleted, so it's different from original\n debugLog(`Symbol property was deleted, returning false`)\n return false\n }\n }\n\n debugLog(`All properties match original values, returning true`)\n // All assigned properties match their original values\n return true\n }\n\n // Update parent status based on child changes\n function checkParentStatus(\n parentState: ChangeTracker<Record<string | symbol, unknown>>,\n childProp: string | symbol | unknown,\n ) {\n debugLog(`checkParentStatus called for child prop:`, childProp)\n\n // Check if all properties of the parent are reverted\n const isReverted = checkIfReverted(parentState)\n debugLog(`Parent checkIfReverted returned:`, isReverted)\n\n if (isReverted) {\n debugLog(`Parent is fully reverted, clearing tracking`)\n // If everything is reverted, clear the tracking\n parentState.modified = false\n parentState.assigned_ = {}\n\n // Continue up the chain\n if (parentState.parent) {\n debugLog(`Continuing up the parent chain`)\n checkParentStatus(parentState.parent.tracker, parentState.parent.prop)\n }\n }\n }\n\n // Create a proxy for the target object\n function createObjectProxy<TObj extends object>(obj: TObj): TObj {\n debugLog(`createObjectProxy`, obj)\n // If we've already created a proxy for this object, return it\n if (proxyCache.has(obj)) {\n debugLog(`proxyCache found match`)\n return proxyCache.get(obj) as TObj\n }\n\n // Create a proxy for the object\n const proxy = new Proxy(obj, {\n get(ptarget, prop) {\n debugLog(`get`, ptarget, prop)\n const value =\n changeTracker.copy_[prop as keyof T] ??\n changeTracker.originalObject[prop as keyof T]\n\n const originalValue = changeTracker.originalObject[prop as keyof T]\n\n debugLog(`value (at top of proxy get)`, value)\n\n // If it's a getter, return the value directly\n const desc = Object.getOwnPropertyDescriptor(ptarget, prop)\n if (desc?.get) {\n return value\n }\n\n // If the value is a function, bind it to the ptarget\n if (typeof value === `function`) {\n // For Array methods that modify the array\n if (Array.isArray(ptarget)) {\n const methodName = prop.toString()\n\n if (ARRAY_MODIFYING_METHODS.has(methodName)) {\n return createModifyingMethodHandler(\n value,\n changeTracker,\n markChanged,\n )\n }\n\n // Handle array iteration methods (find, filter, forEach, etc.)\n const iterationHandler = createArrayIterationHandler(\n methodName,\n value,\n changeTracker,\n memoizedCreateChangeProxy,\n )\n if (iterationHandler) {\n return iterationHandler\n }\n\n // Handle array Symbol.iterator for for...of loops\n if (prop === Symbol.iterator) {\n return createArrayIteratorHandler(\n changeTracker,\n memoizedCreateChangeProxy,\n )\n }\n }\n\n // For Map and Set methods that modify the collection\n if (ptarget instanceof Map || ptarget instanceof Set) {\n const methodName = prop.toString()\n\n if (MAP_SET_MODIFYING_METHODS.has(methodName)) {\n return createModifyingMethodHandler(\n value,\n changeTracker,\n markChanged,\n )\n }\n\n // Handle iterator methods for Map and Set\n const iteratorHandler = createMapSetIteratorHandler(\n methodName,\n prop,\n value,\n ptarget,\n changeTracker,\n memoizedCreateChangeProxy,\n markChanged,\n )\n if (iteratorHandler) {\n return iteratorHandler\n }\n }\n return value.bind(ptarget)\n }\n\n // If the value is an object (but not Date, RegExp, or Temporal), create a proxy for it\n if (isProxiableObject(value)) {\n // Create a parent reference for the nested object\n const nestedParent = {\n tracker: changeTracker,\n prop: String(prop),\n }\n\n // Create a proxy for the nested object\n const { proxy: nestedProxy } = memoizedCreateChangeProxy(\n originalValue,\n nestedParent,\n )\n\n // Cache the proxy\n proxyCache.set(value, nestedProxy)\n\n return nestedProxy\n }\n\n return value\n },\n\n set(_sobj, prop, value) {\n const currentValue = changeTracker.copy_[prop as keyof T]\n debugLog(\n `set called for property ${String(prop)}, current:`,\n currentValue,\n `new:`,\n value,\n )\n\n // Only track the change if the value is actually different\n if (!deepEquals(currentValue, value)) {\n // Check if the new value is equal to the original value\n // Important: Use the originalObject to get the true original value\n const originalValue = changeTracker.originalObject[prop as keyof T]\n const isRevertToOriginal = deepEquals(value, originalValue)\n debugLog(\n `value:`,\n value,\n `original:`,\n originalValue,\n `isRevertToOriginal:`,\n isRevertToOriginal,\n )\n\n if (isRevertToOriginal) {\n debugLog(`Reverting property ${String(prop)} to original value`)\n // If the value is reverted to its original state, remove it from changes\n delete changeTracker.assigned_[prop.toString()]\n\n // Make sure the copy is updated with the original value\n debugLog(`Updating copy with original value for ${String(prop)}`)\n changeTracker.copy_[prop as keyof T] = deepClone(originalValue)\n\n // Check if all properties in this object have been reverted\n debugLog(`Checking if all properties reverted`)\n const allReverted = checkIfReverted(changeTracker)\n debugLog(`All reverted:`, allReverted)\n\n if (allReverted) {\n debugLog(`All properties reverted, clearing tracking`)\n // If all have been reverted, clear tracking\n changeTracker.modified = false\n changeTracker.assigned_ = {}\n\n // If we're a nested object, check if the parent needs updating\n if (parent) {\n debugLog(`Updating parent for property:`, parent.prop)\n checkParentStatus(parent.tracker, parent.prop)\n }\n } else {\n // Some properties are still changed\n debugLog(`Some properties still changed, keeping modified flag`)\n changeTracker.modified = true\n }\n } else {\n debugLog(`Setting new value for property ${String(prop)}`)\n\n // Set the value on the copy\n changeTracker.copy_[prop as keyof T] = value\n\n // Track that this property was assigned - store using the actual property (symbol or string)\n changeTracker.assigned_[prop.toString()] = true\n\n // Mark this object and its ancestors as modified\n debugLog(`Marking object and ancestors as modified`, changeTracker)\n markChanged(changeTracker)\n }\n } else {\n debugLog(`Value unchanged, not tracking`)\n }\n\n return true\n },\n\n defineProperty(ptarget, prop, descriptor) {\n // Forward the defineProperty to the target to maintain Proxy invariants\n // This allows Object.seal() and Object.freeze() to work on the proxy\n const result = Reflect.defineProperty(ptarget, prop, descriptor)\n if (result && `value` in descriptor) {\n changeTracker.copy_[prop as keyof T] = deepClone(descriptor.value)\n changeTracker.assigned_[prop.toString()] = true\n markChanged(changeTracker)\n }\n return result\n },\n\n getOwnPropertyDescriptor(ptarget, prop) {\n // Forward to target to maintain Proxy invariants for seal/freeze\n return Reflect.getOwnPropertyDescriptor(ptarget, prop)\n },\n\n preventExtensions(ptarget) {\n // Forward to target to allow Object.seal() and Object.preventExtensions()\n return Reflect.preventExtensions(ptarget)\n },\n\n isExtensible(ptarget) {\n // Forward to target to maintain consistency\n return Reflect.isExtensible(ptarget)\n },\n\n deleteProperty(dobj, prop) {\n debugLog(`deleteProperty`, dobj, prop)\n const stringProp = typeof prop === `symbol` ? prop.toString() : prop\n\n if (stringProp in dobj) {\n // Check if the property exists in the original object\n const hadPropertyInOriginal =\n stringProp in changeTracker.originalObject\n\n // Forward the delete to the target using Reflect\n // This respects Object.seal/preventExtensions constraints\n const result = Reflect.deleteProperty(dobj, prop)\n\n if (result) {\n // If the property didn't exist in the original object, removing it\n // should revert to the original state\n if (!hadPropertyInOriginal) {\n delete changeTracker.assigned_[stringProp]\n\n // If this is the last change and we're not a nested object,\n // mark the object as unmodified\n if (\n Object.keys(changeTracker.assigned_).length === 0 &&\n Object.getOwnPropertySymbols(changeTracker.assigned_).length ===\n 0\n ) {\n changeTracker.modified = false\n } else {\n // We still have changes, keep as modified\n changeTracker.modified = true\n }\n } else {\n // Mark this property as deleted\n changeTracker.assigned_[stringProp] = false\n markChanged(changeTracker)\n }\n }\n\n return result\n }\n\n return true\n },\n })\n\n // Cache the proxy\n proxyCache.set(obj, proxy)\n\n return proxy\n }\n\n // Create a proxy for the target object\n // Use the unfrozen copy_ as the proxy target to avoid Proxy invariant violations\n // when the original target is frozen (e.g., from Immer)\n const proxy = createObjectProxy(changeTracker.copy_ as unknown as T)\n\n // Return the proxy and a function to get the changes\n return {\n proxy,\n getChanges: () => {\n debugLog(`getChanges called, modified:`, changeTracker.modified)\n debugLog(changeTracker)\n\n // First, check if the object is still considered modified\n if (!changeTracker.modified) {\n debugLog(`Object not modified, returning empty object`)\n return {}\n }\n\n // If we have a copy, return it directly\n // Check if valueObj is actually an object\n if (\n typeof changeTracker.copy_ !== `object` ||\n Array.isArray(changeTracker.copy_)\n ) {\n return changeTracker.copy_\n }\n\n if (Object.keys(changeTracker.assigned_).length === 0) {\n return changeTracker.copy_\n }\n\n const result: Record<string, any | undefined> = {}\n\n // Iterate through keys in keyObj\n for (const key in changeTracker.copy_) {\n // If the key's value is true and the key exists in valueObj\n if (\n changeTracker.assigned_[key] === true &&\n key in changeTracker.copy_\n ) {\n result[key] = changeTracker.copy_[key]\n }\n }\n debugLog(`Returning copy:`, result)\n return result as unknown as Record<string | symbol, unknown>\n },\n }\n}\n\n/**\n * Creates proxies for an array of objects and tracks changes to each\n *\n * @param targets Array of objects to proxy\n * @returns An object containing the array of proxies and a function to get all changes\n */\nexport function createArrayChangeProxy<T extends object>(\n targets: Array<T>,\n): {\n proxies: Array<T>\n getChanges: () => Array<Record<string | symbol, unknown>>\n} {\n const proxiesWithChanges = targets.map((target) => createChangeProxy(target))\n\n return {\n proxies: proxiesWithChanges.map((p) => p.proxy),\n getChanges: () => proxiesWithChanges.map((p) => p.getChanges()),\n }\n}\n\n/**\n * Creates a proxy for an object, passes it to a callback function,\n * and returns the changes made by the callback\n *\n * @param target The object to proxy\n * @param callback Function that receives the proxy and can make changes to it\n * @returns The changes made to the object\n */\nexport function withChangeTracking<T extends object>(\n target: T,\n callback: (proxy: T) => void,\n): Record<string | symbol, unknown> {\n const { proxy, getChanges } = createChangeProxy(target)\n\n callback(proxy)\n\n return getChanges()\n}\n\n/**\n * Creates proxies for an array of objects, passes them to a callback function,\n * and returns the changes made by the callback for each object\n *\n * @param targets Array of objects to proxy\n * @param callback Function that receives the proxies and can make changes to them\n * @returns Array of changes made to each object\n */\nexport function withArrayChangeTracking<T extends object>(\n targets: Array<T>,\n callback: (proxies: Array<T>) => void,\n): Array<Record<string | symbol, unknown>> {\n const { proxies, getChanges } = createArrayChangeProxy(targets)\n\n callback(proxies)\n\n return getChanges()\n}\n"],"names":["clone","proxy"],"mappings":"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"}
1
+ {"version":3,"file":"proxy.js","sources":["../../src/proxy.ts"],"sourcesContent":["/**\n * A utility for creating a proxy that captures changes to an object\n * and provides a way to retrieve those changes.\n */\n\nimport { deepEquals, isTemporal } from './utils'\n\n/**\n * Set of array methods that iterate with callbacks and may return elements.\n * Hoisted to module scope to avoid creating a new Set on every property access.\n */\nconst CALLBACK_ITERATION_METHODS = new Set([\n `find`,\n `findLast`,\n `findIndex`,\n `findLastIndex`,\n `filter`,\n `map`,\n `flatMap`,\n `forEach`,\n `some`,\n `every`,\n `reduce`,\n `reduceRight`,\n])\n\n/**\n * Set of array methods that modify the array in place.\n */\nconst ARRAY_MODIFYING_METHODS = new Set([\n `pop`,\n `push`,\n `shift`,\n `unshift`,\n `splice`,\n `sort`,\n `reverse`,\n `fill`,\n `copyWithin`,\n])\n\n/**\n * Set of Map/Set methods that modify the collection in place.\n */\nconst MAP_SET_MODIFYING_METHODS = new Set([`set`, `delete`, `clear`, `add`])\n\n/**\n * Set of Map/Set iterator methods.\n */\nconst MAP_SET_ITERATOR_METHODS = new Set([\n `entries`,\n `keys`,\n `values`,\n `forEach`,\n])\n\n/**\n * Check if a value is a proxiable object (not Date, RegExp, or Temporal)\n */\nfunction isProxiableObject(\n value: unknown,\n): value is Record<string | symbol, unknown> {\n return (\n value !== null &&\n typeof value === `object` &&\n !((value as any) instanceof Date) &&\n !((value as any) instanceof RegExp) &&\n !isTemporal(value)\n )\n}\n\n/**\n * Creates a handler for array iteration methods that ensures proxied elements\n * are passed to callbacks and returned from methods like find/filter.\n */\nfunction createArrayIterationHandler<T extends object>(\n methodName: string,\n methodFn: (...args: Array<unknown>) => unknown,\n changeTracker: ChangeTracker<T>,\n memoizedCreateChangeProxy: (\n obj: Record<string | symbol, unknown>,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n ) => { proxy: Record<string | symbol, unknown> },\n): ((...args: Array<unknown>) => unknown) | undefined {\n if (!CALLBACK_ITERATION_METHODS.has(methodName)) {\n return undefined\n }\n\n return function (...args: Array<unknown>) {\n const callback = args[0]\n if (typeof callback !== `function`) {\n return methodFn.apply(changeTracker.copy_, args)\n }\n\n // Create a helper to get proxied version of an array element\n const getProxiedElement = (element: unknown, index: number): unknown => {\n if (isProxiableObject(element)) {\n const nestedParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: String(index),\n }\n const { proxy: elementProxy } = memoizedCreateChangeProxy(\n element,\n nestedParent,\n )\n return elementProxy\n }\n return element\n }\n\n // Wrap the callback to pass proxied elements\n const wrappedCallback = function (\n this: unknown,\n element: unknown,\n index: number,\n array: unknown,\n ) {\n const proxiedElement = getProxiedElement(element, index)\n return callback.call(this, proxiedElement, index, array)\n }\n\n // For reduce/reduceRight, the callback signature is different\n if (methodName === `reduce` || methodName === `reduceRight`) {\n const reduceCallback = function (\n this: unknown,\n accumulator: unknown,\n element: unknown,\n index: number,\n array: unknown,\n ) {\n const proxiedElement = getProxiedElement(element, index)\n return callback.call(this, accumulator, proxiedElement, index, array)\n }\n return methodFn.apply(changeTracker.copy_, [\n reduceCallback,\n ...args.slice(1),\n ])\n }\n\n const result = methodFn.apply(changeTracker.copy_, [\n wrappedCallback,\n ...args.slice(1),\n ])\n\n // For find/findLast, proxy the returned element if it's an object\n if (\n (methodName === `find` || methodName === `findLast`) &&\n result &&\n typeof result === `object`\n ) {\n const foundIndex = (\n changeTracker.copy_ as unknown as Array<unknown>\n ).indexOf(result)\n if (foundIndex !== -1) {\n return getProxiedElement(result, foundIndex)\n }\n }\n\n // For filter, proxy each element in the result array\n if (methodName === `filter` && Array.isArray(result)) {\n return result.map((element) => {\n const originalIndex = (\n changeTracker.copy_ as unknown as Array<unknown>\n ).indexOf(element)\n if (originalIndex !== -1) {\n return getProxiedElement(element, originalIndex)\n }\n return element\n })\n }\n\n return result\n }\n}\n\n/**\n * Creates a Symbol.iterator handler for arrays that yields proxied elements.\n */\nfunction createArrayIteratorHandler<T extends object>(\n changeTracker: ChangeTracker<T>,\n memoizedCreateChangeProxy: (\n obj: Record<string | symbol, unknown>,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n ) => { proxy: Record<string | symbol, unknown> },\n): () => Iterator<unknown> {\n return function () {\n const array = changeTracker.copy_ as unknown as Array<unknown>\n let index = 0\n\n return {\n next() {\n if (index >= array.length) {\n return { done: true, value: undefined }\n }\n\n const element = array[index]\n let proxiedElement = element\n\n if (isProxiableObject(element)) {\n const nestedParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: String(index),\n }\n const { proxy: elementProxy } = memoizedCreateChangeProxy(\n element,\n nestedParent,\n )\n proxiedElement = elementProxy\n }\n\n index++\n return { done: false, value: proxiedElement }\n },\n [Symbol.iterator]() {\n return this\n },\n }\n }\n}\n\n/**\n * Creates a wrapper for methods that modify a collection (array, Map, Set).\n * The wrapper calls the method and marks the change tracker as modified.\n */\nfunction createModifyingMethodHandler<T extends object>(\n methodFn: (...args: Array<unknown>) => unknown,\n changeTracker: ChangeTracker<T>,\n markChanged: (tracker: ChangeTracker<T>) => void,\n): (...args: Array<unknown>) => unknown {\n return function (...args: Array<unknown>) {\n const result = methodFn.apply(changeTracker.copy_, args)\n markChanged(changeTracker)\n return result\n }\n}\n\n/**\n * Creates handlers for Map/Set iterator methods (entries, keys, values, forEach).\n * Returns proxied values for iteration to enable change tracking.\n */\nfunction createMapSetIteratorHandler<T extends object>(\n methodName: string,\n prop: string | symbol,\n methodFn: (...args: Array<unknown>) => unknown,\n target: Map<unknown, unknown> | Set<unknown>,\n changeTracker: ChangeTracker<T>,\n memoizedCreateChangeProxy: (\n obj: Record<string | symbol, unknown>,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n ) => { proxy: Record<string | symbol, unknown> },\n markChanged: (tracker: ChangeTracker<T>) => void,\n): ((...args: Array<unknown>) => unknown) | undefined {\n const isIteratorMethod =\n MAP_SET_ITERATOR_METHODS.has(methodName) || prop === Symbol.iterator\n\n if (!isIteratorMethod) {\n return undefined\n }\n\n return function (this: unknown, ...args: Array<unknown>) {\n const result = methodFn.apply(changeTracker.copy_, args)\n\n // For forEach, wrap the callback to track changes\n if (methodName === `forEach`) {\n const callback = args[0]\n if (typeof callback === `function`) {\n const wrappedCallback = function (\n this: unknown,\n value: unknown,\n key: unknown,\n collection: unknown,\n ) {\n const cbresult = callback.call(this, value, key, collection)\n markChanged(changeTracker)\n return cbresult\n }\n return methodFn.apply(target, [wrappedCallback, ...args.slice(1)])\n }\n }\n\n // For iterators (entries, keys, values, Symbol.iterator)\n const isValueIterator =\n methodName === `entries` ||\n methodName === `values` ||\n methodName === Symbol.iterator.toString() ||\n prop === Symbol.iterator\n\n if (isValueIterator) {\n const originalIterator = result as Iterator<unknown>\n\n // For values() iterator on Maps, create a value-to-key mapping\n const valueToKeyMap = new Map()\n if (methodName === `values` && target instanceof Map) {\n for (const [key, mapValue] of (\n changeTracker.copy_ as unknown as Map<unknown, unknown>\n ).entries()) {\n valueToKeyMap.set(mapValue, key)\n }\n }\n\n // For Set iterators, create an original-to-modified mapping\n const originalToModifiedMap = new Map()\n if (target instanceof Set) {\n for (const setValue of (\n changeTracker.copy_ as unknown as Set<unknown>\n ).values()) {\n originalToModifiedMap.set(setValue, setValue)\n }\n }\n\n // Return a wrapped iterator that proxies values\n return {\n next() {\n const nextResult = originalIterator.next()\n\n if (\n !nextResult.done &&\n nextResult.value &&\n typeof nextResult.value === `object`\n ) {\n // For entries, the value is a [key, value] pair\n if (\n methodName === `entries` &&\n Array.isArray(nextResult.value) &&\n nextResult.value.length === 2\n ) {\n if (\n nextResult.value[1] &&\n typeof nextResult.value[1] === `object`\n ) {\n const mapKey = nextResult.value[0]\n const mapParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: mapKey as string | symbol,\n updateMap: (newValue: unknown) => {\n if (changeTracker.copy_ instanceof Map) {\n ;(changeTracker.copy_ as Map<unknown, unknown>).set(\n mapKey,\n newValue,\n )\n }\n },\n }\n const { proxy: valueProxy } = memoizedCreateChangeProxy(\n nextResult.value[1] as Record<string | symbol, unknown>,\n mapParent as unknown as {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n )\n nextResult.value[1] = valueProxy\n }\n } else if (\n methodName === `values` ||\n methodName === Symbol.iterator.toString() ||\n prop === Symbol.iterator\n ) {\n // For Map values(), use the key mapping\n if (methodName === `values` && target instanceof Map) {\n const mapKey = valueToKeyMap.get(nextResult.value)\n if (mapKey !== undefined) {\n const mapParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: mapKey as string | symbol,\n updateMap: (newValue: unknown) => {\n if (changeTracker.copy_ instanceof Map) {\n ;(changeTracker.copy_ as Map<unknown, unknown>).set(\n mapKey,\n newValue,\n )\n }\n },\n }\n const { proxy: valueProxy } = memoizedCreateChangeProxy(\n nextResult.value as Record<string | symbol, unknown>,\n mapParent as unknown as {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n )\n nextResult.value = valueProxy\n }\n } else if (target instanceof Set) {\n // For Set, track modifications\n const setOriginalValue = nextResult.value\n const setParent = {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: setOriginalValue as unknown as string | symbol,\n updateSet: (newValue: unknown) => {\n if (changeTracker.copy_ instanceof Set) {\n ;(changeTracker.copy_ as Set<unknown>).delete(\n setOriginalValue,\n )\n ;(changeTracker.copy_ as Set<unknown>).add(newValue)\n originalToModifiedMap.set(setOriginalValue, newValue)\n }\n },\n }\n const { proxy: valueProxy } = memoizedCreateChangeProxy(\n nextResult.value as Record<string | symbol, unknown>,\n setParent as unknown as {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n )\n nextResult.value = valueProxy\n } else {\n // For other cases, use a symbol placeholder\n const tempKey = Symbol(`iterator-value`)\n const { proxy: valueProxy } = memoizedCreateChangeProxy(\n nextResult.value as Record<string | symbol, unknown>,\n {\n tracker: changeTracker as unknown as ChangeTracker<\n Record<string | symbol, unknown>\n >,\n prop: tempKey,\n },\n )\n nextResult.value = valueProxy\n }\n }\n }\n\n return nextResult\n },\n [Symbol.iterator]() {\n return this\n },\n }\n }\n\n return result\n }\n}\n\n/**\n * Simple debug utility that only logs when debug mode is enabled\n * Set DEBUG to true in localStorage to enable debug logging\n */\nfunction debugLog(...args: Array<unknown>): void {\n // Check if we're in a browser environment\n const isBrowser =\n typeof window !== `undefined` && typeof localStorage !== `undefined`\n\n // In browser, check localStorage for debug flag\n if (isBrowser && localStorage.getItem(`DEBUG`) === `true`) {\n console.log(`[proxy]`, ...args)\n }\n // In Node.js environment, check for environment variable (though this is primarily for browser)\n else if (\n // true\n !isBrowser &&\n typeof process !== `undefined` &&\n process.env.DEBUG === `true`\n ) {\n console.log(`[proxy]`, ...args)\n }\n}\n\n// Add TypedArray interface with proper type\ninterface TypedArray {\n length: number\n [index: number]: number\n}\n\n// Update type for ChangeTracker\ninterface ChangeTracker<T extends object> {\n originalObject: T\n modified: boolean\n copy_: T\n proxyCount: number\n assigned_: Record<string | symbol, boolean>\n parent?:\n | {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n }\n | {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n updateMap: (newValue: unknown) => void\n }\n | {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: unknown\n updateSet: (newValue: unknown) => void\n }\n target: T\n}\n\n/**\n * Deep clones an object while preserving special types like Date and RegExp\n */\n\nfunction deepClone<T extends unknown>(\n obj: T,\n visited = new WeakMap<object, unknown>(),\n): T {\n // Handle null and undefined\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle primitive types\n if (typeof obj !== `object`) {\n return obj\n }\n\n // If we've already cloned this object, return the cached clone\n if (visited.has(obj as object)) {\n return visited.get(obj as object) as T\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as unknown as T\n }\n\n if (obj instanceof RegExp) {\n return new RegExp(obj.source, obj.flags) as unknown as T\n }\n\n if (Array.isArray(obj)) {\n const arrayClone = [] as Array<unknown>\n visited.set(obj as object, arrayClone)\n obj.forEach((item, index) => {\n arrayClone[index] = deepClone(item, visited)\n })\n return arrayClone as unknown as T\n }\n\n // Handle TypedArrays\n if (ArrayBuffer.isView(obj) && !(obj instanceof DataView)) {\n // Get the constructor to create a new instance of the same type\n const TypedArrayConstructor = Object.getPrototypeOf(obj).constructor\n const clone = new TypedArrayConstructor(\n (obj as unknown as TypedArray).length,\n ) as unknown as TypedArray\n visited.set(obj as object, clone)\n\n // Copy the values\n for (let i = 0; i < (obj as unknown as TypedArray).length; i++) {\n clone[i] = (obj as unknown as TypedArray)[i]!\n }\n\n return clone as unknown as T\n }\n\n if (obj instanceof Map) {\n const clone = new Map() as Map<unknown, unknown>\n visited.set(obj as object, clone)\n obj.forEach((value, key) => {\n clone.set(key, deepClone(value, visited))\n })\n return clone as unknown as T\n }\n\n if (obj instanceof Set) {\n const clone = new Set()\n visited.set(obj as object, clone)\n obj.forEach((value) => {\n clone.add(deepClone(value, visited))\n })\n return clone as unknown as T\n }\n\n // Handle Temporal objects\n if (isTemporal(obj)) {\n // Temporal objects are immutable, so we can return them directly\n // This preserves all their internal state correctly\n return obj\n }\n\n const clone = {} as Record<string | symbol, unknown>\n visited.set(obj as object, clone)\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n clone[key] = deepClone(\n (obj as Record<string | symbol, unknown>)[key],\n visited,\n )\n }\n }\n\n const symbolProps = Object.getOwnPropertySymbols(obj)\n for (const sym of symbolProps) {\n clone[sym] = deepClone(\n (obj as Record<string | symbol, unknown>)[sym],\n visited,\n )\n }\n\n return clone as T\n}\n\nlet count = 0\nfunction getProxyCount() {\n count += 1\n return count\n}\n\n/**\n * Creates a proxy that tracks changes to the target object\n *\n * @param target The object to proxy\n * @param parent Optional parent information\n * @returns An object containing the proxy and a function to get the changes\n */\nexport function createChangeProxy<\n T extends Record<string | symbol, any | undefined>,\n>(\n target: T,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n): {\n proxy: T\n\n getChanges: () => Record<string | symbol, any>\n} {\n const changeProxyCache = new Map<object, object>()\n\n function memoizedCreateChangeProxy<\n TInner extends Record<string | symbol, any | undefined>,\n >(\n innerTarget: TInner,\n innerParent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n },\n ): {\n proxy: TInner\n getChanges: () => Record<string | symbol, any>\n } {\n debugLog(`Object ID:`, innerTarget.constructor.name)\n if (changeProxyCache.has(innerTarget)) {\n return changeProxyCache.get(innerTarget) as {\n proxy: TInner\n getChanges: () => Record<string | symbol, any>\n }\n } else {\n const changeProxy = createChangeProxy(innerTarget, innerParent)\n changeProxyCache.set(innerTarget, changeProxy)\n return changeProxy\n }\n }\n // Create a WeakMap to cache proxies for nested objects\n // This prevents creating multiple proxies for the same object\n // and handles circular references\n const proxyCache = new Map<object, object>()\n\n // Create a change tracker to track changes to the object\n const changeTracker: ChangeTracker<T> = {\n copy_: deepClone(target),\n originalObject: deepClone(target),\n proxyCount: getProxyCount(),\n modified: false,\n assigned_: {},\n parent,\n target, // Store reference to the target object\n }\n\n debugLog(\n `createChangeProxy called for target`,\n target,\n changeTracker.proxyCount,\n )\n // Mark this object and all its ancestors as modified\n // Also propagate the actual changes up the chain\n function markChanged(state: ChangeTracker<object>) {\n if (!state.modified) {\n state.modified = true\n }\n\n // Propagate the change up the parent chain\n if (state.parent) {\n debugLog(`propagating change to parent`)\n\n // Check if this is a special Map parent with updateMap function\n if (`updateMap` in state.parent) {\n // Use the special updateMap function for Maps\n state.parent.updateMap(state.copy_)\n } else if (`updateSet` in state.parent) {\n // Use the special updateSet function for Sets\n state.parent.updateSet(state.copy_)\n } else {\n // Update parent's copy with this object's current state\n state.parent.tracker.copy_[state.parent.prop] = state.copy_\n state.parent.tracker.assigned_[state.parent.prop] = true\n }\n\n // Mark parent as changed\n markChanged(state.parent.tracker)\n }\n }\n\n // Check if all properties in the current state have reverted to original values\n function checkIfReverted(\n state: ChangeTracker<Record<string | symbol, unknown>>,\n ): boolean {\n debugLog(\n `checkIfReverted called with assigned keys:`,\n Object.keys(state.assigned_),\n )\n\n // If there are no assigned properties, object is unchanged\n if (\n Object.keys(state.assigned_).length === 0 &&\n Object.getOwnPropertySymbols(state.assigned_).length === 0\n ) {\n debugLog(`No assigned properties, returning true`)\n return true\n }\n\n // Check each assigned regular property\n for (const prop in state.assigned_) {\n // If this property is marked as assigned\n if (state.assigned_[prop] === true) {\n const currentValue = state.copy_[prop]\n const originalValue = (state.originalObject as any)[prop]\n\n debugLog(\n `Checking property ${String(prop)}, current:`,\n currentValue,\n `original:`,\n originalValue,\n )\n\n // If the value is not equal to original, something is still changed\n if (!deepEquals(currentValue, originalValue)) {\n debugLog(`Property ${String(prop)} is different, returning false`)\n return false\n }\n } else if (state.assigned_[prop] === false) {\n // Property was deleted, so it's different from original\n debugLog(`Property ${String(prop)} was deleted, returning false`)\n return false\n }\n }\n\n // Check each assigned symbol property\n const symbolProps = Object.getOwnPropertySymbols(state.assigned_)\n for (const sym of symbolProps) {\n if (state.assigned_[sym] === true) {\n const currentValue = (state.copy_ as any)[sym]\n const originalValue = (state.originalObject as any)[sym]\n\n // If the value is not equal to original, something is still changed\n if (!deepEquals(currentValue, originalValue)) {\n debugLog(`Symbol property is different, returning false`)\n return false\n }\n } else if (state.assigned_[sym] === false) {\n // Property was deleted, so it's different from original\n debugLog(`Symbol property was deleted, returning false`)\n return false\n }\n }\n\n debugLog(`All properties match original values, returning true`)\n // All assigned properties match their original values\n return true\n }\n\n // Update parent status based on child changes\n function checkParentStatus(\n parentState: ChangeTracker<Record<string | symbol, unknown>>,\n childProp: string | symbol | unknown,\n ) {\n debugLog(`checkParentStatus called for child prop:`, childProp)\n\n // Check if all properties of the parent are reverted\n const isReverted = checkIfReverted(parentState)\n debugLog(`Parent checkIfReverted returned:`, isReverted)\n\n if (isReverted) {\n debugLog(`Parent is fully reverted, clearing tracking`)\n // If everything is reverted, clear the tracking\n parentState.modified = false\n parentState.assigned_ = {}\n\n // Continue up the chain\n if (parentState.parent) {\n debugLog(`Continuing up the parent chain`)\n checkParentStatus(parentState.parent.tracker, parentState.parent.prop)\n }\n }\n }\n\n // Create a proxy for the target object\n function createObjectProxy<TObj extends object>(obj: TObj): TObj {\n debugLog(`createObjectProxy`, obj)\n // If we've already created a proxy for this object, return it\n if (proxyCache.has(obj)) {\n debugLog(`proxyCache found match`)\n return proxyCache.get(obj) as TObj\n }\n\n // Create a proxy for the object\n const proxy = new Proxy(obj, {\n get(ptarget, prop) {\n debugLog(`get`, ptarget, prop)\n const value =\n changeTracker.copy_[prop as keyof T] ??\n changeTracker.originalObject[prop as keyof T]\n\n const originalValue = changeTracker.originalObject[prop as keyof T]\n\n debugLog(`value (at top of proxy get)`, value)\n\n // If it's a getter, return the value directly\n const desc = Object.getOwnPropertyDescriptor(ptarget, prop)\n if (desc?.get) {\n return value\n }\n\n // If the value is a function, bind it to the ptarget\n if (typeof value === `function`) {\n // For Array methods that modify the array\n if (Array.isArray(ptarget)) {\n const methodName = prop.toString()\n\n if (ARRAY_MODIFYING_METHODS.has(methodName)) {\n return createModifyingMethodHandler(\n value,\n changeTracker,\n markChanged,\n )\n }\n\n // Handle array iteration methods (find, filter, forEach, etc.)\n const iterationHandler = createArrayIterationHandler(\n methodName,\n value,\n changeTracker,\n memoizedCreateChangeProxy,\n )\n if (iterationHandler) {\n return iterationHandler\n }\n\n // Handle array Symbol.iterator for for...of loops\n if (prop === Symbol.iterator) {\n return createArrayIteratorHandler(\n changeTracker,\n memoizedCreateChangeProxy,\n )\n }\n }\n\n // For Map and Set methods that modify the collection\n if (ptarget instanceof Map || ptarget instanceof Set) {\n const methodName = prop.toString()\n\n if (MAP_SET_MODIFYING_METHODS.has(methodName)) {\n return createModifyingMethodHandler(\n value,\n changeTracker,\n markChanged,\n )\n }\n\n // Handle iterator methods for Map and Set\n const iteratorHandler = createMapSetIteratorHandler(\n methodName,\n prop,\n value,\n ptarget,\n changeTracker,\n memoizedCreateChangeProxy,\n markChanged,\n )\n if (iteratorHandler) {\n return iteratorHandler\n }\n }\n return value.bind(ptarget)\n }\n\n // If the value is an object (but not Date, RegExp, or Temporal), create a proxy for it\n if (isProxiableObject(value)) {\n // Create a parent reference for the nested object\n const nestedParent = {\n tracker: changeTracker,\n prop: String(prop),\n }\n\n // Create a proxy for the nested object\n const { proxy: nestedProxy } = memoizedCreateChangeProxy(\n originalValue,\n nestedParent,\n )\n\n // Cache the proxy\n proxyCache.set(value, nestedProxy)\n\n return nestedProxy\n }\n\n return value\n },\n\n set(_sobj, prop, value) {\n const currentValue = changeTracker.copy_[prop as keyof T]\n debugLog(\n `set called for property ${String(prop)}, current:`,\n currentValue,\n `new:`,\n value,\n )\n\n // Only track the change if the value is actually different\n if (!deepEquals(currentValue, value)) {\n // Check if the new value is equal to the original value\n // Important: Use the originalObject to get the true original value\n const originalValue = changeTracker.originalObject[prop as keyof T]\n const isRevertToOriginal = deepEquals(value, originalValue)\n debugLog(\n `value:`,\n value,\n `original:`,\n originalValue,\n `isRevertToOriginal:`,\n isRevertToOriginal,\n )\n\n if (isRevertToOriginal) {\n debugLog(`Reverting property ${String(prop)} to original value`)\n // If the value is reverted to its original state, remove it from changes\n delete changeTracker.assigned_[prop.toString()]\n\n // Make sure the copy is updated with the original value\n debugLog(`Updating copy with original value for ${String(prop)}`)\n changeTracker.copy_[prop as keyof T] = deepClone(originalValue)\n\n // Check if all properties in this object have been reverted\n debugLog(`Checking if all properties reverted`)\n const allReverted = checkIfReverted(changeTracker)\n debugLog(`All reverted:`, allReverted)\n\n if (allReverted) {\n debugLog(`All properties reverted, clearing tracking`)\n // If all have been reverted, clear tracking\n changeTracker.modified = false\n changeTracker.assigned_ = {}\n\n // If we're a nested object, check if the parent needs updating\n if (parent) {\n debugLog(`Updating parent for property:`, parent.prop)\n checkParentStatus(parent.tracker, parent.prop)\n }\n } else {\n // Some properties are still changed\n debugLog(`Some properties still changed, keeping modified flag`)\n changeTracker.modified = true\n }\n } else {\n debugLog(`Setting new value for property ${String(prop)}`)\n\n // Set the value on the copy\n changeTracker.copy_[prop as keyof T] = value\n\n // Track that this property was assigned - store using the actual property (symbol or string)\n changeTracker.assigned_[prop.toString()] = true\n\n // Mark this object and its ancestors as modified\n debugLog(`Marking object and ancestors as modified`, changeTracker)\n markChanged(changeTracker)\n }\n } else {\n debugLog(`Value unchanged, not tracking`)\n }\n\n return true\n },\n\n defineProperty(ptarget, prop, descriptor) {\n // Forward the defineProperty to the target to maintain Proxy invariants\n // This allows Object.seal() and Object.freeze() to work on the proxy\n const result = Reflect.defineProperty(ptarget, prop, descriptor)\n if (result && `value` in descriptor) {\n changeTracker.copy_[prop as keyof T] = deepClone(descriptor.value)\n changeTracker.assigned_[prop.toString()] = true\n markChanged(changeTracker)\n }\n return result\n },\n\n getOwnPropertyDescriptor(ptarget, prop) {\n // Forward to target to maintain Proxy invariants for seal/freeze\n return Reflect.getOwnPropertyDescriptor(ptarget, prop)\n },\n\n preventExtensions(ptarget) {\n // Forward to target to allow Object.seal() and Object.preventExtensions()\n return Reflect.preventExtensions(ptarget)\n },\n\n isExtensible(ptarget) {\n // Forward to target to maintain consistency\n return Reflect.isExtensible(ptarget)\n },\n\n deleteProperty(dobj, prop) {\n debugLog(`deleteProperty`, dobj, prop)\n const stringProp = typeof prop === `symbol` ? prop.toString() : prop\n\n if (stringProp in dobj) {\n // Check if the property exists in the original object\n const hadPropertyInOriginal =\n stringProp in changeTracker.originalObject\n\n // Forward the delete to the target using Reflect\n // This respects Object.seal/preventExtensions constraints\n const result = Reflect.deleteProperty(dobj, prop)\n\n if (result) {\n // If the property didn't exist in the original object, removing it\n // should revert to the original state\n if (!hadPropertyInOriginal) {\n delete changeTracker.assigned_[stringProp]\n\n // If this is the last change and we're not a nested object,\n // mark the object as unmodified\n if (\n Object.keys(changeTracker.assigned_).length === 0 &&\n Object.getOwnPropertySymbols(changeTracker.assigned_).length ===\n 0\n ) {\n changeTracker.modified = false\n } else {\n // We still have changes, keep as modified\n changeTracker.modified = true\n }\n } else {\n // Mark this property as deleted\n changeTracker.assigned_[stringProp] = false\n markChanged(changeTracker)\n }\n }\n\n return result\n }\n\n return true\n },\n })\n\n // Cache the proxy\n proxyCache.set(obj, proxy)\n\n return proxy\n }\n\n // Create a proxy for the target object\n // Use the unfrozen copy_ as the proxy target to avoid Proxy invariant violations\n // when the original target is frozen (e.g., from Immer)\n const proxy = createObjectProxy(changeTracker.copy_ as unknown as T)\n\n // Return the proxy and a function to get the changes\n return {\n proxy,\n getChanges: () => {\n debugLog(`getChanges called, modified:`, changeTracker.modified)\n debugLog(changeTracker)\n\n // First, check if the object is still considered modified\n if (!changeTracker.modified) {\n debugLog(`Object not modified, returning empty object`)\n return {}\n }\n\n // If we have a copy, return it directly\n // Check if valueObj is actually an object\n if (\n typeof changeTracker.copy_ !== `object` ||\n Array.isArray(changeTracker.copy_)\n ) {\n return changeTracker.copy_\n }\n\n if (Object.keys(changeTracker.assigned_).length === 0) {\n return changeTracker.copy_\n }\n\n const result: Record<string, any | undefined> = {}\n\n // Iterate through keys in keyObj\n for (const key in changeTracker.copy_) {\n // If the key's value is true and the key exists in valueObj\n if (\n changeTracker.assigned_[key] === true &&\n key in changeTracker.copy_\n ) {\n result[key] = changeTracker.copy_[key]\n }\n }\n debugLog(`Returning copy:`, result)\n return result as unknown as Record<string | symbol, unknown>\n },\n }\n}\n\n/**\n * Creates proxies for an array of objects and tracks changes to each\n *\n * @param targets Array of objects to proxy\n * @returns An object containing the array of proxies and a function to get all changes\n */\nexport function createArrayChangeProxy<T extends object>(\n targets: Array<T>,\n): {\n proxies: Array<T>\n getChanges: () => Array<Record<string | symbol, unknown>>\n} {\n const proxiesWithChanges = targets.map((target) => createChangeProxy(target))\n\n return {\n proxies: proxiesWithChanges.map((p) => p.proxy),\n getChanges: () => proxiesWithChanges.map((p) => p.getChanges()),\n }\n}\n\n/**\n * Creates a proxy for an object, passes it to a callback function,\n * and returns the changes made by the callback\n *\n * @param target The object to proxy\n * @param callback Function that receives the proxy and can make changes to it\n * @returns The changes made to the object\n */\nexport function withChangeTracking<T extends object>(\n target: T,\n callback: (proxy: T) => void,\n): Record<string | symbol, unknown> {\n const { proxy, getChanges } = createChangeProxy(target)\n\n callback(proxy)\n\n return getChanges()\n}\n\n/**\n * Creates proxies for an array of objects, passes them to a callback function,\n * and returns the changes made by the callback for each object\n *\n * @param targets Array of objects to proxy\n * @param callback Function that receives the proxies and can make changes to them\n * @returns Array of changes made to each object\n */\nexport function withArrayChangeTracking<T extends object>(\n targets: Array<T>,\n callback: (proxies: Array<T>) => void,\n): Array<Record<string | symbol, unknown>> {\n const { proxies, getChanges } = createArrayChangeProxy(targets)\n\n callback(proxies)\n\n return getChanges()\n}\n"],"names":["clone","proxy"],"mappings":"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"}