@tanstack/db 0.5.31 → 0.5.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/collection/subscription.cjs +6 -6
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/errors.cjs +8 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/index.cjs +3 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/query/builder/types.d.cts +28 -31
- package/dist/cjs/query/compiler/index.cjs +3 -0
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/index.d.cts +1 -0
- package/dist/cjs/query/query-once.cjs +28 -0
- package/dist/cjs/query/query-once.cjs.map +1 -0
- package/dist/cjs/query/query-once.d.cts +57 -0
- package/dist/cjs/query/subset-dedupe.cjs +8 -7
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/esm/collection/subscription.js +6 -6
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +8 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +28 -31
- package/dist/esm/query/compiler/index.js +4 -1
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/index.d.ts +1 -0
- package/dist/esm/query/query-once.d.ts +57 -0
- package/dist/esm/query/query-once.js +28 -0
- package/dist/esm/query/query-once.js.map +1 -0
- package/dist/esm/query/subset-dedupe.js +8 -7
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/package.json +1 -1
- package/src/collection/subscription.ts +6 -6
- package/src/errors.ts +11 -0
- package/src/query/builder/types.ts +64 -50
- package/src/query/compiler/index.ts +5 -0
- package/src/query/index.ts +3 -0
- package/src/query/query-once.ts +115 -0
- package/src/query/subset-dedupe.ts +14 -15
|
@@ -322,16 +322,16 @@ class CollectionSubscription extends eventEmitter.EventEmitter {
|
|
|
322
322
|
const whereFromCursor = cursor.buildCursor(orderBy, minValues);
|
|
323
323
|
if (whereFromCursor) {
|
|
324
324
|
const { expression } = orderBy[0];
|
|
325
|
-
const
|
|
325
|
+
const cursorMinValue = minValues[0];
|
|
326
326
|
let whereCurrentCursor;
|
|
327
|
-
if (
|
|
328
|
-
const
|
|
327
|
+
if (cursorMinValue instanceof Date) {
|
|
328
|
+
const cursorMinValuePlus1ms = new Date(cursorMinValue.getTime() + 1);
|
|
329
329
|
whereCurrentCursor = functions.and(
|
|
330
|
-
functions.gte(expression, new ir.Value(
|
|
331
|
-
functions.lt(expression, new ir.Value(
|
|
330
|
+
functions.gte(expression, new ir.Value(cursorMinValue)),
|
|
331
|
+
functions.lt(expression, new ir.Value(cursorMinValuePlus1ms))
|
|
332
332
|
);
|
|
333
333
|
} else {
|
|
334
|
-
whereCurrentCursor = functions.eq(expression, new ir.Value(
|
|
334
|
+
whereCurrentCursor = functions.eq(expression, new ir.Value(cursorMinValue));
|
|
335
335
|
}
|
|
336
336
|
cursorExpressions = {
|
|
337
337
|
whereFrom: whereFromCursor,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription.cjs","sources":["../../../src/collection/subscription.ts"],"sourcesContent":["import { ensureIndexForExpression } from '../indexes/auto-index.js'\nimport { and, eq, gte, lt } from '../query/builder/functions.js'\nimport { PropRef, Value } from '../query/ir.js'\nimport { EventEmitter } from '../event-emitter.js'\nimport { compileExpression } from '../query/compiler/evaluators.js'\nimport { buildCursor } from '../utils/cursor.js'\nimport {\n createFilterFunctionFromExpression,\n createFilteredCallback,\n} from './change-events.js'\nimport type { BasicExpression, OrderBy } from '../query/ir.js'\nimport type { IndexInterface } from '../indexes/base-index.js'\nimport type {\n ChangeMessage,\n LoadSubsetOptions,\n Subscription,\n SubscriptionEvents,\n SubscriptionStatus,\n SubscriptionUnsubscribedEvent,\n} from '../types.js'\nimport type { CollectionImpl } from './index.js'\n\ntype RequestSnapshotOptions = {\n where?: BasicExpression<boolean>\n optimizedOnly?: boolean\n trackLoadSubsetPromise?: boolean\n /** Optional orderBy to pass to loadSubset for backend optimization */\n orderBy?: OrderBy\n /** Optional limit to pass to loadSubset for backend optimization */\n limit?: number\n /** Callback that receives the raw loadSubset result for external tracking */\n onLoadSubsetResult?: (result: Promise<void> | true) => void\n}\n\ntype RequestLimitedSnapshotOptions = {\n orderBy: OrderBy\n limit: number\n /** All column values for cursor (first value used for local index, all values for sync layer) */\n minValues?: Array<unknown>\n /** Row offset for offset-based pagination (passed to sync layer) */\n offset?: number\n /** Whether to track the loadSubset promise on this subscription (default: true) */\n trackLoadSubsetPromise?: boolean\n /** Callback that receives the raw loadSubset result for external tracking */\n onLoadSubsetResult?: (result: Promise<void> | true) => void\n}\n\ntype CollectionSubscriptionOptions = {\n includeInitialState?: boolean\n /** Pre-compiled expression for filtering changes */\n whereExpression?: BasicExpression<boolean>\n /** Callback to call when the subscription is unsubscribed */\n onUnsubscribe?: (event: SubscriptionUnsubscribedEvent) => void\n}\n\nexport class CollectionSubscription\n extends EventEmitter<SubscriptionEvents>\n implements Subscription\n{\n private loadedInitialState = false\n\n // Flag to skip filtering in filterAndFlipChanges.\n // This is separate from loadedInitialState because we want to allow\n // requestSnapshot to still work even when filtering is skipped.\n private skipFiltering = false\n\n // Flag to indicate that we have sent at least 1 snapshot.\n // While `snapshotSent` is false we filter out all changes from subscription to the collection.\n private snapshotSent = false\n\n /**\n * Track all loadSubset calls made by this subscription so we can unload them on cleanup.\n * We store the exact LoadSubsetOptions we passed to loadSubset to ensure symmetric unload.\n */\n private loadedSubsets: Array<LoadSubsetOptions> = []\n\n // Keep track of the keys we've sent (needed for join and orderBy optimizations)\n private sentKeys = new Set<string | number>()\n\n // Track the count of rows sent via requestLimitedSnapshot for offset-based pagination\n private limitedSnapshotRowCount = 0\n\n // Track the last key sent via requestLimitedSnapshot for cursor-based pagination\n private lastSentKey: string | number | undefined\n\n private filteredCallback: (changes: Array<ChangeMessage<any, any>>) => void\n\n private orderByIndex: IndexInterface<string | number> | undefined\n\n // Status tracking\n private _status: SubscriptionStatus = `ready`\n private pendingLoadSubsetPromises: Set<Promise<void>> = new Set()\n\n // Cleanup function for truncate event listener\n private truncateCleanup: (() => void) | undefined\n\n // Truncate buffering state\n // When a truncate occurs, we buffer changes until all loadSubset refetches complete\n // This prevents a flash of missing content between deletes and new inserts\n private isBufferingForTruncate = false\n private truncateBuffer: Array<Array<ChangeMessage<any, any>>> = []\n private pendingTruncateRefetches: Set<Promise<void>> = new Set()\n\n public get status(): SubscriptionStatus {\n return this._status\n }\n\n constructor(\n private collection: CollectionImpl<any, any, any, any, any>,\n private callback: (changes: Array<ChangeMessage<any, any>>) => void,\n private options: CollectionSubscriptionOptions,\n ) {\n super()\n if (options.onUnsubscribe) {\n this.on(`unsubscribed`, (event) => options.onUnsubscribe!(event))\n }\n\n // Auto-index for where expressions if enabled\n if (options.whereExpression) {\n ensureIndexForExpression(options.whereExpression, this.collection)\n }\n\n const callbackWithSentKeysTracking = (\n changes: Array<ChangeMessage<any, any>>,\n ) => {\n callback(changes)\n this.trackSentKeys(changes)\n }\n\n this.callback = callbackWithSentKeysTracking\n\n // Create a filtered callback if where clause is provided\n this.filteredCallback = options.whereExpression\n ? createFilteredCallback(this.callback, options)\n : this.callback\n\n // Listen for truncate events to re-request data after must-refetch\n // When a truncate happens (e.g., from a 409 must-refetch), all collection data is cleared.\n // We need to re-request all previously loaded subsets to repopulate the data.\n this.truncateCleanup = this.collection.on(`truncate`, () => {\n this.handleTruncate()\n })\n }\n\n /**\n * Handle collection truncate event by resetting state and re-requesting subsets.\n * This is called when the sync layer receives a must-refetch and clears all data.\n *\n * To prevent a flash of missing content, we buffer all changes (deletes from truncate\n * and inserts from refetch) until all loadSubset promises resolve, then emit them together.\n */\n private handleTruncate() {\n // Copy the loaded subsets before clearing (we'll re-request them)\n const subsetsToReload = [...this.loadedSubsets]\n\n // Only buffer if there's an actual loadSubset handler that can do async work.\n // Without a loadSubset handler, there's nothing to re-request and no reason to buffer.\n // This prevents unnecessary buffering in eager sync mode or when loadSubset isn't implemented.\n const hasLoadSubsetHandler = this.collection._sync.syncLoadSubsetFn !== null\n\n // If there are no subsets to reload OR no loadSubset handler, just reset state\n if (subsetsToReload.length === 0 || !hasLoadSubsetHandler) {\n this.snapshotSent = false\n this.loadedInitialState = false\n this.limitedSnapshotRowCount = 0\n this.lastSentKey = undefined\n this.loadedSubsets = []\n return\n }\n\n // Start buffering BEFORE we receive the delete events from the truncate commit\n // This ensures we capture both the deletes and subsequent inserts\n this.isBufferingForTruncate = true\n this.truncateBuffer = []\n this.pendingTruncateRefetches.clear()\n\n // Reset snapshot/pagination tracking state\n // Note: We don't need to populate sentKeys here because filterAndFlipChanges\n // will skip the delete filter when isBufferingForTruncate is true\n this.snapshotSent = false\n this.loadedInitialState = false\n this.limitedSnapshotRowCount = 0\n this.lastSentKey = undefined\n\n // Clear the loadedSubsets array since we're re-requesting fresh\n this.loadedSubsets = []\n\n // Defer the loadSubset calls to a microtask so the truncate commit's delete events\n // are buffered BEFORE the loadSubset calls potentially trigger nested commits.\n // This ensures correct event ordering: deletes first, then inserts.\n queueMicrotask(() => {\n // Check if we were unsubscribed while waiting\n if (!this.isBufferingForTruncate) {\n return\n }\n\n // Re-request all previously loaded subsets and track their promises\n for (const options of subsetsToReload) {\n const syncResult = this.collection._sync.loadSubset(options)\n\n // Track this loadSubset call so we can unload it later\n this.loadedSubsets.push(options)\n this.trackLoadSubsetPromise(syncResult)\n\n // Track the promise for buffer flushing\n if (syncResult instanceof Promise) {\n this.pendingTruncateRefetches.add(syncResult)\n syncResult\n .catch(() => {\n // Ignore errors - we still want to flush the buffer even if some requests fail\n })\n .finally(() => {\n this.pendingTruncateRefetches.delete(syncResult)\n this.checkTruncateRefetchComplete()\n })\n }\n }\n\n // If all loadSubset calls were synchronous (returned true), flush now\n // At this point, delete events have already been buffered from the truncate commit\n if (this.pendingTruncateRefetches.size === 0) {\n this.flushTruncateBuffer()\n }\n })\n }\n\n /**\n * Check if all truncate refetch promises have completed and flush buffer if so\n */\n private checkTruncateRefetchComplete() {\n if (\n this.pendingTruncateRefetches.size === 0 &&\n this.isBufferingForTruncate\n ) {\n this.flushTruncateBuffer()\n }\n }\n\n /**\n * Flush the truncate buffer, emitting all buffered changes to the callback\n */\n private flushTruncateBuffer() {\n this.isBufferingForTruncate = false\n\n // Flatten all buffered changes into a single array for atomic emission\n // This ensures consumers see all truncate changes (deletes + inserts) in one callback\n const merged = this.truncateBuffer.flat()\n if (merged.length > 0) {\n this.filteredCallback(merged)\n }\n\n this.truncateBuffer = []\n }\n\n setOrderByIndex(index: IndexInterface<any>) {\n this.orderByIndex = index\n }\n\n /**\n * Set subscription status and emit events if changed\n */\n private setStatus(newStatus: SubscriptionStatus) {\n if (this._status === newStatus) {\n return // No change\n }\n\n const previousStatus = this._status\n this._status = newStatus\n\n // Emit status:change event\n this.emitInner(`status:change`, {\n type: `status:change`,\n subscription: this,\n previousStatus,\n status: newStatus,\n })\n\n // Emit specific status event\n const eventKey: `status:${SubscriptionStatus}` = `status:${newStatus}`\n this.emitInner(eventKey, {\n type: eventKey,\n subscription: this,\n previousStatus,\n status: newStatus,\n } as SubscriptionEvents[typeof eventKey])\n }\n\n /**\n * Track a loadSubset promise and manage loading status\n */\n private trackLoadSubsetPromise(syncResult: Promise<void> | true) {\n // Track the promise if it's actually a promise (async work)\n if (syncResult instanceof Promise) {\n this.pendingLoadSubsetPromises.add(syncResult)\n this.setStatus(`loadingSubset`)\n\n syncResult.finally(() => {\n this.pendingLoadSubsetPromises.delete(syncResult)\n if (this.pendingLoadSubsetPromises.size === 0) {\n this.setStatus(`ready`)\n }\n })\n }\n }\n\n hasLoadedInitialState() {\n return this.loadedInitialState\n }\n\n hasSentAtLeastOneSnapshot() {\n return this.snapshotSent\n }\n\n emitEvents(changes: Array<ChangeMessage<any, any>>) {\n const newChanges = this.filterAndFlipChanges(changes)\n\n if (this.isBufferingForTruncate) {\n // Buffer the changes instead of emitting immediately\n // This prevents a flash of missing content during truncate/refetch\n if (newChanges.length > 0) {\n this.truncateBuffer.push(newChanges)\n }\n } else {\n this.filteredCallback(newChanges)\n }\n }\n\n /**\n * Sends the snapshot to the callback.\n * Returns a boolean indicating if it succeeded.\n * It can only fail if there is no index to fulfill the request\n * and the optimizedOnly option is set to true,\n * or, the entire state was already loaded.\n */\n requestSnapshot(opts?: RequestSnapshotOptions): boolean {\n if (this.loadedInitialState) {\n // Subscription was deoptimized so we already sent the entire initial state\n return false\n }\n\n const stateOpts: RequestSnapshotOptions = {\n where: this.options.whereExpression,\n optimizedOnly: opts?.optimizedOnly ?? false,\n }\n\n if (opts) {\n if (`where` in opts) {\n const snapshotWhereExp = opts.where\n if (stateOpts.where) {\n // Combine the two where expressions\n const subWhereExp = stateOpts.where\n const combinedWhereExp = and(subWhereExp, snapshotWhereExp)\n stateOpts.where = combinedWhereExp\n } else {\n stateOpts.where = snapshotWhereExp\n }\n }\n } else {\n // No options provided so it's loading the entire initial state\n this.loadedInitialState = true\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n const loadOptions: LoadSubsetOptions = {\n where: stateOpts.where,\n subscription: this,\n // Include orderBy and limit if provided so sync layer can optimize the query\n orderBy: opts?.orderBy,\n limit: opts?.limit,\n }\n const syncResult = this.collection._sync.loadSubset(loadOptions)\n\n // Pass the raw loadSubset result to the caller for external tracking\n opts?.onLoadSubsetResult?.(syncResult)\n\n // Track this loadSubset call so we can unload it later\n this.loadedSubsets.push(loadOptions)\n\n const trackLoadSubsetPromise = opts?.trackLoadSubsetPromise ?? true\n if (trackLoadSubsetPromise) {\n this.trackLoadSubsetPromise(syncResult)\n }\n\n // Also load data immediately from the collection\n const snapshot = this.collection.currentStateAsChanges(stateOpts)\n\n if (snapshot === undefined) {\n // Couldn't load from indexes\n return false\n }\n\n // Only send changes that have not been sent yet\n const filteredSnapshot = snapshot.filter(\n (change) => !this.sentKeys.has(change.key),\n )\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of filteredSnapshot) {\n this.sentKeys.add(change.key)\n }\n\n this.snapshotSent = true\n this.callback(filteredSnapshot)\n return true\n }\n\n /**\n * Sends a snapshot that fulfills the `where` clause and all rows are bigger or equal to the cursor.\n * Requires a range index to be set with `setOrderByIndex` prior to calling this method.\n * It uses that range index to load the items in the order of the index.\n *\n * For multi-column orderBy:\n * - Uses first value from `minValues` for LOCAL index operations (wide bounds, ensures no missed rows)\n * - Uses all `minValues` to build a precise composite cursor for SYNC layer loadSubset\n *\n * Note 1: it may load more rows than the provided LIMIT because it loads all values equal to the first cursor value + limit values greater.\n * This is needed to ensure that it does not accidentally skip duplicate values when the limit falls in the middle of some duplicated values.\n * Note 2: it does not send keys that have already been sent before.\n */\n requestLimitedSnapshot({\n orderBy,\n limit,\n minValues,\n offset,\n trackLoadSubsetPromise: shouldTrackLoadSubsetPromise = true,\n onLoadSubsetResult,\n }: RequestLimitedSnapshotOptions) {\n if (!limit) throw new Error(`limit is required`)\n\n if (!this.orderByIndex) {\n throw new Error(\n `Ordered snapshot was requested but no index was found. You have to call setOrderByIndex before requesting an ordered snapshot.`,\n )\n }\n\n // Check if minValues has a first element (regardless of its value)\n // This distinguishes between \"no min value provided\" vs \"min value is undefined\"\n const hasMinValue = minValues !== undefined && minValues.length > 0\n // Derive first column value from minValues (used for local index operations)\n const minValue = minValues?.[0]\n // Cast for index operations (index expects string | number)\n const minValueForIndex = minValue as string | number | undefined\n\n const index = this.orderByIndex\n const where = this.options.whereExpression\n const whereFilterFn = where\n ? createFilterFunctionFromExpression(where)\n : undefined\n\n const filterFn = (key: string | number | undefined): boolean => {\n if (key !== undefined && this.sentKeys.has(key)) {\n return false\n }\n\n const value = this.collection.get(key)\n if (value === undefined) {\n return false\n }\n\n return whereFilterFn?.(value) ?? true\n }\n\n let biggestObservedValue = minValueForIndex\n const changes: Array<ChangeMessage<any, string | number>> = []\n\n // If we have a minValue we need to handle the case\n // where there might be duplicate values equal to minValue that we need to include\n // because we can have data like this: [1, 2, 3, 3, 3, 4, 5]\n // so if minValue is 3 then the previous snapshot may not have included all 3s\n // e.g. if it was offset 0 and limit 3 it would only have loaded the first 3\n // so we load all rows equal to minValue first, to be sure we don't skip any duplicate values\n //\n // For multi-column orderBy, we use the first column value for index operations (wide bounds)\n // This may load some duplicates but ensures we never miss any rows.\n let keys: Array<string | number> = []\n if (hasMinValue) {\n // First, get all items with the same FIRST COLUMN value as minValue\n // This provides wide bounds for the local index\n const { expression } = orderBy[0]!\n const allRowsWithMinValue = this.collection.currentStateAsChanges({\n where: eq(expression, new Value(minValueForIndex)),\n })\n\n if (allRowsWithMinValue) {\n const keysWithMinValue = allRowsWithMinValue\n .map((change) => change.key)\n .filter((key) => !this.sentKeys.has(key) && filterFn(key))\n\n // Add items with the minValue first\n keys.push(...keysWithMinValue)\n\n // Then get items greater than minValue\n const keysGreaterThanMin = index.take(\n limit - keys.length,\n minValueForIndex!,\n filterFn,\n )\n keys.push(...keysGreaterThanMin)\n } else {\n keys = index.take(limit, minValueForIndex!, filterFn)\n }\n } else {\n // No min value provided, start from the beginning\n keys = index.takeFromStart(limit, filterFn)\n }\n\n const valuesNeeded = () => Math.max(limit - changes.length, 0)\n const collectionExhausted = () => keys.length === 0\n\n // Create a value extractor for the orderBy field to properly track the biggest indexed value\n const orderByExpression = orderBy[0]!.expression\n const valueExtractor =\n orderByExpression.type === `ref`\n ? compileExpression(new PropRef(orderByExpression.path), true)\n : null\n\n while (valuesNeeded() > 0 && !collectionExhausted()) {\n const insertedKeys = new Set<string | number>() // Track keys we add to `changes` in this iteration\n\n for (const key of keys) {\n const value = this.collection.get(key)!\n changes.push({\n type: `insert`,\n key,\n value,\n })\n // Extract the indexed value (e.g., salary) from the row, not the full row\n // This is needed for index.take() to work correctly with the BTree comparator\n biggestObservedValue = valueExtractor ? valueExtractor(value) : value\n insertedKeys.add(key) // Track this key\n }\n\n keys = index.take(valuesNeeded(), biggestObservedValue!, filterFn)\n }\n\n // Track row count for offset-based pagination (before sending to callback)\n // Use the current count as the offset for this load\n const currentOffset = this.limitedSnapshotRowCount\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of changes) {\n this.sentKeys.add(change.key)\n }\n\n this.callback(changes)\n\n // Update the row count and last key after sending (for next call's offset/cursor)\n this.limitedSnapshotRowCount += changes.length\n if (changes.length > 0) {\n this.lastSentKey = changes[changes.length - 1]!.key\n }\n\n // Build cursor expressions for sync layer loadSubset\n // The cursor expressions are separate from the main where clause\n // so the sync layer can choose cursor-based or offset-based pagination\n let cursorExpressions:\n | {\n whereFrom: BasicExpression<boolean>\n whereCurrent: BasicExpression<boolean>\n lastKey?: string | number\n }\n | undefined\n\n if (minValues !== undefined && minValues.length > 0) {\n const whereFromCursor = buildCursor(orderBy, minValues)\n\n if (whereFromCursor) {\n const { expression } = orderBy[0]!\n const minValue = minValues[0]\n\n // Build the whereCurrent expression for the first orderBy column\n // For Date values, we need to handle precision differences between JS (ms) and backends (μs)\n // A JS Date represents a 1ms range, so we query for all values within that range\n let whereCurrentCursor: BasicExpression<boolean>\n if (minValue instanceof Date) {\n const minValuePlus1ms = new Date(minValue.getTime() + 1)\n whereCurrentCursor = and(\n gte(expression, new Value(minValue)),\n lt(expression, new Value(minValuePlus1ms)),\n )\n } else {\n whereCurrentCursor = eq(expression, new Value(minValue))\n }\n\n cursorExpressions = {\n whereFrom: whereFromCursor,\n whereCurrent: whereCurrentCursor,\n lastKey: this.lastSentKey,\n }\n }\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n // Note: `where` does NOT include cursor expressions - they are passed separately\n // The sync layer can choose to use cursor-based or offset-based pagination\n const loadOptions: LoadSubsetOptions = {\n where, // Main filter only, no cursor\n limit,\n orderBy,\n cursor: cursorExpressions, // Cursor expressions passed separately\n offset: offset ?? currentOffset, // Use provided offset, or auto-tracked offset\n subscription: this,\n }\n const syncResult = this.collection._sync.loadSubset(loadOptions)\n\n // Pass the raw loadSubset result to the caller for external tracking\n onLoadSubsetResult?.(syncResult)\n\n // Track this loadSubset call\n this.loadedSubsets.push(loadOptions)\n if (shouldTrackLoadSubsetPromise) {\n this.trackLoadSubsetPromise(syncResult)\n }\n }\n\n // TODO: also add similar test but that checks that it can also load it from the collection's loadSubset function\n // and that that also works properly (i.e. does not skip duplicate values)\n\n /**\n * Filters and flips changes for keys that have not been sent yet.\n * Deletes are filtered out for keys that have not been sent yet.\n * Updates are flipped into inserts for keys that have not been sent yet.\n * Duplicate inserts are filtered out to prevent D2 multiplicity > 1.\n */\n private filterAndFlipChanges(changes: Array<ChangeMessage<any, any>>) {\n if (this.loadedInitialState || this.skipFiltering) {\n // We loaded the entire initial state or filtering is explicitly skipped\n // so no need to filter or flip changes\n return changes\n }\n\n // When buffering for truncate, we need all changes (including deletes) to pass through.\n // This is important because:\n // 1. If loadedInitialState was previously true, sentKeys will be empty\n // (trackSentKeys early-returns when loadedInitialState is true)\n // 2. The truncate deletes are for keys that WERE sent to the subscriber\n // 3. We're collecting all changes atomically, so filtering doesn't make sense\n const skipDeleteFilter = this.isBufferingForTruncate\n\n const newChanges = []\n for (const change of changes) {\n let newChange = change\n const keyInSentKeys = this.sentKeys.has(change.key)\n\n if (!keyInSentKeys) {\n if (change.type === `update`) {\n newChange = { ...change, type: `insert`, previousValue: undefined }\n } else if (change.type === `delete`) {\n // Filter out deletes for keys that have not been sent,\n // UNLESS we're buffering for truncate (where all deletes should pass through)\n if (!skipDeleteFilter) {\n continue\n }\n }\n this.sentKeys.add(change.key)\n } else {\n // Key was already sent - handle based on change type\n if (change.type === `insert`) {\n // Filter out duplicate inserts - the key was already inserted.\n // This prevents D2 multiplicity from going above 1, which would\n // cause deletes to not properly remove items (multiplicity would\n // go from 2 to 1 instead of 1 to 0).\n continue\n } else if (change.type === `delete`) {\n // Remove from sentKeys so future inserts for this key are allowed\n // (e.g., after truncate + reinsert)\n this.sentKeys.delete(change.key)\n }\n }\n newChanges.push(newChange)\n }\n return newChanges\n }\n\n private trackSentKeys(changes: Array<ChangeMessage<any, string | number>>) {\n if (this.loadedInitialState || this.skipFiltering) {\n // No need to track sent keys if we loaded the entire state or filtering is skipped.\n // Since filtering won't be applied, all keys are effectively \"observed\".\n return\n }\n\n for (const change of changes) {\n if (change.type === `delete`) {\n this.sentKeys.delete(change.key)\n } else {\n this.sentKeys.add(change.key)\n }\n }\n\n // Keep the limited snapshot offset in sync with keys we've actually sent.\n // This matters when loadSubset resolves asynchronously and requestLimitedSnapshot\n // didn't have local rows to count yet.\n if (this.orderByIndex) {\n this.limitedSnapshotRowCount = Math.max(\n this.limitedSnapshotRowCount,\n this.sentKeys.size,\n )\n }\n }\n\n /**\n * Mark that the subscription should not filter any changes.\n * This is used when includeInitialState is explicitly set to false,\n * meaning the caller doesn't want initial state but does want ALL future changes.\n */\n markAllStateAsSeen() {\n this.skipFiltering = true\n }\n\n unsubscribe() {\n // Clean up truncate event listener\n this.truncateCleanup?.()\n this.truncateCleanup = undefined\n\n // Clean up truncate buffer state\n this.isBufferingForTruncate = false\n this.truncateBuffer = []\n this.pendingTruncateRefetches.clear()\n\n // Unload all subsets that this subscription loaded\n // We pass the exact same LoadSubsetOptions we used for loadSubset\n for (const options of this.loadedSubsets) {\n this.collection._sync.unloadSubset(options)\n }\n this.loadedSubsets = []\n\n this.emitInner(`unsubscribed`, {\n type: `unsubscribed`,\n subscription: this,\n })\n // Clear all event listeners to prevent memory leaks\n this.clearListeners()\n }\n}\n"],"names":["EventEmitter","ensureIndexForExpression","createFilteredCallback","and","createFilterFunctionFromExpression","eq","Value","compileExpression","PropRef","buildCursor","minValue","gte","lt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"subscription.cjs","sources":["../../../src/collection/subscription.ts"],"sourcesContent":["import { ensureIndexForExpression } from '../indexes/auto-index.js'\nimport { and, eq, gte, lt } from '../query/builder/functions.js'\nimport { PropRef, Value } from '../query/ir.js'\nimport { EventEmitter } from '../event-emitter.js'\nimport { compileExpression } from '../query/compiler/evaluators.js'\nimport { buildCursor } from '../utils/cursor.js'\nimport {\n createFilterFunctionFromExpression,\n createFilteredCallback,\n} from './change-events.js'\nimport type { BasicExpression, OrderBy } from '../query/ir.js'\nimport type { IndexInterface } from '../indexes/base-index.js'\nimport type {\n ChangeMessage,\n LoadSubsetOptions,\n Subscription,\n SubscriptionEvents,\n SubscriptionStatus,\n SubscriptionUnsubscribedEvent,\n} from '../types.js'\nimport type { CollectionImpl } from './index.js'\n\ntype RequestSnapshotOptions = {\n where?: BasicExpression<boolean>\n optimizedOnly?: boolean\n trackLoadSubsetPromise?: boolean\n /** Optional orderBy to pass to loadSubset for backend optimization */\n orderBy?: OrderBy\n /** Optional limit to pass to loadSubset for backend optimization */\n limit?: number\n /** Callback that receives the raw loadSubset result for external tracking */\n onLoadSubsetResult?: (result: Promise<void> | true) => void\n}\n\ntype RequestLimitedSnapshotOptions = {\n orderBy: OrderBy\n limit: number\n /** All column values for cursor (first value used for local index, all values for sync layer) */\n minValues?: Array<unknown>\n /** Row offset for offset-based pagination (passed to sync layer) */\n offset?: number\n /** Whether to track the loadSubset promise on this subscription (default: true) */\n trackLoadSubsetPromise?: boolean\n /** Callback that receives the raw loadSubset result for external tracking */\n onLoadSubsetResult?: (result: Promise<void> | true) => void\n}\n\ntype CollectionSubscriptionOptions = {\n includeInitialState?: boolean\n /** Pre-compiled expression for filtering changes */\n whereExpression?: BasicExpression<boolean>\n /** Callback to call when the subscription is unsubscribed */\n onUnsubscribe?: (event: SubscriptionUnsubscribedEvent) => void\n}\n\nexport class CollectionSubscription\n extends EventEmitter<SubscriptionEvents>\n implements Subscription\n{\n private loadedInitialState = false\n\n // Flag to skip filtering in filterAndFlipChanges.\n // This is separate from loadedInitialState because we want to allow\n // requestSnapshot to still work even when filtering is skipped.\n private skipFiltering = false\n\n // Flag to indicate that we have sent at least 1 snapshot.\n // While `snapshotSent` is false we filter out all changes from subscription to the collection.\n private snapshotSent = false\n\n /**\n * Track all loadSubset calls made by this subscription so we can unload them on cleanup.\n * We store the exact LoadSubsetOptions we passed to loadSubset to ensure symmetric unload.\n */\n private loadedSubsets: Array<LoadSubsetOptions> = []\n\n // Keep track of the keys we've sent (needed for join and orderBy optimizations)\n private sentKeys = new Set<string | number>()\n\n // Track the count of rows sent via requestLimitedSnapshot for offset-based pagination\n private limitedSnapshotRowCount = 0\n\n // Track the last key sent via requestLimitedSnapshot for cursor-based pagination\n private lastSentKey: string | number | undefined\n\n private filteredCallback: (changes: Array<ChangeMessage<any, any>>) => void\n\n private orderByIndex: IndexInterface<string | number> | undefined\n\n // Status tracking\n private _status: SubscriptionStatus = `ready`\n private pendingLoadSubsetPromises: Set<Promise<void>> = new Set()\n\n // Cleanup function for truncate event listener\n private truncateCleanup: (() => void) | undefined\n\n // Truncate buffering state\n // When a truncate occurs, we buffer changes until all loadSubset refetches complete\n // This prevents a flash of missing content between deletes and new inserts\n private isBufferingForTruncate = false\n private truncateBuffer: Array<Array<ChangeMessage<any, any>>> = []\n private pendingTruncateRefetches: Set<Promise<void>> = new Set()\n\n public get status(): SubscriptionStatus {\n return this._status\n }\n\n constructor(\n private collection: CollectionImpl<any, any, any, any, any>,\n private callback: (changes: Array<ChangeMessage<any, any>>) => void,\n private options: CollectionSubscriptionOptions,\n ) {\n super()\n if (options.onUnsubscribe) {\n this.on(`unsubscribed`, (event) => options.onUnsubscribe!(event))\n }\n\n // Auto-index for where expressions if enabled\n if (options.whereExpression) {\n ensureIndexForExpression(options.whereExpression, this.collection)\n }\n\n const callbackWithSentKeysTracking = (\n changes: Array<ChangeMessage<any, any>>,\n ) => {\n callback(changes)\n this.trackSentKeys(changes)\n }\n\n this.callback = callbackWithSentKeysTracking\n\n // Create a filtered callback if where clause is provided\n this.filteredCallback = options.whereExpression\n ? createFilteredCallback(this.callback, options)\n : this.callback\n\n // Listen for truncate events to re-request data after must-refetch\n // When a truncate happens (e.g., from a 409 must-refetch), all collection data is cleared.\n // We need to re-request all previously loaded subsets to repopulate the data.\n this.truncateCleanup = this.collection.on(`truncate`, () => {\n this.handleTruncate()\n })\n }\n\n /**\n * Handle collection truncate event by resetting state and re-requesting subsets.\n * This is called when the sync layer receives a must-refetch and clears all data.\n *\n * To prevent a flash of missing content, we buffer all changes (deletes from truncate\n * and inserts from refetch) until all loadSubset promises resolve, then emit them together.\n */\n private handleTruncate() {\n // Copy the loaded subsets before clearing (we'll re-request them)\n const subsetsToReload = [...this.loadedSubsets]\n\n // Only buffer if there's an actual loadSubset handler that can do async work.\n // Without a loadSubset handler, there's nothing to re-request and no reason to buffer.\n // This prevents unnecessary buffering in eager sync mode or when loadSubset isn't implemented.\n const hasLoadSubsetHandler = this.collection._sync.syncLoadSubsetFn !== null\n\n // If there are no subsets to reload OR no loadSubset handler, just reset state\n if (subsetsToReload.length === 0 || !hasLoadSubsetHandler) {\n this.snapshotSent = false\n this.loadedInitialState = false\n this.limitedSnapshotRowCount = 0\n this.lastSentKey = undefined\n this.loadedSubsets = []\n return\n }\n\n // Start buffering BEFORE we receive the delete events from the truncate commit\n // This ensures we capture both the deletes and subsequent inserts\n this.isBufferingForTruncate = true\n this.truncateBuffer = []\n this.pendingTruncateRefetches.clear()\n\n // Reset snapshot/pagination tracking state\n // Note: We don't need to populate sentKeys here because filterAndFlipChanges\n // will skip the delete filter when isBufferingForTruncate is true\n this.snapshotSent = false\n this.loadedInitialState = false\n this.limitedSnapshotRowCount = 0\n this.lastSentKey = undefined\n\n // Clear the loadedSubsets array since we're re-requesting fresh\n this.loadedSubsets = []\n\n // Defer the loadSubset calls to a microtask so the truncate commit's delete events\n // are buffered BEFORE the loadSubset calls potentially trigger nested commits.\n // This ensures correct event ordering: deletes first, then inserts.\n queueMicrotask(() => {\n // Check if we were unsubscribed while waiting\n if (!this.isBufferingForTruncate) {\n return\n }\n\n // Re-request all previously loaded subsets and track their promises\n for (const options of subsetsToReload) {\n const syncResult = this.collection._sync.loadSubset(options)\n\n // Track this loadSubset call so we can unload it later\n this.loadedSubsets.push(options)\n this.trackLoadSubsetPromise(syncResult)\n\n // Track the promise for buffer flushing\n if (syncResult instanceof Promise) {\n this.pendingTruncateRefetches.add(syncResult)\n syncResult\n .catch(() => {\n // Ignore errors - we still want to flush the buffer even if some requests fail\n })\n .finally(() => {\n this.pendingTruncateRefetches.delete(syncResult)\n this.checkTruncateRefetchComplete()\n })\n }\n }\n\n // If all loadSubset calls were synchronous (returned true), flush now\n // At this point, delete events have already been buffered from the truncate commit\n if (this.pendingTruncateRefetches.size === 0) {\n this.flushTruncateBuffer()\n }\n })\n }\n\n /**\n * Check if all truncate refetch promises have completed and flush buffer if so\n */\n private checkTruncateRefetchComplete() {\n if (\n this.pendingTruncateRefetches.size === 0 &&\n this.isBufferingForTruncate\n ) {\n this.flushTruncateBuffer()\n }\n }\n\n /**\n * Flush the truncate buffer, emitting all buffered changes to the callback\n */\n private flushTruncateBuffer() {\n this.isBufferingForTruncate = false\n\n // Flatten all buffered changes into a single array for atomic emission\n // This ensures consumers see all truncate changes (deletes + inserts) in one callback\n const merged = this.truncateBuffer.flat()\n if (merged.length > 0) {\n this.filteredCallback(merged)\n }\n\n this.truncateBuffer = []\n }\n\n setOrderByIndex(index: IndexInterface<any>) {\n this.orderByIndex = index\n }\n\n /**\n * Set subscription status and emit events if changed\n */\n private setStatus(newStatus: SubscriptionStatus) {\n if (this._status === newStatus) {\n return // No change\n }\n\n const previousStatus = this._status\n this._status = newStatus\n\n // Emit status:change event\n this.emitInner(`status:change`, {\n type: `status:change`,\n subscription: this,\n previousStatus,\n status: newStatus,\n })\n\n // Emit specific status event\n const eventKey: `status:${SubscriptionStatus}` = `status:${newStatus}`\n this.emitInner(eventKey, {\n type: eventKey,\n subscription: this,\n previousStatus,\n status: newStatus,\n } as SubscriptionEvents[typeof eventKey])\n }\n\n /**\n * Track a loadSubset promise and manage loading status\n */\n private trackLoadSubsetPromise(syncResult: Promise<void> | true) {\n // Track the promise if it's actually a promise (async work)\n if (syncResult instanceof Promise) {\n this.pendingLoadSubsetPromises.add(syncResult)\n this.setStatus(`loadingSubset`)\n\n syncResult.finally(() => {\n this.pendingLoadSubsetPromises.delete(syncResult)\n if (this.pendingLoadSubsetPromises.size === 0) {\n this.setStatus(`ready`)\n }\n })\n }\n }\n\n hasLoadedInitialState() {\n return this.loadedInitialState\n }\n\n hasSentAtLeastOneSnapshot() {\n return this.snapshotSent\n }\n\n emitEvents(changes: Array<ChangeMessage<any, any>>) {\n const newChanges = this.filterAndFlipChanges(changes)\n\n if (this.isBufferingForTruncate) {\n // Buffer the changes instead of emitting immediately\n // This prevents a flash of missing content during truncate/refetch\n if (newChanges.length > 0) {\n this.truncateBuffer.push(newChanges)\n }\n } else {\n this.filteredCallback(newChanges)\n }\n }\n\n /**\n * Sends the snapshot to the callback.\n * Returns a boolean indicating if it succeeded.\n * It can only fail if there is no index to fulfill the request\n * and the optimizedOnly option is set to true,\n * or, the entire state was already loaded.\n */\n requestSnapshot(opts?: RequestSnapshotOptions): boolean {\n if (this.loadedInitialState) {\n // Subscription was deoptimized so we already sent the entire initial state\n return false\n }\n\n const stateOpts: RequestSnapshotOptions = {\n where: this.options.whereExpression,\n optimizedOnly: opts?.optimizedOnly ?? false,\n }\n\n if (opts) {\n if (`where` in opts) {\n const snapshotWhereExp = opts.where\n if (stateOpts.where) {\n // Combine the two where expressions\n const subWhereExp = stateOpts.where\n const combinedWhereExp = and(subWhereExp, snapshotWhereExp)\n stateOpts.where = combinedWhereExp\n } else {\n stateOpts.where = snapshotWhereExp\n }\n }\n } else {\n // No options provided so it's loading the entire initial state\n this.loadedInitialState = true\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n const loadOptions: LoadSubsetOptions = {\n where: stateOpts.where,\n subscription: this,\n // Include orderBy and limit if provided so sync layer can optimize the query\n orderBy: opts?.orderBy,\n limit: opts?.limit,\n }\n const syncResult = this.collection._sync.loadSubset(loadOptions)\n\n // Pass the raw loadSubset result to the caller for external tracking\n opts?.onLoadSubsetResult?.(syncResult)\n\n // Track this loadSubset call so we can unload it later\n this.loadedSubsets.push(loadOptions)\n\n const trackLoadSubsetPromise = opts?.trackLoadSubsetPromise ?? true\n if (trackLoadSubsetPromise) {\n this.trackLoadSubsetPromise(syncResult)\n }\n\n // Also load data immediately from the collection\n const snapshot = this.collection.currentStateAsChanges(stateOpts)\n\n if (snapshot === undefined) {\n // Couldn't load from indexes\n return false\n }\n\n // Only send changes that have not been sent yet\n const filteredSnapshot = snapshot.filter(\n (change) => !this.sentKeys.has(change.key),\n )\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of filteredSnapshot) {\n this.sentKeys.add(change.key)\n }\n\n this.snapshotSent = true\n this.callback(filteredSnapshot)\n return true\n }\n\n /**\n * Sends a snapshot that fulfills the `where` clause and all rows are bigger or equal to the cursor.\n * Requires a range index to be set with `setOrderByIndex` prior to calling this method.\n * It uses that range index to load the items in the order of the index.\n *\n * For multi-column orderBy:\n * - Uses first value from `minValues` for LOCAL index operations (wide bounds, ensures no missed rows)\n * - Uses all `minValues` to build a precise composite cursor for SYNC layer loadSubset\n *\n * Note 1: it may load more rows than the provided LIMIT because it loads all values equal to the first cursor value + limit values greater.\n * This is needed to ensure that it does not accidentally skip duplicate values when the limit falls in the middle of some duplicated values.\n * Note 2: it does not send keys that have already been sent before.\n */\n requestLimitedSnapshot({\n orderBy,\n limit,\n minValues,\n offset,\n trackLoadSubsetPromise: shouldTrackLoadSubsetPromise = true,\n onLoadSubsetResult,\n }: RequestLimitedSnapshotOptions) {\n if (!limit) throw new Error(`limit is required`)\n\n if (!this.orderByIndex) {\n throw new Error(\n `Ordered snapshot was requested but no index was found. You have to call setOrderByIndex before requesting an ordered snapshot.`,\n )\n }\n\n // Check if minValues has a first element (regardless of its value)\n // This distinguishes between \"no min value provided\" vs \"min value is undefined\"\n const hasMinValue = minValues !== undefined && minValues.length > 0\n // Derive first column value from minValues (used for local index operations)\n const minValue = minValues?.[0]\n // Cast for index operations (index expects string | number)\n const minValueForIndex = minValue as string | number | undefined\n\n const index = this.orderByIndex\n const where = this.options.whereExpression\n const whereFilterFn = where\n ? createFilterFunctionFromExpression(where)\n : undefined\n\n const filterFn = (key: string | number | undefined): boolean => {\n if (key !== undefined && this.sentKeys.has(key)) {\n return false\n }\n\n const value = this.collection.get(key)\n if (value === undefined) {\n return false\n }\n\n return whereFilterFn?.(value) ?? true\n }\n\n let biggestObservedValue = minValueForIndex\n const changes: Array<ChangeMessage<any, string | number>> = []\n\n // If we have a minValue we need to handle the case\n // where there might be duplicate values equal to minValue that we need to include\n // because we can have data like this: [1, 2, 3, 3, 3, 4, 5]\n // so if minValue is 3 then the previous snapshot may not have included all 3s\n // e.g. if it was offset 0 and limit 3 it would only have loaded the first 3\n // so we load all rows equal to minValue first, to be sure we don't skip any duplicate values\n //\n // For multi-column orderBy, we use the first column value for index operations (wide bounds)\n // This may load some duplicates but ensures we never miss any rows.\n let keys: Array<string | number> = []\n if (hasMinValue) {\n // First, get all items with the same FIRST COLUMN value as minValue\n // This provides wide bounds for the local index\n const { expression } = orderBy[0]!\n const allRowsWithMinValue = this.collection.currentStateAsChanges({\n where: eq(expression, new Value(minValueForIndex)),\n })\n\n if (allRowsWithMinValue) {\n const keysWithMinValue = allRowsWithMinValue\n .map((change) => change.key)\n .filter((key) => !this.sentKeys.has(key) && filterFn(key))\n\n // Add items with the minValue first\n keys.push(...keysWithMinValue)\n\n // Then get items greater than minValue\n const keysGreaterThanMin = index.take(\n limit - keys.length,\n minValueForIndex!,\n filterFn,\n )\n keys.push(...keysGreaterThanMin)\n } else {\n keys = index.take(limit, minValueForIndex!, filterFn)\n }\n } else {\n // No min value provided, start from the beginning\n keys = index.takeFromStart(limit, filterFn)\n }\n\n const valuesNeeded = () => Math.max(limit - changes.length, 0)\n const collectionExhausted = () => keys.length === 0\n\n // Create a value extractor for the orderBy field to properly track the biggest indexed value\n const orderByExpression = orderBy[0]!.expression\n const valueExtractor =\n orderByExpression.type === `ref`\n ? compileExpression(new PropRef(orderByExpression.path), true)\n : null\n\n while (valuesNeeded() > 0 && !collectionExhausted()) {\n const insertedKeys = new Set<string | number>() // Track keys we add to `changes` in this iteration\n\n for (const key of keys) {\n const value = this.collection.get(key)!\n changes.push({\n type: `insert`,\n key,\n value,\n })\n // Extract the indexed value (e.g., salary) from the row, not the full row\n // This is needed for index.take() to work correctly with the BTree comparator\n biggestObservedValue = valueExtractor ? valueExtractor(value) : value\n insertedKeys.add(key) // Track this key\n }\n\n keys = index.take(valuesNeeded(), biggestObservedValue!, filterFn)\n }\n\n // Track row count for offset-based pagination (before sending to callback)\n // Use the current count as the offset for this load\n const currentOffset = this.limitedSnapshotRowCount\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of changes) {\n this.sentKeys.add(change.key)\n }\n\n this.callback(changes)\n\n // Update the row count and last key after sending (for next call's offset/cursor)\n this.limitedSnapshotRowCount += changes.length\n if (changes.length > 0) {\n this.lastSentKey = changes[changes.length - 1]!.key\n }\n\n // Build cursor expressions for sync layer loadSubset\n // The cursor expressions are separate from the main where clause\n // so the sync layer can choose cursor-based or offset-based pagination\n let cursorExpressions:\n | {\n whereFrom: BasicExpression<boolean>\n whereCurrent: BasicExpression<boolean>\n lastKey?: string | number\n }\n | undefined\n\n if (minValues !== undefined && minValues.length > 0) {\n const whereFromCursor = buildCursor(orderBy, minValues)\n\n if (whereFromCursor) {\n const { expression } = orderBy[0]!\n const cursorMinValue = minValues[0]\n\n // Build the whereCurrent expression for the first orderBy column\n // For Date values, we need to handle precision differences between JS (ms) and backends (μs)\n // A JS Date represents a 1ms range, so we query for all values within that range\n let whereCurrentCursor: BasicExpression<boolean>\n if (cursorMinValue instanceof Date) {\n const cursorMinValuePlus1ms = new Date(cursorMinValue.getTime() + 1)\n whereCurrentCursor = and(\n gte(expression, new Value(cursorMinValue)),\n lt(expression, new Value(cursorMinValuePlus1ms)),\n )\n } else {\n whereCurrentCursor = eq(expression, new Value(cursorMinValue))\n }\n\n cursorExpressions = {\n whereFrom: whereFromCursor,\n whereCurrent: whereCurrentCursor,\n lastKey: this.lastSentKey,\n }\n }\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n // Note: `where` does NOT include cursor expressions - they are passed separately\n // The sync layer can choose to use cursor-based or offset-based pagination\n const loadOptions: LoadSubsetOptions = {\n where, // Main filter only, no cursor\n limit,\n orderBy,\n cursor: cursorExpressions, // Cursor expressions passed separately\n offset: offset ?? currentOffset, // Use provided offset, or auto-tracked offset\n subscription: this,\n }\n const syncResult = this.collection._sync.loadSubset(loadOptions)\n\n // Pass the raw loadSubset result to the caller for external tracking\n onLoadSubsetResult?.(syncResult)\n\n // Track this loadSubset call\n this.loadedSubsets.push(loadOptions)\n if (shouldTrackLoadSubsetPromise) {\n this.trackLoadSubsetPromise(syncResult)\n }\n }\n\n // TODO: also add similar test but that checks that it can also load it from the collection's loadSubset function\n // and that that also works properly (i.e. does not skip duplicate values)\n\n /**\n * Filters and flips changes for keys that have not been sent yet.\n * Deletes are filtered out for keys that have not been sent yet.\n * Updates are flipped into inserts for keys that have not been sent yet.\n * Duplicate inserts are filtered out to prevent D2 multiplicity > 1.\n */\n private filterAndFlipChanges(changes: Array<ChangeMessage<any, any>>) {\n if (this.loadedInitialState || this.skipFiltering) {\n // We loaded the entire initial state or filtering is explicitly skipped\n // so no need to filter or flip changes\n return changes\n }\n\n // When buffering for truncate, we need all changes (including deletes) to pass through.\n // This is important because:\n // 1. If loadedInitialState was previously true, sentKeys will be empty\n // (trackSentKeys early-returns when loadedInitialState is true)\n // 2. The truncate deletes are for keys that WERE sent to the subscriber\n // 3. We're collecting all changes atomically, so filtering doesn't make sense\n const skipDeleteFilter = this.isBufferingForTruncate\n\n const newChanges = []\n for (const change of changes) {\n let newChange = change\n const keyInSentKeys = this.sentKeys.has(change.key)\n\n if (!keyInSentKeys) {\n if (change.type === `update`) {\n newChange = { ...change, type: `insert`, previousValue: undefined }\n } else if (change.type === `delete`) {\n // Filter out deletes for keys that have not been sent,\n // UNLESS we're buffering for truncate (where all deletes should pass through)\n if (!skipDeleteFilter) {\n continue\n }\n }\n this.sentKeys.add(change.key)\n } else {\n // Key was already sent - handle based on change type\n if (change.type === `insert`) {\n // Filter out duplicate inserts - the key was already inserted.\n // This prevents D2 multiplicity from going above 1, which would\n // cause deletes to not properly remove items (multiplicity would\n // go from 2 to 1 instead of 1 to 0).\n continue\n } else if (change.type === `delete`) {\n // Remove from sentKeys so future inserts for this key are allowed\n // (e.g., after truncate + reinsert)\n this.sentKeys.delete(change.key)\n }\n }\n newChanges.push(newChange)\n }\n return newChanges\n }\n\n private trackSentKeys(changes: Array<ChangeMessage<any, string | number>>) {\n if (this.loadedInitialState || this.skipFiltering) {\n // No need to track sent keys if we loaded the entire state or filtering is skipped.\n // Since filtering won't be applied, all keys are effectively \"observed\".\n return\n }\n\n for (const change of changes) {\n if (change.type === `delete`) {\n this.sentKeys.delete(change.key)\n } else {\n this.sentKeys.add(change.key)\n }\n }\n\n // Keep the limited snapshot offset in sync with keys we've actually sent.\n // This matters when loadSubset resolves asynchronously and requestLimitedSnapshot\n // didn't have local rows to count yet.\n if (this.orderByIndex) {\n this.limitedSnapshotRowCount = Math.max(\n this.limitedSnapshotRowCount,\n this.sentKeys.size,\n )\n }\n }\n\n /**\n * Mark that the subscription should not filter any changes.\n * This is used when includeInitialState is explicitly set to false,\n * meaning the caller doesn't want initial state but does want ALL future changes.\n */\n markAllStateAsSeen() {\n this.skipFiltering = true\n }\n\n unsubscribe() {\n // Clean up truncate event listener\n this.truncateCleanup?.()\n this.truncateCleanup = undefined\n\n // Clean up truncate buffer state\n this.isBufferingForTruncate = false\n this.truncateBuffer = []\n this.pendingTruncateRefetches.clear()\n\n // Unload all subsets that this subscription loaded\n // We pass the exact same LoadSubsetOptions we used for loadSubset\n for (const options of this.loadedSubsets) {\n this.collection._sync.unloadSubset(options)\n }\n this.loadedSubsets = []\n\n this.emitInner(`unsubscribed`, {\n type: `unsubscribed`,\n subscription: this,\n })\n // Clear all event listeners to prevent memory leaks\n this.clearListeners()\n }\n}\n"],"names":["EventEmitter","ensureIndexForExpression","createFilteredCallback","and","createFilterFunctionFromExpression","eq","Value","compileExpression","PropRef","buildCursor","gte","lt"],"mappings":"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"}
|
package/dist/cjs/errors.cjs
CHANGED
|
@@ -340,6 +340,13 @@ class DistinctRequiresSelectError extends QueryCompilationError {
|
|
|
340
340
|
super(`DISTINCT requires a SELECT clause.`);
|
|
341
341
|
}
|
|
342
342
|
}
|
|
343
|
+
class FnSelectWithGroupByError extends QueryCompilationError {
|
|
344
|
+
constructor() {
|
|
345
|
+
super(
|
|
346
|
+
`fn.select() cannot be used with groupBy(). groupBy requires the compiler to statically analyze aggregate functions (count, sum, max, etc.) in the SELECT clause, which is not possible with fn.select() since it is an opaque function. Use .select() instead of .fn.select() when combining with groupBy().`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
343
350
|
class HavingRequiresGroupByError extends QueryCompilationError {
|
|
344
351
|
constructor() {
|
|
345
352
|
super(`HAVING clause requires GROUP BY clause`);
|
|
@@ -579,6 +586,7 @@ exports.DuplicateDbInstanceError = DuplicateDbInstanceError;
|
|
|
579
586
|
exports.DuplicateKeyError = DuplicateKeyError;
|
|
580
587
|
exports.DuplicateKeySyncError = DuplicateKeySyncError;
|
|
581
588
|
exports.EmptyReferencePathError = EmptyReferencePathError;
|
|
589
|
+
exports.FnSelectWithGroupByError = FnSelectWithGroupByError;
|
|
582
590
|
exports.GroupByError = GroupByError;
|
|
583
591
|
exports.HavingRequiresGroupByError = HavingRequiresGroupByError;
|
|
584
592
|
exports.InvalidCollectionStatusTransitionError = InvalidCollectionStatusTransitionError;
|
package/dist/cjs/errors.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","sources":["../../src/errors.ts"],"sourcesContent":["// Root error class for all TanStack DB errors\nexport class TanStackDBError extends Error {\n constructor(message: string) {\n super(message)\n this.name = `TanStackDBError`\n }\n}\n\n// Base error classes\nexport class NonRetriableError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `NonRetriableError`\n }\n}\n\n// Schema validation error (exported from index for backward compatibility)\nexport class SchemaValidationError extends TanStackDBError {\n type: `insert` | `update`\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>\n\n constructor(\n type: `insert` | `update`,\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>,\n message?: string,\n ) {\n const defaultMessage = `${type === `insert` ? `Insert` : `Update`} validation failed: ${issues\n .map((issue) => `\\n- ${issue.message} - path: ${issue.path}`)\n .join(``)}`\n\n super(message || defaultMessage)\n this.name = `SchemaValidationError`\n this.type = type\n this.issues = issues\n }\n}\n\n// Module Instance Errors\nexport class DuplicateDbInstanceError extends TanStackDBError {\n constructor() {\n super(\n `Multiple instances of @tanstack/db detected!\\n\\n` +\n `This causes transaction context to be lost because each instance maintains ` +\n `its own transaction stack.\\n\\n` +\n `Common causes:\\n` +\n `1. Different versions of @tanstack/db installed\\n` +\n `2. Incompatible peer dependency versions in packages\\n` +\n `3. Module resolution issues in bundler configuration\\n\\n` +\n `To fix:\\n` +\n `1. Check installed versions: npm list @tanstack/db (or pnpm/yarn list)\\n` +\n `2. Force a single version using package manager overrides:\\n` +\n ` - npm: \"overrides\" in package.json\\n` +\n ` - pnpm: \"pnpm.overrides\" in package.json\\n` +\n ` - yarn: \"resolutions\" in package.json\\n` +\n `3. Clear node_modules and lockfile, then reinstall\\n\\n` +\n `To temporarily disable this check (not recommended):\\n` +\n `Set environment variable: TANSTACK_DB_DISABLE_DUP_CHECK=1\\n\\n` +\n `See: https://tanstack.com/db/latest/docs/troubleshooting#duplicate-instances`,\n )\n this.name = `DuplicateDbInstanceError`\n }\n}\n\n// Collection Configuration Errors\nexport class CollectionConfigurationError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `CollectionConfigurationError`\n }\n}\n\nexport class CollectionRequiresConfigError extends CollectionConfigurationError {\n constructor() {\n super(`Collection requires a config`)\n }\n}\n\nexport class CollectionRequiresSyncConfigError extends CollectionConfigurationError {\n constructor() {\n super(`Collection requires a sync config`)\n }\n}\n\nexport class InvalidSchemaError extends CollectionConfigurationError {\n constructor() {\n super(`Schema must implement the standard-schema interface`)\n }\n}\n\nexport class SchemaMustBeSynchronousError extends CollectionConfigurationError {\n constructor() {\n super(`Schema validation must be synchronous`)\n }\n}\n\n// Collection State Errors\nexport class CollectionStateError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `CollectionStateError`\n }\n}\n\nexport class CollectionInErrorStateError extends CollectionStateError {\n constructor(operation: string, collectionId: string) {\n super(\n `Cannot perform ${operation} on collection \"${collectionId}\" - collection is in error state. Try calling cleanup() and restarting the collection.`,\n )\n }\n}\n\nexport class InvalidCollectionStatusTransitionError extends CollectionStateError {\n constructor(from: string, to: string, collectionId: string) {\n super(\n `Invalid collection status transition from \"${from}\" to \"${to}\" for collection \"${collectionId}\"`,\n )\n }\n}\n\nexport class CollectionIsInErrorStateError extends CollectionStateError {\n constructor() {\n super(`Collection is in error state`)\n }\n}\n\nexport class NegativeActiveSubscribersError extends CollectionStateError {\n constructor() {\n super(`Active subscribers count is negative - this should never happen`)\n }\n}\n\n// Collection Operation Errors\nexport class CollectionOperationError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `CollectionOperationError`\n }\n}\n\nexport class UndefinedKeyError extends CollectionOperationError {\n constructor(item: any) {\n super(\n `An object was created without a defined key: ${JSON.stringify(item)}`,\n )\n }\n}\n\nexport class InvalidKeyError extends CollectionOperationError {\n constructor(key: unknown, item: unknown) {\n const keyType = key === null ? `null` : typeof key\n super(\n `getKey returned an invalid key type. Expected string or number, but got ${keyType}: ${JSON.stringify(key)}. Item: ${JSON.stringify(item)}`,\n )\n }\n}\n\nexport class DuplicateKeyError extends CollectionOperationError {\n constructor(key: string | number) {\n super(\n `Cannot insert document with ID \"${key}\" because it already exists in the collection`,\n )\n }\n}\n\nexport class DuplicateKeySyncError extends CollectionOperationError {\n constructor(\n key: string | number,\n collectionId: string,\n options?: {\n hasCustomGetKey?: boolean\n hasJoins?: boolean\n hasDistinct?: boolean\n },\n ) {\n const baseMessage = `Cannot insert document with key \"${key}\" from sync because it already exists in the collection \"${collectionId}\"`\n\n // Provide enhanced guidance when custom getKey is used with distinct\n if (options?.hasCustomGetKey && options.hasDistinct) {\n super(\n `${baseMessage}. ` +\n `This collection uses a custom getKey with .distinct(). ` +\n `The .distinct() operator deduplicates by the ENTIRE selected object (standard SQL behavior), ` +\n `but your custom getKey extracts only a subset of fields. This causes multiple distinct rows ` +\n `(with different values in non-key fields) to receive the same key. ` +\n `To fix this, either: (1) ensure your SELECT only includes fields that uniquely identify each row, ` +\n `(2) use .groupBy() with min()/max() aggregates to select one value per group, or ` +\n `(3) remove the custom getKey to use the default key behavior.`,\n )\n } else if (options?.hasCustomGetKey && options.hasJoins) {\n // Provide enhanced guidance when custom getKey is used with joins\n super(\n `${baseMessage}. ` +\n `This collection uses a custom getKey with joined queries. ` +\n `Joined queries can produce multiple rows with the same key when relationships are not 1:1. ` +\n `Consider: (1) using a composite key in your getKey function (e.g., \\`\\${item.key1}-\\${item.key2}\\`), ` +\n `(2) ensuring your join produces unique rows per key, or (3) removing the custom getKey ` +\n `to use the default composite key behavior.`,\n )\n } else {\n super(baseMessage)\n }\n }\n}\n\nexport class MissingUpdateArgumentError extends CollectionOperationError {\n constructor() {\n super(`The first argument to update is missing`)\n }\n}\n\nexport class NoKeysPassedToUpdateError extends CollectionOperationError {\n constructor() {\n super(`No keys were passed to update`)\n }\n}\n\nexport class UpdateKeyNotFoundError extends CollectionOperationError {\n constructor(key: string | number) {\n super(\n `The key \"${key}\" was passed to update but an object for this key was not found in the collection`,\n )\n }\n}\n\nexport class KeyUpdateNotAllowedError extends CollectionOperationError {\n constructor(originalKey: string | number, newKey: string | number) {\n super(\n `Updating the key of an item is not allowed. Original key: \"${originalKey}\", Attempted new key: \"${newKey}\". Please delete the old item and create a new one if a key change is necessary.`,\n )\n }\n}\n\nexport class NoKeysPassedToDeleteError extends CollectionOperationError {\n constructor() {\n super(`No keys were passed to delete`)\n }\n}\n\nexport class DeleteKeyNotFoundError extends CollectionOperationError {\n constructor(key: string | number) {\n super(\n `Collection.delete was called with key '${key}' but there is no item in the collection with this key`,\n )\n }\n}\n\n// Collection Handler Errors\nexport class MissingHandlerError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `MissingHandlerError`\n }\n}\n\nexport class MissingInsertHandlerError extends MissingHandlerError {\n constructor() {\n super(\n `Collection.insert called directly (not within an explicit transaction) but no 'onInsert' handler is configured.`,\n )\n }\n}\n\nexport class MissingUpdateHandlerError extends MissingHandlerError {\n constructor() {\n super(\n `Collection.update called directly (not within an explicit transaction) but no 'onUpdate' handler is configured.`,\n )\n }\n}\n\nexport class MissingDeleteHandlerError extends MissingHandlerError {\n constructor() {\n super(\n `Collection.delete called directly (not within an explicit transaction) but no 'onDelete' handler is configured.`,\n )\n }\n}\n\n// Transaction Errors\nexport class TransactionError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `TransactionError`\n }\n}\n\nexport class MissingMutationFunctionError extends TransactionError {\n constructor() {\n super(`mutationFn is required when creating a transaction`)\n }\n}\n\nexport class OnMutateMustBeSynchronousError extends TransactionError {\n constructor() {\n super(\n `onMutate must be synchronous and cannot return a promise. Remove async/await or returned promises from onMutate.`,\n )\n this.name = `OnMutateMustBeSynchronousError`\n }\n}\n\nexport class TransactionNotPendingMutateError extends TransactionError {\n constructor() {\n super(\n `You can no longer call .mutate() as the transaction is no longer pending`,\n )\n }\n}\n\nexport class TransactionAlreadyCompletedRollbackError extends TransactionError {\n constructor() {\n super(\n `You can no longer call .rollback() as the transaction is already completed`,\n )\n }\n}\n\nexport class TransactionNotPendingCommitError extends TransactionError {\n constructor() {\n super(\n `You can no longer call .commit() as the transaction is no longer pending`,\n )\n }\n}\n\nexport class NoPendingSyncTransactionWriteError extends TransactionError {\n constructor() {\n super(`No pending sync transaction to write to`)\n }\n}\n\nexport class SyncTransactionAlreadyCommittedWriteError extends TransactionError {\n constructor() {\n super(\n `The pending sync transaction is already committed, you can't still write to it.`,\n )\n }\n}\n\nexport class NoPendingSyncTransactionCommitError extends TransactionError {\n constructor() {\n super(`No pending sync transaction to commit`)\n }\n}\n\nexport class SyncTransactionAlreadyCommittedError extends TransactionError {\n constructor() {\n super(\n `The pending sync transaction is already committed, you can't commit it again.`,\n )\n }\n}\n\n// Query Builder Errors\nexport class QueryBuilderError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `QueryBuilderError`\n }\n}\n\nexport class OnlyOneSourceAllowedError extends QueryBuilderError {\n constructor(context: string) {\n super(`Only one source is allowed in the ${context}`)\n }\n}\n\nexport class SubQueryMustHaveFromClauseError extends QueryBuilderError {\n constructor(context: string) {\n super(`A sub query passed to a ${context} must have a from clause itself`)\n }\n}\n\nexport class InvalidSourceError extends QueryBuilderError {\n constructor(alias: string) {\n super(\n `Invalid source for live query: The value provided for alias \"${alias}\" is not a Collection or subquery. Live queries only accept Collection instances or subqueries. Please ensure you're passing a valid Collection or QueryBuilder, not a plain array or other data type.`,\n )\n }\n}\n\nexport class InvalidSourceTypeError extends QueryBuilderError {\n constructor(context: string, type: string) {\n super(\n `Invalid source for ${context}: Expected an object with a single key-value pair like { alias: collection }. ` +\n `For example: .from({ todos: todosCollection }). Got: ${type}`,\n )\n }\n}\n\nexport class JoinConditionMustBeEqualityError extends QueryBuilderError {\n constructor() {\n super(`Join condition must be an equality expression`)\n }\n}\n\nexport class QueryMustHaveFromClauseError extends QueryBuilderError {\n constructor() {\n super(`Query must have a from clause`)\n }\n}\n\nexport class InvalidWhereExpressionError extends QueryBuilderError {\n constructor(valueType: string) {\n super(\n `Invalid where() expression: Expected a query expression, but received a ${valueType}. ` +\n `This usually happens when using JavaScript's comparison operators (===, !==, <, >, etc.) directly. ` +\n `Instead, use the query builder functions:\\n\\n` +\n ` ❌ .where(({ user }) => user.id === 'abc')\\n` +\n ` ✅ .where(({ user }) => eq(user.id, 'abc'))\\n\\n` +\n `Available comparison functions: eq, gt, gte, lt, lte, and, or, not, like, ilike, isNull, isUndefined`,\n )\n }\n}\n\n// Query Compilation Errors\nexport class QueryCompilationError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `QueryCompilationError`\n }\n}\n\nexport class DistinctRequiresSelectError extends QueryCompilationError {\n constructor() {\n super(`DISTINCT requires a SELECT clause.`)\n }\n}\n\nexport class HavingRequiresGroupByError extends QueryCompilationError {\n constructor() {\n super(`HAVING clause requires GROUP BY clause`)\n }\n}\n\nexport class LimitOffsetRequireOrderByError extends QueryCompilationError {\n constructor() {\n super(\n `LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`,\n )\n }\n}\n\n/**\n * Error thrown when a collection input stream is not found during query compilation.\n * In self-joins, each alias (e.g., 'employee', 'manager') requires its own input stream.\n */\nexport class CollectionInputNotFoundError extends QueryCompilationError {\n constructor(\n alias: string,\n collectionId?: string,\n availableKeys?: Array<string>,\n ) {\n const details = collectionId\n ? `alias \"${alias}\" (collection \"${collectionId}\")`\n : `collection \"${alias}\"`\n const availableKeysMsg = availableKeys?.length\n ? `. Available keys: ${availableKeys.join(`, `)}`\n : ``\n super(`Input for ${details} not found in inputs map${availableKeysMsg}`)\n }\n}\n\n/**\n * Error thrown when a subquery uses the same alias as its parent query.\n * This causes issues because parent and subquery would share the same input streams,\n * leading to empty results or incorrect data (aggregation cross-leaking).\n */\nexport class DuplicateAliasInSubqueryError extends QueryCompilationError {\n constructor(alias: string, parentAliases: Array<string>) {\n super(\n `Subquery uses alias \"${alias}\" which is already used in the parent query. ` +\n `Each alias must be unique across parent and subquery contexts. ` +\n `Parent query aliases: ${parentAliases.join(`, `)}. ` +\n `Please rename \"${alias}\" in either the parent query or subquery to avoid conflicts.`,\n )\n }\n}\n\nexport class UnsupportedFromTypeError extends QueryCompilationError {\n constructor(type: string) {\n super(`Unsupported FROM type: ${type}`)\n }\n}\n\nexport class UnknownExpressionTypeError extends QueryCompilationError {\n constructor(type: string) {\n super(`Unknown expression type: ${type}`)\n }\n}\n\nexport class EmptyReferencePathError extends QueryCompilationError {\n constructor() {\n super(`Reference path cannot be empty`)\n }\n}\n\nexport class UnknownFunctionError extends QueryCompilationError {\n constructor(functionName: string) {\n super(`Unknown function: ${functionName}`)\n }\n}\n\nexport class JoinCollectionNotFoundError extends QueryCompilationError {\n constructor(collectionId: string) {\n super(`Collection \"${collectionId}\" not found during compilation of join`)\n }\n}\n\n// JOIN Operation Errors\nexport class JoinError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `JoinError`\n }\n}\n\nexport class UnsupportedJoinTypeError extends JoinError {\n constructor(joinType: string) {\n super(`Unsupported join type: ${joinType}`)\n }\n}\n\nexport class InvalidJoinConditionSameSourceError extends JoinError {\n constructor(sourceAlias: string) {\n super(\n `Invalid join condition: both expressions refer to the same source \"${sourceAlias}\"`,\n )\n }\n}\n\nexport class InvalidJoinConditionSourceMismatchError extends JoinError {\n constructor() {\n super(`Invalid join condition: expressions must reference source aliases`)\n }\n}\n\nexport class InvalidJoinConditionLeftSourceError extends JoinError {\n constructor(sourceAlias: string) {\n super(\n `Invalid join condition: left expression refers to an unavailable source \"${sourceAlias}\"`,\n )\n }\n}\n\nexport class InvalidJoinConditionRightSourceError extends JoinError {\n constructor(sourceAlias: string) {\n super(\n `Invalid join condition: right expression does not refer to the joined source \"${sourceAlias}\"`,\n )\n }\n}\n\nexport class InvalidJoinCondition extends JoinError {\n constructor() {\n super(`Invalid join condition`)\n }\n}\n\nexport class UnsupportedJoinSourceTypeError extends JoinError {\n constructor(type: string) {\n super(`Unsupported join source type: ${type}`)\n }\n}\n\n// GROUP BY and Aggregation Errors\nexport class GroupByError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `GroupByError`\n }\n}\n\nexport class NonAggregateExpressionNotInGroupByError extends GroupByError {\n constructor(alias: string) {\n super(\n `Non-aggregate expression '${alias}' in SELECT must also appear in GROUP BY clause`,\n )\n }\n}\n\nexport class UnsupportedAggregateFunctionError extends GroupByError {\n constructor(functionName: string) {\n super(`Unsupported aggregate function: ${functionName}`)\n }\n}\n\nexport class AggregateFunctionNotInSelectError extends GroupByError {\n constructor(functionName: string) {\n super(\n `Aggregate function in HAVING clause must also be in SELECT clause: ${functionName}`,\n )\n }\n}\n\nexport class UnknownHavingExpressionTypeError extends GroupByError {\n constructor(type: string) {\n super(`Unknown expression type in HAVING clause: ${type}`)\n }\n}\n\n// Storage Errors\nexport class StorageError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `StorageError`\n }\n}\n\nexport class SerializationError extends StorageError {\n constructor(operation: string, originalError: string) {\n super(\n `Cannot ${operation} item because it cannot be JSON serialized: ${originalError}`,\n )\n }\n}\n\n// LocalStorage Collection Errors\nexport class LocalStorageCollectionError extends StorageError {\n constructor(message: string) {\n super(message)\n this.name = `LocalStorageCollectionError`\n }\n}\n\nexport class StorageKeyRequiredError extends LocalStorageCollectionError {\n constructor() {\n super(`[LocalStorageCollection] storageKey must be provided.`)\n }\n}\n\nexport class InvalidStorageDataFormatError extends LocalStorageCollectionError {\n constructor(storageKey: string, key: string) {\n super(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\" for key \"${key}\".`,\n )\n }\n}\n\nexport class InvalidStorageObjectFormatError extends LocalStorageCollectionError {\n constructor(storageKey: string) {\n super(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\". Expected object format.`,\n )\n }\n}\n\n// Sync Cleanup Errors\nexport class SyncCleanupError extends TanStackDBError {\n constructor(collectionId: string, error: Error | string) {\n const message = error instanceof Error ? error.message : String(error)\n super(\n `Collection \"${collectionId}\" sync cleanup function threw an error: ${message}`,\n )\n this.name = `SyncCleanupError`\n }\n}\n\n// Query Optimizer Errors\nexport class QueryOptimizerError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `QueryOptimizerError`\n }\n}\n\nexport class CannotCombineEmptyExpressionListError extends QueryOptimizerError {\n constructor() {\n super(`Cannot combine empty expression list`)\n }\n}\n\n/**\n * Internal error when the query optimizer fails to convert a WHERE clause to a collection filter.\n */\nexport class WhereClauseConversionError extends QueryOptimizerError {\n constructor(collectionId: string, alias: string) {\n super(\n `Failed to convert WHERE clause to collection filter for collection '${collectionId}' alias '${alias}'. This indicates a bug in the query optimization logic.`,\n )\n }\n}\n\n/**\n * Error when a subscription cannot be found during lazy join processing.\n * For subqueries, aliases may be remapped (e.g., 'activeUser' → 'user').\n */\nexport class SubscriptionNotFoundError extends QueryCompilationError {\n constructor(\n resolvedAlias: string,\n originalAlias: string,\n collectionId: string,\n availableAliases: Array<string>,\n ) {\n super(\n `Internal error: subscription for alias '${resolvedAlias}' (remapped from '${originalAlias}', collection '${collectionId}') is missing in join pipeline. Available aliases: ${availableAliases.join(`, `)}. This indicates a bug in alias tracking.`,\n )\n }\n}\n\n/**\n * Error thrown when aggregate expressions are used outside of a GROUP BY context.\n */\nexport class AggregateNotSupportedError extends QueryCompilationError {\n constructor() {\n super(\n `Aggregate expressions are not supported in this context. Use GROUP BY clause for aggregates.`,\n )\n }\n}\n\n/**\n * Internal error when the compiler returns aliases that don't have corresponding input streams.\n * This should never happen since all aliases come from user declarations.\n */\nexport class MissingAliasInputsError extends QueryCompilationError {\n constructor(missingAliases: Array<string>) {\n super(\n `Internal error: compiler returned aliases without inputs: ${missingAliases.join(`, `)}. ` +\n `This indicates a bug in query compilation. Please report this issue.`,\n )\n }\n}\n\n/**\n * Error thrown when setWindow is called on a collection without an ORDER BY clause.\n */\nexport class SetWindowRequiresOrderByError extends QueryCompilationError {\n constructor() {\n super(\n `setWindow() can only be called on collections with an ORDER BY clause. ` +\n `Add .orderBy() to your query to enable window movement.`,\n )\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors.cjs","sources":["../../src/errors.ts"],"sourcesContent":["// Root error class for all TanStack DB errors\nexport class TanStackDBError extends Error {\n constructor(message: string) {\n super(message)\n this.name = `TanStackDBError`\n }\n}\n\n// Base error classes\nexport class NonRetriableError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `NonRetriableError`\n }\n}\n\n// Schema validation error (exported from index for backward compatibility)\nexport class SchemaValidationError extends TanStackDBError {\n type: `insert` | `update`\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>\n\n constructor(\n type: `insert` | `update`,\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>,\n message?: string,\n ) {\n const defaultMessage = `${type === `insert` ? `Insert` : `Update`} validation failed: ${issues\n .map((issue) => `\\n- ${issue.message} - path: ${issue.path}`)\n .join(``)}`\n\n super(message || defaultMessage)\n this.name = `SchemaValidationError`\n this.type = type\n this.issues = issues\n }\n}\n\n// Module Instance Errors\nexport class DuplicateDbInstanceError extends TanStackDBError {\n constructor() {\n super(\n `Multiple instances of @tanstack/db detected!\\n\\n` +\n `This causes transaction context to be lost because each instance maintains ` +\n `its own transaction stack.\\n\\n` +\n `Common causes:\\n` +\n `1. Different versions of @tanstack/db installed\\n` +\n `2. Incompatible peer dependency versions in packages\\n` +\n `3. Module resolution issues in bundler configuration\\n\\n` +\n `To fix:\\n` +\n `1. Check installed versions: npm list @tanstack/db (or pnpm/yarn list)\\n` +\n `2. Force a single version using package manager overrides:\\n` +\n ` - npm: \"overrides\" in package.json\\n` +\n ` - pnpm: \"pnpm.overrides\" in package.json\\n` +\n ` - yarn: \"resolutions\" in package.json\\n` +\n `3. Clear node_modules and lockfile, then reinstall\\n\\n` +\n `To temporarily disable this check (not recommended):\\n` +\n `Set environment variable: TANSTACK_DB_DISABLE_DUP_CHECK=1\\n\\n` +\n `See: https://tanstack.com/db/latest/docs/troubleshooting#duplicate-instances`,\n )\n this.name = `DuplicateDbInstanceError`\n }\n}\n\n// Collection Configuration Errors\nexport class CollectionConfigurationError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `CollectionConfigurationError`\n }\n}\n\nexport class CollectionRequiresConfigError extends CollectionConfigurationError {\n constructor() {\n super(`Collection requires a config`)\n }\n}\n\nexport class CollectionRequiresSyncConfigError extends CollectionConfigurationError {\n constructor() {\n super(`Collection requires a sync config`)\n }\n}\n\nexport class InvalidSchemaError extends CollectionConfigurationError {\n constructor() {\n super(`Schema must implement the standard-schema interface`)\n }\n}\n\nexport class SchemaMustBeSynchronousError extends CollectionConfigurationError {\n constructor() {\n super(`Schema validation must be synchronous`)\n }\n}\n\n// Collection State Errors\nexport class CollectionStateError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `CollectionStateError`\n }\n}\n\nexport class CollectionInErrorStateError extends CollectionStateError {\n constructor(operation: string, collectionId: string) {\n super(\n `Cannot perform ${operation} on collection \"${collectionId}\" - collection is in error state. Try calling cleanup() and restarting the collection.`,\n )\n }\n}\n\nexport class InvalidCollectionStatusTransitionError extends CollectionStateError {\n constructor(from: string, to: string, collectionId: string) {\n super(\n `Invalid collection status transition from \"${from}\" to \"${to}\" for collection \"${collectionId}\"`,\n )\n }\n}\n\nexport class CollectionIsInErrorStateError extends CollectionStateError {\n constructor() {\n super(`Collection is in error state`)\n }\n}\n\nexport class NegativeActiveSubscribersError extends CollectionStateError {\n constructor() {\n super(`Active subscribers count is negative - this should never happen`)\n }\n}\n\n// Collection Operation Errors\nexport class CollectionOperationError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `CollectionOperationError`\n }\n}\n\nexport class UndefinedKeyError extends CollectionOperationError {\n constructor(item: any) {\n super(\n `An object was created without a defined key: ${JSON.stringify(item)}`,\n )\n }\n}\n\nexport class InvalidKeyError extends CollectionOperationError {\n constructor(key: unknown, item: unknown) {\n const keyType = key === null ? `null` : typeof key\n super(\n `getKey returned an invalid key type. Expected string or number, but got ${keyType}: ${JSON.stringify(key)}. Item: ${JSON.stringify(item)}`,\n )\n }\n}\n\nexport class DuplicateKeyError extends CollectionOperationError {\n constructor(key: string | number) {\n super(\n `Cannot insert document with ID \"${key}\" because it already exists in the collection`,\n )\n }\n}\n\nexport class DuplicateKeySyncError extends CollectionOperationError {\n constructor(\n key: string | number,\n collectionId: string,\n options?: {\n hasCustomGetKey?: boolean\n hasJoins?: boolean\n hasDistinct?: boolean\n },\n ) {\n const baseMessage = `Cannot insert document with key \"${key}\" from sync because it already exists in the collection \"${collectionId}\"`\n\n // Provide enhanced guidance when custom getKey is used with distinct\n if (options?.hasCustomGetKey && options.hasDistinct) {\n super(\n `${baseMessage}. ` +\n `This collection uses a custom getKey with .distinct(). ` +\n `The .distinct() operator deduplicates by the ENTIRE selected object (standard SQL behavior), ` +\n `but your custom getKey extracts only a subset of fields. This causes multiple distinct rows ` +\n `(with different values in non-key fields) to receive the same key. ` +\n `To fix this, either: (1) ensure your SELECT only includes fields that uniquely identify each row, ` +\n `(2) use .groupBy() with min()/max() aggregates to select one value per group, or ` +\n `(3) remove the custom getKey to use the default key behavior.`,\n )\n } else if (options?.hasCustomGetKey && options.hasJoins) {\n // Provide enhanced guidance when custom getKey is used with joins\n super(\n `${baseMessage}. ` +\n `This collection uses a custom getKey with joined queries. ` +\n `Joined queries can produce multiple rows with the same key when relationships are not 1:1. ` +\n `Consider: (1) using a composite key in your getKey function (e.g., \\`\\${item.key1}-\\${item.key2}\\`), ` +\n `(2) ensuring your join produces unique rows per key, or (3) removing the custom getKey ` +\n `to use the default composite key behavior.`,\n )\n } else {\n super(baseMessage)\n }\n }\n}\n\nexport class MissingUpdateArgumentError extends CollectionOperationError {\n constructor() {\n super(`The first argument to update is missing`)\n }\n}\n\nexport class NoKeysPassedToUpdateError extends CollectionOperationError {\n constructor() {\n super(`No keys were passed to update`)\n }\n}\n\nexport class UpdateKeyNotFoundError extends CollectionOperationError {\n constructor(key: string | number) {\n super(\n `The key \"${key}\" was passed to update but an object for this key was not found in the collection`,\n )\n }\n}\n\nexport class KeyUpdateNotAllowedError extends CollectionOperationError {\n constructor(originalKey: string | number, newKey: string | number) {\n super(\n `Updating the key of an item is not allowed. Original key: \"${originalKey}\", Attempted new key: \"${newKey}\". Please delete the old item and create a new one if a key change is necessary.`,\n )\n }\n}\n\nexport class NoKeysPassedToDeleteError extends CollectionOperationError {\n constructor() {\n super(`No keys were passed to delete`)\n }\n}\n\nexport class DeleteKeyNotFoundError extends CollectionOperationError {\n constructor(key: string | number) {\n super(\n `Collection.delete was called with key '${key}' but there is no item in the collection with this key`,\n )\n }\n}\n\n// Collection Handler Errors\nexport class MissingHandlerError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `MissingHandlerError`\n }\n}\n\nexport class MissingInsertHandlerError extends MissingHandlerError {\n constructor() {\n super(\n `Collection.insert called directly (not within an explicit transaction) but no 'onInsert' handler is configured.`,\n )\n }\n}\n\nexport class MissingUpdateHandlerError extends MissingHandlerError {\n constructor() {\n super(\n `Collection.update called directly (not within an explicit transaction) but no 'onUpdate' handler is configured.`,\n )\n }\n}\n\nexport class MissingDeleteHandlerError extends MissingHandlerError {\n constructor() {\n super(\n `Collection.delete called directly (not within an explicit transaction) but no 'onDelete' handler is configured.`,\n )\n }\n}\n\n// Transaction Errors\nexport class TransactionError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `TransactionError`\n }\n}\n\nexport class MissingMutationFunctionError extends TransactionError {\n constructor() {\n super(`mutationFn is required when creating a transaction`)\n }\n}\n\nexport class OnMutateMustBeSynchronousError extends TransactionError {\n constructor() {\n super(\n `onMutate must be synchronous and cannot return a promise. Remove async/await or returned promises from onMutate.`,\n )\n this.name = `OnMutateMustBeSynchronousError`\n }\n}\n\nexport class TransactionNotPendingMutateError extends TransactionError {\n constructor() {\n super(\n `You can no longer call .mutate() as the transaction is no longer pending`,\n )\n }\n}\n\nexport class TransactionAlreadyCompletedRollbackError extends TransactionError {\n constructor() {\n super(\n `You can no longer call .rollback() as the transaction is already completed`,\n )\n }\n}\n\nexport class TransactionNotPendingCommitError extends TransactionError {\n constructor() {\n super(\n `You can no longer call .commit() as the transaction is no longer pending`,\n )\n }\n}\n\nexport class NoPendingSyncTransactionWriteError extends TransactionError {\n constructor() {\n super(`No pending sync transaction to write to`)\n }\n}\n\nexport class SyncTransactionAlreadyCommittedWriteError extends TransactionError {\n constructor() {\n super(\n `The pending sync transaction is already committed, you can't still write to it.`,\n )\n }\n}\n\nexport class NoPendingSyncTransactionCommitError extends TransactionError {\n constructor() {\n super(`No pending sync transaction to commit`)\n }\n}\n\nexport class SyncTransactionAlreadyCommittedError extends TransactionError {\n constructor() {\n super(\n `The pending sync transaction is already committed, you can't commit it again.`,\n )\n }\n}\n\n// Query Builder Errors\nexport class QueryBuilderError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `QueryBuilderError`\n }\n}\n\nexport class OnlyOneSourceAllowedError extends QueryBuilderError {\n constructor(context: string) {\n super(`Only one source is allowed in the ${context}`)\n }\n}\n\nexport class SubQueryMustHaveFromClauseError extends QueryBuilderError {\n constructor(context: string) {\n super(`A sub query passed to a ${context} must have a from clause itself`)\n }\n}\n\nexport class InvalidSourceError extends QueryBuilderError {\n constructor(alias: string) {\n super(\n `Invalid source for live query: The value provided for alias \"${alias}\" is not a Collection or subquery. Live queries only accept Collection instances or subqueries. Please ensure you're passing a valid Collection or QueryBuilder, not a plain array or other data type.`,\n )\n }\n}\n\nexport class InvalidSourceTypeError extends QueryBuilderError {\n constructor(context: string, type: string) {\n super(\n `Invalid source for ${context}: Expected an object with a single key-value pair like { alias: collection }. ` +\n `For example: .from({ todos: todosCollection }). Got: ${type}`,\n )\n }\n}\n\nexport class JoinConditionMustBeEqualityError extends QueryBuilderError {\n constructor() {\n super(`Join condition must be an equality expression`)\n }\n}\n\nexport class QueryMustHaveFromClauseError extends QueryBuilderError {\n constructor() {\n super(`Query must have a from clause`)\n }\n}\n\nexport class InvalidWhereExpressionError extends QueryBuilderError {\n constructor(valueType: string) {\n super(\n `Invalid where() expression: Expected a query expression, but received a ${valueType}. ` +\n `This usually happens when using JavaScript's comparison operators (===, !==, <, >, etc.) directly. ` +\n `Instead, use the query builder functions:\\n\\n` +\n ` ❌ .where(({ user }) => user.id === 'abc')\\n` +\n ` ✅ .where(({ user }) => eq(user.id, 'abc'))\\n\\n` +\n `Available comparison functions: eq, gt, gte, lt, lte, and, or, not, like, ilike, isNull, isUndefined`,\n )\n }\n}\n\n// Query Compilation Errors\nexport class QueryCompilationError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `QueryCompilationError`\n }\n}\n\nexport class DistinctRequiresSelectError extends QueryCompilationError {\n constructor() {\n super(`DISTINCT requires a SELECT clause.`)\n }\n}\n\nexport class FnSelectWithGroupByError extends QueryCompilationError {\n constructor() {\n super(\n `fn.select() cannot be used with groupBy(). ` +\n `groupBy requires the compiler to statically analyze aggregate functions (count, sum, max, etc.) in the SELECT clause, ` +\n `which is not possible with fn.select() since it is an opaque function. ` +\n `Use .select() instead of .fn.select() when combining with groupBy().`,\n )\n }\n}\n\nexport class HavingRequiresGroupByError extends QueryCompilationError {\n constructor() {\n super(`HAVING clause requires GROUP BY clause`)\n }\n}\n\nexport class LimitOffsetRequireOrderByError extends QueryCompilationError {\n constructor() {\n super(\n `LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`,\n )\n }\n}\n\n/**\n * Error thrown when a collection input stream is not found during query compilation.\n * In self-joins, each alias (e.g., 'employee', 'manager') requires its own input stream.\n */\nexport class CollectionInputNotFoundError extends QueryCompilationError {\n constructor(\n alias: string,\n collectionId?: string,\n availableKeys?: Array<string>,\n ) {\n const details = collectionId\n ? `alias \"${alias}\" (collection \"${collectionId}\")`\n : `collection \"${alias}\"`\n const availableKeysMsg = availableKeys?.length\n ? `. Available keys: ${availableKeys.join(`, `)}`\n : ``\n super(`Input for ${details} not found in inputs map${availableKeysMsg}`)\n }\n}\n\n/**\n * Error thrown when a subquery uses the same alias as its parent query.\n * This causes issues because parent and subquery would share the same input streams,\n * leading to empty results or incorrect data (aggregation cross-leaking).\n */\nexport class DuplicateAliasInSubqueryError extends QueryCompilationError {\n constructor(alias: string, parentAliases: Array<string>) {\n super(\n `Subquery uses alias \"${alias}\" which is already used in the parent query. ` +\n `Each alias must be unique across parent and subquery contexts. ` +\n `Parent query aliases: ${parentAliases.join(`, `)}. ` +\n `Please rename \"${alias}\" in either the parent query or subquery to avoid conflicts.`,\n )\n }\n}\n\nexport class UnsupportedFromTypeError extends QueryCompilationError {\n constructor(type: string) {\n super(`Unsupported FROM type: ${type}`)\n }\n}\n\nexport class UnknownExpressionTypeError extends QueryCompilationError {\n constructor(type: string) {\n super(`Unknown expression type: ${type}`)\n }\n}\n\nexport class EmptyReferencePathError extends QueryCompilationError {\n constructor() {\n super(`Reference path cannot be empty`)\n }\n}\n\nexport class UnknownFunctionError extends QueryCompilationError {\n constructor(functionName: string) {\n super(`Unknown function: ${functionName}`)\n }\n}\n\nexport class JoinCollectionNotFoundError extends QueryCompilationError {\n constructor(collectionId: string) {\n super(`Collection \"${collectionId}\" not found during compilation of join`)\n }\n}\n\n// JOIN Operation Errors\nexport class JoinError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `JoinError`\n }\n}\n\nexport class UnsupportedJoinTypeError extends JoinError {\n constructor(joinType: string) {\n super(`Unsupported join type: ${joinType}`)\n }\n}\n\nexport class InvalidJoinConditionSameSourceError extends JoinError {\n constructor(sourceAlias: string) {\n super(\n `Invalid join condition: both expressions refer to the same source \"${sourceAlias}\"`,\n )\n }\n}\n\nexport class InvalidJoinConditionSourceMismatchError extends JoinError {\n constructor() {\n super(`Invalid join condition: expressions must reference source aliases`)\n }\n}\n\nexport class InvalidJoinConditionLeftSourceError extends JoinError {\n constructor(sourceAlias: string) {\n super(\n `Invalid join condition: left expression refers to an unavailable source \"${sourceAlias}\"`,\n )\n }\n}\n\nexport class InvalidJoinConditionRightSourceError extends JoinError {\n constructor(sourceAlias: string) {\n super(\n `Invalid join condition: right expression does not refer to the joined source \"${sourceAlias}\"`,\n )\n }\n}\n\nexport class InvalidJoinCondition extends JoinError {\n constructor() {\n super(`Invalid join condition`)\n }\n}\n\nexport class UnsupportedJoinSourceTypeError extends JoinError {\n constructor(type: string) {\n super(`Unsupported join source type: ${type}`)\n }\n}\n\n// GROUP BY and Aggregation Errors\nexport class GroupByError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `GroupByError`\n }\n}\n\nexport class NonAggregateExpressionNotInGroupByError extends GroupByError {\n constructor(alias: string) {\n super(\n `Non-aggregate expression '${alias}' in SELECT must also appear in GROUP BY clause`,\n )\n }\n}\n\nexport class UnsupportedAggregateFunctionError extends GroupByError {\n constructor(functionName: string) {\n super(`Unsupported aggregate function: ${functionName}`)\n }\n}\n\nexport class AggregateFunctionNotInSelectError extends GroupByError {\n constructor(functionName: string) {\n super(\n `Aggregate function in HAVING clause must also be in SELECT clause: ${functionName}`,\n )\n }\n}\n\nexport class UnknownHavingExpressionTypeError extends GroupByError {\n constructor(type: string) {\n super(`Unknown expression type in HAVING clause: ${type}`)\n }\n}\n\n// Storage Errors\nexport class StorageError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `StorageError`\n }\n}\n\nexport class SerializationError extends StorageError {\n constructor(operation: string, originalError: string) {\n super(\n `Cannot ${operation} item because it cannot be JSON serialized: ${originalError}`,\n )\n }\n}\n\n// LocalStorage Collection Errors\nexport class LocalStorageCollectionError extends StorageError {\n constructor(message: string) {\n super(message)\n this.name = `LocalStorageCollectionError`\n }\n}\n\nexport class StorageKeyRequiredError extends LocalStorageCollectionError {\n constructor() {\n super(`[LocalStorageCollection] storageKey must be provided.`)\n }\n}\n\nexport class InvalidStorageDataFormatError extends LocalStorageCollectionError {\n constructor(storageKey: string, key: string) {\n super(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\" for key \"${key}\".`,\n )\n }\n}\n\nexport class InvalidStorageObjectFormatError extends LocalStorageCollectionError {\n constructor(storageKey: string) {\n super(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\". Expected object format.`,\n )\n }\n}\n\n// Sync Cleanup Errors\nexport class SyncCleanupError extends TanStackDBError {\n constructor(collectionId: string, error: Error | string) {\n const message = error instanceof Error ? error.message : String(error)\n super(\n `Collection \"${collectionId}\" sync cleanup function threw an error: ${message}`,\n )\n this.name = `SyncCleanupError`\n }\n}\n\n// Query Optimizer Errors\nexport class QueryOptimizerError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `QueryOptimizerError`\n }\n}\n\nexport class CannotCombineEmptyExpressionListError extends QueryOptimizerError {\n constructor() {\n super(`Cannot combine empty expression list`)\n }\n}\n\n/**\n * Internal error when the query optimizer fails to convert a WHERE clause to a collection filter.\n */\nexport class WhereClauseConversionError extends QueryOptimizerError {\n constructor(collectionId: string, alias: string) {\n super(\n `Failed to convert WHERE clause to collection filter for collection '${collectionId}' alias '${alias}'. This indicates a bug in the query optimization logic.`,\n )\n }\n}\n\n/**\n * Error when a subscription cannot be found during lazy join processing.\n * For subqueries, aliases may be remapped (e.g., 'activeUser' → 'user').\n */\nexport class SubscriptionNotFoundError extends QueryCompilationError {\n constructor(\n resolvedAlias: string,\n originalAlias: string,\n collectionId: string,\n availableAliases: Array<string>,\n ) {\n super(\n `Internal error: subscription for alias '${resolvedAlias}' (remapped from '${originalAlias}', collection '${collectionId}') is missing in join pipeline. Available aliases: ${availableAliases.join(`, `)}. This indicates a bug in alias tracking.`,\n )\n }\n}\n\n/**\n * Error thrown when aggregate expressions are used outside of a GROUP BY context.\n */\nexport class AggregateNotSupportedError extends QueryCompilationError {\n constructor() {\n super(\n `Aggregate expressions are not supported in this context. Use GROUP BY clause for aggregates.`,\n )\n }\n}\n\n/**\n * Internal error when the compiler returns aliases that don't have corresponding input streams.\n * This should never happen since all aliases come from user declarations.\n */\nexport class MissingAliasInputsError extends QueryCompilationError {\n constructor(missingAliases: Array<string>) {\n super(\n `Internal error: compiler returned aliases without inputs: ${missingAliases.join(`, `)}. ` +\n `This indicates a bug in query compilation. Please report this issue.`,\n )\n }\n}\n\n/**\n * Error thrown when setWindow is called on a collection without an ORDER BY clause.\n */\nexport class SetWindowRequiresOrderByError extends QueryCompilationError {\n constructor() {\n super(\n `setWindow() can only be called on collections with an ORDER BY clause. ` +\n `Add .orderBy() to your query to enable window movement.`,\n )\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/cjs/errors.d.cts
CHANGED
|
@@ -157,6 +157,9 @@ export declare class QueryCompilationError extends TanStackDBError {
|
|
|
157
157
|
export declare class DistinctRequiresSelectError extends QueryCompilationError {
|
|
158
158
|
constructor();
|
|
159
159
|
}
|
|
160
|
+
export declare class FnSelectWithGroupByError extends QueryCompilationError {
|
|
161
|
+
constructor();
|
|
162
|
+
}
|
|
160
163
|
export declare class HavingRequiresGroupByError extends QueryCompilationError {
|
|
161
164
|
constructor();
|
|
162
165
|
}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -22,6 +22,7 @@ const index$2 = require("./query/compiler/index.cjs");
|
|
|
22
22
|
const liveQueryCollection = require("./query/live-query-collection.cjs");
|
|
23
23
|
const debounceStrategy = require("./strategies/debounceStrategy.cjs");
|
|
24
24
|
const predicateUtils = require("./query/predicate-utils.cjs");
|
|
25
|
+
const queryOnce = require("./query/query-once.cjs");
|
|
25
26
|
const queueStrategy = require("./strategies/queueStrategy.cjs");
|
|
26
27
|
const throttleStrategy = require("./strategies/throttleStrategy.cjs");
|
|
27
28
|
exports.IR = ir;
|
|
@@ -55,6 +56,7 @@ exports.DuplicateDbInstanceError = errors.DuplicateDbInstanceError;
|
|
|
55
56
|
exports.DuplicateKeyError = errors.DuplicateKeyError;
|
|
56
57
|
exports.DuplicateKeySyncError = errors.DuplicateKeySyncError;
|
|
57
58
|
exports.EmptyReferencePathError = errors.EmptyReferencePathError;
|
|
59
|
+
exports.FnSelectWithGroupByError = errors.FnSelectWithGroupByError;
|
|
58
60
|
exports.GroupByError = errors.GroupByError;
|
|
59
61
|
exports.HavingRequiresGroupByError = errors.HavingRequiresGroupByError;
|
|
60
62
|
exports.InvalidCollectionStatusTransitionError = errors.InvalidCollectionStatusTransitionError;
|
|
@@ -175,6 +177,7 @@ exports.isPredicateSubset = predicateUtils.isPredicateSubset;
|
|
|
175
177
|
exports.isWhereSubset = predicateUtils.isWhereSubset;
|
|
176
178
|
exports.minusWherePredicates = predicateUtils.minusWherePredicates;
|
|
177
179
|
exports.unionWherePredicates = predicateUtils.unionWherePredicates;
|
|
180
|
+
exports.queryOnce = queryOnce.queryOnce;
|
|
178
181
|
exports.queueStrategy = queueStrategy.queueStrategy;
|
|
179
182
|
exports.throttleStrategy = throttleStrategy.throttleStrategy;
|
|
180
183
|
//# sourceMappingURL=index.cjs.map
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -181,7 +181,7 @@ export type SelectObject<T extends SelectShape = SelectShape> = T;
|
|
|
181
181
|
* ```
|
|
182
182
|
*/
|
|
183
183
|
export type ResultTypeFromSelect<TSelectObject> = WithoutRefBrand<Prettify<{
|
|
184
|
-
[K in keyof TSelectObject]: NeedsExtraction<TSelectObject[K]> extends true ? ExtractExpressionType<TSelectObject[K]> : TSelectObject[K] extends Ref<infer _T> ? ExtractRef<TSelectObject[K]> : TSelectObject[K] extends RefLeaf<infer T> ? T : TSelectObject[K] extends RefLeaf<infer T> | undefined ? T | undefined : TSelectObject[K] extends RefLeaf<infer T> | null ? T | null : TSelectObject[K] extends Ref<infer _T> | undefined ? ExtractRef<TSelectObject[K]
|
|
184
|
+
[K in keyof TSelectObject]: NeedsExtraction<TSelectObject[K]> extends true ? ExtractExpressionType<TSelectObject[K]> : TSelectObject[K] extends Ref<infer _T> ? ExtractRef<TSelectObject[K]> : TSelectObject[K] extends RefLeaf<infer T> ? IsNullableRef<TSelectObject[K]> extends true ? T | undefined : T : TSelectObject[K] extends RefLeaf<infer T> | undefined ? T | undefined : TSelectObject[K] extends RefLeaf<infer T> | null ? IsNullableRef<Exclude<TSelectObject[K], null>> extends true ? T | null | undefined : T | null : TSelectObject[K] extends Ref<infer _T> | undefined ? ExtractRef<Exclude<TSelectObject[K], undefined>> | undefined : TSelectObject[K] extends Ref<infer _T> | null ? ExtractRef<Exclude<TSelectObject[K], null>> | null : TSelectObject[K] extends Aggregate<infer T> ? T : TSelectObject[K] extends string | number | boolean | null | undefined ? TSelectObject[K] : TSelectObject[K] extends Record<string, any> ? ResultTypeFromSelect<TSelectObject[K]> : never;
|
|
185
185
|
}>>;
|
|
186
186
|
type ExtractRef<T> = Prettify<ResultTypeFromSelect<WithoutRefBrand<T>>>;
|
|
187
187
|
type ExtractExpressionType<T> = T extends PropRef<infer U> ? U : T extends Value<infer U> ? U : T extends Func<infer U> ? U : T extends Aggregate<infer U> ? U : T extends BasicExpression<infer U> ? U : T;
|
|
@@ -260,33 +260,23 @@ export type FunctionalHavingRow<TContext extends Context> = TContext[`schema`] &
|
|
|
260
260
|
$selected: TContext[`result`];
|
|
261
261
|
} : {});
|
|
262
262
|
/**
|
|
263
|
-
*
|
|
263
|
+
* RefsForContext - Creates ref proxies for all tables/collections in a query context
|
|
264
264
|
*
|
|
265
265
|
* This is the main entry point for creating ref objects in query builder callbacks.
|
|
266
|
-
*
|
|
267
|
-
*
|
|
266
|
+
* For nullable join sides (left/right/full joins), it produces `Ref<T, true>` instead
|
|
267
|
+
* of `Ref<T> | undefined`. This accurately reflects that the proxy object is always
|
|
268
|
+
* present at build time (it's a truthy proxy that records property access paths),
|
|
269
|
+
* while the `Nullable` flag ensures the result type correctly includes `| undefined`.
|
|
268
270
|
*
|
|
269
271
|
* Examples:
|
|
270
|
-
* - Required field: `
|
|
271
|
-
* -
|
|
272
|
-
* - Nullable field: `RefProxy<User> | null` → user?.name works
|
|
273
|
-
* - Both optional and nullable: `RefProxy<User> | undefined` → user?.name works
|
|
274
|
-
*
|
|
275
|
-
* The key insight is that `RefProxy<User | undefined>` would NOT allow `user?.name`
|
|
276
|
-
* because the undefined is "inside" the proxy, but `RefProxy<User> | undefined`
|
|
277
|
-
* does allow it because the undefined is "outside" the proxy.
|
|
278
|
-
*
|
|
279
|
-
* The logic prioritizes optional chaining by always placing `undefined` outside when
|
|
280
|
-
* a type is both optional and nullable (e.g., `string | null | undefined`).
|
|
272
|
+
* - Required field: `Ref<User>` → user.name works, result is T
|
|
273
|
+
* - Nullable join side: `Ref<User, true>` → user.name works, result is T | undefined
|
|
281
274
|
*
|
|
282
275
|
* After `select()` is called, this type also includes `$selected` which provides access
|
|
283
276
|
* to the SELECT result fields via `$selected.fieldName` syntax.
|
|
284
277
|
*/
|
|
285
278
|
export type RefsForContext<TContext extends Context> = {
|
|
286
|
-
[K in keyof TContext[`schema`]]: IsNonExactOptional<TContext[`schema`][K]> extends true ? IsNonExactNullable<TContext[`schema`][K]> extends true ?
|
|
287
|
-
Ref<NonNullable<TContext[`schema`][K]>> | undefined : // T is optional (T | undefined) but not exactly undefined, and not nullable
|
|
288
|
-
Ref<NonUndefined<TContext[`schema`][K]>> | undefined : IsNonExactNullable<TContext[`schema`][K]> extends true ? // T is nullable (T | null) but not exactly null, and not optional
|
|
289
|
-
Ref<NonNull<TContext[`schema`][K]>> | null : Ref<TContext[`schema`][K]>;
|
|
279
|
+
[K in keyof TContext[`schema`]]: IsNonExactOptional<TContext[`schema`][K]> extends true ? IsNonExactNullable<TContext[`schema`][K]> extends true ? Ref<NonNullable<TContext[`schema`][K]>, true> : Ref<NonUndefined<TContext[`schema`][K]>, true> : IsNonExactNullable<TContext[`schema`][K]> extends true ? Ref<NonNull<TContext[`schema`][K]>, true> : Ref<TContext[`schema`][K]>;
|
|
290
280
|
} & (TContext[`result`] extends object ? {
|
|
291
281
|
$selected: Ref<TContext[`result`]>;
|
|
292
282
|
} : {});
|
|
@@ -331,23 +321,26 @@ type NonNull<T> = T extends null ? never : T;
|
|
|
331
321
|
* It provides a recursive interface that allows nested property access while
|
|
332
322
|
* preserving optionality and nullability correctly.
|
|
333
323
|
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
324
|
+
* The `Nullable` parameter indicates whether this ref comes from a nullable
|
|
325
|
+
* join side (left/right/full). When `true`, the `Nullable` flag propagates
|
|
326
|
+
* through all nested property accesses, ensuring the result type includes
|
|
327
|
+
* `| undefined` for all fields accessed through this ref.
|
|
336
328
|
*
|
|
337
329
|
* Example usage:
|
|
338
330
|
* ```typescript
|
|
339
|
-
* //
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
331
|
+
* // Non-nullable ref (inner join or from table):
|
|
332
|
+
* select(({ user }) => ({ name: user.name })) // result: string
|
|
333
|
+
*
|
|
334
|
+
* // Nullable ref (left join right side):
|
|
335
|
+
* select(({ dept }) => ({ name: dept.name })) // result: string | undefined
|
|
343
336
|
*
|
|
344
337
|
* // Spread operations work cleanly:
|
|
345
338
|
* select(({ user }) => ({ ...user })) // Returns User type, not Ref types
|
|
346
339
|
* ```
|
|
347
340
|
*/
|
|
348
|
-
export type Ref<T = any> = {
|
|
349
|
-
[K in keyof T]: IsNonExactOptional<T[K]> extends true ? IsNonExactNullable<T[K]> extends true ? IsPlainObject<NonNullable<T[K]>> extends true ? Ref<NonNullable<T[K]
|
|
350
|
-
} & RefLeaf<T>;
|
|
341
|
+
export type Ref<T = any, Nullable extends boolean = false> = {
|
|
342
|
+
[K in keyof T]: IsNonExactOptional<T[K]> extends true ? IsNonExactNullable<T[K]> extends true ? IsPlainObject<NonNullable<T[K]>> extends true ? Ref<NonNullable<T[K]>, Nullable> | undefined : RefLeaf<NonNullable<T[K]>, Nullable> | undefined : IsPlainObject<NonUndefined<T[K]>> extends true ? Ref<NonUndefined<T[K]>, Nullable> | undefined : RefLeaf<NonUndefined<T[K]>, Nullable> | undefined : IsNonExactNullable<T[K]> extends true ? IsPlainObject<NonNull<T[K]>> extends true ? Ref<NonNull<T[K]>, Nullable> | null : RefLeaf<NonNull<T[K]>, Nullable> | null : IsPlainObject<T[K]> extends true ? Ref<T[K], Nullable> : RefLeaf<T[K], Nullable>;
|
|
343
|
+
} & RefLeaf<T, Nullable>;
|
|
351
344
|
/**
|
|
352
345
|
* Ref - The user-facing ref type with clean IDE display
|
|
353
346
|
*
|
|
@@ -360,10 +353,14 @@ export type Ref<T = any> = {
|
|
|
360
353
|
* - No internal properties like __refProxy, __path, __type are visible
|
|
361
354
|
*/
|
|
362
355
|
declare const RefBrand: unique symbol;
|
|
363
|
-
|
|
356
|
+
declare const NullableBrand: unique symbol;
|
|
357
|
+
export type RefLeaf<T = any, Nullable extends boolean = false> = {
|
|
364
358
|
readonly [RefBrand]?: T;
|
|
365
|
-
}
|
|
366
|
-
|
|
359
|
+
} & ([Nullable] extends [true] ? {
|
|
360
|
+
readonly [NullableBrand]?: true;
|
|
361
|
+
} : {});
|
|
362
|
+
type IsNullableRef<T> = typeof NullableBrand extends keyof T ? true : false;
|
|
363
|
+
type WithoutRefBrand<T> = T extends Record<string, any> ? Omit<T, typeof RefBrand | typeof NullableBrand> : T;
|
|
367
364
|
/**
|
|
368
365
|
* PreserveSingleResultFlag - Conditionally includes the singleResult flag
|
|
369
366
|
*
|
|
@@ -94,6 +94,9 @@ function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks, l
|
|
|
94
94
|
if (query.distinct && !query.fnSelect && !query.select) {
|
|
95
95
|
throw new errors.DistinctRequiresSelectError();
|
|
96
96
|
}
|
|
97
|
+
if (query.fnSelect && query.groupBy && query.groupBy.length > 0) {
|
|
98
|
+
throw new errors.FnSelectWithGroupByError();
|
|
99
|
+
}
|
|
97
100
|
if (query.fnSelect) {
|
|
98
101
|
pipeline = pipeline.pipe(
|
|
99
102
|
dbIvm.map(([key, namespacedRow]) => {
|