@tanstack/db 0.0.12 → 0.0.14

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 (49) hide show
  1. package/dist/cjs/SortedMap.cjs +38 -11
  2. package/dist/cjs/SortedMap.cjs.map +1 -1
  3. package/dist/cjs/SortedMap.d.cts +10 -0
  4. package/dist/cjs/collection.cjs +467 -95
  5. package/dist/cjs/collection.cjs.map +1 -1
  6. package/dist/cjs/collection.d.cts +81 -5
  7. package/dist/cjs/index.cjs +2 -0
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/index.d.cts +1 -0
  10. package/dist/cjs/optimistic-action.cjs +21 -0
  11. package/dist/cjs/optimistic-action.cjs.map +1 -0
  12. package/dist/cjs/optimistic-action.d.cts +39 -0
  13. package/dist/cjs/query/compiled-query.cjs +21 -11
  14. package/dist/cjs/query/compiled-query.cjs.map +1 -1
  15. package/dist/cjs/query/query-builder.cjs +2 -2
  16. package/dist/cjs/query/query-builder.cjs.map +1 -1
  17. package/dist/cjs/transactions.cjs +3 -1
  18. package/dist/cjs/transactions.cjs.map +1 -1
  19. package/dist/cjs/transactions.d.cts +4 -4
  20. package/dist/cjs/types.d.cts +45 -1
  21. package/dist/esm/SortedMap.d.ts +10 -0
  22. package/dist/esm/SortedMap.js +38 -11
  23. package/dist/esm/SortedMap.js.map +1 -1
  24. package/dist/esm/collection.d.ts +81 -5
  25. package/dist/esm/collection.js +467 -95
  26. package/dist/esm/collection.js.map +1 -1
  27. package/dist/esm/index.d.ts +1 -0
  28. package/dist/esm/index.js +2 -0
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/optimistic-action.d.ts +39 -0
  31. package/dist/esm/optimistic-action.js +21 -0
  32. package/dist/esm/optimistic-action.js.map +1 -0
  33. package/dist/esm/query/compiled-query.js +21 -11
  34. package/dist/esm/query/compiled-query.js.map +1 -1
  35. package/dist/esm/query/query-builder.js +2 -2
  36. package/dist/esm/query/query-builder.js.map +1 -1
  37. package/dist/esm/transactions.d.ts +4 -4
  38. package/dist/esm/transactions.js +3 -1
  39. package/dist/esm/transactions.js.map +1 -1
  40. package/dist/esm/types.d.ts +45 -1
  41. package/package.json +1 -1
  42. package/src/SortedMap.ts +46 -13
  43. package/src/collection.ts +624 -119
  44. package/src/index.ts +1 -0
  45. package/src/optimistic-action.ts +65 -0
  46. package/src/query/compiled-query.ts +36 -14
  47. package/src/query/query-builder.ts +2 -2
  48. package/src/transactions.ts +14 -5
  49. package/src/types.ts +48 -1
@@ -1 +1 @@
1
- {"version":3,"file":"collection.js","sources":["../../src/collection.ts"],"sourcesContent":["import { Store } from \"@tanstack/store\"\nimport { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { Transaction, getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\nimport type {\n ChangeListener,\n ChangeMessage,\n CollectionConfig,\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 * // Using explicit type\n * const todos = createCollection<Todo>({\n * getKey: (todo) => todo.id,\n * sync: { sync: () => {} }\n * })\n *\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> {\n public transactions: SortedMap<string, Transaction<any>>\n\n // Core state - make public for testing\n public syncedData = new Map<TKey, T>()\n public syncedMetadata = new Map<TKey, unknown>()\n\n // Optimistic state tracking - make public for testing\n public derivedUpserts = new Map<TKey, T>()\n public derivedDeletes = 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 private pendingSyncedTransactions: Array<PendingSyncedTransaction<T>> = []\n private syncedKeys = new Set<TKey>()\n public config: CollectionConfig<T, TKey, any>\n private hasReceivedFirstCommit = false\n\n // Array to store one-time commit listeners\n private onFirstCommitCallbacks: Array<() => void> = []\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 */\n public onFirstCommit(callback: () => void): void {\n this.onFirstCommitCallbacks.push(callback)\n }\n\n public id = ``\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>>(\n (a, b) => a.createdAt.getTime() - b.createdAt.getTime()\n )\n\n this.config = config\n\n // Start the sync process\n 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 this.commitPendingTransactions()\n },\n })\n }\n\n /**\n * Recompute optimistic state from active transactions\n */\n private recomputeOptimisticState(): void {\n const previousState = new Map(this.derivedUpserts)\n const previousDeletes = new Set(this.derivedDeletes)\n\n // Clear current optimistic state\n this.derivedUpserts.clear()\n this.derivedDeletes.clear()\n\n // Apply active transactions\n const activeTransactions = Array.from(this.transactions.values())\n for (const transaction of activeTransactions) {\n if (![`completed`, `failed`].includes(transaction.state)) {\n for (const mutation of transaction.mutations) {\n if (mutation.collection === this) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.derivedUpserts.set(mutation.key, mutation.modified as T)\n this.derivedDeletes.delete(mutation.key)\n break\n case `delete`:\n this.derivedUpserts.delete(mutation.key)\n this.derivedDeletes.add(mutation.key)\n break\n }\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 // Emit all events at once\n this.emitEvents(events)\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.derivedDeletes).filter(\n (key) => this.syncedData.has(key) && !this.derivedUpserts.has(key)\n ).length\n const upsertsNotInSynced = Array.from(this.derivedUpserts.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.derivedUpserts.keys(),\n ...previousDeletes,\n ...this.derivedDeletes,\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 multiple events at once to all listeners\n */\n private emitEvents(changes: Array<ChangeMessage<T, TKey>>): void {\n if (changes.length > 0) {\n // Emit to general listeners\n for (const listener of this.changeListeners) {\n listener(changes)\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 changes) {\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 /**\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.derivedDeletes.has(key)) {\n return undefined\n }\n\n // Check optimistic upserts first\n if (this.derivedUpserts.has(key)) {\n return this.derivedUpserts.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.derivedDeletes.has(key)) {\n return false\n }\n\n // Check optimistic upserts first\n if (this.derivedUpserts.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.derivedDeletes.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.derivedUpserts.keys()) {\n if (!this.syncedData.has(key) && !this.derivedDeletes.has(key)) {\n // The derivedDeletes 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 * 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 if (\n !Array.from(this.transactions.values()).some(\n ({ state }) => state === `persisting`\n )\n ) {\n const changedKeys = new Set<TKey>()\n const events: Array<ChangeMessage<T, TKey>> = []\n\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n const key = operation.key as TKey\n changedKeys.add(key)\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 and collect events\n const previousValue = this.syncedData.get(key)\n\n switch (operation.type) {\n case `insert`:\n this.syncedData.set(key, operation.value)\n if (\n !this.derivedDeletes.has(key) &&\n !this.derivedUpserts.has(key)\n ) {\n events.push({\n type: `insert`,\n key,\n value: operation.value,\n })\n }\n break\n case `update`: {\n const updatedValue = Object.assign(\n {},\n this.syncedData.get(key),\n operation.value\n )\n this.syncedData.set(key, updatedValue)\n if (\n !this.derivedDeletes.has(key) &&\n !this.derivedUpserts.has(key)\n ) {\n events.push({\n type: `update`,\n key,\n value: updatedValue,\n previousValue,\n })\n }\n break\n }\n case `delete`:\n this.syncedData.delete(key)\n if (\n !this.derivedDeletes.has(key) &&\n !this.derivedUpserts.has(key)\n ) {\n if (previousValue) {\n events.push({\n type: `delete`,\n key,\n value: previousValue,\n })\n }\n }\n break\n }\n }\n }\n\n // Update cached size after synced data changes\n this._size = this.calculateSize()\n\n // Emit all events at once\n this.emitEvents(events)\n\n this.pendingSyncedTransactions = []\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 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 and custom keys\n * @returns A TransactionType object representing the insert operation(s)\n * @throws {SchemaValidationError} If the data fails schema validation\n * @example\n * // Insert a single item\n * insert({ text: \"Buy groceries\", completed: false })\n *\n * // Insert multiple items\n * insert([\n * { text: \"Buy groceries\", completed: false },\n * { text: \"Walk dog\", completed: false }\n * ])\n *\n * // Insert with custom key\n * insert({ text: \"Buy groceries\" }, { key: \"grocery-task\" })\n */\n insert = (data: T | Array<T>, config?: InsertConfig) => {\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 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 = new Transaction<T>({\n mutationFn: async (params) => {\n // Call the onInsert handler with the transaction\n return this.config.onInsert!(params)\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 items - Single item/key or array of items/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 a single item\n * update(todo, (draft) => { draft.completed = true })\n *\n * // Update multiple items\n * update([todo1, todo2], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n *\n * // Update with metadata\n * update(todo, { metadata: { reason: \"user update\" } }, (draft) => { draft.text = \"Updated text\" })\n */\n\n /**\n * Updates one or more items in the collection using a callback function\n * @param ids - Single ID or array of IDs 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 a single item\n * update(\"todo-1\", (draft) => { draft.completed = true })\n *\n * // Update multiple items\n * update([\"todo-1\", \"todo-2\"], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n *\n * // Update with metadata\n * update(\"todo-1\", { metadata: { reason: \"user update\" } }, (draft) => { draft.text = \"Updated text\" })\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 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 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 = new Transaction({\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 = new Transaction<T>({\n mutationFn: async (params) => {\n // Call the onUpdate handler with the transaction\n return this.config.onUpdate!(params)\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 ids - Single ID or array of IDs to delete\n * @param config - Optional configuration including metadata\n * @returns A TransactionType object representing the delete operation(s)\n * @example\n * // Delete a single item\n * delete(\"todo-1\")\n *\n * // Delete multiple items\n * delete([\"todo-1\", \"todo-2\"])\n *\n * // Delete with metadata\n * delete(\"todo-1\", { metadata: { reason: \"completed\" } })\n */\n delete = (\n keys: Array<TKey> | TKey,\n config?: OperationConfig\n ): TransactionType<any> => {\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 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 = new Transaction<T>({\n autoCommit: true,\n mutationFn: async (params) => {\n // Call the onDelete handler with the transaction\n return this.config.onDelete!(params)\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 *\n * @returns A Map containing all items in the collection, with keys as identifiers\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 const array = Array.from(this.values())\n\n // Currently a query with an orderBy will add a _orderByIndex to the items\n // so for now we need to sort the array by _orderByIndex if it exists\n // TODO: in the future it would be much better is the keys are sorted - this\n // should be done by the query engine.\n if (array[0] && (array[0] as { _orderByIndex?: number })._orderByIndex) {\n return (array as Array<{ _orderByIndex: number }>).sort(\n (a, b) => a._orderByIndex - b._orderByIndex\n ) as Array<T>\n }\n\n return array\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 - A function that will be called with the changes in the collection\n * @returns A function that can be called to unsubscribe from the changes\n */\n public subscribeChanges(\n callback: (changes: Array<ChangeMessage<T>>) => void,\n { includeInitialState = false }: { includeInitialState?: boolean } = {}\n ): () => void {\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 }\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 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 }\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 this.recomputeOptimisticState()\n }\n\n private _storeMap: Store<Map<TKey, T>> | undefined\n\n /**\n * Returns a Tanstack Store Map that is updated when the collection changes\n * This is a temporary solution to enable the existing framework hooks to work\n * with the new internals of Collection until they are rewritten.\n * TODO: Remove this once the framework hooks are rewritten.\n */\n public asStoreMap(): Store<Map<TKey, T>> {\n if (!this._storeMap) {\n this._storeMap = new Store(new Map(this.entries()))\n this.subscribeChanges(() => {\n this._storeMap!.setState(() => new Map(this.entries()))\n })\n }\n return this._storeMap\n }\n\n private _storeArray: Store<Array<T>> | undefined\n\n /**\n * Returns a Tanstack Store Array that is updated when the collection changes\n * This is a temporary solution to enable the existing framework hooks to work\n * with the new internals of Collection until they are rewritten.\n * TODO: Remove this once the framework hooks are rewritten.\n */\n public asStoreArray(): Store<Array<T>> {\n if (!this._storeArray) {\n this._storeArray = new Store(this.toArray)\n this.subscribeChanges(() => {\n this._storeArray!.setState(() => this.toArray)\n })\n }\n return this._storeArray\n }\n}\n"],"names":["config","result"],"mappings":"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"}
1
+ {"version":3,"file":"collection.js","sources":["../../src/collection.ts"],"sourcesContent":["import { Store } from \"@tanstack/store\"\nimport { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { Transaction, getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\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 * // Using explicit type\n * const todos = createCollection<Todo>({\n * getKey: (todo) => todo.id,\n * sync: { sync: () => {} }\n * })\n *\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> {\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 derivedUpserts = new Map<TKey, T>()\n public derivedDeletes = 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 // 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 */\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: [`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>>(\n (a, b) => a.createdAt.getTime() - b.createdAt.getTime()\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 this.commitPendingTransactions()\n\n // Update status to ready after first commit\n if (this._status === `loading`) {\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.derivedUpserts.clear()\n this.derivedDeletes.clear()\n this._size = 0\n this.pendingSyncedTransactions = []\n this.syncedKeys.clear()\n this.hasReceivedFirstCommit = false\n this.onFirstCommitCallbacks = []\n this.preloadPromise = null\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.derivedUpserts)\n const previousDeletes = new Set(this.derivedDeletes)\n\n // Clear current optimistic state\n this.derivedUpserts.clear()\n this.derivedDeletes.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) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.derivedUpserts.set(mutation.key, mutation.modified as T)\n this.derivedDeletes.delete(mutation.key)\n break\n case `delete`:\n this.derivedUpserts.delete(mutation.key)\n this.derivedDeletes.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.derivedDeletes).filter(\n (key) => this.syncedData.has(key) && !this.derivedUpserts.has(key)\n ).length\n const upsertsNotInSynced = Array.from(this.derivedUpserts.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.derivedUpserts.keys(),\n ...previousDeletes,\n ...this.derivedDeletes,\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 multiple events at once to all listeners\n */\n private emitEvents(changes: Array<ChangeMessage<T, TKey>>): void {\n if (changes.length > 0) {\n // Emit to general listeners\n for (const listener of this.changeListeners) {\n listener(changes)\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 changes) {\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 /**\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.derivedDeletes.has(key)) {\n return undefined\n }\n\n // Check optimistic upserts first\n if (this.derivedUpserts.has(key)) {\n return this.derivedUpserts.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.derivedDeletes.has(key)) {\n return false\n }\n\n // Check optimistic upserts first\n if (this.derivedUpserts.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.derivedDeletes.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.derivedUpserts.keys()) {\n if (!this.syncedData.has(key) && !this.derivedDeletes.has(key)) {\n // The derivedDeletes 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 const { _orderByIndex, ...copy } = value as T & {\n _orderByIndex?: number | string\n }\n yield copy as T\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 const { _orderByIndex, ...copy } = value as T & {\n _orderByIndex?: number | string\n }\n yield [key, copy as T]\n }\n }\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\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 const updatedValue = Object.assign(\n {},\n this.syncedData.get(key),\n operation.value\n )\n this.syncedData.set(key, updatedValue)\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.derivedUpserts.clear()\n this.derivedDeletes.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) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.derivedUpserts.set(mutation.key, mutation.modified as T)\n this.derivedDeletes.delete(mutation.key)\n break\n case `delete`:\n this.derivedUpserts.delete(mutation.key)\n this.derivedDeletes.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 // Emit all events at once\n this.emitEvents(events)\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 and custom keys\n * @returns A TransactionType object representing the insert operation(s)\n * @throws {SchemaValidationError} If the data fails schema validation\n * @example\n * // Insert a single item\n * insert({ text: \"Buy groceries\", completed: false })\n *\n * // Insert multiple items\n * insert([\n * { text: \"Buy groceries\", completed: false },\n * { text: \"Walk dog\", completed: false }\n * ])\n *\n * // Insert with custom key\n * insert({ text: \"Buy groceries\" }, { key: \"grocery-task\" })\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 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 = new Transaction<T>({\n mutationFn: async (params) => {\n // Call the onInsert handler with the transaction\n return this.config.onInsert!(params)\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 items - Single item/key or array of items/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 a single item\n * update(todo, (draft) => { draft.completed = true })\n *\n * // Update multiple items\n * update([todo1, todo2], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n *\n * // Update with metadata\n * update(todo, { metadata: { reason: \"user update\" } }, (draft) => { draft.text = \"Updated text\" })\n */\n\n /**\n * Updates one or more items in the collection using a callback function\n * @param ids - Single ID or array of IDs 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 a single item\n * update(\"todo-1\", (draft) => { draft.completed = true })\n *\n * // Update multiple items\n * update([\"todo-1\", \"todo-2\"], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n *\n * // Update with metadata\n * update(\"todo-1\", { metadata: { reason: \"user update\" } }, (draft) => { draft.text = \"Updated text\" })\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 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 = new Transaction({\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 = new Transaction<T>({\n mutationFn: async (params) => {\n // Call the onUpdate handler with the transaction\n return this.config.onUpdate!(params)\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 ids - Single ID or array of IDs to delete\n * @param config - Optional configuration including metadata\n * @returns A TransactionType object representing the delete operation(s)\n * @example\n * // Delete a single item\n * delete(\"todo-1\")\n *\n * // Delete multiple items\n * delete([\"todo-1\", \"todo-2\"])\n *\n * // Delete with metadata\n * delete(\"todo-1\", { metadata: { reason: \"completed\" } })\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 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 = new Transaction<T>({\n autoCommit: true,\n mutationFn: async (params) => {\n // Call the onDelete handler with the transaction\n return this.config.onDelete!(params)\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 *\n * @returns A Map containing all items in the collection, with keys as identifiers\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 const array = Array.from(this.values())\n\n // Currently a query with an orderBy will add a _orderByIndex to the items\n // so for now we need to sort the array by _orderByIndex if it exists\n // TODO: in the future it would be much better is the keys are sorted - this\n // should be done by the query engine.\n if (array[0] && (array[0] as { _orderByIndex?: number })._orderByIndex) {\n return (array as Array<{ _orderByIndex: number }>).sort(\n (a, b) => a._orderByIndex - b._orderByIndex\n ) as Array<T>\n }\n\n return array\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 - A function that will be called with the changes in the collection\n * @returns A function that can be called to unsubscribe from the changes\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 // CRITICAL: Capture visible state BEFORE clearing optimistic state\n this.capturePreSyncVisibleState()\n\n this.recomputeOptimisticState()\n }\n\n private _storeMap: Store<Map<TKey, T>> | undefined\n\n /**\n * Returns a Tanstack Store Map that is updated when the collection changes\n * This is a temporary solution to enable the existing framework hooks to work\n * with the new internals of Collection until they are rewritten.\n * TODO: Remove this once the framework hooks are rewritten.\n */\n public asStoreMap(): Store<Map<TKey, T>> {\n if (!this._storeMap) {\n this._storeMap = new Store(new Map(this.entries()))\n this.changeListeners.add(() => {\n this._storeMap!.setState(() => new Map(this.entries()))\n })\n }\n return this._storeMap\n }\n\n private _storeArray: Store<Array<T>> | undefined\n\n /**\n * Returns a Tanstack Store Array that is updated when the collection changes\n * This is a temporary solution to enable the existing framework hooks to work\n * with the new internals of Collection until they are rewritten.\n * TODO: Remove this once the framework hooks are rewritten.\n */\n public asStoreArray(): Store<Array<T>> {\n if (!this._storeArray) {\n this._storeArray = new Store(this.toArray)\n this.changeListeners.add(() => {\n this._storeArray!.setState(() => this.toArray)\n })\n }\n return this._storeArray\n }\n}\n"],"names":["config","result"],"mappings":"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"}
@@ -6,4 +6,5 @@ export * from './errors.js';
6
6
  export * from './utils.js';
7
7
  export * from './proxy.js';
8
8
  export * from './query/index.js';
9
+ export * from './optimistic-action.js';
9
10
  export type { Collection } from './collection.js';
package/dist/esm/index.js CHANGED
@@ -4,6 +4,7 @@ import { Transaction, createTransaction, getActiveTransaction } from "./transact
4
4
  import { NonRetriableError } from "./errors.js";
5
5
  import { advanceGlobalVersion, getGlobalVersion, getLockedObjects } from "./utils.js";
6
6
  import { createArrayChangeProxy, createChangeProxy, withArrayChangeTracking, withChangeTracking } from "./proxy.js";
7
+ import { createOptimisticAction } from "./optimistic-action.js";
7
8
  import { BaseQueryBuilder, queryBuilder } from "./query/query-builder.js";
8
9
  import { CompiledQuery, compileQuery } from "./query/compiled-query.js";
9
10
  import { compileQueryPipeline } from "./query/pipeline-compiler.js";
@@ -22,6 +23,7 @@ export {
22
23
  createArrayChangeProxy,
23
24
  createChangeProxy,
24
25
  createCollection,
26
+ createOptimisticAction,
25
27
  createTransaction,
26
28
  getActiveTransaction,
27
29
  getGlobalVersion,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -0,0 +1,39 @@
1
+ import { CreateOptimisticActionsOptions, Transaction } from './types.js';
2
+ /**
3
+ * Creates an optimistic action function that applies local optimistic updates immediately
4
+ * before executing the actual mutation on the server.
5
+ *
6
+ * This pattern allows for responsive UI updates while the actual mutation is in progress.
7
+ * The optimistic update is applied via the `onMutate` callback, and the server mutation
8
+ * is executed via the `mutationFn`.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const addTodo = createOptimisticAction<string>({
13
+ * onMutate: (text) => {
14
+ * // Instantly applies local optimistic state
15
+ * todoCollection.insert({
16
+ * id: uuid(),
17
+ * text,
18
+ * completed: false
19
+ * })
20
+ * },
21
+ * mutationFn: async (text, params) => {
22
+ * // Persist the todo to your backend
23
+ * const response = await fetch('/api/todos', {
24
+ * method: 'POST',
25
+ * body: JSON.stringify({ text, completed: false }),
26
+ * })
27
+ * return response.json()
28
+ * }
29
+ * })
30
+ *
31
+ * // Usage
32
+ * const transaction = addTodo('New Todo Item')
33
+ * ```
34
+ *
35
+ * @template TVariables - The type of variables that will be passed to the action function
36
+ * @param options - Configuration options for the optimistic action
37
+ * @returns A function that accepts variables of type TVariables and returns a Transaction
38
+ */
39
+ export declare function createOptimisticAction<TVariables = unknown>(options: CreateOptimisticActionsOptions<TVariables>): (variables: TVariables) => Transaction;
@@ -0,0 +1,21 @@
1
+ import { createTransaction } from "./transactions.js";
2
+ function createOptimisticAction(options) {
3
+ const { mutationFn, onMutate, ...config } = options;
4
+ return (variables) => {
5
+ const transaction = createTransaction({
6
+ ...config,
7
+ // Wire the mutationFn to use the provided variables
8
+ mutationFn: async (params) => {
9
+ return await mutationFn(variables, params);
10
+ }
11
+ });
12
+ transaction.mutate(() => {
13
+ onMutate(variables);
14
+ });
15
+ return transaction;
16
+ };
17
+ }
18
+ export {
19
+ createOptimisticAction
20
+ };
21
+ //# sourceMappingURL=optimistic-action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimistic-action.js","sources":["../../src/optimistic-action.ts"],"sourcesContent":["import { createTransaction } from \"./transactions\"\nimport type { CreateOptimisticActionsOptions, Transaction } from \"./types\"\n\n/**\n * Creates an optimistic action function that applies local optimistic updates immediately\n * before executing the actual mutation on the server.\n *\n * This pattern allows for responsive UI updates while the actual mutation is in progress.\n * The optimistic update is applied via the `onMutate` callback, and the server mutation\n * is executed via the `mutationFn`.\n *\n * @example\n * ```ts\n * const addTodo = createOptimisticAction<string>({\n * onMutate: (text) => {\n * // Instantly applies local optimistic state\n * todoCollection.insert({\n * id: uuid(),\n * text,\n * completed: false\n * })\n * },\n * mutationFn: async (text, params) => {\n * // Persist the todo to your backend\n * const response = await fetch('/api/todos', {\n * method: 'POST',\n * body: JSON.stringify({ text, completed: false }),\n * })\n * return response.json()\n * }\n * })\n *\n * // Usage\n * const transaction = addTodo('New Todo Item')\n * ```\n *\n * @template TVariables - The type of variables that will be passed to the action function\n * @param options - Configuration options for the optimistic action\n * @returns A function that accepts variables of type TVariables and returns a Transaction\n */\nexport function createOptimisticAction<TVariables = unknown>(\n options: CreateOptimisticActionsOptions<TVariables>\n) {\n const { mutationFn, onMutate, ...config } = options\n\n return (variables: TVariables): Transaction => {\n // Create transaction with the original config\n const transaction = createTransaction({\n ...config,\n // Wire the mutationFn to use the provided variables\n mutationFn: async (params) => {\n return await mutationFn(variables, params)\n },\n })\n\n // Execute the transaction. The mutationFn is called once mutate()\n // is finished.\n transaction.mutate(() => {\n // Call onMutate with variables to apply optimistic updates\n onMutate(variables)\n })\n\n return transaction\n }\n}\n"],"names":[],"mappings":";AAwCO,SAAS,uBACd,SACA;AACA,QAAM,EAAE,YAAY,UAAU,GAAG,OAAW,IAAA;AAE5C,SAAO,CAAC,cAAuC;AAE7C,UAAM,cAAc,kBAAkB;AAAA,MACpC,GAAG;AAAA;AAAA,MAEH,YAAY,OAAO,WAAW;AACrB,eAAA,MAAM,WAAW,WAAW,MAAM;AAAA,MAAA;AAAA,IAC3C,CACD;AAID,gBAAY,OAAO,MAAM;AAEvB,eAAS,SAAS;AAAA,IAAA,CACnB;AAEM,WAAA;AAAA,EACT;AACF;"}
@@ -80,10 +80,24 @@ class CompiledQuery {
80
80
  };
81
81
  this.graph = graph;
82
82
  this.inputs = inputs;
83
+ const compare = query.orderBy ? (val1, val2) => {
84
+ const x = val1;
85
+ const y = val2;
86
+ if (x._orderByIndex < y._orderByIndex) {
87
+ return -1;
88
+ } else if (x._orderByIndex > y._orderByIndex) {
89
+ return 1;
90
+ } else {
91
+ return 0;
92
+ }
93
+ } : void 0;
83
94
  this.resultCollection = createCollection({
84
95
  getKey: (val) => {
85
96
  return val._key;
86
97
  },
98
+ gcTime: 0,
99
+ startSync: true,
100
+ compare,
87
101
  sync: {
88
102
  sync
89
103
  }
@@ -118,20 +132,16 @@ class CompiledQuery {
118
132
  throw new Error(`Query is stopped`);
119
133
  }
120
134
  Object.entries(this.inputCollections).forEach(([key, collection]) => {
121
- this.sendChangesToInput(
122
- key,
123
- collection.currentStateAsChanges(),
124
- collection.config.getKey
135
+ const unsubscribe = collection.subscribeChanges(
136
+ (changes) => {
137
+ this.sendChangesToInput(key, changes, collection.config.getKey);
138
+ this.runGraph();
139
+ },
140
+ { includeInitialState: true }
125
141
  );
126
- });
127
- this.runGraph();
128
- Object.entries(this.inputCollections).forEach(([key, collection]) => {
129
- const unsubscribe = collection.subscribeChanges((changes) => {
130
- this.sendChangesToInput(key, changes, collection.config.getKey);
131
- this.runGraph();
132
- });
133
142
  this.unsubscribeCallbacks.push(unsubscribe);
134
143
  });
144
+ this.runGraph();
135
145
  this.state = `running`;
136
146
  return () => {
137
147
  this.stop();
@@ -1 +1 @@
1
- {"version":3,"file":"compiled-query.js","sources":["../../../src/query/compiled-query.ts"],"sourcesContent":["import { D2, MultiSet, output } from \"@electric-sql/d2mini\"\nimport { createCollection } from \"../collection.js\"\nimport { compileQueryPipeline } from \"./pipeline-compiler.js\"\nimport type { Collection } from \"../collection.js\"\nimport type { ChangeMessage, ResolveType, SyncConfig } from \"../types.js\"\nimport type {\n IStreamBuilder,\n MultiSetArray,\n RootStreamBuilder,\n} from \"@electric-sql/d2mini\"\nimport type { QueryBuilder, ResultsFromContext } from \"./query-builder.js\"\nimport type { Context, Schema } from \"./types.js\"\n\nexport function compileQuery<TContext extends Context<Schema>>(\n queryBuilder: QueryBuilder<TContext>\n) {\n return new CompiledQuery<\n ResultsFromContext<TContext> & { _key?: string | number }\n >(queryBuilder)\n}\n\nexport class CompiledQuery<TResults extends object = Record<string, unknown>> {\n private graph: D2\n private inputs: Record<string, RootStreamBuilder<any>>\n private inputCollections: Record<string, Collection<any>>\n private resultCollection: Collection<TResults>\n public state: `compiled` | `running` | `stopped` = `compiled`\n private unsubscribeCallbacks: Array<() => void> = []\n\n constructor(queryBuilder: QueryBuilder<Context<Schema>>) {\n const query = queryBuilder._query\n const collections = query.collections\n\n if (!collections) {\n throw new Error(`No collections provided`)\n }\n\n this.inputCollections = collections\n\n const graph = new D2()\n const inputs = Object.fromEntries(\n Object.entries(collections).map(([key]) => [key, graph.newInput<any>()])\n )\n\n // Use TResults directly to ensure type compatibility\n const sync: SyncConfig<TResults>[`sync`] = ({\n begin,\n write,\n commit,\n collection,\n }) => {\n compileQueryPipeline<IStreamBuilder<[unknown, TResults]>>(\n query,\n inputs\n ).pipe(\n output((data) => {\n begin()\n data\n .getInner()\n .reduce((acc, [[key, value], multiplicity]) => {\n const changes = acc.get(key) || {\n deletes: 0,\n inserts: 0,\n value,\n }\n if (multiplicity < 0) {\n changes.deletes += Math.abs(multiplicity)\n } else if (multiplicity > 0) {\n changes.inserts += multiplicity\n changes.value = value\n }\n acc.set(key, changes)\n return acc\n }, new Map<unknown, { deletes: number; inserts: number; value: TResults }>())\n .forEach((changes, rawKey) => {\n const { deletes, inserts, value } = changes\n const valueWithKey = { ...value, _key: rawKey }\n\n // Simple singular insert.\n if (inserts && deletes === 0) {\n write({\n value: valueWithKey,\n type: `insert`,\n })\n } else if (\n // Insert & update(s) (updates are a delete & insert)\n inserts > deletes ||\n // Just update(s) but the item is already in the collection (so\n // was inserted previously).\n (inserts === deletes &&\n collection.has(valueWithKey._key as string | number))\n ) {\n write({\n value: valueWithKey,\n type: `update`,\n })\n // Only delete is left as an option\n } else if (deletes > 0) {\n write({\n value: valueWithKey,\n type: `delete`,\n })\n } else {\n throw new Error(\n `This should never happen ${JSON.stringify(changes)}`\n )\n }\n })\n commit()\n })\n )\n graph.finalize()\n }\n\n this.graph = graph\n this.inputs = inputs\n this.resultCollection = createCollection<TResults>({\n getKey: (val: unknown) => {\n return (val as any)._key\n },\n sync: {\n sync: sync as unknown as (params: {\n collection: Collection<\n ResolveType<TResults, never, Record<string, unknown>>,\n string | number,\n {}\n >\n begin: () => void\n write: (\n message: Omit<\n ChangeMessage<\n ResolveType<TResults, never, Record<string, unknown>>,\n string | number\n >,\n `key`\n >\n ) => void\n commit: () => void\n }) => void,\n },\n }) as unknown as Collection<TResults, string | number, {}>\n }\n\n get results() {\n return this.resultCollection\n }\n\n private sendChangesToInput(\n inputKey: string,\n changes: Array<ChangeMessage>,\n getKey: (item: ChangeMessage[`value`]) => any\n ) {\n const input = this.inputs[inputKey]!\n const multiSetArray: MultiSetArray<unknown> = []\n for (const change of changes) {\n const key = getKey(change.value)\n if (change.type === `insert`) {\n multiSetArray.push([[key, change.value], 1])\n } else if (change.type === `update`) {\n multiSetArray.push([[key, change.previousValue], -1])\n multiSetArray.push([[key, change.value], 1])\n } else {\n // change.type === `delete`\n multiSetArray.push([[key, change.value], -1])\n }\n }\n input.sendData(new MultiSet(multiSetArray))\n }\n\n private runGraph() {\n this.graph.run()\n }\n\n start() {\n if (this.state === `running`) {\n throw new Error(`Query is already running`)\n } else if (this.state === `stopped`) {\n throw new Error(`Query is stopped`)\n }\n\n // Send initial state\n Object.entries(this.inputCollections).forEach(([key, collection]) => {\n this.sendChangesToInput(\n key,\n collection.currentStateAsChanges(),\n collection.config.getKey\n )\n })\n this.runGraph()\n\n // Subscribe to changes\n Object.entries(this.inputCollections).forEach(([key, collection]) => {\n const unsubscribe = collection.subscribeChanges((changes) => {\n this.sendChangesToInput(key, changes, collection.config.getKey)\n this.runGraph()\n })\n\n this.unsubscribeCallbacks.push(unsubscribe)\n })\n\n this.state = `running`\n return () => {\n this.stop()\n }\n }\n\n stop() {\n this.unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe())\n this.unsubscribeCallbacks = []\n this.state = `stopped`\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compiled-query.js","sources":["../../../src/query/compiled-query.ts"],"sourcesContent":["import { D2, MultiSet, output } from \"@electric-sql/d2mini\"\nimport { createCollection } from \"../collection.js\"\nimport { compileQueryPipeline } from \"./pipeline-compiler.js\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\nimport type { Collection } from \"../collection.js\"\nimport type { ChangeMessage, ResolveType, SyncConfig } from \"../types.js\"\nimport type {\n IStreamBuilder,\n MultiSetArray,\n RootStreamBuilder,\n} from \"@electric-sql/d2mini\"\nimport type { QueryBuilder, ResultsFromContext } from \"./query-builder.js\"\nimport type { Context, Schema } from \"./types.js\"\n\nexport function compileQuery<TContext extends Context<Schema>>(\n queryBuilder: QueryBuilder<TContext>\n) {\n return new CompiledQuery<\n ResultsFromContext<TContext> & { _key?: string | number }\n >(queryBuilder)\n}\n\nexport class CompiledQuery<TResults extends object = Record<string, unknown>> {\n private graph: D2\n private inputs: Record<string, RootStreamBuilder<any>>\n private inputCollections: Record<string, Collection<any>>\n private resultCollection: Collection<TResults>\n public state: `compiled` | `running` | `stopped` = `compiled`\n private unsubscribeCallbacks: Array<() => void> = []\n\n constructor(queryBuilder: QueryBuilder<Context<Schema>>) {\n const query = queryBuilder._query\n const collections = query.collections\n\n if (!collections) {\n throw new Error(`No collections provided`)\n }\n\n this.inputCollections = collections\n\n const graph = new D2()\n const inputs = Object.fromEntries(\n Object.entries(collections).map(([key]) => [key, graph.newInput<any>()])\n )\n\n // Use TResults directly to ensure type compatibility\n const sync: SyncConfig<TResults>[`sync`] = ({\n begin,\n write,\n commit,\n collection,\n }) => {\n compileQueryPipeline<IStreamBuilder<[unknown, TResults]>>(\n query,\n inputs\n ).pipe(\n output((data) => {\n begin()\n data\n .getInner()\n .reduce((acc, [[key, value], multiplicity]) => {\n const changes = acc.get(key) || {\n deletes: 0,\n inserts: 0,\n value,\n }\n if (multiplicity < 0) {\n changes.deletes += Math.abs(multiplicity)\n } else if (multiplicity > 0) {\n changes.inserts += multiplicity\n changes.value = value\n }\n acc.set(key, changes)\n return acc\n }, new Map<unknown, { deletes: number; inserts: number; value: TResults }>())\n .forEach((changes, rawKey) => {\n const { deletes, inserts, value } = changes\n const valueWithKey = { ...value, _key: rawKey }\n\n // Simple singular insert.\n if (inserts && deletes === 0) {\n write({\n value: valueWithKey,\n type: `insert`,\n })\n } else if (\n // Insert & update(s) (updates are a delete & insert)\n inserts > deletes ||\n // Just update(s) but the item is already in the collection (so\n // was inserted previously).\n (inserts === deletes &&\n collection.has(valueWithKey._key as string | number))\n ) {\n write({\n value: valueWithKey,\n type: `update`,\n })\n // Only delete is left as an option\n } else if (deletes > 0) {\n write({\n value: valueWithKey,\n type: `delete`,\n })\n } else {\n throw new Error(\n `This should never happen ${JSON.stringify(changes)}`\n )\n }\n })\n commit()\n })\n )\n graph.finalize()\n }\n\n this.graph = graph\n this.inputs = inputs\n\n const compare = query.orderBy\n ? (\n val1: ResolveType<\n TResults,\n StandardSchemaV1,\n Record<string, unknown>\n >,\n val2: ResolveType<TResults, StandardSchemaV1, Record<string, unknown>>\n ): number => {\n // The query builder always adds an _orderByIndex property if the results are ordered\n const x = val1 as TResults & { _orderByIndex: number }\n const y = val2 as TResults & { _orderByIndex: number }\n if (x._orderByIndex < y._orderByIndex) {\n return -1\n } else if (x._orderByIndex > y._orderByIndex) {\n return 1\n } else {\n return 0\n }\n }\n : undefined\n\n this.resultCollection = createCollection<TResults>({\n getKey: (val: unknown) => {\n return (val as any)._key\n },\n gcTime: 0,\n startSync: true,\n compare,\n sync: {\n sync: sync as unknown as (params: {\n collection: Collection<\n ResolveType<TResults, never, Record<string, unknown>>,\n string | number,\n {}\n >\n begin: () => void\n write: (\n message: Omit<\n ChangeMessage<\n ResolveType<TResults, never, Record<string, unknown>>,\n string | number\n >,\n `key`\n >\n ) => void\n commit: () => void\n }) => void,\n },\n }) as unknown as Collection<TResults, string | number, {}>\n }\n\n get results() {\n return this.resultCollection\n }\n\n private sendChangesToInput(\n inputKey: string,\n changes: Array<ChangeMessage>,\n getKey: (item: ChangeMessage[`value`]) => any\n ) {\n const input = this.inputs[inputKey]!\n const multiSetArray: MultiSetArray<unknown> = []\n for (const change of changes) {\n const key = getKey(change.value)\n if (change.type === `insert`) {\n multiSetArray.push([[key, change.value], 1])\n } else if (change.type === `update`) {\n multiSetArray.push([[key, change.previousValue], -1])\n multiSetArray.push([[key, change.value], 1])\n } else {\n // change.type === `delete`\n multiSetArray.push([[key, change.value], -1])\n }\n }\n input.sendData(new MultiSet(multiSetArray))\n }\n\n private runGraph() {\n this.graph.run()\n }\n\n start() {\n if (this.state === `running`) {\n throw new Error(`Query is already running`)\n } else if (this.state === `stopped`) {\n throw new Error(`Query is stopped`)\n }\n\n // Subscribe to changes\n Object.entries(this.inputCollections).forEach(([key, collection]) => {\n const unsubscribe = collection.subscribeChanges(\n (changes) => {\n this.sendChangesToInput(key, changes, collection.config.getKey)\n this.runGraph()\n },\n { includeInitialState: true }\n )\n\n this.unsubscribeCallbacks.push(unsubscribe)\n })\n\n this.runGraph()\n\n this.state = `running`\n return () => {\n this.stop()\n }\n }\n\n stop() {\n this.unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe())\n this.unsubscribeCallbacks = []\n this.state = `stopped`\n }\n}\n"],"names":[],"mappings":"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"}
@@ -98,7 +98,7 @@ class BaseQueryBuilder {
98
98
  return select;
99
99
  });
100
100
  if (this._query.orderBy) {
101
- validatedSelects.push({ _orderByIndex: { ORDER_INDEX: `numeric` } });
101
+ validatedSelects.push({ _orderByIndex: { ORDER_INDEX: `fractional` } });
102
102
  }
103
103
  const newBuilder = new BaseQueryBuilder(
104
104
  this.query
@@ -226,7 +226,7 @@ class BaseQueryBuilder {
226
226
  newBuilder.query.orderBy = orderBy;
227
227
  newBuilder.query.select = [
228
228
  ...newBuilder.query.select ?? [],
229
- { _orderByIndex: { ORDER_INDEX: `numeric` } }
229
+ { _orderByIndex: { ORDER_INDEX: `fractional` } }
230
230
  ];
231
231
  return newBuilder;
232
232
  }