@open-mercato/shared 0.6.6-develop.6460.1.f94c74174c → 0.6.6-develop.6471.1.9ab5bdd79a

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.
Files changed (164) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +1 -0
  3. package/dist/lib/commands/command-bus.js +4 -2
  4. package/dist/lib/commands/command-bus.js.map +2 -2
  5. package/dist/lib/commands/command-interceptor-runner.js +4 -2
  6. package/dist/lib/commands/command-interceptor-runner.js.map +2 -2
  7. package/dist/lib/commands/flush.js +3 -4
  8. package/dist/lib/commands/flush.js.map +2 -2
  9. package/dist/lib/commands/helpers.js +3 -1
  10. package/dist/lib/commands/helpers.js.map +2 -2
  11. package/dist/lib/commands/registry.js +5 -3
  12. package/dist/lib/commands/registry.js.map +2 -2
  13. package/dist/lib/commands/scope.js +5 -3
  14. package/dist/lib/commands/scope.js.map +2 -2
  15. package/dist/lib/crud/advanced-filter-integration.js +3 -1
  16. package/dist/lib/crud/advanced-filter-integration.js.map +2 -2
  17. package/dist/lib/crud/cache.js +3 -1
  18. package/dist/lib/crud/cache.js.map +2 -2
  19. package/dist/lib/crud/custom-fields.js +4 -2
  20. package/dist/lib/crud/custom-fields.js.map +2 -2
  21. package/dist/lib/crud/enricher-runner.js +6 -10
  22. package/dist/lib/crud/enricher-runner.js.map +2 -2
  23. package/dist/lib/crud/factory.js +10 -8
  24. package/dist/lib/crud/factory.js.map +2 -2
  25. package/dist/lib/crud/interceptor-registry.js +3 -3
  26. package/dist/lib/crud/interceptor-registry.js.map +2 -2
  27. package/dist/lib/crud/mutation-guard.js +4 -2
  28. package/dist/lib/crud/mutation-guard.js.map +2 -2
  29. package/dist/lib/crud/optimistic-lock.js +5 -6
  30. package/dist/lib/crud/optimistic-lock.js.map +2 -2
  31. package/dist/lib/crud/route-mutation-guard.js +3 -1
  32. package/dist/lib/crud/route-mutation-guard.js.map +2 -2
  33. package/dist/lib/crud/sync-event-runner.js +3 -1
  34. package/dist/lib/crud/sync-event-runner.js.map +2 -2
  35. package/dist/lib/data/engine.js +5 -5
  36. package/dist/lib/data/engine.js.map +2 -2
  37. package/dist/lib/db/mikro.js +5 -3
  38. package/dist/lib/db/mikro.js.map +2 -2
  39. package/dist/lib/di/container.js +3 -1
  40. package/dist/lib/di/container.js.map +2 -2
  41. package/dist/lib/encryption/aes.js +3 -1
  42. package/dist/lib/encryption/aes.js.map +2 -2
  43. package/dist/lib/encryption/entityFields.js +3 -1
  44. package/dist/lib/encryption/entityFields.js.map +2 -2
  45. package/dist/lib/encryption/entityIds.js +3 -1
  46. package/dist/lib/encryption/entityIds.js.map +2 -2
  47. package/dist/lib/encryption/kms.js +25 -32
  48. package/dist/lib/encryption/kms.js.map +2 -2
  49. package/dist/lib/encryption/subscriber.js +3 -1
  50. package/dist/lib/encryption/subscriber.js.map +2 -2
  51. package/dist/lib/encryption/tenantDataEncryptionService.js +3 -1
  52. package/dist/lib/encryption/tenantDataEncryptionService.js.map +2 -2
  53. package/dist/lib/indexers/error-log.js +4 -2
  54. package/dist/lib/indexers/error-log.js.map +2 -2
  55. package/dist/lib/indexers/status-log.js +5 -3
  56. package/dist/lib/indexers/status-log.js.map +2 -2
  57. package/dist/lib/logger/index.js +28 -0
  58. package/dist/lib/logger/index.js.map +7 -0
  59. package/dist/lib/logger/level.js +43 -0
  60. package/dist/lib/logger/level.js.map +7 -0
  61. package/dist/lib/logger/transport.console.js +39 -0
  62. package/dist/lib/logger/transport.console.js.map +7 -0
  63. package/dist/lib/logger/transport.js +17 -0
  64. package/dist/lib/logger/transport.js.map +7 -0
  65. package/dist/lib/logger/transport.pretty.js +85 -0
  66. package/dist/lib/logger/transport.pretty.js.map +7 -0
  67. package/dist/lib/logger/transport.server.js +85 -0
  68. package/dist/lib/logger/transport.server.js.map +7 -0
  69. package/dist/lib/middleware/page-executor.js +3 -1
  70. package/dist/lib/middleware/page-executor.js.map +2 -2
  71. package/dist/lib/modules/registry.js +3 -1
  72. package/dist/lib/modules/registry.js.map +2 -2
  73. package/dist/lib/profiler/index.js +3 -2
  74. package/dist/lib/profiler/index.js.map +2 -2
  75. package/dist/lib/query/engine.js +5 -6
  76. package/dist/lib/query/engine.js.map +2 -2
  77. package/dist/lib/query/query-extension-runner.js +5 -11
  78. package/dist/lib/query/query-extension-runner.js.map +2 -2
  79. package/dist/lib/redis/connection.js +3 -1
  80. package/dist/lib/redis/connection.js.map +2 -2
  81. package/dist/lib/url.js +7 -5
  82. package/dist/lib/url.js.map +2 -2
  83. package/dist/lib/version.js +1 -1
  84. package/dist/lib/version.js.map +1 -1
  85. package/dist/modules/analytics.js +3 -1
  86. package/dist/modules/analytics.js.map +2 -2
  87. package/dist/modules/dashboard/widgets.js +3 -1
  88. package/dist/modules/dashboard/widgets.js.map +2 -2
  89. package/dist/modules/events/factory.js +5 -3
  90. package/dist/modules/events/factory.js.map +2 -2
  91. package/dist/modules/overrides.js +16 -34
  92. package/dist/modules/overrides.js.map +2 -2
  93. package/dist/modules/registry.js +3 -1
  94. package/dist/modules/registry.js.map +2 -2
  95. package/dist/modules/search.js +3 -1
  96. package/dist/modules/search.js.map +2 -2
  97. package/dist/modules/widgets/injection-loader.js +6 -6
  98. package/dist/modules/widgets/injection-loader.js.map +2 -2
  99. package/dist/modules/widgets/injection-position.js +3 -1
  100. package/dist/modules/widgets/injection-position.js.map +2 -2
  101. package/package.json +7 -2
  102. package/src/lib/commands/__tests__/command-interceptor-runner.test.ts +19 -6
  103. package/src/lib/commands/__tests__/flush.test.ts +24 -15
  104. package/src/lib/commands/__tests__/normalizeAuthorUserId.test.ts +14 -2
  105. package/src/lib/commands/__tests__/registry.test.ts +17 -2
  106. package/src/lib/commands/__tests__/scope.test.ts +21 -9
  107. package/src/lib/commands/command-bus.ts +5 -2
  108. package/src/lib/commands/command-interceptor-runner.ts +5 -2
  109. package/src/lib/commands/flush.ts +4 -5
  110. package/src/lib/commands/helpers.ts +9 -2
  111. package/src/lib/commands/registry.ts +6 -3
  112. package/src/lib/commands/scope.ts +6 -3
  113. package/src/lib/crud/__tests__/optimistic-lock.test.ts +21 -6
  114. package/src/lib/crud/__tests__/route-mutation-guard.test.ts +17 -2
  115. package/src/lib/crud/__tests__/sync-event-runner.test.ts +17 -3
  116. package/src/lib/crud/advanced-filter-integration.ts +4 -1
  117. package/src/lib/crud/cache.ts +4 -1
  118. package/src/lib/crud/custom-fields.ts +5 -2
  119. package/src/lib/crud/enricher-runner.ts +7 -10
  120. package/src/lib/crud/factory.ts +11 -10
  121. package/src/lib/crud/interceptor-registry.ts +4 -3
  122. package/src/lib/crud/mutation-guard.ts +5 -2
  123. package/src/lib/crud/optimistic-lock.ts +6 -10
  124. package/src/lib/crud/route-mutation-guard.ts +4 -1
  125. package/src/lib/crud/sync-event-runner.ts +4 -1
  126. package/src/lib/data/__tests__/engine.event-validation.test.ts +26 -15
  127. package/src/lib/data/engine.ts +6 -6
  128. package/src/lib/db/mikro.ts +6 -3
  129. package/src/lib/di/container.ts +4 -1
  130. package/src/lib/encryption/aes.ts +4 -2
  131. package/src/lib/encryption/entityFields.ts +5 -1
  132. package/src/lib/encryption/entityIds.ts +4 -1
  133. package/src/lib/encryption/kms.ts +27 -33
  134. package/src/lib/encryption/subscriber.ts +4 -2
  135. package/src/lib/encryption/tenantDataEncryptionService.ts +4 -2
  136. package/src/lib/indexers/__tests__/status-log.test.ts +22 -10
  137. package/src/lib/indexers/error-log.ts +5 -2
  138. package/src/lib/indexers/status-log.ts +6 -3
  139. package/src/lib/logger/__tests__/logger.test.ts +539 -0
  140. package/src/lib/logger/index.ts +33 -0
  141. package/src/lib/logger/level.ts +50 -0
  142. package/src/lib/logger/transport.console.ts +41 -0
  143. package/src/lib/logger/transport.pretty.ts +94 -0
  144. package/src/lib/logger/transport.server.ts +125 -0
  145. package/src/lib/logger/transport.ts +17 -0
  146. package/src/lib/middleware/page-executor.ts +4 -1
  147. package/src/lib/modules/__tests__/registry.test.ts +28 -21
  148. package/src/lib/modules/registry.ts +4 -1
  149. package/src/lib/profiler/index.ts +4 -2
  150. package/src/lib/query/__tests__/query-extension-runner.test.ts +21 -7
  151. package/src/lib/query/engine.ts +7 -10
  152. package/src/lib/query/query-extension-runner.ts +6 -11
  153. package/src/lib/redis/connection.ts +5 -1
  154. package/src/lib/url.ts +10 -5
  155. package/src/modules/__tests__/overrides.test.ts +17 -3
  156. package/src/modules/__tests__/route-overrides.test.ts +29 -20
  157. package/src/modules/analytics.ts +5 -1
  158. package/src/modules/dashboard/widgets.ts +4 -1
  159. package/src/modules/events/factory.ts +6 -3
  160. package/src/modules/overrides.ts +18 -34
  161. package/src/modules/registry.ts +4 -1
  162. package/src/modules/search.ts +4 -1
  163. package/src/modules/widgets/injection-loader.ts +7 -6
  164. package/src/modules/widgets/injection-position.ts +5 -1
@@ -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, QueryResultMeta, EncryptedSortRowCapWarning, QueryCustomFieldSource, QueryExtensionsConfig, Sort } 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'\nimport {\n buildCustomFieldDefinitionIndexFromRows,\n resolveCfDefIndexOrgCandidates,\n type CustomFieldDefinitionRow,\n type ResolvedCustomFieldDefinitions,\n} from '../crud/custom-field-definition-index'\nimport { resolveEncryptedSortFields, resolveEncryptedSortMaxRows, sortRowsInMemory } from './encrypted-sort'\nimport { mapWithConcurrency } from './bounded-decrypt'\n\nconst DECRYPT_CONCURRENCY = 8\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 getEncryptedFieldNames?: (entityId: EntityId, tenantId?: string | null, organizationId?: string | null) => Promise<readonly string[]>\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\n/**\n * Canonical `module:entity` entity-id shape (both segments snake_case,\n * starting with a lowercase letter). Used to validate caller-supplied entity\n * ids at security-sensitive boundaries before they reach table resolution.\n */\nexport const ENTITY_ID_PATTERN = /^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*$/\n\nexport const isValidEntityIdShape = (value: string): boolean => ENTITY_ID_PATTERN.test(value)\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\n/**\n * Resolve an entity id to a table name strictly via registered MikroORM metadata.\n *\n * Unlike {@link resolveEntityTableName}, this never falls back to a pluralized\n * guess: it returns `null` when no registered entity matches. Use it for\n * security-sensitive call sites (e.g. the reindexer) that must refuse to read\n * arbitrary, attacker-chosen tables that happen to exist in the schema.\n */\nexport function resolveRegisteredEntityTableName(\n em: EntityManager | undefined,\n entity: EntityId,\n): string | null {\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) return null\n\n const candidates = candidateClassNames(rawName)\n for (const candidate of candidates) {\n try {\n const meta = metadata.find?.(candidate)\n if (meta?.tableName) {\n return String(meta.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 return String(meta.tableName)\n }\n }\n } catch {}\n\n return null\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\n const registered = resolveRegisteredEntityTableName(em, entity)\n if (registered) {\n entityTableCache.set(entity, registered)\n return registered\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 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\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 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\n const fallbackOrgId =\n opts.organizationId\n ?? (Array.isArray(opts.organizationIds) && opts.organizationIds.length === 1 ? opts.organizationIds[0] : null)\n const encryptionService = this.getEncryptionService()\n const resolvedSorts: Sort[] = []\n for (const s of opts.sort || []) {\n if (s.field.startsWith('cf:')) {\n resolvedSorts.push(s)\n } else {\n const column = await this.resolveBaseColumn(table, s.field)\n if (column) resolvedSorts.push({ ...s, field: column })\n }\n }\n const encryptedSortFields = await resolveEncryptedSortFields(\n encryptionService,\n entity,\n resolvedSorts.filter((sort) => !sort.field.startsWith('cf:')).map((sort) => sort.field),\n opts.tenantId ?? null,\n fallbackOrgId,\n )\n const requiresPlaintextSort = encryptedSortFields.size > 0\n\n const tenantId = opts.tenantId\n const sanitize = (s: string) => s.replace(/[^a-zA-Z0-9_]/g, '_')\n\n type BuiltQuery = {\n builder: AnyBuilder\n hasJoinedAggregates: boolean\n cfJsonAliases: Set<string>\n cfMultiAliasByAlias: Map<string, string>\n resolvedCustomFieldDefinitions: ResolvedCustomFieldDefinitions | undefined\n }\n\n // Builds the fully-scoped query from a fresh root. `projection: 'full'` reproduces\n // today's complete selection (base fields + CF projections + extension joins).\n // `projection: 'sortKeys'` selects only `id` + the sort columns \u2014 the slim phase-1\n // candidate scan used when `requiresPlaintextSort`. Re-running the WHERE/JOIN logic\n // twice is cheap: every `columnExists`/`tableExists` check is memoized on\n // `this.columnCache`/`this.tableCache`, so the second pass hits no extra DB calls.\n const buildQuery = async (projection: 'full' | 'sortKeys'): Promise<BuiltQuery> => {\n const isSortKeysProjection = projection === 'sortKeys'\n let q: AnyBuilder = db.selectFrom(table as any)\n\n // Tenant/org/soft-delete scope\n if (!skipAutoScope && orgScope && await this.columnExists(table, 'organization_id')) {\n q = this.applyOrganizationScope(q, qualify('organization_id'), orgScope)\n }\n if (!skipAutoScope && await this.columnExists(table, 'tenant_id')) {\n q = q.where(qualify('tenant_id'), '=', opts.tenantId)\n }\n if (!opts.withDeleted && await this.columnExists(table, 'deleted_at')) {\n q = q.where(qualify('deleted_at'), 'is', null)\n }\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 type ResolvedOrClause =\n | { kind: 'column'; qualified: string; op: NormalizedFilter['op']; value: unknown }\n | { kind: 'doc'; field: string; op: NormalizedFilter['op']; value: unknown }\n const resolvedGroupFilters: ResolvedOrClause[][] = []\n for (const [, groupFilters] of groups) {\n const resolved: ResolvedOrClause[] = []\n for (const filter of groupFilters) {\n const column = await this.resolveBaseColumn(table, String(filter.field))\n if (column) {\n resolved.push({ kind: 'column', qualified: qualify(column), op: filter.op, value: filter.value })\n } else {\n // Field is not a base column \u2014 for custom-entity records it lives in\n // entity_indexes.doc. Build an EXISTS sub-filter so `$or` searches\n // across doc fields resolve instead of being silently dropped (#3229).\n resolved.push({ kind: 'doc', field: String(filter.field), op: filter.op, value: filter.value })\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) => {\n const parts = group.map((rf) => rf.kind === 'column'\n ? this.buildColumnOpExpression(eb, rf.qualified, rf.op, rf.value)\n : this.buildIndexDocOpExpression(eb, {\n entity: String(entity),\n field: rf.field,\n op: rf.op,\n value: rf.value,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n withDeleted: opts.withDeleted === true,\n }))\n return parts.length === 1 ? parts[0] : eb.and(parts)\n })\n ))\n }\n }\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\n // Selection (base columns only here; cf:* handled later)\n if (isSortKeysProjection) {\n q = q.select(sql.ref(qualify('id')).as('id'))\n if (await this.columnExists(table, 'tenant_id')) {\n q = q.select(sql.ref(qualify('tenant_id')).as('tenant_id'))\n }\n if (await this.columnExists(table, 'organization_id')) {\n q = q.select(sql.ref(qualify('organization_id')).as('organization_id'))\n }\n for (const s of resolvedSorts) {\n if (!s.field.startsWith('cf:')) q = q.select(sql.ref(qualify(s.field)).as(s.field))\n }\n } else if (opts.fields && opts.fields.length) {\n const cols = new Set(opts.fields.filter((f) => !f.startsWith('cf:')))\n if (requiresPlaintextSort) {\n for (const field of encryptedSortFields) cols.add(field)\n }\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 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 = (!isSortKeysProjection && 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 // Custom-field definition index threaded onto the result so the CRUD factory\n // can decorate list rows without reloading definitions from the DB (#2133).\n // Output-only \u2014 never resolved for the slim sortKeys projection.\n let resolvedCustomFieldDefinitions: ResolvedCustomFieldDefinitions | undefined\n // Explicit in fields/filters\n if (!isSortKeysProjection) {\n for (const f of (opts.fields || [])) {\n if (typeof f === 'string' && f.startsWith('cf:')) cfKeys.add(f.slice(3))\n }\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 (!isSortKeysProjection && 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([\n 'key' as any,\n 'entity_id' as any,\n 'config_json' as any,\n 'kind' as any,\n 'organization_id' as any,\n 'tenant_id' as any,\n 'updated_at' as any,\n 'deleted_at' as any,\n ])\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<{\n key: string\n entity_id: string\n config_json: unknown\n kind: string\n organization_id: string | null\n tenant_id: string | null\n updated_at: Date | string | number | null\n deleted_at: Date | string | number | null\n }>\n // Build the decoration index from the same rows, scoped exactly like the\n // factory's loadCustomFieldDefinitionIndex (tenant + is_active already\n // applied in SQL; org + soft-delete applied in the shared builder).\n const orgCandidates = resolveCfDefIndexOrgCandidates(opts.organizationIds, opts.organizationId ?? null)\n const definitionRows: CustomFieldDefinitionRow[] = rows.map((row) => ({\n key: String(row.key),\n entityId: String(row.entity_id),\n kind: row.kind == null ? null : String(row.kind),\n configJson: row.config_json,\n organizationId: row.organization_id == null ? null : String(row.organization_id),\n tenantId: row.tenant_id == null ? null : String(row.tenant_id),\n deletedAt: row.deleted_at ?? null,\n updatedAt: row.updated_at ?? null,\n }))\n resolvedCustomFieldDefinitions = {\n index: buildCustomFieldDefinitionIndexFromRows(definitionRows, { organizationIds: orgCandidates }),\n entityIds: entityIdList,\n tenantId: tenantId ?? null,\n organizationIds: orgCandidates,\n }\n type ScoredCustomFieldRow = {\n key: string\n entityId: string\n kind: string\n config: Record<string, unknown>\n }\n const sorted: ScoredCustomFieldRow[] = 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 (!isSortKeysProjection && 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 (!isSortKeysProjection && ((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 resolvedSorts) {\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 if (!requiresPlaintextSort) q = q.orderBy(alias, (s.dir ?? 'asc') as any)\n } else {\n if (!requiresPlaintextSort) q = q.orderBy(qualify(s.field), (s.dir ?? 'asc') as any)\n }\n }\n\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\n return { builder: q, hasJoinedAggregates, cfJsonAliases, cfMultiAliasByAlias, resolvedCustomFieldDefinitions }\n }\n\n // Pagination\n const page = opts.page?.page ?? 1\n const pageSize = opts.page?.pageSize ?? 20\n\n const {\n builder: qFull,\n hasJoinedAggregates,\n cfJsonAliases,\n cfMultiAliasByAlias,\n resolvedCustomFieldDefinitions,\n } = await buildQuery('full')\n\n // `count(distinct base.id)` is only required when a join can multiply base rows\n // (CF/extension aggregates, explicit relation joins, or custom-field sources).\n // Without such joins base.id is the unique PK, so `count(*)` is equivalent and\n // lets Postgres skip the redundant DISTINCT sort/hash for an index-only count (#2227).\n const mayMultiplyBaseRows =\n hasJoinedAggregates ||\n (Array.isArray(opts.joins) && opts.joins.length > 0) ||\n (Array.isArray(opts.customFieldSources) && opts.customFieldSources.length > 0)\n const countExpr = mayMultiplyBaseRows\n ? sql<string>`count(distinct ${sql.ref(`${table}.id`)})`\n : sql<string>`count(*)`\n const countBuilder = hasJoinedAggregates\n ? qFull.clearSelect().clearOrderBy().clearGroupBy().select(countExpr.as('count'))\n : qFull.clearSelect().clearOrderBy().select(countExpr.as('count'))\n const countRow = await countBuilder.executeTakeFirst() as { count: unknown } | undefined\n const total = Number((countRow as any)?.count ?? 0)\n\n const svc = encryptionService\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\n const decryptRow = async (item: ResultRow): Promise<ResultRow> => {\n if (!decryptPayload) return item\n try {\n const decrypted = await decryptPayload(\n entity,\n item,\n (item?.tenant_id ?? item?.tenantId ?? opts.tenantId ?? null) as string | null,\n (item?.organization_id ?? item?.organizationId ?? fallbackOrgId ?? null) as string | 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 const normalizeCfJsonAliases = (rows: ResultRow[]) => {\n if (cfJsonAliases.size === 0) return\n for (const row of rows) {\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 let pagedItems: ResultRow[]\n let encryptedSortRowCapWarning: EncryptedSortRowCapWarning | undefined\n\n if (requiresPlaintextSort) {\n // Phase 1: slim id+sort-columns candidate scan, bounded-concurrency decrypt,\n // in-memory sort over the full candidate set, then slice to the page's ids.\n const cap = resolveEncryptedSortMaxRows()\n let qSort = (await buildQuery('sortKeys')).builder\n if (cap !== null) {\n qSort = qSort.limit(cap).orderBy(qualify('id'), 'asc' as any)\n }\n const candidateRows = await qSort.execute() as ResultRow[]\n const decryptedCandidates = decryptPayload\n ? await mapWithConcurrency(candidateRows, DECRYPT_CONCURRENCY, decryptRow)\n : candidateRows\n const orderedCandidates = sortRowsInMemory(decryptedCandidates, resolvedSorts)\n const pageIds = orderedCandidates\n .slice((page - 1) * pageSize, page * pageSize)\n .map((row) => row.id)\n\n if (cap !== null && total > cap) {\n encryptedSortRowCapWarning = {\n entity,\n sortFields: resolvedSorts.map((s) => s.field),\n maxRows: cap,\n totalMatched: total,\n }\n }\n\n // Phase 2: fetch + decrypt full rows for just the page's ids, then reassemble\n // in phase-1's order (SQL `WHERE id IN (...)` order is unspecified \u2014 never\n // trust it for ordering).\n if (pageIds.length === 0) {\n pagedItems = []\n } else {\n const pageRows = await qFull.where(qualify('id'), 'in', pageIds).execute() as ResultRow[]\n normalizeCfJsonAliases(pageRows)\n const decryptedPageRows = decryptPayload\n ? await mapWithConcurrency(pageRows, DECRYPT_CONCURRENCY, decryptRow)\n : pageRows\n const byId = new Map(decryptedPageRows.map((row) => [String(row.id), row]))\n pagedItems = pageIds\n .map((id) => byId.get(String(id)))\n .filter((row): row is ResultRow => row != null)\n }\n } else {\n const dataQuery = qFull.limit(pageSize).offset((page - 1) * pageSize)\n const items = await dataQuery.execute() as ResultRow[]\n normalizeCfJsonAliases(items)\n pagedItems = decryptPayload\n ? await mapWithConcurrency(items, DECRYPT_CONCURRENCY, decryptRow)\n : items\n }\n\n let queryResult: QueryResult<T> = { items: pagedItems as unknown as T[], page, pageSize, total }\n\n if (encryptedSortRowCapWarning) {\n const meta: QueryResultMeta = { encryptedSortRowCapWarning }\n queryResult.meta = meta\n }\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 // Attach after the extension pipeline so the field always survives even if a\n // subscriber replaces the whole result object.\n if (resolvedCustomFieldDefinitions) {\n queryResult.customFieldDefinitions = resolvedCustomFieldDefinitions\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 return q.where((eb: any) => this.buildIndexDocOpExpression(eb, opts))\n }\n\n // Builds the entity_indexes EXISTS expression for a single doc-field operator,\n // shared by the regular doc-filter path (applyIndexDocFilter) and the OR-group\n // path so `$or` queries over custom-entity doc fields resolve instead of being\n // silently dropped (#3229).\n private buildIndexDocOpExpression(\n eb: any,\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 }\n ): any {\n const alias = `ei_${this.searchAliasSeq++}`\n const engine = this\n return 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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",
4
+ "sourcesContent": ["import type { QueryEngine, QueryOptions, QueryResult, QueryResultMeta, EncryptedSortRowCapWarning, QueryCustomFieldSource, QueryExtensionsConfig, Sort } 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'\nimport {\n buildCustomFieldDefinitionIndexFromRows,\n resolveCfDefIndexOrgCandidates,\n type CustomFieldDefinitionRow,\n type ResolvedCustomFieldDefinitions,\n} from '../crud/custom-field-definition-index'\nimport { resolveEncryptedSortFields, resolveEncryptedSortMaxRows, sortRowsInMemory } from './encrypted-sort'\nimport { mapWithConcurrency } from './bounded-decrypt'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'query' })\n\nconst DECRYPT_CONCURRENCY = 8\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 getEncryptedFieldNames?: (entityId: EntityId, tenantId?: string | null, organizationId?: string | null) => Promise<readonly string[]>\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\n/**\n * Canonical `module:entity` entity-id shape (both segments snake_case,\n * starting with a lowercase letter). Used to validate caller-supplied entity\n * ids at security-sensitive boundaries before they reach table resolution.\n */\nexport const ENTITY_ID_PATTERN = /^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*$/\n\nexport const isValidEntityIdShape = (value: string): boolean => ENTITY_ID_PATTERN.test(value)\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\n/**\n * Resolve an entity id to a table name strictly via registered MikroORM metadata.\n *\n * Unlike {@link resolveEntityTableName}, this never falls back to a pluralized\n * guess: it returns `null` when no registered entity matches. Use it for\n * security-sensitive call sites (e.g. the reindexer) that must refuse to read\n * arbitrary, attacker-chosen tables that happen to exist in the schema.\n */\nexport function resolveRegisteredEntityTableName(\n em: EntityManager | undefined,\n entity: EntityId,\n): string | null {\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) return null\n\n const candidates = candidateClassNames(rawName)\n for (const candidate of candidates) {\n try {\n const meta = metadata.find?.(candidate)\n if (meta?.tableName) {\n return String(meta.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 return String(meta.tableName)\n }\n }\n } catch {}\n\n return null\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\n const registered = resolveRegisteredEntityTableName(em, entity)\n if (registered) {\n entityTableCache.set(entity, registered)\n return registered\n }\n\n const fallback = pluralizeBaseName(rawName || '')\n logger.warn('Could not resolve entity via ORM metadata; falling back to table name \u2014 ensure the entity ID segment matches the class name convention', { entity, fallback })\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 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\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 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\n const fallbackOrgId =\n opts.organizationId\n ?? (Array.isArray(opts.organizationIds) && opts.organizationIds.length === 1 ? opts.organizationIds[0] : null)\n const encryptionService = this.getEncryptionService()\n const resolvedSorts: Sort[] = []\n for (const s of opts.sort || []) {\n if (s.field.startsWith('cf:')) {\n resolvedSorts.push(s)\n } else {\n const column = await this.resolveBaseColumn(table, s.field)\n if (column) resolvedSorts.push({ ...s, field: column })\n }\n }\n const encryptedSortFields = await resolveEncryptedSortFields(\n encryptionService,\n entity,\n resolvedSorts.filter((sort) => !sort.field.startsWith('cf:')).map((sort) => sort.field),\n opts.tenantId ?? null,\n fallbackOrgId,\n )\n const requiresPlaintextSort = encryptedSortFields.size > 0\n\n const tenantId = opts.tenantId\n const sanitize = (s: string) => s.replace(/[^a-zA-Z0-9_]/g, '_')\n\n type BuiltQuery = {\n builder: AnyBuilder\n hasJoinedAggregates: boolean\n cfJsonAliases: Set<string>\n cfMultiAliasByAlias: Map<string, string>\n resolvedCustomFieldDefinitions: ResolvedCustomFieldDefinitions | undefined\n }\n\n // Builds the fully-scoped query from a fresh root. `projection: 'full'` reproduces\n // today's complete selection (base fields + CF projections + extension joins).\n // `projection: 'sortKeys'` selects only `id` + the sort columns \u2014 the slim phase-1\n // candidate scan used when `requiresPlaintextSort`. Re-running the WHERE/JOIN logic\n // twice is cheap: every `columnExists`/`tableExists` check is memoized on\n // `this.columnCache`/`this.tableCache`, so the second pass hits no extra DB calls.\n const buildQuery = async (projection: 'full' | 'sortKeys'): Promise<BuiltQuery> => {\n const isSortKeysProjection = projection === 'sortKeys'\n let q: AnyBuilder = db.selectFrom(table as any)\n\n // Tenant/org/soft-delete scope\n if (!skipAutoScope && orgScope && await this.columnExists(table, 'organization_id')) {\n q = this.applyOrganizationScope(q, qualify('organization_id'), orgScope)\n }\n if (!skipAutoScope && await this.columnExists(table, 'tenant_id')) {\n q = q.where(qualify('tenant_id'), '=', opts.tenantId)\n }\n if (!opts.withDeleted && await this.columnExists(table, 'deleted_at')) {\n q = q.where(qualify('deleted_at'), 'is', null)\n }\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 type ResolvedOrClause =\n | { kind: 'column'; qualified: string; op: NormalizedFilter['op']; value: unknown }\n | { kind: 'doc'; field: string; op: NormalizedFilter['op']; value: unknown }\n const resolvedGroupFilters: ResolvedOrClause[][] = []\n for (const [, groupFilters] of groups) {\n const resolved: ResolvedOrClause[] = []\n for (const filter of groupFilters) {\n const column = await this.resolveBaseColumn(table, String(filter.field))\n if (column) {\n resolved.push({ kind: 'column', qualified: qualify(column), op: filter.op, value: filter.value })\n } else {\n // Field is not a base column \u2014 for custom-entity records it lives in\n // entity_indexes.doc. Build an EXISTS sub-filter so `$or` searches\n // across doc fields resolve instead of being silently dropped (#3229).\n resolved.push({ kind: 'doc', field: String(filter.field), op: filter.op, value: filter.value })\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) => {\n const parts = group.map((rf) => rf.kind === 'column'\n ? this.buildColumnOpExpression(eb, rf.qualified, rf.op, rf.value)\n : this.buildIndexDocOpExpression(eb, {\n entity: String(entity),\n field: rf.field,\n op: rf.op,\n value: rf.value,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n withDeleted: opts.withDeleted === true,\n }))\n return parts.length === 1 ? parts[0] : eb.and(parts)\n })\n ))\n }\n }\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\n // Selection (base columns only here; cf:* handled later)\n if (isSortKeysProjection) {\n q = q.select(sql.ref(qualify('id')).as('id'))\n if (await this.columnExists(table, 'tenant_id')) {\n q = q.select(sql.ref(qualify('tenant_id')).as('tenant_id'))\n }\n if (await this.columnExists(table, 'organization_id')) {\n q = q.select(sql.ref(qualify('organization_id')).as('organization_id'))\n }\n for (const s of resolvedSorts) {\n if (!s.field.startsWith('cf:')) q = q.select(sql.ref(qualify(s.field)).as(s.field))\n }\n } else if (opts.fields && opts.fields.length) {\n const cols = new Set(opts.fields.filter((f) => !f.startsWith('cf:')))\n if (requiresPlaintextSort) {\n for (const field of encryptedSortFields) cols.add(field)\n }\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 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 = (!isSortKeysProjection && 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 // Custom-field definition index threaded onto the result so the CRUD factory\n // can decorate list rows without reloading definitions from the DB (#2133).\n // Output-only \u2014 never resolved for the slim sortKeys projection.\n let resolvedCustomFieldDefinitions: ResolvedCustomFieldDefinitions | undefined\n // Explicit in fields/filters\n if (!isSortKeysProjection) {\n for (const f of (opts.fields || [])) {\n if (typeof f === 'string' && f.startsWith('cf:')) cfKeys.add(f.slice(3))\n }\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 (!isSortKeysProjection && 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([\n 'key' as any,\n 'entity_id' as any,\n 'config_json' as any,\n 'kind' as any,\n 'organization_id' as any,\n 'tenant_id' as any,\n 'updated_at' as any,\n 'deleted_at' as any,\n ])\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<{\n key: string\n entity_id: string\n config_json: unknown\n kind: string\n organization_id: string | null\n tenant_id: string | null\n updated_at: Date | string | number | null\n deleted_at: Date | string | number | null\n }>\n // Build the decoration index from the same rows, scoped exactly like the\n // factory's loadCustomFieldDefinitionIndex (tenant + is_active already\n // applied in SQL; org + soft-delete applied in the shared builder).\n const orgCandidates = resolveCfDefIndexOrgCandidates(opts.organizationIds, opts.organizationId ?? null)\n const definitionRows: CustomFieldDefinitionRow[] = rows.map((row) => ({\n key: String(row.key),\n entityId: String(row.entity_id),\n kind: row.kind == null ? null : String(row.kind),\n configJson: row.config_json,\n organizationId: row.organization_id == null ? null : String(row.organization_id),\n tenantId: row.tenant_id == null ? null : String(row.tenant_id),\n deletedAt: row.deleted_at ?? null,\n updatedAt: row.updated_at ?? null,\n }))\n resolvedCustomFieldDefinitions = {\n index: buildCustomFieldDefinitionIndexFromRows(definitionRows, { organizationIds: orgCandidates }),\n entityIds: entityIdList,\n tenantId: tenantId ?? null,\n organizationIds: orgCandidates,\n }\n type ScoredCustomFieldRow = {\n key: string\n entityId: string\n kind: string\n config: Record<string, unknown>\n }\n const sorted: ScoredCustomFieldRow[] = 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 (!isSortKeysProjection && 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 (!isSortKeysProjection && ((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 resolvedSorts) {\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 if (!requiresPlaintextSort) q = q.orderBy(alias, (s.dir ?? 'asc') as any)\n } else {\n if (!requiresPlaintextSort) q = q.orderBy(qualify(s.field), (s.dir ?? 'asc') as any)\n }\n }\n\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\n return { builder: q, hasJoinedAggregates, cfJsonAliases, cfMultiAliasByAlias, resolvedCustomFieldDefinitions }\n }\n\n // Pagination\n const page = opts.page?.page ?? 1\n const pageSize = opts.page?.pageSize ?? 20\n\n const {\n builder: qFull,\n hasJoinedAggregates,\n cfJsonAliases,\n cfMultiAliasByAlias,\n resolvedCustomFieldDefinitions,\n } = await buildQuery('full')\n\n // `count(distinct base.id)` is only required when a join can multiply base rows\n // (CF/extension aggregates, explicit relation joins, or custom-field sources).\n // Without such joins base.id is the unique PK, so `count(*)` is equivalent and\n // lets Postgres skip the redundant DISTINCT sort/hash for an index-only count (#2227).\n const mayMultiplyBaseRows =\n hasJoinedAggregates ||\n (Array.isArray(opts.joins) && opts.joins.length > 0) ||\n (Array.isArray(opts.customFieldSources) && opts.customFieldSources.length > 0)\n const countExpr = mayMultiplyBaseRows\n ? sql<string>`count(distinct ${sql.ref(`${table}.id`)})`\n : sql<string>`count(*)`\n const countBuilder = hasJoinedAggregates\n ? qFull.clearSelect().clearOrderBy().clearGroupBy().select(countExpr.as('count'))\n : qFull.clearSelect().clearOrderBy().select(countExpr.as('count'))\n const countRow = await countBuilder.executeTakeFirst() as { count: unknown } | undefined\n const total = Number((countRow as any)?.count ?? 0)\n\n const svc = encryptionService\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\n const decryptRow = async (item: ResultRow): Promise<ResultRow> => {\n if (!decryptPayload) return item\n try {\n const decrypted = await decryptPayload(\n entity,\n item,\n (item?.tenant_id ?? item?.tenantId ?? opts.tenantId ?? null) as string | null,\n (item?.organization_id ?? item?.organizationId ?? fallbackOrgId ?? null) as string | null,\n )\n return { ...item, ...decrypted }\n } catch (err) {\n logger.error('Error decrypting entity payload', { err })\n return item\n }\n }\n\n const normalizeCfJsonAliases = (rows: ResultRow[]) => {\n if (cfJsonAliases.size === 0) return\n for (const row of rows) {\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 let pagedItems: ResultRow[]\n let encryptedSortRowCapWarning: EncryptedSortRowCapWarning | undefined\n\n if (requiresPlaintextSort) {\n // Phase 1: slim id+sort-columns candidate scan, bounded-concurrency decrypt,\n // in-memory sort over the full candidate set, then slice to the page's ids.\n const cap = resolveEncryptedSortMaxRows()\n let qSort = (await buildQuery('sortKeys')).builder\n if (cap !== null) {\n qSort = qSort.limit(cap).orderBy(qualify('id'), 'asc' as any)\n }\n const candidateRows = await qSort.execute() as ResultRow[]\n const decryptedCandidates = decryptPayload\n ? await mapWithConcurrency(candidateRows, DECRYPT_CONCURRENCY, decryptRow)\n : candidateRows\n const orderedCandidates = sortRowsInMemory(decryptedCandidates, resolvedSorts)\n const pageIds = orderedCandidates\n .slice((page - 1) * pageSize, page * pageSize)\n .map((row) => row.id)\n\n if (cap !== null && total > cap) {\n encryptedSortRowCapWarning = {\n entity,\n sortFields: resolvedSorts.map((s) => s.field),\n maxRows: cap,\n totalMatched: total,\n }\n }\n\n // Phase 2: fetch + decrypt full rows for just the page's ids, then reassemble\n // in phase-1's order (SQL `WHERE id IN (...)` order is unspecified \u2014 never\n // trust it for ordering).\n if (pageIds.length === 0) {\n pagedItems = []\n } else {\n const pageRows = await qFull.where(qualify('id'), 'in', pageIds).execute() as ResultRow[]\n normalizeCfJsonAliases(pageRows)\n const decryptedPageRows = decryptPayload\n ? await mapWithConcurrency(pageRows, DECRYPT_CONCURRENCY, decryptRow)\n : pageRows\n const byId = new Map(decryptedPageRows.map((row) => [String(row.id), row]))\n pagedItems = pageIds\n .map((id) => byId.get(String(id)))\n .filter((row): row is ResultRow => row != null)\n }\n } else {\n const dataQuery = qFull.limit(pageSize).offset((page - 1) * pageSize)\n const items = await dataQuery.execute() as ResultRow[]\n normalizeCfJsonAliases(items)\n pagedItems = decryptPayload\n ? await mapWithConcurrency(items, DECRYPT_CONCURRENCY, decryptRow)\n : items\n }\n\n let queryResult: QueryResult<T> = { items: pagedItems as unknown as T[], page, pageSize, total }\n\n if (encryptedSortRowCapWarning) {\n const meta: QueryResultMeta = { encryptedSortRowCapWarning }\n queryResult.meta = meta\n }\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 // Attach after the extension pipeline so the field always survives even if a\n // subscriber replaces the whole result object.\n if (resolvedCustomFieldDefinitions) {\n queryResult.customFieldDefinitions = resolvedCustomFieldDefinitions\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 return q.where((eb: any) => this.buildIndexDocOpExpression(eb, opts))\n }\n\n // Builds the entity_indexes EXISTS expression for a single doc-field operator,\n // shared by the regular doc-filter path (applyIndexDocFilter) and the OR-group\n // path so `$or` queries over custom-entity doc fields resolve instead of being\n // silently dropped (#3229).\n private buildIndexDocOpExpression(\n eb: any,\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 }\n ): any {\n const alias = `ei_${this.searchAliasSeq++}`\n const engine = this\n return 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 logger.debug(event, payload)\n } catch {}\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
6
  "names": ["resolvedCustomFieldDefinitions", "cfJsonAliases", "cfMultiAliasByAlias", "hasJoinedAggregates", "next"]
7
7
  }
@@ -2,6 +2,8 @@ import { getAllSyncSubscribers } from "../crud/sync-subscriber-store.js";
2
2
  import { matchesEventPattern } from "../crud/sync-event-runner.js";
3
3
  import { getEnrichersForEntity } from "../crud/enricher-registry.js";
4
4
  import { applyResponseEnrichers, applyResponseEnricherToRecord } from "../crud/enricher-runner.js";
5
+ import { createLogger } from "../logger/index.js";
6
+ const logger = createLogger("shared").child({ component: "query-extensions" });
5
7
  function toQueryingEventId(entity) {
6
8
  return `${entity}.querying`;
7
9
  }
@@ -40,10 +42,7 @@ async function runBeforeQueryEvent(payload, ctx) {
40
42
  currentQuery = { ...currentQuery, ...queryResult.modifiedQuery };
41
43
  }
42
44
  } catch (error) {
43
- console.error(
44
- `[query-extension] before-query subscriber failed: ${subscriber.metadata.id}`,
45
- error
46
- );
45
+ logger.error("Before-query subscriber failed", { subscriberId: subscriber.metadata.id, err: error });
47
46
  return {
48
47
  ok: false,
49
48
  errorMessage: `Subscriber ${subscriber.metadata.id} threw unexpectedly`,
@@ -75,16 +74,11 @@ async function runAfterQueryEvent(payload, ctx) {
75
74
  if (isValidQueryResult(queryResult.modifiedResult)) {
76
75
  currentResult = queryResult.modifiedResult;
77
76
  } else {
78
- console.warn(
79
- `[query-extension] after-query subscriber ${subscriber.metadata.id} returned invalid modifiedResult shape \u2014 ignored`
80
- );
77
+ logger.warn("After-query subscriber returned invalid modifiedResult shape \u2014 ignored", { subscriberId: subscriber.metadata.id });
81
78
  }
82
79
  }
83
80
  } catch (error) {
84
- console.error(
85
- `[query-extension] after-query subscriber failed: ${subscriber.metadata.id}`,
86
- error
87
- );
81
+ logger.error("After-query subscriber failed", { subscriberId: subscriber.metadata.id, err: error });
88
82
  }
89
83
  }
90
84
  const resultChanged = currentResult !== payload.result;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/query/query-extension-runner.ts"],
4
- "sourcesContent": ["/**\n * Query Extension Runner\n *\n * Shared pipeline runner for both BasicQueryEngine and HybridQueryEngine.\n * Handles:\n * 1. Sync before-query events (can block / modify query)\n * 2. Scope guard re-application after modifications\n * 3. Query-level enricher application\n * 4. Sync after-query events (can modify result)\n */\n\nimport type { SyncSubscriberEntry } from '../crud/sync-subscriber-store'\nimport { getAllSyncSubscribers } from '../crud/sync-subscriber-store'\nimport { collectSyncSubscribers, matchesEventPattern } from '../crud/sync-event-runner'\nimport type { EnricherContext } from '../crud/response-enricher'\nimport type { EnricherSurfaceSelector } from '../crud/enricher-registry'\nimport { getEnrichersForEntity } from '../crud/enricher-registry'\nimport { applyResponseEnrichers, applyResponseEnricherToRecord } from '../crud/enricher-runner'\nimport type { SyncQueryEventPayload, SyncQueryEventResult } from './sync-query-event-types'\nimport type { QueryOptions, QueryResult } from './types'\n\n// ---------------------------------------------------------------------------\n// Entity-to-event-ID helpers\n// ---------------------------------------------------------------------------\n\nfunction toQueryingEventId(entity: string): string {\n return `${entity}.querying`\n}\n\nfunction toQueriedEventId(entity: string): string {\n return `${entity}.queried`\n}\n\n/**\n * Convert an entity identifier from query engine format (`module:entity`)\n * to event format (`module.entity`).\n */\nexport function entityIdToEventEntity(entityId: string): string {\n return entityId.replace(/:/g, '.')\n}\n\n// ---------------------------------------------------------------------------\n// Collect query-event subscribers\n// ---------------------------------------------------------------------------\n\nexport function collectQuerySubscribers(\n eventId: string,\n): SyncSubscriberEntry[] {\n const allSync = getAllSyncSubscribers()\n return allSync\n .filter((s) => matchesEventPattern(s.metadata.event, eventId))\n .sort((a, b) => (a.metadata.priority ?? 50) - (b.metadata.priority ?? 50))\n}\n\n// ---------------------------------------------------------------------------\n// Before-query event runner\n// ---------------------------------------------------------------------------\n\nexport interface BeforeQueryResult {\n ok: boolean\n errorMessage?: string\n errorStatus?: number\n modifiedQuery?: Partial<QueryOptions>\n}\n\nexport async function runBeforeQueryEvent(\n payload: SyncQueryEventPayload,\n ctx: { resolve: <T = unknown>(name: string) => T },\n): Promise<BeforeQueryResult> {\n const eventId = toQueryingEventId(payload.entity)\n const subscribers = collectQuerySubscribers(eventId)\n\n if (subscribers.length === 0) {\n return { ok: true }\n }\n\n let currentQuery = payload.query\n\n for (const subscriber of subscribers) {\n try {\n const result = await subscriber.handler(\n { ...payload, eventId, query: currentQuery } as never,\n ctx,\n )\n const queryResult = result as SyncQueryEventResult | void\n\n if (queryResult?.ok === false) {\n return {\n ok: false,\n errorMessage: queryResult.message ?? 'Query blocked by subscriber',\n errorStatus: queryResult.status ?? 422,\n }\n }\n\n if (queryResult?.modifiedQuery) {\n currentQuery = { ...currentQuery, ...queryResult.modifiedQuery }\n }\n } catch (error) {\n console.error(\n `[query-extension] before-query subscriber failed: ${subscriber.metadata.id}`,\n error,\n )\n return {\n ok: false,\n errorMessage: `Subscriber ${subscriber.metadata.id} threw unexpectedly`,\n errorStatus: 500,\n }\n }\n }\n\n const queryChanged = currentQuery !== payload.query\n return {\n ok: true,\n modifiedQuery: queryChanged ? currentQuery : undefined,\n }\n}\n\n// ---------------------------------------------------------------------------\n// After-query event runner\n// ---------------------------------------------------------------------------\n\nexport interface AfterQueryResult {\n modifiedResult?: QueryResult<Record<string, unknown>>\n}\n\nexport async function runAfterQueryEvent(\n payload: SyncQueryEventPayload,\n ctx: { resolve: <T = unknown>(name: string) => T },\n): Promise<AfterQueryResult> {\n const eventId = toQueriedEventId(payload.entity)\n const subscribers = collectQuerySubscribers(eventId)\n\n if (subscribers.length === 0) {\n return {}\n }\n\n let currentResult = payload.result\n\n for (const subscriber of subscribers) {\n try {\n const result = await subscriber.handler(\n { ...payload, eventId, result: currentResult } as never,\n ctx,\n )\n const queryResult = result as SyncQueryEventResult | void\n\n if (queryResult?.modifiedResult) {\n if (isValidQueryResult(queryResult.modifiedResult)) {\n currentResult = queryResult.modifiedResult\n } else {\n console.warn(\n `[query-extension] after-query subscriber ${subscriber.metadata.id} returned invalid modifiedResult shape \u2014 ignored`,\n )\n }\n }\n } catch (error) {\n console.error(\n `[query-extension] after-query subscriber failed: ${subscriber.metadata.id}`,\n error,\n )\n }\n }\n\n const resultChanged = currentResult !== payload.result\n return {\n modifiedResult: resultChanged ? currentResult : undefined,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Query result validation\n// ---------------------------------------------------------------------------\n\nfunction isValidQueryResult(value: unknown): value is QueryResult<Record<string, unknown>> {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return (\n Array.isArray(candidate.items) &&\n typeof candidate.page === 'number' &&\n typeof candidate.pageSize === 'number' &&\n typeof candidate.total === 'number'\n )\n}\n\n// ---------------------------------------------------------------------------\n// Scope guard re-application\n// ---------------------------------------------------------------------------\n\nexport function reapplyScopeGuards(\n query: QueryOptions,\n tenantId: string,\n organizationId?: string | null,\n): QueryOptions {\n return {\n ...query,\n tenantId,\n ...(organizationId != null ? { organizationId } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Query-level enricher application\n// ---------------------------------------------------------------------------\n\nexport async function applyQueryLevelEnrichers<T extends Record<string, unknown>>(\n items: T[],\n entity: string,\n engine: 'basic' | 'hybrid',\n context: EnricherContext,\n mode: 'list' | 'detail' = 'list',\n): Promise<{ items: T[]; enrichedBy: string[]; enricherErrors: string[] }> {\n const selector: EnricherSurfaceSelector = {\n surface: 'query-engine',\n engine,\n }\n\n const entries = getEnrichersForEntity(entity, selector)\n\n const filteredEntries = entries.filter((entry) => {\n const applyOn = entry.enricher.queryEngine?.applyOn ?? ['list', 'detail']\n return applyOn.includes(mode)\n })\n\n if (filteredEntries.length === 0) {\n return { items, enrichedBy: [], enricherErrors: [] }\n }\n\n if (mode === 'detail' && items.length === 1) {\n const singleResult = await applyResponseEnricherToRecord(items[0], entity, context, filteredEntries)\n return {\n items: [singleResult.record as T],\n enrichedBy: singleResult._meta.enrichedBy,\n enricherErrors: singleResult._meta.enricherErrors ?? [],\n }\n }\n\n const result = await applyResponseEnrichers(items, entity, context, filteredEntries)\n return {\n items: result.items as T[],\n enrichedBy: result._meta.enrichedBy,\n enricherErrors: result._meta.enricherErrors ?? [],\n }\n}\n\n// ---------------------------------------------------------------------------\n// Full pipeline orchestrator\n// ---------------------------------------------------------------------------\n\nexport interface QueryExtensionContext {\n entity: string\n engine: 'basic' | 'hybrid'\n tenantId: string\n organizationId?: string | null\n userId?: string\n em: unknown\n container?: unknown\n userFeatures?: string[]\n}\n\nexport interface QueryExtensionPipelineResult<T> {\n blocked: boolean\n errorMessage?: string\n errorStatus?: number\n modifiedQuery?: QueryOptions\n finalResult?: QueryResult<T>\n}\n\n/**\n * Run the before-query phase of the extension pipeline.\n *\n * 1. Emit sync before-query event (can block / modify query)\n * 2. Re-apply mandatory scope guards after modifications\n */\nexport async function runBeforeQueryPipeline(\n query: QueryOptions,\n extensionCtx: QueryExtensionContext,\n diCtx: { resolve: <T = unknown>(name: string) => T },\n): Promise<{ blocked: boolean; errorMessage?: string; errorStatus?: number; query: QueryOptions }> {\n const eventEntity = entityIdToEventEntity(extensionCtx.entity)\n\n const beforePayload: SyncQueryEventPayload = {\n eventId: toQueryingEventId(eventEntity),\n entity: eventEntity,\n timing: 'before',\n engine: extensionCtx.engine,\n query,\n userId: extensionCtx.userId,\n organizationId: extensionCtx.organizationId,\n tenantId: extensionCtx.tenantId,\n em: extensionCtx.em as never,\n }\n\n const beforeResult = await runBeforeQueryEvent(beforePayload, diCtx)\n\n if (!beforeResult.ok) {\n return {\n blocked: true,\n errorMessage: beforeResult.errorMessage,\n errorStatus: beforeResult.errorStatus,\n query,\n }\n }\n\n let effectiveQuery = beforeResult.modifiedQuery\n ? { ...query, ...beforeResult.modifiedQuery }\n : query\n\n effectiveQuery = reapplyScopeGuards(\n effectiveQuery,\n extensionCtx.tenantId,\n extensionCtx.organizationId,\n )\n\n return { blocked: false, query: effectiveQuery }\n}\n\n/**\n * Run the after-query phase of the extension pipeline.\n *\n * 1. Apply query-level enrichers (opt-in only)\n * 2. Emit sync after-query event (can modify result)\n */\nexport async function runAfterQueryPipeline<T extends Record<string, unknown>>(\n result: QueryResult<T>,\n query: QueryOptions,\n extensionCtx: QueryExtensionContext,\n diCtx: { resolve: <T = unknown>(name: string) => T },\n): Promise<QueryResult<T>> {\n const eventEntity = entityIdToEventEntity(extensionCtx.entity)\n let currentResult: QueryResult<Record<string, unknown>> = result as QueryResult<Record<string, unknown>>\n\n const enricherContext: EnricherContext = {\n organizationId: extensionCtx.organizationId ?? '',\n tenantId: extensionCtx.tenantId,\n userId: extensionCtx.userId ?? '',\n em: extensionCtx.em,\n container: extensionCtx.container,\n userFeatures: extensionCtx.userFeatures,\n }\n\n const mode = currentResult.items.length === 1 ? 'detail' : 'list'\n const enrichResult = await applyQueryLevelEnrichers(\n currentResult.items,\n eventEntity,\n extensionCtx.engine,\n enricherContext,\n mode,\n )\n\n currentResult = { ...currentResult, items: enrichResult.items }\n\n const afterPayload: SyncQueryEventPayload = {\n eventId: toQueriedEventId(eventEntity),\n entity: eventEntity,\n timing: 'after',\n engine: extensionCtx.engine,\n query,\n result: currentResult,\n userId: extensionCtx.userId,\n organizationId: extensionCtx.organizationId,\n tenantId: extensionCtx.tenantId,\n em: extensionCtx.em as never,\n }\n\n const afterResult = await runAfterQueryEvent(afterPayload, diCtx)\n\n if (afterResult.modifiedResult) {\n currentResult = afterResult.modifiedResult\n }\n\n return currentResult as QueryResult<T>\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Query Extension Runner\n *\n * Shared pipeline runner for both BasicQueryEngine and HybridQueryEngine.\n * Handles:\n * 1. Sync before-query events (can block / modify query)\n * 2. Scope guard re-application after modifications\n * 3. Query-level enricher application\n * 4. Sync after-query events (can modify result)\n */\n\nimport type { SyncSubscriberEntry } from '../crud/sync-subscriber-store'\nimport { getAllSyncSubscribers } from '../crud/sync-subscriber-store'\nimport { collectSyncSubscribers, matchesEventPattern } from '../crud/sync-event-runner'\nimport type { EnricherContext } from '../crud/response-enricher'\nimport type { EnricherSurfaceSelector } from '../crud/enricher-registry'\nimport { getEnrichersForEntity } from '../crud/enricher-registry'\nimport { applyResponseEnrichers, applyResponseEnricherToRecord } from '../crud/enricher-runner'\nimport type { SyncQueryEventPayload, SyncQueryEventResult } from './sync-query-event-types'\nimport type { QueryOptions, QueryResult } from './types'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'query-extensions' })\n\n// ---------------------------------------------------------------------------\n// Entity-to-event-ID helpers\n// ---------------------------------------------------------------------------\n\nfunction toQueryingEventId(entity: string): string {\n return `${entity}.querying`\n}\n\nfunction toQueriedEventId(entity: string): string {\n return `${entity}.queried`\n}\n\n/**\n * Convert an entity identifier from query engine format (`module:entity`)\n * to event format (`module.entity`).\n */\nexport function entityIdToEventEntity(entityId: string): string {\n return entityId.replace(/:/g, '.')\n}\n\n// ---------------------------------------------------------------------------\n// Collect query-event subscribers\n// ---------------------------------------------------------------------------\n\nexport function collectQuerySubscribers(\n eventId: string,\n): SyncSubscriberEntry[] {\n const allSync = getAllSyncSubscribers()\n return allSync\n .filter((s) => matchesEventPattern(s.metadata.event, eventId))\n .sort((a, b) => (a.metadata.priority ?? 50) - (b.metadata.priority ?? 50))\n}\n\n// ---------------------------------------------------------------------------\n// Before-query event runner\n// ---------------------------------------------------------------------------\n\nexport interface BeforeQueryResult {\n ok: boolean\n errorMessage?: string\n errorStatus?: number\n modifiedQuery?: Partial<QueryOptions>\n}\n\nexport async function runBeforeQueryEvent(\n payload: SyncQueryEventPayload,\n ctx: { resolve: <T = unknown>(name: string) => T },\n): Promise<BeforeQueryResult> {\n const eventId = toQueryingEventId(payload.entity)\n const subscribers = collectQuerySubscribers(eventId)\n\n if (subscribers.length === 0) {\n return { ok: true }\n }\n\n let currentQuery = payload.query\n\n for (const subscriber of subscribers) {\n try {\n const result = await subscriber.handler(\n { ...payload, eventId, query: currentQuery } as never,\n ctx,\n )\n const queryResult = result as SyncQueryEventResult | void\n\n if (queryResult?.ok === false) {\n return {\n ok: false,\n errorMessage: queryResult.message ?? 'Query blocked by subscriber',\n errorStatus: queryResult.status ?? 422,\n }\n }\n\n if (queryResult?.modifiedQuery) {\n currentQuery = { ...currentQuery, ...queryResult.modifiedQuery }\n }\n } catch (error) {\n logger.error('Before-query subscriber failed', { subscriberId: subscriber.metadata.id, err: error })\n return {\n ok: false,\n errorMessage: `Subscriber ${subscriber.metadata.id} threw unexpectedly`,\n errorStatus: 500,\n }\n }\n }\n\n const queryChanged = currentQuery !== payload.query\n return {\n ok: true,\n modifiedQuery: queryChanged ? currentQuery : undefined,\n }\n}\n\n// ---------------------------------------------------------------------------\n// After-query event runner\n// ---------------------------------------------------------------------------\n\nexport interface AfterQueryResult {\n modifiedResult?: QueryResult<Record<string, unknown>>\n}\n\nexport async function runAfterQueryEvent(\n payload: SyncQueryEventPayload,\n ctx: { resolve: <T = unknown>(name: string) => T },\n): Promise<AfterQueryResult> {\n const eventId = toQueriedEventId(payload.entity)\n const subscribers = collectQuerySubscribers(eventId)\n\n if (subscribers.length === 0) {\n return {}\n }\n\n let currentResult = payload.result\n\n for (const subscriber of subscribers) {\n try {\n const result = await subscriber.handler(\n { ...payload, eventId, result: currentResult } as never,\n ctx,\n )\n const queryResult = result as SyncQueryEventResult | void\n\n if (queryResult?.modifiedResult) {\n if (isValidQueryResult(queryResult.modifiedResult)) {\n currentResult = queryResult.modifiedResult\n } else {\n logger.warn('After-query subscriber returned invalid modifiedResult shape \u2014 ignored', { subscriberId: subscriber.metadata.id })\n }\n }\n } catch (error) {\n logger.error('After-query subscriber failed', { subscriberId: subscriber.metadata.id, err: error })\n }\n }\n\n const resultChanged = currentResult !== payload.result\n return {\n modifiedResult: resultChanged ? currentResult : undefined,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Query result validation\n// ---------------------------------------------------------------------------\n\nfunction isValidQueryResult(value: unknown): value is QueryResult<Record<string, unknown>> {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return (\n Array.isArray(candidate.items) &&\n typeof candidate.page === 'number' &&\n typeof candidate.pageSize === 'number' &&\n typeof candidate.total === 'number'\n )\n}\n\n// ---------------------------------------------------------------------------\n// Scope guard re-application\n// ---------------------------------------------------------------------------\n\nexport function reapplyScopeGuards(\n query: QueryOptions,\n tenantId: string,\n organizationId?: string | null,\n): QueryOptions {\n return {\n ...query,\n tenantId,\n ...(organizationId != null ? { organizationId } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Query-level enricher application\n// ---------------------------------------------------------------------------\n\nexport async function applyQueryLevelEnrichers<T extends Record<string, unknown>>(\n items: T[],\n entity: string,\n engine: 'basic' | 'hybrid',\n context: EnricherContext,\n mode: 'list' | 'detail' = 'list',\n): Promise<{ items: T[]; enrichedBy: string[]; enricherErrors: string[] }> {\n const selector: EnricherSurfaceSelector = {\n surface: 'query-engine',\n engine,\n }\n\n const entries = getEnrichersForEntity(entity, selector)\n\n const filteredEntries = entries.filter((entry) => {\n const applyOn = entry.enricher.queryEngine?.applyOn ?? ['list', 'detail']\n return applyOn.includes(mode)\n })\n\n if (filteredEntries.length === 0) {\n return { items, enrichedBy: [], enricherErrors: [] }\n }\n\n if (mode === 'detail' && items.length === 1) {\n const singleResult = await applyResponseEnricherToRecord(items[0], entity, context, filteredEntries)\n return {\n items: [singleResult.record as T],\n enrichedBy: singleResult._meta.enrichedBy,\n enricherErrors: singleResult._meta.enricherErrors ?? [],\n }\n }\n\n const result = await applyResponseEnrichers(items, entity, context, filteredEntries)\n return {\n items: result.items as T[],\n enrichedBy: result._meta.enrichedBy,\n enricherErrors: result._meta.enricherErrors ?? [],\n }\n}\n\n// ---------------------------------------------------------------------------\n// Full pipeline orchestrator\n// ---------------------------------------------------------------------------\n\nexport interface QueryExtensionContext {\n entity: string\n engine: 'basic' | 'hybrid'\n tenantId: string\n organizationId?: string | null\n userId?: string\n em: unknown\n container?: unknown\n userFeatures?: string[]\n}\n\nexport interface QueryExtensionPipelineResult<T> {\n blocked: boolean\n errorMessage?: string\n errorStatus?: number\n modifiedQuery?: QueryOptions\n finalResult?: QueryResult<T>\n}\n\n/**\n * Run the before-query phase of the extension pipeline.\n *\n * 1. Emit sync before-query event (can block / modify query)\n * 2. Re-apply mandatory scope guards after modifications\n */\nexport async function runBeforeQueryPipeline(\n query: QueryOptions,\n extensionCtx: QueryExtensionContext,\n diCtx: { resolve: <T = unknown>(name: string) => T },\n): Promise<{ blocked: boolean; errorMessage?: string; errorStatus?: number; query: QueryOptions }> {\n const eventEntity = entityIdToEventEntity(extensionCtx.entity)\n\n const beforePayload: SyncQueryEventPayload = {\n eventId: toQueryingEventId(eventEntity),\n entity: eventEntity,\n timing: 'before',\n engine: extensionCtx.engine,\n query,\n userId: extensionCtx.userId,\n organizationId: extensionCtx.organizationId,\n tenantId: extensionCtx.tenantId,\n em: extensionCtx.em as never,\n }\n\n const beforeResult = await runBeforeQueryEvent(beforePayload, diCtx)\n\n if (!beforeResult.ok) {\n return {\n blocked: true,\n errorMessage: beforeResult.errorMessage,\n errorStatus: beforeResult.errorStatus,\n query,\n }\n }\n\n let effectiveQuery = beforeResult.modifiedQuery\n ? { ...query, ...beforeResult.modifiedQuery }\n : query\n\n effectiveQuery = reapplyScopeGuards(\n effectiveQuery,\n extensionCtx.tenantId,\n extensionCtx.organizationId,\n )\n\n return { blocked: false, query: effectiveQuery }\n}\n\n/**\n * Run the after-query phase of the extension pipeline.\n *\n * 1. Apply query-level enrichers (opt-in only)\n * 2. Emit sync after-query event (can modify result)\n */\nexport async function runAfterQueryPipeline<T extends Record<string, unknown>>(\n result: QueryResult<T>,\n query: QueryOptions,\n extensionCtx: QueryExtensionContext,\n diCtx: { resolve: <T = unknown>(name: string) => T },\n): Promise<QueryResult<T>> {\n const eventEntity = entityIdToEventEntity(extensionCtx.entity)\n let currentResult: QueryResult<Record<string, unknown>> = result as QueryResult<Record<string, unknown>>\n\n const enricherContext: EnricherContext = {\n organizationId: extensionCtx.organizationId ?? '',\n tenantId: extensionCtx.tenantId,\n userId: extensionCtx.userId ?? '',\n em: extensionCtx.em,\n container: extensionCtx.container,\n userFeatures: extensionCtx.userFeatures,\n }\n\n const mode = currentResult.items.length === 1 ? 'detail' : 'list'\n const enrichResult = await applyQueryLevelEnrichers(\n currentResult.items,\n eventEntity,\n extensionCtx.engine,\n enricherContext,\n mode,\n )\n\n currentResult = { ...currentResult, items: enrichResult.items }\n\n const afterPayload: SyncQueryEventPayload = {\n eventId: toQueriedEventId(eventEntity),\n entity: eventEntity,\n timing: 'after',\n engine: extensionCtx.engine,\n query,\n result: currentResult,\n userId: extensionCtx.userId,\n organizationId: extensionCtx.organizationId,\n tenantId: extensionCtx.tenantId,\n em: extensionCtx.em as never,\n }\n\n const afterResult = await runAfterQueryEvent(afterPayload, diCtx)\n\n if (afterResult.modifiedResult) {\n currentResult = afterResult.modifiedResult\n }\n\n return currentResult as QueryResult<T>\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }