@tanstack/db 0.5.7 → 0.5.9

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.
@@ -1 +1 @@
1
- {"version":3,"file":"base-index.js","sources":["../../../src/indexes/base-index.ts"],"sourcesContent":["import { compileSingleRowExpression } from \"../query/compiler/evaluators.js\"\nimport { comparisonFunctions } from \"../query/builder/functions.js\"\nimport { DEFAULT_COMPARE_OPTIONS, deepEquals } from \"../utils.js\"\nimport type { RangeQueryOptions } from \"./btree-index.js\"\nimport type { CompareOptions } from \"../query/builder/types.js\"\nimport type { BasicExpression, OrderByDirection } from \"../query/ir.js\"\n\n/**\n * Operations that indexes can support, imported from available comparison functions\n */\nexport const IndexOperation = comparisonFunctions\n\n/**\n * Type for index operation values\n */\nexport type IndexOperation = (typeof comparisonFunctions)[number]\n\n/**\n * Statistics about index usage and performance\n */\nexport interface IndexStats {\n readonly entryCount: number\n readonly lookupCount: number\n readonly averageLookupTime: number\n readonly lastUpdated: Date\n}\n\nexport interface IndexInterface<\n TKey extends string | number = string | number,\n> {\n add: (key: TKey, item: any) => void\n remove: (key: TKey, item: any) => void\n update: (key: TKey, oldItem: any, newItem: any) => void\n\n build: (entries: Iterable<[TKey, any]>) => void\n clear: () => void\n\n lookup: (operation: IndexOperation, value: any) => Set<TKey>\n\n equalityLookup: (value: any) => Set<TKey>\n inArrayLookup: (values: Array<any>) => Set<TKey>\n\n rangeQuery: (options: RangeQueryOptions) => Set<TKey>\n rangeQueryReversed: (options: RangeQueryOptions) => Set<TKey>\n\n take: (\n n: number,\n from?: TKey,\n filterFn?: (key: TKey) => boolean\n ) => Array<TKey>\n takeReversed: (\n n: number,\n from?: TKey,\n filterFn?: (key: TKey) => boolean\n ) => Array<TKey>\n\n get keyCount(): number\n get orderedEntriesArray(): Array<[any, Set<TKey>]>\n get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n\n get indexedKeysSet(): Set<TKey>\n get valueMapData(): Map<any, Set<TKey>>\n\n supports: (operation: IndexOperation) => boolean\n\n matchesField: (fieldPath: Array<string>) => boolean\n matchesCompareOptions: (compareOptions: CompareOptions) => boolean\n matchesDirection: (direction: OrderByDirection) => boolean\n\n getStats: () => IndexStats\n}\n\n/**\n * Base abstract class that all index types extend\n */\nexport abstract class BaseIndex<TKey extends string | number = string | number>\n implements IndexInterface<TKey>\n{\n public readonly id: number\n public readonly name?: string\n public readonly expression: BasicExpression\n public abstract readonly supportedOperations: Set<IndexOperation>\n\n protected lookupCount = 0\n protected totalLookupTime = 0\n protected lastUpdated = new Date()\n protected compareOptions: CompareOptions\n\n constructor(\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any\n ) {\n this.id = id\n this.expression = expression\n this.compareOptions = DEFAULT_COMPARE_OPTIONS\n this.name = name\n this.initialize(options)\n }\n\n // Abstract methods that each index type must implement\n abstract add(key: TKey, item: any): void\n abstract remove(key: TKey, item: any): void\n abstract update(key: TKey, oldItem: any, newItem: any): void\n abstract build(entries: Iterable<[TKey, any]>): void\n abstract clear(): void\n abstract lookup(operation: IndexOperation, value: any): Set<TKey>\n abstract take(\n n: number,\n from?: TKey,\n filterFn?: (key: TKey) => boolean\n ): Array<TKey>\n abstract takeReversed(\n n: number,\n from?: TKey,\n filterFn?: (key: TKey) => boolean\n ): Array<TKey>\n abstract get keyCount(): number\n abstract equalityLookup(value: any): Set<TKey>\n abstract inArrayLookup(values: Array<any>): Set<TKey>\n abstract rangeQuery(options: RangeQueryOptions): Set<TKey>\n abstract rangeQueryReversed(options: RangeQueryOptions): Set<TKey>\n abstract get orderedEntriesArray(): Array<[any, Set<TKey>]>\n abstract get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n abstract get indexedKeysSet(): Set<TKey>\n abstract get valueMapData(): Map<any, Set<TKey>>\n\n // Common methods\n supports(operation: IndexOperation): boolean {\n return this.supportedOperations.has(operation)\n }\n\n matchesField(fieldPath: Array<string>): boolean {\n return (\n this.expression.type === `ref` &&\n this.expression.path.length === fieldPath.length &&\n this.expression.path.every((part, i) => part === fieldPath[i])\n )\n }\n\n /**\n * Checks if the compare options match the index's compare options.\n * The direction is ignored because the index can be reversed if the direction is different.\n */\n matchesCompareOptions(compareOptions: CompareOptions): boolean {\n const thisCompareOptionsWithoutDirection = {\n ...this.compareOptions,\n direction: undefined,\n }\n const compareOptionsWithoutDirection = {\n ...compareOptions,\n direction: undefined,\n }\n\n return deepEquals(\n thisCompareOptionsWithoutDirection,\n compareOptionsWithoutDirection\n )\n }\n\n /**\n * Checks if the index matches the provided direction.\n */\n matchesDirection(direction: OrderByDirection): boolean {\n return this.compareOptions.direction === direction\n }\n\n getStats(): IndexStats {\n return {\n entryCount: this.keyCount,\n lookupCount: this.lookupCount,\n averageLookupTime:\n this.lookupCount > 0 ? this.totalLookupTime / this.lookupCount : 0,\n lastUpdated: this.lastUpdated,\n }\n }\n\n // Protected methods for subclasses\n protected abstract initialize(options?: any): void\n\n protected evaluateIndexExpression(item: any): any {\n const evaluator = compileSingleRowExpression(this.expression)\n return evaluator(item as Record<string, unknown>)\n }\n\n protected trackLookup(startTime: number): void {\n const duration = performance.now() - startTime\n this.lookupCount++\n this.totalLookupTime += duration\n }\n\n protected updateTimestamp(): void {\n this.lastUpdated = new Date()\n }\n}\n\n/**\n * Type for index constructor\n */\nexport type IndexConstructor<TKey extends string | number = string | number> =\n new (\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any\n ) => BaseIndex<TKey>\n\n/**\n * Index resolver can be either a class constructor or async loader\n */\nexport type IndexResolver<TKey extends string | number = string | number> =\n | IndexConstructor<TKey>\n | (() => Promise<IndexConstructor<TKey>>)\n"],"names":[],"mappings":";;;AAUO,MAAM,iBAAiB;AAiEvB,MAAe,UAEtB;AAAA,EAWE,YACE,IACA,YACA,MACA,SACA;AAVF,SAAU,cAAc;AACxB,SAAU,kBAAkB;AAC5B,SAAU,kCAAkB,KAAA;AAS1B,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,iBAAiB;AACtB,SAAK,OAAO;AACZ,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA,EA8BA,SAAS,WAAoC;AAC3C,WAAO,KAAK,oBAAoB,IAAI,SAAS;AAAA,EAC/C;AAAA,EAEA,aAAa,WAAmC;AAC9C,WACE,KAAK,WAAW,SAAS,SACzB,KAAK,WAAW,KAAK,WAAW,UAAU,UAC1C,KAAK,WAAW,KAAK,MAAM,CAAC,MAAM,MAAM,SAAS,UAAU,CAAC,CAAC;AAAA,EAEjE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,gBAAyC;AAC7D,UAAM,qCAAqC;AAAA,MACzC,GAAG,KAAK;AAAA,MACR,WAAW;AAAA,IAAA;AAEb,UAAM,iCAAiC;AAAA,MACrC,GAAG;AAAA,MACH,WAAW;AAAA,IAAA;AAGb,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,WAAsC;AACrD,WAAO,KAAK,eAAe,cAAc;AAAA,EAC3C;AAAA,EAEA,WAAuB;AACrB,WAAO;AAAA,MACL,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,mBACE,KAAK,cAAc,IAAI,KAAK,kBAAkB,KAAK,cAAc;AAAA,MACnE,aAAa,KAAK;AAAA,IAAA;AAAA,EAEtB;AAAA,EAKU,wBAAwB,MAAgB;AAChD,UAAM,YAAY,2BAA2B,KAAK,UAAU;AAC5D,WAAO,UAAU,IAA+B;AAAA,EAClD;AAAA,EAEU,YAAY,WAAyB;AAC7C,UAAM,WAAW,YAAY,IAAA,IAAQ;AACrC,SAAK;AACL,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEU,kBAAwB;AAChC,SAAK,kCAAkB,KAAA;AAAA,EACzB;AACF;"}
1
+ {"version":3,"file":"base-index.js","sources":["../../../src/indexes/base-index.ts"],"sourcesContent":["import { compileSingleRowExpression } from \"../query/compiler/evaluators.js\"\nimport { comparisonFunctions } from \"../query/builder/functions.js\"\nimport { DEFAULT_COMPARE_OPTIONS, deepEquals } from \"../utils.js\"\nimport type { RangeQueryOptions } from \"./btree-index.js\"\nimport type { CompareOptions } from \"../query/builder/types.js\"\nimport type { BasicExpression, OrderByDirection } from \"../query/ir.js\"\n\n/**\n * Operations that indexes can support, imported from available comparison functions\n */\nexport const IndexOperation = comparisonFunctions\n\n/**\n * Type for index operation values\n */\nexport type IndexOperation = (typeof comparisonFunctions)[number]\n\n/**\n * Statistics about index usage and performance\n */\nexport interface IndexStats {\n readonly entryCount: number\n readonly lookupCount: number\n readonly averageLookupTime: number\n readonly lastUpdated: Date\n}\n\nexport interface IndexInterface<\n TKey extends string | number = string | number,\n> {\n add: (key: TKey, item: any) => void\n remove: (key: TKey, item: any) => void\n update: (key: TKey, oldItem: any, newItem: any) => void\n\n build: (entries: Iterable<[TKey, any]>) => void\n clear: () => void\n\n lookup: (operation: IndexOperation, value: any) => Set<TKey>\n\n equalityLookup: (value: any) => Set<TKey>\n inArrayLookup: (values: Array<any>) => Set<TKey>\n\n rangeQuery: (options: RangeQueryOptions) => Set<TKey>\n rangeQueryReversed: (options: RangeQueryOptions) => Set<TKey>\n\n take: (\n n: number,\n from?: TKey,\n filterFn?: (key: TKey) => boolean\n ) => Array<TKey>\n takeReversed: (\n n: number,\n from?: TKey,\n filterFn?: (key: TKey) => boolean\n ) => Array<TKey>\n\n get keyCount(): number\n get orderedEntriesArray(): Array<[any, Set<TKey>]>\n get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n\n get indexedKeysSet(): Set<TKey>\n get valueMapData(): Map<any, Set<TKey>>\n\n supports: (operation: IndexOperation) => boolean\n\n matchesField: (fieldPath: Array<string>) => boolean\n matchesCompareOptions: (compareOptions: CompareOptions) => boolean\n matchesDirection: (direction: OrderByDirection) => boolean\n\n getStats: () => IndexStats\n}\n\n/**\n * Base abstract class that all index types extend\n */\nexport abstract class BaseIndex<\n TKey extends string | number = string | number,\n> implements IndexInterface<TKey> {\n public readonly id: number\n public readonly name?: string\n public readonly expression: BasicExpression\n public abstract readonly supportedOperations: Set<IndexOperation>\n\n protected lookupCount = 0\n protected totalLookupTime = 0\n protected lastUpdated = new Date()\n protected compareOptions: CompareOptions\n\n constructor(\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any\n ) {\n this.id = id\n this.expression = expression\n this.compareOptions = DEFAULT_COMPARE_OPTIONS\n this.name = name\n this.initialize(options)\n }\n\n // Abstract methods that each index type must implement\n abstract add(key: TKey, item: any): void\n abstract remove(key: TKey, item: any): void\n abstract update(key: TKey, oldItem: any, newItem: any): void\n abstract build(entries: Iterable<[TKey, any]>): void\n abstract clear(): void\n abstract lookup(operation: IndexOperation, value: any): Set<TKey>\n abstract take(\n n: number,\n from?: TKey,\n filterFn?: (key: TKey) => boolean\n ): Array<TKey>\n abstract takeReversed(\n n: number,\n from?: TKey,\n filterFn?: (key: TKey) => boolean\n ): Array<TKey>\n abstract get keyCount(): number\n abstract equalityLookup(value: any): Set<TKey>\n abstract inArrayLookup(values: Array<any>): Set<TKey>\n abstract rangeQuery(options: RangeQueryOptions): Set<TKey>\n abstract rangeQueryReversed(options: RangeQueryOptions): Set<TKey>\n abstract get orderedEntriesArray(): Array<[any, Set<TKey>]>\n abstract get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n abstract get indexedKeysSet(): Set<TKey>\n abstract get valueMapData(): Map<any, Set<TKey>>\n\n // Common methods\n supports(operation: IndexOperation): boolean {\n return this.supportedOperations.has(operation)\n }\n\n matchesField(fieldPath: Array<string>): boolean {\n return (\n this.expression.type === `ref` &&\n this.expression.path.length === fieldPath.length &&\n this.expression.path.every((part, i) => part === fieldPath[i])\n )\n }\n\n /**\n * Checks if the compare options match the index's compare options.\n * The direction is ignored because the index can be reversed if the direction is different.\n */\n matchesCompareOptions(compareOptions: CompareOptions): boolean {\n const thisCompareOptionsWithoutDirection = {\n ...this.compareOptions,\n direction: undefined,\n }\n const compareOptionsWithoutDirection = {\n ...compareOptions,\n direction: undefined,\n }\n\n return deepEquals(\n thisCompareOptionsWithoutDirection,\n compareOptionsWithoutDirection\n )\n }\n\n /**\n * Checks if the index matches the provided direction.\n */\n matchesDirection(direction: OrderByDirection): boolean {\n return this.compareOptions.direction === direction\n }\n\n getStats(): IndexStats {\n return {\n entryCount: this.keyCount,\n lookupCount: this.lookupCount,\n averageLookupTime:\n this.lookupCount > 0 ? this.totalLookupTime / this.lookupCount : 0,\n lastUpdated: this.lastUpdated,\n }\n }\n\n // Protected methods for subclasses\n protected abstract initialize(options?: any): void\n\n protected evaluateIndexExpression(item: any): any {\n const evaluator = compileSingleRowExpression(this.expression)\n return evaluator(item as Record<string, unknown>)\n }\n\n protected trackLookup(startTime: number): void {\n const duration = performance.now() - startTime\n this.lookupCount++\n this.totalLookupTime += duration\n }\n\n protected updateTimestamp(): void {\n this.lastUpdated = new Date()\n }\n}\n\n/**\n * Type for index constructor\n */\nexport type IndexConstructor<TKey extends string | number = string | number> =\n new (\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any\n ) => BaseIndex<TKey>\n\n/**\n * Index resolver can be either a class constructor or async loader\n */\nexport type IndexResolver<TKey extends string | number = string | number> =\n | IndexConstructor<TKey>\n | (() => Promise<IndexConstructor<TKey>>)\n"],"names":[],"mappings":";;;AAUO,MAAM,iBAAiB;AAiEvB,MAAe,UAEY;AAAA,EAWhC,YACE,IACA,YACA,MACA,SACA;AAVF,SAAU,cAAc;AACxB,SAAU,kBAAkB;AAC5B,SAAU,kCAAkB,KAAA;AAS1B,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,iBAAiB;AACtB,SAAK,OAAO;AACZ,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA,EA8BA,SAAS,WAAoC;AAC3C,WAAO,KAAK,oBAAoB,IAAI,SAAS;AAAA,EAC/C;AAAA,EAEA,aAAa,WAAmC;AAC9C,WACE,KAAK,WAAW,SAAS,SACzB,KAAK,WAAW,KAAK,WAAW,UAAU,UAC1C,KAAK,WAAW,KAAK,MAAM,CAAC,MAAM,MAAM,SAAS,UAAU,CAAC,CAAC;AAAA,EAEjE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,gBAAyC;AAC7D,UAAM,qCAAqC;AAAA,MACzC,GAAG,KAAK;AAAA,MACR,WAAW;AAAA,IAAA;AAEb,UAAM,iCAAiC;AAAA,MACrC,GAAG;AAAA,MACH,WAAW;AAAA,IAAA;AAGb,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,WAAsC;AACrD,WAAO,KAAK,eAAe,cAAc;AAAA,EAC3C;AAAA,EAEA,WAAuB;AACrB,WAAO;AAAA,MACL,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,mBACE,KAAK,cAAc,IAAI,KAAK,kBAAkB,KAAK,cAAc;AAAA,MACnE,aAAa,KAAK;AAAA,IAAA;AAAA,EAEtB;AAAA,EAKU,wBAAwB,MAAgB;AAChD,UAAM,YAAY,2BAA2B,KAAK,UAAU;AAC5D,WAAO,UAAU,IAA+B;AAAA,EAClD;AAAA,EAEU,YAAY,WAAyB;AAC7C,UAAM,WAAW,YAAY,IAAA,IAAQ;AACrC,SAAK;AACL,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEU,kBAAwB;AAChC,SAAK,kCAAkB,KAAA;AAAA,EACzB;AACF;"}
@@ -1 +1 @@
1
- {"version":3,"file":"reverse-index.js","sources":["../../../src/indexes/reverse-index.ts"],"sourcesContent":["import type { CompareOptions } from \"../query/builder/types\"\nimport type { OrderByDirection } from \"../query/ir\"\nimport type { IndexInterface, IndexOperation, IndexStats } from \"./base-index\"\nimport type { RangeQueryOptions } from \"./btree-index\"\n\nexport class ReverseIndex<TKey extends string | number>\n implements IndexInterface<TKey>\n{\n private originalIndex: IndexInterface<TKey>\n\n constructor(index: IndexInterface<TKey>) {\n this.originalIndex = index\n }\n\n // Define the reversed operations\n\n lookup(operation: IndexOperation, value: any): Set<TKey> {\n const reverseOperation =\n operation === `gt`\n ? `lt`\n : operation === `gte`\n ? `lte`\n : operation === `lt`\n ? `gt`\n : operation === `lte`\n ? `gte`\n : operation\n return this.originalIndex.lookup(reverseOperation, value)\n }\n\n rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {\n return this.originalIndex.rangeQueryReversed(options)\n }\n\n rangeQueryReversed(options: RangeQueryOptions = {}): Set<TKey> {\n return this.originalIndex.rangeQuery(options)\n }\n\n take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey> {\n return this.originalIndex.takeReversed(n, from, filterFn)\n }\n\n takeReversed(\n n: number,\n from?: any,\n filterFn?: (key: TKey) => boolean\n ): Array<TKey> {\n return this.originalIndex.take(n, from, filterFn)\n }\n\n get orderedEntriesArray(): Array<[any, Set<TKey>]> {\n return this.originalIndex.orderedEntriesArrayReversed\n }\n\n get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]> {\n return this.originalIndex.orderedEntriesArray\n }\n\n // All operations below delegate to the original index\n\n supports(operation: IndexOperation): boolean {\n return this.originalIndex.supports(operation)\n }\n\n matchesField(fieldPath: Array<string>): boolean {\n return this.originalIndex.matchesField(fieldPath)\n }\n\n matchesCompareOptions(compareOptions: CompareOptions): boolean {\n return this.originalIndex.matchesCompareOptions(compareOptions)\n }\n\n matchesDirection(direction: OrderByDirection): boolean {\n return this.originalIndex.matchesDirection(direction)\n }\n\n getStats(): IndexStats {\n return this.originalIndex.getStats()\n }\n\n add(key: TKey, item: any): void {\n this.originalIndex.add(key, item)\n }\n\n remove(key: TKey, item: any): void {\n this.originalIndex.remove(key, item)\n }\n\n update(key: TKey, oldItem: any, newItem: any): void {\n this.originalIndex.update(key, oldItem, newItem)\n }\n\n build(entries: Iterable<[TKey, any]>): void {\n this.originalIndex.build(entries)\n }\n\n clear(): void {\n this.originalIndex.clear()\n }\n\n get keyCount(): number {\n return this.originalIndex.keyCount\n }\n\n equalityLookup(value: any): Set<TKey> {\n return this.originalIndex.equalityLookup(value)\n }\n\n inArrayLookup(values: Array<any>): Set<TKey> {\n return this.originalIndex.inArrayLookup(values)\n }\n\n get indexedKeysSet(): Set<TKey> {\n return this.originalIndex.indexedKeysSet\n }\n\n get valueMapData(): Map<any, Set<TKey>> {\n return this.originalIndex.valueMapData\n }\n}\n"],"names":[],"mappings":"AAKO,MAAM,aAEb;AAAA,EAGE,YAAY,OAA6B;AACvC,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAIA,OAAO,WAA2B,OAAuB;AACvD,UAAM,mBACJ,cAAc,OACV,OACA,cAAc,QACZ,QACA,cAAc,OACZ,OACA,cAAc,QACZ,QACA;AACZ,WAAO,KAAK,cAAc,OAAO,kBAAkB,KAAK;AAAA,EAC1D;AAAA,EAEA,WAAW,UAA6B,IAAe;AACrD,WAAO,KAAK,cAAc,mBAAmB,OAAO;AAAA,EACtD;AAAA,EAEA,mBAAmB,UAA6B,IAAe;AAC7D,WAAO,KAAK,cAAc,WAAW,OAAO;AAAA,EAC9C;AAAA,EAEA,KAAK,GAAW,MAAY,UAAgD;AAC1E,WAAO,KAAK,cAAc,aAAa,GAAG,MAAM,QAAQ;AAAA,EAC1D;AAAA,EAEA,aACE,GACA,MACA,UACa;AACb,WAAO,KAAK,cAAc,KAAK,GAAG,MAAM,QAAQ;AAAA,EAClD;AAAA,EAEA,IAAI,sBAA+C;AACjD,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA,EAEA,IAAI,8BAAuD;AACzD,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA,EAIA,SAAS,WAAoC;AAC3C,WAAO,KAAK,cAAc,SAAS,SAAS;AAAA,EAC9C;AAAA,EAEA,aAAa,WAAmC;AAC9C,WAAO,KAAK,cAAc,aAAa,SAAS;AAAA,EAClD;AAAA,EAEA,sBAAsB,gBAAyC;AAC7D,WAAO,KAAK,cAAc,sBAAsB,cAAc;AAAA,EAChE;AAAA,EAEA,iBAAiB,WAAsC;AACrD,WAAO,KAAK,cAAc,iBAAiB,SAAS;AAAA,EACtD;AAAA,EAEA,WAAuB;AACrB,WAAO,KAAK,cAAc,SAAA;AAAA,EAC5B;AAAA,EAEA,IAAI,KAAW,MAAiB;AAC9B,SAAK,cAAc,IAAI,KAAK,IAAI;AAAA,EAClC;AAAA,EAEA,OAAO,KAAW,MAAiB;AACjC,SAAK,cAAc,OAAO,KAAK,IAAI;AAAA,EACrC;AAAA,EAEA,OAAO,KAAW,SAAc,SAAoB;AAClD,SAAK,cAAc,OAAO,KAAK,SAAS,OAAO;AAAA,EACjD;AAAA,EAEA,MAAM,SAAsC;AAC1C,SAAK,cAAc,MAAM,OAAO;AAAA,EAClC;AAAA,EAEA,QAAc;AACZ,SAAK,cAAc,MAAA;AAAA,EACrB;AAAA,EAEA,IAAI,WAAmB;AACrB,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA,EAEA,eAAe,OAAuB;AACpC,WAAO,KAAK,cAAc,eAAe,KAAK;AAAA,EAChD;AAAA,EAEA,cAAc,QAA+B;AAC3C,WAAO,KAAK,cAAc,cAAc,MAAM;AAAA,EAChD;AAAA,EAEA,IAAI,iBAA4B;AAC9B,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA,EAEA,IAAI,eAAoC;AACtC,WAAO,KAAK,cAAc;AAAA,EAC5B;AACF;"}
1
+ {"version":3,"file":"reverse-index.js","sources":["../../../src/indexes/reverse-index.ts"],"sourcesContent":["import type { CompareOptions } from \"../query/builder/types\"\nimport type { OrderByDirection } from \"../query/ir\"\nimport type { IndexInterface, IndexOperation, IndexStats } from \"./base-index\"\nimport type { RangeQueryOptions } from \"./btree-index\"\n\nexport class ReverseIndex<\n TKey extends string | number,\n> implements IndexInterface<TKey> {\n private originalIndex: IndexInterface<TKey>\n\n constructor(index: IndexInterface<TKey>) {\n this.originalIndex = index\n }\n\n // Define the reversed operations\n\n lookup(operation: IndexOperation, value: any): Set<TKey> {\n const reverseOperation =\n operation === `gt`\n ? `lt`\n : operation === `gte`\n ? `lte`\n : operation === `lt`\n ? `gt`\n : operation === `lte`\n ? `gte`\n : operation\n return this.originalIndex.lookup(reverseOperation, value)\n }\n\n rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {\n return this.originalIndex.rangeQueryReversed(options)\n }\n\n rangeQueryReversed(options: RangeQueryOptions = {}): Set<TKey> {\n return this.originalIndex.rangeQuery(options)\n }\n\n take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey> {\n return this.originalIndex.takeReversed(n, from, filterFn)\n }\n\n takeReversed(\n n: number,\n from?: any,\n filterFn?: (key: TKey) => boolean\n ): Array<TKey> {\n return this.originalIndex.take(n, from, filterFn)\n }\n\n get orderedEntriesArray(): Array<[any, Set<TKey>]> {\n return this.originalIndex.orderedEntriesArrayReversed\n }\n\n get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]> {\n return this.originalIndex.orderedEntriesArray\n }\n\n // All operations below delegate to the original index\n\n supports(operation: IndexOperation): boolean {\n return this.originalIndex.supports(operation)\n }\n\n matchesField(fieldPath: Array<string>): boolean {\n return this.originalIndex.matchesField(fieldPath)\n }\n\n matchesCompareOptions(compareOptions: CompareOptions): boolean {\n return this.originalIndex.matchesCompareOptions(compareOptions)\n }\n\n matchesDirection(direction: OrderByDirection): boolean {\n return this.originalIndex.matchesDirection(direction)\n }\n\n getStats(): IndexStats {\n return this.originalIndex.getStats()\n }\n\n add(key: TKey, item: any): void {\n this.originalIndex.add(key, item)\n }\n\n remove(key: TKey, item: any): void {\n this.originalIndex.remove(key, item)\n }\n\n update(key: TKey, oldItem: any, newItem: any): void {\n this.originalIndex.update(key, oldItem, newItem)\n }\n\n build(entries: Iterable<[TKey, any]>): void {\n this.originalIndex.build(entries)\n }\n\n clear(): void {\n this.originalIndex.clear()\n }\n\n get keyCount(): number {\n return this.originalIndex.keyCount\n }\n\n equalityLookup(value: any): Set<TKey> {\n return this.originalIndex.equalityLookup(value)\n }\n\n inArrayLookup(values: Array<any>): Set<TKey> {\n return this.originalIndex.inArrayLookup(values)\n }\n\n get indexedKeysSet(): Set<TKey> {\n return this.originalIndex.indexedKeysSet\n }\n\n get valueMapData(): Map<any, Set<TKey>> {\n return this.originalIndex.valueMapData\n }\n}\n"],"names":[],"mappings":"AAKO,MAAM,aAEqB;AAAA,EAGhC,YAAY,OAA6B;AACvC,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAIA,OAAO,WAA2B,OAAuB;AACvD,UAAM,mBACJ,cAAc,OACV,OACA,cAAc,QACZ,QACA,cAAc,OACZ,OACA,cAAc,QACZ,QACA;AACZ,WAAO,KAAK,cAAc,OAAO,kBAAkB,KAAK;AAAA,EAC1D;AAAA,EAEA,WAAW,UAA6B,IAAe;AACrD,WAAO,KAAK,cAAc,mBAAmB,OAAO;AAAA,EACtD;AAAA,EAEA,mBAAmB,UAA6B,IAAe;AAC7D,WAAO,KAAK,cAAc,WAAW,OAAO;AAAA,EAC9C;AAAA,EAEA,KAAK,GAAW,MAAY,UAAgD;AAC1E,WAAO,KAAK,cAAc,aAAa,GAAG,MAAM,QAAQ;AAAA,EAC1D;AAAA,EAEA,aACE,GACA,MACA,UACa;AACb,WAAO,KAAK,cAAc,KAAK,GAAG,MAAM,QAAQ;AAAA,EAClD;AAAA,EAEA,IAAI,sBAA+C;AACjD,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA,EAEA,IAAI,8BAAuD;AACzD,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA,EAIA,SAAS,WAAoC;AAC3C,WAAO,KAAK,cAAc,SAAS,SAAS;AAAA,EAC9C;AAAA,EAEA,aAAa,WAAmC;AAC9C,WAAO,KAAK,cAAc,aAAa,SAAS;AAAA,EAClD;AAAA,EAEA,sBAAsB,gBAAyC;AAC7D,WAAO,KAAK,cAAc,sBAAsB,cAAc;AAAA,EAChE;AAAA,EAEA,iBAAiB,WAAsC;AACrD,WAAO,KAAK,cAAc,iBAAiB,SAAS;AAAA,EACtD;AAAA,EAEA,WAAuB;AACrB,WAAO,KAAK,cAAc,SAAA;AAAA,EAC5B;AAAA,EAEA,IAAI,KAAW,MAAiB;AAC9B,SAAK,cAAc,IAAI,KAAK,IAAI;AAAA,EAClC;AAAA,EAEA,OAAO,KAAW,MAAiB;AACjC,SAAK,cAAc,OAAO,KAAK,IAAI;AAAA,EACrC;AAAA,EAEA,OAAO,KAAW,SAAc,SAAoB;AAClD,SAAK,cAAc,OAAO,KAAK,SAAS,OAAO;AAAA,EACjD;AAAA,EAEA,MAAM,SAAsC;AAC1C,SAAK,cAAc,MAAM,OAAO;AAAA,EAClC;AAAA,EAEA,QAAc;AACZ,SAAK,cAAc,MAAA;AAAA,EACrB;AAAA,EAEA,IAAI,WAAmB;AACrB,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA,EAEA,eAAe,OAAuB;AACpC,WAAO,KAAK,cAAc,eAAe,KAAK;AAAA,EAChD;AAAA,EAEA,cAAc,QAA+B;AAC3C,WAAO,KAAK,cAAc,cAAc,MAAM;AAAA,EAChD;AAAA,EAEA,IAAI,iBAA4B;AAC9B,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA,EAEA,IAAI,eAAoC;AACtC,WAAO,KAAK,cAAc;AAAA,EAC5B;AACF;"}
@@ -1 +1 @@
1
- {"version":3,"file":"local-only.js","sources":["../../src/local-only.ts"],"sourcesContent":["import type {\n BaseCollectionConfig,\n CollectionConfig,\n DeleteMutationFnParams,\n InferSchemaOutput,\n InsertMutationFnParams,\n OperationType,\n PendingMutation,\n SyncConfig,\n UpdateMutationFnParams,\n UtilsRecord,\n} from \"./types\"\nimport type { Collection } from \"./collection/index\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\n\n/**\n * Configuration interface for Local-only collection options\n * @template T - The type of items in the collection\n * @template TSchema - The schema type for validation\n * @template TKey - The type of the key returned by `getKey`\n */\nexport interface LocalOnlyCollectionConfig<\n T extends object = object,\n TSchema extends StandardSchemaV1 = never,\n TKey extends string | number = string | number,\n> extends Omit<\n BaseCollectionConfig<T, TKey, TSchema, LocalOnlyCollectionUtils>,\n `gcTime` | `startSync`\n > {\n /**\n * Optional initial data to populate the collection with on creation\n * This data will be applied during the initial sync process\n */\n initialData?: Array<T>\n}\n\n/**\n * Local-only collection utilities type\n */\nexport interface LocalOnlyCollectionUtils extends UtilsRecord {\n /**\n * Accepts mutations from a transaction that belong to this collection and persists them.\n * This should be called in your transaction's mutationFn to persist local-only data.\n *\n * @param transaction - The transaction containing mutations to accept\n * @example\n * const localData = createCollection(localOnlyCollectionOptions({...}))\n *\n * const tx = createTransaction({\n * mutationFn: async ({ transaction }) => {\n * // Make API call first\n * await api.save(...)\n * // Then persist local-only mutations after success\n * localData.utils.acceptMutations(transaction)\n * }\n * })\n */\n acceptMutations: (transaction: {\n mutations: Array<PendingMutation<Record<string, unknown>>>\n }) => void\n}\n\ntype LocalOnlyCollectionOptionsResult<\n T extends object,\n TKey extends string | number,\n TSchema extends StandardSchemaV1 | never = never,\n> = CollectionConfig<T, TKey, TSchema> & {\n utils: LocalOnlyCollectionUtils\n}\n\n/**\n * Creates Local-only collection options for use with a standard Collection\n *\n * This is an in-memory collection that doesn't sync with external sources but uses a loopback sync config\n * that immediately \"syncs\" all optimistic changes to the collection, making them permanent.\n * Perfect for local-only data that doesn't need persistence or external synchronization.\n *\n * **Using with Manual Transactions:**\n *\n * For manual transactions, you must call `utils.acceptMutations()` in your transaction's `mutationFn`\n * to persist changes made during `tx.mutate()`. This is necessary because local-only collections\n * don't participate in the standard mutation handler flow for manual transactions.\n *\n * @template T - The schema type if a schema is provided, otherwise the type of items in the collection\n * @template TKey - The type of the key returned by getKey\n * @param config - Configuration options for the Local-only collection\n * @returns Collection options with utilities including acceptMutations\n *\n * @example\n * // Basic local-only collection\n * const collection = createCollection(\n * localOnlyCollectionOptions({\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // Local-only collection with initial data\n * const collection = createCollection(\n * localOnlyCollectionOptions({\n * getKey: (item) => item.id,\n * initialData: [\n * { id: 1, name: 'Item 1' },\n * { id: 2, name: 'Item 2' },\n * ],\n * })\n * )\n *\n * @example\n * // Local-only collection with mutation handlers\n * const collection = createCollection(\n * localOnlyCollectionOptions({\n * getKey: (item) => item.id,\n * onInsert: async ({ transaction }) => {\n * console.log('Item inserted:', transaction.mutations[0].modified)\n * // Custom logic after insert\n * },\n * })\n * )\n *\n * @example\n * // Using with manual transactions\n * const localData = createCollection(\n * localOnlyCollectionOptions({\n * getKey: (item) => item.id,\n * })\n * )\n *\n * const tx = createTransaction({\n * mutationFn: async ({ transaction }) => {\n * // Use local data in API call\n * const localMutations = transaction.mutations.filter(m => m.collection === localData)\n * await api.save({ metadata: localMutations[0]?.modified })\n *\n * // Persist local-only mutations after API success\n * localData.utils.acceptMutations(transaction)\n * }\n * })\n *\n * tx.mutate(() => {\n * localData.insert({ id: 1, data: 'metadata' })\n * apiCollection.insert({ id: 2, data: 'main data' })\n * })\n *\n * await tx.commit()\n */\n\n// Overload for when schema is provided\nexport function localOnlyCollectionOptions<\n T extends StandardSchemaV1,\n TKey extends string | number = string | number,\n>(\n config: LocalOnlyCollectionConfig<InferSchemaOutput<T>, T, TKey> & {\n schema: T\n }\n): LocalOnlyCollectionOptionsResult<InferSchemaOutput<T>, TKey, T> & {\n schema: T\n}\n\n// Overload for when no schema is provided\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function localOnlyCollectionOptions<\n T extends object,\n TKey extends string | number = string | number,\n>(\n config: LocalOnlyCollectionConfig<T, never, TKey> & {\n schema?: never // prohibit schema\n }\n): LocalOnlyCollectionOptionsResult<T, TKey> & {\n schema?: never // no schema in the result\n}\n\nexport function localOnlyCollectionOptions<\n T extends object = object,\n TSchema extends StandardSchemaV1 = never,\n TKey extends string | number = string | number,\n>(\n config: LocalOnlyCollectionConfig<T, TSchema, TKey>\n): LocalOnlyCollectionOptionsResult<T, TKey, TSchema> & {\n schema?: StandardSchemaV1\n} {\n const { initialData, onInsert, onUpdate, onDelete, ...restConfig } = config\n\n // Create the sync configuration with transaction confirmation capability\n const syncResult = createLocalOnlySync<T, TKey>(initialData)\n\n /**\n * Create wrapper handlers that call user handlers first, then confirm transactions\n * Wraps the user's onInsert handler to also confirm the transaction immediately\n */\n const wrappedOnInsert = async (\n params: InsertMutationFnParams<T, TKey, LocalOnlyCollectionUtils>\n ) => {\n // Call user handler first if provided\n let handlerResult\n if (onInsert) {\n handlerResult = (await onInsert(params)) ?? {}\n }\n\n // Then synchronously confirm the transaction by looping through mutations\n syncResult.confirmOperationsSync(params.transaction.mutations)\n\n return handlerResult\n }\n\n /**\n * Wrapper for onUpdate handler that also confirms the transaction immediately\n */\n const wrappedOnUpdate = async (\n params: UpdateMutationFnParams<T, TKey, LocalOnlyCollectionUtils>\n ) => {\n // Call user handler first if provided\n let handlerResult\n if (onUpdate) {\n handlerResult = (await onUpdate(params)) ?? {}\n }\n\n // Then synchronously confirm the transaction by looping through mutations\n syncResult.confirmOperationsSync(params.transaction.mutations)\n\n return handlerResult\n }\n\n /**\n * Wrapper for onDelete handler that also confirms the transaction immediately\n */\n const wrappedOnDelete = async (\n params: DeleteMutationFnParams<T, TKey, LocalOnlyCollectionUtils>\n ) => {\n // Call user handler first if provided\n let handlerResult\n if (onDelete) {\n handlerResult = (await onDelete(params)) ?? {}\n }\n\n // Then synchronously confirm the transaction by looping through mutations\n syncResult.confirmOperationsSync(params.transaction.mutations)\n\n return handlerResult\n }\n\n /**\n * Accepts mutations from a transaction that belong to this collection and persists them\n */\n const acceptMutations = (transaction: {\n mutations: Array<PendingMutation<Record<string, unknown>>>\n }) => {\n // Filter mutations that belong to this collection\n const collectionMutations = transaction.mutations.filter(\n (m) =>\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n m.collection === syncResult.collection\n )\n\n if (collectionMutations.length === 0) {\n return\n }\n\n // Persist the mutations through sync\n syncResult.confirmOperationsSync(\n collectionMutations as Array<PendingMutation<T>>\n )\n }\n\n return {\n ...restConfig,\n sync: syncResult.sync,\n onInsert: wrappedOnInsert,\n onUpdate: wrappedOnUpdate,\n onDelete: wrappedOnDelete,\n utils: {\n acceptMutations,\n },\n startSync: true,\n gcTime: 0,\n } as LocalOnlyCollectionOptionsResult<T, TKey, TSchema> & {\n schema?: StandardSchemaV1\n }\n}\n\n/**\n * Internal function to create Local-only sync configuration with transaction confirmation\n *\n * This captures the sync functions and provides synchronous confirmation of operations.\n * It creates a loopback sync that immediately confirms all optimistic operations,\n * making them permanent in the collection.\n *\n * @param initialData - Optional array of initial items to populate the collection\n * @returns Object with sync configuration and confirmOperationsSync function\n */\nfunction createLocalOnlySync<T extends object, TKey extends string | number>(\n initialData?: Array<T>\n) {\n // Capture sync functions and collection for transaction confirmation\n let syncBegin: (() => void) | null = null\n let syncWrite: ((message: { type: OperationType; value: T }) => void) | null =\n null\n let syncCommit: (() => void) | null = null\n let collection: Collection<T, TKey, LocalOnlyCollectionUtils> | null = null\n\n const sync: SyncConfig<T, TKey> = {\n /**\n * Sync function that captures sync parameters and applies initial data\n * @param params - Sync parameters containing begin, write, and commit functions\n * @returns Unsubscribe function (empty since no ongoing sync is needed)\n */\n sync: (params) => {\n const { begin, write, commit, markReady } = params\n\n // Capture sync functions and collection for later use\n syncBegin = begin\n syncWrite = write\n syncCommit = commit\n collection = params.collection\n\n // Apply initial data if provided\n if (initialData && initialData.length > 0) {\n begin()\n initialData.forEach((item) => {\n write({\n type: `insert`,\n value: item,\n })\n })\n commit()\n }\n\n // Mark collection as ready since local-only collections are immediately ready\n markReady()\n\n // Return empty unsubscribe function - no ongoing sync needed\n return () => {}\n },\n /**\n * Get sync metadata - returns empty object for local-only collections\n * @returns Empty metadata object\n */\n getSyncMetadata: () => ({}),\n }\n\n /**\n * Synchronously confirms optimistic operations by immediately writing through sync\n *\n * This loops through transaction mutations and applies them to move from optimistic to synced state.\n * It's called after user handlers to make optimistic changes permanent.\n *\n * @param mutations - Array of mutation objects from the transaction\n */\n const confirmOperationsSync = (mutations: Array<PendingMutation<T>>) => {\n if (!syncBegin || !syncWrite || !syncCommit) {\n return // Sync not initialized yet, which is fine\n }\n\n // Immediately write back through sync interface\n syncBegin()\n mutations.forEach((mutation) => {\n if (syncWrite) {\n syncWrite({\n type: mutation.type,\n value: mutation.modified,\n })\n }\n })\n syncCommit()\n }\n\n return {\n sync,\n confirmOperationsSync,\n collection,\n }\n}\n"],"names":[],"mappings":"AA4KO,SAAS,2BAKd,QAGA;AACA,QAAM,EAAE,aAAa,UAAU,UAAU,UAAU,GAAG,eAAe;AAGrE,QAAM,aAAa,oBAA6B,WAAW;AAM3D,QAAM,kBAAkB,OACtB,WACG;AAEH,QAAI;AACJ,QAAI,UAAU;AACZ,sBAAiB,MAAM,SAAS,MAAM,KAAM,CAAA;AAAA,IAC9C;AAGA,eAAW,sBAAsB,OAAO,YAAY,SAAS;AAE7D,WAAO;AAAA,EACT;AAKA,QAAM,kBAAkB,OACtB,WACG;AAEH,QAAI;AACJ,QAAI,UAAU;AACZ,sBAAiB,MAAM,SAAS,MAAM,KAAM,CAAA;AAAA,IAC9C;AAGA,eAAW,sBAAsB,OAAO,YAAY,SAAS;AAE7D,WAAO;AAAA,EACT;AAKA,QAAM,kBAAkB,OACtB,WACG;AAEH,QAAI;AACJ,QAAI,UAAU;AACZ,sBAAiB,MAAM,SAAS,MAAM,KAAM,CAAA;AAAA,IAC9C;AAGA,eAAW,sBAAsB,OAAO,YAAY,SAAS;AAE7D,WAAO;AAAA,EACT;AAKA,QAAM,kBAAkB,CAAC,gBAEnB;AAEJ,UAAM,sBAAsB,YAAY,UAAU;AAAA,MAChD,CAAC;AAAA;AAAA,QAEC,EAAE,eAAe,WAAW;AAAA;AAAA,IAAA;AAGhC,QAAI,oBAAoB,WAAW,GAAG;AACpC;AAAA,IACF;AAGA,eAAW;AAAA,MACT;AAAA,IAAA;AAAA,EAEJ;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,WAAW;AAAA,IACjB,UAAU;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,MACL;AAAA,IAAA;AAAA,IAEF,WAAW;AAAA,IACX,QAAQ;AAAA,EAAA;AAIZ;AAYA,SAAS,oBACP,aACA;AAEA,MAAI,YAAiC;AACrC,MAAI,YACF;AACF,MAAI,aAAkC;AACtC,MAAI,aAAmE;AAEvE,QAAM,OAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMhC,MAAM,CAAC,WAAW;AAChB,YAAM,EAAE,OAAO,OAAO,QAAQ,cAAc;AAG5C,kBAAY;AACZ,kBAAY;AACZ,mBAAa;AACb,mBAAa,OAAO;AAGpB,UAAI,eAAe,YAAY,SAAS,GAAG;AACzC,cAAA;AACA,oBAAY,QAAQ,CAAC,SAAS;AAC5B,gBAAM;AAAA,YACJ,MAAM;AAAA,YACN,OAAO;AAAA,UAAA,CACR;AAAA,QACH,CAAC;AACD,eAAA;AAAA,MACF;AAGA,gBAAA;AAGA,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,iBAAiB,OAAO,CAAA;AAAA,EAAC;AAW3B,QAAM,wBAAwB,CAAC,cAAyC;AACtE,QAAI,CAAC,aAAa,CAAC,aAAa,CAAC,YAAY;AAC3C;AAAA,IACF;AAGA,cAAA;AACA,cAAU,QAAQ,CAAC,aAAa;AAC9B,UAAI,WAAW;AACb,kBAAU;AAAA,UACR,MAAM,SAAS;AAAA,UACf,OAAO,SAAS;AAAA,QAAA,CACjB;AAAA,MACH;AAAA,IACF,CAAC;AACD,eAAA;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"local-only.js","sources":["../../src/local-only.ts"],"sourcesContent":["import type {\n BaseCollectionConfig,\n CollectionConfig,\n DeleteMutationFnParams,\n InferSchemaOutput,\n InsertMutationFnParams,\n OperationType,\n PendingMutation,\n SyncConfig,\n UpdateMutationFnParams,\n UtilsRecord,\n} from \"./types\"\nimport type { Collection } from \"./collection/index\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\n\n/**\n * Configuration interface for Local-only collection options\n * @template T - The type of items in the collection\n * @template TSchema - The schema type for validation\n * @template TKey - The type of the key returned by `getKey`\n */\nexport interface LocalOnlyCollectionConfig<\n T extends object = object,\n TSchema extends StandardSchemaV1 = never,\n TKey extends string | number = string | number,\n> extends Omit<\n BaseCollectionConfig<T, TKey, TSchema, LocalOnlyCollectionUtils>,\n `gcTime` | `startSync`\n> {\n /**\n * Optional initial data to populate the collection with on creation\n * This data will be applied during the initial sync process\n */\n initialData?: Array<T>\n}\n\n/**\n * Local-only collection utilities type\n */\nexport interface LocalOnlyCollectionUtils extends UtilsRecord {\n /**\n * Accepts mutations from a transaction that belong to this collection and persists them.\n * This should be called in your transaction's mutationFn to persist local-only data.\n *\n * @param transaction - The transaction containing mutations to accept\n * @example\n * const localData = createCollection(localOnlyCollectionOptions({...}))\n *\n * const tx = createTransaction({\n * mutationFn: async ({ transaction }) => {\n * // Make API call first\n * await api.save(...)\n * // Then persist local-only mutations after success\n * localData.utils.acceptMutations(transaction)\n * }\n * })\n */\n acceptMutations: (transaction: {\n mutations: Array<PendingMutation<Record<string, unknown>>>\n }) => void\n}\n\ntype LocalOnlyCollectionOptionsResult<\n T extends object,\n TKey extends string | number,\n TSchema extends StandardSchemaV1 | never = never,\n> = CollectionConfig<T, TKey, TSchema> & {\n utils: LocalOnlyCollectionUtils\n}\n\n/**\n * Creates Local-only collection options for use with a standard Collection\n *\n * This is an in-memory collection that doesn't sync with external sources but uses a loopback sync config\n * that immediately \"syncs\" all optimistic changes to the collection, making them permanent.\n * Perfect for local-only data that doesn't need persistence or external synchronization.\n *\n * **Using with Manual Transactions:**\n *\n * For manual transactions, you must call `utils.acceptMutations()` in your transaction's `mutationFn`\n * to persist changes made during `tx.mutate()`. This is necessary because local-only collections\n * don't participate in the standard mutation handler flow for manual transactions.\n *\n * @template T - The schema type if a schema is provided, otherwise the type of items in the collection\n * @template TKey - The type of the key returned by getKey\n * @param config - Configuration options for the Local-only collection\n * @returns Collection options with utilities including acceptMutations\n *\n * @example\n * // Basic local-only collection\n * const collection = createCollection(\n * localOnlyCollectionOptions({\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // Local-only collection with initial data\n * const collection = createCollection(\n * localOnlyCollectionOptions({\n * getKey: (item) => item.id,\n * initialData: [\n * { id: 1, name: 'Item 1' },\n * { id: 2, name: 'Item 2' },\n * ],\n * })\n * )\n *\n * @example\n * // Local-only collection with mutation handlers\n * const collection = createCollection(\n * localOnlyCollectionOptions({\n * getKey: (item) => item.id,\n * onInsert: async ({ transaction }) => {\n * console.log('Item inserted:', transaction.mutations[0].modified)\n * // Custom logic after insert\n * },\n * })\n * )\n *\n * @example\n * // Using with manual transactions\n * const localData = createCollection(\n * localOnlyCollectionOptions({\n * getKey: (item) => item.id,\n * })\n * )\n *\n * const tx = createTransaction({\n * mutationFn: async ({ transaction }) => {\n * // Use local data in API call\n * const localMutations = transaction.mutations.filter(m => m.collection === localData)\n * await api.save({ metadata: localMutations[0]?.modified })\n *\n * // Persist local-only mutations after API success\n * localData.utils.acceptMutations(transaction)\n * }\n * })\n *\n * tx.mutate(() => {\n * localData.insert({ id: 1, data: 'metadata' })\n * apiCollection.insert({ id: 2, data: 'main data' })\n * })\n *\n * await tx.commit()\n */\n\n// Overload for when schema is provided\nexport function localOnlyCollectionOptions<\n T extends StandardSchemaV1,\n TKey extends string | number = string | number,\n>(\n config: LocalOnlyCollectionConfig<InferSchemaOutput<T>, T, TKey> & {\n schema: T\n }\n): LocalOnlyCollectionOptionsResult<InferSchemaOutput<T>, TKey, T> & {\n schema: T\n}\n\n// Overload for when no schema is provided\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function localOnlyCollectionOptions<\n T extends object,\n TKey extends string | number = string | number,\n>(\n config: LocalOnlyCollectionConfig<T, never, TKey> & {\n schema?: never // prohibit schema\n }\n): LocalOnlyCollectionOptionsResult<T, TKey> & {\n schema?: never // no schema in the result\n}\n\nexport function localOnlyCollectionOptions<\n T extends object = object,\n TSchema extends StandardSchemaV1 = never,\n TKey extends string | number = string | number,\n>(\n config: LocalOnlyCollectionConfig<T, TSchema, TKey>\n): LocalOnlyCollectionOptionsResult<T, TKey, TSchema> & {\n schema?: StandardSchemaV1\n} {\n const { initialData, onInsert, onUpdate, onDelete, ...restConfig } = config\n\n // Create the sync configuration with transaction confirmation capability\n const syncResult = createLocalOnlySync<T, TKey>(initialData)\n\n /**\n * Create wrapper handlers that call user handlers first, then confirm transactions\n * Wraps the user's onInsert handler to also confirm the transaction immediately\n */\n const wrappedOnInsert = async (\n params: InsertMutationFnParams<T, TKey, LocalOnlyCollectionUtils>\n ) => {\n // Call user handler first if provided\n let handlerResult\n if (onInsert) {\n handlerResult = (await onInsert(params)) ?? {}\n }\n\n // Then synchronously confirm the transaction by looping through mutations\n syncResult.confirmOperationsSync(params.transaction.mutations)\n\n return handlerResult\n }\n\n /**\n * Wrapper for onUpdate handler that also confirms the transaction immediately\n */\n const wrappedOnUpdate = async (\n params: UpdateMutationFnParams<T, TKey, LocalOnlyCollectionUtils>\n ) => {\n // Call user handler first if provided\n let handlerResult\n if (onUpdate) {\n handlerResult = (await onUpdate(params)) ?? {}\n }\n\n // Then synchronously confirm the transaction by looping through mutations\n syncResult.confirmOperationsSync(params.transaction.mutations)\n\n return handlerResult\n }\n\n /**\n * Wrapper for onDelete handler that also confirms the transaction immediately\n */\n const wrappedOnDelete = async (\n params: DeleteMutationFnParams<T, TKey, LocalOnlyCollectionUtils>\n ) => {\n // Call user handler first if provided\n let handlerResult\n if (onDelete) {\n handlerResult = (await onDelete(params)) ?? {}\n }\n\n // Then synchronously confirm the transaction by looping through mutations\n syncResult.confirmOperationsSync(params.transaction.mutations)\n\n return handlerResult\n }\n\n /**\n * Accepts mutations from a transaction that belong to this collection and persists them\n */\n const acceptMutations = (transaction: {\n mutations: Array<PendingMutation<Record<string, unknown>>>\n }) => {\n // Filter mutations that belong to this collection\n const collectionMutations = transaction.mutations.filter(\n (m) =>\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n m.collection === syncResult.collection\n )\n\n if (collectionMutations.length === 0) {\n return\n }\n\n // Persist the mutations through sync\n syncResult.confirmOperationsSync(\n collectionMutations as Array<PendingMutation<T>>\n )\n }\n\n return {\n ...restConfig,\n sync: syncResult.sync,\n onInsert: wrappedOnInsert,\n onUpdate: wrappedOnUpdate,\n onDelete: wrappedOnDelete,\n utils: {\n acceptMutations,\n },\n startSync: true,\n gcTime: 0,\n } as LocalOnlyCollectionOptionsResult<T, TKey, TSchema> & {\n schema?: StandardSchemaV1\n }\n}\n\n/**\n * Internal function to create Local-only sync configuration with transaction confirmation\n *\n * This captures the sync functions and provides synchronous confirmation of operations.\n * It creates a loopback sync that immediately confirms all optimistic operations,\n * making them permanent in the collection.\n *\n * @param initialData - Optional array of initial items to populate the collection\n * @returns Object with sync configuration and confirmOperationsSync function\n */\nfunction createLocalOnlySync<T extends object, TKey extends string | number>(\n initialData?: Array<T>\n) {\n // Capture sync functions and collection for transaction confirmation\n let syncBegin: (() => void) | null = null\n let syncWrite: ((message: { type: OperationType; value: T }) => void) | null =\n null\n let syncCommit: (() => void) | null = null\n let collection: Collection<T, TKey, LocalOnlyCollectionUtils> | null = null\n\n const sync: SyncConfig<T, TKey> = {\n /**\n * Sync function that captures sync parameters and applies initial data\n * @param params - Sync parameters containing begin, write, and commit functions\n * @returns Unsubscribe function (empty since no ongoing sync is needed)\n */\n sync: (params) => {\n const { begin, write, commit, markReady } = params\n\n // Capture sync functions and collection for later use\n syncBegin = begin\n syncWrite = write\n syncCommit = commit\n collection = params.collection\n\n // Apply initial data if provided\n if (initialData && initialData.length > 0) {\n begin()\n initialData.forEach((item) => {\n write({\n type: `insert`,\n value: item,\n })\n })\n commit()\n }\n\n // Mark collection as ready since local-only collections are immediately ready\n markReady()\n\n // Return empty unsubscribe function - no ongoing sync needed\n return () => {}\n },\n /**\n * Get sync metadata - returns empty object for local-only collections\n * @returns Empty metadata object\n */\n getSyncMetadata: () => ({}),\n }\n\n /**\n * Synchronously confirms optimistic operations by immediately writing through sync\n *\n * This loops through transaction mutations and applies them to move from optimistic to synced state.\n * It's called after user handlers to make optimistic changes permanent.\n *\n * @param mutations - Array of mutation objects from the transaction\n */\n const confirmOperationsSync = (mutations: Array<PendingMutation<T>>) => {\n if (!syncBegin || !syncWrite || !syncCommit) {\n return // Sync not initialized yet, which is fine\n }\n\n // Immediately write back through sync interface\n syncBegin()\n mutations.forEach((mutation) => {\n if (syncWrite) {\n syncWrite({\n type: mutation.type,\n value: mutation.modified,\n })\n }\n })\n syncCommit()\n }\n\n return {\n sync,\n confirmOperationsSync,\n collection,\n }\n}\n"],"names":[],"mappings":"AA4KO,SAAS,2BAKd,QAGA;AACA,QAAM,EAAE,aAAa,UAAU,UAAU,UAAU,GAAG,eAAe;AAGrE,QAAM,aAAa,oBAA6B,WAAW;AAM3D,QAAM,kBAAkB,OACtB,WACG;AAEH,QAAI;AACJ,QAAI,UAAU;AACZ,sBAAiB,MAAM,SAAS,MAAM,KAAM,CAAA;AAAA,IAC9C;AAGA,eAAW,sBAAsB,OAAO,YAAY,SAAS;AAE7D,WAAO;AAAA,EACT;AAKA,QAAM,kBAAkB,OACtB,WACG;AAEH,QAAI;AACJ,QAAI,UAAU;AACZ,sBAAiB,MAAM,SAAS,MAAM,KAAM,CAAA;AAAA,IAC9C;AAGA,eAAW,sBAAsB,OAAO,YAAY,SAAS;AAE7D,WAAO;AAAA,EACT;AAKA,QAAM,kBAAkB,OACtB,WACG;AAEH,QAAI;AACJ,QAAI,UAAU;AACZ,sBAAiB,MAAM,SAAS,MAAM,KAAM,CAAA;AAAA,IAC9C;AAGA,eAAW,sBAAsB,OAAO,YAAY,SAAS;AAE7D,WAAO;AAAA,EACT;AAKA,QAAM,kBAAkB,CAAC,gBAEnB;AAEJ,UAAM,sBAAsB,YAAY,UAAU;AAAA,MAChD,CAAC;AAAA;AAAA,QAEC,EAAE,eAAe,WAAW;AAAA;AAAA,IAAA;AAGhC,QAAI,oBAAoB,WAAW,GAAG;AACpC;AAAA,IACF;AAGA,eAAW;AAAA,MACT;AAAA,IAAA;AAAA,EAEJ;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,WAAW;AAAA,IACjB,UAAU;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,MACL;AAAA,IAAA;AAAA,IAEF,WAAW;AAAA,IACX,QAAQ;AAAA,EAAA;AAIZ;AAYA,SAAS,oBACP,aACA;AAEA,MAAI,YAAiC;AACrC,MAAI,YACF;AACF,MAAI,aAAkC;AACtC,MAAI,aAAmE;AAEvE,QAAM,OAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMhC,MAAM,CAAC,WAAW;AAChB,YAAM,EAAE,OAAO,OAAO,QAAQ,cAAc;AAG5C,kBAAY;AACZ,kBAAY;AACZ,mBAAa;AACb,mBAAa,OAAO;AAGpB,UAAI,eAAe,YAAY,SAAS,GAAG;AACzC,cAAA;AACA,oBAAY,QAAQ,CAAC,SAAS;AAC5B,gBAAM;AAAA,YACJ,MAAM;AAAA,YACN,OAAO;AAAA,UAAA,CACR;AAAA,QACH,CAAC;AACD,eAAA;AAAA,MACF;AAGA,gBAAA;AAGA,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,iBAAiB,OAAO,CAAA;AAAA,EAAC;AAW3B,QAAM,wBAAwB,CAAC,cAAyC;AACtE,QAAI,CAAC,aAAa,CAAC,aAAa,CAAC,YAAY;AAC3C;AAAA,IACF;AAGA,cAAA;AACA,cAAU,QAAQ,CAAC,aAAa;AAC9B,UAAI,WAAW;AACb,kBAAU;AAAA,UACR,MAAM,SAAS;AAAA,UACf,OAAO,SAAS;AAAA,QAAA,CACjB;AAAA,MACH;AAAA,IACF,CAAC;AACD,eAAA;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
@@ -418,8 +418,23 @@ function isLimitSubset(subset, superset) {
418
418
  return subset <= superset;
419
419
  }
420
420
  function isPredicateSubset(subset, superset) {
421
+ if (superset.limit !== void 0) {
422
+ if (!areWhereClausesEqual(subset.where, superset.where)) {
423
+ return false;
424
+ }
425
+ return isOrderBySubset(subset.orderBy, superset.orderBy) && isLimitSubset(subset.limit, superset.limit);
426
+ }
421
427
  return isWhereSubset(subset.where, superset.where) && isOrderBySubset(subset.orderBy, superset.orderBy) && isLimitSubset(subset.limit, superset.limit);
422
428
  }
429
+ function areWhereClausesEqual(a, b) {
430
+ if (a === void 0 && b === void 0) {
431
+ return true;
432
+ }
433
+ if (a === void 0 || b === void 0) {
434
+ return false;
435
+ }
436
+ return areExpressionsEqual(a, b);
437
+ }
423
438
  function findCommonConditions(predicate1, predicate2) {
424
439
  const conditions1 = extractAllConditions(predicate1);
425
440
  const conditions2 = extractAllConditions(predicate2);
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-utils.js","sources":["../../../src/query/predicate-utils.ts"],"sourcesContent":["import { Func, Value } from \"./ir.js\"\nimport type { BasicExpression, OrderBy, PropRef } from \"./ir.js\"\nimport type { LoadSubsetOptions } from \"../types.js\"\n\n/**\n * Check if one where clause is a logical subset of another.\n * Returns true if the subset predicate is more restrictive than (or equal to) the superset predicate.\n *\n * @example\n * // age > 20 is subset of age > 10 (more restrictive)\n * isWhereSubset(gt(ref('age'), val(20)), gt(ref('age'), val(10))) // true\n *\n * @example\n * // age > 10 AND name = 'X' is subset of age > 10 (more conditions)\n * isWhereSubset(and(gt(ref('age'), val(10)), eq(ref('name'), val('X'))), gt(ref('age'), val(10))) // true\n *\n * @param subset - The potentially more restrictive predicate\n * @param superset - The potentially less restrictive predicate\n * @returns true if subset logically implies superset\n */\nexport function isWhereSubset(\n subset: BasicExpression<boolean> | undefined,\n superset: BasicExpression<boolean> | undefined\n): boolean {\n // undefined/missing where clause means \"no filter\" (all data)\n // Both undefined means subset relationship holds (all data ⊆ all data)\n if (subset === undefined && superset === undefined) {\n return true\n }\n\n // If subset is undefined but superset is not, we're requesting ALL data\n // but have only loaded SOME data - subset relationship does NOT hold\n if (subset === undefined && superset !== undefined) {\n return false\n }\n\n // If superset is undefined (no filter = all data loaded),\n // then any constrained subset is contained\n if (superset === undefined && subset !== undefined) {\n return true\n }\n\n return isWhereSubsetInternal(subset!, superset!)\n}\n\nfunction makeDisjunction(\n preds: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> {\n if (preds.length === 0) {\n return new Value(false)\n }\n if (preds.length === 1) {\n return preds[0]!\n }\n return new Func(`or`, preds)\n}\n\nfunction convertInToOr(inField: InField) {\n const equalities = inField.values.map(\n (value) => new Func(`eq`, [inField.ref, new Value(value)])\n )\n return makeDisjunction(equalities)\n}\n\nfunction isWhereSubsetInternal(\n subset: BasicExpression<boolean>,\n superset: BasicExpression<boolean>\n): boolean {\n // If subset is false it is requesting no data,\n // thus the result set is empty\n // and the empty set is a subset of any set\n if (subset.type === `val` && subset.value === false) {\n return true\n }\n\n // If expressions are structurally equal, subset relationship holds\n if (areExpressionsEqual(subset, superset)) {\n return true\n }\n\n // Handle superset being an AND: subset must imply ALL conjuncts\n // If superset is (A AND B), then subset ⊆ (A AND B) only if subset ⊆ A AND subset ⊆ B\n // Example: (age > 20) ⊆ (age > 10 AND status = 'active') is false (doesn't imply status condition)\n if (superset.type === `func` && superset.name === `and`) {\n return superset.args.every((arg) =>\n isWhereSubsetInternal(subset, arg as BasicExpression<boolean>)\n )\n }\n\n // Handle subset being an AND: (A AND B) implies both A and B\n if (subset.type === `func` && subset.name === `and`) {\n // For (A AND B) ⊆ C, since (A AND B) implies A, we check if any conjunct implies C\n return subset.args.some((arg) =>\n isWhereSubsetInternal(arg as BasicExpression<boolean>, superset)\n )\n }\n\n // Turn x IN [A, B, C] into x = A OR x = B OR x = C\n // for unified handling of IN and OR\n if (subset.type === `func` && subset.name === `in`) {\n const inField = extractInField(subset)\n if (inField) {\n return isWhereSubsetInternal(convertInToOr(inField), superset)\n }\n }\n\n if (superset.type === `func` && superset.name === `in`) {\n const inField = extractInField(superset)\n if (inField) {\n return isWhereSubsetInternal(subset, convertInToOr(inField))\n }\n }\n\n // Handle OR in subset: (A OR B) is subset of C only if both A and B are subsets of C\n if (subset.type === `func` && subset.name === `or`) {\n return subset.args.every((arg) =>\n isWhereSubsetInternal(arg as BasicExpression<boolean>, superset)\n )\n }\n\n // Handle OR in superset: subset ⊆ (A OR B) if subset ⊆ A or subset ⊆ B\n // (A OR B) as superset means data can satisfy A or B\n // If subset is contained in any disjunct, it's contained in the union\n if (superset.type === `func` && superset.name === `or`) {\n return superset.args.some((arg) =>\n isWhereSubsetInternal(subset, arg as BasicExpression<boolean>)\n )\n }\n\n // Handle comparison operators on the same field\n if (subset.type === `func` && superset.type === `func`) {\n const subsetFunc = subset as Func\n const supersetFunc = superset as Func\n\n // Check if both are comparisons on the same field\n const subsetField = extractComparisonField(subsetFunc)\n const supersetField = extractComparisonField(supersetFunc)\n\n if (\n subsetField &&\n supersetField &&\n areRefsEqual(subsetField.ref, supersetField.ref)\n ) {\n return isComparisonSubset(\n subsetFunc,\n subsetField.value,\n supersetFunc,\n supersetField.value\n )\n }\n\n /*\n // Handle eq vs in\n if (subsetFunc.name === `eq` && supersetFunc.name === `in`) {\n const subsetFieldEq = extractEqualityField(subsetFunc)\n const supersetFieldIn = extractInField(supersetFunc)\n if (\n subsetFieldEq &&\n supersetFieldIn &&\n areRefsEqual(subsetFieldEq.ref, supersetFieldIn.ref)\n ) {\n // field = X is subset of field IN [X, Y, Z] if X is in the array\n // Use cached primitive set and metadata from extraction\n return arrayIncludesWithSet(\n supersetFieldIn.values,\n subsetFieldEq.value,\n supersetFieldIn.primitiveSet ?? null,\n supersetFieldIn.areAllPrimitives\n )\n }\n }\n\n // Handle in vs in\n if (subsetFunc.name === `in` && supersetFunc.name === `in`) {\n const subsetFieldIn = extractInField(subsetFunc)\n const supersetFieldIn = extractInField(supersetFunc)\n if (\n subsetFieldIn &&\n supersetFieldIn &&\n areRefsEqual(subsetFieldIn.ref, supersetFieldIn.ref)\n ) {\n // field IN [A, B] is subset of field IN [A, B, C] if all values in subset are in superset\n // Use cached primitive set and metadata from extraction\n return subsetFieldIn.values.every((subVal) =>\n arrayIncludesWithSet(\n supersetFieldIn.values,\n subVal,\n supersetFieldIn.primitiveSet ?? null,\n supersetFieldIn.areAllPrimitives\n )\n )\n }\n }\n */\n }\n\n // Conservative: if we can't determine, return false\n return false\n}\n\n/**\n * Helper to combine where predicates with common logic for AND/OR operations\n */\nfunction combineWherePredicates(\n predicates: Array<BasicExpression<boolean>>,\n operation: `and` | `or`,\n simplifyFn: (\n preds: Array<BasicExpression<boolean>>\n ) => BasicExpression<boolean> | null\n): BasicExpression<boolean> {\n const emptyValue = operation === `and` ? true : false\n const identityValue = operation === `and` ? true : false\n\n if (predicates.length === 0) {\n return { type: `val`, value: emptyValue } as BasicExpression<boolean>\n }\n\n if (predicates.length === 1) {\n return predicates[0]!\n }\n\n // Flatten nested expressions of the same operation\n const flatPredicates: Array<BasicExpression<boolean>> = []\n for (const pred of predicates) {\n if (pred.type === `func` && pred.name === operation) {\n flatPredicates.push(...pred.args)\n } else {\n flatPredicates.push(pred)\n }\n }\n\n // Group predicates by field for simplification\n const grouped = groupPredicatesByField(flatPredicates)\n\n // Simplify each group\n const simplified: Array<BasicExpression<boolean>> = []\n for (const [field, preds] of grouped.entries()) {\n if (field === null) {\n // Complex predicates that we can't group by field\n simplified.push(...preds)\n } else {\n // Try to simplify same-field predicates\n const result = simplifyFn(preds)\n\n // For intersection: check for empty set (contradiction)\n if (\n operation === `and` &&\n result &&\n result.type === `val` &&\n result.value === false\n ) {\n // Intersection is empty (conflicting constraints) - entire AND is false\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n // For union: result may be null if simplification failed\n if (result) {\n simplified.push(result)\n }\n }\n }\n\n if (simplified.length === 0) {\n return { type: `val`, value: identityValue } as BasicExpression<boolean>\n }\n\n if (simplified.length === 1) {\n return simplified[0]!\n }\n\n // Return combined predicate\n return {\n type: `func`,\n name: operation,\n args: simplified,\n } as BasicExpression<boolean>\n}\n\n/**\n * Combine multiple where predicates with OR logic (union).\n * Returns a predicate that is satisfied when any input predicate is satisfied.\n * Simplifies when possible (e.g., age > 10 OR age > 20 → age > 10).\n *\n * @example\n * // Take least restrictive\n * unionWherePredicates([gt(ref('age'), val(10)), gt(ref('age'), val(20))]) // age > 10\n *\n * @example\n * // Combine equals into IN\n * unionWherePredicates([eq(ref('age'), val(5)), eq(ref('age'), val(10))]) // age IN [5, 10]\n *\n * @param predicates - Array of where predicates to union\n * @returns Combined predicate representing the union\n */\nexport function unionWherePredicates(\n predicates: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> {\n return combineWherePredicates(predicates, `or`, unionSameFieldPredicates)\n}\n\n/**\n * Compute the difference between two where predicates: `fromPredicate AND NOT(subtractPredicate)`.\n * Returns the simplified predicate, or null if the difference cannot be simplified\n * (in which case the caller should fetch the full fromPredicate).\n *\n * @example\n * // Range difference\n * minusWherePredicates(\n * gt(ref('age'), val(10)), // age > 10\n * gt(ref('age'), val(20)) // age > 20\n * ) // → age > 10 AND age <= 20\n *\n * @example\n * // Set difference\n * minusWherePredicates(\n * inOp(ref('status'), ['A', 'B', 'C', 'D']), // status IN ['A','B','C','D']\n * inOp(ref('status'), ['B', 'C']) // status IN ['B','C']\n * ) // → status IN ['A', 'D']\n *\n * @example\n * // Common conditions\n * minusWherePredicates(\n * and(gt(ref('age'), val(10)), eq(ref('status'), val('active'))), // age > 10 AND status = 'active'\n * and(gt(ref('age'), val(20)), eq(ref('status'), val('active'))) // age > 20 AND status = 'active'\n * ) // → age > 10 AND age <= 20 AND status = 'active'\n *\n * @example\n * // Complete overlap - empty result\n * minusWherePredicates(\n * gt(ref('age'), val(20)), // age > 20\n * gt(ref('age'), val(10)) // age > 10\n * ) // → {type: 'val', value: false} (empty set)\n *\n * @param fromPredicate - The predicate to subtract from\n * @param subtractPredicate - The predicate to subtract\n * @returns The simplified difference, or null if cannot be simplified\n */\nexport function minusWherePredicates(\n fromPredicate: BasicExpression<boolean> | undefined,\n subtractPredicate: BasicExpression<boolean> | undefined\n): BasicExpression<boolean> | null {\n // If nothing to subtract, return the original\n if (subtractPredicate === undefined) {\n return (\n fromPredicate ??\n ({ type: `val`, value: true } as BasicExpression<boolean>)\n )\n }\n\n // If from is undefined then we are asking for all data\n // so we need to load all data minus what we already loaded\n // i.e. we need to load NOT(subtractPredicate)\n if (fromPredicate === undefined) {\n return {\n type: `func`,\n name: `not`,\n args: [subtractPredicate],\n } as BasicExpression<boolean>\n }\n\n // Check if fromPredicate is entirely contained in subtractPredicate\n // In that case, fromPredicate AND NOT(subtractPredicate) = empty set\n if (isWhereSubset(fromPredicate, subtractPredicate)) {\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n // Try to detect and handle common conditions\n const commonConditions = findCommonConditions(\n fromPredicate,\n subtractPredicate\n )\n if (commonConditions.length > 0) {\n // Extract predicates without common conditions\n const fromWithoutCommon = removeConditions(fromPredicate, commonConditions)\n const subtractWithoutCommon = removeConditions(\n subtractPredicate,\n commonConditions\n )\n\n // Recursively compute difference on simplified predicates\n const simplifiedDifference = minusWherePredicates(\n fromWithoutCommon,\n subtractWithoutCommon\n )\n\n if (simplifiedDifference !== null) {\n // Combine the simplified difference with common conditions\n return combineConditions([...commonConditions, simplifiedDifference])\n }\n }\n\n // Check if they are on the same field - if so, we can try to simplify\n if (fromPredicate.type === `func` && subtractPredicate.type === `func`) {\n const result = minusSameFieldPredicates(fromPredicate, subtractPredicate)\n if (result !== null) {\n return result\n }\n }\n\n // Can't simplify - return null to indicate caller should fetch full fromPredicate\n return null\n}\n\n/**\n * Helper function to compute difference for same-field predicates\n */\nfunction minusSameFieldPredicates(\n fromPred: Func,\n subtractPred: Func\n): BasicExpression<boolean> | null {\n // Extract field information\n const fromField =\n extractComparisonField(fromPred) ||\n extractEqualityField(fromPred) ||\n extractInField(fromPred)\n const subtractField =\n extractComparisonField(subtractPred) ||\n extractEqualityField(subtractPred) ||\n extractInField(subtractPred)\n\n // Must be on the same field\n if (\n !fromField ||\n !subtractField ||\n !areRefsEqual(fromField.ref, subtractField.ref)\n ) {\n return null\n }\n\n // Handle IN minus IN: status IN [A,B,C,D] - status IN [B,C] = status IN [A,D]\n if (fromPred.name === `in` && subtractPred.name === `in`) {\n const fromInField = fromField as InField\n const subtractInField = subtractField as InField\n\n // Filter out values that are in the subtract set\n const remainingValues = fromInField.values.filter(\n (v) =>\n !arrayIncludesWithSet(\n subtractInField.values,\n v,\n subtractInField.primitiveSet ?? null,\n subtractInField.areAllPrimitives\n )\n )\n\n if (remainingValues.length === 0) {\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n if (remainingValues.length === 1) {\n return {\n type: `func`,\n name: `eq`,\n args: [fromField.ref, { type: `val`, value: remainingValues[0] }],\n } as BasicExpression<boolean>\n }\n\n return {\n type: `func`,\n name: `in`,\n args: [fromField.ref, { type: `val`, value: remainingValues }],\n } as BasicExpression<boolean>\n }\n\n // Handle IN minus equality: status IN [A,B,C] - status = B = status IN [A,C]\n if (fromPred.name === `in` && subtractPred.name === `eq`) {\n const fromInField = fromField as InField\n const subtractValue = (subtractField as { ref: PropRef; value: any }).value\n\n const remainingValues = fromInField.values.filter(\n (v) => !areValuesEqual(v, subtractValue)\n )\n\n if (remainingValues.length === 0) {\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n if (remainingValues.length === 1) {\n return {\n type: `func`,\n name: `eq`,\n args: [fromField.ref, { type: `val`, value: remainingValues[0] }],\n } as BasicExpression<boolean>\n }\n\n return {\n type: `func`,\n name: `in`,\n args: [fromField.ref, { type: `val`, value: remainingValues }],\n } as BasicExpression<boolean>\n }\n\n // Handle equality minus equality: age = 15 - age = 15 = empty, age = 15 - age = 20 = age = 15\n if (fromPred.name === `eq` && subtractPred.name === `eq`) {\n const fromValue = (fromField as { ref: PropRef; value: any }).value\n const subtractValue = (subtractField as { ref: PropRef; value: any }).value\n\n if (areValuesEqual(fromValue, subtractValue)) {\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n // No overlap - return original\n return fromPred as BasicExpression<boolean>\n }\n\n // Handle range minus range: age > 10 - age > 20 = age > 10 AND age <= 20\n const fromComp = extractComparisonField(fromPred)\n const subtractComp = extractComparisonField(subtractPred)\n\n if (\n fromComp &&\n subtractComp &&\n areRefsEqual(fromComp.ref, subtractComp.ref)\n ) {\n // Try to compute the difference using range logic\n const result = minusRangePredicates(\n fromPred,\n fromComp.value,\n subtractPred,\n subtractComp.value\n )\n return result\n }\n\n // Can't simplify\n return null\n}\n\n/**\n * Helper to compute difference between range predicates\n */\nfunction minusRangePredicates(\n fromFunc: Func,\n fromValue: any,\n subtractFunc: Func,\n subtractValue: any\n): BasicExpression<boolean> | null {\n const fromOp = fromFunc.name as `gt` | `gte` | `lt` | `lte` | `eq`\n const subtractOp = subtractFunc.name as `gt` | `gte` | `lt` | `lte` | `eq`\n const ref = (extractComparisonField(fromFunc) ||\n extractEqualityField(fromFunc))!.ref\n\n // age > 10 - age > 20 = (age > 10 AND age <= 20)\n if (fromOp === `gt` && subtractOp === `gt`) {\n if (fromValue < subtractValue) {\n // Result is: fromValue < field <= subtractValue\n return {\n type: `func`,\n name: `and`,\n args: [\n fromFunc as BasicExpression<boolean>,\n {\n type: `func`,\n name: `lte`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n // fromValue >= subtractValue means no overlap\n return fromFunc as BasicExpression<boolean>\n }\n\n // age >= 10 - age >= 20 = (age >= 10 AND age < 20)\n if (fromOp === `gte` && subtractOp === `gte`) {\n if (fromValue < subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n fromFunc as BasicExpression<boolean>,\n {\n type: `func`,\n name: `lt`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age > 10 - age >= 20 = (age > 10 AND age < 20)\n if (fromOp === `gt` && subtractOp === `gte`) {\n if (fromValue < subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n fromFunc as BasicExpression<boolean>,\n {\n type: `func`,\n name: `lt`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age >= 10 - age > 20 = (age >= 10 AND age <= 20)\n if (fromOp === `gte` && subtractOp === `gt`) {\n if (fromValue <= subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n fromFunc as BasicExpression<boolean>,\n {\n type: `func`,\n name: `lte`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age < 30 - age < 20 = (age >= 20 AND age < 30)\n if (fromOp === `lt` && subtractOp === `lt`) {\n if (fromValue > subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n {\n type: `func`,\n name: `gte`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n fromFunc as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age <= 30 - age <= 20 = (age > 20 AND age <= 30)\n if (fromOp === `lte` && subtractOp === `lte`) {\n if (fromValue > subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n {\n type: `func`,\n name: `gt`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n fromFunc as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age < 30 - age <= 20 = (age > 20 AND age < 30)\n if (fromOp === `lt` && subtractOp === `lte`) {\n if (fromValue > subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n {\n type: `func`,\n name: `gt`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n fromFunc as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age <= 30 - age < 20 = (age >= 20 AND age <= 30)\n if (fromOp === `lte` && subtractOp === `lt`) {\n if (fromValue >= subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n {\n type: `func`,\n name: `gte`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n fromFunc as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // Can't simplify other combinations\n return null\n}\n\n/**\n * Check if one orderBy clause is a subset of another.\n * Returns true if the subset ordering requirements are satisfied by the superset ordering.\n *\n * @example\n * // Subset is prefix of superset\n * isOrderBySubset([{expr: age, asc}], [{expr: age, asc}, {expr: name, desc}]) // true\n *\n * @param subset - The ordering requirements to check\n * @param superset - The ordering that might satisfy the requirements\n * @returns true if subset is satisfied by superset\n */\nexport function isOrderBySubset(\n subset: OrderBy | undefined,\n superset: OrderBy | undefined\n): boolean {\n // No ordering requirement is always satisfied\n if (!subset || subset.length === 0) {\n return true\n }\n\n // If there's no superset ordering but subset requires ordering, not satisfied\n if (!superset || superset.length === 0) {\n return false\n }\n\n // Check if subset is a prefix of superset with matching expressions and compare options\n if (subset.length > superset.length) {\n return false\n }\n\n for (let i = 0; i < subset.length; i++) {\n const subClause = subset[i]!\n const superClause = superset[i]!\n\n // Check if expressions match\n if (!areExpressionsEqual(subClause.expression, superClause.expression)) {\n return false\n }\n\n // Check if compare options match\n if (\n !areCompareOptionsEqual(\n subClause.compareOptions,\n superClause.compareOptions\n )\n ) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Check if one limit is a subset of another.\n * Returns true if the subset limit requirements are satisfied by the superset limit.\n *\n * @example\n * isLimitSubset(10, 20) // true (requesting 10 items when 20 are available)\n * isLimitSubset(20, 10) // false (requesting 20 items when only 10 are available)\n * isLimitSubset(10, undefined) // true (requesting 10 items when unlimited are available)\n *\n * @param subset - The limit requirement to check\n * @param superset - The limit that might satisfy the requirement\n * @returns true if subset is satisfied by superset\n */\nexport function isLimitSubset(\n subset: number | undefined,\n superset: number | undefined\n): boolean {\n // Unlimited superset satisfies any limit requirement\n if (superset === undefined) {\n return true\n }\n\n // If requesting all data (no limit), we need unlimited data to satisfy it\n // But we know superset is not unlimited so we return false\n if (subset === undefined) {\n return false\n }\n\n // Otherwise, subset must be less than or equal to superset\n return subset <= superset\n}\n\n/**\n * Check if one predicate (where + orderBy + limit) is a subset of another.\n * Returns true if all aspects of the subset predicate are satisfied by the superset.\n *\n * @example\n * isPredicateSubset(\n * { where: gt(ref('age'), val(20)), limit: 10 },\n * { where: gt(ref('age'), val(10)), limit: 20 }\n * ) // true\n *\n * @param subset - The predicate requirements to check\n * @param superset - The predicate that might satisfy the requirements\n * @returns true if subset is satisfied by superset\n */\nexport function isPredicateSubset(\n subset: LoadSubsetOptions,\n superset: LoadSubsetOptions\n): boolean {\n return (\n isWhereSubset(subset.where, superset.where) &&\n isOrderBySubset(subset.orderBy, superset.orderBy) &&\n isLimitSubset(subset.limit, superset.limit)\n )\n}\n\n// ============================================================================\n// Helper functions\n// ============================================================================\n\n/**\n * Find common conditions between two predicates.\n * Returns an array of conditions that appear in both predicates.\n */\nfunction findCommonConditions(\n predicate1: BasicExpression<boolean>,\n predicate2: BasicExpression<boolean>\n): Array<BasicExpression<boolean>> {\n const conditions1 = extractAllConditions(predicate1)\n const conditions2 = extractAllConditions(predicate2)\n\n const common: Array<BasicExpression<boolean>> = []\n\n for (const cond1 of conditions1) {\n for (const cond2 of conditions2) {\n if (areExpressionsEqual(cond1, cond2)) {\n // Avoid duplicates\n if (!common.some((c) => areExpressionsEqual(c, cond1))) {\n common.push(cond1)\n }\n break\n }\n }\n }\n\n return common\n}\n\n/**\n * Extract all individual conditions from a predicate, flattening AND operations.\n */\nfunction extractAllConditions(\n predicate: BasicExpression<boolean>\n): Array<BasicExpression<boolean>> {\n if (predicate.type === `func` && predicate.name === `and`) {\n const conditions: Array<BasicExpression<boolean>> = []\n for (const arg of predicate.args) {\n conditions.push(...extractAllConditions(arg as BasicExpression<boolean>))\n }\n return conditions\n }\n\n return [predicate]\n}\n\n/**\n * Remove specified conditions from a predicate.\n * Returns the predicate with the specified conditions removed, or undefined if all conditions are removed.\n */\nfunction removeConditions(\n predicate: BasicExpression<boolean>,\n conditionsToRemove: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> | undefined {\n if (predicate.type === `func` && predicate.name === `and`) {\n const remainingArgs = predicate.args.filter(\n (arg) =>\n !conditionsToRemove.some((cond) =>\n areExpressionsEqual(arg as BasicExpression<boolean>, cond)\n )\n )\n\n if (remainingArgs.length === 0) {\n return undefined\n } else if (remainingArgs.length === 1) {\n return remainingArgs[0]!\n } else {\n return {\n type: `func`,\n name: `and`,\n args: remainingArgs,\n } as BasicExpression<boolean>\n }\n }\n\n // For non-AND predicates, don't remove anything\n return predicate\n}\n\n/**\n * Combine multiple conditions into a single predicate using AND logic.\n * Flattens nested AND operations to avoid unnecessary nesting.\n */\nfunction combineConditions(\n conditions: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> {\n if (conditions.length === 0) {\n return { type: `val`, value: true } as BasicExpression<boolean>\n } else if (conditions.length === 1) {\n return conditions[0]!\n } else {\n // Flatten all conditions, including those that are already AND operations\n const flattenedConditions: Array<BasicExpression<boolean>> = []\n\n for (const condition of conditions) {\n if (condition.type === `func` && condition.name === `and`) {\n // Flatten nested AND operations\n flattenedConditions.push(...condition.args)\n } else {\n flattenedConditions.push(condition)\n }\n }\n\n if (flattenedConditions.length === 1) {\n return flattenedConditions[0]!\n } else {\n return {\n type: `func`,\n name: `and`,\n args: flattenedConditions,\n } as BasicExpression<boolean>\n }\n }\n}\n\n/**\n * Find a predicate with a specific operator and value\n */\nfunction findPredicateWithOperator(\n predicates: Array<BasicExpression<boolean>>,\n operator: string,\n value: any\n): BasicExpression<boolean> | undefined {\n return predicates.find((p) => {\n if (p.type === `func`) {\n const f = p as Func\n const field = extractComparisonField(f)\n return f.name === operator && field && areValuesEqual(field.value, value)\n }\n return false\n })\n}\n\nfunction areExpressionsEqual(a: BasicExpression, b: BasicExpression): boolean {\n if (a.type !== b.type) {\n return false\n }\n\n if (a.type === `val` && b.type === `val`) {\n return areValuesEqual(a.value, b.value)\n }\n\n if (a.type === `ref` && b.type === `ref`) {\n return areRefsEqual(a, b)\n }\n\n if (a.type === `func` && b.type === `func`) {\n const aFunc = a\n const bFunc = b\n if (aFunc.name !== bFunc.name) {\n return false\n }\n if (aFunc.args.length !== bFunc.args.length) {\n return false\n }\n return aFunc.args.every((arg, i) =>\n areExpressionsEqual(arg, bFunc.args[i]!)\n )\n }\n\n return false\n}\n\nfunction areValuesEqual(a: any, b: any): boolean {\n // Simple equality check - could be enhanced for deep object comparison\n if (a === b) {\n return true\n }\n\n // Handle NaN\n if (typeof a === `number` && typeof b === `number` && isNaN(a) && isNaN(b)) {\n return true\n }\n\n // Handle Date objects\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime()\n }\n\n // For arrays and objects, use reference equality\n // (In practice, we don't need deep equality for these cases -\n // same object reference means same value for our use case)\n if (\n typeof a === `object` &&\n typeof b === `object` &&\n a !== null &&\n b !== null\n ) {\n return a === b\n }\n\n return false\n}\n\nfunction areRefsEqual(a: PropRef, b: PropRef): boolean {\n if (a.path.length !== b.path.length) {\n return false\n }\n return a.path.every((segment, i) => segment === b.path[i])\n}\n\n/**\n * Check if a value is a primitive (string, number, boolean, null, undefined)\n * Primitives can use Set for fast lookups\n */\nfunction isPrimitive(value: any): boolean {\n return (\n value === null ||\n value === undefined ||\n typeof value === `string` ||\n typeof value === `number` ||\n typeof value === `boolean`\n )\n}\n\n/**\n * Check if all values in an array are primitives\n */\nfunction areAllPrimitives(values: Array<any>): boolean {\n return values.every(isPrimitive)\n}\n\n/**\n * Check if a value is in an array, with optional pre-built Set for optimization.\n * The primitiveSet is cached in InField during extraction and reused for all lookups.\n */\nfunction arrayIncludesWithSet(\n array: Array<any>,\n value: any,\n primitiveSet: Set<any> | null,\n arrayIsAllPrimitives?: boolean\n): boolean {\n // Fast path: use pre-built Set for O(1) lookup\n if (primitiveSet) {\n // Skip isPrimitive check if we know the value must be primitive for a match\n // (if array is all primitives, only primitives can match)\n if (arrayIsAllPrimitives || isPrimitive(value)) {\n return primitiveSet.has(value)\n }\n return false // Non-primitive can't be in primitive-only set\n }\n\n // Fallback: use areValuesEqual for Dates and objects\n return array.some((v) => areValuesEqual(v, value))\n}\n\n/**\n * Get the maximum of two values, handling both numbers and Dates\n */\nfunction maxValue(a: any, b: any): any {\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() > b.getTime() ? a : b\n }\n return Math.max(a, b)\n}\n\n/**\n * Get the minimum of two values, handling both numbers and Dates\n */\nfunction minValue(a: any, b: any): any {\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() < b.getTime() ? a : b\n }\n return Math.min(a, b)\n}\n\nfunction areCompareOptionsEqual(\n a: { direction?: `asc` | `desc`; [key: string]: any },\n b: { direction?: `asc` | `desc`; [key: string]: any }\n): boolean {\n // For now, just compare direction - could be enhanced for other options\n return a.direction === b.direction\n}\n\ninterface ComparisonField {\n ref: PropRef\n value: any\n}\n\nfunction extractComparisonField(func: Func): ComparisonField | null {\n // Handle comparison operators: eq, gt, gte, lt, lte\n if ([`eq`, `gt`, `gte`, `lt`, `lte`].includes(func.name)) {\n // Assume first arg is ref, second is value\n const firstArg = func.args[0]\n const secondArg = func.args[1]\n\n if (firstArg?.type === `ref` && secondArg?.type === `val`) {\n return {\n ref: firstArg,\n value: secondArg.value,\n }\n }\n }\n\n return null\n}\n\nfunction extractEqualityField(func: Func): ComparisonField | null {\n if (func.name === `eq`) {\n const firstArg = func.args[0]\n const secondArg = func.args[1]\n\n if (firstArg?.type === `ref` && secondArg?.type === `val`) {\n return {\n ref: firstArg,\n value: secondArg.value,\n }\n }\n }\n return null\n}\n\ninterface InField {\n ref: PropRef\n values: Array<any>\n // Cached optimization data (computed once, reused many times)\n areAllPrimitives?: boolean\n primitiveSet?: Set<any> | null\n}\n\nfunction extractInField(func: Func): InField | null {\n if (func.name === `in`) {\n const firstArg = func.args[0]\n const secondArg = func.args[1]\n\n if (\n firstArg?.type === `ref` &&\n secondArg?.type === `val` &&\n Array.isArray(secondArg.value)\n ) {\n let values = secondArg.value\n // Precompute optimization metadata once\n const allPrimitives = areAllPrimitives(values)\n let primitiveSet: Set<any> | null = null\n\n if (allPrimitives && values.length > 10) {\n // Build Set and dedupe values at the same time\n primitiveSet = new Set(values)\n // If we found duplicates, use the deduped array going forward\n if (primitiveSet.size < values.length) {\n values = Array.from(primitiveSet)\n }\n }\n\n return {\n ref: firstArg,\n values,\n areAllPrimitives: allPrimitives,\n primitiveSet,\n }\n }\n }\n return null\n}\n\nfunction isComparisonSubset(\n subsetFunc: Func,\n subsetValue: any,\n supersetFunc: Func,\n supersetValue: any\n): boolean {\n const subOp = subsetFunc.name\n const superOp = supersetFunc.name\n\n // Handle same operator\n if (subOp === superOp) {\n if (subOp === `eq`) {\n // field = X is subset of field = X only\n // Fast path: primitives can use strict equality\n if (isPrimitive(subsetValue) && isPrimitive(supersetValue)) {\n return subsetValue === supersetValue\n }\n return areValuesEqual(subsetValue, supersetValue)\n } else if (subOp === `gt`) {\n // field > 20 is subset of field > 10 if 20 > 10\n return subsetValue >= supersetValue\n } else if (subOp === `gte`) {\n // field >= 20 is subset of field >= 10 if 20 >= 10\n return subsetValue >= supersetValue\n } else if (subOp === `lt`) {\n // field < 10 is subset of field < 20 if 10 <= 20\n return subsetValue <= supersetValue\n } else if (subOp === `lte`) {\n // field <= 10 is subset of field <= 20 if 10 <= 20\n return subsetValue <= supersetValue\n }\n }\n\n // Handle different operators on same field\n // eq vs gt/gte: field = 15 is subset of field > 10 if 15 > 10\n if (subOp === `eq` && superOp === `gt`) {\n return subsetValue > supersetValue\n }\n if (subOp === `eq` && superOp === `gte`) {\n return subsetValue >= supersetValue\n }\n if (subOp === `eq` && superOp === `lt`) {\n return subsetValue < supersetValue\n }\n if (subOp === `eq` && superOp === `lte`) {\n return subsetValue <= supersetValue\n }\n\n // gt/gte vs gte/gt\n if (subOp === `gt` && superOp === `gte`) {\n // field > 10 is subset of field >= 10 if 10 >= 10 (always true for same value)\n return subsetValue >= supersetValue\n }\n if (subOp === `gte` && superOp === `gt`) {\n // field >= 11 is subset of field > 10 if 11 > 10\n return subsetValue > supersetValue\n }\n\n // lt/lte vs lte/lt\n if (subOp === `lt` && superOp === `lte`) {\n // field < 10 is subset of field <= 10 if 10 <= 10\n return subsetValue <= supersetValue\n }\n if (subOp === `lte` && superOp === `lt`) {\n // field <= 9 is subset of field < 10 if 9 < 10\n return subsetValue < supersetValue\n }\n\n return false\n}\n\nfunction groupPredicatesByField(\n predicates: Array<BasicExpression<boolean>>\n): Map<string | null, Array<BasicExpression<boolean>>> {\n const groups = new Map<string | null, Array<BasicExpression<boolean>>>()\n\n for (const pred of predicates) {\n let fieldKey: string | null = null\n\n if (pred.type === `func`) {\n const func = pred as Func\n const field =\n extractComparisonField(func) ||\n extractEqualityField(func) ||\n extractInField(func)\n if (field) {\n fieldKey = field.ref.path.join(`.`)\n }\n }\n\n const group = groups.get(fieldKey) || []\n group.push(pred)\n groups.set(fieldKey, group)\n }\n\n return groups\n}\n\nfunction unionSameFieldPredicates(\n predicates: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> | null {\n if (predicates.length === 1) {\n return predicates[0]!\n }\n\n // Try to extract range constraints\n let maxGt: number | null = null\n let maxGte: number | null = null\n let minLt: number | null = null\n let minLte: number | null = null\n const eqValues: Set<any> = new Set()\n const inValues: Set<any> = new Set()\n const otherPredicates: Array<BasicExpression<boolean>> = []\n\n for (const pred of predicates) {\n if (pred.type === `func`) {\n const func = pred as Func\n const field = extractComparisonField(func)\n\n if (field) {\n const value = field.value\n if (func.name === `gt`) {\n maxGt = maxGt === null ? value : minValue(maxGt, value)\n } else if (func.name === `gte`) {\n maxGte = maxGte === null ? value : minValue(maxGte, value)\n } else if (func.name === `lt`) {\n minLt = minLt === null ? value : maxValue(minLt, value)\n } else if (func.name === `lte`) {\n minLte = minLte === null ? value : maxValue(minLte, value)\n } else if (func.name === `eq`) {\n eqValues.add(value)\n } else {\n otherPredicates.push(pred)\n }\n } else {\n const inField = extractInField(func)\n if (inField) {\n for (const val of inField.values) {\n inValues.add(val)\n }\n } else {\n otherPredicates.push(pred)\n }\n }\n } else {\n otherPredicates.push(pred)\n }\n }\n\n // If we have multiple equality values, combine into IN\n if (eqValues.size > 1 || (eqValues.size > 0 && inValues.size > 0)) {\n const allValues = [...eqValues, ...inValues]\n const ref = predicates.find((p) => {\n if (p.type === `func`) {\n const field =\n extractComparisonField(p as Func) || extractInField(p as Func)\n return field !== null\n }\n return false\n })\n\n if (ref && ref.type === `func`) {\n const field =\n extractComparisonField(ref as Func) || extractInField(ref as Func)\n if (field) {\n return {\n type: `func`,\n name: `in`,\n args: [\n field.ref,\n { type: `val`, value: allValues } as BasicExpression,\n ],\n } as BasicExpression<boolean>\n }\n }\n }\n\n // Build the least restrictive range\n const result: Array<BasicExpression<boolean>> = []\n\n // Choose the least restrictive lower bound\n if (maxGt !== null && maxGte !== null) {\n // Take the smaller one (less restrictive)\n const pred =\n maxGte <= maxGt\n ? findPredicateWithOperator(predicates, `gte`, maxGte)\n : findPredicateWithOperator(predicates, `gt`, maxGt)\n if (pred) result.push(pred)\n } else if (maxGt !== null) {\n const pred = findPredicateWithOperator(predicates, `gt`, maxGt)\n if (pred) result.push(pred)\n } else if (maxGte !== null) {\n const pred = findPredicateWithOperator(predicates, `gte`, maxGte)\n if (pred) result.push(pred)\n }\n\n // Choose the least restrictive upper bound\n if (minLt !== null && minLte !== null) {\n const pred =\n minLte >= minLt\n ? findPredicateWithOperator(predicates, `lte`, minLte)\n : findPredicateWithOperator(predicates, `lt`, minLt)\n if (pred) result.push(pred)\n } else if (minLt !== null) {\n const pred = findPredicateWithOperator(predicates, `lt`, minLt)\n if (pred) result.push(pred)\n } else if (minLte !== null) {\n const pred = findPredicateWithOperator(predicates, `lte`, minLte)\n if (pred) result.push(pred)\n }\n\n // Add single eq value\n if (eqValues.size === 1 && inValues.size === 0) {\n const pred = findPredicateWithOperator(predicates, `eq`, [...eqValues][0])\n if (pred) result.push(pred)\n }\n\n // Add IN if only IN values\n if (eqValues.size === 0 && inValues.size > 0) {\n result.push(\n predicates.find((p) => {\n if (p.type === `func`) {\n return (p as Func).name === `in`\n }\n return false\n })!\n )\n }\n\n // Add other predicates\n result.push(...otherPredicates)\n\n if (result.length === 0) {\n return { type: `val`, value: true } as BasicExpression<boolean>\n }\n\n if (result.length === 1) {\n return result[0]!\n }\n\n return {\n type: `func`,\n name: `or`,\n args: result,\n } as BasicExpression<boolean>\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"predicate-utils.js","sources":["../../../src/query/predicate-utils.ts"],"sourcesContent":["import { Func, Value } from \"./ir.js\"\nimport type { BasicExpression, OrderBy, PropRef } from \"./ir.js\"\nimport type { LoadSubsetOptions } from \"../types.js\"\n\n/**\n * Check if one where clause is a logical subset of another.\n * Returns true if the subset predicate is more restrictive than (or equal to) the superset predicate.\n *\n * @example\n * // age > 20 is subset of age > 10 (more restrictive)\n * isWhereSubset(gt(ref('age'), val(20)), gt(ref('age'), val(10))) // true\n *\n * @example\n * // age > 10 AND name = 'X' is subset of age > 10 (more conditions)\n * isWhereSubset(and(gt(ref('age'), val(10)), eq(ref('name'), val('X'))), gt(ref('age'), val(10))) // true\n *\n * @param subset - The potentially more restrictive predicate\n * @param superset - The potentially less restrictive predicate\n * @returns true if subset logically implies superset\n */\nexport function isWhereSubset(\n subset: BasicExpression<boolean> | undefined,\n superset: BasicExpression<boolean> | undefined\n): boolean {\n // undefined/missing where clause means \"no filter\" (all data)\n // Both undefined means subset relationship holds (all data ⊆ all data)\n if (subset === undefined && superset === undefined) {\n return true\n }\n\n // If subset is undefined but superset is not, we're requesting ALL data\n // but have only loaded SOME data - subset relationship does NOT hold\n if (subset === undefined && superset !== undefined) {\n return false\n }\n\n // If superset is undefined (no filter = all data loaded),\n // then any constrained subset is contained\n if (superset === undefined && subset !== undefined) {\n return true\n }\n\n return isWhereSubsetInternal(subset!, superset!)\n}\n\nfunction makeDisjunction(\n preds: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> {\n if (preds.length === 0) {\n return new Value(false)\n }\n if (preds.length === 1) {\n return preds[0]!\n }\n return new Func(`or`, preds)\n}\n\nfunction convertInToOr(inField: InField) {\n const equalities = inField.values.map(\n (value) => new Func(`eq`, [inField.ref, new Value(value)])\n )\n return makeDisjunction(equalities)\n}\n\nfunction isWhereSubsetInternal(\n subset: BasicExpression<boolean>,\n superset: BasicExpression<boolean>\n): boolean {\n // If subset is false it is requesting no data,\n // thus the result set is empty\n // and the empty set is a subset of any set\n if (subset.type === `val` && subset.value === false) {\n return true\n }\n\n // If expressions are structurally equal, subset relationship holds\n if (areExpressionsEqual(subset, superset)) {\n return true\n }\n\n // Handle superset being an AND: subset must imply ALL conjuncts\n // If superset is (A AND B), then subset ⊆ (A AND B) only if subset ⊆ A AND subset ⊆ B\n // Example: (age > 20) ⊆ (age > 10 AND status = 'active') is false (doesn't imply status condition)\n if (superset.type === `func` && superset.name === `and`) {\n return superset.args.every((arg) =>\n isWhereSubsetInternal(subset, arg as BasicExpression<boolean>)\n )\n }\n\n // Handle subset being an AND: (A AND B) implies both A and B\n if (subset.type === `func` && subset.name === `and`) {\n // For (A AND B) ⊆ C, since (A AND B) implies A, we check if any conjunct implies C\n return subset.args.some((arg) =>\n isWhereSubsetInternal(arg as BasicExpression<boolean>, superset)\n )\n }\n\n // Turn x IN [A, B, C] into x = A OR x = B OR x = C\n // for unified handling of IN and OR\n if (subset.type === `func` && subset.name === `in`) {\n const inField = extractInField(subset)\n if (inField) {\n return isWhereSubsetInternal(convertInToOr(inField), superset)\n }\n }\n\n if (superset.type === `func` && superset.name === `in`) {\n const inField = extractInField(superset)\n if (inField) {\n return isWhereSubsetInternal(subset, convertInToOr(inField))\n }\n }\n\n // Handle OR in subset: (A OR B) is subset of C only if both A and B are subsets of C\n if (subset.type === `func` && subset.name === `or`) {\n return subset.args.every((arg) =>\n isWhereSubsetInternal(arg as BasicExpression<boolean>, superset)\n )\n }\n\n // Handle OR in superset: subset ⊆ (A OR B) if subset ⊆ A or subset ⊆ B\n // (A OR B) as superset means data can satisfy A or B\n // If subset is contained in any disjunct, it's contained in the union\n if (superset.type === `func` && superset.name === `or`) {\n return superset.args.some((arg) =>\n isWhereSubsetInternal(subset, arg as BasicExpression<boolean>)\n )\n }\n\n // Handle comparison operators on the same field\n if (subset.type === `func` && superset.type === `func`) {\n const subsetFunc = subset as Func\n const supersetFunc = superset as Func\n\n // Check if both are comparisons on the same field\n const subsetField = extractComparisonField(subsetFunc)\n const supersetField = extractComparisonField(supersetFunc)\n\n if (\n subsetField &&\n supersetField &&\n areRefsEqual(subsetField.ref, supersetField.ref)\n ) {\n return isComparisonSubset(\n subsetFunc,\n subsetField.value,\n supersetFunc,\n supersetField.value\n )\n }\n\n /*\n // Handle eq vs in\n if (subsetFunc.name === `eq` && supersetFunc.name === `in`) {\n const subsetFieldEq = extractEqualityField(subsetFunc)\n const supersetFieldIn = extractInField(supersetFunc)\n if (\n subsetFieldEq &&\n supersetFieldIn &&\n areRefsEqual(subsetFieldEq.ref, supersetFieldIn.ref)\n ) {\n // field = X is subset of field IN [X, Y, Z] if X is in the array\n // Use cached primitive set and metadata from extraction\n return arrayIncludesWithSet(\n supersetFieldIn.values,\n subsetFieldEq.value,\n supersetFieldIn.primitiveSet ?? null,\n supersetFieldIn.areAllPrimitives\n )\n }\n }\n\n // Handle in vs in\n if (subsetFunc.name === `in` && supersetFunc.name === `in`) {\n const subsetFieldIn = extractInField(subsetFunc)\n const supersetFieldIn = extractInField(supersetFunc)\n if (\n subsetFieldIn &&\n supersetFieldIn &&\n areRefsEqual(subsetFieldIn.ref, supersetFieldIn.ref)\n ) {\n // field IN [A, B] is subset of field IN [A, B, C] if all values in subset are in superset\n // Use cached primitive set and metadata from extraction\n return subsetFieldIn.values.every((subVal) =>\n arrayIncludesWithSet(\n supersetFieldIn.values,\n subVal,\n supersetFieldIn.primitiveSet ?? null,\n supersetFieldIn.areAllPrimitives\n )\n )\n }\n }\n */\n }\n\n // Conservative: if we can't determine, return false\n return false\n}\n\n/**\n * Helper to combine where predicates with common logic for AND/OR operations\n */\nfunction combineWherePredicates(\n predicates: Array<BasicExpression<boolean>>,\n operation: `and` | `or`,\n simplifyFn: (\n preds: Array<BasicExpression<boolean>>\n ) => BasicExpression<boolean> | null\n): BasicExpression<boolean> {\n const emptyValue = operation === `and` ? true : false\n const identityValue = operation === `and` ? true : false\n\n if (predicates.length === 0) {\n return { type: `val`, value: emptyValue } as BasicExpression<boolean>\n }\n\n if (predicates.length === 1) {\n return predicates[0]!\n }\n\n // Flatten nested expressions of the same operation\n const flatPredicates: Array<BasicExpression<boolean>> = []\n for (const pred of predicates) {\n if (pred.type === `func` && pred.name === operation) {\n flatPredicates.push(...pred.args)\n } else {\n flatPredicates.push(pred)\n }\n }\n\n // Group predicates by field for simplification\n const grouped = groupPredicatesByField(flatPredicates)\n\n // Simplify each group\n const simplified: Array<BasicExpression<boolean>> = []\n for (const [field, preds] of grouped.entries()) {\n if (field === null) {\n // Complex predicates that we can't group by field\n simplified.push(...preds)\n } else {\n // Try to simplify same-field predicates\n const result = simplifyFn(preds)\n\n // For intersection: check for empty set (contradiction)\n if (\n operation === `and` &&\n result &&\n result.type === `val` &&\n result.value === false\n ) {\n // Intersection is empty (conflicting constraints) - entire AND is false\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n // For union: result may be null if simplification failed\n if (result) {\n simplified.push(result)\n }\n }\n }\n\n if (simplified.length === 0) {\n return { type: `val`, value: identityValue } as BasicExpression<boolean>\n }\n\n if (simplified.length === 1) {\n return simplified[0]!\n }\n\n // Return combined predicate\n return {\n type: `func`,\n name: operation,\n args: simplified,\n } as BasicExpression<boolean>\n}\n\n/**\n * Combine multiple where predicates with OR logic (union).\n * Returns a predicate that is satisfied when any input predicate is satisfied.\n * Simplifies when possible (e.g., age > 10 OR age > 20 → age > 10).\n *\n * @example\n * // Take least restrictive\n * unionWherePredicates([gt(ref('age'), val(10)), gt(ref('age'), val(20))]) // age > 10\n *\n * @example\n * // Combine equals into IN\n * unionWherePredicates([eq(ref('age'), val(5)), eq(ref('age'), val(10))]) // age IN [5, 10]\n *\n * @param predicates - Array of where predicates to union\n * @returns Combined predicate representing the union\n */\nexport function unionWherePredicates(\n predicates: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> {\n return combineWherePredicates(predicates, `or`, unionSameFieldPredicates)\n}\n\n/**\n * Compute the difference between two where predicates: `fromPredicate AND NOT(subtractPredicate)`.\n * Returns the simplified predicate, or null if the difference cannot be simplified\n * (in which case the caller should fetch the full fromPredicate).\n *\n * @example\n * // Range difference\n * minusWherePredicates(\n * gt(ref('age'), val(10)), // age > 10\n * gt(ref('age'), val(20)) // age > 20\n * ) // → age > 10 AND age <= 20\n *\n * @example\n * // Set difference\n * minusWherePredicates(\n * inOp(ref('status'), ['A', 'B', 'C', 'D']), // status IN ['A','B','C','D']\n * inOp(ref('status'), ['B', 'C']) // status IN ['B','C']\n * ) // → status IN ['A', 'D']\n *\n * @example\n * // Common conditions\n * minusWherePredicates(\n * and(gt(ref('age'), val(10)), eq(ref('status'), val('active'))), // age > 10 AND status = 'active'\n * and(gt(ref('age'), val(20)), eq(ref('status'), val('active'))) // age > 20 AND status = 'active'\n * ) // → age > 10 AND age <= 20 AND status = 'active'\n *\n * @example\n * // Complete overlap - empty result\n * minusWherePredicates(\n * gt(ref('age'), val(20)), // age > 20\n * gt(ref('age'), val(10)) // age > 10\n * ) // → {type: 'val', value: false} (empty set)\n *\n * @param fromPredicate - The predicate to subtract from\n * @param subtractPredicate - The predicate to subtract\n * @returns The simplified difference, or null if cannot be simplified\n */\nexport function minusWherePredicates(\n fromPredicate: BasicExpression<boolean> | undefined,\n subtractPredicate: BasicExpression<boolean> | undefined\n): BasicExpression<boolean> | null {\n // If nothing to subtract, return the original\n if (subtractPredicate === undefined) {\n return (\n fromPredicate ??\n ({ type: `val`, value: true } as BasicExpression<boolean>)\n )\n }\n\n // If from is undefined then we are asking for all data\n // so we need to load all data minus what we already loaded\n // i.e. we need to load NOT(subtractPredicate)\n if (fromPredicate === undefined) {\n return {\n type: `func`,\n name: `not`,\n args: [subtractPredicate],\n } as BasicExpression<boolean>\n }\n\n // Check if fromPredicate is entirely contained in subtractPredicate\n // In that case, fromPredicate AND NOT(subtractPredicate) = empty set\n if (isWhereSubset(fromPredicate, subtractPredicate)) {\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n // Try to detect and handle common conditions\n const commonConditions = findCommonConditions(\n fromPredicate,\n subtractPredicate\n )\n if (commonConditions.length > 0) {\n // Extract predicates without common conditions\n const fromWithoutCommon = removeConditions(fromPredicate, commonConditions)\n const subtractWithoutCommon = removeConditions(\n subtractPredicate,\n commonConditions\n )\n\n // Recursively compute difference on simplified predicates\n const simplifiedDifference = minusWherePredicates(\n fromWithoutCommon,\n subtractWithoutCommon\n )\n\n if (simplifiedDifference !== null) {\n // Combine the simplified difference with common conditions\n return combineConditions([...commonConditions, simplifiedDifference])\n }\n }\n\n // Check if they are on the same field - if so, we can try to simplify\n if (fromPredicate.type === `func` && subtractPredicate.type === `func`) {\n const result = minusSameFieldPredicates(fromPredicate, subtractPredicate)\n if (result !== null) {\n return result\n }\n }\n\n // Can't simplify - return null to indicate caller should fetch full fromPredicate\n return null\n}\n\n/**\n * Helper function to compute difference for same-field predicates\n */\nfunction minusSameFieldPredicates(\n fromPred: Func,\n subtractPred: Func\n): BasicExpression<boolean> | null {\n // Extract field information\n const fromField =\n extractComparisonField(fromPred) ||\n extractEqualityField(fromPred) ||\n extractInField(fromPred)\n const subtractField =\n extractComparisonField(subtractPred) ||\n extractEqualityField(subtractPred) ||\n extractInField(subtractPred)\n\n // Must be on the same field\n if (\n !fromField ||\n !subtractField ||\n !areRefsEqual(fromField.ref, subtractField.ref)\n ) {\n return null\n }\n\n // Handle IN minus IN: status IN [A,B,C,D] - status IN [B,C] = status IN [A,D]\n if (fromPred.name === `in` && subtractPred.name === `in`) {\n const fromInField = fromField as InField\n const subtractInField = subtractField as InField\n\n // Filter out values that are in the subtract set\n const remainingValues = fromInField.values.filter(\n (v) =>\n !arrayIncludesWithSet(\n subtractInField.values,\n v,\n subtractInField.primitiveSet ?? null,\n subtractInField.areAllPrimitives\n )\n )\n\n if (remainingValues.length === 0) {\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n if (remainingValues.length === 1) {\n return {\n type: `func`,\n name: `eq`,\n args: [fromField.ref, { type: `val`, value: remainingValues[0] }],\n } as BasicExpression<boolean>\n }\n\n return {\n type: `func`,\n name: `in`,\n args: [fromField.ref, { type: `val`, value: remainingValues }],\n } as BasicExpression<boolean>\n }\n\n // Handle IN minus equality: status IN [A,B,C] - status = B = status IN [A,C]\n if (fromPred.name === `in` && subtractPred.name === `eq`) {\n const fromInField = fromField as InField\n const subtractValue = (subtractField as { ref: PropRef; value: any }).value\n\n const remainingValues = fromInField.values.filter(\n (v) => !areValuesEqual(v, subtractValue)\n )\n\n if (remainingValues.length === 0) {\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n if (remainingValues.length === 1) {\n return {\n type: `func`,\n name: `eq`,\n args: [fromField.ref, { type: `val`, value: remainingValues[0] }],\n } as BasicExpression<boolean>\n }\n\n return {\n type: `func`,\n name: `in`,\n args: [fromField.ref, { type: `val`, value: remainingValues }],\n } as BasicExpression<boolean>\n }\n\n // Handle equality minus equality: age = 15 - age = 15 = empty, age = 15 - age = 20 = age = 15\n if (fromPred.name === `eq` && subtractPred.name === `eq`) {\n const fromValue = (fromField as { ref: PropRef; value: any }).value\n const subtractValue = (subtractField as { ref: PropRef; value: any }).value\n\n if (areValuesEqual(fromValue, subtractValue)) {\n return { type: `val`, value: false } as BasicExpression<boolean>\n }\n\n // No overlap - return original\n return fromPred as BasicExpression<boolean>\n }\n\n // Handle range minus range: age > 10 - age > 20 = age > 10 AND age <= 20\n const fromComp = extractComparisonField(fromPred)\n const subtractComp = extractComparisonField(subtractPred)\n\n if (\n fromComp &&\n subtractComp &&\n areRefsEqual(fromComp.ref, subtractComp.ref)\n ) {\n // Try to compute the difference using range logic\n const result = minusRangePredicates(\n fromPred,\n fromComp.value,\n subtractPred,\n subtractComp.value\n )\n return result\n }\n\n // Can't simplify\n return null\n}\n\n/**\n * Helper to compute difference between range predicates\n */\nfunction minusRangePredicates(\n fromFunc: Func,\n fromValue: any,\n subtractFunc: Func,\n subtractValue: any\n): BasicExpression<boolean> | null {\n const fromOp = fromFunc.name as `gt` | `gte` | `lt` | `lte` | `eq`\n const subtractOp = subtractFunc.name as `gt` | `gte` | `lt` | `lte` | `eq`\n const ref = (extractComparisonField(fromFunc) ||\n extractEqualityField(fromFunc))!.ref\n\n // age > 10 - age > 20 = (age > 10 AND age <= 20)\n if (fromOp === `gt` && subtractOp === `gt`) {\n if (fromValue < subtractValue) {\n // Result is: fromValue < field <= subtractValue\n return {\n type: `func`,\n name: `and`,\n args: [\n fromFunc as BasicExpression<boolean>,\n {\n type: `func`,\n name: `lte`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n // fromValue >= subtractValue means no overlap\n return fromFunc as BasicExpression<boolean>\n }\n\n // age >= 10 - age >= 20 = (age >= 10 AND age < 20)\n if (fromOp === `gte` && subtractOp === `gte`) {\n if (fromValue < subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n fromFunc as BasicExpression<boolean>,\n {\n type: `func`,\n name: `lt`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age > 10 - age >= 20 = (age > 10 AND age < 20)\n if (fromOp === `gt` && subtractOp === `gte`) {\n if (fromValue < subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n fromFunc as BasicExpression<boolean>,\n {\n type: `func`,\n name: `lt`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age >= 10 - age > 20 = (age >= 10 AND age <= 20)\n if (fromOp === `gte` && subtractOp === `gt`) {\n if (fromValue <= subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n fromFunc as BasicExpression<boolean>,\n {\n type: `func`,\n name: `lte`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age < 30 - age < 20 = (age >= 20 AND age < 30)\n if (fromOp === `lt` && subtractOp === `lt`) {\n if (fromValue > subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n {\n type: `func`,\n name: `gte`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n fromFunc as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age <= 30 - age <= 20 = (age > 20 AND age <= 30)\n if (fromOp === `lte` && subtractOp === `lte`) {\n if (fromValue > subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n {\n type: `func`,\n name: `gt`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n fromFunc as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age < 30 - age <= 20 = (age > 20 AND age < 30)\n if (fromOp === `lt` && subtractOp === `lte`) {\n if (fromValue > subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n {\n type: `func`,\n name: `gt`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n fromFunc as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // age <= 30 - age < 20 = (age >= 20 AND age <= 30)\n if (fromOp === `lte` && subtractOp === `lt`) {\n if (fromValue >= subtractValue) {\n return {\n type: `func`,\n name: `and`,\n args: [\n {\n type: `func`,\n name: `gte`,\n args: [ref, { type: `val`, value: subtractValue }],\n } as BasicExpression<boolean>,\n fromFunc as BasicExpression<boolean>,\n ],\n } as BasicExpression<boolean>\n }\n return fromFunc as BasicExpression<boolean>\n }\n\n // Can't simplify other combinations\n return null\n}\n\n/**\n * Check if one orderBy clause is a subset of another.\n * Returns true if the subset ordering requirements are satisfied by the superset ordering.\n *\n * @example\n * // Subset is prefix of superset\n * isOrderBySubset([{expr: age, asc}], [{expr: age, asc}, {expr: name, desc}]) // true\n *\n * @param subset - The ordering requirements to check\n * @param superset - The ordering that might satisfy the requirements\n * @returns true if subset is satisfied by superset\n */\nexport function isOrderBySubset(\n subset: OrderBy | undefined,\n superset: OrderBy | undefined\n): boolean {\n // No ordering requirement is always satisfied\n if (!subset || subset.length === 0) {\n return true\n }\n\n // If there's no superset ordering but subset requires ordering, not satisfied\n if (!superset || superset.length === 0) {\n return false\n }\n\n // Check if subset is a prefix of superset with matching expressions and compare options\n if (subset.length > superset.length) {\n return false\n }\n\n for (let i = 0; i < subset.length; i++) {\n const subClause = subset[i]!\n const superClause = superset[i]!\n\n // Check if expressions match\n if (!areExpressionsEqual(subClause.expression, superClause.expression)) {\n return false\n }\n\n // Check if compare options match\n if (\n !areCompareOptionsEqual(\n subClause.compareOptions,\n superClause.compareOptions\n )\n ) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Check if one limit is a subset of another.\n * Returns true if the subset limit requirements are satisfied by the superset limit.\n *\n * @example\n * isLimitSubset(10, 20) // true (requesting 10 items when 20 are available)\n * isLimitSubset(20, 10) // false (requesting 20 items when only 10 are available)\n * isLimitSubset(10, undefined) // true (requesting 10 items when unlimited are available)\n *\n * @param subset - The limit requirement to check\n * @param superset - The limit that might satisfy the requirement\n * @returns true if subset is satisfied by superset\n */\nexport function isLimitSubset(\n subset: number | undefined,\n superset: number | undefined\n): boolean {\n // Unlimited superset satisfies any limit requirement\n if (superset === undefined) {\n return true\n }\n\n // If requesting all data (no limit), we need unlimited data to satisfy it\n // But we know superset is not unlimited so we return false\n if (subset === undefined) {\n return false\n }\n\n // Otherwise, subset must be less than or equal to superset\n return subset <= superset\n}\n\n/**\n * Check if one predicate (where + orderBy + limit) is a subset of another.\n * Returns true if all aspects of the subset predicate are satisfied by the superset.\n *\n * @example\n * isPredicateSubset(\n * { where: gt(ref('age'), val(20)), limit: 10 },\n * { where: gt(ref('age'), val(10)), limit: 20 }\n * ) // true\n *\n * @param subset - The predicate requirements to check\n * @param superset - The predicate that might satisfy the requirements\n * @returns true if subset is satisfied by superset\n */\nexport function isPredicateSubset(\n subset: LoadSubsetOptions,\n superset: LoadSubsetOptions\n): boolean {\n // When the superset has a limit, we can only determine subset relationship\n // if the where clauses are equal (not just subset relationship).\n //\n // This is because a limited query only loads a portion of the matching rows.\n // A more restrictive where clause might require rows outside that portion.\n //\n // Example: superset = {where: undefined, limit: 10, orderBy: desc}\n // subset = {where: LIKE 'search%', limit: 10, orderBy: desc}\n // The top 10 items matching 'search%' might include items outside the overall top 10.\n //\n // However, if the where clauses are equal, then the subset relationship can\n // be determined by orderBy and limit alone:\n // Example: superset = {where: status='active', limit: 10, orderBy: desc}\n // subset = {where: status='active', limit: 5, orderBy: desc}\n // The top 5 active items ARE contained in the top 10 active items.\n if (superset.limit !== undefined) {\n // For limited supersets, where clauses must be equal\n if (!areWhereClausesEqual(subset.where, superset.where)) {\n return false\n }\n return (\n isOrderBySubset(subset.orderBy, superset.orderBy) &&\n isLimitSubset(subset.limit, superset.limit)\n )\n }\n\n // For unlimited supersets, use the normal subset logic\n return (\n isWhereSubset(subset.where, superset.where) &&\n isOrderBySubset(subset.orderBy, superset.orderBy) &&\n isLimitSubset(subset.limit, superset.limit)\n )\n}\n\n/**\n * Check if two where clauses are structurally equal.\n * Used for limited query subset checks where subset relationship isn't sufficient.\n */\nfunction areWhereClausesEqual(\n a: BasicExpression<boolean> | undefined,\n b: BasicExpression<boolean> | undefined\n): boolean {\n if (a === undefined && b === undefined) {\n return true\n }\n if (a === undefined || b === undefined) {\n return false\n }\n return areExpressionsEqual(a, b)\n}\n\n// ============================================================================\n// Helper functions\n// ============================================================================\n\n/**\n * Find common conditions between two predicates.\n * Returns an array of conditions that appear in both predicates.\n */\nfunction findCommonConditions(\n predicate1: BasicExpression<boolean>,\n predicate2: BasicExpression<boolean>\n): Array<BasicExpression<boolean>> {\n const conditions1 = extractAllConditions(predicate1)\n const conditions2 = extractAllConditions(predicate2)\n\n const common: Array<BasicExpression<boolean>> = []\n\n for (const cond1 of conditions1) {\n for (const cond2 of conditions2) {\n if (areExpressionsEqual(cond1, cond2)) {\n // Avoid duplicates\n if (!common.some((c) => areExpressionsEqual(c, cond1))) {\n common.push(cond1)\n }\n break\n }\n }\n }\n\n return common\n}\n\n/**\n * Extract all individual conditions from a predicate, flattening AND operations.\n */\nfunction extractAllConditions(\n predicate: BasicExpression<boolean>\n): Array<BasicExpression<boolean>> {\n if (predicate.type === `func` && predicate.name === `and`) {\n const conditions: Array<BasicExpression<boolean>> = []\n for (const arg of predicate.args) {\n conditions.push(...extractAllConditions(arg as BasicExpression<boolean>))\n }\n return conditions\n }\n\n return [predicate]\n}\n\n/**\n * Remove specified conditions from a predicate.\n * Returns the predicate with the specified conditions removed, or undefined if all conditions are removed.\n */\nfunction removeConditions(\n predicate: BasicExpression<boolean>,\n conditionsToRemove: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> | undefined {\n if (predicate.type === `func` && predicate.name === `and`) {\n const remainingArgs = predicate.args.filter(\n (arg) =>\n !conditionsToRemove.some((cond) =>\n areExpressionsEqual(arg as BasicExpression<boolean>, cond)\n )\n )\n\n if (remainingArgs.length === 0) {\n return undefined\n } else if (remainingArgs.length === 1) {\n return remainingArgs[0]!\n } else {\n return {\n type: `func`,\n name: `and`,\n args: remainingArgs,\n } as BasicExpression<boolean>\n }\n }\n\n // For non-AND predicates, don't remove anything\n return predicate\n}\n\n/**\n * Combine multiple conditions into a single predicate using AND logic.\n * Flattens nested AND operations to avoid unnecessary nesting.\n */\nfunction combineConditions(\n conditions: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> {\n if (conditions.length === 0) {\n return { type: `val`, value: true } as BasicExpression<boolean>\n } else if (conditions.length === 1) {\n return conditions[0]!\n } else {\n // Flatten all conditions, including those that are already AND operations\n const flattenedConditions: Array<BasicExpression<boolean>> = []\n\n for (const condition of conditions) {\n if (condition.type === `func` && condition.name === `and`) {\n // Flatten nested AND operations\n flattenedConditions.push(...condition.args)\n } else {\n flattenedConditions.push(condition)\n }\n }\n\n if (flattenedConditions.length === 1) {\n return flattenedConditions[0]!\n } else {\n return {\n type: `func`,\n name: `and`,\n args: flattenedConditions,\n } as BasicExpression<boolean>\n }\n }\n}\n\n/**\n * Find a predicate with a specific operator and value\n */\nfunction findPredicateWithOperator(\n predicates: Array<BasicExpression<boolean>>,\n operator: string,\n value: any\n): BasicExpression<boolean> | undefined {\n return predicates.find((p) => {\n if (p.type === `func`) {\n const f = p as Func\n const field = extractComparisonField(f)\n return f.name === operator && field && areValuesEqual(field.value, value)\n }\n return false\n })\n}\n\nfunction areExpressionsEqual(a: BasicExpression, b: BasicExpression): boolean {\n if (a.type !== b.type) {\n return false\n }\n\n if (a.type === `val` && b.type === `val`) {\n return areValuesEqual(a.value, b.value)\n }\n\n if (a.type === `ref` && b.type === `ref`) {\n return areRefsEqual(a, b)\n }\n\n if (a.type === `func` && b.type === `func`) {\n const aFunc = a\n const bFunc = b\n if (aFunc.name !== bFunc.name) {\n return false\n }\n if (aFunc.args.length !== bFunc.args.length) {\n return false\n }\n return aFunc.args.every((arg, i) =>\n areExpressionsEqual(arg, bFunc.args[i]!)\n )\n }\n\n return false\n}\n\nfunction areValuesEqual(a: any, b: any): boolean {\n // Simple equality check - could be enhanced for deep object comparison\n if (a === b) {\n return true\n }\n\n // Handle NaN\n if (typeof a === `number` && typeof b === `number` && isNaN(a) && isNaN(b)) {\n return true\n }\n\n // Handle Date objects\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime()\n }\n\n // For arrays and objects, use reference equality\n // (In practice, we don't need deep equality for these cases -\n // same object reference means same value for our use case)\n if (\n typeof a === `object` &&\n typeof b === `object` &&\n a !== null &&\n b !== null\n ) {\n return a === b\n }\n\n return false\n}\n\nfunction areRefsEqual(a: PropRef, b: PropRef): boolean {\n if (a.path.length !== b.path.length) {\n return false\n }\n return a.path.every((segment, i) => segment === b.path[i])\n}\n\n/**\n * Check if a value is a primitive (string, number, boolean, null, undefined)\n * Primitives can use Set for fast lookups\n */\nfunction isPrimitive(value: any): boolean {\n return (\n value === null ||\n value === undefined ||\n typeof value === `string` ||\n typeof value === `number` ||\n typeof value === `boolean`\n )\n}\n\n/**\n * Check if all values in an array are primitives\n */\nfunction areAllPrimitives(values: Array<any>): boolean {\n return values.every(isPrimitive)\n}\n\n/**\n * Check if a value is in an array, with optional pre-built Set for optimization.\n * The primitiveSet is cached in InField during extraction and reused for all lookups.\n */\nfunction arrayIncludesWithSet(\n array: Array<any>,\n value: any,\n primitiveSet: Set<any> | null,\n arrayIsAllPrimitives?: boolean\n): boolean {\n // Fast path: use pre-built Set for O(1) lookup\n if (primitiveSet) {\n // Skip isPrimitive check if we know the value must be primitive for a match\n // (if array is all primitives, only primitives can match)\n if (arrayIsAllPrimitives || isPrimitive(value)) {\n return primitiveSet.has(value)\n }\n return false // Non-primitive can't be in primitive-only set\n }\n\n // Fallback: use areValuesEqual for Dates and objects\n return array.some((v) => areValuesEqual(v, value))\n}\n\n/**\n * Get the maximum of two values, handling both numbers and Dates\n */\nfunction maxValue(a: any, b: any): any {\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() > b.getTime() ? a : b\n }\n return Math.max(a, b)\n}\n\n/**\n * Get the minimum of two values, handling both numbers and Dates\n */\nfunction minValue(a: any, b: any): any {\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() < b.getTime() ? a : b\n }\n return Math.min(a, b)\n}\n\nfunction areCompareOptionsEqual(\n a: { direction?: `asc` | `desc`; [key: string]: any },\n b: { direction?: `asc` | `desc`; [key: string]: any }\n): boolean {\n // For now, just compare direction - could be enhanced for other options\n return a.direction === b.direction\n}\n\ninterface ComparisonField {\n ref: PropRef\n value: any\n}\n\nfunction extractComparisonField(func: Func): ComparisonField | null {\n // Handle comparison operators: eq, gt, gte, lt, lte\n if ([`eq`, `gt`, `gte`, `lt`, `lte`].includes(func.name)) {\n // Assume first arg is ref, second is value\n const firstArg = func.args[0]\n const secondArg = func.args[1]\n\n if (firstArg?.type === `ref` && secondArg?.type === `val`) {\n return {\n ref: firstArg,\n value: secondArg.value,\n }\n }\n }\n\n return null\n}\n\nfunction extractEqualityField(func: Func): ComparisonField | null {\n if (func.name === `eq`) {\n const firstArg = func.args[0]\n const secondArg = func.args[1]\n\n if (firstArg?.type === `ref` && secondArg?.type === `val`) {\n return {\n ref: firstArg,\n value: secondArg.value,\n }\n }\n }\n return null\n}\n\ninterface InField {\n ref: PropRef\n values: Array<any>\n // Cached optimization data (computed once, reused many times)\n areAllPrimitives?: boolean\n primitiveSet?: Set<any> | null\n}\n\nfunction extractInField(func: Func): InField | null {\n if (func.name === `in`) {\n const firstArg = func.args[0]\n const secondArg = func.args[1]\n\n if (\n firstArg?.type === `ref` &&\n secondArg?.type === `val` &&\n Array.isArray(secondArg.value)\n ) {\n let values = secondArg.value\n // Precompute optimization metadata once\n const allPrimitives = areAllPrimitives(values)\n let primitiveSet: Set<any> | null = null\n\n if (allPrimitives && values.length > 10) {\n // Build Set and dedupe values at the same time\n primitiveSet = new Set(values)\n // If we found duplicates, use the deduped array going forward\n if (primitiveSet.size < values.length) {\n values = Array.from(primitiveSet)\n }\n }\n\n return {\n ref: firstArg,\n values,\n areAllPrimitives: allPrimitives,\n primitiveSet,\n }\n }\n }\n return null\n}\n\nfunction isComparisonSubset(\n subsetFunc: Func,\n subsetValue: any,\n supersetFunc: Func,\n supersetValue: any\n): boolean {\n const subOp = subsetFunc.name\n const superOp = supersetFunc.name\n\n // Handle same operator\n if (subOp === superOp) {\n if (subOp === `eq`) {\n // field = X is subset of field = X only\n // Fast path: primitives can use strict equality\n if (isPrimitive(subsetValue) && isPrimitive(supersetValue)) {\n return subsetValue === supersetValue\n }\n return areValuesEqual(subsetValue, supersetValue)\n } else if (subOp === `gt`) {\n // field > 20 is subset of field > 10 if 20 > 10\n return subsetValue >= supersetValue\n } else if (subOp === `gte`) {\n // field >= 20 is subset of field >= 10 if 20 >= 10\n return subsetValue >= supersetValue\n } else if (subOp === `lt`) {\n // field < 10 is subset of field < 20 if 10 <= 20\n return subsetValue <= supersetValue\n } else if (subOp === `lte`) {\n // field <= 10 is subset of field <= 20 if 10 <= 20\n return subsetValue <= supersetValue\n }\n }\n\n // Handle different operators on same field\n // eq vs gt/gte: field = 15 is subset of field > 10 if 15 > 10\n if (subOp === `eq` && superOp === `gt`) {\n return subsetValue > supersetValue\n }\n if (subOp === `eq` && superOp === `gte`) {\n return subsetValue >= supersetValue\n }\n if (subOp === `eq` && superOp === `lt`) {\n return subsetValue < supersetValue\n }\n if (subOp === `eq` && superOp === `lte`) {\n return subsetValue <= supersetValue\n }\n\n // gt/gte vs gte/gt\n if (subOp === `gt` && superOp === `gte`) {\n // field > 10 is subset of field >= 10 if 10 >= 10 (always true for same value)\n return subsetValue >= supersetValue\n }\n if (subOp === `gte` && superOp === `gt`) {\n // field >= 11 is subset of field > 10 if 11 > 10\n return subsetValue > supersetValue\n }\n\n // lt/lte vs lte/lt\n if (subOp === `lt` && superOp === `lte`) {\n // field < 10 is subset of field <= 10 if 10 <= 10\n return subsetValue <= supersetValue\n }\n if (subOp === `lte` && superOp === `lt`) {\n // field <= 9 is subset of field < 10 if 9 < 10\n return subsetValue < supersetValue\n }\n\n return false\n}\n\nfunction groupPredicatesByField(\n predicates: Array<BasicExpression<boolean>>\n): Map<string | null, Array<BasicExpression<boolean>>> {\n const groups = new Map<string | null, Array<BasicExpression<boolean>>>()\n\n for (const pred of predicates) {\n let fieldKey: string | null = null\n\n if (pred.type === `func`) {\n const func = pred as Func\n const field =\n extractComparisonField(func) ||\n extractEqualityField(func) ||\n extractInField(func)\n if (field) {\n fieldKey = field.ref.path.join(`.`)\n }\n }\n\n const group = groups.get(fieldKey) || []\n group.push(pred)\n groups.set(fieldKey, group)\n }\n\n return groups\n}\n\nfunction unionSameFieldPredicates(\n predicates: Array<BasicExpression<boolean>>\n): BasicExpression<boolean> | null {\n if (predicates.length === 1) {\n return predicates[0]!\n }\n\n // Try to extract range constraints\n let maxGt: number | null = null\n let maxGte: number | null = null\n let minLt: number | null = null\n let minLte: number | null = null\n const eqValues: Set<any> = new Set()\n const inValues: Set<any> = new Set()\n const otherPredicates: Array<BasicExpression<boolean>> = []\n\n for (const pred of predicates) {\n if (pred.type === `func`) {\n const func = pred as Func\n const field = extractComparisonField(func)\n\n if (field) {\n const value = field.value\n if (func.name === `gt`) {\n maxGt = maxGt === null ? value : minValue(maxGt, value)\n } else if (func.name === `gte`) {\n maxGte = maxGte === null ? value : minValue(maxGte, value)\n } else if (func.name === `lt`) {\n minLt = minLt === null ? value : maxValue(minLt, value)\n } else if (func.name === `lte`) {\n minLte = minLte === null ? value : maxValue(minLte, value)\n } else if (func.name === `eq`) {\n eqValues.add(value)\n } else {\n otherPredicates.push(pred)\n }\n } else {\n const inField = extractInField(func)\n if (inField) {\n for (const val of inField.values) {\n inValues.add(val)\n }\n } else {\n otherPredicates.push(pred)\n }\n }\n } else {\n otherPredicates.push(pred)\n }\n }\n\n // If we have multiple equality values, combine into IN\n if (eqValues.size > 1 || (eqValues.size > 0 && inValues.size > 0)) {\n const allValues = [...eqValues, ...inValues]\n const ref = predicates.find((p) => {\n if (p.type === `func`) {\n const field =\n extractComparisonField(p as Func) || extractInField(p as Func)\n return field !== null\n }\n return false\n })\n\n if (ref && ref.type === `func`) {\n const field =\n extractComparisonField(ref as Func) || extractInField(ref as Func)\n if (field) {\n return {\n type: `func`,\n name: `in`,\n args: [\n field.ref,\n { type: `val`, value: allValues } as BasicExpression,\n ],\n } as BasicExpression<boolean>\n }\n }\n }\n\n // Build the least restrictive range\n const result: Array<BasicExpression<boolean>> = []\n\n // Choose the least restrictive lower bound\n if (maxGt !== null && maxGte !== null) {\n // Take the smaller one (less restrictive)\n const pred =\n maxGte <= maxGt\n ? findPredicateWithOperator(predicates, `gte`, maxGte)\n : findPredicateWithOperator(predicates, `gt`, maxGt)\n if (pred) result.push(pred)\n } else if (maxGt !== null) {\n const pred = findPredicateWithOperator(predicates, `gt`, maxGt)\n if (pred) result.push(pred)\n } else if (maxGte !== null) {\n const pred = findPredicateWithOperator(predicates, `gte`, maxGte)\n if (pred) result.push(pred)\n }\n\n // Choose the least restrictive upper bound\n if (minLt !== null && minLte !== null) {\n const pred =\n minLte >= minLt\n ? findPredicateWithOperator(predicates, `lte`, minLte)\n : findPredicateWithOperator(predicates, `lt`, minLt)\n if (pred) result.push(pred)\n } else if (minLt !== null) {\n const pred = findPredicateWithOperator(predicates, `lt`, minLt)\n if (pred) result.push(pred)\n } else if (minLte !== null) {\n const pred = findPredicateWithOperator(predicates, `lte`, minLte)\n if (pred) result.push(pred)\n }\n\n // Add single eq value\n if (eqValues.size === 1 && inValues.size === 0) {\n const pred = findPredicateWithOperator(predicates, `eq`, [...eqValues][0])\n if (pred) result.push(pred)\n }\n\n // Add IN if only IN values\n if (eqValues.size === 0 && inValues.size > 0) {\n result.push(\n predicates.find((p) => {\n if (p.type === `func`) {\n return (p as Func).name === `in`\n }\n return false\n })!\n )\n }\n\n // Add other predicates\n result.push(...otherPredicates)\n\n if (result.length === 0) {\n return { type: `val`, value: true } as BasicExpression<boolean>\n }\n\n if (result.length === 1) {\n return result[0]!\n }\n\n return {\n type: `func`,\n name: `or`,\n args: result,\n } as BasicExpression<boolean>\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/db",
3
3
  "description": "A reactive client store for building super fast apps on sync",
4
- "version": "0.5.7",
4
+ "version": "0.5.9",
5
5
  "dependencies": {
6
6
  "@standard-schema/spec": "^1.0.0",
7
7
  "@tanstack/pacer-lite": "^0.1.0",
@@ -10,7 +10,7 @@
10
10
  "devDependencies": {
11
11
  "@vitest/coverage-istanbul": "^3.2.4",
12
12
  "arktype": "^2.1.27",
13
- "superjson": "^2.2.5",
13
+ "superjson": "^2.2.6",
14
14
  "temporal-polyfill": "^0.3.0"
15
15
  },
16
16
  "exports": {
@@ -163,17 +163,19 @@ export class CollectionMutationsManager<
163
163
 
164
164
  const items = Array.isArray(data) ? data : [data]
165
165
  const mutations: Array<PendingMutation<TOutput>> = []
166
+ const keysInCurrentBatch = new Set<TKey>()
166
167
 
167
168
  // Create mutations for each item
168
169
  items.forEach((item) => {
169
170
  // Validate the data against the schema if one exists
170
171
  const validatedData = this.validateData(item, `insert`)
171
172
 
172
- // Check if an item with this ID already exists in the collection
173
+ // Check if an item with this ID already exists in the collection or in the current batch
173
174
  const key = this.config.getKey(validatedData)
174
- if (this.state.has(key)) {
175
+ if (this.state.has(key) || keysInCurrentBatch.has(key)) {
175
176
  throw new DuplicateKeyError(key)
176
177
  }
178
+ keysInCurrentBatch.add(key)
177
179
  const globalKey = this.generateGlobalKey(key, item)
178
180
 
179
181
  const mutation: PendingMutation<TOutput, `insert`> = {
@@ -1,5 +1,5 @@
1
1
  import { ensureIndexForExpression } from "../indexes/auto-index.js"
2
- import { and, eq, gt, lt } from "../query/builder/functions.js"
2
+ import { and, eq, gt, gte, lt } from "../query/builder/functions.js"
3
3
  import { Value } from "../query/ir.js"
4
4
  import { EventEmitter } from "../event-emitter.js"
5
5
  import {
@@ -361,7 +361,19 @@ export class CollectionSubscription
361
361
  // First promise: load all values equal to minValue
362
362
  if (typeof minValue !== `undefined`) {
363
363
  const { expression } = orderBy[0]!
364
- const exactValueFilter = eq(expression, new Value(minValue))
364
+
365
+ // For Date values, we need to handle precision differences between JS (ms) and backends (μs)
366
+ // A JS Date represents a 1ms range, so we query for all values within that range
367
+ let exactValueFilter
368
+ if (minValue instanceof Date) {
369
+ const minValuePlus1ms = new Date(minValue.getTime() + 1)
370
+ exactValueFilter = and(
371
+ gte(expression, new Value(minValue)),
372
+ lt(expression, new Value(minValuePlus1ms))
373
+ )
374
+ } else {
375
+ exactValueFilter = eq(expression, new Value(minValue))
376
+ }
365
377
 
366
378
  const loadOptions2: LoadSubsetOptions = {
367
379
  where: exactValueFilter,
@@ -73,9 +73,9 @@ export interface IndexInterface<
73
73
  /**
74
74
  * Base abstract class that all index types extend
75
75
  */
76
- export abstract class BaseIndex<TKey extends string | number = string | number>
77
- implements IndexInterface<TKey>
78
- {
76
+ export abstract class BaseIndex<
77
+ TKey extends string | number = string | number,
78
+ > implements IndexInterface<TKey> {
79
79
  public readonly id: number
80
80
  public readonly name?: string
81
81
  public readonly expression: BasicExpression
@@ -3,9 +3,9 @@ import type { OrderByDirection } from "../query/ir"
3
3
  import type { IndexInterface, IndexOperation, IndexStats } from "./base-index"
4
4
  import type { RangeQueryOptions } from "./btree-index"
5
5
 
6
- export class ReverseIndex<TKey extends string | number>
7
- implements IndexInterface<TKey>
8
- {
6
+ export class ReverseIndex<
7
+ TKey extends string | number,
8
+ > implements IndexInterface<TKey> {
9
9
  private originalIndex: IndexInterface<TKey>
10
10
 
11
11
  constructor(index: IndexInterface<TKey>) {
package/src/local-only.ts CHANGED
@@ -24,9 +24,9 @@ export interface LocalOnlyCollectionConfig<
24
24
  TSchema extends StandardSchemaV1 = never,
25
25
  TKey extends string | number = string | number,
26
26
  > extends Omit<
27
- BaseCollectionConfig<T, TKey, TSchema, LocalOnlyCollectionUtils>,
28
- `gcTime` | `startSync`
29
- > {
27
+ BaseCollectionConfig<T, TKey, TSchema, LocalOnlyCollectionUtils>,
28
+ `gcTime` | `startSync`
29
+ > {
30
30
  /**
31
31
  * Optional initial data to populate the collection with on creation
32
32
  * This data will be applied during the initial sync process