@dcupl/common 2.0.0-beta.10 → 2.0.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +68 -47
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indices.controller.d.ts +6 -0
- package/dist/esm/scheduling.d.ts +17 -0
- package/dist/node/index.cjs +2 -2
- package/dist/node/index.cjs.map +1 -1
- package/package.json +1 -3
package/dist/node/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/object-hash.ts","../../src/performance.ts","../../src/analytics.controller.ts","../../src/dependency-graph.ts","../../src/attribute-dependency-graph.ts","../../src/logger/logger.ts","../../src/cache.controller.ts","../../src/helper.ts","../../src/changeDetection.ts","../../src/computeSuggestions.ts","../../src/date-utils.ts","../../src/deep-query.service.ts","../../src/getAggregation.ts","../../src/query-builder.ts","../../src/getFacets.ts","../../src/indices.controller.ts","../../src/pivot.ts","../../src/property-parser.ts","../../src/query.helper.ts","../../src/queryData.ts","../../src/resource-id.ts","../../src/script.controller.ts","../../src/template-parser.ts","../../src/types/model.types.ts","../../src/types/query.types.ts"],"sourcesContent":["export function generateObjectHash(input: any): string {\n const stringifiedInput = fastStringify(input);\n return simpleHash(stringifiedInput);\n}\n\nfunction fastStringify(input: any): string {\n if (typeof input !== 'object' || input === null) {\n return String(input);\n }\n\n if (Array.isArray(input)) {\n return '[' + input.map(fastStringify).join(',') + ']';\n }\n\n const keys = Object.keys(input).sort();\n let result = '{';\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i] as string;\n result += key + ':' + fastStringify(input[key]);\n if (i < keys.length - 1) {\n result += ',';\n }\n }\n\n result += '}';\n return result;\n}\n\nfunction simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash |= 0; // Convert to 32bit integer\n }\n return hash.toString(16);\n}\n","export const perf = {\n now: (): number => {\n if (typeof performance !== 'undefined' && performance.now) {\n return performance.now();\n } else {\n return new Date().getTime();\n }\n },\n\n mark: (name: string): void => {\n if (typeof performance !== 'undefined' && performance.mark) {\n performance.mark(name);\n }\n },\n measure: (name: string, start: string, end: string): void => {\n if (typeof performance !== 'undefined' && performance.measure) {\n performance.measure(name, start, end);\n }\n },\n getEntriesByName(url: string): PerformanceEntryList | undefined {\n if (typeof performance !== 'undefined' && performance.getEntriesByName) {\n return performance.getEntriesByName(url);\n }\n return undefined;\n },\n getEntriesByType(type: string): PerformanceEntryList | undefined {\n if (typeof performance !== 'undefined' && performance.getEntriesByType) {\n return performance.getEntriesByType(type);\n }\n return undefined;\n },\n};\n","import { DcuplUpdateType } from './changeDetection';\nimport { generateObjectHash } from './object-hash';\nimport { perf } from './performance';\n\nexport type AnalyticsEntryType = 'resource' | 'mark' | 'measure' | 'data' | 'sdk_action';\n\nexport type AnalyticsEntryBase<Value = any> = {\n name: string;\n type: AnalyticsEntryType;\n value: Value;\n createdAt: number;\n};\n\nexport type AnalyticsEntryResource = AnalyticsEntryBase<AnalyticsEntryResourceValue> & {\n type: 'resource';\n};\nexport type AnalyticsEntryMark = AnalyticsEntryBase<number> & {\n type: 'mark';\n};\n\nexport type AnalyticsEntryMeasure = AnalyticsEntryBase<number> & {\n type: 'measure';\n detail?: any;\n};\n\nexport type AnalyticsEntryData = AnalyticsEntryBase<any> & {\n type: 'data';\n};\n\nexport type AnalyticsEntrySdkAction = AnalyticsEntryBase<AnalyticsEntrySdkActionValue> & {\n type: 'sdk_action';\n};\n\nexport type AnalyticsEntry =\n | AnalyticsEntryResource\n | AnalyticsEntryMark\n | AnalyticsEntryMeasure\n | AnalyticsEntryData\n | AnalyticsEntrySdkAction;\n\nexport type AnalyticsEntrySdkActionValue = {\n duration: number;\n model?: string;\n args?: any;\n};\n\nexport type AnalyticsEntryResourceValue = {\n model: string;\n type: 'data' | 'model';\n start: number;\n startTime: number;\n requestStart: number;\n responseStart: number;\n responseEnd: number;\n end: number;\n duration: number;\n parseStart: number;\n parseEnd: number;\n rawTransformStart?: number;\n rawTransformEnd?: number;\n csvToJsonStart?: number;\n csvToJsonEnd?: number;\n parsedTransformStart?: number;\n parsedTransformEnd?: number;\n decodedBodySize: number;\n encodedBodySize: number;\n};\n\nexport class AnalyticsController {\n private data!: Map<string, AnalyticsEntry>;\n\n private enabled = false;\n\n constructor() {}\n\n public init(enabled: boolean): void {\n this.data = new Map();\n this.enabled = enabled;\n }\n\n public set(options: { value: AnalyticsEntry; context?: any }): AnalyticsEntry | undefined {\n if (!this.enabled) return options.value;\n\n const uniqueKey = this.getKey(options.value.name, options.context);\n\n this.data.set(uniqueKey, options.value);\n return this.data.get(uniqueKey);\n }\n\n public mark(options: { name: string; context?: any }): AnalyticsEntryMark | undefined {\n if (!this.enabled) return undefined;\n\n const uniqueKey = this.getKey(options.name, options.context);\n\n const entry: AnalyticsEntryMark = {\n name: options.name,\n type: 'mark',\n value: perf.now(),\n createdAt: Date.now(),\n };\n\n this.data.set(uniqueKey, entry);\n return entry;\n }\n\n public measure(options: {\n name: string;\n start: string;\n end: string;\n context?: any;\n detail?: any;\n sum?: boolean;\n }): AnalyticsEntryMeasure | undefined {\n if (!this.enabled) return undefined;\n\n const uniqueKey = this.getKey(options.name, options.context);\n\n const startMark = this.data.get(this.getKey(options.start, options.context));\n const endMark = this.data.get(this.getKey(options.end, options.context));\n\n if (!startMark || !endMark) {\n return undefined;\n }\n\n const duration = endMark.value - startMark.value;\n\n const roundedDuration = Math.round(duration * 100) / 100;\n\n const existingEntry = this.data.get(uniqueKey);\n\n if (existingEntry && options.sum) {\n const newDuration = existingEntry.value + roundedDuration;\n const newEntry: AnalyticsEntryMeasure = {\n ...(existingEntry as AnalyticsEntryMeasure),\n value: newDuration,\n };\n this.data.set(uniqueKey, newEntry);\n return newEntry;\n }\n\n const entry: AnalyticsEntryMeasure = {\n name: options.name,\n type: 'measure',\n value: roundedDuration,\n createdAt: Date.now(),\n };\n if (options.detail) {\n entry.detail = options.detail;\n }\n\n this.data.set(uniqueKey, entry);\n return entry;\n }\n\n public update(options: { value: AnalyticsEntry; context?: any }): AnalyticsEntry | undefined {\n if (!this.enabled) return options.value;\n\n const uniqueKey = this.getKey(options.value.name, options.context);\n\n const currentItem = this.data.get(uniqueKey);\n\n if (currentItem) {\n const newItem = { ...currentItem, ...options.value };\n this.data.set(uniqueKey, newItem);\n } else {\n this.data.set(uniqueKey, options.value);\n }\n return this.data.get(uniqueKey);\n }\n\n public getResourceInfosFromPerformanceAPI(): Array<PerformanceResourceTiming> {\n if (typeof performance === 'undefined') return [];\n const entries = perf.getEntriesByType('resource');\n return entries as Array<PerformanceResourceTiming>;\n }\n\n public get(options: { name: string; context?: any }): AnalyticsEntry | undefined {\n const uniqueKey = this.getKey(options.name, options.context);\n\n return this.data.get(uniqueKey);\n }\n\n public getByType(type: AnalyticsEntryType): Array<AnalyticsEntry> {\n return Array.from(this.data.values()).filter((entry) => entry.type === type);\n }\n\n public getByName(name: DcuplUpdateType): Array<AnalyticsEntry> {\n return Array.from(this.data.values()).filter((entry) => entry.name === name);\n }\n\n public clear(): void {\n this.data.clear();\n }\n\n public getKey(key: string, context: any): string {\n if (!context) {\n return key;\n }\n\n const hash = generateObjectHash(context);\n return key + '_' + hash;\n }\n}\n","export class DependencyGraph<K extends string = string, M = any> {\n private nodes: Set<string>;\n private edges: Map<string, Set<string>>;\n private nodeKinds: Map<string, K> = new Map();\n private nodeMeta: Map<string, M> = new Map();\n private edgePolicy?: (from: { name: string; kind?: K }, to: { name: string; kind?: K }) => boolean;\n\n constructor() {\n this.nodes = new Set();\n this.edges = new Map();\n }\n\n addNode(name: string, options?: { kind?: K; meta?: M }): void {\n if (!this.nodes.has(name)) {\n this.nodes.add(name);\n this.edges.set(name, new Set());\n }\n if (options?.kind !== undefined) {\n this.nodeKinds.set(name, options.kind);\n }\n if (options?.meta !== undefined) {\n this.nodeMeta.set(name, options.meta);\n }\n }\n\n addDependency(from: string, to: string): void {\n if (!this.nodes.has(from) || !this.nodes.has(to)) {\n throw new Error('Both nodes must exist before adding a dependency.');\n }\n if (this.edgePolicy) {\n const allow = this.edgePolicy(\n { name: from, kind: this.nodeKinds.get(from) },\n { name: to, kind: this.nodeKinds.get(to) },\n );\n if (!allow) {\n throw new Error(`Edge policy rejected dependency from \"${from}\" to \"${to}\".`);\n }\n }\n this.edges.get(from)!.add(to);\n }\n\n /** Configure an optional policy to validate edges before they are added */\n setEdgePolicy(policy?: (from: { name: string; kind?: K }, to: { name: string; kind?: K }) => boolean): void {\n this.edgePolicy = policy;\n }\n\n /** Assign or change a node's kind */\n setNodeKind(name: string, kind?: K): void {\n if (!this.nodes.has(name)) throw new Error('Node does not exist.');\n if (kind === undefined) {\n this.nodeKinds.delete(name);\n } else {\n this.nodeKinds.set(name, kind);\n }\n }\n\n /** Get a node's kind */\n getNodeKind(name: string): K | undefined {\n return this.nodeKinds.get(name);\n }\n\n /** Set arbitrary metadata for a node */\n setNodeMeta(name: string, meta: M): void {\n if (!this.nodes.has(name)) throw new Error('Node does not exist.');\n this.nodeMeta.set(name, meta);\n }\n\n /** Get metadata for a node */\n getNodeMeta<T = M>(name: string): T | undefined {\n return this.nodeMeta.get(name) as T | undefined;\n }\n\n removeNode(name: string): void {\n if (!this.nodes.has(name)) {\n throw new Error('Node does not exist.');\n }\n this.nodes.delete(name);\n this.edges.delete(name);\n\n for (let deps of this.edges.values()) {\n deps.delete(name);\n }\n }\n\n removeDependency(from: string, to: string): void {\n if (!this.edges.has(from)) {\n throw new Error('From node does not exist.');\n }\n this.edges.get(from)!.delete(to);\n }\n\n hasNode(name: string): boolean {\n return this.nodes.has(name);\n }\n\n hasDependency(from: string, to: string): boolean {\n if (!this.edges.has(from)) {\n return false;\n }\n return this.edges.get(from)!.has(to);\n }\n\n getDependencies(name: string): string[] {\n if (!this.edges.has(name)) {\n throw new Error('Node does not exist.');\n }\n return Array.from(this.edges.get(name)!);\n }\n\n getDependents(name: string): string[] {\n const dependents: string[] = [];\n for (let [node, deps] of this.edges) {\n if (deps.has(name)) {\n dependents.push(node);\n }\n }\n return dependents;\n }\n\n isCyclic(): boolean {\n const visited: Set<string> = new Set();\n const visiting: Set<string> = new Set();\n\n const visit = (node: string): boolean => {\n if (visited.has(node)) {\n return false;\n }\n if (visiting.has(node)) {\n return true;\n }\n\n visiting.add(node);\n const dependencies = this.edges.get(node);\n if (dependencies) {\n for (let dep of dependencies) {\n if (visit(dep)) {\n return true;\n }\n }\n }\n visiting.delete(node);\n visited.add(node);\n return false;\n };\n\n for (let node of this.nodes) {\n if (visit(node)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Validates that the graph has no cycles.\n * Returns an object with hasCycle boolean and the cycle path if found.\n * @returns {hasCycle: boolean, path?: string[]} - hasCycle is true if a cycle exists, path contains the cycle nodes\n */\n validateNoCycles(): { hasCycle: boolean; path?: string[] } {\n const visited: Set<string> = new Set();\n const visiting: Set<string> = new Set();\n const pathStack: string[] = [];\n\n const visit = (node: string): string[] | null => {\n if (visited.has(node)) {\n return null;\n }\n if (visiting.has(node)) {\n // Found a cycle - extract the cycle path\n const cycleStart = pathStack.indexOf(node);\n return pathStack.slice(cycleStart).concat([node]);\n }\n\n visiting.add(node);\n pathStack.push(node);\n\n const dependencies = this.edges.get(node);\n if (dependencies) {\n for (let dep of dependencies) {\n const cyclePath = visit(dep);\n if (cyclePath) {\n return cyclePath;\n }\n }\n }\n\n pathStack.pop();\n visiting.delete(node);\n visited.add(node);\n return null;\n };\n\n for (let node of this.nodes) {\n const cyclePath = visit(node);\n if (cyclePath) {\n return { hasCycle: true, path: cyclePath };\n }\n }\n\n return { hasCycle: false };\n }\n\n order(options?: { allowCycles?: boolean }): string[] {\n const visited: Set<string> = new Set();\n const visiting: Set<string> = new Set();\n const result: string[] = [];\n const allowCycles = options?.allowCycles ?? false;\n\n const visit = (node: string): void => {\n if (visited.has(node)) {\n return;\n }\n if (visiting.has(node)) {\n if (allowCycles) {\n // Skip circular dependency instead of throwing\n return;\n }\n throw new Error('Graph has a cycle.');\n }\n\n visiting.add(node);\n const dependencies = this.edges.get(node);\n if (dependencies) {\n for (let dep of dependencies) {\n visit(dep);\n }\n }\n visiting.delete(node);\n visited.add(node);\n result.push(node);\n };\n\n for (let node of this.nodes) {\n if (!visited.has(node)) {\n visit(node);\n }\n }\n\n return result;\n }\n\n clear(): void {\n this.nodes.clear();\n this.edges.clear();\n }\n\n getNodes(): string[] {\n return Array.from(this.nodes);\n }\n\n getAllEdges(): [string, string][] {\n const edges: [string, string][] = [];\n for (let [from, deps] of this.edges) {\n for (let to of deps) {\n edges.push([from, to]);\n }\n }\n return edges;\n }\n\n /**\n * Returns all nodes that have no dependants (i.e., no other node depends on them).\n * In graph terms, nodes with zero incoming edges.\n */\n getNodesWithNoDependants(): string[] {\n if (this.nodes.size === 0) {\n return [];\n }\n\n const nodesWithIncoming: Set<string> = new Set();\n for (const deps of this.edges.values()) {\n for (const to of deps) {\n nodesWithIncoming.add(to);\n }\n }\n\n const result: string[] = [];\n for (const node of this.nodes) {\n if (!nodesWithIncoming.has(node)) {\n result.push(node);\n }\n }\n\n return result;\n }\n\n /** Return nodes filtered by kind */\n getNodesByKind(kind: K): string[] {\n const result: string[] = [];\n for (const n of this.nodes) {\n if (this.nodeKinds.get(n) === kind) result.push(n);\n }\n return result;\n }\n\n /** Roots by kind (zero incoming) */\n getRootsByKind(kind?: K): string[] {\n const roots = new Set(this.getNodesWithNoDependants());\n const arr = Array.from(roots);\n if (!kind) return arr;\n return arr.filter((n) => this.nodeKinds.get(n) === kind);\n }\n\n /** Leaves by kind (zero outgoing) */\n getLeavesByKind(kind?: K): string[] {\n const res: string[] = [];\n for (const n of this.nodes) {\n const outgoing = this.edges.get(n);\n if (!outgoing || outgoing.size === 0) {\n if (!kind || this.nodeKinds.get(n) === kind) {\n res.push(n);\n }\n }\n }\n return res;\n }\n\n /** Filter dependencies of a node by kind */\n getDependenciesOfKind(name: string, kind: K): string[] {\n const deps = this.getDependencies(name);\n return deps.filter((n) => this.nodeKinds.get(n) === kind);\n }\n\n /** Filter dependents of a node by kind */\n getDependentsOfKind(name: string, kind: K): string[] {\n const deps = this.getDependents(name);\n return deps.filter((n) => this.nodeKinds.get(n) === kind);\n }\n\n /**\n * Create execution batches (topological levels). Throws if the graph has a cycle.\n * If filterKinds is provided, batches contain only nodes of these kinds, but readiness\n * is evaluated against the complete graph.\n */\n plan(options?: { filterKinds?: K[] }): { batches: string[][] } {\n // Build in-degree map\n const inDegree: Map<string, number> = new Map();\n for (const n of this.nodes) inDegree.set(n, 0);\n for (const deps of this.edges.values()) {\n for (const to of deps) inDegree.set(to, (inDegree.get(to) || 0) + 1);\n }\n\n // Kahn's algorithm for levels\n const levelBatches: string[][] = [];\n let queue: string[] = Array.from(this.nodes).filter((n) => (inDegree.get(n) || 0) === 0);\n\n const seen: Set<string> = new Set();\n while (queue.length > 0) {\n const nextLevel = queue.slice();\n levelBatches.push(nextLevel);\n queue = [];\n\n for (const n of nextLevel) {\n if (seen.has(n)) continue;\n seen.add(n);\n const outs = this.edges.get(n);\n if (!outs) continue;\n for (const to of outs) {\n inDegree.set(to, (inDegree.get(to) || 0) - 1);\n if ((inDegree.get(to) || 0) === 0) {\n queue.push(to);\n }\n }\n }\n }\n\n if (seen.size !== this.nodes.size) {\n // There is a cycle\n throw new Error('Graph has a cycle.');\n }\n\n if (options?.filterKinds && options.filterKinds.length > 0) {\n const set = new Set(options.filterKinds);\n return {\n batches: levelBatches.map((lvl) => lvl.filter((n) => set.has(this.nodeKinds.get(n) as K))),\n };\n }\n\n return { batches: levelBatches };\n }\n\n /**\n * Impact analysis: return nodes impacted by a set of changed nodes.\n * direction:\n * - 'downstream': nodes that depend (directly/indirectly) on changed\n * - 'upstream': nodes that changed depends on\n * - 'both': union of upstream and downstream\n */\n getImpactedNodes(\n changed: string[] | Set<string>,\n direction: 'downstream' | 'upstream' | 'both' = 'downstream',\n filterKinds?: K[],\n ): string[] {\n const changedSet = new Set(changed);\n\n const matchesKind = (n: string): boolean =>\n !filterKinds || filterKinds.length === 0 || filterKinds.includes((this.nodeKinds.get(n) as K) ?? (undefined as any));\n\n const traverseDown = (): Set<string> => {\n const impacted: Set<string> = new Set();\n const queue: string[] = Array.from(changedSet);\n while (queue.length) {\n const n = queue.shift()!;\n for (const [depender, deps] of this.edges) {\n if (deps.has(n) && !impacted.has(depender)) {\n impacted.add(depender);\n queue.push(depender);\n }\n }\n }\n return impacted;\n };\n\n const traverseUp = (): Set<string> => {\n const impacted: Set<string> = new Set();\n const queue: string[] = Array.from(changedSet);\n while (queue.length) {\n const n = queue.shift()!;\n const deps = this.edges.get(n);\n if (!deps) continue;\n for (const d of deps) {\n if (!impacted.has(d)) {\n impacted.add(d);\n queue.push(d);\n }\n }\n }\n return impacted;\n };\n\n let result: Set<string>;\n if (direction === 'both') {\n result = new Set([...traverseDown(), ...traverseUp()]);\n } else if (direction === 'upstream') {\n result = traverseUp();\n } else {\n result = traverseDown();\n }\n\n return Array.from(result).filter(matchesKind);\n }\n\n toJSON(): { nodes: Array<{ name: string; kind?: K; meta?: M }>; edges: Array<{ from: string; to: string }> } {\n const nodes = Array.from(this.nodes).map((name) => ({\n name,\n kind: this.nodeKinds.get(name),\n meta: this.nodeMeta.get(name),\n }));\n const edges = this.getAllEdges().map(([from, to]) => ({ from, to }));\n return { nodes, edges };\n }\n\n static fromJSON<KK extends string, MM = any>(data: { nodes: Array<{ name: string; kind?: KK; meta?: MM }>; edges: Array<{ from: string; to: string }> }): DependencyGraph<KK, MM> {\n const g = new DependencyGraph<KK, MM>();\n for (const n of data.nodes) {\n g.addNode(n.name, { kind: n.kind, meta: n.meta });\n }\n for (const e of data.edges) {\n g.addDependency(e.from, e.to);\n }\n return g;\n }\n\n diff(other: DependencyGraph<K, any>): {\n addedNodes: string[];\n removedNodes: string[];\n addedEdges: [string, string][];\n removedEdges: [string, string][];\n kindChanges: Array<{ name: string; from?: K; to?: K }>;\n } {\n const thisNodes = new Set(this.getNodes());\n const otherNodes = new Set(other.getNodes());\n\n const addedNodes = Array.from(otherNodes).filter((n) => !thisNodes.has(n));\n const removedNodes = Array.from(thisNodes).filter((n) => !otherNodes.has(n));\n\n const thisEdges = new Set(this.getAllEdges().map((e) => e.join('→')));\n const otherEdges = new Set(other.getAllEdges().map((e) => e.join('→')));\n\n const addedEdges = Array.from(otherEdges)\n .filter((e) => !thisEdges.has(e))\n .map((s) => s.split('→') as [string, string]);\n const removedEdges = Array.from(thisEdges)\n .filter((e) => !otherEdges.has(e))\n .map((s) => s.split('→') as [string, string]);\n\n const kindChanges: Array<{ name: string; from?: K; to?: K }> = [];\n for (const n of new Set([...thisNodes, ...otherNodes])) {\n const from = this.nodeKinds.get(n);\n const to = other.nodeKinds.get(n as string) as K | undefined;\n if (from !== to) kindChanges.push({ name: n, from, to });\n }\n\n return { addedNodes, removedNodes, addedEdges, removedEdges, kindChanges };\n }\n}\n","import { DependencyGraph } from './dependency-graph';\n\nexport type AttributeNode = {\n modelKey: string;\n attributeKey: string;\n type: 'property' | 'reference';\n processingStep: 1 | 2 | 3 | 4; // Maps to current 4-step pipeline\n};\n\nexport type AttributeNodeId = string; // Format: \"modelKey.attributeKey\"\n\nexport type ProcessingPlan = Map<string, Map<number, Set<string>>>;\n// Structure: Map<modelKey, Map<processingStep, Set<attributeKeys>>>\n\n/**\n * AttributeDependencyGraph tracks attribute-level dependencies between models.\n *\n * This graph is item-agnostic: when ModelA.price depends on ModelB.cost,\n * any change to ModelB.cost triggers reprocessing of ALL ModelA items\n * that have the price derivation.\n *\n * The graph ensures:\n * - Correct processing order based on dependencies\n * - Impact analysis when attributes change\n * - Efficient batch processing by grouping attributes by model and step\n */\nexport class AttributeDependencyGraph {\n private graph: DependencyGraph<string, AttributeNode>;\n private nodeIndex: Map<AttributeNodeId, AttributeNode>;\n\n constructor() {\n this.graph = new DependencyGraph<string, AttributeNode>();\n this.nodeIndex = new Map();\n }\n\n /**\n * Creates a unique ID for an attribute node\n */\n private getNodeId(modelKey: string, attributeKey: string): AttributeNodeId {\n return `${modelKey}.${attributeKey}`;\n }\n\n /**\n * Parses a node ID back into components\n */\n private parseNodeId(nodeId: AttributeNodeId): { modelKey: string; attributeKey: string } {\n const parts = nodeId.split('.');\n return {\n modelKey: parts[0]!,\n attributeKey: parts.slice(1).join('.'), // Handle attribute keys with dots\n };\n }\n\n /**\n * Adds an attribute node to the graph\n */\n public addAttribute(node: AttributeNode): void {\n const nodeId = this.getNodeId(node.modelKey, node.attributeKey);\n\n if (!this.nodeIndex.has(nodeId)) {\n this.graph.addNode(nodeId, { meta: node });\n this.nodeIndex.set(nodeId, node);\n }\n }\n\n /**\n * Adds a dependency between two attributes\n * @param from - The dependent attribute (e.g., ModelA.derivedPrice)\n * @param to - The source attribute it depends on (e.g., ModelB.baseCost)\n */\n public addAttributeDependency(from: AttributeNode, to: AttributeNode): void {\n // Ensure both nodes exist\n this.addAttribute(from);\n this.addAttribute(to);\n\n const fromId = this.getNodeId(from.modelKey, from.attributeKey);\n const toId = this.getNodeId(to.modelKey, to.attributeKey);\n\n // Add dependency: from depends on to\n if (!this.graph.hasDependency(fromId, toId)) {\n this.graph.addDependency(fromId, toId);\n }\n }\n\n /**\n * Gets all attributes impacted by changes to the given attributes\n * @param changed - Array of changed attribute nodes\n * @returns Array of all impacted attribute nodes (including the changed ones)\n */\n public getImpactedAttributes(changed: AttributeNode[]): AttributeNode[] {\n const changedIds = changed.map((node) => this.getNodeId(node.modelKey, node.attributeKey));\n\n // Get all downstream dependents (attributes that depend on the changed ones)\n const impactedIds = this.graph.getImpactedNodes(changedIds, 'downstream');\n\n // Convert IDs back to AttributeNode objects\n const impactedNodes = impactedIds\n .map((id) => this.nodeIndex.get(id))\n .filter((node): node is AttributeNode => node !== undefined);\n\n // Include the originally changed attributes\n const allImpacted = [...changed, ...impactedNodes];\n\n // Deduplicate\n const seen = new Set<AttributeNodeId>();\n return allImpacted.filter((node) => {\n const id = this.getNodeId(node.modelKey, node.attributeKey);\n if (seen.has(id)) return false;\n seen.add(id);\n return true;\n });\n }\n\n /**\n * Generates a processing plan for the given attributes\n * Groups attributes by model and processing step for efficient batch processing\n *\n * @param attributes - Array of attributes to process\n * @returns Map<modelKey, Map<processingStep, Set<attributeKeys>>>\n */\n public getProcessingOrder(attributes: AttributeNode[]): ProcessingPlan {\n const plan: ProcessingPlan = new Map();\n\n // Sort attributes by processing step (1, 2, 3, 4)\n const sortedAttributes = [...attributes].sort((a, b) => a.processingStep - b.processingStep);\n\n for (const node of sortedAttributes) {\n const { modelKey, attributeKey, processingStep } = node;\n\n // Get or create model entry\n if (!plan.has(modelKey)) {\n plan.set(modelKey, new Map());\n }\n const modelPlan = plan.get(modelKey)!;\n\n // Get or create step entry\n if (!modelPlan.has(processingStep)) {\n modelPlan.set(processingStep, new Set());\n }\n const stepAttributes = modelPlan.get(processingStep)!;\n\n // Add attribute to this step\n stepAttributes.add(attributeKey);\n }\n\n return plan;\n }\n\n /**\n * Gets all attributes for a specific model\n */\n public getAttributesForModel(modelKey: string): AttributeNode[] {\n const nodes: AttributeNode[] = [];\n\n for (const node of this.nodeIndex.values()) {\n if (node.modelKey === modelKey) {\n nodes.push(node);\n }\n }\n\n return nodes;\n }\n\n /**\n * Gets dependencies for a specific attribute\n */\n public getDependencies(node: AttributeNode): AttributeNode[] {\n const nodeId = this.getNodeId(node.modelKey, node.attributeKey);\n\n if (!this.graph.hasNode(nodeId)) {\n return [];\n }\n\n const depIds = this.graph.getDependencies(nodeId);\n return depIds\n .map((id) => this.nodeIndex.get(id))\n .filter((node): node is AttributeNode => node !== undefined);\n }\n\n /**\n * Gets dependents for a specific attribute (attributes that depend on this one)\n */\n public getDependents(node: AttributeNode): AttributeNode[] {\n const nodeId = this.getNodeId(node.modelKey, node.attributeKey);\n\n if (!this.graph.hasNode(nodeId)) {\n return [];\n }\n\n const depIds = this.graph.getDependents(nodeId);\n return depIds\n .map((id) => this.nodeIndex.get(id))\n .filter((node): node is AttributeNode => node !== undefined);\n }\n\n /**\n * Checks if an attribute has any dependencies\n */\n public hasDependencies(node: AttributeNode): boolean {\n const nodeId = this.getNodeId(node.modelKey, node.attributeKey);\n if (!this.graph.hasNode(nodeId)) return false;\n return this.graph.getDependencies(nodeId).length > 0;\n }\n\n /**\n * Checks if a node exists in the graph\n */\n public hasNode(nodeId: AttributeNodeId): boolean {\n return this.nodeIndex.has(nodeId);\n }\n\n /**\n * Gets all attribute nodes in the graph\n */\n public getAllAttributes(): AttributeNode[] {\n return Array.from(this.nodeIndex.values());\n }\n\n /**\n * Gets the maximum dependency depth in the graph\n * Useful for determining if full update might be more efficient\n */\n public getMaxDepth(): number {\n let maxDepth = 0;\n\n const calculateDepth = (nodeId: string, visited: Set<string> = new Set()): number => {\n if (visited.has(nodeId)) return 0; // Circular dependency\n visited.add(nodeId);\n\n const deps = this.graph.getDependencies(nodeId);\n if (deps.length === 0) return 1;\n\n let maxChildDepth = 0;\n for (const depId of deps) {\n const depth = calculateDepth(depId, new Set(visited));\n maxChildDepth = Math.max(maxChildDepth, depth);\n }\n\n return maxChildDepth + 1;\n };\n\n for (const nodeId of this.graph.getNodes()) {\n const depth = calculateDepth(nodeId);\n maxDepth = Math.max(maxDepth, depth);\n }\n\n return maxDepth;\n }\n\n /**\n * Validates that all expected attributes are present in the graph.\n * This helps catch silent failures during graph building where attributes\n * with expressions or derivations might not be properly added.\n *\n * @param expectedAttributes - Map of modelKey to array of attribute keys that should exist\n * @returns Object with validation result and any missing attributes\n */\n public validateCompleteness(expectedAttributes: Map<string, string[]>): {\n isComplete: boolean;\n missing: Array<{ modelKey: string; attributeKey: string }>;\n } {\n const missing: Array<{ modelKey: string; attributeKey: string }> = [];\n\n for (const [modelKey, attributeKeys] of expectedAttributes) {\n for (const attributeKey of attributeKeys) {\n const nodeId = this.getNodeId(modelKey, attributeKey);\n if (!this.hasNode(nodeId)) {\n missing.push({ modelKey, attributeKey });\n }\n }\n }\n\n return {\n isComplete: missing.length === 0,\n missing,\n };\n }\n\n /**\n * Clears the entire graph\n */\n public clear(): void {\n this.graph.clear();\n this.nodeIndex.clear();\n }\n\n /**\n * Gets statistics about the graph\n */\n public getStats(): {\n totalAttributes: number;\n totalDependencies: number;\n maxDepth: number;\n attributesByModel: Map<string, number>;\n attributesByStep: Map<number, number>;\n } {\n const attributesByModel = new Map<string, number>();\n const attributesByStep = new Map<number, number>();\n\n for (const node of this.nodeIndex.values()) {\n // Count by model\n attributesByModel.set(node.modelKey, (attributesByModel.get(node.modelKey) || 0) + 1);\n\n // Count by step\n attributesByStep.set(node.processingStep, (attributesByStep.get(node.processingStep) || 0) + 1);\n }\n\n return {\n totalAttributes: this.nodeIndex.size,\n totalDependencies: this.graph.getAllEdges().length,\n maxDepth: this.getMaxDepth(),\n attributesByModel,\n attributesByStep,\n };\n }\n\n /**\n * Exports the graph to JSON for debugging\n */\n public toJSON(): any {\n return {\n nodes: Array.from(this.nodeIndex.values()),\n edges: this.graph.getAllEdges().map(([from, to]) => {\n const fromNode = this.parseNodeId(from);\n const toNode = this.parseNodeId(to);\n return {\n from: fromNode,\n to: toNode,\n };\n }),\n };\n }\n}\n","export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';\n\nexport interface LoggerConfig {\n /** Minimum log level to output */\n level?: LogLevel;\n /** Output format: 'json' for production/cloud, 'pretty' for development */\n format?: 'json' | 'pretty';\n /** Optional prefix for all log messages (useful for module identification) */\n prefix?: string;\n /** Custom log handler (useful for sending logs to external services) */\n onLog?: (entry: LogEntry) => void;\n /** Handler for tracked errors (integrates with QualityController) */\n onTrackError?: (entry: TrackedErrorEntry) => void;\n}\n\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: number;\n prefix?: string;\n context?: Record<string, any>;\n error?: Error;\n}\n\nexport interface TrackedErrorEntry extends LogEntry {\n level: 'error';\n errorType?: string;\n errorGroup?: string;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nexport class Logger {\n private config: Required<Omit<LoggerConfig, 'onLog' | 'onTrackError' | 'prefix'>> & {\n prefix?: string;\n onLog?: (entry: LogEntry) => void;\n onTrackError?: (entry: TrackedErrorEntry) => void;\n };\n\n private static globalConfig: Partial<LoggerConfig> = {};\n\n constructor(config: LoggerConfig = {}) {\n this.config = {\n level: config.level ?? Logger.globalConfig.level ?? this.getDefaultLevel(),\n format: config.format ?? Logger.globalConfig.format ?? this.getDefaultFormat(),\n prefix: config.prefix,\n onLog: config.onLog ?? Logger.globalConfig.onLog,\n onTrackError: config.onTrackError ?? Logger.globalConfig.onTrackError,\n };\n }\n\n /**\n * Set global logger configuration (affects all logger instances)\n */\n public static setGlobalConfig(config: Partial<LoggerConfig>): void {\n Logger.globalConfig = { ...Logger.globalConfig, ...config };\n }\n\n /**\n * Get the global logger configuration\n */\n public static getGlobalConfig(): Partial<LoggerConfig> {\n return { ...Logger.globalConfig };\n }\n\n /**\n * Configure module-specific log levels\n * @example\n * logger.setModuleLevel('ModelParser', 'debug');\n * logger.setModuleLevel('Catalog', 'warn');\n */\n public static setModuleLevel(module: string, level: LogLevel): void {\n if (!Logger.globalConfig.onLog) {\n Logger.globalConfig.onLog = undefined; // Initialize if needed\n }\n // Store in a static registry that will be checked by child loggers\n if (!(Logger as any)._moduleOverrides) {\n (Logger as any)._moduleOverrides = new Map<string, LogLevel>();\n }\n (Logger as any)._moduleOverrides.set(module, level);\n }\n\n /**\n * Get module-specific log level\n */\n private static getModuleLevel(module?: string): LogLevel | undefined {\n if (!module || !(Logger as any)._moduleOverrides) {\n return undefined;\n }\n return (Logger as any)._moduleOverrides.get(module);\n }\n\n private getDefaultLevel(): LogLevel {\n return 'info';\n }\n\n private getDefaultFormat(): 'json' | 'pretty' {\n return 'pretty';\n }\n\n\n private getEffectiveLevel(): LogLevel {\n // Check for module override first\n if (this.config.prefix) {\n const moduleLevel = Logger.getModuleLevel(this.config.prefix);\n if (moduleLevel) {\n return moduleLevel;\n }\n }\n return this.config.level;\n }\n\n private shouldLog(level: LogLevel): boolean {\n const effectiveLevel = this.getEffectiveLevel();\n return LOG_LEVELS[level] <= LOG_LEVELS[effectiveLevel];\n }\n\n private formatMessage(entry: LogEntry): string {\n if (this.config.format === 'json') {\n const jsonEntry: any = {\n timestamp: new Date(entry.timestamp).toISOString(),\n level: entry.level,\n message: entry.message,\n };\n\n if (entry.prefix) {\n jsonEntry.module = entry.prefix;\n }\n\n if (entry.context) {\n jsonEntry.context = entry.context;\n }\n\n if (entry.error) {\n jsonEntry.error = {\n name: entry.error.name,\n message: entry.error.message,\n stack: entry.error.stack,\n };\n }\n\n return JSON.stringify(jsonEntry);\n }\n\n // Pretty format for development\n const timestamp = new Date(entry.timestamp).toISOString().substring(11, 23); // HH:mm:ss.SSS\n const prefix = entry.prefix ? `[${entry.prefix}]` : '';\n const levelIcon = this.getLevelIcon(entry.level);\n const contextStr = entry.context ? ` ${JSON.stringify(entry.context)}` : '';\n\n return `${timestamp} ${levelIcon} ${prefix} ${entry.message}${contextStr}`.trim();\n }\n\n private getLevelIcon(level: LogLevel): string {\n switch (level) {\n case 'error':\n return '✖';\n case 'warn':\n return '⚠';\n case 'info':\n return 'ℹ';\n case 'debug':\n return '⚙';\n default:\n return ' ';\n }\n }\n\n private log(level: LogLevel, message: string, context?: Record<string, any>, error?: Error): void {\n // Fast path: check level first to minimize overhead\n if (!this.shouldLog(level)) {\n return;\n }\n\n const entry: LogEntry = {\n level,\n message,\n timestamp: Date.now(),\n prefix: this.config.prefix,\n context,\n error,\n };\n\n // Custom handler takes precedence\n if (this.config.onLog) {\n this.config.onLog(entry);\n return;\n }\n\n const formatted = this.formatMessage(entry);\n\n // Use appropriate console method\n if (typeof console !== 'undefined') {\n switch (level) {\n case 'error':\n if (error) {\n console.error(formatted, error);\n } else {\n console.error(formatted);\n }\n break;\n case 'warn':\n console.warn(formatted);\n break;\n case 'debug':\n if (console.debug) {\n console.debug(formatted);\n } else {\n console.log(formatted);\n }\n break;\n default:\n console.log(formatted);\n }\n }\n }\n\n /**\n * Log an error message\n */\n public error(message: string, errorOrContext?: Error | Record<string, any>, context?: Record<string, any>): void {\n const error = errorOrContext instanceof Error ? errorOrContext : undefined;\n const ctx = errorOrContext instanceof Error ? context : errorOrContext;\n this.log('error', message, ctx, error);\n }\n\n /**\n * Log and track an error (integrates with QualityController)\n * @param message Error message\n * @param error Error object\n * @param options Additional context and error classification\n */\n public trackError(\n message: string,\n error?: Error,\n options?: {\n context?: Record<string, any>;\n errorType?: string;\n errorGroup?: string;\n },\n ): void {\n // Log the error normally\n this.error(message, error, options?.context);\n\n // If tracking handler is registered, invoke it\n if (this.config.onTrackError) {\n const entry: TrackedErrorEntry = {\n level: 'error',\n message,\n timestamp: Date.now(),\n prefix: this.config.prefix,\n context: options?.context,\n error,\n errorType: options?.errorType,\n errorGroup: options?.errorGroup,\n };\n this.config.onTrackError(entry);\n }\n }\n\n /**\n * Log a warning message\n */\n public warn(message: string, context?: Record<string, any>): void {\n this.log('warn', message, context);\n }\n\n /**\n * Log an info message\n */\n public info(message: string, context?: Record<string, any>): void {\n this.log('info', message, context);\n }\n\n /**\n * Log a debug message\n */\n public debug(message: string, context?: Record<string, any>): void {\n this.log('debug', message, context);\n }\n\n /**\n * Create a child logger with additional context or configuration\n * Child loggers inherit parent config but can override specific settings\n *\n * @example\n * const modelLogger = logger.child({ prefix: 'ModelParser' });\n * modelLogger.info('Processing data'); // [ModelParser] Processing data\n *\n * @example\n * const debugLogger = logger.child({ prefix: 'Catalog', level: 'debug' });\n */\n public child(childConfig: Partial<LoggerConfig>): Logger {\n const mergedConfig: LoggerConfig = {\n ...this.config,\n ...childConfig,\n // Handle prefix concatenation for nested children\n prefix: childConfig.prefix\n ? this.config.prefix\n ? `${this.config.prefix}:${childConfig.prefix}`\n : childConfig.prefix\n : this.config.prefix,\n };\n\n return new Logger(mergedConfig);\n }\n\n /**\n * Update this logger's configuration\n */\n public configure(config: Partial<LoggerConfig>): void {\n Object.assign(this.config, config);\n }\n}\n\n// Export default global logger instance\nexport const logger = new Logger();\n","import { cloneDeep } from 'lodash-es';\nimport { generateObjectHash } from './object-hash';\nimport { logger } from './logger';\n\nexport class CacheController {\n private cache!: Map<string, any>;\n // Track which models and attributes each cache entry depends on\n private cacheDependencies!: Map<string, CacheDependencyInfo>;\n // Track cache hits and misses for statistics\n private hitCount: number = 0;\n private missCount: number = 0;\n private log = logger.child({ prefix: 'CacheController' });\n\n constructor(public enabled: boolean) {\n this.cache = new Map();\n this.cacheDependencies = new Map();\n }\n\n public get(key: string, context: any): any {\n if (!this.enabled) return undefined;\n const cacheName = this.getCacheName(key, context);\n const hasValue = this.cache.has(cacheName);\n\n if (hasValue) {\n this.hitCount++;\n } else {\n this.missCount++;\n }\n\n return this.cache.get(cacheName);\n }\n\n public set(key: string, context: any, value: any, dependencies?: CacheDependencyInfo): void {\n if (!this.enabled) return undefined;\n const cacheName = this.getCacheName(key, context);\n this.cache.set(cacheName, value);\n\n // Store dependency information if provided\n if (dependencies) {\n this.cacheDependencies.set(cacheName, dependencies);\n } else {\n // Warn about missing dependencies in debug mode\n this.log.debug(\n `Cache entry '${key}' stored without dependencies. ` +\n `This prevents granular invalidation and may cause stale data.`,\n );\n }\n }\n\n public has(key: string, context: any): boolean {\n return this.cache.has(this.getCacheName(key, context));\n }\n\n public clear(): void {\n this.cache.clear();\n this.cacheDependencies.clear();\n this.hitCount = 0;\n this.missCount = 0;\n }\n\n /**\n * Invalidates cache entries that depend on the specified models and attributes\n * @param changedModels - Map of modelKey → Set of changed attributeKeys\n */\n public invalidateByDependencies(changedModels: Map<string, Set<string>>): void {\n if (!this.enabled) return;\n\n const entriesToRemove: string[] = [];\n\n for (const [cacheName, deps] of this.cacheDependencies) {\n let shouldInvalidate = false;\n\n // Check if any of the changed models/attributes affect this cache entry\n for (const [modelKey, attributeKeys] of changedModels) {\n if (deps.models.has(modelKey)) {\n // If we track specific attributes for this model\n if (deps.modelAttributes.has(modelKey)) {\n const trackedAttrs = deps.modelAttributes.get(modelKey)!;\n // Check if any changed attribute is tracked\n for (const attr of attributeKeys) {\n if (trackedAttrs.has(attr)) {\n shouldInvalidate = true;\n break;\n }\n }\n } else {\n // No specific attributes tracked, so any change to this model invalidates\n shouldInvalidate = true;\n }\n }\n\n if (shouldInvalidate) break;\n }\n\n if (shouldInvalidate) {\n entriesToRemove.push(cacheName);\n }\n }\n\n // Remove invalidated cache entries\n for (const cacheName of entriesToRemove) {\n this.cache.delete(cacheName);\n this.cacheDependencies.delete(cacheName);\n }\n }\n\n /**\n * Invalidates all cache entries for specific models\n * @param modelKeys - Array of model keys to invalidate\n */\n public invalidateByModels(modelKeys: string[]): void {\n if (!this.enabled) return;\n\n const modelSet = new Set(modelKeys);\n const entriesToRemove: string[] = [];\n\n for (const [cacheName, deps] of this.cacheDependencies) {\n // Check if this cache entry depends on any of the specified models\n for (const modelKey of modelSet) {\n if (deps.models.has(modelKey)) {\n entriesToRemove.push(cacheName);\n break;\n }\n }\n }\n\n // Remove invalidated cache entries\n for (const cacheName of entriesToRemove) {\n this.cache.delete(cacheName);\n this.cacheDependencies.delete(cacheName);\n }\n }\n\n /**\n * Invalidates cache entries by key prefix (e.g., 'query', 'facets')\n * @param keyPrefix - The key prefix to match\n */\n public invalidateByKeyPrefix(keyPrefix: string): void {\n if (!this.enabled) return;\n\n const entriesToRemove: string[] = [];\n\n for (const cacheName of this.cache.keys()) {\n if (cacheName.startsWith(keyPrefix + '_')) {\n entriesToRemove.push(cacheName);\n }\n }\n\n for (const cacheName of entriesToRemove) {\n this.cache.delete(cacheName);\n this.cacheDependencies.delete(cacheName);\n }\n }\n\n /**\n * Invalidates all cache entries for a specific model\n * @param modelKey - The model key to invalidate\n */\n public invalidateForModel(modelKey: string): void {\n this.invalidateByModels([modelKey]);\n }\n\n /**\n * Invalidates cache entries that depend on a specific attribute of a model\n * @param modelKey - The model key\n * @param attributeKey - The attribute key to invalidate\n */\n public invalidateForAttribute(modelKey: string, attributeKey: string): void {\n if (!this.enabled) return;\n\n const changedModels = new Map<string, Set<string>>();\n changedModels.set(modelKey, new Set([attributeKey]));\n this.invalidateByDependencies(changedModels);\n }\n\n /**\n * Invalidates cache entries matching a custom predicate\n * @param predicate - Function that returns true for cache keys to invalidate\n */\n public invalidateMatching(predicate: (key: string) => boolean): void {\n if (!this.enabled) return;\n\n const entriesToRemove: string[] = [];\n\n for (const cacheName of this.cache.keys()) {\n if (predicate(cacheName)) {\n entriesToRemove.push(cacheName);\n }\n }\n\n for (const cacheName of entriesToRemove) {\n this.cache.delete(cacheName);\n this.cacheDependencies.delete(cacheName);\n }\n }\n\n public generateContext(constext: any): any {\n try {\n return cloneDeep(constext);\n } catch (err) {\n this.log.error('Error in cache generateContext', err as Error);\n return constext;\n }\n }\n\n public getCacheName(key: string, context: any): string {\n const hash = generateObjectHash(context);\n return key + '_' + hash;\n }\n\n /**\n * Gets cache statistics including hit rate\n */\n public getStats(): {\n size: number;\n hitRate: number;\n totalEntries: number;\n entriesWithDependencies: number;\n hits: number;\n misses: number;\n } {\n const totalRequests = this.hitCount + this.missCount;\n const hitRate = totalRequests > 0 ? this.hitCount / totalRequests : 0;\n\n return {\n size: this.cache.size,\n hitRate: hitRate,\n totalEntries: this.cache.size,\n entriesWithDependencies: this.cacheDependencies.size,\n hits: this.hitCount,\n misses: this.missCount,\n };\n }\n}\n\n/**\n * Information about which models and attributes a cache entry depends on\n */\nexport interface CacheDependencyInfo {\n /** Set of model keys this cache entry depends on */\n models: Set<string>;\n /** Map of modelKey → Set of attribute keys (if specific attributes matter) */\n modelAttributes: Map<string, Set<string>>;\n}\n","import { DateFilterOptions, NumberFilterOptions } from './types';\n\nexport const getFilterEntryId = (filterItemId: string, value = ''): string => {\n return `${filterItemId}#${value}`;\n};\n\nexport const getNumberConstraintUid = (options: NumberFilterOptions): string => {\n const value = `${options.gt}_${options.gte}_${options.lt}_${options.lte}_${options.eq}_${options.ne}`;\n return value;\n};\n\nexport const getDateConstraintUid = (options: DateFilterOptions): string => {\n const value = `${options.gt}_${options.gte}_${options.lt}_${options.lte}_${options.eq}_${options.ne}`;\n return value;\n};\n\nexport const uniqBy = (arr: any[], predicate: string): any[] => {\n const cb = typeof predicate === 'function' ? predicate : (o: any): any => o[predicate];\n\n return [\n ...arr\n .reduce((map, item) => {\n const key = item === null || item === undefined ? item : cb(item);\n\n if (!map.has(key)) {\n map.set(key, item);\n }\n\n return map;\n }, new Map())\n .values(),\n ];\n};\n\nexport const groupBy = (arr: any[], criteria: any): any => {\n return arr.reduce(function (obj, item) {\n // Check if the criteria is a function to run on the item or a property of it\n const key = typeof criteria === 'function' ? criteria(item) : item[criteria];\n\n // If the key doesn't exist yet, create it\n if (!Object.prototype.hasOwnProperty.call(obj, key)) {\n obj[key] = [];\n }\n\n // Push the value to the object\n obj[key].push(item);\n\n // Return the object to the next item in the loop\n return obj;\n }, {});\n};\n\nexport function generateKey(length = 10): string {\n let text = '';\n const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\n for (let i = 0; i < length; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n return text;\n}\n\nexport const removeTrailingSlash = (str: string): string => {\n return str.replace(/\\/+$/, '');\n};\n\nexport const escapeRegExp = (text: string): string => {\n if (typeof text !== 'string') return '';\n\n return text.replace(/[[\\]{}()*+?.,\\\\^$|#]/g, '\\\\$&');\n /**\n * original regex\n * remove \\s -\n */\n // return text.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&');\n};\n\nexport const unescapeRegExp = (escapedText: string): string => {\n if (typeof escapedText !== 'string') return '';\n\n return escapedText.replace(/\\\\([[\\]{}()*+?.,\\\\^$|#])/g, '$1');\n};\n\nexport const hasMinimumVersion = (version: string, minVersion: string): boolean => {\n try {\n return compareVersions(version, minVersion) >= 0;\n } catch (_err) {\n return false;\n }\n};\n\n/**\n * Compares two semantic version strings.\n * @param v1 - First version string (e.g., \"1.10.2\").\n * @param v2 - Second version string (e.g., \"1.9.0\").\n * @returns A number: -1 if v1 < v2, 1 if v1 > v2, 0 if equal.\n * @throws Error if the input is not a valid semantic version.\n */\nexport const compareVersions = (v1: string, v2: string): number => {\n if (!isValidVersion(v1) || !isValidVersion(v2)) {\n throw new Error(`Invalid version format: v1=\"${v1}\", v2=\"${v2}\". Expected format: \"x.y.z\"`);\n }\n\n const v1Parts = v1.split('.').map(Number);\n const v2Parts = v2.split('.').map(Number);\n\n for (let i = 0; i < 3; i++) {\n const num1 = v1Parts[i] || 0; // Default to 0 if undefined (e.g., \"1.2\" treated as \"1.2.0\")\n const num2 = v2Parts[i] || 0;\n\n if (num1 > num2) return 1;\n if (num1 < num2) return -1;\n }\n\n return 0;\n};\n\n/**\n * Validates if a string follows semantic versioning (x.y.z).\n * @param version - Version string to validate.\n * @returns True if valid, otherwise false.\n */\nexport const isValidVersion = (version: string): boolean => {\n return /^\\d+(\\.\\d+){0,2}$/.test(version);\n};\n","import { cloneDeep } from 'lodash-es';\n\nimport { perf } from './performance';\nimport { CallbackFunction, DcuplInitOptions } from './types';\nimport { AnalyticsController, AnalyticsEntrySdkActionValue } from './analytics.controller';\nimport { generateKey } from './helper';\n\nexport type DcuplUpdateScope = 'all' | 'global' | 'list' | 'list$';\nexport type DcuplUpdateTrigger = 'user' | 'loader';\nexport type DcuplUpdateType =\n | 'data_change'\n | 'dcupl_destroyed'\n | 'dcupl_handle_datacontainer'\n | 'dcupl_initialized'\n | 'dcupl_updated_manually'\n | 'dcupl_partial_update'\n | 'filter_many'\n | 'filter_one'\n | 'fn_aggregate'\n | 'fn_facets'\n | 'fn_groupBy'\n | 'fn_metadata'\n | 'fn_pivot'\n | 'fn_suggest'\n | 'list_created'\n | 'list_destroyed'\n | 'list_updated'\n | 'loader_added'\n | 'loader_config_fetched'\n | 'loader_data_fetched'\n | 'loader_models_fetched'\n | 'loader_scripts_fetched'\n | 'loader_processed'\n | 'loader_removed'\n | 'model_add'\n | 'query_apply_options'\n | 'query_apply'\n | 'query_execute'\n | 'query_get'\n | 'query_has'\n | 'query_many'\n | 'query_one'\n | 'query_remove'\n | 'query_reset'\n | 'result_updated'\n | 'view_add';\n\nexport type DcuplUpdateAction = 'update';\n\nexport type DcuplUpdateMessage = {\n key?: number;\n scope: DcuplUpdateScope;\n type: DcuplUpdateType;\n method?: string;\n args?: any;\n action?: DcuplUpdateAction;\n // name?: string;\n origin?: DcuplUpdateTrigger;\n start?: number;\n end?: number;\n duration?: number;\n originCdRefKey?: string;\n modelKey?: string;\n listKey?: string;\n result?: any;\n cloneResult?: boolean;\n connectMessage?: boolean;\n connectMessageResult?: boolean;\n};\n\nexport class ChangeDetector {\n public key!: string;\n protected parentCd: ChangeDetector | undefined;\n public dcuplInitOptions!: DcuplInitOptions;\n public analyticsController!: AnalyticsController;\n private off!: () => void;\n\n private updateListener: Set<CallbackFunction> = new Set();\n\n // only the \"root\" counter will be increased and used in the events\n private counter = 0;\n\n constructor() {}\n\n public _init(options: {\n cdRefKey: string;\n dcuplInitOptions: DcuplInitOptions;\n parentCd?: ChangeDetector;\n analyticsController: AnalyticsController;\n }): void {\n this.key = options.cdRefKey;\n this.analyticsController = options.analyticsController;\n\n this.parentCd = options.parentCd;\n\n this.dcuplInitOptions = options.dcuplInitOptions;\n\n if (this.parentCd) {\n this.off = this.parentCd.on((msg: DcuplUpdateMessage) => {\n if (msg.action === 'update' && msg.originCdRefKey === this.parentCd?.key) {\n // prevent connect message relay if the message is coming from the parent\n msg.connectMessage = false;\n this.trigger(msg);\n }\n });\n }\n }\n\n public getCounter(): number {\n if (this.parentCd) {\n return this.parentCd.getCounter();\n }\n return this.counter;\n }\n\n public increaseCounter(): void {\n if (this.parentCd) {\n this.parentCd.increaseCounter();\n } else {\n this.counter++;\n }\n }\n\n public destroy(): void {\n if (this.off) {\n this.off();\n }\n }\n\n public trigger(msg: DcuplUpdateMessage): void {\n if (!msg.originCdRefKey) {\n msg.originCdRefKey = this.key;\n }\n\n if (!msg.key) {\n this.increaseCounter();\n msg.key = this.getCounter();\n }\n\n if (!msg.end) {\n msg.end = perf.now();\n msg.duration = msg.start! - msg.end;\n }\n\n this.updateListener.forEach((cb) => {\n cb(msg);\n });\n }\n\n public on(cb: CallbackFunction): () => void {\n this.updateListener.add(cb);\n return () => {\n this.updateListener.delete(cb);\n };\n }\n}\n\nexport const clone = (obj: any, shouldClone: boolean): any => {\n if (!shouldClone) {\n return obj;\n }\n if (typeof structuredClone !== 'undefined') {\n return structuredClone(obj);\n } else {\n return cloneDeep(obj);\n }\n};\n\nconst finish = (\n targetClass: any,\n cdRef: ChangeDetector,\n msg: DcuplUpdateMessage,\n start: number,\n context: any,\n result: any,\n): any => {\n try {\n const shouldClone = !!cdRef.dcuplInitOptions.performance?.clone?.enabled && !!msg.cloneResult;\n\n const valueToReturn = clone(result, shouldClone);\n\n if (msg.scope === 'list') {\n msg.listKey = targetClass.cdRef?.key || targetClass.key;\n msg.modelKey = targetClass.model?.key || targetClass.modelKey;\n }\n\n if (msg.type === 'list_created') {\n const listKey = msg.args[0].listKey as string;\n if (listKey.startsWith('__global_list__')) {\n return valueToReturn;\n }\n }\n\n if (msg.listKey?.startsWith('__global_list__')) {\n return valueToReturn;\n }\n\n const end = perf.now();\n\n const duration = end - start;\n const roundedDuration = Math.round(duration * 1000) / 1000;\n msg.duration = roundedDuration;\n msg.start = start;\n msg.end = end;\n\n const value: AnalyticsEntrySdkActionValue = {\n duration: msg.duration,\n };\n\n if (msg.modelKey) {\n value.model = msg.modelKey;\n }\n\n if (msg.connectMessageResult) {\n msg.result = valueToReturn;\n }\n\n if (msg.args) {\n value.args = msg.args;\n }\n\n cdRef?.trigger(msg);\n\n cdRef.analyticsController.mark({\n name: `${msg.type}:end`,\n context: context,\n });\n\n cdRef.analyticsController.measure({\n name: msg.type,\n start: `${msg.type}:start`,\n end: `${msg.type}:end`,\n context: context,\n detail: value,\n });\n\n return valueToReturn;\n } catch (_err) {\n return result;\n }\n};\n\nexport const trigger = (_msg: DcuplUpdateMessage): any => {\n const log = (_target: any, _propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor => {\n const originalMethod = descriptor.value;\n\n descriptor.value = function (...args: any[]): any {\n const start = perf.now();\n\n const cdRef: ChangeDetector = (this as any).cdRef;\n\n const msg = clone(_msg, true);\n\n if (!msg.key) {\n cdRef.increaseCounter();\n msg.key = cdRef.getCounter();\n }\n\n const context = generateKey();\n\n cdRef.analyticsController.mark({\n name: `${msg.type}:start`,\n context,\n });\n\n const modelKey = (this as any).model?.key;\n\n if (!msg.originCdRefKey && cdRef.key) {\n msg.originCdRefKey = cdRef.key;\n }\n\n if (modelKey) {\n msg.modelKey = modelKey;\n }\n\n const result = originalMethod.apply(this, args);\n\n msg.method = _propertyKey;\n msg.args = args;\n\n if (result && typeof result === 'object' && 'then' in result) {\n return result.then((result: any) => {\n return finish(this, cdRef, msg, start, context, result);\n });\n } else {\n return finish(this, cdRef, msg, start, context, result);\n }\n };\n return descriptor;\n };\n\n return log;\n};\n","import { ListItem, Suggestion, SuggestionOptions, InverseIndexValuesMap } from './types';\n\nimport { QueryManager } from '.';\n\nexport function computeSuggestions<ListType extends ListItem = ListItem>(\n data: Map<string, ListType>,\n index: InverseIndexValuesMap,\n options: SuggestionOptions,\n queryManager: QueryManager,\n): Suggestion[] {\n if (!options.max) {\n options.max = 20;\n }\n const suggestions: Suggestion[] = [];\n\n const relevantSearchTerm = options.transform\n ? queryManager.transformValue(options.value, options.transform)\n : options.value;\n\n for (const [key, value] of index.entries()) {\n const isRelevantAttributeIndex = Array.from(value).some((item) => data.has(item));\n\n const relevantKey = options.transform ? queryManager.transformValue(key, options.transform) : key;\n\n if (options.excludeUndefineds && typeof key === 'undefined') {\n continue;\n }\n\n if (options.excludeNulls && key === null) {\n continue;\n }\n\n // Handle empty search term - return all values\n if (isRelevantAttributeIndex && (relevantSearchTerm === '' || relevantSearchTerm === '//')) {\n const suggestion: Suggestion = {\n value: key,\n keys: options.calculateResultKeys ? Array.from(value) : [],\n };\n suggestions.push(suggestion);\n }\n // Handle regex patterns (format: /pattern/)\n else if (\n isRelevantAttributeIndex &&\n typeof relevantSearchTerm === 'string' &&\n relevantSearchTerm.startsWith('/') &&\n relevantSearchTerm.endsWith('/')\n ) {\n if (queryManager.queryFind(relevantKey, relevantSearchTerm, 'some')) {\n const suggestion: Suggestion = {\n value: key,\n keys: options.calculateResultKeys ? Array.from(value) : [],\n };\n suggestions.push(suggestion);\n }\n }\n // Default: case-insensitive startsWith matching for search terms\n else if (isRelevantAttributeIndex && typeof relevantSearchTerm === 'string') {\n const keyStr = String(relevantKey ?? '');\n const searchStr = relevantSearchTerm;\n if (keyStr.toLowerCase().startsWith(searchStr.toLowerCase())) {\n const suggestion: Suggestion = {\n value: key,\n keys: options.calculateResultKeys ? Array.from(value) : [],\n };\n suggestions.push(suggestion);\n }\n }\n // Fallback to exact matching for non-string search terms\n else if (isRelevantAttributeIndex && queryManager.queryFind(relevantKey, relevantSearchTerm, 'some')) {\n const suggestion: Suggestion = {\n value: key,\n keys: options.calculateResultKeys ? Array.from(value) : [],\n };\n suggestions.push(suggestion);\n }\n\n if (suggestions.length === options.max) {\n break;\n }\n }\n\n return suggestions;\n}\n","/**\n * Custom date utilities to replace date-fns\n * Supports custom format patterns for date parsing\n */\n\n/**\n * Check if a value is a Date instance\n * @param value - Value to check\n * @returns true if value is a Date instance (valid or invalid), false otherwise\n */\nexport function isDateInstance(value: any): value is Date {\n return value instanceof Date;\n}\n\n/**\n * Check if a Date object represents a valid date\n * @param date - Date object to validate\n * @returns true if date is valid, false otherwise\n */\nexport function isValidDate(date: Date): boolean {\n return date instanceof Date && !isNaN(date.getTime());\n}\n\n/**\n * Format token definitions for date parsing\n */\ninterface FormatToken {\n type: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond';\n pattern: string;\n regex: string;\n}\n\n/**\n * Supported format tokens mapped to regex patterns\n * Tokens are sorted by length (longest first) to avoid partial matches\n */\nconst FORMAT_TOKENS: Record<string, FormatToken> = {\n yyyy: { type: 'year', pattern: 'yyyy', regex: '(\\\\d{4})' },\n yy: { type: 'year', pattern: 'yy', regex: '(\\\\d{2})' },\n MM: { type: 'month', pattern: 'MM', regex: '(\\\\d{2})' },\n M: { type: 'month', pattern: 'M', regex: '(\\\\d{1,2})' },\n dd: { type: 'day', pattern: 'dd', regex: '(\\\\d{2})' },\n d: { type: 'day', pattern: 'd', regex: '(\\\\d{1,2})' },\n HH: { type: 'hour', pattern: 'HH', regex: '(\\\\d{2})' },\n H: { type: 'hour', pattern: 'H', regex: '(\\\\d{1,2})' },\n mm: { type: 'minute', pattern: 'mm', regex: '(\\\\d{2})' },\n m: { type: 'minute', pattern: 'm', regex: '(\\\\d{1,2})' },\n ss: { type: 'second', pattern: 'ss', regex: '(\\\\d{2})' },\n s: { type: 'second', pattern: 's', regex: '(\\\\d{1,2})' },\n SSS: { type: 'millisecond', pattern: 'SSS', regex: '(\\\\d{3})' },\n};\n\n/**\n * Date component structure\n */\ninterface DateComponents {\n year: number;\n month: number;\n day: number;\n hour: number;\n minute: number;\n second: number;\n millisecond: number;\n}\n\n/**\n * Token match info\n */\ninterface TokenMatch {\n type: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond';\n pattern: string;\n}\n\n/**\n * Regex build result\n */\ninterface RegexBuildResult {\n regex: RegExp;\n tokenOrder: TokenMatch[];\n}\n\n/**\n * Build a regex pattern from a format string\n * @param format - Format string (e.g., 'yyyy-MM-dd', 'dd.MM.yyyy')\n * @returns Regex pattern and token order\n */\nfunction buildRegexFromFormat(format: string): RegexBuildResult {\n let regexStr = '^';\n const tokenOrder: TokenMatch[] = [];\n let remaining = format;\n\n // Sort tokens by length (longest first) to avoid partial matches\n const sortedTokens = Object.entries(FORMAT_TOKENS).sort((a, b) => b[0].length - a[0].length);\n\n while (remaining.length > 0) {\n let matched = false;\n\n // Try to match tokens\n for (const [pattern, token] of sortedTokens) {\n if (remaining.startsWith(pattern)) {\n regexStr += token.regex;\n tokenOrder.push({ type: token.type, pattern });\n remaining = remaining.slice(pattern.length);\n matched = true;\n break;\n }\n }\n\n // Handle literal characters (separators, spaces, etc.)\n if (!matched) {\n const char = remaining[0];\n if (!char) {\n break;\n }\n // Escape special regex characters\n regexStr += char.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n remaining = remaining.slice(1);\n }\n }\n\n regexStr += '$';\n return { regex: new RegExp(regexStr), tokenOrder };\n}\n\n/**\n * Extract date components from regex match\n * @param match - Regex match result\n * @param tokenOrder - Order of tokens in the format string\n * @returns Date components\n */\nfunction extractComponents(match: RegExpMatchArray, tokenOrder: TokenMatch[]): DateComponents {\n const components: DateComponents = {\n year: 1970,\n month: 1,\n day: 1,\n hour: 0,\n minute: 0,\n second: 0,\n millisecond: 0,\n };\n\n for (let i = 0; i < tokenOrder.length; i++) {\n const matchValue = match[i + 1];\n if (!matchValue) {\n continue;\n }\n const value = parseInt(matchValue, 10);\n const tokenMatch = tokenOrder[i];\n if (!tokenMatch) {\n continue;\n }\n\n // Handle 2-digit year (00-99 → 2000-2099) - only for 'yy' pattern\n if (tokenMatch.type === 'year' && tokenMatch.pattern === 'yy') {\n components.year = 2000 + value;\n } else {\n components[tokenMatch.type] = value;\n }\n }\n\n return components;\n}\n\n/**\n * Construct and validate a Date object from components\n * @param components - Date components\n * @returns Valid Date object or Invalid Date\n */\nfunction constructDate(components: DateComponents): Date {\n // Validate ranges\n if (components.month < 1 || components.month > 12) {\n return new Date(NaN);\n }\n if (components.day < 1 || components.day > 31) {\n return new Date(NaN);\n }\n if (components.hour < 0 || components.hour > 23) {\n return new Date(NaN);\n }\n if (components.minute < 0 || components.minute > 59) {\n return new Date(NaN);\n }\n if (components.second < 0 || components.second > 59) {\n return new Date(NaN);\n }\n if (components.millisecond < 0 || components.millisecond > 999) {\n return new Date(NaN);\n }\n\n // Construct date (JS months are 0-based)\n const date = new Date(\n components.year,\n components.month - 1,\n components.day,\n components.hour,\n components.minute,\n components.second,\n components.millisecond,\n );\n\n // Validate that date is valid\n if (!isValidDate(date)) {\n return new Date(NaN);\n }\n\n // Verify no overflow (e.g., Feb 31 → Mar 3, Apr 31 → May 1)\n if (\n date.getFullYear() !== components.year ||\n date.getMonth() !== components.month - 1 ||\n date.getDate() !== components.day\n ) {\n return new Date(NaN);\n }\n\n return date;\n}\n\n/**\n * Parse a date string according to a format pattern\n *\n * Supported format tokens:\n * - yyyy: 4-digit year\n * - yy: 2-digit year (00-99 → 2000-2099)\n * - MM: 2-digit month (01-12)\n * - M: 1-2 digit month (1-12)\n * - dd: 2-digit day (01-31)\n * - d: 1-2 digit day (1-31)\n * - HH: 2-digit hour (00-23)\n * - H: 1-2 digit hour (0-23)\n * - mm: 2-digit minute (00-59)\n * - m: 1-2 digit minute (0-59)\n * - ss: 2-digit second (00-59)\n * - s: 1-2 digit second (0-59)\n * - SSS: 3-digit millisecond (000-999)\n *\n * Special formats:\n * - 't': Unix timestamp in seconds\n * - 'T': Unix timestamp in milliseconds\n *\n * @param value - The date string to parse\n * @param format - Format pattern (e.g., 'yyyy-MM-dd', 'dd.MM.yyyy', 't', 'T')\n * @param referenceDate - Optional reference date (reserved for future use, currently unused)\n * @returns Parsed Date object or Invalid Date (NaN)\n *\n * @example\n * parseDate('2023-12-25', 'yyyy-MM-dd') // Dec 25, 2023\n * parseDate('25.12.2023', 'dd.MM.yyyy') // Dec 25, 2023\n * parseDate('1640000000', 't') // Unix seconds\n * parseDate('2023-02-31', 'yyyy-MM-dd') // Invalid Date (Feb only has 28/29 days)\n */\nexport function parseDate(value: string, format: string, _referenceDate?: Date): Date {\n // Trim whitespace\n value = value.trim();\n\n // Handle special format: Unix timestamp in seconds\n if (format === 't') {\n const timestamp = Number(value);\n if (isNaN(timestamp)) {\n return new Date(NaN);\n }\n return new Date(timestamp * 1000);\n }\n\n // Handle special format: Unix timestamp in milliseconds\n if (format === 'T') {\n const timestamp = Number(value);\n if (isNaN(timestamp)) {\n return new Date(NaN);\n }\n return new Date(timestamp);\n }\n\n // Build regex from format string\n const { regex, tokenOrder } = buildRegexFromFormat(format);\n const match = value.match(regex);\n\n if (!match) {\n return new Date(NaN);\n }\n\n // Extract components\n const components = extractComponents(match, tokenOrder);\n\n // Validate and construct date\n return constructDate(components);\n}\n","import { QueryAttributeEntry } from './query-builder';\nimport { get } from 'lodash-es';\n\n/**\n * Result of path validation\n */\nexport interface ValidationResult {\n valid: boolean;\n errors?: string[];\n}\n\n/**\n * Simple LRU Cache implementation for storing parse results\n */\nclass LRUCache<K, V> {\n private cache: Map<K, V>;\n private readonly maxSize: number;\n private accessOrder: K[];\n\n constructor(maxSize: number) {\n this.cache = new Map();\n this.maxSize = maxSize;\n this.accessOrder = [];\n }\n\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used)\n this.updateAccessOrder(key);\n }\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n // Update existing entry\n this.cache.set(key, value);\n this.updateAccessOrder(key);\n } else {\n // Add new entry\n if (this.cache.size >= this.maxSize) {\n // Evict least recently used\n const lruKey = this.accessOrder.shift();\n if (lruKey !== undefined) {\n this.cache.delete(lruKey);\n }\n }\n this.cache.set(key, value);\n this.accessOrder.push(key);\n }\n }\n\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n clear(): void {\n this.cache.clear();\n this.accessOrder = [];\n }\n\n get size(): number {\n return this.cache.size;\n }\n\n private updateAccessOrder(key: K): void {\n const index = this.accessOrder.indexOf(key);\n if (index > -1) {\n this.accessOrder.splice(index, 1);\n }\n this.accessOrder.push(key);\n }\n\n // For testing/debugging\n getStats(): { size: number; maxSize: number; hitRate?: number } {\n return {\n size: this.cache.size,\n maxSize: this.maxSize,\n };\n }\n}\n\n/**\n * Dependency graph for detecting circular references\n */\nclass DependencyGraph {\n private graph: Map<string, Set<string>>;\n\n constructor() {\n this.graph = new Map();\n }\n\n addDependency(from: string, to: string): void {\n if (!this.graph.has(from)) {\n this.graph.set(from, new Set());\n }\n this.graph.get(from)!.add(to);\n }\n\n hasCycle(path: string[]): boolean {\n if (path.length === 0) return false;\n\n const visited = new Set<string>();\n const recursionStack = new Set<string>();\n\n const hasCycleRecursive = (node: string): boolean => {\n visited.add(node);\n recursionStack.add(node);\n\n const neighbors = this.graph.get(node);\n if (neighbors) {\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor)) {\n if (hasCycleRecursive(neighbor)) {\n return true;\n }\n } else if (recursionStack.has(neighbor)) {\n return true;\n }\n }\n }\n\n recursionStack.delete(node);\n return false;\n };\n\n // Check if the path itself forms a cycle\n for (let i = 0; i < path.length - 1; i++) {\n const from = path[i];\n const to = path[i + 1];\n if (from && to) {\n this.addDependency(from, to);\n }\n }\n\n // Check for any node in the path\n for (const node of path) {\n if (!visited.has(node)) {\n if (hasCycleRecursive(node)) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n clear(): void {\n this.graph.clear();\n }\n}\n\n/**\n * DeepQueryService - Centralized service for parsing, validating, and caching deep query attributes\n *\n * This service provides:\n * - LRU caching for parsed attribute paths (1000 entries, targeting >80% hit rate)\n * - Circular reference detection\n * - Multi-level nesting support (3+ levels)\n * - Path validation with descriptive errors\n */\nexport class DeepQueryService {\n private parseCache: LRUCache<string, QueryAttributeEntry>;\n private circularRefDetector: DependencyGraph;\n private cacheHits: number = 0;\n private cacheMisses: number = 0;\n\n constructor(cacheSize: number = 1000) {\n this.parseCache = new LRUCache(cacheSize);\n this.circularRefDetector = new DependencyGraph();\n }\n\n /**\n * Parse an attribute string into its components\n *\n * Supports:\n * - Dot notation: `customer.name`\n * - Bracket notation: `items[0].price`, `items[*].price`\n * - Multi-level: `order.customer.address.city`\n *\n * @param attribute - The attribute path to parse\n * @returns QueryAttributeEntry with isDeepQuery, reference, referenceAttribute\n *\n * @example\n * ```typescript\n * const service = new DeepQueryService();\n * const entry = service.parse('customer.name');\n * // Returns: { attribute: 'customer.name', isDeepQuery: true, reference: 'customer', referenceAttribute: 'name' }\n * ```\n */\n parse(attribute: string): QueryAttributeEntry {\n // Check cache first\n if (this.parseCache.has(attribute)) {\n this.cacheHits++;\n return this.parseCache.get(attribute)!;\n }\n\n this.cacheMisses++;\n\n // Parse the attribute\n let referenceAttribute: string | undefined;\n let reference: string | undefined;\n\n const hasDot = attribute.includes('.');\n const hasBracket = attribute.includes('[');\n const isDeepQuery = hasDot || hasBracket;\n\n if (isDeepQuery) {\n // The reference is always the segment before the first '.' or '['\n reference = attribute.split(/[.[]/)[0];\n\n // The bracket belongs to the reference segment only when the first '['\n // comes before the first '.' (e.g. items[0].prop). When a dot comes first\n // (e.g. order.items[0].price), the bracket is part of a *later* segment and\n // must stay in referenceAttribute so the recursive projection builder can\n // descend through it — otherwise the intermediate segment is silently dropped.\n const firstDot = attribute.indexOf('.');\n const firstBracket = attribute.indexOf('[');\n const bracketLeadsReference = hasBracket && (firstDot === -1 || firstBracket < firstDot);\n\n if (bracketLeadsReference) {\n // Handle patterns like: ref[0].prop, ref[*].prop, ref[10].prop\n const closingBracketIdx = attribute.indexOf(']');\n if (closingBracketIdx > -1) {\n const dotAfterBracketIdx = attribute.indexOf('.', closingBracketIdx + 1);\n if (dotAfterBracketIdx > -1) {\n // There is a property after the array index/wildcard\n referenceAttribute = attribute.substring(dotAfterBracketIdx + 1);\n } else {\n // Array of primitives like ref[0] (no trailing property)\n referenceAttribute = undefined;\n }\n } else if (hasDot) {\n // Fallback: bracket not properly closed but dot exists\n referenceAttribute = attribute.substring(firstDot + 1);\n }\n } else if (hasDot) {\n // Simple deep path like ref.prop, or dot-then-bracket like order.items[0].price\n // (the trailing bracket is handled by the recursive re-parse of referenceAttribute)\n referenceAttribute = attribute.substring(firstDot + 1);\n }\n }\n\n const entry: QueryAttributeEntry = {\n attribute,\n isDeepQuery,\n };\n\n if (reference) entry.reference = reference;\n if (typeof referenceAttribute !== 'undefined' && referenceAttribute !== '') {\n entry.referenceAttribute = referenceAttribute;\n }\n\n // Cache the result\n this.parseCache.set(attribute, entry);\n\n return entry;\n }\n\n /**\n * Detect circular references in a path\n *\n * Checks for:\n * - Direct circular: A→B→A\n * - Indirect circular: A→B→C→A\n * - Self-referential: A→A\n *\n * @param path - Array of model/reference keys in the dependency chain\n * @returns true if circular reference detected\n *\n * @example\n * ```typescript\n * const service = new DeepQueryService();\n * const hasCircular = service.detectCircular(['Order', 'Customer', 'Order']);\n * // Returns: true\n * ```\n */\n detectCircular(path: string[]): boolean {\n if (path.length === 0) return false;\n\n // Simple check: if any element appears twice, it's circular\n const uniqueElements = new Set(path);\n if (uniqueElements.size !== path.length) {\n return true;\n }\n\n // More complex check using dependency graph\n return this.circularRefDetector.hasCycle(path);\n }\n\n /**\n * Validate attribute path syntax\n *\n * Checks for:\n * - Empty strings\n * - Malformed bracket notation\n * - Invalid characters\n * - Excessive nesting depth\n *\n * @param attribute - The attribute path to validate\n * @param maxDepth - Maximum allowed nesting depth (default: 10)\n * @returns ValidationResult with errors if invalid\n *\n * @example\n * ```typescript\n * const service = new DeepQueryService();\n * const result = service.validatePath('customer.name');\n * // Returns: { valid: true }\n *\n * const badResult = service.validatePath('customer.[invalid]');\n * // Returns: { valid: false, errors: ['Invalid bracket notation: customer.[invalid]'] }\n * ```\n */\n validatePath(attribute: string, maxDepth: number = 10): ValidationResult {\n const errors: string[] = [];\n\n // Check for empty string\n if (!attribute || attribute.trim().length === 0) {\n errors.push('Attribute path cannot be empty');\n return { valid: false, errors };\n }\n\n // Check for leading/trailing dots\n if (attribute.startsWith('.') || attribute.endsWith('.')) {\n errors.push(`Invalid path: Leading or trailing dots are not allowed: ${attribute}`);\n }\n\n // Check for consecutive dots\n if (attribute.includes('..')) {\n errors.push(`Invalid path: Consecutive dots are not allowed: ${attribute}`);\n }\n\n // Check bracket notation\n const openBrackets = (attribute.match(/\\[/g) || []).length;\n const closeBrackets = (attribute.match(/]/g) || []).length;\n if (openBrackets !== closeBrackets) {\n errors.push(`Invalid bracket notation: Mismatched brackets in ${attribute}`);\n }\n\n // Check for empty brackets\n if (attribute.includes('[]')) {\n errors.push(`Invalid bracket notation: Empty brackets not allowed: ${attribute}`);\n }\n\n // Check bracket content (should be number or *)\n const bracketPattern = /\\[([^\\]]+)\\]/g;\n let match;\n while ((match = bracketPattern.exec(attribute)) !== null) {\n const content = match[1];\n if (content && content !== '*' && !/^\\d+$/.test(content)) {\n errors.push(`Invalid bracket content: Must be a number or '*', got '${content}' in ${attribute}`);\n }\n }\n\n // Check nesting depth\n const depth = (attribute.match(/\\./g) || []).length + 1;\n if (depth > maxDepth) {\n errors.push(`Path too deep: Maximum depth is ${maxDepth}, got ${depth} in ${attribute}`);\n }\n\n // Check for invalid characters (allow alphanumeric, dots, brackets, underscores, hyphens)\n const invalidChars = attribute.match(/[^a-zA-Z0-9._[\\]*-]/g);\n if (invalidChars) {\n errors.push(`Invalid characters in path: ${invalidChars.join(', ')} in ${attribute}`);\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n };\n }\n\n /**\n * Clear the parse cache\n * Should be called when models are updated or removed\n */\n clearCache(): void {\n this.parseCache.clear();\n this.cacheHits = 0;\n this.cacheMisses = 0;\n }\n\n /**\n * Clear the circular reference detector\n */\n clearCircularDetector(): void {\n this.circularRefDetector.clear();\n }\n\n /**\n * Get cache statistics for monitoring and debugging\n *\n * @returns Object containing cache size, hit rate, and other metrics\n */\n getCacheStats(): {\n size: number;\n maxSize: number;\n hits: number;\n misses: number;\n hitRate: number;\n } {\n const stats = this.parseCache.getStats();\n const total = this.cacheHits + this.cacheMisses;\n const hitRate = total > 0 ? this.cacheHits / total : 0;\n\n return {\n ...stats,\n hits: this.cacheHits,\n misses: this.cacheMisses,\n hitRate,\n };\n }\n\n /**\n * Extract path segments from an attribute string\n * Useful for analyzing deep query structure\n *\n * @param attribute - The attribute path\n * @returns Array of path segments\n *\n * @example\n * ```typescript\n * const service = new DeepQueryService();\n * const segments = service.getPathSegments('order.customer.name');\n * // Returns: ['order', 'customer', 'name']\n * ```\n */\n getPathSegments(attribute: string): string[] {\n // Remove array indices/wildcards\n const withoutBrackets = attribute.replace(/\\[[^\\]]+\\]/g, '');\n return withoutBrackets.split('.').filter((s) => s.length > 0);\n }\n\n /**\n * Get the depth of a deep query path\n *\n * @param attribute - The attribute path\n * @returns Depth of the path (1 for shallow, 2+ for deep)\n */\n getPathDepth(attribute: string): number {\n return this.getPathSegments(attribute).length;\n }\n}\n\n/**\n * Singleton instance for global use\n */\nexport const deepQueryService = new DeepQueryService();\n\n/**\n * Extract all values from an object using a path that may contain [*] wildcards.\n *\n * This function handles patterns like:\n * - `articleIds[*].vendorId.location` - expands [*] to iterate over array elements\n * - `a[*].b[*].c` - supports nested wildcards (cross-product)\n * - `simple.path` - falls back to lodash get for non-wildcard paths\n *\n * @param item - The object to extract values from\n * @param path - The path string, possibly containing [*] wildcards\n * @returns Array of all values found at the path\n *\n * @example\n * ```typescript\n * const order = {\n * articleIds: [\n * { vendorId: { location: 'Berlin' } },\n * { vendorId: { location: 'Vienna' } }\n * ]\n * };\n * getDeepPathValues(order, 'articleIds[*].vendorId.location');\n * // Returns: ['Berlin', 'Vienna']\n * ```\n */\nexport function getDeepPathValues(item: any, path: string): any[] {\n if (path.includes('[*]')) {\n const starIdx = path.indexOf('[*]');\n const beforeStar = path.substring(0, starIdx);\n const afterStar = path.substring(starIdx + 3); // skip '[*]'\n\n const arr = beforeStar ? get(item, beforeStar) : item;\n if (!Array.isArray(arr)) return [];\n\n const results: any[] = [];\n for (const el of arr) {\n if (!afterStar || afterStar === '') {\n if (el !== undefined) results.push(el);\n } else if (afterStar.startsWith('.')) {\n results.push(...getDeepPathValues(el, afterStar.slice(1)));\n }\n }\n return results;\n }\n\n // Plain dot path — traverse segment by segment and auto-unwind arrays\n // encountered at intermediate segments (multi-valued reference paths).\n const segments = path.split('.');\n let current: any[] = [item];\n for (const segment of segments) {\n const next: any[] = [];\n for (const node of current) {\n if (node === undefined || node === null) continue;\n if (Array.isArray(node)) {\n for (const el of node) {\n if (el === undefined || el === null) continue;\n const v = el[segment];\n if (v !== undefined) next.push(v);\n }\n } else {\n const v = node[segment];\n if (v !== undefined) next.push(v);\n }\n }\n current = next;\n if (current.length === 0) return [];\n }\n\n // Final flatten: if a leaf resolution yielded arrays (e.g. a multi-valued\n // ref at the end), expand one level so aggregations see scalars.\n const flat: any[] = [];\n for (const v of current) {\n if (Array.isArray(v)) {\n for (const el of v) if (el !== undefined) flat.push(el);\n } else if (v !== undefined) {\n flat.push(v);\n }\n }\n return flat;\n}\n","import {\n Aggregation,\n AggregationDistinctValue,\n AggregationGroupValue,\n AggregationOptions,\n InverseIndexValuesMap,\n ListItem,\n NumberAggregationValue,\n} from './types';\nimport { deepQueryService, getDeepPathValues } from './deep-query.service';\n\nfunction getScalingFactor(numbers: number[]): number {\n let maxDecimalPlaces = 0;\n for (let num of numbers) {\n const decimalPart = num.toString().split('.')[1];\n if (decimalPart) {\n maxDecimalPlaces = Math.max(maxDecimalPlaces, decimalPart.length);\n }\n }\n return Math.pow(10, maxDecimalPlaces);\n}\n\nfunction add(a: number, b: number, factor: number): number {\n return (a * factor + b * factor) / factor;\n}\n\nfunction divide(a: number, b: number, factor: number): number {\n return (a * factor) / (b * factor);\n}\n\nexport const getAggregation = (\n options: AggregationOptions,\n relevantData: Map<string, ListItem>,\n indexMap: InverseIndexValuesMap,\n attribute: string,\n): Aggregation => {\n if (!attribute) {\n throw new Error('Attribute not found');\n }\n\n const aggregation: Aggregation = {\n attribute: attribute,\n types: options.types,\n };\n\n let numberAgg: NumberAggregationValue | undefined;\n if (aggregation.types.includes('sum') || aggregation.types.includes('avg')) {\n numberAgg = getNumberAggregation(attribute, relevantData);\n }\n\n let minMax: any[] | undefined;\n\n if (aggregation.types.includes('min') || aggregation.types.includes('max')) {\n minMax = getMinMax(attribute, relevantData);\n }\n\n options.types.forEach((type) => {\n if (type === 'sum') {\n aggregation.sum = numberAgg?.sum;\n } else if (type === 'avg') {\n aggregation.avg = numberAgg?.average;\n } else if (type === 'count') {\n aggregation.count = getCount(attribute, relevantData);\n } else if (type === 'min') {\n aggregation.min = minMax && (minMax[0] as any);\n } else if (type === 'max') {\n aggregation.max = minMax && (minMax[1] as any);\n } else if (type === 'distinct') {\n aggregation.distinct = getDistinct(options, relevantData!, indexMap!);\n } else if (type === 'group') {\n aggregation.group = getGroups(options, relevantData!);\n }\n });\n return aggregation;\n};\n\nconst getCount = (attribute: string, data: Map<string, ListItem>): number => {\n const attributeEntry = deepQueryService.parse(attribute);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n let count = 0;\n for (const item of data.values()) {\n if (!item) continue;\n\n if (isDeepQuery) {\n count += getDeepPathValues(item, attribute).filter((v) => v !== undefined && v !== null).length;\n } else {\n const raw = item[attribute];\n if (raw === undefined || raw === null) continue;\n if (Array.isArray(raw)) {\n count += raw.filter((v) => v !== undefined && v !== null).length;\n } else {\n count++;\n }\n }\n }\n return count;\n};\n\nconst getMinMax = (attribute: string, data: Map<string, ListItem>): any[] | undefined => {\n let minV: any = undefined;\n let maxV: any = undefined;\n\n const attributeEntry = deepQueryService.parse(attribute);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n for (const item of data.values()) {\n if (!item) continue;\n\n let values: any[];\n if (isDeepQuery) {\n values = getDeepPathValues(item, attribute);\n } else {\n const raw = item[attribute];\n if (raw === undefined) {\n values = [];\n } else if (Array.isArray(raw)) {\n values = raw.filter((v) => v !== undefined);\n } else {\n values = [raw];\n }\n }\n\n for (const value of values) {\n // min/max only make sense for primitives — skip resolved reference objects\n if (value === undefined || value === null || typeof value === 'object') continue;\n if (minV === undefined || value < minV) minV = value;\n if (maxV === undefined || value > maxV) maxV = value;\n }\n }\n\n if (minV === undefined || maxV === undefined) {\n return undefined;\n }\n\n return [minV, maxV];\n};\n\nconst getGroups = (options: AggregationOptions, data: Map<string, ListItem>): AggregationGroupValue[] => {\n const values: AggregationGroupValue[] = [];\n\n options.groupOptions?.groups?.forEach((group) => {\n values.push({\n option: group,\n count: 0,\n keys: [],\n });\n });\n\n const notFound: AggregationGroupValue = {\n option: { value: ['$dcupl_not_found'] },\n count: 0,\n keys: [],\n };\n\n // Check if this is a deep query attribute\n const attributeEntry = deepQueryService.parse(options.attribute);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n for (const item of data.values()) {\n let relevantEntries: any[];\n if (isDeepQuery) {\n relevantEntries = getDeepPathValues(item, options.attribute);\n } else {\n relevantEntries = [item[options.attribute]];\n }\n\n let itemMatched = false;\n for (const relevantEntry of relevantEntries) {\n for (const group of values) {\n if (\n (typeof group.option.value !== 'undefined' && group.option.value.includes(relevantEntry)) ||\n (typeof group.option.from !== 'undefined' &&\n group.option.from < relevantEntry &&\n typeof group.option.to !== 'undefined' &&\n group.option.to > relevantEntry)\n ) {\n // Only count each item once per group\n if (!group.keys.includes(item.key)) {\n group.count++;\n group.keys.push(item.key);\n }\n itemMatched = true;\n }\n }\n }\n if (!itemMatched && options?.groupOptions?.calculateNotFound) {\n notFound.count++;\n notFound.keys.push(item.key);\n }\n }\n\n if (options?.groupOptions?.calculateNotFound) {\n values.push(notFound);\n }\n\n return values;\n};\n\nconst getNumberAggregation = (attribute: string, data: Map<string, ListItem>): NumberAggregationValue | undefined => {\n let sum = 0;\n let count = 0;\n\n const attributeEntry = deepQueryService.parse(attribute);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n for (const item of data.values()) {\n if (!item) continue;\n\n let values: any[];\n if (isDeepQuery) {\n values = getDeepPathValues(item, attribute);\n } else {\n const raw = item[attribute];\n if (raw === undefined) {\n values = [];\n } else if (Array.isArray(raw)) {\n values = raw;\n } else {\n values = [raw];\n }\n }\n\n for (const value of values) {\n if (Array.isArray(value)) {\n for (const val of value) {\n if (typeof val === 'number') {\n sum = add(sum, val, getScalingFactor([sum, val]));\n count++;\n }\n }\n } else if (typeof value === 'number') {\n sum = add(sum, value, getScalingFactor([sum, value]));\n count++;\n }\n }\n }\n\n if (count === 0) {\n return undefined;\n }\n\n const average = divide(sum, count, getScalingFactor([sum, count]));\n\n return {\n average,\n count,\n sum,\n };\n};\n\nexport const getDistinct = (\n options: AggregationOptions,\n data: Map<string, ListItem>,\n indexMap: InverseIndexValuesMap,\n): AggregationDistinctValue[] => {\n const values: AggregationDistinctValue[] = [];\n\n for (const [attributeValue, itemKeys] of indexMap) {\n if (options.excludeUndefineds && typeof attributeValue === 'undefined') {\n continue;\n }\n\n if (Array.from(itemKeys).find((key) => data.has(key))) {\n const value: AggregationDistinctValue = {\n value: attributeValue,\n count: itemKeys.size,\n };\n if (!options.distinctOptions?.skipKeys) {\n value.keys = Array.from(itemKeys);\n }\n\n values.push(value);\n\n if (options.distinctOptions?.limit && values.length >= options.distinctOptions.limit) {\n break;\n }\n }\n }\n\n return values;\n};\n","import { cloneDeep, every, findIndex } from 'lodash-es';\nimport { DcuplGlobalQueryOptions, DcuplItemsOptions, DcuplQuery, DcuplQueryGroup, SortingProjection } from './types';\nimport { deepQueryService, DeepQueryService } from './deep-query.service';\n\nexport type QueryAttributeEntry = {\n attribute: string;\n reference?: string;\n referenceAttribute?: string;\n // referenceModel?: string;\n isDeepQuery?: boolean;\n};\n\nclass DcuplQueryError extends Error {\n public query: DcuplQuery | DcuplQueryGroup;\n\n constructor(message: string, query: DcuplQuery | DcuplQueryGroup) {\n const msg = `Query Error: ${message} in ${JSON.stringify(query, null, 2)}`;\n super(msg);\n this.query = query;\n this.name = 'DcuplQueryError';\n }\n}\n\nexport class DcuplQueryBuilder {\n private _defaultQueryOptions: DcuplGlobalQueryOptions<any> = {\n modelKey: '',\n groupKey: 'root',\n groupType: 'and',\n queries: [],\n };\n private _initOptions!: any;\n private currentQuery: DcuplGlobalQueryOptions<any> = cloneDeep(this._defaultQueryOptions);\n private deepQueryService: DeepQueryService;\n\n constructor(deepQueryServiceInstance?: DeepQueryService) {\n this.deepQueryService = deepQueryServiceInstance || deepQueryService;\n }\n\n /**\n * Initialize the query builder.\n * Accepts either minimal options with just modelKey, or a complete query object.\n *\n * @param options - Minimal { modelKey } or full DcuplGlobalQueryOptions\n *\n * @example\n * // Minimal initialization\n * builder.init({ modelKey: 'Product' });\n *\n * @example\n * // Full query initialization\n * builder.init({\n * modelKey: 'Product',\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ],\n * count: 10,\n * sort: { price: 'asc' }\n * });\n */\n public init(options: { modelKey: string } | DcuplGlobalQueryOptions<any>): void {\n this._initOptions = cloneDeep(options);\n this.currentQuery = cloneDeep(this._defaultQueryOptions);\n this.currentQuery.modelKey = options.modelKey;\n\n // Auto-detect full query initialization\n if ('queries' in options && Array.isArray(options.queries)) {\n if (options.groupType) this.currentQuery.groupType = options.groupType;\n if (options.queries) this.currentQuery.queries = cloneDeep(options.queries);\n if (options.count !== undefined) this.currentQuery.count = options.count;\n if (options.start !== undefined) this.currentQuery.start = options.start;\n if (options.sort) this.currentQuery.sort = options.sort;\n if (options.projection) this.currentQuery.projection = options.projection;\n }\n }\n\n public reset(): DcuplGlobalQueryOptions<any> {\n this.init(this._initOptions);\n return this.getQuery();\n }\n\n public isQuery(query: Partial<DcuplQuery> | Partial<DcuplQueryGroup>): query is DcuplQuery {\n return 'attribute' in query;\n }\n public isQueryGroup(query: Partial<DcuplQuery> | Partial<DcuplQueryGroup>): query is DcuplQueryGroup {\n // A group is identified by its `queries` array (always present on groups,\n // absent on leaf queries). `groupKey` is optional, so relying on it alone\n // missed keyless nested groups, which then fell through as invalid queries.\n return 'queries' in query || 'groupKey' in query;\n }\n\n public isRootQueryGroup(\n groupOrQuery: Partial<DcuplGlobalQueryOptions<any>> | Partial<DcuplQuery> | Partial<DcuplQueryGroup>,\n ): groupOrQuery is DcuplGlobalQueryOptions<any> {\n return this.isQueryGroup(groupOrQuery) && groupOrQuery.groupKey === 'root';\n }\n\n public getQueryGroup(groupKey?: string): DcuplQueryGroup | undefined {\n return this.currentQuery.queries.find((q) => this.isQueryGroup(q) && q.groupKey === groupKey) as\n | DcuplQueryGroup\n | undefined;\n }\n\n public getQuery(): DcuplGlobalQueryOptions<any> {\n return cloneDeep(this.currentQuery);\n }\n\n public hasQueryGroup(groupKey: string): boolean {\n return this.currentQuery.queries.some((query) => this.isQueryGroup(query) && query.groupKey === groupKey);\n }\n\n public has(options: { groupKey: string; queryKey?: string }): boolean {\n if (options.groupKey && options.queryKey) {\n const group = this.getQueryGroup(options.groupKey);\n\n if (group) {\n return group.queries.some((q) => this.isQuery(q) && q.queryKey === options.queryKey);\n }\n } else if (options.groupKey) {\n return this.hasQueryGroup(options.groupKey);\n }\n return false;\n }\n\n public validate(query: DcuplGlobalQueryOptions<any>): { valid: boolean; errors?: Error[] } {\n const errors = query.queries.map((q) => this.validateQuery(q));\n const valid = every(errors, (e) => e.valid);\n return { valid, errors: errors.map((e) => e.errors).flat() as Error[] };\n }\n\n public applyOptions(\n options: DcuplItemsOptions<any>,\n mode: 'set' | 'update' = 'update',\n ): DcuplGlobalQueryOptions<any> {\n if (mode === 'set') {\n this.currentQuery.count = options.count;\n this.currentQuery.start = options.start;\n this.currentQuery.sort = options.sort;\n this.currentQuery.projection = options.projection;\n } else {\n if (typeof options?.sort !== 'undefined') {\n this.currentQuery!.sort = options.sort as SortingProjection<any>;\n }\n if (typeof options?.count !== 'undefined') {\n this.currentQuery.count = options.count;\n }\n if (typeof options?.start !== 'undefined') {\n this.currentQuery.start = options?.start;\n }\n if (typeof options?.projection !== 'undefined') {\n this.currentQuery.projection = options?.projection;\n }\n }\n\n return this.getQuery();\n }\n\n /**\n * Apply query conditions, groups, or complete queries.\n * This method has implicit behavior based on input type.\n *\n * @deprecated Use explicit methods instead for clearer intent:\n * - {@link addCondition} - Add single condition\n * - {@link addConditions} - Add multiple conditions\n * - {@link addGroup} - Add query group\n * - {@link setCondition} - Replace condition\n * - {@link setConditions} - Replace conditions\n * - {@link setGroup} - Replace query group\n * - {@link mergeQuery} - Merge complete query\n * - {@link replaceQuery} - Replace complete query\n *\n * @param queryToApply - Query, condition, or array to apply\n * @param options - Mode: 'add' (default) or 'set' (replace)\n * @param relevantQuery - Target query (defaults to current)\n * @returns Updated query\n */\n public applyQuery(\n queryToApply: DcuplGlobalQueryOptions<any> | DcuplQueryGroup | DcuplQuery | DcuplQuery[] | DcuplQueryGroup[],\n options?: { mode?: 'set' | 'add' },\n relevantQuery?: DcuplGlobalQueryOptions<any>,\n ): DcuplGlobalQueryOptions<any> {\n if (!relevantQuery) relevantQuery = this.currentQuery;\n\n if (Array.isArray(queryToApply)) {\n for (const query of queryToApply) {\n this.applyQuery(query, options, relevantQuery);\n }\n } else if (this.isRootQueryGroup(queryToApply)) {\n if (queryToApply.groupType) relevantQuery.groupType = queryToApply.groupType;\n // if (queryToApply.options) relevantQuery.options = queryToApply.options;\n\n if (options?.mode === 'set') {\n relevantQuery.queries = queryToApply.queries;\n } else {\n relevantQuery.queries = [...relevantQuery.queries, ...queryToApply.queries];\n }\n\n this.applyOptions(queryToApply, 'set');\n } else if (this.isQuery(queryToApply)) {\n const idx = relevantQuery.queries.findIndex(\n (query) => this.isQueryGroup(query) && query.groupKey === queryToApply.attribute,\n );\n\n if (idx > -1) {\n const queryGroup: DcuplQueryGroup = relevantQuery.queries[idx] as DcuplQueryGroup;\n\n if (options?.mode === 'set') {\n queryGroup.queries = [queryToApply];\n } else {\n queryGroup.queries.push(queryToApply);\n }\n } else {\n relevantQuery.queries.push({\n groupKey: queryToApply.attribute,\n groupType: 'or',\n queries: [queryToApply],\n });\n }\n // handle query group\n } else if (this.isQueryGroup(queryToApply)) {\n const idx = relevantQuery.queries.findIndex(\n (query) => this.isQueryGroup(query) && query.groupKey === queryToApply.groupKey,\n );\n if (idx > -1) {\n const queryGroup: DcuplQueryGroup = relevantQuery.queries[idx] as DcuplQueryGroup;\n if (queryToApply.groupType) queryGroup.groupType = queryToApply.groupType;\n // if (queryToApply.options) queryGroup.options = queryToApply.options;\n if (options?.mode === 'set') {\n relevantQuery.queries[idx] = queryToApply;\n } else {\n queryGroup.queries = [...queryGroup.queries, ...queryToApply.queries];\n }\n } else {\n relevantQuery.queries.push({\n groupKey: queryToApply.groupKey,\n groupType: queryToApply.groupType || 'or',\n queries: queryToApply.queries,\n // options: queryToApply.options,\n });\n }\n }\n\n return relevantQuery;\n }\n\n /**\n * Remove all query conditions.\n *\n * @deprecated Use {@link clearConditions} for clearer intent.\n * @returns Query with no conditions\n */\n public removeAllQueries(): DcuplGlobalQueryOptions<any> {\n this.currentQuery.queries = [];\n return this.getQuery();\n }\n\n /**\n * Remove a specific query or query group.\n *\n * @param queryToRemove - Query or group to remove (partial match)\n * @param relevantQuery - Target query (defaults to current)\n * @returns Updated query\n */\n public removeQuery(\n queryToRemove: Partial<DcuplQueryGroup | DcuplQuery>,\n relevantQuery?: DcuplGlobalQueryOptions<any>,\n ): DcuplGlobalQueryOptions<any> {\n // handle default query\n\n if (!relevantQuery) relevantQuery = this.currentQuery;\n\n if (this.isQuery(queryToRemove)) {\n const idx = relevantQuery.queries.findIndex(\n (query) => this.isQueryGroup(query) && query.groupKey === queryToRemove.attribute,\n );\n\n if (idx > -1) {\n const queryGroup: DcuplQueryGroup = relevantQuery.queries[idx] as DcuplQueryGroup;\n\n const idxOfQueryToRemove = findIndex(queryGroup.queries, queryToRemove);\n if (idxOfQueryToRemove > -1) {\n queryGroup.queries.splice(idxOfQueryToRemove, 1);\n }\n\n if (queryGroup.queries.length === 0) {\n relevantQuery.queries.splice(idx, 1);\n }\n }\n } else if (this.isQueryGroup(queryToRemove)) {\n const idx = relevantQuery.queries.findIndex(\n (query) => this.isQueryGroup(query) && query.groupKey === queryToRemove.groupKey,\n );\n if (idx > -1) {\n if (!queryToRemove.queries) {\n relevantQuery.queries.splice(idx, 1);\n } else {\n const queryGroup: DcuplQueryGroup = relevantQuery.queries[idx] as DcuplQueryGroup;\n for (const q of queryToRemove.queries) {\n const idxOfQueryToRemove = findIndex(queryGroup.queries, q);\n if (idxOfQueryToRemove > -1) {\n queryGroup.queries.splice(idxOfQueryToRemove, 1);\n }\n }\n if (queryGroup.queries.length === 0) {\n relevantQuery.queries.splice(idx, 1);\n }\n }\n }\n }\n\n return relevantQuery;\n }\n\n private validateQuery(query: DcuplQuery | DcuplQueryGroup): { valid: boolean; errors?: DcuplQueryError[] } {\n const errors: DcuplQueryError[] = [];\n if (this.isQuery(query)) {\n if (!query.attribute) {\n errors.push(new DcuplQueryError('Missing attribute', query));\n }\n\n if (!query.operator) {\n errors.push(new DcuplQueryError('Missing operator', query));\n }\n\n if (typeof query.value === 'undefined') {\n errors.push(new DcuplQueryError('Missing value', query));\n }\n\n if (query.operator === 'find' && Array.isArray(query.value)) {\n errors.push(new DcuplQueryError('find operator cannot have array value', query));\n }\n\n if (errors.length > 0) {\n return { valid: false, errors: errors };\n } else {\n return { valid: true };\n }\n } else if (this.isQueryGroup(query)) {\n const queryErrors = query.queries.map((q) => this.validateQuery(q));\n const invalidQueries = queryErrors.filter((q) => !q.valid);\n const errors = invalidQueries.map((q) => q.errors).flat() as DcuplQueryError[];\n if (errors.length > 0) {\n return { valid: false, errors: errors };\n } else {\n return { valid: true };\n }\n }\n\n errors.push(new DcuplQueryError('Invalid query definition', query));\n return { valid: false, errors: errors };\n }\n\n /**\n * Get all attribute names used in the query.\n *\n * @deprecated Use {@link getAttributeNames} for clearer naming.\n * @param query - Optional query to analyze\n * @returns Array of attribute names\n */\n public getQueryAttributes(query?: DcuplGlobalQueryOptions<any>): string[] {\n const attributes: string[] = [];\n const visited = new Set<string>();\n\n const extractAttributes = (queries: Array<DcuplQuery | DcuplQueryGroup>): void => {\n for (const query of queries) {\n if (this.isQuery(query)) {\n if (query.attribute && !visited.has(query.attribute)) {\n attributes.push(query.attribute);\n visited.add(query.attribute);\n }\n } else if (this.isQueryGroup(query)) {\n extractAttributes(query.queries);\n }\n }\n };\n\n extractAttributes(query?.queries || this.currentQuery.queries);\n return attributes;\n }\n\n /**\n * Extract detailed attribute information from query.\n *\n * @deprecated Use {@link getAttributeDetails} for clearer naming.\n * @param query - Query to analyze\n * @returns Array of QueryAttributeEntry objects\n */\n public extractQueryAttributes(query: DcuplGlobalQueryOptions<any>): QueryAttributeEntry[] {\n const attributes: QueryAttributeEntry[] = [];\n const visited = new Set<string>();\n\n const extractAttributes = (queries: Array<DcuplQuery | DcuplQueryGroup>): void => {\n for (const query of queries) {\n if (this.isQuery(query)) {\n if (query.attribute && !visited.has(query.attribute)) {\n const entry = this.getQueryAttributeForString(query.attribute);\n\n attributes.push(entry);\n visited.add(query.attribute);\n }\n } else if (this.isQueryGroup(query)) {\n extractAttributes(query.queries);\n }\n }\n };\n\n extractAttributes(query?.queries || this.currentQuery.queries);\n return attributes;\n }\n\n /**\n * Parse an attribute string into its components using the DeepQueryService\n * This method now delegates to the centralized DeepQueryService for consistent\n * parsing, caching, and validation across the codebase.\n *\n * @param attribute - The attribute path to parse (e.g., 'customer.name', 'items[0].price')\n * @returns QueryAttributeEntry with isDeepQuery, reference, referenceAttribute\n */\n public getQueryAttributeForString(attribute: string): QueryAttributeEntry {\n // Delegate to DeepQueryService for parsing\n return this.deepQueryService.parse(attribute);\n }\n\n /**\n * Get the DeepQueryService instance used by this query builder\n * Useful for accessing cache statistics and other service methods\n *\n * @returns The DeepQueryService instance\n */\n public getDeepQueryService(): DeepQueryService {\n return this.deepQueryService;\n }\n\n public getProjectionForAttributes(attributes: string[]): Record<string, boolean> {\n const projection: Record<string, boolean> = {};\n attributes.forEach((attribute) => {\n projection[attribute] = true;\n });\n return projection;\n }\n\n public getProjectionForAttributeEntries(\n attributes: QueryAttributeEntry[],\n ): Record<string, boolean | Record<string, boolean>> {\n const projection: Record<string, boolean | Record<string, boolean>> = {\n $: true,\n };\n attributes.forEach((attribute) => {\n if (attribute.isDeepQuery) {\n if (attribute.reference) {\n if (attribute.referenceAttribute && attribute.referenceAttribute.length > 0) {\n if (!projection[attribute.reference]) {\n projection[attribute.reference] = { $: true }; // Add $: true for nested object\n }\n // Recursively handle nested deep queries (e.g., customer.address.city)\n const nestedEntry = this.deepQueryService.parse(attribute.referenceAttribute);\n if (nestedEntry.isDeepQuery && attribute.reference) {\n // Nested deep query - recursively build projection\n const nestedProjection = this.getProjectionForAttributeEntries([nestedEntry]);\n // Merge nested projection (excluding root $: true, but keeping nested ones)\n Object.keys(nestedProjection).forEach((key) => {\n if (key !== '$' && attribute.reference) {\n (projection[attribute.reference] as Record<string, any>)[key] = nestedProjection[key];\n }\n });\n } else if (attribute.reference) {\n // Simple property at this level\n (projection[attribute.reference] as Record<string, boolean>)[attribute.referenceAttribute] = true;\n }\n } else {\n // Array of primitives like someArray[0] – request the whole array\n projection[attribute.reference] = true;\n }\n }\n } else {\n projection[attribute.attribute] = true;\n }\n });\n\n return projection;\n }\n\n // ============================================================================\n // Semantic Method Aliases - Clearer naming for improved developer experience\n // ============================================================================\n\n /**\n * Get all attribute names used in the query.\n * Returns a simple array of unique attribute names.\n *\n * Clearer alternative to {@link getQueryAttributes}.\n *\n * @param query - Optional query to analyze. If not provided, uses current query.\n * @returns Array of attribute names used in the query\n *\n * @example\n * ```typescript\n * builder.addCondition({ operator: 'eq', attribute: 'status', value: 'active' });\n * builder.addCondition({ operator: 'gte', attribute: 'price', value: 100 });\n * const attrs = builder.getAttributeNames(); // ['status', 'price']\n * ```\n */\n public getAttributeNames(query?: DcuplGlobalQueryOptions<any>): string[] {\n return this.getQueryAttributes(query);\n }\n\n /**\n * Get detailed information about attributes used in the query.\n * Returns entries with deep query information (reference, referenceAttribute).\n *\n * Clearer alternative to {@link extractQueryAttributes}.\n *\n * @param query - Query to analyze. Defaults to current query if not provided.\n * @returns Array of QueryAttributeEntry objects with details about each attribute\n *\n * @example\n * ```typescript\n * builder.addCondition({ operator: 'eq', attribute: 'customer.name', value: 'John' });\n * const details = builder.getAttributeDetails();\n * // [{ attribute: 'customer.name', isDeepQuery: true, reference: 'customer', referenceAttribute: 'name' }]\n * ```\n */\n public getAttributeDetails(query?: DcuplGlobalQueryOptions<any>): QueryAttributeEntry[] {\n return this.extractQueryAttributes(query || this.currentQuery);\n }\n\n /**\n * Get attribute names from the current query state.\n * Convenience method that operates on the current query.\n *\n * @returns Array of attribute names in current query\n *\n * @example\n * ```typescript\n * const attrs = builder.getCurrentAttributeNames();\n * ```\n */\n public getCurrentAttributeNames(): string[] {\n return this.getAttributeNames(this.currentQuery);\n }\n\n /**\n * Get detailed attribute information from the current query state.\n * Convenience method that operates on the current query.\n *\n * @returns Array of QueryAttributeEntry objects from current query\n *\n * @example\n * ```typescript\n * const details = builder.getCurrentAttributeDetails();\n * ```\n */\n public getCurrentAttributeDetails(): QueryAttributeEntry[] {\n return this.getAttributeDetails(this.currentQuery);\n }\n\n // ============================================================================\n // Explicit Query Application Methods - Intention-revealing alternatives to applyQuery\n // ============================================================================\n\n /**\n * Add query condition(s) to the current query.\n * Accepts single condition or array of conditions.\n * Each condition is added to its respective attribute group.\n *\n * Clearer alternative to `applyQuery(condition, { mode: 'add' })`.\n *\n * @param condition - Single query condition or array of conditions to add\n * @returns Updated query\n *\n * @example\n * ```typescript\n * // Add single condition\n * builder.addCondition({ operator: 'eq', attribute: 'status', value: 'active' });\n *\n * // Add multiple conditions\n * builder.addCondition([\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]);\n * ```\n */\n public addCondition(condition: DcuplQuery | DcuplQuery[]): DcuplGlobalQueryOptions<any> {\n return this.applyQuery(condition, { mode: 'add' });\n }\n\n /**\n * Add a query group to the current query.\n * If a group with the same groupKey exists, extends it; otherwise creates new group.\n *\n * @param group - Query group to add\n * @returns Updated query\n *\n * @example\n * ```typescript\n * builder.addGroup({\n * groupKey: 'priceRange',\n * groupType: 'and',\n * queries: [\n * { operator: 'gte', attribute: 'price', value: 50 },\n * { operator: 'lte', attribute: 'price', value: 100 }\n * ]\n * });\n * ```\n */\n public addGroup(group: DcuplQueryGroup): DcuplGlobalQueryOptions<any> {\n return this.applyQuery(group, { mode: 'add' });\n }\n\n /**\n * Replace query with new condition(s).\n * Accepts single condition or array of conditions.\n * Each condition replaces all existing conditions for its attribute.\n *\n * Clearer alternative to `applyQuery(condition, { mode: 'set' })`.\n *\n * @param condition - Single query condition or array of conditions to set\n * @returns Updated query\n *\n * @example\n * ```typescript\n * // Replace with single condition\n * builder.setCondition({ operator: 'eq', attribute: 'status', value: 'active' });\n *\n * // Replace with multiple conditions\n * builder.setCondition([\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]);\n * ```\n */\n public setCondition(condition: DcuplQuery | DcuplQuery[]): DcuplGlobalQueryOptions<any> {\n return this.applyQuery(condition, { mode: 'set' });\n }\n\n /**\n * Replace a query group with new definition.\n * If group exists, replaces it; otherwise creates new group.\n *\n * @param group - Query group to set\n * @returns Updated query\n *\n * @example\n * ```typescript\n * builder.setGroup({\n * groupKey: 'priceRange',\n * groupType: 'and',\n * queries: [\n * { operator: 'gte', attribute: 'price', value: 100 },\n * { operator: 'lte', attribute: 'price', value: 200 }\n * ]\n * });\n * ```\n */\n public setGroup(group: DcuplQueryGroup): DcuplGlobalQueryOptions<any> {\n return this.applyQuery(group, { mode: 'set' });\n }\n\n /**\n * Merge a complete query into the current query.\n * Adds all conditions and groups from the provided query.\n *\n * @param query - Complete query to merge\n * @returns Updated query\n *\n * @example\n * ```typescript\n * builder.mergeQuery({\n * modelKey: 'Product',\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]\n * });\n * ```\n */\n public mergeQuery(query: DcuplGlobalQueryOptions<any>): DcuplGlobalQueryOptions<any> {\n if (query.queries && Array.isArray(query.queries)) {\n for (const q of query.queries) {\n this.applyQuery(q, { mode: 'add' });\n }\n }\n return this.currentQuery;\n }\n\n /**\n * Replace the entire current query with a new query.\n * Completely replaces all conditions, groups, and options.\n *\n * @param query - Complete query to replace with\n * @returns Updated query\n *\n * @example\n * ```typescript\n * builder.replaceQuery({\n * modelKey: 'Product',\n * groupType: 'or',\n * queries: [\n * { operator: 'eq', attribute: 'featured', value: true },\n * { operator: 'gte', attribute: 'rating', value: 4.5 }\n * ]\n * });\n * ```\n */\n public replaceQuery(query: DcuplGlobalQueryOptions<any>): DcuplGlobalQueryOptions<any> {\n // Clear current query and set new one\n this.currentQuery.queries = [];\n if (query.groupType) this.currentQuery.groupType = query.groupType;\n if (query.queries && Array.isArray(query.queries)) {\n this.currentQuery.queries = query.queries;\n }\n return this.currentQuery;\n }\n\n // ============================================================================\n // State Management Clarity Methods\n // ============================================================================\n\n /**\n * Clear all query conditions and reset to initial state.\n * Alias for {@link reset} with clearer intent.\n *\n * @returns Empty query\n *\n * @example\n * ```typescript\n * builder.addCondition({ operator: 'eq', attribute: 'status', value: 'active' });\n * builder.clear(); // Removes all conditions\n * ```\n */\n public clear(): DcuplGlobalQueryOptions<any> {\n return this.reset();\n }\n\n /**\n * Remove all query conditions but keep query structure.\n * Alias for {@link removeAllQueries} with clearer intent.\n *\n * @returns Query with no conditions\n *\n * @example\n * ```typescript\n * builder.clearConditions(); // Remove all conditions but keep modelKey, etc.\n * ```\n */\n public clearConditions(): DcuplGlobalQueryOptions<any> {\n return this.removeAllQueries();\n }\n\n /**\n * Get an immutable copy of the current query state.\n * Alias for {@link getQuery} with clearer intent.\n *\n * @returns Deep clone of current query\n *\n * @example\n * ```typescript\n * const snapshot = builder.getCurrentQuery();\n * ```\n */\n public getCurrentQuery(): DcuplGlobalQueryOptions<any> {\n return this.getQuery();\n }\n\n /**\n * Check if the query has any conditions.\n *\n * @returns true if query has no conditions, false otherwise\n *\n * @example\n * ```typescript\n * if (builder.isEmpty()) {\n * console.log('No filters applied');\n * }\n * ```\n */\n public isEmpty(): boolean {\n return this.currentQuery.queries.length === 0;\n }\n\n /**\n * Check if a specific condition exists in the query.\n *\n * @param condition - Condition to check for (partial match)\n * @returns true if condition exists\n *\n * @example\n * ```typescript\n * const hasStatus = builder.hasCondition({ attribute: 'status', value: 'active' });\n * ```\n */\n public hasCondition(condition: Partial<DcuplQuery>): boolean {\n const checkQueries = (queries: Array<DcuplQuery | DcuplQueryGroup>): boolean => {\n for (const query of queries) {\n if (this.isQuery(query)) {\n // Check if all provided properties match\n let matches = true;\n for (const key in condition) {\n if (query[key as keyof DcuplQuery] !== condition[key as keyof DcuplQuery]) {\n matches = false;\n break;\n }\n }\n if (matches) return true;\n } else if (this.isQueryGroup(query)) {\n if (checkQueries(query.queries)) return true;\n }\n }\n return false;\n };\n\n return checkQueries(this.currentQuery.queries);\n }\n\n /**\n * Check if a query group with the given key exists.\n * Alias for {@link hasQueryGroup} with consistent naming.\n *\n * @param groupKey - Group key to check\n * @returns true if group exists\n *\n * @example\n * ```typescript\n * if (builder.hasGroup('priceRange')) {\n * console.log('Price range filter is active');\n * }\n * ```\n */\n public hasGroup(groupKey: string): boolean {\n return this.hasQueryGroup(groupKey);\n }\n\n // ============================================================================\n // Query Inspection Methods\n // ============================================================================\n\n /**\n * Get all unique attribute names used in the query.\n * Convenience alias for {@link getAttributeNames}.\n *\n * @returns Array of attribute names\n *\n * @example\n * ```typescript\n * const attrs = builder.getUsedAttributes(); // ['status', 'price', 'customer.name']\n * ```\n */\n public getUsedAttributes(): string[] {\n return this.getAttributeNames();\n }\n\n /**\n * Get only deep query attributes (e.g., 'customer.name', 'items[0].price').\n * Filters out simple attributes and returns only those with references.\n *\n * @returns Array of deep query attribute details\n *\n * @example\n * ```typescript\n * const deepAttrs = builder.getDeepQueryAttributes();\n * // [{ attribute: 'customer.name', isDeepQuery: true, reference: 'customer', referenceAttribute: 'name' }]\n * ```\n */\n public getDeepQueryAttributes(): QueryAttributeEntry[] {\n const details = this.getAttributeDetails();\n return details.filter((entry) => entry.isDeepQuery);\n }\n\n /**\n * Get all query groups in the current query.\n *\n * @returns Array of query groups\n *\n * @example\n * ```typescript\n * const groups = builder.getGroups();\n * // [{ groupKey: 'priceRange', groupType: 'and', queries: [...] }]\n * ```\n */\n public getGroups(): DcuplQueryGroup[] {\n return this.currentQuery.queries.filter((q) => this.isQueryGroup(q)) as DcuplQueryGroup[];\n }\n\n /**\n * Get top-level conditions (queries not inside groups).\n *\n * @returns Array of top-level query conditions\n *\n * @example\n * ```typescript\n * const topLevel = builder.getTopLevelConditions();\n * ```\n */\n public getTopLevelConditions(): DcuplQuery[] {\n return this.currentQuery.queries.filter((q) => this.isQuery(q)) as DcuplQuery[];\n }\n\n /**\n * Get a specific query group by its key.\n * Alias for {@link getQueryGroup} with consistent naming.\n *\n * @param groupKey - Key of the group to retrieve\n * @returns Query group or undefined if not found\n *\n * @example\n * ```typescript\n * const group = builder.getGroup('priceRange');\n * if (group) {\n * console.log('Price range:', group.queries);\n * }\n * ```\n */\n public getGroup(groupKey?: string): DcuplQueryGroup | undefined {\n return this.getQueryGroup(groupKey);\n }\n\n /**\n * Count total number of conditions in the query (including nested).\n *\n * @returns Total count of query conditions\n *\n * @example\n * ```typescript\n * const count = builder.getConditionCount(); // 5\n * ```\n */\n public getConditionCount(): number {\n const countQueries = (queries: Array<DcuplQuery | DcuplQueryGroup>): number => {\n let count = 0;\n for (const query of queries) {\n if (this.isQuery(query)) {\n count++;\n } else if (this.isQueryGroup(query)) {\n count += countQueries(query.queries);\n }\n }\n return count;\n };\n\n return countQueries(this.currentQuery.queries);\n }\n}\n","import {\n ListItem,\n DcuplFilterOptions,\n InverseIndexReferenceMap,\n DcuplGlobalQueryOptions,\n DcuplQueryGroup,\n DcuplFacet,\n} from './types';\n\nimport { QueryManager } from './queryData';\nimport { DcuplQueryBuilder } from './query-builder';\nimport { cloneDeep } from 'lodash-es';\n\nconst queryBuilder = new DcuplQueryBuilder();\n\n/**\n * Build facet entries for `attribute`.\n *\n * Pipeline order — IMPORTANT, do not shuffle:\n *\n * Fast path (options.sort omitted, default):\n * 1. walk validFacetKeys in insertion order\n * 2. for each key: compute entry (binary size if calculateFacets is false)\n * 3. skip if excludeZeros && size === 0 (does NOT advance count)\n * 4. early-return when filterItemEntries.length === count\n * 5. push and continue\n *\n * Sort path (options.sort set):\n * 1. walk ALL validFacetKeys (ignore count for early-return)\n * 2. for each key: compute entry with FULL size (override calculateFacets: false)\n * 3. push every entry\n * 4. drop zeros if excludeZeros\n * 5. sort by chosen mode (size sorts tie-break on value asc, undefined last)\n * 6. truncate to count\n *\n * The sort path is opt-in because it forfeits both early-return optimizations.\n * Most call sites only need a partial answer and stay on the fast path.\n */\nexport function getFacets<ListType extends ListItem = ListItem>(\n modelData: Map<string, ListType>,\n remoteModelData: Map<string, ListType> | undefined,\n options: DcuplFilterOptions,\n query: DcuplGlobalQueryOptions<any>,\n attribute: string,\n modelIndexMap: InverseIndexReferenceMap,\n queryManager: QueryManager,\n): DcuplFacet[] {\n const queryWithoutCurrentFilter = queryBuilder.removeQuery({ groupKey: attribute }, cloneDeep(query));\n const currentFilteredData = queryManager.queryData(modelData, queryWithoutCurrentFilter, false, modelIndexMap);\n\n const remoteModelDataKeys = remoteModelData ? [...remoteModelData.keys()] : [];\n const validFacetKeys: Set<any> = new Set(remoteModelDataKeys);\n\n // A model with zero items never fills its inverse index for the attribute, so the\n // entry can legitimately be missing (e.g. faceting an empty error-tracking model).\n // Treat it as an empty index instead of crashing.\n const indexMap = modelIndexMap.get(attribute) ?? new Map<any, Set<string>>();\n\n if (!options.excludeUnresolved) {\n for (const key of indexMap.keys()) {\n validFacetKeys.add(key);\n }\n }\n\n if (options.excludeUndefineds) {\n validFacetKeys.delete(undefined);\n } else {\n validFacetKeys.add(undefined);\n }\n\n // Sort path: opt-in, walks all keys, computes full size, sorts, then truncates.\n if (options.sort) {\n const entries: DcuplFacet[] = [];\n\n for (const validFacetKey of validFacetKeys.values()) {\n entries.push(buildFacetEntry(validFacetKey, indexMap, currentFilteredData, remoteModelData, attribute, query, true));\n }\n\n const filtered = options.excludeZeros ? entries.filter((e) => e.size > 0) : entries;\n filtered.sort(getComparator(options.sort));\n\n if (options.count) {\n return filtered.slice(0, options.count);\n }\n return filtered;\n }\n\n // Fast path: original behavior, unchanged. Insertion order, mid-iteration\n // count truncation, honors calculateFacets: false inner break.\n const filterItemEntries: DcuplFacet[] = [];\n\n for (const validFacetKey of validFacetKeys.values()) {\n const facetEntry = buildFacetEntry(\n validFacetKey,\n indexMap,\n currentFilteredData,\n remoteModelData,\n attribute,\n query,\n !!options?.calculateFacets,\n );\n\n if (options?.excludeZeros && facetEntry.size === 0) {\n continue;\n }\n\n if (options?.count && filterItemEntries.length === options?.count) {\n return filterItemEntries;\n }\n\n filterItemEntries.push(facetEntry);\n }\n\n return filterItemEntries;\n}\n\n/**\n * Build one facet entry. When `fullSize` is true, walks every indexed key\n * to compute a real size. When false, breaks after the first match so size\n * is effectively binary (0 or 1) — the existing optimization for callers\n * that only need an enabled/disabled signal.\n */\nfunction buildFacetEntry<ListType extends ListItem>(\n validFacetKey: any,\n indexMap: Map<any, Set<string>>,\n currentFilteredData: Map<string, ListType>,\n remoteModelData: Map<string, ListType> | undefined,\n attribute: string,\n query: DcuplGlobalQueryOptions<any>,\n fullSize: boolean,\n): DcuplFacet {\n const result = new Map<string, ListType>();\n const indexedValueKeys = indexMap.get(validFacetKey) || new Set<string>();\n\n for (const key of indexedValueKeys) {\n const value = currentFilteredData.get(key);\n if (value) {\n result.set(key, value);\n if (!fullSize) {\n break;\n }\n }\n }\n\n return {\n key: String(validFacetKey),\n value: validFacetKey,\n selected: isFilterItemEntrySelected(attribute, validFacetKey, query),\n enabled: result.size > 0,\n size: result.size,\n entry: remoteModelData?.get(validFacetKey) as DcuplFacet['entry'],\n resultKeys: Array.from(result.keys()),\n };\n}\n\n/**\n * Deterministic compare on raw facet values. `undefined` is always last,\n * regardless of direction — keeping \"unset\" out of the way is the common\n * UI need and avoids `undefined` colliding with string/number comparators.\n * Numbers compared numerically, strings lexicographically. Mixed types\n * (not expected for a single attribute) fall back to typeof ordering\n * so the result is still deterministic.\n */\nfunction compareValuesAsc(a: any, b: any): number {\n if (a === undefined && b === undefined) return 0;\n if (a === undefined) return 1;\n if (b === undefined) return -1;\n\n const ta = typeof a;\n const tb = typeof b;\n if (ta !== tb) return ta < tb ? -1 : 1;\n\n if (ta === 'number') return (a as number) - (b as number);\n if (ta === 'string') return (a as string).localeCompare(b as string);\n\n // Fallback for objects/booleans/etc — stringify for a stable order.\n const sa = String(a);\n const sb = String(b);\n return sa < sb ? -1 : sa > sb ? 1 : 0;\n}\n\nfunction getComparator(sort: NonNullable<DcuplFilterOptions['sort']>): (a: DcuplFacet, b: DcuplFacet) => number {\n switch (sort) {\n case 'size-desc':\n return (a, b) => b.size - a.size || compareValuesAsc(a.value, b.value);\n case 'size-asc':\n return (a, b) => a.size - b.size || compareValuesAsc(a.value, b.value);\n case 'value-asc':\n return (a, b) => compareValuesAsc(a.value, b.value);\n case 'value-desc':\n // value-desc: invert the asc result, BUT keep undefined last.\n return (a, b) => {\n if (a.value === undefined && b.value === undefined) return 0;\n if (a.value === undefined) return 1;\n if (b.value === undefined) return -1;\n return -compareValuesAsc(a.value, b.value);\n };\n }\n}\n\nconst isFilterItemEntrySelected = (\n groupKey: string,\n facetKey: string,\n query: DcuplGlobalQueryOptions<any>,\n): boolean => {\n const idx = query.queries.findIndex((query) => queryBuilder.isQueryGroup(query) && query.groupKey === groupKey);\n\n if (idx > -1) {\n const queryGroup = query.queries[idx] as DcuplQueryGroup;\n\n return !!queryGroup.queries.find((query) => {\n if (queryBuilder.isQuery(query)) {\n const key = query.value?.key || query.value;\n return query.attribute === groupKey && key === facetKey;\n }\n return false;\n });\n }\n return false;\n};\n","import { generateObjectHash } from './object-hash';\nimport {\n InverseIndexReferenceMap,\n InverseIndexValuesMap,\n InverseIndexDataMap,\n InverseIndexDataValuesMap,\n ListItem,\n} from './types';\nimport { deepQueryService, getDeepPathValues } from './deep-query.service';\n\n/**\n * Metadata tracked for each index to enable lifecycle management\n */\ninterface IndexMetadata {\n createdAt: number;\n lastAccessedAt: number;\n context: any;\n type: 'shallow' | 'deep';\n}\n\n/**\n * Statistics about indices for monitoring\n */\nexport interface IndexStats {\n shallow: number;\n deep: number;\n total: number;\n oldestAge: number;\n avgAge: number;\n}\n\nexport class IndicesController {\n private indexMap: InverseIndexReferenceMap = new Map();\n // Possible performance gain*\n private dataMap: InverseIndexDataMap = new Map();\n private indexMetadata: Map<string, IndexMetadata> = new Map();\n\n constructor() {}\n\n public reset(attributeKey?: string): void {\n if (attributeKey) {\n this.indexMap.delete(attributeKey);\n this.dataMap.delete(attributeKey);\n this.indexMetadata.delete(attributeKey);\n } else {\n this.indexMap.clear();\n this.dataMap.clear();\n this.indexMetadata.clear();\n }\n }\n\n public fillDataMap(attributeKey: string, attributeValue: Array<string | undefined | ListItem>): void {\n let valueMap = this.dataMap.get(attributeKey);\n\n if (!valueMap) {\n valueMap = new Map();\n this.dataMap.set(attributeKey, valueMap);\n }\n\n for (const value of attributeValue) {\n let keyAsString!: any;\n if (typeof value === 'object' && value && value.key) {\n keyAsString = value.key;\n } else if (Array.isArray(value)) {\n for (const item of value) {\n this.fillDataMap(attributeKey, [item]);\n }\n return;\n } else if (value instanceof Date) {\n // Convert Date objects to ISO string for consistent indexing\n keyAsString = value.toISOString();\n } else {\n keyAsString = value;\n }\n\n let listItem = valueMap!.get(keyAsString);\n if (!listItem) {\n listItem = { key: keyAsString };\n this.dataMap.get(attributeKey)?.set(keyAsString, listItem);\n }\n }\n }\n\n public fillInverseIndex(\n attributeKey: string,\n originKey: string,\n foreignKeys: Array<string | undefined | ListItem>,\n ): void {\n if (!attributeKey || !originKey || !foreignKeys || !Array.isArray(foreignKeys)) {\n return;\n }\n\n const valueMap = this.indexMap.get(attributeKey) || new Map();\n\n for (const value of foreignKeys) {\n let keyAsString!: any;\n\n if (typeof value === 'object' && value && value.key) {\n keyAsString = value.key;\n } else if (Array.isArray(value)) {\n // keyAsString = JSON.stringify(value);\n for (const item of value) {\n this.fillInverseIndex(attributeKey, originKey, [item]);\n }\n return;\n } else if (value instanceof Date) {\n // Convert Date objects to ISO string for consistent indexing\n keyAsString = value.toISOString();\n } else {\n keyAsString = value;\n }\n\n // Get existing Set or create new one\n let valueSet = valueMap.get(keyAsString);\n if (!valueSet) {\n valueSet = new Set<string>();\n valueMap.set(keyAsString, valueSet);\n }\n\n // O(1) duplicate check with Set.add() - automatically handles duplicates\n valueSet.add(originKey);\n }\n this.indexMap.set(attributeKey, valueMap);\n }\n\n public getIndex(attributeKey: string, context?: any): InverseIndexValuesMap | undefined {\n const indexName = this.getIndexName(attributeKey, context);\n\n return this.indexMap.get(indexName);\n }\n\n public getData(attributeKey: string, context?: any): InverseIndexDataValuesMap | undefined {\n const indexName = this.getIndexName(attributeKey, context);\n return this.dataMap.get(indexName);\n }\n\n public getOrCreateIndex(\n attributeKey: string,\n relevantData: Map<string, ListItem>,\n context?: any,\n ): InverseIndexValuesMap {\n const indexName = this.getIndexName(attributeKey, context);\n\n let index = this.getIndex(indexName);\n\n // Track or update metadata\n const existing = this.indexMetadata.get(indexName);\n if (existing) {\n // Update last accessed time for existing index\n existing.lastAccessedAt = Date.now();\n } else if (!index) {\n // Only create metadata when creating new index (not for each access)\n const attributeEntry = deepQueryService.parse(attributeKey);\n const isDeepQuery = attributeEntry.isDeepQuery ?? false;\n\n this.indexMetadata.set(indexName, {\n createdAt: Date.now(),\n lastAccessedAt: Date.now(),\n context,\n type: isDeepQuery ? 'deep' : 'shallow',\n });\n }\n\n if (!index) {\n this.reset(indexName);\n\n this.indexMap.set(indexName, new Map());\n\n // Check if this is a deep query attribute\n const attributeEntry = deepQueryService.parse(attributeKey);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n for (const entry of relevantData.values()) {\n let attributeValues: any[];\n if (isDeepQuery) {\n attributeValues = getDeepPathValues(entry, attributeKey);\n // Preserve undefined facet behavior when path yields no matches\n if (attributeValues.length === 0) {\n attributeValues = [undefined];\n }\n } else {\n attributeValues = [entry[attributeKey]];\n }\n this.fillInverseIndex(indexName, entry.key, attributeValues);\n this.fillDataMap(indexName, attributeValues);\n }\n index = this.getIndex(indexName) || new Map();\n }\n return index!;\n }\n\n public getIndexName(attributeKey: string, context?: any): string {\n return context ? attributeKey + '_' + generateObjectHash(context) : attributeKey;\n }\n\n // ========== PARTIAL UPDATE METHODS ==========\n\n /**\n * Removes a specific item from an inverse index\n * @param attributeKey - The attribute key\n * @param itemKey - The item key to remove\n * @param oldValues - The old attribute values to remove from the index\n */\n public removeFromInverseIndex(\n attributeKey: string,\n itemKey: string,\n oldValues: Array<string | undefined | ListItem>,\n ): void {\n const valueMap = this.indexMap.get(attributeKey);\n if (!valueMap) return;\n\n for (const value of oldValues) {\n let keyAsString!: any;\n\n if (typeof value === 'object' && value && value.key) {\n keyAsString = value.key;\n } else if (Array.isArray(value)) {\n for (const item of value) {\n this.removeFromInverseIndex(attributeKey, itemKey, [item]);\n }\n return;\n } else if (value instanceof Date) {\n // Convert Date objects to ISO string for consistent indexing\n keyAsString = value.toISOString();\n } else {\n keyAsString = value;\n }\n\n const valueSet = valueMap.get(keyAsString);\n if (valueSet) {\n // O(1) deletion with Set\n valueSet.delete(itemKey);\n\n // If Set is now empty, remove the entry\n if (valueSet.size === 0) {\n valueMap.delete(keyAsString);\n }\n }\n }\n }\n\n /**\n * Updates inverse index for a specific item by removing old values and adding new ones\n * @param attributeKey - The attribute key\n * @param itemKey - The item key being updated\n * @param oldValues - The old attribute values to remove\n * @param newValues - The new attribute values to add\n */\n public updateInverseIndex(\n attributeKey: string,\n itemKey: string,\n oldValues: Array<string | undefined | ListItem>,\n newValues: Array<string | undefined | ListItem>,\n ): void {\n // Remove old values\n this.removeFromInverseIndex(attributeKey, itemKey, oldValues);\n\n // Add new values\n this.fillInverseIndex(attributeKey, itemKey, newValues);\n }\n\n /**\n * Removes a specific item from the data map\n * @param attributeKey - The attribute key\n * @param values - The values to remove\n */\n public removeFromDataMap(\n attributeKey: string,\n values: Array<string | undefined | ListItem>,\n ): void {\n const dataMap = this.dataMap.get(attributeKey);\n if (!dataMap) return;\n\n for (const value of values) {\n let keyAsString!: any;\n\n if (typeof value === 'object' && value && value.key) {\n keyAsString = value.key;\n } else if (Array.isArray(value)) {\n for (const item of value) {\n this.removeFromDataMap(attributeKey, [item]);\n }\n return;\n } else if (value instanceof Date) {\n // Convert Date objects to ISO string for consistent indexing\n keyAsString = value.toISOString();\n } else {\n keyAsString = value;\n }\n\n dataMap.delete(keyAsString);\n }\n }\n\n /**\n * Updates data map for a specific item by removing old values and adding new ones\n * @param attributeKey - The attribute key\n * @param oldValues - The old values to remove\n * @param newValues - The new values to add\n */\n public updateDataMap(\n attributeKey: string,\n oldValues: Array<string | undefined | ListItem>,\n newValues: Array<string | undefined | ListItem>,\n ): void {\n // Remove old values\n this.removeFromDataMap(attributeKey, oldValues);\n\n // Add new values\n this.fillDataMap(attributeKey, newValues);\n }\n\n /**\n * Completely removes an item from all indices\n * Used when an item is deleted\n * @param itemKey - The item key to remove\n */\n public removeItemFromAllIndices(itemKey: string): void {\n // Remove from inverse indices\n for (const valueMap of this.indexMap.values()) {\n for (const [value, itemKeys] of valueMap) {\n // O(1) deletion with Set\n if (itemKeys.delete(itemKey)) {\n // If Set is now empty, remove the entry\n if (itemKeys.size === 0) {\n valueMap.delete(value);\n }\n }\n }\n }\n\n // Remove from data maps (item key as value)\n for (const dataMap of this.dataMap.values()) {\n if (dataMap.has(itemKey)) {\n dataMap.delete(itemKey);\n }\n }\n }\n\n /**\n * Resets indices for specific attributes only\n * Useful for partial updates when only certain attributes changed\n * @param attributeKeys - Array of attribute keys to reset\n */\n public resetAttributes(attributeKeys: string[]): void {\n for (const attributeKey of attributeKeys) {\n this.reset(attributeKey);\n }\n }\n\n /**\n * Checks if an index key represents a deep query (contains dots in the attribute path)\n * Uses deep query service for accurate detection\n * @param indexKey - The index key to check\n * @returns true if the index is for a deep query attribute\n */\n private isDeepQueryIndex(indexKey: string): boolean {\n // Remove context hash suffix if present (format: \"attribute_hash\")\n const attributeKey = indexKey.includes('_') ? indexKey.split('_')[0]! : indexKey;\n\n try {\n const entry = deepQueryService.parse(attributeKey);\n return entry.isDeepQuery ?? false;\n } catch {\n // If parsing fails, conservatively assume it's not a deep query\n return false;\n }\n }\n\n /**\n * Gets all deep query index keys currently in the index map\n * @returns Array of index keys that represent deep queries\n */\n public getDeepIndexKeys(): string[] {\n const deepKeys: string[] = [];\n\n for (const indexKey of this.indexMap.keys()) {\n if (this.isDeepQueryIndex(indexKey)) {\n deepKeys.push(indexKey);\n }\n }\n\n return deepKeys;\n }\n\n /**\n * Resets all deep query indices (e.g., \"customer.country\", \"order.customer.address.city\")\n * This is critical for data updates as deep query indices need to be rebuilt\n * when underlying data changes.\n *\n * Shallow indices (direct attributes) are preserved as they're updated incrementally.\n */\n public resetAllDeepIndices(): void {\n const deepKeys = this.getDeepIndexKeys();\n\n for (const indexKey of deepKeys) {\n this.indexMap.delete(indexKey);\n this.dataMap.delete(indexKey);\n }\n }\n\n /**\n * Gets count of indices by type for monitoring and debugging\n * @returns Object with counts of shallow and deep indices\n */\n public getIndexCount(): { shallow: number; deep: number; total: number } {\n let shallow = 0;\n let deep = 0;\n\n for (const indexKey of this.indexMap.keys()) {\n if (this.isDeepQueryIndex(indexKey)) {\n deep++;\n } else {\n shallow++;\n }\n }\n\n return {\n shallow,\n deep,\n total: shallow + deep,\n };\n }\n\n /**\n * Removes stale indices that haven't been accessed recently\n * This helps prevent memory buildup from indices with varying contexts\n * @param maxAge - Maximum age in milliseconds (default: 5 minutes)\n * @returns Number of indices cleaned up\n */\n public cleanupStaleIndices(maxAge: number = 5 * 60 * 1000): number {\n const now = Date.now();\n let cleaned = 0;\n\n for (const [indexKey, metadata] of this.indexMetadata) {\n if (now - metadata.lastAccessedAt > maxAge) {\n this.reset(indexKey);\n cleaned++;\n }\n }\n\n return cleaned;\n }\n\n /**\n * Gets comprehensive statistics about indices for monitoring\n * @returns IndexStats object with counts and age information\n */\n public getStats(): IndexStats {\n const counts = this.getIndexCount();\n const now = Date.now();\n let totalAge = 0;\n let oldestAge = 0;\n\n for (const metadata of this.indexMetadata.values()) {\n const age = now - metadata.createdAt;\n totalAge += age;\n if (age > oldestAge) {\n oldestAge = age;\n }\n }\n\n const avgAge = this.indexMetadata.size > 0 ? totalAge / this.indexMetadata.size : 0;\n\n return {\n shallow: counts.shallow,\n deep: counts.deep,\n total: counts.total,\n oldestAge,\n avgAge,\n };\n }\n}\n","import { getAggregation } from './getAggregation';\nimport { IndicesController } from './indices.controller';\nimport { ListItem, Aggregation, AggregationOptions } from './types';\nimport { deepQueryService } from './deep-query.service';\n\ntype PivotRowOption = {\n attribute: string;\n calculateTotals?: boolean;\n};\n\ntype PivotColumnOption = {\n attribute: string;\n calculateTotals?: boolean;\n};\n\ntype PivotValueOption = AggregationOptions;\n\nexport type PivotOptions = {\n rows: PivotRowOption[];\n columns: PivotColumnOption[];\n values: PivotValueOption[];\n options?: {\n calculateTotals?: boolean;\n };\n};\n\ntype PivotResponseItem = {\n key: string;\n value?: any;\n values?: Aggregation[];\n columns?: Omit<PivotResponseItem, 'rows'>[];\n rows?: PivotResponseItem[];\n};\n\nexport type PivotResponse = PivotResponseItem;\n\nconst pivotPathSeparator = '__';\n\nexport function pivot<ListType extends ListItem = ListItem>(\n relevantData: Map<string, ListType>,\n options: PivotOptions,\n response: PivotResponse = { key: 'root' },\n parentPath = 'root',\n indicesCtrl = new IndicesController(),\n projectedData?: Map<string, ListType>,\n): PivotResponse {\n // Use projected data for indexing if provided, otherwise use relevantData\n const dataForIndexing = projectedData || relevantData;\n\n // Parse and create indices for column attributes (supports deep queries)\n for (const column of options.columns) {\n if (column.attribute) {\n // Parse to ensure deep query syntax is valid\n deepQueryService.parse(column.attribute);\n indicesCtrl.getOrCreateIndex(column.attribute, dataForIndexing);\n }\n }\n\n // Parse and create indices for row attributes (supports deep queries)\n for (const row of options.rows) {\n if (row.attribute) {\n // Parse to ensure deep query syntax is valid\n deepQueryService.parse(row.attribute);\n indicesCtrl.getOrCreateIndex(row.attribute, dataForIndexing);\n }\n }\n\n // Parse and create indices for value attributes (supports deep queries)\n for (const value of options.values) {\n if (value.attribute) {\n // Parse to ensure deep query syntax is valid\n deepQueryService.parse(value.attribute);\n indicesCtrl.getOrCreateIndex(value.attribute, dataForIndexing);\n }\n }\n\n /**\n * Rows\n */\n if (options.rows && options.rows.length > 0) {\n response.rows = response.rows || [];\n\n const item = getPivotRowItem(relevantData, options.rows[0]!, options, indicesCtrl, parentPath, projectedData);\n response.rows.push(item);\n }\n\n /**\n * Columns\n */\n if (options.columns && options.columns.length > 0) {\n response.columns = response.columns || [];\n const item = getPivotColumnItem(relevantData, options.columns[0]!, options, indicesCtrl, parentPath, projectedData);\n response.columns.push(item);\n }\n\n /**\n * Value Aggregations\n */\n if (response.key === 'root') {\n for (const value of options.values) {\n response.values = response.values || [];\n\n if (parentPath === 'root' && options.options?.calculateTotals === false) {\n continue;\n }\n\n const agg = getAggregation(value, relevantData, indicesCtrl.getIndex(value.attribute)!, value.attribute);\n response.values.push(agg);\n }\n }\n\n return response;\n}\n\nfunction getPivotRowItem(\n modelData: Map<string, ListItem>,\n options: PivotRowOption | PivotColumnOption,\n globalOptions: PivotOptions,\n modelIndexMap: IndicesController,\n parentPath = '',\n projectedData?: Map<string, ListItem>,\n): PivotResponseItem {\n const item: PivotResponseItem = { key: options.attribute };\n\n const indexMap = modelIndexMap.getIndex(options.attribute);\n\n if (!indexMap) {\n return item;\n }\n\n for (const [key, itemKeys] of indexMap.entries()) {\n const rowItem: PivotResponseItem = {\n key: key,\n value: key,\n };\n\n const relevantData = new Map();\n\n for (const key of itemKeys) {\n if (!modelData.has(key)) {\n continue;\n }\n\n relevantData.set(key, modelData.get(key));\n }\n\n if (!relevantData.size) continue;\n\n for (const value of globalOptions.values) {\n rowItem.values = rowItem.values || [];\n\n if (parentPath === 'root' && options?.calculateTotals === false) {\n continue;\n }\n\n const agg = getAggregation(value, relevantData, modelIndexMap.getIndex(value.attribute)!, value.attribute);\n rowItem.values.push(agg);\n }\n\n item.rows = item.rows || [];\n item.rows.push(rowItem);\n\n if (globalOptions.rows.length > 1) {\n const newOptions = { ...globalOptions, rows: globalOptions.rows.slice(1) };\n\n pivot(\n relevantData,\n newOptions,\n rowItem,\n parentPath + pivotPathSeparator + options.attribute + pivotPathSeparator + key,\n modelIndexMap,\n projectedData,\n );\n } else {\n if (globalOptions.columns) {\n for (const columnOption of globalOptions.columns) {\n rowItem.columns = rowItem.columns || [];\n const columnItem = getPivotColumnItem(\n relevantData,\n columnOption,\n globalOptions,\n modelIndexMap,\n parentPath + pivotPathSeparator + options.attribute + pivotPathSeparator + key,\n projectedData,\n );\n rowItem.columns.push(columnItem);\n }\n }\n }\n }\n return item;\n}\n\nfunction getPivotColumnItem(\n modelData: Map<string, ListItem>,\n options: PivotRowOption | PivotColumnOption,\n globalOptions: PivotOptions,\n modelIndexMap: IndicesController,\n parentPath = '',\n projectedData?: Map<string, ListItem>,\n): PivotResponseItem {\n const item: PivotResponseItem = { key: options.attribute };\n\n const indexMap = modelIndexMap.getIndex(options.attribute);\n\n if (!indexMap) {\n return item;\n }\n\n for (const [key, itemKeys] of indexMap.entries()) {\n const rowItem: PivotResponseItem = {\n key: key,\n value: key,\n };\n\n const relevantData = new Map();\n for (const key of itemKeys) {\n if (!modelData.has(key)) {\n continue;\n }\n relevantData.set(key, modelData.get(key));\n }\n\n if (!relevantData.size) continue;\n\n for (const value of globalOptions.values) {\n rowItem.values = rowItem.values || [];\n\n if (parentPath === 'root' && options?.calculateTotals === false) {\n continue;\n }\n\n const agg = getAggregation(value, relevantData, modelIndexMap.getIndex(value.attribute)!, value.attribute);\n\n rowItem.values.push(agg);\n }\n\n item.columns = item.columns || [];\n item.columns.push(rowItem);\n\n if (globalOptions.columns.length > 1) {\n const newOptions = { ...globalOptions, columns: globalOptions.columns.slice(1) };\n pivot(\n relevantData,\n newOptions,\n rowItem,\n parentPath + pivotPathSeparator + options.attribute + pivotPathSeparator + key,\n modelIndexMap,\n projectedData,\n );\n }\n }\n\n return item;\n}\n","import { isDateInstance } from './date-utils';\nimport { Property, PropertyType } from './types';\n\nconst ISO_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2})?)?$/;\nconst INT_RE = /^-?\\d+$/;\nconst FLOAT_RE = /^-?\\d+\\.\\d+$/;\nconst DATE_LIKE_RE = /^\\d{1,2}([/.-])\\d{1,2}\\1\\d{2,4}$/;\n\n// NOTE: leading-zero numerics (\"042\") classify as 'int' — zero-padded codes/ZIPs\n// must be pinned to type 'string' manually in the model.\n// Classifies a raw string value into a dcupl PropertyType.\nexport const coerceStringType = (value: string): PropertyType => {\n if (value === 'true' || value === 'false') return 'boolean';\n if (INT_RE.test(value)) return 'int';\n if (FLOAT_RE.test(value)) return 'float';\n if (ISO_DATE_RE.test(value) && !Number.isNaN(Date.parse(value))) return 'date';\n return 'string';\n};\n\n// True for non-ISO values that visually resemble a date (used only for diagnostics hints).\nexport const looksDateLike = (value: string): boolean => DATE_LIKE_RE.test(value);\n\nexport type GetPropertiesForDataOptions = {\n deep?: boolean;\n sampleSize?: number;\n /** @deprecated No longer honored — empty values are always skipped for type\n * inference and counted toward ColumnDiagnostic.emptyRate. */\n ignoreEmpty?: boolean;\n};\n\nconst isEmptyValue = (value: unknown): boolean => value === null || value === undefined || value === '';\n\nexport type ColumnDiagnostic = {\n column: string;\n inferredType: PropertyType;\n observedTypes: PropertyType[];\n // 0..1 — fraction of the rows in which this column was *present* whose value\n // was empty (null/undefined/''). For ragged data this denominator is the\n // column's occurrence count, not the total row count.\n emptyRate: number;\n conflict: boolean; // true when observed types disagree and collapse to 'any'\n hint?: string;\n};\n\nexport type InferModelOptions = GetPropertiesForDataOptions & {\n // When true (default), string values are classified via coerceStringType.\n coerceStrings?: boolean;\n};\n\nexport type InferModelResult = {\n properties: Property[];\n diagnostics: ColumnDiagnostic[];\n};\n\n// Reconciles an observed-type set into a single PropertyType.\nconst reconcileTypes = (types: Set<PropertyType>): { type: PropertyType; conflict: boolean } => {\n const arr = [...types];\n if (arr.length <= 1) return { type: arr[0] ?? 'any', conflict: false };\n if (arr.every((t) => t === 'int' || t === 'float')) return { type: 'float', conflict: false };\n return { type: 'any', conflict: true };\n};\n\n// Infers typed properties + per-column diagnostics from a list of data rows.\nexport const inferModel = (data: any[], options: InferModelOptions = {}): InferModelResult => {\n // sampleSize > 1 implies deep scanning; sampleSize of 0 or 1 behaves as a single-row scan.\n const deep = options.deep ?? (options.sampleSize !== undefined && options.sampleSize > 1);\n const limit = deep ? options.sampleSize ?? Infinity : 1;\n const coerceStrings = options.coerceStrings ?? true;\n\n type Acc = { observed: Set<PropertyType>; empty: number; scanned: number; hint?: string };\n const columns = new Map<string, Acc>();\n const order: string[] = [];\n\n const rowsToScan = Math.min(data.length, limit);\n for (let i = 0; i < rowsToScan; i++) {\n const item = data[i];\n if (!item) continue;\n\n for (const [key, value] of Object.entries(item)) {\n let acc = columns.get(key);\n if (!acc) {\n acc = { observed: new Set(), empty: 0, scanned: 0 };\n columns.set(key, acc);\n order.push(key);\n }\n acc.scanned++;\n\n if (isEmptyValue(value)) {\n acc.empty++;\n continue;\n }\n\n let type: PropertyType;\n if (typeof value === 'string' && coerceStrings) {\n type = coerceStringType(value);\n if (type === 'string' && !acc.hint && looksDateLike(value)) {\n acc.hint = 'column looks date-like (non-ISO) — review manually';\n }\n } else {\n type = getDcuplPropertyTypeForNativeType(value);\n }\n acc.observed.add(type);\n }\n }\n\n const properties: Property[] = [];\n const diagnostics: ColumnDiagnostic[] = [];\n\n for (const key of order) {\n const acc = columns.get(key)!;\n const { type, conflict } = reconcileTypes(acc.observed.size ? acc.observed : new Set<PropertyType>(['string']));\n properties.push({ key, type } as Property);\n diagnostics.push({\n column: key,\n inferredType: type,\n observedTypes: [...acc.observed].sort(),\n emptyRate: acc.scanned === 0 ? 0 : acc.empty / acc.scanned,\n conflict,\n hint: acc.hint,\n });\n }\n\n return { properties, diagnostics };\n};\n\n// returns a list of properties based on the input data.\n// Second arg accepts a boolean for back-compat (`deep` flag) or an options object.\n// NOTE: now string-aware — string values are classified by coerceStringType.\nexport const getPropertiesForData = (\n data: any[],\n deepOrOptions: boolean | GetPropertiesForDataOptions = false,\n): Property[] => {\n const options: GetPropertiesForDataOptions =\n typeof deepOrOptions === 'boolean' ? { deep: deepOrOptions } : deepOrOptions;\n return inferModel(data, options).properties;\n};\n\nexport const isInt = (n: any): boolean => {\n return Number(n) === n && n % 1 === 0;\n};\n\nexport const isFloat = (n: any): boolean => {\n return Number(n) === n && n % 1 !== 0;\n};\n\nexport const getDcuplPropertyTypeForNativeType = (value: any): PropertyType => {\n const type = typeof value;\n\n switch (type) {\n case 'string':\n return 'string';\n case 'number':\n if (isInt(value)) {\n return 'int';\n } else if (isFloat(value)) {\n return 'float';\n } else {\n return 'any';\n }\n case 'boolean':\n return 'boolean';\n case 'object':\n return isDateInstance(value) ? 'date' : 'json';\n default:\n return 'any';\n }\n};\n","import { escapeRegExp } from './helper';\nimport {\n BooleanFilterItem,\n BooleanFilterOptions,\n DateFilterItem,\n DateFilterOptions,\n DcuplQuery,\n DcuplQueryGroup,\n DcuplQueryStatement,\n NumberFilterItem,\n NumberFilterOptions,\n ReferenceFilterItem,\n ReferenceFilterOptions,\n TextFilterItem,\n TextFilterOptions,\n} from './types';\n\nexport const generateTextQuery = (options: TextFilterOptions, filterItem?: TextFilterItem): DcuplQueryGroup => {\n const escapedText = escapeRegExp(options.text || '');\n\n const attribute = filterItem?.attributeKey || options.filterKey;\n\n let query!: DcuplQueryGroup<DcuplQuery>;\n\n const comparisonType = filterItem?.comparisonType || 'containsIgnoreCase';\n\n if (comparisonType === 'containsIgnoreCase') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: `/${escapedText}/`,\n operator: 'find',\n options: {\n transform: ['lowercase'],\n },\n },\n ],\n };\n } else if (comparisonType === 'containsIgnoreCaseAndWhitespace') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: `/${escapedText}/`,\n operator: 'find',\n options: {\n transform: ['removeWhitespace', 'lowercase'],\n },\n },\n ],\n };\n } else if (comparisonType === 'contains') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: `/${escapedText}/`,\n operator: 'find',\n },\n ],\n };\n } else if (comparisonType === 'equal') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: options.text,\n operator: 'eq',\n },\n ],\n };\n } else if (comparisonType === 'equalIgnoreCase') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: options.text,\n operator: 'eq',\n options: {\n transform: ['lowercase'],\n },\n },\n ],\n };\n }\n\n return query;\n};\n\nexport const generateBooleanQuery = (\n options: BooleanFilterOptions,\n filterItem?: BooleanFilterItem,\n): DcuplQueryGroup => {\n const attribute = filterItem?.attributeKey || options.filterKey;\n\n const query: DcuplQueryGroup = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.value,\n attribute: attribute,\n value: options.value,\n operator: 'eq',\n },\n ],\n };\n return query;\n};\n\nexport const generateNumberAndDateQuery = (\n options: NumberFilterOptions | DateFilterOptions,\n filterItem?: NumberFilterItem | DateFilterItem,\n): DcuplQueryGroup => {\n const attribute = filterItem?.attributeKey || options.filterKey;\n const query: DcuplQueryGroup = {\n groupKey: options.filterKey,\n queries: [],\n };\n\n if (typeof options.eq !== 'undefined') {\n query.groupType = 'or';\n if (Array.isArray(options.eq)) {\n options.eq.forEach((value) => {\n query.queries.push({ attribute: attribute, value: value, operator: 'eq' });\n });\n } else {\n query.queries.push({ attribute: attribute, value: options.eq, operator: 'eq' });\n }\n }\n if (typeof options.ne !== 'undefined') {\n query.groupType = 'and';\n if (Array.isArray(options.ne)) {\n options.ne.forEach((value) => {\n query.queries.push({ attribute: attribute, value: value, operator: 'eq', options: { invert: true } });\n });\n } else {\n query.queries.push({\n attribute: attribute,\n value: options.ne,\n operator: 'eq',\n options: { invert: true },\n });\n }\n }\n if (typeof options.gt !== 'undefined') {\n query.groupType = 'and';\n query.queries.push({ attribute: attribute, value: options.gt, operator: 'gt' });\n }\n if (typeof options.lt !== 'undefined') {\n query.groupType = 'and';\n query.queries.push({ attribute: attribute, value: options.lt, operator: 'lt' });\n }\n if (typeof options.gte !== 'undefined') {\n query.groupType = 'and';\n query.queries.push({ attribute: attribute, value: options.gte, operator: 'gte' });\n }\n if (typeof options.lte !== 'undefined') {\n query.groupType = 'and';\n query.queries.push({ attribute: attribute, value: options.lte, operator: 'lte' });\n }\n\n return query;\n};\n\nexport const generateReferenceQuery = (\n options: ReferenceFilterOptions,\n filterItem?: ReferenceFilterItem,\n): DcuplQueryGroup => {\n const attribute = filterItem?.attributeKey || options.filterKey;\n const query: DcuplQueryGroup = {\n groupKey: options.filterKey,\n queries: [],\n };\n\n options.itemKeys.forEach((key) => {\n query.queries.push({\n queryKey: key,\n attribute: attribute,\n value: { key: key },\n operator: 'eq',\n });\n });\n\n return query;\n};\n\nexport const isValidQueryStatement = (query: DcuplQueryStatement): boolean => {\n if (typeof query.modelKey !== 'string') return false;\n\n if (!Array.isArray(query.queries)) return false;\n\n return true;\n};\n","import {\n ListItem,\n DcuplQueryGroup,\n DcuplQuery,\n InverseIndexReferenceMap,\n DcuplQueryTransformOptions,\n CustomOperatorFnMap,\n CustomOperatorFn,\n DcuplGlobalQueryOptions,\n} from './types';\nimport { get, isEqual, find, size, startsWith, isEqualWith, differenceBy, every, endsWith, isMatch } from 'lodash-es';\nimport { DcuplQueryBuilder } from './query-builder';\nimport { ScriptController } from './script.controller';\nimport { ModelParser } from '@dcupl/core/model-parser';\nimport { ItemService } from '@dcupl/core/catalog';\n\nexport class QueryManager {\n private registeredCustomOperator: CustomOperatorFnMap = new Map();\n\n private queryBuilder = new DcuplQueryBuilder();\n\n constructor(\n public scriptController: ScriptController,\n public globalItemService: ItemService,\n public modelParser: ModelParser,\n ) {}\n\n public registerCustomOperator(operator: string, fn: CustomOperatorFn): void {\n this.registeredCustomOperator.set(operator, fn);\n }\n\n public transformValue(value: any, transformers: DcuplQueryTransformOptions[]): any {\n if (Array.isArray(value)) {\n return value.map((val) => this.transformValue(val, transformers));\n }\n\n if (typeof value !== 'string') {\n return value;\n }\n\n for (const transformer of transformers) {\n if (transformer === 'lowercase') {\n value = value.toLowerCase();\n } else if (transformer === 'trim') {\n value = value.trim();\n } else if (transformer === 'removeWhitespace') {\n value = value.replace(/\\s/g, '');\n }\n }\n return value;\n }\n\n private evaluateQuery(query: DcuplQuery, dataEntry: ListItem): boolean {\n let dataEntryValue;\n let queryValue = query.value;\n\n const arrayValueHandling = query.options?.arrayValueHandling || 'some';\n\n // apply transformations to query value\n if (Array.isArray(query.options?.transform) && query.options!.transform.length > 0) {\n queryValue = this.transformValue(queryValue, query.options!.transform);\n }\n\n // important\n // get the data entry value based on the query attribute\n if (query.attribute.includes('.') || query.attribute.includes('[')) {\n dataEntryValue = get(dataEntry, query.attribute);\n } else {\n dataEntryValue = dataEntry[query.attribute];\n }\n\n // apply transformations to data entry value\n if (Array.isArray(query.options?.transform) && query.options!.transform.length > 0) {\n dataEntryValue = this.transformValue(dataEntryValue, query.options!.transform);\n }\n\n // todo.. check if \"find\" should be in this list\n if (typeof dataEntryValue === 'undefined' || dataEntryValue === null) {\n const allowedUndefinedAndNullOperators = ['typeof', 'isTruthy', 'eq'];\n if (!allowedUndefinedAndNullOperators.includes(query.operator)) {\n return false;\n }\n }\n\n switch (query.operator) {\n case 'eq':\n if (!query.options?.invert) {\n return isEqual(dataEntryValue, queryValue);\n } else {\n return !isEqual(dataEntryValue, queryValue);\n }\n case 'find': {\n if (!query.options?.invert) {\n return this.queryFind(dataEntryValue, queryValue, arrayValueHandling);\n } else {\n return !this.queryFind(dataEntryValue, queryValue, arrayValueHandling);\n }\n }\n case 'lt':\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => value < queryValue);\n } else {\n return dataEntryValue.every((value) => value < queryValue);\n }\n } else {\n return dataEntryValue < queryValue;\n }\n case 'gt':\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => value > queryValue);\n } else {\n return dataEntryValue.every((value) => value > queryValue);\n }\n } else {\n return dataEntryValue > queryValue;\n }\n case 'lte':\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => value <= queryValue);\n } else {\n return dataEntryValue.every((value) => value <= queryValue);\n }\n } else {\n return dataEntryValue <= queryValue;\n }\n case 'gte':\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => value >= queryValue);\n } else {\n return dataEntryValue.every((value) => value >= queryValue);\n }\n } else {\n return dataEntryValue >= queryValue;\n }\n case 'typeof':\n if (!query.options?.invert) {\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => typeof value === queryValue);\n } else {\n return dataEntryValue.every((value) => typeof value === queryValue);\n }\n } else {\n return typeof dataEntryValue === queryValue;\n }\n } else {\n return typeof dataEntryValue !== queryValue;\n }\n case 'isTruthy':\n if (queryValue === true) {\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => typeof !!value);\n } else {\n return dataEntryValue.every((value) => typeof !!value);\n }\n } else {\n return !!dataEntryValue;\n }\n } else {\n return !dataEntryValue;\n }\n case 'size':\n if (typeof dataEntryValue === 'string') {\n return dataEntryValue.length === queryValue;\n } else {\n return size(dataEntryValue) === queryValue;\n }\n\n default:\n if (this.scriptController.hasScript((query as any).operator)) {\n return this.scriptController.executeScript((query as any).operator, 'operator', { query, item: dataEntry });\n }\n\n /**\n * @deprecated\n * Use custom script instead\n */\n if (this.registeredCustomOperator.has((query as any).operator)) {\n const fn = this.registeredCustomOperator.get((query as any).operator);\n if (typeof fn === 'function') {\n return fn(query, dataEntry);\n }\n }\n }\n\n return false;\n }\n\n private evaluateEqOperator(\n rootQuery: DcuplGlobalQueryOptions<any>,\n query: DcuplQuery,\n initialDataset: Map<string, ListItem>,\n indexMap: InverseIndexReferenceMap,\n ): Map<string, ListItem> {\n const filteredData: Map<string, ListItem> = new Map();\n\n const indices = indexMap.get(query.attribute);\n if (indices) {\n let value: string | undefined;\n if (typeof query.value === 'string') {\n value = query.value;\n } else if (query.value?.key && Object.keys(query.value).length === 1) {\n value = query.value.key;\n }\n\n if (typeof value !== 'undefined') {\n const indexValues = indices.get(value);\n if (indexValues) {\n indexValues.forEach((key) => {\n if (initialDataset.has(key)) {\n filteredData.set(key, initialDataset.get(key)!);\n }\n });\n\n if (query.options?.invert) {\n const diff = differenceBy(Array.from(initialDataset.values()), Array.from(filteredData.values()), 'key');\n const diffMap = new Map();\n diff.forEach((item) => {\n diffMap.set(item.key, item);\n });\n return diffMap;\n } else {\n return filteredData;\n }\n }\n }\n }\n\n // fallback if indices do not work properly...\n for (let [key, value] of initialDataset) {\n const queryAttributes = this.queryBuilder.extractQueryAttributes({ queries: [query] } as any);\n\n if (queryAttributes.some((attr) => attr.isDeepQuery)) {\n const projection = this.queryBuilder.getProjectionForAttributeEntries(\n queryAttributes.filter((attr) => attr.isDeepQuery),\n );\n\n value = this.globalItemService.getProjectedData(\n projection,\n this.modelParser.models.get(rootQuery.modelKey)!,\n value,\n );\n }\n if (this.evaluateQuery(query, value)) {\n filteredData.set(key, value);\n }\n }\n\n return filteredData;\n }\n\n private equalRegexFn(value: any, queryValue: any): boolean {\n if (typeof queryValue === 'string' && startsWith(queryValue, '/') && endsWith(queryValue, '/')) {\n try {\n const regex = new RegExp(queryValue.slice(1, -1));\n return regex.test(value);\n } catch (_err) {\n return false;\n }\n } else if (Array.isArray(value)) {\n return !!value.find((val) => isEqualWith(val, queryValue, this.equalRegexFn));\n } else if (typeof queryValue === 'object') {\n return isMatch(value, queryValue);\n } else {\n return value === queryValue;\n }\n }\n\n public queryFind(entryValue: any | any[], queryValue: any, arrayValueHandling: 'some' | 'every'): boolean {\n if (Array.isArray(entryValue)) {\n if (typeof queryValue === 'string' || typeof queryValue === 'number' || typeof queryValue === 'boolean') {\n if (arrayValueHandling === 'some') {\n return entryValue.some((value) => isEqualWith(value, queryValue, this.equalRegexFn));\n } else {\n return entryValue.every((value) => isEqualWith(value, queryValue, this.equalRegexFn));\n }\n } else {\n return !!find(entryValue, queryValue);\n }\n } else {\n return isEqualWith(entryValue, queryValue, this.equalRegexFn);\n }\n }\n\n private validateQuery(query: DcuplQuery | DcuplQueryGroup): boolean {\n if (this.queryBuilder.isQuery(query)) {\n if (!query.attribute || !query.operator) {\n throw new Error('invalid query - missing attribute or operator');\n }\n\n if (query.operator === 'find' && Array.isArray(query.value)) {\n throw new Error('invalid query - find operator cannot have array value');\n }\n\n return true;\n } else if (this.queryBuilder.isQueryGroup(query)) {\n return every(query.queries, (q) => this.validateQuery(q));\n } else {\n throw new Error('invalid query - missing query or attribute');\n }\n }\n\n private getEvaluatedQueryDataset(\n rootQuery: DcuplGlobalQueryOptions<any>,\n query: DcuplQuery,\n initialDataset: Map<string, ListItem>,\n indexMap: InverseIndexReferenceMap,\n ): Map<string, ListItem> {\n const filteredData: Map<string, ListItem> = new Map();\n\n if (query.attribute.includes('[*]')) {\n const isInverted = query.options?.invert === true;\n const matchedKeys = new Set<string>();\n\n for (let value of initialDataset.values()) {\n const queryAttributes = this.queryBuilder.extractQueryAttributes({ queries: [query] } as any);\n if (queryAttributes.some((attr) => attr.isDeepQuery)) {\n const projection = this.queryBuilder.getProjectionForAttributeEntries(\n queryAttributes.filter((attr) => attr.isDeepQuery),\n );\n\n value = this.globalItemService.getProjectedData(\n projection,\n this.modelParser.models.get(rootQuery.modelKey)!,\n value,\n );\n }\n\n // Create expanded queries WITHOUT invert - we handle invert at the group level\n const newQuery = this.handleArrayStarQuery(query, value);\n\n // Check if ANY of the expanded queries match (OR semantics)\n let anyMatch = false;\n for (const expandedQuery of newQuery.queries) {\n // Evaluate without invert - we apply invert to the final result\n const queryWithoutInvert = { ...expandedQuery, options: { ...expandedQuery.options, invert: false } };\n if (this.evaluateQuery(queryWithoutInvert, value)) {\n anyMatch = true;\n break;\n }\n }\n\n if (anyMatch) {\n matchedKeys.add(value.key);\n if (!isInverted) {\n filteredData.set(value.key, value);\n }\n }\n }\n\n // If inverted, return items that did NOT match\n if (isInverted) {\n for (const [key, value] of initialDataset) {\n if (!matchedKeys.has(key)) {\n filteredData.set(key, value);\n }\n }\n }\n } else if (query.operator === 'eq') {\n return this.evaluateEqOperator(rootQuery, query, initialDataset, indexMap);\n } else {\n for (let [key, value] of initialDataset) {\n const queryAttributes = this.queryBuilder.extractQueryAttributes({ queries: [query] } as any);\n if (queryAttributes.some((attr) => attr.isDeepQuery)) {\n const projection = this.queryBuilder.getProjectionForAttributeEntries(\n queryAttributes.filter((attr) => attr.isDeepQuery),\n );\n\n value = this.globalItemService.getProjectedData(\n projection,\n this.modelParser.models.get(rootQuery.modelKey)!,\n value,\n );\n }\n\n if (this.evaluateQuery(query, value)) {\n filteredData.set(key, value);\n }\n }\n }\n\n return filteredData;\n }\n\n private handleArrayStarQuery(query: DcuplQuery, value: ListItem): DcuplQueryGroup {\n // regex to match the attribute with [*]\n\n const starIndex = query.attribute.indexOf('[*]');\n const newQueryGroup: DcuplQueryGroup = {\n groupKey: query.queryKey || query.attribute + '[*]',\n groupType: 'or',\n queries: [],\n };\n\n if (starIndex !== -1) {\n // replace [*] with the first match\n const cleanAttribute = query.attribute.substring(0, starIndex);\n // For projected data, the nested refs are already objects, use get() for deep paths\n const arrayValue: any[] = cleanAttribute.includes('.') ? get(value, cleanAttribute) : value[cleanAttribute];\n if (Array.isArray(arrayValue)) {\n for (let i = 0; i < arrayValue.length; i++) {\n const newAttribute = query.attribute.replace('[*]', `[${i}]`);\n newQueryGroup.queries.push({\n attribute: newAttribute,\n operator: query.operator,\n value: query.value,\n options: query.options,\n });\n }\n }\n }\n\n return newQueryGroup;\n }\n\n private evaluateQueryGroup(\n rootQuery: DcuplGlobalQueryOptions<any>,\n queryGroup: DcuplQueryGroup,\n initialDataset: Map<string, ListItem>,\n earlyReturn: boolean,\n indexMap: InverseIndexReferenceMap,\n ): Map<string, ListItem> {\n let filteredData: Map<string, ListItem> = new Map();\n let currentDataset = initialDataset;\n\n if (!queryGroup.queries || queryGroup.queries?.length === 0) {\n return currentDataset;\n }\n\n for (const query of queryGroup.queries) {\n let result = new Map();\n\n if (this.queryBuilder.isQuery(query)) {\n result = this.getEvaluatedQueryDataset(rootQuery, query, currentDataset, indexMap);\n } else {\n result = this.evaluateQueryGroup(rootQuery, query, currentDataset, earlyReturn, indexMap);\n }\n\n if (filteredData.size === 0) {\n filteredData = result;\n }\n\n if (queryGroup.groupType === 'or') {\n result.forEach((item) => {\n filteredData.set(item.key, item);\n });\n } else {\n const diff = differenceBy(Array.from(filteredData.values()), Array.from(result.values()), 'key');\n for (const item of diff) {\n filteredData.delete(item.key);\n }\n }\n\n if (queryGroup.groupType === 'and') {\n currentDataset = filteredData;\n } else {\n currentDataset = initialDataset;\n }\n }\n\n return filteredData;\n }\n\n public queryData<ListType extends ListItem = ListItem>(\n initialData: Map<string, ListType>,\n rootQuery: DcuplGlobalQueryOptions<any>,\n earlyReturn: boolean,\n indexMap: InverseIndexReferenceMap,\n ): Map<string, ListType> {\n const currentDataset = new Map(initialData);\n\n if (rootQuery.queries.length === 0) {\n return initialData;\n }\n\n if (!rootQuery.groupKey) {\n rootQuery.groupKey = 'root';\n }\n if (!rootQuery.groupType) {\n rootQuery.groupType = 'and';\n }\n if (!rootQuery.queries) {\n rootQuery.queries = [];\n }\n\n this.validateQuery(rootQuery);\n\n // const model = this.modelParser.models.get(rootQuery.modelKey);\n\n // const relevantData = this.getRelevantQueryData(currentDataset, rootQuery, model!);\n\n return this.evaluateQueryGroup(rootQuery, rootQuery, currentDataset, earlyReturn, indexMap) as Map<\n string,\n ListType\n >;\n }\n\n // public getRelevantQueryData(data: any, options: DcuplGlobalQueryOptions<any>, model: DcuplModel<ListItem>) {\n // // detect if any of the query attributes are a deep query.\n // // a deep query is a query that references another models property.\n // // if so we need to make a getItems call with the projected data.\n // const attributes = this.queryBuilder.getQueryAttributes(options);\n // const deepQueries = attributes.filter((attr) => {\n // return attr.includes('.') || attr.includes('[');\n // });\n\n // if (deepQueries.length > 0) {\n // const projection: Projection<any> = {\n // $: true,\n // };\n\n // for (const deepQuery of deepQueries) {\n // // split the deep query into modelKey and attribute\n // // e.g. modelA.someAttribute or modelB[0].someAttribute or modelB[*].someAttribute\n // // the result should the modelKey: modelA and attribute: someAttribute\n // // const [modelKey, attribute] = deepQuery.split(new RegExp(/\\.|\\[|\\]/)).filter(Boolean);\n // const parts = deepQuery.split(new RegExp(/\\.|\\[|\\]/)).filter(Boolean);\n // const modelKey = parts[0];\n\n // if (modelKey) {\n // (projection as any)[modelKey] = {\n // $: true,\n // };\n // }\n // }\n\n // const relevantData = this.globalItemService.getItemsAsMap(data, model, {\n // projection,\n // } as DcuplItemsOptions<ListItem>);\n // return relevantData;\n // } else {\n // return data;\n // }\n // }\n}\n","import { generateObjectHash } from './object-hash';\n\n/** Authored `resource.key` values are namespaced so they can never collide with a derived hash. */\nexport const AUTHORED_RESOURCE_ID_PREFIX = 'k:';\nexport const DERIVED_RESOURCE_ID_PREFIX = 'h:';\n\n/**\n * The fields a resource identity is derived from.\n *\n * Structural rather than `AppLoaderConfiguration.Resource` so the same function serves a raw config\n * entry and a runtime progress item — `model` only exists on data resources, `rawUrl` only at runtime.\n */\nexport type ResourceIdentityInput = {\n key?: string;\n type: string;\n url: string;\n rawUrl?: string;\n model?: string;\n};\n\n/**\n * A stable id for one resource.\n *\n * An authored `key` wins and is used verbatim — that id survives a url edit, which is the reason to\n * author keys at all. Otherwise the id is a hash of the RAW url plus type/model. Deriving from the\n * raw url (never the resolved one) is what makes the id identical on both sides of the config↔runtime\n * boundary, since `${variable}` substitution happens only on the runtime side.\n *\n * A derived id therefore changes when the url, type or model changes. That is the accepted limit:\n * it is stable across runs, not across edits.\n *\n * `occurrence` disambiguates config entries that are identical in type/url/model. Prefer\n * `resolveResourceIds` for a list — it assigns occurrences in document order, so reordering the\n * config does not churn ids.\n */\nexport function resolveResourceId(resource: ResourceIdentityInput, occurrence = 0): string {\n if (resource.key) {\n return `${AUTHORED_RESOURCE_ID_PREFIX}${resource.key}`;\n }\n\n const hash = generateObjectHash({\n type: resource.type,\n url: resource.rawUrl ?? resource.url,\n model: resource.model ?? '',\n occurrence,\n });\n\n return `${DERIVED_RESOURCE_ID_PREFIX}${hash}`;\n}\n\n/**\n * Ids for a resource list, positionally aligned with the input, guaranteed unique.\n *\n * Uniqueness is enforced on the OUTPUT, not just on the (type, url, model) basis, because two other\n * things can collide: two entries authored with the same `key` (nothing in the config validates key\n * uniqueness — `loader.resources.get()` simply returns the first match), and a hash collision, which\n * `generateObjectHash` admits by construction since it is a 32-bit rolling hash. Random collisions\n * are vanishingly rare at realistic config sizes, but a duplicate id silently merges two nodes in a\n * consumer's graph, so it is worth ruling out rather than reasoning about.\n */\nexport function resolveResourceIds(resources: readonly ResourceIdentityInput[]): string[] {\n const occurrences = new Map<string, number>();\n const emitted = new Set<string>();\n\n return resources.map((resource) => {\n const basis = `${resource.key ?? ''}|${resource.type}|${resource.rawUrl ?? resource.url}|${resource.model ?? ''}`;\n let occurrence = occurrences.get(basis) ?? 0;\n\n let id = resolveResourceId(resource, occurrence);\n while (emitted.has(id)) {\n occurrence += 1;\n // An authored key ignores `occurrence`, so bumping it alone would loop forever — suffix instead.\n id = resource.key\n ? `${resolveResourceId(resource)}#${occurrence}`\n : resolveResourceId(resource, occurrence);\n }\n\n occurrences.set(basis, occurrence + 1);\n emitted.add(id);\n return id;\n });\n}\n","export type ScriptType = 'script' | 'async_script' | 'sort' | 'operator' | 'validator';\n\nexport class ScriptController {\n private scripts: Map<string, any> = new Map();\n private scriptFunctions: Map<string, any> = new Map();\n constructor() {}\n\n public keys(): any {\n return Array.from(this.scripts.keys());\n }\n\n public setScript(key: string, script: any): void {\n this.scripts.set(key, script);\n }\n\n public removeScript(key: string): void {\n this.scripts.delete(key);\n }\n\n public getScript(key: string): any {\n return this.scripts.get(key);\n }\n\n public hasScript(key: string): boolean {\n return this.scripts.has(key);\n }\n\n private getScriptAsFn(key: string, argsAsKeys: string[]): any {\n if (this.scriptFunctions.has(key)) {\n return this.scriptFunctions.get(key);\n }\n\n const script = this.getScript(key);\n if (!script) {\n throw new Error(`Script ${key} not found`);\n }\n\n const wrapperScript = this.getWrapperScript(script);\n\n const fn = new Function('script_type', ...argsAsKeys, wrapperScript);\n this.scriptFunctions.set(key, fn);\n\n return fn;\n }\n\n private getWrapperScript(script: string): string {\n const wrapperScript = `\n \"use strict\";\n ${script}\n `;\n return wrapperScript;\n }\n\n public executeScript(key: string, type: ScriptType, args: any): any {\n const script = this.getScript(key);\n if (!script) {\n throw new Error(`Script ${key} not found`);\n }\n\n if (!args.context) {\n args.context = args.options || {};\n }\n\n const argsAsKeys = Object.keys(args);\n const argsAsArray = Object.values(args);\n\n const fn = this.getScriptAsFn(key, argsAsKeys);\n\n return fn(type, ...argsAsArray);\n }\n}\n","import { get } from 'lodash-es';\nexport const evaluateTemplate = (template: string | undefined, variables: Record<string, any> | undefined): string => {\n if (!template) {\n return '';\n }\n\n if (!variables) {\n variables = {};\n }\n\n return template.replace(/\\$\\{(.*?)\\}/g, (_, varName) => {\n return get(variables, varName.trim(), '');\n });\n};\n\nexport const parseTemplate = (templateString: string): { variables: { name: string }[] } => {\n const variableNames: Set<string> = new Set();\n\n templateString.replace(/\\$\\{(.*?)\\}/g, (_, varName) => {\n variableNames.add(varName.trim());\n return '';\n });\n\n const variablesArray = Array.from(variableNames).map((name) => ({ name }));\n return { variables: variablesArray };\n};\n","import { AggregationOptions, DcuplGlobalQueryOptions, DcuplQuery, DcuplQueryGroup, RawItem } from '.';\nimport { KeyPropertySpec } from './key-property.types';\nimport { PivotOptions } from '../pivot';\n\ntype AggregationOptionsModel = Omit<AggregationOptions, 'attribute'>;\n\nexport type PropertyType =\n | 'any'\n | 'Array<date>'\n | 'Array<float>'\n | 'Array<int>'\n | 'Array<string>'\n | 'boolean'\n | 'date'\n | 'float'\n | 'int'\n | 'json'\n | 'string';\n\nexport const PropertyTypesAsArray: PropertyType[] = [\n 'any',\n 'Array<date>',\n 'Array<float>',\n 'Array<int>',\n 'Array<string>',\n 'boolean',\n 'date',\n 'float',\n 'int',\n 'json',\n 'string',\n];\n\nexport const NumberPropertyTypes: PropertyType[] = ['int', 'float', 'Array<int>', 'Array<float>'];\nexport const DatePropertyTypes: PropertyType[] = ['date', 'Array<date>'];\n\n/**\n * Configures how property auto-generation inspects input data.\n *\n * - `true` / `false`: enable/disable auto-generation. When enabled with `true`,\n * only the first row of input data is inspected (fast, default behavior).\n * - Object form lets you scan more rows:\n * - `deep: true` walks every row and reconciles types across them.\n * - `sampleSize: N` walks at most N rows (implies deep). Useful for large datasets\n * where row 1 alone may not represent the schema.\n *\n * When more than one row is scanned, empty values (`null`, `undefined`, `''`)\n * are ignored during inference so a single blank cell does not poison a column's type.\n */\nexport type AutoGeneratePropertiesOption =\n | boolean\n | {\n deep?: boolean;\n sampleSize?: number;\n };\n\n/**\n * must be singleValued or multiValued\n * singleValued contains only one key to an object\n * multiValued contains multiple key of objects\n *\n * @minLength 1\n * @chance {\n * \"pickone\": [ [ \"singleValued\", \"multiValued\" ] ]\n * }\n * @ignoreThis 2\n * @important\n */\nexport type ReferenceType = 'singleValued' | 'multiValued';\n\nexport type AttributeType = PropertyType | ReferenceType;\n\nexport type ModelMetadata = {\n name: string;\n description?: string;\n [key: string]: any;\n};\n\nexport type FilterComparisonType =\n | 'equal'\n | 'equalIgnoreCase'\n | 'contains'\n | 'containsIgnoreCase'\n | 'containsIgnoreCaseAndWhitespace';\n\nexport type PropertyAggregate = (PropertyNumber | PropertyString) & {\n aggregate: true;\n};\n\nexport type FilterStringArray = {\n key: string;\n comparisonType: FilterComparisonType;\n};\n\nexport type FilterStringSingle = {\n comparisonType?: FilterComparisonType;\n};\n\n/**\n * A property is a quality or characteristic belonging an object.\n * @defaultSnippets {\"label\":\"Property\", \"properties\":[\"key\",\"type\"]}\n *\n */\nexport type PropertyBase = {\n /**\n * A key is an unique identifier overall properties in this model.\n *\n * @minLength 1\n * @important\n */\n key: string;\n type: PropertyType;\n\n expression?: string;\n\n derive?: {\n localReference: string;\n remoteProperty: string;\n separator?: string;\n };\n\n origin?: string;\n filter?: boolean | FilterStringSingle | FilterStringArray[];\n aggregate?: boolean | AggregationOptionsModel;\n meta?: ModelMetadata;\n index?: boolean;\n quality?: AttributeQualityConfig;\n};\n\n/**\n * @defaultSnippets { \"label\": \"String Property\", \"properties\": [\"key\", \"type:string:boolean\"] }\n */\nexport type PropertyBoolean = PropertyBase & {\n type: 'boolean';\n aggregate?: boolean | AggregationOptionsModel;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Int Property\", \"properties\": [\"key\", \"type:int\"] }\n */\n\nexport type PropertyInt = PropertyBase & {\n type: 'int';\n aggregate?: boolean | AggregationOptionsModel;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Int Array Property\", \"properties\": [\"key\", \"type:Array<int>\"] }\n */\n\nexport type PropertyIntArray = PropertyBase & {\n type: 'Array<int>';\n separator?: string;\n aggregate?: boolean | AggregationOptionsModel;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Float Property\", \"properties\": [\"key\", \"type:float\"] }\n */\nexport type PropertyFloat = PropertyBase & {\n type: 'float';\n aggregate?: boolean | AggregationOptionsModel;\n fractionDigits?: number;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Float Property\", \"properties\": [\"key\", \"type:float\"] }\n */\nexport type PropertyFloatArray = PropertyBase & {\n type: 'Array<float>';\n separator?: string;\n aggregate?: boolean | AggregationOptionsModel;\n fractionDigits?: number;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Json Property\", \"properties\": [\"key\", \"type:string:json\"] }\n */\nexport type PropertyJson = PropertyBase & {\n type: 'json' | 'Array<json>';\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Any Property\", \"properties\": [\"key\", \"type:any\"] }\n */\nexport type PropertyAny = PropertyBase & {\n type: 'any' | 'Array<any>';\n};\n\n/**\n * Example: 'yyyy-MM-dd' | 'dd.MM.yyyy' | ...\n */\n// export type PropertyDateInputFormat = 'date' | 'ts_seconds' | 'ts_milliseconds' | 'string' | string;\nexport type PropertyDateInputFormat = string;\n// export type PropertyDateOutputFormat = 'date' | 'ts_seconds' | 'ts_milliseconds' | 'string';\n\n/**\n * @defaultSnippets { \"label\": \"Date Property\", \"properties\": [\"key\", \"type:date\"] }\n */\nexport type PropertyDate = PropertyBase & {\n type: 'date';\n inputFormat?: PropertyDateInputFormat;\n UTC?: boolean;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Date Property\", \"properties\": [\"key\", \"type:date\"] }\n */\nexport type PropertyDateArray = PropertyBase & {\n type: 'Array<date>';\n separator?: string;\n inputFormat?: PropertyDateInputFormat;\n UTC?: boolean;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"String Property\", \"properties\": [\"key\", \"type:string:string\"] }\n */\nexport type PropertyString = PropertyBase & {\n type: 'string';\n aggregate?: boolean | AggregationOptionsModel;\n filter?: boolean | FilterStringSingle | FilterStringArray[];\n};\n\n/**\n * @defaultSnippets { \"label\": \"String Property\", \"properties\": [\"key\", \"type:string:string\"] }\n */\nexport type PropertyStringArray = PropertyBase & {\n type: 'Array<string>';\n separator?: string;\n filter?: boolean | FilterStringSingle | FilterStringArray[];\n aggregate?: boolean | AggregationOptionsModel;\n};\n\nexport type PropertyNumber = PropertyFloat | PropertyInt | PropertyFloatArray | PropertyIntArray;\nexport type Property =\n | PropertyString\n | PropertyStringArray\n | PropertyInt\n | PropertyIntArray\n | PropertyFloat\n | PropertyFloatArray\n | PropertyDate\n | PropertyDateArray\n | PropertyJson\n | PropertyBoolean\n | PropertyAny;\n\n/**\n * Facet calculation is disabled by default\n */\nexport type ReferenceFilterDefinition = {\n calculateFacets?: boolean;\n excludeZeros?: boolean;\n excludeUndefineds?: boolean;\n};\n\nexport type BooleanFilterDefinition = {\n calculateFacets?: boolean;\n excludeUndefineds?: boolean;\n};\n\nexport type ReferenceValidity = 'loose' | 'strict';\n\n/**\n * @defaultSnippets { \"label\": \"Default Reference\", \"properties\": [\"key\", \"type:singleValued\", \"model\"] }\n */\nexport type ReferenceBase = {\n /**\n * A key is an unique identifier for all reference in this model.\n *\n * @minLength 1\n * @important\n */\n key: string;\n type: ReferenceType;\n /**\n * The Model this reference is pointing to.\n *\n * @minLength 1\n * @important\n */\n model: string;\n\n origin?: string;\n filter?: boolean | ReferenceFilterDefinition;\n aggregate?: boolean | AggregationOptionsModel;\n separator?: string;\n meta?: ModelMetadata;\n index?: boolean;\n validity?: ReferenceValidity;\n quality?: AttributeQualityConfig;\n};\n\nexport type AggregatedReference = ReferenceBase & {\n aggregate: true;\n};\n\n/**\n * Resolves a Reference from a foreign model\n *\n * @defaultSnippets { \"label\": \"Resolved Reference\", \"properties\": [\"key\", \"type:multiValued\", \"model\", \"resolve:json:{\\\"reference\\\":\\\"\\\", \\\"model\\\":\\\"\\\" }\"] }\n */\nexport type ResolvedReference = ReferenceBase & {\n // type: 'multiValued';\n /**\n * Resolve a Relationship to an foreign model\n *\n * @defaultSnippets {\"label\":\"Resolve Relationship\", \"properties\":[\"reference\",\"model\"]}\n */\n resolve: {\n /**\n * The reference on the foreign object which should be used for resolve the relation.\n *\n * @minLength 1\n * @important\n */\n reference: string;\n\n /**\n * The model of the foreign object reference.\n *\n * @minLength 1\n * @important\n */\n model: string;\n };\n};\n\n/**\n * Queries a Reference from a foreign model\n *\n * @defaultSnippets { \"label\": \"Resolved Reference\", \"properties\": [\"key\", \"type:multiValued\", \"model\", \"resolve:json:{\\\"reference\\\":\\\"\\\", \\\"model\\\":\\\"\\\" }\"] }\n */\nexport type QueryReference = ReferenceBase & {\n // type: 'multiValued';\n /**\n * Resolve a Relationship to an foreign model\n *\n * @defaultSnippets {\"label\":\"Resolve Relationship\", \"properties\":[\"reference\",\"model\"]}\n */\n query: DcuplGlobalQueryOptions<any> | DcuplQuery | DcuplQueryGroup | DcuplQuery[] | DcuplQueryGroup[];\n};\n\n/**\n * Derives a References for a specific value from an other model\n *\n * @defaultSnippets { \"label\": \"Derived Reference\", \"properties\": [\"key\", \"type:singleValued\", \"model\", \"derive:json:{\\\"localReference\\\":\\\"\\\", \\\"remoteReference\\\":\\\"\\\"}\"] }\n */\nexport type DerivedReference = ReferenceBase & {\n type: ReferenceType;\n /**\n * Derive a specific value from an other object/entity\n * @defaultSnippets {\"label\":\"Derive Values\", \"properties\": [\"localReference\",\"remoteReference\"]}\n */\n derive: {\n /**\n * The reference on the foreign object which should be used for resolve the relation.\n *\n * @minLength 1\n * @important\n */\n localReference: string;\n /**\n * The reference on the foreign object which should be derived.\n *\n * @minLength 1\n * @important\n */\n remoteReference: string;\n };\n};\n\n/**\n * Groups a Reference by a specific value from an other model\n *\n * @defaultSnippets { \"label\": \"Grouped Reference\", \"properties\": [\"key\", \"type:singleValued\", \"model\", \"groupBy:json:{\\\"reference\\\":\\\"\\\" }\", \"includeSelfInGroup:boolean\"] }\n */\nexport type GroupedRefeference = ReferenceBase & {\n type: ReferenceType;\n /**\n * Group by a specific reference\n * @defaultSnippets {\"label\":\"Grouped Values\", \"properties\": [\"reference\",\"includeSelfInGroup\"]}\n */\n groupBy: {\n /**\n * The reference that groups are generated from\n *\n * @minLength 1\n * @important\n */\n reference: string;\n\n includeSelfInGroup?: boolean;\n };\n};\n\nexport type Reference = ResolvedReference | DerivedReference | GroupedRefeference | ReferenceBase | QueryReference;\n\nexport type ValueMappingValue = string | '$dcupl_falsy';\n\nexport type ValueMappingValues = {\n from: ValueMappingValue[];\n to: any;\n};\n\nexport type ValueMappingConfig = {\n attributes: string[];\n values: ValueMappingValues[];\n};\n\nexport type ModelAttribute = Property | Reference;\n\nexport type ModelQualityConfig = {\n /**\n * Master switch for quality checks on this model. Default: true.\n * Checks only run when error tracking is also enabled globally\n * (two-tier enablement).\n */\n enabled?: boolean;\n /**\n * Model-wide defaults for the attribute quality FLAGS\n * (required / nullable / forceStrictDataType / validatorHandling),\n * merged under each attribute's own `quality`.\n * Concrete `validators` are per-attribute only and cannot be set here.\n */\n attributes?: Omit<AttributeQualityConfig, 'validators'>;\n};\n\nexport type ValidatorErrorType = 'loose' | 'strict';\n\nexport type ValidatorConfig = {\n value?: any;\n // errorType?: ValidatorErrorType;\n};\n\nexport type CustomValidatorConfig = {\n key: string;\n value?: any;\n validatorHandling?: ValidatorErrorType;\n};\n\nexport type AttributeValidatorConfig = {\n // custom?: CustomValidatorConfig;\n\n email?: ValidatorConfig;\n endsWith?: ValidatorConfig;\n enum?: ValidatorConfig;\n includes?: ValidatorConfig;\n max?: ValidatorConfig;\n maxLength?: ValidatorConfig;\n min?: ValidatorConfig;\n minLength?: ValidatorConfig;\n pattern?: ValidatorConfig;\n unique?: ValidatorConfig;\n startsWith?: ValidatorConfig;\n};\n\n/**\n * Quality rules for a single property or reference.\n *\n * Defaults: required: true, nullable: false, forceStrictDataType: false,\n * validatorHandling: 'loose'.\n *\n * validatorHandling decides what happens when a validator fails:\n * 'loose' (default) reports an InvalidValidator error and keeps the value;\n * 'strict' reports AND drops the value from the loaded data.\n * forceStrictDataType: true disables type coercion — wrong-typed raw\n * values are reported as WrongDataType and dropped.\n */\nexport type AttributeQualityConfig = {\n required?: boolean;\n nullable?: boolean;\n forceStrictDataType?: boolean;\n validators?: AttributeValidatorConfig;\n validatorHandling?: ValidatorErrorType;\n};\n\n/**\n * A model describes a real world object and hols attributes.\n *\n * @defaultSnippets {\"label\":\"Model Definition\", \"properties\":[\"key:string:myModel\", \"properties:json:[{\\\"key\\\":\\\"my_property\\\", \\\"type\\\": \\\"string\\\"}]\", \"references:array\"]}\n */\nexport type ModelDefinition<ModelNames extends string = string> = {\n /**\n * The key is an unique identifier overall models in the application.\n *\n * @minLength 1\n */\n key: ModelNames;\n properties?: Property[];\n references?: Reference[];\n data?: RawItem[];\n\n /**\n * Property name(s) on each row that uniquely identify it.\n * See `KeyPropertySpec` for composite-key behavior.\n */\n keyProperty?: KeyPropertySpec;\n /**\n * Separator joining composite `keyProperty` parts. Default `'::'`.\n */\n keyPropertySeparator?: string;\n autoGenerateKey?: boolean;\n autoGenerateProperties?: AutoGeneratePropertiesOption;\n supportsAutoCreation?: boolean;\n valueMappings?: ValueMappingConfig[];\n meta?: ModelMetadata;\n quality?: ModelQualityConfig;\n};\n\nexport type DcuplViewType = 'section' | 'pivot';\n\nexport type ViewDefinitionBase = {\n /**\n * The key is an unique identifier overall models in the application.\n *\n * @minLength 1\n */\n key: string;\n /**\n * @minLength 1\n */\n model: string;\n /**\n * @minLength 1\n */\n type: DcuplViewType;\n\n meta?: ModelMetadata;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Dcupl Section View\", \"properties\": [\"key\", \"model\", \"type:section\", \"primaryAttribute\", \"aggregates:array\"] }\n */\n\nexport type ViewDefinitionSection = ViewDefinitionBase & {\n type: 'section';\n\n /**\n * @minLength 1\n */\n\n primaryAttribute: string;\n\n aggregates: AggregationOptions[];\n};\n\n/**\n * @defaultSnippets { \"label\": \"Dcupl Pivot View\", \"properties\": [\"key\", \"model\", \"type:pivot\", \"options:json:{\\\"rows\\\":[{\\\"attribute\\\": \\\"\\\"}], \\\"columns\\\":[{\\\"attribute\\\": \\\"\\\"}], \\\"values\\\":[{\\\"attribute\\\": \\\"\\\", \\\"types\\\": [\\\"sum\\\"]}]}\"] }\n */\nexport type ViewDefinitionPivot = ViewDefinitionBase & {\n type: 'pivot';\n options: PivotOptions;\n};\n\nexport type ViewDefinition = ViewDefinitionSection | ViewDefinitionPivot;\n\n/**\n * Container for ModelDescriptions & Views\n *\n */\nexport type Model = ViewDefinition | ModelDefinition | Array<ModelDefinition | ViewDefinition>;\n","import { ListItem } from './data.types';\nimport { Projection, SortingProjection } from './projection.types';\n\n/**\n * Transform options for query values.\n * - `lowercase`: Convert value to lowercase before comparison\n * - `trim`: Remove leading/trailing whitespace\n * - `removeWhitespace`: Remove all whitespace characters\n */\nexport type DcuplQueryTransformOptions = 'lowercase' | 'trim' | 'removeWhitespace';\n\nexport const DcuplQueryOperatorsAsArray = [\n 'eq',\n 'find',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'typeof',\n 'isTruthy',\n 'size',\n] as const;\n\n/**\n * Query operators for filtering data.\n *\n * Available operators:\n * - `eq`: Equality check - matches exact values\n * - `find`: Regex / exact / object match - bare string is exact-equality, `/regex/` for substring/pattern, object for property match\n * - `gt`: Greater than - numeric/date comparison (exclusive)\n * - `gte`: Greater than or equal - numeric/date comparison (inclusive)\n * - `lt`: Less than - numeric/date comparison (exclusive)\n * - `lte`: Less than or equal - numeric/date comparison (inclusive)\n * - `typeof`: Type check - validates data type\n * - `isTruthy`: Boolean check - evaluates truthiness\n * - `size`: Array/string length check - matches collection size\n *\n * @example\n * ```typescript\n * // Equality\n * { operator: 'eq', attribute: 'status', value: 'active' }\n *\n * // Numeric comparison\n * { operator: 'gte', attribute: 'price', value: 100 }\n *\n * // Substring search (slash-delimited regex; bare string would be exact-match)\n * { operator: 'find', attribute: 'name', value: '/search term/' }\n * ```\n */\nexport type DcuplQueryOperator = (typeof DcuplQueryOperatorsAsArray)[number];\n\n/**\n * Query combination logic.\n * - `and`: All conditions must match (intersection)\n * - `or`: At least one condition must match (union)\n *\n * @example\n * ```typescript\n * // AND logic - both conditions must be true\n * { groupType: 'and', queries: [condition1, condition2] }\n *\n * // OR logic - either condition can be true\n * { groupType: 'or', queries: [condition1, condition2] }\n * ```\n */\nexport type DcuplQueryType = 'and' | 'or';\n\n/**\n * Base query properties shared by all query types.\n */\ntype DcuplQueryBase = {\n /**\n * The attribute/property name to query against.\n * Can reference nested properties using dot notation (e.g., 'user.email').\n */\n attribute: string;\n\n /**\n * Optional identifier for this query condition.\n * Useful for tracking or removing specific queries.\n */\n queryKey?: any;\n\n /**\n * Optional discriminator for type narrowing.\n * @internal\n */\n __type?: 'query';\n\n /**\n * Query behavior modifiers.\n */\n options?: {\n /**\n * Transform value before comparison.\n * Applied in order: ['lowercase', 'trim'] applies lowercase first, then trim.\n */\n transform?: DcuplQueryTransformOptions[];\n\n /**\n * Invert the query result (NOT operation).\n * @example\n * ```typescript\n * // Find items where status is NOT 'active'\n * { operator: 'eq', attribute: 'status', value: 'active', options: { invert: true } }\n * ```\n */\n invert?: boolean;\n\n /**\n * When querying array values, determines match behavior.\n * - `some`: Match if at least one array element matches (default)\n * - `every`: Match only if all array elements match\n *\n * @example\n * ```typescript\n * // Match items where ANY tag equals 'featured'\n * { operator: 'eq', attribute: 'tags', value: 'featured', options: { arrayValueHandling: 'some' } }\n *\n * // Match items where ALL tags equal 'featured'\n * { operator: 'eq', attribute: 'tags', value: 'featured', options: { arrayValueHandling: 'every' } }\n * ```\n */\n arrayValueHandling?: 'some' | 'every';\n };\n};\n\n/**\n * Equality query - matches exact values.\n *\n * @example\n * ```typescript\n * // Match items with status 'active'\n * { operator: 'eq', attribute: 'status', value: 'active' }\n *\n * // Match items with a reference key\n * { operator: 'eq', attribute: 'categoryId', value: { key: 'cat-123' } }\n *\n * // Case-insensitive match\n * { operator: 'eq', attribute: 'name', value: 'john', options: { transform: ['lowercase'] } }\n * ```\n */\ntype DcuplQueryEq = DcuplQueryBase & {\n operator: 'eq';\n /**\n * Value to match against.\n * Note: For indexed search optimization, use string or { key: string } values.\n */\n value: any;\n};\n\n/**\n * Find query - regex / exact / object match.\n *\n * Matching depends on the shape of `value`:\n * - **Bare string** (e.g. `'search'`) — exact-equality against the *whole* field\n * value, NOT a substring. `find` with `'Ball'` does not match `'Soccer Ball'`.\n * - **Slash-delimited regex** (e.g. `'/search/'`, `'/^foo/i'`) — JS regex literal;\n * use this for substring and pattern matching.\n * - **Object** (e.g. `{ key: 'value' }`) — property matching against the field.\n *\n * @example\n * ```typescript\n * // Substring search — slash-delimited regex (bare string would be exact-match)\n * { operator: 'find', attribute: 'description', value: '/search/' }\n *\n * // Exact-match the whole field value\n * { operator: 'find', attribute: 'status', value: 'active' }\n *\n * // Find items with specific nested property\n * { operator: 'find', attribute: 'metadata', value: { key: 'value' } }\n * ```\n */\ntype DcuplQueryFind = DcuplQueryBase & {\n operator: 'find';\n /**\n * Bare string (exact-match), `/regex/` (substring/pattern), or object (property match).\n */\n value: Record<string, unknown> | string;\n};\n\n/**\n * Numeric/date comparison queries.\n * Supports greater than, less than, and inclusive variants.\n *\n * @example\n * ```typescript\n * // Items with price greater than 100\n * { operator: 'gt', attribute: 'price', value: 100 }\n *\n * // Items created after a date\n * { operator: 'gte', attribute: 'createdAt', value: new Date('2024-01-01') }\n *\n * // Items with quantity less than or equal to 10\n * { operator: 'lte', attribute: 'quantity', value: 10 }\n * ```\n */\ntype DcuplQueryNumbers = DcuplQueryBase & {\n operator: 'gt' | 'lt' | 'gte' | 'lte';\n /**\n * Numeric value, date, or string representation for comparison.\n */\n value: string | number | Date;\n};\n\n/**\n * Type check query - validates data type.\n *\n * @example\n * ```typescript\n * // Match items where price is a number\n * { operator: 'typeof', attribute: 'price', value: 'number' }\n *\n * // Match items where tags is an array\n * { operator: 'typeof', attribute: 'tags', value: 'object' }\n * ```\n */\ntype DcuplQueryTypeOf = DcuplQueryBase & {\n operator: 'typeof';\n /**\n * JavaScript type name: 'string', 'number', 'boolean', 'object', 'undefined', 'function', 'symbol'\n */\n value: string;\n};\n\n/**\n * Truthiness check query - evaluates boolean truthiness.\n *\n * @example\n * ```typescript\n * // Match items where isActive is truthy\n * { operator: 'isTruthy', attribute: 'isActive', value: true }\n *\n * // Match items where isDeleted is falsy\n * { operator: 'isTruthy', attribute: 'isDeleted', value: false }\n * ```\n */\ntype DcuplQueryIsTruthy = DcuplQueryBase & {\n operator: 'isTruthy';\n /**\n * Expected truthiness: true for truthy values, false for falsy values.\n */\n value: boolean;\n};\n\n/**\n * Size check query - matches array length or string length.\n *\n * @example\n * ```typescript\n * // Match items with exactly 3 tags\n * { operator: 'size', attribute: 'tags', value: 3 }\n *\n * // Match items with name exactly 10 characters long\n * { operator: 'size', attribute: 'name', value: 10 }\n * ```\n */\ntype DcuplQuerySize = DcuplQueryBase & {\n operator: 'size';\n /**\n * Expected length/size of the array or string.\n */\n value: number;\n};\n\n/**\n * Query condition - represents a single filter criterion.\n * Use this to filter items based on attribute values.\n *\n * @see QueryCondition - Type alias with clearer naming\n *\n * @example\n * ```typescript\n * // Simple equality check\n * const query: DcuplQuery = {\n * operator: 'eq',\n * attribute: 'status',\n * value: 'active'\n * };\n *\n * // Numeric comparison with options\n * const priceQuery: DcuplQuery = {\n * operator: 'gte',\n * attribute: 'price',\n * value: 100,\n * options: { invert: false }\n * };\n * ```\n */\nexport type DcuplQuery =\n | DcuplQueryEq\n | DcuplQueryNumbers\n | DcuplQueryTypeOf\n | DcuplQueryIsTruthy\n | DcuplQuerySize\n | DcuplQueryFind;\n\n/**\n * Query group - combines multiple conditions with AND/OR logic.\n * Groups can be nested to create complex filter expressions.\n *\n * @see QueryConditionGroup - Type alias with clearer naming\n *\n * @example\n * ```typescript\n * // AND group - all conditions must match\n * const andGroup: DcuplQueryGroup = {\n * groupType: 'and',\n * groupKey: 'active-products',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]\n * };\n *\n * // OR group - at least one condition must match\n * const orGroup: DcuplQueryGroup = {\n * groupType: 'or',\n * queries: [\n * { operator: 'eq', attribute: 'category', value: 'electronics' },\n * { operator: 'eq', attribute: 'category', value: 'books' }\n * ]\n * };\n *\n * // Nested groups\n * const nestedGroup: DcuplQueryGroup = {\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * {\n * groupType: 'or',\n * queries: [\n * { operator: 'eq', attribute: 'featured', value: true },\n * { operator: 'gte', attribute: 'rating', value: 4.5 }\n * ]\n * }\n * ]\n * };\n * ```\n */\nexport type DcuplQueryGroup<ChildQuery extends DcuplQuery | DcuplQueryGroup = any> = {\n /**\n * Optional discriminator for type narrowing.\n * @internal\n */\n __type?: 'group';\n\n /**\n * Combination logic for conditions in this group.\n * Defaults to 'and' if not specified.\n */\n groupType?: DcuplQueryType;\n\n /**\n * Optional identifier for this group.\n * Useful for tracking or removing specific query groups.\n */\n groupKey?: string;\n\n /**\n * Array of query conditions or nested groups.\n */\n queries: Array<ChildQuery>;\n};\n\n/**\n * Common options for data retrieval operations.\n * Controls pagination, projection, and sorting.\n *\n * @see ItemDataOptions - Type alias with clearer naming\n */\ntype DcuplItemOptions<T> = {\n /**\n * Starting index for pagination (0-based).\n * @example start: 10 // Skip first 10 items\n */\n start?: number;\n\n /**\n * Maximum number of items to return.\n * @example count: 20 // Return up to 20 items\n */\n count?: number;\n\n /**\n * Specify which properties to include/exclude in results.\n */\n projection?: Projection<T>;\n\n /**\n * Define sorting order for results.\n */\n sort?: SortingProjection<T>;\n};\n\n/**\n * Options for executing queries across the global dcupl instance.\n * Combines model specification, query conditions, and data retrieval options.\n *\n * @see QueryExecutionContext - Type alias with clearer naming\n *\n * @example\n * ```typescript\n * const options: DcuplGlobalQueryOptions<Product> = {\n * modelKey: 'Product',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' }\n * ],\n * start: 0,\n * count: 20,\n * sort: { price: 'asc' }\n * };\n * ```\n */\nexport type DcuplGlobalQueryOptions<T> = {\n /**\n * Model identifier to query against.\n */\n modelKey: string;\n} & DcuplItemOptions<T> &\n DcuplQueryGroup;\n\n/**\n * Options for retrieving a single item from the global dcupl instance.\n */\nexport type DcuplGlobalItemOptions<T> = {\n /**\n * Model identifier.\n */\n modelKey: string;\n\n /**\n * Item identifier.\n */\n itemKey: string;\n\n /**\n * Optional projection to control returned properties.\n */\n projection?: Projection<T>;\n};\n\n/**\n * Options for retrieving multiple specific items from the global dcupl instance.\n */\nexport type DcuplGlobalManyItemOptions<T> = {\n /**\n * Model identifier.\n */\n modelKey: string;\n\n /**\n * Array of item identifiers.\n */\n itemKeys: string[];\n\n /**\n * Optional projection to control returned properties.\n */\n projection?: Projection<T>;\n};\n\n/**\n * Options for executing queries on a list instance.\n * Combines query conditions with data retrieval options.\n *\n * @see ListQueryOptions - Type alias with clearer naming\n *\n * @example\n * ```typescript\n * const options: DcuplListQueryOptions<Product> = {\n * queries: [\n * { operator: 'gte', attribute: 'price', value: 50 }\n * ],\n * groupType: 'and',\n * count: 10\n * };\n * const results = list.catalog.query.execute(options);\n * ```\n */\nexport type DcuplListQueryOptions<T> = DcuplItemOptions<T> & DcuplQueryGroup;\n\n/**\n * Options for retrieving a specific item from a list instance.\n */\nexport type DcuplListItemOptions<T> = DcuplItemOptions<T> & {\n /**\n * Item identifier.\n */\n itemKey: string;\n};\n\n/**\n * Custom operator function for extending query capabilities.\n * Receives a query condition and list item, returns whether the item matches.\n *\n * @example\n * ```typescript\n * const customOperator: CustomOperatorFn = (query, listItem) => {\n * const value = listItem.data[query.attribute];\n * return value !== undefined;\n * };\n * ```\n */\nexport type CustomOperatorFn = (query: DcuplQuery, listItem: ListItem) => boolean;\n\n/**\n * Map of custom operator names to their implementation functions.\n */\nexport type CustomOperatorFnMap = Map<string, CustomOperatorFn>;\n\n/**\n * Internal query statement representation.\n * Used for query execution and optimization.\n *\n * @internal\n */\nexport type DcuplQueryStatement<T = any> = DcuplItemOptions<T> & {\n modelKey: string;\n groupType?: DcuplQueryType;\n queries: DcuplQuery[] | DcuplQueryGroup<DcuplQuery>[] | DcuplQueryGroup<DcuplQueryGroup<DcuplQuery>>[];\n};\n\n// ============================================================================\n// Type Aliases - Clearer naming for improved developer experience\n// ============================================================================\n\n/**\n * Query condition - represents a single filter criterion.\n * Alias for {@link DcuplQuery} with clearer naming.\n *\n * Use this to filter items based on attribute values using various operators.\n *\n * @example\n * ```typescript\n * // Equality check\n * const condition: QueryCondition = {\n * operator: 'eq',\n * attribute: 'status',\n * value: 'active'\n * };\n *\n * // Numeric comparison\n * const priceCondition: QueryCondition = {\n * operator: 'gte',\n * attribute: 'price',\n * value: 100\n * };\n * ```\n */\nexport type QueryCondition = DcuplQuery;\n\n/**\n * Query group - combines multiple conditions with AND/OR logic.\n * Alias for {@link DcuplQueryGroup} with clearer naming.\n *\n * Groups can be nested to create complex filter expressions.\n *\n * @example\n * ```typescript\n * // AND group\n * const group: QueryConditionGroup = {\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]\n * };\n *\n * // Nested groups\n * const nested: QueryConditionGroup = {\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * {\n * groupType: 'or',\n * queries: [\n * { operator: 'eq', attribute: 'featured', value: true },\n * { operator: 'gte', attribute: 'rating', value: 4.5 }\n * ]\n * }\n * ]\n * };\n * ```\n */\nexport type QueryConditionGroup<ChildQuery extends DcuplQuery | DcuplQueryGroup = any> =\n DcuplQueryGroup<ChildQuery>;\n\n/**\n * Query execution context for global dcupl operations.\n * Alias for {@link DcuplGlobalQueryOptions} with clearer naming.\n *\n * Combines model specification, query conditions, and data retrieval options.\n *\n * @example\n * ```typescript\n * const context: QueryExecutionContext<Product> = {\n * modelKey: 'Product',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' }\n * ],\n * groupType: 'and',\n * start: 0,\n * count: 20,\n * sort: { price: 'asc' }\n * };\n * const results = dcupl.data.query(context);\n * ```\n */\nexport type QueryExecutionContext<T> = DcuplGlobalQueryOptions<T>;\n\n/**\n * Options for executing queries on a list instance.\n * Alias for {@link DcuplListQueryOptions} with clearer naming.\n *\n * Combines query conditions with pagination, projection, and sorting options.\n *\n * @example\n * ```typescript\n * const options: ListQueryOptions<Product> = {\n * queries: [\n * { operator: 'gte', attribute: 'price', value: 50 }\n * ],\n * groupType: 'and',\n * start: 0,\n * count: 10,\n * sort: { name: 'asc' }\n * };\n * const results = list.catalog.query.execute(options);\n * ```\n */\nexport type ListQueryOptions<T> = DcuplListQueryOptions<T>;\n\n/**\n * Common data retrieval options.\n * Alias for {@link DcuplItemOptions} with clearer naming.\n *\n * Controls pagination, projection, and sorting for data operations.\n *\n * @example\n * ```typescript\n * const options: ItemDataOptions<Product> = {\n * start: 0,\n * count: 20,\n * projection: { name: true, price: true },\n * sort: { price: 'desc' }\n * };\n * ```\n */\nexport type ItemDataOptions<T> = DcuplItemOptions<T>;\n"],"names":["generateObjectHash","input","stringifiedInput","fastStringify","simpleHash","keys","result","i","key","str","hash","char","perf","name","start","end","url","type","AnalyticsController","enabled","options","uniqueKey","entry","startMark","endMark","duration","roundedDuration","existingEntry","newDuration","newEntry","currentItem","newItem","context","DependencyGraph$1","DependencyGraph","from","to","policy","kind","meta","deps","dependents","node","visited","visiting","visit","dependencies","dep","pathStack","cycleStart","cyclePath","allowCycles","edges","nodesWithIncoming","n","roots","arr","res","outgoing","inDegree","levelBatches","queue","seen","nextLevel","outs","set","lvl","changed","direction","filterKinds","changedSet","matchesKind","traverseDown","impacted","depender","traverseUp","d","nodes","data","g","e","other","thisNodes","otherNodes","addedNodes","removedNodes","thisEdges","otherEdges","addedEdges","s","removedEdges","kindChanges","AttributeDependencyGraph","modelKey","attributeKey","nodeId","parts","fromId","toId","changedIds","impactedNodes","id","allImpacted","attributes","plan","sortedAttributes","a","b","processingStep","modelPlan","maxDepth","calculateDepth","maxChildDepth","depId","depth","expectedAttributes","missing","attributeKeys","attributesByModel","attributesByStep","fromNode","toNode","LOG_LEVELS","Logger","config","module","level","moduleLevel","effectiveLevel","jsonEntry","timestamp","prefix","levelIcon","contextStr","message","error","formatted","errorOrContext","ctx","childConfig","mergedConfig","logger","CacheController","cacheName","value","changedModels","entriesToRemove","shouldInvalidate","trackedAttrs","attr","modelKeys","modelSet","keyPrefix","predicate","constext","cloneDeep","err","totalRequests","hitRate","getFilterEntryId","filterItemId","getNumberConstraintUid","getDateConstraintUid","uniqBy","cb","o","map","item","groupBy","criteria","obj","generateKey","length","text","possible","removeTrailingSlash","escapeRegExp","unescapeRegExp","escapedText","hasMinimumVersion","version","minVersion","compareVersions","v1","v2","isValidVersion","v1Parts","v2Parts","num1","num2","ChangeDetector","msg","clone","shouldClone","finish","targetClass","cdRef","valueToReturn","trigger","_msg","_target","_propertyKey","descriptor","originalMethod","args","computeSuggestions","index","queryManager","suggestions","relevantSearchTerm","isRelevantAttributeIndex","relevantKey","suggestion","keyStr","searchStr","isDateInstance","isValidDate","date","FORMAT_TOKENS","buildRegexFromFormat","format","regexStr","tokenOrder","remaining","sortedTokens","matched","pattern","token","extractComponents","match","components","matchValue","tokenMatch","constructDate","parseDate","_referenceDate","regex","LRUCache","maxSize","lruKey","path","recursionStack","hasCycleRecursive","neighbors","neighbor","DeepQueryService","cacheSize","attribute","referenceAttribute","reference","hasDot","hasBracket","isDeepQuery","firstDot","firstBracket","closingBracketIdx","dotAfterBracketIdx","errors","openBrackets","closeBrackets","bracketPattern","content","invalidChars","stats","total","deepQueryService","getDeepPathValues","starIdx","beforeStar","afterStar","get","results","el","segments","current","segment","next","v","flat","getScalingFactor","numbers","maxDecimalPlaces","num","decimalPart","add","factor","divide","getAggregation","relevantData","indexMap","aggregation","numberAgg","getNumberAggregation","minMax","getMinMax","getCount","getDistinct","getGroups","count","raw","minV","maxV","values","group","notFound","relevantEntries","itemMatched","relevantEntry","sum","val","attributeValue","itemKeys","DcuplQueryError","query","DcuplQueryBuilder","deepQueryServiceInstance","groupOrQuery","groupKey","q","every","mode","queryToApply","relevantQuery","idx","queryGroup","queryToRemove","idxOfQueryToRemove","findIndex","extractAttributes","queries","projection","nestedEntry","nestedProjection","condition","checkQueries","matches","countQueries","queryBuilder","getFacets","modelData","remoteModelData","modelIndexMap","queryWithoutCurrentFilter","currentFilteredData","remoteModelDataKeys","validFacetKeys","entries","validFacetKey","buildFacetEntry","filtered","getComparator","filterItemEntries","facetEntry","fullSize","indexedValueKeys","isFilterItemEntrySelected","compareValuesAsc","ta","tb","sa","sb","sort","facetKey","IndicesController","valueMap","keyAsString","listItem","originKey","foreignKeys","valueSet","indexName","existing","attributeValues","itemKey","oldValues","newValues","dataMap","indexKey","deepKeys","shallow","deep","maxAge","now","cleaned","metadata","counts","totalAge","oldestAge","age","avgAge","pivotPathSeparator","pivot","response","parentPath","indicesCtrl","projectedData","dataForIndexing","column","row","getPivotRowItem","getPivotColumnItem","agg","globalOptions","rowItem","newOptions","columnOption","columnItem","ISO_DATE_RE","INT_RE","FLOAT_RE","DATE_LIKE_RE","coerceStringType","looksDateLike","isEmptyValue","reconcileTypes","types","inferModel","limit","coerceStrings","columns","order","rowsToScan","acc","getDcuplPropertyTypeForNativeType","properties","diagnostics","conflict","getPropertiesForData","deepOrOptions","isInt","isFloat","generateTextQuery","filterItem","comparisonType","generateBooleanQuery","generateNumberAndDateQuery","generateReferenceQuery","isValidQueryStatement","QueryManager","scriptController","globalItemService","modelParser","operator","fn","transformers","transformer","dataEntry","dataEntryValue","queryValue","arrayValueHandling","isEqual","size","rootQuery","initialDataset","filteredData","indices","indexValues","diff","differenceBy","diffMap","queryAttributes","startsWith","endsWith","isEqualWith","isMatch","entryValue","find","isInverted","matchedKeys","newQuery","anyMatch","expandedQuery","queryWithoutInvert","starIndex","newQueryGroup","cleanAttribute","arrayValue","newAttribute","earlyReturn","currentDataset","initialData","AUTHORED_RESOURCE_ID_PREFIX","DERIVED_RESOURCE_ID_PREFIX","resolveResourceId","resource","occurrence","resolveResourceIds","resources","occurrences","emitted","basis","ScriptController","script","argsAsKeys","wrapperScript","argsAsArray","evaluateTemplate","template","variables","_","varName","parseTemplate","templateString","variableNames","PropertyTypesAsArray","NumberPropertyTypes","DatePropertyTypes","DcuplQueryOperatorsAsArray"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/object-hash.ts","../../src/performance.ts","../../src/analytics.controller.ts","../../src/dependency-graph.ts","../../src/attribute-dependency-graph.ts","../../src/logger/logger.ts","../../src/cache.controller.ts","../../src/helper.ts","../../src/changeDetection.ts","../../src/computeSuggestions.ts","../../src/date-utils.ts","../../src/deep-query.service.ts","../../src/getAggregation.ts","../../src/query-builder.ts","../../src/getFacets.ts","../../src/indices.controller.ts","../../src/pivot.ts","../../src/property-parser.ts","../../src/query.helper.ts","../../src/queryData.ts","../../src/resource-id.ts","../../src/scheduling.ts","../../src/script.controller.ts","../../src/template-parser.ts","../../src/types/model.types.ts","../../src/types/query.types.ts"],"sourcesContent":["export function generateObjectHash(input: any): string {\n const stringifiedInput = fastStringify(input);\n return simpleHash(stringifiedInput);\n}\n\nfunction fastStringify(input: any): string {\n if (typeof input !== 'object' || input === null) {\n return String(input);\n }\n\n if (Array.isArray(input)) {\n return '[' + input.map(fastStringify).join(',') + ']';\n }\n\n const keys = Object.keys(input).sort();\n let result = '{';\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i] as string;\n result += key + ':' + fastStringify(input[key]);\n if (i < keys.length - 1) {\n result += ',';\n }\n }\n\n result += '}';\n return result;\n}\n\nfunction simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash |= 0; // Convert to 32bit integer\n }\n return hash.toString(16);\n}\n","export const perf = {\n now: (): number => {\n if (typeof performance !== 'undefined' && performance.now) {\n return performance.now();\n } else {\n return new Date().getTime();\n }\n },\n\n mark: (name: string): void => {\n if (typeof performance !== 'undefined' && performance.mark) {\n performance.mark(name);\n }\n },\n measure: (name: string, start: string, end: string): void => {\n if (typeof performance !== 'undefined' && performance.measure) {\n performance.measure(name, start, end);\n }\n },\n getEntriesByName(url: string): PerformanceEntryList | undefined {\n if (typeof performance !== 'undefined' && performance.getEntriesByName) {\n return performance.getEntriesByName(url);\n }\n return undefined;\n },\n getEntriesByType(type: string): PerformanceEntryList | undefined {\n if (typeof performance !== 'undefined' && performance.getEntriesByType) {\n return performance.getEntriesByType(type);\n }\n return undefined;\n },\n};\n","import { DcuplUpdateType } from './changeDetection';\nimport { generateObjectHash } from './object-hash';\nimport { perf } from './performance';\n\nexport type AnalyticsEntryType = 'resource' | 'mark' | 'measure' | 'data' | 'sdk_action';\n\nexport type AnalyticsEntryBase<Value = any> = {\n name: string;\n type: AnalyticsEntryType;\n value: Value;\n createdAt: number;\n};\n\nexport type AnalyticsEntryResource = AnalyticsEntryBase<AnalyticsEntryResourceValue> & {\n type: 'resource';\n};\nexport type AnalyticsEntryMark = AnalyticsEntryBase<number> & {\n type: 'mark';\n};\n\nexport type AnalyticsEntryMeasure = AnalyticsEntryBase<number> & {\n type: 'measure';\n detail?: any;\n};\n\nexport type AnalyticsEntryData = AnalyticsEntryBase<any> & {\n type: 'data';\n};\n\nexport type AnalyticsEntrySdkAction = AnalyticsEntryBase<AnalyticsEntrySdkActionValue> & {\n type: 'sdk_action';\n};\n\nexport type AnalyticsEntry =\n | AnalyticsEntryResource\n | AnalyticsEntryMark\n | AnalyticsEntryMeasure\n | AnalyticsEntryData\n | AnalyticsEntrySdkAction;\n\nexport type AnalyticsEntrySdkActionValue = {\n duration: number;\n model?: string;\n args?: any;\n};\n\nexport type AnalyticsEntryResourceValue = {\n model: string;\n type: 'data' | 'model';\n start: number;\n startTime: number;\n requestStart: number;\n responseStart: number;\n responseEnd: number;\n end: number;\n duration: number;\n parseStart: number;\n parseEnd: number;\n rawTransformStart?: number;\n rawTransformEnd?: number;\n csvToJsonStart?: number;\n csvToJsonEnd?: number;\n parsedTransformStart?: number;\n parsedTransformEnd?: number;\n decodedBodySize: number;\n encodedBodySize: number;\n};\n\nexport class AnalyticsController {\n private data!: Map<string, AnalyticsEntry>;\n\n private enabled = false;\n\n constructor() {}\n\n public init(enabled: boolean): void {\n this.data = new Map();\n this.enabled = enabled;\n }\n\n public set(options: { value: AnalyticsEntry; context?: any }): AnalyticsEntry | undefined {\n if (!this.enabled) return options.value;\n\n const uniqueKey = this.getKey(options.value.name, options.context);\n\n this.data.set(uniqueKey, options.value);\n return this.data.get(uniqueKey);\n }\n\n public mark(options: { name: string; context?: any }): AnalyticsEntryMark | undefined {\n if (!this.enabled) return undefined;\n\n const uniqueKey = this.getKey(options.name, options.context);\n\n const entry: AnalyticsEntryMark = {\n name: options.name,\n type: 'mark',\n value: perf.now(),\n createdAt: Date.now(),\n };\n\n this.data.set(uniqueKey, entry);\n return entry;\n }\n\n public measure(options: {\n name: string;\n start: string;\n end: string;\n context?: any;\n detail?: any;\n sum?: boolean;\n }): AnalyticsEntryMeasure | undefined {\n if (!this.enabled) return undefined;\n\n const uniqueKey = this.getKey(options.name, options.context);\n\n const startMark = this.data.get(this.getKey(options.start, options.context));\n const endMark = this.data.get(this.getKey(options.end, options.context));\n\n if (!startMark || !endMark) {\n return undefined;\n }\n\n const duration = endMark.value - startMark.value;\n\n const roundedDuration = Math.round(duration * 100) / 100;\n\n const existingEntry = this.data.get(uniqueKey);\n\n if (existingEntry && options.sum) {\n const newDuration = existingEntry.value + roundedDuration;\n const newEntry: AnalyticsEntryMeasure = {\n ...(existingEntry as AnalyticsEntryMeasure),\n value: newDuration,\n };\n this.data.set(uniqueKey, newEntry);\n return newEntry;\n }\n\n const entry: AnalyticsEntryMeasure = {\n name: options.name,\n type: 'measure',\n value: roundedDuration,\n createdAt: Date.now(),\n };\n if (options.detail) {\n entry.detail = options.detail;\n }\n\n this.data.set(uniqueKey, entry);\n return entry;\n }\n\n public update(options: { value: AnalyticsEntry; context?: any }): AnalyticsEntry | undefined {\n if (!this.enabled) return options.value;\n\n const uniqueKey = this.getKey(options.value.name, options.context);\n\n const currentItem = this.data.get(uniqueKey);\n\n if (currentItem) {\n const newItem = { ...currentItem, ...options.value };\n this.data.set(uniqueKey, newItem);\n } else {\n this.data.set(uniqueKey, options.value);\n }\n return this.data.get(uniqueKey);\n }\n\n public getResourceInfosFromPerformanceAPI(): Array<PerformanceResourceTiming> {\n if (typeof performance === 'undefined') return [];\n const entries = perf.getEntriesByType('resource');\n return entries as Array<PerformanceResourceTiming>;\n }\n\n public get(options: { name: string; context?: any }): AnalyticsEntry | undefined {\n const uniqueKey = this.getKey(options.name, options.context);\n\n return this.data.get(uniqueKey);\n }\n\n public getByType(type: AnalyticsEntryType): Array<AnalyticsEntry> {\n return Array.from(this.data.values()).filter((entry) => entry.type === type);\n }\n\n public getByName(name: DcuplUpdateType): Array<AnalyticsEntry> {\n return Array.from(this.data.values()).filter((entry) => entry.name === name);\n }\n\n public clear(): void {\n this.data.clear();\n }\n\n public getKey(key: string, context: any): string {\n if (!context) {\n return key;\n }\n\n const hash = generateObjectHash(context);\n return key + '_' + hash;\n }\n}\n","export class DependencyGraph<K extends string = string, M = any> {\n private nodes: Set<string>;\n private edges: Map<string, Set<string>>;\n private nodeKinds: Map<string, K> = new Map();\n private nodeMeta: Map<string, M> = new Map();\n private edgePolicy?: (from: { name: string; kind?: K }, to: { name: string; kind?: K }) => boolean;\n\n constructor() {\n this.nodes = new Set();\n this.edges = new Map();\n }\n\n addNode(name: string, options?: { kind?: K; meta?: M }): void {\n if (!this.nodes.has(name)) {\n this.nodes.add(name);\n this.edges.set(name, new Set());\n }\n if (options?.kind !== undefined) {\n this.nodeKinds.set(name, options.kind);\n }\n if (options?.meta !== undefined) {\n this.nodeMeta.set(name, options.meta);\n }\n }\n\n addDependency(from: string, to: string): void {\n if (!this.nodes.has(from) || !this.nodes.has(to)) {\n throw new Error('Both nodes must exist before adding a dependency.');\n }\n if (this.edgePolicy) {\n const allow = this.edgePolicy(\n { name: from, kind: this.nodeKinds.get(from) },\n { name: to, kind: this.nodeKinds.get(to) },\n );\n if (!allow) {\n throw new Error(`Edge policy rejected dependency from \"${from}\" to \"${to}\".`);\n }\n }\n this.edges.get(from)!.add(to);\n }\n\n /** Configure an optional policy to validate edges before they are added */\n setEdgePolicy(policy?: (from: { name: string; kind?: K }, to: { name: string; kind?: K }) => boolean): void {\n this.edgePolicy = policy;\n }\n\n /** Assign or change a node's kind */\n setNodeKind(name: string, kind?: K): void {\n if (!this.nodes.has(name)) throw new Error('Node does not exist.');\n if (kind === undefined) {\n this.nodeKinds.delete(name);\n } else {\n this.nodeKinds.set(name, kind);\n }\n }\n\n /** Get a node's kind */\n getNodeKind(name: string): K | undefined {\n return this.nodeKinds.get(name);\n }\n\n /** Set arbitrary metadata for a node */\n setNodeMeta(name: string, meta: M): void {\n if (!this.nodes.has(name)) throw new Error('Node does not exist.');\n this.nodeMeta.set(name, meta);\n }\n\n /** Get metadata for a node */\n getNodeMeta<T = M>(name: string): T | undefined {\n return this.nodeMeta.get(name) as T | undefined;\n }\n\n removeNode(name: string): void {\n if (!this.nodes.has(name)) {\n throw new Error('Node does not exist.');\n }\n this.nodes.delete(name);\n this.edges.delete(name);\n\n for (let deps of this.edges.values()) {\n deps.delete(name);\n }\n }\n\n removeDependency(from: string, to: string): void {\n if (!this.edges.has(from)) {\n throw new Error('From node does not exist.');\n }\n this.edges.get(from)!.delete(to);\n }\n\n hasNode(name: string): boolean {\n return this.nodes.has(name);\n }\n\n hasDependency(from: string, to: string): boolean {\n if (!this.edges.has(from)) {\n return false;\n }\n return this.edges.get(from)!.has(to);\n }\n\n getDependencies(name: string): string[] {\n if (!this.edges.has(name)) {\n throw new Error('Node does not exist.');\n }\n return Array.from(this.edges.get(name)!);\n }\n\n getDependents(name: string): string[] {\n const dependents: string[] = [];\n for (let [node, deps] of this.edges) {\n if (deps.has(name)) {\n dependents.push(node);\n }\n }\n return dependents;\n }\n\n isCyclic(): boolean {\n const visited: Set<string> = new Set();\n const visiting: Set<string> = new Set();\n\n const visit = (node: string): boolean => {\n if (visited.has(node)) {\n return false;\n }\n if (visiting.has(node)) {\n return true;\n }\n\n visiting.add(node);\n const dependencies = this.edges.get(node);\n if (dependencies) {\n for (let dep of dependencies) {\n if (visit(dep)) {\n return true;\n }\n }\n }\n visiting.delete(node);\n visited.add(node);\n return false;\n };\n\n for (let node of this.nodes) {\n if (visit(node)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Validates that the graph has no cycles.\n * Returns an object with hasCycle boolean and the cycle path if found.\n * @returns {hasCycle: boolean, path?: string[]} - hasCycle is true if a cycle exists, path contains the cycle nodes\n */\n validateNoCycles(): { hasCycle: boolean; path?: string[] } {\n const visited: Set<string> = new Set();\n const visiting: Set<string> = new Set();\n const pathStack: string[] = [];\n\n const visit = (node: string): string[] | null => {\n if (visited.has(node)) {\n return null;\n }\n if (visiting.has(node)) {\n // Found a cycle - extract the cycle path\n const cycleStart = pathStack.indexOf(node);\n return pathStack.slice(cycleStart).concat([node]);\n }\n\n visiting.add(node);\n pathStack.push(node);\n\n const dependencies = this.edges.get(node);\n if (dependencies) {\n for (let dep of dependencies) {\n const cyclePath = visit(dep);\n if (cyclePath) {\n return cyclePath;\n }\n }\n }\n\n pathStack.pop();\n visiting.delete(node);\n visited.add(node);\n return null;\n };\n\n for (let node of this.nodes) {\n const cyclePath = visit(node);\n if (cyclePath) {\n return { hasCycle: true, path: cyclePath };\n }\n }\n\n return { hasCycle: false };\n }\n\n order(options?: { allowCycles?: boolean }): string[] {\n const visited: Set<string> = new Set();\n const visiting: Set<string> = new Set();\n const result: string[] = [];\n const allowCycles = options?.allowCycles ?? false;\n\n const visit = (node: string): void => {\n if (visited.has(node)) {\n return;\n }\n if (visiting.has(node)) {\n if (allowCycles) {\n // Skip circular dependency instead of throwing\n return;\n }\n throw new Error('Graph has a cycle.');\n }\n\n visiting.add(node);\n const dependencies = this.edges.get(node);\n if (dependencies) {\n for (let dep of dependencies) {\n visit(dep);\n }\n }\n visiting.delete(node);\n visited.add(node);\n result.push(node);\n };\n\n for (let node of this.nodes) {\n if (!visited.has(node)) {\n visit(node);\n }\n }\n\n return result;\n }\n\n clear(): void {\n this.nodes.clear();\n this.edges.clear();\n }\n\n getNodes(): string[] {\n return Array.from(this.nodes);\n }\n\n getAllEdges(): [string, string][] {\n const edges: [string, string][] = [];\n for (let [from, deps] of this.edges) {\n for (let to of deps) {\n edges.push([from, to]);\n }\n }\n return edges;\n }\n\n /**\n * Returns all nodes that have no dependants (i.e., no other node depends on them).\n * In graph terms, nodes with zero incoming edges.\n */\n getNodesWithNoDependants(): string[] {\n if (this.nodes.size === 0) {\n return [];\n }\n\n const nodesWithIncoming: Set<string> = new Set();\n for (const deps of this.edges.values()) {\n for (const to of deps) {\n nodesWithIncoming.add(to);\n }\n }\n\n const result: string[] = [];\n for (const node of this.nodes) {\n if (!nodesWithIncoming.has(node)) {\n result.push(node);\n }\n }\n\n return result;\n }\n\n /** Return nodes filtered by kind */\n getNodesByKind(kind: K): string[] {\n const result: string[] = [];\n for (const n of this.nodes) {\n if (this.nodeKinds.get(n) === kind) result.push(n);\n }\n return result;\n }\n\n /** Roots by kind (zero incoming) */\n getRootsByKind(kind?: K): string[] {\n const roots = new Set(this.getNodesWithNoDependants());\n const arr = Array.from(roots);\n if (!kind) return arr;\n return arr.filter((n) => this.nodeKinds.get(n) === kind);\n }\n\n /** Leaves by kind (zero outgoing) */\n getLeavesByKind(kind?: K): string[] {\n const res: string[] = [];\n for (const n of this.nodes) {\n const outgoing = this.edges.get(n);\n if (!outgoing || outgoing.size === 0) {\n if (!kind || this.nodeKinds.get(n) === kind) {\n res.push(n);\n }\n }\n }\n return res;\n }\n\n /** Filter dependencies of a node by kind */\n getDependenciesOfKind(name: string, kind: K): string[] {\n const deps = this.getDependencies(name);\n return deps.filter((n) => this.nodeKinds.get(n) === kind);\n }\n\n /** Filter dependents of a node by kind */\n getDependentsOfKind(name: string, kind: K): string[] {\n const deps = this.getDependents(name);\n return deps.filter((n) => this.nodeKinds.get(n) === kind);\n }\n\n /**\n * Create execution batches (topological levels). Throws if the graph has a cycle.\n * If filterKinds is provided, batches contain only nodes of these kinds, but readiness\n * is evaluated against the complete graph.\n */\n plan(options?: { filterKinds?: K[] }): { batches: string[][] } {\n // Build in-degree map\n const inDegree: Map<string, number> = new Map();\n for (const n of this.nodes) inDegree.set(n, 0);\n for (const deps of this.edges.values()) {\n for (const to of deps) inDegree.set(to, (inDegree.get(to) || 0) + 1);\n }\n\n // Kahn's algorithm for levels\n const levelBatches: string[][] = [];\n let queue: string[] = Array.from(this.nodes).filter((n) => (inDegree.get(n) || 0) === 0);\n\n const seen: Set<string> = new Set();\n while (queue.length > 0) {\n const nextLevel = queue.slice();\n levelBatches.push(nextLevel);\n queue = [];\n\n for (const n of nextLevel) {\n if (seen.has(n)) continue;\n seen.add(n);\n const outs = this.edges.get(n);\n if (!outs) continue;\n for (const to of outs) {\n inDegree.set(to, (inDegree.get(to) || 0) - 1);\n if ((inDegree.get(to) || 0) === 0) {\n queue.push(to);\n }\n }\n }\n }\n\n if (seen.size !== this.nodes.size) {\n // There is a cycle\n throw new Error('Graph has a cycle.');\n }\n\n if (options?.filterKinds && options.filterKinds.length > 0) {\n const set = new Set(options.filterKinds);\n return {\n batches: levelBatches.map((lvl) => lvl.filter((n) => set.has(this.nodeKinds.get(n) as K))),\n };\n }\n\n return { batches: levelBatches };\n }\n\n /**\n * Impact analysis: return nodes impacted by a set of changed nodes.\n * direction:\n * - 'downstream': nodes that depend (directly/indirectly) on changed\n * - 'upstream': nodes that changed depends on\n * - 'both': union of upstream and downstream\n */\n getImpactedNodes(\n changed: string[] | Set<string>,\n direction: 'downstream' | 'upstream' | 'both' = 'downstream',\n filterKinds?: K[],\n ): string[] {\n const changedSet = new Set(changed);\n\n const matchesKind = (n: string): boolean =>\n !filterKinds || filterKinds.length === 0 || filterKinds.includes((this.nodeKinds.get(n) as K) ?? (undefined as any));\n\n const traverseDown = (): Set<string> => {\n const impacted: Set<string> = new Set();\n const queue: string[] = Array.from(changedSet);\n while (queue.length) {\n const n = queue.shift()!;\n for (const [depender, deps] of this.edges) {\n if (deps.has(n) && !impacted.has(depender)) {\n impacted.add(depender);\n queue.push(depender);\n }\n }\n }\n return impacted;\n };\n\n const traverseUp = (): Set<string> => {\n const impacted: Set<string> = new Set();\n const queue: string[] = Array.from(changedSet);\n while (queue.length) {\n const n = queue.shift()!;\n const deps = this.edges.get(n);\n if (!deps) continue;\n for (const d of deps) {\n if (!impacted.has(d)) {\n impacted.add(d);\n queue.push(d);\n }\n }\n }\n return impacted;\n };\n\n let result: Set<string>;\n if (direction === 'both') {\n result = new Set([...traverseDown(), ...traverseUp()]);\n } else if (direction === 'upstream') {\n result = traverseUp();\n } else {\n result = traverseDown();\n }\n\n return Array.from(result).filter(matchesKind);\n }\n\n toJSON(): { nodes: Array<{ name: string; kind?: K; meta?: M }>; edges: Array<{ from: string; to: string }> } {\n const nodes = Array.from(this.nodes).map((name) => ({\n name,\n kind: this.nodeKinds.get(name),\n meta: this.nodeMeta.get(name),\n }));\n const edges = this.getAllEdges().map(([from, to]) => ({ from, to }));\n return { nodes, edges };\n }\n\n static fromJSON<KK extends string, MM = any>(data: { nodes: Array<{ name: string; kind?: KK; meta?: MM }>; edges: Array<{ from: string; to: string }> }): DependencyGraph<KK, MM> {\n const g = new DependencyGraph<KK, MM>();\n for (const n of data.nodes) {\n g.addNode(n.name, { kind: n.kind, meta: n.meta });\n }\n for (const e of data.edges) {\n g.addDependency(e.from, e.to);\n }\n return g;\n }\n\n diff(other: DependencyGraph<K, any>): {\n addedNodes: string[];\n removedNodes: string[];\n addedEdges: [string, string][];\n removedEdges: [string, string][];\n kindChanges: Array<{ name: string; from?: K; to?: K }>;\n } {\n const thisNodes = new Set(this.getNodes());\n const otherNodes = new Set(other.getNodes());\n\n const addedNodes = Array.from(otherNodes).filter((n) => !thisNodes.has(n));\n const removedNodes = Array.from(thisNodes).filter((n) => !otherNodes.has(n));\n\n const thisEdges = new Set(this.getAllEdges().map((e) => e.join('→')));\n const otherEdges = new Set(other.getAllEdges().map((e) => e.join('→')));\n\n const addedEdges = Array.from(otherEdges)\n .filter((e) => !thisEdges.has(e))\n .map((s) => s.split('→') as [string, string]);\n const removedEdges = Array.from(thisEdges)\n .filter((e) => !otherEdges.has(e))\n .map((s) => s.split('→') as [string, string]);\n\n const kindChanges: Array<{ name: string; from?: K; to?: K }> = [];\n for (const n of new Set([...thisNodes, ...otherNodes])) {\n const from = this.nodeKinds.get(n);\n const to = other.nodeKinds.get(n as string) as K | undefined;\n if (from !== to) kindChanges.push({ name: n, from, to });\n }\n\n return { addedNodes, removedNodes, addedEdges, removedEdges, kindChanges };\n }\n}\n","import { DependencyGraph } from './dependency-graph';\n\nexport type AttributeNode = {\n modelKey: string;\n attributeKey: string;\n type: 'property' | 'reference';\n processingStep: 1 | 2 | 3 | 4; // Maps to current 4-step pipeline\n};\n\nexport type AttributeNodeId = string; // Format: \"modelKey.attributeKey\"\n\nexport type ProcessingPlan = Map<string, Map<number, Set<string>>>;\n// Structure: Map<modelKey, Map<processingStep, Set<attributeKeys>>>\n\n/**\n * AttributeDependencyGraph tracks attribute-level dependencies between models.\n *\n * This graph is item-agnostic: when ModelA.price depends on ModelB.cost,\n * any change to ModelB.cost triggers reprocessing of ALL ModelA items\n * that have the price derivation.\n *\n * The graph ensures:\n * - Correct processing order based on dependencies\n * - Impact analysis when attributes change\n * - Efficient batch processing by grouping attributes by model and step\n */\nexport class AttributeDependencyGraph {\n private graph: DependencyGraph<string, AttributeNode>;\n private nodeIndex: Map<AttributeNodeId, AttributeNode>;\n\n constructor() {\n this.graph = new DependencyGraph<string, AttributeNode>();\n this.nodeIndex = new Map();\n }\n\n /**\n * Creates a unique ID for an attribute node\n */\n private getNodeId(modelKey: string, attributeKey: string): AttributeNodeId {\n return `${modelKey}.${attributeKey}`;\n }\n\n /**\n * Parses a node ID back into components\n */\n private parseNodeId(nodeId: AttributeNodeId): { modelKey: string; attributeKey: string } {\n const parts = nodeId.split('.');\n return {\n modelKey: parts[0]!,\n attributeKey: parts.slice(1).join('.'), // Handle attribute keys with dots\n };\n }\n\n /**\n * Adds an attribute node to the graph\n */\n public addAttribute(node: AttributeNode): void {\n const nodeId = this.getNodeId(node.modelKey, node.attributeKey);\n\n if (!this.nodeIndex.has(nodeId)) {\n this.graph.addNode(nodeId, { meta: node });\n this.nodeIndex.set(nodeId, node);\n }\n }\n\n /**\n * Adds a dependency between two attributes\n * @param from - The dependent attribute (e.g., ModelA.derivedPrice)\n * @param to - The source attribute it depends on (e.g., ModelB.baseCost)\n */\n public addAttributeDependency(from: AttributeNode, to: AttributeNode): void {\n // Ensure both nodes exist\n this.addAttribute(from);\n this.addAttribute(to);\n\n const fromId = this.getNodeId(from.modelKey, from.attributeKey);\n const toId = this.getNodeId(to.modelKey, to.attributeKey);\n\n // Add dependency: from depends on to\n if (!this.graph.hasDependency(fromId, toId)) {\n this.graph.addDependency(fromId, toId);\n }\n }\n\n /**\n * Gets all attributes impacted by changes to the given attributes\n * @param changed - Array of changed attribute nodes\n * @returns Array of all impacted attribute nodes (including the changed ones)\n */\n public getImpactedAttributes(changed: AttributeNode[]): AttributeNode[] {\n const changedIds = changed.map((node) => this.getNodeId(node.modelKey, node.attributeKey));\n\n // Get all downstream dependents (attributes that depend on the changed ones)\n const impactedIds = this.graph.getImpactedNodes(changedIds, 'downstream');\n\n // Convert IDs back to AttributeNode objects\n const impactedNodes = impactedIds\n .map((id) => this.nodeIndex.get(id))\n .filter((node): node is AttributeNode => node !== undefined);\n\n // Include the originally changed attributes\n const allImpacted = [...changed, ...impactedNodes];\n\n // Deduplicate\n const seen = new Set<AttributeNodeId>();\n return allImpacted.filter((node) => {\n const id = this.getNodeId(node.modelKey, node.attributeKey);\n if (seen.has(id)) return false;\n seen.add(id);\n return true;\n });\n }\n\n /**\n * Generates a processing plan for the given attributes\n * Groups attributes by model and processing step for efficient batch processing\n *\n * @param attributes - Array of attributes to process\n * @returns Map<modelKey, Map<processingStep, Set<attributeKeys>>>\n */\n public getProcessingOrder(attributes: AttributeNode[]): ProcessingPlan {\n const plan: ProcessingPlan = new Map();\n\n // Sort attributes by processing step (1, 2, 3, 4)\n const sortedAttributes = [...attributes].sort((a, b) => a.processingStep - b.processingStep);\n\n for (const node of sortedAttributes) {\n const { modelKey, attributeKey, processingStep } = node;\n\n // Get or create model entry\n if (!plan.has(modelKey)) {\n plan.set(modelKey, new Map());\n }\n const modelPlan = plan.get(modelKey)!;\n\n // Get or create step entry\n if (!modelPlan.has(processingStep)) {\n modelPlan.set(processingStep, new Set());\n }\n const stepAttributes = modelPlan.get(processingStep)!;\n\n // Add attribute to this step\n stepAttributes.add(attributeKey);\n }\n\n return plan;\n }\n\n /**\n * Gets all attributes for a specific model\n */\n public getAttributesForModel(modelKey: string): AttributeNode[] {\n const nodes: AttributeNode[] = [];\n\n for (const node of this.nodeIndex.values()) {\n if (node.modelKey === modelKey) {\n nodes.push(node);\n }\n }\n\n return nodes;\n }\n\n /**\n * Gets dependencies for a specific attribute\n */\n public getDependencies(node: AttributeNode): AttributeNode[] {\n const nodeId = this.getNodeId(node.modelKey, node.attributeKey);\n\n if (!this.graph.hasNode(nodeId)) {\n return [];\n }\n\n const depIds = this.graph.getDependencies(nodeId);\n return depIds\n .map((id) => this.nodeIndex.get(id))\n .filter((node): node is AttributeNode => node !== undefined);\n }\n\n /**\n * Gets dependents for a specific attribute (attributes that depend on this one)\n */\n public getDependents(node: AttributeNode): AttributeNode[] {\n const nodeId = this.getNodeId(node.modelKey, node.attributeKey);\n\n if (!this.graph.hasNode(nodeId)) {\n return [];\n }\n\n const depIds = this.graph.getDependents(nodeId);\n return depIds\n .map((id) => this.nodeIndex.get(id))\n .filter((node): node is AttributeNode => node !== undefined);\n }\n\n /**\n * Checks if an attribute has any dependencies\n */\n public hasDependencies(node: AttributeNode): boolean {\n const nodeId = this.getNodeId(node.modelKey, node.attributeKey);\n if (!this.graph.hasNode(nodeId)) return false;\n return this.graph.getDependencies(nodeId).length > 0;\n }\n\n /**\n * Checks if a node exists in the graph\n */\n public hasNode(nodeId: AttributeNodeId): boolean {\n return this.nodeIndex.has(nodeId);\n }\n\n /**\n * Gets all attribute nodes in the graph\n */\n public getAllAttributes(): AttributeNode[] {\n return Array.from(this.nodeIndex.values());\n }\n\n /**\n * Gets the maximum dependency depth in the graph\n * Useful for determining if full update might be more efficient\n */\n public getMaxDepth(): number {\n let maxDepth = 0;\n\n const calculateDepth = (nodeId: string, visited: Set<string> = new Set()): number => {\n if (visited.has(nodeId)) return 0; // Circular dependency\n visited.add(nodeId);\n\n const deps = this.graph.getDependencies(nodeId);\n if (deps.length === 0) return 1;\n\n let maxChildDepth = 0;\n for (const depId of deps) {\n const depth = calculateDepth(depId, new Set(visited));\n maxChildDepth = Math.max(maxChildDepth, depth);\n }\n\n return maxChildDepth + 1;\n };\n\n for (const nodeId of this.graph.getNodes()) {\n const depth = calculateDepth(nodeId);\n maxDepth = Math.max(maxDepth, depth);\n }\n\n return maxDepth;\n }\n\n /**\n * Validates that all expected attributes are present in the graph.\n * This helps catch silent failures during graph building where attributes\n * with expressions or derivations might not be properly added.\n *\n * @param expectedAttributes - Map of modelKey to array of attribute keys that should exist\n * @returns Object with validation result and any missing attributes\n */\n public validateCompleteness(expectedAttributes: Map<string, string[]>): {\n isComplete: boolean;\n missing: Array<{ modelKey: string; attributeKey: string }>;\n } {\n const missing: Array<{ modelKey: string; attributeKey: string }> = [];\n\n for (const [modelKey, attributeKeys] of expectedAttributes) {\n for (const attributeKey of attributeKeys) {\n const nodeId = this.getNodeId(modelKey, attributeKey);\n if (!this.hasNode(nodeId)) {\n missing.push({ modelKey, attributeKey });\n }\n }\n }\n\n return {\n isComplete: missing.length === 0,\n missing,\n };\n }\n\n /**\n * Clears the entire graph\n */\n public clear(): void {\n this.graph.clear();\n this.nodeIndex.clear();\n }\n\n /**\n * Gets statistics about the graph\n */\n public getStats(): {\n totalAttributes: number;\n totalDependencies: number;\n maxDepth: number;\n attributesByModel: Map<string, number>;\n attributesByStep: Map<number, number>;\n } {\n const attributesByModel = new Map<string, number>();\n const attributesByStep = new Map<number, number>();\n\n for (const node of this.nodeIndex.values()) {\n // Count by model\n attributesByModel.set(node.modelKey, (attributesByModel.get(node.modelKey) || 0) + 1);\n\n // Count by step\n attributesByStep.set(node.processingStep, (attributesByStep.get(node.processingStep) || 0) + 1);\n }\n\n return {\n totalAttributes: this.nodeIndex.size,\n totalDependencies: this.graph.getAllEdges().length,\n maxDepth: this.getMaxDepth(),\n attributesByModel,\n attributesByStep,\n };\n }\n\n /**\n * Exports the graph to JSON for debugging\n */\n public toJSON(): any {\n return {\n nodes: Array.from(this.nodeIndex.values()),\n edges: this.graph.getAllEdges().map(([from, to]) => {\n const fromNode = this.parseNodeId(from);\n const toNode = this.parseNodeId(to);\n return {\n from: fromNode,\n to: toNode,\n };\n }),\n };\n }\n}\n","export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';\n\nexport interface LoggerConfig {\n /** Minimum log level to output */\n level?: LogLevel;\n /** Output format: 'json' for production/cloud, 'pretty' for development */\n format?: 'json' | 'pretty';\n /** Optional prefix for all log messages (useful for module identification) */\n prefix?: string;\n /** Custom log handler (useful for sending logs to external services) */\n onLog?: (entry: LogEntry) => void;\n /** Handler for tracked errors (integrates with QualityController) */\n onTrackError?: (entry: TrackedErrorEntry) => void;\n}\n\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: number;\n prefix?: string;\n context?: Record<string, any>;\n error?: Error;\n}\n\nexport interface TrackedErrorEntry extends LogEntry {\n level: 'error';\n errorType?: string;\n errorGroup?: string;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nexport class Logger {\n private config: Required<Omit<LoggerConfig, 'onLog' | 'onTrackError' | 'prefix'>> & {\n prefix?: string;\n onLog?: (entry: LogEntry) => void;\n onTrackError?: (entry: TrackedErrorEntry) => void;\n };\n\n private static globalConfig: Partial<LoggerConfig> = {};\n\n constructor(config: LoggerConfig = {}) {\n this.config = {\n level: config.level ?? Logger.globalConfig.level ?? this.getDefaultLevel(),\n format: config.format ?? Logger.globalConfig.format ?? this.getDefaultFormat(),\n prefix: config.prefix,\n onLog: config.onLog ?? Logger.globalConfig.onLog,\n onTrackError: config.onTrackError ?? Logger.globalConfig.onTrackError,\n };\n }\n\n /**\n * Set global logger configuration (affects all logger instances)\n */\n public static setGlobalConfig(config: Partial<LoggerConfig>): void {\n Logger.globalConfig = { ...Logger.globalConfig, ...config };\n }\n\n /**\n * Get the global logger configuration\n */\n public static getGlobalConfig(): Partial<LoggerConfig> {\n return { ...Logger.globalConfig };\n }\n\n /**\n * Configure module-specific log levels\n * @example\n * logger.setModuleLevel('ModelParser', 'debug');\n * logger.setModuleLevel('Catalog', 'warn');\n */\n public static setModuleLevel(module: string, level: LogLevel): void {\n if (!Logger.globalConfig.onLog) {\n Logger.globalConfig.onLog = undefined; // Initialize if needed\n }\n // Store in a static registry that will be checked by child loggers\n if (!(Logger as any)._moduleOverrides) {\n (Logger as any)._moduleOverrides = new Map<string, LogLevel>();\n }\n (Logger as any)._moduleOverrides.set(module, level);\n }\n\n /**\n * Get module-specific log level\n */\n private static getModuleLevel(module?: string): LogLevel | undefined {\n if (!module || !(Logger as any)._moduleOverrides) {\n return undefined;\n }\n return (Logger as any)._moduleOverrides.get(module);\n }\n\n private getDefaultLevel(): LogLevel {\n return 'info';\n }\n\n private getDefaultFormat(): 'json' | 'pretty' {\n return 'pretty';\n }\n\n\n private getEffectiveLevel(): LogLevel {\n // Check for module override first\n if (this.config.prefix) {\n const moduleLevel = Logger.getModuleLevel(this.config.prefix);\n if (moduleLevel) {\n return moduleLevel;\n }\n }\n return this.config.level;\n }\n\n private shouldLog(level: LogLevel): boolean {\n const effectiveLevel = this.getEffectiveLevel();\n return LOG_LEVELS[level] <= LOG_LEVELS[effectiveLevel];\n }\n\n private formatMessage(entry: LogEntry): string {\n if (this.config.format === 'json') {\n const jsonEntry: any = {\n timestamp: new Date(entry.timestamp).toISOString(),\n level: entry.level,\n message: entry.message,\n };\n\n if (entry.prefix) {\n jsonEntry.module = entry.prefix;\n }\n\n if (entry.context) {\n jsonEntry.context = entry.context;\n }\n\n if (entry.error) {\n jsonEntry.error = {\n name: entry.error.name,\n message: entry.error.message,\n stack: entry.error.stack,\n };\n }\n\n return JSON.stringify(jsonEntry);\n }\n\n // Pretty format for development\n const timestamp = new Date(entry.timestamp).toISOString().substring(11, 23); // HH:mm:ss.SSS\n const prefix = entry.prefix ? `[${entry.prefix}]` : '';\n const levelIcon = this.getLevelIcon(entry.level);\n const contextStr = entry.context ? ` ${JSON.stringify(entry.context)}` : '';\n\n return `${timestamp} ${levelIcon} ${prefix} ${entry.message}${contextStr}`.trim();\n }\n\n private getLevelIcon(level: LogLevel): string {\n switch (level) {\n case 'error':\n return '✖';\n case 'warn':\n return '⚠';\n case 'info':\n return 'ℹ';\n case 'debug':\n return '⚙';\n default:\n return ' ';\n }\n }\n\n private log(level: LogLevel, message: string, context?: Record<string, any>, error?: Error): void {\n // Fast path: check level first to minimize overhead\n if (!this.shouldLog(level)) {\n return;\n }\n\n const entry: LogEntry = {\n level,\n message,\n timestamp: Date.now(),\n prefix: this.config.prefix,\n context,\n error,\n };\n\n // Custom handler takes precedence\n if (this.config.onLog) {\n this.config.onLog(entry);\n return;\n }\n\n const formatted = this.formatMessage(entry);\n\n // Use appropriate console method\n if (typeof console !== 'undefined') {\n switch (level) {\n case 'error':\n if (error) {\n console.error(formatted, error);\n } else {\n console.error(formatted);\n }\n break;\n case 'warn':\n console.warn(formatted);\n break;\n case 'debug':\n if (console.debug) {\n console.debug(formatted);\n } else {\n console.log(formatted);\n }\n break;\n default:\n console.log(formatted);\n }\n }\n }\n\n /**\n * Log an error message\n */\n public error(message: string, errorOrContext?: Error | Record<string, any>, context?: Record<string, any>): void {\n const error = errorOrContext instanceof Error ? errorOrContext : undefined;\n const ctx = errorOrContext instanceof Error ? context : errorOrContext;\n this.log('error', message, ctx, error);\n }\n\n /**\n * Log and track an error (integrates with QualityController)\n * @param message Error message\n * @param error Error object\n * @param options Additional context and error classification\n */\n public trackError(\n message: string,\n error?: Error,\n options?: {\n context?: Record<string, any>;\n errorType?: string;\n errorGroup?: string;\n },\n ): void {\n // Log the error normally\n this.error(message, error, options?.context);\n\n // If tracking handler is registered, invoke it\n if (this.config.onTrackError) {\n const entry: TrackedErrorEntry = {\n level: 'error',\n message,\n timestamp: Date.now(),\n prefix: this.config.prefix,\n context: options?.context,\n error,\n errorType: options?.errorType,\n errorGroup: options?.errorGroup,\n };\n this.config.onTrackError(entry);\n }\n }\n\n /**\n * Log a warning message\n */\n public warn(message: string, context?: Record<string, any>): void {\n this.log('warn', message, context);\n }\n\n /**\n * Log an info message\n */\n public info(message: string, context?: Record<string, any>): void {\n this.log('info', message, context);\n }\n\n /**\n * Log a debug message\n */\n public debug(message: string, context?: Record<string, any>): void {\n this.log('debug', message, context);\n }\n\n /**\n * Create a child logger with additional context or configuration\n * Child loggers inherit parent config but can override specific settings\n *\n * @example\n * const modelLogger = logger.child({ prefix: 'ModelParser' });\n * modelLogger.info('Processing data'); // [ModelParser] Processing data\n *\n * @example\n * const debugLogger = logger.child({ prefix: 'Catalog', level: 'debug' });\n */\n public child(childConfig: Partial<LoggerConfig>): Logger {\n const mergedConfig: LoggerConfig = {\n ...this.config,\n ...childConfig,\n // Handle prefix concatenation for nested children\n prefix: childConfig.prefix\n ? this.config.prefix\n ? `${this.config.prefix}:${childConfig.prefix}`\n : childConfig.prefix\n : this.config.prefix,\n };\n\n return new Logger(mergedConfig);\n }\n\n /**\n * Update this logger's configuration\n */\n public configure(config: Partial<LoggerConfig>): void {\n Object.assign(this.config, config);\n }\n}\n\n// Export default global logger instance\nexport const logger = new Logger();\n","import { cloneDeep } from 'lodash-es';\nimport { generateObjectHash } from './object-hash';\nimport { logger } from './logger';\n\nexport class CacheController {\n private cache!: Map<string, any>;\n // Track which models and attributes each cache entry depends on\n private cacheDependencies!: Map<string, CacheDependencyInfo>;\n // Track cache hits and misses for statistics\n private hitCount: number = 0;\n private missCount: number = 0;\n private log = logger.child({ prefix: 'CacheController' });\n\n constructor(public enabled: boolean) {\n this.cache = new Map();\n this.cacheDependencies = new Map();\n }\n\n public get(key: string, context: any): any {\n if (!this.enabled) return undefined;\n const cacheName = this.getCacheName(key, context);\n const hasValue = this.cache.has(cacheName);\n\n if (hasValue) {\n this.hitCount++;\n } else {\n this.missCount++;\n }\n\n return this.cache.get(cacheName);\n }\n\n public set(key: string, context: any, value: any, dependencies?: CacheDependencyInfo): void {\n if (!this.enabled) return undefined;\n const cacheName = this.getCacheName(key, context);\n this.cache.set(cacheName, value);\n\n // Store dependency information if provided\n if (dependencies) {\n this.cacheDependencies.set(cacheName, dependencies);\n } else {\n // Warn about missing dependencies in debug mode\n this.log.debug(\n `Cache entry '${key}' stored without dependencies. ` +\n `This prevents granular invalidation and may cause stale data.`,\n );\n }\n }\n\n public has(key: string, context: any): boolean {\n return this.cache.has(this.getCacheName(key, context));\n }\n\n public clear(): void {\n this.cache.clear();\n this.cacheDependencies.clear();\n this.hitCount = 0;\n this.missCount = 0;\n }\n\n /**\n * Invalidates cache entries that depend on the specified models and attributes\n * @param changedModels - Map of modelKey → Set of changed attributeKeys\n */\n public invalidateByDependencies(changedModels: Map<string, Set<string>>): void {\n if (!this.enabled) return;\n\n const entriesToRemove: string[] = [];\n\n for (const [cacheName, deps] of this.cacheDependencies) {\n let shouldInvalidate = false;\n\n // Check if any of the changed models/attributes affect this cache entry\n for (const [modelKey, attributeKeys] of changedModels) {\n if (deps.models.has(modelKey)) {\n // If we track specific attributes for this model\n if (deps.modelAttributes.has(modelKey)) {\n const trackedAttrs = deps.modelAttributes.get(modelKey)!;\n // Check if any changed attribute is tracked\n for (const attr of attributeKeys) {\n if (trackedAttrs.has(attr)) {\n shouldInvalidate = true;\n break;\n }\n }\n } else {\n // No specific attributes tracked, so any change to this model invalidates\n shouldInvalidate = true;\n }\n }\n\n if (shouldInvalidate) break;\n }\n\n if (shouldInvalidate) {\n entriesToRemove.push(cacheName);\n }\n }\n\n // Remove invalidated cache entries\n for (const cacheName of entriesToRemove) {\n this.cache.delete(cacheName);\n this.cacheDependencies.delete(cacheName);\n }\n }\n\n /**\n * Invalidates all cache entries for specific models\n * @param modelKeys - Array of model keys to invalidate\n */\n public invalidateByModels(modelKeys: string[]): void {\n if (!this.enabled) return;\n\n const modelSet = new Set(modelKeys);\n const entriesToRemove: string[] = [];\n\n for (const [cacheName, deps] of this.cacheDependencies) {\n // Check if this cache entry depends on any of the specified models\n for (const modelKey of modelSet) {\n if (deps.models.has(modelKey)) {\n entriesToRemove.push(cacheName);\n break;\n }\n }\n }\n\n // Remove invalidated cache entries\n for (const cacheName of entriesToRemove) {\n this.cache.delete(cacheName);\n this.cacheDependencies.delete(cacheName);\n }\n }\n\n /**\n * Invalidates cache entries by key prefix (e.g., 'query', 'facets')\n * @param keyPrefix - The key prefix to match\n */\n public invalidateByKeyPrefix(keyPrefix: string): void {\n if (!this.enabled) return;\n\n const entriesToRemove: string[] = [];\n\n for (const cacheName of this.cache.keys()) {\n if (cacheName.startsWith(keyPrefix + '_')) {\n entriesToRemove.push(cacheName);\n }\n }\n\n for (const cacheName of entriesToRemove) {\n this.cache.delete(cacheName);\n this.cacheDependencies.delete(cacheName);\n }\n }\n\n /**\n * Invalidates all cache entries for a specific model\n * @param modelKey - The model key to invalidate\n */\n public invalidateForModel(modelKey: string): void {\n this.invalidateByModels([modelKey]);\n }\n\n /**\n * Invalidates cache entries that depend on a specific attribute of a model\n * @param modelKey - The model key\n * @param attributeKey - The attribute key to invalidate\n */\n public invalidateForAttribute(modelKey: string, attributeKey: string): void {\n if (!this.enabled) return;\n\n const changedModels = new Map<string, Set<string>>();\n changedModels.set(modelKey, new Set([attributeKey]));\n this.invalidateByDependencies(changedModels);\n }\n\n /**\n * Invalidates cache entries matching a custom predicate\n * @param predicate - Function that returns true for cache keys to invalidate\n */\n public invalidateMatching(predicate: (key: string) => boolean): void {\n if (!this.enabled) return;\n\n const entriesToRemove: string[] = [];\n\n for (const cacheName of this.cache.keys()) {\n if (predicate(cacheName)) {\n entriesToRemove.push(cacheName);\n }\n }\n\n for (const cacheName of entriesToRemove) {\n this.cache.delete(cacheName);\n this.cacheDependencies.delete(cacheName);\n }\n }\n\n public generateContext(constext: any): any {\n try {\n return cloneDeep(constext);\n } catch (err) {\n this.log.error('Error in cache generateContext', err as Error);\n return constext;\n }\n }\n\n public getCacheName(key: string, context: any): string {\n const hash = generateObjectHash(context);\n return key + '_' + hash;\n }\n\n /**\n * Gets cache statistics including hit rate\n */\n public getStats(): {\n size: number;\n hitRate: number;\n totalEntries: number;\n entriesWithDependencies: number;\n hits: number;\n misses: number;\n } {\n const totalRequests = this.hitCount + this.missCount;\n const hitRate = totalRequests > 0 ? this.hitCount / totalRequests : 0;\n\n return {\n size: this.cache.size,\n hitRate: hitRate,\n totalEntries: this.cache.size,\n entriesWithDependencies: this.cacheDependencies.size,\n hits: this.hitCount,\n misses: this.missCount,\n };\n }\n}\n\n/**\n * Information about which models and attributes a cache entry depends on\n */\nexport interface CacheDependencyInfo {\n /** Set of model keys this cache entry depends on */\n models: Set<string>;\n /** Map of modelKey → Set of attribute keys (if specific attributes matter) */\n modelAttributes: Map<string, Set<string>>;\n}\n","import { DateFilterOptions, NumberFilterOptions } from './types';\n\nexport const getFilterEntryId = (filterItemId: string, value = ''): string => {\n return `${filterItemId}#${value}`;\n};\n\nexport const getNumberConstraintUid = (options: NumberFilterOptions): string => {\n const value = `${options.gt}_${options.gte}_${options.lt}_${options.lte}_${options.eq}_${options.ne}`;\n return value;\n};\n\nexport const getDateConstraintUid = (options: DateFilterOptions): string => {\n const value = `${options.gt}_${options.gte}_${options.lt}_${options.lte}_${options.eq}_${options.ne}`;\n return value;\n};\n\nexport const uniqBy = (arr: any[], predicate: string): any[] => {\n const cb = typeof predicate === 'function' ? predicate : (o: any): any => o[predicate];\n\n return [\n ...arr\n .reduce((map, item) => {\n const key = item === null || item === undefined ? item : cb(item);\n\n if (!map.has(key)) {\n map.set(key, item);\n }\n\n return map;\n }, new Map())\n .values(),\n ];\n};\n\nexport const groupBy = (arr: any[], criteria: any): any => {\n return arr.reduce(function (obj, item) {\n // Check if the criteria is a function to run on the item or a property of it\n const key = typeof criteria === 'function' ? criteria(item) : item[criteria];\n\n // If the key doesn't exist yet, create it\n if (!Object.prototype.hasOwnProperty.call(obj, key)) {\n obj[key] = [];\n }\n\n // Push the value to the object\n obj[key].push(item);\n\n // Return the object to the next item in the loop\n return obj;\n }, {});\n};\n\nexport function generateKey(length = 10): string {\n let text = '';\n const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\n for (let i = 0; i < length; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n return text;\n}\n\nexport const removeTrailingSlash = (str: string): string => {\n return str.replace(/\\/+$/, '');\n};\n\nexport const escapeRegExp = (text: string): string => {\n if (typeof text !== 'string') return '';\n\n return text.replace(/[[\\]{}()*+?.,\\\\^$|#]/g, '\\\\$&');\n /**\n * original regex\n * remove \\s -\n */\n // return text.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&');\n};\n\nexport const unescapeRegExp = (escapedText: string): string => {\n if (typeof escapedText !== 'string') return '';\n\n return escapedText.replace(/\\\\([[\\]{}()*+?.,\\\\^$|#])/g, '$1');\n};\n\nexport const hasMinimumVersion = (version: string, minVersion: string): boolean => {\n try {\n return compareVersions(version, minVersion) >= 0;\n } catch (_err) {\n return false;\n }\n};\n\n/**\n * Compares two semantic version strings.\n * @param v1 - First version string (e.g., \"1.10.2\").\n * @param v2 - Second version string (e.g., \"1.9.0\").\n * @returns A number: -1 if v1 < v2, 1 if v1 > v2, 0 if equal.\n * @throws Error if the input is not a valid semantic version.\n */\nexport const compareVersions = (v1: string, v2: string): number => {\n if (!isValidVersion(v1) || !isValidVersion(v2)) {\n throw new Error(`Invalid version format: v1=\"${v1}\", v2=\"${v2}\". Expected format: \"x.y.z\"`);\n }\n\n const v1Parts = v1.split('.').map(Number);\n const v2Parts = v2.split('.').map(Number);\n\n for (let i = 0; i < 3; i++) {\n const num1 = v1Parts[i] || 0; // Default to 0 if undefined (e.g., \"1.2\" treated as \"1.2.0\")\n const num2 = v2Parts[i] || 0;\n\n if (num1 > num2) return 1;\n if (num1 < num2) return -1;\n }\n\n return 0;\n};\n\n/**\n * Validates if a string follows semantic versioning (x.y.z).\n * @param version - Version string to validate.\n * @returns True if valid, otherwise false.\n */\nexport const isValidVersion = (version: string): boolean => {\n return /^\\d+(\\.\\d+){0,2}$/.test(version);\n};\n","import { cloneDeep } from 'lodash-es';\n\nimport { perf } from './performance';\nimport { CallbackFunction, DcuplInitOptions } from './types';\nimport { AnalyticsController, AnalyticsEntrySdkActionValue } from './analytics.controller';\nimport { generateKey } from './helper';\n\nexport type DcuplUpdateScope = 'all' | 'global' | 'list' | 'list$';\nexport type DcuplUpdateTrigger = 'user' | 'loader';\nexport type DcuplUpdateType =\n | 'data_change'\n | 'dcupl_destroyed'\n | 'dcupl_handle_datacontainer'\n | 'dcupl_initialized'\n | 'dcupl_updated_manually'\n | 'dcupl_partial_update'\n | 'filter_many'\n | 'filter_one'\n | 'fn_aggregate'\n | 'fn_facets'\n | 'fn_groupBy'\n | 'fn_metadata'\n | 'fn_pivot'\n | 'fn_suggest'\n | 'list_created'\n | 'list_destroyed'\n | 'list_updated'\n | 'loader_added'\n | 'loader_config_fetched'\n | 'loader_data_fetched'\n | 'loader_models_fetched'\n | 'loader_scripts_fetched'\n | 'loader_processed'\n | 'loader_removed'\n | 'model_add'\n | 'query_apply_options'\n | 'query_apply'\n | 'query_execute'\n | 'query_get'\n | 'query_has'\n | 'query_many'\n | 'query_one'\n | 'query_remove'\n | 'query_reset'\n | 'result_updated'\n | 'view_add';\n\nexport type DcuplUpdateAction = 'update';\n\nexport type DcuplUpdateMessage = {\n key?: number;\n scope: DcuplUpdateScope;\n type: DcuplUpdateType;\n method?: string;\n args?: any;\n action?: DcuplUpdateAction;\n // name?: string;\n origin?: DcuplUpdateTrigger;\n start?: number;\n end?: number;\n duration?: number;\n originCdRefKey?: string;\n modelKey?: string;\n listKey?: string;\n result?: any;\n cloneResult?: boolean;\n connectMessage?: boolean;\n connectMessageResult?: boolean;\n};\n\nexport class ChangeDetector {\n public key!: string;\n protected parentCd: ChangeDetector | undefined;\n public dcuplInitOptions!: DcuplInitOptions;\n public analyticsController!: AnalyticsController;\n private off!: () => void;\n\n private updateListener: Set<CallbackFunction> = new Set();\n\n // only the \"root\" counter will be increased and used in the events\n private counter = 0;\n\n constructor() {}\n\n public _init(options: {\n cdRefKey: string;\n dcuplInitOptions: DcuplInitOptions;\n parentCd?: ChangeDetector;\n analyticsController: AnalyticsController;\n }): void {\n this.key = options.cdRefKey;\n this.analyticsController = options.analyticsController;\n\n this.parentCd = options.parentCd;\n\n this.dcuplInitOptions = options.dcuplInitOptions;\n\n if (this.parentCd) {\n this.off = this.parentCd.on((msg: DcuplUpdateMessage) => {\n if (msg.action === 'update' && msg.originCdRefKey === this.parentCd?.key) {\n // prevent connect message relay if the message is coming from the parent\n msg.connectMessage = false;\n this.trigger(msg);\n }\n });\n }\n }\n\n public getCounter(): number {\n if (this.parentCd) {\n return this.parentCd.getCounter();\n }\n return this.counter;\n }\n\n public increaseCounter(): void {\n if (this.parentCd) {\n this.parentCd.increaseCounter();\n } else {\n this.counter++;\n }\n }\n\n public destroy(): void {\n if (this.off) {\n this.off();\n }\n }\n\n public trigger(msg: DcuplUpdateMessage): void {\n if (!msg.originCdRefKey) {\n msg.originCdRefKey = this.key;\n }\n\n if (!msg.key) {\n this.increaseCounter();\n msg.key = this.getCounter();\n }\n\n if (!msg.end) {\n msg.end = perf.now();\n msg.duration = msg.start! - msg.end;\n }\n\n this.updateListener.forEach((cb) => {\n cb(msg);\n });\n }\n\n public on(cb: CallbackFunction): () => void {\n this.updateListener.add(cb);\n return () => {\n this.updateListener.delete(cb);\n };\n }\n}\n\nexport const clone = (obj: any, shouldClone: boolean): any => {\n if (!shouldClone) {\n return obj;\n }\n if (typeof structuredClone !== 'undefined') {\n return structuredClone(obj);\n } else {\n return cloneDeep(obj);\n }\n};\n\nconst finish = (\n targetClass: any,\n cdRef: ChangeDetector,\n msg: DcuplUpdateMessage,\n start: number,\n context: any,\n result: any,\n): any => {\n try {\n const shouldClone = !!cdRef.dcuplInitOptions.performance?.clone?.enabled && !!msg.cloneResult;\n\n const valueToReturn = clone(result, shouldClone);\n\n if (msg.scope === 'list') {\n msg.listKey = targetClass.cdRef?.key || targetClass.key;\n msg.modelKey = targetClass.model?.key || targetClass.modelKey;\n }\n\n if (msg.type === 'list_created') {\n const listKey = msg.args[0].listKey as string;\n if (listKey.startsWith('__global_list__')) {\n return valueToReturn;\n }\n }\n\n if (msg.listKey?.startsWith('__global_list__')) {\n return valueToReturn;\n }\n\n const end = perf.now();\n\n const duration = end - start;\n const roundedDuration = Math.round(duration * 1000) / 1000;\n msg.duration = roundedDuration;\n msg.start = start;\n msg.end = end;\n\n const value: AnalyticsEntrySdkActionValue = {\n duration: msg.duration,\n };\n\n if (msg.modelKey) {\n value.model = msg.modelKey;\n }\n\n if (msg.connectMessageResult) {\n msg.result = valueToReturn;\n }\n\n if (msg.args) {\n value.args = msg.args;\n }\n\n cdRef?.trigger(msg);\n\n cdRef.analyticsController.mark({\n name: `${msg.type}:end`,\n context: context,\n });\n\n cdRef.analyticsController.measure({\n name: msg.type,\n start: `${msg.type}:start`,\n end: `${msg.type}:end`,\n context: context,\n detail: value,\n });\n\n return valueToReturn;\n } catch (_err) {\n return result;\n }\n};\n\nexport const trigger = (_msg: DcuplUpdateMessage): any => {\n const log = (_target: any, _propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor => {\n const originalMethod = descriptor.value;\n\n descriptor.value = function (...args: any[]): any {\n const start = perf.now();\n\n const cdRef: ChangeDetector = (this as any).cdRef;\n\n const msg = clone(_msg, true);\n\n if (!msg.key) {\n cdRef.increaseCounter();\n msg.key = cdRef.getCounter();\n }\n\n const context = generateKey();\n\n cdRef.analyticsController.mark({\n name: `${msg.type}:start`,\n context,\n });\n\n const modelKey = (this as any).model?.key;\n\n if (!msg.originCdRefKey && cdRef.key) {\n msg.originCdRefKey = cdRef.key;\n }\n\n if (modelKey) {\n msg.modelKey = modelKey;\n }\n\n const result = originalMethod.apply(this, args);\n\n msg.method = _propertyKey;\n msg.args = args;\n\n if (result && typeof result === 'object' && 'then' in result) {\n return result.then((result: any) => {\n return finish(this, cdRef, msg, start, context, result);\n });\n } else {\n return finish(this, cdRef, msg, start, context, result);\n }\n };\n return descriptor;\n };\n\n return log;\n};\n","import { ListItem, Suggestion, SuggestionOptions, InverseIndexValuesMap } from './types';\n\nimport { QueryManager } from '.';\n\nexport function computeSuggestions<ListType extends ListItem = ListItem>(\n data: Map<string, ListType>,\n index: InverseIndexValuesMap,\n options: SuggestionOptions,\n queryManager: QueryManager,\n): Suggestion[] {\n if (!options.max) {\n options.max = 20;\n }\n const suggestions: Suggestion[] = [];\n\n const relevantSearchTerm = options.transform\n ? queryManager.transformValue(options.value, options.transform)\n : options.value;\n\n for (const [key, value] of index.entries()) {\n const isRelevantAttributeIndex = Array.from(value).some((item) => data.has(item));\n\n const relevantKey = options.transform ? queryManager.transformValue(key, options.transform) : key;\n\n if (options.excludeUndefineds && typeof key === 'undefined') {\n continue;\n }\n\n if (options.excludeNulls && key === null) {\n continue;\n }\n\n // Handle empty search term - return all values\n if (isRelevantAttributeIndex && (relevantSearchTerm === '' || relevantSearchTerm === '//')) {\n const suggestion: Suggestion = {\n value: key,\n keys: options.calculateResultKeys ? Array.from(value) : [],\n };\n suggestions.push(suggestion);\n }\n // Handle regex patterns (format: /pattern/)\n else if (\n isRelevantAttributeIndex &&\n typeof relevantSearchTerm === 'string' &&\n relevantSearchTerm.startsWith('/') &&\n relevantSearchTerm.endsWith('/')\n ) {\n if (queryManager.queryFind(relevantKey, relevantSearchTerm, 'some')) {\n const suggestion: Suggestion = {\n value: key,\n keys: options.calculateResultKeys ? Array.from(value) : [],\n };\n suggestions.push(suggestion);\n }\n }\n // Default: case-insensitive startsWith matching for search terms\n else if (isRelevantAttributeIndex && typeof relevantSearchTerm === 'string') {\n const keyStr = String(relevantKey ?? '');\n const searchStr = relevantSearchTerm;\n if (keyStr.toLowerCase().startsWith(searchStr.toLowerCase())) {\n const suggestion: Suggestion = {\n value: key,\n keys: options.calculateResultKeys ? Array.from(value) : [],\n };\n suggestions.push(suggestion);\n }\n }\n // Fallback to exact matching for non-string search terms\n else if (isRelevantAttributeIndex && queryManager.queryFind(relevantKey, relevantSearchTerm, 'some')) {\n const suggestion: Suggestion = {\n value: key,\n keys: options.calculateResultKeys ? Array.from(value) : [],\n };\n suggestions.push(suggestion);\n }\n\n if (suggestions.length === options.max) {\n break;\n }\n }\n\n return suggestions;\n}\n","/**\n * Custom date utilities to replace date-fns\n * Supports custom format patterns for date parsing\n */\n\n/**\n * Check if a value is a Date instance\n * @param value - Value to check\n * @returns true if value is a Date instance (valid or invalid), false otherwise\n */\nexport function isDateInstance(value: any): value is Date {\n return value instanceof Date;\n}\n\n/**\n * Check if a Date object represents a valid date\n * @param date - Date object to validate\n * @returns true if date is valid, false otherwise\n */\nexport function isValidDate(date: Date): boolean {\n return date instanceof Date && !isNaN(date.getTime());\n}\n\n/**\n * Format token definitions for date parsing\n */\ninterface FormatToken {\n type: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond';\n pattern: string;\n regex: string;\n}\n\n/**\n * Supported format tokens mapped to regex patterns\n * Tokens are sorted by length (longest first) to avoid partial matches\n */\nconst FORMAT_TOKENS: Record<string, FormatToken> = {\n yyyy: { type: 'year', pattern: 'yyyy', regex: '(\\\\d{4})' },\n yy: { type: 'year', pattern: 'yy', regex: '(\\\\d{2})' },\n MM: { type: 'month', pattern: 'MM', regex: '(\\\\d{2})' },\n M: { type: 'month', pattern: 'M', regex: '(\\\\d{1,2})' },\n dd: { type: 'day', pattern: 'dd', regex: '(\\\\d{2})' },\n d: { type: 'day', pattern: 'd', regex: '(\\\\d{1,2})' },\n HH: { type: 'hour', pattern: 'HH', regex: '(\\\\d{2})' },\n H: { type: 'hour', pattern: 'H', regex: '(\\\\d{1,2})' },\n mm: { type: 'minute', pattern: 'mm', regex: '(\\\\d{2})' },\n m: { type: 'minute', pattern: 'm', regex: '(\\\\d{1,2})' },\n ss: { type: 'second', pattern: 'ss', regex: '(\\\\d{2})' },\n s: { type: 'second', pattern: 's', regex: '(\\\\d{1,2})' },\n SSS: { type: 'millisecond', pattern: 'SSS', regex: '(\\\\d{3})' },\n};\n\n/**\n * Date component structure\n */\ninterface DateComponents {\n year: number;\n month: number;\n day: number;\n hour: number;\n minute: number;\n second: number;\n millisecond: number;\n}\n\n/**\n * Token match info\n */\ninterface TokenMatch {\n type: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond';\n pattern: string;\n}\n\n/**\n * Regex build result\n */\ninterface RegexBuildResult {\n regex: RegExp;\n tokenOrder: TokenMatch[];\n}\n\n/**\n * Build a regex pattern from a format string\n * @param format - Format string (e.g., 'yyyy-MM-dd', 'dd.MM.yyyy')\n * @returns Regex pattern and token order\n */\nfunction buildRegexFromFormat(format: string): RegexBuildResult {\n let regexStr = '^';\n const tokenOrder: TokenMatch[] = [];\n let remaining = format;\n\n // Sort tokens by length (longest first) to avoid partial matches\n const sortedTokens = Object.entries(FORMAT_TOKENS).sort((a, b) => b[0].length - a[0].length);\n\n while (remaining.length > 0) {\n let matched = false;\n\n // Try to match tokens\n for (const [pattern, token] of sortedTokens) {\n if (remaining.startsWith(pattern)) {\n regexStr += token.regex;\n tokenOrder.push({ type: token.type, pattern });\n remaining = remaining.slice(pattern.length);\n matched = true;\n break;\n }\n }\n\n // Handle literal characters (separators, spaces, etc.)\n if (!matched) {\n const char = remaining[0];\n if (!char) {\n break;\n }\n // Escape special regex characters\n regexStr += char.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n remaining = remaining.slice(1);\n }\n }\n\n regexStr += '$';\n return { regex: new RegExp(regexStr), tokenOrder };\n}\n\n/**\n * Extract date components from regex match\n * @param match - Regex match result\n * @param tokenOrder - Order of tokens in the format string\n * @returns Date components\n */\nfunction extractComponents(match: RegExpMatchArray, tokenOrder: TokenMatch[]): DateComponents {\n const components: DateComponents = {\n year: 1970,\n month: 1,\n day: 1,\n hour: 0,\n minute: 0,\n second: 0,\n millisecond: 0,\n };\n\n for (let i = 0; i < tokenOrder.length; i++) {\n const matchValue = match[i + 1];\n if (!matchValue) {\n continue;\n }\n const value = parseInt(matchValue, 10);\n const tokenMatch = tokenOrder[i];\n if (!tokenMatch) {\n continue;\n }\n\n // Handle 2-digit year (00-99 → 2000-2099) - only for 'yy' pattern\n if (tokenMatch.type === 'year' && tokenMatch.pattern === 'yy') {\n components.year = 2000 + value;\n } else {\n components[tokenMatch.type] = value;\n }\n }\n\n return components;\n}\n\n/**\n * Construct and validate a Date object from components\n * @param components - Date components\n * @returns Valid Date object or Invalid Date\n */\nfunction constructDate(components: DateComponents): Date {\n // Validate ranges\n if (components.month < 1 || components.month > 12) {\n return new Date(NaN);\n }\n if (components.day < 1 || components.day > 31) {\n return new Date(NaN);\n }\n if (components.hour < 0 || components.hour > 23) {\n return new Date(NaN);\n }\n if (components.minute < 0 || components.minute > 59) {\n return new Date(NaN);\n }\n if (components.second < 0 || components.second > 59) {\n return new Date(NaN);\n }\n if (components.millisecond < 0 || components.millisecond > 999) {\n return new Date(NaN);\n }\n\n // Construct date (JS months are 0-based)\n const date = new Date(\n components.year,\n components.month - 1,\n components.day,\n components.hour,\n components.minute,\n components.second,\n components.millisecond,\n );\n\n // Validate that date is valid\n if (!isValidDate(date)) {\n return new Date(NaN);\n }\n\n // Verify no overflow (e.g., Feb 31 → Mar 3, Apr 31 → May 1)\n if (\n date.getFullYear() !== components.year ||\n date.getMonth() !== components.month - 1 ||\n date.getDate() !== components.day\n ) {\n return new Date(NaN);\n }\n\n return date;\n}\n\n/**\n * Parse a date string according to a format pattern\n *\n * Supported format tokens:\n * - yyyy: 4-digit year\n * - yy: 2-digit year (00-99 → 2000-2099)\n * - MM: 2-digit month (01-12)\n * - M: 1-2 digit month (1-12)\n * - dd: 2-digit day (01-31)\n * - d: 1-2 digit day (1-31)\n * - HH: 2-digit hour (00-23)\n * - H: 1-2 digit hour (0-23)\n * - mm: 2-digit minute (00-59)\n * - m: 1-2 digit minute (0-59)\n * - ss: 2-digit second (00-59)\n * - s: 1-2 digit second (0-59)\n * - SSS: 3-digit millisecond (000-999)\n *\n * Special formats:\n * - 't': Unix timestamp in seconds\n * - 'T': Unix timestamp in milliseconds\n *\n * @param value - The date string to parse\n * @param format - Format pattern (e.g., 'yyyy-MM-dd', 'dd.MM.yyyy', 't', 'T')\n * @param referenceDate - Optional reference date (reserved for future use, currently unused)\n * @returns Parsed Date object or Invalid Date (NaN)\n *\n * @example\n * parseDate('2023-12-25', 'yyyy-MM-dd') // Dec 25, 2023\n * parseDate('25.12.2023', 'dd.MM.yyyy') // Dec 25, 2023\n * parseDate('1640000000', 't') // Unix seconds\n * parseDate('2023-02-31', 'yyyy-MM-dd') // Invalid Date (Feb only has 28/29 days)\n */\nexport function parseDate(value: string, format: string, _referenceDate?: Date): Date {\n // Trim whitespace\n value = value.trim();\n\n // Handle special format: Unix timestamp in seconds\n if (format === 't') {\n const timestamp = Number(value);\n if (isNaN(timestamp)) {\n return new Date(NaN);\n }\n return new Date(timestamp * 1000);\n }\n\n // Handle special format: Unix timestamp in milliseconds\n if (format === 'T') {\n const timestamp = Number(value);\n if (isNaN(timestamp)) {\n return new Date(NaN);\n }\n return new Date(timestamp);\n }\n\n // Build regex from format string\n const { regex, tokenOrder } = buildRegexFromFormat(format);\n const match = value.match(regex);\n\n if (!match) {\n return new Date(NaN);\n }\n\n // Extract components\n const components = extractComponents(match, tokenOrder);\n\n // Validate and construct date\n return constructDate(components);\n}\n","import { QueryAttributeEntry } from './query-builder';\nimport { get } from 'lodash-es';\n\n/**\n * Result of path validation\n */\nexport interface ValidationResult {\n valid: boolean;\n errors?: string[];\n}\n\n/**\n * Simple LRU Cache implementation for storing parse results\n */\nclass LRUCache<K, V> {\n private cache: Map<K, V>;\n private readonly maxSize: number;\n private accessOrder: K[];\n\n constructor(maxSize: number) {\n this.cache = new Map();\n this.maxSize = maxSize;\n this.accessOrder = [];\n }\n\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used)\n this.updateAccessOrder(key);\n }\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n // Update existing entry\n this.cache.set(key, value);\n this.updateAccessOrder(key);\n } else {\n // Add new entry\n if (this.cache.size >= this.maxSize) {\n // Evict least recently used\n const lruKey = this.accessOrder.shift();\n if (lruKey !== undefined) {\n this.cache.delete(lruKey);\n }\n }\n this.cache.set(key, value);\n this.accessOrder.push(key);\n }\n }\n\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n clear(): void {\n this.cache.clear();\n this.accessOrder = [];\n }\n\n get size(): number {\n return this.cache.size;\n }\n\n private updateAccessOrder(key: K): void {\n const index = this.accessOrder.indexOf(key);\n if (index > -1) {\n this.accessOrder.splice(index, 1);\n }\n this.accessOrder.push(key);\n }\n\n // For testing/debugging\n getStats(): { size: number; maxSize: number; hitRate?: number } {\n return {\n size: this.cache.size,\n maxSize: this.maxSize,\n };\n }\n}\n\n/**\n * Dependency graph for detecting circular references\n */\nclass DependencyGraph {\n private graph: Map<string, Set<string>>;\n\n constructor() {\n this.graph = new Map();\n }\n\n addDependency(from: string, to: string): void {\n if (!this.graph.has(from)) {\n this.graph.set(from, new Set());\n }\n this.graph.get(from)!.add(to);\n }\n\n hasCycle(path: string[]): boolean {\n if (path.length === 0) return false;\n\n const visited = new Set<string>();\n const recursionStack = new Set<string>();\n\n const hasCycleRecursive = (node: string): boolean => {\n visited.add(node);\n recursionStack.add(node);\n\n const neighbors = this.graph.get(node);\n if (neighbors) {\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor)) {\n if (hasCycleRecursive(neighbor)) {\n return true;\n }\n } else if (recursionStack.has(neighbor)) {\n return true;\n }\n }\n }\n\n recursionStack.delete(node);\n return false;\n };\n\n // Check if the path itself forms a cycle\n for (let i = 0; i < path.length - 1; i++) {\n const from = path[i];\n const to = path[i + 1];\n if (from && to) {\n this.addDependency(from, to);\n }\n }\n\n // Check for any node in the path\n for (const node of path) {\n if (!visited.has(node)) {\n if (hasCycleRecursive(node)) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n clear(): void {\n this.graph.clear();\n }\n}\n\n/**\n * DeepQueryService - Centralized service for parsing, validating, and caching deep query attributes\n *\n * This service provides:\n * - LRU caching for parsed attribute paths (1000 entries, targeting >80% hit rate)\n * - Circular reference detection\n * - Multi-level nesting support (3+ levels)\n * - Path validation with descriptive errors\n */\nexport class DeepQueryService {\n private parseCache: LRUCache<string, QueryAttributeEntry>;\n private circularRefDetector: DependencyGraph;\n private cacheHits: number = 0;\n private cacheMisses: number = 0;\n\n constructor(cacheSize: number = 1000) {\n this.parseCache = new LRUCache(cacheSize);\n this.circularRefDetector = new DependencyGraph();\n }\n\n /**\n * Parse an attribute string into its components\n *\n * Supports:\n * - Dot notation: `customer.name`\n * - Bracket notation: `items[0].price`, `items[*].price`\n * - Multi-level: `order.customer.address.city`\n *\n * @param attribute - The attribute path to parse\n * @returns QueryAttributeEntry with isDeepQuery, reference, referenceAttribute\n *\n * @example\n * ```typescript\n * const service = new DeepQueryService();\n * const entry = service.parse('customer.name');\n * // Returns: { attribute: 'customer.name', isDeepQuery: true, reference: 'customer', referenceAttribute: 'name' }\n * ```\n */\n parse(attribute: string): QueryAttributeEntry {\n // Check cache first\n if (this.parseCache.has(attribute)) {\n this.cacheHits++;\n return this.parseCache.get(attribute)!;\n }\n\n this.cacheMisses++;\n\n // Parse the attribute\n let referenceAttribute: string | undefined;\n let reference: string | undefined;\n\n const hasDot = attribute.includes('.');\n const hasBracket = attribute.includes('[');\n const isDeepQuery = hasDot || hasBracket;\n\n if (isDeepQuery) {\n // The reference is always the segment before the first '.' or '['\n reference = attribute.split(/[.[]/)[0];\n\n // The bracket belongs to the reference segment only when the first '['\n // comes before the first '.' (e.g. items[0].prop). When a dot comes first\n // (e.g. order.items[0].price), the bracket is part of a *later* segment and\n // must stay in referenceAttribute so the recursive projection builder can\n // descend through it — otherwise the intermediate segment is silently dropped.\n const firstDot = attribute.indexOf('.');\n const firstBracket = attribute.indexOf('[');\n const bracketLeadsReference = hasBracket && (firstDot === -1 || firstBracket < firstDot);\n\n if (bracketLeadsReference) {\n // Handle patterns like: ref[0].prop, ref[*].prop, ref[10].prop\n const closingBracketIdx = attribute.indexOf(']');\n if (closingBracketIdx > -1) {\n const dotAfterBracketIdx = attribute.indexOf('.', closingBracketIdx + 1);\n if (dotAfterBracketIdx > -1) {\n // There is a property after the array index/wildcard\n referenceAttribute = attribute.substring(dotAfterBracketIdx + 1);\n } else {\n // Array of primitives like ref[0] (no trailing property)\n referenceAttribute = undefined;\n }\n } else if (hasDot) {\n // Fallback: bracket not properly closed but dot exists\n referenceAttribute = attribute.substring(firstDot + 1);\n }\n } else if (hasDot) {\n // Simple deep path like ref.prop, or dot-then-bracket like order.items[0].price\n // (the trailing bracket is handled by the recursive re-parse of referenceAttribute)\n referenceAttribute = attribute.substring(firstDot + 1);\n }\n }\n\n const entry: QueryAttributeEntry = {\n attribute,\n isDeepQuery,\n };\n\n if (reference) entry.reference = reference;\n if (typeof referenceAttribute !== 'undefined' && referenceAttribute !== '') {\n entry.referenceAttribute = referenceAttribute;\n }\n\n // Cache the result\n this.parseCache.set(attribute, entry);\n\n return entry;\n }\n\n /**\n * Detect circular references in a path\n *\n * Checks for:\n * - Direct circular: A→B→A\n * - Indirect circular: A→B→C→A\n * - Self-referential: A→A\n *\n * @param path - Array of model/reference keys in the dependency chain\n * @returns true if circular reference detected\n *\n * @example\n * ```typescript\n * const service = new DeepQueryService();\n * const hasCircular = service.detectCircular(['Order', 'Customer', 'Order']);\n * // Returns: true\n * ```\n */\n detectCircular(path: string[]): boolean {\n if (path.length === 0) return false;\n\n // Simple check: if any element appears twice, it's circular\n const uniqueElements = new Set(path);\n if (uniqueElements.size !== path.length) {\n return true;\n }\n\n // More complex check using dependency graph\n return this.circularRefDetector.hasCycle(path);\n }\n\n /**\n * Validate attribute path syntax\n *\n * Checks for:\n * - Empty strings\n * - Malformed bracket notation\n * - Invalid characters\n * - Excessive nesting depth\n *\n * @param attribute - The attribute path to validate\n * @param maxDepth - Maximum allowed nesting depth (default: 10)\n * @returns ValidationResult with errors if invalid\n *\n * @example\n * ```typescript\n * const service = new DeepQueryService();\n * const result = service.validatePath('customer.name');\n * // Returns: { valid: true }\n *\n * const badResult = service.validatePath('customer.[invalid]');\n * // Returns: { valid: false, errors: ['Invalid bracket notation: customer.[invalid]'] }\n * ```\n */\n validatePath(attribute: string, maxDepth: number = 10): ValidationResult {\n const errors: string[] = [];\n\n // Check for empty string\n if (!attribute || attribute.trim().length === 0) {\n errors.push('Attribute path cannot be empty');\n return { valid: false, errors };\n }\n\n // Check for leading/trailing dots\n if (attribute.startsWith('.') || attribute.endsWith('.')) {\n errors.push(`Invalid path: Leading or trailing dots are not allowed: ${attribute}`);\n }\n\n // Check for consecutive dots\n if (attribute.includes('..')) {\n errors.push(`Invalid path: Consecutive dots are not allowed: ${attribute}`);\n }\n\n // Check bracket notation\n const openBrackets = (attribute.match(/\\[/g) || []).length;\n const closeBrackets = (attribute.match(/]/g) || []).length;\n if (openBrackets !== closeBrackets) {\n errors.push(`Invalid bracket notation: Mismatched brackets in ${attribute}`);\n }\n\n // Check for empty brackets\n if (attribute.includes('[]')) {\n errors.push(`Invalid bracket notation: Empty brackets not allowed: ${attribute}`);\n }\n\n // Check bracket content (should be number or *)\n const bracketPattern = /\\[([^\\]]+)\\]/g;\n let match;\n while ((match = bracketPattern.exec(attribute)) !== null) {\n const content = match[1];\n if (content && content !== '*' && !/^\\d+$/.test(content)) {\n errors.push(`Invalid bracket content: Must be a number or '*', got '${content}' in ${attribute}`);\n }\n }\n\n // Check nesting depth\n const depth = (attribute.match(/\\./g) || []).length + 1;\n if (depth > maxDepth) {\n errors.push(`Path too deep: Maximum depth is ${maxDepth}, got ${depth} in ${attribute}`);\n }\n\n // Check for invalid characters (allow alphanumeric, dots, brackets, underscores, hyphens)\n const invalidChars = attribute.match(/[^a-zA-Z0-9._[\\]*-]/g);\n if (invalidChars) {\n errors.push(`Invalid characters in path: ${invalidChars.join(', ')} in ${attribute}`);\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n };\n }\n\n /**\n * Clear the parse cache\n * Should be called when models are updated or removed\n */\n clearCache(): void {\n this.parseCache.clear();\n this.cacheHits = 0;\n this.cacheMisses = 0;\n }\n\n /**\n * Clear the circular reference detector\n */\n clearCircularDetector(): void {\n this.circularRefDetector.clear();\n }\n\n /**\n * Get cache statistics for monitoring and debugging\n *\n * @returns Object containing cache size, hit rate, and other metrics\n */\n getCacheStats(): {\n size: number;\n maxSize: number;\n hits: number;\n misses: number;\n hitRate: number;\n } {\n const stats = this.parseCache.getStats();\n const total = this.cacheHits + this.cacheMisses;\n const hitRate = total > 0 ? this.cacheHits / total : 0;\n\n return {\n ...stats,\n hits: this.cacheHits,\n misses: this.cacheMisses,\n hitRate,\n };\n }\n\n /**\n * Extract path segments from an attribute string\n * Useful for analyzing deep query structure\n *\n * @param attribute - The attribute path\n * @returns Array of path segments\n *\n * @example\n * ```typescript\n * const service = new DeepQueryService();\n * const segments = service.getPathSegments('order.customer.name');\n * // Returns: ['order', 'customer', 'name']\n * ```\n */\n getPathSegments(attribute: string): string[] {\n // Remove array indices/wildcards\n const withoutBrackets = attribute.replace(/\\[[^\\]]+\\]/g, '');\n return withoutBrackets.split('.').filter((s) => s.length > 0);\n }\n\n /**\n * Get the depth of a deep query path\n *\n * @param attribute - The attribute path\n * @returns Depth of the path (1 for shallow, 2+ for deep)\n */\n getPathDepth(attribute: string): number {\n return this.getPathSegments(attribute).length;\n }\n}\n\n/**\n * Singleton instance for global use\n */\nexport const deepQueryService = new DeepQueryService();\n\n/**\n * Extract all values from an object using a path that may contain [*] wildcards.\n *\n * This function handles patterns like:\n * - `articleIds[*].vendorId.location` - expands [*] to iterate over array elements\n * - `a[*].b[*].c` - supports nested wildcards (cross-product)\n * - `simple.path` - falls back to lodash get for non-wildcard paths\n *\n * @param item - The object to extract values from\n * @param path - The path string, possibly containing [*] wildcards\n * @returns Array of all values found at the path\n *\n * @example\n * ```typescript\n * const order = {\n * articleIds: [\n * { vendorId: { location: 'Berlin' } },\n * { vendorId: { location: 'Vienna' } }\n * ]\n * };\n * getDeepPathValues(order, 'articleIds[*].vendorId.location');\n * // Returns: ['Berlin', 'Vienna']\n * ```\n */\nexport function getDeepPathValues(item: any, path: string): any[] {\n if (path.includes('[*]')) {\n const starIdx = path.indexOf('[*]');\n const beforeStar = path.substring(0, starIdx);\n const afterStar = path.substring(starIdx + 3); // skip '[*]'\n\n const arr = beforeStar ? get(item, beforeStar) : item;\n if (!Array.isArray(arr)) return [];\n\n const results: any[] = [];\n for (const el of arr) {\n if (!afterStar || afterStar === '') {\n if (el !== undefined) results.push(el);\n } else if (afterStar.startsWith('.')) {\n results.push(...getDeepPathValues(el, afterStar.slice(1)));\n }\n }\n return results;\n }\n\n // Plain dot path — traverse segment by segment and auto-unwind arrays\n // encountered at intermediate segments (multi-valued reference paths).\n const segments = path.split('.');\n let current: any[] = [item];\n for (const segment of segments) {\n const next: any[] = [];\n for (const node of current) {\n if (node === undefined || node === null) continue;\n if (Array.isArray(node)) {\n for (const el of node) {\n if (el === undefined || el === null) continue;\n const v = el[segment];\n if (v !== undefined) next.push(v);\n }\n } else {\n const v = node[segment];\n if (v !== undefined) next.push(v);\n }\n }\n current = next;\n if (current.length === 0) return [];\n }\n\n // Final flatten: if a leaf resolution yielded arrays (e.g. a multi-valued\n // ref at the end), expand one level so aggregations see scalars.\n const flat: any[] = [];\n for (const v of current) {\n if (Array.isArray(v)) {\n for (const el of v) if (el !== undefined) flat.push(el);\n } else if (v !== undefined) {\n flat.push(v);\n }\n }\n return flat;\n}\n","import {\n Aggregation,\n AggregationDistinctValue,\n AggregationGroupValue,\n AggregationOptions,\n InverseIndexValuesMap,\n ListItem,\n NumberAggregationValue,\n} from './types';\nimport { deepQueryService, getDeepPathValues } from './deep-query.service';\n\nfunction getScalingFactor(numbers: number[]): number {\n let maxDecimalPlaces = 0;\n for (let num of numbers) {\n const decimalPart = num.toString().split('.')[1];\n if (decimalPart) {\n maxDecimalPlaces = Math.max(maxDecimalPlaces, decimalPart.length);\n }\n }\n return Math.pow(10, maxDecimalPlaces);\n}\n\nfunction add(a: number, b: number, factor: number): number {\n return (a * factor + b * factor) / factor;\n}\n\nfunction divide(a: number, b: number, factor: number): number {\n return (a * factor) / (b * factor);\n}\n\nexport const getAggregation = (\n options: AggregationOptions,\n relevantData: Map<string, ListItem>,\n indexMap: InverseIndexValuesMap,\n attribute: string,\n): Aggregation => {\n if (!attribute) {\n throw new Error('Attribute not found');\n }\n\n const aggregation: Aggregation = {\n attribute: attribute,\n types: options.types,\n };\n\n let numberAgg: NumberAggregationValue | undefined;\n if (aggregation.types.includes('sum') || aggregation.types.includes('avg')) {\n numberAgg = getNumberAggregation(attribute, relevantData);\n }\n\n let minMax: any[] | undefined;\n\n if (aggregation.types.includes('min') || aggregation.types.includes('max')) {\n minMax = getMinMax(attribute, relevantData);\n }\n\n options.types.forEach((type) => {\n if (type === 'sum') {\n aggregation.sum = numberAgg?.sum;\n } else if (type === 'avg') {\n aggregation.avg = numberAgg?.average;\n } else if (type === 'count') {\n aggregation.count = getCount(attribute, relevantData);\n } else if (type === 'min') {\n aggregation.min = minMax && (minMax[0] as any);\n } else if (type === 'max') {\n aggregation.max = minMax && (minMax[1] as any);\n } else if (type === 'distinct') {\n aggregation.distinct = getDistinct(options, relevantData!, indexMap!);\n } else if (type === 'group') {\n aggregation.group = getGroups(options, relevantData!);\n }\n });\n return aggregation;\n};\n\nconst getCount = (attribute: string, data: Map<string, ListItem>): number => {\n const attributeEntry = deepQueryService.parse(attribute);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n let count = 0;\n for (const item of data.values()) {\n if (!item) continue;\n\n if (isDeepQuery) {\n count += getDeepPathValues(item, attribute).filter((v) => v !== undefined && v !== null).length;\n } else {\n const raw = item[attribute];\n if (raw === undefined || raw === null) continue;\n if (Array.isArray(raw)) {\n count += raw.filter((v) => v !== undefined && v !== null).length;\n } else {\n count++;\n }\n }\n }\n return count;\n};\n\nconst getMinMax = (attribute: string, data: Map<string, ListItem>): any[] | undefined => {\n let minV: any = undefined;\n let maxV: any = undefined;\n\n const attributeEntry = deepQueryService.parse(attribute);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n for (const item of data.values()) {\n if (!item) continue;\n\n let values: any[];\n if (isDeepQuery) {\n values = getDeepPathValues(item, attribute);\n } else {\n const raw = item[attribute];\n if (raw === undefined) {\n values = [];\n } else if (Array.isArray(raw)) {\n values = raw.filter((v) => v !== undefined);\n } else {\n values = [raw];\n }\n }\n\n for (const value of values) {\n // min/max only make sense for primitives — skip resolved reference objects\n if (value === undefined || value === null || typeof value === 'object') continue;\n if (minV === undefined || value < minV) minV = value;\n if (maxV === undefined || value > maxV) maxV = value;\n }\n }\n\n if (minV === undefined || maxV === undefined) {\n return undefined;\n }\n\n return [minV, maxV];\n};\n\nconst getGroups = (options: AggregationOptions, data: Map<string, ListItem>): AggregationGroupValue[] => {\n const values: AggregationGroupValue[] = [];\n\n options.groupOptions?.groups?.forEach((group) => {\n values.push({\n option: group,\n count: 0,\n keys: [],\n });\n });\n\n const notFound: AggregationGroupValue = {\n option: { value: ['$dcupl_not_found'] },\n count: 0,\n keys: [],\n };\n\n // Check if this is a deep query attribute\n const attributeEntry = deepQueryService.parse(options.attribute);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n for (const item of data.values()) {\n let relevantEntries: any[];\n if (isDeepQuery) {\n relevantEntries = getDeepPathValues(item, options.attribute);\n } else {\n relevantEntries = [item[options.attribute]];\n }\n\n let itemMatched = false;\n for (const relevantEntry of relevantEntries) {\n for (const group of values) {\n if (\n (typeof group.option.value !== 'undefined' && group.option.value.includes(relevantEntry)) ||\n (typeof group.option.from !== 'undefined' &&\n group.option.from < relevantEntry &&\n typeof group.option.to !== 'undefined' &&\n group.option.to > relevantEntry)\n ) {\n // Only count each item once per group\n if (!group.keys.includes(item.key)) {\n group.count++;\n group.keys.push(item.key);\n }\n itemMatched = true;\n }\n }\n }\n if (!itemMatched && options?.groupOptions?.calculateNotFound) {\n notFound.count++;\n notFound.keys.push(item.key);\n }\n }\n\n if (options?.groupOptions?.calculateNotFound) {\n values.push(notFound);\n }\n\n return values;\n};\n\nconst getNumberAggregation = (attribute: string, data: Map<string, ListItem>): NumberAggregationValue | undefined => {\n let sum = 0;\n let count = 0;\n\n const attributeEntry = deepQueryService.parse(attribute);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n for (const item of data.values()) {\n if (!item) continue;\n\n let values: any[];\n if (isDeepQuery) {\n values = getDeepPathValues(item, attribute);\n } else {\n const raw = item[attribute];\n if (raw === undefined) {\n values = [];\n } else if (Array.isArray(raw)) {\n values = raw;\n } else {\n values = [raw];\n }\n }\n\n for (const value of values) {\n if (Array.isArray(value)) {\n for (const val of value) {\n if (typeof val === 'number') {\n sum = add(sum, val, getScalingFactor([sum, val]));\n count++;\n }\n }\n } else if (typeof value === 'number') {\n sum = add(sum, value, getScalingFactor([sum, value]));\n count++;\n }\n }\n }\n\n if (count === 0) {\n return undefined;\n }\n\n const average = divide(sum, count, getScalingFactor([sum, count]));\n\n return {\n average,\n count,\n sum,\n };\n};\n\nexport const getDistinct = (\n options: AggregationOptions,\n data: Map<string, ListItem>,\n indexMap: InverseIndexValuesMap,\n): AggregationDistinctValue[] => {\n const values: AggregationDistinctValue[] = [];\n\n for (const [attributeValue, itemKeys] of indexMap) {\n if (options.excludeUndefineds && typeof attributeValue === 'undefined') {\n continue;\n }\n\n if (Array.from(itemKeys).find((key) => data.has(key))) {\n const value: AggregationDistinctValue = {\n value: attributeValue,\n count: itemKeys.size,\n };\n if (!options.distinctOptions?.skipKeys) {\n value.keys = Array.from(itemKeys);\n }\n\n values.push(value);\n\n if (options.distinctOptions?.limit && values.length >= options.distinctOptions.limit) {\n break;\n }\n }\n }\n\n return values;\n};\n","import { cloneDeep, every, findIndex } from 'lodash-es';\nimport { DcuplGlobalQueryOptions, DcuplItemsOptions, DcuplQuery, DcuplQueryGroup, SortingProjection } from './types';\nimport { deepQueryService, DeepQueryService } from './deep-query.service';\n\nexport type QueryAttributeEntry = {\n attribute: string;\n reference?: string;\n referenceAttribute?: string;\n // referenceModel?: string;\n isDeepQuery?: boolean;\n};\n\nclass DcuplQueryError extends Error {\n public query: DcuplQuery | DcuplQueryGroup;\n\n constructor(message: string, query: DcuplQuery | DcuplQueryGroup) {\n const msg = `Query Error: ${message} in ${JSON.stringify(query, null, 2)}`;\n super(msg);\n this.query = query;\n this.name = 'DcuplQueryError';\n }\n}\n\nexport class DcuplQueryBuilder {\n private _defaultQueryOptions: DcuplGlobalQueryOptions<any> = {\n modelKey: '',\n groupKey: 'root',\n groupType: 'and',\n queries: [],\n };\n private _initOptions!: any;\n private currentQuery: DcuplGlobalQueryOptions<any> = cloneDeep(this._defaultQueryOptions);\n private deepQueryService: DeepQueryService;\n\n constructor(deepQueryServiceInstance?: DeepQueryService) {\n this.deepQueryService = deepQueryServiceInstance || deepQueryService;\n }\n\n /**\n * Initialize the query builder.\n * Accepts either minimal options with just modelKey, or a complete query object.\n *\n * @param options - Minimal { modelKey } or full DcuplGlobalQueryOptions\n *\n * @example\n * // Minimal initialization\n * builder.init({ modelKey: 'Product' });\n *\n * @example\n * // Full query initialization\n * builder.init({\n * modelKey: 'Product',\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ],\n * count: 10,\n * sort: { price: 'asc' }\n * });\n */\n public init(options: { modelKey: string } | DcuplGlobalQueryOptions<any>): void {\n this._initOptions = cloneDeep(options);\n this.currentQuery = cloneDeep(this._defaultQueryOptions);\n this.currentQuery.modelKey = options.modelKey;\n\n // Auto-detect full query initialization\n if ('queries' in options && Array.isArray(options.queries)) {\n if (options.groupType) this.currentQuery.groupType = options.groupType;\n if (options.queries) this.currentQuery.queries = cloneDeep(options.queries);\n if (options.count !== undefined) this.currentQuery.count = options.count;\n if (options.start !== undefined) this.currentQuery.start = options.start;\n if (options.sort) this.currentQuery.sort = options.sort;\n if (options.projection) this.currentQuery.projection = options.projection;\n }\n }\n\n public reset(): DcuplGlobalQueryOptions<any> {\n this.init(this._initOptions);\n return this.getQuery();\n }\n\n public isQuery(query: Partial<DcuplQuery> | Partial<DcuplQueryGroup>): query is DcuplQuery {\n return 'attribute' in query;\n }\n public isQueryGroup(query: Partial<DcuplQuery> | Partial<DcuplQueryGroup>): query is DcuplQueryGroup {\n // A group is identified by its `queries` array (always present on groups,\n // absent on leaf queries). `groupKey` is optional, so relying on it alone\n // missed keyless nested groups, which then fell through as invalid queries.\n return 'queries' in query || 'groupKey' in query;\n }\n\n public isRootQueryGroup(\n groupOrQuery: Partial<DcuplGlobalQueryOptions<any>> | Partial<DcuplQuery> | Partial<DcuplQueryGroup>,\n ): groupOrQuery is DcuplGlobalQueryOptions<any> {\n return this.isQueryGroup(groupOrQuery) && groupOrQuery.groupKey === 'root';\n }\n\n public getQueryGroup(groupKey?: string): DcuplQueryGroup | undefined {\n return this.currentQuery.queries.find((q) => this.isQueryGroup(q) && q.groupKey === groupKey) as\n | DcuplQueryGroup\n | undefined;\n }\n\n public getQuery(): DcuplGlobalQueryOptions<any> {\n return cloneDeep(this.currentQuery);\n }\n\n public hasQueryGroup(groupKey: string): boolean {\n return this.currentQuery.queries.some((query) => this.isQueryGroup(query) && query.groupKey === groupKey);\n }\n\n public has(options: { groupKey: string; queryKey?: string }): boolean {\n if (options.groupKey && options.queryKey) {\n const group = this.getQueryGroup(options.groupKey);\n\n if (group) {\n return group.queries.some((q) => this.isQuery(q) && q.queryKey === options.queryKey);\n }\n } else if (options.groupKey) {\n return this.hasQueryGroup(options.groupKey);\n }\n return false;\n }\n\n public validate(query: DcuplGlobalQueryOptions<any>): { valid: boolean; errors?: Error[] } {\n const errors = query.queries.map((q) => this.validateQuery(q));\n const valid = every(errors, (e) => e.valid);\n return { valid, errors: errors.map((e) => e.errors).flat() as Error[] };\n }\n\n public applyOptions(\n options: DcuplItemsOptions<any>,\n mode: 'set' | 'update' = 'update',\n ): DcuplGlobalQueryOptions<any> {\n if (mode === 'set') {\n this.currentQuery.count = options.count;\n this.currentQuery.start = options.start;\n this.currentQuery.sort = options.sort;\n this.currentQuery.projection = options.projection;\n } else {\n if (typeof options?.sort !== 'undefined') {\n this.currentQuery!.sort = options.sort as SortingProjection<any>;\n }\n if (typeof options?.count !== 'undefined') {\n this.currentQuery.count = options.count;\n }\n if (typeof options?.start !== 'undefined') {\n this.currentQuery.start = options?.start;\n }\n if (typeof options?.projection !== 'undefined') {\n this.currentQuery.projection = options?.projection;\n }\n }\n\n return this.getQuery();\n }\n\n /**\n * Apply query conditions, groups, or complete queries.\n * This method has implicit behavior based on input type.\n *\n * @deprecated Use explicit methods instead for clearer intent:\n * - {@link addCondition} - Add single condition\n * - {@link addConditions} - Add multiple conditions\n * - {@link addGroup} - Add query group\n * - {@link setCondition} - Replace condition\n * - {@link setConditions} - Replace conditions\n * - {@link setGroup} - Replace query group\n * - {@link mergeQuery} - Merge complete query\n * - {@link replaceQuery} - Replace complete query\n *\n * @param queryToApply - Query, condition, or array to apply\n * @param options - Mode: 'add' (default) or 'set' (replace)\n * @param relevantQuery - Target query (defaults to current)\n * @returns Updated query\n */\n public applyQuery(\n queryToApply: DcuplGlobalQueryOptions<any> | DcuplQueryGroup | DcuplQuery | DcuplQuery[] | DcuplQueryGroup[],\n options?: { mode?: 'set' | 'add' },\n relevantQuery?: DcuplGlobalQueryOptions<any>,\n ): DcuplGlobalQueryOptions<any> {\n if (!relevantQuery) relevantQuery = this.currentQuery;\n\n if (Array.isArray(queryToApply)) {\n for (const query of queryToApply) {\n this.applyQuery(query, options, relevantQuery);\n }\n } else if (this.isRootQueryGroup(queryToApply)) {\n if (queryToApply.groupType) relevantQuery.groupType = queryToApply.groupType;\n // if (queryToApply.options) relevantQuery.options = queryToApply.options;\n\n if (options?.mode === 'set') {\n relevantQuery.queries = queryToApply.queries;\n } else {\n relevantQuery.queries = [...relevantQuery.queries, ...queryToApply.queries];\n }\n\n this.applyOptions(queryToApply, 'set');\n } else if (this.isQuery(queryToApply)) {\n const idx = relevantQuery.queries.findIndex(\n (query) => this.isQueryGroup(query) && query.groupKey === queryToApply.attribute,\n );\n\n if (idx > -1) {\n const queryGroup: DcuplQueryGroup = relevantQuery.queries[idx] as DcuplQueryGroup;\n\n if (options?.mode === 'set') {\n queryGroup.queries = [queryToApply];\n } else {\n queryGroup.queries.push(queryToApply);\n }\n } else {\n relevantQuery.queries.push({\n groupKey: queryToApply.attribute,\n groupType: 'or',\n queries: [queryToApply],\n });\n }\n // handle query group\n } else if (this.isQueryGroup(queryToApply)) {\n const idx = relevantQuery.queries.findIndex(\n (query) => this.isQueryGroup(query) && query.groupKey === queryToApply.groupKey,\n );\n if (idx > -1) {\n const queryGroup: DcuplQueryGroup = relevantQuery.queries[idx] as DcuplQueryGroup;\n if (queryToApply.groupType) queryGroup.groupType = queryToApply.groupType;\n // if (queryToApply.options) queryGroup.options = queryToApply.options;\n if (options?.mode === 'set') {\n relevantQuery.queries[idx] = queryToApply;\n } else {\n queryGroup.queries = [...queryGroup.queries, ...queryToApply.queries];\n }\n } else {\n relevantQuery.queries.push({\n groupKey: queryToApply.groupKey,\n groupType: queryToApply.groupType || 'or',\n queries: queryToApply.queries,\n // options: queryToApply.options,\n });\n }\n }\n\n return relevantQuery;\n }\n\n /**\n * Remove all query conditions.\n *\n * @deprecated Use {@link clearConditions} for clearer intent.\n * @returns Query with no conditions\n */\n public removeAllQueries(): DcuplGlobalQueryOptions<any> {\n this.currentQuery.queries = [];\n return this.getQuery();\n }\n\n /**\n * Remove a specific query or query group.\n *\n * @param queryToRemove - Query or group to remove (partial match)\n * @param relevantQuery - Target query (defaults to current)\n * @returns Updated query\n */\n public removeQuery(\n queryToRemove: Partial<DcuplQueryGroup | DcuplQuery>,\n relevantQuery?: DcuplGlobalQueryOptions<any>,\n ): DcuplGlobalQueryOptions<any> {\n // handle default query\n\n if (!relevantQuery) relevantQuery = this.currentQuery;\n\n if (this.isQuery(queryToRemove)) {\n const idx = relevantQuery.queries.findIndex(\n (query) => this.isQueryGroup(query) && query.groupKey === queryToRemove.attribute,\n );\n\n if (idx > -1) {\n const queryGroup: DcuplQueryGroup = relevantQuery.queries[idx] as DcuplQueryGroup;\n\n const idxOfQueryToRemove = findIndex(queryGroup.queries, queryToRemove);\n if (idxOfQueryToRemove > -1) {\n queryGroup.queries.splice(idxOfQueryToRemove, 1);\n }\n\n if (queryGroup.queries.length === 0) {\n relevantQuery.queries.splice(idx, 1);\n }\n }\n } else if (this.isQueryGroup(queryToRemove)) {\n const idx = relevantQuery.queries.findIndex(\n (query) => this.isQueryGroup(query) && query.groupKey === queryToRemove.groupKey,\n );\n if (idx > -1) {\n if (!queryToRemove.queries) {\n relevantQuery.queries.splice(idx, 1);\n } else {\n const queryGroup: DcuplQueryGroup = relevantQuery.queries[idx] as DcuplQueryGroup;\n for (const q of queryToRemove.queries) {\n const idxOfQueryToRemove = findIndex(queryGroup.queries, q);\n if (idxOfQueryToRemove > -1) {\n queryGroup.queries.splice(idxOfQueryToRemove, 1);\n }\n }\n if (queryGroup.queries.length === 0) {\n relevantQuery.queries.splice(idx, 1);\n }\n }\n }\n }\n\n return relevantQuery;\n }\n\n private validateQuery(query: DcuplQuery | DcuplQueryGroup): { valid: boolean; errors?: DcuplQueryError[] } {\n const errors: DcuplQueryError[] = [];\n if (this.isQuery(query)) {\n if (!query.attribute) {\n errors.push(new DcuplQueryError('Missing attribute', query));\n }\n\n if (!query.operator) {\n errors.push(new DcuplQueryError('Missing operator', query));\n }\n\n if (typeof query.value === 'undefined') {\n errors.push(new DcuplQueryError('Missing value', query));\n }\n\n if (query.operator === 'find' && Array.isArray(query.value)) {\n errors.push(new DcuplQueryError('find operator cannot have array value', query));\n }\n\n if (errors.length > 0) {\n return { valid: false, errors: errors };\n } else {\n return { valid: true };\n }\n } else if (this.isQueryGroup(query)) {\n const queryErrors = query.queries.map((q) => this.validateQuery(q));\n const invalidQueries = queryErrors.filter((q) => !q.valid);\n const errors = invalidQueries.map((q) => q.errors).flat() as DcuplQueryError[];\n if (errors.length > 0) {\n return { valid: false, errors: errors };\n } else {\n return { valid: true };\n }\n }\n\n errors.push(new DcuplQueryError('Invalid query definition', query));\n return { valid: false, errors: errors };\n }\n\n /**\n * Get all attribute names used in the query.\n *\n * @deprecated Use {@link getAttributeNames} for clearer naming.\n * @param query - Optional query to analyze\n * @returns Array of attribute names\n */\n public getQueryAttributes(query?: DcuplGlobalQueryOptions<any>): string[] {\n const attributes: string[] = [];\n const visited = new Set<string>();\n\n const extractAttributes = (queries: Array<DcuplQuery | DcuplQueryGroup>): void => {\n for (const query of queries) {\n if (this.isQuery(query)) {\n if (query.attribute && !visited.has(query.attribute)) {\n attributes.push(query.attribute);\n visited.add(query.attribute);\n }\n } else if (this.isQueryGroup(query)) {\n extractAttributes(query.queries);\n }\n }\n };\n\n extractAttributes(query?.queries || this.currentQuery.queries);\n return attributes;\n }\n\n /**\n * Extract detailed attribute information from query.\n *\n * @deprecated Use {@link getAttributeDetails} for clearer naming.\n * @param query - Query to analyze\n * @returns Array of QueryAttributeEntry objects\n */\n public extractQueryAttributes(query: DcuplGlobalQueryOptions<any>): QueryAttributeEntry[] {\n const attributes: QueryAttributeEntry[] = [];\n const visited = new Set<string>();\n\n const extractAttributes = (queries: Array<DcuplQuery | DcuplQueryGroup>): void => {\n for (const query of queries) {\n if (this.isQuery(query)) {\n if (query.attribute && !visited.has(query.attribute)) {\n const entry = this.getQueryAttributeForString(query.attribute);\n\n attributes.push(entry);\n visited.add(query.attribute);\n }\n } else if (this.isQueryGroup(query)) {\n extractAttributes(query.queries);\n }\n }\n };\n\n extractAttributes(query?.queries || this.currentQuery.queries);\n return attributes;\n }\n\n /**\n * Parse an attribute string into its components using the DeepQueryService\n * This method now delegates to the centralized DeepQueryService for consistent\n * parsing, caching, and validation across the codebase.\n *\n * @param attribute - The attribute path to parse (e.g., 'customer.name', 'items[0].price')\n * @returns QueryAttributeEntry with isDeepQuery, reference, referenceAttribute\n */\n public getQueryAttributeForString(attribute: string): QueryAttributeEntry {\n // Delegate to DeepQueryService for parsing\n return this.deepQueryService.parse(attribute);\n }\n\n /**\n * Get the DeepQueryService instance used by this query builder\n * Useful for accessing cache statistics and other service methods\n *\n * @returns The DeepQueryService instance\n */\n public getDeepQueryService(): DeepQueryService {\n return this.deepQueryService;\n }\n\n public getProjectionForAttributes(attributes: string[]): Record<string, boolean> {\n const projection: Record<string, boolean> = {};\n attributes.forEach((attribute) => {\n projection[attribute] = true;\n });\n return projection;\n }\n\n public getProjectionForAttributeEntries(\n attributes: QueryAttributeEntry[],\n ): Record<string, boolean | Record<string, boolean>> {\n const projection: Record<string, boolean | Record<string, boolean>> = {\n $: true,\n };\n attributes.forEach((attribute) => {\n if (attribute.isDeepQuery) {\n if (attribute.reference) {\n if (attribute.referenceAttribute && attribute.referenceAttribute.length > 0) {\n if (!projection[attribute.reference]) {\n projection[attribute.reference] = { $: true }; // Add $: true for nested object\n }\n // Recursively handle nested deep queries (e.g., customer.address.city)\n const nestedEntry = this.deepQueryService.parse(attribute.referenceAttribute);\n if (nestedEntry.isDeepQuery && attribute.reference) {\n // Nested deep query - recursively build projection\n const nestedProjection = this.getProjectionForAttributeEntries([nestedEntry]);\n // Merge nested projection (excluding root $: true, but keeping nested ones)\n Object.keys(nestedProjection).forEach((key) => {\n if (key !== '$' && attribute.reference) {\n (projection[attribute.reference] as Record<string, any>)[key] = nestedProjection[key];\n }\n });\n } else if (attribute.reference) {\n // Simple property at this level\n (projection[attribute.reference] as Record<string, boolean>)[attribute.referenceAttribute] = true;\n }\n } else {\n // Array of primitives like someArray[0] – request the whole array\n projection[attribute.reference] = true;\n }\n }\n } else {\n projection[attribute.attribute] = true;\n }\n });\n\n return projection;\n }\n\n // ============================================================================\n // Semantic Method Aliases - Clearer naming for improved developer experience\n // ============================================================================\n\n /**\n * Get all attribute names used in the query.\n * Returns a simple array of unique attribute names.\n *\n * Clearer alternative to {@link getQueryAttributes}.\n *\n * @param query - Optional query to analyze. If not provided, uses current query.\n * @returns Array of attribute names used in the query\n *\n * @example\n * ```typescript\n * builder.addCondition({ operator: 'eq', attribute: 'status', value: 'active' });\n * builder.addCondition({ operator: 'gte', attribute: 'price', value: 100 });\n * const attrs = builder.getAttributeNames(); // ['status', 'price']\n * ```\n */\n public getAttributeNames(query?: DcuplGlobalQueryOptions<any>): string[] {\n return this.getQueryAttributes(query);\n }\n\n /**\n * Get detailed information about attributes used in the query.\n * Returns entries with deep query information (reference, referenceAttribute).\n *\n * Clearer alternative to {@link extractQueryAttributes}.\n *\n * @param query - Query to analyze. Defaults to current query if not provided.\n * @returns Array of QueryAttributeEntry objects with details about each attribute\n *\n * @example\n * ```typescript\n * builder.addCondition({ operator: 'eq', attribute: 'customer.name', value: 'John' });\n * const details = builder.getAttributeDetails();\n * // [{ attribute: 'customer.name', isDeepQuery: true, reference: 'customer', referenceAttribute: 'name' }]\n * ```\n */\n public getAttributeDetails(query?: DcuplGlobalQueryOptions<any>): QueryAttributeEntry[] {\n return this.extractQueryAttributes(query || this.currentQuery);\n }\n\n /**\n * Get attribute names from the current query state.\n * Convenience method that operates on the current query.\n *\n * @returns Array of attribute names in current query\n *\n * @example\n * ```typescript\n * const attrs = builder.getCurrentAttributeNames();\n * ```\n */\n public getCurrentAttributeNames(): string[] {\n return this.getAttributeNames(this.currentQuery);\n }\n\n /**\n * Get detailed attribute information from the current query state.\n * Convenience method that operates on the current query.\n *\n * @returns Array of QueryAttributeEntry objects from current query\n *\n * @example\n * ```typescript\n * const details = builder.getCurrentAttributeDetails();\n * ```\n */\n public getCurrentAttributeDetails(): QueryAttributeEntry[] {\n return this.getAttributeDetails(this.currentQuery);\n }\n\n // ============================================================================\n // Explicit Query Application Methods - Intention-revealing alternatives to applyQuery\n // ============================================================================\n\n /**\n * Add query condition(s) to the current query.\n * Accepts single condition or array of conditions.\n * Each condition is added to its respective attribute group.\n *\n * Clearer alternative to `applyQuery(condition, { mode: 'add' })`.\n *\n * @param condition - Single query condition or array of conditions to add\n * @returns Updated query\n *\n * @example\n * ```typescript\n * // Add single condition\n * builder.addCondition({ operator: 'eq', attribute: 'status', value: 'active' });\n *\n * // Add multiple conditions\n * builder.addCondition([\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]);\n * ```\n */\n public addCondition(condition: DcuplQuery | DcuplQuery[]): DcuplGlobalQueryOptions<any> {\n return this.applyQuery(condition, { mode: 'add' });\n }\n\n /**\n * Add a query group to the current query.\n * If a group with the same groupKey exists, extends it; otherwise creates new group.\n *\n * @param group - Query group to add\n * @returns Updated query\n *\n * @example\n * ```typescript\n * builder.addGroup({\n * groupKey: 'priceRange',\n * groupType: 'and',\n * queries: [\n * { operator: 'gte', attribute: 'price', value: 50 },\n * { operator: 'lte', attribute: 'price', value: 100 }\n * ]\n * });\n * ```\n */\n public addGroup(group: DcuplQueryGroup): DcuplGlobalQueryOptions<any> {\n return this.applyQuery(group, { mode: 'add' });\n }\n\n /**\n * Replace query with new condition(s).\n * Accepts single condition or array of conditions.\n * Each condition replaces all existing conditions for its attribute.\n *\n * Clearer alternative to `applyQuery(condition, { mode: 'set' })`.\n *\n * @param condition - Single query condition or array of conditions to set\n * @returns Updated query\n *\n * @example\n * ```typescript\n * // Replace with single condition\n * builder.setCondition({ operator: 'eq', attribute: 'status', value: 'active' });\n *\n * // Replace with multiple conditions\n * builder.setCondition([\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]);\n * ```\n */\n public setCondition(condition: DcuplQuery | DcuplQuery[]): DcuplGlobalQueryOptions<any> {\n return this.applyQuery(condition, { mode: 'set' });\n }\n\n /**\n * Replace a query group with new definition.\n * If group exists, replaces it; otherwise creates new group.\n *\n * @param group - Query group to set\n * @returns Updated query\n *\n * @example\n * ```typescript\n * builder.setGroup({\n * groupKey: 'priceRange',\n * groupType: 'and',\n * queries: [\n * { operator: 'gte', attribute: 'price', value: 100 },\n * { operator: 'lte', attribute: 'price', value: 200 }\n * ]\n * });\n * ```\n */\n public setGroup(group: DcuplQueryGroup): DcuplGlobalQueryOptions<any> {\n return this.applyQuery(group, { mode: 'set' });\n }\n\n /**\n * Merge a complete query into the current query.\n * Adds all conditions and groups from the provided query.\n *\n * @param query - Complete query to merge\n * @returns Updated query\n *\n * @example\n * ```typescript\n * builder.mergeQuery({\n * modelKey: 'Product',\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]\n * });\n * ```\n */\n public mergeQuery(query: DcuplGlobalQueryOptions<any>): DcuplGlobalQueryOptions<any> {\n if (query.queries && Array.isArray(query.queries)) {\n for (const q of query.queries) {\n this.applyQuery(q, { mode: 'add' });\n }\n }\n return this.currentQuery;\n }\n\n /**\n * Replace the entire current query with a new query.\n * Completely replaces all conditions, groups, and options.\n *\n * @param query - Complete query to replace with\n * @returns Updated query\n *\n * @example\n * ```typescript\n * builder.replaceQuery({\n * modelKey: 'Product',\n * groupType: 'or',\n * queries: [\n * { operator: 'eq', attribute: 'featured', value: true },\n * { operator: 'gte', attribute: 'rating', value: 4.5 }\n * ]\n * });\n * ```\n */\n public replaceQuery(query: DcuplGlobalQueryOptions<any>): DcuplGlobalQueryOptions<any> {\n // Clear current query and set new one\n this.currentQuery.queries = [];\n if (query.groupType) this.currentQuery.groupType = query.groupType;\n if (query.queries && Array.isArray(query.queries)) {\n this.currentQuery.queries = query.queries;\n }\n return this.currentQuery;\n }\n\n // ============================================================================\n // State Management Clarity Methods\n // ============================================================================\n\n /**\n * Clear all query conditions and reset to initial state.\n * Alias for {@link reset} with clearer intent.\n *\n * @returns Empty query\n *\n * @example\n * ```typescript\n * builder.addCondition({ operator: 'eq', attribute: 'status', value: 'active' });\n * builder.clear(); // Removes all conditions\n * ```\n */\n public clear(): DcuplGlobalQueryOptions<any> {\n return this.reset();\n }\n\n /**\n * Remove all query conditions but keep query structure.\n * Alias for {@link removeAllQueries} with clearer intent.\n *\n * @returns Query with no conditions\n *\n * @example\n * ```typescript\n * builder.clearConditions(); // Remove all conditions but keep modelKey, etc.\n * ```\n */\n public clearConditions(): DcuplGlobalQueryOptions<any> {\n return this.removeAllQueries();\n }\n\n /**\n * Get an immutable copy of the current query state.\n * Alias for {@link getQuery} with clearer intent.\n *\n * @returns Deep clone of current query\n *\n * @example\n * ```typescript\n * const snapshot = builder.getCurrentQuery();\n * ```\n */\n public getCurrentQuery(): DcuplGlobalQueryOptions<any> {\n return this.getQuery();\n }\n\n /**\n * Check if the query has any conditions.\n *\n * @returns true if query has no conditions, false otherwise\n *\n * @example\n * ```typescript\n * if (builder.isEmpty()) {\n * console.log('No filters applied');\n * }\n * ```\n */\n public isEmpty(): boolean {\n return this.currentQuery.queries.length === 0;\n }\n\n /**\n * Check if a specific condition exists in the query.\n *\n * @param condition - Condition to check for (partial match)\n * @returns true if condition exists\n *\n * @example\n * ```typescript\n * const hasStatus = builder.hasCondition({ attribute: 'status', value: 'active' });\n * ```\n */\n public hasCondition(condition: Partial<DcuplQuery>): boolean {\n const checkQueries = (queries: Array<DcuplQuery | DcuplQueryGroup>): boolean => {\n for (const query of queries) {\n if (this.isQuery(query)) {\n // Check if all provided properties match\n let matches = true;\n for (const key in condition) {\n if (query[key as keyof DcuplQuery] !== condition[key as keyof DcuplQuery]) {\n matches = false;\n break;\n }\n }\n if (matches) return true;\n } else if (this.isQueryGroup(query)) {\n if (checkQueries(query.queries)) return true;\n }\n }\n return false;\n };\n\n return checkQueries(this.currentQuery.queries);\n }\n\n /**\n * Check if a query group with the given key exists.\n * Alias for {@link hasQueryGroup} with consistent naming.\n *\n * @param groupKey - Group key to check\n * @returns true if group exists\n *\n * @example\n * ```typescript\n * if (builder.hasGroup('priceRange')) {\n * console.log('Price range filter is active');\n * }\n * ```\n */\n public hasGroup(groupKey: string): boolean {\n return this.hasQueryGroup(groupKey);\n }\n\n // ============================================================================\n // Query Inspection Methods\n // ============================================================================\n\n /**\n * Get all unique attribute names used in the query.\n * Convenience alias for {@link getAttributeNames}.\n *\n * @returns Array of attribute names\n *\n * @example\n * ```typescript\n * const attrs = builder.getUsedAttributes(); // ['status', 'price', 'customer.name']\n * ```\n */\n public getUsedAttributes(): string[] {\n return this.getAttributeNames();\n }\n\n /**\n * Get only deep query attributes (e.g., 'customer.name', 'items[0].price').\n * Filters out simple attributes and returns only those with references.\n *\n * @returns Array of deep query attribute details\n *\n * @example\n * ```typescript\n * const deepAttrs = builder.getDeepQueryAttributes();\n * // [{ attribute: 'customer.name', isDeepQuery: true, reference: 'customer', referenceAttribute: 'name' }]\n * ```\n */\n public getDeepQueryAttributes(): QueryAttributeEntry[] {\n const details = this.getAttributeDetails();\n return details.filter((entry) => entry.isDeepQuery);\n }\n\n /**\n * Get all query groups in the current query.\n *\n * @returns Array of query groups\n *\n * @example\n * ```typescript\n * const groups = builder.getGroups();\n * // [{ groupKey: 'priceRange', groupType: 'and', queries: [...] }]\n * ```\n */\n public getGroups(): DcuplQueryGroup[] {\n return this.currentQuery.queries.filter((q) => this.isQueryGroup(q)) as DcuplQueryGroup[];\n }\n\n /**\n * Get top-level conditions (queries not inside groups).\n *\n * @returns Array of top-level query conditions\n *\n * @example\n * ```typescript\n * const topLevel = builder.getTopLevelConditions();\n * ```\n */\n public getTopLevelConditions(): DcuplQuery[] {\n return this.currentQuery.queries.filter((q) => this.isQuery(q)) as DcuplQuery[];\n }\n\n /**\n * Get a specific query group by its key.\n * Alias for {@link getQueryGroup} with consistent naming.\n *\n * @param groupKey - Key of the group to retrieve\n * @returns Query group or undefined if not found\n *\n * @example\n * ```typescript\n * const group = builder.getGroup('priceRange');\n * if (group) {\n * console.log('Price range:', group.queries);\n * }\n * ```\n */\n public getGroup(groupKey?: string): DcuplQueryGroup | undefined {\n return this.getQueryGroup(groupKey);\n }\n\n /**\n * Count total number of conditions in the query (including nested).\n *\n * @returns Total count of query conditions\n *\n * @example\n * ```typescript\n * const count = builder.getConditionCount(); // 5\n * ```\n */\n public getConditionCount(): number {\n const countQueries = (queries: Array<DcuplQuery | DcuplQueryGroup>): number => {\n let count = 0;\n for (const query of queries) {\n if (this.isQuery(query)) {\n count++;\n } else if (this.isQueryGroup(query)) {\n count += countQueries(query.queries);\n }\n }\n return count;\n };\n\n return countQueries(this.currentQuery.queries);\n }\n}\n","import {\n ListItem,\n DcuplFilterOptions,\n InverseIndexReferenceMap,\n DcuplGlobalQueryOptions,\n DcuplQueryGroup,\n DcuplFacet,\n} from './types';\n\nimport { QueryManager } from './queryData';\nimport { DcuplQueryBuilder } from './query-builder';\nimport { cloneDeep } from 'lodash-es';\n\nconst queryBuilder = new DcuplQueryBuilder();\n\n/**\n * Build facet entries for `attribute`.\n *\n * Pipeline order — IMPORTANT, do not shuffle:\n *\n * Fast path (options.sort omitted, default):\n * 1. walk validFacetKeys in insertion order\n * 2. for each key: compute entry (binary size if calculateFacets is false)\n * 3. skip if excludeZeros && size === 0 (does NOT advance count)\n * 4. early-return when filterItemEntries.length === count\n * 5. push and continue\n *\n * Sort path (options.sort set):\n * 1. walk ALL validFacetKeys (ignore count for early-return)\n * 2. for each key: compute entry with FULL size (override calculateFacets: false)\n * 3. push every entry\n * 4. drop zeros if excludeZeros\n * 5. sort by chosen mode (size sorts tie-break on value asc, undefined last)\n * 6. truncate to count\n *\n * The sort path is opt-in because it forfeits both early-return optimizations.\n * Most call sites only need a partial answer and stay on the fast path.\n */\nexport function getFacets<ListType extends ListItem = ListItem>(\n modelData: Map<string, ListType>,\n remoteModelData: Map<string, ListType> | undefined,\n options: DcuplFilterOptions,\n query: DcuplGlobalQueryOptions<any>,\n attribute: string,\n modelIndexMap: InverseIndexReferenceMap,\n queryManager: QueryManager,\n): DcuplFacet[] {\n const queryWithoutCurrentFilter = queryBuilder.removeQuery({ groupKey: attribute }, cloneDeep(query));\n const currentFilteredData = queryManager.queryData(modelData, queryWithoutCurrentFilter, false, modelIndexMap);\n\n const remoteModelDataKeys = remoteModelData ? [...remoteModelData.keys()] : [];\n const validFacetKeys: Set<any> = new Set(remoteModelDataKeys);\n\n // A model with zero items never fills its inverse index for the attribute, so the\n // entry can legitimately be missing (e.g. faceting an empty error-tracking model).\n // Treat it as an empty index instead of crashing.\n const indexMap = modelIndexMap.get(attribute) ?? new Map<any, Set<string>>();\n\n if (!options.excludeUnresolved) {\n for (const key of indexMap.keys()) {\n validFacetKeys.add(key);\n }\n }\n\n if (options.excludeUndefineds) {\n validFacetKeys.delete(undefined);\n } else {\n validFacetKeys.add(undefined);\n }\n\n // Sort path: opt-in, walks all keys, computes full size, sorts, then truncates.\n if (options.sort) {\n const entries: DcuplFacet[] = [];\n\n for (const validFacetKey of validFacetKeys.values()) {\n entries.push(buildFacetEntry(validFacetKey, indexMap, currentFilteredData, remoteModelData, attribute, query, true));\n }\n\n const filtered = options.excludeZeros ? entries.filter((e) => e.size > 0) : entries;\n filtered.sort(getComparator(options.sort));\n\n if (options.count) {\n return filtered.slice(0, options.count);\n }\n return filtered;\n }\n\n // Fast path: original behavior, unchanged. Insertion order, mid-iteration\n // count truncation, honors calculateFacets: false inner break.\n const filterItemEntries: DcuplFacet[] = [];\n\n for (const validFacetKey of validFacetKeys.values()) {\n const facetEntry = buildFacetEntry(\n validFacetKey,\n indexMap,\n currentFilteredData,\n remoteModelData,\n attribute,\n query,\n !!options?.calculateFacets,\n );\n\n if (options?.excludeZeros && facetEntry.size === 0) {\n continue;\n }\n\n if (options?.count && filterItemEntries.length === options?.count) {\n return filterItemEntries;\n }\n\n filterItemEntries.push(facetEntry);\n }\n\n return filterItemEntries;\n}\n\n/**\n * Build one facet entry. When `fullSize` is true, walks every indexed key\n * to compute a real size. When false, breaks after the first match so size\n * is effectively binary (0 or 1) — the existing optimization for callers\n * that only need an enabled/disabled signal.\n */\nfunction buildFacetEntry<ListType extends ListItem>(\n validFacetKey: any,\n indexMap: Map<any, Set<string>>,\n currentFilteredData: Map<string, ListType>,\n remoteModelData: Map<string, ListType> | undefined,\n attribute: string,\n query: DcuplGlobalQueryOptions<any>,\n fullSize: boolean,\n): DcuplFacet {\n const result = new Map<string, ListType>();\n const indexedValueKeys = indexMap.get(validFacetKey) || new Set<string>();\n\n for (const key of indexedValueKeys) {\n const value = currentFilteredData.get(key);\n if (value) {\n result.set(key, value);\n if (!fullSize) {\n break;\n }\n }\n }\n\n return {\n key: String(validFacetKey),\n value: validFacetKey,\n selected: isFilterItemEntrySelected(attribute, validFacetKey, query),\n enabled: result.size > 0,\n size: result.size,\n entry: remoteModelData?.get(validFacetKey) as DcuplFacet['entry'],\n resultKeys: Array.from(result.keys()),\n };\n}\n\n/**\n * Deterministic compare on raw facet values. `undefined` is always last,\n * regardless of direction — keeping \"unset\" out of the way is the common\n * UI need and avoids `undefined` colliding with string/number comparators.\n * Numbers compared numerically, strings lexicographically. Mixed types\n * (not expected for a single attribute) fall back to typeof ordering\n * so the result is still deterministic.\n */\nfunction compareValuesAsc(a: any, b: any): number {\n if (a === undefined && b === undefined) return 0;\n if (a === undefined) return 1;\n if (b === undefined) return -1;\n\n const ta = typeof a;\n const tb = typeof b;\n if (ta !== tb) return ta < tb ? -1 : 1;\n\n if (ta === 'number') return (a as number) - (b as number);\n if (ta === 'string') return (a as string).localeCompare(b as string);\n\n // Fallback for objects/booleans/etc — stringify for a stable order.\n const sa = String(a);\n const sb = String(b);\n return sa < sb ? -1 : sa > sb ? 1 : 0;\n}\n\nfunction getComparator(sort: NonNullable<DcuplFilterOptions['sort']>): (a: DcuplFacet, b: DcuplFacet) => number {\n switch (sort) {\n case 'size-desc':\n return (a, b) => b.size - a.size || compareValuesAsc(a.value, b.value);\n case 'size-asc':\n return (a, b) => a.size - b.size || compareValuesAsc(a.value, b.value);\n case 'value-asc':\n return (a, b) => compareValuesAsc(a.value, b.value);\n case 'value-desc':\n // value-desc: invert the asc result, BUT keep undefined last.\n return (a, b) => {\n if (a.value === undefined && b.value === undefined) return 0;\n if (a.value === undefined) return 1;\n if (b.value === undefined) return -1;\n return -compareValuesAsc(a.value, b.value);\n };\n }\n}\n\nconst isFilterItemEntrySelected = (\n groupKey: string,\n facetKey: string,\n query: DcuplGlobalQueryOptions<any>,\n): boolean => {\n const idx = query.queries.findIndex((query) => queryBuilder.isQueryGroup(query) && query.groupKey === groupKey);\n\n if (idx > -1) {\n const queryGroup = query.queries[idx] as DcuplQueryGroup;\n\n return !!queryGroup.queries.find((query) => {\n if (queryBuilder.isQuery(query)) {\n const key = query.value?.key || query.value;\n return query.attribute === groupKey && key === facetKey;\n }\n return false;\n });\n }\n return false;\n};\n","import { generateObjectHash } from './object-hash';\nimport {\n InverseIndexReferenceMap,\n InverseIndexValuesMap,\n InverseIndexDataMap,\n InverseIndexDataValuesMap,\n ListItem,\n} from './types';\nimport { deepQueryService, getDeepPathValues } from './deep-query.service';\n\n/**\n * Metadata tracked for each index to enable lifecycle management\n */\ninterface IndexMetadata {\n createdAt: number;\n lastAccessedAt: number;\n context: any;\n type: 'shallow' | 'deep';\n}\n\n/**\n * Statistics about indices for monitoring\n */\nexport interface IndexStats {\n shallow: number;\n deep: number;\n total: number;\n oldestAge: number;\n avgAge: number;\n}\n\nexport class IndicesController {\n private indexMap: InverseIndexReferenceMap = new Map();\n // Possible performance gain*\n private dataMap: InverseIndexDataMap = new Map();\n private indexMetadata: Map<string, IndexMetadata> = new Map();\n\n constructor() {}\n\n public reset(attributeKey?: string): void {\n if (attributeKey) {\n this.indexMap.delete(attributeKey);\n this.dataMap.delete(attributeKey);\n this.indexMetadata.delete(attributeKey);\n } else {\n this.indexMap.clear();\n this.dataMap.clear();\n this.indexMetadata.clear();\n }\n }\n\n /**\n * Removes every index the predicate selects. Used to drop indices that\n * were built from now-stale data; the next read lazily rebuilds them via\n * {@link getOrCreateIndex}.\n */\n public resetIndicesWhere(predicate: (indexKey: string) => boolean): void {\n for (const indexKey of Array.from(this.indexMap.keys())) {\n if (predicate(indexKey)) {\n this.reset(indexKey);\n }\n }\n }\n\n public fillDataMap(attributeKey: string, attributeValue: Array<string | undefined | ListItem>): void {\n let valueMap = this.dataMap.get(attributeKey);\n\n if (!valueMap) {\n valueMap = new Map();\n this.dataMap.set(attributeKey, valueMap);\n }\n\n for (const value of attributeValue) {\n let keyAsString!: any;\n if (typeof value === 'object' && value && value.key) {\n keyAsString = value.key;\n } else if (Array.isArray(value)) {\n for (const item of value) {\n this.fillDataMap(attributeKey, [item]);\n }\n return;\n } else if (value instanceof Date) {\n // Convert Date objects to ISO string for consistent indexing\n keyAsString = value.toISOString();\n } else {\n keyAsString = value;\n }\n\n let listItem = valueMap!.get(keyAsString);\n if (!listItem) {\n listItem = { key: keyAsString };\n this.dataMap.get(attributeKey)?.set(keyAsString, listItem);\n }\n }\n }\n\n public fillInverseIndex(\n attributeKey: string,\n originKey: string,\n foreignKeys: Array<string | undefined | ListItem>,\n ): void {\n if (!attributeKey || !originKey || !foreignKeys || !Array.isArray(foreignKeys)) {\n return;\n }\n\n const valueMap = this.indexMap.get(attributeKey) || new Map();\n\n for (const value of foreignKeys) {\n let keyAsString!: any;\n\n if (typeof value === 'object' && value && value.key) {\n keyAsString = value.key;\n } else if (Array.isArray(value)) {\n // keyAsString = JSON.stringify(value);\n for (const item of value) {\n this.fillInverseIndex(attributeKey, originKey, [item]);\n }\n return;\n } else if (value instanceof Date) {\n // Convert Date objects to ISO string for consistent indexing\n keyAsString = value.toISOString();\n } else {\n keyAsString = value;\n }\n\n // Get existing Set or create new one\n let valueSet = valueMap.get(keyAsString);\n if (!valueSet) {\n valueSet = new Set<string>();\n valueMap.set(keyAsString, valueSet);\n }\n\n // O(1) duplicate check with Set.add() - automatically handles duplicates\n valueSet.add(originKey);\n }\n this.indexMap.set(attributeKey, valueMap);\n }\n\n public getIndex(attributeKey: string, context?: any): InverseIndexValuesMap | undefined {\n const indexName = this.getIndexName(attributeKey, context);\n\n return this.indexMap.get(indexName);\n }\n\n public getData(attributeKey: string, context?: any): InverseIndexDataValuesMap | undefined {\n const indexName = this.getIndexName(attributeKey, context);\n return this.dataMap.get(indexName);\n }\n\n public getOrCreateIndex(\n attributeKey: string,\n relevantData: Map<string, ListItem>,\n context?: any,\n ): InverseIndexValuesMap {\n const indexName = this.getIndexName(attributeKey, context);\n\n let index = this.getIndex(indexName);\n\n // Track or update metadata\n const existing = this.indexMetadata.get(indexName);\n if (existing) {\n // Update last accessed time for existing index\n existing.lastAccessedAt = Date.now();\n } else if (!index) {\n // Only create metadata when creating new index (not for each access)\n const attributeEntry = deepQueryService.parse(attributeKey);\n const isDeepQuery = attributeEntry.isDeepQuery ?? false;\n\n this.indexMetadata.set(indexName, {\n createdAt: Date.now(),\n lastAccessedAt: Date.now(),\n context,\n type: isDeepQuery ? 'deep' : 'shallow',\n });\n }\n\n if (!index) {\n this.reset(indexName);\n\n this.indexMap.set(indexName, new Map());\n\n // Check if this is a deep query attribute\n const attributeEntry = deepQueryService.parse(attributeKey);\n const isDeepQuery = attributeEntry.isDeepQuery;\n\n for (const entry of relevantData.values()) {\n let attributeValues: any[];\n if (isDeepQuery) {\n attributeValues = getDeepPathValues(entry, attributeKey);\n // Preserve undefined facet behavior when path yields no matches\n if (attributeValues.length === 0) {\n attributeValues = [undefined];\n }\n } else {\n attributeValues = [entry[attributeKey]];\n }\n this.fillInverseIndex(indexName, entry.key, attributeValues);\n this.fillDataMap(indexName, attributeValues);\n }\n index = this.getIndex(indexName) || new Map();\n }\n return index!;\n }\n\n public getIndexName(attributeKey: string, context?: any): string {\n return context ? attributeKey + '_' + generateObjectHash(context) : attributeKey;\n }\n\n // ========== PARTIAL UPDATE METHODS ==========\n\n /**\n * Removes a specific item from an inverse index\n * @param attributeKey - The attribute key\n * @param itemKey - The item key to remove\n * @param oldValues - The old attribute values to remove from the index\n */\n public removeFromInverseIndex(\n attributeKey: string,\n itemKey: string,\n oldValues: Array<string | undefined | ListItem>,\n ): void {\n const valueMap = this.indexMap.get(attributeKey);\n if (!valueMap) return;\n\n for (const value of oldValues) {\n let keyAsString!: any;\n\n if (typeof value === 'object' && value && value.key) {\n keyAsString = value.key;\n } else if (Array.isArray(value)) {\n for (const item of value) {\n this.removeFromInverseIndex(attributeKey, itemKey, [item]);\n }\n return;\n } else if (value instanceof Date) {\n // Convert Date objects to ISO string for consistent indexing\n keyAsString = value.toISOString();\n } else {\n keyAsString = value;\n }\n\n const valueSet = valueMap.get(keyAsString);\n if (valueSet) {\n // O(1) deletion with Set\n valueSet.delete(itemKey);\n\n // If Set is now empty, remove the entry\n if (valueSet.size === 0) {\n valueMap.delete(keyAsString);\n }\n }\n }\n }\n\n /**\n * Updates inverse index for a specific item by removing old values and adding new ones\n * @param attributeKey - The attribute key\n * @param itemKey - The item key being updated\n * @param oldValues - The old attribute values to remove\n * @param newValues - The new attribute values to add\n */\n public updateInverseIndex(\n attributeKey: string,\n itemKey: string,\n oldValues: Array<string | undefined | ListItem>,\n newValues: Array<string | undefined | ListItem>,\n ): void {\n // Remove old values\n this.removeFromInverseIndex(attributeKey, itemKey, oldValues);\n\n // Add new values\n this.fillInverseIndex(attributeKey, itemKey, newValues);\n }\n\n /**\n * Removes a specific item from the data map\n * @param attributeKey - The attribute key\n * @param values - The values to remove\n */\n public removeFromDataMap(\n attributeKey: string,\n values: Array<string | undefined | ListItem>,\n ): void {\n const dataMap = this.dataMap.get(attributeKey);\n if (!dataMap) return;\n\n for (const value of values) {\n let keyAsString!: any;\n\n if (typeof value === 'object' && value && value.key) {\n keyAsString = value.key;\n } else if (Array.isArray(value)) {\n for (const item of value) {\n this.removeFromDataMap(attributeKey, [item]);\n }\n return;\n } else if (value instanceof Date) {\n // Convert Date objects to ISO string for consistent indexing\n keyAsString = value.toISOString();\n } else {\n keyAsString = value;\n }\n\n dataMap.delete(keyAsString);\n }\n }\n\n /**\n * Updates data map for a specific item by removing old values and adding new ones\n * @param attributeKey - The attribute key\n * @param oldValues - The old values to remove\n * @param newValues - The new values to add\n */\n public updateDataMap(\n attributeKey: string,\n oldValues: Array<string | undefined | ListItem>,\n newValues: Array<string | undefined | ListItem>,\n ): void {\n // Remove old values\n this.removeFromDataMap(attributeKey, oldValues);\n\n // Add new values\n this.fillDataMap(attributeKey, newValues);\n }\n\n /**\n * Completely removes an item from all indices\n * Used when an item is deleted\n * @param itemKey - The item key to remove\n */\n public removeItemFromAllIndices(itemKey: string): void {\n // Remove from inverse indices\n for (const valueMap of this.indexMap.values()) {\n for (const [value, itemKeys] of valueMap) {\n // O(1) deletion with Set\n if (itemKeys.delete(itemKey)) {\n // If Set is now empty, remove the entry\n if (itemKeys.size === 0) {\n valueMap.delete(value);\n }\n }\n }\n }\n\n // Remove from data maps (item key as value)\n for (const dataMap of this.dataMap.values()) {\n if (dataMap.has(itemKey)) {\n dataMap.delete(itemKey);\n }\n }\n }\n\n /**\n * Resets indices for specific attributes only\n * Useful for partial updates when only certain attributes changed\n * @param attributeKeys - Array of attribute keys to reset\n */\n public resetAttributes(attributeKeys: string[]): void {\n for (const attributeKey of attributeKeys) {\n this.reset(attributeKey);\n }\n }\n\n /**\n * Checks if an index key represents a deep query (contains dots in the attribute path)\n * Uses deep query service for accurate detection\n * @param indexKey - The index key to check\n * @returns true if the index is for a deep query attribute\n */\n private isDeepQueryIndex(indexKey: string): boolean {\n // Remove context hash suffix if present (format: \"attribute_hash\")\n const attributeKey = indexKey.includes('_') ? indexKey.split('_')[0]! : indexKey;\n\n try {\n const entry = deepQueryService.parse(attributeKey);\n return entry.isDeepQuery ?? false;\n } catch {\n // If parsing fails, conservatively assume it's not a deep query\n return false;\n }\n }\n\n /**\n * Gets all deep query index keys currently in the index map\n * @returns Array of index keys that represent deep queries\n */\n public getDeepIndexKeys(): string[] {\n const deepKeys: string[] = [];\n\n for (const indexKey of this.indexMap.keys()) {\n if (this.isDeepQueryIndex(indexKey)) {\n deepKeys.push(indexKey);\n }\n }\n\n return deepKeys;\n }\n\n /**\n * Resets all deep query indices (e.g., \"customer.country\", \"order.customer.address.city\")\n * This is critical for data updates as deep query indices need to be rebuilt\n * when underlying data changes.\n *\n * Shallow indices (direct attributes) are preserved as they're updated incrementally.\n */\n public resetAllDeepIndices(): void {\n const deepKeys = this.getDeepIndexKeys();\n\n for (const indexKey of deepKeys) {\n this.indexMap.delete(indexKey);\n this.dataMap.delete(indexKey);\n }\n }\n\n /**\n * Gets count of indices by type for monitoring and debugging\n * @returns Object with counts of shallow and deep indices\n */\n public getIndexCount(): { shallow: number; deep: number; total: number } {\n let shallow = 0;\n let deep = 0;\n\n for (const indexKey of this.indexMap.keys()) {\n if (this.isDeepQueryIndex(indexKey)) {\n deep++;\n } else {\n shallow++;\n }\n }\n\n return {\n shallow,\n deep,\n total: shallow + deep,\n };\n }\n\n /**\n * Removes stale indices that haven't been accessed recently\n * This helps prevent memory buildup from indices with varying contexts\n * @param maxAge - Maximum age in milliseconds (default: 5 minutes)\n * @returns Number of indices cleaned up\n */\n public cleanupStaleIndices(maxAge: number = 5 * 60 * 1000): number {\n const now = Date.now();\n let cleaned = 0;\n\n for (const [indexKey, metadata] of this.indexMetadata) {\n if (now - metadata.lastAccessedAt > maxAge) {\n this.reset(indexKey);\n cleaned++;\n }\n }\n\n return cleaned;\n }\n\n /**\n * Gets comprehensive statistics about indices for monitoring\n * @returns IndexStats object with counts and age information\n */\n public getStats(): IndexStats {\n const counts = this.getIndexCount();\n const now = Date.now();\n let totalAge = 0;\n let oldestAge = 0;\n\n for (const metadata of this.indexMetadata.values()) {\n const age = now - metadata.createdAt;\n totalAge += age;\n if (age > oldestAge) {\n oldestAge = age;\n }\n }\n\n const avgAge = this.indexMetadata.size > 0 ? totalAge / this.indexMetadata.size : 0;\n\n return {\n shallow: counts.shallow,\n deep: counts.deep,\n total: counts.total,\n oldestAge,\n avgAge,\n };\n }\n}\n","import { getAggregation } from './getAggregation';\nimport { IndicesController } from './indices.controller';\nimport { ListItem, Aggregation, AggregationOptions } from './types';\nimport { deepQueryService } from './deep-query.service';\n\ntype PivotRowOption = {\n attribute: string;\n calculateTotals?: boolean;\n};\n\ntype PivotColumnOption = {\n attribute: string;\n calculateTotals?: boolean;\n};\n\ntype PivotValueOption = AggregationOptions;\n\nexport type PivotOptions = {\n rows: PivotRowOption[];\n columns: PivotColumnOption[];\n values: PivotValueOption[];\n options?: {\n calculateTotals?: boolean;\n };\n};\n\ntype PivotResponseItem = {\n key: string;\n value?: any;\n values?: Aggregation[];\n columns?: Omit<PivotResponseItem, 'rows'>[];\n rows?: PivotResponseItem[];\n};\n\nexport type PivotResponse = PivotResponseItem;\n\nconst pivotPathSeparator = '__';\n\nexport function pivot<ListType extends ListItem = ListItem>(\n relevantData: Map<string, ListType>,\n options: PivotOptions,\n response: PivotResponse = { key: 'root' },\n parentPath = 'root',\n indicesCtrl = new IndicesController(),\n projectedData?: Map<string, ListType>,\n): PivotResponse {\n // Use projected data for indexing if provided, otherwise use relevantData\n const dataForIndexing = projectedData || relevantData;\n\n // Parse and create indices for column attributes (supports deep queries)\n for (const column of options.columns) {\n if (column.attribute) {\n // Parse to ensure deep query syntax is valid\n deepQueryService.parse(column.attribute);\n indicesCtrl.getOrCreateIndex(column.attribute, dataForIndexing);\n }\n }\n\n // Parse and create indices for row attributes (supports deep queries)\n for (const row of options.rows) {\n if (row.attribute) {\n // Parse to ensure deep query syntax is valid\n deepQueryService.parse(row.attribute);\n indicesCtrl.getOrCreateIndex(row.attribute, dataForIndexing);\n }\n }\n\n // Parse and create indices for value attributes (supports deep queries)\n for (const value of options.values) {\n if (value.attribute) {\n // Parse to ensure deep query syntax is valid\n deepQueryService.parse(value.attribute);\n indicesCtrl.getOrCreateIndex(value.attribute, dataForIndexing);\n }\n }\n\n /**\n * Rows\n */\n if (options.rows && options.rows.length > 0) {\n response.rows = response.rows || [];\n\n const item = getPivotRowItem(relevantData, options.rows[0]!, options, indicesCtrl, parentPath, projectedData);\n response.rows.push(item);\n }\n\n /**\n * Columns\n */\n if (options.columns && options.columns.length > 0) {\n response.columns = response.columns || [];\n const item = getPivotColumnItem(relevantData, options.columns[0]!, options, indicesCtrl, parentPath, projectedData);\n response.columns.push(item);\n }\n\n /**\n * Value Aggregations\n */\n if (response.key === 'root') {\n for (const value of options.values) {\n response.values = response.values || [];\n\n if (parentPath === 'root' && options.options?.calculateTotals === false) {\n continue;\n }\n\n const agg = getAggregation(value, relevantData, indicesCtrl.getIndex(value.attribute)!, value.attribute);\n response.values.push(agg);\n }\n }\n\n return response;\n}\n\nfunction getPivotRowItem(\n modelData: Map<string, ListItem>,\n options: PivotRowOption | PivotColumnOption,\n globalOptions: PivotOptions,\n modelIndexMap: IndicesController,\n parentPath = '',\n projectedData?: Map<string, ListItem>,\n): PivotResponseItem {\n const item: PivotResponseItem = { key: options.attribute };\n\n const indexMap = modelIndexMap.getIndex(options.attribute);\n\n if (!indexMap) {\n return item;\n }\n\n for (const [key, itemKeys] of indexMap.entries()) {\n const rowItem: PivotResponseItem = {\n key: key,\n value: key,\n };\n\n const relevantData = new Map();\n\n for (const key of itemKeys) {\n if (!modelData.has(key)) {\n continue;\n }\n\n relevantData.set(key, modelData.get(key));\n }\n\n if (!relevantData.size) continue;\n\n for (const value of globalOptions.values) {\n rowItem.values = rowItem.values || [];\n\n if (parentPath === 'root' && options?.calculateTotals === false) {\n continue;\n }\n\n const agg = getAggregation(value, relevantData, modelIndexMap.getIndex(value.attribute)!, value.attribute);\n rowItem.values.push(agg);\n }\n\n item.rows = item.rows || [];\n item.rows.push(rowItem);\n\n if (globalOptions.rows.length > 1) {\n const newOptions = { ...globalOptions, rows: globalOptions.rows.slice(1) };\n\n pivot(\n relevantData,\n newOptions,\n rowItem,\n parentPath + pivotPathSeparator + options.attribute + pivotPathSeparator + key,\n modelIndexMap,\n projectedData,\n );\n } else {\n if (globalOptions.columns) {\n for (const columnOption of globalOptions.columns) {\n rowItem.columns = rowItem.columns || [];\n const columnItem = getPivotColumnItem(\n relevantData,\n columnOption,\n globalOptions,\n modelIndexMap,\n parentPath + pivotPathSeparator + options.attribute + pivotPathSeparator + key,\n projectedData,\n );\n rowItem.columns.push(columnItem);\n }\n }\n }\n }\n return item;\n}\n\nfunction getPivotColumnItem(\n modelData: Map<string, ListItem>,\n options: PivotRowOption | PivotColumnOption,\n globalOptions: PivotOptions,\n modelIndexMap: IndicesController,\n parentPath = '',\n projectedData?: Map<string, ListItem>,\n): PivotResponseItem {\n const item: PivotResponseItem = { key: options.attribute };\n\n const indexMap = modelIndexMap.getIndex(options.attribute);\n\n if (!indexMap) {\n return item;\n }\n\n for (const [key, itemKeys] of indexMap.entries()) {\n const rowItem: PivotResponseItem = {\n key: key,\n value: key,\n };\n\n const relevantData = new Map();\n for (const key of itemKeys) {\n if (!modelData.has(key)) {\n continue;\n }\n relevantData.set(key, modelData.get(key));\n }\n\n if (!relevantData.size) continue;\n\n for (const value of globalOptions.values) {\n rowItem.values = rowItem.values || [];\n\n if (parentPath === 'root' && options?.calculateTotals === false) {\n continue;\n }\n\n const agg = getAggregation(value, relevantData, modelIndexMap.getIndex(value.attribute)!, value.attribute);\n\n rowItem.values.push(agg);\n }\n\n item.columns = item.columns || [];\n item.columns.push(rowItem);\n\n if (globalOptions.columns.length > 1) {\n const newOptions = { ...globalOptions, columns: globalOptions.columns.slice(1) };\n pivot(\n relevantData,\n newOptions,\n rowItem,\n parentPath + pivotPathSeparator + options.attribute + pivotPathSeparator + key,\n modelIndexMap,\n projectedData,\n );\n }\n }\n\n return item;\n}\n","import { isDateInstance } from './date-utils';\nimport { Property, PropertyType } from './types';\n\nconst ISO_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2})?)?$/;\nconst INT_RE = /^-?\\d+$/;\nconst FLOAT_RE = /^-?\\d+\\.\\d+$/;\nconst DATE_LIKE_RE = /^\\d{1,2}([/.-])\\d{1,2}\\1\\d{2,4}$/;\n\n// NOTE: leading-zero numerics (\"042\") classify as 'int' — zero-padded codes/ZIPs\n// must be pinned to type 'string' manually in the model.\n// Classifies a raw string value into a dcupl PropertyType.\nexport const coerceStringType = (value: string): PropertyType => {\n if (value === 'true' || value === 'false') return 'boolean';\n if (INT_RE.test(value)) return 'int';\n if (FLOAT_RE.test(value)) return 'float';\n if (ISO_DATE_RE.test(value) && !Number.isNaN(Date.parse(value))) return 'date';\n return 'string';\n};\n\n// True for non-ISO values that visually resemble a date (used only for diagnostics hints).\nexport const looksDateLike = (value: string): boolean => DATE_LIKE_RE.test(value);\n\nexport type GetPropertiesForDataOptions = {\n deep?: boolean;\n sampleSize?: number;\n /** @deprecated No longer honored — empty values are always skipped for type\n * inference and counted toward ColumnDiagnostic.emptyRate. */\n ignoreEmpty?: boolean;\n};\n\nconst isEmptyValue = (value: unknown): boolean => value === null || value === undefined || value === '';\n\nexport type ColumnDiagnostic = {\n column: string;\n inferredType: PropertyType;\n observedTypes: PropertyType[];\n // 0..1 — fraction of the rows in which this column was *present* whose value\n // was empty (null/undefined/''). For ragged data this denominator is the\n // column's occurrence count, not the total row count.\n emptyRate: number;\n conflict: boolean; // true when observed types disagree and collapse to 'any'\n hint?: string;\n};\n\nexport type InferModelOptions = GetPropertiesForDataOptions & {\n // When true (default), string values are classified via coerceStringType.\n coerceStrings?: boolean;\n};\n\nexport type InferModelResult = {\n properties: Property[];\n diagnostics: ColumnDiagnostic[];\n};\n\n// Reconciles an observed-type set into a single PropertyType.\nconst reconcileTypes = (types: Set<PropertyType>): { type: PropertyType; conflict: boolean } => {\n const arr = [...types];\n if (arr.length <= 1) return { type: arr[0] ?? 'any', conflict: false };\n if (arr.every((t) => t === 'int' || t === 'float')) return { type: 'float', conflict: false };\n return { type: 'any', conflict: true };\n};\n\n// Infers typed properties + per-column diagnostics from a list of data rows.\nexport const inferModel = (data: any[], options: InferModelOptions = {}): InferModelResult => {\n // sampleSize > 1 implies deep scanning; sampleSize of 0 or 1 behaves as a single-row scan.\n const deep = options.deep ?? (options.sampleSize !== undefined && options.sampleSize > 1);\n const limit = deep ? options.sampleSize ?? Infinity : 1;\n const coerceStrings = options.coerceStrings ?? true;\n\n type Acc = { observed: Set<PropertyType>; empty: number; scanned: number; hint?: string };\n const columns = new Map<string, Acc>();\n const order: string[] = [];\n\n const rowsToScan = Math.min(data.length, limit);\n for (let i = 0; i < rowsToScan; i++) {\n const item = data[i];\n if (!item) continue;\n\n for (const [key, value] of Object.entries(item)) {\n let acc = columns.get(key);\n if (!acc) {\n acc = { observed: new Set(), empty: 0, scanned: 0 };\n columns.set(key, acc);\n order.push(key);\n }\n acc.scanned++;\n\n if (isEmptyValue(value)) {\n acc.empty++;\n continue;\n }\n\n let type: PropertyType;\n if (typeof value === 'string' && coerceStrings) {\n type = coerceStringType(value);\n if (type === 'string' && !acc.hint && looksDateLike(value)) {\n acc.hint = 'column looks date-like (non-ISO) — review manually';\n }\n } else {\n type = getDcuplPropertyTypeForNativeType(value);\n }\n acc.observed.add(type);\n }\n }\n\n const properties: Property[] = [];\n const diagnostics: ColumnDiagnostic[] = [];\n\n for (const key of order) {\n const acc = columns.get(key)!;\n const { type, conflict } = reconcileTypes(acc.observed.size ? acc.observed : new Set<PropertyType>(['string']));\n properties.push({ key, type } as Property);\n diagnostics.push({\n column: key,\n inferredType: type,\n observedTypes: [...acc.observed].sort(),\n emptyRate: acc.scanned === 0 ? 0 : acc.empty / acc.scanned,\n conflict,\n hint: acc.hint,\n });\n }\n\n return { properties, diagnostics };\n};\n\n// returns a list of properties based on the input data.\n// Second arg accepts a boolean for back-compat (`deep` flag) or an options object.\n// NOTE: now string-aware — string values are classified by coerceStringType.\nexport const getPropertiesForData = (\n data: any[],\n deepOrOptions: boolean | GetPropertiesForDataOptions = false,\n): Property[] => {\n const options: GetPropertiesForDataOptions =\n typeof deepOrOptions === 'boolean' ? { deep: deepOrOptions } : deepOrOptions;\n return inferModel(data, options).properties;\n};\n\nexport const isInt = (n: any): boolean => {\n return Number(n) === n && n % 1 === 0;\n};\n\nexport const isFloat = (n: any): boolean => {\n return Number(n) === n && n % 1 !== 0;\n};\n\nexport const getDcuplPropertyTypeForNativeType = (value: any): PropertyType => {\n const type = typeof value;\n\n switch (type) {\n case 'string':\n return 'string';\n case 'number':\n if (isInt(value)) {\n return 'int';\n } else if (isFloat(value)) {\n return 'float';\n } else {\n return 'any';\n }\n case 'boolean':\n return 'boolean';\n case 'object':\n return isDateInstance(value) ? 'date' : 'json';\n default:\n return 'any';\n }\n};\n","import { escapeRegExp } from './helper';\nimport {\n BooleanFilterItem,\n BooleanFilterOptions,\n DateFilterItem,\n DateFilterOptions,\n DcuplQuery,\n DcuplQueryGroup,\n DcuplQueryStatement,\n NumberFilterItem,\n NumberFilterOptions,\n ReferenceFilterItem,\n ReferenceFilterOptions,\n TextFilterItem,\n TextFilterOptions,\n} from './types';\n\nexport const generateTextQuery = (options: TextFilterOptions, filterItem?: TextFilterItem): DcuplQueryGroup => {\n const escapedText = escapeRegExp(options.text || '');\n\n const attribute = filterItem?.attributeKey || options.filterKey;\n\n let query!: DcuplQueryGroup<DcuplQuery>;\n\n const comparisonType = filterItem?.comparisonType || 'containsIgnoreCase';\n\n if (comparisonType === 'containsIgnoreCase') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: `/${escapedText}/`,\n operator: 'find',\n options: {\n transform: ['lowercase'],\n },\n },\n ],\n };\n } else if (comparisonType === 'containsIgnoreCaseAndWhitespace') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: `/${escapedText}/`,\n operator: 'find',\n options: {\n transform: ['removeWhitespace', 'lowercase'],\n },\n },\n ],\n };\n } else if (comparisonType === 'contains') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: `/${escapedText}/`,\n operator: 'find',\n },\n ],\n };\n } else if (comparisonType === 'equal') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: options.text,\n operator: 'eq',\n },\n ],\n };\n } else if (comparisonType === 'equalIgnoreCase') {\n query = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.text,\n attribute: attribute,\n value: options.text,\n operator: 'eq',\n options: {\n transform: ['lowercase'],\n },\n },\n ],\n };\n }\n\n return query;\n};\n\nexport const generateBooleanQuery = (\n options: BooleanFilterOptions,\n filterItem?: BooleanFilterItem,\n): DcuplQueryGroup => {\n const attribute = filterItem?.attributeKey || options.filterKey;\n\n const query: DcuplQueryGroup = {\n groupKey: options.filterKey,\n queries: [\n {\n queryKey: options.value,\n attribute: attribute,\n value: options.value,\n operator: 'eq',\n },\n ],\n };\n return query;\n};\n\nexport const generateNumberAndDateQuery = (\n options: NumberFilterOptions | DateFilterOptions,\n filterItem?: NumberFilterItem | DateFilterItem,\n): DcuplQueryGroup => {\n const attribute = filterItem?.attributeKey || options.filterKey;\n const query: DcuplQueryGroup = {\n groupKey: options.filterKey,\n queries: [],\n };\n\n if (typeof options.eq !== 'undefined') {\n query.groupType = 'or';\n if (Array.isArray(options.eq)) {\n options.eq.forEach((value) => {\n query.queries.push({ attribute: attribute, value: value, operator: 'eq' });\n });\n } else {\n query.queries.push({ attribute: attribute, value: options.eq, operator: 'eq' });\n }\n }\n if (typeof options.ne !== 'undefined') {\n query.groupType = 'and';\n if (Array.isArray(options.ne)) {\n options.ne.forEach((value) => {\n query.queries.push({ attribute: attribute, value: value, operator: 'eq', options: { invert: true } });\n });\n } else {\n query.queries.push({\n attribute: attribute,\n value: options.ne,\n operator: 'eq',\n options: { invert: true },\n });\n }\n }\n if (typeof options.gt !== 'undefined') {\n query.groupType = 'and';\n query.queries.push({ attribute: attribute, value: options.gt, operator: 'gt' });\n }\n if (typeof options.lt !== 'undefined') {\n query.groupType = 'and';\n query.queries.push({ attribute: attribute, value: options.lt, operator: 'lt' });\n }\n if (typeof options.gte !== 'undefined') {\n query.groupType = 'and';\n query.queries.push({ attribute: attribute, value: options.gte, operator: 'gte' });\n }\n if (typeof options.lte !== 'undefined') {\n query.groupType = 'and';\n query.queries.push({ attribute: attribute, value: options.lte, operator: 'lte' });\n }\n\n return query;\n};\n\nexport const generateReferenceQuery = (\n options: ReferenceFilterOptions,\n filterItem?: ReferenceFilterItem,\n): DcuplQueryGroup => {\n const attribute = filterItem?.attributeKey || options.filterKey;\n const query: DcuplQueryGroup = {\n groupKey: options.filterKey,\n queries: [],\n };\n\n options.itemKeys.forEach((key) => {\n query.queries.push({\n queryKey: key,\n attribute: attribute,\n value: { key: key },\n operator: 'eq',\n });\n });\n\n return query;\n};\n\nexport const isValidQueryStatement = (query: DcuplQueryStatement): boolean => {\n if (typeof query.modelKey !== 'string') return false;\n\n if (!Array.isArray(query.queries)) return false;\n\n return true;\n};\n","import {\n ListItem,\n DcuplQueryGroup,\n DcuplQuery,\n InverseIndexReferenceMap,\n DcuplQueryTransformOptions,\n CustomOperatorFnMap,\n CustomOperatorFn,\n DcuplGlobalQueryOptions,\n} from './types';\nimport { get, isEqual, find, size, startsWith, isEqualWith, differenceBy, every, endsWith, isMatch } from 'lodash-es';\nimport { DcuplQueryBuilder } from './query-builder';\nimport { ScriptController } from './script.controller';\nimport { ModelParser } from '@dcupl/core/model-parser';\nimport { ItemService } from '@dcupl/core/catalog';\n\nexport class QueryManager {\n private registeredCustomOperator: CustomOperatorFnMap = new Map();\n\n private queryBuilder = new DcuplQueryBuilder();\n\n constructor(\n public scriptController: ScriptController,\n public globalItemService: ItemService,\n public modelParser: ModelParser,\n ) {}\n\n public registerCustomOperator(operator: string, fn: CustomOperatorFn): void {\n this.registeredCustomOperator.set(operator, fn);\n }\n\n public transformValue(value: any, transformers: DcuplQueryTransformOptions[]): any {\n if (Array.isArray(value)) {\n return value.map((val) => this.transformValue(val, transformers));\n }\n\n if (typeof value !== 'string') {\n return value;\n }\n\n for (const transformer of transformers) {\n if (transformer === 'lowercase') {\n value = value.toLowerCase();\n } else if (transformer === 'trim') {\n value = value.trim();\n } else if (transformer === 'removeWhitespace') {\n value = value.replace(/\\s/g, '');\n }\n }\n return value;\n }\n\n private evaluateQuery(query: DcuplQuery, dataEntry: ListItem): boolean {\n let dataEntryValue;\n let queryValue = query.value;\n\n const arrayValueHandling = query.options?.arrayValueHandling || 'some';\n\n // apply transformations to query value\n if (Array.isArray(query.options?.transform) && query.options!.transform.length > 0) {\n queryValue = this.transformValue(queryValue, query.options!.transform);\n }\n\n // important\n // get the data entry value based on the query attribute\n if (query.attribute.includes('.') || query.attribute.includes('[')) {\n dataEntryValue = get(dataEntry, query.attribute);\n } else {\n dataEntryValue = dataEntry[query.attribute];\n }\n\n // apply transformations to data entry value\n if (Array.isArray(query.options?.transform) && query.options!.transform.length > 0) {\n dataEntryValue = this.transformValue(dataEntryValue, query.options!.transform);\n }\n\n // todo.. check if \"find\" should be in this list\n if (typeof dataEntryValue === 'undefined' || dataEntryValue === null) {\n const allowedUndefinedAndNullOperators = ['typeof', 'isTruthy', 'eq'];\n if (!allowedUndefinedAndNullOperators.includes(query.operator)) {\n return false;\n }\n }\n\n switch (query.operator) {\n case 'eq':\n if (!query.options?.invert) {\n return isEqual(dataEntryValue, queryValue);\n } else {\n return !isEqual(dataEntryValue, queryValue);\n }\n case 'find': {\n if (!query.options?.invert) {\n return this.queryFind(dataEntryValue, queryValue, arrayValueHandling);\n } else {\n return !this.queryFind(dataEntryValue, queryValue, arrayValueHandling);\n }\n }\n case 'lt':\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => value < queryValue);\n } else {\n return dataEntryValue.every((value) => value < queryValue);\n }\n } else {\n return dataEntryValue < queryValue;\n }\n case 'gt':\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => value > queryValue);\n } else {\n return dataEntryValue.every((value) => value > queryValue);\n }\n } else {\n return dataEntryValue > queryValue;\n }\n case 'lte':\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => value <= queryValue);\n } else {\n return dataEntryValue.every((value) => value <= queryValue);\n }\n } else {\n return dataEntryValue <= queryValue;\n }\n case 'gte':\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => value >= queryValue);\n } else {\n return dataEntryValue.every((value) => value >= queryValue);\n }\n } else {\n return dataEntryValue >= queryValue;\n }\n case 'typeof':\n if (!query.options?.invert) {\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => typeof value === queryValue);\n } else {\n return dataEntryValue.every((value) => typeof value === queryValue);\n }\n } else {\n return typeof dataEntryValue === queryValue;\n }\n } else {\n return typeof dataEntryValue !== queryValue;\n }\n case 'isTruthy':\n if (queryValue === true) {\n if (Array.isArray(dataEntryValue)) {\n if (arrayValueHandling === 'some') {\n return dataEntryValue.some((value) => typeof !!value);\n } else {\n return dataEntryValue.every((value) => typeof !!value);\n }\n } else {\n return !!dataEntryValue;\n }\n } else {\n return !dataEntryValue;\n }\n case 'size':\n if (typeof dataEntryValue === 'string') {\n return dataEntryValue.length === queryValue;\n } else {\n return size(dataEntryValue) === queryValue;\n }\n\n default:\n if (this.scriptController.hasScript((query as any).operator)) {\n return this.scriptController.executeScript((query as any).operator, 'operator', { query, item: dataEntry });\n }\n\n /**\n * @deprecated\n * Use custom script instead\n */\n if (this.registeredCustomOperator.has((query as any).operator)) {\n const fn = this.registeredCustomOperator.get((query as any).operator);\n if (typeof fn === 'function') {\n return fn(query, dataEntry);\n }\n }\n }\n\n return false;\n }\n\n private evaluateEqOperator(\n rootQuery: DcuplGlobalQueryOptions<any>,\n query: DcuplQuery,\n initialDataset: Map<string, ListItem>,\n indexMap: InverseIndexReferenceMap,\n ): Map<string, ListItem> {\n const filteredData: Map<string, ListItem> = new Map();\n\n const indices = indexMap.get(query.attribute);\n if (indices) {\n let value: string | undefined;\n if (typeof query.value === 'string') {\n value = query.value;\n } else if (query.value?.key && Object.keys(query.value).length === 1) {\n value = query.value.key;\n }\n\n if (typeof value !== 'undefined') {\n const indexValues = indices.get(value);\n if (indexValues) {\n indexValues.forEach((key) => {\n if (initialDataset.has(key)) {\n filteredData.set(key, initialDataset.get(key)!);\n }\n });\n\n if (query.options?.invert) {\n const diff = differenceBy(Array.from(initialDataset.values()), Array.from(filteredData.values()), 'key');\n const diffMap = new Map();\n diff.forEach((item) => {\n diffMap.set(item.key, item);\n });\n return diffMap;\n } else {\n return filteredData;\n }\n }\n }\n }\n\n // fallback if indices do not work properly...\n for (let [key, value] of initialDataset) {\n const queryAttributes = this.queryBuilder.extractQueryAttributes({ queries: [query] } as any);\n\n if (queryAttributes.some((attr) => attr.isDeepQuery)) {\n const projection = this.queryBuilder.getProjectionForAttributeEntries(\n queryAttributes.filter((attr) => attr.isDeepQuery),\n );\n\n value = this.globalItemService.getProjectedData(\n projection,\n this.modelParser.models.get(rootQuery.modelKey)!,\n value,\n );\n }\n if (this.evaluateQuery(query, value)) {\n filteredData.set(key, value);\n }\n }\n\n return filteredData;\n }\n\n private equalRegexFn(value: any, queryValue: any): boolean {\n if (typeof queryValue === 'string' && startsWith(queryValue, '/') && endsWith(queryValue, '/')) {\n try {\n const regex = new RegExp(queryValue.slice(1, -1));\n return regex.test(value);\n } catch (_err) {\n return false;\n }\n } else if (Array.isArray(value)) {\n return !!value.find((val) => isEqualWith(val, queryValue, this.equalRegexFn));\n } else if (typeof queryValue === 'object') {\n return isMatch(value, queryValue);\n } else {\n return value === queryValue;\n }\n }\n\n public queryFind(entryValue: any | any[], queryValue: any, arrayValueHandling: 'some' | 'every'): boolean {\n if (Array.isArray(entryValue)) {\n if (typeof queryValue === 'string' || typeof queryValue === 'number' || typeof queryValue === 'boolean') {\n if (arrayValueHandling === 'some') {\n return entryValue.some((value) => isEqualWith(value, queryValue, this.equalRegexFn));\n } else {\n return entryValue.every((value) => isEqualWith(value, queryValue, this.equalRegexFn));\n }\n } else {\n return !!find(entryValue, queryValue);\n }\n } else {\n return isEqualWith(entryValue, queryValue, this.equalRegexFn);\n }\n }\n\n private validateQuery(query: DcuplQuery | DcuplQueryGroup): boolean {\n if (this.queryBuilder.isQuery(query)) {\n if (!query.attribute || !query.operator) {\n throw new Error('invalid query - missing attribute or operator');\n }\n\n if (query.operator === 'find' && Array.isArray(query.value)) {\n throw new Error('invalid query - find operator cannot have array value');\n }\n\n return true;\n } else if (this.queryBuilder.isQueryGroup(query)) {\n return every(query.queries, (q) => this.validateQuery(q));\n } else {\n throw new Error('invalid query - missing query or attribute');\n }\n }\n\n private getEvaluatedQueryDataset(\n rootQuery: DcuplGlobalQueryOptions<any>,\n query: DcuplQuery,\n initialDataset: Map<string, ListItem>,\n indexMap: InverseIndexReferenceMap,\n ): Map<string, ListItem> {\n const filteredData: Map<string, ListItem> = new Map();\n\n if (query.attribute.includes('[*]')) {\n const isInverted = query.options?.invert === true;\n const matchedKeys = new Set<string>();\n\n for (let value of initialDataset.values()) {\n const queryAttributes = this.queryBuilder.extractQueryAttributes({ queries: [query] } as any);\n if (queryAttributes.some((attr) => attr.isDeepQuery)) {\n const projection = this.queryBuilder.getProjectionForAttributeEntries(\n queryAttributes.filter((attr) => attr.isDeepQuery),\n );\n\n value = this.globalItemService.getProjectedData(\n projection,\n this.modelParser.models.get(rootQuery.modelKey)!,\n value,\n );\n }\n\n // Create expanded queries WITHOUT invert - we handle invert at the group level\n const newQuery = this.handleArrayStarQuery(query, value);\n\n // Check if ANY of the expanded queries match (OR semantics)\n let anyMatch = false;\n for (const expandedQuery of newQuery.queries) {\n // Evaluate without invert - we apply invert to the final result\n const queryWithoutInvert = { ...expandedQuery, options: { ...expandedQuery.options, invert: false } };\n if (this.evaluateQuery(queryWithoutInvert, value)) {\n anyMatch = true;\n break;\n }\n }\n\n if (anyMatch) {\n matchedKeys.add(value.key);\n if (!isInverted) {\n filteredData.set(value.key, value);\n }\n }\n }\n\n // If inverted, return items that did NOT match\n if (isInverted) {\n for (const [key, value] of initialDataset) {\n if (!matchedKeys.has(key)) {\n filteredData.set(key, value);\n }\n }\n }\n } else if (query.operator === 'eq') {\n return this.evaluateEqOperator(rootQuery, query, initialDataset, indexMap);\n } else {\n for (let [key, value] of initialDataset) {\n const queryAttributes = this.queryBuilder.extractQueryAttributes({ queries: [query] } as any);\n if (queryAttributes.some((attr) => attr.isDeepQuery)) {\n const projection = this.queryBuilder.getProjectionForAttributeEntries(\n queryAttributes.filter((attr) => attr.isDeepQuery),\n );\n\n value = this.globalItemService.getProjectedData(\n projection,\n this.modelParser.models.get(rootQuery.modelKey)!,\n value,\n );\n }\n\n if (this.evaluateQuery(query, value)) {\n filteredData.set(key, value);\n }\n }\n }\n\n return filteredData;\n }\n\n private handleArrayStarQuery(query: DcuplQuery, value: ListItem): DcuplQueryGroup {\n // regex to match the attribute with [*]\n\n const starIndex = query.attribute.indexOf('[*]');\n const newQueryGroup: DcuplQueryGroup = {\n groupKey: query.queryKey || query.attribute + '[*]',\n groupType: 'or',\n queries: [],\n };\n\n if (starIndex !== -1) {\n // replace [*] with the first match\n const cleanAttribute = query.attribute.substring(0, starIndex);\n // For projected data, the nested refs are already objects, use get() for deep paths\n const arrayValue: any[] = cleanAttribute.includes('.') ? get(value, cleanAttribute) : value[cleanAttribute];\n if (Array.isArray(arrayValue)) {\n for (let i = 0; i < arrayValue.length; i++) {\n const newAttribute = query.attribute.replace('[*]', `[${i}]`);\n newQueryGroup.queries.push({\n attribute: newAttribute,\n operator: query.operator,\n value: query.value,\n options: query.options,\n });\n }\n }\n }\n\n return newQueryGroup;\n }\n\n private evaluateQueryGroup(\n rootQuery: DcuplGlobalQueryOptions<any>,\n queryGroup: DcuplQueryGroup,\n initialDataset: Map<string, ListItem>,\n earlyReturn: boolean,\n indexMap: InverseIndexReferenceMap,\n ): Map<string, ListItem> {\n let filteredData: Map<string, ListItem> = new Map();\n let currentDataset = initialDataset;\n\n if (!queryGroup.queries || queryGroup.queries?.length === 0) {\n return currentDataset;\n }\n\n for (const query of queryGroup.queries) {\n let result = new Map();\n\n if (this.queryBuilder.isQuery(query)) {\n result = this.getEvaluatedQueryDataset(rootQuery, query, currentDataset, indexMap);\n } else {\n result = this.evaluateQueryGroup(rootQuery, query, currentDataset, earlyReturn, indexMap);\n }\n\n if (filteredData.size === 0) {\n filteredData = result;\n }\n\n if (queryGroup.groupType === 'or') {\n result.forEach((item) => {\n filteredData.set(item.key, item);\n });\n } else {\n const diff = differenceBy(Array.from(filteredData.values()), Array.from(result.values()), 'key');\n for (const item of diff) {\n filteredData.delete(item.key);\n }\n }\n\n if (queryGroup.groupType === 'and') {\n currentDataset = filteredData;\n } else {\n currentDataset = initialDataset;\n }\n }\n\n return filteredData;\n }\n\n public queryData<ListType extends ListItem = ListItem>(\n initialData: Map<string, ListType>,\n rootQuery: DcuplGlobalQueryOptions<any>,\n earlyReturn: boolean,\n indexMap: InverseIndexReferenceMap,\n ): Map<string, ListType> {\n if (rootQuery.queries.length === 0) {\n return initialData;\n }\n\n const currentDataset = new Map(initialData);\n\n if (!rootQuery.groupKey) {\n rootQuery.groupKey = 'root';\n }\n if (!rootQuery.groupType) {\n rootQuery.groupType = 'and';\n }\n if (!rootQuery.queries) {\n rootQuery.queries = [];\n }\n\n this.validateQuery(rootQuery);\n\n // const model = this.modelParser.models.get(rootQuery.modelKey);\n\n // const relevantData = this.getRelevantQueryData(currentDataset, rootQuery, model!);\n\n return this.evaluateQueryGroup(rootQuery, rootQuery, currentDataset, earlyReturn, indexMap) as Map<\n string,\n ListType\n >;\n }\n\n // public getRelevantQueryData(data: any, options: DcuplGlobalQueryOptions<any>, model: DcuplModel<ListItem>) {\n // // detect if any of the query attributes are a deep query.\n // // a deep query is a query that references another models property.\n // // if so we need to make a getItems call with the projected data.\n // const attributes = this.queryBuilder.getQueryAttributes(options);\n // const deepQueries = attributes.filter((attr) => {\n // return attr.includes('.') || attr.includes('[');\n // });\n\n // if (deepQueries.length > 0) {\n // const projection: Projection<any> = {\n // $: true,\n // };\n\n // for (const deepQuery of deepQueries) {\n // // split the deep query into modelKey and attribute\n // // e.g. modelA.someAttribute or modelB[0].someAttribute or modelB[*].someAttribute\n // // the result should the modelKey: modelA and attribute: someAttribute\n // // const [modelKey, attribute] = deepQuery.split(new RegExp(/\\.|\\[|\\]/)).filter(Boolean);\n // const parts = deepQuery.split(new RegExp(/\\.|\\[|\\]/)).filter(Boolean);\n // const modelKey = parts[0];\n\n // if (modelKey) {\n // (projection as any)[modelKey] = {\n // $: true,\n // };\n // }\n // }\n\n // const relevantData = this.globalItemService.getItemsAsMap(data, model, {\n // projection,\n // } as DcuplItemsOptions<ListItem>);\n // return relevantData;\n // } else {\n // return data;\n // }\n // }\n}\n","import { generateObjectHash } from './object-hash';\n\n/** Authored `resource.key` values are namespaced so they can never collide with a derived hash. */\nexport const AUTHORED_RESOURCE_ID_PREFIX = 'k:';\nexport const DERIVED_RESOURCE_ID_PREFIX = 'h:';\n\n/**\n * The fields a resource identity is derived from.\n *\n * Structural rather than `AppLoaderConfiguration.Resource` so the same function serves a raw config\n * entry and a runtime progress item — `model` only exists on data resources, `rawUrl` only at runtime.\n */\nexport type ResourceIdentityInput = {\n key?: string;\n type: string;\n url: string;\n rawUrl?: string;\n model?: string;\n};\n\n/**\n * A stable id for one resource.\n *\n * An authored `key` wins and is used verbatim — that id survives a url edit, which is the reason to\n * author keys at all. Otherwise the id is a hash of the RAW url plus type/model. Deriving from the\n * raw url (never the resolved one) is what makes the id identical on both sides of the config↔runtime\n * boundary, since `${variable}` substitution happens only on the runtime side.\n *\n * A derived id therefore changes when the url, type or model changes. That is the accepted limit:\n * it is stable across runs, not across edits.\n *\n * `occurrence` disambiguates config entries that are identical in type/url/model. Prefer\n * `resolveResourceIds` for a list — it assigns occurrences in document order, so reordering the\n * config does not churn ids.\n */\nexport function resolveResourceId(resource: ResourceIdentityInput, occurrence = 0): string {\n if (resource.key) {\n return `${AUTHORED_RESOURCE_ID_PREFIX}${resource.key}`;\n }\n\n const hash = generateObjectHash({\n type: resource.type,\n url: resource.rawUrl ?? resource.url,\n model: resource.model ?? '',\n occurrence,\n });\n\n return `${DERIVED_RESOURCE_ID_PREFIX}${hash}`;\n}\n\n/**\n * Ids for a resource list, positionally aligned with the input, guaranteed unique.\n *\n * Uniqueness is enforced on the OUTPUT, not just on the (type, url, model) basis, because two other\n * things can collide: two entries authored with the same `key` (nothing in the config validates key\n * uniqueness — `loader.resources.get()` simply returns the first match), and a hash collision, which\n * `generateObjectHash` admits by construction since it is a 32-bit rolling hash. Random collisions\n * are vanishingly rare at realistic config sizes, but a duplicate id silently merges two nodes in a\n * consumer's graph, so it is worth ruling out rather than reasoning about.\n */\nexport function resolveResourceIds(resources: readonly ResourceIdentityInput[]): string[] {\n const occurrences = new Map<string, number>();\n const emitted = new Set<string>();\n\n return resources.map((resource) => {\n const basis = `${resource.key ?? ''}|${resource.type}|${resource.rawUrl ?? resource.url}|${resource.model ?? ''}`;\n let occurrence = occurrences.get(basis) ?? 0;\n\n let id = resolveResourceId(resource, occurrence);\n while (emitted.has(id)) {\n occurrence += 1;\n // An authored key ignores `occurrence`, so bumping it alone would loop forever — suffix instead.\n id = resource.key\n ? `${resolveResourceId(resource)}#${occurrence}`\n : resolveResourceId(resource, occurrence);\n }\n\n occurrences.set(basis, occurrence + 1);\n emitted.add(id);\n return id;\n });\n}\n","type SchedulerLike = { yield?: () => Promise<void> };\n\n/**\n * Yields control back to the event loop as a macrotask WITHOUT going through `setTimeout`.\n *\n * Browsers throttle DOM timers on hidden/background tabs: Chrome aligns chained timers to\n * ~1 s wake-ups (and to 1 min after five minutes hidden). A loop that awaited\n * `setTimeout(resolve, 0)` per iteration therefore stalled ~1 s per iteration whenever the\n * tab was not in the foreground, turning a sub-second `dcupl.init()` into minutes.\n * `scheduler.yield()`, `setImmediate` and `MessageChannel` tasks are not throttled that way,\n * while still letting rendering and input events run between iterations.\n *\n * Resolution order:\n * 1. `scheduler.yield()` — browsers with the Prioritized Task Scheduling API (Chrome 129+)\n * 2. `setImmediate` — Node.js\n * 3. `MessageChannel` — every other browser\n * 4. `setTimeout(0)` — last resort\n */\nexport function yieldToEventLoop(): Promise<void> {\n const g = globalThis as typeof globalThis & { scheduler?: SchedulerLike };\n\n if (typeof g.scheduler?.yield === 'function') {\n return g.scheduler.yield();\n }\n\n if (typeof g.setImmediate === 'function') {\n return new Promise<void>((resolve) => g.setImmediate(() => resolve()));\n }\n\n if (typeof g.MessageChannel === 'function') {\n return new Promise<void>((resolve) => {\n const channel = new g.MessageChannel();\n channel.port1.onmessage = (): void => {\n channel.port1.close();\n resolve();\n };\n channel.port2.postMessage(null);\n });\n }\n\n return new Promise<void>((resolve) => setTimeout(resolve, 0));\n}\n","export type ScriptType = 'script' | 'async_script' | 'sort' | 'operator' | 'validator';\n\nexport class ScriptController {\n private scripts: Map<string, any> = new Map();\n private scriptFunctions: Map<string, any> = new Map();\n constructor() {}\n\n public keys(): any {\n return Array.from(this.scripts.keys());\n }\n\n public setScript(key: string, script: any): void {\n this.scripts.set(key, script);\n }\n\n public removeScript(key: string): void {\n this.scripts.delete(key);\n }\n\n public getScript(key: string): any {\n return this.scripts.get(key);\n }\n\n public hasScript(key: string): boolean {\n return this.scripts.has(key);\n }\n\n private getScriptAsFn(key: string, argsAsKeys: string[]): any {\n if (this.scriptFunctions.has(key)) {\n return this.scriptFunctions.get(key);\n }\n\n const script = this.getScript(key);\n if (!script) {\n throw new Error(`Script ${key} not found`);\n }\n\n const wrapperScript = this.getWrapperScript(script);\n\n const fn = new Function('script_type', ...argsAsKeys, wrapperScript);\n this.scriptFunctions.set(key, fn);\n\n return fn;\n }\n\n private getWrapperScript(script: string): string {\n const wrapperScript = `\n \"use strict\";\n ${script}\n `;\n return wrapperScript;\n }\n\n public executeScript(key: string, type: ScriptType, args: any): any {\n const script = this.getScript(key);\n if (!script) {\n throw new Error(`Script ${key} not found`);\n }\n\n if (!args.context) {\n args.context = args.options || {};\n }\n\n const argsAsKeys = Object.keys(args);\n const argsAsArray = Object.values(args);\n\n const fn = this.getScriptAsFn(key, argsAsKeys);\n\n return fn(type, ...argsAsArray);\n }\n}\n","import { get } from 'lodash-es';\nexport const evaluateTemplate = (template: string | undefined, variables: Record<string, any> | undefined): string => {\n if (!template) {\n return '';\n }\n\n if (!variables) {\n variables = {};\n }\n\n return template.replace(/\\$\\{(.*?)\\}/g, (_, varName) => {\n return get(variables, varName.trim(), '');\n });\n};\n\nexport const parseTemplate = (templateString: string): { variables: { name: string }[] } => {\n const variableNames: Set<string> = new Set();\n\n templateString.replace(/\\$\\{(.*?)\\}/g, (_, varName) => {\n variableNames.add(varName.trim());\n return '';\n });\n\n const variablesArray = Array.from(variableNames).map((name) => ({ name }));\n return { variables: variablesArray };\n};\n","import { AggregationOptions, DcuplGlobalQueryOptions, DcuplQuery, DcuplQueryGroup, RawItem } from '.';\nimport { KeyPropertySpec } from './key-property.types';\nimport { PivotOptions } from '../pivot';\n\ntype AggregationOptionsModel = Omit<AggregationOptions, 'attribute'>;\n\nexport type PropertyType =\n | 'any'\n | 'Array<date>'\n | 'Array<float>'\n | 'Array<int>'\n | 'Array<string>'\n | 'boolean'\n | 'date'\n | 'float'\n | 'int'\n | 'json'\n | 'string';\n\nexport const PropertyTypesAsArray: PropertyType[] = [\n 'any',\n 'Array<date>',\n 'Array<float>',\n 'Array<int>',\n 'Array<string>',\n 'boolean',\n 'date',\n 'float',\n 'int',\n 'json',\n 'string',\n];\n\nexport const NumberPropertyTypes: PropertyType[] = ['int', 'float', 'Array<int>', 'Array<float>'];\nexport const DatePropertyTypes: PropertyType[] = ['date', 'Array<date>'];\n\n/**\n * Configures how property auto-generation inspects input data.\n *\n * - `true` / `false`: enable/disable auto-generation. When enabled with `true`,\n * only the first row of input data is inspected (fast, default behavior).\n * - Object form lets you scan more rows:\n * - `deep: true` walks every row and reconciles types across them.\n * - `sampleSize: N` walks at most N rows (implies deep). Useful for large datasets\n * where row 1 alone may not represent the schema.\n *\n * When more than one row is scanned, empty values (`null`, `undefined`, `''`)\n * are ignored during inference so a single blank cell does not poison a column's type.\n */\nexport type AutoGeneratePropertiesOption =\n | boolean\n | {\n deep?: boolean;\n sampleSize?: number;\n };\n\n/**\n * must be singleValued or multiValued\n * singleValued contains only one key to an object\n * multiValued contains multiple key of objects\n *\n * @minLength 1\n * @chance {\n * \"pickone\": [ [ \"singleValued\", \"multiValued\" ] ]\n * }\n * @ignoreThis 2\n * @important\n */\nexport type ReferenceType = 'singleValued' | 'multiValued';\n\nexport type AttributeType = PropertyType | ReferenceType;\n\nexport type ModelMetadata = {\n name: string;\n description?: string;\n [key: string]: any;\n};\n\nexport type FilterComparisonType =\n | 'equal'\n | 'equalIgnoreCase'\n | 'contains'\n | 'containsIgnoreCase'\n | 'containsIgnoreCaseAndWhitespace';\n\nexport type PropertyAggregate = (PropertyNumber | PropertyString) & {\n aggregate: true;\n};\n\nexport type FilterStringArray = {\n key: string;\n comparisonType: FilterComparisonType;\n};\n\nexport type FilterStringSingle = {\n comparisonType?: FilterComparisonType;\n};\n\n/**\n * A property is a quality or characteristic belonging an object.\n * @defaultSnippets {\"label\":\"Property\", \"properties\":[\"key\",\"type\"]}\n *\n */\nexport type PropertyBase = {\n /**\n * A key is an unique identifier overall properties in this model.\n *\n * @minLength 1\n * @important\n */\n key: string;\n type: PropertyType;\n\n expression?: string;\n\n derive?: {\n localReference: string;\n remoteProperty: string;\n separator?: string;\n };\n\n origin?: string;\n filter?: boolean | FilterStringSingle | FilterStringArray[];\n aggregate?: boolean | AggregationOptionsModel;\n meta?: ModelMetadata;\n index?: boolean;\n quality?: AttributeQualityConfig;\n};\n\n/**\n * @defaultSnippets { \"label\": \"String Property\", \"properties\": [\"key\", \"type:string:boolean\"] }\n */\nexport type PropertyBoolean = PropertyBase & {\n type: 'boolean';\n aggregate?: boolean | AggregationOptionsModel;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Int Property\", \"properties\": [\"key\", \"type:int\"] }\n */\n\nexport type PropertyInt = PropertyBase & {\n type: 'int';\n aggregate?: boolean | AggregationOptionsModel;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Int Array Property\", \"properties\": [\"key\", \"type:Array<int>\"] }\n */\n\nexport type PropertyIntArray = PropertyBase & {\n type: 'Array<int>';\n separator?: string;\n aggregate?: boolean | AggregationOptionsModel;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Float Property\", \"properties\": [\"key\", \"type:float\"] }\n */\nexport type PropertyFloat = PropertyBase & {\n type: 'float';\n aggregate?: boolean | AggregationOptionsModel;\n fractionDigits?: number;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Float Property\", \"properties\": [\"key\", \"type:float\"] }\n */\nexport type PropertyFloatArray = PropertyBase & {\n type: 'Array<float>';\n separator?: string;\n aggregate?: boolean | AggregationOptionsModel;\n fractionDigits?: number;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Json Property\", \"properties\": [\"key\", \"type:string:json\"] }\n */\nexport type PropertyJson = PropertyBase & {\n type: 'json' | 'Array<json>';\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Any Property\", \"properties\": [\"key\", \"type:any\"] }\n */\nexport type PropertyAny = PropertyBase & {\n type: 'any' | 'Array<any>';\n};\n\n/**\n * Example: 'yyyy-MM-dd' | 'dd.MM.yyyy' | ...\n */\n// export type PropertyDateInputFormat = 'date' | 'ts_seconds' | 'ts_milliseconds' | 'string' | string;\nexport type PropertyDateInputFormat = string;\n// export type PropertyDateOutputFormat = 'date' | 'ts_seconds' | 'ts_milliseconds' | 'string';\n\n/**\n * @defaultSnippets { \"label\": \"Date Property\", \"properties\": [\"key\", \"type:date\"] }\n */\nexport type PropertyDate = PropertyBase & {\n type: 'date';\n inputFormat?: PropertyDateInputFormat;\n UTC?: boolean;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Date Property\", \"properties\": [\"key\", \"type:date\"] }\n */\nexport type PropertyDateArray = PropertyBase & {\n type: 'Array<date>';\n separator?: string;\n inputFormat?: PropertyDateInputFormat;\n UTC?: boolean;\n filter?: boolean;\n};\n\n/**\n * @defaultSnippets { \"label\": \"String Property\", \"properties\": [\"key\", \"type:string:string\"] }\n */\nexport type PropertyString = PropertyBase & {\n type: 'string';\n aggregate?: boolean | AggregationOptionsModel;\n filter?: boolean | FilterStringSingle | FilterStringArray[];\n};\n\n/**\n * @defaultSnippets { \"label\": \"String Property\", \"properties\": [\"key\", \"type:string:string\"] }\n */\nexport type PropertyStringArray = PropertyBase & {\n type: 'Array<string>';\n separator?: string;\n filter?: boolean | FilterStringSingle | FilterStringArray[];\n aggregate?: boolean | AggregationOptionsModel;\n};\n\nexport type PropertyNumber = PropertyFloat | PropertyInt | PropertyFloatArray | PropertyIntArray;\nexport type Property =\n | PropertyString\n | PropertyStringArray\n | PropertyInt\n | PropertyIntArray\n | PropertyFloat\n | PropertyFloatArray\n | PropertyDate\n | PropertyDateArray\n | PropertyJson\n | PropertyBoolean\n | PropertyAny;\n\n/**\n * Facet calculation is disabled by default\n */\nexport type ReferenceFilterDefinition = {\n calculateFacets?: boolean;\n excludeZeros?: boolean;\n excludeUndefineds?: boolean;\n};\n\nexport type BooleanFilterDefinition = {\n calculateFacets?: boolean;\n excludeUndefineds?: boolean;\n};\n\nexport type ReferenceValidity = 'loose' | 'strict';\n\n/**\n * @defaultSnippets { \"label\": \"Default Reference\", \"properties\": [\"key\", \"type:singleValued\", \"model\"] }\n */\nexport type ReferenceBase = {\n /**\n * A key is an unique identifier for all reference in this model.\n *\n * @minLength 1\n * @important\n */\n key: string;\n type: ReferenceType;\n /**\n * The Model this reference is pointing to.\n *\n * @minLength 1\n * @important\n */\n model: string;\n\n origin?: string;\n filter?: boolean | ReferenceFilterDefinition;\n aggregate?: boolean | AggregationOptionsModel;\n separator?: string;\n meta?: ModelMetadata;\n index?: boolean;\n validity?: ReferenceValidity;\n quality?: AttributeQualityConfig;\n};\n\nexport type AggregatedReference = ReferenceBase & {\n aggregate: true;\n};\n\n/**\n * Resolves a Reference from a foreign model\n *\n * @defaultSnippets { \"label\": \"Resolved Reference\", \"properties\": [\"key\", \"type:multiValued\", \"model\", \"resolve:json:{\\\"reference\\\":\\\"\\\", \\\"model\\\":\\\"\\\" }\"] }\n */\nexport type ResolvedReference = ReferenceBase & {\n // type: 'multiValued';\n /**\n * Resolve a Relationship to an foreign model\n *\n * @defaultSnippets {\"label\":\"Resolve Relationship\", \"properties\":[\"reference\",\"model\"]}\n */\n resolve: {\n /**\n * The reference on the foreign object which should be used for resolve the relation.\n *\n * @minLength 1\n * @important\n */\n reference: string;\n\n /**\n * The model of the foreign object reference.\n *\n * @minLength 1\n * @important\n */\n model: string;\n };\n};\n\n/**\n * Queries a Reference from a foreign model\n *\n * @defaultSnippets { \"label\": \"Resolved Reference\", \"properties\": [\"key\", \"type:multiValued\", \"model\", \"resolve:json:{\\\"reference\\\":\\\"\\\", \\\"model\\\":\\\"\\\" }\"] }\n */\nexport type QueryReference = ReferenceBase & {\n // type: 'multiValued';\n /**\n * Resolve a Relationship to an foreign model\n *\n * @defaultSnippets {\"label\":\"Resolve Relationship\", \"properties\":[\"reference\",\"model\"]}\n */\n query: DcuplGlobalQueryOptions<any> | DcuplQuery | DcuplQueryGroup | DcuplQuery[] | DcuplQueryGroup[];\n};\n\n/**\n * Derives a References for a specific value from an other model\n *\n * @defaultSnippets { \"label\": \"Derived Reference\", \"properties\": [\"key\", \"type:singleValued\", \"model\", \"derive:json:{\\\"localReference\\\":\\\"\\\", \\\"remoteReference\\\":\\\"\\\"}\"] }\n */\nexport type DerivedReference = ReferenceBase & {\n type: ReferenceType;\n /**\n * Derive a specific value from an other object/entity\n * @defaultSnippets {\"label\":\"Derive Values\", \"properties\": [\"localReference\",\"remoteReference\"]}\n */\n derive: {\n /**\n * The reference on the foreign object which should be used for resolve the relation.\n *\n * @minLength 1\n * @important\n */\n localReference: string;\n /**\n * The reference on the foreign object which should be derived.\n *\n * @minLength 1\n * @important\n */\n remoteReference: string;\n };\n};\n\n/**\n * Groups a Reference by a specific value from an other model\n *\n * @defaultSnippets { \"label\": \"Grouped Reference\", \"properties\": [\"key\", \"type:singleValued\", \"model\", \"groupBy:json:{\\\"reference\\\":\\\"\\\" }\", \"includeSelfInGroup:boolean\"] }\n */\nexport type GroupedRefeference = ReferenceBase & {\n type: ReferenceType;\n /**\n * Group by a specific reference\n * @defaultSnippets {\"label\":\"Grouped Values\", \"properties\": [\"reference\",\"includeSelfInGroup\"]}\n */\n groupBy: {\n /**\n * The reference that groups are generated from\n *\n * @minLength 1\n * @important\n */\n reference: string;\n\n includeSelfInGroup?: boolean;\n };\n};\n\nexport type Reference = ResolvedReference | DerivedReference | GroupedRefeference | ReferenceBase | QueryReference;\n\nexport type ValueMappingValue = string | '$dcupl_falsy';\n\nexport type ValueMappingValues = {\n from: ValueMappingValue[];\n to: any;\n};\n\nexport type ValueMappingConfig = {\n attributes: string[];\n values: ValueMappingValues[];\n};\n\nexport type ModelAttribute = Property | Reference;\n\nexport type ModelQualityConfig = {\n /**\n * Master switch for quality checks on this model. Default: true.\n * Checks only run when error tracking is also enabled globally\n * (two-tier enablement).\n */\n enabled?: boolean;\n /**\n * Model-wide defaults for the attribute quality FLAGS\n * (required / nullable / forceStrictDataType / validatorHandling),\n * merged under each attribute's own `quality`.\n * Concrete `validators` are per-attribute only and cannot be set here.\n */\n attributes?: Omit<AttributeQualityConfig, 'validators'>;\n};\n\nexport type ValidatorErrorType = 'loose' | 'strict';\n\nexport type ValidatorConfig = {\n value?: any;\n // errorType?: ValidatorErrorType;\n};\n\nexport type CustomValidatorConfig = {\n key: string;\n value?: any;\n validatorHandling?: ValidatorErrorType;\n};\n\nexport type AttributeValidatorConfig = {\n // custom?: CustomValidatorConfig;\n\n email?: ValidatorConfig;\n endsWith?: ValidatorConfig;\n enum?: ValidatorConfig;\n includes?: ValidatorConfig;\n max?: ValidatorConfig;\n maxLength?: ValidatorConfig;\n min?: ValidatorConfig;\n minLength?: ValidatorConfig;\n pattern?: ValidatorConfig;\n unique?: ValidatorConfig;\n startsWith?: ValidatorConfig;\n};\n\n/**\n * Quality rules for a single property or reference.\n *\n * Defaults: required: true, nullable: false, forceStrictDataType: false,\n * validatorHandling: 'loose'.\n *\n * validatorHandling decides what happens when a validator fails:\n * 'loose' (default) reports an InvalidValidator error and keeps the value;\n * 'strict' reports AND drops the value from the loaded data.\n * forceStrictDataType: true disables type coercion — wrong-typed raw\n * values are reported as WrongDataType and dropped.\n */\nexport type AttributeQualityConfig = {\n required?: boolean;\n nullable?: boolean;\n forceStrictDataType?: boolean;\n validators?: AttributeValidatorConfig;\n validatorHandling?: ValidatorErrorType;\n};\n\n/**\n * A model describes a real world object and hols attributes.\n *\n * @defaultSnippets {\"label\":\"Model Definition\", \"properties\":[\"key:string:myModel\", \"properties:json:[{\\\"key\\\":\\\"my_property\\\", \\\"type\\\": \\\"string\\\"}]\", \"references:array\"]}\n */\nexport type ModelDefinition<ModelNames extends string = string> = {\n /**\n * The key is an unique identifier overall models in the application.\n *\n * @minLength 1\n */\n key: ModelNames;\n properties?: Property[];\n references?: Reference[];\n data?: RawItem[];\n\n /**\n * Property name(s) on each row that uniquely identify it.\n * See `KeyPropertySpec` for composite-key behavior.\n */\n keyProperty?: KeyPropertySpec;\n /**\n * Separator joining composite `keyProperty` parts. Default `'::'`.\n */\n keyPropertySeparator?: string;\n autoGenerateKey?: boolean;\n autoGenerateProperties?: AutoGeneratePropertiesOption;\n supportsAutoCreation?: boolean;\n valueMappings?: ValueMappingConfig[];\n meta?: ModelMetadata;\n quality?: ModelQualityConfig;\n};\n\nexport type DcuplViewType = 'section' | 'pivot';\n\nexport type ViewDefinitionBase = {\n /**\n * The key is an unique identifier overall models in the application.\n *\n * @minLength 1\n */\n key: string;\n /**\n * @minLength 1\n */\n model: string;\n /**\n * @minLength 1\n */\n type: DcuplViewType;\n\n meta?: ModelMetadata;\n};\n\n/**\n * @defaultSnippets { \"label\": \"Dcupl Section View\", \"properties\": [\"key\", \"model\", \"type:section\", \"primaryAttribute\", \"aggregates:array\"] }\n */\n\nexport type ViewDefinitionSection = ViewDefinitionBase & {\n type: 'section';\n\n /**\n * @minLength 1\n */\n\n primaryAttribute: string;\n\n aggregates: AggregationOptions[];\n};\n\n/**\n * @defaultSnippets { \"label\": \"Dcupl Pivot View\", \"properties\": [\"key\", \"model\", \"type:pivot\", \"options:json:{\\\"rows\\\":[{\\\"attribute\\\": \\\"\\\"}], \\\"columns\\\":[{\\\"attribute\\\": \\\"\\\"}], \\\"values\\\":[{\\\"attribute\\\": \\\"\\\", \\\"types\\\": [\\\"sum\\\"]}]}\"] }\n */\nexport type ViewDefinitionPivot = ViewDefinitionBase & {\n type: 'pivot';\n options: PivotOptions;\n};\n\nexport type ViewDefinition = ViewDefinitionSection | ViewDefinitionPivot;\n\n/**\n * Container for ModelDescriptions & Views\n *\n */\nexport type Model = ViewDefinition | ModelDefinition | Array<ModelDefinition | ViewDefinition>;\n","import { ListItem } from './data.types';\nimport { Projection, SortingProjection } from './projection.types';\n\n/**\n * Transform options for query values.\n * - `lowercase`: Convert value to lowercase before comparison\n * - `trim`: Remove leading/trailing whitespace\n * - `removeWhitespace`: Remove all whitespace characters\n */\nexport type DcuplQueryTransformOptions = 'lowercase' | 'trim' | 'removeWhitespace';\n\nexport const DcuplQueryOperatorsAsArray = [\n 'eq',\n 'find',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'typeof',\n 'isTruthy',\n 'size',\n] as const;\n\n/**\n * Query operators for filtering data.\n *\n * Available operators:\n * - `eq`: Equality check - matches exact values\n * - `find`: Regex / exact / object match - bare string is exact-equality, `/regex/` for substring/pattern, object for property match\n * - `gt`: Greater than - numeric/date comparison (exclusive)\n * - `gte`: Greater than or equal - numeric/date comparison (inclusive)\n * - `lt`: Less than - numeric/date comparison (exclusive)\n * - `lte`: Less than or equal - numeric/date comparison (inclusive)\n * - `typeof`: Type check - validates data type\n * - `isTruthy`: Boolean check - evaluates truthiness\n * - `size`: Array/string length check - matches collection size\n *\n * @example\n * ```typescript\n * // Equality\n * { operator: 'eq', attribute: 'status', value: 'active' }\n *\n * // Numeric comparison\n * { operator: 'gte', attribute: 'price', value: 100 }\n *\n * // Substring search (slash-delimited regex; bare string would be exact-match)\n * { operator: 'find', attribute: 'name', value: '/search term/' }\n * ```\n */\nexport type DcuplQueryOperator = (typeof DcuplQueryOperatorsAsArray)[number];\n\n/**\n * Query combination logic.\n * - `and`: All conditions must match (intersection)\n * - `or`: At least one condition must match (union)\n *\n * @example\n * ```typescript\n * // AND logic - both conditions must be true\n * { groupType: 'and', queries: [condition1, condition2] }\n *\n * // OR logic - either condition can be true\n * { groupType: 'or', queries: [condition1, condition2] }\n * ```\n */\nexport type DcuplQueryType = 'and' | 'or';\n\n/**\n * Base query properties shared by all query types.\n */\ntype DcuplQueryBase = {\n /**\n * The attribute/property name to query against.\n * Can reference nested properties using dot notation (e.g., 'user.email').\n */\n attribute: string;\n\n /**\n * Optional identifier for this query condition.\n * Useful for tracking or removing specific queries.\n */\n queryKey?: any;\n\n /**\n * Optional discriminator for type narrowing.\n * @internal\n */\n __type?: 'query';\n\n /**\n * Query behavior modifiers.\n */\n options?: {\n /**\n * Transform value before comparison.\n * Applied in order: ['lowercase', 'trim'] applies lowercase first, then trim.\n */\n transform?: DcuplQueryTransformOptions[];\n\n /**\n * Invert the query result (NOT operation).\n * @example\n * ```typescript\n * // Find items where status is NOT 'active'\n * { operator: 'eq', attribute: 'status', value: 'active', options: { invert: true } }\n * ```\n */\n invert?: boolean;\n\n /**\n * When querying array values, determines match behavior.\n * - `some`: Match if at least one array element matches (default)\n * - `every`: Match only if all array elements match\n *\n * @example\n * ```typescript\n * // Match items where ANY tag equals 'featured'\n * { operator: 'eq', attribute: 'tags', value: 'featured', options: { arrayValueHandling: 'some' } }\n *\n * // Match items where ALL tags equal 'featured'\n * { operator: 'eq', attribute: 'tags', value: 'featured', options: { arrayValueHandling: 'every' } }\n * ```\n */\n arrayValueHandling?: 'some' | 'every';\n };\n};\n\n/**\n * Equality query - matches exact values.\n *\n * @example\n * ```typescript\n * // Match items with status 'active'\n * { operator: 'eq', attribute: 'status', value: 'active' }\n *\n * // Match items with a reference key\n * { operator: 'eq', attribute: 'categoryId', value: { key: 'cat-123' } }\n *\n * // Case-insensitive match\n * { operator: 'eq', attribute: 'name', value: 'john', options: { transform: ['lowercase'] } }\n * ```\n */\ntype DcuplQueryEq = DcuplQueryBase & {\n operator: 'eq';\n /**\n * Value to match against.\n * Note: For indexed search optimization, use string or { key: string } values.\n */\n value: any;\n};\n\n/**\n * Find query - regex / exact / object match.\n *\n * Matching depends on the shape of `value`:\n * - **Bare string** (e.g. `'search'`) — exact-equality against the *whole* field\n * value, NOT a substring. `find` with `'Ball'` does not match `'Soccer Ball'`.\n * - **Slash-delimited regex** (e.g. `'/search/'`, `'/^foo/i'`) — JS regex literal;\n * use this for substring and pattern matching.\n * - **Object** (e.g. `{ key: 'value' }`) — property matching against the field.\n *\n * @example\n * ```typescript\n * // Substring search — slash-delimited regex (bare string would be exact-match)\n * { operator: 'find', attribute: 'description', value: '/search/' }\n *\n * // Exact-match the whole field value\n * { operator: 'find', attribute: 'status', value: 'active' }\n *\n * // Find items with specific nested property\n * { operator: 'find', attribute: 'metadata', value: { key: 'value' } }\n * ```\n */\ntype DcuplQueryFind = DcuplQueryBase & {\n operator: 'find';\n /**\n * Bare string (exact-match), `/regex/` (substring/pattern), or object (property match).\n */\n value: Record<string, unknown> | string;\n};\n\n/**\n * Numeric/date comparison queries.\n * Supports greater than, less than, and inclusive variants.\n *\n * @example\n * ```typescript\n * // Items with price greater than 100\n * { operator: 'gt', attribute: 'price', value: 100 }\n *\n * // Items created after a date\n * { operator: 'gte', attribute: 'createdAt', value: new Date('2024-01-01') }\n *\n * // Items with quantity less than or equal to 10\n * { operator: 'lte', attribute: 'quantity', value: 10 }\n * ```\n */\ntype DcuplQueryNumbers = DcuplQueryBase & {\n operator: 'gt' | 'lt' | 'gte' | 'lte';\n /**\n * Numeric value, date, or string representation for comparison.\n */\n value: string | number | Date;\n};\n\n/**\n * Type check query - validates data type.\n *\n * @example\n * ```typescript\n * // Match items where price is a number\n * { operator: 'typeof', attribute: 'price', value: 'number' }\n *\n * // Match items where tags is an array\n * { operator: 'typeof', attribute: 'tags', value: 'object' }\n * ```\n */\ntype DcuplQueryTypeOf = DcuplQueryBase & {\n operator: 'typeof';\n /**\n * JavaScript type name: 'string', 'number', 'boolean', 'object', 'undefined', 'function', 'symbol'\n */\n value: string;\n};\n\n/**\n * Truthiness check query - evaluates boolean truthiness.\n *\n * @example\n * ```typescript\n * // Match items where isActive is truthy\n * { operator: 'isTruthy', attribute: 'isActive', value: true }\n *\n * // Match items where isDeleted is falsy\n * { operator: 'isTruthy', attribute: 'isDeleted', value: false }\n * ```\n */\ntype DcuplQueryIsTruthy = DcuplQueryBase & {\n operator: 'isTruthy';\n /**\n * Expected truthiness: true for truthy values, false for falsy values.\n */\n value: boolean;\n};\n\n/**\n * Size check query - matches array length or string length.\n *\n * @example\n * ```typescript\n * // Match items with exactly 3 tags\n * { operator: 'size', attribute: 'tags', value: 3 }\n *\n * // Match items with name exactly 10 characters long\n * { operator: 'size', attribute: 'name', value: 10 }\n * ```\n */\ntype DcuplQuerySize = DcuplQueryBase & {\n operator: 'size';\n /**\n * Expected length/size of the array or string.\n */\n value: number;\n};\n\n/**\n * Query condition - represents a single filter criterion.\n * Use this to filter items based on attribute values.\n *\n * @see QueryCondition - Type alias with clearer naming\n *\n * @example\n * ```typescript\n * // Simple equality check\n * const query: DcuplQuery = {\n * operator: 'eq',\n * attribute: 'status',\n * value: 'active'\n * };\n *\n * // Numeric comparison with options\n * const priceQuery: DcuplQuery = {\n * operator: 'gte',\n * attribute: 'price',\n * value: 100,\n * options: { invert: false }\n * };\n * ```\n */\nexport type DcuplQuery =\n | DcuplQueryEq\n | DcuplQueryNumbers\n | DcuplQueryTypeOf\n | DcuplQueryIsTruthy\n | DcuplQuerySize\n | DcuplQueryFind;\n\n/**\n * Query group - combines multiple conditions with AND/OR logic.\n * Groups can be nested to create complex filter expressions.\n *\n * @see QueryConditionGroup - Type alias with clearer naming\n *\n * @example\n * ```typescript\n * // AND group - all conditions must match\n * const andGroup: DcuplQueryGroup = {\n * groupType: 'and',\n * groupKey: 'active-products',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]\n * };\n *\n * // OR group - at least one condition must match\n * const orGroup: DcuplQueryGroup = {\n * groupType: 'or',\n * queries: [\n * { operator: 'eq', attribute: 'category', value: 'electronics' },\n * { operator: 'eq', attribute: 'category', value: 'books' }\n * ]\n * };\n *\n * // Nested groups\n * const nestedGroup: DcuplQueryGroup = {\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * {\n * groupType: 'or',\n * queries: [\n * { operator: 'eq', attribute: 'featured', value: true },\n * { operator: 'gte', attribute: 'rating', value: 4.5 }\n * ]\n * }\n * ]\n * };\n * ```\n */\nexport type DcuplQueryGroup<ChildQuery extends DcuplQuery | DcuplQueryGroup = any> = {\n /**\n * Optional discriminator for type narrowing.\n * @internal\n */\n __type?: 'group';\n\n /**\n * Combination logic for conditions in this group.\n * Defaults to 'and' if not specified.\n */\n groupType?: DcuplQueryType;\n\n /**\n * Optional identifier for this group.\n * Useful for tracking or removing specific query groups.\n */\n groupKey?: string;\n\n /**\n * Array of query conditions or nested groups.\n */\n queries: Array<ChildQuery>;\n};\n\n/**\n * Common options for data retrieval operations.\n * Controls pagination, projection, and sorting.\n *\n * @see ItemDataOptions - Type alias with clearer naming\n */\ntype DcuplItemOptions<T> = {\n /**\n * Starting index for pagination (0-based).\n * @example start: 10 // Skip first 10 items\n */\n start?: number;\n\n /**\n * Maximum number of items to return.\n * @example count: 20 // Return up to 20 items\n */\n count?: number;\n\n /**\n * Specify which properties to include/exclude in results.\n */\n projection?: Projection<T>;\n\n /**\n * Define sorting order for results.\n */\n sort?: SortingProjection<T>;\n};\n\n/**\n * Options for executing queries across the global dcupl instance.\n * Combines model specification, query conditions, and data retrieval options.\n *\n * @see QueryExecutionContext - Type alias with clearer naming\n *\n * @example\n * ```typescript\n * const options: DcuplGlobalQueryOptions<Product> = {\n * modelKey: 'Product',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' }\n * ],\n * start: 0,\n * count: 20,\n * sort: { price: 'asc' }\n * };\n * ```\n */\nexport type DcuplGlobalQueryOptions<T> = {\n /**\n * Model identifier to query against.\n */\n modelKey: string;\n} & DcuplItemOptions<T> &\n DcuplQueryGroup;\n\n/**\n * Options for retrieving a single item from the global dcupl instance.\n */\nexport type DcuplGlobalItemOptions<T> = {\n /**\n * Model identifier.\n */\n modelKey: string;\n\n /**\n * Item identifier.\n */\n itemKey: string;\n\n /**\n * Optional projection to control returned properties.\n */\n projection?: Projection<T>;\n};\n\n/**\n * Options for retrieving multiple specific items from the global dcupl instance.\n */\nexport type DcuplGlobalManyItemOptions<T> = {\n /**\n * Model identifier.\n */\n modelKey: string;\n\n /**\n * Array of item identifiers.\n */\n itemKeys: string[];\n\n /**\n * Optional projection to control returned properties.\n */\n projection?: Projection<T>;\n};\n\n/**\n * Options for executing queries on a list instance.\n * Combines query conditions with data retrieval options.\n *\n * @see ListQueryOptions - Type alias with clearer naming\n *\n * @example\n * ```typescript\n * const options: DcuplListQueryOptions<Product> = {\n * queries: [\n * { operator: 'gte', attribute: 'price', value: 50 }\n * ],\n * groupType: 'and',\n * count: 10\n * };\n * const results = list.catalog.query.execute(options);\n * ```\n */\nexport type DcuplListQueryOptions<T> = DcuplItemOptions<T> & DcuplQueryGroup;\n\n/**\n * Options for retrieving a specific item from a list instance.\n */\nexport type DcuplListItemOptions<T> = DcuplItemOptions<T> & {\n /**\n * Item identifier.\n */\n itemKey: string;\n};\n\n/**\n * Custom operator function for extending query capabilities.\n * Receives a query condition and list item, returns whether the item matches.\n *\n * @example\n * ```typescript\n * const customOperator: CustomOperatorFn = (query, listItem) => {\n * const value = listItem.data[query.attribute];\n * return value !== undefined;\n * };\n * ```\n */\nexport type CustomOperatorFn = (query: DcuplQuery, listItem: ListItem) => boolean;\n\n/**\n * Map of custom operator names to their implementation functions.\n */\nexport type CustomOperatorFnMap = Map<string, CustomOperatorFn>;\n\n/**\n * Internal query statement representation.\n * Used for query execution and optimization.\n *\n * @internal\n */\nexport type DcuplQueryStatement<T = any> = DcuplItemOptions<T> & {\n modelKey: string;\n groupType?: DcuplQueryType;\n queries: DcuplQuery[] | DcuplQueryGroup<DcuplQuery>[] | DcuplQueryGroup<DcuplQueryGroup<DcuplQuery>>[];\n};\n\n// ============================================================================\n// Type Aliases - Clearer naming for improved developer experience\n// ============================================================================\n\n/**\n * Query condition - represents a single filter criterion.\n * Alias for {@link DcuplQuery} with clearer naming.\n *\n * Use this to filter items based on attribute values using various operators.\n *\n * @example\n * ```typescript\n * // Equality check\n * const condition: QueryCondition = {\n * operator: 'eq',\n * attribute: 'status',\n * value: 'active'\n * };\n *\n * // Numeric comparison\n * const priceCondition: QueryCondition = {\n * operator: 'gte',\n * attribute: 'price',\n * value: 100\n * };\n * ```\n */\nexport type QueryCondition = DcuplQuery;\n\n/**\n * Query group - combines multiple conditions with AND/OR logic.\n * Alias for {@link DcuplQueryGroup} with clearer naming.\n *\n * Groups can be nested to create complex filter expressions.\n *\n * @example\n * ```typescript\n * // AND group\n * const group: QueryConditionGroup = {\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * { operator: 'gte', attribute: 'price', value: 100 }\n * ]\n * };\n *\n * // Nested groups\n * const nested: QueryConditionGroup = {\n * groupType: 'and',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' },\n * {\n * groupType: 'or',\n * queries: [\n * { operator: 'eq', attribute: 'featured', value: true },\n * { operator: 'gte', attribute: 'rating', value: 4.5 }\n * ]\n * }\n * ]\n * };\n * ```\n */\nexport type QueryConditionGroup<ChildQuery extends DcuplQuery | DcuplQueryGroup = any> =\n DcuplQueryGroup<ChildQuery>;\n\n/**\n * Query execution context for global dcupl operations.\n * Alias for {@link DcuplGlobalQueryOptions} with clearer naming.\n *\n * Combines model specification, query conditions, and data retrieval options.\n *\n * @example\n * ```typescript\n * const context: QueryExecutionContext<Product> = {\n * modelKey: 'Product',\n * queries: [\n * { operator: 'eq', attribute: 'status', value: 'active' }\n * ],\n * groupType: 'and',\n * start: 0,\n * count: 20,\n * sort: { price: 'asc' }\n * };\n * const results = dcupl.data.query(context);\n * ```\n */\nexport type QueryExecutionContext<T> = DcuplGlobalQueryOptions<T>;\n\n/**\n * Options for executing queries on a list instance.\n * Alias for {@link DcuplListQueryOptions} with clearer naming.\n *\n * Combines query conditions with pagination, projection, and sorting options.\n *\n * @example\n * ```typescript\n * const options: ListQueryOptions<Product> = {\n * queries: [\n * { operator: 'gte', attribute: 'price', value: 50 }\n * ],\n * groupType: 'and',\n * start: 0,\n * count: 10,\n * sort: { name: 'asc' }\n * };\n * const results = list.catalog.query.execute(options);\n * ```\n */\nexport type ListQueryOptions<T> = DcuplListQueryOptions<T>;\n\n/**\n * Common data retrieval options.\n * Alias for {@link DcuplItemOptions} with clearer naming.\n *\n * Controls pagination, projection, and sorting for data operations.\n *\n * @example\n * ```typescript\n * const options: ItemDataOptions<Product> = {\n * start: 0,\n * count: 20,\n * projection: { name: true, price: true },\n * sort: { price: 'desc' }\n * };\n * ```\n */\nexport type ItemDataOptions<T> = DcuplItemOptions<T>;\n"],"names":["generateObjectHash","input","stringifiedInput","fastStringify","simpleHash","keys","result","i","key","str","hash","char","perf","name","start","end","url","type","AnalyticsController","enabled","options","uniqueKey","entry","startMark","endMark","duration","roundedDuration","existingEntry","newDuration","newEntry","currentItem","newItem","context","DependencyGraph$1","DependencyGraph","from","to","policy","kind","meta","deps","dependents","node","visited","visiting","visit","dependencies","dep","pathStack","cycleStart","cyclePath","allowCycles","edges","nodesWithIncoming","n","roots","arr","res","outgoing","inDegree","levelBatches","queue","seen","nextLevel","outs","set","lvl","changed","direction","filterKinds","changedSet","matchesKind","traverseDown","impacted","depender","traverseUp","d","nodes","data","g","e","other","thisNodes","otherNodes","addedNodes","removedNodes","thisEdges","otherEdges","addedEdges","s","removedEdges","kindChanges","AttributeDependencyGraph","modelKey","attributeKey","nodeId","parts","fromId","toId","changedIds","impactedNodes","id","allImpacted","attributes","plan","sortedAttributes","a","b","processingStep","modelPlan","maxDepth","calculateDepth","maxChildDepth","depId","depth","expectedAttributes","missing","attributeKeys","attributesByModel","attributesByStep","fromNode","toNode","LOG_LEVELS","Logger","config","module","level","moduleLevel","effectiveLevel","jsonEntry","timestamp","prefix","levelIcon","contextStr","message","error","formatted","errorOrContext","ctx","childConfig","mergedConfig","logger","CacheController","cacheName","value","changedModels","entriesToRemove","shouldInvalidate","trackedAttrs","attr","modelKeys","modelSet","keyPrefix","predicate","constext","cloneDeep","err","totalRequests","hitRate","getFilterEntryId","filterItemId","getNumberConstraintUid","getDateConstraintUid","uniqBy","cb","o","map","item","groupBy","criteria","obj","generateKey","length","text","possible","removeTrailingSlash","escapeRegExp","unescapeRegExp","escapedText","hasMinimumVersion","version","minVersion","compareVersions","v1","v2","isValidVersion","v1Parts","v2Parts","num1","num2","ChangeDetector","msg","clone","shouldClone","finish","targetClass","cdRef","valueToReturn","trigger","_msg","_target","_propertyKey","descriptor","originalMethod","args","computeSuggestions","index","queryManager","suggestions","relevantSearchTerm","isRelevantAttributeIndex","relevantKey","suggestion","keyStr","searchStr","isDateInstance","isValidDate","date","FORMAT_TOKENS","buildRegexFromFormat","format","regexStr","tokenOrder","remaining","sortedTokens","matched","pattern","token","extractComponents","match","components","matchValue","tokenMatch","constructDate","parseDate","_referenceDate","regex","LRUCache","maxSize","lruKey","path","recursionStack","hasCycleRecursive","neighbors","neighbor","DeepQueryService","cacheSize","attribute","referenceAttribute","reference","hasDot","hasBracket","isDeepQuery","firstDot","firstBracket","closingBracketIdx","dotAfterBracketIdx","errors","openBrackets","closeBrackets","bracketPattern","content","invalidChars","stats","total","deepQueryService","getDeepPathValues","starIdx","beforeStar","afterStar","get","results","el","segments","current","segment","next","v","flat","getScalingFactor","numbers","maxDecimalPlaces","num","decimalPart","add","factor","divide","getAggregation","relevantData","indexMap","aggregation","numberAgg","getNumberAggregation","minMax","getMinMax","getCount","getDistinct","getGroups","count","raw","minV","maxV","values","group","notFound","relevantEntries","itemMatched","relevantEntry","sum","val","attributeValue","itemKeys","DcuplQueryError","query","DcuplQueryBuilder","deepQueryServiceInstance","groupOrQuery","groupKey","q","every","mode","queryToApply","relevantQuery","idx","queryGroup","queryToRemove","idxOfQueryToRemove","findIndex","extractAttributes","queries","projection","nestedEntry","nestedProjection","condition","checkQueries","matches","countQueries","queryBuilder","getFacets","modelData","remoteModelData","modelIndexMap","queryWithoutCurrentFilter","currentFilteredData","remoteModelDataKeys","validFacetKeys","entries","validFacetKey","buildFacetEntry","filtered","getComparator","filterItemEntries","facetEntry","fullSize","indexedValueKeys","isFilterItemEntrySelected","compareValuesAsc","ta","tb","sa","sb","sort","facetKey","IndicesController","indexKey","valueMap","keyAsString","listItem","originKey","foreignKeys","valueSet","indexName","existing","attributeValues","itemKey","oldValues","newValues","dataMap","deepKeys","shallow","deep","maxAge","now","cleaned","metadata","counts","totalAge","oldestAge","age","avgAge","pivotPathSeparator","pivot","response","parentPath","indicesCtrl","projectedData","dataForIndexing","column","row","getPivotRowItem","getPivotColumnItem","agg","globalOptions","rowItem","newOptions","columnOption","columnItem","ISO_DATE_RE","INT_RE","FLOAT_RE","DATE_LIKE_RE","coerceStringType","looksDateLike","isEmptyValue","reconcileTypes","types","inferModel","limit","coerceStrings","columns","order","rowsToScan","acc","getDcuplPropertyTypeForNativeType","properties","diagnostics","conflict","getPropertiesForData","deepOrOptions","isInt","isFloat","generateTextQuery","filterItem","comparisonType","generateBooleanQuery","generateNumberAndDateQuery","generateReferenceQuery","isValidQueryStatement","QueryManager","scriptController","globalItemService","modelParser","operator","fn","transformers","transformer","dataEntry","dataEntryValue","queryValue","arrayValueHandling","isEqual","size","rootQuery","initialDataset","filteredData","indices","indexValues","diff","differenceBy","diffMap","queryAttributes","startsWith","endsWith","isEqualWith","isMatch","entryValue","find","isInverted","matchedKeys","newQuery","anyMatch","expandedQuery","queryWithoutInvert","starIndex","newQueryGroup","cleanAttribute","arrayValue","newAttribute","earlyReturn","currentDataset","initialData","AUTHORED_RESOURCE_ID_PREFIX","DERIVED_RESOURCE_ID_PREFIX","resolveResourceId","resource","occurrence","resolveResourceIds","resources","occurrences","emitted","basis","yieldToEventLoop","resolve","channel","ScriptController","script","argsAsKeys","wrapperScript","argsAsArray","evaluateTemplate","template","variables","_","varName","parseTemplate","templateString","variableNames","PropertyTypesAsArray","NumberPropertyTypes","DatePropertyTypes","DcuplQueryOperatorsAsArray"],"mappings":"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"}
|