@tanstack/db 0.0.9 → 0.0.11
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/collection.cjs +6 -1
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/query/compiled-query.cjs +40 -59
- package/dist/cjs/query/compiled-query.cjs.map +1 -1
- package/dist/cjs/query/compiled-query.d.cts +0 -4
- package/dist/cjs/query/group-by.cjs +3 -3
- package/dist/cjs/query/group-by.cjs.map +1 -1
- package/dist/cjs/query/group-by.d.cts +1 -1
- package/dist/cjs/query/joins.cjs +16 -16
- package/dist/cjs/query/joins.cjs.map +1 -1
- package/dist/cjs/query/joins.d.cts +1 -1
- package/dist/cjs/query/order-by.cjs +6 -6
- package/dist/cjs/query/order-by.cjs.map +1 -1
- package/dist/cjs/query/pipeline-compiler.cjs +5 -5
- package/dist/cjs/query/pipeline-compiler.cjs.map +1 -1
- package/dist/cjs/query/pipeline-compiler.d.cts +1 -1
- package/dist/cjs/query/select.cjs +2 -2
- package/dist/cjs/query/select.cjs.map +1 -1
- package/dist/cjs/transactions.cjs +5 -12
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/transactions.d.cts +1 -1
- package/dist/cjs/types.d.cts +1 -1
- package/dist/esm/collection.js +6 -1
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/query/compiled-query.d.ts +0 -4
- package/dist/esm/query/compiled-query.js +40 -59
- package/dist/esm/query/compiled-query.js.map +1 -1
- package/dist/esm/query/group-by.d.ts +1 -1
- package/dist/esm/query/group-by.js +1 -1
- package/dist/esm/query/group-by.js.map +1 -1
- package/dist/esm/query/joins.d.ts +1 -1
- package/dist/esm/query/joins.js +1 -1
- package/dist/esm/query/joins.js.map +1 -1
- package/dist/esm/query/order-by.js +1 -1
- package/dist/esm/query/order-by.js.map +1 -1
- package/dist/esm/query/pipeline-compiler.d.ts +1 -1
- package/dist/esm/query/pipeline-compiler.js +1 -1
- package/dist/esm/query/pipeline-compiler.js.map +1 -1
- package/dist/esm/query/select.js +1 -1
- package/dist/esm/query/select.js.map +1 -1
- package/dist/esm/transactions.d.ts +1 -1
- package/dist/esm/transactions.js +5 -12
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +1 -1
- package/package.json +2 -2
- package/src/collection.ts +6 -2
- package/src/query/compiled-query.ts +44 -66
- package/src/query/group-by.ts +1 -1
- package/src/query/joins.ts +2 -2
- package/src/query/order-by.ts +1 -1
- package/src/query/pipeline-compiler.ts +2 -2
- package/src/query/select.ts +1 -1
- package/src/transactions.ts +8 -20
- package/src/types.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.js","sources":["../../src/collection.ts"],"sourcesContent":["import { Store } from \"@tanstack/store\"\nimport { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { Transaction, getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\nimport type {\n ChangeListener,\n ChangeMessage,\n CollectionConfig,\n Fn,\n InsertConfig,\n OperationConfig,\n OptimisticChangeMessage,\n PendingMutation,\n 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 early\n if (mutations.length === 0) {\n throw new Error(`No changes were made to any of the objects`)\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","result"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collection.js","sources":["../../src/collection.ts"],"sourcesContent":["import { Store } from \"@tanstack/store\"\nimport { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { Transaction, getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\nimport type {\n ChangeListener,\n ChangeMessage,\n CollectionConfig,\n 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","result"],"mappings":"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"}
|
|
@@ -10,14 +10,10 @@ export declare class CompiledQuery<TResults extends object = Record<string, unkn
|
|
|
10
10
|
private inputCollections;
|
|
11
11
|
private resultCollection;
|
|
12
12
|
state: `compiled` | `running` | `stopped`;
|
|
13
|
-
private version;
|
|
14
13
|
private unsubscribeCallbacks;
|
|
15
14
|
constructor(queryBuilder: QueryBuilder<Context<Schema>>);
|
|
16
15
|
get results(): Collection<TResults, string | number, {}>;
|
|
17
16
|
private sendChangesToInput;
|
|
18
|
-
private sendFrontierToInput;
|
|
19
|
-
private sendFrontierToAllInputs;
|
|
20
|
-
private incrementVersion;
|
|
21
17
|
private runGraph;
|
|
22
18
|
start(): () => void;
|
|
23
19
|
stop(): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D2, MultiSet, output
|
|
1
|
+
import { D2, MultiSet, output } from "@electric-sql/d2mini";
|
|
2
2
|
import { createCollection } from "../collection.js";
|
|
3
3
|
import { compileQueryPipeline } from "./pipeline-compiler.js";
|
|
4
4
|
function compileQuery(queryBuilder) {
|
|
@@ -7,7 +7,6 @@ function compileQuery(queryBuilder) {
|
|
|
7
7
|
class CompiledQuery {
|
|
8
8
|
constructor(queryBuilder) {
|
|
9
9
|
this.state = `compiled`;
|
|
10
|
-
this.version = 0;
|
|
11
10
|
this.unsubscribeCallbacks = [];
|
|
12
11
|
const query = queryBuilder._query;
|
|
13
12
|
const collections = query.collections;
|
|
@@ -15,7 +14,7 @@ class CompiledQuery {
|
|
|
15
14
|
throw new Error(`No collections provided`);
|
|
16
15
|
}
|
|
17
16
|
this.inputCollections = collections;
|
|
18
|
-
const graph = new D2(
|
|
17
|
+
const graph = new D2();
|
|
19
18
|
const inputs = Object.fromEntries(
|
|
20
19
|
Object.entries(collections).map(([key]) => [key, graph.newInput()])
|
|
21
20
|
);
|
|
@@ -24,45 +23,43 @@ class CompiledQuery {
|
|
|
24
23
|
query,
|
|
25
24
|
inputs
|
|
26
25
|
).pipe(
|
|
27
|
-
output((
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
commit();
|
|
65
|
-
}
|
|
26
|
+
output((data) => {
|
|
27
|
+
begin();
|
|
28
|
+
data.getInner().reduce((acc, [[key, value], multiplicity]) => {
|
|
29
|
+
const changes = acc.get(key) || {
|
|
30
|
+
deletes: 0,
|
|
31
|
+
inserts: 0,
|
|
32
|
+
value
|
|
33
|
+
};
|
|
34
|
+
if (multiplicity < 0) {
|
|
35
|
+
changes.deletes += Math.abs(multiplicity);
|
|
36
|
+
} else if (multiplicity > 0) {
|
|
37
|
+
changes.inserts += multiplicity;
|
|
38
|
+
changes.value = value;
|
|
39
|
+
}
|
|
40
|
+
acc.set(key, changes);
|
|
41
|
+
return acc;
|
|
42
|
+
}, /* @__PURE__ */ new Map()).forEach((changes, rawKey) => {
|
|
43
|
+
const { deletes, inserts, value } = changes;
|
|
44
|
+
const valueWithKey = { ...value, _key: rawKey };
|
|
45
|
+
if (inserts && !deletes) {
|
|
46
|
+
write({
|
|
47
|
+
value: valueWithKey,
|
|
48
|
+
type: `insert`
|
|
49
|
+
});
|
|
50
|
+
} else if (inserts >= deletes) {
|
|
51
|
+
write({
|
|
52
|
+
value: valueWithKey,
|
|
53
|
+
type: `update`
|
|
54
|
+
});
|
|
55
|
+
} else if (deletes > 0) {
|
|
56
|
+
write({
|
|
57
|
+
value: valueWithKey,
|
|
58
|
+
type: `delete`
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
commit();
|
|
66
63
|
})
|
|
67
64
|
);
|
|
68
65
|
graph.finalize();
|
|
@@ -97,19 +94,7 @@ class CompiledQuery {
|
|
|
97
94
|
multiSetArray.push([[key, change.value], -1]);
|
|
98
95
|
}
|
|
99
96
|
}
|
|
100
|
-
input.sendData(
|
|
101
|
-
}
|
|
102
|
-
sendFrontierToInput(inputKey) {
|
|
103
|
-
const input = this.inputs[inputKey];
|
|
104
|
-
input.sendFrontier(this.version);
|
|
105
|
-
}
|
|
106
|
-
sendFrontierToAllInputs() {
|
|
107
|
-
Object.entries(this.inputs).forEach(([key]) => {
|
|
108
|
-
this.sendFrontierToInput(key);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
incrementVersion() {
|
|
112
|
-
this.version++;
|
|
97
|
+
input.sendData(new MultiSet(multiSetArray));
|
|
113
98
|
}
|
|
114
99
|
runGraph() {
|
|
115
100
|
this.graph.run();
|
|
@@ -127,14 +112,10 @@ class CompiledQuery {
|
|
|
127
112
|
collection.config.getKey
|
|
128
113
|
);
|
|
129
114
|
});
|
|
130
|
-
this.incrementVersion();
|
|
131
|
-
this.sendFrontierToAllInputs();
|
|
132
115
|
this.runGraph();
|
|
133
116
|
Object.entries(this.inputCollections).forEach(([key, collection]) => {
|
|
134
117
|
const unsubscribe = collection.subscribeChanges((changes) => {
|
|
135
118
|
this.sendChangesToInput(key, changes, collection.config.getKey);
|
|
136
|
-
this.incrementVersion();
|
|
137
|
-
this.sendFrontierToAllInputs();
|
|
138
119
|
this.runGraph();
|
|
139
120
|
});
|
|
140
121
|
this.unsubscribeCallbacks.push(unsubscribe);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiled-query.js","sources":["../../../src/query/compiled-query.ts"],"sourcesContent":["import { D2,
|
|
1
|
+
{"version":3,"file":"compiled-query.js","sources":["../../../src/query/compiled-query.ts"],"sourcesContent":["import { D2, MultiSet, output } from \"@electric-sql/d2mini\"\nimport { createCollection } from \"../collection.js\"\nimport { compileQueryPipeline } from \"./pipeline-compiler.js\"\nimport type { Collection } from \"../collection.js\"\nimport type { ChangeMessage, SyncConfig } from \"../types.js\"\nimport type {\n IStreamBuilder,\n MultiSetArray,\n RootStreamBuilder,\n} from \"@electric-sql/d2mini\"\nimport type { QueryBuilder, ResultsFromContext } from \"./query-builder.js\"\nimport type { Context, Schema } from \"./types.js\"\n\nexport function compileQuery<TContext extends Context<Schema>>(\n queryBuilder: QueryBuilder<TContext>\n) {\n return new CompiledQuery<\n ResultsFromContext<TContext> & { _key?: string | number }\n >(queryBuilder)\n}\n\nexport class CompiledQuery<TResults extends object = Record<string, unknown>> {\n private graph: D2\n private inputs: Record<string, RootStreamBuilder<any>>\n private inputCollections: Record<string, Collection<any>>\n private resultCollection: Collection<TResults>\n public state: `compiled` | `running` | `stopped` = `compiled`\n private unsubscribeCallbacks: Array<() => void> = []\n\n constructor(queryBuilder: QueryBuilder<Context<Schema>>) {\n const query = queryBuilder._query\n const collections = query.collections\n\n if (!collections) {\n throw new Error(`No collections provided`)\n }\n\n this.inputCollections = collections\n\n const graph = new D2()\n const inputs = Object.fromEntries(\n Object.entries(collections).map(([key]) => [key, graph.newInput<any>()])\n )\n\n const sync: SyncConfig<TResults>[`sync`] = ({ begin, write, commit }) => {\n compileQueryPipeline<IStreamBuilder<[unknown, TResults]>>(\n query,\n inputs\n ).pipe(\n output((data) => {\n begin()\n data\n .getInner()\n .reduce((acc, [[key, value], multiplicity]) => {\n const changes = acc.get(key) || {\n deletes: 0,\n inserts: 0,\n value,\n }\n if (multiplicity < 0) {\n changes.deletes += Math.abs(multiplicity)\n } else if (multiplicity > 0) {\n changes.inserts += multiplicity\n changes.value = value\n }\n acc.set(key, changes)\n return acc\n }, new Map<unknown, { deletes: number; inserts: number; value: TResults }>())\n .forEach((changes, rawKey) => {\n const { deletes, inserts, value } = changes\n const valueWithKey = { ...value, _key: rawKey }\n if (inserts && !deletes) {\n write({\n value: valueWithKey,\n type: `insert`,\n })\n } else if (inserts >= deletes) {\n write({\n value: valueWithKey,\n type: `update`,\n })\n } else if (deletes > 0) {\n write({\n value: valueWithKey,\n type: `delete`,\n })\n }\n })\n commit()\n })\n )\n graph.finalize()\n }\n\n this.graph = graph\n this.inputs = inputs\n this.resultCollection = createCollection<TResults>({\n id: crypto.randomUUID(), // TODO: remove when we don't require any more\n getKey: (val: unknown) => {\n return (val as any)._key\n },\n sync: {\n sync,\n },\n })\n }\n\n get results() {\n return this.resultCollection\n }\n\n private sendChangesToInput(\n inputKey: string,\n changes: Array<ChangeMessage>,\n getKey: (item: ChangeMessage[`value`]) => any\n ) {\n const input = this.inputs[inputKey]!\n const multiSetArray: MultiSetArray<unknown> = []\n for (const change of changes) {\n const key = getKey(change.value)\n if (change.type === `insert`) {\n multiSetArray.push([[key, change.value], 1])\n } else if (change.type === `update`) {\n multiSetArray.push([[key, change.previousValue], -1])\n multiSetArray.push([[key, change.value], 1])\n } else {\n // change.type === `delete`\n multiSetArray.push([[key, change.value], -1])\n }\n }\n input.sendData(new MultiSet(multiSetArray))\n }\n\n private runGraph() {\n this.graph.run()\n }\n\n start() {\n if (this.state === `running`) {\n throw new Error(`Query is already running`)\n } else if (this.state === `stopped`) {\n throw new Error(`Query is stopped`)\n }\n\n // Send initial state\n Object.entries(this.inputCollections).forEach(([key, collection]) => {\n this.sendChangesToInput(\n key,\n collection.currentStateAsChanges(),\n collection.config.getKey\n )\n })\n this.runGraph()\n\n // Subscribe to changes\n Object.entries(this.inputCollections).forEach(([key, collection]) => {\n const unsubscribe = collection.subscribeChanges((changes) => {\n this.sendChangesToInput(key, changes, collection.config.getKey)\n this.runGraph()\n })\n\n this.unsubscribeCallbacks.push(unsubscribe)\n })\n\n this.state = `running`\n return () => {\n this.stop()\n }\n }\n\n stop() {\n this.unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe())\n this.unsubscribeCallbacks = []\n this.state = `stopped`\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ export declare function getAggregateFunction(functionName: string, columnRef: st
|
|
|
12
12
|
reduce: (values: [number, number][]) => number;
|
|
13
13
|
postMap?: ((result: number) => number) | undefined;
|
|
14
14
|
} | {
|
|
15
|
-
pipe: (stream: import('@electric-sql/
|
|
15
|
+
pipe: (stream: import('@electric-sql/d2mini').IStreamBuilder<[string, Record<string, unknown>]>) => import('@electric-sql/d2mini').IStreamBuilder<import('@electric-sql/d2mini').KeyValue<string, number>>;
|
|
16
16
|
} | {
|
|
17
17
|
preMap: (data: [string, Record<string, unknown>]) => {
|
|
18
18
|
sum: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { groupBy, groupByOperators } from "@electric-sql/
|
|
1
|
+
import { groupBy, groupByOperators } from "@electric-sql/d2mini";
|
|
2
2
|
import { extractValueFromNamespacedRow, evaluateOperandOnNamespacedRow } from "./extractors.js";
|
|
3
3
|
import { isAggregateFunctionCall } from "./utils.js";
|
|
4
4
|
const { sum, count, avg, min, max, median, mode } = groupByOperators;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group-by.js","sources":["../../../src/query/group-by.ts"],"sourcesContent":["import { groupBy, groupByOperators } from \"@electric-sql/
|
|
1
|
+
{"version":3,"file":"group-by.js","sources":["../../../src/query/group-by.ts"],"sourcesContent":["import { groupBy, groupByOperators } from \"@electric-sql/d2mini\"\nimport {\n evaluateOperandOnNamespacedRow,\n extractValueFromNamespacedRow,\n} from \"./extractors\"\nimport { isAggregateFunctionCall } from \"./utils\"\nimport type { ConditionOperand, FunctionCall, Query } from \"./schema\"\nimport type { NamespacedAndKeyedStream } from \"../types.js\"\n\nconst { sum, count, avg, min, max, median, mode } = groupByOperators\n\n/**\n * Process the groupBy clause in a D2QL query\n */\nexport function processGroupBy(\n pipeline: NamespacedAndKeyedStream,\n query: Query,\n mainTableAlias: string\n) {\n // Normalize groupBy to an array of column references\n const groupByColumns = Array.isArray(query.groupBy)\n ? query.groupBy\n : [query.groupBy]\n\n // Create a key extractor function for the groupBy operator\n const keyExtractor = ([_oldKey, namespacedRow]: [\n string,\n Record<string, unknown>,\n ]) => {\n const key: Record<string, unknown> = {}\n\n // Extract each groupBy column value\n for (const column of groupByColumns) {\n if (typeof column === `string` && (column as string).startsWith(`@`)) {\n const columnRef = (column as string).substring(1)\n const columnName = columnRef.includes(`.`)\n ? columnRef.split(`.`)[1]\n : columnRef\n\n key[columnName!] = extractValueFromNamespacedRow(\n namespacedRow,\n columnRef,\n mainTableAlias\n )\n }\n }\n\n return key\n }\n\n // Create aggregate functions for any aggregated columns in the SELECT clause\n const aggregates: Record<string, any> = {}\n\n if (!query.select) {\n throw new Error(`SELECT clause is required for GROUP BY`)\n }\n\n // Scan the SELECT clause for aggregate functions\n for (const item of query.select) {\n if (typeof item === `object`) {\n for (const [alias, expr] of Object.entries(item)) {\n if (typeof expr === `object` && isAggregateFunctionCall(expr)) {\n // Get the function name (the only key in the object)\n const functionName = Object.keys(expr)[0]\n // Get the column reference or expression to aggregate\n const columnRef = (expr as FunctionCall)[\n functionName as keyof FunctionCall\n ]\n\n // Add the aggregate function to our aggregates object\n aggregates[alias] = getAggregateFunction(\n functionName!,\n columnRef,\n mainTableAlias\n )\n }\n }\n }\n }\n\n // Apply the groupBy operator if we have any aggregates\n if (Object.keys(aggregates).length > 0) {\n pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates))\n }\n\n return pipeline\n}\n\n/**\n * Helper function to get an aggregate function based on the function name\n */\nexport function getAggregateFunction(\n functionName: string,\n columnRef: string | ConditionOperand,\n mainTableAlias: string\n) {\n // Create a value extractor function for the column to aggregate\n const valueExtractor = ([_oldKey, namespacedRow]: [\n string,\n Record<string, unknown>,\n ]) => {\n let value: unknown\n if (typeof columnRef === `string` && columnRef.startsWith(`@`)) {\n value = extractValueFromNamespacedRow(\n namespacedRow,\n columnRef.substring(1),\n mainTableAlias\n )\n } else {\n value = evaluateOperandOnNamespacedRow(\n namespacedRow,\n columnRef as ConditionOperand,\n mainTableAlias\n )\n }\n // Ensure we return a number for aggregate functions\n return typeof value === `number` ? value : 0\n }\n\n // Return the appropriate aggregate function\n switch (functionName.toUpperCase()) {\n case `SUM`:\n return sum(valueExtractor)\n case `COUNT`:\n return count() // count() doesn't need a value extractor\n case `AVG`:\n return avg(valueExtractor)\n case `MIN`:\n return min(valueExtractor)\n case `MAX`:\n return max(valueExtractor)\n case `MEDIAN`:\n return median(valueExtractor)\n case `MODE`:\n return mode(valueExtractor)\n default:\n throw new Error(`Unsupported aggregate function: ${functionName}`)\n }\n}\n"],"names":[],"mappings":";;;AASA,MAAM,EAAE,KAAK,OAAO,KAAK,KAAK,KAAK,QAAQ,SAAS;AAKpC,SAAA,eACd,UACA,OACA,gBACA;AAEM,QAAA,iBAAiB,MAAM,QAAQ,MAAM,OAAO,IAC9C,MAAM,UACN,CAAC,MAAM,OAAO;AAGlB,QAAM,eAAe,CAAC,CAAC,SAAS,aAAa,MAGvC;AACJ,UAAM,MAA+B,CAAC;AAGtC,eAAW,UAAU,gBAAgB;AACnC,UAAI,OAAO,WAAW,YAAa,OAAkB,WAAW,GAAG,GAAG;AAC9D,cAAA,YAAa,OAAkB,UAAU,CAAC;AAC1C,cAAA,aAAa,UAAU,SAAS,GAAG,IACrC,UAAU,MAAM,GAAG,EAAE,CAAC,IACtB;AAEJ,YAAI,UAAW,IAAI;AAAA,UACjB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MAAA;AAAA,IACF;AAGK,WAAA;AAAA,EACT;AAGA,QAAM,aAAkC,CAAC;AAErC,MAAA,CAAC,MAAM,QAAQ;AACX,UAAA,IAAI,MAAM,wCAAwC;AAAA,EAAA;AAI/C,aAAA,QAAQ,MAAM,QAAQ;AAC3B,QAAA,OAAO,SAAS,UAAU;AAC5B,iBAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,IAAI,GAAG;AAChD,YAAI,OAAO,SAAS,YAAY,wBAAwB,IAAI,GAAG;AAE7D,gBAAM,eAAe,OAAO,KAAK,IAAI,EAAE,CAAC;AAElC,gBAAA,YAAa,KACjB,YACF;AAGA,qBAAW,KAAK,IAAI;AAAA,YAClB;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAIF,MAAI,OAAO,KAAK,UAAU,EAAE,SAAS,GAAG;AACtC,eAAW,SAAS,KAAK,QAAQ,cAAc,UAAU,CAAC;AAAA,EAAA;AAGrD,SAAA;AACT;AAKgB,SAAA,qBACd,cACA,WACA,gBACA;AAEA,QAAM,iBAAiB,CAAC,CAAC,SAAS,aAAa,MAGzC;AACA,QAAA;AACJ,QAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG,GAAG;AACtD,cAAA;AAAA,QACN;AAAA,QACA,UAAU,UAAU,CAAC;AAAA,QACrB;AAAA,MACF;AAAA,IAAA,OACK;AACG,cAAA;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IAAA;AAGK,WAAA,OAAO,UAAU,WAAW,QAAQ;AAAA,EAC7C;AAGQ,UAAA,aAAa,YAAe,GAAA;AAAA,IAClC,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,MAAM;AAAA;AAAA,IACf,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,OAAO,cAAc;AAAA,IAC9B,KAAK;AACH,aAAO,KAAK,cAAc;AAAA,IAC5B;AACE,YAAM,IAAI,MAAM,mCAAmC,YAAY,EAAE;AAAA,EAAA;AAEvE;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Query } from './index.js';
|
|
2
|
-
import { IStreamBuilder } from '@electric-sql/
|
|
2
|
+
import { IStreamBuilder } from '@electric-sql/d2mini';
|
|
3
3
|
import { KeyedStream, NamespacedAndKeyedStream, NamespacedRow } from '../types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Creates a processing pipeline for join clauses
|
package/dist/esm/query/joins.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { map, join, consolidate, filter } from "@electric-sql/
|
|
1
|
+
import { map, join, consolidate, filter } from "@electric-sql/d2mini";
|
|
2
2
|
import { evaluateConditionOnNamespacedRow } from "./evaluators.js";
|
|
3
3
|
import { extractJoinKey } from "./extractors.js";
|
|
4
4
|
function processJoinClause(pipeline, query, tables, mainTableAlias, allInputs) {
|