@open-mercato/shared 0.5.1-develop.2691.d8a0934b37 → 0.5.1-develop.2694.732417c5ec
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/lib/api/crud.js +1 -1
- package/dist/lib/api/crud.js.map +2 -2
- package/dist/lib/auth/server.js +1 -1
- package/dist/lib/auth/server.js.map +2 -2
- package/dist/lib/data/engine.js +68 -27
- package/dist/lib/data/engine.js.map +2 -2
- package/dist/lib/db/mikro.js +18 -22
- package/dist/lib/db/mikro.js.map +2 -2
- package/dist/lib/indexers/error-log.js +10 -12
- package/dist/lib/indexers/error-log.js.map +2 -2
- package/dist/lib/indexers/status-log.js +14 -16
- package/dist/lib/indexers/status-log.js.map +2 -2
- package/dist/lib/query/engine.js +220 -228
- package/dist/lib/query/engine.js.map +3 -3
- package/dist/lib/query/join-utils.js +28 -23
- package/dist/lib/query/join-utils.js.map +2 -2
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/jest.config.cjs +4 -2
- package/package.json +1 -1
- package/src/lib/api/__tests__/crud.test.ts +5 -3
- package/src/lib/api/crud.ts +1 -1
- package/src/lib/auth/__tests__/server.apiKeyCache.test.ts +10 -4
- package/src/lib/auth/server.ts +1 -1
- package/src/lib/bootstrap/types.ts +2 -2
- package/src/lib/crud/__tests__/crud-factory.test.ts +27 -17
- package/src/lib/data/engine.ts +95 -47
- package/src/lib/db/mikro.ts +26 -25
- package/src/lib/indexers/error-log.ts +23 -23
- package/src/lib/indexers/status-log.ts +36 -33
- package/src/lib/query/__tests__/engine.scope-and-or.test.ts +253 -114
- package/src/lib/query/__tests__/engine.test.ts +206 -139
- package/src/lib/query/engine.ts +306 -263
- package/src/lib/query/join-utils.ts +38 -30
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/query/engine.ts"],
|
|
4
|
-
"sourcesContent": ["import type { QueryEngine, QueryOptions, QueryResult, QueryCustomFieldSource, QueryExtensionsConfig } from './types'\nimport type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Knex } from 'knex'\nimport {\n applyJoinFilters,\n normalizeFilters,\n partitionFilters,\n resolveJoins,\n type BaseFilter,\n type NormalizedFilter,\n type ResolvedJoin,\n} from './join-utils'\nimport { resolveSearchConfig } from '../search/config'\nimport { tokenizeText } from '../search/tokenize'\nimport { runBeforeQueryPipeline, runAfterQueryPipeline, type QueryExtensionContext } from './query-extension-runner'\n\nconst entityTableCache = new Map<string, string>()\n\ntype EncryptionResolver = () => {\n decryptEntityPayload?: (entityId: EntityId, payload: Record<string, unknown>, tenantId?: string | null, organizationId?: string | null) => Promise<Record<string, unknown>>\n isEnabled?: () => boolean\n} | null\n\ntype ResolvedCustomFieldSource = {\n entityId: EntityId\n alias: string\n table: string\n recordIdExpr: any\n}\n\ntype ResultRow = Record<string, unknown>\n\nconst pluralizeBaseName = (name: string): string => {\n if (!name) return name\n if (name.endsWith('s')) return name\n if (name.endsWith('y')) return `${name.slice(0, -1)}ies`\n return `${name}s`\n}\n\nconst toPascalCase = (value: string): string => {\n return value\n .split(/[_\\s]+/)\n .filter(Boolean)\n .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))\n .join('')\n}\n\nconst candidateClassNames = (rawName: string): string[] => {\n const base = toPascalCase(rawName)\n const candidates = new Set<string>()\n if (base) candidates.add(base)\n if (base && !base.endsWith('Entity')) candidates.add(`${base}Entity`)\n return Array.from(candidates)\n}\n\nexport function resolveEntityTableName(em: EntityManager | undefined, entity: EntityId): string {\n if (entityTableCache.has(entity)) {\n return entityTableCache.get(entity)!\n }\n const parts = String(entity || '').split(':')\n const rawName = (parts[1] && parts[1].trim().length > 0) ? parts[1] : (parts[0] || '').trim()\n const metadata = (em as any)?.getMetadata?.()\n\n if (metadata && rawName) {\n const candidates = candidateClassNames(rawName)\n for (const candidate of candidates) {\n try {\n const meta = metadata.find?.(candidate)\n if (meta?.tableName) {\n const tableName = String(meta.tableName)\n entityTableCache.set(entity, tableName)\n return tableName\n }\n } catch {}\n }\n\n // Secondary lookup: search ORM metadata by candidate table names\n const modulePrefix = parts[0] ?? ''\n const candidateTables = [\n `${modulePrefix}_${rawName}`,\n pluralizeBaseName(rawName),\n `${modulePrefix}_${pluralizeBaseName(rawName)}`,\n ]\n try {\n const allMeta: any[] = metadata.getAll?.() ?? []\n for (const meta of allMeta) {\n if (meta?.tableName && candidateTables.includes(String(meta.tableName))) {\n const tableName = String(meta.tableName)\n entityTableCache.set(entity, tableName)\n return tableName\n }\n }\n } catch {}\n }\n\n const fallback = pluralizeBaseName(rawName || '')\n console.warn(\n `[QueryEngine] Could not resolve entity \"${entity}\" via ORM metadata. ` +\n `Falling back to table name \"${fallback}\". ` +\n `Ensure the entity ID segment matches the class name convention.`\n )\n entityTableCache.set(entity, fallback)\n return fallback\n}\n\nfunction buildFilterableCustomFieldJoins(\n sources: QueryCustomFieldSource[] | undefined,\n): Array<{\n alias: string\n table?: string\n entityId: EntityId\n from: { field: string }\n to: { field: string }\n type: 'left' | 'inner'\n}> {\n if (!sources || sources.length === 0) return []\n return sources.flatMap((source, index) => {\n if (!source.join) return []\n const alias = typeof source.alias === 'string' && source.alias.trim().length > 0\n ? source.alias.trim()\n : `cfs_${index}`\n return [{\n alias,\n table: source.table,\n entityId: source.entityId,\n from: { field: source.join.fromField },\n to: { field: source.join.toField },\n type: source.join.type === 'inner' ? 'inner' : 'left',\n }]\n })\n}\n\n\n// Minimal default implementation placeholder.\n// For now, only supports basic base-entity querying by table name inferred from EntityId ('<module>:<entity>' -> '<entities>') via convention.\n// Extensions and custom fields will be added iteratively.\n\nconst computeScore = (cfg: Record<string, unknown>, kind: string, entityIndex: number) => {\n const listVisibleScore = cfg.listVisible === false ? 0 : 1\n const formEditableScore = cfg.formEditable === false ? 0 : 1\n const filterableScore = cfg.filterable ? 1 : 0\n const kindScore = (() => {\n switch (kind) {\n case 'dictionary':\n return 8\n case 'relation':\n return 6\n case 'select':\n return 4\n case 'multiline':\n return 3\n case 'boolean':\n case 'integer':\n case 'float':\n return 2\n default:\n return 1\n }\n })()\n const optionsBonus = Array.isArray(cfg.options) && cfg.options.length ? 2 : 0\n const dictionaryBonus = typeof cfg.dictionaryId === 'string' && cfg.dictionaryId.trim().length ? 5 : 0\n const base = (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus\n const penalty = typeof cfg.priority === 'number' ? cfg.priority : 0\n return { base, penalty, entityIndex }\n}\n\nexport class BasicQueryEngine implements QueryEngine {\n private columnCache = new Map<string, boolean>()\n private tableCache = new Map<string, boolean>()\n private searchAliasSeq = 0\n\n constructor(\n private em: EntityManager,\n private getKnexFn?: () => any,\n private resolveEncryptionService?: EncryptionResolver,\n ) {}\n\n private getEncryptionService() {\n try {\n return this.resolveEncryptionService?.() ?? null\n } catch {\n return null\n }\n }\n\n async query<T = any>(entity: EntityId, opts: QueryOptions = {}): Promise<QueryResult<T>> {\n // --- UMES query extension: before-query pipeline ---\n const ext = opts.extensions\n let effectiveOpts = opts\n let extensionCtx: QueryExtensionContext | null = null\n const noop = { resolve: <R = unknown>(_name: string): R => { throw new Error('No DI context') } }\n\n if (ext) {\n extensionCtx = {\n entity: String(entity),\n engine: 'basic',\n tenantId: opts.tenantId ?? '',\n organizationId: opts.organizationId,\n userId: ext.userId,\n em: this.em,\n container: ext.container,\n userFeatures: ext.userFeatures,\n }\n const diCtx = ext.resolve ? { resolve: ext.resolve } : noop\n const beforeResult = await runBeforeQueryPipeline(opts, extensionCtx, diCtx)\n if (beforeResult.blocked) {\n throw new Error(beforeResult.errorMessage ?? 'Query blocked by extension subscriber')\n }\n effectiveOpts = beforeResult.query\n }\n // Strip extensions from effectiveOpts so they don't propagate to sub-queries\n const { extensions: _ext, ...coreOpts } = effectiveOpts\n opts = coreOpts\n\n // Heuristic: map '<module>:user' -> table 'users'\n const table = resolveEntityTableName(this.em, entity)\n const knex = this.getKnexFn ? this.getKnexFn() : (this.em as any).getConnection().getKnex()\n\n let q = knex(table)\n const qualify = (col: string) => `${table}.${col}`\n const orgScope = this.resolveOrganizationScope(opts)\n this.searchAliasSeq = 0\n // Require tenant scope for all queries\n if (!opts.tenantId) {\n throw new Error(\n 'QueryEngine: tenantId is now required for all queries (breaking change). ' +\n 'Please provide a tenantId in QueryOptions, e.g., query(entity, { tenantId: ... }). ' +\n 'See migration guide or documentation for details.'\n )\n }\n const skipAutoScope = opts.omitAutomaticTenantOrgScope === true\n // Optional organization filter (when present in schema)\n if (!skipAutoScope && orgScope && await this.columnExists(table, 'organization_id')) {\n q = this.applyOrganizationScope(q, qualify('organization_id'), orgScope)\n }\n // Tenant guard (required) when present in schema\n if (!skipAutoScope && await this.columnExists(table, 'tenant_id')) {\n q = q.where(qualify('tenant_id'), opts.tenantId)\n }\n // Default soft-delete guard: exclude rows with deleted_at when column exists\n if (!opts.withDeleted && await this.columnExists(table, 'deleted_at')) {\n q = q.whereNull(qualify('deleted_at'))\n }\n\n const normalizedFilters = normalizeFilters(opts.filters)\n const resolvedJoins = resolveJoins(\n table,\n [...(opts.joins ?? []), ...buildFilterableCustomFieldJoins(opts.customFieldSources)],\n (entityId) => resolveEntityTableName(this.em, entityId as any),\n )\n const joinMap = new Map<string, ResolvedJoin>()\n const aliasTables = new Map<string, string>()\n aliasTables.set(table, table)\n aliasTables.set('base', table)\n for (const join of resolvedJoins) {\n joinMap.set(join.alias, join)\n aliasTables.set(join.alias, join.table)\n }\n const { baseFilters, joinFilters } = partitionFilters(table, normalizedFilters, joinMap)\n const cfFilters = normalizedFilters.filter((filter) => String(filter.field).startsWith('cf:'))\n const searchConfig = resolveSearchConfig()\n const searchEnabled = searchConfig.enabled && await this.tableExists('search_tokens')\n const hasSearchTokens = searchEnabled\n ? await this.hasSearchTokens(String(entity), opts.tenantId ?? null, orgScope)\n : false\n const searchActive = searchEnabled && hasSearchTokens\n const joinSearchAvailability = new Map<string, boolean>()\n const searchFilters = [...baseFilters, ...cfFilters].filter((filter) => filter.op === 'like' || filter.op === 'ilike')\n if (searchFilters.length) {\n const fields = searchFilters.map((filter) => String(filter.field))\n this.logSearchDebug('search:init', {\n entity: String(entity),\n table,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n fields,\n searchEnabled,\n hasSearchTokens,\n searchActive,\n searchConfig: {\n enabled: searchConfig.enabled,\n minTokenLength: searchConfig.minTokenLength,\n enablePartials: searchConfig.enablePartials,\n hashAlgorithm: searchConfig.hashAlgorithm,\n blocklistedFields: searchConfig.blocklistedFields,\n },\n })\n if (!searchEnabled) {\n this.logSearchDebug('search:disabled', { entity: String(entity), table })\n } else if (!hasSearchTokens) {\n this.logSearchDebug('search:no-search-tokens', {\n entity: String(entity),\n table,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n })\n }\n }\n const recordIdColumn = qualify('id')\n\n const applyFilterOp = (builder: any, column: string, op: any, value: any, fieldName?: string) => {\n if (\n (op === 'like' || op === 'ilike') &&\n searchActive &&\n typeof value === 'string' &&\n fieldName\n ) {\n const tokens = tokenizeText(String(value), searchConfig)\n const hashes = tokens.hashes\n if (hashes.length) {\n const applied = this.applySearchTokens(builder, {\n entity: String(entity),\n field: fieldName,\n hashes,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n tokens: tokens.tokens,\n })\n this.logSearchDebug('search:filter', {\n entity: String(entity),\n field: fieldName,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n })\n if (applied) return builder\n } else {\n this.logSearchDebug('search:skip-empty-hashes', {\n entity: String(entity),\n field: fieldName,\n value,\n })\n }\n }\n switch (op) {\n case 'eq':\n if (value === null) builder.whereNull(column)\n else builder.where(column, value)\n break\n case 'ne':\n if (value === null) builder.whereNotNull(column)\n else builder.whereNot(column, value)\n break\n case 'gt': builder.where(column, '>', value); break\n case 'gte': builder.where(column, '>=', value); break\n case 'lt': builder.where(column, '<', value); break\n case 'lte': builder.where(column, '<=', value); break\n case 'in': builder.whereIn(column, Array.isArray(value) ? value : [value]); break\n case 'nin': builder.whereNotIn(column, Array.isArray(value) ? value : [value]); break\n case 'like': builder.where(column, 'like', value); break\n case 'ilike': builder.where(column, 'ilike', value); break\n case 'exists': value ? builder.whereNotNull(column) : builder.whereNull(column); break\n default: break\n }\n return builder\n }\n\n // `eq` is accepted alongside `like`/`ilike` so that filters against\n // encrypted joined columns (whose ciphertext cannot be compared for\n // equality in SQL) can still resolve via tokenized search. Routing\n // only applies when `searchEnabled` is true AND the joined entity has\n // search tokens installed (`searchAvailable`); for non-searchable or\n // non-encrypted columns the caller still falls through to exact SQL\n // equality via `applyFilterOp`. Note that token match is approximate \u2014\n // callers needing strict equality on encrypted fields should filter on\n // the deterministic `*_hash` column instead.\n const applyJoinFilterOp = async (\n builder: any,\n filter: { column: string; op: string; value?: unknown },\n _qualified: string,\n join: ResolvedJoin,\n ): Promise<boolean> => {\n if (!searchEnabled || !join.entityId) return false\n if (!['eq', 'like', 'ilike'].includes(filter.op)) return false\n if (typeof filter.value !== 'string' || filter.value.trim().length === 0) return false\n\n let searchAvailable = joinSearchAvailability.get(join.entityId)\n if (searchAvailable === undefined) {\n searchAvailable = await this.hasSearchTokens(join.entityId, opts.tenantId ?? null, orgScope)\n joinSearchAvailability.set(join.entityId, searchAvailable)\n }\n if (!searchAvailable) return false\n\n const tokens = tokenizeText(String(filter.value), searchConfig)\n if (!tokens.hashes.length) return false\n\n return this.applySearchTokens(builder, {\n entity: join.entityId,\n field: filter.column,\n hashes: tokens.hashes,\n recordIdColumn: `${join.alias}.id`,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n tokens: tokens.tokens,\n })\n }\n\n const regularBaseFilters = baseFilters.filter((f) => !f.orGroup)\n const orGroupFilters = baseFilters.filter((f) => f.orGroup)\n\n for (const filter of regularBaseFilters) {\n const fieldName = String(filter.field)\n let qualified = filter.qualified ?? null\n if (!qualified) {\n const column = await this.resolveBaseColumn(table, fieldName)\n if (!column) {\n q = this.applyIndexDocFilter(q, {\n entity: String(entity),\n field: fieldName,\n op: filter.op,\n value: filter.value,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n withDeleted: opts.withDeleted === true,\n searchActive,\n searchConfig,\n })\n continue\n }\n qualified = qualify(column)\n }\n applyFilterOp(q, qualified, filter.op, filter.value, fieldName)\n }\n\n // OR-grouped filters: AND within each group (one $or disjunct), OR between groups.\n if (orGroupFilters.length > 0) {\n const groups = new Map<string, typeof orGroupFilters>()\n for (const f of orGroupFilters) {\n const group = groups.get(f.orGroup!) ?? []\n group.push(f)\n groups.set(f.orGroup!, group)\n }\n const resolvedGroupFilters: Array<Array<{ qualified: string; op: string; value: unknown; fieldName: string }>> = []\n for (const [, groupFilters] of groups) {\n const resolved: Array<{ qualified: string; op: string; value: unknown; fieldName: string }> = []\n for (const filter of groupFilters) {\n const column = await this.resolveBaseColumn(table, String(filter.field))\n if (column) {\n resolved.push({\n qualified: qualify(column),\n op: filter.op,\n value: filter.value,\n fieldName: String(filter.field),\n })\n }\n }\n if (resolved.length > 0) resolvedGroupFilters.push(resolved)\n }\n if (resolvedGroupFilters.length > 0) {\n q = q.where(function (this: any) {\n const applyConjunctiveGroup = (builder: any, conjuncts: (typeof resolvedGroupFilters)[0]) => {\n for (const rf of conjuncts) {\n applyFilterOp(builder, rf.qualified, rf.op, rf.value, rf.fieldName)\n }\n }\n applyConjunctiveGroup(this, resolvedGroupFilters[0])\n for (let gi = 1; gi < resolvedGroupFilters.length; gi++) {\n this.orWhere(function (nested: any) {\n applyConjunctiveGroup(nested, resolvedGroupFilters[gi])\n })\n }\n })\n }\n }\n\n const applyAliasScopes = async (builder: any, aliasName: string) => {\n const targetTable = aliasTables.get(aliasName)\n if (!targetTable) return\n if (!skipAutoScope && orgScope && await this.columnExists(targetTable, 'organization_id')) {\n this.applyOrganizationScope(builder, `${aliasName}.organization_id`, orgScope)\n }\n if (!skipAutoScope && opts.tenantId && await this.columnExists(targetTable, 'tenant_id')) {\n builder.where(`${aliasName}.tenant_id`, opts.tenantId)\n }\n }\n await applyJoinFilters({\n knex,\n baseTable: table,\n builder: q,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase: (column) => qualify(column),\n applyAliasScope: (builder, alias) => applyAliasScopes(builder, alias),\n applyFilterOp,\n applyJoinFilterOp,\n columnExists: (tbl, column) => this.columnExists(tbl, column),\n })\n // Selection (base columns only here; cf:* handled later)\n if (opts.fields && opts.fields.length) {\n const cols = opts.fields.filter((f) => !f.startsWith('cf:'))\n if (cols.length) {\n // Qualify and alias to base names to avoid ambiguity\n const baseSelects = cols.map((c) => knex.raw('?? as ??', [qualify(c), c]))\n q = q.select(baseSelects)\n }\n } else {\n // Default to selecting only base table columns to avoid ambiguity when joining\n q = q.select(knex.raw('??.*', [table]))\n }\n\n // Resolve which custom fields to include\n const tenantId = opts.tenantId\n const sanitize = (s: string) => s.replace(/[^a-zA-Z0-9_]/g, '_')\n const cfSources = this.configureCustomFieldSources(q, table, entity, knex, opts, qualify)\n const entityIdToSource = new Map<string, ResolvedCustomFieldSource>()\n for (const source of cfSources) {\n entityIdToSource.set(String(source.entityId), source)\n }\n const requestedCustomFieldKeys = Array.isArray(opts.includeCustomFields)\n ? opts.includeCustomFields.map((key) => String(key))\n : []\n const cfKeys = new Set<string>()\n const keySource = new Map<string, ResolvedCustomFieldSource>()\n // Explicit in fields/filters\n for (const f of (opts.fields || [])) {\n if (typeof f === 'string' && f.startsWith('cf:')) cfKeys.add(f.slice(3))\n }\n for (const f of cfFilters) {\n if (typeof f.field === 'string' && f.field.startsWith('cf:')) cfKeys.add(f.field.slice(3))\n }\n if (opts.includeCustomFields === true) {\n if (entityIdToSource.size > 0) {\n const entityIdList = Array.from(entityIdToSource.keys())\n const entityOrder = new Map<string, number>()\n entityIdList.forEach((id, idx) => entityOrder.set(id, idx))\n const rows = await knex('custom_field_defs')\n .select('key', 'entity_id', 'config_json', 'kind')\n .whereIn('entity_id', entityIdList)\n .andWhere('is_active', true)\n .modify((qb: any) => {\n qb.andWhere((inner: any) => {\n inner.where({ tenant_id: tenantId }).orWhereNull('tenant_id')\n })\n })\n type CustomFieldDefinitionRow = {\n key: string\n entityId: string\n kind: string\n config: Record<string, unknown>\n }\n const sorted: CustomFieldDefinitionRow[] = rows.map((row: any) => {\n const raw = row.config_json\n let cfg: Record<string, any> = {}\n if (raw && typeof raw === 'string') {\n try { cfg = JSON.parse(raw) } catch { cfg = {} }\n } else if (raw && typeof raw === 'object') {\n cfg = raw\n }\n return {\n key: String(row.key),\n entityId: String(row.entity_id),\n kind: String(row.kind || ''),\n config: cfg,\n }\n })\n sorted.sort((a: CustomFieldDefinitionRow, b: CustomFieldDefinitionRow) => {\n const ai = entityOrder.get(a.entityId) ?? Number.MAX_SAFE_INTEGER\n const bi = entityOrder.get(b.entityId) ?? Number.MAX_SAFE_INTEGER\n if (ai !== bi) return ai - bi\n return a.key.localeCompare(b.key)\n })\n const selectedSources = new Map<string, { source: ResolvedCustomFieldSource; score: number; penalty: number; entityIndex: number }>()\n for (const row of sorted) {\n const source = entityIdToSource.get(row.entityId)\n if (!source) continue\n const cfg = row.config || {}\n const entityIndex = entityOrder.get(row.entityId) ?? Number.MAX_SAFE_INTEGER\n const scores = computeScore(cfg, row.kind, entityIndex)\n const existing = selectedSources.get(row.key)\n if (!existing || scores.base > existing.score || (scores.base === existing.score && (scores.penalty < existing.penalty || (scores.penalty === existing.penalty && scores.entityIndex < existing.entityIndex)))) {\n selectedSources.set(row.key, { source, score: scores.base, penalty: scores.penalty, entityIndex: scores.entityIndex })\n }\n cfKeys.add(row.key)\n }\n for (const [key, entry] of selectedSources.entries()) {\n keySource.set(key, entry.source)\n }\n }\n } else if (requestedCustomFieldKeys.length > 0) {\n for (const key of requestedCustomFieldKeys) cfKeys.add(key)\n }\n const unresolvedKeys = Array.from(cfKeys).filter((key) => !keySource.has(key))\n if (unresolvedKeys.length > 0 && entityIdToSource.size > 0) {\n const rows = await knex('custom_field_defs')\n .select('key', 'entity_id')\n .whereIn('entity_id', Array.from(entityIdToSource.keys()))\n .whereIn('key', unresolvedKeys)\n .andWhere('is_active', true)\n .modify((qb: any) => {\n qb.andWhere((inner: any) => {\n inner.where({ tenant_id: tenantId }).orWhereNull('tenant_id')\n })\n })\n for (const row of rows) {\n const source = entityIdToSource.get(String(row.entity_id))\n if (!source) continue\n if (!keySource.has(row.key)) keySource.set(row.key, source)\n }\n }\n\n const cfValueExprByKey: Record<string, any> = {}\n const cfSelectedAliases: string[] = []\n const cfJsonAliases = new Set<string>()\n const cfMultiAliasByAlias = new Map<string, string>()\n for (const key of cfKeys) {\n const source = keySource.get(key)\n if (!source) continue\n const entityIdForKey = source.entityId\n const recordIdExpr = source.recordIdExpr\n const sourceAliasSafe = sanitize(source.alias || 'src')\n const keyAliasSafe = sanitize(key)\n const defAlias = `cfd_${sourceAliasSafe}_${keyAliasSafe}`\n const valAlias = `cfv_${sourceAliasSafe}_${keyAliasSafe}`\n // Join definitions for kind resolution\n q = q.leftJoin({ [defAlias]: 'custom_field_defs' }, function (this: any) {\n this.on(`${defAlias}.entity_id`, '=', knex.raw('?', [entityIdForKey]))\n .andOn(`${defAlias}.key`, '=', knex.raw('?', [key]))\n .andOn(`${defAlias}.is_active`, '=', knex.raw('true'))\n .andOn(knex.raw(`(${defAlias}.tenant_id = ? OR ${defAlias}.tenant_id IS NULL)`, [tenantId]))\n })\n // Join values with record match\n q = q.leftJoin({ [valAlias]: 'custom_field_values' }, function (this: any) {\n this.on(`${valAlias}.entity_id`, '=', knex.raw('?', [entityIdForKey]))\n .andOn(`${valAlias}.field_key`, '=', knex.raw('?', [key]))\n .andOn(`${valAlias}.record_id`, '=', recordIdExpr)\n .andOn(knex.raw(`(${valAlias}.tenant_id = ? OR ${valAlias}.tenant_id IS NULL)`, [tenantId]))\n })\n // Force a common SQL type across branches to avoid Postgres CASE type conflicts\n const caseExpr = knex.raw(\n `CASE ${defAlias}.kind\n WHEN 'integer' THEN (${valAlias}.value_int)::text\n WHEN 'float' THEN (${valAlias}.value_float)::text\n WHEN 'boolean' THEN (${valAlias}.value_bool)::text\n WHEN 'multiline' THEN (${valAlias}.value_multiline)::text\n ELSE (${valAlias}.value_text)::text\n END`\n )\n cfValueExprByKey[key] = caseExpr\n const alias = sanitize(`cf:${key}`)\n // Project as aggregated to avoid duplicates when multi values exist\n if ((opts.fields || []).includes(`cf:${key}`) || opts.includeCustomFields === true || (requestedCustomFieldKeys.length > 0 && requestedCustomFieldKeys.includes(key))) {\n // Use bool_or over config_json->>multi so it's valid under GROUP BY\n const isMulti = knex.raw(`bool_or(coalesce((${defAlias}.config_json->>'multi')::boolean, false))`)\n const aggregatedArray = `array_remove(array_agg(DISTINCT ${caseExpr.toString()}), NULL)`\n const expr = `CASE WHEN ${isMulti.toString()}\n THEN to_jsonb(${aggregatedArray})\n ELSE to_jsonb(max(${caseExpr.toString()}))\n END`\n const multiAlias = `${alias}__is_multi`\n q = q.select(knex.raw(`${expr} as ??`, [alias]))\n q = q.select(knex.raw(`${isMulti.toString()} as ??`, [multiAlias]))\n cfSelectedAliases.push(alias)\n cfJsonAliases.add(alias)\n cfMultiAliasByAlias.set(alias, multiAlias)\n }\n }\n\n // Apply cf:* filters (on raw expressions)\n for (const f of cfFilters) {\n if (!f.field.startsWith('cf:')) continue\n const key = f.field.slice(3)\n const expr = cfValueExprByKey[key]\n if (!expr) continue\n if ((f.op === 'like' || f.op === 'ilike') && searchActive && typeof f.value === 'string') {\n const tokens = tokenizeText(String(f.value), searchConfig)\n const hashes = tokens.hashes\n if (hashes.length) {\n const applied = this.applySearchTokens(q, {\n entity: String(entity),\n field: f.field,\n hashes,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n tokens: tokens.tokens,\n })\n this.logSearchDebug('search:cf-filter', {\n entity: String(entity),\n field: f.field,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n })\n if (applied) continue\n } else {\n this.logSearchDebug('search:cf-skip-empty-hashes', {\n entity: String(entity),\n field: f.field,\n value: f.value,\n })\n }\n }\n switch (f.op) {\n case 'eq': q = q.where(expr, '=', f.value); break\n case 'ne': q = q.where(expr, '!=', f.value); break\n case 'gt': q = q.where(expr, '>', f.value); break\n case 'gte': q = q.where(expr, '>=', f.value); break\n case 'lt': q = q.where(expr, '<', f.value); break\n case 'lte': q = q.where(expr, '<=', f.value); break\n case 'in': q = q.whereIn(expr as any, f.value ?? []); break\n case 'nin': q = q.whereNotIn(expr as any, f.value ?? []); break\n case 'like': q = q.where(expr, 'like', f.value); break\n case 'ilike': q = q.where(expr, 'ilike', f.value); break\n case 'exists': f.value ? q = q.whereNotNull(expr) : q = q.whereNull(expr); break\n }\n }\n\n // Entity extensions joins (no selection yet; enables future filters/projections)\n if (opts.includeExtensions) {\n const { getModules } = await import('@open-mercato/shared/lib/i18n/server')\n const allMods = getModules() as any[]\n const allExts = allMods.flatMap((m) => (m as any).entityExtensions || [])\n const exts = allExts.filter((e: any) => e.base === entity)\n const chosen = Array.isArray(opts.includeExtensions)\n ? exts.filter((e: any) => (opts.includeExtensions as string[]).includes(e.extension))\n : exts\n for (const e of chosen) {\n const [, extName] = (e.extension as string).split(':')\n const extTable = extName.endsWith('s') ? extName : `${extName}s`\n const alias = `ext_${sanitize(extName)}`\n q = q.leftJoin({ [alias]: extTable }, function (this: any) {\n this.on(`${alias}.${e.join.extensionKey}`, '=', knex.raw('??', [`${table}.${e.join.baseKey}`]))\n })\n }\n }\n\n // Sorting: base fields and cf:* (use aggregated alias for cf)\n for (const s of opts.sort || []) {\n if (s.field.startsWith('cf:')) {\n const key = s.field.slice(3)\n const alias = sanitize(`cf:${key}`)\n // Ensure included in projection to sort by\n if (!cfSelectedAliases.includes(alias)) {\n const expr = cfValueExprByKey[key]\n if (expr) {\n q = q.select(knex.raw(`max(${expr.toString()}) as ??`, [alias]))\n cfSelectedAliases.push(alias)\n }\n }\n q = q.orderBy(alias, s.dir ?? 'asc')\n } else {\n const column = await this.resolveBaseColumn(table, s.field)\n if (!column) continue\n q = q.orderBy(qualify(column), s.dir ?? 'asc')\n }\n }\n\n // Pagination\n const page = opts.page?.page ?? 1\n const pageSize = opts.page?.pageSize ?? 20\n // Deduplicate if we joined CFs or extensions by grouping on base id\n if ((opts.includeExtensions && (Array.isArray(opts.includeExtensions) ? (opts.includeExtensions.length > 0) : true)) || Object.keys(cfValueExprByKey).length > 0) {\n q = q.groupBy(`${table}.id`)\n }\n const countClone: any = q.clone()\n if (typeof countClone.clearSelect === 'function') countClone.clearSelect()\n if (typeof countClone.clearOrder === 'function') countClone.clearOrder()\n if (typeof countClone.clearGroup === 'function') countClone.clearGroup()\n const countRow = await countClone\n .countDistinct(`${table}.id as count`)\n .first()\n const total = Number((countRow as any)?.count ?? 0)\n const items = await q.limit(pageSize).offset((page - 1) * pageSize)\n\n if (cfJsonAliases.size > 0) {\n for (const row of items as any[]) {\n for (const alias of cfJsonAliases) {\n const multiAlias = cfMultiAliasByAlias.get(alias)\n const isMulti = multiAlias ? Boolean(row[multiAlias]) : false\n let raw = row[alias]\n if (typeof raw === 'string') {\n try { raw = JSON.parse(raw) } catch { /* ignore malformed json */ }\n }\n if (isMulti) {\n if (raw == null) row[alias] = []\n else if (Array.isArray(raw)) row[alias] = raw\n else row[alias] = [raw]\n } else {\n if (Array.isArray(raw)) row[alias] = raw.length > 0 ? raw[0] : null\n else row[alias] = raw\n }\n if (multiAlias) delete row[multiAlias]\n }\n }\n }\n\n const svc = this.getEncryptionService()\n const decryptPayload =\n svc?.decryptEntityPayload?.bind(svc) as\n | ((\n entityId: EntityId,\n payload: Record<string, unknown>,\n tenantId: string | null,\n organizationId: string | null,\n ) => Promise<Record<string, unknown>>)\n | null\n let decryptedItems = items\n if (decryptPayload) {\n const fallbackOrgId =\n opts.organizationId\n ?? (Array.isArray(opts.organizationIds) && opts.organizationIds.length === 1 ? opts.organizationIds[0] : null)\n decryptedItems = await Promise.all(\n (items as any[]).map(async (item) => {\n try {\n const decrypted = await decryptPayload(\n entity,\n item,\n item?.tenant_id ?? item?.tenantId ?? opts.tenantId ?? null,\n item?.organization_id ?? item?.organizationId ?? fallbackOrgId ?? null,\n )\n return { ...item, ...decrypted }\n } catch (err) {\n console.error('QueryEngine: error decrypting entity payload', err);\n return item\n }\n })\n )\n }\n\n let queryResult: QueryResult<T> = { items: decryptedItems, page, pageSize, total }\n\n // --- UMES query extension: after-query pipeline ---\n if (ext && extensionCtx) {\n const diCtx = ext.resolve ? { resolve: ext.resolve } : noop\n queryResult = await runAfterQueryPipeline(\n queryResult as QueryResult<Record<string, unknown>>,\n opts,\n extensionCtx,\n diCtx,\n ) as QueryResult<T>\n }\n\n return queryResult\n }\n\n private async resolveBaseColumn(table: string, field: string): Promise<string | null> {\n if (await this.columnExists(table, field)) return field\n if (field === 'organization_id' && await this.columnExists(table, 'id')) return 'id'\n return null\n }\n\n private async columnExists(table: string, column: string): Promise<boolean> {\n const key = `${table}.${column}`\n if (this.columnCache.has(key)) {\n const cached = this.columnCache.get(key)\n if (cached === true) return true\n this.columnCache.delete(key)\n }\n const knex = this.getKnexFn ? this.getKnexFn() : (this.em as any).getConnection().getKnex()\n const exists = await knex('information_schema.columns')\n .where({ table_name: table, column_name: column })\n .first()\n const present = !!exists\n if (present) this.columnCache.set(key, true)\n else this.columnCache.delete(key)\n return present\n }\n\n private async tableExists(table: string): Promise<boolean> {\n if (this.tableCache.has(table)) return this.tableCache.get(table) ?? false\n const knex = this.getKnexFn ? this.getKnexFn() : (this.em as any).getConnection().getKnex()\n const exists = await knex('information_schema.tables')\n .where({ table_name: table })\n .first()\n const present = !!exists\n this.tableCache.set(table, present)\n return present\n }\n\n private async hasSearchTokens(\n entity: string,\n tenantId: string | null,\n orgScope?: { ids: string[]; includeNull: boolean } | null\n ): Promise<boolean> {\n try {\n const knex = this.getKnexFn ? this.getKnexFn() : (this.em as any).getConnection().getKnex()\n const query = knex('search_tokens').select(1).where('entity_type', entity).limit(1)\n if (tenantId !== undefined) {\n query.andWhereRaw('tenant_id is not distinct from ?', [tenantId])\n }\n if (orgScope) {\n this.applyOrganizationScope(query as any, 'search_tokens.organization_id', orgScope)\n }\n const row = await query.first()\n return !!row\n } catch (err) {\n this.logSearchDebug('search:has-tokens-error', {\n entity,\n tenantId,\n organizationScope: orgScope,\n error: err instanceof Error ? err.message : String(err),\n })\n return false\n }\n }\n\n private applySearchTokens<TRecord extends ResultRow, TResult>(\n q: Knex.QueryBuilder<TRecord, TResult>,\n opts: {\n entity: string\n field: string\n hashes: string[]\n recordIdColumn: string\n tenantId?: string | null\n organizationScope?: { ids: string[]; includeNull: boolean } | null\n combineWith?: 'and' | 'or'\n tokens?: string[]\n }\n ): boolean {\n if (!opts.hashes.length) {\n this.logSearchDebug('search:skip-no-hashes', {\n entity: opts.entity,\n field: opts.field,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n })\n return false\n }\n const alias = `st_${this.searchAliasSeq++}`\n const combineWith = opts.combineWith === 'or' ? 'orWhereExists' : 'whereExists'\n const engine = this\n this.logSearchDebug('search:apply-search-tokens', {\n entity: opts.entity,\n field: opts.field,\n alias,\n tokenCount: opts.hashes.length,\n tokens: opts.tokens,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n combineWith: opts.combineWith ?? 'and',\n })\n ;(q as any)[combineWith](function (this: Knex.QueryBuilder) {\n this.select(1)\n .from({ [alias]: 'search_tokens' })\n .where(`${alias}.entity_type`, opts.entity)\n .andWhere(`${alias}.field`, opts.field)\n .andWhereRaw('?? = ??::text', [`${alias}.entity_id`, opts.recordIdColumn])\n .whereIn(`${alias}.token_hash`, opts.hashes)\n .groupBy(`${alias}.entity_id`, `${alias}.field`)\n .havingRaw(`count(distinct ${alias}.token_hash) >= ?`, [opts.hashes.length])\n if (opts.tenantId !== undefined) {\n this.andWhereRaw(`${alias}.tenant_id is not distinct from ?`, [opts.tenantId ?? null])\n }\n if (opts.organizationScope) {\n engine.applyOrganizationScope(this as any, `${alias}.organization_id`, opts.organizationScope)\n }\n })\n return true\n }\n\n private applyIndexDocFilter<TRecord extends ResultRow, TResult>(\n q: Knex.QueryBuilder<TRecord, TResult>,\n opts: {\n entity: string\n field: string\n op: NormalizedFilter['op']\n value: unknown\n recordIdColumn: string\n tenantId?: string | null\n organizationScope?: { ids: string[]; includeNull: boolean } | null\n withDeleted: boolean\n searchActive: boolean\n searchConfig: ReturnType<typeof resolveSearchConfig>\n }\n ): Knex.QueryBuilder<TRecord, TResult> {\n if ((opts.op === 'like' || opts.op === 'ilike') && opts.searchActive && typeof opts.value === 'string') {\n const tokens = tokenizeText(String(opts.value), opts.searchConfig)\n const hashes = tokens.hashes\n if (hashes.length) {\n const applied = this.applySearchTokens(q, {\n entity: opts.entity,\n field: opts.field,\n hashes,\n recordIdColumn: opts.recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n tokens: tokens.tokens,\n })\n this.logSearchDebug('search:index-doc-filter', {\n entity: opts.entity,\n field: opts.field,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n })\n if (applied) return q\n } else {\n this.logSearchDebug('search:index-doc-skip-empty-hashes', {\n entity: opts.entity,\n field: opts.field,\n value: opts.value,\n })\n }\n return q\n }\n\n const knex = this.getKnexFn ? this.getKnexFn() : (this.em as any).getConnection().getKnex()\n const alias = `ei_${this.searchAliasSeq++}`\n const engine = this\n return q.whereExists(function (this: Knex.QueryBuilder) {\n this.select(1)\n .from({ [alias]: 'entity_indexes' })\n .where(`${alias}.entity_type`, opts.entity)\n .andWhereRaw('?? = ??::text', [`${alias}.entity_id`, opts.recordIdColumn])\n\n if (opts.tenantId !== undefined) {\n this.andWhereRaw(`${alias}.tenant_id is not distinct from ?`, [opts.tenantId ?? null])\n }\n if (opts.organizationScope) {\n engine.applyOrganizationScope(this as any, `${alias}.organization_id`, opts.organizationScope)\n }\n if (!opts.withDeleted) {\n this.whereNull(`${alias}.deleted_at`)\n }\n\n const text = knex.raw(`(${alias}.doc ->> ?)`, [opts.field])\n switch (opts.op) {\n case 'eq':\n this.where(text, '=', opts.value as Knex.Value)\n break\n case 'ne':\n this.where(text, '!=', opts.value as Knex.Value)\n break\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = opts.op === 'gt' ? '>' : opts.op === 'gte' ? '>=' : opts.op === 'lt' ? '<' : '<='\n this.where(text, operator, opts.value as Knex.Value)\n break\n }\n case 'in':\n this.whereIn(text as any, Array.isArray(opts.value) ? opts.value : [opts.value])\n break\n case 'nin':\n this.whereNotIn(text as any, Array.isArray(opts.value) ? opts.value : [opts.value])\n break\n case 'like':\n this.where(text, 'like', opts.value as Knex.Value)\n break\n case 'ilike':\n this.where(text, 'ilike', opts.value as Knex.Value)\n break\n case 'exists':\n opts.value ? this.whereNotNull(text as any) : this.whereNull(text as any)\n break\n default:\n break\n }\n })\n }\n\n private configureCustomFieldSources(\n q: any,\n baseTable: string,\n baseEntity: EntityId,\n knex: any,\n opts: QueryOptions,\n qualify: (column: string) => string\n ): ResolvedCustomFieldSource[] {\n const sources: ResolvedCustomFieldSource[] = [\n {\n entityId: baseEntity,\n alias: 'base',\n table: baseTable,\n recordIdExpr: knex.raw('??::text', [`${baseTable}.id`]),\n },\n ]\n const extras: QueryCustomFieldSource[] = opts.customFieldSources ?? []\n extras.forEach((srcOpt, index) => {\n const joinTable = srcOpt.table ?? resolveEntityTableName(this.em, srcOpt.entityId)\n const alias = srcOpt.alias ?? `cfs_${index}`\n const join = srcOpt.join\n if (!join) {\n throw new Error(`QueryEngine: customFieldSources entry for ${String(srcOpt.entityId)} requires a join configuration`)\n }\n const joinArgs = { [alias]: joinTable }\n const joinCallback = function (this: any) {\n this.on(`${alias}.${join.toField}`, '=', qualify(join.fromField))\n }\n const joinType = join.type ?? 'left'\n if (joinType === 'inner') q.join(joinArgs, joinCallback)\n else q.leftJoin(joinArgs, joinCallback)\n const recordColumn = srcOpt.recordIdColumn ?? 'id'\n sources.push({\n entityId: srcOpt.entityId,\n alias,\n table: joinTable,\n recordIdExpr: knex.raw('??::text', [`${alias}.${recordColumn}`]),\n })\n })\n return sources\n }\n\n private logSearchDebug(event: string, payload: Record<string, unknown>) {\n try {\n console.info('[query:search]', event, JSON.stringify(payload))\n } catch {\n console.info('[query:search]', event, payload)\n }\n }\n\n private resolveOrganizationScope(opts: QueryOptions): { ids: string[]; includeNull: boolean } | null {\n if (opts.organizationIds !== undefined) {\n const raw = (opts.organizationIds ?? []).map((id) => (typeof id === 'string' ? id.trim() : id))\n const includeNull = raw.some((id) => id == null || id === '')\n const ids = raw.filter((id): id is string => typeof id === 'string' && id.length > 0)\n return { ids: Array.from(new Set(ids)), includeNull }\n }\n if (typeof opts.organizationId === 'string' && opts.organizationId.trim().length > 0) {\n return { ids: [opts.organizationId], includeNull: false }\n }\n return null\n }\n\n private applyOrganizationScope(q: any, column: string, scope: { ids: string[]; includeNull: boolean }): any {\n if (!scope) return q\n if (scope.ids.length === 0 && !scope.includeNull) {\n return q.whereRaw('1 = 0')\n }\n return q.where((builder: any) => {\n let applied = false\n if (scope.ids.length > 0) {\n builder.whereIn(column as any, scope.ids)\n applied = true\n }\n if (scope.includeNull) {\n if (applied) builder.orWhereNull(column)\n else builder.whereNull(column)\n applied = true\n }\n if (!applied) builder.whereRaw('1 = 0')\n })\n }\n\n}\n"],
|
|
5
|
-
"mappings": "AAIA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB,6BAAyD;AAE1F,MAAM,mBAAmB,oBAAI,IAAoB;AAgBjD,MAAM,oBAAoB,CAAC,SAAyB;AAClD,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,SAAS,GAAG,EAAG,QAAO;AAC/B,MAAI,KAAK,SAAS,GAAG,EAAG,QAAO,GAAG,KAAK,MAAM,GAAG,EAAE,CAAC;AACnD,SAAO,GAAG,IAAI;AAChB;AAEA,MAAM,eAAe,CAAC,UAA0B;AAC9C,SAAO,MACJ,MAAM,QAAQ,EACd,OAAO,OAAO,EACd,IAAI,CAAC,YAAY,QAAQ,OAAO,CAAC,EAAE,YAAY,IAAI,QAAQ,MAAM,CAAC,CAAC,EACnE,KAAK,EAAE;AACZ;AAEA,MAAM,sBAAsB,CAAC,YAA8B;AACzD,QAAM,OAAO,aAAa,OAAO;AACjC,QAAM,aAAa,oBAAI,IAAY;AACnC,MAAI,KAAM,YAAW,IAAI,IAAI;AAC7B,MAAI,QAAQ,CAAC,KAAK,SAAS,QAAQ,EAAG,YAAW,IAAI,GAAG,IAAI,QAAQ;AACpE,SAAO,MAAM,KAAK,UAAU;AAC9B;AAEO,SAAS,uBAAuB,IAA+B,QAA0B;AAC9F,MAAI,iBAAiB,IAAI,MAAM,GAAG;AAChC,WAAO,iBAAiB,IAAI,MAAM;AAAA,EACpC;AACA,QAAM,QAAQ,OAAO,UAAU,EAAE,EAAE,MAAM,GAAG;AAC5C,QAAM,UAAW,MAAM,CAAC,KAAK,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,IAAK,MAAM,CAAC,KAAK,MAAM,CAAC,KAAK,IAAI,KAAK;AAC5F,QAAM,WAAY,IAAY,cAAc;AAE5C,MAAI,YAAY,SAAS;AACvB,UAAM,aAAa,oBAAoB,OAAO;AAC9C,eAAW,aAAa,YAAY;AAClC,UAAI;AACF,cAAM,OAAO,SAAS,OAAO,SAAS;AACtC,YAAI,MAAM,WAAW;AACnB,gBAAM,YAAY,OAAO,KAAK,SAAS;AACvC,2BAAiB,IAAI,QAAQ,SAAS;AACtC,iBAAO;AAAA,QACT;AAAA,MACF,QAAQ;AAAA,MAAC;AAAA,IACX;AAGA,UAAM,eAAe,MAAM,CAAC,KAAK;AACjC,UAAM,kBAAkB;AAAA,MACtB,GAAG,YAAY,IAAI,OAAO;AAAA,MAC1B,kBAAkB,OAAO;AAAA,MACzB,GAAG,YAAY,IAAI,kBAAkB,OAAO,CAAC;AAAA,IAC/C;AACA,QAAI;AACF,YAAM,UAAiB,SAAS,SAAS,KAAK,CAAC;AAC/C,iBAAW,QAAQ,SAAS;AAC1B,YAAI,MAAM,aAAa,gBAAgB,SAAS,OAAO,KAAK,SAAS,CAAC,GAAG;AACvE,gBAAM,YAAY,OAAO,KAAK,SAAS;AACvC,2BAAiB,IAAI,QAAQ,SAAS;AACtC,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,QAAM,WAAW,kBAAkB,WAAW,EAAE;AAChD,UAAQ;AAAA,IACN,2CAA2C,MAAM,mDAClB,QAAQ;AAAA,EAEzC;AACA,mBAAiB,IAAI,QAAQ,QAAQ;AACrC,SAAO;AACT;AAEA,SAAS,gCACP,SAQC;AACD,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO,CAAC;AAC9C,SAAO,QAAQ,QAAQ,CAAC,QAAQ,UAAU;AACxC,QAAI,CAAC,OAAO,KAAM,QAAO,CAAC;AAC1B,UAAM,QAAQ,OAAO,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,EAAE,SAAS,IAC3E,OAAO,MAAM,KAAK,IAClB,OAAO,KAAK;AAChB,WAAO,CAAC;AAAA,MACN;AAAA,MACA,OAAO,OAAO;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,MAAM,EAAE,OAAO,OAAO,KAAK,UAAU;AAAA,MACrC,IAAI,EAAE,OAAO,OAAO,KAAK,QAAQ;AAAA,MACjC,MAAM,OAAO,KAAK,SAAS,UAAU,UAAU;AAAA,IACjD,CAAC;AAAA,EACH,CAAC;AACH;AAOA,MAAM,eAAe,CAAC,KAA8B,MAAc,gBAAwB;AACxF,QAAM,mBAAmB,IAAI,gBAAgB,QAAQ,IAAI;AACzD,QAAM,oBAAoB,IAAI,iBAAiB,QAAQ,IAAI;AAC3D,QAAM,kBAAkB,IAAI,aAAa,IAAI;AAC7C,QAAM,aAAa,MAAM;AACvB,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF,GAAG;AACH,QAAM,eAAe,MAAM,QAAQ,IAAI,OAAO,KAAK,IAAI,QAAQ,SAAS,IAAI;AAC5E,QAAM,kBAAkB,OAAO,IAAI,iBAAiB,YAAY,IAAI,aAAa,KAAK,EAAE,SAAS,IAAI;AACrG,QAAM,OAAQ,mBAAmB,KAAO,oBAAoB,IAAM,kBAAkB,IAAK,YAAY,eAAe;AACpH,QAAM,UAAU,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;AAClE,SAAO,EAAE,MAAM,SAAS,YAAY;AACtC;AAEO,MAAM,iBAAwC;AAAA,EAKnD,YACU,IACA,WACA,0BACR;AAHQ;AACA;AACA;AAPV,SAAQ,cAAc,oBAAI,IAAqB;AAC/C,SAAQ,aAAa,oBAAI,IAAqB;AAC9C,SAAQ,iBAAiB;AAAA,EAMtB;AAAA,EAEK,uBAAuB;AAC7B,QAAI;AACF,aAAO,KAAK,2BAA2B,KAAK;AAAA,IAC9C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,MAAe,QAAkB,OAAqB,CAAC,GAA4B;AAEvF,UAAM,MAAM,KAAK;AACjB,QAAI,gBAAgB;AACpB,QAAI,eAA6C;AACjD,UAAM,OAAO,EAAE,SAAS,CAAc,UAAqB;AAAE,YAAM,IAAI,MAAM,eAAe;AAAA,IAAE,EAAE;AAEhG,QAAI,KAAK;AACP,qBAAe;AAAA,QACb,QAAQ,OAAO,MAAM;AAAA,QACrB,QAAQ;AAAA,QACR,UAAU,KAAK,YAAY;AAAA,QAC3B,gBAAgB,KAAK;AAAA,QACrB,QAAQ,IAAI;AAAA,QACZ,IAAI,KAAK;AAAA,QACT,WAAW,IAAI;AAAA,QACf,cAAc,IAAI;AAAA,MACpB;AACA,YAAM,QAAQ,IAAI,UAAU,EAAE,SAAS,IAAI,QAAQ,IAAI;AACvD,YAAM,eAAe,MAAM,uBAAuB,MAAM,cAAc,KAAK;AAC3E,UAAI,aAAa,SAAS;AACxB,cAAM,IAAI,MAAM,aAAa,gBAAgB,uCAAuC;AAAA,MACtF;AACA,sBAAgB,aAAa;AAAA,IAC/B;AAEA,UAAM,EAAE,YAAY,MAAM,GAAG,SAAS,IAAI;AAC1C,WAAO;AAGP,UAAM,QAAQ,uBAAuB,KAAK,IAAI,MAAM;AACpD,UAAM,OAAO,KAAK,YAAY,KAAK,UAAU,IAAK,KAAK,GAAW,cAAc,EAAE,QAAQ;AAE1F,QAAI,IAAI,KAAK,KAAK;AAClB,UAAM,UAAU,CAAC,QAAgB,GAAG,KAAK,IAAI,GAAG;AAChD,UAAM,WAAW,KAAK,yBAAyB,IAAI;AACnD,SAAK,iBAAiB;AAEtB,QAAI,CAAC,KAAK,UAAU;AAClB,YAAM,IAAI;AAAA,QACR;AAAA,MAGF;AAAA,IACF;AACA,UAAM,gBAAgB,KAAK,gCAAgC;AAE3D,QAAI,CAAC,iBAAiB,YAAY,MAAM,KAAK,aAAa,OAAO,iBAAiB,GAAG;AACnF,UAAI,KAAK,uBAAuB,GAAG,QAAQ,iBAAiB,GAAG,QAAQ;AAAA,IACzE;AAEA,QAAI,CAAC,iBAAiB,MAAM,KAAK,aAAa,OAAO,WAAW,GAAG;AACjE,UAAI,EAAE,MAAM,QAAQ,WAAW,GAAG,KAAK,QAAQ;AAAA,IACjD;AAEA,QAAI,CAAC,KAAK,eAAe,MAAM,KAAK,aAAa,OAAO,YAAY,GAAG;AACrE,UAAI,EAAE,UAAU,QAAQ,YAAY,CAAC;AAAA,IACvC;AAEA,UAAM,oBAAoB,iBAAiB,KAAK,OAAO;AACvD,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA,CAAC,GAAI,KAAK,SAAS,CAAC,GAAI,GAAG,gCAAgC,KAAK,kBAAkB,CAAC;AAAA,MACnF,CAAC,aAAa,uBAAuB,KAAK,IAAI,QAAe;AAAA,IAC/D;AACA,UAAM,UAAU,oBAAI,IAA0B;AAC9C,UAAM,cAAc,oBAAI,IAAoB;AAC5C,gBAAY,IAAI,OAAO,KAAK;AAC5B,gBAAY,IAAI,QAAQ,KAAK;AAC7B,eAAW,QAAQ,eAAe;AAChC,cAAQ,IAAI,KAAK,OAAO,IAAI;AAC5B,kBAAY,IAAI,KAAK,OAAO,KAAK,KAAK;AAAA,IACxC;AACA,UAAM,EAAE,aAAa,YAAY,IAAI,iBAAiB,OAAO,mBAAmB,OAAO;AACvF,UAAM,YAAY,kBAAkB,OAAO,CAAC,WAAW,OAAO,OAAO,KAAK,EAAE,WAAW,KAAK,CAAC;AAC7F,UAAM,eAAe,oBAAoB;AACzC,UAAM,gBAAgB,aAAa,WAAW,MAAM,KAAK,YAAY,eAAe;AACpF,UAAM,kBAAkB,gBACpB,MAAM,KAAK,gBAAgB,OAAO,MAAM,GAAG,KAAK,YAAY,MAAM,QAAQ,IAC1E;AACJ,UAAM,eAAe,iBAAiB;AACtC,UAAM,yBAAyB,oBAAI,IAAqB;AACxD,UAAM,gBAAgB,CAAC,GAAG,aAAa,GAAG,SAAS,EAAE,OAAO,CAAC,WAAW,OAAO,OAAO,UAAU,OAAO,OAAO,OAAO;AACrH,QAAI,cAAc,QAAQ;AACxB,YAAM,SAAS,cAAc,IAAI,CAAC,WAAW,OAAO,OAAO,KAAK,CAAC;AACjE,WAAK,eAAe,eAAe;AAAA,QACjC,QAAQ,OAAO,MAAM;AAAA,QACrB;AAAA,QACA,UAAU,KAAK,YAAY;AAAA,QAC3B,mBAAmB;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc;AAAA,UACZ,SAAS,aAAa;AAAA,UACtB,gBAAgB,aAAa;AAAA,UAC7B,gBAAgB,aAAa;AAAA,UAC7B,eAAe,aAAa;AAAA,UAC5B,mBAAmB,aAAa;AAAA,QAClC;AAAA,MACF,CAAC;AACD,UAAI,CAAC,eAAe;AAClB,aAAK,eAAe,mBAAmB,EAAE,QAAQ,OAAO,MAAM,GAAG,MAAM,CAAC;AAAA,MAC1E,WAAW,CAAC,iBAAiB;AAC3B,aAAK,eAAe,2BAA2B;AAAA,UAC7C,QAAQ,OAAO,MAAM;AAAA,UACrB;AAAA,UACA,UAAU,KAAK,YAAY;AAAA,UAC3B,mBAAmB;AAAA,QACrB,CAAC;AAAA,MACH;AAAA,IACF;AACA,UAAM,iBAAiB,QAAQ,IAAI;AAEnC,UAAM,gBAAgB,CAAC,SAAc,QAAgB,IAAS,OAAY,cAAuB;AAC/F,WACG,OAAO,UAAU,OAAO,YACzB,gBACA,OAAO,UAAU,YACjB,WACA;AACA,cAAM,SAAS,aAAa,OAAO,KAAK,GAAG,YAAY;AACvD,cAAM,SAAS,OAAO;AACtB,YAAI,OAAO,QAAQ;AACjB,gBAAM,UAAU,KAAK,kBAAkB,SAAS;AAAA,YAC9C,QAAQ,OAAO,MAAM;AAAA,YACrB,OAAO;AAAA,YACP;AAAA,YACA;AAAA,YACA,UAAU,KAAK,YAAY;AAAA,YAC3B,mBAAmB;AAAA,YACnB,QAAQ,OAAO;AAAA,UACjB,CAAC;AACD,eAAK,eAAe,iBAAiB;AAAA,YACnC,QAAQ,OAAO,MAAM;AAAA,YACrB,OAAO;AAAA,YACP,QAAQ,OAAO;AAAA,YACf;AAAA,YACA;AAAA,YACA,UAAU,KAAK,YAAY;AAAA,YAC3B,mBAAmB;AAAA,UACrB,CAAC;AACD,cAAI,QAAS,QAAO;AAAA,QACtB,OAAO;AACL,eAAK,eAAe,4BAA4B;AAAA,YAC9C,QAAQ,OAAO,MAAM;AAAA,YACrB,OAAO;AAAA,YACP;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AACA,cAAQ,IAAI;AAAA,QACV,KAAK;AACH,cAAI,UAAU,KAAM,SAAQ,UAAU,MAAM;AAAA,cACvC,SAAQ,MAAM,QAAQ,KAAK;AAChC;AAAA,QACF,KAAK;AACH,cAAI,UAAU,KAAM,SAAQ,aAAa,MAAM;AAAA,cAC1C,SAAQ,SAAS,QAAQ,KAAK;AACnC;AAAA,QACF,KAAK;AAAM,kBAAQ,MAAM,QAAQ,KAAK,KAAK;AAAG;AAAA,QAC9C,KAAK;AAAO,kBAAQ,MAAM,QAAQ,MAAM,KAAK;AAAG;AAAA,QAChD,KAAK;AAAM,kBAAQ,MAAM,QAAQ,KAAK,KAAK;AAAG;AAAA,QAC9C,KAAK;AAAO,kBAAQ,MAAM,QAAQ,MAAM,KAAK;AAAG;AAAA,QAChD,KAAK;AAAM,kBAAQ,QAAQ,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;AAAG;AAAA,QAC5E,KAAK;AAAO,kBAAQ,WAAW,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;AAAG;AAAA,QAChF,KAAK;AAAQ,kBAAQ,MAAM,QAAQ,QAAQ,KAAK;AAAG;AAAA,QACnD,KAAK;AAAS,kBAAQ,MAAM,QAAQ,SAAS,KAAK;AAAG;AAAA,QACrD,KAAK;AAAU,kBAAQ,QAAQ,aAAa,MAAM,IAAI,QAAQ,UAAU,MAAM;AAAG;AAAA,QACjF;AAAS;AAAA,MACX;AACA,aAAO;AAAA,IACT;AAWA,UAAM,oBAAoB,OACxB,SACA,QACA,YACA,SACqB;AACrB,UAAI,CAAC,iBAAiB,CAAC,KAAK,SAAU,QAAO;AAC7C,UAAI,CAAC,CAAC,MAAM,QAAQ,OAAO,EAAE,SAAS,OAAO,EAAE,EAAG,QAAO;AACzD,UAAI,OAAO,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,EAAE,WAAW,EAAG,QAAO;AAEjF,UAAI,kBAAkB,uBAAuB,IAAI,KAAK,QAAQ;AAC9D,UAAI,oBAAoB,QAAW;AACjC,0BAAkB,MAAM,KAAK,gBAAgB,KAAK,UAAU,KAAK,YAAY,MAAM,QAAQ;AAC3F,+BAAuB,IAAI,KAAK,UAAU,eAAe;AAAA,MAC3D;AACA,UAAI,CAAC,gBAAiB,QAAO;AAE7B,YAAM,SAAS,aAAa,OAAO,OAAO,KAAK,GAAG,YAAY;AAC9D,UAAI,CAAC,OAAO,OAAO,OAAQ,QAAO;AAElC,aAAO,KAAK,kBAAkB,SAAS;AAAA,QACrC,QAAQ,KAAK;AAAA,QACb,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,gBAAgB,GAAG,KAAK,KAAK;AAAA,QAC7B,UAAU,KAAK,YAAY;AAAA,QAC3B,mBAAmB;AAAA,QACnB,QAAQ,OAAO;AAAA,MACjB,CAAC;AAAA,IACH;AAEA,UAAM,qBAAqB,YAAY,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO;AAC/D,UAAM,iBAAiB,YAAY,OAAO,CAAC,MAAM,EAAE,OAAO;AAE1D,eAAW,UAAU,oBAAoB;AACvC,YAAM,YAAY,OAAO,OAAO,KAAK;AACrC,UAAI,YAAY,OAAO,aAAa;AACpC,UAAI,CAAC,WAAW;AACd,cAAM,SAAS,MAAM,KAAK,kBAAkB,OAAO,SAAS;AAC5D,YAAI,CAAC,QAAQ;AACX,cAAI,KAAK,oBAAoB,GAAG;AAAA,YAC9B,QAAQ,OAAO,MAAM;AAAA,YACrB,OAAO;AAAA,YACP,IAAI,OAAO;AAAA,YACX,OAAO,OAAO;AAAA,YACd;AAAA,YACA,UAAU,KAAK,YAAY;AAAA,YAC3B,mBAAmB;AAAA,YACnB,aAAa,KAAK,gBAAgB;AAAA,YAClC;AAAA,YACA;AAAA,UACF,CAAC;AACD;AAAA,QACF;AACA,oBAAY,QAAQ,MAAM;AAAA,MAC5B;AACA,oBAAc,GAAG,WAAW,OAAO,IAAI,OAAO,OAAO,SAAS;AAAA,IAChE;AAGA,QAAI,eAAe,SAAS,GAAG;AAC7B,YAAM,SAAS,oBAAI,IAAmC;AACtD,iBAAW,KAAK,gBAAgB;AAC9B,cAAM,QAAQ,OAAO,IAAI,EAAE,OAAQ,KAAK,CAAC;AACzC,cAAM,KAAK,CAAC;AACZ,eAAO,IAAI,EAAE,SAAU,KAAK;AAAA,MAC9B;AACA,YAAM,uBAA2G,CAAC;AAClH,iBAAW,CAAC,EAAE,YAAY,KAAK,QAAQ;AACrC,cAAM,WAAwF,CAAC;AAC/F,mBAAW,UAAU,cAAc;AACjC,gBAAM,SAAS,MAAM,KAAK,kBAAkB,OAAO,OAAO,OAAO,KAAK,CAAC;AACvE,cAAI,QAAQ;AACV,qBAAS,KAAK;AAAA,cACZ,WAAW,QAAQ,MAAM;AAAA,cACzB,IAAI,OAAO;AAAA,cACX,OAAO,OAAO;AAAA,cACd,WAAW,OAAO,OAAO,KAAK;AAAA,YAChC,CAAC;AAAA,UACH;AAAA,QACF;AACA,YAAI,SAAS,SAAS,EAAG,sBAAqB,KAAK,QAAQ;AAAA,MAC7D;AACA,UAAI,qBAAqB,SAAS,GAAG;AACnC,YAAI,EAAE,MAAM,WAAqB;AAC/B,gBAAM,wBAAwB,CAAC,SAAc,cAAgD;AAC3F,uBAAW,MAAM,WAAW;AAC1B,4BAAc,SAAS,GAAG,WAAW,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS;AAAA,YACpE;AAAA,UACF;AACA,gCAAsB,MAAM,qBAAqB,CAAC,CAAC;AACnD,mBAAS,KAAK,GAAG,KAAK,qBAAqB,QAAQ,MAAM;AACvD,iBAAK,QAAQ,SAAU,QAAa;AAClC,oCAAsB,QAAQ,qBAAqB,EAAE,CAAC;AAAA,YACxD,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,mBAAmB,OAAO,SAAc,cAAsB;AAClE,YAAM,cAAc,YAAY,IAAI,SAAS;AAC7C,UAAI,CAAC,YAAa;AAClB,UAAI,CAAC,iBAAiB,YAAY,MAAM,KAAK,aAAa,aAAa,iBAAiB,GAAG;AACzF,aAAK,uBAAuB,SAAS,GAAG,SAAS,oBAAoB,QAAQ;AAAA,MAC/E;AACA,UAAI,CAAC,iBAAiB,KAAK,YAAY,MAAM,KAAK,aAAa,aAAa,WAAW,GAAG;AACxF,gBAAQ,MAAM,GAAG,SAAS,cAAc,KAAK,QAAQ;AAAA,MACvD;AAAA,IACF;AACA,UAAM,iBAAiB;AAAA,MACrB;AAAA,MACA,WAAW;AAAA,MACX,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,CAAC,WAAW,QAAQ,MAAM;AAAA,MACvC,iBAAiB,CAAC,SAAS,UAAU,iBAAiB,SAAS,KAAK;AAAA,MACpE;AAAA,MACA;AAAA,MACA,cAAc,CAAC,KAAK,WAAW,KAAK,aAAa,KAAK,MAAM;AAAA,IAC9D,CAAC;AAED,QAAI,KAAK,UAAU,KAAK,OAAO,QAAQ;AACrC,YAAM,OAAO,KAAK,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,CAAC;AAC3D,UAAI,KAAK,QAAQ;AAEf,cAAM,cAAc,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;AACzE,YAAI,EAAE,OAAO,WAAW;AAAA,MAC1B;AAAA,IACF,OAAO;AAEL,UAAI,EAAE,OAAO,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;AAAA,IACxC;AAGA,UAAM,WAAW,KAAK;AACtB,UAAM,WAAW,CAAC,MAAc,EAAE,QAAQ,kBAAkB,GAAG;AAC/D,UAAM,YAAY,KAAK,4BAA4B,GAAG,OAAO,QAAQ,MAAM,MAAM,OAAO;AACxF,UAAM,mBAAmB,oBAAI,IAAuC;AACpE,eAAW,UAAU,WAAW;AAC9B,uBAAiB,IAAI,OAAO,OAAO,QAAQ,GAAG,MAAM;AAAA,IACtD;AACA,UAAM,2BAA2B,MAAM,QAAQ,KAAK,mBAAmB,IACnE,KAAK,oBAAoB,IAAI,CAAC,QAAQ,OAAO,GAAG,CAAC,IACjD,CAAC;AACL,UAAM,SAAS,oBAAI,IAAY;AAC/B,UAAM,YAAY,oBAAI,IAAuC;AAE7D,eAAW,KAAM,KAAK,UAAU,CAAC,GAAI;AACnC,UAAI,OAAO,MAAM,YAAY,EAAE,WAAW,KAAK,EAAG,QAAO,IAAI,EAAE,MAAM,CAAC,CAAC;AAAA,IACzE;AACA,eAAW,KAAK,WAAW;AACzB,UAAI,OAAO,EAAE,UAAU,YAAY,EAAE,MAAM,WAAW,KAAK,EAAG,QAAO,IAAI,EAAE,MAAM,MAAM,CAAC,CAAC;AAAA,IAC3F;AACA,QAAI,KAAK,wBAAwB,MAAM;AACrC,UAAI,iBAAiB,OAAO,GAAG;AAC7B,cAAM,eAAe,MAAM,KAAK,iBAAiB,KAAK,CAAC;AACvD,cAAM,cAAc,oBAAI,IAAoB;AAC5C,qBAAa,QAAQ,CAAC,IAAI,QAAQ,YAAY,IAAI,IAAI,GAAG,CAAC;AAC1D,cAAM,OAAO,MAAM,KAAK,mBAAmB,EACxC,OAAO,OAAO,aAAa,eAAe,MAAM,EAChD,QAAQ,aAAa,YAAY,EACjC,SAAS,aAAa,IAAI,EAC1B,OAAO,CAAC,OAAY;AACnB,aAAG,SAAS,CAAC,UAAe;AAC1B,kBAAM,MAAM,EAAE,WAAW,SAAS,CAAC,EAAE,YAAY,WAAW;AAAA,UAC9D,CAAC;AAAA,QACH,CAAC;AAOH,cAAM,SAAqC,KAAK,IAAI,CAAC,QAAa;AAChE,gBAAM,MAAM,IAAI;AAChB,cAAI,MAA2B,CAAC;AAChC,cAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,gBAAI;AAAE,oBAAM,KAAK,MAAM,GAAG;AAAA,YAAE,QAAQ;AAAE,oBAAM,CAAC;AAAA,YAAE;AAAA,UACjD,WAAW,OAAO,OAAO,QAAQ,UAAU;AACzC,kBAAM;AAAA,UACR;AACA,iBAAO;AAAA,YACL,KAAK,OAAO,IAAI,GAAG;AAAA,YACnB,UAAU,OAAO,IAAI,SAAS;AAAA,YAC9B,MAAM,OAAO,IAAI,QAAQ,EAAE;AAAA,YAC3B,QAAQ;AAAA,UACV;AAAA,QACF,CAAC;AACD,eAAO,KAAK,CAAC,GAA6B,MAAgC;AACxE,gBAAM,KAAK,YAAY,IAAI,EAAE,QAAQ,KAAK,OAAO;AACjD,gBAAM,KAAK,YAAY,IAAI,EAAE,QAAQ,KAAK,OAAO;AACjD,cAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,iBAAO,EAAE,IAAI,cAAc,EAAE,GAAG;AAAA,QAClC,CAAC;AACD,cAAM,kBAAkB,oBAAI,IAAwG;AACpI,mBAAW,OAAO,QAAQ;AACxB,gBAAM,SAAS,iBAAiB,IAAI,IAAI,QAAQ;AAChD,cAAI,CAAC,OAAQ;AACb,gBAAM,MAAM,IAAI,UAAU,CAAC;AAC3B,gBAAM,cAAc,YAAY,IAAI,IAAI,QAAQ,KAAK,OAAO;AAC5D,gBAAM,SAAS,aAAa,KAAK,IAAI,MAAM,WAAW;AACtD,gBAAM,WAAW,gBAAgB,IAAI,IAAI,GAAG;AAC5C,cAAI,CAAC,YAAY,OAAO,OAAO,SAAS,SAAU,OAAO,SAAS,SAAS,UAAU,OAAO,UAAU,SAAS,WAAY,OAAO,YAAY,SAAS,WAAW,OAAO,cAAc,SAAS,cAAgB;AAC9M,4BAAgB,IAAI,IAAI,KAAK,EAAE,QAAQ,OAAO,OAAO,MAAM,SAAS,OAAO,SAAS,aAAa,OAAO,YAAY,CAAC;AAAA,UACvH;AACA,iBAAO,IAAI,IAAI,GAAG;AAAA,QACpB;AACA,mBAAW,CAAC,KAAK,KAAK,KAAK,gBAAgB,QAAQ,GAAG;AACpD,oBAAU,IAAI,KAAK,MAAM,MAAM;AAAA,QACjC;AAAA,MACF;AAAA,IACF,WAAW,yBAAyB,SAAS,GAAG;AAC9C,iBAAW,OAAO,yBAA0B,QAAO,IAAI,GAAG;AAAA,IAC5D;AACA,UAAM,iBAAiB,MAAM,KAAK,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,CAAC;AAC7E,QAAI,eAAe,SAAS,KAAK,iBAAiB,OAAO,GAAG;AAC1D,YAAM,OAAO,MAAM,KAAK,mBAAmB,EACxC,OAAO,OAAO,WAAW,EACzB,QAAQ,aAAa,MAAM,KAAK,iBAAiB,KAAK,CAAC,CAAC,EACxD,QAAQ,OAAO,cAAc,EAC7B,SAAS,aAAa,IAAI,EAC1B,OAAO,CAAC,OAAY;AACnB,WAAG,SAAS,CAAC,UAAe;AAC1B,gBAAM,MAAM,EAAE,WAAW,SAAS,CAAC,EAAE,YAAY,WAAW;AAAA,QAC9D,CAAC;AAAA,MACH,CAAC;AACH,iBAAW,OAAO,MAAM;AACtB,cAAM,SAAS,iBAAiB,IAAI,OAAO,IAAI,SAAS,CAAC;AACzD,YAAI,CAAC,OAAQ;AACb,YAAI,CAAC,UAAU,IAAI,IAAI,GAAG,EAAG,WAAU,IAAI,IAAI,KAAK,MAAM;AAAA,MAC5D;AAAA,IACF;AAEA,UAAM,mBAAwC,CAAC;AAC/C,UAAM,oBAA8B,CAAC;AACrC,UAAM,gBAAgB,oBAAI,IAAY;AACtC,UAAM,sBAAsB,oBAAI,IAAoB;AACpD,eAAW,OAAO,QAAQ;AACxB,YAAM,SAAS,UAAU,IAAI,GAAG;AAChC,UAAI,CAAC,OAAQ;AACb,YAAM,iBAAiB,OAAO;AAC9B,YAAM,eAAe,OAAO;AAC5B,YAAM,kBAAkB,SAAS,OAAO,SAAS,KAAK;AACtD,YAAM,eAAe,SAAS,GAAG;AACjC,YAAM,WAAW,OAAO,eAAe,IAAI,YAAY;AACvD,YAAM,WAAW,OAAO,eAAe,IAAI,YAAY;AAEvD,UAAI,EAAE,SAAS,EAAE,CAAC,QAAQ,GAAG,oBAAoB,GAAG,WAAqB;AACvE,aAAK,GAAG,GAAG,QAAQ,cAAc,KAAK,KAAK,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC,EAClE,MAAM,GAAG,QAAQ,QAAQ,KAAK,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,EAClD,MAAM,GAAG,QAAQ,cAAc,KAAK,KAAK,IAAI,MAAM,CAAC,EACpD,MAAM,KAAK,IAAI,IAAI,QAAQ,qBAAqB,QAAQ,uBAAuB,CAAC,QAAQ,CAAC,CAAC;AAAA,MAC/F,CAAC;AAED,UAAI,EAAE,SAAS,EAAE,CAAC,QAAQ,GAAG,sBAAsB,GAAG,WAAqB;AACzE,aAAK,GAAG,GAAG,QAAQ,cAAc,KAAK,KAAK,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC,EAClE,MAAM,GAAG,QAAQ,cAAc,KAAK,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,EACxD,MAAM,GAAG,QAAQ,cAAc,KAAK,YAAY,EAChD,MAAM,KAAK,IAAI,IAAI,QAAQ,qBAAqB,QAAQ,uBAAuB,CAAC,QAAQ,CAAC,CAAC;AAAA,MAC/F,CAAC;AAED,YAAM,WAAW,KAAK;AAAA,QACpB,QAAQ,QAAQ;AAAA,kCACU,QAAQ;AAAA,gCACV,QAAQ;AAAA,kCACN,QAAQ;AAAA,oCACN,QAAQ;AAAA,mBACzB,QAAQ;AAAA;AAAA,MAErB;AACA,uBAAiB,GAAG,IAAI;AACxB,YAAM,QAAQ,SAAS,MAAM,GAAG,EAAE;AAElC,WAAK,KAAK,UAAU,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,KAAK,KAAK,wBAAwB,QAAS,yBAAyB,SAAS,KAAK,yBAAyB,SAAS,GAAG,GAAI;AAErK,cAAM,UAAU,KAAK,IAAI,qBAAqB,QAAQ,2CAA2C;AACjG,cAAM,kBAAkB,mCAAmC,SAAS,SAAS,CAAC;AAC9E,cAAM,OAAO,aAAa,QAAQ,SAAS,CAAC;AAAA,gCACpB,eAAe;AAAA,oCACX,SAAS,SAAS,CAAC;AAAA;AAE/C,cAAM,aAAa,GAAG,KAAK;AAC3B,YAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;AAC/C,YAAI,EAAE,OAAO,KAAK,IAAI,GAAG,QAAQ,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAClE,0BAAkB,KAAK,KAAK;AAC5B,sBAAc,IAAI,KAAK;AACvB,4BAAoB,IAAI,OAAO,UAAU;AAAA,MAC3C;AAAA,IACF;AAGA,eAAW,KAAK,WAAW;AACzB,UAAI,CAAC,EAAE,MAAM,WAAW,KAAK,EAAG;AAChC,YAAM,MAAM,EAAE,MAAM,MAAM,CAAC;AAC3B,YAAM,OAAO,iBAAiB,GAAG;AACjC,UAAI,CAAC,KAAM;AACX,WAAK,EAAE,OAAO,UAAU,EAAE,OAAO,YAAY,gBAAgB,OAAO,EAAE,UAAU,UAAU;AACxF,cAAM,SAAS,aAAa,OAAO,EAAE,KAAK,GAAG,YAAY;AACzD,cAAM,SAAS,OAAO;AACtB,YAAI,OAAO,QAAQ;AACjB,gBAAM,UAAU,KAAK,kBAAkB,GAAG;AAAA,YACxC,QAAQ,OAAO,MAAM;AAAA,YACrB,OAAO,EAAE;AAAA,YACT;AAAA,YACA;AAAA,YACA,UAAU,KAAK,YAAY;AAAA,YAC3B,mBAAmB;AAAA,YACnB,QAAQ,OAAO;AAAA,UACjB,CAAC;AACD,eAAK,eAAe,oBAAoB;AAAA,YACtC,QAAQ,OAAO,MAAM;AAAA,YACrB,OAAO,EAAE;AAAA,YACT,QAAQ,OAAO;AAAA,YACf;AAAA,YACA;AAAA,YACA,UAAU,KAAK,YAAY;AAAA,YAC3B,mBAAmB;AAAA,UACrB,CAAC;AACD,cAAI,QAAS;AAAA,QACf,OAAO;AACL,eAAK,eAAe,+BAA+B;AAAA,YACjD,QAAQ,OAAO,MAAM;AAAA,YACrB,OAAO,EAAE;AAAA,YACT,OAAO,EAAE;AAAA,UACX,CAAC;AAAA,QACH;AAAA,MACF;AACA,cAAQ,EAAE,IAAI;AAAA,QACZ,KAAK;AAAM,cAAI,EAAE,MAAM,MAAM,KAAK,EAAE,KAAK;AAAG;AAAA,QAC5C,KAAK;AAAM,cAAI,EAAE,MAAM,MAAM,MAAM,EAAE,KAAK;AAAG;AAAA,QAC7C,KAAK;AAAM,cAAI,EAAE,MAAM,MAAM,KAAK,EAAE,KAAK;AAAG;AAAA,QAC5C,KAAK;AAAO,cAAI,EAAE,MAAM,MAAM,MAAM,EAAE,KAAK;AAAG;AAAA,QAC9C,KAAK;AAAM,cAAI,EAAE,MAAM,MAAM,KAAK,EAAE,KAAK;AAAG;AAAA,QAC5C,KAAK;AAAO,cAAI,EAAE,MAAM,MAAM,MAAM,EAAE,KAAK;AAAG;AAAA,QAC9C,KAAK;AAAM,cAAI,EAAE,QAAQ,MAAa,EAAE,SAAS,CAAC,CAAC;AAAG;AAAA,QACtD,KAAK;AAAO,cAAI,EAAE,WAAW,MAAa,EAAE,SAAS,CAAC,CAAC;AAAG;AAAA,QAC1D,KAAK;AAAQ,cAAI,EAAE,MAAM,MAAM,QAAQ,EAAE,KAAK;AAAG;AAAA,QACjD,KAAK;AAAS,cAAI,EAAE,MAAM,MAAM,SAAS,EAAE,KAAK;AAAG;AAAA,QACnD,KAAK;AAAU,YAAE,QAAQ,IAAI,EAAE,aAAa,IAAI,IAAI,IAAI,EAAE,UAAU,IAAI;AAAG;AAAA,MAC7E;AAAA,IACF;AAGA,QAAI,KAAK,mBAAmB;AAC1B,YAAM,EAAE,WAAW,IAAI,MAAM,OAAO,sCAAsC;AAC1E,YAAM,UAAU,WAAW;AAC3B,YAAM,UAAU,QAAQ,QAAQ,CAAC,MAAO,EAAU,oBAAoB,CAAC,CAAC;AACxE,YAAM,OAAO,QAAQ,OAAO,CAAC,MAAW,EAAE,SAAS,MAAM;AACzD,YAAM,SAAS,MAAM,QAAQ,KAAK,iBAAiB,IAC/C,KAAK,OAAO,CAAC,MAAY,KAAK,kBAA+B,SAAS,EAAE,SAAS,CAAC,IAClF;AACJ,iBAAW,KAAK,QAAQ;AACtB,cAAM,CAAC,EAAE,OAAO,IAAK,EAAE,UAAqB,MAAM,GAAG;AACrD,cAAM,WAAW,QAAQ,SAAS,GAAG,IAAI,UAAU,GAAG,OAAO;AAC7D,cAAM,QAAQ,OAAO,SAAS,OAAO,CAAC;AACtC,YAAI,EAAE,SAAS,EAAE,CAAC,KAAK,GAAG,SAAS,GAAG,WAAqB;AACzD,eAAK,GAAG,GAAG,KAAK,IAAI,EAAE,KAAK,YAAY,IAAI,KAAK,KAAK,IAAI,MAAM,CAAC,GAAG,KAAK,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC,CAAC;AAAA,QAChG,CAAC;AAAA,MACH;AAAA,IACF;AAGA,eAAW,KAAK,KAAK,QAAQ,CAAC,GAAG;AAC/B,UAAI,EAAE,MAAM,WAAW,KAAK,GAAG;AAC7B,cAAM,MAAM,EAAE,MAAM,MAAM,CAAC;AAC3B,cAAM,QAAQ,SAAS,MAAM,GAAG,EAAE;AAElC,YAAI,CAAC,kBAAkB,SAAS,KAAK,GAAG;AACtC,gBAAM,OAAO,iBAAiB,GAAG;AACjC,cAAI,MAAM;AACR,gBAAI,EAAE,OAAO,KAAK,IAAI,OAAO,KAAK,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC/D,8BAAkB,KAAK,KAAK;AAAA,UAC9B;AAAA,QACF;AACA,YAAI,EAAE,QAAQ,OAAO,EAAE,OAAO,KAAK;AAAA,MACrC,OAAO;AACL,cAAM,SAAS,MAAM,KAAK,kBAAkB,OAAO,EAAE,KAAK;AAC1D,YAAI,CAAC,OAAQ;AACb,YAAI,EAAE,QAAQ,QAAQ,MAAM,GAAG,EAAE,OAAO,KAAK;AAAA,MAC/C;AAAA,IACF;AAGA,UAAM,OAAO,KAAK,MAAM,QAAQ;AAChC,UAAM,WAAW,KAAK,MAAM,YAAY;AAExC,QAAK,KAAK,sBAAsB,MAAM,QAAQ,KAAK,iBAAiB,IAAK,KAAK,kBAAkB,SAAS,IAAK,SAAU,OAAO,KAAK,gBAAgB,EAAE,SAAS,GAAG;AAChK,UAAI,EAAE,QAAQ,GAAG,KAAK,KAAK;AAAA,IAC7B;AACA,UAAM,aAAkB,EAAE,MAAM;AAChC,QAAI,OAAO,WAAW,gBAAgB,WAAY,YAAW,YAAY;AACzE,QAAI,OAAO,WAAW,eAAe,WAAY,YAAW,WAAW;AACvE,QAAI,OAAO,WAAW,eAAe,WAAY,YAAW,WAAW;AACvE,UAAM,WAAW,MAAM,WACpB,cAAc,GAAG,KAAK,cAAc,EACpC,MAAM;AACT,UAAM,QAAQ,OAAQ,UAAkB,SAAS,CAAC;AAClD,UAAM,QAAQ,MAAM,EAAE,MAAM,QAAQ,EAAE,QAAQ,OAAO,KAAK,QAAQ;AAElE,QAAI,cAAc,OAAO,GAAG;AAC1B,iBAAW,OAAO,OAAgB;AAChC,mBAAW,SAAS,eAAe;AACjC,gBAAM,aAAa,oBAAoB,IAAI,KAAK;AAChD,gBAAM,UAAU,aAAa,QAAQ,IAAI,UAAU,CAAC,IAAI;AACxD,cAAI,MAAM,IAAI,KAAK;AACnB,cAAI,OAAO,QAAQ,UAAU;AAC3B,gBAAI;AAAE,oBAAM,KAAK,MAAM,GAAG;AAAA,YAAE,QAAQ;AAAA,YAA8B;AAAA,UACpE;AACA,cAAI,SAAS;AACX,gBAAI,OAAO,KAAM,KAAI,KAAK,IAAI,CAAC;AAAA,qBACtB,MAAM,QAAQ,GAAG,EAAG,KAAI,KAAK,IAAI;AAAA,gBACrC,KAAI,KAAK,IAAI,CAAC,GAAG;AAAA,UACxB,OAAO;AACL,gBAAI,MAAM,QAAQ,GAAG,EAAG,KAAI,KAAK,IAAI,IAAI,SAAS,IAAI,IAAI,CAAC,IAAI;AAAA,gBAC1D,KAAI,KAAK,IAAI;AAAA,UACpB;AACA,cAAI,WAAY,QAAO,IAAI,UAAU;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,KAAK,qBAAqB;AACtC,UAAM,iBACJ,KAAK,sBAAsB,KAAK,GAAG;AAQrC,QAAI,iBAAiB;AACrB,QAAI,gBAAgB;AAClB,YAAM,gBACJ,KAAK,mBACD,MAAM,QAAQ,KAAK,eAAe,KAAK,KAAK,gBAAgB,WAAW,IAAI,KAAK,gBAAgB,CAAC,IAAI;AAC3G,uBAAiB,MAAM,QAAQ;AAAA,QAC5B,MAAgB,IAAI,OAAO,SAAS;AACnC,cAAI;AACF,kBAAM,YAAY,MAAM;AAAA,cACtB;AAAA,cACA;AAAA,cACA,MAAM,aAAa,MAAM,YAAY,KAAK,YAAY;AAAA,cACtD,MAAM,mBAAmB,MAAM,kBAAkB,iBAAiB;AAAA,YACpE;AACA,mBAAO,EAAE,GAAG,MAAM,GAAG,UAAU;AAAA,UACjC,SAAS,KAAK;AACZ,oBAAQ,MAAM,gDAAgD,GAAG;AACjE,mBAAO;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,cAA8B,EAAE,OAAO,gBAAgB,MAAM,UAAU,MAAM;AAGjF,QAAI,OAAO,cAAc;AACvB,YAAM,QAAQ,IAAI,UAAU,EAAE,SAAS,IAAI,QAAQ,IAAI;AACvD,oBAAc,MAAM;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,kBAAkB,OAAe,OAAuC;AACpF,QAAI,MAAM,KAAK,aAAa,OAAO,KAAK,EAAG,QAAO;AAClD,QAAI,UAAU,qBAAqB,MAAM,KAAK,aAAa,OAAO,IAAI,EAAG,QAAO;AAChF,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,aAAa,OAAe,QAAkC;AAC1E,UAAM,MAAM,GAAG,KAAK,IAAI,MAAM;AAC9B,QAAI,KAAK,YAAY,IAAI,GAAG,GAAG;AAC7B,YAAM,SAAS,KAAK,YAAY,IAAI,GAAG;AACvC,UAAI,WAAW,KAAM,QAAO;AAC5B,WAAK,YAAY,OAAO,GAAG;AAAA,IAC7B;AACA,UAAM,OAAO,KAAK,YAAY,KAAK,UAAU,IAAK,KAAK,GAAW,cAAc,EAAE,QAAQ;AAC1F,UAAM,SAAS,MAAM,KAAK,4BAA4B,EACnD,MAAM,EAAE,YAAY,OAAO,aAAa,OAAO,CAAC,EAChD,MAAM;AACT,UAAM,UAAU,CAAC,CAAC;AAClB,QAAI,QAAS,MAAK,YAAY,IAAI,KAAK,IAAI;AAAA,QACtC,MAAK,YAAY,OAAO,GAAG;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,YAAY,OAAiC;AACzD,QAAI,KAAK,WAAW,IAAI,KAAK,EAAG,QAAO,KAAK,WAAW,IAAI,KAAK,KAAK;AACrE,UAAM,OAAO,KAAK,YAAY,KAAK,UAAU,IAAK,KAAK,GAAW,cAAc,EAAE,QAAQ;AAC1F,UAAM,SAAS,MAAM,KAAK,2BAA2B,EAClD,MAAM,EAAE,YAAY,MAAM,CAAC,EAC3B,MAAM;AACT,UAAM,UAAU,CAAC,CAAC;AAClB,SAAK,WAAW,IAAI,OAAO,OAAO;AAClC,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,gBACZ,QACA,UACA,UACkB;AAClB,QAAI;AACF,YAAM,OAAO,KAAK,YAAY,KAAK,UAAU,IAAK,KAAK,GAAW,cAAc,EAAE,QAAQ;AAC1F,YAAM,QAAQ,KAAK,eAAe,EAAE,OAAO,CAAC,EAAE,MAAM,eAAe,MAAM,EAAE,MAAM,CAAC;AAClF,UAAI,aAAa,QAAW;AAC1B,cAAM,YAAY,oCAAoC,CAAC,QAAQ,CAAC;AAAA,MAClE;AACA,UAAI,UAAU;AACZ,aAAK,uBAAuB,OAAc,iCAAiC,QAAQ;AAAA,MACrF;AACA,YAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,aAAO,CAAC,CAAC;AAAA,IACX,SAAS,KAAK;AACZ,WAAK,eAAe,2BAA2B;AAAA,QAC7C;AAAA,QACA;AAAA,QACA,mBAAmB;AAAA,QACnB,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,kBACN,GACA,MAUS;AACT,QAAI,CAAC,KAAK,OAAO,QAAQ;AACvB,WAAK,eAAe,yBAAyB;AAAA,QAC3C,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,UAAU,KAAK,YAAY;AAAA,QAC3B,mBAAmB,KAAK;AAAA,MAC1B,CAAC;AACD,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,MAAM,KAAK,gBAAgB;AACzC,UAAM,cAAc,KAAK,gBAAgB,OAAO,kBAAkB;AAClE,UAAM,SAAS;AACf,SAAK,eAAe,8BAA8B;AAAA,MAChD,QAAQ,KAAK;AAAA,MACb,OAAO,KAAK;AAAA,MACZ;AAAA,MACA,YAAY,KAAK,OAAO;AAAA,MACxB,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK,YAAY;AAAA,MAC3B,mBAAmB,KAAK;AAAA,MACxB,aAAa,KAAK,eAAe;AAAA,IACnC,CAAC;AACA,IAAC,EAAU,WAAW,EAAE,WAAmC;AAC1D,WAAK,OAAO,CAAC,EACV,KAAK,EAAE,CAAC,KAAK,GAAG,gBAAgB,CAAC,EACjC,MAAM,GAAG,KAAK,gBAAgB,KAAK,MAAM,EACzC,SAAS,GAAG,KAAK,UAAU,KAAK,KAAK,EACrC,YAAY,iBAAiB,CAAC,GAAG,KAAK,cAAc,KAAK,cAAc,CAAC,EACxE,QAAQ,GAAG,KAAK,eAAe,KAAK,MAAM,EAC1C,QAAQ,GAAG,KAAK,cAAc,GAAG,KAAK,QAAQ,EAC9C,UAAU,kBAAkB,KAAK,qBAAqB,CAAC,KAAK,OAAO,MAAM,CAAC;AAC7E,UAAI,KAAK,aAAa,QAAW;AAC/B,aAAK,YAAY,GAAG,KAAK,qCAAqC,CAAC,KAAK,YAAY,IAAI,CAAC;AAAA,MACvF;AACA,UAAI,KAAK,mBAAmB;AAC1B,eAAO,uBAAuB,MAAa,GAAG,KAAK,oBAAoB,KAAK,iBAAiB;AAAA,MAC/F;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEQ,oBACN,GACA,MAYqC;AACrC,SAAK,KAAK,OAAO,UAAU,KAAK,OAAO,YAAY,KAAK,gBAAgB,OAAO,KAAK,UAAU,UAAU;AACtG,YAAM,SAAS,aAAa,OAAO,KAAK,KAAK,GAAG,KAAK,YAAY;AACjE,YAAM,SAAS,OAAO;AACtB,UAAI,OAAO,QAAQ;AACjB,cAAM,UAAU,KAAK,kBAAkB,GAAG;AAAA,UACxC,QAAQ,KAAK;AAAA,UACb,OAAO,KAAK;AAAA,UACZ;AAAA,UACA,gBAAgB,KAAK;AAAA,UACrB,UAAU,KAAK,YAAY;AAAA,UAC3B,mBAAmB,KAAK;AAAA,UACxB,QAAQ,OAAO;AAAA,QACjB,CAAC;AACD,aAAK,eAAe,2BAA2B;AAAA,UAC7C,QAAQ,KAAK;AAAA,UACb,OAAO,KAAK;AAAA,UACZ,QAAQ,OAAO;AAAA,UACf;AAAA,UACA;AAAA,UACA,UAAU,KAAK,YAAY;AAAA,UAC3B,mBAAmB,KAAK;AAAA,QAC1B,CAAC;AACD,YAAI,QAAS,QAAO;AAAA,MACtB,OAAO;AACL,aAAK,eAAe,sCAAsC;AAAA,UACxD,QAAQ,KAAK;AAAA,UACb,OAAO,KAAK;AAAA,UACZ,OAAO,KAAK;AAAA,QACd,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,KAAK,YAAY,KAAK,UAAU,IAAK,KAAK,GAAW,cAAc,EAAE,QAAQ;AAC1F,UAAM,QAAQ,MAAM,KAAK,gBAAgB;AACzC,UAAM,SAAS;AACf,WAAO,EAAE,YAAY,WAAmC;AACtD,WAAK,OAAO,CAAC,EACV,KAAK,EAAE,CAAC,KAAK,GAAG,iBAAiB,CAAC,EAClC,MAAM,GAAG,KAAK,gBAAgB,KAAK,MAAM,EACzC,YAAY,iBAAiB,CAAC,GAAG,KAAK,cAAc,KAAK,cAAc,CAAC;AAE3E,UAAI,KAAK,aAAa,QAAW;AAC/B,aAAK,YAAY,GAAG,KAAK,qCAAqC,CAAC,KAAK,YAAY,IAAI,CAAC;AAAA,MACvF;AACA,UAAI,KAAK,mBAAmB;AAC1B,eAAO,uBAAuB,MAAa,GAAG,KAAK,oBAAoB,KAAK,iBAAiB;AAAA,MAC/F;AACA,UAAI,CAAC,KAAK,aAAa;AACrB,aAAK,UAAU,GAAG,KAAK,aAAa;AAAA,MACtC;AAEA,YAAM,OAAO,KAAK,IAAI,IAAI,KAAK,eAAe,CAAC,KAAK,KAAK,CAAC;AAC1D,cAAQ,KAAK,IAAI;AAAA,QACf,KAAK;AACH,eAAK,MAAM,MAAM,KAAK,KAAK,KAAmB;AAC9C;AAAA,QACF,KAAK;AACH,eAAK,MAAM,MAAM,MAAM,KAAK,KAAmB;AAC/C;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK,OAAO;AACV,gBAAM,WAAW,KAAK,OAAO,OAAO,MAAM,KAAK,OAAO,QAAQ,OAAO,KAAK,OAAO,OAAO,MAAM;AAC9F,eAAK,MAAM,MAAM,UAAU,KAAK,KAAmB;AACnD;AAAA,QACF;AAAA,QACA,KAAK;AACH,eAAK,QAAQ,MAAa,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC,KAAK,KAAK,CAAC;AAC/E;AAAA,QACF,KAAK;AACH,eAAK,WAAW,MAAa,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC,KAAK,KAAK,CAAC;AAClF;AAAA,QACF,KAAK;AACH,eAAK,MAAM,MAAM,QAAQ,KAAK,KAAmB;AACjD;AAAA,QACF,KAAK;AACH,eAAK,MAAM,MAAM,SAAS,KAAK,KAAmB;AAClD;AAAA,QACF,KAAK;AACH,eAAK,QAAQ,KAAK,aAAa,IAAW,IAAI,KAAK,UAAU,IAAW;AACxE;AAAA,QACF;AACE;AAAA,MACJ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,4BACN,GACA,WACA,YACA,MACA,MACA,SAC6B;AAC7B,UAAM,UAAuC;AAAA,MAC3C;AAAA,QACE,UAAU;AAAA,QACV,OAAO;AAAA,QACP,OAAO;AAAA,QACP,cAAc,KAAK,IAAI,YAAY,CAAC,GAAG,SAAS,KAAK,CAAC;AAAA,MACxD;AAAA,IACF;AACA,UAAM,SAAmC,KAAK,sBAAsB,CAAC;AACrE,WAAO,QAAQ,CAAC,QAAQ,UAAU;AAChC,YAAM,YAAY,OAAO,SAAS,uBAAuB,KAAK,IAAI,OAAO,QAAQ;AACjF,YAAM,QAAQ,OAAO,SAAS,OAAO,KAAK;AAC1C,YAAM,OAAO,OAAO;AACpB,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,6CAA6C,OAAO,OAAO,QAAQ,CAAC,gCAAgC;AAAA,MACtH;AACA,YAAM,WAAW,EAAE,CAAC,KAAK,GAAG,UAAU;AACtC,YAAM,eAAe,WAAqB;AACxC,aAAK,GAAG,GAAG,KAAK,IAAI,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,WAAW,KAAK,QAAQ;AAC9B,UAAI,aAAa,QAAS,GAAE,KAAK,UAAU,YAAY;AAAA,UAClD,GAAE,SAAS,UAAU,YAAY;AACtC,YAAM,eAAe,OAAO,kBAAkB;AAC9C,cAAQ,KAAK;AAAA,QACX,UAAU,OAAO;AAAA,QACjB;AAAA,QACA,OAAO;AAAA,QACP,cAAc,KAAK,IAAI,YAAY,CAAC,GAAG,KAAK,IAAI,YAAY,EAAE,CAAC;AAAA,MACjE,CAAC;AAAA,IACH,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe,OAAe,SAAkC;AACtE,QAAI;AACF,cAAQ,KAAK,kBAAkB,OAAO,KAAK,UAAU,OAAO,CAAC;AAAA,IAC/D,QAAQ;AACN,cAAQ,KAAK,kBAAkB,OAAO,OAAO;AAAA,IAC/C;AAAA,EACF;AAAA,EAEQ,yBAAyB,MAAoE;AACnG,QAAI,KAAK,oBAAoB,QAAW;AACtC,YAAM,OAAO,KAAK,mBAAmB,CAAC,GAAG,IAAI,CAAC,OAAQ,OAAO,OAAO,WAAW,GAAG,KAAK,IAAI,EAAG;AAC9F,YAAM,cAAc,IAAI,KAAK,CAAC,OAAO,MAAM,QAAQ,OAAO,EAAE;AAC5D,YAAM,MAAM,IAAI,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AACpF,aAAO,EAAE,KAAK,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,GAAG,YAAY;AAAA,IACtD;AACA,QAAI,OAAO,KAAK,mBAAmB,YAAY,KAAK,eAAe,KAAK,EAAE,SAAS,GAAG;AACpF,aAAO,EAAE,KAAK,CAAC,KAAK,cAAc,GAAG,aAAa,MAAM;AAAA,IAC1D;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,GAAQ,QAAgB,OAAqD;AAC1G,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,MAAM,IAAI,WAAW,KAAK,CAAC,MAAM,aAAa;AAChD,aAAO,EAAE,SAAS,OAAO;AAAA,IAC3B;AACA,WAAO,EAAE,MAAM,CAAC,YAAiB;AAC/B,UAAI,UAAU;AACd,UAAI,MAAM,IAAI,SAAS,GAAG;AACxB,gBAAQ,QAAQ,QAAe,MAAM,GAAG;AACxC,kBAAU;AAAA,MACZ;AACA,UAAI,MAAM,aAAa;AACrB,YAAI,QAAS,SAAQ,YAAY,MAAM;AAAA,YAClC,SAAQ,UAAU,MAAM;AAC7B,kBAAU;AAAA,MACZ;AACA,UAAI,CAAC,QAAS,SAAQ,SAAS,OAAO;AAAA,IACxC,CAAC;AAAA,EACH;AAEF;",
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import type { QueryEngine, QueryOptions, QueryResult, QueryCustomFieldSource, QueryExtensionsConfig } from './types'\nimport type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { type Kysely, sql, type RawBuilder } from 'kysely'\nimport {\n applyJoinFilters,\n normalizeFilters,\n partitionFilters,\n resolveJoins,\n type BaseFilter,\n type NormalizedFilter,\n type ResolvedJoin,\n} from './join-utils'\nimport { resolveSearchConfig } from '../search/config'\nimport { tokenizeText } from '../search/tokenize'\nimport { runBeforeQueryPipeline, runAfterQueryPipeline, type QueryExtensionContext } from './query-extension-runner'\n\ntype AnyDb = Kysely<any>\ntype AnyBuilder = any\n\nconst entityTableCache = new Map<string, string>()\n\ntype EncryptionResolver = () => {\n decryptEntityPayload?: (entityId: EntityId, payload: Record<string, unknown>, tenantId?: string | null, organizationId?: string | null) => Promise<Record<string, unknown>>\n isEnabled?: () => boolean\n} | null\n\ntype ResolvedCustomFieldSource = {\n entityId: EntityId\n alias: string\n table: string\n recordIdExpr: RawBuilder<string>\n}\n\ntype ResultRow = Record<string, unknown>\n\nconst pluralizeBaseName = (name: string): string => {\n if (!name) return name\n if (name.endsWith('s')) return name\n if (name.endsWith('y')) return `${name.slice(0, -1)}ies`\n return `${name}s`\n}\n\nconst toPascalCase = (value: string): string => {\n return value\n .split(/[_\\s]+/)\n .filter(Boolean)\n .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))\n .join('')\n}\n\nconst candidateClassNames = (rawName: string): string[] => {\n const base = toPascalCase(rawName)\n const candidates = new Set<string>()\n if (base) candidates.add(base)\n if (base && !base.endsWith('Entity')) candidates.add(`${base}Entity`)\n return Array.from(candidates)\n}\n\nexport function resolveEntityTableName(em: EntityManager | undefined, entity: EntityId): string {\n if (entityTableCache.has(entity)) {\n return entityTableCache.get(entity)!\n }\n const parts = String(entity || '').split(':')\n const rawName = (parts[1] && parts[1].trim().length > 0) ? parts[1] : (parts[0] || '').trim()\n const metadata = (em as any)?.getMetadata?.()\n\n if (metadata && rawName) {\n const candidates = candidateClassNames(rawName)\n for (const candidate of candidates) {\n try {\n const meta = metadata.find?.(candidate)\n if (meta?.tableName) {\n const tableName = String(meta.tableName)\n entityTableCache.set(entity, tableName)\n return tableName\n }\n } catch {}\n }\n\n // Secondary lookup: search ORM metadata by candidate table names\n const modulePrefix = parts[0] ?? ''\n const candidateTables = [\n `${modulePrefix}_${rawName}`,\n pluralizeBaseName(rawName),\n `${modulePrefix}_${pluralizeBaseName(rawName)}`,\n ]\n try {\n const allMeta: any[] = metadata.getAll?.() ?? []\n for (const meta of allMeta) {\n if (meta?.tableName && candidateTables.includes(String(meta.tableName))) {\n const tableName = String(meta.tableName)\n entityTableCache.set(entity, tableName)\n return tableName\n }\n }\n } catch {}\n }\n\n const fallback = pluralizeBaseName(rawName || '')\n console.warn(\n `[QueryEngine] Could not resolve entity \"${entity}\" via ORM metadata. ` +\n `Falling back to table name \"${fallback}\". ` +\n `Ensure the entity ID segment matches the class name convention.`\n )\n entityTableCache.set(entity, fallback)\n return fallback\n}\n\nfunction buildFilterableCustomFieldJoins(\n sources: QueryCustomFieldSource[] | undefined,\n): Array<{\n alias: string\n table?: string\n entityId: EntityId\n from: { field: string }\n to: { field: string }\n type: 'left' | 'inner'\n}> {\n if (!sources || sources.length === 0) return []\n return sources.flatMap((source, index) => {\n if (!source.join) return []\n const alias = typeof source.alias === 'string' && source.alias.trim().length > 0\n ? source.alias.trim()\n : `cfs_${index}`\n return [{\n alias,\n table: source.table,\n entityId: source.entityId,\n from: { field: source.join.fromField },\n to: { field: source.join.toField },\n type: source.join.type === 'inner' ? 'inner' : 'left',\n }]\n })\n}\n\nfunction computeCustomFieldScore(cfg: Record<string, unknown>, kind: string, entityIndex: number) {\n const listVisibleScore = cfg.listVisible === false ? 0 : 1\n const formEditableScore = cfg.formEditable === false ? 0 : 1\n const filterableScore = cfg.filterable ? 1 : 0\n const kindScore = (() => {\n switch (kind) {\n case 'dictionary': return 8\n case 'relation': return 6\n case 'select': return 4\n case 'multiline': return 3\n case 'boolean':\n case 'integer':\n case 'float': return 2\n default: return 1\n }\n })()\n const optionsBonus = Array.isArray(cfg.options) && cfg.options.length ? 2 : 0\n const dictionaryBonus = typeof cfg.dictionaryId === 'string' && (cfg.dictionaryId as string).trim().length ? 5 : 0\n const base = (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus\n const penalty = typeof cfg.priority === 'number' ? cfg.priority : 0\n return { base, penalty, entityIndex }\n}\n\n/**\n * BasicQueryEngine \u2014 Kysely-backed fallback query engine.\n *\n * Resolves base tables via MikroORM metadata, applies tenant/organization/\n * deleted_at scoping, handles custom field (cf:*) selection and filtering,\n * and performs entity-extension joins. Used as the fallback for\n * {@link HybridQueryEngine} when the query index is unavailable or incomplete.\n */\nexport class BasicQueryEngine implements QueryEngine {\n private columnCache = new Map<string, boolean>()\n private tableCache = new Map<string, boolean>()\n private searchAliasSeq = 0\n\n constructor(\n private em: EntityManager,\n private getDbFn?: () => AnyDb,\n private resolveEncryptionService?: EncryptionResolver,\n ) {}\n\n private getEncryptionService() {\n try {\n return this.resolveEncryptionService?.() ?? null\n } catch {\n return null\n }\n }\n\n private getDb(): AnyDb {\n if (this.getDbFn) return this.getDbFn()\n const emAny = this.em as any\n if (typeof emAny?.getKysely === 'function') return emAny.getKysely() as AnyDb\n throw new Error('BasicQueryEngine requires an EntityManager exposing getKysely() (MikroORM v7)')\n }\n\n async query<T = any>(entity: EntityId, opts: QueryOptions = {}): Promise<QueryResult<T>> {\n // --- UMES query extension: before-query pipeline ---\n const ext = opts.extensions\n let effectiveOpts = opts\n let extensionCtx: QueryExtensionContext | null = null\n const noop = { resolve: <R = unknown>(_name: string): R => { throw new Error('No DI context') } }\n\n if (ext) {\n extensionCtx = {\n entity: String(entity),\n engine: 'basic',\n tenantId: opts.tenantId ?? '',\n organizationId: opts.organizationId,\n userId: ext.userId,\n em: this.em,\n container: ext.container,\n userFeatures: ext.userFeatures,\n }\n const diCtx = ext.resolve ? { resolve: ext.resolve } : noop\n const beforeResult = await runBeforeQueryPipeline(opts, extensionCtx, diCtx)\n if (beforeResult.blocked) {\n throw new Error(beforeResult.errorMessage ?? 'Query blocked by extension subscriber')\n }\n effectiveOpts = beforeResult.query\n }\n // Strip extensions from effectiveOpts so they don't propagate to sub-queries\n const { extensions: _ext, ...coreOpts } = effectiveOpts\n opts = coreOpts\n\n // Heuristic: map '<module>:user' -> table 'users'\n const table = resolveEntityTableName(this.em, entity)\n const db = this.getDb()\n\n let q: AnyBuilder = db.selectFrom(table as any)\n const qualify = (col: string) => `${table}.${col}`\n const orgScope = this.resolveOrganizationScope(opts)\n this.searchAliasSeq = 0\n // Require tenant scope for all queries\n if (!opts.tenantId) {\n throw new Error(\n 'QueryEngine: tenantId is now required for all queries (breaking change). ' +\n 'Please provide a tenantId in QueryOptions, e.g., query(entity, { tenantId: ... }). ' +\n 'See migration guide or documentation for details.'\n )\n }\n const skipAutoScope = opts.omitAutomaticTenantOrgScope === true\n // Optional organization filter (when present in schema)\n if (!skipAutoScope && orgScope && await this.columnExists(table, 'organization_id')) {\n q = this.applyOrganizationScope(q, qualify('organization_id'), orgScope)\n }\n // Tenant guard (required) when present in schema\n if (!skipAutoScope && await this.columnExists(table, 'tenant_id')) {\n q = q.where(qualify('tenant_id'), '=', opts.tenantId)\n }\n // Default soft-delete guard: exclude rows with deleted_at when column exists\n if (!opts.withDeleted && await this.columnExists(table, 'deleted_at')) {\n q = q.where(qualify('deleted_at'), 'is', null)\n }\n\n const normalizedFilters = normalizeFilters(opts.filters)\n const resolvedJoins = resolveJoins(\n table,\n [...(opts.joins ?? []), ...buildFilterableCustomFieldJoins(opts.customFieldSources)],\n (entityId) => resolveEntityTableName(this.em, entityId as any),\n )\n const joinMap = new Map<string, ResolvedJoin>()\n const aliasTables = new Map<string, string>()\n aliasTables.set(table, table)\n aliasTables.set('base', table)\n for (const join of resolvedJoins) {\n joinMap.set(join.alias, join)\n aliasTables.set(join.alias, join.table)\n }\n const { baseFilters, joinFilters } = partitionFilters(table, normalizedFilters, joinMap)\n const cfFilters = normalizedFilters.filter((filter) => String(filter.field).startsWith('cf:'))\n const searchConfig = resolveSearchConfig()\n const searchEnabled = searchConfig.enabled && await this.tableExists('search_tokens')\n const hasSearchTokens = searchEnabled\n ? await this.hasSearchTokens(String(entity), opts.tenantId ?? null, orgScope)\n : false\n const searchActive = searchEnabled && hasSearchTokens\n const joinSearchAvailability = new Map<string, boolean>()\n const searchFilters = [...baseFilters, ...cfFilters].filter((filter) => filter.op === 'like' || filter.op === 'ilike')\n if (searchFilters.length) {\n const fields = searchFilters.map((filter) => String(filter.field))\n this.logSearchDebug('search:init', {\n entity: String(entity),\n table,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n fields,\n searchEnabled,\n hasSearchTokens,\n searchActive,\n searchConfig: {\n enabled: searchConfig.enabled,\n minTokenLength: searchConfig.minTokenLength,\n enablePartials: searchConfig.enablePartials,\n hashAlgorithm: searchConfig.hashAlgorithm,\n blocklistedFields: searchConfig.blocklistedFields,\n },\n })\n if (!searchEnabled) {\n this.logSearchDebug('search:disabled', { entity: String(entity), table })\n } else if (!hasSearchTokens) {\n this.logSearchDebug('search:no-search-tokens', {\n entity: String(entity),\n table,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n })\n }\n }\n const recordIdColumn = qualify('id')\n\n const applyFilterOp = (builder: AnyBuilder, column: string | RawBuilder<unknown>, op: string, value: unknown, fieldName?: string): AnyBuilder => {\n if (\n (op === 'like' || op === 'ilike') &&\n searchActive &&\n typeof value === 'string' &&\n fieldName &&\n typeof column === 'string'\n ) {\n const tokens = tokenizeText(String(value), searchConfig)\n const hashes = tokens.hashes\n if (hashes.length) {\n const result = this.applySearchTokens(builder, {\n entity: String(entity),\n field: fieldName,\n hashes,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n tokens: tokens.tokens,\n })\n this.logSearchDebug('search:filter', {\n entity: String(entity),\n field: fieldName,\n tokens: tokens.tokens,\n hashes,\n applied: result.applied,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n })\n if (result.applied) return result.builder\n } else {\n this.logSearchDebug('search:skip-empty-hashes', {\n entity: String(entity),\n field: fieldName,\n value,\n })\n }\n }\n return this.applyColumnOp(builder, column, op, value)\n }\n\n // `eq` is accepted alongside `like`/`ilike` so that filters against\n // encrypted joined columns (whose ciphertext cannot be compared for\n // equality in SQL) can still resolve via tokenized search. Routing\n // only applies when `searchEnabled` is true AND the joined entity has\n // search tokens installed (`searchAvailable`); for non-searchable or\n // non-encrypted columns the caller still falls through to exact SQL\n // equality via `applyFilterOp`. Note that token match is approximate \u2014\n // callers needing strict equality on encrypted fields should filter on\n // the deterministic `*_hash` column instead.\n const applyJoinFilterOp = async (\n builder: AnyBuilder,\n filter: { column: string; op: string; value?: unknown },\n _qualified: string,\n join: ResolvedJoin,\n ): Promise<{ applied: boolean; builder: AnyBuilder }> => {\n if (!searchEnabled || !join.entityId) return { applied: false, builder }\n if (!['like', 'ilike'].includes(filter.op)) return { applied: false, builder }\n if (typeof filter.value !== 'string' || filter.value.trim().length === 0) return { applied: false, builder }\n\n let searchAvailable = joinSearchAvailability.get(join.entityId)\n if (searchAvailable === undefined) {\n searchAvailable = await this.hasSearchTokens(join.entityId, opts.tenantId ?? null, orgScope)\n joinSearchAvailability.set(join.entityId, searchAvailable)\n }\n if (!searchAvailable) return { applied: false, builder }\n\n const tokens = tokenizeText(String(filter.value), searchConfig)\n if (!tokens.hashes.length) return { applied: false, builder }\n\n const result = this.applySearchTokens(builder, {\n entity: join.entityId,\n field: filter.column,\n hashes: tokens.hashes,\n recordIdColumn: `${join.alias}.id`,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n tokens: tokens.tokens,\n })\n return { applied: result.applied, builder: result.builder }\n }\n\n const regularBaseFilters = baseFilters.filter((f) => !f.orGroup)\n const orGroupFilters = baseFilters.filter((f) => f.orGroup)\n\n for (const filter of regularBaseFilters) {\n const fieldName = String(filter.field)\n let qualified = filter.qualified ?? null\n if (!qualified) {\n const column = await this.resolveBaseColumn(table, fieldName)\n if (!column) {\n q = this.applyIndexDocFilter(q, {\n entity: String(entity),\n field: fieldName,\n op: filter.op,\n value: filter.value,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n withDeleted: opts.withDeleted === true,\n searchActive,\n searchConfig,\n })\n continue\n }\n qualified = qualify(column)\n }\n q = applyFilterOp(q, qualified, filter.op, filter.value, fieldName)\n }\n\n // OR-grouped filters: AND within each group (one $or disjunct), OR between groups.\n if (orGroupFilters.length > 0) {\n const groups = new Map<string, typeof orGroupFilters>()\n for (const f of orGroupFilters) {\n const group = groups.get(f.orGroup!) ?? []\n group.push(f)\n groups.set(f.orGroup!, group)\n }\n const resolvedGroupFilters: Array<Array<{ qualified: string; op: string; value: unknown; fieldName: string }>> = []\n for (const [, groupFilters] of groups) {\n const resolved: Array<{ qualified: string; op: string; value: unknown; fieldName: string }> = []\n for (const filter of groupFilters) {\n const column = await this.resolveBaseColumn(table, String(filter.field))\n if (column) {\n resolved.push({\n qualified: qualify(column),\n op: filter.op,\n value: filter.value,\n fieldName: String(filter.field),\n })\n }\n }\n if (resolved.length > 0) resolvedGroupFilters.push(resolved)\n }\n if (resolvedGroupFilters.length > 0) {\n q = q.where((eb: any) => eb.or(\n resolvedGroupFilters.map((group) => eb.and(\n group.map((rf) => this.buildColumnOpExpression(eb, rf.qualified, rf.op, rf.value))\n ))\n ))\n }\n }\n\n const applyAliasScopes = async (builder: AnyBuilder, aliasName: string): Promise<AnyBuilder> => {\n const targetTable = aliasTables.get(aliasName)\n if (!targetTable) return builder\n let next = builder\n if (!skipAutoScope && orgScope && await this.columnExists(targetTable, 'organization_id')) {\n next = this.applyOrganizationScope(next, `${aliasName}.organization_id`, orgScope)\n }\n if (!skipAutoScope && opts.tenantId && await this.columnExists(targetTable, 'tenant_id')) {\n next = next.where(`${aliasName}.tenant_id`, '=', opts.tenantId)\n }\n return next\n }\n q = await applyJoinFilters({\n db,\n baseTable: table,\n builder: q,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase: (column) => qualify(column),\n applyAliasScope: (builder, alias) => applyAliasScopes(builder, alias),\n applyFilterOp: (builder, column, op, value) => applyFilterOp(builder, column, op, value),\n applyJoinFilterOp,\n columnExists: (tbl, column) => this.columnExists(tbl, column),\n })\n // Selection (base columns only here; cf:* handled later)\n if (opts.fields && opts.fields.length) {\n const cols = opts.fields.filter((f) => !f.startsWith('cf:'))\n for (const c of cols) {\n // Qualify and alias to base names to avoid ambiguity\n q = q.select(sql.ref(qualify(c)).as(c))\n }\n } else {\n // Default to selecting only base table columns to avoid ambiguity when joining\n q = q.select(sql`${sql.ref(table)}.*`.as('__all'))\n }\n\n // Resolve which custom fields to include\n const tenantId = opts.tenantId\n const sanitize = (s: string) => s.replace(/[^a-zA-Z0-9_]/g, '_')\n const cfSourcesResult = this.configureCustomFieldSources(q, table, entity, db, opts, qualify)\n q = cfSourcesResult.builder\n const cfSources = cfSourcesResult.sources\n const entityIdToSource = new Map<string, ResolvedCustomFieldSource>()\n for (const source of cfSources) {\n entityIdToSource.set(String(source.entityId), source)\n }\n const requestedCustomFieldKeys = Array.isArray(opts.includeCustomFields)\n ? opts.includeCustomFields.map((key) => String(key))\n : []\n const cfKeys = new Set<string>()\n const keySource = new Map<string, ResolvedCustomFieldSource>()\n // Explicit in fields/filters\n for (const f of (opts.fields || [])) {\n if (typeof f === 'string' && f.startsWith('cf:')) cfKeys.add(f.slice(3))\n }\n for (const f of cfFilters) {\n if (typeof f.field === 'string' && f.field.startsWith('cf:')) cfKeys.add(f.field.slice(3))\n }\n if (opts.includeCustomFields === true) {\n if (entityIdToSource.size > 0) {\n const entityIdList = Array.from(entityIdToSource.keys())\n const entityOrder = new Map<string, number>()\n entityIdList.forEach((id, idx) => entityOrder.set(id, idx))\n const rows = await db\n .selectFrom('custom_field_defs' as any)\n .select(['key' as any, 'entity_id' as any, 'config_json' as any, 'kind' as any])\n .where('entity_id' as any, 'in', entityIdList)\n .where('is_active' as any, '=', true)\n .where((eb: any) => eb.or([\n eb('tenant_id' as any, '=', tenantId),\n eb('tenant_id' as any, 'is', null),\n ]))\n .execute() as Array<{ key: string; entity_id: string; config_json: unknown; kind: string }>\n type CustomFieldDefinitionRow = {\n key: string\n entityId: string\n kind: string\n config: Record<string, unknown>\n }\n const sorted: CustomFieldDefinitionRow[] = rows.map((row) => {\n const raw = row.config_json\n let cfg: Record<string, any> = {}\n if (raw && typeof raw === 'string') {\n try { cfg = JSON.parse(raw) } catch { cfg = {} }\n } else if (raw && typeof raw === 'object') {\n cfg = raw as Record<string, any>\n }\n return {\n key: String(row.key),\n entityId: String(row.entity_id),\n kind: String(row.kind || ''),\n config: cfg,\n }\n })\n sorted.sort((a, b) => {\n const ai = entityOrder.get(a.entityId) ?? Number.MAX_SAFE_INTEGER\n const bi = entityOrder.get(b.entityId) ?? Number.MAX_SAFE_INTEGER\n if (ai !== bi) return ai - bi\n return a.key.localeCompare(b.key)\n })\n const selectedSources = new Map<string, { source: ResolvedCustomFieldSource; score: number; penalty: number; entityIndex: number }>()\n for (const row of sorted) {\n const source = entityIdToSource.get(row.entityId)\n if (!source) continue\n const cfg = row.config || {}\n const entityIndex = entityOrder.get(row.entityId) ?? Number.MAX_SAFE_INTEGER\n const scores = computeCustomFieldScore(cfg, row.kind, entityIndex)\n const existing = selectedSources.get(row.key)\n if (!existing || scores.base > existing.score || (scores.base === existing.score && (scores.penalty < existing.penalty || (scores.penalty === existing.penalty && scores.entityIndex < existing.entityIndex)))) {\n selectedSources.set(row.key, { source, score: scores.base, penalty: scores.penalty, entityIndex: scores.entityIndex })\n }\n cfKeys.add(row.key)\n }\n for (const [key, entry] of selectedSources.entries()) {\n keySource.set(key, entry.source)\n }\n }\n } else if (requestedCustomFieldKeys.length > 0) {\n for (const key of requestedCustomFieldKeys) cfKeys.add(key)\n }\n const unresolvedKeys = Array.from(cfKeys).filter((key) => !keySource.has(key))\n if (unresolvedKeys.length > 0 && entityIdToSource.size > 0) {\n const rows = await db\n .selectFrom('custom_field_defs' as any)\n .select(['key' as any, 'entity_id' as any])\n .where('entity_id' as any, 'in', Array.from(entityIdToSource.keys()))\n .where('key' as any, 'in', unresolvedKeys)\n .where('is_active' as any, '=', true)\n .where((eb: any) => eb.or([\n eb('tenant_id' as any, '=', tenantId),\n eb('tenant_id' as any, 'is', null),\n ]))\n .execute() as Array<{ key: string; entity_id: string }>\n for (const row of rows) {\n const source = entityIdToSource.get(String(row.entity_id))\n if (!source) continue\n if (!keySource.has(row.key)) keySource.set(row.key, source)\n }\n }\n\n const cfValueExprByKey: Record<string, RawBuilder<string | null>> = {}\n const cfSelectedAliases: string[] = []\n const cfJsonAliases = new Set<string>()\n const cfMultiAliasByAlias = new Map<string, string>()\n for (const key of cfKeys) {\n const source = keySource.get(key)\n if (!source) continue\n const entityIdForKey = source.entityId\n const recordIdExpr = source.recordIdExpr\n const sourceAliasSafe = sanitize(source.alias || 'src')\n const keyAliasSafe = sanitize(key)\n const defAlias = `cfd_${sourceAliasSafe}_${keyAliasSafe}`\n const valAlias = `cfv_${sourceAliasSafe}_${keyAliasSafe}`\n // Join definitions for kind resolution\n q = q.leftJoin(`custom_field_defs as ${defAlias}` as any, (jb: any) =>\n jb.on(`${defAlias}.entity_id`, '=', String(entityIdForKey))\n .on(`${defAlias}.key`, '=', key)\n .on(`${defAlias}.is_active`, '=', true)\n .on((eb: any) => eb.or([\n eb(`${defAlias}.tenant_id`, '=', tenantId),\n eb(`${defAlias}.tenant_id`, 'is', null),\n ]))\n )\n // Join values with record match\n q = q.leftJoin(`custom_field_values as ${valAlias}` as any, (jb: any) =>\n jb.on(`${valAlias}.entity_id`, '=', String(entityIdForKey))\n .on(`${valAlias}.field_key`, '=', key)\n .onRef(`${valAlias}.record_id`, '=', recordIdExpr as any)\n .on((eb: any) => eb.or([\n eb(`${valAlias}.tenant_id`, '=', tenantId),\n eb(`${valAlias}.tenant_id`, 'is', null),\n ]))\n )\n // Force a common SQL type across branches to avoid Postgres CASE type conflicts\n const caseExpr = sql<string | null>`CASE ${sql.ref(`${defAlias}.kind`)}\n WHEN 'integer' THEN (${sql.ref(`${valAlias}.value_int`)})::text\n WHEN 'float' THEN (${sql.ref(`${valAlias}.value_float`)})::text\n WHEN 'boolean' THEN (${sql.ref(`${valAlias}.value_bool`)})::text\n WHEN 'multiline' THEN (${sql.ref(`${valAlias}.value_multiline`)})::text\n ELSE (${sql.ref(`${valAlias}.value_text`)})::text\n END`\n cfValueExprByKey[key] = caseExpr\n const alias = sanitize(`cf:${key}`)\n // Project as aggregated to avoid duplicates when multi values exist\n if ((opts.fields || []).includes(`cf:${key}`) || opts.includeCustomFields === true || (requestedCustomFieldKeys.length > 0 && requestedCustomFieldKeys.includes(key))) {\n const multiAlias = `${alias}__is_multi`\n const isMultiExpr = sql<boolean>`bool_or(coalesce((${sql.ref(`${defAlias}.config_json`)}->>'multi')::boolean, false))`\n const aggregatedArray = sql<unknown>`array_remove(array_agg(DISTINCT ${caseExpr}), NULL)`\n const projExpr = sql<unknown>`CASE WHEN ${isMultiExpr}\n THEN to_jsonb(${aggregatedArray})\n ELSE to_jsonb(max(${caseExpr}))\n END`\n q = q.select(projExpr.as(alias))\n q = q.select(isMultiExpr.as(multiAlias))\n cfSelectedAliases.push(alias)\n cfJsonAliases.add(alias)\n cfMultiAliasByAlias.set(alias, multiAlias)\n }\n }\n\n // Apply cf:* filters (on raw expressions)\n for (const f of cfFilters) {\n if (!f.field.startsWith('cf:')) continue\n const key = f.field.slice(3)\n const expr = cfValueExprByKey[key]\n if (!expr) continue\n if ((f.op === 'like' || f.op === 'ilike') && searchActive && typeof f.value === 'string') {\n const tokens = tokenizeText(String(f.value), searchConfig)\n const hashes = tokens.hashes\n if (hashes.length) {\n const result = this.applySearchTokens(q, {\n entity: String(entity),\n field: f.field,\n hashes,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n tokens: tokens.tokens,\n })\n this.logSearchDebug('search:cf-filter', {\n entity: String(entity),\n field: f.field,\n tokens: tokens.tokens,\n hashes,\n applied: result.applied,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n })\n if (result.applied) {\n q = result.builder\n continue\n }\n } else {\n this.logSearchDebug('search:cf-skip-empty-hashes', {\n entity: String(entity),\n field: f.field,\n value: f.value,\n })\n }\n }\n q = this.applyColumnOp(q, expr, f.op, f.value)\n }\n\n // Entity extensions joins (no selection yet; enables future filters/projections)\n if (opts.includeExtensions) {\n const { getModules } = await import('@open-mercato/shared/lib/i18n/server')\n const allMods = getModules() as any[]\n const allExts = allMods.flatMap((m) => (m as any).entityExtensions || [])\n const exts = allExts.filter((e: any) => e.base === entity)\n const chosen = Array.isArray(opts.includeExtensions)\n ? exts.filter((e: any) => (opts.includeExtensions as string[]).includes(e.extension))\n : exts\n for (const e of chosen) {\n const [, extName] = (e.extension as string).split(':')\n const extTable = extName.endsWith('s') ? extName : `${extName}s`\n const alias = `ext_${sanitize(extName)}`\n q = q.leftJoin(`${extTable} as ${alias}` as any, (jb: any) =>\n jb.onRef(`${alias}.${e.join.extensionKey}`, '=', `${table}.${e.join.baseKey}`)\n )\n }\n }\n\n // Sorting: base fields and cf:* (use aggregated alias for cf)\n for (const s of opts.sort || []) {\n if (s.field.startsWith('cf:')) {\n const key = s.field.slice(3)\n const alias = sanitize(`cf:${key}`)\n // Ensure included in projection to sort by\n if (!cfSelectedAliases.includes(alias)) {\n const expr = cfValueExprByKey[key]\n if (expr) {\n q = q.select(sql<string | null>`max(${expr})`.as(alias))\n cfSelectedAliases.push(alias)\n }\n }\n q = q.orderBy(alias, (s.dir ?? 'asc') as any)\n } else {\n const column = await this.resolveBaseColumn(table, s.field)\n if (!column) continue\n q = q.orderBy(qualify(column), (s.dir ?? 'asc') as any)\n }\n }\n\n // Pagination\n const page = opts.page?.page ?? 1\n const pageSize = opts.page?.pageSize ?? 20\n // Deduplicate if we joined CFs or extensions by grouping on base id\n const hasJoinedAggregates = (opts.includeExtensions && (Array.isArray(opts.includeExtensions) ? (opts.includeExtensions.length > 0) : true)) || Object.keys(cfValueExprByKey).length > 0\n if (hasJoinedAggregates) {\n q = q.groupBy(`${table}.id`)\n }\n const countBuilder = hasJoinedAggregates\n ? q.clearSelect().clearOrderBy().clearGroupBy().select(sql<string>`count(distinct ${sql.ref(`${table}.id`)})`.as('count'))\n : q.clearSelect().clearOrderBy().select(sql<string>`count(distinct ${sql.ref(`${table}.id`)})`.as('count'))\n const countRow = await countBuilder.executeTakeFirst() as { count: unknown } | undefined\n const total = Number((countRow as any)?.count ?? 0)\n const items = await q.limit(pageSize).offset((page - 1) * pageSize).execute() as any[]\n\n if (cfJsonAliases.size > 0) {\n for (const row of items) {\n for (const alias of cfJsonAliases) {\n const multiAlias = cfMultiAliasByAlias.get(alias)\n const isMulti = multiAlias ? Boolean(row[multiAlias]) : false\n let raw = row[alias]\n if (typeof raw === 'string') {\n try { raw = JSON.parse(raw) } catch { /* ignore malformed json */ }\n }\n if (isMulti) {\n if (raw == null) row[alias] = []\n else if (Array.isArray(raw)) row[alias] = raw\n else row[alias] = [raw]\n } else {\n if (Array.isArray(raw)) row[alias] = raw.length > 0 ? raw[0] : null\n else row[alias] = raw\n }\n if (multiAlias) delete row[multiAlias]\n }\n }\n }\n\n const svc = this.getEncryptionService()\n const decryptPayload =\n svc?.decryptEntityPayload?.bind(svc) as\n | ((\n entityId: EntityId,\n payload: Record<string, unknown>,\n tenantId: string | null,\n organizationId: string | null,\n ) => Promise<Record<string, unknown>>)\n | null\n let decryptedItems = items\n if (decryptPayload) {\n const fallbackOrgId =\n opts.organizationId\n ?? (Array.isArray(opts.organizationIds) && opts.organizationIds.length === 1 ? opts.organizationIds[0] : null)\n decryptedItems = await Promise.all(\n (items as any[]).map(async (item) => {\n try {\n const decrypted = await decryptPayload(\n entity,\n item,\n item?.tenant_id ?? item?.tenantId ?? opts.tenantId ?? null,\n item?.organization_id ?? item?.organizationId ?? fallbackOrgId ?? null,\n )\n return { ...item, ...decrypted }\n } catch (err) {\n console.error('QueryEngine: error decrypting entity payload', err);\n return item\n }\n })\n )\n }\n\n let queryResult: QueryResult<T> = { items: decryptedItems, page, pageSize, total }\n\n // --- UMES query extension: after-query pipeline ---\n if (ext && extensionCtx) {\n const diCtx = ext.resolve ? { resolve: ext.resolve } : noop\n queryResult = await runAfterQueryPipeline(\n queryResult as QueryResult<Record<string, unknown>>,\n opts,\n extensionCtx,\n diCtx,\n ) as QueryResult<T>\n }\n\n return queryResult\n }\n\n private applyColumnOp(builder: AnyBuilder, column: string | RawBuilder<unknown>, op: string, value: unknown): AnyBuilder {\n switch (op) {\n case 'eq':\n return value === null\n ? builder.where(column as any, 'is', null)\n : builder.where(column as any, '=', value as any)\n case 'ne':\n return value === null\n ? builder.where(column as any, 'is not', null)\n : builder.where(column as any, '!=', value as any)\n case 'gt':\n return builder.where(column as any, '>', value as any)\n case 'gte':\n return builder.where(column as any, '>=', value as any)\n case 'lt':\n return builder.where(column as any, '<', value as any)\n case 'lte':\n return builder.where(column as any, '<=', value as any)\n case 'in':\n return builder.where(column as any, 'in', Array.isArray(value) ? value : [value])\n case 'nin':\n return builder.where(column as any, 'not in', Array.isArray(value) ? value : [value])\n case 'like':\n return builder.where(column as any, 'like', value as any)\n case 'ilike':\n return builder.where(column as any, 'ilike', value as any)\n case 'exists':\n return value\n ? builder.where(column as any, 'is not', null)\n : builder.where(column as any, 'is', null)\n default:\n return builder\n }\n }\n\n private buildColumnOpExpression(eb: any, column: string, op: string, value: unknown): any {\n switch (op) {\n case 'eq': return value === null ? eb(column, 'is', null) : eb(column, '=', value)\n case 'ne': return value === null ? eb(column, 'is not', null) : eb(column, '!=', value)\n case 'gt': return eb(column, '>', value)\n case 'gte': return eb(column, '>=', value)\n case 'lt': return eb(column, '<', value)\n case 'lte': return eb(column, '<=', value)\n case 'in': return eb(column, 'in', Array.isArray(value) ? value : [value])\n case 'nin': return eb(column, 'not in', Array.isArray(value) ? value : [value])\n case 'like': return eb(column, 'like', value)\n case 'ilike': return eb(column, 'ilike', value)\n case 'exists': return value ? eb(column, 'is not', null) : eb(column, 'is', null)\n default: return eb.val(true)\n }\n }\n\n private async resolveBaseColumn(table: string, field: string): Promise<string | null> {\n if (await this.columnExists(table, field)) return field\n if (field === 'organization_id' && await this.columnExists(table, 'id')) return 'id'\n return null\n }\n\n private async columnExists(table: string, column: string): Promise<boolean> {\n const key = `${table}.${column}`\n if (this.columnCache.has(key)) {\n const cached = this.columnCache.get(key)\n if (cached === true) return true\n this.columnCache.delete(key)\n }\n const db = this.getDb()\n const exists = await db\n .selectFrom('information_schema.columns' as any)\n .select(sql<number>`1`.as('one'))\n .where('table_name' as any, '=', table)\n .where('column_name' as any, '=', column)\n .limit(1)\n .executeTakeFirst()\n const present = !!exists\n if (present) this.columnCache.set(key, true)\n else this.columnCache.delete(key)\n return present\n }\n\n private async tableExists(table: string): Promise<boolean> {\n if (this.tableCache.has(table)) return this.tableCache.get(table) ?? false\n const db = this.getDb()\n const exists = await db\n .selectFrom('information_schema.tables' as any)\n .select(sql<number>`1`.as('one'))\n .where('table_name' as any, '=', table)\n .limit(1)\n .executeTakeFirst()\n const present = !!exists\n this.tableCache.set(table, present)\n return present\n }\n\n private async hasSearchTokens(\n entity: string,\n tenantId: string | null,\n orgScope?: { ids: string[]; includeNull: boolean } | null\n ): Promise<boolean> {\n try {\n const db = this.getDb()\n let query: AnyBuilder = db\n .selectFrom('search_tokens' as any)\n .select(sql<number>`1`.as('one'))\n .where('entity_type' as any, '=', entity)\n .limit(1)\n if (tenantId !== undefined) {\n query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantId}`)\n }\n if (orgScope) {\n query = this.applyOrganizationScope(query, 'search_tokens.organization_id', orgScope)\n }\n const row = await query.executeTakeFirst()\n return !!row\n } catch (err) {\n this.logSearchDebug('search:has-tokens-error', {\n entity,\n tenantId,\n organizationScope: orgScope,\n error: err instanceof Error ? err.message : String(err),\n })\n return false\n }\n }\n\n private applySearchTokens(\n q: AnyBuilder,\n opts: {\n entity: string\n field: string\n hashes: string[]\n recordIdColumn: string\n tenantId?: string | null\n organizationScope?: { ids: string[]; includeNull: boolean } | null\n combineWith?: 'and' | 'or'\n tokens?: string[]\n }\n ): { applied: boolean; builder: AnyBuilder } {\n if (!opts.hashes.length) {\n this.logSearchDebug('search:skip-no-hashes', {\n entity: opts.entity,\n field: opts.field,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n })\n return { applied: false, builder: q }\n }\n const alias = `st_${this.searchAliasSeq++}`\n const engine = this\n this.logSearchDebug('search:apply-search-tokens', {\n entity: opts.entity,\n field: opts.field,\n alias,\n tokenCount: opts.hashes.length,\n tokens: opts.tokens,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n combineWith: opts.combineWith ?? 'and',\n })\n const buildSub = (eb: any) => {\n let sub: AnyBuilder = eb\n .selectFrom(`search_tokens as ${alias}`)\n .select(sql<number>`1`.as('one'))\n .where(`${alias}.entity_type`, '=', opts.entity)\n .where(`${alias}.field`, '=', opts.field)\n .where(sql<boolean>`${sql.ref(`${alias}.entity_id`)} = ${sql.ref(opts.recordIdColumn)}::text`)\n .where(`${alias}.token_hash`, 'in', opts.hashes)\n .groupBy([`${alias}.entity_id`, `${alias}.field`])\n .having(sql<boolean>`count(distinct ${sql.ref(`${alias}.token_hash`)}) >= ${opts.hashes.length}`)\n if (opts.tenantId !== undefined) {\n sub = sub.where(sql<boolean>`${sql.ref(`${alias}.tenant_id`)} is not distinct from ${opts.tenantId ?? null}`)\n }\n if (opts.organizationScope) {\n sub = engine.applyOrganizationScope(sub, `${alias}.organization_id`, opts.organizationScope)\n }\n return sub\n }\n const combiner = opts.combineWith === 'or' ? 'or' : 'and'\n if (combiner === 'or') {\n // When OR combining, caller expects a raw predicate to include in eb.or([...]).\n // We keep the same semantics as the previous knex orWhereExists by mutating the outer builder with a WHERE EXISTS.\n // Return the mutated builder; callers that need per-predicate control should build the sub themselves.\n const next = q.where((eb: any) => eb.or([eb.exists(buildSub(eb))]))\n return { applied: true, builder: next }\n }\n const next = q.where((eb: any) => eb.exists(buildSub(eb)))\n return { applied: true, builder: next }\n }\n\n private applyIndexDocFilter(\n q: AnyBuilder,\n opts: {\n entity: string\n field: string\n op: NormalizedFilter['op']\n value: unknown\n recordIdColumn: string\n tenantId?: string | null\n organizationScope?: { ids: string[]; includeNull: boolean } | null\n withDeleted: boolean\n searchActive: boolean\n searchConfig: ReturnType<typeof resolveSearchConfig>\n }\n ): AnyBuilder {\n if ((opts.op === 'like' || opts.op === 'ilike') && opts.searchActive && typeof opts.value === 'string') {\n const tokens = tokenizeText(String(opts.value), opts.searchConfig)\n const hashes = tokens.hashes\n if (hashes.length) {\n const result = this.applySearchTokens(q, {\n entity: opts.entity,\n field: opts.field,\n hashes,\n recordIdColumn: opts.recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n tokens: tokens.tokens,\n })\n this.logSearchDebug('search:index-doc-filter', {\n entity: opts.entity,\n field: opts.field,\n tokens: tokens.tokens,\n hashes,\n applied: result.applied,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n })\n if (result.applied) return result.builder\n } else {\n this.logSearchDebug('search:index-doc-skip-empty-hashes', {\n entity: opts.entity,\n field: opts.field,\n value: opts.value,\n })\n }\n return q\n }\n\n const alias = `ei_${this.searchAliasSeq++}`\n const engine = this\n return q.where((eb: any) => eb.exists((() => {\n let sub: AnyBuilder = eb\n .selectFrom(`entity_indexes as ${alias}`)\n .select(sql<number>`1`.as('one'))\n .where(`${alias}.entity_type`, '=', opts.entity)\n .where(sql<boolean>`${sql.ref(`${alias}.entity_id`)} = ${sql.ref(opts.recordIdColumn)}::text`)\n if (opts.tenantId !== undefined) {\n sub = sub.where(sql<boolean>`${sql.ref(`${alias}.tenant_id`)} is not distinct from ${opts.tenantId ?? null}`)\n }\n if (opts.organizationScope) {\n sub = engine.applyOrganizationScope(sub, `${alias}.organization_id`, opts.organizationScope)\n }\n if (!opts.withDeleted) {\n sub = sub.where(`${alias}.deleted_at`, 'is', null)\n }\n\n const textExpr = sql<string | null>`(${sql.ref(`${alias}.doc`)} ->> ${opts.field})`\n switch (opts.op) {\n case 'eq':\n sub = sub.where(sql<boolean>`${textExpr} = ${opts.value}`); break\n case 'ne':\n sub = sub.where(sql<boolean>`${textExpr} <> ${opts.value}`); break\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = sql.raw(opts.op === 'gt' ? '>' : opts.op === 'gte' ? '>=' : opts.op === 'lt' ? '<' : '<=')\n sub = sub.where(sql<boolean>`${textExpr} ${operator} ${opts.value}`)\n break\n }\n case 'in': {\n const vals = Array.isArray(opts.value) ? opts.value : [opts.value]\n sub = sub.where(sql<boolean>`${textExpr} in (${sql.join(vals.map((v) => sql`${v}`), sql`, `)})`)\n break\n }\n case 'nin': {\n const vals = Array.isArray(opts.value) ? opts.value : [opts.value]\n sub = sub.where(sql<boolean>`${textExpr} not in (${sql.join(vals.map((v) => sql`${v}`), sql`, `)})`)\n break\n }\n case 'like':\n sub = sub.where(sql<boolean>`${textExpr} like ${opts.value}`); break\n case 'ilike':\n sub = sub.where(sql<boolean>`${textExpr} ilike ${opts.value}`); break\n case 'exists':\n sub = opts.value\n ? sub.where(sql<boolean>`${textExpr} is not null`)\n : sub.where(sql<boolean>`${textExpr} is null`)\n break\n default:\n break\n }\n return sub\n })()))\n }\n\n private configureCustomFieldSources(\n q: AnyBuilder,\n baseTable: string,\n baseEntity: EntityId,\n db: AnyDb,\n opts: QueryOptions,\n qualify: (column: string) => string,\n ): { builder: AnyBuilder; sources: ResolvedCustomFieldSource[] } {\n const sources: ResolvedCustomFieldSource[] = [\n {\n entityId: baseEntity,\n alias: 'base',\n table: baseTable,\n recordIdExpr: sql<string>`${sql.ref(`${baseTable}.id`)}::text`,\n },\n ]\n const extras: QueryCustomFieldSource[] = opts.customFieldSources ?? []\n let next = q\n extras.forEach((srcOpt, index) => {\n const joinTable = srcOpt.table ?? resolveEntityTableName(this.em, srcOpt.entityId)\n const alias = srcOpt.alias ?? `cfs_${index}`\n const join = srcOpt.join\n if (!join) {\n throw new Error(`QueryEngine: customFieldSources entry for ${String(srcOpt.entityId)} requires a join configuration`)\n }\n const joinFn = (join.type ?? 'left') === 'inner' ? 'innerJoin' : 'leftJoin'\n next = (next as any)[joinFn](`${joinTable} as ${alias}`, (jb: any) =>\n jb.onRef(`${alias}.${join.toField}`, '=', qualify(join.fromField)))\n const recordColumn = srcOpt.recordIdColumn ?? 'id'\n sources.push({\n entityId: srcOpt.entityId,\n alias,\n table: joinTable,\n recordIdExpr: sql<string>`${sql.ref(`${alias}.${recordColumn}`)}::text`,\n })\n })\n return { builder: next, sources }\n }\n\n private logSearchDebug(event: string, payload: Record<string, unknown>) {\n try {\n console.info('[query:search]', event, JSON.stringify(payload))\n } catch {\n console.info('[query:search]', event, payload)\n }\n }\n\n private resolveOrganizationScope(opts: QueryOptions): { ids: string[]; includeNull: boolean } | null {\n if (opts.organizationIds !== undefined) {\n const raw = (opts.organizationIds ?? []).map((id) => (typeof id === 'string' ? id.trim() : id))\n const includeNull = raw.some((id) => id == null || id === '')\n const ids = raw.filter((id): id is string => typeof id === 'string' && id.length > 0)\n return { ids: Array.from(new Set(ids)), includeNull }\n }\n if (typeof opts.organizationId === 'string' && opts.organizationId.trim().length > 0) {\n return { ids: [opts.organizationId], includeNull: false }\n }\n return null\n }\n\n private applyOrganizationScope(q: AnyBuilder, column: string, scope: { ids: string[]; includeNull: boolean }): AnyBuilder {\n if (!scope) return q\n if (scope.ids.length === 0 && !scope.includeNull) {\n return q.where(sql<boolean>`1 = 0`)\n }\n return q.where((eb: any) => {\n const parts: any[] = []\n if (scope.ids.length > 0) parts.push(eb(column, 'in', scope.ids))\n if (scope.includeNull) parts.push(eb(column, 'is', null))\n if (parts.length === 1) return parts[0]\n return eb.or(parts)\n })\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["next"]
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
1
2
|
function normalizeFilters(filters) {
|
|
2
3
|
if (!filters) return [];
|
|
3
4
|
const normalizeField = (key) => key.startsWith("cf_") ? `cf:${key.slice(3)}` : key;
|
|
@@ -156,7 +157,7 @@ function partitionFilters(baseTable, filters, joinMap) {
|
|
|
156
157
|
return { baseFilters, joinFilters };
|
|
157
158
|
}
|
|
158
159
|
async function applyJoinFilters({
|
|
159
|
-
|
|
160
|
+
db,
|
|
160
161
|
baseTable,
|
|
161
162
|
builder,
|
|
162
163
|
joinMap,
|
|
@@ -172,29 +173,22 @@ async function applyJoinFilters({
|
|
|
172
173
|
if (!aliasName || aliasName === "base") return baseTable;
|
|
173
174
|
return aliasName;
|
|
174
175
|
};
|
|
176
|
+
let nextBuilder = builder;
|
|
175
177
|
for (const [alias, filtersForAlias] of joinFilters.entries()) {
|
|
176
178
|
const chain = buildJoinChain(alias, joinMap, baseTable);
|
|
177
179
|
if (!chain.length) continue;
|
|
178
180
|
const first = chain[0];
|
|
179
|
-
|
|
180
|
-
await applyAliasScope(sub, first.alias, first.table);
|
|
181
|
+
let sub = db.selectFrom(`${first.table} as ${first.alias}`).select(sql`1`.as("one"));
|
|
182
|
+
sub = await applyAliasScope(sub, first.alias, first.table);
|
|
181
183
|
const parentAlias = resolveAliasName(first.fromAlias);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
} else {
|
|
185
|
-
sub.whereRaw("?? = ??", [`${first.alias}.${first.toField}`, `${parentAlias}.${first.fromField}`]);
|
|
186
|
-
}
|
|
184
|
+
const parentRef = parentAlias === baseTable ? qualifyBase(first.fromField) : `${parentAlias}.${first.fromField}`;
|
|
185
|
+
sub = sub.whereRef(`${first.alias}.${first.toField}`, "=", parentRef);
|
|
187
186
|
for (const cfg of chain.slice(1)) {
|
|
188
|
-
const joinArgs = { [cfg.alias]: cfg.table };
|
|
189
187
|
const parent = resolveAliasName(cfg.fromAlias);
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
};
|
|
195
|
-
if (cfg.type === "inner") sub.join(joinArgs, joinFn);
|
|
196
|
-
else sub.leftJoin(joinArgs, joinFn);
|
|
197
|
-
await applyAliasScope(sub, cfg.alias, cfg.table);
|
|
188
|
+
const rightRef = parent === baseTable ? qualifyBase(cfg.fromField) : `${parent}.${cfg.fromField}`;
|
|
189
|
+
const joinFn = cfg.type === "inner" ? "innerJoin" : "leftJoin";
|
|
190
|
+
sub = sub[joinFn](`${cfg.table} as ${cfg.alias}`, (jb) => jb.onRef(`${cfg.alias}.${cfg.toField}`, "=", rightRef));
|
|
191
|
+
sub = await applyAliasScope(sub, cfg.alias, cfg.table);
|
|
198
192
|
}
|
|
199
193
|
let existsDirective = null;
|
|
200
194
|
for (const filter of filtersForAlias) {
|
|
@@ -213,15 +207,26 @@ async function applyJoinFilters({
|
|
|
213
207
|
}
|
|
214
208
|
const qualified = `${filter.alias}.${filter.column}`;
|
|
215
209
|
if (applyJoinFilterOp) {
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
210
|
+
const result = await applyJoinFilterOp(sub, filter, qualified, join, targetTable);
|
|
211
|
+
if (result && result.applied) {
|
|
212
|
+
sub = result.builder;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (result && result.builder) {
|
|
216
|
+
sub = result.builder;
|
|
217
|
+
}
|
|
218
218
|
}
|
|
219
|
-
applyFilterOp(sub, qualified, filter.op, filter.value);
|
|
219
|
+
sub = applyFilterOp(sub, qualified, filter.op, filter.value);
|
|
220
|
+
}
|
|
221
|
+
if (existsDirective === false) {
|
|
222
|
+
const capturedSub = sub;
|
|
223
|
+
nextBuilder = nextBuilder.where((eb) => eb.not(eb.exists(capturedSub)));
|
|
224
|
+
} else {
|
|
225
|
+
const capturedSub = sub;
|
|
226
|
+
nextBuilder = nextBuilder.where((eb) => eb.exists(capturedSub));
|
|
220
227
|
}
|
|
221
|
-
if (existsDirective === false) builder = builder.whereNotExists(sub);
|
|
222
|
-
else builder = builder.whereExists(sub);
|
|
223
228
|
}
|
|
224
|
-
return
|
|
229
|
+
return nextBuilder;
|
|
225
230
|
}
|
|
226
231
|
export {
|
|
227
232
|
applyJoinFilters,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/query/join-utils.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Knex } from 'knex'\nimport type { QueryOptions, QueryJoinEdge } from './types'\nimport type { FilterOp } from './types'\n\nexport type NormalizedFilter = { field: string; op: FilterOp; value?: unknown; orGroup?: string; qualified?: string | null }\n\nexport function normalizeFilters(filters?: QueryOptions['filters']): NormalizedFilter[] {\n if (!filters) return []\n const normalizeField = (key: string) => (key.startsWith('cf_') ? `cf:${key.slice(3)}` : key)\n if (Array.isArray(filters)) {\n return (filters as any[]).map((f) => ({\n ...f,\n field: normalizeField(String((f as any).field)),\n }))\n }\n const out: NormalizedFilter[] = []\n const obj = filters as Record<string, unknown>\n const push = (field: string, op: FilterOp, value?: unknown, orGroup?: string) => {\n out.push({ field, op, value, orGroup })\n }\n // Handle $or at top level \u2014 one group id per disjunct so fields inside a clause are ANDed by the engine.\n if (Array.isArray(obj.$or)) {\n const clauses = obj.$or as Record<string, unknown>[]\n for (let clauseIndex = 0; clauseIndex < clauses.length; clauseIndex++) {\n const clause = clauses[clauseIndex]\n const orGroupId = `or_${clauseIndex}`\n if (clause && typeof clause === 'object') {\n for (const [rawKey, rawVal] of Object.entries(clause)) {\n const field = normalizeField(rawKey)\n if (rawVal !== null && typeof rawVal === 'object' && !Array.isArray(rawVal)) {\n for (const [opKey, opVal] of Object.entries(rawVal as Record<string, unknown>)) {\n const op = opKey.replace('$', '') as FilterOp\n if (['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'like', 'ilike', 'exists'].includes(op)) {\n push(field, op, opVal, orGroupId)\n }\n }\n } else {\n push(field, 'eq', rawVal, orGroupId)\n }\n }\n }\n }\n }\n for (const [rawKey, rawVal] of Object.entries(obj)) {\n if (rawKey === '$or') continue\n const field = normalizeField(rawKey)\n if (rawVal !== null && typeof rawVal === 'object' && !Array.isArray(rawVal)) {\n for (const [opKey, opVal] of Object.entries(rawVal as Record<string, unknown>)) {\n switch (opKey) {\n case '$eq':\n push(field, 'eq', opVal)\n break\n case '$ne':\n push(field, 'ne', opVal)\n break\n case '$gt':\n push(field, 'gt', opVal)\n break\n case '$gte':\n push(field, 'gte', opVal)\n break\n case '$lt':\n push(field, 'lt', opVal)\n break\n case '$lte':\n push(field, 'lte', opVal)\n break\n case '$in':\n push(field, 'in', opVal)\n break\n case '$nin':\n push(field, 'nin', opVal)\n break\n case '$like':\n push(field, 'like', opVal)\n break\n case '$ilike':\n push(field, 'ilike', opVal)\n break\n case '$exists':\n push(field, 'exists', opVal)\n break\n }\n }\n } else {\n push(field, 'eq', rawVal)\n }\n }\n return out\n}\n\nexport type ResolvedJoin = {\n alias: string\n table: string\n entityId?: string | null\n fromAlias: string\n fromField: string\n toField: string\n type: 'left' | 'inner'\n}\n\nexport type BaseFilter = NormalizedFilter & { qualified?: string | null }\nexport type JoinFilter = { alias: string; column: string; op: FilterOp; value?: unknown }\n\nexport function resolveJoins(\n baseTable: string,\n joins: QueryJoinEdge[] | null | undefined,\n resolveTable: (entityId: string) => string | null,\n): ResolvedJoin[] {\n if (!joins || joins.length === 0) return []\n const resolved: ResolvedJoin[] = []\n const seen = new Set<string>()\n for (const entry of joins) {\n if (!entry || typeof entry !== 'object') continue\n const alias = typeof entry.alias === 'string' ? entry.alias.trim() : ''\n if (!alias) continue\n if (seen.has(alias)) continue\n const table =\n entry.table ??\n (entry.entityId ? resolveTable(String(entry.entityId)) : null)\n if (!table) continue\n const fromField = entry.from?.field?.trim()\n const toField = entry.to?.field?.trim()\n if (!fromField || !toField) continue\n const fromAliasRaw = entry.from?.alias?.trim()\n const fromAlias = fromAliasRaw && fromAliasRaw.length > 0 ? fromAliasRaw : 'base'\n const type: 'left' | 'inner' = entry.type === 'inner' ? 'inner' : 'left'\n resolved.push({\n alias,\n table,\n entityId: entry.entityId ? String(entry.entityId) : null,\n fromAlias,\n fromField,\n toField,\n type,\n })\n seen.add(alias)\n }\n return resolved\n}\n\nexport function buildJoinChain(\n alias: string,\n joinMap: Map<string, ResolvedJoin>,\n baseTable: string,\n visited: Set<string> = new Set(),\n): ResolvedJoin[] {\n if (visited.has(alias)) {\n throw new Error(`QueryEngine: circular join reference detected for alias ${alias}`)\n }\n const cfg = joinMap.get(alias)\n if (!cfg) return []\n visited.add(alias)\n if (!cfg.fromAlias || cfg.fromAlias === 'base' || cfg.fromAlias === baseTable) {\n return [cfg]\n }\n const parentChain = buildJoinChain(cfg.fromAlias, joinMap, baseTable, visited)\n if (parentChain.length === 0) return []\n return [...parentChain, cfg]\n}\n\nexport function partitionFilters(\n baseTable: string,\n filters: NormalizedFilter[],\n joinMap: Map<string, ResolvedJoin>,\n): { baseFilters: BaseFilter[]; joinFilters: Map<string, JoinFilter[]> } {\n const baseFilters: BaseFilter[] = []\n const joinFilters = new Map<string, JoinFilter[]>()\n for (const filter of filters) {\n const field = String(filter.field)\n if (field.startsWith('cf:')) continue\n const parts = field.split('.')\n if (parts.length === 2) {\n const [aliasNameRaw, column] = parts\n const aliasName = aliasNameRaw || ''\n if (joinMap.has(aliasName)) {\n const list = joinFilters.get(aliasName) ?? []\n list.push({ alias: aliasName, column, op: filter.op, value: filter.value })\n joinFilters.set(aliasName, list)\n continue\n }\n if (aliasName === baseTable || aliasName === 'base') {\n baseFilters.push({\n field: column,\n op: filter.op,\n value: filter.value,\n qualified: `${baseTable}.${column}`,\n })\n continue\n }\n }\n baseFilters.push({ ...filter })\n }\n return { baseFilters, joinFilters }\n}\n\ntype ApplyJoinFiltersOptions = {\n knex: Knex\n baseTable: string\n builder: Knex.QueryBuilder\n joinMap: Map<string, ResolvedJoin>\n joinFilters: Map<string, JoinFilter[]>\n aliasTables: Map<string, string>\n qualifyBase: (column: string) => string\n applyAliasScope: (builder: Knex.QueryBuilder, alias: string, table: string) => Promise<void> | void\n applyFilterOp: (builder: Knex.QueryBuilder, column: string, op: FilterOp, value?: unknown) => void\n applyJoinFilterOp?: (builder: Knex.QueryBuilder, filter: JoinFilter, qualified: string, join: ResolvedJoin, table: string) => Promise<boolean> | boolean\n columnExists?: (table: string, column: string) => Promise<boolean> | boolean\n}\n\nexport async function applyJoinFilters({\n knex,\n baseTable,\n builder,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase,\n applyAliasScope,\n applyFilterOp,\n applyJoinFilterOp,\n columnExists,\n}: ApplyJoinFiltersOptions): Promise<Knex.QueryBuilder> {\n const resolveAliasName = (aliasName?: string | null) => {\n if (!aliasName || aliasName === 'base') return baseTable\n return aliasName\n }\n\n for (const [alias, filtersForAlias] of joinFilters.entries()) {\n const chain = buildJoinChain(alias, joinMap, baseTable)\n if (!chain.length) continue\n const first = chain[0]\n const sub = knex({ [first.alias]: first.table }).select(1)\n await applyAliasScope(sub, first.alias, first.table)\n const parentAlias = resolveAliasName(first.fromAlias)\n if (parentAlias === baseTable) {\n sub.whereRaw('?? = ??', [`${first.alias}.${first.toField}`, qualifyBase(first.fromField)])\n } else {\n sub.whereRaw('?? = ??', [`${first.alias}.${first.toField}`, `${parentAlias}.${first.fromField}`])\n }\n for (const cfg of chain.slice(1)) {\n const joinArgs = { [cfg.alias]: cfg.table }\n const parent = resolveAliasName(cfg.fromAlias)\n const joinFn = function (this: Knex.JoinClause) {\n const left = `${cfg.alias}.${cfg.toField}`\n const right = parent === baseTable ? qualifyBase(cfg.fromField) : `${parent}.${cfg.fromField}`\n this.on(knex.raw('?? = ??', [left, right]))\n }\n if (cfg.type === 'inner') sub.join(joinArgs, joinFn)\n else sub.leftJoin(joinArgs, joinFn)\n await applyAliasScope(sub, cfg.alias, cfg.table)\n }\n let existsDirective: boolean | null = null\n for (const filter of filtersForAlias) {\n if (filter.op === 'exists') {\n if (filter.value === false) existsDirective = false\n else if (existsDirective === null) existsDirective = true\n continue\n }\n const join = joinMap.get(filter.alias)\n if (!join) continue\n const targetTable = aliasTables.get(filter.alias)\n if (!targetTable) continue\n if (columnExists) {\n const exists = await columnExists(targetTable, filter.column)\n if (!exists) continue\n }\n const qualified = `${filter.alias}.${filter.column}`\n if (applyJoinFilterOp) {\n const handled = await applyJoinFilterOp(sub, filter, qualified, join, targetTable)\n if (handled) continue\n }\n applyFilterOp(sub, qualified, filter.op, filter.value)\n }\n if (existsDirective === false) builder = builder.whereNotExists(sub)\n else builder = builder.whereExists(sub)\n }\n return builder\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { Kysely } from 'kysely'\nimport { sql } from 'kysely'\nimport type { QueryOptions, QueryJoinEdge } from './types'\nimport type { FilterOp } from './types'\n\ntype AnyBuilder = any\n\nexport type NormalizedFilter = { field: string; op: FilterOp; value?: unknown; orGroup?: string; qualified?: string | null }\n\nexport function normalizeFilters(filters?: QueryOptions['filters']): NormalizedFilter[] {\n if (!filters) return []\n const normalizeField = (key: string) => (key.startsWith('cf_') ? `cf:${key.slice(3)}` : key)\n if (Array.isArray(filters)) {\n return (filters as any[]).map((f) => ({\n ...f,\n field: normalizeField(String((f as any).field)),\n }))\n }\n const out: NormalizedFilter[] = []\n const obj = filters as Record<string, unknown>\n const push = (field: string, op: FilterOp, value?: unknown, orGroup?: string) => {\n out.push({ field, op, value, orGroup })\n }\n // Handle $or at top level \u2014 one group id per disjunct so fields inside a clause are ANDed by the engine.\n if (Array.isArray(obj.$or)) {\n const clauses = obj.$or as Record<string, unknown>[]\n for (let clauseIndex = 0; clauseIndex < clauses.length; clauseIndex++) {\n const clause = clauses[clauseIndex]\n const orGroupId = `or_${clauseIndex}`\n if (clause && typeof clause === 'object') {\n for (const [rawKey, rawVal] of Object.entries(clause)) {\n const field = normalizeField(rawKey)\n if (rawVal !== null && typeof rawVal === 'object' && !Array.isArray(rawVal)) {\n for (const [opKey, opVal] of Object.entries(rawVal as Record<string, unknown>)) {\n const op = opKey.replace('$', '') as FilterOp\n if (['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'like', 'ilike', 'exists'].includes(op)) {\n push(field, op, opVal, orGroupId)\n }\n }\n } else {\n push(field, 'eq', rawVal, orGroupId)\n }\n }\n }\n }\n }\n for (const [rawKey, rawVal] of Object.entries(obj)) {\n if (rawKey === '$or') continue\n const field = normalizeField(rawKey)\n if (rawVal !== null && typeof rawVal === 'object' && !Array.isArray(rawVal)) {\n for (const [opKey, opVal] of Object.entries(rawVal as Record<string, unknown>)) {\n switch (opKey) {\n case '$eq':\n push(field, 'eq', opVal)\n break\n case '$ne':\n push(field, 'ne', opVal)\n break\n case '$gt':\n push(field, 'gt', opVal)\n break\n case '$gte':\n push(field, 'gte', opVal)\n break\n case '$lt':\n push(field, 'lt', opVal)\n break\n case '$lte':\n push(field, 'lte', opVal)\n break\n case '$in':\n push(field, 'in', opVal)\n break\n case '$nin':\n push(field, 'nin', opVal)\n break\n case '$like':\n push(field, 'like', opVal)\n break\n case '$ilike':\n push(field, 'ilike', opVal)\n break\n case '$exists':\n push(field, 'exists', opVal)\n break\n }\n }\n } else {\n push(field, 'eq', rawVal)\n }\n }\n return out\n}\n\nexport type ResolvedJoin = {\n alias: string\n table: string\n entityId?: string | null\n fromAlias: string\n fromField: string\n toField: string\n type: 'left' | 'inner'\n}\n\nexport type BaseFilter = NormalizedFilter & { qualified?: string | null }\nexport type JoinFilter = { alias: string; column: string; op: FilterOp; value?: unknown }\n\nexport function resolveJoins(\n baseTable: string,\n joins: QueryJoinEdge[] | null | undefined,\n resolveTable: (entityId: string) => string | null,\n): ResolvedJoin[] {\n if (!joins || joins.length === 0) return []\n const resolved: ResolvedJoin[] = []\n const seen = new Set<string>()\n for (const entry of joins) {\n if (!entry || typeof entry !== 'object') continue\n const alias = typeof entry.alias === 'string' ? entry.alias.trim() : ''\n if (!alias) continue\n if (seen.has(alias)) continue\n const table =\n entry.table ??\n (entry.entityId ? resolveTable(String(entry.entityId)) : null)\n if (!table) continue\n const fromField = entry.from?.field?.trim()\n const toField = entry.to?.field?.trim()\n if (!fromField || !toField) continue\n const fromAliasRaw = entry.from?.alias?.trim()\n const fromAlias = fromAliasRaw && fromAliasRaw.length > 0 ? fromAliasRaw : 'base'\n const type: 'left' | 'inner' = entry.type === 'inner' ? 'inner' : 'left'\n resolved.push({\n alias,\n table,\n entityId: entry.entityId ? String(entry.entityId) : null,\n fromAlias,\n fromField,\n toField,\n type,\n })\n seen.add(alias)\n }\n return resolved\n}\n\nexport function buildJoinChain(\n alias: string,\n joinMap: Map<string, ResolvedJoin>,\n baseTable: string,\n visited: Set<string> = new Set(),\n): ResolvedJoin[] {\n if (visited.has(alias)) {\n throw new Error(`QueryEngine: circular join reference detected for alias ${alias}`)\n }\n const cfg = joinMap.get(alias)\n if (!cfg) return []\n visited.add(alias)\n if (!cfg.fromAlias || cfg.fromAlias === 'base' || cfg.fromAlias === baseTable) {\n return [cfg]\n }\n const parentChain = buildJoinChain(cfg.fromAlias, joinMap, baseTable, visited)\n if (parentChain.length === 0) return []\n return [...parentChain, cfg]\n}\n\nexport function partitionFilters(\n baseTable: string,\n filters: NormalizedFilter[],\n joinMap: Map<string, ResolvedJoin>,\n): { baseFilters: BaseFilter[]; joinFilters: Map<string, JoinFilter[]> } {\n const baseFilters: BaseFilter[] = []\n const joinFilters = new Map<string, JoinFilter[]>()\n for (const filter of filters) {\n const field = String(filter.field)\n if (field.startsWith('cf:')) continue\n const parts = field.split('.')\n if (parts.length === 2) {\n const [aliasNameRaw, column] = parts\n const aliasName = aliasNameRaw || ''\n if (joinMap.has(aliasName)) {\n const list = joinFilters.get(aliasName) ?? []\n list.push({ alias: aliasName, column, op: filter.op, value: filter.value })\n joinFilters.set(aliasName, list)\n continue\n }\n if (aliasName === baseTable || aliasName === 'base') {\n baseFilters.push({\n field: column,\n op: filter.op,\n value: filter.value,\n qualified: `${baseTable}.${column}`,\n })\n continue\n }\n }\n baseFilters.push({ ...filter })\n }\n return { baseFilters, joinFilters }\n}\n\ntype ApplyJoinFiltersOptions = {\n db: Kysely<any>\n baseTable: string\n builder: AnyBuilder\n joinMap: Map<string, ResolvedJoin>\n joinFilters: Map<string, JoinFilter[]>\n aliasTables: Map<string, string>\n qualifyBase: (column: string) => string\n applyAliasScope: (builder: AnyBuilder, alias: string, table: string) => Promise<AnyBuilder> | AnyBuilder\n applyFilterOp: (builder: AnyBuilder, column: string, op: FilterOp, value?: unknown) => AnyBuilder\n applyJoinFilterOp?: (builder: AnyBuilder, filter: JoinFilter, qualified: string, join: ResolvedJoin, table: string) => Promise<{ applied: boolean; builder: AnyBuilder }> | { applied: boolean; builder: AnyBuilder }\n columnExists?: (table: string, column: string) => Promise<boolean> | boolean\n}\n\nexport async function applyJoinFilters({\n db,\n baseTable,\n builder,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase,\n applyAliasScope,\n applyFilterOp,\n applyJoinFilterOp,\n columnExists,\n}: ApplyJoinFiltersOptions): Promise<AnyBuilder> {\n const resolveAliasName = (aliasName?: string | null) => {\n if (!aliasName || aliasName === 'base') return baseTable\n return aliasName\n }\n\n let nextBuilder = builder\n for (const [alias, filtersForAlias] of joinFilters.entries()) {\n const chain = buildJoinChain(alias, joinMap, baseTable)\n if (!chain.length) continue\n const first = chain[0]\n let sub: AnyBuilder = db.selectFrom(`${first.table} as ${first.alias}` as any).select(sql`1`.as('one'))\n sub = await applyAliasScope(sub, first.alias, first.table)\n const parentAlias = resolveAliasName(first.fromAlias)\n const parentRef = parentAlias === baseTable ? qualifyBase(first.fromField) : `${parentAlias}.${first.fromField}`\n sub = sub.whereRef(`${first.alias}.${first.toField}`, '=', parentRef)\n for (const cfg of chain.slice(1)) {\n const parent = resolveAliasName(cfg.fromAlias)\n const rightRef = parent === baseTable ? qualifyBase(cfg.fromField) : `${parent}.${cfg.fromField}`\n const joinFn = cfg.type === 'inner' ? 'innerJoin' : 'leftJoin'\n sub = (sub as any)[joinFn](`${cfg.table} as ${cfg.alias}`, (jb: any) =>\n jb.onRef(`${cfg.alias}.${cfg.toField}`, '=', rightRef))\n sub = await applyAliasScope(sub, cfg.alias, cfg.table)\n }\n let existsDirective: boolean | null = null\n for (const filter of filtersForAlias) {\n if (filter.op === 'exists') {\n if (filter.value === false) existsDirective = false\n else if (existsDirective === null) existsDirective = true\n continue\n }\n const join = joinMap.get(filter.alias)\n if (!join) continue\n const targetTable = aliasTables.get(filter.alias)\n if (!targetTable) continue\n if (columnExists) {\n const exists = await columnExists(targetTable, filter.column)\n if (!exists) continue\n }\n const qualified = `${filter.alias}.${filter.column}`\n if (applyJoinFilterOp) {\n const result = await applyJoinFilterOp(sub, filter, qualified, join, targetTable)\n if (result && result.applied) {\n sub = result.builder\n continue\n }\n if (result && result.builder) {\n sub = result.builder\n }\n }\n sub = applyFilterOp(sub, qualified, filter.op, filter.value)\n }\n if (existsDirective === false) {\n const capturedSub = sub\n nextBuilder = nextBuilder.where((eb: any) => eb.not(eb.exists(capturedSub)))\n } else {\n const capturedSub = sub\n nextBuilder = nextBuilder.where((eb: any) => eb.exists(capturedSub))\n }\n }\n return nextBuilder\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/version.js
CHANGED
package/dist/lib/version.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/version.ts"],
|
|
4
|
-
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.5.1-develop.
|
|
4
|
+
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.5.1-develop.2694.732417c5ec'\nexport const appVersion = APP_VERSION\n"],
|
|
5
5
|
"mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/jest.config.cjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/** @type {import('jest').Config} */
|
|
2
2
|
module.exports = {
|
|
3
|
-
preset: 'ts-jest',
|
|
4
3
|
testEnvironment: 'node',
|
|
5
4
|
watchman: false,
|
|
6
5
|
rootDir: '.',
|
|
@@ -11,7 +10,7 @@ module.exports = {
|
|
|
11
10
|
},
|
|
12
11
|
transform: {
|
|
13
12
|
'^.+\\.(t|j)sx?$': [
|
|
14
|
-
'
|
|
13
|
+
'<rootDir>/../../scripts/jest-mikroorm-transformer.cjs',
|
|
15
14
|
{
|
|
16
15
|
tsconfig: {
|
|
17
16
|
jsx: 'react-jsx',
|
|
@@ -19,6 +18,9 @@ module.exports = {
|
|
|
19
18
|
},
|
|
20
19
|
],
|
|
21
20
|
},
|
|
21
|
+
transformIgnorePatterns: [
|
|
22
|
+
'node_modules/(?!(@mikro-orm)/)',
|
|
23
|
+
],
|
|
22
24
|
testMatch: ['<rootDir>/src/**/__tests__/**/*.test.(ts|tsx)'],
|
|
23
25
|
passWithNoTests: true,
|
|
24
26
|
}
|