@tanstack/db 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/dist/cjs/{change-events.cjs → collection/change-events.cjs} +13 -42
  2. package/dist/cjs/collection/change-events.cjs.map +1 -0
  3. package/dist/{esm/change-events.d.ts → cjs/collection/change-events.d.cts} +6 -6
  4. package/dist/cjs/collection/changes.cjs +108 -0
  5. package/dist/cjs/collection/changes.cjs.map +1 -0
  6. package/dist/cjs/collection/changes.d.cts +53 -0
  7. package/dist/cjs/{collection-events.cjs → collection/events.cjs} +7 -5
  8. package/dist/cjs/collection/events.cjs.map +1 -0
  9. package/dist/cjs/{collection-events.d.cts → collection/events.d.cts} +7 -4
  10. package/dist/cjs/collection/index.cjs +417 -0
  11. package/dist/cjs/collection/index.cjs.map +1 -0
  12. package/dist/{esm/collection.d.ts → cjs/collection/index.d.cts} +46 -184
  13. package/dist/cjs/collection/indexes.cjs +124 -0
  14. package/dist/cjs/collection/indexes.cjs.map +1 -0
  15. package/dist/cjs/collection/indexes.d.cts +47 -0
  16. package/dist/cjs/collection/lifecycle.cjs +196 -0
  17. package/dist/cjs/collection/lifecycle.cjs.map +1 -0
  18. package/dist/cjs/collection/lifecycle.d.cts +81 -0
  19. package/dist/cjs/collection/mutations.cjs +315 -0
  20. package/dist/cjs/collection/mutations.cjs.map +1 -0
  21. package/dist/cjs/collection/mutations.d.cts +33 -0
  22. package/dist/cjs/collection/state.cjs +597 -0
  23. package/dist/cjs/collection/state.cjs.map +1 -0
  24. package/dist/cjs/collection/state.d.cts +122 -0
  25. package/dist/cjs/collection/subscription.cjs +160 -0
  26. package/dist/cjs/collection/subscription.cjs.map +1 -0
  27. package/dist/cjs/collection/subscription.d.cts +57 -0
  28. package/dist/cjs/collection/sync.cjs +154 -0
  29. package/dist/cjs/collection/sync.cjs.map +1 -0
  30. package/dist/cjs/collection/sync.d.cts +34 -0
  31. package/dist/cjs/index.cjs +8 -8
  32. package/dist/cjs/index.d.cts +2 -2
  33. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  34. package/dist/cjs/indexes/auto-index.d.cts +1 -1
  35. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  36. package/dist/cjs/indexes/base-index.d.cts +2 -2
  37. package/dist/cjs/indexes/btree-index.cjs +2 -2
  38. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  39. package/dist/cjs/indexes/btree-index.d.cts +1 -1
  40. package/dist/cjs/query/builder/index.cjs +2 -2
  41. package/dist/cjs/query/builder/index.cjs.map +1 -1
  42. package/dist/cjs/query/builder/types.d.cts +1 -1
  43. package/dist/cjs/query/compiler/index.cjs +5 -2
  44. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  45. package/dist/cjs/query/compiler/index.d.cts +3 -2
  46. package/dist/cjs/query/compiler/joins.cjs +22 -24
  47. package/dist/cjs/query/compiler/joins.cjs.map +1 -1
  48. package/dist/cjs/query/compiler/joins.d.cts +3 -2
  49. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  50. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  51. package/dist/cjs/query/ir.cjs.map +1 -1
  52. package/dist/cjs/query/ir.d.cts +1 -1
  53. package/dist/cjs/query/live/collection-config-builder.cjs +29 -12
  54. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  55. package/dist/cjs/query/live/collection-config-builder.d.cts +3 -0
  56. package/dist/cjs/query/live/collection-subscriber.cjs +43 -104
  57. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  58. package/dist/cjs/query/live/collection-subscriber.d.cts +4 -7
  59. package/dist/cjs/query/live-query-collection.cjs +2 -2
  60. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  61. package/dist/cjs/query/live-query-collection.d.cts +1 -1
  62. package/dist/cjs/transactions.cjs +3 -3
  63. package/dist/cjs/transactions.cjs.map +1 -1
  64. package/dist/cjs/types.d.cts +12 -10
  65. package/dist/cjs/utils/browser-polyfills.cjs +22 -0
  66. package/dist/cjs/utils/browser-polyfills.cjs.map +1 -0
  67. package/dist/cjs/utils/browser-polyfills.d.cts +9 -0
  68. package/dist/{cjs/change-events.d.cts → esm/collection/change-events.d.ts} +6 -6
  69. package/dist/esm/{change-events.js → collection/change-events.js} +13 -42
  70. package/dist/esm/collection/change-events.js.map +1 -0
  71. package/dist/esm/collection/changes.d.ts +53 -0
  72. package/dist/esm/collection/changes.js +108 -0
  73. package/dist/esm/collection/changes.js.map +1 -0
  74. package/dist/esm/{collection-events.d.ts → collection/events.d.ts} +7 -4
  75. package/dist/esm/{collection-events.js → collection/events.js} +7 -5
  76. package/dist/esm/collection/events.js.map +1 -0
  77. package/dist/{cjs/collection.d.cts → esm/collection/index.d.ts} +46 -184
  78. package/dist/esm/collection/index.js +417 -0
  79. package/dist/esm/collection/index.js.map +1 -0
  80. package/dist/esm/collection/indexes.d.ts +47 -0
  81. package/dist/esm/collection/indexes.js +124 -0
  82. package/dist/esm/collection/indexes.js.map +1 -0
  83. package/dist/esm/collection/lifecycle.d.ts +81 -0
  84. package/dist/esm/collection/lifecycle.js +196 -0
  85. package/dist/esm/collection/lifecycle.js.map +1 -0
  86. package/dist/esm/collection/mutations.d.ts +33 -0
  87. package/dist/esm/collection/mutations.js +315 -0
  88. package/dist/esm/collection/mutations.js.map +1 -0
  89. package/dist/esm/collection/state.d.ts +122 -0
  90. package/dist/esm/collection/state.js +597 -0
  91. package/dist/esm/collection/state.js.map +1 -0
  92. package/dist/esm/collection/subscription.d.ts +57 -0
  93. package/dist/esm/collection/subscription.js +160 -0
  94. package/dist/esm/collection/subscription.js.map +1 -0
  95. package/dist/esm/collection/sync.d.ts +34 -0
  96. package/dist/esm/collection/sync.js +154 -0
  97. package/dist/esm/collection/sync.js.map +1 -0
  98. package/dist/esm/index.d.ts +2 -2
  99. package/dist/esm/index.js +1 -1
  100. package/dist/esm/indexes/auto-index.d.ts +1 -1
  101. package/dist/esm/indexes/auto-index.js.map +1 -1
  102. package/dist/esm/indexes/base-index.d.ts +2 -2
  103. package/dist/esm/indexes/base-index.js.map +1 -1
  104. package/dist/esm/indexes/btree-index.d.ts +1 -1
  105. package/dist/esm/indexes/btree-index.js +2 -2
  106. package/dist/esm/indexes/btree-index.js.map +1 -1
  107. package/dist/esm/proxy.js +1 -1
  108. package/dist/esm/query/builder/index.js +1 -1
  109. package/dist/esm/query/builder/index.js.map +1 -1
  110. package/dist/esm/query/builder/types.d.ts +1 -1
  111. package/dist/esm/query/compiler/index.d.ts +3 -2
  112. package/dist/esm/query/compiler/index.js +5 -2
  113. package/dist/esm/query/compiler/index.js.map +1 -1
  114. package/dist/esm/query/compiler/joins.d.ts +3 -2
  115. package/dist/esm/query/compiler/joins.js +22 -24
  116. package/dist/esm/query/compiler/joins.js.map +1 -1
  117. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  118. package/dist/esm/query/compiler/order-by.js.map +1 -1
  119. package/dist/esm/query/ir.d.ts +1 -1
  120. package/dist/esm/query/ir.js.map +1 -1
  121. package/dist/esm/query/live/collection-config-builder.d.ts +3 -0
  122. package/dist/esm/query/live/collection-config-builder.js +29 -12
  123. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  124. package/dist/esm/query/live/collection-subscriber.d.ts +4 -7
  125. package/dist/esm/query/live/collection-subscriber.js +43 -104
  126. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  127. package/dist/esm/query/live-query-collection.d.ts +1 -1
  128. package/dist/esm/query/live-query-collection.js +1 -1
  129. package/dist/esm/query/live-query-collection.js.map +1 -1
  130. package/dist/esm/transactions.js +3 -3
  131. package/dist/esm/transactions.js.map +1 -1
  132. package/dist/esm/types.d.ts +12 -10
  133. package/dist/esm/utils/browser-polyfills.d.ts +9 -0
  134. package/dist/esm/utils/browser-polyfills.js +22 -0
  135. package/dist/esm/utils/browser-polyfills.js.map +1 -0
  136. package/package.json +2 -2
  137. package/src/{change-events.ts → collection/change-events.ts} +25 -39
  138. package/src/collection/changes.ts +163 -0
  139. package/src/{collection-events.ts → collection/events.ts} +8 -6
  140. package/src/collection/index.ts +808 -0
  141. package/src/collection/indexes.ts +172 -0
  142. package/src/collection/lifecycle.ts +289 -0
  143. package/src/collection/mutations.ts +535 -0
  144. package/src/collection/state.ts +866 -0
  145. package/src/collection/subscription.ts +239 -0
  146. package/src/collection/sync.ts +235 -0
  147. package/src/index.ts +2 -2
  148. package/src/indexes/auto-index.ts +1 -1
  149. package/src/indexes/base-index.ts +3 -3
  150. package/src/indexes/btree-index.ts +2 -2
  151. package/src/query/builder/index.ts +1 -1
  152. package/src/query/builder/types.ts +1 -1
  153. package/src/query/compiler/index.ts +7 -1
  154. package/src/query/compiler/joins.ts +28 -41
  155. package/src/query/compiler/order-by.ts +1 -1
  156. package/src/query/ir.ts +1 -1
  157. package/src/query/live/collection-config-builder.ts +48 -22
  158. package/src/query/live/collection-subscriber.ts +63 -168
  159. package/src/query/live-query-collection.ts +2 -2
  160. package/src/transactions.ts +3 -3
  161. package/src/types.ts +14 -15
  162. package/src/utils/browser-polyfills.ts +39 -0
  163. package/dist/cjs/change-events.cjs.map +0 -1
  164. package/dist/cjs/collection-events.cjs.map +0 -1
  165. package/dist/cjs/collection.cjs +0 -1625
  166. package/dist/cjs/collection.cjs.map +0 -1
  167. package/dist/esm/change-events.js.map +0 -1
  168. package/dist/esm/collection-events.js.map +0 -1
  169. package/dist/esm/collection.js +0 -1625
  170. package/dist/esm/collection.js.map +0 -1
  171. package/src/collection.ts +0 -2564
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutations.cjs","sources":["../../../src/collection/mutations.ts"],"sourcesContent":["import { withArrayChangeTracking, withChangeTracking } from \"../proxy\"\nimport { createTransaction, getActiveTransaction } from \"../transactions\"\nimport {\n DeleteKeyNotFoundError,\n DuplicateKeyError,\n InvalidSchemaError,\n KeyUpdateNotAllowedError,\n MissingDeleteHandlerError,\n MissingInsertHandlerError,\n MissingUpdateArgumentError,\n MissingUpdateHandlerError,\n NoKeysPassedToDeleteError,\n NoKeysPassedToUpdateError,\n SchemaMustBeSynchronousError,\n SchemaValidationError,\n UndefinedKeyError,\n UpdateKeyNotFoundError,\n} from \"../errors\"\nimport type { Collection, CollectionImpl } from \"./index.js\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\nimport type {\n CollectionConfig,\n InsertConfig,\n OperationConfig,\n PendingMutation,\n StandardSchema,\n Transaction as TransactionType,\n TransactionWithMutations,\n UtilsRecord,\n WritableDeep,\n} from \"../types\"\nimport type { CollectionLifecycleManager } from \"./lifecycle\"\nimport type { CollectionStateManager } from \"./state\"\n\nexport class CollectionMutationsManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private state!: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n private collection!: CollectionImpl<TOutput, TKey, TUtils, TSchema, TInput>\n private config!: CollectionConfig<TOutput, TKey, TSchema>\n private id: string\n\n constructor(config: CollectionConfig<TOutput, TKey, TSchema>, id: string) {\n this.id = id\n this.config = config\n }\n\n setDeps(deps: {\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n collection: CollectionImpl<TOutput, TKey, TUtils, TSchema, TInput>\n }) {\n this.lifecycle = deps.lifecycle\n this.state = deps.state\n this.collection = deps.collection\n }\n\n private ensureStandardSchema(schema: unknown): StandardSchema<TOutput> {\n // If the schema already implements the standard-schema interface, return it\n if (schema && `~standard` in (schema as {})) {\n return schema as StandardSchema<TOutput>\n }\n\n throw new InvalidSchemaError()\n }\n\n public validateData(\n data: unknown,\n type: `insert` | `update`,\n key?: TKey\n ): TOutput | never {\n if (!this.config.schema) return data as TOutput\n\n const standardSchema = this.ensureStandardSchema(this.config.schema)\n\n // For updates, we need to merge with the existing data before validation\n if (type === `update` && key) {\n // Get the existing data for this key\n const existingData = this.state.get(key)\n\n if (\n existingData &&\n data &&\n typeof data === `object` &&\n typeof existingData === `object`\n ) {\n // Merge the update with the existing data\n const mergedData = Object.assign({}, existingData, data)\n\n // Validate the merged data\n const result = standardSchema[`~standard`].validate(mergedData)\n\n // Ensure validation is synchronous\n if (result instanceof Promise) {\n throw new SchemaMustBeSynchronousError()\n }\n\n // If validation fails, throw a SchemaValidationError with the issues\n if (`issues` in result && result.issues) {\n const typedIssues = result.issues.map((issue) => ({\n message: issue.message,\n path: issue.path?.map((p) => String(p)),\n }))\n throw new SchemaValidationError(type, typedIssues)\n }\n\n // Extract only the modified keys from the validated result\n const validatedMergedData = result.value as TOutput\n const modifiedKeys = Object.keys(data)\n const extractedChanges = Object.fromEntries(\n modifiedKeys.map((k) => [k, validatedMergedData[k as keyof TOutput]])\n ) as TOutput\n\n return extractedChanges\n }\n }\n\n // For inserts or updates without existing data, validate the data directly\n const result = standardSchema[`~standard`].validate(data)\n\n // Ensure validation is synchronous\n if (result instanceof Promise) {\n throw new SchemaMustBeSynchronousError()\n }\n\n // If validation fails, throw a SchemaValidationError with the issues\n if (`issues` in result && result.issues) {\n const typedIssues = result.issues.map((issue) => ({\n message: issue.message,\n path: issue.path?.map((p) => String(p)),\n }))\n throw new SchemaValidationError(type, typedIssues)\n }\n\n return result.value as TOutput\n }\n\n public generateGlobalKey(key: any, item: any): string {\n if (typeof key === `undefined`) {\n throw new UndefinedKeyError(item)\n }\n\n return `KEY::${this.id}/${key}`\n }\n\n /**\n * Inserts one or more items into the collection\n */\n insert = (data: TInput | Array<TInput>, config?: InsertConfig) => {\n this.lifecycle.validateCollectionUsable(`insert`)\n const state = this.state\n const ambientTransaction = getActiveTransaction()\n\n // If no ambient transaction exists, check for an onInsert handler early\n if (!ambientTransaction && !this.config.onInsert) {\n throw new MissingInsertHandlerError()\n }\n\n const items = Array.isArray(data) ? data : [data]\n const mutations: Array<PendingMutation<TOutput>> = []\n\n // Create mutations for each item\n items.forEach((item) => {\n // Validate the data against the schema if one exists\n const validatedData = this.validateData(item, `insert`)\n\n // Check if an item with this ID already exists in the collection\n const key = this.config.getKey(validatedData)\n if (this.state.has(key)) {\n throw new DuplicateKeyError(key)\n }\n const globalKey = this.generateGlobalKey(key, item)\n\n const mutation: PendingMutation<TOutput, `insert`> = {\n mutationId: crypto.randomUUID(),\n original: {},\n modified: validatedData,\n // Pick the values from validatedData based on what's passed in - this is for cases\n // where a schema has default values. The validated data has the extra default\n // values but for changes, we just want to show the data that was actually passed in.\n changes: Object.fromEntries(\n Object.keys(item).map((k) => [\n k,\n validatedData[k as keyof typeof validatedData],\n ])\n ) as TInput,\n globalKey,\n key,\n metadata: config?.metadata as unknown,\n syncMetadata: this.config.sync.getSyncMetadata?.() || {},\n optimistic: config?.optimistic ?? true,\n type: `insert`,\n createdAt: new Date(),\n updatedAt: new Date(),\n collection: this.collection,\n }\n\n mutations.push(mutation)\n })\n\n // If an ambient transaction exists, use it\n if (ambientTransaction) {\n ambientTransaction.applyMutations(mutations)\n\n state.transactions.set(ambientTransaction.id, ambientTransaction)\n state.scheduleTransactionCleanup(ambientTransaction)\n state.recomputeOptimisticState(true)\n\n return ambientTransaction\n } else {\n // Create a new transaction with a mutation function that calls the onInsert handler\n const directOpTransaction = createTransaction<TOutput>({\n mutationFn: async (params) => {\n // Call the onInsert handler with the transaction and collection\n return await this.config.onInsert!({\n transaction:\n params.transaction as unknown as TransactionWithMutations<\n TOutput,\n `insert`\n >,\n collection: this.collection as unknown as Collection<TOutput, TKey>,\n })\n },\n })\n\n // Apply mutations to the new transaction\n directOpTransaction.applyMutations(mutations)\n directOpTransaction.commit()\n\n // Add the transaction to the collection's transactions store\n state.transactions.set(directOpTransaction.id, directOpTransaction)\n state.scheduleTransactionCleanup(directOpTransaction)\n state.recomputeOptimisticState(true)\n\n return directOpTransaction\n }\n }\n\n /**\n * Updates one or more items in the collection using a callback function\n */\n update(\n keys: (TKey | unknown) | Array<TKey | unknown>,\n configOrCallback:\n | ((draft: WritableDeep<TInput>) => void)\n | ((drafts: Array<WritableDeep<TInput>>) => void)\n | OperationConfig,\n maybeCallback?:\n | ((draft: WritableDeep<TInput>) => void)\n | ((drafts: Array<WritableDeep<TInput>>) => void)\n ) {\n if (typeof keys === `undefined`) {\n throw new MissingUpdateArgumentError()\n }\n\n const state = this.state\n this.lifecycle.validateCollectionUsable(`update`)\n\n const ambientTransaction = getActiveTransaction()\n\n // If no ambient transaction exists, check for an onUpdate handler early\n if (!ambientTransaction && !this.config.onUpdate) {\n throw new MissingUpdateHandlerError()\n }\n\n const isArray = Array.isArray(keys)\n const keysArray = isArray ? keys : [keys]\n\n if (isArray && keysArray.length === 0) {\n throw new NoKeysPassedToUpdateError()\n }\n\n const callback =\n typeof configOrCallback === `function` ? configOrCallback : maybeCallback!\n const config =\n typeof configOrCallback === `function` ? {} : configOrCallback\n\n // Get the current objects or empty objects if they don't exist\n const currentObjects = keysArray.map((key) => {\n const item = this.state.get(key)\n if (!item) {\n throw new UpdateKeyNotFoundError(key)\n }\n\n return item\n }) as unknown as Array<TInput>\n\n let changesArray\n if (isArray) {\n // Use the proxy to track changes for all objects\n changesArray = withArrayChangeTracking(\n currentObjects,\n callback as (draft: Array<TInput>) => void\n )\n } else {\n const result = withChangeTracking(\n currentObjects[0]!,\n callback as (draft: TInput) => void\n )\n changesArray = [result]\n }\n\n // Create mutations for each object that has changes\n const mutations: Array<\n PendingMutation<\n TOutput,\n `update`,\n CollectionImpl<TOutput, TKey, TUtils, TSchema, TInput>\n >\n > = keysArray\n .map((key, index) => {\n const itemChanges = changesArray[index] // User-provided changes for this specific item\n\n // Skip items with no changes\n if (!itemChanges || Object.keys(itemChanges).length === 0) {\n return null\n }\n\n const originalItem = currentObjects[index] as unknown as TOutput\n // Validate the user-provided changes for this item\n const validatedUpdatePayload = this.validateData(\n itemChanges,\n `update`,\n key\n )\n\n // Construct the full modified item by applying the validated update payload to the original item\n const modifiedItem = Object.assign(\n {},\n originalItem,\n validatedUpdatePayload\n )\n\n // Check if the ID of the item is being changed\n const originalItemId = this.config.getKey(originalItem)\n const modifiedItemId = this.config.getKey(modifiedItem)\n\n if (originalItemId !== modifiedItemId) {\n throw new KeyUpdateNotAllowedError(originalItemId, modifiedItemId)\n }\n\n const globalKey = this.generateGlobalKey(modifiedItemId, modifiedItem)\n\n return {\n mutationId: crypto.randomUUID(),\n original: originalItem,\n modified: modifiedItem,\n // Pick the values from modifiedItem based on what's passed in - this is for cases\n // where a schema has default values or transforms. The modified data has the extra\n // default or transformed values but for changes, we just want to show the data that\n // was actually passed in.\n changes: Object.fromEntries(\n Object.keys(itemChanges).map((k) => [\n k,\n modifiedItem[k as keyof typeof modifiedItem],\n ])\n ) as TInput,\n globalKey,\n key,\n metadata: config.metadata as unknown,\n syncMetadata: (state.syncedMetadata.get(key) || {}) as Record<\n string,\n unknown\n >,\n optimistic: config.optimistic ?? true,\n type: `update`,\n createdAt: new Date(),\n updatedAt: new Date(),\n collection: this.collection,\n }\n })\n .filter(Boolean) as Array<\n PendingMutation<\n TOutput,\n `update`,\n CollectionImpl<TOutput, TKey, TUtils, TSchema, TInput>\n >\n >\n\n // If no changes were made, return an empty transaction early\n if (mutations.length === 0) {\n const emptyTransaction = createTransaction({\n mutationFn: async () => {},\n })\n emptyTransaction.commit()\n // Schedule cleanup for empty transaction\n state.scheduleTransactionCleanup(emptyTransaction)\n return emptyTransaction\n }\n\n // If an ambient transaction exists, use it\n if (ambientTransaction) {\n ambientTransaction.applyMutations(mutations)\n\n state.transactions.set(ambientTransaction.id, ambientTransaction)\n state.scheduleTransactionCleanup(ambientTransaction)\n state.recomputeOptimisticState(true)\n\n return ambientTransaction\n }\n\n // No need to check for onUpdate handler here as we've already checked at the beginning\n\n // Create a new transaction with a mutation function that calls the onUpdate handler\n const directOpTransaction = createTransaction<TOutput>({\n mutationFn: async (params) => {\n // Call the onUpdate handler with the transaction and collection\n return this.config.onUpdate!({\n transaction:\n params.transaction as unknown as TransactionWithMutations<\n TOutput,\n `update`\n >,\n collection: this.collection as unknown as Collection<TOutput, TKey>,\n })\n },\n })\n\n // Apply mutations to the new transaction\n directOpTransaction.applyMutations(mutations)\n directOpTransaction.commit()\n\n // Add the transaction to the collection's transactions store\n\n state.transactions.set(directOpTransaction.id, directOpTransaction)\n state.scheduleTransactionCleanup(directOpTransaction)\n state.recomputeOptimisticState(true)\n\n return directOpTransaction\n }\n\n /**\n * Deletes one or more items from the collection\n */\n delete = (\n keys: Array<TKey> | TKey,\n config?: OperationConfig\n ): TransactionType<any> => {\n const state = this.state\n this.lifecycle.validateCollectionUsable(`delete`)\n\n const ambientTransaction = getActiveTransaction()\n\n // If no ambient transaction exists, check for an onDelete handler early\n if (!ambientTransaction && !this.config.onDelete) {\n throw new MissingDeleteHandlerError()\n }\n\n if (Array.isArray(keys) && keys.length === 0) {\n throw new NoKeysPassedToDeleteError()\n }\n\n const keysArray = Array.isArray(keys) ? keys : [keys]\n const mutations: Array<\n PendingMutation<\n TOutput,\n `delete`,\n CollectionImpl<TOutput, TKey, TUtils, TSchema, TInput>\n >\n > = []\n\n for (const key of keysArray) {\n if (!this.state.has(key)) {\n throw new DeleteKeyNotFoundError(key)\n }\n const globalKey = this.generateGlobalKey(key, this.state.get(key)!)\n const mutation: PendingMutation<\n TOutput,\n `delete`,\n CollectionImpl<TOutput, TKey, TUtils, TSchema, TInput>\n > = {\n mutationId: crypto.randomUUID(),\n original: this.state.get(key)!,\n modified: this.state.get(key)!,\n changes: this.state.get(key)!,\n globalKey,\n key,\n metadata: config?.metadata as unknown,\n syncMetadata: (state.syncedMetadata.get(key) || {}) as Record<\n string,\n unknown\n >,\n optimistic: config?.optimistic ?? true,\n type: `delete`,\n createdAt: new Date(),\n updatedAt: new Date(),\n collection: this.collection,\n }\n\n mutations.push(mutation)\n }\n\n // If an ambient transaction exists, use it\n if (ambientTransaction) {\n ambientTransaction.applyMutations(mutations)\n\n state.transactions.set(ambientTransaction.id, ambientTransaction)\n state.scheduleTransactionCleanup(ambientTransaction)\n state.recomputeOptimisticState(true)\n\n return ambientTransaction\n }\n\n // Create a new transaction with a mutation function that calls the onDelete handler\n const directOpTransaction = createTransaction<TOutput>({\n autoCommit: true,\n mutationFn: async (params) => {\n // Call the onDelete handler with the transaction and collection\n return this.config.onDelete!({\n transaction:\n params.transaction as unknown as TransactionWithMutations<\n TOutput,\n `delete`\n >,\n collection: this.collection as unknown as Collection<TOutput, TKey>,\n })\n },\n })\n\n // Apply mutations to the new transaction\n directOpTransaction.applyMutations(mutations)\n directOpTransaction.commit()\n\n state.transactions.set(directOpTransaction.id, directOpTransaction)\n state.scheduleTransactionCleanup(directOpTransaction)\n state.recomputeOptimisticState(true)\n\n return directOpTransaction\n }\n}\n"],"names":["config","getActiveTransaction","MissingInsertHandlerError","DuplicateKeyError","createTransaction","MissingDeleteHandlerError","NoKeysPassedToDeleteError","DeleteKeyNotFoundError","InvalidSchemaError","result","SchemaMustBeSynchronousError","SchemaValidationError","UndefinedKeyError","MissingUpdateArgumentError","MissingUpdateHandlerError","NoKeysPassedToUpdateError","UpdateKeyNotFoundError","withArrayChangeTracking","withChangeTracking","KeyUpdateNotAllowedError"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { CollectionImpl } from './index.js';
2
+ import { StandardSchemaV1 } from '@standard-schema/spec';
3
+ import { CollectionConfig, InsertConfig, OperationConfig, Transaction as TransactionType, UtilsRecord, WritableDeep } from '../types.cjs';
4
+ import { CollectionLifecycleManager } from './lifecycle.cjs';
5
+ import { CollectionStateManager } from './state.cjs';
6
+ export declare class CollectionMutationsManager<TOutput extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInput extends object = TOutput> {
7
+ private lifecycle;
8
+ private state;
9
+ private collection;
10
+ private config;
11
+ private id;
12
+ constructor(config: CollectionConfig<TOutput, TKey, TSchema>, id: string);
13
+ setDeps(deps: {
14
+ lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>;
15
+ state: CollectionStateManager<TOutput, TKey, TSchema, TInput>;
16
+ collection: CollectionImpl<TOutput, TKey, TUtils, TSchema, TInput>;
17
+ }): void;
18
+ private ensureStandardSchema;
19
+ validateData(data: unknown, type: `insert` | `update`, key?: TKey): TOutput | never;
20
+ generateGlobalKey(key: any, item: any): string;
21
+ /**
22
+ * Inserts one or more items into the collection
23
+ */
24
+ insert: (data: TInput | Array<TInput>, config?: InsertConfig) => TransactionType<Record<string, unknown>> | TransactionType<TOutput>;
25
+ /**
26
+ * Updates one or more items in the collection using a callback function
27
+ */
28
+ update(keys: (TKey | unknown) | Array<TKey | unknown>, configOrCallback: ((draft: WritableDeep<TInput>) => void) | ((drafts: Array<WritableDeep<TInput>>) => void) | OperationConfig, maybeCallback?: ((draft: WritableDeep<TInput>) => void) | ((drafts: Array<WritableDeep<TInput>>) => void)): TransactionType<Record<string, unknown>> | TransactionType<TOutput>;
29
+ /**
30
+ * Deletes one or more items from the collection
31
+ */
32
+ delete: (keys: Array<TKey> | TKey, config?: OperationConfig) => TransactionType<any>;
33
+ }
@@ -0,0 +1,597 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const utils = require("../utils.cjs");
4
+ const SortedMap = require("../SortedMap.cjs");
5
+ class CollectionStateManager {
6
+ /**
7
+ * Creates a new CollectionState manager
8
+ */
9
+ constructor(config) {
10
+ this.pendingSyncedTransactions = [];
11
+ this.syncedMetadata = /* @__PURE__ */ new Map();
12
+ this.optimisticUpserts = /* @__PURE__ */ new Map();
13
+ this.optimisticDeletes = /* @__PURE__ */ new Set();
14
+ this.size = 0;
15
+ this.syncedKeys = /* @__PURE__ */ new Set();
16
+ this.preSyncVisibleState = /* @__PURE__ */ new Map();
17
+ this.recentlySyncedKeys = /* @__PURE__ */ new Set();
18
+ this.hasReceivedFirstCommit = false;
19
+ this.isCommittingSyncTransactions = false;
20
+ this.commitPendingTransactions = () => {
21
+ let hasPersistingTransaction = false;
22
+ for (const transaction of this.transactions.values()) {
23
+ if (transaction.state === `persisting`) {
24
+ hasPersistingTransaction = true;
25
+ break;
26
+ }
27
+ }
28
+ const {
29
+ committedSyncedTransactions,
30
+ uncommittedSyncedTransactions,
31
+ hasTruncateSync
32
+ } = this.pendingSyncedTransactions.reduce(
33
+ (acc, t) => {
34
+ if (t.committed) {
35
+ acc.committedSyncedTransactions.push(t);
36
+ if (t.truncate === true) {
37
+ acc.hasTruncateSync = true;
38
+ }
39
+ } else {
40
+ acc.uncommittedSyncedTransactions.push(t);
41
+ }
42
+ return acc;
43
+ },
44
+ {
45
+ committedSyncedTransactions: [],
46
+ uncommittedSyncedTransactions: [],
47
+ hasTruncateSync: false
48
+ }
49
+ );
50
+ if (!hasPersistingTransaction || hasTruncateSync) {
51
+ this.isCommittingSyncTransactions = true;
52
+ const changedKeys = /* @__PURE__ */ new Set();
53
+ for (const transaction of committedSyncedTransactions) {
54
+ for (const operation of transaction.operations) {
55
+ changedKeys.add(operation.key);
56
+ }
57
+ }
58
+ let currentVisibleState = this.preSyncVisibleState;
59
+ if (currentVisibleState.size === 0) {
60
+ currentVisibleState = /* @__PURE__ */ new Map();
61
+ for (const key of changedKeys) {
62
+ const currentValue = this.get(key);
63
+ if (currentValue !== void 0) {
64
+ currentVisibleState.set(key, currentValue);
65
+ }
66
+ }
67
+ }
68
+ const events = [];
69
+ const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`;
70
+ for (const transaction of committedSyncedTransactions) {
71
+ if (transaction.truncate) {
72
+ for (const key of this.syncedData.keys()) {
73
+ if (this.optimisticDeletes.has(key)) continue;
74
+ const previousValue = this.optimisticUpserts.get(key) || this.syncedData.get(key);
75
+ if (previousValue !== void 0) {
76
+ events.push({ type: `delete`, key, value: previousValue });
77
+ }
78
+ }
79
+ this.syncedData.clear();
80
+ this.syncedMetadata.clear();
81
+ this.syncedKeys.clear();
82
+ for (const key of changedKeys) {
83
+ currentVisibleState.delete(key);
84
+ }
85
+ }
86
+ for (const operation of transaction.operations) {
87
+ const key = operation.key;
88
+ this.syncedKeys.add(key);
89
+ switch (operation.type) {
90
+ case `insert`:
91
+ this.syncedMetadata.set(key, operation.metadata);
92
+ break;
93
+ case `update`:
94
+ this.syncedMetadata.set(
95
+ key,
96
+ Object.assign(
97
+ {},
98
+ this.syncedMetadata.get(key),
99
+ operation.metadata
100
+ )
101
+ );
102
+ break;
103
+ case `delete`:
104
+ this.syncedMetadata.delete(key);
105
+ break;
106
+ }
107
+ switch (operation.type) {
108
+ case `insert`:
109
+ this.syncedData.set(key, operation.value);
110
+ break;
111
+ case `update`: {
112
+ if (rowUpdateMode === `partial`) {
113
+ const updatedValue = Object.assign(
114
+ {},
115
+ this.syncedData.get(key),
116
+ operation.value
117
+ );
118
+ this.syncedData.set(key, updatedValue);
119
+ } else {
120
+ this.syncedData.set(key, operation.value);
121
+ }
122
+ break;
123
+ }
124
+ case `delete`:
125
+ this.syncedData.delete(key);
126
+ break;
127
+ }
128
+ }
129
+ }
130
+ if (hasTruncateSync) {
131
+ const syncedInsertedOrUpdatedKeys = /* @__PURE__ */ new Set();
132
+ for (const t of committedSyncedTransactions) {
133
+ for (const op of t.operations) {
134
+ if (op.type === `insert` || op.type === `update`) {
135
+ syncedInsertedOrUpdatedKeys.add(op.key);
136
+ }
137
+ }
138
+ }
139
+ const reapplyUpserts = /* @__PURE__ */ new Map();
140
+ const reapplyDeletes = /* @__PURE__ */ new Set();
141
+ for (const tx of this.transactions.values()) {
142
+ if ([`completed`, `failed`].includes(tx.state)) continue;
143
+ for (const mutation of tx.mutations) {
144
+ if (!this.isThisCollection(mutation.collection) || !mutation.optimistic)
145
+ continue;
146
+ const key = mutation.key;
147
+ switch (mutation.type) {
148
+ case `insert`:
149
+ reapplyUpserts.set(key, mutation.modified);
150
+ reapplyDeletes.delete(key);
151
+ break;
152
+ case `update`: {
153
+ const base = this.syncedData.get(key);
154
+ const next = base ? Object.assign({}, base, mutation.changes) : mutation.modified;
155
+ reapplyUpserts.set(key, next);
156
+ reapplyDeletes.delete(key);
157
+ break;
158
+ }
159
+ case `delete`:
160
+ reapplyUpserts.delete(key);
161
+ reapplyDeletes.add(key);
162
+ break;
163
+ }
164
+ }
165
+ }
166
+ for (const [key, value] of reapplyUpserts) {
167
+ if (reapplyDeletes.has(key)) continue;
168
+ if (syncedInsertedOrUpdatedKeys.has(key)) {
169
+ let foundInsert = false;
170
+ for (let i = events.length - 1; i >= 0; i--) {
171
+ const evt = events[i];
172
+ if (evt.key === key && evt.type === `insert`) {
173
+ evt.value = value;
174
+ foundInsert = true;
175
+ break;
176
+ }
177
+ }
178
+ if (!foundInsert) {
179
+ events.push({ type: `insert`, key, value });
180
+ }
181
+ } else {
182
+ events.push({ type: `insert`, key, value });
183
+ }
184
+ }
185
+ if (events.length > 0 && reapplyDeletes.size > 0) {
186
+ const filtered = [];
187
+ for (const evt of events) {
188
+ if (evt.type === `insert` && reapplyDeletes.has(evt.key)) {
189
+ continue;
190
+ }
191
+ filtered.push(evt);
192
+ }
193
+ events.length = 0;
194
+ events.push(...filtered);
195
+ }
196
+ if (this.lifecycle.status !== `ready`) {
197
+ this.lifecycle.setStatus(`ready`);
198
+ }
199
+ }
200
+ this.optimisticUpserts.clear();
201
+ this.optimisticDeletes.clear();
202
+ this.isCommittingSyncTransactions = false;
203
+ for (const transaction of this.transactions.values()) {
204
+ if (![`completed`, `failed`].includes(transaction.state)) {
205
+ for (const mutation of transaction.mutations) {
206
+ if (this.isThisCollection(mutation.collection) && mutation.optimistic) {
207
+ switch (mutation.type) {
208
+ case `insert`:
209
+ case `update`:
210
+ this.optimisticUpserts.set(
211
+ mutation.key,
212
+ mutation.modified
213
+ );
214
+ this.optimisticDeletes.delete(mutation.key);
215
+ break;
216
+ case `delete`:
217
+ this.optimisticUpserts.delete(mutation.key);
218
+ this.optimisticDeletes.add(mutation.key);
219
+ break;
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ const completedOptimisticOps = /* @__PURE__ */ new Map();
226
+ for (const transaction of this.transactions.values()) {
227
+ if (transaction.state === `completed`) {
228
+ for (const mutation of transaction.mutations) {
229
+ if (this.isThisCollection(mutation.collection) && changedKeys.has(mutation.key)) {
230
+ completedOptimisticOps.set(mutation.key, {
231
+ type: mutation.type,
232
+ value: mutation.modified
233
+ });
234
+ }
235
+ }
236
+ }
237
+ }
238
+ for (const key of changedKeys) {
239
+ const previousVisibleValue = currentVisibleState.get(key);
240
+ const newVisibleValue = this.get(key);
241
+ const completedOp = completedOptimisticOps.get(key);
242
+ const isRedundantSync = completedOp && newVisibleValue !== void 0 && utils.deepEquals(completedOp.value, newVisibleValue);
243
+ if (!isRedundantSync) {
244
+ if (previousVisibleValue === void 0 && newVisibleValue !== void 0) {
245
+ events.push({
246
+ type: `insert`,
247
+ key,
248
+ value: newVisibleValue
249
+ });
250
+ } else if (previousVisibleValue !== void 0 && newVisibleValue === void 0) {
251
+ events.push({
252
+ type: `delete`,
253
+ key,
254
+ value: previousVisibleValue
255
+ });
256
+ } else if (previousVisibleValue !== void 0 && newVisibleValue !== void 0 && !utils.deepEquals(previousVisibleValue, newVisibleValue)) {
257
+ events.push({
258
+ type: `update`,
259
+ key,
260
+ value: newVisibleValue,
261
+ previousValue: previousVisibleValue
262
+ });
263
+ }
264
+ }
265
+ }
266
+ this.size = this.calculateSize();
267
+ if (events.length > 0) {
268
+ this.indexes.updateIndexes(events);
269
+ }
270
+ this.changes.emitEvents(events, true);
271
+ this.pendingSyncedTransactions = uncommittedSyncedTransactions;
272
+ this.preSyncVisibleState.clear();
273
+ Promise.resolve().then(() => {
274
+ this.recentlySyncedKeys.clear();
275
+ });
276
+ if (!this.hasReceivedFirstCommit) {
277
+ this.hasReceivedFirstCommit = true;
278
+ const callbacks = [...this.lifecycle.onFirstReadyCallbacks];
279
+ this.lifecycle.onFirstReadyCallbacks = [];
280
+ callbacks.forEach((callback) => callback());
281
+ }
282
+ }
283
+ };
284
+ this.config = config;
285
+ this.transactions = new SortedMap.SortedMap(
286
+ (a, b) => a.compareCreatedAt(b)
287
+ );
288
+ if (config.compare) {
289
+ this.syncedData = new SortedMap.SortedMap(config.compare);
290
+ } else {
291
+ this.syncedData = /* @__PURE__ */ new Map();
292
+ }
293
+ }
294
+ setDeps(deps) {
295
+ this.collection = deps.collection;
296
+ this.lifecycle = deps.lifecycle;
297
+ this.changes = deps.changes;
298
+ this.indexes = deps.indexes;
299
+ }
300
+ /**
301
+ * Get the current value for a key (virtual derived state)
302
+ */
303
+ get(key) {
304
+ const { optimisticDeletes, optimisticUpserts, syncedData } = this;
305
+ if (optimisticDeletes.has(key)) {
306
+ return void 0;
307
+ }
308
+ if (optimisticUpserts.has(key)) {
309
+ return optimisticUpserts.get(key);
310
+ }
311
+ return syncedData.get(key);
312
+ }
313
+ /**
314
+ * Check if a key exists in the collection (virtual derived state)
315
+ */
316
+ has(key) {
317
+ const { optimisticDeletes, optimisticUpserts, syncedData } = this;
318
+ if (optimisticDeletes.has(key)) {
319
+ return false;
320
+ }
321
+ if (optimisticUpserts.has(key)) {
322
+ return true;
323
+ }
324
+ return syncedData.has(key);
325
+ }
326
+ /**
327
+ * Get all keys (virtual derived state)
328
+ */
329
+ *keys() {
330
+ const { syncedData, optimisticDeletes, optimisticUpserts } = this;
331
+ for (const key of syncedData.keys()) {
332
+ if (!optimisticDeletes.has(key)) {
333
+ yield key;
334
+ }
335
+ }
336
+ for (const key of optimisticUpserts.keys()) {
337
+ if (!syncedData.has(key) && !optimisticDeletes.has(key)) {
338
+ yield key;
339
+ }
340
+ }
341
+ }
342
+ /**
343
+ * Get all values (virtual derived state)
344
+ */
345
+ *values() {
346
+ for (const key of this.keys()) {
347
+ const value = this.get(key);
348
+ if (value !== void 0) {
349
+ yield value;
350
+ }
351
+ }
352
+ }
353
+ /**
354
+ * Get all entries (virtual derived state)
355
+ */
356
+ *entries() {
357
+ for (const key of this.keys()) {
358
+ const value = this.get(key);
359
+ if (value !== void 0) {
360
+ yield [key, value];
361
+ }
362
+ }
363
+ }
364
+ /**
365
+ * Get all entries (virtual derived state)
366
+ */
367
+ *[Symbol.iterator]() {
368
+ for (const [key, value] of this.entries()) {
369
+ yield [key, value];
370
+ }
371
+ }
372
+ /**
373
+ * Execute a callback for each entry in the collection
374
+ */
375
+ forEach(callbackfn) {
376
+ let index = 0;
377
+ for (const [key, value] of this.entries()) {
378
+ callbackfn(value, key, index++);
379
+ }
380
+ }
381
+ /**
382
+ * Create a new array with the results of calling a function for each entry in the collection
383
+ */
384
+ map(callbackfn) {
385
+ const result = [];
386
+ let index = 0;
387
+ for (const [key, value] of this.entries()) {
388
+ result.push(callbackfn(value, key, index++));
389
+ }
390
+ return result;
391
+ }
392
+ /**
393
+ * Check if the given collection is this collection
394
+ * @param collection The collection to check
395
+ * @returns True if the given collection is this collection, false otherwise
396
+ */
397
+ isThisCollection(collection) {
398
+ return collection === this.collection;
399
+ }
400
+ /**
401
+ * Recompute optimistic state from active transactions
402
+ */
403
+ recomputeOptimisticState(triggeredByUserAction = false) {
404
+ if (this.isCommittingSyncTransactions) {
405
+ return;
406
+ }
407
+ const previousState = new Map(this.optimisticUpserts);
408
+ const previousDeletes = new Set(this.optimisticDeletes);
409
+ this.optimisticUpserts.clear();
410
+ this.optimisticDeletes.clear();
411
+ const activeTransactions = [];
412
+ for (const transaction of this.transactions.values()) {
413
+ if (![`completed`, `failed`].includes(transaction.state)) {
414
+ activeTransactions.push(transaction);
415
+ }
416
+ }
417
+ for (const transaction of activeTransactions) {
418
+ for (const mutation of transaction.mutations) {
419
+ if (this.isThisCollection(mutation.collection) && mutation.optimistic) {
420
+ switch (mutation.type) {
421
+ case `insert`:
422
+ case `update`:
423
+ this.optimisticUpserts.set(
424
+ mutation.key,
425
+ mutation.modified
426
+ );
427
+ this.optimisticDeletes.delete(mutation.key);
428
+ break;
429
+ case `delete`:
430
+ this.optimisticUpserts.delete(mutation.key);
431
+ this.optimisticDeletes.add(mutation.key);
432
+ break;
433
+ }
434
+ }
435
+ }
436
+ }
437
+ this.size = this.calculateSize();
438
+ const events = [];
439
+ this.collectOptimisticChanges(previousState, previousDeletes, events);
440
+ const filteredEventsBySyncStatus = events.filter((event) => {
441
+ if (!this.recentlySyncedKeys.has(event.key)) {
442
+ return true;
443
+ }
444
+ if (triggeredByUserAction) {
445
+ return true;
446
+ }
447
+ return false;
448
+ });
449
+ if (this.pendingSyncedTransactions.length > 0 && !triggeredByUserAction) {
450
+ const pendingSyncKeys = /* @__PURE__ */ new Set();
451
+ for (const transaction of this.pendingSyncedTransactions) {
452
+ for (const operation of transaction.operations) {
453
+ pendingSyncKeys.add(operation.key);
454
+ }
455
+ }
456
+ const filteredEvents = filteredEventsBySyncStatus.filter((event) => {
457
+ if (event.type === `delete` && pendingSyncKeys.has(event.key)) {
458
+ const hasActiveOptimisticMutation = activeTransactions.some(
459
+ (tx) => tx.mutations.some(
460
+ (m) => this.isThisCollection(m.collection) && m.key === event.key
461
+ )
462
+ );
463
+ if (!hasActiveOptimisticMutation) {
464
+ return false;
465
+ }
466
+ }
467
+ return true;
468
+ });
469
+ if (filteredEvents.length > 0) {
470
+ this.indexes.updateIndexes(filteredEvents);
471
+ }
472
+ this.changes.emitEvents(filteredEvents, triggeredByUserAction);
473
+ } else {
474
+ if (filteredEventsBySyncStatus.length > 0) {
475
+ this.indexes.updateIndexes(filteredEventsBySyncStatus);
476
+ }
477
+ this.changes.emitEvents(filteredEventsBySyncStatus, triggeredByUserAction);
478
+ }
479
+ }
480
+ /**
481
+ * Calculate the current size based on synced data and optimistic changes
482
+ */
483
+ calculateSize() {
484
+ const syncedSize = this.syncedData.size;
485
+ const deletesFromSynced = Array.from(this.optimisticDeletes).filter(
486
+ (key) => this.syncedData.has(key) && !this.optimisticUpserts.has(key)
487
+ ).length;
488
+ const upsertsNotInSynced = Array.from(this.optimisticUpserts.keys()).filter(
489
+ (key) => !this.syncedData.has(key)
490
+ ).length;
491
+ return syncedSize - deletesFromSynced + upsertsNotInSynced;
492
+ }
493
+ /**
494
+ * Collect events for optimistic changes
495
+ */
496
+ collectOptimisticChanges(previousUpserts, previousDeletes, events) {
497
+ const allKeys = /* @__PURE__ */ new Set([
498
+ ...previousUpserts.keys(),
499
+ ...this.optimisticUpserts.keys(),
500
+ ...previousDeletes,
501
+ ...this.optimisticDeletes
502
+ ]);
503
+ for (const key of allKeys) {
504
+ const currentValue = this.get(key);
505
+ const previousValue = this.getPreviousValue(
506
+ key,
507
+ previousUpserts,
508
+ previousDeletes
509
+ );
510
+ if (previousValue !== void 0 && currentValue === void 0) {
511
+ events.push({ type: `delete`, key, value: previousValue });
512
+ } else if (previousValue === void 0 && currentValue !== void 0) {
513
+ events.push({ type: `insert`, key, value: currentValue });
514
+ } else if (previousValue !== void 0 && currentValue !== void 0 && previousValue !== currentValue) {
515
+ events.push({
516
+ type: `update`,
517
+ key,
518
+ value: currentValue,
519
+ previousValue
520
+ });
521
+ }
522
+ }
523
+ }
524
+ /**
525
+ * Get the previous value for a key given previous optimistic state
526
+ */
527
+ getPreviousValue(key, previousUpserts, previousDeletes) {
528
+ if (previousDeletes.has(key)) {
529
+ return void 0;
530
+ }
531
+ if (previousUpserts.has(key)) {
532
+ return previousUpserts.get(key);
533
+ }
534
+ return this.syncedData.get(key);
535
+ }
536
+ /**
537
+ * Schedule cleanup of a transaction when it completes
538
+ */
539
+ scheduleTransactionCleanup(transaction) {
540
+ if (transaction.state === `completed`) {
541
+ this.transactions.delete(transaction.id);
542
+ return;
543
+ }
544
+ transaction.isPersisted.promise.then(() => {
545
+ this.transactions.delete(transaction.id);
546
+ }).catch(() => {
547
+ });
548
+ }
549
+ /**
550
+ * Capture visible state for keys that will be affected by pending sync operations
551
+ * This must be called BEFORE onTransactionStateChange clears optimistic state
552
+ */
553
+ capturePreSyncVisibleState() {
554
+ if (this.pendingSyncedTransactions.length === 0) return;
555
+ this.preSyncVisibleState.clear();
556
+ const syncedKeys = /* @__PURE__ */ new Set();
557
+ for (const transaction of this.pendingSyncedTransactions) {
558
+ for (const operation of transaction.operations) {
559
+ syncedKeys.add(operation.key);
560
+ }
561
+ }
562
+ for (const key of syncedKeys) {
563
+ this.recentlySyncedKeys.add(key);
564
+ }
565
+ for (const key of syncedKeys) {
566
+ const currentValue = this.get(key);
567
+ if (currentValue !== void 0) {
568
+ this.preSyncVisibleState.set(key, currentValue);
569
+ }
570
+ }
571
+ }
572
+ /**
573
+ * Trigger a recomputation when transactions change
574
+ * This method should be called by the Transaction class when state changes
575
+ */
576
+ onTransactionStateChange() {
577
+ this.changes.shouldBatchEvents = this.pendingSyncedTransactions.length > 0;
578
+ this.capturePreSyncVisibleState();
579
+ this.recomputeOptimisticState(false);
580
+ }
581
+ /**
582
+ * Clean up the collection by stopping sync and clearing data
583
+ * This can be called manually or automatically by garbage collection
584
+ */
585
+ cleanup() {
586
+ this.syncedData.clear();
587
+ this.syncedMetadata.clear();
588
+ this.optimisticUpserts.clear();
589
+ this.optimisticDeletes.clear();
590
+ this.size = 0;
591
+ this.pendingSyncedTransactions = [];
592
+ this.syncedKeys.clear();
593
+ this.hasReceivedFirstCommit = false;
594
+ }
595
+ }
596
+ exports.CollectionStateManager = CollectionStateManager;
597
+ //# sourceMappingURL=state.cjs.map