@tanstack/db 0.0.31 → 0.0.33

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.
@@ -129,7 +129,7 @@ function createFilteredCallback(originalCallback, options) {
129
129
  }
130
130
  }
131
131
  }
132
- if (filteredChanges.length > 0) {
132
+ if (filteredChanges.length > 0 || changes.length === 0) {
133
133
  originalCallback(filteredChanges);
134
134
  }
135
135
  };
@@ -1 +1 @@
1
- {"version":3,"file":"change-events.cjs","sources":["../../src/change-events.ts"],"sourcesContent":["import {\n createSingleRowRefProxy,\n toExpression,\n} from \"./query/builder/ref-proxy\"\nimport { compileSingleRowExpression } from \"./query/compiler/evaluators.js\"\nimport { optimizeExpressionWithIndexes } from \"./utils/index-optimization.js\"\nimport type {\n ChangeMessage,\n CurrentStateAsChangesOptions,\n SubscribeChangesOptions,\n} from \"./types\"\nimport type { Collection } from \"./collection\"\nimport type { SingleRowRefProxy } from \"./query/builder/ref-proxy\"\nimport type { BasicExpression } from \"./query/ir.js\"\n\n/**\n * Interface for a collection-like object that provides the necessary methods\n * for the change events system to work\n */\nexport interface CollectionLike<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n> extends Pick<Collection<T, TKey>, `get` | `has` | `entries` | `indexes`> {}\n\n/**\n * Returns the current state of the collection as an array of changes\n * @param collection - The collection to get changes from\n * @param options - Options including optional where filter\n * @returns An array of changes\n * @example\n * // Get all items as changes\n * const allChanges = currentStateAsChanges(collection)\n *\n * // Get only items matching a condition\n * const activeChanges = currentStateAsChanges(collection, {\n * where: (row) => row.status === 'active'\n * })\n *\n * // Get only items using a pre-compiled expression\n * const activeChanges = currentStateAsChanges(collection, {\n * whereExpression: eq(row.status, 'active')\n * })\n */\nexport function currentStateAsChanges<\n T extends object,\n TKey extends string | number,\n>(\n collection: CollectionLike<T, TKey>,\n options: CurrentStateAsChangesOptions<T> = {}\n): Array<ChangeMessage<T>> {\n // Helper function to collect filtered results\n const collectFilteredResults = (\n filterFn?: (value: T) => boolean\n ): Array<ChangeMessage<T>> => {\n const result: Array<ChangeMessage<T>> = []\n for (const [key, value] of collection.entries()) {\n // If no filter function is provided, include all items\n if (filterFn?.(value) ?? true) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n }\n\n if (!options.where && !options.whereExpression) {\n // No filtering, return all items\n return collectFilteredResults()\n }\n\n // There's a where clause, let's see if we can use an index\n try {\n let expression: BasicExpression<boolean>\n\n if (options.whereExpression) {\n // Use the pre-compiled expression directly\n expression = options.whereExpression\n } else if (options.where) {\n // Create the single-row refProxy for the callback\n const singleRowRefProxy = createSingleRowRefProxy<T>()\n\n // Execute the callback to get the expression\n const whereExpression = options.where(singleRowRefProxy)\n\n // Convert the result to a BasicExpression\n expression = toExpression(whereExpression)\n } else {\n // This should never happen due to the check above, but TypeScript needs it\n return []\n }\n\n // Try to optimize the query using indexes\n const optimizationResult = optimizeExpressionWithIndexes(\n expression,\n collection.indexes\n )\n\n if (optimizationResult.canOptimize) {\n // Use index optimization\n const result: Array<ChangeMessage<T>> = []\n for (const key of optimizationResult.matchingKeys) {\n const value = collection.get(key)\n if (value !== undefined) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n } else {\n // No index found or complex expression, fall back to full scan with filter\n const filterFn = options.where\n ? createFilterFunction(options.where)\n : createFilterFunctionFromExpression(expression)\n\n return collectFilteredResults(filterFn)\n }\n } catch (error) {\n // If anything goes wrong with the where clause, fall back to full scan\n console.warn(\n `Error processing where clause, falling back to full scan:`,\n error\n )\n\n const filterFn = options.where\n ? createFilterFunction(options.where)\n : createFilterFunctionFromExpression(options.whereExpression!)\n\n return collectFilteredResults(filterFn)\n }\n}\n\n/**\n * Creates a filter function from a where callback\n * @param whereCallback - The callback function that defines the filter condition\n * @returns A function that takes an item and returns true if it matches the filter\n */\nexport function createFilterFunction<T extends object>(\n whereCallback: (row: SingleRowRefProxy<T>) => any\n): (item: T) => boolean {\n return (item: T): boolean => {\n try {\n // First try the RefProxy approach for query builder functions\n const singleRowRefProxy = createSingleRowRefProxy<T>()\n const whereExpression = whereCallback(singleRowRefProxy)\n const expression = toExpression(whereExpression)\n const evaluator = compileSingleRowExpression(expression)\n const result = evaluator(item as Record<string, unknown>)\n // WHERE clauses should always evaluate to boolean predicates (Kevin's feedback)\n return result\n } catch {\n // If RefProxy approach fails (e.g., arithmetic operations), fall back to direct evaluation\n try {\n // Create a simple proxy that returns actual values for arithmetic operations\n const simpleProxy = new Proxy(item as any, {\n get(target, prop) {\n return target[prop]\n },\n }) as SingleRowRefProxy<T>\n\n const result = whereCallback(simpleProxy)\n return result\n } catch {\n // If both approaches fail, exclude the item\n return false\n }\n }\n }\n}\n\n/**\n * Creates a filter function from a pre-compiled expression\n * @param expression - The pre-compiled expression to evaluate\n * @returns A function that takes an item and returns true if it matches the filter\n */\nexport function createFilterFunctionFromExpression<T extends object>(\n expression: BasicExpression<boolean>\n): (item: T) => boolean {\n return (item: T): boolean => {\n try {\n const evaluator = compileSingleRowExpression(expression)\n const result = evaluator(item as Record<string, unknown>)\n return Boolean(result)\n } catch {\n // If evaluation fails, exclude the item\n return false\n }\n }\n}\n\n/**\n * Creates a filtered callback that only calls the original callback with changes that match the where clause\n * @param originalCallback - The original callback to filter\n * @param options - The subscription options containing the where clause\n * @returns A filtered callback function\n */\nexport function createFilteredCallback<T extends object>(\n originalCallback: (changes: Array<ChangeMessage<T>>) => void,\n options: SubscribeChangesOptions<T>\n): (changes: Array<ChangeMessage<T>>) => void {\n const filterFn = options.whereExpression\n ? createFilterFunctionFromExpression(options.whereExpression)\n : createFilterFunction(options.where!)\n\n return (changes: Array<ChangeMessage<T>>) => {\n const filteredChanges: Array<ChangeMessage<T>> = []\n\n for (const change of changes) {\n if (change.type === `insert`) {\n // For inserts, check if the new value matches the filter\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n } else if (change.type === `update`) {\n // For updates, we need to check both old and new values\n const newValueMatches = filterFn(change.value)\n const oldValueMatches = change.previousValue\n ? filterFn(change.previousValue)\n : false\n\n if (newValueMatches && oldValueMatches) {\n // Both old and new match: emit update\n filteredChanges.push(change)\n } else if (newValueMatches && !oldValueMatches) {\n // New matches but old didn't: emit insert\n filteredChanges.push({\n ...change,\n type: `insert`,\n })\n } else if (!newValueMatches && oldValueMatches) {\n // Old matched but new doesn't: emit delete\n filteredChanges.push({\n ...change,\n type: `delete`,\n value: change.previousValue!, // Use the previous value for the delete\n })\n }\n // If neither matches, don't emit anything\n } else {\n // For deletes, include if the previous value would have matched\n // (so subscribers know something they were tracking was deleted)\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n }\n }\n\n if (filteredChanges.length > 0) {\n originalCallback(filteredChanges)\n }\n }\n}\n"],"names":["createSingleRowRefProxy","toExpression","optimizeExpressionWithIndexes","compileSingleRowExpression"],"mappings":"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"}
1
+ {"version":3,"file":"change-events.cjs","sources":["../../src/change-events.ts"],"sourcesContent":["import {\n createSingleRowRefProxy,\n toExpression,\n} from \"./query/builder/ref-proxy\"\nimport { compileSingleRowExpression } from \"./query/compiler/evaluators.js\"\nimport { optimizeExpressionWithIndexes } from \"./utils/index-optimization.js\"\nimport type {\n ChangeMessage,\n CurrentStateAsChangesOptions,\n SubscribeChangesOptions,\n} from \"./types\"\nimport type { Collection } from \"./collection\"\nimport type { SingleRowRefProxy } from \"./query/builder/ref-proxy\"\nimport type { BasicExpression } from \"./query/ir.js\"\n\n/**\n * Interface for a collection-like object that provides the necessary methods\n * for the change events system to work\n */\nexport interface CollectionLike<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n> extends Pick<Collection<T, TKey>, `get` | `has` | `entries` | `indexes`> {}\n\n/**\n * Returns the current state of the collection as an array of changes\n * @param collection - The collection to get changes from\n * @param options - Options including optional where filter\n * @returns An array of changes\n * @example\n * // Get all items as changes\n * const allChanges = currentStateAsChanges(collection)\n *\n * // Get only items matching a condition\n * const activeChanges = currentStateAsChanges(collection, {\n * where: (row) => row.status === 'active'\n * })\n *\n * // Get only items using a pre-compiled expression\n * const activeChanges = currentStateAsChanges(collection, {\n * whereExpression: eq(row.status, 'active')\n * })\n */\nexport function currentStateAsChanges<\n T extends object,\n TKey extends string | number,\n>(\n collection: CollectionLike<T, TKey>,\n options: CurrentStateAsChangesOptions<T> = {}\n): Array<ChangeMessage<T>> {\n // Helper function to collect filtered results\n const collectFilteredResults = (\n filterFn?: (value: T) => boolean\n ): Array<ChangeMessage<T>> => {\n const result: Array<ChangeMessage<T>> = []\n for (const [key, value] of collection.entries()) {\n // If no filter function is provided, include all items\n if (filterFn?.(value) ?? true) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n }\n\n if (!options.where && !options.whereExpression) {\n // No filtering, return all items\n return collectFilteredResults()\n }\n\n // There's a where clause, let's see if we can use an index\n try {\n let expression: BasicExpression<boolean>\n\n if (options.whereExpression) {\n // Use the pre-compiled expression directly\n expression = options.whereExpression\n } else if (options.where) {\n // Create the single-row refProxy for the callback\n const singleRowRefProxy = createSingleRowRefProxy<T>()\n\n // Execute the callback to get the expression\n const whereExpression = options.where(singleRowRefProxy)\n\n // Convert the result to a BasicExpression\n expression = toExpression(whereExpression)\n } else {\n // This should never happen due to the check above, but TypeScript needs it\n return []\n }\n\n // Try to optimize the query using indexes\n const optimizationResult = optimizeExpressionWithIndexes(\n expression,\n collection.indexes\n )\n\n if (optimizationResult.canOptimize) {\n // Use index optimization\n const result: Array<ChangeMessage<T>> = []\n for (const key of optimizationResult.matchingKeys) {\n const value = collection.get(key)\n if (value !== undefined) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n } else {\n // No index found or complex expression, fall back to full scan with filter\n const filterFn = options.where\n ? createFilterFunction(options.where)\n : createFilterFunctionFromExpression(expression)\n\n return collectFilteredResults(filterFn)\n }\n } catch (error) {\n // If anything goes wrong with the where clause, fall back to full scan\n console.warn(\n `Error processing where clause, falling back to full scan:`,\n error\n )\n\n const filterFn = options.where\n ? createFilterFunction(options.where)\n : createFilterFunctionFromExpression(options.whereExpression!)\n\n return collectFilteredResults(filterFn)\n }\n}\n\n/**\n * Creates a filter function from a where callback\n * @param whereCallback - The callback function that defines the filter condition\n * @returns A function that takes an item and returns true if it matches the filter\n */\nexport function createFilterFunction<T extends object>(\n whereCallback: (row: SingleRowRefProxy<T>) => any\n): (item: T) => boolean {\n return (item: T): boolean => {\n try {\n // First try the RefProxy approach for query builder functions\n const singleRowRefProxy = createSingleRowRefProxy<T>()\n const whereExpression = whereCallback(singleRowRefProxy)\n const expression = toExpression(whereExpression)\n const evaluator = compileSingleRowExpression(expression)\n const result = evaluator(item as Record<string, unknown>)\n // WHERE clauses should always evaluate to boolean predicates (Kevin's feedback)\n return result\n } catch {\n // If RefProxy approach fails (e.g., arithmetic operations), fall back to direct evaluation\n try {\n // Create a simple proxy that returns actual values for arithmetic operations\n const simpleProxy = new Proxy(item as any, {\n get(target, prop) {\n return target[prop]\n },\n }) as SingleRowRefProxy<T>\n\n const result = whereCallback(simpleProxy)\n return result\n } catch {\n // If both approaches fail, exclude the item\n return false\n }\n }\n }\n}\n\n/**\n * Creates a filter function from a pre-compiled expression\n * @param expression - The pre-compiled expression to evaluate\n * @returns A function that takes an item and returns true if it matches the filter\n */\nexport function createFilterFunctionFromExpression<T extends object>(\n expression: BasicExpression<boolean>\n): (item: T) => boolean {\n return (item: T): boolean => {\n try {\n const evaluator = compileSingleRowExpression(expression)\n const result = evaluator(item as Record<string, unknown>)\n return Boolean(result)\n } catch {\n // If evaluation fails, exclude the item\n return false\n }\n }\n}\n\n/**\n * Creates a filtered callback that only calls the original callback with changes that match the where clause\n * @param originalCallback - The original callback to filter\n * @param options - The subscription options containing the where clause\n * @returns A filtered callback function\n */\nexport function createFilteredCallback<T extends object>(\n originalCallback: (changes: Array<ChangeMessage<T>>) => void,\n options: SubscribeChangesOptions<T>\n): (changes: Array<ChangeMessage<T>>) => void {\n const filterFn = options.whereExpression\n ? createFilterFunctionFromExpression(options.whereExpression)\n : createFilterFunction(options.where!)\n\n return (changes: Array<ChangeMessage<T>>) => {\n const filteredChanges: Array<ChangeMessage<T>> = []\n\n for (const change of changes) {\n if (change.type === `insert`) {\n // For inserts, check if the new value matches the filter\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n } else if (change.type === `update`) {\n // For updates, we need to check both old and new values\n const newValueMatches = filterFn(change.value)\n const oldValueMatches = change.previousValue\n ? filterFn(change.previousValue)\n : false\n\n if (newValueMatches && oldValueMatches) {\n // Both old and new match: emit update\n filteredChanges.push(change)\n } else if (newValueMatches && !oldValueMatches) {\n // New matches but old didn't: emit insert\n filteredChanges.push({\n ...change,\n type: `insert`,\n })\n } else if (!newValueMatches && oldValueMatches) {\n // Old matched but new doesn't: emit delete\n filteredChanges.push({\n ...change,\n type: `delete`,\n value: change.previousValue!, // Use the previous value for the delete\n })\n }\n // If neither matches, don't emit anything\n } else {\n // For deletes, include if the previous value would have matched\n // (so subscribers know something they were tracking was deleted)\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n }\n }\n\n // Always call the original callback if we have filtered changes OR\n // if the original changes array was empty (which indicates a ready signal)\n if (filteredChanges.length > 0 || changes.length === 0) {\n originalCallback(filteredChanges)\n }\n }\n}\n"],"names":["createSingleRowRefProxy","toExpression","optimizeExpressionWithIndexes","compileSingleRowExpression"],"mappings":"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"}
@@ -9,7 +9,6 @@ const autoIndex = require("./indexes/auto-index.cjs");
9
9
  const transactions = require("./transactions.cjs");
10
10
  const errors = require("./errors.cjs");
11
11
  const changeEvents = require("./change-events.cjs");
12
- const collectionsStore = /* @__PURE__ */ new Map();
13
12
  function createCollection(options) {
14
13
  const collection = new CollectionImpl(options);
15
14
  if (options.utils) {
@@ -345,7 +344,6 @@ class CollectionImpl {
345
344
  ...config,
346
345
  autoIndex: config.autoIndex ?? `eager`
347
346
  };
348
- collectionsStore.set(this.id, this);
349
347
  if (this.config.compare) {
350
348
  this.syncedData = new SortedMap.SortedMap(this.config.compare);
351
349
  } else {
@@ -404,6 +402,9 @@ class CollectionImpl {
404
402
  const callbacks = [...this.onFirstReadyCallbacks];
405
403
  this.onFirstReadyCallbacks = [];
406
404
  callbacks.forEach((callback) => callback());
405
+ if (this.size === 0 && this.changeListeners.size > 0) {
406
+ this.emitEmptyReadyEvent();
407
+ }
407
408
  }
408
409
  }
409
410
  }
@@ -794,6 +795,15 @@ class CollectionImpl {
794
795
  }
795
796
  return this.syncedData.get(key);
796
797
  }
798
+ /**
799
+ * Emit an empty ready event to notify subscribers that the collection is ready
800
+ * This bypasses the normal empty array check in emitEvents
801
+ */
802
+ emitEmptyReadyEvent() {
803
+ for (const listener of this.changeListeners) {
804
+ listener([]);
805
+ }
806
+ }
797
807
  /**
798
808
  * Emit events either immediately or batch them for later emission
799
809
  */
@@ -1432,6 +1442,5 @@ class CollectionImpl {
1432
1442
  }
1433
1443
  }
1434
1444
  exports.CollectionImpl = CollectionImpl;
1435
- exports.collectionsStore = collectionsStore;
1436
1445
  exports.createCollection = createCollection;
1437
1446
  //# sourceMappingURL=collection.cjs.map