@tanstack/db 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/cjs/collection.cjs +71 -21
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +8 -7
  4. package/dist/cjs/query/evaluators.cjs +15 -0
  5. package/dist/cjs/query/evaluators.cjs.map +1 -1
  6. package/dist/cjs/query/evaluators.d.cts +5 -1
  7. package/dist/cjs/query/pipeline-compiler.cjs +2 -2
  8. package/dist/cjs/query/pipeline-compiler.cjs.map +1 -1
  9. package/dist/cjs/query/query-builder.cjs +22 -17
  10. package/dist/cjs/query/query-builder.cjs.map +1 -1
  11. package/dist/cjs/query/query-builder.d.cts +12 -2
  12. package/dist/cjs/query/schema.d.cts +11 -5
  13. package/dist/cjs/query/select.cjs +12 -0
  14. package/dist/cjs/query/select.cjs.map +1 -1
  15. package/dist/cjs/transactions.cjs +3 -1
  16. package/dist/cjs/transactions.cjs.map +1 -1
  17. package/dist/cjs/types.d.cts +25 -0
  18. package/dist/esm/collection.d.ts +8 -7
  19. package/dist/esm/collection.js +72 -22
  20. package/dist/esm/collection.js.map +1 -1
  21. package/dist/esm/query/evaluators.d.ts +5 -1
  22. package/dist/esm/query/evaluators.js +16 -1
  23. package/dist/esm/query/evaluators.js.map +1 -1
  24. package/dist/esm/query/pipeline-compiler.js +3 -3
  25. package/dist/esm/query/pipeline-compiler.js.map +1 -1
  26. package/dist/esm/query/query-builder.d.ts +12 -2
  27. package/dist/esm/query/query-builder.js +22 -17
  28. package/dist/esm/query/query-builder.js.map +1 -1
  29. package/dist/esm/query/schema.d.ts +11 -5
  30. package/dist/esm/query/select.js +12 -0
  31. package/dist/esm/query/select.js.map +1 -1
  32. package/dist/esm/transactions.js +3 -1
  33. package/dist/esm/transactions.js.map +1 -1
  34. package/dist/esm/types.d.ts +25 -0
  35. package/package.json +1 -1
  36. package/src/collection.ts +117 -30
  37. package/src/query/evaluators.ts +27 -0
  38. package/src/query/pipeline-compiler.ts +3 -3
  39. package/src/query/query-builder.ts +40 -23
  40. package/src/query/schema.ts +17 -5
  41. package/src/query/select.ts +24 -1
  42. package/src/transactions.ts +8 -1
  43. package/src/types.ts +28 -0
@@ -140,9 +140,11 @@ class Collection {
140
140
  }
141
141
  };
142
142
  this.insert = (data, config2) => {
143
- const transaction = transactions.getActiveTransaction();
144
- if (typeof transaction === `undefined`) {
145
- throw `no transaction found when calling collection.insert`;
143
+ const ambientTransaction = transactions.getActiveTransaction();
144
+ if (!ambientTransaction && !this.config.onInsert) {
145
+ throw new Error(
146
+ `Collection.insert called directly (not within an explicit transaction) but no 'onInsert' handler is configured.`
147
+ );
146
148
  }
147
149
  const items = Array.isArray(data) ? data : [data];
148
150
  const mutations = [];
@@ -172,17 +174,34 @@ class Collection {
172
174
  };
173
175
  mutations.push(mutation);
174
176
  });
175
- transaction.applyMutations(mutations);
176
- this.transactions.setState((sortedMap) => {
177
- sortedMap.set(transaction.id, transaction);
178
- return sortedMap;
179
- });
180
- return transaction;
177
+ if (ambientTransaction) {
178
+ ambientTransaction.applyMutations(mutations);
179
+ this.transactions.setState((sortedMap) => {
180
+ sortedMap.set(ambientTransaction.id, ambientTransaction);
181
+ return sortedMap;
182
+ });
183
+ return ambientTransaction;
184
+ } else {
185
+ const directOpTransaction = new transactions.Transaction({
186
+ mutationFn: async (params) => {
187
+ return this.config.onInsert(params);
188
+ }
189
+ });
190
+ directOpTransaction.applyMutations(mutations);
191
+ directOpTransaction.commit();
192
+ this.transactions.setState((sortedMap) => {
193
+ sortedMap.set(directOpTransaction.id, directOpTransaction);
194
+ return sortedMap;
195
+ });
196
+ return directOpTransaction;
197
+ }
181
198
  };
182
199
  this.delete = (ids, config2) => {
183
- const transaction = transactions.getActiveTransaction();
184
- if (typeof transaction === `undefined`) {
185
- throw `no transaction found when calling collection.delete`;
200
+ const ambientTransaction = transactions.getActiveTransaction();
201
+ if (!ambientTransaction && !this.config.onDelete) {
202
+ throw new Error(
203
+ `Collection.delete called directly (not within an explicit transaction) but no 'onDelete' handler is configured.`
204
+ );
186
205
  }
187
206
  const idsArray = (Array.isArray(ids) ? ids : [ids]).map(
188
207
  (id) => this.getKeyFromId(id)
@@ -204,12 +223,27 @@ class Collection {
204
223
  };
205
224
  mutations.push(mutation);
206
225
  }
207
- transaction.applyMutations(mutations);
226
+ if (ambientTransaction) {
227
+ ambientTransaction.applyMutations(mutations);
228
+ this.transactions.setState((sortedMap) => {
229
+ sortedMap.set(ambientTransaction.id, ambientTransaction);
230
+ return sortedMap;
231
+ });
232
+ return ambientTransaction;
233
+ }
234
+ const directOpTransaction = new transactions.Transaction({
235
+ autoCommit: true,
236
+ mutationFn: async (transaction) => {
237
+ return this.config.onDelete(transaction);
238
+ }
239
+ });
240
+ directOpTransaction.applyMutations(mutations);
241
+ directOpTransaction.commit();
208
242
  this.transactions.setState((sortedMap) => {
209
- sortedMap.set(transaction.id, transaction);
243
+ sortedMap.set(directOpTransaction.id, directOpTransaction);
210
244
  return sortedMap;
211
245
  });
212
- return transaction;
246
+ return directOpTransaction;
213
247
  };
214
248
  if (!config) {
215
249
  throw new Error(`Collection requires a config`);
@@ -468,9 +502,11 @@ class Collection {
468
502
  if (typeof ids === `undefined`) {
469
503
  throw new Error(`The first argument to update is missing`);
470
504
  }
471
- const transaction = transactions.getActiveTransaction();
472
- if (typeof transaction === `undefined`) {
473
- throw `no transaction found when calling collection.update`;
505
+ const ambientTransaction = transactions.getActiveTransaction();
506
+ if (!ambientTransaction && !this.config.onUpdate) {
507
+ throw new Error(
508
+ `Collection.update called directly (not within an explicit transaction) but no 'onUpdate' handler is configured.`
509
+ );
474
510
  }
475
511
  const isArray = Array.isArray(ids);
476
512
  const idsArray = (Array.isArray(ids) ? ids : [ids]).map(
@@ -536,12 +572,26 @@ class Collection {
536
572
  if (mutations.length === 0) {
537
573
  throw new Error(`No changes were made to any of the objects`);
538
574
  }
539
- transaction.applyMutations(mutations);
575
+ if (ambientTransaction) {
576
+ ambientTransaction.applyMutations(mutations);
577
+ this.transactions.setState((sortedMap) => {
578
+ sortedMap.set(ambientTransaction.id, ambientTransaction);
579
+ return sortedMap;
580
+ });
581
+ return ambientTransaction;
582
+ }
583
+ const directOpTransaction = new transactions.Transaction({
584
+ mutationFn: async (transaction) => {
585
+ return this.config.onUpdate(transaction);
586
+ }
587
+ });
588
+ directOpTransaction.applyMutations(mutations);
589
+ directOpTransaction.commit();
540
590
  this.transactions.setState((sortedMap) => {
541
- sortedMap.set(transaction.id, transaction);
591
+ sortedMap.set(directOpTransaction.id, directOpTransaction);
542
592
  return sortedMap;
543
593
  });
544
- return transaction;
594
+ return directOpTransaction;
545
595
  }
546
596
  /**
547
597
  * Gets the current state of the collection as a Map
@@ -1 +1 @@
1
- {"version":3,"file":"collection.cjs","sources":["../../src/collection.ts"],"sourcesContent":["import { Derived, Store, batch } from \"@tanstack/store\"\nimport { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\nimport type {\n ChangeMessage,\n CollectionConfig,\n InsertConfig,\n OperationConfig,\n OptimisticChangeMessage,\n PendingMutation,\n StandardSchema,\n Transaction,\n} from \"./types\"\n\n// Store collections in memory using Tanstack store\nexport const collectionsStore = new Store(new Map<string, Collection<any>>())\n\n// Map to track loading collections\n\nconst loadingCollections = new Map<\n string,\n Promise<Collection<Record<string, unknown>>>\n>()\n\ninterface PendingSyncedTransaction<T extends object = Record<string, unknown>> {\n committed: boolean\n operations: Array<OptimisticChangeMessage<T>>\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 * @param config - Configuration for the collection, including id and sync\n * @returns A new Collection instance\n */\nexport function createCollection<T extends object = Record<string, unknown>>(\n config: CollectionConfig<T>\n): Collection<T> {\n return new Collection<T>(config)\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<T extends object = Record<string, unknown>>(\n config: CollectionConfig<T>\n): Promise<Collection<T>> {\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.state.has(config.id) &&\n !loadingCollections.has(config.id)\n ) {\n return Promise.resolve(\n collectionsStore.state.get(config.id)! as Collection<T>\n )\n }\n\n // If the collection is in the process of loading, return its promise\n if (loadingCollections.has(config.id)) {\n return loadingCollections.get(config.id)! as Promise<Collection<T>>\n }\n\n // Create a new collection instance if it doesn't exist\n if (!collectionsStore.state.has(config.id)) {\n collectionsStore.setState((prev) => {\n const next = new Map(prev)\n if (!config.id) {\n throw new Error(`The id property is required for preloadCollection`)\n }\n next.set(\n config.id,\n new Collection<T>({\n id: config.id,\n getId: config.getId,\n sync: config.sync,\n schema: config.schema,\n })\n )\n return next\n })\n }\n\n const collection = collectionsStore.state.get(config.id)! as Collection<T>\n\n // Create a promise that will resolve after the first commit\n let resolveFirstCommit: () => void\n const firstCommitPromise = new Promise<Collection<T>>((resolve) => {\n resolveFirstCommit = () => {\n resolve(collection)\n }\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 (loadingCollections.has(config.id)) {\n loadingCollections.delete(config.id)\n resolveFirstCommit()\n }\n })\n\n // Store the loading promise\n loadingCollections.set(\n config.id,\n firstCommitPromise as Promise<Collection<Record<string, unknown>>>\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 Collection<T extends object = Record<string, unknown>> {\n public transactions: Store<SortedMap<string, Transaction>>\n public optimisticOperations: Derived<Array<OptimisticChangeMessage<T>>>\n public derivedState: Derived<Map<string, T>>\n public derivedArray: Derived<Array<T>>\n public derivedChanges: Derived<Array<ChangeMessage<T>>>\n public syncedData = new Store<Map<string, T>>(new Map())\n public syncedMetadata = new Store(new Map<string, unknown>())\n private pendingSyncedTransactions: Array<PendingSyncedTransaction<T>> = []\n private syncedKeys = new Set<string>()\n public config: CollectionConfig<T>\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>) {\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 Store(\n new SortedMap<string, Transaction>(\n (a, b) => a.createdAt.getTime() - b.createdAt.getTime()\n )\n )\n\n // Copies of live mutations are stored here and removed once the transaction completes.\n this.optimisticOperations = new Derived({\n fn: ({ currDepVals: [transactions] }) => {\n const result = Array.from(transactions.values())\n .map((transaction) => {\n const isActive = ![`completed`, `failed`].includes(\n transaction.state\n )\n return transaction.mutations\n .filter((mutation) => mutation.collection === this)\n .map((mutation) => {\n const message: OptimisticChangeMessage<T> = {\n type: mutation.type,\n key: mutation.key,\n value: mutation.modified as T,\n isActive,\n }\n if (\n mutation.metadata !== undefined &&\n mutation.metadata !== null\n ) {\n message.metadata = mutation.metadata as Record<\n string,\n unknown\n >\n }\n return message\n })\n })\n .flat()\n\n return result\n },\n deps: [this.transactions],\n })\n this.optimisticOperations.mount()\n\n // Combine together synced data & optimistic operations.\n this.derivedState = new Derived({\n fn: ({ currDepVals: [syncedData, operations] }) => {\n const combined = new Map<string, T>(syncedData)\n\n // Apply the optimistic operations on top of the synced state.\n for (const operation of operations) {\n if (operation.isActive) {\n switch (operation.type) {\n case `insert`:\n combined.set(operation.key, operation.value)\n break\n case `update`:\n combined.set(operation.key, operation.value)\n break\n case `delete`:\n combined.delete(operation.key)\n break\n }\n }\n }\n\n return combined\n },\n deps: [this.syncedData, this.optimisticOperations],\n })\n\n // Create a derived array from the map to avoid recalculating it\n this.derivedArray = new Derived({\n fn: ({ currDepVals: [stateMap] }) => {\n // Collections returned by a query that has an orderBy are annotated\n // with the _orderByIndex field.\n // This is used to sort the array when it's derived.\n const array: Array<T & { _orderByIndex?: number }> = Array.from(\n stateMap.values()\n )\n if (array[0] && `_orderByIndex` in array[0]) {\n ;(array as Array<T & { _orderByIndex: number }>).sort((a, b) => {\n if (a._orderByIndex === b._orderByIndex) {\n return 0\n }\n return a._orderByIndex < b._orderByIndex ? -1 : 1\n })\n }\n return array\n },\n deps: [this.derivedState],\n })\n this.derivedArray.mount()\n\n this.derivedChanges = new Derived({\n fn: ({\n currDepVals: [derivedState, optimisticOperations],\n prevDepVals,\n }) => {\n const prevDerivedState = prevDepVals?.[0] ?? new Map<string, T>()\n const prevOptimisticOperations = prevDepVals?.[1] ?? []\n const changedKeys = new Set(this.syncedKeys)\n optimisticOperations\n .flat()\n .filter((op) => op.isActive)\n .forEach((op) => changedKeys.add(op.key))\n prevOptimisticOperations.flat().forEach((op) => {\n changedKeys.add(op.key)\n })\n\n if (changedKeys.size === 0) {\n return []\n }\n\n const changes: Array<ChangeMessage<T>> = []\n for (const key of changedKeys) {\n if (prevDerivedState.has(key) && !derivedState.has(key)) {\n changes.push({\n type: `delete`,\n key,\n value: prevDerivedState.get(key)!,\n })\n } else if (!prevDerivedState.has(key) && derivedState.has(key)) {\n changes.push({ type: `insert`, key, value: derivedState.get(key)! })\n } else if (prevDerivedState.has(key) && derivedState.has(key)) {\n const value = derivedState.get(key)!\n const previousValue = prevDerivedState.get(key)\n if (value !== previousValue) {\n // Comparing objects by reference as records are not mutated\n changes.push({\n type: `update`,\n key,\n value,\n previousValue,\n })\n }\n }\n }\n\n this.syncedKeys.clear()\n\n return changes\n },\n deps: [this.derivedState, this.optimisticOperations],\n })\n this.derivedChanges.mount()\n\n this.config = config\n\n this.derivedState.mount()\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.generateObjectKey(\n this.config.getId(messageWithoutKey.value),\n messageWithoutKey.value\n )\n\n // Check if an item with this ID already exists when inserting\n if (messageWithoutKey.type === `insert`) {\n if (\n this.syncedData.state.has(key) &&\n !pendingTransaction.operations.some(\n (op) => op.key === key && op.type === `delete`\n )\n ) {\n const id = this.config.getId(messageWithoutKey.value)\n throw new Error(\n `Cannot insert document with ID \"${id}\" from sync because it already exists in the collection \"${this.id}\"`\n )\n }\n }\n\n const message: ChangeMessage<T> = {\n ...messageWithoutKey,\n key,\n }\n pendingTransaction.operations.push(message)\n },\n commit: () => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to commit`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't commit it again.`\n )\n }\n\n pendingTransaction.committed = true\n\n this.commitPendingTransactions()\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.state.values()).some(\n ({ state }) => state === `persisting`\n )\n ) {\n batch(() => {\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n this.syncedKeys.add(operation.key)\n this.syncedMetadata.setState((prevData) => {\n switch (operation.type) {\n case `insert`:\n prevData.set(operation.key, operation.metadata)\n break\n case `update`:\n prevData.set(operation.key, {\n ...prevData.get(operation.key)!,\n ...operation.metadata,\n })\n break\n case `delete`:\n prevData.delete(operation.key)\n break\n }\n return prevData\n })\n this.syncedData.setState((prevData) => {\n switch (operation.type) {\n case `insert`:\n prevData.set(operation.key, operation.value)\n break\n case `update`:\n prevData.set(operation.key, {\n ...prevData.get(operation.key)!,\n ...operation.value,\n })\n break\n case `delete`:\n prevData.delete(operation.key)\n break\n }\n return prevData\n })\n }\n }\n })\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 private getKeyFromId(id: unknown): string {\n if (typeof id === `undefined`) {\n throw new Error(`id is undefined`)\n }\n if (typeof id === `string` && id.startsWith(`KEY::`)) {\n return id\n } else {\n // if it's not a string, then it's some other\n // primitive type and needs turned into a key.\n return this.generateObjectKey(id, null)\n }\n }\n\n public generateObjectKey(id: any, item: any): string {\n if (typeof id === `undefined`) {\n throw new Error(\n `An object was created without a defined id: ${JSON.stringify(item)}`\n )\n }\n\n return `KEY::${this.id}/${id}`\n }\n\n private validateData(\n data: unknown,\n type: `insert` | `update`,\n key?: string\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.state.get(key)\n\n if (\n existingData &&\n data &&\n typeof data === `object` &&\n typeof existingData === `object`\n ) {\n // Merge the update with the existing data\n const mergedData = { ...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 Transaction 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 transaction = getActiveTransaction()\n if (typeof transaction === `undefined`) {\n throw `no transaction found when calling collection.insert`\n }\n\n const items = Array.isArray(data) ? data : [data]\n const mutations: Array<PendingMutation<T>> = []\n\n // Handle keys - convert to array if string, or generate if not provided\n const keys: Array<unknown> = items.map((item) =>\n this.generateObjectKey(this.config.getId(item), item)\n )\n\n // Create mutations for each item\n items.forEach((item, index) => {\n // Validate the data against the schema if one exists\n const validatedData = this.validateData(item, `insert`)\n const key = keys[index]!\n\n // Check if an item with this ID already exists in the collection\n const id = this.config.getId(item)\n if (this.state.has(this.getKeyFromId(id))) {\n throw `Cannot insert document with ID \"${id}\" because it already exists in the collection`\n }\n\n const mutation: PendingMutation<T> = {\n mutationId: crypto.randomUUID(),\n original: {},\n modified: validatedData as Record<string, unknown>,\n changes: validatedData as Record<string, unknown>,\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 transaction.applyMutations(mutations)\n\n this.transactions.setState((sortedMap) => {\n sortedMap.set(transaction.id, transaction)\n return sortedMap\n })\n\n return transaction\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 update<TItem extends object = T>(\n id: unknown,\n configOrCallback: ((draft: TItem) => void) | OperationConfig,\n maybeCallback?: (draft: TItem) => void\n ): Transaction\n\n update<TItem extends object = T>(\n ids: Array<unknown>,\n configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: Array<TItem>) => void\n ): Transaction\n\n update<TItem extends object = T>(\n ids: unknown | Array<unknown>,\n configOrCallback: ((draft: TItem | Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: TItem | Array<TItem>) => void\n ) {\n if (typeof ids === `undefined`) {\n throw new Error(`The first argument to update is missing`)\n }\n\n const transaction = getActiveTransaction()\n if (typeof transaction === `undefined`) {\n throw `no transaction found when calling collection.update`\n }\n\n const isArray = Array.isArray(ids)\n const idsArray = (Array.isArray(ids) ? ids : [ids]).map((id) =>\n this.getKeyFromId(id)\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 = idsArray.map((id) => {\n const item = this.state.get(id)\n if (!item) {\n throw new Error(\n `The id \"${id}\" was passed to update but an object for this ID 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] as TItem,\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>> = idsArray\n .map((id, 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 id\n )\n\n // Construct the full modified item by applying the validated update payload to the original item\n const modifiedItem = { ...originalItem, ...validatedUpdatePayload }\n\n // Check if the ID of the item is being changed\n const originalItemId = this.config.getId(originalItem)\n const modifiedItemId = this.config.getId(modifiedItem)\n\n if (originalItemId !== modifiedItemId) {\n throw new Error(\n `Updating the ID of an item is not allowed. Original ID: \"${originalItemId}\", Attempted new ID: \"${modifiedItemId}\". Please delete the old item and create a new one if an ID change is necessary.`\n )\n }\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 key: id,\n metadata: config.metadata as unknown,\n syncMetadata: (this.syncedMetadata.state.get(id) || {}) 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 transaction.applyMutations(mutations)\n\n this.transactions.setState((sortedMap) => {\n sortedMap.set(transaction.id, transaction)\n return sortedMap\n })\n\n return transaction\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 Transaction 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 = (ids: Array<string> | string, config?: OperationConfig) => {\n const transaction = getActiveTransaction()\n if (typeof transaction === `undefined`) {\n throw `no transaction found when calling collection.delete`\n }\n\n const idsArray = (Array.isArray(ids) ? ids : [ids]).map((id) =>\n this.getKeyFromId(id)\n )\n const mutations: Array<PendingMutation<T>> = []\n\n for (const id of idsArray) {\n const mutation: PendingMutation<T> = {\n mutationId: crypto.randomUUID(),\n original: (this.state.get(id) || {}) as Record<string, unknown>,\n modified: (this.state.get(id) || {}) as Record<string, unknown>,\n changes: (this.state.get(id) || {}) as Record<string, unknown>,\n key: id,\n metadata: config?.metadata as unknown,\n syncMetadata: (this.syncedMetadata.state.get(id) || {}) 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 transaction.applyMutations(mutations)\n\n this.transactions.setState((sortedMap) => {\n sortedMap.set(transaction.id, transaction)\n return sortedMap\n })\n\n return transaction\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 return this.derivedState.state\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<string, T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.state.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<string, T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.state)\n })\n })\n }\n\n /**\n * Gets the current state of the collection as an Array\n *\n * @returns An Array containing all items in the collection\n */\n get toArray() {\n return this.derivedArray.state\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.toArray.length > 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 [...this.state.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 ): () => void {\n // First send the current state as changes\n callback(this.currentStateAsChanges())\n\n // Then subscribe to changes, this returns an unsubscribe function\n return this.derivedChanges.subscribe((changes) => {\n if (changes.currentVal.length > 0) {\n callback(changes.currentVal)\n }\n })\n }\n}\n"],"names":["Store","batch","config","getActiveTransaction","SortedMap","Derived","transactions","result","withArrayChangeTracking","withChangeTracking"],"mappings":"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"}
1
+ {"version":3,"file":"collection.cjs","sources":["../../src/collection.ts"],"sourcesContent":["import { Derived, Store, batch } from \"@tanstack/store\"\nimport { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { Transaction, getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\nimport type {\n ChangeMessage,\n CollectionConfig,\n InsertConfig,\n OperationConfig,\n OptimisticChangeMessage,\n PendingMutation,\n StandardSchema,\n Transaction as TransactionType,\n} from \"./types\"\n\n// Store collections in memory using Tanstack store\nexport const collectionsStore = new Store(new Map<string, Collection<any>>())\n\n// Map to track loading collections\n\nconst loadingCollections = new Map<\n string,\n Promise<Collection<Record<string, unknown>>>\n>()\n\ninterface PendingSyncedTransaction<T extends object = Record<string, unknown>> {\n committed: boolean\n operations: Array<OptimisticChangeMessage<T>>\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 * @param config - Configuration for the collection, including id and sync\n * @returns A new Collection instance\n */\nexport function createCollection<T extends object = Record<string, unknown>>(\n config: CollectionConfig<T>\n): Collection<T> {\n return new Collection<T>(config)\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<T extends object = Record<string, unknown>>(\n config: CollectionConfig<T>\n): Promise<Collection<T>> {\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.state.has(config.id) &&\n !loadingCollections.has(config.id)\n ) {\n return Promise.resolve(\n collectionsStore.state.get(config.id)! as Collection<T>\n )\n }\n\n // If the collection is in the process of loading, return its promise\n if (loadingCollections.has(config.id)) {\n return loadingCollections.get(config.id)! as Promise<Collection<T>>\n }\n\n // Create a new collection instance if it doesn't exist\n if (!collectionsStore.state.has(config.id)) {\n collectionsStore.setState((prev) => {\n const next = new Map(prev)\n if (!config.id) {\n throw new Error(`The id property is required for preloadCollection`)\n }\n next.set(\n config.id,\n new Collection<T>({\n id: config.id,\n getId: config.getId,\n sync: config.sync,\n schema: config.schema,\n })\n )\n return next\n })\n }\n\n const collection = collectionsStore.state.get(config.id)! as Collection<T>\n\n // Create a promise that will resolve after the first commit\n let resolveFirstCommit: () => void\n const firstCommitPromise = new Promise<Collection<T>>((resolve) => {\n resolveFirstCommit = () => {\n resolve(collection)\n }\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 (loadingCollections.has(config.id)) {\n loadingCollections.delete(config.id)\n resolveFirstCommit()\n }\n })\n\n // Store the loading promise\n loadingCollections.set(\n config.id,\n firstCommitPromise as Promise<Collection<Record<string, unknown>>>\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 Collection<T extends object = Record<string, unknown>> {\n public transactions: Store<SortedMap<string, TransactionType>>\n public optimisticOperations: Derived<Array<OptimisticChangeMessage<T>>>\n public derivedState: Derived<Map<string, T>>\n public derivedArray: Derived<Array<T>>\n public derivedChanges: Derived<Array<ChangeMessage<T>>>\n public syncedData = new Store<Map<string, T>>(new Map())\n public syncedMetadata = new Store(new Map<string, unknown>())\n private pendingSyncedTransactions: Array<PendingSyncedTransaction<T>> = []\n private syncedKeys = new Set<string>()\n public config: CollectionConfig<T>\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>) {\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 Store(\n new SortedMap<string, TransactionType>(\n (a, b) => a.createdAt.getTime() - b.createdAt.getTime()\n )\n )\n\n // Copies of live mutations are stored here and removed once the transaction completes.\n this.optimisticOperations = new Derived({\n fn: ({ currDepVals: [transactions] }) => {\n const result = Array.from(transactions.values())\n .map((transaction) => {\n const isActive = ![`completed`, `failed`].includes(\n transaction.state\n )\n return transaction.mutations\n .filter((mutation) => mutation.collection === this)\n .map((mutation) => {\n const message: OptimisticChangeMessage<T> = {\n type: mutation.type,\n key: mutation.key,\n value: mutation.modified as T,\n isActive,\n }\n if (\n mutation.metadata !== undefined &&\n mutation.metadata !== null\n ) {\n message.metadata = mutation.metadata as Record<\n string,\n unknown\n >\n }\n return message\n })\n })\n .flat()\n\n return result\n },\n deps: [this.transactions],\n })\n this.optimisticOperations.mount()\n\n // Combine together synced data & optimistic operations.\n this.derivedState = new Derived({\n fn: ({ currDepVals: [syncedData, operations] }) => {\n const combined = new Map<string, T>(syncedData)\n\n // Apply the optimistic operations on top of the synced state.\n for (const operation of operations) {\n if (operation.isActive) {\n switch (operation.type) {\n case `insert`:\n combined.set(operation.key, operation.value)\n break\n case `update`:\n combined.set(operation.key, operation.value)\n break\n case `delete`:\n combined.delete(operation.key)\n break\n }\n }\n }\n\n return combined\n },\n deps: [this.syncedData, this.optimisticOperations],\n })\n\n // Create a derived array from the map to avoid recalculating it\n this.derivedArray = new Derived({\n fn: ({ currDepVals: [stateMap] }) => {\n // Collections returned by a query that has an orderBy are annotated\n // with the _orderByIndex field.\n // This is used to sort the array when it's derived.\n const array: Array<T & { _orderByIndex?: number }> = Array.from(\n stateMap.values()\n )\n if (array[0] && `_orderByIndex` in array[0]) {\n ;(array as Array<T & { _orderByIndex: number }>).sort((a, b) => {\n if (a._orderByIndex === b._orderByIndex) {\n return 0\n }\n return a._orderByIndex < b._orderByIndex ? -1 : 1\n })\n }\n return array\n },\n deps: [this.derivedState],\n })\n this.derivedArray.mount()\n\n this.derivedChanges = new Derived({\n fn: ({\n currDepVals: [derivedState, optimisticOperations],\n prevDepVals,\n }) => {\n const prevDerivedState = prevDepVals?.[0] ?? new Map<string, T>()\n const prevOptimisticOperations = prevDepVals?.[1] ?? []\n const changedKeys = new Set(this.syncedKeys)\n optimisticOperations\n .flat()\n .filter((op) => op.isActive)\n .forEach((op) => changedKeys.add(op.key))\n prevOptimisticOperations.flat().forEach((op) => {\n changedKeys.add(op.key)\n })\n\n if (changedKeys.size === 0) {\n return []\n }\n\n const changes: Array<ChangeMessage<T>> = []\n for (const key of changedKeys) {\n if (prevDerivedState.has(key) && !derivedState.has(key)) {\n changes.push({\n type: `delete`,\n key,\n value: prevDerivedState.get(key)!,\n })\n } else if (!prevDerivedState.has(key) && derivedState.has(key)) {\n changes.push({ type: `insert`, key, value: derivedState.get(key)! })\n } else if (prevDerivedState.has(key) && derivedState.has(key)) {\n const value = derivedState.get(key)!\n const previousValue = prevDerivedState.get(key)\n if (value !== previousValue) {\n // Comparing objects by reference as records are not mutated\n changes.push({\n type: `update`,\n key,\n value,\n previousValue,\n })\n }\n }\n }\n\n this.syncedKeys.clear()\n\n return changes\n },\n deps: [this.derivedState, this.optimisticOperations],\n })\n this.derivedChanges.mount()\n\n this.config = config\n\n this.derivedState.mount()\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.generateObjectKey(\n this.config.getId(messageWithoutKey.value),\n messageWithoutKey.value\n )\n\n // Check if an item with this ID already exists when inserting\n if (messageWithoutKey.type === `insert`) {\n if (\n this.syncedData.state.has(key) &&\n !pendingTransaction.operations.some(\n (op) => op.key === key && op.type === `delete`\n )\n ) {\n const id = this.config.getId(messageWithoutKey.value)\n throw new Error(\n `Cannot insert document with ID \"${id}\" from sync because it already exists in the collection \"${this.id}\"`\n )\n }\n }\n\n const message: ChangeMessage<T> = {\n ...messageWithoutKey,\n key,\n }\n pendingTransaction.operations.push(message)\n },\n commit: () => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to commit`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't commit it again.`\n )\n }\n\n pendingTransaction.committed = true\n\n this.commitPendingTransactions()\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.state.values()).some(\n ({ state }) => state === `persisting`\n )\n ) {\n batch(() => {\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n this.syncedKeys.add(operation.key)\n this.syncedMetadata.setState((prevData) => {\n switch (operation.type) {\n case `insert`:\n prevData.set(operation.key, operation.metadata)\n break\n case `update`:\n prevData.set(operation.key, {\n ...prevData.get(operation.key)!,\n ...operation.metadata,\n })\n break\n case `delete`:\n prevData.delete(operation.key)\n break\n }\n return prevData\n })\n this.syncedData.setState((prevData) => {\n switch (operation.type) {\n case `insert`:\n prevData.set(operation.key, operation.value)\n break\n case `update`:\n prevData.set(operation.key, {\n ...prevData.get(operation.key)!,\n ...operation.value,\n })\n break\n case `delete`:\n prevData.delete(operation.key)\n break\n }\n return prevData\n })\n }\n }\n })\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 private getKeyFromId(id: unknown): string {\n if (typeof id === `undefined`) {\n throw new Error(`id is undefined`)\n }\n if (typeof id === `string` && id.startsWith(`KEY::`)) {\n return id\n } else {\n // if it's not a string, then it's some other\n // primitive type and needs turned into a key.\n return this.generateObjectKey(id, null)\n }\n }\n\n public generateObjectKey(id: any, item: any): string {\n if (typeof id === `undefined`) {\n throw new Error(\n `An object was created without a defined id: ${JSON.stringify(item)}`\n )\n }\n\n return `KEY::${this.id}/${id}`\n }\n\n private validateData(\n data: unknown,\n type: `insert` | `update`,\n key?: string\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.state.get(key)\n\n if (\n existingData &&\n data &&\n typeof data === `object` &&\n typeof existingData === `object`\n ) {\n // Merge the update with the existing data\n const mergedData = { ...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 // Handle keys - convert to array if string, or generate if not provided\n const keys: Array<unknown> = items.map((item) =>\n this.generateObjectKey(this.config.getId(item), item)\n )\n\n // Create mutations for each item\n items.forEach((item, index) => {\n // Validate the data against the schema if one exists\n const validatedData = this.validateData(item, `insert`)\n const key = keys[index]!\n\n // Check if an item with this ID already exists in the collection\n const id = this.config.getId(item)\n if (this.state.has(this.getKeyFromId(id))) {\n throw `Cannot insert document with ID \"${id}\" because it already exists in the collection`\n }\n\n const mutation: PendingMutation<T> = {\n mutationId: crypto.randomUUID(),\n original: {},\n modified: validatedData as Record<string, unknown>,\n changes: validatedData as Record<string, unknown>,\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.setState((sortedMap) => {\n sortedMap.set(ambientTransaction.id, ambientTransaction)\n return sortedMap\n })\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({\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.setState((sortedMap) => {\n sortedMap.set(directOpTransaction.id, directOpTransaction)\n return sortedMap\n })\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 update<TItem extends object = T>(\n id: unknown,\n configOrCallback: ((draft: TItem) => void) | OperationConfig,\n maybeCallback?: (draft: TItem) => void\n ): TransactionType\n\n update<TItem extends object = T>(\n ids: Array<unknown>,\n configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: Array<TItem>) => void\n ): TransactionType\n\n update<TItem extends object = T>(\n ids: unknown | Array<unknown>,\n configOrCallback: ((draft: TItem | Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: TItem | Array<TItem>) => void\n ) {\n if (typeof ids === `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(ids)\n const idsArray = (Array.isArray(ids) ? ids : [ids]).map((id) =>\n this.getKeyFromId(id)\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 = idsArray.map((id) => {\n const item = this.state.get(id)\n if (!item) {\n throw new Error(\n `The id \"${id}\" was passed to update but an object for this ID 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] as TItem,\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>> = idsArray\n .map((id, 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 id\n )\n\n // Construct the full modified item by applying the validated update payload to the original item\n const modifiedItem = { ...originalItem, ...validatedUpdatePayload }\n\n // Check if the ID of the item is being changed\n const originalItemId = this.config.getId(originalItem)\n const modifiedItemId = this.config.getId(modifiedItem)\n\n if (originalItemId !== modifiedItemId) {\n throw new Error(\n `Updating the ID of an item is not allowed. Original ID: \"${originalItemId}\", Attempted new ID: \"${modifiedItemId}\". Please delete the old item and create a new one if an ID change is necessary.`\n )\n }\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 key: id,\n metadata: config.metadata as unknown,\n syncMetadata: (this.syncedMetadata.state.get(id) || {}) 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.setState((sortedMap) => {\n sortedMap.set(ambientTransaction.id, ambientTransaction)\n return sortedMap\n })\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({\n mutationFn: async (transaction) => {\n // Call the onUpdate handler with the transaction\n return this.config.onUpdate!(transaction)\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.setState((sortedMap) => {\n sortedMap.set(directOpTransaction.id, directOpTransaction)\n return sortedMap\n })\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 ids: Array<string> | string,\n config?: OperationConfig\n ): TransactionType => {\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 const idsArray = (Array.isArray(ids) ? ids : [ids]).map((id) =>\n this.getKeyFromId(id)\n )\n const mutations: Array<PendingMutation<T>> = []\n\n for (const id of idsArray) {\n const mutation: PendingMutation<T> = {\n mutationId: crypto.randomUUID(),\n original: (this.state.get(id) || {}) as Record<string, unknown>,\n modified: (this.state.get(id) || {}) as Record<string, unknown>,\n changes: (this.state.get(id) || {}) as Record<string, unknown>,\n key: id,\n metadata: config?.metadata as unknown,\n syncMetadata: (this.syncedMetadata.state.get(id) || {}) 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.setState((sortedMap) => {\n sortedMap.set(ambientTransaction.id, ambientTransaction)\n return sortedMap\n })\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({\n autoCommit: true,\n mutationFn: async (transaction) => {\n // Call the onDelete handler with the transaction\n return this.config.onDelete!(transaction)\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.setState((sortedMap) => {\n sortedMap.set(directOpTransaction.id, directOpTransaction)\n return sortedMap\n })\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 return this.derivedState.state\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<string, T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.state.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<string, T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.state)\n })\n })\n }\n\n /**\n * Gets the current state of the collection as an Array\n *\n * @returns An Array containing all items in the collection\n */\n get toArray() {\n return this.derivedArray.state\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.toArray.length > 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 [...this.state.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 ): () => void {\n // First send the current state as changes\n callback(this.currentStateAsChanges())\n\n // Then subscribe to changes, this returns an unsubscribe function\n return this.derivedChanges.subscribe((changes) => {\n if (changes.currentVal.length > 0) {\n callback(changes.currentVal)\n }\n })\n }\n}\n"],"names":["Store","batch","config","getActiveTransaction","Transaction","SortedMap","Derived","transactions","result","withArrayChangeTracking","withChangeTracking"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { Derived, Store } from '@tanstack/store';
2
+ import { Transaction } from './transactions.cjs';
2
3
  import { SortedMap } from './SortedMap.cjs';
3
- import { ChangeMessage, CollectionConfig, InsertConfig, OperationConfig, OptimisticChangeMessage, Transaction } from './types.cjs';
4
+ import { ChangeMessage, CollectionConfig, InsertConfig, OperationConfig, OptimisticChangeMessage, Transaction as TransactionType } from './types.cjs';
4
5
  export declare const collectionsStore: Store<Map<string, Collection<any>>, (cb: Map<string, Collection<any>>) => Map<string, Collection<any>>>;
5
6
  /**
6
7
  * Creates a new Collection instance with the given configuration
@@ -52,7 +53,7 @@ export declare class SchemaValidationError extends Error {
52
53
  }>, message?: string);
53
54
  }
54
55
  export declare class Collection<T extends object = Record<string, unknown>> {
55
- transactions: Store<SortedMap<string, Transaction>>;
56
+ transactions: Store<SortedMap<string, TransactionType>>;
56
57
  optimisticOperations: Derived<Array<OptimisticChangeMessage<T>>>;
57
58
  derivedState: Derived<Map<string, T>>;
58
59
  derivedArray: Derived<Array<T>>;
@@ -91,7 +92,7 @@ export declare class Collection<T extends object = Record<string, unknown>> {
91
92
  * Inserts one or more items into the collection
92
93
  * @param items - Single item or array of items to insert
93
94
  * @param config - Optional configuration including metadata and custom keys
94
- * @returns A Transaction object representing the insert operation(s)
95
+ * @returns A TransactionType object representing the insert operation(s)
95
96
  * @throws {SchemaValidationError} If the data fails schema validation
96
97
  * @example
97
98
  * // Insert a single item
@@ -145,13 +146,13 @@ export declare class Collection<T extends object = Record<string, unknown>> {
145
146
  * // Update with metadata
146
147
  * update("todo-1", { metadata: { reason: "user update" } }, (draft) => { draft.text = "Updated text" })
147
148
  */
148
- update<TItem extends object = T>(id: unknown, configOrCallback: ((draft: TItem) => void) | OperationConfig, maybeCallback?: (draft: TItem) => void): Transaction;
149
- update<TItem extends object = T>(ids: Array<unknown>, configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig, maybeCallback?: (draft: Array<TItem>) => void): Transaction;
149
+ update<TItem extends object = T>(id: unknown, configOrCallback: ((draft: TItem) => void) | OperationConfig, maybeCallback?: (draft: TItem) => void): TransactionType;
150
+ update<TItem extends object = T>(ids: Array<unknown>, configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig, maybeCallback?: (draft: Array<TItem>) => void): TransactionType;
150
151
  /**
151
152
  * Deletes one or more items from the collection
152
153
  * @param ids - Single ID or array of IDs to delete
153
154
  * @param config - Optional configuration including metadata
154
- * @returns A Transaction object representing the delete operation(s)
155
+ * @returns A TransactionType object representing the delete operation(s)
155
156
  * @example
156
157
  * // Delete a single item
157
158
  * delete("todo-1")
@@ -162,7 +163,7 @@ export declare class Collection<T extends object = Record<string, unknown>> {
162
163
  * // Delete with metadata
163
164
  * delete("todo-1", { metadata: { reason: "completed" } })
164
165
  */
165
- delete: (ids: Array<string> | string, config?: OperationConfig) => Transaction;
166
+ delete: (ids: Array<string> | string, config?: OperationConfig) => TransactionType;
166
167
  /**
167
168
  * Gets the current state of the collection as a Map
168
169
  *
@@ -2,6 +2,20 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const extractors = require("./extractors.cjs");
4
4
  const utils = require("./utils.cjs");
5
+ function evaluateWhereOnNamespacedRow(namespacedRow, where, mainTableAlias, joinedTableAlias) {
6
+ return where.every((item) => {
7
+ if (typeof item === `function`) {
8
+ return item(namespacedRow);
9
+ } else {
10
+ return evaluateConditionOnNamespacedRow(
11
+ namespacedRow,
12
+ item,
13
+ mainTableAlias,
14
+ joinedTableAlias
15
+ );
16
+ }
17
+ });
18
+ }
5
19
  function evaluateConditionOnNamespacedRow(namespacedRow, condition, mainTableAlias, joinedTableAlias) {
6
20
  if (condition.length === 3 && !Array.isArray(condition[0])) {
7
21
  const [left, comparator, right] = condition;
@@ -143,4 +157,5 @@ function evaluateSimpleConditionOnNamespacedRow(namespacedRow, left, comparator,
143
157
  }
144
158
  exports.evaluateConditionOnNamespacedRow = evaluateConditionOnNamespacedRow;
145
159
  exports.evaluateSimpleConditionOnNamespacedRow = evaluateSimpleConditionOnNamespacedRow;
160
+ exports.evaluateWhereOnNamespacedRow = evaluateWhereOnNamespacedRow;
146
161
  //# sourceMappingURL=evaluators.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"evaluators.cjs","sources":["../../../src/query/evaluators.ts"],"sourcesContent":["import { evaluateOperandOnNamespacedRow } from \"./extractors.js\"\nimport { compareValues, convertLikeToRegex, isValueInArray } from \"./utils.js\"\nimport type {\n Comparator,\n Condition,\n ConditionOperand,\n LogicalOperator,\n SimpleCondition,\n} from \"./schema.js\"\nimport type { NamespacedRow } from \"../types.js\"\n\n/**\n * Evaluates a condition against a nested row structure\n */\nexport function evaluateConditionOnNamespacedRow(\n namespacedRow: NamespacedRow,\n condition: Condition,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): boolean {\n // Handle simple conditions with exactly 3 elements\n if (condition.length === 3 && !Array.isArray(condition[0])) {\n const [left, comparator, right] = condition as SimpleCondition\n return evaluateSimpleConditionOnNamespacedRow(\n namespacedRow,\n left,\n comparator,\n right,\n mainTableAlias,\n joinedTableAlias\n )\n }\n\n // Handle flat composite conditions (multiple conditions in a single array)\n if (\n condition.length > 3 &&\n !Array.isArray(condition[0]) &&\n typeof condition[1] === `string` &&\n ![`and`, `or`].includes(condition[1] as string)\n ) {\n // Start with the first condition (first 3 elements)\n let result = evaluateSimpleConditionOnNamespacedRow(\n namespacedRow,\n condition[0],\n condition[1] as Comparator,\n condition[2],\n mainTableAlias,\n joinedTableAlias\n )\n\n // Process the rest in groups: logical operator, then 3 elements for each condition\n for (let i = 3; i < condition.length; i += 4) {\n const logicalOp = condition[i] as LogicalOperator\n\n // Make sure we have a complete condition to evaluate\n if (i + 3 <= condition.length) {\n const nextResult = evaluateSimpleConditionOnNamespacedRow(\n namespacedRow,\n condition[i + 1],\n condition[i + 2] as Comparator,\n condition[i + 3],\n mainTableAlias,\n joinedTableAlias\n )\n\n // Apply the logical operator\n if (logicalOp === `and`) {\n result = result && nextResult\n } else {\n // logicalOp === `or`\n result = result || nextResult\n }\n }\n }\n\n return result\n }\n\n // Handle nested composite conditions where the first element is an array\n if (condition.length > 0 && Array.isArray(condition[0])) {\n // Start with the first condition\n let result = evaluateConditionOnNamespacedRow(\n namespacedRow,\n condition[0] as Condition,\n mainTableAlias,\n joinedTableAlias\n )\n\n // Process the rest of the conditions and logical operators in pairs\n for (let i = 1; i < condition.length; i += 2) {\n if (i + 1 >= condition.length) break // Make sure we have a pair\n\n const operator = condition[i] as LogicalOperator\n const nextCondition = condition[i + 1] as Condition\n\n // Apply the logical operator\n if (operator === `and`) {\n result =\n result &&\n evaluateConditionOnNamespacedRow(\n namespacedRow,\n nextCondition,\n mainTableAlias,\n joinedTableAlias\n )\n } else {\n // logicalOp === `or`\n result =\n result ||\n evaluateConditionOnNamespacedRow(\n namespacedRow,\n nextCondition,\n mainTableAlias,\n joinedTableAlias\n )\n }\n }\n\n return result\n }\n\n // Fallback - this should not happen with valid conditions\n return true\n}\n\n/**\n * Evaluates a simple condition against a nested row structure\n */\nexport function evaluateSimpleConditionOnNamespacedRow(\n namespacedRow: Record<string, unknown>,\n left: ConditionOperand,\n comparator: Comparator,\n right: ConditionOperand,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): boolean {\n const leftValue = evaluateOperandOnNamespacedRow(\n namespacedRow,\n left,\n mainTableAlias,\n joinedTableAlias\n )\n\n const rightValue = evaluateOperandOnNamespacedRow(\n namespacedRow,\n right,\n mainTableAlias,\n joinedTableAlias\n )\n\n // The rest of the function remains the same as evaluateSimpleCondition\n switch (comparator) {\n case `=`:\n return leftValue === rightValue\n case `!=`:\n return leftValue !== rightValue\n case `<`:\n return compareValues(leftValue, rightValue, `<`)\n case `<=`:\n return compareValues(leftValue, rightValue, `<=`)\n case `>`:\n return compareValues(leftValue, rightValue, `>`)\n case `>=`:\n return compareValues(leftValue, rightValue, `>=`)\n case `like`:\n case `not like`:\n if (typeof leftValue === `string` && typeof rightValue === `string`) {\n // Convert SQL LIKE pattern to proper regex pattern\n const pattern = convertLikeToRegex(rightValue)\n const matches = new RegExp(`^${pattern}$`, `i`).test(leftValue)\n return comparator === `like` ? matches : !matches\n }\n return comparator === `like` ? false : true\n case `in`:\n // If right value is not an array, we can't do an IN operation\n if (!Array.isArray(rightValue)) {\n return false\n }\n\n // For empty arrays, nothing is contained in them\n if (rightValue.length === 0) {\n return false\n }\n\n // Handle array-to-array comparison (check if any element in leftValue exists in rightValue)\n if (Array.isArray(leftValue)) {\n return leftValue.some((item) => isValueInArray(item, rightValue))\n }\n\n // Handle single value comparison\n return isValueInArray(leftValue, rightValue)\n\n case `not in`:\n // If right value is not an array, everything is \"not in\" it\n if (!Array.isArray(rightValue)) {\n return true\n }\n\n // For empty arrays, everything is \"not in\" them\n if (rightValue.length === 0) {\n return true\n }\n\n // Handle array-to-array comparison (check if no element in leftValue exists in rightValue)\n if (Array.isArray(leftValue)) {\n return !leftValue.some((item) => isValueInArray(item, rightValue))\n }\n\n // Handle single value comparison\n return !isValueInArray(leftValue, rightValue)\n\n case `is`:\n return leftValue === rightValue\n case `is not`:\n // Properly handle null/undefined checks\n if (rightValue === null) {\n return leftValue !== null && leftValue !== undefined\n }\n return leftValue !== rightValue\n default:\n return false\n }\n}\n"],"names":["evaluateOperandOnNamespacedRow","compareValues","convertLikeToRegex","isValueInArray"],"mappings":"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"}
1
+ {"version":3,"file":"evaluators.cjs","sources":["../../../src/query/evaluators.ts"],"sourcesContent":["import { evaluateOperandOnNamespacedRow } from \"./extractors.js\"\nimport { compareValues, convertLikeToRegex, isValueInArray } from \"./utils.js\"\nimport type {\n Comparator,\n Condition,\n ConditionOperand,\n LogicalOperator,\n SimpleCondition,\n Where,\n WhereCallback,\n} from \"./schema.js\"\nimport type { NamespacedRow } from \"../types.js\"\n\n/**\n * Evaluates a Where clause (which is always an array of conditions and/or callbacks) against a nested row structure\n */\nexport function evaluateWhereOnNamespacedRow(\n namespacedRow: NamespacedRow,\n where: Where,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): boolean {\n // Where is always an array of conditions and/or callbacks\n // Evaluate all items and combine with AND logic\n return where.every((item) => {\n if (typeof item === `function`) {\n return (item as WhereCallback)(namespacedRow)\n } else {\n return evaluateConditionOnNamespacedRow(\n namespacedRow,\n item as Condition,\n mainTableAlias,\n joinedTableAlias\n )\n }\n })\n}\n\n/**\n * Evaluates a condition against a nested row structure\n */\nexport function evaluateConditionOnNamespacedRow(\n namespacedRow: NamespacedRow,\n condition: Condition,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): boolean {\n // Handle simple conditions with exactly 3 elements\n if (condition.length === 3 && !Array.isArray(condition[0])) {\n const [left, comparator, right] = condition as SimpleCondition\n return evaluateSimpleConditionOnNamespacedRow(\n namespacedRow,\n left,\n comparator,\n right,\n mainTableAlias,\n joinedTableAlias\n )\n }\n\n // Handle flat composite conditions (multiple conditions in a single array)\n if (\n condition.length > 3 &&\n !Array.isArray(condition[0]) &&\n typeof condition[1] === `string` &&\n ![`and`, `or`].includes(condition[1] as string)\n ) {\n // Start with the first condition (first 3 elements)\n let result = evaluateSimpleConditionOnNamespacedRow(\n namespacedRow,\n condition[0],\n condition[1] as Comparator,\n condition[2],\n mainTableAlias,\n joinedTableAlias\n )\n\n // Process the rest in groups: logical operator, then 3 elements for each condition\n for (let i = 3; i < condition.length; i += 4) {\n const logicalOp = condition[i] as LogicalOperator\n\n // Make sure we have a complete condition to evaluate\n if (i + 3 <= condition.length) {\n const nextResult = evaluateSimpleConditionOnNamespacedRow(\n namespacedRow,\n condition[i + 1],\n condition[i + 2] as Comparator,\n condition[i + 3],\n mainTableAlias,\n joinedTableAlias\n )\n\n // Apply the logical operator\n if (logicalOp === `and`) {\n result = result && nextResult\n } else {\n // logicalOp === `or`\n result = result || nextResult\n }\n }\n }\n\n return result\n }\n\n // Handle nested composite conditions where the first element is an array\n if (condition.length > 0 && Array.isArray(condition[0])) {\n // Start with the first condition\n let result = evaluateConditionOnNamespacedRow(\n namespacedRow,\n condition[0] as Condition,\n mainTableAlias,\n joinedTableAlias\n )\n\n // Process the rest of the conditions and logical operators in pairs\n for (let i = 1; i < condition.length; i += 2) {\n if (i + 1 >= condition.length) break // Make sure we have a pair\n\n const operator = condition[i] as LogicalOperator\n const nextCondition = condition[i + 1] as Condition\n\n // Apply the logical operator\n if (operator === `and`) {\n result =\n result &&\n evaluateConditionOnNamespacedRow(\n namespacedRow,\n nextCondition,\n mainTableAlias,\n joinedTableAlias\n )\n } else {\n // logicalOp === `or`\n result =\n result ||\n evaluateConditionOnNamespacedRow(\n namespacedRow,\n nextCondition,\n mainTableAlias,\n joinedTableAlias\n )\n }\n }\n\n return result\n }\n\n // Fallback - this should not happen with valid conditions\n return true\n}\n\n/**\n * Evaluates a simple condition against a nested row structure\n */\nexport function evaluateSimpleConditionOnNamespacedRow(\n namespacedRow: Record<string, unknown>,\n left: ConditionOperand,\n comparator: Comparator,\n right: ConditionOperand,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): boolean {\n const leftValue = evaluateOperandOnNamespacedRow(\n namespacedRow,\n left,\n mainTableAlias,\n joinedTableAlias\n )\n\n const rightValue = evaluateOperandOnNamespacedRow(\n namespacedRow,\n right,\n mainTableAlias,\n joinedTableAlias\n )\n\n // The rest of the function remains the same as evaluateSimpleCondition\n switch (comparator) {\n case `=`:\n return leftValue === rightValue\n case `!=`:\n return leftValue !== rightValue\n case `<`:\n return compareValues(leftValue, rightValue, `<`)\n case `<=`:\n return compareValues(leftValue, rightValue, `<=`)\n case `>`:\n return compareValues(leftValue, rightValue, `>`)\n case `>=`:\n return compareValues(leftValue, rightValue, `>=`)\n case `like`:\n case `not like`:\n if (typeof leftValue === `string` && typeof rightValue === `string`) {\n // Convert SQL LIKE pattern to proper regex pattern\n const pattern = convertLikeToRegex(rightValue)\n const matches = new RegExp(`^${pattern}$`, `i`).test(leftValue)\n return comparator === `like` ? matches : !matches\n }\n return comparator === `like` ? false : true\n case `in`:\n // If right value is not an array, we can't do an IN operation\n if (!Array.isArray(rightValue)) {\n return false\n }\n\n // For empty arrays, nothing is contained in them\n if (rightValue.length === 0) {\n return false\n }\n\n // Handle array-to-array comparison (check if any element in leftValue exists in rightValue)\n if (Array.isArray(leftValue)) {\n return leftValue.some((item) => isValueInArray(item, rightValue))\n }\n\n // Handle single value comparison\n return isValueInArray(leftValue, rightValue)\n\n case `not in`:\n // If right value is not an array, everything is \"not in\" it\n if (!Array.isArray(rightValue)) {\n return true\n }\n\n // For empty arrays, everything is \"not in\" them\n if (rightValue.length === 0) {\n return true\n }\n\n // Handle array-to-array comparison (check if no element in leftValue exists in rightValue)\n if (Array.isArray(leftValue)) {\n return !leftValue.some((item) => isValueInArray(item, rightValue))\n }\n\n // Handle single value comparison\n return !isValueInArray(leftValue, rightValue)\n\n case `is`:\n return leftValue === rightValue\n case `is not`:\n // Properly handle null/undefined checks\n if (rightValue === null) {\n return leftValue !== null && leftValue !== undefined\n }\n return leftValue !== rightValue\n default:\n return false\n }\n}\n"],"names":["evaluateOperandOnNamespacedRow","compareValues","convertLikeToRegex","isValueInArray"],"mappings":"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"}
@@ -1,5 +1,9 @@
1
- import { Comparator, Condition, ConditionOperand } from './schema.js';
1
+ import { Comparator, Condition, ConditionOperand, Where } from './schema.js';
2
2
  import { NamespacedRow } from '../types.js';
3
+ /**
4
+ * Evaluates a Where clause (which is always an array of conditions and/or callbacks) against a nested row structure
5
+ */
6
+ export declare function evaluateWhereOnNamespacedRow(namespacedRow: NamespacedRow, where: Where, mainTableAlias?: string, joinedTableAlias?: string): boolean;
3
7
  /**
4
8
  * Evaluates a condition against a nested row structure
5
9
  */
@@ -49,7 +49,7 @@ function compileQueryPipeline(query, inputs) {
49
49
  if (query.where) {
50
50
  pipeline = pipeline.pipe(
51
51
  d2ts.filter(([_key, row]) => {
52
- const result = evaluators.evaluateConditionOnNamespacedRow(
52
+ const result = evaluators.evaluateWhereOnNamespacedRow(
53
53
  row,
54
54
  query.where,
55
55
  mainTableAlias
@@ -64,7 +64,7 @@ function compileQueryPipeline(query, inputs) {
64
64
  if (query.having) {
65
65
  pipeline = pipeline.pipe(
66
66
  d2ts.filter(([_key, row]) => {
67
- const result = evaluators.evaluateConditionOnNamespacedRow(
67
+ const result = evaluators.evaluateWhereOnNamespacedRow(
68
68
  row,
69
69
  query.having,
70
70
  mainTableAlias
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline-compiler.cjs","sources":["../../../src/query/pipeline-compiler.ts"],"sourcesContent":["import { filter, map } from \"@electric-sql/d2ts\"\nimport { evaluateConditionOnNamespacedRow } from \"./evaluators.js\"\nimport { processJoinClause } from \"./joins.js\"\nimport { processGroupBy } from \"./group-by.js\"\nimport { processOrderBy } from \"./order-by.js\"\nimport { processSelect } from \"./select.js\"\nimport type { Query } from \"./schema.js\"\nimport type { IStreamBuilder } from \"@electric-sql/d2ts\"\nimport type {\n InputRow,\n KeyedStream,\n NamespacedAndKeyedStream,\n} from \"../types.js\"\n\n/**\n * Compiles a query into a D2 pipeline\n * @param query The query to compile\n * @param inputs Mapping of table names to input streams\n * @returns A stream builder representing the compiled query\n */\nexport function compileQueryPipeline<T extends IStreamBuilder<unknown>>(\n query: Query,\n inputs: Record<string, KeyedStream>\n): T {\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Process WITH queries if they exist\n if (query.with && query.with.length > 0) {\n // Process each WITH query in order\n for (const withQuery of query.with) {\n // Ensure the WITH query has an alias\n if (!withQuery.as) {\n throw new Error(`WITH query must have an \"as\" property`)\n }\n\n // Check if this CTE name already exists in the inputs\n if (allInputs[withQuery.as]) {\n throw new Error(`CTE with name \"${withQuery.as}\" already exists`)\n }\n\n // Create a new query without the 'with' property to avoid circular references\n const withQueryWithoutWith = { ...withQuery, with: undefined }\n\n // Compile the WITH query using the current set of inputs\n // (which includes previously compiled WITH queries)\n const compiledWithQuery = compileQueryPipeline(\n withQueryWithoutWith,\n allInputs\n )\n\n // Add the compiled query to the inputs map using its alias\n allInputs[withQuery.as] = compiledWithQuery as KeyedStream\n }\n }\n\n // Create a map of table aliases to inputs\n const tables: Record<string, KeyedStream> = {}\n\n // The main table is the one in the FROM clause\n const mainTableAlias = query.as || query.from\n\n // Get the main input from the inputs map (now including CTEs)\n const input = allInputs[query.from]\n if (!input) {\n throw new Error(`Input for table \"${query.from}\" not found in inputs map`)\n }\n\n tables[mainTableAlias] = input\n\n // Prepare the initial pipeline with the main table wrapped in its alias\n let pipeline: NamespacedAndKeyedStream = input.pipe(\n map(([key, row]) => {\n // Initialize the record with a nested structure\n const ret = [key, { [mainTableAlias]: row }] as [\n string,\n Record<string, typeof row>,\n ]\n return ret\n })\n )\n\n // Process JOIN clauses if they exist\n if (query.join) {\n pipeline = processJoinClause(\n pipeline,\n query,\n tables,\n mainTableAlias,\n allInputs\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where) {\n pipeline = pipeline.pipe(\n filter(([_key, row]) => {\n const result = evaluateConditionOnNamespacedRow(\n row,\n query.where!,\n mainTableAlias\n )\n return result\n })\n )\n }\n\n // Process the GROUP BY clause if it exists\n if (query.groupBy) {\n pipeline = processGroupBy(pipeline, query, mainTableAlias)\n }\n\n // Process the HAVING clause if it exists\n // This works similarly to WHERE but is applied after any aggregations\n if (query.having) {\n pipeline = pipeline.pipe(\n filter(([_key, row]) => {\n // For HAVING, we're working with the flattened row that contains both\n // the group by keys and the aggregate results directly\n const result = evaluateConditionOnNamespacedRow(\n row,\n query.having!,\n mainTableAlias\n )\n return result\n })\n )\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy) {\n pipeline = processOrderBy(pipeline, query, mainTableAlias)\n } else if (query.limit !== undefined || query.offset !== undefined) {\n // If there's a limit or offset without orderBy, throw an error\n throw new Error(\n `LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`\n )\n }\n\n // Process the SELECT clause - this is where we flatten the structure\n const resultPipeline: KeyedStream | NamespacedAndKeyedStream = query.select\n ? processSelect(pipeline, query, mainTableAlias, allInputs)\n : !query.join && !query.groupBy\n ? pipeline.pipe(\n map(([key, row]) => [key, row[mainTableAlias]] as InputRow)\n )\n : pipeline\n return resultPipeline as T\n}\n"],"names":["map","processJoinClause","filter","evaluateConditionOnNamespacedRow","processGroupBy","processOrderBy","processSelect"],"mappings":";;;;;;;;AAoBgB,SAAA,qBACd,OACA,QACG;AAEG,QAAA,YAAY,EAAE,GAAG,OAAO;AAG9B,MAAI,MAAM,QAAQ,MAAM,KAAK,SAAS,GAAG;AAE5B,eAAA,aAAa,MAAM,MAAM;AAE9B,UAAA,CAAC,UAAU,IAAI;AACX,cAAA,IAAI,MAAM,uCAAuC;AAAA,MAAA;AAIrD,UAAA,UAAU,UAAU,EAAE,GAAG;AAC3B,cAAM,IAAI,MAAM,kBAAkB,UAAU,EAAE,kBAAkB;AAAA,MAAA;AAIlE,YAAM,uBAAuB,EAAE,GAAG,WAAW,MAAM,OAAU;AAI7D,YAAM,oBAAoB;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AAGU,gBAAA,UAAU,EAAE,IAAI;AAAA,IAAA;AAAA,EAC5B;AAIF,QAAM,SAAsC,CAAC;AAGvC,QAAA,iBAAiB,MAAM,MAAM,MAAM;AAGnC,QAAA,QAAQ,UAAU,MAAM,IAAI;AAClC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,oBAAoB,MAAM,IAAI,2BAA2B;AAAA,EAAA;AAG3E,SAAO,cAAc,IAAI;AAGzB,MAAI,WAAqC,MAAM;AAAA,IAC7CA,KAAAA,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM;AAEZ,YAAA,MAAM,CAAC,KAAK,EAAE,CAAC,cAAc,GAAG,KAAK;AAIpC,aAAA;AAAA,IACR,CAAA;AAAA,EACH;AAGA,MAAI,MAAM,MAAM;AACH,eAAAC,MAAA;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EAAA;AAIF,MAAI,MAAM,OAAO;AACf,eAAW,SAAS;AAAA,MAClBC,KAAAA,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM;AACtB,cAAM,SAASC,WAAA;AAAA,UACb;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACF;AACO,eAAA;AAAA,MACR,CAAA;AAAA,IACH;AAAA,EAAA;AAIF,MAAI,MAAM,SAAS;AACN,eAAAC,QAAA,eAAe,UAAU,OAAO,cAAc;AAAA,EAAA;AAK3D,MAAI,MAAM,QAAQ;AAChB,eAAW,SAAS;AAAA,MAClBF,KAAAA,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM;AAGtB,cAAM,SAASC,WAAA;AAAA,UACb;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACF;AACO,eAAA;AAAA,MACR,CAAA;AAAA,IACH;AAAA,EAAA;AAIF,MAAI,MAAM,SAAS;AACN,eAAAE,QAAA,eAAe,UAAU,OAAO,cAAc;AAAA,EAAA,WAChD,MAAM,UAAU,UAAa,MAAM,WAAW,QAAW;AAElE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EAAA;AAIF,QAAM,iBAAyD,MAAM,SACjEC,OAAAA,cAAc,UAAU,OAAO,gBAAgB,SAAS,IACxD,CAAC,MAAM,QAAQ,CAAC,MAAM,UACpB,SAAS;AAAA,IACPN,SAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,cAAc,CAAC,CAAa;AAAA,EAAA,IAE5D;AACC,SAAA;AACT;;"}
1
+ {"version":3,"file":"pipeline-compiler.cjs","sources":["../../../src/query/pipeline-compiler.ts"],"sourcesContent":["import { filter, map } from \"@electric-sql/d2ts\"\nimport { evaluateWhereOnNamespacedRow } from \"./evaluators.js\"\nimport { processJoinClause } from \"./joins.js\"\nimport { processGroupBy } from \"./group-by.js\"\nimport { processOrderBy } from \"./order-by.js\"\nimport { processSelect } from \"./select.js\"\nimport type { Query } from \"./schema.js\"\nimport type { IStreamBuilder } from \"@electric-sql/d2ts\"\nimport type {\n InputRow,\n KeyedStream,\n NamespacedAndKeyedStream,\n} from \"../types.js\"\n\n/**\n * Compiles a query into a D2 pipeline\n * @param query The query to compile\n * @param inputs Mapping of table names to input streams\n * @returns A stream builder representing the compiled query\n */\nexport function compileQueryPipeline<T extends IStreamBuilder<unknown>>(\n query: Query,\n inputs: Record<string, KeyedStream>\n): T {\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Process WITH queries if they exist\n if (query.with && query.with.length > 0) {\n // Process each WITH query in order\n for (const withQuery of query.with) {\n // Ensure the WITH query has an alias\n if (!withQuery.as) {\n throw new Error(`WITH query must have an \"as\" property`)\n }\n\n // Check if this CTE name already exists in the inputs\n if (allInputs[withQuery.as]) {\n throw new Error(`CTE with name \"${withQuery.as}\" already exists`)\n }\n\n // Create a new query without the 'with' property to avoid circular references\n const withQueryWithoutWith = { ...withQuery, with: undefined }\n\n // Compile the WITH query using the current set of inputs\n // (which includes previously compiled WITH queries)\n const compiledWithQuery = compileQueryPipeline(\n withQueryWithoutWith,\n allInputs\n )\n\n // Add the compiled query to the inputs map using its alias\n allInputs[withQuery.as] = compiledWithQuery as KeyedStream\n }\n }\n\n // Create a map of table aliases to inputs\n const tables: Record<string, KeyedStream> = {}\n\n // The main table is the one in the FROM clause\n const mainTableAlias = query.as || query.from\n\n // Get the main input from the inputs map (now including CTEs)\n const input = allInputs[query.from]\n if (!input) {\n throw new Error(`Input for table \"${query.from}\" not found in inputs map`)\n }\n\n tables[mainTableAlias] = input\n\n // Prepare the initial pipeline with the main table wrapped in its alias\n let pipeline: NamespacedAndKeyedStream = input.pipe(\n map(([key, row]) => {\n // Initialize the record with a nested structure\n const ret = [key, { [mainTableAlias]: row }] as [\n string,\n Record<string, typeof row>,\n ]\n return ret\n })\n )\n\n // Process JOIN clauses if they exist\n if (query.join) {\n pipeline = processJoinClause(\n pipeline,\n query,\n tables,\n mainTableAlias,\n allInputs\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where) {\n pipeline = pipeline.pipe(\n filter(([_key, row]) => {\n const result = evaluateWhereOnNamespacedRow(\n row,\n query.where!,\n mainTableAlias\n )\n return result\n })\n )\n }\n\n // Process the GROUP BY clause if it exists\n if (query.groupBy) {\n pipeline = processGroupBy(pipeline, query, mainTableAlias)\n }\n\n // Process the HAVING clause if it exists\n // This works similarly to WHERE but is applied after any aggregations\n if (query.having) {\n pipeline = pipeline.pipe(\n filter(([_key, row]) => {\n // For HAVING, we're working with the flattened row that contains both\n // the group by keys and the aggregate results directly\n const result = evaluateWhereOnNamespacedRow(\n row,\n query.having!,\n mainTableAlias\n )\n return result\n })\n )\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy) {\n pipeline = processOrderBy(pipeline, query, mainTableAlias)\n } else if (query.limit !== undefined || query.offset !== undefined) {\n // If there's a limit or offset without orderBy, throw an error\n throw new Error(\n `LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`\n )\n }\n\n // Process the SELECT clause - this is where we flatten the structure\n const resultPipeline: KeyedStream | NamespacedAndKeyedStream = query.select\n ? processSelect(pipeline, query, mainTableAlias, allInputs)\n : !query.join && !query.groupBy\n ? pipeline.pipe(\n map(([key, row]) => [key, row[mainTableAlias]] as InputRow)\n )\n : pipeline\n return resultPipeline as T\n}\n"],"names":["map","processJoinClause","filter","evaluateWhereOnNamespacedRow","processGroupBy","processOrderBy","processSelect"],"mappings":";;;;;;;;AAoBgB,SAAA,qBACd,OACA,QACG;AAEG,QAAA,YAAY,EAAE,GAAG,OAAO;AAG9B,MAAI,MAAM,QAAQ,MAAM,KAAK,SAAS,GAAG;AAE5B,eAAA,aAAa,MAAM,MAAM;AAE9B,UAAA,CAAC,UAAU,IAAI;AACX,cAAA,IAAI,MAAM,uCAAuC;AAAA,MAAA;AAIrD,UAAA,UAAU,UAAU,EAAE,GAAG;AAC3B,cAAM,IAAI,MAAM,kBAAkB,UAAU,EAAE,kBAAkB;AAAA,MAAA;AAIlE,YAAM,uBAAuB,EAAE,GAAG,WAAW,MAAM,OAAU;AAI7D,YAAM,oBAAoB;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AAGU,gBAAA,UAAU,EAAE,IAAI;AAAA,IAAA;AAAA,EAC5B;AAIF,QAAM,SAAsC,CAAC;AAGvC,QAAA,iBAAiB,MAAM,MAAM,MAAM;AAGnC,QAAA,QAAQ,UAAU,MAAM,IAAI;AAClC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,oBAAoB,MAAM,IAAI,2BAA2B;AAAA,EAAA;AAG3E,SAAO,cAAc,IAAI;AAGzB,MAAI,WAAqC,MAAM;AAAA,IAC7CA,KAAAA,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM;AAEZ,YAAA,MAAM,CAAC,KAAK,EAAE,CAAC,cAAc,GAAG,KAAK;AAIpC,aAAA;AAAA,IACR,CAAA;AAAA,EACH;AAGA,MAAI,MAAM,MAAM;AACH,eAAAC,MAAA;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EAAA;AAIF,MAAI,MAAM,OAAO;AACf,eAAW,SAAS;AAAA,MAClBC,KAAAA,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM;AACtB,cAAM,SAASC,WAAA;AAAA,UACb;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACF;AACO,eAAA;AAAA,MACR,CAAA;AAAA,IACH;AAAA,EAAA;AAIF,MAAI,MAAM,SAAS;AACN,eAAAC,QAAA,eAAe,UAAU,OAAO,cAAc;AAAA,EAAA;AAK3D,MAAI,MAAM,QAAQ;AAChB,eAAW,SAAS;AAAA,MAClBF,KAAAA,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM;AAGtB,cAAM,SAASC,WAAA;AAAA,UACb;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACF;AACO,eAAA;AAAA,MACR,CAAA;AAAA,IACH;AAAA,EAAA;AAIF,MAAI,MAAM,SAAS;AACN,eAAAE,QAAA,eAAe,UAAU,OAAO,cAAc;AAAA,EAAA,WAChD,MAAM,UAAU,UAAa,MAAM,WAAW,QAAW;AAElE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EAAA;AAIF,QAAM,iBAAyD,MAAM,SACjEC,OAAAA,cAAc,UAAU,OAAO,gBAAgB,SAAS,IACxD,CAAC,MAAM,QAAQ,CAAC,MAAM,UACpB,SAAS;AAAA,IACPN,SAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,cAAc,CAAC,CAAa;AAAA,EAAA,IAE5D;AACC,SAAA;AACT;;"}