@tanstack/db 0.0.21 → 0.0.23

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 (38) hide show
  1. package/dist/cjs/collection.cjs +14 -6
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +10 -9
  4. package/dist/cjs/local-storage.cjs +1 -1
  5. package/dist/cjs/local-storage.cjs.map +1 -1
  6. package/dist/cjs/query/builder/index.cjs +20 -0
  7. package/dist/cjs/query/builder/index.cjs.map +1 -1
  8. package/dist/cjs/query/builder/index.d.cts +15 -0
  9. package/dist/cjs/query/compiler/index.cjs +6 -0
  10. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  11. package/dist/cjs/query/ir.cjs.map +1 -1
  12. package/dist/cjs/query/ir.d.cts +1 -0
  13. package/dist/cjs/transactions.cjs.map +1 -1
  14. package/dist/cjs/transactions.d.cts +5 -5
  15. package/dist/cjs/types.d.cts +35 -10
  16. package/dist/esm/collection.d.ts +10 -9
  17. package/dist/esm/collection.js +14 -6
  18. package/dist/esm/collection.js.map +1 -1
  19. package/dist/esm/local-storage.js +1 -1
  20. package/dist/esm/local-storage.js.map +1 -1
  21. package/dist/esm/query/builder/index.d.ts +15 -0
  22. package/dist/esm/query/builder/index.js +20 -0
  23. package/dist/esm/query/builder/index.js.map +1 -1
  24. package/dist/esm/query/compiler/index.js +7 -1
  25. package/dist/esm/query/compiler/index.js.map +1 -1
  26. package/dist/esm/query/ir.d.ts +1 -0
  27. package/dist/esm/query/ir.js.map +1 -1
  28. package/dist/esm/transactions.d.ts +5 -5
  29. package/dist/esm/transactions.js.map +1 -1
  30. package/dist/esm/types.d.ts +35 -10
  31. package/package.json +2 -2
  32. package/src/collection.ts +62 -21
  33. package/src/local-storage.ts +2 -2
  34. package/src/query/builder/index.ts +21 -0
  35. package/src/query/compiler/index.ts +11 -2
  36. package/src/query/ir.ts +1 -0
  37. package/src/transactions.ts +8 -12
  38. package/src/types.ts +69 -14
@@ -1 +1 @@
1
- {"version":3,"file":"collection.js","sources":["../../src/collection.ts"],"sourcesContent":["import { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { createTransaction, getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\nimport type { Transaction } from \"./transactions\"\nimport type {\n ChangeListener,\n ChangeMessage,\n CollectionConfig,\n CollectionStatus,\n Fn,\n InsertConfig,\n OperationConfig,\n OptimisticChangeMessage,\n PendingMutation,\n ResolveType,\n StandardSchema,\n Transaction as TransactionType,\n UtilsRecord,\n} from \"./types\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\n\n// Store collections in memory\nexport const collectionsStore = new Map<string, CollectionImpl<any, any>>()\n\ninterface PendingSyncedTransaction<T extends object = Record<string, unknown>> {\n committed: boolean\n operations: Array<OptimisticChangeMessage<T>>\n}\n\n/**\n * Enhanced Collection interface that includes both data type T and utilities TUtils\n * @template T - The type of items in the collection\n * @template TKey - The type of the key for the collection\n * @template TUtils - The utilities record type\n */\nexport interface Collection<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n> extends CollectionImpl<T, TKey> {\n readonly utils: TUtils\n}\n\n/**\n * Creates a new Collection instance with the given configuration\n *\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TKey - The type of the key for the collection\n * @template TUtils - The utilities record type\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n * @param options - Collection options with optional utilities\n * @returns A new Collection with utilities exposed both at top level and under .utils\n *\n * @example\n * // Pattern 1: With operation handlers (direct collection calls)\n * const todos = createCollection({\n * id: \"todos\",\n * getKey: (todo) => todo.id,\n * schema,\n * onInsert: async ({ transaction, collection }) => {\n * // Send to API\n * await api.createTodo(transaction.mutations[0].modified)\n * },\n * onUpdate: async ({ transaction, collection }) => {\n * await api.updateTodo(transaction.mutations[0].modified)\n * },\n * onDelete: async ({ transaction, collection }) => {\n * await api.deleteTodo(transaction.mutations[0].key)\n * },\n * sync: { sync: () => {} }\n * })\n *\n * // Direct usage (handlers manage transactions)\n * const tx = todos.insert({ id: \"1\", text: \"Buy milk\", completed: false })\n * await tx.isPersisted.promise\n *\n * @example\n * // Pattern 2: Manual transaction management\n * const todos = createCollection({\n * getKey: (todo) => todo.id,\n * schema: todoSchema,\n * sync: { sync: () => {} }\n * })\n *\n * // Explicit transaction usage\n * const tx = createTransaction({\n * mutationFn: async ({ transaction }) => {\n * // Handle all mutations in transaction\n * await api.saveChanges(transaction.mutations)\n * }\n * })\n *\n * tx.mutate(() => {\n * todos.insert({ id: \"1\", text: \"Buy milk\" })\n * todos.update(\"2\", draft => { draft.completed = true })\n * })\n *\n * await tx.isPersisted.promise\n *\n * @example\n * // Using schema for type inference (preferred as it also gives you client side validation)\n * const todoSchema = z.object({\n * id: z.string(),\n * title: z.string(),\n * completed: z.boolean()\n * })\n *\n * const todos = createCollection({\n * schema: todoSchema,\n * getKey: (todo) => todo.id,\n * sync: { sync: () => {} }\n * })\n *\n * // Note: You must provide either an explicit type or a schema, but not both.\n */\nexport function createCollection<\n TExplicit = unknown,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TFallback extends object = Record<string, unknown>,\n>(\n options: CollectionConfig<\n ResolveType<TExplicit, TSchema, TFallback>,\n TKey,\n TSchema\n > & { utils?: TUtils }\n): Collection<ResolveType<TExplicit, TSchema, TFallback>, TKey, TUtils> {\n const collection = new CollectionImpl<\n ResolveType<TExplicit, TSchema, TFallback>,\n TKey\n >(options)\n\n // Copy utils to both top level and .utils namespace\n if (options.utils) {\n collection.utils = { ...options.utils }\n } else {\n collection.utils = {} as TUtils\n }\n\n return collection as Collection<\n ResolveType<TExplicit, TSchema, TFallback>,\n TKey,\n TUtils\n >\n}\n\n/**\n * Custom error class for schema validation errors\n */\nexport class SchemaValidationError extends Error {\n type: `insert` | `update`\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>\n\n constructor(\n type: `insert` | `update`,\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>,\n message?: string\n ) {\n const defaultMessage = `${type === `insert` ? `Insert` : `Update`} validation failed: ${issues\n .map((issue) => `\\n- ${issue.message} - path: ${issue.path}`)\n .join(``)}`\n\n super(message || defaultMessage)\n this.name = `SchemaValidationError`\n this.type = type\n this.issues = issues\n }\n}\n\nexport class CollectionImpl<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n> {\n public config: CollectionConfig<T, TKey, any>\n\n // Core state - make public for testing\n public transactions: SortedMap<string, Transaction<any>>\n public pendingSyncedTransactions: Array<PendingSyncedTransaction<T>> = []\n public syncedData: Map<TKey, T> | SortedMap<TKey, T>\n public syncedMetadata = new Map<TKey, unknown>()\n\n // Optimistic state tracking - make public for testing\n public optimisticUpserts = new Map<TKey, T>()\n public optimisticDeletes = new Set<TKey>()\n\n // Cached size for performance\n private _size = 0\n\n // Event system\n private changeListeners = new Set<ChangeListener<T, TKey>>()\n private changeKeyListeners = new Map<TKey, Set<ChangeListener<T, TKey>>>()\n\n // Utilities namespace\n // This is populated by createCollection\n public utils: Record<string, Fn> = {}\n\n // State used for computing the change events\n private syncedKeys = new Set<TKey>()\n private preSyncVisibleState = new Map<TKey, T>()\n private recentlySyncedKeys = new Set<TKey>()\n private hasReceivedFirstCommit = false\n private isCommittingSyncTransactions = false\n\n // Array to store one-time commit listeners\n private onFirstCommitCallbacks: Array<() => void> = []\n\n // Event batching for preventing duplicate emissions during transaction flows\n private batchedEvents: Array<ChangeMessage<T, TKey>> = []\n private shouldBatchEvents = false\n\n // Lifecycle management\n private _status: CollectionStatus = `idle`\n private activeSubscribersCount = 0\n private gcTimeoutId: ReturnType<typeof setTimeout> | null = null\n private preloadPromise: Promise<void> | null = null\n private syncCleanupFn: (() => void) | null = null\n\n /**\n * Register a callback to be executed on the next commit\n * Useful for preloading collections\n * @param callback Function to call after the next commit\n * @example\n * collection.onFirstCommit(() => {\n * console.log('Collection has received first data')\n * // Safe to access collection.state now\n * })\n */\n public onFirstCommit(callback: () => void): void {\n this.onFirstCommitCallbacks.push(callback)\n }\n\n public id = ``\n\n /**\n * Gets the current status of the collection\n */\n public get status(): CollectionStatus {\n return this._status\n }\n\n /**\n * Validates that the collection is in a usable state for data operations\n * @private\n */\n private validateCollectionUsable(operation: string): void {\n switch (this._status) {\n case `error`:\n throw new Error(\n `Cannot perform ${operation} on collection \"${this.id}\" - collection is in error state. ` +\n `Try calling cleanup() and restarting the collection.`\n )\n case `cleaned-up`:\n throw new Error(\n `Cannot perform ${operation} on collection \"${this.id}\" - collection has been cleaned up. ` +\n `The collection will automatically restart on next access.`\n )\n }\n }\n\n /**\n * Validates state transitions to prevent invalid status changes\n * @private\n */\n private validateStatusTransition(\n from: CollectionStatus,\n to: CollectionStatus\n ): void {\n if (from === to) {\n // Allow same state transitions\n return\n }\n const validTransitions: Record<\n CollectionStatus,\n Array<CollectionStatus>\n > = {\n idle: [`loading`, `error`, `cleaned-up`],\n loading: [`initialCommit`, `error`, `cleaned-up`],\n initialCommit: [`ready`, `error`, `cleaned-up`],\n ready: [`cleaned-up`, `error`],\n error: [`cleaned-up`, `idle`],\n \"cleaned-up\": [`loading`, `error`],\n }\n\n if (!validTransitions[from].includes(to)) {\n throw new Error(\n `Invalid collection status transition from \"${from}\" to \"${to}\" for collection \"${this.id}\"`\n )\n }\n }\n\n /**\n * Safely update the collection status with validation\n * @private\n */\n private setStatus(newStatus: CollectionStatus): void {\n this.validateStatusTransition(this._status, newStatus)\n this._status = newStatus\n }\n\n /**\n * Creates a new Collection instance\n *\n * @param config - Configuration object for the collection\n * @throws Error if sync config is missing\n */\n constructor(config: CollectionConfig<T, TKey, any>) {\n // eslint-disable-next-line\n if (!config) {\n throw new Error(`Collection requires a config`)\n }\n if (config.id) {\n this.id = config.id\n } else {\n this.id = crypto.randomUUID()\n }\n\n // eslint-disable-next-line\n if (!config.sync) {\n throw new Error(`Collection requires a sync config`)\n }\n\n this.transactions = new SortedMap<string, Transaction<any>>((a, b) =>\n a.compareCreatedAt(b)\n )\n\n this.config = config\n\n // Store in global collections store\n collectionsStore.set(this.id, this)\n\n // Set up data storage with optional comparison function\n if (this.config.compare) {\n this.syncedData = new SortedMap<TKey, T>(this.config.compare)\n } else {\n this.syncedData = new Map<TKey, T>()\n }\n\n // Only start sync immediately if explicitly enabled\n if (config.startSync === true) {\n this.startSync()\n }\n }\n\n /**\n * Start sync immediately - internal method for compiled queries\n * This bypasses lazy loading for special cases like live query results\n */\n public startSyncImmediate(): void {\n this.startSync()\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 private startSync(): void {\n if (this._status !== `idle` && this._status !== `cleaned-up`) {\n return // Already started or in progress\n }\n\n this.setStatus(`loading`)\n\n try {\n const cleanupFn = this.config.sync.sync({\n collection: this,\n begin: () => {\n this.pendingSyncedTransactions.push({\n committed: false,\n operations: [],\n })\n },\n write: (messageWithoutKey: Omit<ChangeMessage<T>, `key`>) => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to write to`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't still write to it.`\n )\n }\n const key = this.getKeyFromItem(messageWithoutKey.value)\n\n // Check if an item with this key already exists when inserting\n if (messageWithoutKey.type === `insert`) {\n if (\n this.syncedData.has(key) &&\n !pendingTransaction.operations.some(\n (op) => op.key === key && op.type === `delete`\n )\n ) {\n throw new Error(\n `Cannot insert document with key \"${key}\" from sync because it already exists in the collection \"${this.id}\"`\n )\n }\n }\n\n const message: ChangeMessage<T> = {\n ...messageWithoutKey,\n key,\n }\n pendingTransaction.operations.push(message)\n },\n commit: () => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to commit`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't commit it again.`\n )\n }\n\n pendingTransaction.committed = true\n\n // Update status to initialCommit when transitioning from loading\n // This indicates we're in the process of committing the first transaction\n if (this._status === `loading`) {\n this.setStatus(`initialCommit`)\n }\n\n this.commitPendingTransactions()\n\n // Transition from initialCommit to ready after the first commit is complete\n if (this._status === `initialCommit`) {\n this.setStatus(`ready`)\n }\n },\n })\n\n // Store cleanup function if provided\n this.syncCleanupFn = typeof cleanupFn === `function` ? cleanupFn : null\n } catch (error) {\n this.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 this.preloadPromise = new Promise<void>((resolve, reject) => {\n if (this._status === `ready`) {\n resolve()\n return\n }\n\n if (this._status === `error`) {\n reject(new Error(`Collection is in error state`))\n return\n }\n\n // Register callback BEFORE starting sync to avoid race condition\n this.onFirstCommit(() => {\n resolve()\n })\n\n // Start sync if collection hasn't started yet or was cleaned up\n if (this._status === `idle` || this._status === `cleaned-up`) {\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 * Clean up the collection by stopping sync and clearing data\n * This can be called manually or automatically by garbage collection\n */\n public async cleanup(): Promise<void> {\n // Clear GC timeout\n if (this.gcTimeoutId) {\n clearTimeout(this.gcTimeoutId)\n this.gcTimeoutId = null\n }\n\n // Stop sync - wrap in try/catch since it's user-provided code\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 Error(\n `Collection \"${this.id}\" sync cleanup function threw an error: ${error.message}`\n )\n wrappedError.cause = error\n wrappedError.stack = error.stack\n throw wrappedError\n } else {\n throw new Error(\n `Collection \"${this.id}\" sync cleanup function threw an error: ${String(error)}`\n )\n }\n })\n }\n\n // Clear data\n this.syncedData.clear()\n this.syncedMetadata.clear()\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n this._size = 0\n this.pendingSyncedTransactions = []\n this.syncedKeys.clear()\n this.hasReceivedFirstCommit = false\n this.onFirstCommitCallbacks = []\n this.preloadPromise = null\n this.batchedEvents = []\n this.shouldBatchEvents = false\n\n // Update status\n this.setStatus(`cleaned-up`)\n\n return Promise.resolve()\n }\n\n /**\n * Start the garbage collection timer\n * Called when the collection becomes inactive (no subscribers)\n */\n private startGCTimer(): void {\n if (this.gcTimeoutId) {\n clearTimeout(this.gcTimeoutId)\n }\n\n const gcTime = this.config.gcTime ?? 300000 // 5 minutes default\n this.gcTimeoutId = setTimeout(() => {\n if (this.activeSubscribersCount === 0) {\n this.cleanup()\n }\n }, gcTime)\n }\n\n /**\n * Cancel the garbage collection timer\n * Called when the collection becomes active again\n */\n private cancelGCTimer(): void {\n if (this.gcTimeoutId) {\n clearTimeout(this.gcTimeoutId)\n this.gcTimeoutId = null\n }\n }\n\n /**\n * Increment the active subscribers count and start sync if needed\n */\n private addSubscriber(): void {\n this.activeSubscribersCount++\n this.cancelGCTimer()\n\n // Start sync if collection was cleaned up\n if (this._status === `cleaned-up` || this._status === `idle`) {\n this.startSync()\n }\n }\n\n /**\n * Decrement the active subscribers count and start GC timer if needed\n */\n private removeSubscriber(): void {\n this.activeSubscribersCount--\n\n if (this.activeSubscribersCount === 0) {\n this.activeSubscribersCount = 0\n this.startGCTimer()\n } else if (this.activeSubscribersCount < 0) {\n throw new Error(\n `Active subscribers count is negative - this should never happen`\n )\n }\n }\n\n /**\n * Recompute optimistic state from active transactions\n */\n private recomputeOptimisticState(): void {\n // Skip redundant recalculations when we're in the middle of committing sync transactions\n if (this.isCommittingSyncTransactions) {\n return\n }\n\n const previousState = new Map(this.optimisticUpserts)\n const previousDeletes = new Set(this.optimisticDeletes)\n\n // Clear current optimistic state\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n\n const activeTransactions: Array<Transaction<any>> = []\n const completedTransactions: Array<Transaction<any>> = []\n\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `completed`) {\n completedTransactions.push(transaction)\n } else if (![`completed`, `failed`].includes(transaction.state)) {\n activeTransactions.push(transaction)\n }\n }\n\n // Apply active transactions only (completed transactions are handled by sync operations)\n for (const transaction of activeTransactions) {\n for (const mutation of transaction.mutations) {\n if (mutation.collection === this && mutation.optimistic) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.optimisticUpserts.set(mutation.key, mutation.modified as T)\n this.optimisticDeletes.delete(mutation.key)\n break\n case `delete`:\n this.optimisticUpserts.delete(mutation.key)\n this.optimisticDeletes.add(mutation.key)\n break\n }\n }\n }\n }\n\n // Update cached size\n this._size = this.calculateSize()\n\n // Collect events for changes\n const events: Array<ChangeMessage<T, TKey>> = []\n this.collectOptimisticChanges(previousState, previousDeletes, events)\n\n // Filter out events for recently synced keys to prevent duplicates\n const filteredEventsBySyncStatus = events.filter(\n (event) => !this.recentlySyncedKeys.has(event.key)\n )\n\n // Filter out redundant delete events if there are pending sync transactions\n // that will immediately restore the same data, but only for completed transactions\n if (this.pendingSyncedTransactions.length > 0) {\n const pendingSyncKeys = new Set<TKey>()\n const completedTransactionMutations = new Set<string>()\n\n // Collect keys from pending sync operations\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n pendingSyncKeys.add(operation.key as TKey)\n }\n }\n\n // Collect mutation IDs from completed transactions\n for (const tx of completedTransactions) {\n for (const mutation of tx.mutations) {\n if (mutation.collection === this) {\n completedTransactionMutations.add(mutation.mutationId)\n }\n }\n }\n\n // Only filter out delete events for keys that:\n // 1. Have pending sync operations AND\n // 2. Are from completed transactions (being cleaned up)\n const filteredEvents = filteredEventsBySyncStatus.filter((event) => {\n if (event.type === `delete` && pendingSyncKeys.has(event.key)) {\n // Check if this delete is from clearing optimistic state of completed transactions\n // We can infer this by checking if we have no remaining optimistic mutations for this key\n const hasActiveOptimisticMutation = activeTransactions.some((tx) =>\n tx.mutations.some(\n (m) => m.collection === this && m.key === event.key\n )\n )\n\n if (!hasActiveOptimisticMutation) {\n return false // Skip this delete event as sync will restore the data\n }\n }\n return true\n })\n\n this.emitEvents(filteredEvents)\n } else {\n // Emit all events if no pending sync transactions\n this.emitEvents(filteredEventsBySyncStatus)\n }\n }\n\n /**\n * Calculate the current size based on synced data and optimistic changes\n */\n private calculateSize(): number {\n const syncedSize = this.syncedData.size\n const deletesFromSynced = Array.from(this.optimisticDeletes).filter(\n (key) => this.syncedData.has(key) && !this.optimisticUpserts.has(key)\n ).length\n const upsertsNotInSynced = Array.from(this.optimisticUpserts.keys()).filter(\n (key) => !this.syncedData.has(key)\n ).length\n\n return syncedSize - deletesFromSynced + upsertsNotInSynced\n }\n\n /**\n * Collect events for optimistic changes\n */\n private collectOptimisticChanges(\n previousUpserts: Map<TKey, T>,\n previousDeletes: Set<TKey>,\n events: Array<ChangeMessage<T, TKey>>\n ): void {\n const allKeys = new Set([\n ...previousUpserts.keys(),\n ...this.optimisticUpserts.keys(),\n ...previousDeletes,\n ...this.optimisticDeletes,\n ])\n\n for (const key of allKeys) {\n const currentValue = this.get(key)\n const previousValue = this.getPreviousValue(\n key,\n previousUpserts,\n previousDeletes\n )\n\n if (previousValue !== undefined && currentValue === undefined) {\n events.push({ type: `delete`, key, value: previousValue })\n } else if (previousValue === undefined && currentValue !== undefined) {\n events.push({ type: `insert`, key, value: currentValue })\n } else if (\n previousValue !== undefined &&\n currentValue !== undefined &&\n previousValue !== currentValue\n ) {\n events.push({\n type: `update`,\n key,\n value: currentValue,\n previousValue,\n })\n }\n }\n }\n\n /**\n * Get the previous value for a key given previous optimistic state\n */\n private getPreviousValue(\n key: TKey,\n previousUpserts: Map<TKey, T>,\n previousDeletes: Set<TKey>\n ): T | undefined {\n if (previousDeletes.has(key)) {\n return undefined\n }\n if (previousUpserts.has(key)) {\n return previousUpserts.get(key)\n }\n return this.syncedData.get(key)\n }\n\n /**\n * Emit events either immediately or batch them for later emission\n */\n private emitEvents(\n changes: Array<ChangeMessage<T, TKey>>,\n endBatching = false\n ): void {\n if (this.shouldBatchEvents && !endBatching) {\n // Add events to the batch\n this.batchedEvents.push(...changes)\n return\n }\n\n // Either we're not batching, or we're ending the batching cycle\n let eventsToEmit = changes\n\n if (endBatching) {\n // End batching: combine any batched events with new events and clean up state\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) return\n\n // Emit to all listeners\n for (const listener of this.changeListeners) {\n listener(eventsToEmit)\n }\n\n // Emit to key-specific listeners\n if (this.changeKeyListeners.size > 0) {\n // Group changes by key, but only for keys that have listeners\n const changesByKey = new Map<TKey, Array<ChangeMessage<T, TKey>>>()\n for (const change of eventsToEmit) {\n if (this.changeKeyListeners.has(change.key)) {\n if (!changesByKey.has(change.key)) {\n changesByKey.set(change.key, [])\n }\n changesByKey.get(change.key)!.push(change)\n }\n }\n\n // Emit batched changes to each key's listeners\n for (const [key, keyChanges] of changesByKey) {\n const keyListeners = this.changeKeyListeners.get(key)!\n for (const listener of keyListeners) {\n listener(keyChanges)\n }\n }\n }\n }\n\n /**\n * Get the current value for a key (virtual derived state)\n */\n public get(key: TKey): T | undefined {\n // Check if optimistically deleted\n if (this.optimisticDeletes.has(key)) {\n return undefined\n }\n\n // Check optimistic upserts first\n if (this.optimisticUpserts.has(key)) {\n return this.optimisticUpserts.get(key)\n }\n\n // Fall back to synced data\n return this.syncedData.get(key)\n }\n\n /**\n * Check if a key exists in the collection (virtual derived state)\n */\n public has(key: TKey): boolean {\n // Check if optimistically deleted\n if (this.optimisticDeletes.has(key)) {\n return false\n }\n\n // Check optimistic upserts first\n if (this.optimisticUpserts.has(key)) {\n return true\n }\n\n // Fall back to synced data\n return this.syncedData.has(key)\n }\n\n /**\n * Get the current size of the collection (cached)\n */\n public get size(): number {\n return this._size\n }\n\n /**\n * Get all keys (virtual derived state)\n */\n public *keys(): IterableIterator<TKey> {\n // Yield keys from synced data, skipping any that are deleted.\n for (const key of this.syncedData.keys()) {\n if (!this.optimisticDeletes.has(key)) {\n yield key\n }\n }\n // Yield keys from upserts that were not already in synced data.\n for (const key of this.optimisticUpserts.keys()) {\n if (!this.syncedData.has(key) && !this.optimisticDeletes.has(key)) {\n // The optimisticDeletes check is technically redundant if inserts/updates always remove from deletes,\n // but it's safer to keep it.\n yield key\n }\n }\n }\n\n /**\n * Get all values (virtual derived state)\n */\n public *values(): IterableIterator<T> {\n for (const key of this.keys()) {\n const value = this.get(key)\n if (value !== undefined) {\n yield value\n }\n }\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *entries(): IterableIterator<[TKey, T]> {\n for (const key of this.keys()) {\n const value = this.get(key)\n if (value !== undefined) {\n yield [key, value]\n }\n }\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *[Symbol.iterator](): IterableIterator<[TKey, T]> {\n for (const [key, value] of this.entries()) {\n yield [key, value]\n }\n }\n\n /**\n * Execute a callback for each entry in the collection\n */\n public forEach(\n callbackfn: (value: T, key: TKey, index: number) => void\n ): void {\n let index = 0\n for (const [key, value] of this.entries()) {\n callbackfn(value, key, index++)\n }\n }\n\n /**\n * Create a new array with the results of calling a function for each entry in the collection\n */\n public map<U>(\n callbackfn: (value: T, key: TKey, index: number) => U\n ): Array<U> {\n const result: Array<U> = []\n let index = 0\n for (const [key, value] of this.entries()) {\n result.push(callbackfn(value, key, index++))\n }\n return result\n }\n\n /**\n * Attempts to commit pending synced transactions if there are no active transactions\n * This method processes operations from pending transactions and applies them to the synced data\n */\n commitPendingTransactions = () => {\n // Check if there are any persisting transaction\n let hasPersistingTransaction = false\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `persisting`) {\n hasPersistingTransaction = true\n break\n }\n }\n\n if (!hasPersistingTransaction) {\n // Set flag to prevent redundant optimistic state recalculations\n this.isCommittingSyncTransactions = true\n\n // First collect all keys that will be affected by sync operations\n const changedKeys = new Set<TKey>()\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n changedKeys.add(operation.key as TKey)\n }\n }\n\n // Use pre-captured state if available (from optimistic scenarios),\n // otherwise capture current state (for pure sync scenarios)\n let currentVisibleState = this.preSyncVisibleState\n if (currentVisibleState.size === 0) {\n // No pre-captured state, capture it now for pure sync operations\n currentVisibleState = new Map<TKey, T>()\n for (const key of changedKeys) {\n const currentValue = this.get(key)\n if (currentValue !== undefined) {\n currentVisibleState.set(key, currentValue)\n }\n }\n }\n\n const events: Array<ChangeMessage<T, TKey>> = []\n const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`\n\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n const key = operation.key as TKey\n this.syncedKeys.add(key)\n\n // Update metadata\n switch (operation.type) {\n case `insert`:\n this.syncedMetadata.set(key, operation.metadata)\n break\n case `update`:\n this.syncedMetadata.set(\n key,\n Object.assign(\n {},\n this.syncedMetadata.get(key),\n operation.metadata\n )\n )\n break\n case `delete`:\n this.syncedMetadata.delete(key)\n break\n }\n\n // Update synced data\n switch (operation.type) {\n case `insert`:\n this.syncedData.set(key, operation.value)\n break\n case `update`: {\n if (rowUpdateMode === `partial`) {\n const updatedValue = Object.assign(\n {},\n this.syncedData.get(key),\n operation.value\n )\n this.syncedData.set(key, updatedValue)\n } else {\n this.syncedData.set(key, operation.value)\n }\n break\n }\n case `delete`:\n this.syncedData.delete(key)\n break\n }\n }\n }\n\n // Clear optimistic state since sync operations will now provide the authoritative data\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n\n // Reset flag and recompute optimistic state for any remaining active transactions\n this.isCommittingSyncTransactions = false\n for (const transaction of this.transactions.values()) {\n if (![`completed`, `failed`].includes(transaction.state)) {\n for (const mutation of transaction.mutations) {\n if (mutation.collection === this && mutation.optimistic) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.optimisticUpserts.set(\n mutation.key,\n mutation.modified as T\n )\n this.optimisticDeletes.delete(mutation.key)\n break\n case `delete`:\n this.optimisticUpserts.delete(mutation.key)\n this.optimisticDeletes.add(mutation.key)\n break\n }\n }\n }\n }\n }\n\n // Check for redundant sync operations that match completed optimistic operations\n const completedOptimisticOps = new Map<TKey, any>()\n\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `completed`) {\n for (const mutation of transaction.mutations) {\n if (mutation.collection === this && changedKeys.has(mutation.key)) {\n completedOptimisticOps.set(mutation.key, {\n type: mutation.type,\n value: mutation.modified,\n })\n }\n }\n }\n }\n\n // Now check what actually changed in the final visible state\n for (const key of changedKeys) {\n const previousVisibleValue = currentVisibleState.get(key)\n const newVisibleValue = this.get(key) // This returns the new derived state\n\n // Check if this sync operation is redundant with a completed optimistic operation\n const completedOp = completedOptimisticOps.get(key)\n const isRedundantSync =\n completedOp &&\n newVisibleValue !== undefined &&\n this.deepEqual(completedOp.value, newVisibleValue)\n\n if (!isRedundantSync) {\n if (\n previousVisibleValue === undefined &&\n newVisibleValue !== undefined\n ) {\n events.push({\n type: `insert`,\n key,\n value: newVisibleValue,\n })\n } else if (\n previousVisibleValue !== undefined &&\n newVisibleValue === undefined\n ) {\n events.push({\n type: `delete`,\n key,\n value: previousVisibleValue,\n })\n } else if (\n previousVisibleValue !== undefined &&\n newVisibleValue !== undefined &&\n !this.deepEqual(previousVisibleValue, newVisibleValue)\n ) {\n events.push({\n type: `update`,\n key,\n value: newVisibleValue,\n previousValue: previousVisibleValue,\n })\n }\n }\n }\n\n // Update cached size after synced data changes\n this._size = this.calculateSize()\n\n // End batching and emit all events (combines any batched events with sync events)\n this.emitEvents(events, true)\n\n this.pendingSyncedTransactions = []\n\n // Clear the pre-sync state since sync operations are complete\n this.preSyncVisibleState.clear()\n\n // Clear recently synced keys after a microtask to allow recomputeOptimisticState to see them\n Promise.resolve().then(() => {\n this.recentlySyncedKeys.clear()\n })\n\n // Call any registered one-time commit listeners\n if (!this.hasReceivedFirstCommit) {\n this.hasReceivedFirstCommit = true\n const callbacks = [...this.onFirstCommitCallbacks]\n this.onFirstCommitCallbacks = []\n callbacks.forEach((callback) => callback())\n }\n }\n }\n\n private ensureStandardSchema(schema: unknown): StandardSchema<T> {\n // If the schema already implements the standard-schema interface, return it\n if (schema && typeof schema === `object` && `~standard` in schema) {\n return schema as StandardSchema<T>\n }\n\n throw new Error(\n `Schema must either implement the standard-schema interface or be a Zod schema`\n )\n }\n\n public getKeyFromItem(item: T): TKey {\n return this.config.getKey(item)\n }\n\n public generateGlobalKey(key: any, item: any): string {\n if (typeof key === `undefined`) {\n throw new Error(\n `An object was created without a defined key: ${JSON.stringify(item)}`\n )\n }\n\n return `KEY::${this.id}/${key}`\n }\n\n private deepEqual(a: any, b: any): boolean {\n if (a === b) return true\n if (a == null || b == null) return false\n if (typeof a !== typeof b) return false\n\n if (typeof a === `object`) {\n if (Array.isArray(a) !== Array.isArray(b)) return false\n\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n\n const keysBSet = new Set(keysB)\n for (const key of keysA) {\n if (!keysBSet.has(key)) return false\n if (!this.deepEqual(a[key], b[key])) return false\n }\n return true\n }\n\n return false\n }\n\n private validateData(\n data: unknown,\n type: `insert` | `update`,\n key?: TKey\n ): T | never {\n if (!this.config.schema) return data as T\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.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 TypeError(`Schema validation must be synchronous`)\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 the original update data, not the merged data\n // We only used the merged data for validation\n return data as T\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 TypeError(`Schema validation must be synchronous`)\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 T\n }\n\n /**\n * Inserts one or more items into the collection\n * @param items - Single item or array of items to insert\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the insert operation(s)\n * @throws {SchemaValidationError} If the data fails schema validation\n * @example\n * // Insert a single todo (requires onInsert handler)\n * const tx = collection.insert({ id: \"1\", text: \"Buy milk\", completed: false })\n * await tx.isPersisted.promise\n *\n * @example\n * // Insert multiple todos at once\n * const tx = collection.insert([\n * { id: \"1\", text: \"Buy milk\", completed: false },\n * { id: \"2\", text: \"Walk dog\", completed: true }\n * ])\n * await tx.isPersisted.promise\n *\n * @example\n * // Insert with metadata\n * const tx = collection.insert({ id: \"1\", text: \"Buy groceries\" },\n * { metadata: { source: \"mobile-app\" } }\n * )\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.insert({ id: \"1\", text: \"New item\" })\n * await tx.isPersisted.promise\n * console.log('Insert successful')\n * } catch (error) {\n * console.log('Insert failed:', error)\n * }\n */\n insert = (data: T | Array<T>, config?: InsertConfig) => {\n this.validateCollectionUsable(`insert`)\n\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 Error(\n `Collection.insert called directly (not within an explicit transaction) but no 'onInsert' handler is configured.`\n )\n }\n\n const items = Array.isArray(data) ? data : [data]\n const mutations: Array<PendingMutation<T, `insert`>> = []\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.getKeyFromItem(item)\n if (this.has(key)) {\n throw `Cannot insert document with ID \"${key}\" because it already exists in the collection`\n }\n const globalKey = this.generateGlobalKey(key, item)\n\n const mutation: PendingMutation<T, `insert`> = {\n mutationId: crypto.randomUUID(),\n original: {},\n modified: validatedData,\n changes: validatedData,\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,\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 this.transactions.set(ambientTransaction.id, ambientTransaction)\n this.recomputeOptimisticState()\n\n return ambientTransaction\n } else {\n // Create a new transaction with a mutation function that calls the onInsert handler\n const directOpTransaction = createTransaction<T>({\n mutationFn: async (params) => {\n // Call the onInsert handler with the transaction and collection\n return this.config.onInsert!({\n ...params,\n collection: this as unknown as Collection<T, TKey, TUtils>,\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 this.transactions.set(directOpTransaction.id, directOpTransaction)\n this.recomputeOptimisticState()\n\n return directOpTransaction\n }\n }\n\n /**\n * Updates one or more items in the collection using a callback function\n * @param keys - Single key or array of keys to update\n * @param configOrCallback - Either update configuration or update callback\n * @param maybeCallback - Update callback if config was provided\n * @returns A Transaction object representing the update operation(s)\n * @throws {SchemaValidationError} If the updated data fails schema validation\n * @example\n * // Update single item by key\n * const tx = collection.update(\"todo-1\", (draft) => {\n * draft.completed = true\n * })\n * await tx.isPersisted.promise\n *\n * @example\n * // Update multiple items\n * const tx = collection.update([\"todo-1\", \"todo-2\"], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n * await tx.isPersisted.promise\n *\n * @example\n * // Update with metadata\n * const tx = collection.update(\"todo-1\",\n * { metadata: { reason: \"user update\" } },\n * (draft) => { draft.text = \"Updated text\" }\n * )\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.update(\"item-1\", draft => { draft.value = \"new\" })\n * await tx.isPersisted.promise\n * console.log('Update successful')\n * } catch (error) {\n * console.log('Update failed:', error)\n * }\n */\n\n // Overload 1: Update multiple items with a callback\n update<TItem extends object = T>(\n key: Array<TKey | unknown>,\n callback: (drafts: Array<TItem>) => void\n ): TransactionType\n\n // Overload 2: Update multiple items with config and a callback\n update<TItem extends object = T>(\n keys: Array<TKey | unknown>,\n config: OperationConfig,\n callback: (drafts: Array<TItem>) => void\n ): TransactionType\n\n // Overload 3: Update a single item with a callback\n update<TItem extends object = T>(\n id: TKey | unknown,\n callback: (draft: TItem) => void\n ): TransactionType\n\n // Overload 4: Update a single item with config and a callback\n update<TItem extends object = T>(\n id: TKey | unknown,\n config: OperationConfig,\n callback: (draft: TItem) => void\n ): TransactionType\n\n update<TItem extends object = T>(\n keys: (TKey | unknown) | Array<TKey | unknown>,\n configOrCallback: ((draft: TItem | Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: TItem | Array<TItem>) => void\n ) {\n if (typeof keys === `undefined`) {\n throw new Error(`The first argument to update is missing`)\n }\n\n this.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 Error(\n `Collection.update called directly (not within an explicit transaction) but no 'onUpdate' handler is configured.`\n )\n }\n\n const isArray = Array.isArray(keys)\n const keysArray = isArray ? keys : [keys]\n\n if (isArray && keysArray.length === 0) {\n throw new Error(`No keys were passed to update`)\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.get(key)\n if (!item) {\n throw new Error(\n `The key \"${key}\" was passed to update but an object for this key was not found in the collection`\n )\n }\n\n return item\n }) as unknown as Array<TItem>\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<TItem>) => void\n )\n } else {\n const result = withChangeTracking(\n currentObjects[0]!,\n callback as (draft: TItem) => void\n )\n changesArray = [result]\n }\n\n // Create mutations for each object that has changes\n const mutations: Array<PendingMutation<T, `update`>> = 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 T\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.getKeyFromItem(originalItem)\n const modifiedItemId = this.getKeyFromItem(modifiedItem)\n\n if (originalItemId !== modifiedItemId) {\n throw new Error(\n `Updating the key of an item is not allowed. Original key: \"${originalItemId}\", Attempted new key: \"${modifiedItemId}\". Please delete the old item and create a new one if a key change is necessary.`\n )\n }\n\n const globalKey = this.generateGlobalKey(modifiedItemId, modifiedItem)\n\n return {\n mutationId: crypto.randomUUID(),\n original: originalItem,\n modified: modifiedItem,\n changes: validatedUpdatePayload as Partial<T>,\n globalKey,\n key,\n metadata: config.metadata as unknown,\n syncMetadata: (this.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,\n }\n })\n .filter(Boolean) as Array<PendingMutation<T, `update`>>\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 return emptyTransaction\n }\n\n // If an ambient transaction exists, use it\n if (ambientTransaction) {\n ambientTransaction.applyMutations(mutations)\n\n this.transactions.set(ambientTransaction.id, ambientTransaction)\n this.recomputeOptimisticState()\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<T>({\n mutationFn: async (params) => {\n // Call the onUpdate handler with the transaction and collection\n return this.config.onUpdate!({\n ...params,\n collection: this as unknown as Collection<T, TKey, TUtils>,\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 this.transactions.set(directOpTransaction.id, directOpTransaction)\n this.recomputeOptimisticState()\n\n return directOpTransaction\n }\n\n /**\n * Deletes one or more items from the collection\n * @param keys - Single key or array of keys to delete\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the delete operation(s)\n * @example\n * // Delete a single item\n * const tx = collection.delete(\"todo-1\")\n * await tx.isPersisted.promise\n *\n * @example\n * // Delete multiple items\n * const tx = collection.delete([\"todo-1\", \"todo-2\"])\n * await tx.isPersisted.promise\n *\n * @example\n * // Delete with metadata\n * const tx = collection.delete(\"todo-1\", { metadata: { reason: \"completed\" } })\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.delete(\"item-1\")\n * await tx.isPersisted.promise\n * console.log('Delete successful')\n * } catch (error) {\n * console.log('Delete failed:', error)\n * }\n */\n delete = (\n keys: Array<TKey> | TKey,\n config?: OperationConfig\n ): TransactionType<any> => {\n this.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 Error(\n `Collection.delete called directly (not within an explicit transaction) but no 'onDelete' handler is configured.`\n )\n }\n\n if (Array.isArray(keys) && keys.length === 0) {\n throw new Error(`No keys were passed to delete`)\n }\n\n const keysArray = Array.isArray(keys) ? keys : [keys]\n const mutations: Array<PendingMutation<T, `delete`>> = []\n\n for (const key of keysArray) {\n if (!this.has(key)) {\n throw new Error(\n `Collection.delete was called with key '${key}' but there is no item in the collection with this key`\n )\n }\n const globalKey = this.generateGlobalKey(key, this.get(key)!)\n const mutation: PendingMutation<T, `delete`> = {\n mutationId: crypto.randomUUID(),\n original: this.get(key)!,\n modified: this.get(key)!,\n changes: this.get(key)!,\n globalKey,\n key,\n metadata: config?.metadata as unknown,\n syncMetadata: (this.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,\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 this.transactions.set(ambientTransaction.id, ambientTransaction)\n this.recomputeOptimisticState()\n\n return ambientTransaction\n }\n\n // Create a new transaction with a mutation function that calls the onDelete handler\n const directOpTransaction = createTransaction<T>({\n autoCommit: true,\n mutationFn: async (params) => {\n // Call the onDelete handler with the transaction and collection\n return this.config.onDelete!({\n ...params,\n collection: this as unknown as Collection<T, TKey, TUtils>,\n })\n },\n })\n\n // Apply mutations to the new transaction\n directOpTransaction.applyMutations(mutations)\n directOpTransaction.commit()\n\n this.transactions.set(directOpTransaction.id, directOpTransaction)\n this.recomputeOptimisticState()\n\n return directOpTransaction\n }\n\n /**\n * Gets the current state of the collection as a Map\n * @returns Map containing all items in the collection, with keys as identifiers\n * @example\n * const itemsMap = collection.state\n * console.log(`Collection has ${itemsMap.size} items`)\n *\n * for (const [key, item] of itemsMap) {\n * console.log(`${key}: ${item.title}`)\n * }\n *\n * // Check if specific item exists\n * if (itemsMap.has(\"todo-1\")) {\n * console.log(\"Todo 1 exists:\", itemsMap.get(\"todo-1\"))\n * }\n */\n get state() {\n const result = new Map<TKey, T>()\n for (const [key, value] of this.entries()) {\n result.set(key, value)\n }\n return result\n }\n\n /**\n * Gets the current state of the collection as a Map, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to a Map containing all items in the collection\n */\n stateWhenReady(): Promise<Map<TKey, T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.size > 0 || this.hasReceivedFirstCommit === true) {\n return Promise.resolve(this.state)\n }\n\n // Otherwise, wait for the first commit\n return new Promise<Map<TKey, T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.state)\n })\n })\n }\n\n /**\n * Gets the current state of the collection as an Array\n *\n * @returns An Array containing all items in the collection\n */\n get toArray() {\n return Array.from(this.values())\n }\n\n /**\n * Gets the current state of the collection as an Array, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to an Array containing all items in the collection\n */\n toArrayWhenReady(): Promise<Array<T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.size > 0 || this.hasReceivedFirstCommit === true) {\n return Promise.resolve(this.toArray)\n }\n\n // Otherwise, wait for the first commit\n return new Promise<Array<T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.toArray)\n })\n })\n }\n\n /**\n * Returns the current state of the collection as an array of changes\n * @returns An array of changes\n */\n public currentStateAsChanges(): Array<ChangeMessage<T>> {\n return Array.from(this.entries()).map(([key, value]) => ({\n type: `insert`,\n key,\n value,\n }))\n }\n\n /**\n * Subscribe to changes in the collection\n * @param callback - Function called when items change\n * @param options.includeInitialState - If true, immediately calls callback with current data\n * @returns Unsubscribe function - Call this to stop listening for changes\n * @example\n * // Basic subscription\n * const unsubscribe = collection.subscribeChanges((changes) => {\n * changes.forEach(change => {\n * console.log(`${change.type}: ${change.key}`, change.value)\n * })\n * })\n *\n * // Later: unsubscribe()\n *\n * @example\n * // Include current state immediately\n * const unsubscribe = collection.subscribeChanges((changes) => {\n * updateUI(changes)\n * }, { includeInitialState: true })\n */\n public subscribeChanges(\n callback: (changes: Array<ChangeMessage<T>>) => void,\n { includeInitialState = false }: { includeInitialState?: boolean } = {}\n ): () => void {\n // Start sync and track subscriber\n this.addSubscriber()\n\n if (includeInitialState) {\n // First send the current state as changes\n callback(this.currentStateAsChanges())\n }\n\n // Add to batched listeners\n this.changeListeners.add(callback)\n\n return () => {\n this.changeListeners.delete(callback)\n this.removeSubscriber()\n }\n }\n\n /**\n * Subscribe to changes for a specific key\n */\n public subscribeChangesKey(\n key: TKey,\n listener: ChangeListener<T, TKey>,\n { includeInitialState = false }: { includeInitialState?: boolean } = {}\n ): () => void {\n // Start sync and track subscriber\n this.addSubscriber()\n\n if (!this.changeKeyListeners.has(key)) {\n this.changeKeyListeners.set(key, new Set())\n }\n\n if (includeInitialState) {\n // First send the current state as changes\n listener([\n {\n type: `insert`,\n key,\n value: this.get(key)!,\n },\n ])\n }\n\n this.changeKeyListeners.get(key)!.add(listener)\n\n return () => {\n const listeners = this.changeKeyListeners.get(key)\n if (listeners) {\n listeners.delete(listener)\n if (listeners.size === 0) {\n this.changeKeyListeners.delete(key)\n }\n }\n this.removeSubscriber()\n }\n }\n\n /**\n * Capture visible state for keys that will be affected by pending sync operations\n * This must be called BEFORE onTransactionStateChange clears optimistic state\n */\n private capturePreSyncVisibleState(): void {\n if (this.pendingSyncedTransactions.length === 0) return\n\n // Clear any previous capture\n this.preSyncVisibleState.clear()\n\n // Get all keys that will be affected by sync operations\n const syncedKeys = new Set<TKey>()\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n syncedKeys.add(operation.key as TKey)\n }\n }\n\n // Mark keys as about to be synced to suppress intermediate events from recomputeOptimisticState\n for (const key of syncedKeys) {\n this.recentlySyncedKeys.add(key)\n }\n\n // Only capture current visible state for keys that will be affected by sync operations\n // This is much more efficient than capturing the entire collection state\n for (const key of syncedKeys) {\n const currentValue = this.get(key)\n if (currentValue !== undefined) {\n this.preSyncVisibleState.set(key, currentValue)\n }\n }\n }\n\n /**\n * Trigger a recomputation when transactions change\n * This method should be called by the Transaction class when state changes\n */\n public onTransactionStateChange(): void {\n // Check if commitPendingTransactions will be called after this\n // by checking if there are pending sync transactions (same logic as in transactions.ts)\n this.shouldBatchEvents = this.pendingSyncedTransactions.length > 0\n\n // CRITICAL: Capture visible state BEFORE clearing optimistic state\n this.capturePreSyncVisibleState()\n\n this.recomputeOptimisticState()\n }\n}\n"],"names":["config","result"],"mappings":"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"}
1
+ {"version":3,"file":"collection.js","sources":["../../src/collection.ts"],"sourcesContent":["import { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { createTransaction, getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\nimport type { Transaction } from \"./transactions\"\nimport type {\n ChangeListener,\n ChangeMessage,\n CollectionConfig,\n CollectionStatus,\n Fn,\n InsertConfig,\n OperationConfig,\n OptimisticChangeMessage,\n PendingMutation,\n ResolveInsertInput,\n ResolveType,\n StandardSchema,\n Transaction as TransactionType,\n TransactionWithMutations,\n UtilsRecord,\n} from \"./types\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\n\n// Store collections in memory\nexport const collectionsStore = new Map<string, CollectionImpl<any, any, any>>()\n\ninterface PendingSyncedTransaction<T extends object = Record<string, unknown>> {\n committed: boolean\n operations: Array<OptimisticChangeMessage<T>>\n}\n\n/**\n * Enhanced Collection interface that includes both data type T and utilities TUtils\n * @template T - The type of items in the collection\n * @template TKey - The type of the key for the collection\n * @template TUtils - The utilities record type\n * @template TInsertInput - The type for insert operations (can be different from T for schemas with defaults)\n */\nexport interface Collection<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInsertInput extends object = T,\n> extends CollectionImpl<T, TKey, TUtils, TSchema, TInsertInput> {\n readonly utils: TUtils\n}\n\n/**\n * Creates a new Collection instance with the given configuration\n *\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TKey - The type of the key for the collection\n * @template TUtils - The utilities record type\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n * @param options - Collection options with optional utilities\n * @returns A new Collection with utilities exposed both at top level and under .utils\n *\n * @example\n * // Pattern 1: With operation handlers (direct collection calls)\n * const todos = createCollection({\n * id: \"todos\",\n * getKey: (todo) => todo.id,\n * schema,\n * onInsert: async ({ transaction, collection }) => {\n * // Send to API\n * await api.createTodo(transaction.mutations[0].modified)\n * },\n * onUpdate: async ({ transaction, collection }) => {\n * await api.updateTodo(transaction.mutations[0].modified)\n * },\n * onDelete: async ({ transaction, collection }) => {\n * await api.deleteTodo(transaction.mutations[0].key)\n * },\n * sync: { sync: () => {} }\n * })\n *\n * // Direct usage (handlers manage transactions)\n * const tx = todos.insert({ id: \"1\", text: \"Buy milk\", completed: false })\n * await tx.isPersisted.promise\n *\n * @example\n * // Pattern 2: Manual transaction management\n * const todos = createCollection({\n * getKey: (todo) => todo.id,\n * schema: todoSchema,\n * sync: { sync: () => {} }\n * })\n *\n * // Explicit transaction usage\n * const tx = createTransaction({\n * mutationFn: async ({ transaction }) => {\n * // Handle all mutations in transaction\n * await api.saveChanges(transaction.mutations)\n * }\n * })\n *\n * tx.mutate(() => {\n * todos.insert({ id: \"1\", text: \"Buy milk\" })\n * todos.update(\"2\", draft => { draft.completed = true })\n * })\n *\n * await tx.isPersisted.promise\n *\n * @example\n * // Using schema for type inference (preferred as it also gives you client side validation)\n * const todoSchema = z.object({\n * id: z.string(),\n * title: z.string(),\n * completed: z.boolean()\n * })\n *\n * const todos = createCollection({\n * schema: todoSchema,\n * getKey: (todo) => todo.id,\n * sync: { sync: () => {} }\n * })\n *\n * // Note: You must provide either an explicit type or a schema, but not both.\n */\nexport function createCollection<\n TExplicit = unknown,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TFallback extends object = Record<string, unknown>,\n>(\n options: CollectionConfig<\n ResolveType<TExplicit, TSchema, TFallback>,\n TKey,\n TSchema,\n ResolveInsertInput<TExplicit, TSchema, TFallback>\n > & { utils?: TUtils }\n): Collection<\n ResolveType<TExplicit, TSchema, TFallback>,\n TKey,\n TUtils,\n TSchema,\n ResolveInsertInput<TExplicit, TSchema, TFallback>\n> {\n const collection = new CollectionImpl<\n ResolveType<TExplicit, TSchema, TFallback>,\n TKey,\n TUtils,\n TSchema,\n ResolveInsertInput<TExplicit, TSchema, TFallback>\n >(options)\n\n // Copy utils to both top level and .utils namespace\n if (options.utils) {\n collection.utils = { ...options.utils }\n } else {\n collection.utils = {} as TUtils\n }\n\n return collection as Collection<\n ResolveType<TExplicit, TSchema, TFallback>,\n TKey,\n TUtils,\n TSchema,\n ResolveInsertInput<TExplicit, TSchema, TFallback>\n >\n}\n\n/**\n * Custom error class for schema validation errors\n */\nexport class SchemaValidationError extends Error {\n type: `insert` | `update`\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>\n\n constructor(\n type: `insert` | `update`,\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>,\n message?: string\n ) {\n const defaultMessage = `${type === `insert` ? `Insert` : `Update`} validation failed: ${issues\n .map((issue) => `\\n- ${issue.message} - path: ${issue.path}`)\n .join(``)}`\n\n super(message || defaultMessage)\n this.name = `SchemaValidationError`\n this.type = type\n this.issues = issues\n }\n}\n\nexport class CollectionImpl<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInsertInput extends object = T,\n> {\n public config: CollectionConfig<T, TKey, TSchema, TInsertInput>\n\n // Core state - make public for testing\n public transactions: SortedMap<string, Transaction<any>>\n public pendingSyncedTransactions: Array<PendingSyncedTransaction<T>> = []\n public syncedData: Map<TKey, T> | SortedMap<TKey, T>\n public syncedMetadata = new Map<TKey, unknown>()\n\n // Optimistic state tracking - make public for testing\n public optimisticUpserts = new Map<TKey, T>()\n public optimisticDeletes = new Set<TKey>()\n\n // Cached size for performance\n private _size = 0\n\n // Event system\n private changeListeners = new Set<ChangeListener<T, TKey>>()\n private changeKeyListeners = new Map<TKey, Set<ChangeListener<T, TKey>>>()\n\n // Utilities namespace\n // This is populated by createCollection\n public utils: Record<string, Fn> = {}\n\n // State used for computing the change events\n private syncedKeys = new Set<TKey>()\n private preSyncVisibleState = new Map<TKey, T>()\n private recentlySyncedKeys = new Set<TKey>()\n private hasReceivedFirstCommit = false\n private isCommittingSyncTransactions = false\n\n // Array to store one-time commit listeners\n private onFirstCommitCallbacks: Array<() => void> = []\n\n // Event batching for preventing duplicate emissions during transaction flows\n private batchedEvents: Array<ChangeMessage<T, TKey>> = []\n private shouldBatchEvents = false\n\n // Lifecycle management\n private _status: CollectionStatus = `idle`\n private activeSubscribersCount = 0\n private gcTimeoutId: ReturnType<typeof setTimeout> | null = null\n private preloadPromise: Promise<void> | null = null\n private syncCleanupFn: (() => void) | null = null\n\n /**\n * Register a callback to be executed on the next commit\n * Useful for preloading collections\n * @param callback Function to call after the next commit\n * @example\n * collection.onFirstCommit(() => {\n * console.log('Collection has received first data')\n * // Safe to access collection.state now\n * })\n */\n public onFirstCommit(callback: () => void): void {\n this.onFirstCommitCallbacks.push(callback)\n }\n\n public id = ``\n\n /**\n * Gets the current status of the collection\n */\n public get status(): CollectionStatus {\n return this._status\n }\n\n /**\n * Validates that the collection is in a usable state for data operations\n * @private\n */\n private validateCollectionUsable(operation: string): void {\n switch (this._status) {\n case `error`:\n throw new Error(\n `Cannot perform ${operation} on collection \"${this.id}\" - collection is in error state. ` +\n `Try calling cleanup() and restarting the collection.`\n )\n case `cleaned-up`:\n throw new Error(\n `Cannot perform ${operation} on collection \"${this.id}\" - collection has been cleaned up. ` +\n `The collection will automatically restart on next access.`\n )\n }\n }\n\n /**\n * Validates state transitions to prevent invalid status changes\n * @private\n */\n private validateStatusTransition(\n from: CollectionStatus,\n to: CollectionStatus\n ): void {\n if (from === to) {\n // Allow same state transitions\n return\n }\n const validTransitions: Record<\n CollectionStatus,\n Array<CollectionStatus>\n > = {\n idle: [`loading`, `error`, `cleaned-up`],\n loading: [`initialCommit`, `error`, `cleaned-up`],\n initialCommit: [`ready`, `error`, `cleaned-up`],\n ready: [`cleaned-up`, `error`],\n error: [`cleaned-up`, `idle`],\n \"cleaned-up\": [`loading`, `error`],\n }\n\n if (!validTransitions[from].includes(to)) {\n throw new Error(\n `Invalid collection status transition from \"${from}\" to \"${to}\" for collection \"${this.id}\"`\n )\n }\n }\n\n /**\n * Safely update the collection status with validation\n * @private\n */\n private setStatus(newStatus: CollectionStatus): void {\n this.validateStatusTransition(this._status, newStatus)\n this._status = newStatus\n }\n\n /**\n * Creates a new Collection instance\n *\n * @param config - Configuration object for the collection\n * @throws Error if sync config is missing\n */\n constructor(config: CollectionConfig<T, TKey, TSchema, TInsertInput>) {\n // eslint-disable-next-line\n if (!config) {\n throw new Error(`Collection requires a config`)\n }\n if (config.id) {\n this.id = config.id\n } else {\n this.id = crypto.randomUUID()\n }\n\n // eslint-disable-next-line\n if (!config.sync) {\n throw new Error(`Collection requires a sync config`)\n }\n\n this.transactions = new SortedMap<string, Transaction<any>>((a, b) =>\n a.compareCreatedAt(b)\n )\n\n this.config = config\n\n // Store in global collections store\n collectionsStore.set(this.id, this)\n\n // Set up data storage with optional comparison function\n if (this.config.compare) {\n this.syncedData = new SortedMap<TKey, T>(this.config.compare)\n } else {\n this.syncedData = new Map<TKey, T>()\n }\n\n // Only start sync immediately if explicitly enabled\n if (config.startSync === true) {\n this.startSync()\n }\n }\n\n /**\n * Start sync immediately - internal method for compiled queries\n * This bypasses lazy loading for special cases like live query results\n */\n public startSyncImmediate(): void {\n this.startSync()\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 private startSync(): void {\n if (this._status !== `idle` && this._status !== `cleaned-up`) {\n return // Already started or in progress\n }\n\n this.setStatus(`loading`)\n\n try {\n const cleanupFn = this.config.sync.sync({\n collection: this,\n begin: () => {\n this.pendingSyncedTransactions.push({\n committed: false,\n operations: [],\n })\n },\n write: (messageWithoutKey: Omit<ChangeMessage<T>, `key`>) => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to write to`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't still write to it.`\n )\n }\n const key = this.getKeyFromItem(messageWithoutKey.value)\n\n // Check if an item with this key already exists when inserting\n if (messageWithoutKey.type === `insert`) {\n if (\n this.syncedData.has(key) &&\n !pendingTransaction.operations.some(\n (op) => op.key === key && op.type === `delete`\n )\n ) {\n throw new Error(\n `Cannot insert document with key \"${key}\" from sync because it already exists in the collection \"${this.id}\"`\n )\n }\n }\n\n const message: ChangeMessage<T> = {\n ...messageWithoutKey,\n key,\n }\n pendingTransaction.operations.push(message)\n },\n commit: () => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to commit`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't commit it again.`\n )\n }\n\n pendingTransaction.committed = true\n\n // Update status to initialCommit when transitioning from loading\n // This indicates we're in the process of committing the first transaction\n if (this._status === `loading`) {\n this.setStatus(`initialCommit`)\n }\n\n this.commitPendingTransactions()\n\n // Transition from initialCommit to ready after the first commit is complete\n if (this._status === `initialCommit`) {\n this.setStatus(`ready`)\n }\n },\n })\n\n // Store cleanup function if provided\n this.syncCleanupFn = typeof cleanupFn === `function` ? cleanupFn : null\n } catch (error) {\n this.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 this.preloadPromise = new Promise<void>((resolve, reject) => {\n if (this._status === `ready`) {\n resolve()\n return\n }\n\n if (this._status === `error`) {\n reject(new Error(`Collection is in error state`))\n return\n }\n\n // Register callback BEFORE starting sync to avoid race condition\n this.onFirstCommit(() => {\n resolve()\n })\n\n // Start sync if collection hasn't started yet or was cleaned up\n if (this._status === `idle` || this._status === `cleaned-up`) {\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 * Clean up the collection by stopping sync and clearing data\n * This can be called manually or automatically by garbage collection\n */\n public async cleanup(): Promise<void> {\n // Clear GC timeout\n if (this.gcTimeoutId) {\n clearTimeout(this.gcTimeoutId)\n this.gcTimeoutId = null\n }\n\n // Stop sync - wrap in try/catch since it's user-provided code\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 Error(\n `Collection \"${this.id}\" sync cleanup function threw an error: ${error.message}`\n )\n wrappedError.cause = error\n wrappedError.stack = error.stack\n throw wrappedError\n } else {\n throw new Error(\n `Collection \"${this.id}\" sync cleanup function threw an error: ${String(error)}`\n )\n }\n })\n }\n\n // Clear data\n this.syncedData.clear()\n this.syncedMetadata.clear()\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n this._size = 0\n this.pendingSyncedTransactions = []\n this.syncedKeys.clear()\n this.hasReceivedFirstCommit = false\n this.onFirstCommitCallbacks = []\n this.preloadPromise = null\n this.batchedEvents = []\n this.shouldBatchEvents = false\n\n // Update status\n this.setStatus(`cleaned-up`)\n\n return Promise.resolve()\n }\n\n /**\n * Start the garbage collection timer\n * Called when the collection becomes inactive (no subscribers)\n */\n private startGCTimer(): void {\n if (this.gcTimeoutId) {\n clearTimeout(this.gcTimeoutId)\n }\n\n const gcTime = this.config.gcTime ?? 300000 // 5 minutes default\n this.gcTimeoutId = setTimeout(() => {\n if (this.activeSubscribersCount === 0) {\n this.cleanup()\n }\n }, gcTime)\n }\n\n /**\n * Cancel the garbage collection timer\n * Called when the collection becomes active again\n */\n private cancelGCTimer(): void {\n if (this.gcTimeoutId) {\n clearTimeout(this.gcTimeoutId)\n this.gcTimeoutId = null\n }\n }\n\n /**\n * Increment the active subscribers count and start sync if needed\n */\n private addSubscriber(): void {\n this.activeSubscribersCount++\n this.cancelGCTimer()\n\n // Start sync if collection was cleaned up\n if (this._status === `cleaned-up` || this._status === `idle`) {\n this.startSync()\n }\n }\n\n /**\n * Decrement the active subscribers count and start GC timer if needed\n */\n private removeSubscriber(): void {\n this.activeSubscribersCount--\n\n if (this.activeSubscribersCount === 0) {\n this.activeSubscribersCount = 0\n this.startGCTimer()\n } else if (this.activeSubscribersCount < 0) {\n throw new Error(\n `Active subscribers count is negative - this should never happen`\n )\n }\n }\n\n /**\n * Recompute optimistic state from active transactions\n */\n private recomputeOptimisticState(): void {\n // Skip redundant recalculations when we're in the middle of committing sync transactions\n if (this.isCommittingSyncTransactions) {\n return\n }\n\n const previousState = new Map(this.optimisticUpserts)\n const previousDeletes = new Set(this.optimisticDeletes)\n\n // Clear current optimistic state\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n\n const activeTransactions: Array<Transaction<any>> = []\n const completedTransactions: Array<Transaction<any>> = []\n\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `completed`) {\n completedTransactions.push(transaction)\n } else if (![`completed`, `failed`].includes(transaction.state)) {\n activeTransactions.push(transaction)\n }\n }\n\n // Apply active transactions only (completed transactions are handled by sync operations)\n for (const transaction of activeTransactions) {\n for (const mutation of transaction.mutations) {\n if (mutation.collection === this && mutation.optimistic) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.optimisticUpserts.set(mutation.key, mutation.modified as T)\n this.optimisticDeletes.delete(mutation.key)\n break\n case `delete`:\n this.optimisticUpserts.delete(mutation.key)\n this.optimisticDeletes.add(mutation.key)\n break\n }\n }\n }\n }\n\n // Update cached size\n this._size = this.calculateSize()\n\n // Collect events for changes\n const events: Array<ChangeMessage<T, TKey>> = []\n this.collectOptimisticChanges(previousState, previousDeletes, events)\n\n // Filter out events for recently synced keys to prevent duplicates\n const filteredEventsBySyncStatus = events.filter(\n (event) => !this.recentlySyncedKeys.has(event.key)\n )\n\n // Filter out redundant delete events if there are pending sync transactions\n // that will immediately restore the same data, but only for completed transactions\n if (this.pendingSyncedTransactions.length > 0) {\n const pendingSyncKeys = new Set<TKey>()\n const completedTransactionMutations = new Set<string>()\n\n // Collect keys from pending sync operations\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n pendingSyncKeys.add(operation.key as TKey)\n }\n }\n\n // Collect mutation IDs from completed transactions\n for (const tx of completedTransactions) {\n for (const mutation of tx.mutations) {\n if (mutation.collection === this) {\n completedTransactionMutations.add(mutation.mutationId)\n }\n }\n }\n\n // Only filter out delete events for keys that:\n // 1. Have pending sync operations AND\n // 2. Are from completed transactions (being cleaned up)\n const filteredEvents = filteredEventsBySyncStatus.filter((event) => {\n if (event.type === `delete` && pendingSyncKeys.has(event.key)) {\n // Check if this delete is from clearing optimistic state of completed transactions\n // We can infer this by checking if we have no remaining optimistic mutations for this key\n const hasActiveOptimisticMutation = activeTransactions.some((tx) =>\n tx.mutations.some(\n (m) => m.collection === this && m.key === event.key\n )\n )\n\n if (!hasActiveOptimisticMutation) {\n return false // Skip this delete event as sync will restore the data\n }\n }\n return true\n })\n\n this.emitEvents(filteredEvents)\n } else {\n // Emit all events if no pending sync transactions\n this.emitEvents(filteredEventsBySyncStatus)\n }\n }\n\n /**\n * Calculate the current size based on synced data and optimistic changes\n */\n private calculateSize(): number {\n const syncedSize = this.syncedData.size\n const deletesFromSynced = Array.from(this.optimisticDeletes).filter(\n (key) => this.syncedData.has(key) && !this.optimisticUpserts.has(key)\n ).length\n const upsertsNotInSynced = Array.from(this.optimisticUpserts.keys()).filter(\n (key) => !this.syncedData.has(key)\n ).length\n\n return syncedSize - deletesFromSynced + upsertsNotInSynced\n }\n\n /**\n * Collect events for optimistic changes\n */\n private collectOptimisticChanges(\n previousUpserts: Map<TKey, T>,\n previousDeletes: Set<TKey>,\n events: Array<ChangeMessage<T, TKey>>\n ): void {\n const allKeys = new Set([\n ...previousUpserts.keys(),\n ...this.optimisticUpserts.keys(),\n ...previousDeletes,\n ...this.optimisticDeletes,\n ])\n\n for (const key of allKeys) {\n const currentValue = this.get(key)\n const previousValue = this.getPreviousValue(\n key,\n previousUpserts,\n previousDeletes\n )\n\n if (previousValue !== undefined && currentValue === undefined) {\n events.push({ type: `delete`, key, value: previousValue })\n } else if (previousValue === undefined && currentValue !== undefined) {\n events.push({ type: `insert`, key, value: currentValue })\n } else if (\n previousValue !== undefined &&\n currentValue !== undefined &&\n previousValue !== currentValue\n ) {\n events.push({\n type: `update`,\n key,\n value: currentValue,\n previousValue,\n })\n }\n }\n }\n\n /**\n * Get the previous value for a key given previous optimistic state\n */\n private getPreviousValue(\n key: TKey,\n previousUpserts: Map<TKey, T>,\n previousDeletes: Set<TKey>\n ): T | undefined {\n if (previousDeletes.has(key)) {\n return undefined\n }\n if (previousUpserts.has(key)) {\n return previousUpserts.get(key)\n }\n return this.syncedData.get(key)\n }\n\n /**\n * Emit events either immediately or batch them for later emission\n */\n private emitEvents(\n changes: Array<ChangeMessage<T, TKey>>,\n endBatching = false\n ): void {\n if (this.shouldBatchEvents && !endBatching) {\n // Add events to the batch\n this.batchedEvents.push(...changes)\n return\n }\n\n // Either we're not batching, or we're ending the batching cycle\n let eventsToEmit = changes\n\n if (endBatching) {\n // End batching: combine any batched events with new events and clean up state\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) return\n\n // Emit to all listeners\n for (const listener of this.changeListeners) {\n listener(eventsToEmit)\n }\n\n // Emit to key-specific listeners\n if (this.changeKeyListeners.size > 0) {\n // Group changes by key, but only for keys that have listeners\n const changesByKey = new Map<TKey, Array<ChangeMessage<T, TKey>>>()\n for (const change of eventsToEmit) {\n if (this.changeKeyListeners.has(change.key)) {\n if (!changesByKey.has(change.key)) {\n changesByKey.set(change.key, [])\n }\n changesByKey.get(change.key)!.push(change)\n }\n }\n\n // Emit batched changes to each key's listeners\n for (const [key, keyChanges] of changesByKey) {\n const keyListeners = this.changeKeyListeners.get(key)!\n for (const listener of keyListeners) {\n listener(keyChanges)\n }\n }\n }\n }\n\n /**\n * Get the current value for a key (virtual derived state)\n */\n public get(key: TKey): T | undefined {\n // Check if optimistically deleted\n if (this.optimisticDeletes.has(key)) {\n return undefined\n }\n\n // Check optimistic upserts first\n if (this.optimisticUpserts.has(key)) {\n return this.optimisticUpserts.get(key)\n }\n\n // Fall back to synced data\n return this.syncedData.get(key)\n }\n\n /**\n * Check if a key exists in the collection (virtual derived state)\n */\n public has(key: TKey): boolean {\n // Check if optimistically deleted\n if (this.optimisticDeletes.has(key)) {\n return false\n }\n\n // Check optimistic upserts first\n if (this.optimisticUpserts.has(key)) {\n return true\n }\n\n // Fall back to synced data\n return this.syncedData.has(key)\n }\n\n /**\n * Get the current size of the collection (cached)\n */\n public get size(): number {\n return this._size\n }\n\n /**\n * Get all keys (virtual derived state)\n */\n public *keys(): IterableIterator<TKey> {\n // Yield keys from synced data, skipping any that are deleted.\n for (const key of this.syncedData.keys()) {\n if (!this.optimisticDeletes.has(key)) {\n yield key\n }\n }\n // Yield keys from upserts that were not already in synced data.\n for (const key of this.optimisticUpserts.keys()) {\n if (!this.syncedData.has(key) && !this.optimisticDeletes.has(key)) {\n // The optimisticDeletes check is technically redundant if inserts/updates always remove from deletes,\n // but it's safer to keep it.\n yield key\n }\n }\n }\n\n /**\n * Get all values (virtual derived state)\n */\n public *values(): IterableIterator<T> {\n for (const key of this.keys()) {\n const value = this.get(key)\n if (value !== undefined) {\n yield value\n }\n }\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *entries(): IterableIterator<[TKey, T]> {\n for (const key of this.keys()) {\n const value = this.get(key)\n if (value !== undefined) {\n yield [key, value]\n }\n }\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *[Symbol.iterator](): IterableIterator<[TKey, T]> {\n for (const [key, value] of this.entries()) {\n yield [key, value]\n }\n }\n\n /**\n * Execute a callback for each entry in the collection\n */\n public forEach(\n callbackfn: (value: T, key: TKey, index: number) => void\n ): void {\n let index = 0\n for (const [key, value] of this.entries()) {\n callbackfn(value, key, index++)\n }\n }\n\n /**\n * Create a new array with the results of calling a function for each entry in the collection\n */\n public map<U>(\n callbackfn: (value: T, key: TKey, index: number) => U\n ): Array<U> {\n const result: Array<U> = []\n let index = 0\n for (const [key, value] of this.entries()) {\n result.push(callbackfn(value, key, index++))\n }\n return result\n }\n\n /**\n * Attempts to commit pending synced transactions if there are no active transactions\n * This method processes operations from pending transactions and applies them to the synced data\n */\n commitPendingTransactions = () => {\n // Check if there are any persisting transaction\n let hasPersistingTransaction = false\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `persisting`) {\n hasPersistingTransaction = true\n break\n }\n }\n\n if (!hasPersistingTransaction) {\n // Set flag to prevent redundant optimistic state recalculations\n this.isCommittingSyncTransactions = true\n\n // First collect all keys that will be affected by sync operations\n const changedKeys = new Set<TKey>()\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n changedKeys.add(operation.key as TKey)\n }\n }\n\n // Use pre-captured state if available (from optimistic scenarios),\n // otherwise capture current state (for pure sync scenarios)\n let currentVisibleState = this.preSyncVisibleState\n if (currentVisibleState.size === 0) {\n // No pre-captured state, capture it now for pure sync operations\n currentVisibleState = new Map<TKey, T>()\n for (const key of changedKeys) {\n const currentValue = this.get(key)\n if (currentValue !== undefined) {\n currentVisibleState.set(key, currentValue)\n }\n }\n }\n\n const events: Array<ChangeMessage<T, TKey>> = []\n const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`\n\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n const key = operation.key as TKey\n this.syncedKeys.add(key)\n\n // Update metadata\n switch (operation.type) {\n case `insert`:\n this.syncedMetadata.set(key, operation.metadata)\n break\n case `update`:\n this.syncedMetadata.set(\n key,\n Object.assign(\n {},\n this.syncedMetadata.get(key),\n operation.metadata\n )\n )\n break\n case `delete`:\n this.syncedMetadata.delete(key)\n break\n }\n\n // Update synced data\n switch (operation.type) {\n case `insert`:\n this.syncedData.set(key, operation.value)\n break\n case `update`: {\n if (rowUpdateMode === `partial`) {\n const updatedValue = Object.assign(\n {},\n this.syncedData.get(key),\n operation.value\n )\n this.syncedData.set(key, updatedValue)\n } else {\n this.syncedData.set(key, operation.value)\n }\n break\n }\n case `delete`:\n this.syncedData.delete(key)\n break\n }\n }\n }\n\n // Clear optimistic state since sync operations will now provide the authoritative data\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n\n // Reset flag and recompute optimistic state for any remaining active transactions\n this.isCommittingSyncTransactions = false\n for (const transaction of this.transactions.values()) {\n if (![`completed`, `failed`].includes(transaction.state)) {\n for (const mutation of transaction.mutations) {\n if (mutation.collection === this && mutation.optimistic) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.optimisticUpserts.set(\n mutation.key,\n mutation.modified as T\n )\n this.optimisticDeletes.delete(mutation.key)\n break\n case `delete`:\n this.optimisticUpserts.delete(mutation.key)\n this.optimisticDeletes.add(mutation.key)\n break\n }\n }\n }\n }\n }\n\n // Check for redundant sync operations that match completed optimistic operations\n const completedOptimisticOps = new Map<TKey, any>()\n\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `completed`) {\n for (const mutation of transaction.mutations) {\n if (mutation.collection === this && changedKeys.has(mutation.key)) {\n completedOptimisticOps.set(mutation.key, {\n type: mutation.type,\n value: mutation.modified,\n })\n }\n }\n }\n }\n\n // Now check what actually changed in the final visible state\n for (const key of changedKeys) {\n const previousVisibleValue = currentVisibleState.get(key)\n const newVisibleValue = this.get(key) // This returns the new derived state\n\n // Check if this sync operation is redundant with a completed optimistic operation\n const completedOp = completedOptimisticOps.get(key)\n const isRedundantSync =\n completedOp &&\n newVisibleValue !== undefined &&\n this.deepEqual(completedOp.value, newVisibleValue)\n\n if (!isRedundantSync) {\n if (\n previousVisibleValue === undefined &&\n newVisibleValue !== undefined\n ) {\n events.push({\n type: `insert`,\n key,\n value: newVisibleValue,\n })\n } else if (\n previousVisibleValue !== undefined &&\n newVisibleValue === undefined\n ) {\n events.push({\n type: `delete`,\n key,\n value: previousVisibleValue,\n })\n } else if (\n previousVisibleValue !== undefined &&\n newVisibleValue !== undefined &&\n !this.deepEqual(previousVisibleValue, newVisibleValue)\n ) {\n events.push({\n type: `update`,\n key,\n value: newVisibleValue,\n previousValue: previousVisibleValue,\n })\n }\n }\n }\n\n // Update cached size after synced data changes\n this._size = this.calculateSize()\n\n // End batching and emit all events (combines any batched events with sync events)\n this.emitEvents(events, true)\n\n this.pendingSyncedTransactions = []\n\n // Clear the pre-sync state since sync operations are complete\n this.preSyncVisibleState.clear()\n\n // Clear recently synced keys after a microtask to allow recomputeOptimisticState to see them\n Promise.resolve().then(() => {\n this.recentlySyncedKeys.clear()\n })\n\n // Call any registered one-time commit listeners\n if (!this.hasReceivedFirstCommit) {\n this.hasReceivedFirstCommit = true\n const callbacks = [...this.onFirstCommitCallbacks]\n this.onFirstCommitCallbacks = []\n callbacks.forEach((callback) => callback())\n }\n }\n }\n\n private ensureStandardSchema(schema: unknown): StandardSchema<T> {\n // If the schema already implements the standard-schema interface, return it\n if (schema && typeof schema === `object` && `~standard` in schema) {\n return schema as StandardSchema<T>\n }\n\n throw new Error(\n `Schema must either implement the standard-schema interface or be a Zod schema`\n )\n }\n\n public getKeyFromItem(item: T): TKey {\n return this.config.getKey(item)\n }\n\n public generateGlobalKey(key: any, item: any): string {\n if (typeof key === `undefined`) {\n throw new Error(\n `An object was created without a defined key: ${JSON.stringify(item)}`\n )\n }\n\n return `KEY::${this.id}/${key}`\n }\n\n private deepEqual(a: any, b: any): boolean {\n if (a === b) return true\n if (a == null || b == null) return false\n if (typeof a !== typeof b) return false\n\n if (typeof a === `object`) {\n if (Array.isArray(a) !== Array.isArray(b)) return false\n\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n\n const keysBSet = new Set(keysB)\n for (const key of keysA) {\n if (!keysBSet.has(key)) return false\n if (!this.deepEqual(a[key], b[key])) return false\n }\n return true\n }\n\n return false\n }\n\n private validateData(\n data: unknown,\n type: `insert` | `update`,\n key?: TKey\n ): T | never {\n if (!this.config.schema) return data as T\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.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 TypeError(`Schema validation must be synchronous`)\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 the original update data, not the merged data\n // We only used the merged data for validation\n return data as T\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 TypeError(`Schema validation must be synchronous`)\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 T\n }\n\n /**\n * Inserts one or more items into the collection\n * @param items - Single item or array of items to insert\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the insert operation(s)\n * @throws {SchemaValidationError} If the data fails schema validation\n * @example\n * // Insert a single todo (requires onInsert handler)\n * const tx = collection.insert({ id: \"1\", text: \"Buy milk\", completed: false })\n * await tx.isPersisted.promise\n *\n * @example\n * // Insert multiple todos at once\n * const tx = collection.insert([\n * { id: \"1\", text: \"Buy milk\", completed: false },\n * { id: \"2\", text: \"Walk dog\", completed: true }\n * ])\n * await tx.isPersisted.promise\n *\n * @example\n * // Insert with metadata\n * const tx = collection.insert({ id: \"1\", text: \"Buy groceries\" },\n * { metadata: { source: \"mobile-app\" } }\n * )\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.insert({ id: \"1\", text: \"New item\" })\n * await tx.isPersisted.promise\n * console.log('Insert successful')\n * } catch (error) {\n * console.log('Insert failed:', error)\n * }\n */\n insert = (\n data: TInsertInput | Array<TInsertInput>,\n config?: InsertConfig\n ) => {\n this.validateCollectionUsable(`insert`)\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 Error(\n `Collection.insert called directly (not within an explicit transaction) but no 'onInsert' handler is configured.`\n )\n }\n\n const items = Array.isArray(data) ? data : [data]\n const mutations: Array<PendingMutation<T>> = []\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.getKeyFromItem(validatedData)\n if (this.has(key)) {\n throw `Cannot insert document with ID \"${key}\" because it already exists in the collection`\n }\n const globalKey = this.generateGlobalKey(key, item)\n\n const mutation: PendingMutation<T, `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 TInsertInput,\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,\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 this.transactions.set(ambientTransaction.id, ambientTransaction)\n this.recomputeOptimisticState()\n\n return ambientTransaction\n } else {\n // Create a new transaction with a mutation function that calls the onInsert handler\n const directOpTransaction = createTransaction<T>({\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 TInsertInput,\n `insert`\n >,\n collection: this as unknown as Collection<T, TKey, TUtils>,\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 this.transactions.set(directOpTransaction.id, directOpTransaction)\n this.recomputeOptimisticState()\n\n return directOpTransaction\n }\n }\n\n /**\n * Updates one or more items in the collection using a callback function\n * @param keys - Single key or array of keys to update\n * @param configOrCallback - Either update configuration or update callback\n * @param maybeCallback - Update callback if config was provided\n * @returns A Transaction object representing the update operation(s)\n * @throws {SchemaValidationError} If the updated data fails schema validation\n * @example\n * // Update single item by key\n * const tx = collection.update(\"todo-1\", (draft) => {\n * draft.completed = true\n * })\n * await tx.isPersisted.promise\n *\n * @example\n * // Update multiple items\n * const tx = collection.update([\"todo-1\", \"todo-2\"], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n * await tx.isPersisted.promise\n *\n * @example\n * // Update with metadata\n * const tx = collection.update(\"todo-1\",\n * { metadata: { reason: \"user update\" } },\n * (draft) => { draft.text = \"Updated text\" }\n * )\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.update(\"item-1\", draft => { draft.value = \"new\" })\n * await tx.isPersisted.promise\n * console.log('Update successful')\n * } catch (error) {\n * console.log('Update failed:', error)\n * }\n */\n\n // Overload 1: Update multiple items with a callback\n update<TItem extends object = T>(\n key: Array<TKey | unknown>,\n callback: (drafts: Array<TItem>) => void\n ): TransactionType\n\n // Overload 2: Update multiple items with config and a callback\n update<TItem extends object = T>(\n keys: Array<TKey | unknown>,\n config: OperationConfig,\n callback: (drafts: Array<TItem>) => void\n ): TransactionType\n\n // Overload 3: Update a single item with a callback\n update<TItem extends object = T>(\n id: TKey | unknown,\n callback: (draft: TItem) => void\n ): TransactionType\n\n // Overload 4: Update a single item with config and a callback\n update<TItem extends object = T>(\n id: TKey | unknown,\n config: OperationConfig,\n callback: (draft: TItem) => void\n ): TransactionType\n\n update<TItem extends object = T>(\n keys: (TKey | unknown) | Array<TKey | unknown>,\n configOrCallback: ((draft: TItem | Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: TItem | Array<TItem>) => void\n ) {\n if (typeof keys === `undefined`) {\n throw new Error(`The first argument to update is missing`)\n }\n\n this.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 Error(\n `Collection.update called directly (not within an explicit transaction) but no 'onUpdate' handler is configured.`\n )\n }\n\n const isArray = Array.isArray(keys)\n const keysArray = isArray ? keys : [keys]\n\n if (isArray && keysArray.length === 0) {\n throw new Error(`No keys were passed to update`)\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.get(key)\n if (!item) {\n throw new Error(\n `The key \"${key}\" was passed to update but an object for this key was not found in the collection`\n )\n }\n\n return item\n }) as unknown as Array<TItem>\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<TItem>) => void\n )\n } else {\n const result = withChangeTracking(\n currentObjects[0]!,\n callback as (draft: TItem) => void\n )\n changesArray = [result]\n }\n\n // Create mutations for each object that has changes\n const mutations: Array<PendingMutation<T, `update`, this>> = 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 T\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.getKeyFromItem(originalItem)\n const modifiedItemId = this.getKeyFromItem(modifiedItem)\n\n if (originalItemId !== modifiedItemId) {\n throw new Error(\n `Updating the key of an item is not allowed. Original key: \"${originalItemId}\", Attempted new key: \"${modifiedItemId}\". Please delete the old item and create a new one if a key change is necessary.`\n )\n }\n\n const globalKey = this.generateGlobalKey(modifiedItemId, modifiedItem)\n\n return {\n mutationId: crypto.randomUUID(),\n original: originalItem,\n modified: modifiedItem,\n changes: validatedUpdatePayload as Partial<T>,\n globalKey,\n key,\n metadata: config.metadata as unknown,\n syncMetadata: (this.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,\n }\n })\n .filter(Boolean) as Array<PendingMutation<T, `update`, this>>\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 return emptyTransaction\n }\n\n // If an ambient transaction exists, use it\n if (ambientTransaction) {\n ambientTransaction.applyMutations(mutations)\n\n this.transactions.set(ambientTransaction.id, ambientTransaction)\n this.recomputeOptimisticState()\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<T>({\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 T,\n `update`\n >,\n collection: this as unknown as Collection<T, TKey, TUtils>,\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 this.transactions.set(directOpTransaction.id, directOpTransaction)\n this.recomputeOptimisticState()\n\n return directOpTransaction\n }\n\n /**\n * Deletes one or more items from the collection\n * @param keys - Single key or array of keys to delete\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the delete operation(s)\n * @example\n * // Delete a single item\n * const tx = collection.delete(\"todo-1\")\n * await tx.isPersisted.promise\n *\n * @example\n * // Delete multiple items\n * const tx = collection.delete([\"todo-1\", \"todo-2\"])\n * await tx.isPersisted.promise\n *\n * @example\n * // Delete with metadata\n * const tx = collection.delete(\"todo-1\", { metadata: { reason: \"completed\" } })\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.delete(\"item-1\")\n * await tx.isPersisted.promise\n * console.log('Delete successful')\n * } catch (error) {\n * console.log('Delete failed:', error)\n * }\n */\n delete = (\n keys: Array<TKey> | TKey,\n config?: OperationConfig\n ): TransactionType<any> => {\n this.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 Error(\n `Collection.delete called directly (not within an explicit transaction) but no 'onDelete' handler is configured.`\n )\n }\n\n if (Array.isArray(keys) && keys.length === 0) {\n throw new Error(`No keys were passed to delete`)\n }\n\n const keysArray = Array.isArray(keys) ? keys : [keys]\n const mutations: Array<PendingMutation<T, `delete`, this>> = []\n\n for (const key of keysArray) {\n if (!this.has(key)) {\n throw new Error(\n `Collection.delete was called with key '${key}' but there is no item in the collection with this key`\n )\n }\n const globalKey = this.generateGlobalKey(key, this.get(key)!)\n const mutation: PendingMutation<T, `delete`, this> = {\n mutationId: crypto.randomUUID(),\n original: this.get(key)!,\n modified: this.get(key)!,\n changes: this.get(key)!,\n globalKey,\n key,\n metadata: config?.metadata as unknown,\n syncMetadata: (this.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,\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 this.transactions.set(ambientTransaction.id, ambientTransaction)\n this.recomputeOptimisticState()\n\n return ambientTransaction\n }\n\n // Create a new transaction with a mutation function that calls the onDelete handler\n const directOpTransaction = createTransaction<T>({\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 T,\n `delete`\n >,\n collection: this as unknown as Collection<T, TKey, TUtils>,\n })\n },\n })\n\n // Apply mutations to the new transaction\n directOpTransaction.applyMutations(mutations)\n directOpTransaction.commit()\n\n this.transactions.set(directOpTransaction.id, directOpTransaction)\n this.recomputeOptimisticState()\n\n return directOpTransaction\n }\n\n /**\n * Gets the current state of the collection as a Map\n * @returns Map containing all items in the collection, with keys as identifiers\n * @example\n * const itemsMap = collection.state\n * console.log(`Collection has ${itemsMap.size} items`)\n *\n * for (const [key, item] of itemsMap) {\n * console.log(`${key}: ${item.title}`)\n * }\n *\n * // Check if specific item exists\n * if (itemsMap.has(\"todo-1\")) {\n * console.log(\"Todo 1 exists:\", itemsMap.get(\"todo-1\"))\n * }\n */\n get state() {\n const result = new Map<TKey, T>()\n for (const [key, value] of this.entries()) {\n result.set(key, value)\n }\n return result\n }\n\n /**\n * Gets the current state of the collection as a Map, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to a Map containing all items in the collection\n */\n stateWhenReady(): Promise<Map<TKey, T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.size > 0 || this.hasReceivedFirstCommit === true) {\n return Promise.resolve(this.state)\n }\n\n // Otherwise, wait for the first commit\n return new Promise<Map<TKey, T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.state)\n })\n })\n }\n\n /**\n * Gets the current state of the collection as an Array\n *\n * @returns An Array containing all items in the collection\n */\n get toArray() {\n return Array.from(this.values())\n }\n\n /**\n * Gets the current state of the collection as an Array, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to an Array containing all items in the collection\n */\n toArrayWhenReady(): Promise<Array<T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.size > 0 || this.hasReceivedFirstCommit === true) {\n return Promise.resolve(this.toArray)\n }\n\n // Otherwise, wait for the first commit\n return new Promise<Array<T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.toArray)\n })\n })\n }\n\n /**\n * Returns the current state of the collection as an array of changes\n * @returns An array of changes\n */\n public currentStateAsChanges(): Array<ChangeMessage<T>> {\n return Array.from(this.entries()).map(([key, value]) => ({\n type: `insert`,\n key,\n value,\n }))\n }\n\n /**\n * Subscribe to changes in the collection\n * @param callback - Function called when items change\n * @param options.includeInitialState - If true, immediately calls callback with current data\n * @returns Unsubscribe function - Call this to stop listening for changes\n * @example\n * // Basic subscription\n * const unsubscribe = collection.subscribeChanges((changes) => {\n * changes.forEach(change => {\n * console.log(`${change.type}: ${change.key}`, change.value)\n * })\n * })\n *\n * // Later: unsubscribe()\n *\n * @example\n * // Include current state immediately\n * const unsubscribe = collection.subscribeChanges((changes) => {\n * updateUI(changes)\n * }, { includeInitialState: true })\n */\n public subscribeChanges(\n callback: (changes: Array<ChangeMessage<T>>) => void,\n { includeInitialState = false }: { includeInitialState?: boolean } = {}\n ): () => void {\n // Start sync and track subscriber\n this.addSubscriber()\n\n if (includeInitialState) {\n // First send the current state as changes\n callback(this.currentStateAsChanges())\n }\n\n // Add to batched listeners\n this.changeListeners.add(callback)\n\n return () => {\n this.changeListeners.delete(callback)\n this.removeSubscriber()\n }\n }\n\n /**\n * Subscribe to changes for a specific key\n */\n public subscribeChangesKey(\n key: TKey,\n listener: ChangeListener<T, TKey>,\n { includeInitialState = false }: { includeInitialState?: boolean } = {}\n ): () => void {\n // Start sync and track subscriber\n this.addSubscriber()\n\n if (!this.changeKeyListeners.has(key)) {\n this.changeKeyListeners.set(key, new Set())\n }\n\n if (includeInitialState) {\n // First send the current state as changes\n listener([\n {\n type: `insert`,\n key,\n value: this.get(key)!,\n },\n ])\n }\n\n this.changeKeyListeners.get(key)!.add(listener)\n\n return () => {\n const listeners = this.changeKeyListeners.get(key)\n if (listeners) {\n listeners.delete(listener)\n if (listeners.size === 0) {\n this.changeKeyListeners.delete(key)\n }\n }\n this.removeSubscriber()\n }\n }\n\n /**\n * Capture visible state for keys that will be affected by pending sync operations\n * This must be called BEFORE onTransactionStateChange clears optimistic state\n */\n private capturePreSyncVisibleState(): void {\n if (this.pendingSyncedTransactions.length === 0) return\n\n // Clear any previous capture\n this.preSyncVisibleState.clear()\n\n // Get all keys that will be affected by sync operations\n const syncedKeys = new Set<TKey>()\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n syncedKeys.add(operation.key as TKey)\n }\n }\n\n // Mark keys as about to be synced to suppress intermediate events from recomputeOptimisticState\n for (const key of syncedKeys) {\n this.recentlySyncedKeys.add(key)\n }\n\n // Only capture current visible state for keys that will be affected by sync operations\n // This is much more efficient than capturing the entire collection state\n for (const key of syncedKeys) {\n const currentValue = this.get(key)\n if (currentValue !== undefined) {\n this.preSyncVisibleState.set(key, currentValue)\n }\n }\n }\n\n /**\n * Trigger a recomputation when transactions change\n * This method should be called by the Transaction class when state changes\n */\n public onTransactionStateChange(): void {\n // Check if commitPendingTransactions will be called after this\n // by checking if there are pending sync transactions (same logic as in transactions.ts)\n this.shouldBatchEvents = this.pendingSyncedTransactions.length > 0\n\n // CRITICAL: Capture visible state BEFORE clearing optimistic state\n this.capturePreSyncVisibleState()\n\n this.recomputeOptimisticState()\n }\n}\n"],"names":["config","result"],"mappings":";;;AAwBO,MAAM,uCAAuB,IAAA;AAiG7B,SAAS,iBAOd,SAYA;AACA,QAAM,aAAa,IAAI,eAMrB,OAAO;AAGT,MAAI,QAAQ,OAAO;AACjB,eAAW,QAAQ,EAAE,GAAG,QAAQ,MAAA;AAAA,EAClC,OAAO;AACL,eAAW,QAAQ,CAAA;AAAA,EACrB;AAEA,SAAO;AAOT;AAKO,MAAM,8BAA8B,MAAM;AAAA,EAO/C,YACE,MACA,QAIA,SACA;AACA,UAAM,iBAAiB,GAAG,SAAS,WAAW,WAAW,QAAQ,uBAAuB,OACrF,IAAI,CAAC,UAAU;AAAA,IAAO,MAAM,OAAO,YAAY,MAAM,IAAI,EAAE,EAC3D,KAAK,EAAE,CAAC;AAEX,UAAM,WAAW,cAAc;AAC/B,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAEO,MAAM,eAMX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqIA,YAAY,QAA0D;AAhItE,SAAO,4BAAgE,CAAA;AAEvE,SAAO,qCAAqB,IAAA;AAG5B,SAAO,wCAAwB,IAAA;AAC/B,SAAO,wCAAwB,IAAA;AAG/B,SAAQ,QAAQ;AAGhB,SAAQ,sCAAsB,IAAA;AAC9B,SAAQ,yCAAyB,IAAA;AAIjC,SAAO,QAA4B,CAAA;AAGnC,SAAQ,iCAAiB,IAAA;AACzB,SAAQ,0CAA0B,IAAA;AAClC,SAAQ,yCAAyB,IAAA;AACjC,SAAQ,yBAAyB;AACjC,SAAQ,+BAA+B;AAGvC,SAAQ,yBAA4C,CAAA;AAGpD,SAAQ,gBAA+C,CAAA;AACvD,SAAQ,oBAAoB;AAG5B,SAAQ,UAA4B;AACpC,SAAQ,yBAAyB;AACjC,SAAQ,cAAoD;AAC5D,SAAQ,iBAAuC;AAC/C,SAAQ,gBAAqC;AAgB7C,SAAO,KAAK;AAwtBZ,SAAA,4BAA4B,MAAM;AAEhC,UAAI,2BAA2B;AAC/B,iBAAW,eAAe,KAAK,aAAa,OAAA,GAAU;AACpD,YAAI,YAAY,UAAU,cAAc;AACtC,qCAA2B;AAC3B;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,0BAA0B;AAE7B,aAAK,+BAA+B;AAGpC,cAAM,kCAAkB,IAAA;AACxB,mBAAW,eAAe,KAAK,2BAA2B;AACxD,qBAAW,aAAa,YAAY,YAAY;AAC9C,wBAAY,IAAI,UAAU,GAAW;AAAA,UACvC;AAAA,QACF;AAIA,YAAI,sBAAsB,KAAK;AAC/B,YAAI,oBAAoB,SAAS,GAAG;AAElC,oDAA0B,IAAA;AAC1B,qBAAW,OAAO,aAAa;AAC7B,kBAAM,eAAe,KAAK,IAAI,GAAG;AACjC,gBAAI,iBAAiB,QAAW;AAC9B,kCAAoB,IAAI,KAAK,YAAY;AAAA,YAC3C;AAAA,UACF;AAAA,QACF;AAEA,cAAM,SAAwC,CAAA;AAC9C,cAAM,gBAAgB,KAAK,OAAO,KAAK,iBAAiB;AAExD,mBAAW,eAAe,KAAK,2BAA2B;AACxD,qBAAW,aAAa,YAAY,YAAY;AAC9C,kBAAM,MAAM,UAAU;AACtB,iBAAK,WAAW,IAAI,GAAG;AAGvB,oBAAQ,UAAU,MAAA;AAAA,cAChB,KAAK;AACH,qBAAK,eAAe,IAAI,KAAK,UAAU,QAAQ;AAC/C;AAAA,cACF,KAAK;AACH,qBAAK,eAAe;AAAA,kBAClB;AAAA,kBACA,OAAO;AAAA,oBACL,CAAA;AAAA,oBACA,KAAK,eAAe,IAAI,GAAG;AAAA,oBAC3B,UAAU;AAAA,kBAAA;AAAA,gBACZ;AAEF;AAAA,cACF,KAAK;AACH,qBAAK,eAAe,OAAO,GAAG;AAC9B;AAAA,YAAA;AAIJ,oBAAQ,UAAU,MAAA;AAAA,cAChB,KAAK;AACH,qBAAK,WAAW,IAAI,KAAK,UAAU,KAAK;AACxC;AAAA,cACF,KAAK,UAAU;AACb,oBAAI,kBAAkB,WAAW;AAC/B,wBAAM,eAAe,OAAO;AAAA,oBAC1B,CAAA;AAAA,oBACA,KAAK,WAAW,IAAI,GAAG;AAAA,oBACvB,UAAU;AAAA,kBAAA;AAEZ,uBAAK,WAAW,IAAI,KAAK,YAAY;AAAA,gBACvC,OAAO;AACL,uBAAK,WAAW,IAAI,KAAK,UAAU,KAAK;AAAA,gBAC1C;AACA;AAAA,cACF;AAAA,cACA,KAAK;AACH,qBAAK,WAAW,OAAO,GAAG;AAC1B;AAAA,YAAA;AAAA,UAEN;AAAA,QACF;AAGA,aAAK,kBAAkB,MAAA;AACvB,aAAK,kBAAkB,MAAA;AAGvB,aAAK,+BAA+B;AACpC,mBAAW,eAAe,KAAK,aAAa,OAAA,GAAU;AACpD,cAAI,CAAC,CAAC,aAAa,QAAQ,EAAE,SAAS,YAAY,KAAK,GAAG;AACxD,uBAAW,YAAY,YAAY,WAAW;AAC5C,kBAAI,SAAS,eAAe,QAAQ,SAAS,YAAY;AACvD,wBAAQ,SAAS,MAAA;AAAA,kBACf,KAAK;AAAA,kBACL,KAAK;AACH,yBAAK,kBAAkB;AAAA,sBACrB,SAAS;AAAA,sBACT,SAAS;AAAA,oBAAA;AAEX,yBAAK,kBAAkB,OAAO,SAAS,GAAG;AAC1C;AAAA,kBACF,KAAK;AACH,yBAAK,kBAAkB,OAAO,SAAS,GAAG;AAC1C,yBAAK,kBAAkB,IAAI,SAAS,GAAG;AACvC;AAAA,gBAAA;AAAA,cAEN;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,cAAM,6CAA6B,IAAA;AAEnC,mBAAW,eAAe,KAAK,aAAa,OAAA,GAAU;AACpD,cAAI,YAAY,UAAU,aAAa;AACrC,uBAAW,YAAY,YAAY,WAAW;AAC5C,kBAAI,SAAS,eAAe,QAAQ,YAAY,IAAI,SAAS,GAAG,GAAG;AACjE,uCAAuB,IAAI,SAAS,KAAK;AAAA,kBACvC,MAAM,SAAS;AAAA,kBACf,OAAO,SAAS;AAAA,gBAAA,CACjB;AAAA,cACH;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,mBAAW,OAAO,aAAa;AAC7B,gBAAM,uBAAuB,oBAAoB,IAAI,GAAG;AACxD,gBAAM,kBAAkB,KAAK,IAAI,GAAG;AAGpC,gBAAM,cAAc,uBAAuB,IAAI,GAAG;AAClD,gBAAM,kBACJ,eACA,oBAAoB,UACpB,KAAK,UAAU,YAAY,OAAO,eAAe;AAEnD,cAAI,CAAC,iBAAiB;AACpB,gBACE,yBAAyB,UACzB,oBAAoB,QACpB;AACA,qBAAO,KAAK;AAAA,gBACV,MAAM;AAAA,gBACN;AAAA,gBACA,OAAO;AAAA,cAAA,CACR;AAAA,YACH,WACE,yBAAyB,UACzB,oBAAoB,QACpB;AACA,qBAAO,KAAK;AAAA,gBACV,MAAM;AAAA,gBACN;AAAA,gBACA,OAAO;AAAA,cAAA,CACR;AAAA,YACH,WACE,yBAAyB,UACzB,oBAAoB,UACpB,CAAC,KAAK,UAAU,sBAAsB,eAAe,GACrD;AACA,qBAAO,KAAK;AAAA,gBACV,MAAM;AAAA,gBACN;AAAA,gBACA,OAAO;AAAA,gBACP,eAAe;AAAA,cAAA,CAChB;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAGA,aAAK,QAAQ,KAAK,cAAA;AAGlB,aAAK,WAAW,QAAQ,IAAI;AAE5B,aAAK,4BAA4B,CAAA;AAGjC,aAAK,oBAAoB,MAAA;AAGzB,gBAAQ,UAAU,KAAK,MAAM;AAC3B,eAAK,mBAAmB,MAAA;AAAA,QAC1B,CAAC;AAGD,YAAI,CAAC,KAAK,wBAAwB;AAChC,eAAK,yBAAyB;AAC9B,gBAAM,YAAY,CAAC,GAAG,KAAK,sBAAsB;AACjD,eAAK,yBAAyB,CAAA;AAC9B,oBAAU,QAAQ,CAAC,aAAa,SAAA,CAAU;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAwJA,SAAA,SAAS,CACP,MACAA,YACG;AACH,WAAK,yBAAyB,QAAQ;AACtC,YAAM,qBAAqB,qBAAA;AAG3B,UAAI,CAAC,sBAAsB,CAAC,KAAK,OAAO,UAAU;AAChD,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAEA,YAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;AAChD,YAAM,YAAuC,CAAA;AAG7C,YAAM,QAAQ,CAAC,SAAS;;AAEtB,cAAM,gBAAgB,KAAK,aAAa,MAAM,QAAQ;AAGtD,cAAM,MAAM,KAAK,eAAe,aAAa;AAC7C,YAAI,KAAK,IAAI,GAAG,GAAG;AACjB,gBAAM,mCAAmC,GAAG;AAAA,QAC9C;AACA,cAAM,YAAY,KAAK,kBAAkB,KAAK,IAAI;AAElD,cAAM,WAAyC;AAAA,UAC7C,YAAY,OAAO,WAAA;AAAA,UACnB,UAAU,CAAA;AAAA,UACV,UAAU;AAAA;AAAA;AAAA;AAAA,UAIV,SAAS,OAAO;AAAA,YACd,OAAO,KAAK,IAAI,EAAE,IAAI,CAAC,MAAM;AAAA,cAC3B;AAAA,cACA,cAAc,CAA+B;AAAA,YAAA,CAC9C;AAAA,UAAA;AAAA,UAEH;AAAA,UACA;AAAA,UACA,UAAUA,WAAA,gBAAAA,QAAQ;AAAA,UAClB,gBAAc,gBAAK,OAAO,MAAK,oBAAjB,gCAAwC,CAAA;AAAA,UACtD,aAAYA,WAAA,gBAAAA,QAAQ,eAAc;AAAA,UAClC,MAAM;AAAA,UACN,+BAAe,KAAA;AAAA,UACf,+BAAe,KAAA;AAAA,UACf,YAAY;AAAA,QAAA;AAGd,kBAAU,KAAK,QAAQ;AAAA,MACzB,CAAC;AAGD,UAAI,oBAAoB;AACtB,2BAAmB,eAAe,SAAS;AAE3C,aAAK,aAAa,IAAI,mBAAmB,IAAI,kBAAkB;AAC/D,aAAK,yBAAA;AAEL,eAAO;AAAA,MACT,OAAO;AAEL,cAAM,sBAAsB,kBAAqB;AAAA,UAC/C,YAAY,OAAO,WAAW;AAE5B,mBAAO,MAAM,KAAK,OAAO,SAAU;AAAA,cACjC,aACE,OAAO;AAAA,cAIT,YAAY;AAAA,YAAA,CACb;AAAA,UACH;AAAA,QAAA,CACD;AAGD,4BAAoB,eAAe,SAAS;AAC5C,4BAAoB,OAAA;AAGpB,aAAK,aAAa,IAAI,oBAAoB,IAAI,mBAAmB;AACjE,aAAK,yBAAA;AAEL,eAAO;AAAA,MACT;AAAA,IACF;AAuQA,SAAA,SAAS,CACP,MACAA,YACyB;AACzB,WAAK,yBAAyB,QAAQ;AAEtC,YAAM,qBAAqB,qBAAA;AAG3B,UAAI,CAAC,sBAAsB,CAAC,KAAK,OAAO,UAAU;AAChD,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAEA,UAAI,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,GAAG;AAC5C,cAAM,IAAI,MAAM,+BAA+B;AAAA,MACjD;AAEA,YAAM,YAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;AACpD,YAAM,YAAuD,CAAA;AAE7D,iBAAW,OAAO,WAAW;AAC3B,YAAI,CAAC,KAAK,IAAI,GAAG,GAAG;AAClB,gBAAM,IAAI;AAAA,YACR,0CAA0C,GAAG;AAAA,UAAA;AAAA,QAEjD;AACA,cAAM,YAAY,KAAK,kBAAkB,KAAK,KAAK,IAAI,GAAG,CAAE;AAC5D,cAAM,WAA+C;AAAA,UACnD,YAAY,OAAO,WAAA;AAAA,UACnB,UAAU,KAAK,IAAI,GAAG;AAAA,UACtB,UAAU,KAAK,IAAI,GAAG;AAAA,UACtB,SAAS,KAAK,IAAI,GAAG;AAAA,UACrB;AAAA,UACA;AAAA,UACA,UAAUA,WAAA,gBAAAA,QAAQ;AAAA,UAClB,cAAe,KAAK,eAAe,IAAI,GAAG,KAAK,CAAA;AAAA,UAI/C,aAAYA,WAAA,gBAAAA,QAAQ,eAAc;AAAA,UAClC,MAAM;AAAA,UACN,+BAAe,KAAA;AAAA,UACf,+BAAe,KAAA;AAAA,UACf,YAAY;AAAA,QAAA;AAGd,kBAAU,KAAK,QAAQ;AAAA,MACzB;AAGA,UAAI,oBAAoB;AACtB,2BAAmB,eAAe,SAAS;AAE3C,aAAK,aAAa,IAAI,mBAAmB,IAAI,kBAAkB;AAC/D,aAAK,yBAAA;AAEL,eAAO;AAAA,MACT;AAGA,YAAM,sBAAsB,kBAAqB;AAAA,QAC/C,YAAY;AAAA,QACZ,YAAY,OAAO,WAAW;AAE5B,iBAAO,KAAK,OAAO,SAAU;AAAA,YAC3B,aACE,OAAO;AAAA,YAIT,YAAY;AAAA,UAAA,CACb;AAAA,QACH;AAAA,MAAA,CACD;AAGD,0BAAoB,eAAe,SAAS;AAC5C,0BAAoB,OAAA;AAEpB,WAAK,aAAa,IAAI,oBAAoB,IAAI,mBAAmB;AACjE,WAAK,yBAAA;AAEL,aAAO;AAAA,IACT;AAt6CE,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AACA,QAAI,OAAO,IAAI;AACb,WAAK,KAAK,OAAO;AAAA,IACnB,OAAO;AACL,WAAK,KAAK,OAAO,WAAA;AAAA,IACnB;AAGA,QAAI,CAAC,OAAO,MAAM;AAChB,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AAEA,SAAK,eAAe,IAAI;AAAA,MAAoC,CAAC,GAAG,MAC9D,EAAE,iBAAiB,CAAC;AAAA,IAAA;AAGtB,SAAK,SAAS;AAGd,qBAAiB,IAAI,KAAK,IAAI,IAAI;AAGlC,QAAI,KAAK,OAAO,SAAS;AACvB,WAAK,aAAa,IAAI,UAAmB,KAAK,OAAO,OAAO;AAAA,IAC9D,OAAO;AACL,WAAK,iCAAiB,IAAA;AAAA,IACxB;AAGA,QAAI,OAAO,cAAc,MAAM;AAC7B,WAAK,UAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAlHO,cAAc,UAA4B;AAC/C,SAAK,uBAAuB,KAAK,QAAQ;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAOA,IAAW,SAA2B;AACpC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,yBAAyB,WAAyB;AACxD,YAAQ,KAAK,SAAA;AAAA,MACX,KAAK;AACH,cAAM,IAAI;AAAA,UACR,kBAAkB,SAAS,mBAAmB,KAAK,EAAE;AAAA,QAAA;AAAA,MAGzD,KAAK;AACH,cAAM,IAAI;AAAA,UACR,kBAAkB,SAAS,mBAAmB,KAAK,EAAE;AAAA,QAAA;AAAA,IAEvD;AAAA,EAEN;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,yBACN,MACA,IACM;AACN,QAAI,SAAS,IAAI;AAEf;AAAA,IACF;AACA,UAAM,mBAGF;AAAA,MACF,MAAM,CAAC,WAAW,SAAS,YAAY;AAAA,MACvC,SAAS,CAAC,iBAAiB,SAAS,YAAY;AAAA,MAChD,eAAe,CAAC,SAAS,SAAS,YAAY;AAAA,MAC9C,OAAO,CAAC,cAAc,OAAO;AAAA,MAC7B,OAAO,CAAC,cAAc,MAAM;AAAA,MAC5B,cAAc,CAAC,WAAW,OAAO;AAAA,IAAA;AAGnC,QAAI,CAAC,iBAAiB,IAAI,EAAE,SAAS,EAAE,GAAG;AACxC,YAAM,IAAI;AAAA,QACR,8CAA8C,IAAI,SAAS,EAAE,qBAAqB,KAAK,EAAE;AAAA,MAAA;AAAA,IAE7F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,UAAU,WAAmC;AACnD,SAAK,yBAAyB,KAAK,SAAS,SAAS;AACrD,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAkDO,qBAA2B;AAChC,SAAK,UAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAkB;AACxB,QAAI,KAAK,YAAY,UAAU,KAAK,YAAY,cAAc;AAC5D;AAAA,IACF;AAEA,SAAK,UAAU,SAAS;AAExB,QAAI;AACF,YAAM,YAAY,KAAK,OAAO,KAAK,KAAK;AAAA,QACtC,YAAY;AAAA,QACZ,OAAO,MAAM;AACX,eAAK,0BAA0B,KAAK;AAAA,YAClC,WAAW;AAAA,YACX,YAAY,CAAA;AAAA,UAAC,CACd;AAAA,QACH;AAAA,QACA,OAAO,CAAC,sBAAqD;AAC3D,gBAAM,qBACJ,KAAK,0BACH,KAAK,0BAA0B,SAAS,CAC1C;AACF,cAAI,CAAC,oBAAoB;AACvB,kBAAM,IAAI,MAAM,yCAAyC;AAAA,UAC3D;AACA,cAAI,mBAAmB,WAAW;AAChC,kBAAM,IAAI;AAAA,cACR;AAAA,YAAA;AAAA,UAEJ;AACA,gBAAM,MAAM,KAAK,eAAe,kBAAkB,KAAK;AAGvD,cAAI,kBAAkB,SAAS,UAAU;AACvC,gBACE,KAAK,WAAW,IAAI,GAAG,KACvB,CAAC,mBAAmB,WAAW;AAAA,cAC7B,CAAC,OAAO,GAAG,QAAQ,OAAO,GAAG,SAAS;AAAA,YAAA,GAExC;AACA,oBAAM,IAAI;AAAA,gBACR,oCAAoC,GAAG,4DAA4D,KAAK,EAAE;AAAA,cAAA;AAAA,YAE9G;AAAA,UACF;AAEA,gBAAM,UAA4B;AAAA,YAChC,GAAG;AAAA,YACH;AAAA,UAAA;AAEF,6BAAmB,WAAW,KAAK,OAAO;AAAA,QAC5C;AAAA,QACA,QAAQ,MAAM;AACZ,gBAAM,qBACJ,KAAK,0BACH,KAAK,0BAA0B,SAAS,CAC1C;AACF,cAAI,CAAC,oBAAoB;AACvB,kBAAM,IAAI,MAAM,uCAAuC;AAAA,UACzD;AACA,cAAI,mBAAmB,WAAW;AAChC,kBAAM,IAAI;AAAA,cACR;AAAA,YAAA;AAAA,UAEJ;AAEA,6BAAmB,YAAY;AAI/B,cAAI,KAAK,YAAY,WAAW;AAC9B,iBAAK,UAAU,eAAe;AAAA,UAChC;AAEA,eAAK,0BAAA;AAGL,cAAI,KAAK,YAAY,iBAAiB;AACpC,iBAAK,UAAU,OAAO;AAAA,UACxB;AAAA,QACF;AAAA,MAAA,CACD;AAGD,WAAK,gBAAgB,OAAO,cAAc,aAAa,YAAY;AAAA,IACrE,SAAS,OAAO;AACd,WAAK,UAAU,OAAO;AACtB,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAyB;AAC9B,QAAI,KAAK,gBAAgB;AACvB,aAAO,KAAK;AAAA,IACd;AAEA,SAAK,iBAAiB,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3D,UAAI,KAAK,YAAY,SAAS;AAC5B,gBAAA;AACA;AAAA,MACF;AAEA,UAAI,KAAK,YAAY,SAAS;AAC5B,eAAO,IAAI,MAAM,8BAA8B,CAAC;AAChD;AAAA,MACF;AAGA,WAAK,cAAc,MAAM;AACvB,gBAAA;AAAA,MACF,CAAC;AAGD,UAAI,KAAK,YAAY,UAAU,KAAK,YAAY,cAAc;AAC5D,YAAI;AACF,eAAK,UAAA;AAAA,QACP,SAAS,OAAO;AACd,iBAAO,KAAK;AACZ;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,UAAyB;AAEpC,QAAI,KAAK,aAAa;AACpB,mBAAa,KAAK,WAAW;AAC7B,WAAK,cAAc;AAAA,IACrB;AAGA,QAAI;AACF,UAAI,KAAK,eAAe;AACtB,aAAK,cAAA;AACL,aAAK,gBAAgB;AAAA,MACvB;AAAA,IACF,SAAS,OAAO;AAEd,qBAAe,MAAM;AACnB,YAAI,iBAAiB,OAAO;AAE1B,gBAAM,eAAe,IAAI;AAAA,YACvB,eAAe,KAAK,EAAE,2CAA2C,MAAM,OAAO;AAAA,UAAA;AAEhF,uBAAa,QAAQ;AACrB,uBAAa,QAAQ,MAAM;AAC3B,gBAAM;AAAA,QACR,OAAO;AACL,gBAAM,IAAI;AAAA,YACR,eAAe,KAAK,EAAE,2CAA2C,OAAO,KAAK,CAAC;AAAA,UAAA;AAAA,QAElF;AAAA,MACF,CAAC;AAAA,IACH;AAGA,SAAK,WAAW,MAAA;AAChB,SAAK,eAAe,MAAA;AACpB,SAAK,kBAAkB,MAAA;AACvB,SAAK,kBAAkB,MAAA;AACvB,SAAK,QAAQ;AACb,SAAK,4BAA4B,CAAA;AACjC,SAAK,WAAW,MAAA;AAChB,SAAK,yBAAyB;AAC9B,SAAK,yBAAyB,CAAA;AAC9B,SAAK,iBAAiB;AACtB,SAAK,gBAAgB,CAAA;AACrB,SAAK,oBAAoB;AAGzB,SAAK,UAAU,YAAY;AAE3B,WAAO,QAAQ,QAAA;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAqB;AAC3B,QAAI,KAAK,aAAa;AACpB,mBAAa,KAAK,WAAW;AAAA,IAC/B;AAEA,UAAM,SAAS,KAAK,OAAO,UAAU;AACrC,SAAK,cAAc,WAAW,MAAM;AAClC,UAAI,KAAK,2BAA2B,GAAG;AACrC,aAAK,QAAA;AAAA,MACP;AAAA,IACF,GAAG,MAAM;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAsB;AAC5B,QAAI,KAAK,aAAa;AACpB,mBAAa,KAAK,WAAW;AAC7B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,SAAK;AACL,SAAK,cAAA;AAGL,QAAI,KAAK,YAAY,gBAAgB,KAAK,YAAY,QAAQ;AAC5D,WAAK,UAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,SAAK;AAEL,QAAI,KAAK,2BAA2B,GAAG;AACrC,WAAK,yBAAyB;AAC9B,WAAK,aAAA;AAAA,IACP,WAAW,KAAK,yBAAyB,GAAG;AAC1C,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,2BAAiC;AAEvC,QAAI,KAAK,8BAA8B;AACrC;AAAA,IACF;AAEA,UAAM,gBAAgB,IAAI,IAAI,KAAK,iBAAiB;AACpD,UAAM,kBAAkB,IAAI,IAAI,KAAK,iBAAiB;AAGtD,SAAK,kBAAkB,MAAA;AACvB,SAAK,kBAAkB,MAAA;AAEvB,UAAM,qBAA8C,CAAA;AACpD,UAAM,wBAAiD,CAAA;AAEvD,eAAW,eAAe,KAAK,aAAa,OAAA,GAAU;AACpD,UAAI,YAAY,UAAU,aAAa;AACrC,8BAAsB,KAAK,WAAW;AAAA,MACxC,WAAW,CAAC,CAAC,aAAa,QAAQ,EAAE,SAAS,YAAY,KAAK,GAAG;AAC/D,2BAAmB,KAAK,WAAW;AAAA,MACrC;AAAA,IACF;AAGA,eAAW,eAAe,oBAAoB;AAC5C,iBAAW,YAAY,YAAY,WAAW;AAC5C,YAAI,SAAS,eAAe,QAAQ,SAAS,YAAY;AACvD,kBAAQ,SAAS,MAAA;AAAA,YACf,KAAK;AAAA,YACL,KAAK;AACH,mBAAK,kBAAkB,IAAI,SAAS,KAAK,SAAS,QAAa;AAC/D,mBAAK,kBAAkB,OAAO,SAAS,GAAG;AAC1C;AAAA,YACF,KAAK;AACH,mBAAK,kBAAkB,OAAO,SAAS,GAAG;AAC1C,mBAAK,kBAAkB,IAAI,SAAS,GAAG;AACvC;AAAA,UAAA;AAAA,QAEN;AAAA,MACF;AAAA,IACF;AAGA,SAAK,QAAQ,KAAK,cAAA;AAGlB,UAAM,SAAwC,CAAA;AAC9C,SAAK,yBAAyB,eAAe,iBAAiB,MAAM;AAGpE,UAAM,6BAA6B,OAAO;AAAA,MACxC,CAAC,UAAU,CAAC,KAAK,mBAAmB,IAAI,MAAM,GAAG;AAAA,IAAA;AAKnD,QAAI,KAAK,0BAA0B,SAAS,GAAG;AAC7C,YAAM,sCAAsB,IAAA;AAC5B,YAAM,oDAAoC,IAAA;AAG1C,iBAAW,eAAe,KAAK,2BAA2B;AACxD,mBAAW,aAAa,YAAY,YAAY;AAC9C,0BAAgB,IAAI,UAAU,GAAW;AAAA,QAC3C;AAAA,MACF;AAGA,iBAAW,MAAM,uBAAuB;AACtC,mBAAW,YAAY,GAAG,WAAW;AACnC,cAAI,SAAS,eAAe,MAAM;AAChC,0CAA8B,IAAI,SAAS,UAAU;AAAA,UACvD;AAAA,QACF;AAAA,MACF;AAKA,YAAM,iBAAiB,2BAA2B,OAAO,CAAC,UAAU;AAClE,YAAI,MAAM,SAAS,YAAY,gBAAgB,IAAI,MAAM,GAAG,GAAG;AAG7D,gBAAM,8BAA8B,mBAAmB;AAAA,YAAK,CAAC,OAC3D,GAAG,UAAU;AAAA,cACX,CAAC,MAAM,EAAE,eAAe,QAAQ,EAAE,QAAQ,MAAM;AAAA,YAAA;AAAA,UAClD;AAGF,cAAI,CAAC,6BAA6B;AAChC,mBAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AAED,WAAK,WAAW,cAAc;AAAA,IAChC,OAAO;AAEL,WAAK,WAAW,0BAA0B;AAAA,IAC5C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAwB;AAC9B,UAAM,aAAa,KAAK,WAAW;AACnC,UAAM,oBAAoB,MAAM,KAAK,KAAK,iBAAiB,EAAE;AAAA,MAC3D,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,KAAK,CAAC,KAAK,kBAAkB,IAAI,GAAG;AAAA,IAAA,EACpE;AACF,UAAM,qBAAqB,MAAM,KAAK,KAAK,kBAAkB,KAAA,CAAM,EAAE;AAAA,MACnE,CAAC,QAAQ,CAAC,KAAK,WAAW,IAAI,GAAG;AAAA,IAAA,EACjC;AAEF,WAAO,aAAa,oBAAoB;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKQ,yBACN,iBACA,iBACA,QACM;AACN,UAAM,8BAAc,IAAI;AAAA,MACtB,GAAG,gBAAgB,KAAA;AAAA,MACnB,GAAG,KAAK,kBAAkB,KAAA;AAAA,MAC1B,GAAG;AAAA,MACH,GAAG,KAAK;AAAA,IAAA,CACT;AAED,eAAW,OAAO,SAAS;AACzB,YAAM,eAAe,KAAK,IAAI,GAAG;AACjC,YAAM,gBAAgB,KAAK;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAGF,UAAI,kBAAkB,UAAa,iBAAiB,QAAW;AAC7D,eAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,eAAe;AAAA,MAC3D,WAAW,kBAAkB,UAAa,iBAAiB,QAAW;AACpE,eAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,cAAc;AAAA,MAC1D,WACE,kBAAkB,UAClB,iBAAiB,UACjB,kBAAkB,cAClB;AACA,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN;AAAA,UACA,OAAO;AAAA,UACP;AAAA,QAAA,CACD;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBACN,KACA,iBACA,iBACe;AACf,QAAI,gBAAgB,IAAI,GAAG,GAAG;AAC5B,aAAO;AAAA,IACT;AACA,QAAI,gBAAgB,IAAI,GAAG,GAAG;AAC5B,aAAO,gBAAgB,IAAI,GAAG;AAAA,IAChC;AACA,WAAO,KAAK,WAAW,IAAI,GAAG;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKQ,WACN,SACA,cAAc,OACR;AACN,QAAI,KAAK,qBAAqB,CAAC,aAAa;AAE1C,WAAK,cAAc,KAAK,GAAG,OAAO;AAClC;AAAA,IACF;AAGA,QAAI,eAAe;AAEnB,QAAI,aAAa;AAEf,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,EAAG;AAG/B,eAAW,YAAY,KAAK,iBAAiB;AAC3C,eAAS,YAAY;AAAA,IACvB;AAGA,QAAI,KAAK,mBAAmB,OAAO,GAAG;AAEpC,YAAM,mCAAmB,IAAA;AACzB,iBAAW,UAAU,cAAc;AACjC,YAAI,KAAK,mBAAmB,IAAI,OAAO,GAAG,GAAG;AAC3C,cAAI,CAAC,aAAa,IAAI,OAAO,GAAG,GAAG;AACjC,yBAAa,IAAI,OAAO,KAAK,CAAA,CAAE;AAAA,UACjC;AACA,uBAAa,IAAI,OAAO,GAAG,EAAG,KAAK,MAAM;AAAA,QAC3C;AAAA,MACF;AAGA,iBAAW,CAAC,KAAK,UAAU,KAAK,cAAc;AAC5C,cAAM,eAAe,KAAK,mBAAmB,IAAI,GAAG;AACpD,mBAAW,YAAY,cAAc;AACnC,mBAAS,UAAU;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,IAAI,KAA0B;AAEnC,QAAI,KAAK,kBAAkB,IAAI,GAAG,GAAG;AACnC,aAAO;AAAA,IACT;AAGA,QAAI,KAAK,kBAAkB,IAAI,GAAG,GAAG;AACnC,aAAO,KAAK,kBAAkB,IAAI,GAAG;AAAA,IACvC;AAGA,WAAO,KAAK,WAAW,IAAI,GAAG;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKO,IAAI,KAAoB;AAE7B,QAAI,KAAK,kBAAkB,IAAI,GAAG,GAAG;AACnC,aAAO;AAAA,IACT;AAGA,QAAI,KAAK,kBAAkB,IAAI,GAAG,GAAG;AACnC,aAAO;AAAA,IACT;AAGA,WAAO,KAAK,WAAW,IAAI,GAAG;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAW,OAAe;AACxB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,CAAQ,OAA+B;AAErC,eAAW,OAAO,KAAK,WAAW,KAAA,GAAQ;AACxC,UAAI,CAAC,KAAK,kBAAkB,IAAI,GAAG,GAAG;AACpC,cAAM;AAAA,MACR;AAAA,IACF;AAEA,eAAW,OAAO,KAAK,kBAAkB,KAAA,GAAQ;AAC/C,UAAI,CAAC,KAAK,WAAW,IAAI,GAAG,KAAK,CAAC,KAAK,kBAAkB,IAAI,GAAG,GAAG;AAGjE,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,CAAQ,SAA8B;AACpC,eAAW,OAAO,KAAK,QAAQ;AAC7B,YAAM,QAAQ,KAAK,IAAI,GAAG;AAC1B,UAAI,UAAU,QAAW;AACvB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,CAAQ,UAAuC;AAC7C,eAAW,OAAO,KAAK,QAAQ;AAC7B,YAAM,QAAQ,KAAK,IAAI,GAAG;AAC1B,UAAI,UAAU,QAAW;AACvB,cAAM,CAAC,KAAK,KAAK;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,EAAS,OAAO,QAAQ,IAAiC;AACvD,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,WAAW;AACzC,YAAM,CAAC,KAAK,KAAK;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,QACL,YACM;AACN,QAAI,QAAQ;AACZ,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,WAAW;AACzC,iBAAW,OAAO,KAAK,OAAO;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,IACL,YACU;AACV,UAAM,SAAmB,CAAA;AACzB,QAAI,QAAQ;AACZ,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,WAAW;AACzC,aAAO,KAAK,WAAW,OAAO,KAAK,OAAO,CAAC;AAAA,IAC7C;AACA,WAAO;AAAA,EACT;AAAA,EAoNQ,qBAAqB,QAAoC;AAE/D,QAAI,UAAU,OAAO,WAAW,YAAY,eAAe,QAAQ;AACjE,aAAO;AAAA,IACT;AAEA,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEO,eAAe,MAAe;AACnC,WAAO,KAAK,OAAO,OAAO,IAAI;AAAA,EAChC;AAAA,EAEO,kBAAkB,KAAU,MAAmB;AACpD,QAAI,OAAO,QAAQ,aAAa;AAC9B,YAAM,IAAI;AAAA,QACR,gDAAgD,KAAK,UAAU,IAAI,CAAC;AAAA,MAAA;AAAA,IAExE;AAEA,WAAO,QAAQ,KAAK,EAAE,IAAI,GAAG;AAAA,EAC/B;AAAA,EAEQ,UAAU,GAAQ,GAAiB;AACzC,QAAI,MAAM,EAAG,QAAO;AACpB,QAAI,KAAK,QAAQ,KAAK,KAAM,QAAO;AACnC,QAAI,OAAO,MAAM,OAAO,EAAG,QAAO;AAElC,QAAI,OAAO,MAAM,UAAU;AACzB,UAAI,MAAM,QAAQ,CAAC,MAAM,MAAM,QAAQ,CAAC,EAAG,QAAO;AAElD,YAAM,QAAQ,OAAO,KAAK,CAAC;AAC3B,YAAM,QAAQ,OAAO,KAAK,CAAC;AAC3B,UAAI,MAAM,WAAW,MAAM,OAAQ,QAAO;AAE1C,YAAM,WAAW,IAAI,IAAI,KAAK;AAC9B,iBAAW,OAAO,OAAO;AACvB,YAAI,CAAC,SAAS,IAAI,GAAG,EAAG,QAAO;AAC/B,YAAI,CAAC,KAAK,UAAU,EAAE,GAAG,GAAG,EAAE,GAAG,CAAC,EAAG,QAAO;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,aACN,MACA,MACA,KACW;AACX,QAAI,CAAC,KAAK,OAAO,OAAQ,QAAO;AAEhC,UAAM,iBAAiB,KAAK,qBAAqB,KAAK,OAAO,MAAM;AAGnE,QAAI,SAAS,YAAY,KAAK;AAE5B,YAAM,eAAe,KAAK,IAAI,GAAG;AAEjC,UACE,gBACA,QACA,OAAO,SAAS,YAChB,OAAO,iBAAiB,UACxB;AAEA,cAAM,aAAa,OAAO,OAAO,CAAA,GAAI,cAAc,IAAI;AAGvD,cAAMC,UAAS,eAAe,WAAW,EAAE,SAAS,UAAU;AAG9D,YAAIA,mBAAkB,SAAS;AAC7B,gBAAM,IAAI,UAAU,uCAAuC;AAAA,QAC7D;AAGA,YAAI,YAAYA,WAAUA,QAAO,QAAQ;AACvC,gBAAM,cAAcA,QAAO,OAAO,IAAI,CAAC,UAAA;;AAAW;AAAA,cAChD,SAAS,MAAM;AAAA,cACf,OAAM,WAAM,SAAN,mBAAY,IAAI,CAAC,MAAM,OAAO,CAAC;AAAA,YAAC;AAAA,WACtC;AACF,gBAAM,IAAI,sBAAsB,MAAM,WAAW;AAAA,QACnD;AAIA,eAAO;AAAA,MACT;AAAA,IACF;AAGA,UAAM,SAAS,eAAe,WAAW,EAAE,SAAS,IAAI;AAGxD,QAAI,kBAAkB,SAAS;AAC7B,YAAM,IAAI,UAAU,uCAAuC;AAAA,IAC7D;AAGA,QAAI,YAAY,UAAU,OAAO,QAAQ;AACvC,YAAM,cAAc,OAAO,OAAO,IAAI,CAAC,UAAA;;AAAW;AAAA,UAChD,SAAS,MAAM;AAAA,UACf,OAAM,WAAM,SAAN,mBAAY,IAAI,CAAC,MAAM,OAAO,CAAC;AAAA,QAAC;AAAA,OACtC;AACF,YAAM,IAAI,sBAAsB,MAAM,WAAW;AAAA,IACnD;AAEA,WAAO,OAAO;AAAA,EAChB;AAAA,EAoMA,OACE,MACA,kBACA,eACA;AACA,QAAI,OAAO,SAAS,aAAa;AAC/B,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAEA,SAAK,yBAAyB,QAAQ;AAEtC,UAAM,qBAAqB,qBAAA;AAG3B,QAAI,CAAC,sBAAsB,CAAC,KAAK,OAAO,UAAU;AAChD,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAEA,UAAM,UAAU,MAAM,QAAQ,IAAI;AAClC,UAAM,YAAY,UAAU,OAAO,CAAC,IAAI;AAExC,QAAI,WAAW,UAAU,WAAW,GAAG;AACrC,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AAEA,UAAM,WACJ,OAAO,qBAAqB,aAAa,mBAAmB;AAC9D,UAAM,SACJ,OAAO,qBAAqB,aAAa,CAAA,IAAK;AAGhD,UAAM,iBAAiB,UAAU,IAAI,CAAC,QAAQ;AAC5C,YAAM,OAAO,KAAK,IAAI,GAAG;AACzB,UAAI,CAAC,MAAM;AACT,cAAM,IAAI;AAAA,UACR,YAAY,GAAG;AAAA,QAAA;AAAA,MAEnB;AAEA,aAAO;AAAA,IACT,CAAC;AAED,QAAI;AACJ,QAAI,SAAS;AAEX,qBAAe;AAAA,QACb;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ,OAAO;AACL,YAAM,SAAS;AAAA,QACb,eAAe,CAAC;AAAA,QAChB;AAAA,MAAA;AAEF,qBAAe,CAAC,MAAM;AAAA,IACxB;AAGA,UAAM,YAAuD,UAC1D,IAAI,CAAC,KAAK,UAAU;AACnB,YAAM,cAAc,aAAa,KAAK;AAGtC,UAAI,CAAC,eAAe,OAAO,KAAK,WAAW,EAAE,WAAW,GAAG;AACzD,eAAO;AAAA,MACT;AAEA,YAAM,eAAe,eAAe,KAAK;AAEzC,YAAM,yBAAyB,KAAK;AAAA,QAClC;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAIF,YAAM,eAAe,OAAO;AAAA,QAC1B,CAAA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAIF,YAAM,iBAAiB,KAAK,eAAe,YAAY;AACvD,YAAM,iBAAiB,KAAK,eAAe,YAAY;AAEvD,UAAI,mBAAmB,gBAAgB;AACrC,cAAM,IAAI;AAAA,UACR,8DAA8D,cAAc,0BAA0B,cAAc;AAAA,QAAA;AAAA,MAExH;AAEA,YAAM,YAAY,KAAK,kBAAkB,gBAAgB,YAAY;AAErE,aAAO;AAAA,QACL,YAAY,OAAO,WAAA;AAAA,QACnB,UAAU;AAAA,QACV,UAAU;AAAA,QACV,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA,UAAU,OAAO;AAAA,QACjB,cAAe,KAAK,eAAe,IAAI,GAAG,KAAK,CAAA;AAAA,QAI/C,YAAY,OAAO,cAAc;AAAA,QACjC,MAAM;AAAA,QACN,+BAAe,KAAA;AAAA,QACf,+BAAe,KAAA;AAAA,QACf,YAAY;AAAA,MAAA;AAAA,IAEhB,CAAC,EACA,OAAO,OAAO;AAGjB,QAAI,UAAU,WAAW,GAAG;AAC1B,YAAM,mBAAmB,kBAAkB;AAAA,QACzC,YAAY,YAAY;AAAA,QAAC;AAAA,MAAA,CAC1B;AACD,uBAAiB,OAAA;AACjB,aAAO;AAAA,IACT;AAGA,QAAI,oBAAoB;AACtB,yBAAmB,eAAe,SAAS;AAE3C,WAAK,aAAa,IAAI,mBAAmB,IAAI,kBAAkB;AAC/D,WAAK,yBAAA;AAEL,aAAO;AAAA,IACT;AAKA,UAAM,sBAAsB,kBAAqB;AAAA,MAC/C,YAAY,OAAO,WAAW;AAE5B,eAAO,KAAK,OAAO,SAAU;AAAA,UAC3B,aACE,OAAO;AAAA,UAIT,YAAY;AAAA,QAAA,CACb;AAAA,MACH;AAAA,IAAA,CACD;AAGD,wBAAoB,eAAe,SAAS;AAC5C,wBAAoB,OAAA;AAIpB,SAAK,aAAa,IAAI,oBAAoB,IAAI,mBAAmB;AACjE,SAAK,yBAAA;AAEL,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuIA,IAAI,QAAQ;AACV,UAAM,6BAAa,IAAA;AACnB,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,WAAW;AACzC,aAAO,IAAI,KAAK,KAAK;AAAA,IACvB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,iBAAwC;AAEtC,QAAI,KAAK,OAAO,KAAK,KAAK,2BAA2B,MAAM;AACzD,aAAO,QAAQ,QAAQ,KAAK,KAAK;AAAA,IACnC;AAGA,WAAO,IAAI,QAAsB,CAAC,YAAY;AAC5C,WAAK,cAAc,MAAM;AACvB,gBAAQ,KAAK,KAAK;AAAA,MACpB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,UAAU;AACZ,WAAO,MAAM,KAAK,KAAK,OAAA,CAAQ;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,mBAAsC;AAEpC,QAAI,KAAK,OAAO,KAAK,KAAK,2BAA2B,MAAM;AACzD,aAAO,QAAQ,QAAQ,KAAK,OAAO;AAAA,IACrC;AAGA,WAAO,IAAI,QAAkB,CAAC,YAAY;AACxC,WAAK,cAAc,MAAM;AACvB,gBAAQ,KAAK,OAAO;AAAA,MACtB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,wBAAiD;AACtD,WAAO,MAAM,KAAK,KAAK,QAAA,CAAS,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO;AAAA,MACvD,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IAAA,EACA;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBO,iBACL,UACA,EAAE,sBAAsB,MAAA,IAA6C,CAAA,GACzD;AAEZ,SAAK,cAAA;AAEL,QAAI,qBAAqB;AAEvB,eAAS,KAAK,uBAAuB;AAAA,IACvC;AAGA,SAAK,gBAAgB,IAAI,QAAQ;AAEjC,WAAO,MAAM;AACX,WAAK,gBAAgB,OAAO,QAAQ;AACpC,WAAK,iBAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,oBACL,KACA,UACA,EAAE,sBAAsB,MAAA,IAA6C,IACzD;AAEZ,SAAK,cAAA;AAEL,QAAI,CAAC,KAAK,mBAAmB,IAAI,GAAG,GAAG;AACrC,WAAK,mBAAmB,IAAI,KAAK,oBAAI,KAAK;AAAA,IAC5C;AAEA,QAAI,qBAAqB;AAEvB,eAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN;AAAA,UACA,OAAO,KAAK,IAAI,GAAG;AAAA,QAAA;AAAA,MACrB,CACD;AAAA,IACH;AAEA,SAAK,mBAAmB,IAAI,GAAG,EAAG,IAAI,QAAQ;AAE9C,WAAO,MAAM;AACX,YAAM,YAAY,KAAK,mBAAmB,IAAI,GAAG;AACjD,UAAI,WAAW;AACb,kBAAU,OAAO,QAAQ;AACzB,YAAI,UAAU,SAAS,GAAG;AACxB,eAAK,mBAAmB,OAAO,GAAG;AAAA,QACpC;AAAA,MACF;AACA,WAAK,iBAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,6BAAmC;AACzC,QAAI,KAAK,0BAA0B,WAAW,EAAG;AAGjD,SAAK,oBAAoB,MAAA;AAGzB,UAAM,iCAAiB,IAAA;AACvB,eAAW,eAAe,KAAK,2BAA2B;AACxD,iBAAW,aAAa,YAAY,YAAY;AAC9C,mBAAW,IAAI,UAAU,GAAW;AAAA,MACtC;AAAA,IACF;AAGA,eAAW,OAAO,YAAY;AAC5B,WAAK,mBAAmB,IAAI,GAAG;AAAA,IACjC;AAIA,eAAW,OAAO,YAAY;AAC5B,YAAM,eAAe,KAAK,IAAI,GAAG;AACjC,UAAI,iBAAiB,QAAW;AAC9B,aAAK,oBAAoB,IAAI,KAAK,YAAY;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,2BAAiC;AAGtC,SAAK,oBAAoB,KAAK,0BAA0B,SAAS;AAGjE,SAAK,2BAAA;AAEL,SAAK,yBAAA;AAAA,EACP;AACF;"}
@@ -184,7 +184,7 @@ function loadFromStorage(storageKey, storage) {
184
184
  return /* @__PURE__ */ new Map();
185
185
  }
186
186
  }
187
- function createLocalStorageSync(storageKey, storage, storageEventApi, getKey, lastKnownData) {
187
+ function createLocalStorageSync(storageKey, storage, storageEventApi, _getKey, lastKnownData) {
188
188
  let syncParams = null;
189
189
  const findChanges = (oldData, newData) => {
190
190
  const changes = [];
@@ -1 +1 @@
1
- {"version":3,"file":"local-storage.js","sources":["../../src/local-storage.ts"],"sourcesContent":["import type {\n CollectionConfig,\n DeleteMutationFnParams,\n InsertMutationFnParams,\n ResolveType,\n SyncConfig,\n UpdateMutationFnParams,\n UtilsRecord,\n} from \"./types\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\n\n/**\n * Storage API interface - subset of DOM Storage that we need\n */\nexport type StorageApi = Pick<Storage, `getItem` | `setItem` | `removeItem`>\n\n/**\n * Storage event API - subset of Window for 'storage' events only\n */\nexport type StorageEventApi = {\n addEventListener: (\n type: `storage`,\n listener: (event: StorageEvent) => void\n ) => void\n removeEventListener: (\n type: `storage`,\n listener: (event: StorageEvent) => void\n ) => void\n}\n\n/**\n * Internal storage format that includes version tracking\n */\ninterface StoredItem<T> {\n versionKey: string\n data: T\n}\n\n/**\n * Configuration interface for localStorage collection options\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n *\n * @remarks\n * Type resolution follows a priority order:\n * 1. If you provide an explicit type via generic parameter, it will be used\n * 2. If no explicit type is provided but a schema is, the schema's output type will be inferred\n * 3. If neither explicit type nor schema is provided, the fallback type will be used\n *\n * You should provide EITHER an explicit type OR a schema, but not both, as they would conflict.\n */\nexport interface LocalStorageCollectionConfig<\n TExplicit = unknown,\n TSchema extends StandardSchemaV1 = never,\n TFallback extends object = Record<string, unknown>,\n> {\n /**\n * The key to use for storing the collection data in localStorage/sessionStorage\n */\n storageKey: string\n\n /**\n * Storage API to use (defaults to window.localStorage)\n * Can be any object that implements the Storage interface (e.g., sessionStorage)\n */\n storage?: StorageApi\n\n /**\n * Storage event API to use for cross-tab synchronization (defaults to window)\n * Can be any object that implements addEventListener/removeEventListener for storage events\n */\n storageEventApi?: StorageEventApi\n\n /**\n * Collection identifier (defaults to \"local-collection:{storageKey}\" if not provided)\n */\n id?: string\n schema?: TSchema\n getKey: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>>[`getKey`]\n sync?: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>>[`sync`]\n\n /**\n * Optional asynchronous handler function called before an insert operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onInsert?: (\n params: InsertMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n\n /**\n * Optional asynchronous handler function called before an update operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onUpdate?: (\n params: UpdateMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n\n /**\n * Optional asynchronous handler function called before a delete operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onDelete?: (\n params: DeleteMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n}\n\n/**\n * Type for the clear utility function\n */\nexport type ClearStorageFn = () => void\n\n/**\n * Type for the getStorageSize utility function\n */\nexport type GetStorageSizeFn = () => number\n\n/**\n * LocalStorage collection utilities type\n */\nexport interface LocalStorageCollectionUtils extends UtilsRecord {\n clearStorage: ClearStorageFn\n getStorageSize: GetStorageSizeFn\n}\n\n/**\n * Validates that a value can be JSON serialized\n * @param value - The value to validate for JSON serialization\n * @param operation - The operation type being performed (for error messages)\n * @throws Error if the value cannot be JSON serialized\n */\nfunction validateJsonSerializable(value: any, operation: string): void {\n try {\n JSON.stringify(value)\n } catch (error) {\n throw new Error(\n `Cannot ${operation} item because it cannot be JSON serialized: ${\n error instanceof Error ? error.message : String(error)\n }`\n )\n }\n}\n\n/**\n * Generate a UUID for version tracking\n * @returns A unique identifier string for tracking data versions\n */\nfunction generateUuid(): string {\n return crypto.randomUUID()\n}\n\n/**\n * Creates localStorage collection options for use with a standard Collection\n *\n * This function creates a collection that persists data to localStorage/sessionStorage\n * and synchronizes changes across browser tabs using storage events.\n *\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n * @param config - Configuration options for the localStorage collection\n * @returns Collection options with utilities including clearStorage and getStorageSize\n *\n * @example\n * // Basic localStorage collection\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // localStorage collection with custom storage\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * storage: window.sessionStorage, // Use sessionStorage instead\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // localStorage collection with mutation handlers\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * getKey: (item) => item.id,\n * onInsert: async ({ transaction }) => {\n * console.log('Item inserted:', transaction.mutations[0].modified)\n * },\n * })\n * )\n */\nexport function localStorageCollectionOptions<\n TExplicit = unknown,\n TSchema extends StandardSchemaV1 = never,\n TFallback extends object = Record<string, unknown>,\n>(config: LocalStorageCollectionConfig<TExplicit, TSchema, TFallback>) {\n type ResolvedType = ResolveType<TExplicit, TSchema, TFallback>\n\n // Validate required parameters\n if (!config.storageKey) {\n throw new Error(`[LocalStorageCollection] storageKey must be provided.`)\n }\n\n // Default to window.localStorage if no storage is provided\n const storage =\n config.storage ||\n (typeof window !== `undefined` ? window.localStorage : null)\n\n if (!storage) {\n throw new Error(\n `[LocalStorageCollection] No storage available. Please provide a storage option or ensure window.localStorage is available.`\n )\n }\n\n // Default to window for storage events if not provided\n const storageEventApi =\n config.storageEventApi || (typeof window !== `undefined` ? window : null)\n\n if (!storageEventApi) {\n throw new Error(\n `[LocalStorageCollection] No storage event API available. Please provide a storageEventApi option or ensure window is available.`\n )\n }\n\n // Track the last known state to detect changes\n const lastKnownData = new Map<string | number, StoredItem<ResolvedType>>()\n\n // Create the sync configuration\n const sync = createLocalStorageSync<ResolvedType>(\n config.storageKey,\n storage,\n storageEventApi,\n config.getKey,\n lastKnownData\n )\n\n /**\n * Manual trigger function for local sync updates\n * Forces a check for storage changes and updates the collection if needed\n */\n const triggerLocalSync = () => {\n if (sync.manualTrigger) {\n sync.manualTrigger()\n }\n }\n\n /**\n * Save data to storage\n * @param dataMap - Map of items with version tracking to save to storage\n */\n const saveToStorage = (\n dataMap: Map<string | number, StoredItem<ResolvedType>>\n ): void => {\n try {\n // Convert Map to object format for storage\n const objectData: Record<string, StoredItem<ResolvedType>> = {}\n dataMap.forEach((storedItem, key) => {\n objectData[String(key)] = storedItem\n })\n const serialized = JSON.stringify(objectData)\n storage.setItem(config.storageKey, serialized)\n } catch (error) {\n console.error(\n `[LocalStorageCollection] Error saving data to storage key \"${config.storageKey}\":`,\n error\n )\n throw error\n }\n }\n\n /**\n * Removes all collection data from the configured storage\n */\n const clearStorage: ClearStorageFn = (): void => {\n storage.removeItem(config.storageKey)\n }\n\n /**\n * Get the size of the stored data in bytes (approximate)\n * @returns The approximate size in bytes of the stored collection data\n */\n const getStorageSize: GetStorageSizeFn = (): number => {\n const data = storage.getItem(config.storageKey)\n return data ? new Blob([data]).size : 0\n }\n\n /*\n * Create wrapper handlers for direct persistence operations that perform actual storage operations\n * Wraps the user's onInsert handler to also save changes to localStorage\n */\n const wrappedOnInsert = async (\n params: InsertMutationFnParams<ResolvedType>\n ) => {\n // Validate that all values in the transaction can be JSON serialized\n params.transaction.mutations.forEach((mutation) => {\n validateJsonSerializable(mutation.modified, `insert`)\n })\n\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onInsert) {\n handlerResult = (await config.onInsert(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Add new items with version keys\n params.transaction.mutations.forEach((mutation) => {\n const key = config.getKey(mutation.modified)\n const storedItem: StoredItem<ResolvedType> = {\n versionKey: generateUuid(),\n data: mutation.modified,\n }\n currentData.set(key, storedItem)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n const wrappedOnUpdate = async (\n params: UpdateMutationFnParams<ResolvedType>\n ) => {\n // Validate that all values in the transaction can be JSON serialized\n params.transaction.mutations.forEach((mutation) => {\n validateJsonSerializable(mutation.modified, `update`)\n })\n\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onUpdate) {\n handlerResult = (await config.onUpdate(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Update items with new version keys\n params.transaction.mutations.forEach((mutation) => {\n const key = config.getKey(mutation.modified)\n const storedItem: StoredItem<ResolvedType> = {\n versionKey: generateUuid(),\n data: mutation.modified,\n }\n currentData.set(key, storedItem)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n const wrappedOnDelete = async (\n params: DeleteMutationFnParams<ResolvedType>\n ) => {\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onDelete) {\n handlerResult = (await config.onDelete(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Remove items\n params.transaction.mutations.forEach((mutation) => {\n // For delete operations, mutation.original contains the full object\n const key = config.getKey(mutation.original)\n currentData.delete(key)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n // Extract standard Collection config properties\n const {\n storageKey: _storageKey,\n storage: _storage,\n storageEventApi: _storageEventApi,\n onInsert: _onInsert,\n onUpdate: _onUpdate,\n onDelete: _onDelete,\n id,\n ...restConfig\n } = config\n\n // Default id to a pattern based on storage key if not provided\n const collectionId = id ?? `local-collection:${config.storageKey}`\n\n return {\n ...restConfig,\n id: collectionId,\n sync,\n onInsert: wrappedOnInsert,\n onUpdate: wrappedOnUpdate,\n onDelete: wrappedOnDelete,\n utils: {\n clearStorage,\n getStorageSize,\n },\n }\n}\n\n/**\n * Load data from storage and return as a Map\n * @param storageKey - The key used to store data in the storage API\n * @param storage - The storage API to load from (localStorage, sessionStorage, etc.)\n * @returns Map of stored items with version tracking, or empty Map if loading fails\n */\nfunction loadFromStorage<T extends object>(\n storageKey: string,\n storage: StorageApi\n): Map<string | number, StoredItem<T>> {\n try {\n const rawData = storage.getItem(storageKey)\n if (!rawData) {\n return new Map()\n }\n\n const parsed = JSON.parse(rawData)\n const dataMap = new Map<string | number, StoredItem<T>>()\n\n // Handle object format where keys map to StoredItem values\n if (\n typeof parsed === `object` &&\n parsed !== null &&\n !Array.isArray(parsed)\n ) {\n Object.entries(parsed).forEach(([key, value]) => {\n // Runtime check to ensure the value has the expected StoredItem structure\n if (\n value &&\n typeof value === `object` &&\n `versionKey` in value &&\n `data` in value\n ) {\n const storedItem = value as StoredItem<T>\n dataMap.set(key, storedItem)\n } else {\n throw new Error(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\" for key \"${key}\".`\n )\n }\n })\n } else {\n throw new Error(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\". Expected object format.`\n )\n }\n\n return dataMap\n } catch (error) {\n console.warn(\n `[LocalStorageCollection] Error loading data from storage key \"${storageKey}\":`,\n error\n )\n return new Map()\n }\n}\n\n/**\n * Internal function to create localStorage sync configuration\n * Creates a sync configuration that handles localStorage persistence and cross-tab synchronization\n * @param storageKey - The key used for storing data in localStorage\n * @param storage - The storage API to use (localStorage, sessionStorage, etc.)\n * @param storageEventApi - The event API for listening to storage changes\n * @param getKey - Function to extract the key from an item\n * @param lastKnownData - Map tracking the last known state for change detection\n * @returns Sync configuration with manual trigger capability\n */\nfunction createLocalStorageSync<T extends object>(\n storageKey: string,\n storage: StorageApi,\n storageEventApi: StorageEventApi,\n getKey: (item: T) => string | number,\n lastKnownData: Map<string | number, StoredItem<T>>\n): SyncConfig<T> & { manualTrigger?: () => void } {\n let syncParams: Parameters<SyncConfig<T>[`sync`]>[0] | null = null\n\n /**\n * Compare two Maps to find differences using version keys\n * @param oldData - The previous state of stored items\n * @param newData - The current state of stored items\n * @returns Array of changes with type, key, and value information\n */\n const findChanges = (\n oldData: Map<string | number, StoredItem<T>>,\n newData: Map<string | number, StoredItem<T>>\n ): Array<{\n type: `insert` | `update` | `delete`\n key: string | number\n value?: T\n }> => {\n const changes: Array<{\n type: `insert` | `update` | `delete`\n key: string | number\n value?: T\n }> = []\n\n // Check for deletions and updates\n oldData.forEach((oldStoredItem, key) => {\n const newStoredItem = newData.get(key)\n if (!newStoredItem) {\n changes.push({ type: `delete`, key, value: oldStoredItem.data })\n } else if (oldStoredItem.versionKey !== newStoredItem.versionKey) {\n changes.push({ type: `update`, key, value: newStoredItem.data })\n }\n })\n\n // Check for insertions\n newData.forEach((newStoredItem, key) => {\n if (!oldData.has(key)) {\n changes.push({ type: `insert`, key, value: newStoredItem.data })\n }\n })\n\n return changes\n }\n\n /**\n * Process storage changes and update collection\n * Loads new data from storage, compares with last known state, and applies changes\n */\n const processStorageChanges = () => {\n if (!syncParams) return\n\n const { begin, write, commit } = syncParams\n\n // Load the new data\n const newData = loadFromStorage<T>(storageKey, storage)\n\n // Find the specific changes\n const changes = findChanges(lastKnownData, newData)\n\n if (changes.length > 0) {\n begin()\n changes.forEach(({ type, value }) => {\n if (value) {\n validateJsonSerializable(value, type)\n write({ type, value })\n }\n })\n commit()\n\n // Update lastKnownData\n lastKnownData.clear()\n newData.forEach((storedItem, key) => {\n lastKnownData.set(key, storedItem)\n })\n }\n }\n\n const syncConfig: SyncConfig<T> & { manualTrigger?: () => void } = {\n sync: (params: Parameters<SyncConfig<T>[`sync`]>[0]) => {\n const { begin, write, commit } = params\n\n // Store sync params for later use\n syncParams = params\n\n // Initial load\n const initialData = loadFromStorage<T>(storageKey, storage)\n if (initialData.size > 0) {\n begin()\n initialData.forEach((storedItem) => {\n validateJsonSerializable(storedItem.data, `load`)\n write({ type: `insert`, value: storedItem.data })\n })\n commit()\n }\n\n // Update lastKnownData\n lastKnownData.clear()\n initialData.forEach((storedItem, key) => {\n lastKnownData.set(key, storedItem)\n })\n\n // Listen for storage events from other tabs\n const handleStorageEvent = (event: StorageEvent) => {\n // Only respond to changes to our specific key and from our storage\n if (event.key !== storageKey || event.storageArea !== storage) {\n return\n }\n\n processStorageChanges()\n }\n\n // Add storage event listener for cross-tab sync\n storageEventApi.addEventListener(`storage`, handleStorageEvent)\n\n // Note: Cleanup is handled automatically by the collection when it's disposed\n },\n\n /**\n * Get sync metadata - returns storage key information\n * @returns Object containing storage key and storage type metadata\n */\n getSyncMetadata: () => ({\n storageKey,\n storageType:\n storage === (typeof window !== `undefined` ? window.localStorage : null)\n ? `localStorage`\n : `custom`,\n }),\n\n // Manual trigger function for local updates\n manualTrigger: processStorageChanges,\n }\n\n return syncConfig\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-storage.js","sources":["../../src/local-storage.ts"],"sourcesContent":["import type {\n CollectionConfig,\n DeleteMutationFnParams,\n InsertMutationFnParams,\n ResolveType,\n SyncConfig,\n UpdateMutationFnParams,\n UtilsRecord,\n} from \"./types\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\n\n/**\n * Storage API interface - subset of DOM Storage that we need\n */\nexport type StorageApi = Pick<Storage, `getItem` | `setItem` | `removeItem`>\n\n/**\n * Storage event API - subset of Window for 'storage' events only\n */\nexport type StorageEventApi = {\n addEventListener: (\n type: `storage`,\n listener: (event: StorageEvent) => void\n ) => void\n removeEventListener: (\n type: `storage`,\n listener: (event: StorageEvent) => void\n ) => void\n}\n\n/**\n * Internal storage format that includes version tracking\n */\ninterface StoredItem<T> {\n versionKey: string\n data: T\n}\n\n/**\n * Configuration interface for localStorage collection options\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n *\n * @remarks\n * Type resolution follows a priority order:\n * 1. If you provide an explicit type via generic parameter, it will be used\n * 2. If no explicit type is provided but a schema is, the schema's output type will be inferred\n * 3. If neither explicit type nor schema is provided, the fallback type will be used\n *\n * You should provide EITHER an explicit type OR a schema, but not both, as they would conflict.\n */\nexport interface LocalStorageCollectionConfig<\n TExplicit = unknown,\n TSchema extends StandardSchemaV1 = never,\n TFallback extends object = Record<string, unknown>,\n> {\n /**\n * The key to use for storing the collection data in localStorage/sessionStorage\n */\n storageKey: string\n\n /**\n * Storage API to use (defaults to window.localStorage)\n * Can be any object that implements the Storage interface (e.g., sessionStorage)\n */\n storage?: StorageApi\n\n /**\n * Storage event API to use for cross-tab synchronization (defaults to window)\n * Can be any object that implements addEventListener/removeEventListener for storage events\n */\n storageEventApi?: StorageEventApi\n\n /**\n * Collection identifier (defaults to \"local-collection:{storageKey}\" if not provided)\n */\n id?: string\n schema?: TSchema\n getKey: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>>[`getKey`]\n sync?: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>>[`sync`]\n\n /**\n * Optional asynchronous handler function called before an insert operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onInsert?: (\n params: InsertMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n\n /**\n * Optional asynchronous handler function called before an update operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onUpdate?: (\n params: UpdateMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n\n /**\n * Optional asynchronous handler function called before a delete operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onDelete?: (\n params: DeleteMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n}\n\n/**\n * Type for the clear utility function\n */\nexport type ClearStorageFn = () => void\n\n/**\n * Type for the getStorageSize utility function\n */\nexport type GetStorageSizeFn = () => number\n\n/**\n * LocalStorage collection utilities type\n */\nexport interface LocalStorageCollectionUtils extends UtilsRecord {\n clearStorage: ClearStorageFn\n getStorageSize: GetStorageSizeFn\n}\n\n/**\n * Validates that a value can be JSON serialized\n * @param value - The value to validate for JSON serialization\n * @param operation - The operation type being performed (for error messages)\n * @throws Error if the value cannot be JSON serialized\n */\nfunction validateJsonSerializable(value: any, operation: string): void {\n try {\n JSON.stringify(value)\n } catch (error) {\n throw new Error(\n `Cannot ${operation} item because it cannot be JSON serialized: ${\n error instanceof Error ? error.message : String(error)\n }`\n )\n }\n}\n\n/**\n * Generate a UUID for version tracking\n * @returns A unique identifier string for tracking data versions\n */\nfunction generateUuid(): string {\n return crypto.randomUUID()\n}\n\n/**\n * Creates localStorage collection options for use with a standard Collection\n *\n * This function creates a collection that persists data to localStorage/sessionStorage\n * and synchronizes changes across browser tabs using storage events.\n *\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n * @param config - Configuration options for the localStorage collection\n * @returns Collection options with utilities including clearStorage and getStorageSize\n *\n * @example\n * // Basic localStorage collection\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // localStorage collection with custom storage\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * storage: window.sessionStorage, // Use sessionStorage instead\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // localStorage collection with mutation handlers\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * getKey: (item) => item.id,\n * onInsert: async ({ transaction }) => {\n * console.log('Item inserted:', transaction.mutations[0].modified)\n * },\n * })\n * )\n */\nexport function localStorageCollectionOptions<\n TExplicit = unknown,\n TSchema extends StandardSchemaV1 = never,\n TFallback extends object = Record<string, unknown>,\n>(config: LocalStorageCollectionConfig<TExplicit, TSchema, TFallback>) {\n type ResolvedType = ResolveType<TExplicit, TSchema, TFallback>\n\n // Validate required parameters\n if (!config.storageKey) {\n throw new Error(`[LocalStorageCollection] storageKey must be provided.`)\n }\n\n // Default to window.localStorage if no storage is provided\n const storage =\n config.storage ||\n (typeof window !== `undefined` ? window.localStorage : null)\n\n if (!storage) {\n throw new Error(\n `[LocalStorageCollection] No storage available. Please provide a storage option or ensure window.localStorage is available.`\n )\n }\n\n // Default to window for storage events if not provided\n const storageEventApi =\n config.storageEventApi || (typeof window !== `undefined` ? window : null)\n\n if (!storageEventApi) {\n throw new Error(\n `[LocalStorageCollection] No storage event API available. Please provide a storageEventApi option or ensure window is available.`\n )\n }\n\n // Track the last known state to detect changes\n const lastKnownData = new Map<string | number, StoredItem<ResolvedType>>()\n\n // Create the sync configuration\n const sync = createLocalStorageSync<ResolvedType>(\n config.storageKey,\n storage,\n storageEventApi,\n config.getKey,\n lastKnownData\n )\n\n /**\n * Manual trigger function for local sync updates\n * Forces a check for storage changes and updates the collection if needed\n */\n const triggerLocalSync = () => {\n if (sync.manualTrigger) {\n sync.manualTrigger()\n }\n }\n\n /**\n * Save data to storage\n * @param dataMap - Map of items with version tracking to save to storage\n */\n const saveToStorage = (\n dataMap: Map<string | number, StoredItem<ResolvedType>>\n ): void => {\n try {\n // Convert Map to object format for storage\n const objectData: Record<string, StoredItem<ResolvedType>> = {}\n dataMap.forEach((storedItem, key) => {\n objectData[String(key)] = storedItem\n })\n const serialized = JSON.stringify(objectData)\n storage.setItem(config.storageKey, serialized)\n } catch (error) {\n console.error(\n `[LocalStorageCollection] Error saving data to storage key \"${config.storageKey}\":`,\n error\n )\n throw error\n }\n }\n\n /**\n * Removes all collection data from the configured storage\n */\n const clearStorage: ClearStorageFn = (): void => {\n storage.removeItem(config.storageKey)\n }\n\n /**\n * Get the size of the stored data in bytes (approximate)\n * @returns The approximate size in bytes of the stored collection data\n */\n const getStorageSize: GetStorageSizeFn = (): number => {\n const data = storage.getItem(config.storageKey)\n return data ? new Blob([data]).size : 0\n }\n\n /*\n * Create wrapper handlers for direct persistence operations that perform actual storage operations\n * Wraps the user's onInsert handler to also save changes to localStorage\n */\n const wrappedOnInsert = async (\n params: InsertMutationFnParams<ResolvedType>\n ) => {\n // Validate that all values in the transaction can be JSON serialized\n params.transaction.mutations.forEach((mutation) => {\n validateJsonSerializable(mutation.modified, `insert`)\n })\n\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onInsert) {\n handlerResult = (await config.onInsert(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Add new items with version keys\n params.transaction.mutations.forEach((mutation) => {\n const key = config.getKey(mutation.modified)\n const storedItem: StoredItem<ResolvedType> = {\n versionKey: generateUuid(),\n data: mutation.modified,\n }\n currentData.set(key, storedItem)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n const wrappedOnUpdate = async (\n params: UpdateMutationFnParams<ResolvedType>\n ) => {\n // Validate that all values in the transaction can be JSON serialized\n params.transaction.mutations.forEach((mutation) => {\n validateJsonSerializable(mutation.modified, `update`)\n })\n\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onUpdate) {\n handlerResult = (await config.onUpdate(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Update items with new version keys\n params.transaction.mutations.forEach((mutation) => {\n const key = config.getKey(mutation.modified)\n const storedItem: StoredItem<ResolvedType> = {\n versionKey: generateUuid(),\n data: mutation.modified,\n }\n currentData.set(key, storedItem)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n const wrappedOnDelete = async (\n params: DeleteMutationFnParams<ResolvedType>\n ) => {\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onDelete) {\n handlerResult = (await config.onDelete(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Remove items\n params.transaction.mutations.forEach((mutation) => {\n // For delete operations, mutation.original contains the full object\n const key = config.getKey(mutation.original as ResolvedType)\n currentData.delete(key)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n // Extract standard Collection config properties\n const {\n storageKey: _storageKey,\n storage: _storage,\n storageEventApi: _storageEventApi,\n onInsert: _onInsert,\n onUpdate: _onUpdate,\n onDelete: _onDelete,\n id,\n ...restConfig\n } = config\n\n // Default id to a pattern based on storage key if not provided\n const collectionId = id ?? `local-collection:${config.storageKey}`\n\n return {\n ...restConfig,\n id: collectionId,\n sync,\n onInsert: wrappedOnInsert,\n onUpdate: wrappedOnUpdate,\n onDelete: wrappedOnDelete,\n utils: {\n clearStorage,\n getStorageSize,\n },\n }\n}\n\n/**\n * Load data from storage and return as a Map\n * @param storageKey - The key used to store data in the storage API\n * @param storage - The storage API to load from (localStorage, sessionStorage, etc.)\n * @returns Map of stored items with version tracking, or empty Map if loading fails\n */\nfunction loadFromStorage<T extends object>(\n storageKey: string,\n storage: StorageApi\n): Map<string | number, StoredItem<T>> {\n try {\n const rawData = storage.getItem(storageKey)\n if (!rawData) {\n return new Map()\n }\n\n const parsed = JSON.parse(rawData)\n const dataMap = new Map<string | number, StoredItem<T>>()\n\n // Handle object format where keys map to StoredItem values\n if (\n typeof parsed === `object` &&\n parsed !== null &&\n !Array.isArray(parsed)\n ) {\n Object.entries(parsed).forEach(([key, value]) => {\n // Runtime check to ensure the value has the expected StoredItem structure\n if (\n value &&\n typeof value === `object` &&\n `versionKey` in value &&\n `data` in value\n ) {\n const storedItem = value as StoredItem<T>\n dataMap.set(key, storedItem)\n } else {\n throw new Error(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\" for key \"${key}\".`\n )\n }\n })\n } else {\n throw new Error(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\". Expected object format.`\n )\n }\n\n return dataMap\n } catch (error) {\n console.warn(\n `[LocalStorageCollection] Error loading data from storage key \"${storageKey}\":`,\n error\n )\n return new Map()\n }\n}\n\n/**\n * Internal function to create localStorage sync configuration\n * Creates a sync configuration that handles localStorage persistence and cross-tab synchronization\n * @param storageKey - The key used for storing data in localStorage\n * @param storage - The storage API to use (localStorage, sessionStorage, etc.)\n * @param storageEventApi - The event API for listening to storage changes\n * @param getKey - Function to extract the key from an item\n * @param lastKnownData - Map tracking the last known state for change detection\n * @returns Sync configuration with manual trigger capability\n */\nfunction createLocalStorageSync<T extends object>(\n storageKey: string,\n storage: StorageApi,\n storageEventApi: StorageEventApi,\n _getKey: (item: T) => string | number,\n lastKnownData: Map<string | number, StoredItem<T>>\n): SyncConfig<T> & { manualTrigger?: () => void } {\n let syncParams: Parameters<SyncConfig<T>[`sync`]>[0] | null = null\n\n /**\n * Compare two Maps to find differences using version keys\n * @param oldData - The previous state of stored items\n * @param newData - The current state of stored items\n * @returns Array of changes with type, key, and value information\n */\n const findChanges = (\n oldData: Map<string | number, StoredItem<T>>,\n newData: Map<string | number, StoredItem<T>>\n ): Array<{\n type: `insert` | `update` | `delete`\n key: string | number\n value?: T\n }> => {\n const changes: Array<{\n type: `insert` | `update` | `delete`\n key: string | number\n value?: T\n }> = []\n\n // Check for deletions and updates\n oldData.forEach((oldStoredItem, key) => {\n const newStoredItem = newData.get(key)\n if (!newStoredItem) {\n changes.push({ type: `delete`, key, value: oldStoredItem.data })\n } else if (oldStoredItem.versionKey !== newStoredItem.versionKey) {\n changes.push({ type: `update`, key, value: newStoredItem.data })\n }\n })\n\n // Check for insertions\n newData.forEach((newStoredItem, key) => {\n if (!oldData.has(key)) {\n changes.push({ type: `insert`, key, value: newStoredItem.data })\n }\n })\n\n return changes\n }\n\n /**\n * Process storage changes and update collection\n * Loads new data from storage, compares with last known state, and applies changes\n */\n const processStorageChanges = () => {\n if (!syncParams) return\n\n const { begin, write, commit } = syncParams\n\n // Load the new data\n const newData = loadFromStorage<T>(storageKey, storage)\n\n // Find the specific changes\n const changes = findChanges(lastKnownData, newData)\n\n if (changes.length > 0) {\n begin()\n changes.forEach(({ type, value }) => {\n if (value) {\n validateJsonSerializable(value, type)\n write({ type, value })\n }\n })\n commit()\n\n // Update lastKnownData\n lastKnownData.clear()\n newData.forEach((storedItem, key) => {\n lastKnownData.set(key, storedItem)\n })\n }\n }\n\n const syncConfig: SyncConfig<T> & { manualTrigger?: () => void } = {\n sync: (params: Parameters<SyncConfig<T>[`sync`]>[0]) => {\n const { begin, write, commit } = params\n\n // Store sync params for later use\n syncParams = params\n\n // Initial load\n const initialData = loadFromStorage<T>(storageKey, storage)\n if (initialData.size > 0) {\n begin()\n initialData.forEach((storedItem) => {\n validateJsonSerializable(storedItem.data, `load`)\n write({ type: `insert`, value: storedItem.data })\n })\n commit()\n }\n\n // Update lastKnownData\n lastKnownData.clear()\n initialData.forEach((storedItem, key) => {\n lastKnownData.set(key, storedItem)\n })\n\n // Listen for storage events from other tabs\n const handleStorageEvent = (event: StorageEvent) => {\n // Only respond to changes to our specific key and from our storage\n if (event.key !== storageKey || event.storageArea !== storage) {\n return\n }\n\n processStorageChanges()\n }\n\n // Add storage event listener for cross-tab sync\n storageEventApi.addEventListener(`storage`, handleStorageEvent)\n\n // Note: Cleanup is handled automatically by the collection when it's disposed\n },\n\n /**\n * Get sync metadata - returns storage key information\n * @returns Object containing storage key and storage type metadata\n */\n getSyncMetadata: () => ({\n storageKey,\n storageType:\n storage === (typeof window !== `undefined` ? window.localStorage : null)\n ? `localStorage`\n : `custom`,\n }),\n\n // Manual trigger function for local updates\n manualTrigger: processStorageChanges,\n }\n\n return syncConfig\n}\n"],"names":[],"mappings":"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"}
@@ -244,6 +244,21 @@ export declare class BaseQueryBuilder<TContext extends Context = Context> {
244
244
  * ```
245
245
  */
246
246
  offset(count: number): QueryBuilder<TContext>;
247
+ /**
248
+ * Specify that the query should return distinct rows.
249
+ * Deduplicates rows based on the selected columns.
250
+ * @returns A QueryBuilder with distinct enabled
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * // Get countries our users are from
255
+ * query
256
+ * .from({ users: usersCollection })
257
+ * .select(({users}) => users.country)
258
+ * .distinct()
259
+ * ```
260
+ */
261
+ distinct(): QueryBuilder<TContext>;
247
262
  private _getCurrentAliases;
248
263
  /**
249
264
  * Functional variants of the query builder