@tanstack/db 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/SortedMap.cjs +38 -11
- package/dist/cjs/SortedMap.cjs.map +1 -1
- package/dist/cjs/SortedMap.d.cts +10 -0
- package/dist/cjs/collection.cjs +476 -144
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +107 -32
- package/dist/cjs/index.cjs +2 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/optimistic-action.cjs +21 -0
- package/dist/cjs/optimistic-action.cjs.map +1 -0
- package/dist/cjs/optimistic-action.d.cts +39 -0
- package/dist/cjs/query/compiled-query.cjs +38 -16
- package/dist/cjs/query/compiled-query.cjs.map +1 -1
- package/dist/cjs/query/query-builder.cjs +2 -2
- package/dist/cjs/query/query-builder.cjs.map +1 -1
- package/dist/cjs/transactions.cjs +3 -1
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +83 -10
- package/dist/esm/SortedMap.d.ts +10 -0
- package/dist/esm/SortedMap.js +38 -11
- package/dist/esm/SortedMap.js.map +1 -1
- package/dist/esm/collection.d.ts +107 -32
- package/dist/esm/collection.js +477 -145
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +3 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/optimistic-action.d.ts +39 -0
- package/dist/esm/optimistic-action.js +21 -0
- package/dist/esm/optimistic-action.js.map +1 -0
- package/dist/esm/query/compiled-query.js +38 -16
- package/dist/esm/query/compiled-query.js.map +1 -1
- package/dist/esm/query/query-builder.js +2 -2
- package/dist/esm/query/query-builder.js.map +1 -1
- package/dist/esm/transactions.js +3 -1
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +83 -10
- package/package.json +1 -1
- package/src/SortedMap.ts +46 -13
- package/src/collection.ts +689 -239
- package/src/index.ts +1 -0
- package/src/optimistic-action.ts +65 -0
- package/src/query/compiled-query.ts +79 -21
- package/src/query/query-builder.ts +2 -2
- package/src/transactions.ts +6 -1
- package/src/types.ts +124 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.cjs","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 StandardSchema,\n Transaction as TransactionType,\n UtilsRecord,\n} from \"./types\"\n\n// Store collections in memory\nexport const collectionsStore = new Map<string, CollectionImpl<any, any>>()\n\n// Map to track loading collections\nconst loadingCollectionResolvers = new Map<\n string,\n {\n promise: Promise<CollectionImpl<any, any>>\n resolve: (value: CollectionImpl<any, any>) => void\n }\n>()\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 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 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 * @param options - Collection options with optional utilities\n * @returns A new Collection with utilities exposed both at top level and under .utils\n */\nexport function createCollection<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n>(\n options: CollectionConfig<T, TKey> & { utils?: TUtils }\n): Collection<T, TKey, TUtils> {\n const collection = new CollectionImpl<T, TKey>(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<T, TKey, TUtils>\n}\n\n/**\n * Preloads a collection with the given configuration\n * Returns a promise that resolves once the sync tool has done its first commit (initial sync is finished)\n * If the collection has already loaded, it resolves immediately\n *\n * This function is useful in route loaders or similar pre-rendering scenarios where you want\n * to ensure data is available before a route transition completes. It uses the same shared collection\n * instance that will be used by useCollection, ensuring data consistency.\n *\n * @example\n * ```typescript\n * // In a route loader\n * async function loader({ params }) {\n * await preloadCollection({\n * id: `users-${params.userId}`,\n * sync: { ... },\n * });\n *\n * return null;\n * }\n * ```\n *\n * @template T - The type of items in the collection\n * @param config - Configuration for the collection, including id and sync\n * @returns Promise that resolves when the initial sync is finished\n */\nexport function preloadCollection<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n>(config: CollectionConfig<T, TKey>): Promise<CollectionImpl<T, TKey>> {\n if (!config.id) {\n throw new Error(`The id property is required for preloadCollection`)\n }\n\n // If the collection is already fully loaded, return a resolved promise\n if (\n collectionsStore.has(config.id) &&\n !loadingCollectionResolvers.has(config.id)\n ) {\n return Promise.resolve(\n collectionsStore.get(config.id)! as CollectionImpl<T, TKey>\n )\n }\n\n // If the collection is in the process of loading, return its promise\n if (loadingCollectionResolvers.has(config.id)) {\n return loadingCollectionResolvers.get(config.id)!.promise\n }\n\n // Create a new collection instance if it doesn't exist\n if (!collectionsStore.has(config.id)) {\n collectionsStore.set(\n config.id,\n createCollection<T, TKey>({\n id: config.id,\n getKey: config.getKey,\n sync: config.sync,\n schema: config.schema,\n })\n )\n }\n\n const collection = collectionsStore.get(config.id)! as CollectionImpl<T, TKey>\n\n // Create a promise that will resolve after the first commit\n let resolveFirstCommit: (value: CollectionImpl<T, TKey>) => void\n const firstCommitPromise = new Promise<CollectionImpl<T, TKey>>((resolve) => {\n resolveFirstCommit = resolve\n })\n\n // Store the loading promise first\n loadingCollectionResolvers.set(config.id, {\n promise: firstCommitPromise,\n resolve: resolveFirstCommit!,\n })\n\n // Register a one-time listener for the first commit\n collection.onFirstCommit(() => {\n if (!config.id) {\n throw new Error(`The id property is required for preloadCollection`)\n }\n if (loadingCollectionResolvers.has(config.id)) {\n const resolver = loadingCollectionResolvers.get(config.id)!\n loadingCollectionResolvers.delete(config.id)\n resolver.resolve(collection)\n }\n })\n\n return firstCommitPromise\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) => issue.message)\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>\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>) {\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>> = []\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> = {\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>> = 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 as Record<string, unknown>,\n modified: modifiedItem as Record<string, unknown>,\n changes: validatedUpdatePayload as Record<string, unknown>,\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>>\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>> = []\n\n for (const key of keysArray) {\n const globalKey = this.generateGlobalKey(key, this.get(key)!)\n const mutation: PendingMutation<T> = {\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","getActiveTransaction","Transaction","SortedMap","result","withArrayChangeTracking","withChangeTracking","Store"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collection.cjs","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","getActiveTransaction","Transaction","SortedMap","result","withArrayChangeTracking","withChangeTracking","Store"],"mappings":"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"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { Store } from '@tanstack/store';
|
|
2
2
|
import { Transaction } from './transactions.cjs';
|
|
3
3
|
import { SortedMap } from './SortedMap.cjs';
|
|
4
|
-
import { ChangeListener, ChangeMessage, CollectionConfig, Fn, InsertConfig, OperationConfig, Transaction as TransactionType, UtilsRecord } from './types.cjs';
|
|
4
|
+
import { ChangeListener, ChangeMessage, CollectionConfig, CollectionStatus, Fn, InsertConfig, OperationConfig, OptimisticChangeMessage, ResolveType, Transaction as TransactionType, UtilsRecord } from './types.cjs';
|
|
5
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
5
6
|
export declare const collectionsStore: Map<string, CollectionImpl<any, any>>;
|
|
7
|
+
interface PendingSyncedTransaction<T extends object = Record<string, unknown>> {
|
|
8
|
+
committed: boolean;
|
|
9
|
+
operations: Array<OptimisticChangeMessage<T>>;
|
|
10
|
+
}
|
|
6
11
|
/**
|
|
7
12
|
* Enhanced Collection interface that includes both data type T and utilities TUtils
|
|
8
13
|
* @template T - The type of items in the collection
|
|
14
|
+
* @template TKey - The type of the key for the collection
|
|
9
15
|
* @template TUtils - The utilities record type
|
|
10
16
|
*/
|
|
11
17
|
export interface Collection<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}> extends CollectionImpl<T, TKey> {
|
|
@@ -14,42 +20,39 @@ export interface Collection<T extends object = Record<string, unknown>, TKey ext
|
|
|
14
20
|
/**
|
|
15
21
|
* Creates a new Collection instance with the given configuration
|
|
16
22
|
*
|
|
17
|
-
* @template
|
|
23
|
+
* @template TExplicit - The explicit type of items in the collection (highest priority)
|
|
18
24
|
* @template TKey - The type of the key for the collection
|
|
19
25
|
* @template TUtils - The utilities record type
|
|
26
|
+
* @template TSchema - The schema type for validation and type inference (second priority)
|
|
27
|
+
* @template TFallback - The fallback type if no explicit or schema type is provided
|
|
20
28
|
* @param options - Collection options with optional utilities
|
|
21
29
|
* @returns A new Collection with utilities exposed both at top level and under .utils
|
|
22
|
-
*/
|
|
23
|
-
export declare function createCollection<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}>(options: CollectionConfig<T, TKey> & {
|
|
24
|
-
utils?: TUtils;
|
|
25
|
-
}): Collection<T, TKey, TUtils>;
|
|
26
|
-
/**
|
|
27
|
-
* Preloads a collection with the given configuration
|
|
28
|
-
* Returns a promise that resolves once the sync tool has done its first commit (initial sync is finished)
|
|
29
|
-
* If the collection has already loaded, it resolves immediately
|
|
30
|
-
*
|
|
31
|
-
* This function is useful in route loaders or similar pre-rendering scenarios where you want
|
|
32
|
-
* to ensure data is available before a route transition completes. It uses the same shared collection
|
|
33
|
-
* instance that will be used by useCollection, ensuring data consistency.
|
|
34
30
|
*
|
|
35
31
|
* @example
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* sync: { ... },
|
|
42
|
-
* });
|
|
32
|
+
* // Using explicit type
|
|
33
|
+
* const todos = createCollection<Todo>({
|
|
34
|
+
* getKey: (todo) => todo.id,
|
|
35
|
+
* sync: { sync: () => {} }
|
|
36
|
+
* })
|
|
43
37
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
38
|
+
* // Using schema for type inference (preferred as it also gives you client side validation)
|
|
39
|
+
* const todoSchema = z.object({
|
|
40
|
+
* id: z.string(),
|
|
41
|
+
* title: z.string(),
|
|
42
|
+
* completed: z.boolean()
|
|
43
|
+
* })
|
|
47
44
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
45
|
+
* const todos = createCollection({
|
|
46
|
+
* schema: todoSchema,
|
|
47
|
+
* getKey: (todo) => todo.id,
|
|
48
|
+
* sync: { sync: () => {} }
|
|
49
|
+
* })
|
|
50
|
+
*
|
|
51
|
+
* // Note: You must provide either an explicit type or a schema, but not both
|
|
51
52
|
*/
|
|
52
|
-
export declare function
|
|
53
|
+
export declare function createCollection<TExplicit = unknown, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}, TSchema extends StandardSchemaV1 = StandardSchemaV1, TFallback extends object = Record<string, unknown>>(options: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>, TKey, TSchema> & {
|
|
54
|
+
utils?: TUtils;
|
|
55
|
+
}): Collection<ResolveType<TExplicit, TSchema, TFallback>, TKey, TUtils>;
|
|
53
56
|
/**
|
|
54
57
|
* Custom error class for schema validation errors
|
|
55
58
|
*/
|
|
@@ -65,8 +68,10 @@ export declare class SchemaValidationError extends Error {
|
|
|
65
68
|
}>, message?: string);
|
|
66
69
|
}
|
|
67
70
|
export declare class CollectionImpl<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
|
|
71
|
+
config: CollectionConfig<T, TKey, any>;
|
|
68
72
|
transactions: SortedMap<string, Transaction<any>>;
|
|
69
|
-
|
|
73
|
+
pendingSyncedTransactions: Array<PendingSyncedTransaction<T>>;
|
|
74
|
+
syncedData: Map<TKey, T> | SortedMap<TKey, T>;
|
|
70
75
|
syncedMetadata: Map<TKey, unknown>;
|
|
71
76
|
derivedUpserts: Map<TKey, T>;
|
|
72
77
|
derivedDeletes: Set<TKey>;
|
|
@@ -74,11 +79,17 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
|
|
|
74
79
|
private changeListeners;
|
|
75
80
|
private changeKeyListeners;
|
|
76
81
|
utils: Record<string, Fn>;
|
|
77
|
-
private pendingSyncedTransactions;
|
|
78
82
|
private syncedKeys;
|
|
79
|
-
|
|
83
|
+
private preSyncVisibleState;
|
|
84
|
+
private recentlySyncedKeys;
|
|
80
85
|
private hasReceivedFirstCommit;
|
|
86
|
+
private isCommittingSyncTransactions;
|
|
81
87
|
private onFirstCommitCallbacks;
|
|
88
|
+
private _status;
|
|
89
|
+
private activeSubscribersCount;
|
|
90
|
+
private gcTimeoutId;
|
|
91
|
+
private preloadPromise;
|
|
92
|
+
private syncCleanupFn;
|
|
82
93
|
/**
|
|
83
94
|
* Register a callback to be executed on the next commit
|
|
84
95
|
* Useful for preloading collections
|
|
@@ -86,13 +97,70 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
|
|
|
86
97
|
*/
|
|
87
98
|
onFirstCommit(callback: () => void): void;
|
|
88
99
|
id: string;
|
|
100
|
+
/**
|
|
101
|
+
* Gets the current status of the collection
|
|
102
|
+
*/
|
|
103
|
+
get status(): CollectionStatus;
|
|
104
|
+
/**
|
|
105
|
+
* Validates that the collection is in a usable state for data operations
|
|
106
|
+
* @private
|
|
107
|
+
*/
|
|
108
|
+
private validateCollectionUsable;
|
|
109
|
+
/**
|
|
110
|
+
* Validates state transitions to prevent invalid status changes
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
private validateStatusTransition;
|
|
114
|
+
/**
|
|
115
|
+
* Safely update the collection status with validation
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
private setStatus;
|
|
89
119
|
/**
|
|
90
120
|
* Creates a new Collection instance
|
|
91
121
|
*
|
|
92
122
|
* @param config - Configuration object for the collection
|
|
93
123
|
* @throws Error if sync config is missing
|
|
94
124
|
*/
|
|
95
|
-
constructor(config: CollectionConfig<T, TKey>);
|
|
125
|
+
constructor(config: CollectionConfig<T, TKey, any>);
|
|
126
|
+
/**
|
|
127
|
+
* Start sync immediately - internal method for compiled queries
|
|
128
|
+
* This bypasses lazy loading for special cases like live query results
|
|
129
|
+
*/
|
|
130
|
+
startSyncImmediate(): void;
|
|
131
|
+
/**
|
|
132
|
+
* Start the sync process for this collection
|
|
133
|
+
* This is called when the collection is first accessed or preloaded
|
|
134
|
+
*/
|
|
135
|
+
private startSync;
|
|
136
|
+
/**
|
|
137
|
+
* Preload the collection data by starting sync if not already started
|
|
138
|
+
* Multiple concurrent calls will share the same promise
|
|
139
|
+
*/
|
|
140
|
+
preload(): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Clean up the collection by stopping sync and clearing data
|
|
143
|
+
* This can be called manually or automatically by garbage collection
|
|
144
|
+
*/
|
|
145
|
+
cleanup(): Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* Start the garbage collection timer
|
|
148
|
+
* Called when the collection becomes inactive (no subscribers)
|
|
149
|
+
*/
|
|
150
|
+
private startGCTimer;
|
|
151
|
+
/**
|
|
152
|
+
* Cancel the garbage collection timer
|
|
153
|
+
* Called when the collection becomes active again
|
|
154
|
+
*/
|
|
155
|
+
private cancelGCTimer;
|
|
156
|
+
/**
|
|
157
|
+
* Increment the active subscribers count and start sync if needed
|
|
158
|
+
*/
|
|
159
|
+
private addSubscriber;
|
|
160
|
+
/**
|
|
161
|
+
* Decrement the active subscribers count and start GC timer if needed
|
|
162
|
+
*/
|
|
163
|
+
private removeSubscriber;
|
|
96
164
|
/**
|
|
97
165
|
* Recompute optimistic state from active transactions
|
|
98
166
|
*/
|
|
@@ -145,6 +213,7 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
|
|
|
145
213
|
private ensureStandardSchema;
|
|
146
214
|
getKeyFromItem(item: T): TKey;
|
|
147
215
|
generateGlobalKey(key: any, item: any): string;
|
|
216
|
+
private deepEqual;
|
|
148
217
|
private validateData;
|
|
149
218
|
/**
|
|
150
219
|
* Inserts one or more items into the collection
|
|
@@ -269,6 +338,11 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
|
|
|
269
338
|
subscribeChangesKey(key: TKey, listener: ChangeListener<T, TKey>, { includeInitialState }?: {
|
|
270
339
|
includeInitialState?: boolean;
|
|
271
340
|
}): () => void;
|
|
341
|
+
/**
|
|
342
|
+
* Capture visible state for keys that will be affected by pending sync operations
|
|
343
|
+
* This must be called BEFORE onTransactionStateChange clears optimistic state
|
|
344
|
+
*/
|
|
345
|
+
private capturePreSyncVisibleState;
|
|
272
346
|
/**
|
|
273
347
|
* Trigger a recomputation when transactions change
|
|
274
348
|
* This method should be called by the Transaction class when state changes
|
|
@@ -291,3 +365,4 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
|
|
|
291
365
|
*/
|
|
292
366
|
asStoreArray(): Store<Array<T>>;
|
|
293
367
|
}
|
|
368
|
+
export {};
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -6,6 +6,7 @@ const transactions = require("./transactions.cjs");
|
|
|
6
6
|
const errors = require("./errors.cjs");
|
|
7
7
|
const utils = require("./utils.cjs");
|
|
8
8
|
const proxy = require("./proxy.cjs");
|
|
9
|
+
const optimisticAction = require("./optimistic-action.cjs");
|
|
9
10
|
const queryBuilder = require("./query/query-builder.cjs");
|
|
10
11
|
const compiledQuery = require("./query/compiled-query.cjs");
|
|
11
12
|
const pipelineCompiler = require("./query/pipeline-compiler.cjs");
|
|
@@ -13,7 +14,6 @@ exports.CollectionImpl = collection.CollectionImpl;
|
|
|
13
14
|
exports.SchemaValidationError = collection.SchemaValidationError;
|
|
14
15
|
exports.collectionsStore = collection.collectionsStore;
|
|
15
16
|
exports.createCollection = collection.createCollection;
|
|
16
|
-
exports.preloadCollection = collection.preloadCollection;
|
|
17
17
|
exports.SortedMap = SortedMap.SortedMap;
|
|
18
18
|
exports.Transaction = transactions.Transaction;
|
|
19
19
|
exports.createTransaction = transactions.createTransaction;
|
|
@@ -26,6 +26,7 @@ exports.createArrayChangeProxy = proxy.createArrayChangeProxy;
|
|
|
26
26
|
exports.createChangeProxy = proxy.createChangeProxy;
|
|
27
27
|
exports.withArrayChangeTracking = proxy.withArrayChangeTracking;
|
|
28
28
|
exports.withChangeTracking = proxy.withChangeTracking;
|
|
29
|
+
exports.createOptimisticAction = optimisticAction.createOptimisticAction;
|
|
29
30
|
exports.BaseQueryBuilder = queryBuilder.BaseQueryBuilder;
|
|
30
31
|
exports.queryBuilder = queryBuilder.queryBuilder;
|
|
31
32
|
exports.CompiledQuery = compiledQuery.CompiledQuery;
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const transactions = require("./transactions.cjs");
|
|
4
|
+
function createOptimisticAction(options) {
|
|
5
|
+
const { mutationFn, onMutate, ...config } = options;
|
|
6
|
+
return (variables) => {
|
|
7
|
+
const transaction = transactions.createTransaction({
|
|
8
|
+
...config,
|
|
9
|
+
// Wire the mutationFn to use the provided variables
|
|
10
|
+
mutationFn: async (params) => {
|
|
11
|
+
return await mutationFn(variables, params);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
transaction.mutate(() => {
|
|
15
|
+
onMutate(variables);
|
|
16
|
+
});
|
|
17
|
+
return transaction;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
exports.createOptimisticAction = createOptimisticAction;
|
|
21
|
+
//# sourceMappingURL=optimistic-action.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimistic-action.cjs","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":["createTransaction"],"mappings":";;;AAwCO,SAAS,uBACd,SACA;AACA,QAAM,EAAE,YAAY,UAAU,GAAG,OAAW,IAAA;AAE5C,SAAO,CAAC,cAAuC;AAE7C,UAAM,cAAcA,aAAAA,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;;"}
|