@open-mercato/core 0.6.7-develop.6785.1.1dd7cfac55 → 0.6.7-develop.6795.1.8a3f27921c

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 (196) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities.ids.generated.js +1 -0
  3. package/dist/generated/entities.ids.generated.js.map +2 -2
  4. package/dist/modules/auth/backend/settings/page.js +14 -9
  5. package/dist/modules/auth/backend/settings/page.js.map +2 -2
  6. package/dist/modules/customers/cli.js +17 -7
  7. package/dist/modules/customers/cli.js.map +2 -2
  8. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +229 -216
  9. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  10. package/dist/modules/customers/search.js +50 -29
  11. package/dist/modules/customers/search.js.map +2 -2
  12. package/dist/modules/data_sync/api/mappings/[id]/route.js +22 -9
  13. package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
  14. package/dist/modules/data_sync/api/mappings/route.js +13 -4
  15. package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
  16. package/dist/modules/data_sync/api/options.js +7 -2
  17. package/dist/modules/data_sync/api/options.js.map +2 -2
  18. package/dist/modules/data_sync/api/run.js +7 -2
  19. package/dist/modules/data_sync/api/run.js.map +2 -2
  20. package/dist/modules/data_sync/api/runs/[id]/cancel.js +8 -3
  21. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  22. package/dist/modules/data_sync/api/runs/[id]/retry.js +7 -2
  23. package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
  24. package/dist/modules/data_sync/api/runs/[id]/route.js +8 -3
  25. package/dist/modules/data_sync/api/runs/[id]/route.js.map +2 -2
  26. package/dist/modules/data_sync/api/runs.js +7 -2
  27. package/dist/modules/data_sync/api/runs.js.map +2 -2
  28. package/dist/modules/data_sync/api/schedules/[id]/route.js +19 -6
  29. package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
  30. package/dist/modules/data_sync/api/schedules/route.js +13 -4
  31. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  32. package/dist/modules/data_sync/api/validate.js +9 -4
  33. package/dist/modules/data_sync/api/validate.js.map +2 -2
  34. package/dist/modules/design_system/acl.js +9 -0
  35. package/dist/modules/design_system/acl.js.map +7 -0
  36. package/dist/modules/design_system/backend/design-system/page.js +11 -0
  37. package/dist/modules/design_system/backend/design-system/page.js.map +7 -0
  38. package/dist/modules/design_system/backend/design-system/page.meta.js +15 -0
  39. package/dist/modules/design_system/backend/design-system/page.meta.js.map +7 -0
  40. package/dist/modules/design_system/gallery/components/CodeSnippet.js +52 -0
  41. package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +7 -0
  42. package/dist/modules/design_system/gallery/components/EntryCard.js +63 -0
  43. package/dist/modules/design_system/gallery/components/EntryCard.js.map +7 -0
  44. package/dist/modules/design_system/gallery/components/GalleryShell.js +163 -0
  45. package/dist/modules/design_system/gallery/components/GalleryShell.js.map +7 -0
  46. package/dist/modules/design_system/gallery/components/VariantPreview.js +9 -0
  47. package/dist/modules/design_system/gallery/components/VariantPreview.js.map +7 -0
  48. package/dist/modules/design_system/gallery/entries/banners.js +186 -0
  49. package/dist/modules/design_system/gallery/entries/banners.js.map +7 -0
  50. package/dist/modules/design_system/gallery/entries/buttons.js +361 -0
  51. package/dist/modules/design_system/gallery/entries/buttons.js.map +7 -0
  52. package/dist/modules/design_system/gallery/entries/charts.js +457 -0
  53. package/dist/modules/design_system/gallery/entries/charts.js.map +7 -0
  54. package/dist/modules/design_system/gallery/entries/dates.js +306 -0
  55. package/dist/modules/design_system/gallery/entries/dates.js.map +7 -0
  56. package/dist/modules/design_system/gallery/entries/detail.js +358 -0
  57. package/dist/modules/design_system/gallery/entries/detail.js.map +7 -0
  58. package/dist/modules/design_system/gallery/entries/display.js +785 -0
  59. package/dist/modules/design_system/gallery/entries/display.js.map +7 -0
  60. package/dist/modules/design_system/gallery/entries/feedback.js +658 -0
  61. package/dist/modules/design_system/gallery/entries/feedback.js.map +7 -0
  62. package/dist/modules/design_system/gallery/entries/filters.js +505 -0
  63. package/dist/modules/design_system/gallery/entries/filters.js.map +7 -0
  64. package/dist/modules/design_system/gallery/entries/foundations.js +453 -0
  65. package/dist/modules/design_system/gallery/entries/foundations.js.map +7 -0
  66. package/dist/modules/design_system/gallery/entries/icons.js +140 -0
  67. package/dist/modules/design_system/gallery/entries/icons.js.map +7 -0
  68. package/dist/modules/design_system/gallery/entries/inputs.js +1217 -0
  69. package/dist/modules/design_system/gallery/entries/inputs.js.map +7 -0
  70. package/dist/modules/design_system/gallery/entries/messages.js +334 -0
  71. package/dist/modules/design_system/gallery/entries/messages.js.map +7 -0
  72. package/dist/modules/design_system/gallery/entries/navigation.js +475 -0
  73. package/dist/modules/design_system/gallery/entries/navigation.js.map +7 -0
  74. package/dist/modules/design_system/gallery/entries/notifications.js +257 -0
  75. package/dist/modules/design_system/gallery/entries/notifications.js.map +7 -0
  76. package/dist/modules/design_system/gallery/entries/overlays.js +576 -0
  77. package/dist/modules/design_system/gallery/entries/overlays.js.map +7 -0
  78. package/dist/modules/design_system/gallery/entries/scaffolding.js +378 -0
  79. package/dist/modules/design_system/gallery/entries/scaffolding.js.map +7 -0
  80. package/dist/modules/design_system/gallery/entries/schedule.js +297 -0
  81. package/dist/modules/design_system/gallery/entries/schedule.js.map +7 -0
  82. package/dist/modules/design_system/gallery/registry.js +139 -0
  83. package/dist/modules/design_system/gallery/registry.js.map +7 -0
  84. package/dist/modules/design_system/gallery/types.js +1 -0
  85. package/dist/modules/design_system/gallery/types.js.map +7 -0
  86. package/dist/modules/design_system/index.js +9 -0
  87. package/dist/modules/design_system/index.js.map +7 -0
  88. package/dist/modules/design_system/setup.js +14 -0
  89. package/dist/modules/design_system/setup.js.map +7 -0
  90. package/dist/modules/inbox_ops/search.js +15 -4
  91. package/dist/modules/inbox_ops/search.js.map +2 -2
  92. package/dist/modules/integrations/api/[id]/credentials/route.js +11 -5
  93. package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
  94. package/dist/modules/integrations/api/[id]/health/route.js +6 -3
  95. package/dist/modules/integrations/api/[id]/health/route.js.map +2 -2
  96. package/dist/modules/integrations/api/[id]/route.js +6 -3
  97. package/dist/modules/integrations/api/[id]/route.js.map +2 -2
  98. package/dist/modules/integrations/api/[id]/state/route.js +6 -3
  99. package/dist/modules/integrations/api/[id]/state/route.js.map +2 -2
  100. package/dist/modules/integrations/api/[id]/version/route.js +6 -3
  101. package/dist/modules/integrations/api/[id]/version/route.js.map +2 -2
  102. package/dist/modules/integrations/api/logs/route.js +6 -3
  103. package/dist/modules/integrations/api/logs/route.js.map +2 -2
  104. package/dist/modules/integrations/api/route.js +6 -3
  105. package/dist/modules/integrations/api/route.js.map +2 -2
  106. package/dist/modules/integrations/api/umes-read.js +2 -2
  107. package/dist/modules/integrations/api/umes-read.js.map +2 -2
  108. package/dist/modules/integrations/lib/organization-scope.js +2 -9
  109. package/dist/modules/integrations/lib/organization-scope.js.map +2 -2
  110. package/dist/modules/messages/search.js +10 -5
  111. package/dist/modules/messages/search.js.map +2 -2
  112. package/dist/modules/query_index/data/entities.js +2 -1
  113. package/dist/modules/query_index/data/entities.js.map +2 -2
  114. package/dist/modules/query_index/lib/engine.js +32 -50
  115. package/dist/modules/query_index/lib/engine.js.map +2 -2
  116. package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js +17 -0
  117. package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js.map +7 -0
  118. package/generated/entities.ids.generated.ts +1 -0
  119. package/package.json +16 -16
  120. package/src/modules/auth/backend/settings/page.tsx +17 -8
  121. package/src/modules/customers/cli.ts +17 -7
  122. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +24 -13
  123. package/src/modules/customers/i18n/de.json +17 -0
  124. package/src/modules/customers/i18n/en.json +17 -0
  125. package/src/modules/customers/i18n/es.json +17 -0
  126. package/src/modules/customers/i18n/pl.json +17 -0
  127. package/src/modules/customers/search.ts +59 -27
  128. package/src/modules/data_sync/AGENTS.md +1 -0
  129. package/src/modules/data_sync/api/mappings/[id]/route.ts +22 -9
  130. package/src/modules/data_sync/api/mappings/route.ts +13 -4
  131. package/src/modules/data_sync/api/options.ts +7 -2
  132. package/src/modules/data_sync/api/run.ts +7 -2
  133. package/src/modules/data_sync/api/runs/[id]/cancel.ts +8 -3
  134. package/src/modules/data_sync/api/runs/[id]/retry.ts +7 -2
  135. package/src/modules/data_sync/api/runs/[id]/route.ts +8 -3
  136. package/src/modules/data_sync/api/runs.ts +7 -2
  137. package/src/modules/data_sync/api/schedules/[id]/route.ts +19 -6
  138. package/src/modules/data_sync/api/schedules/route.ts +13 -4
  139. package/src/modules/data_sync/api/validate.ts +9 -4
  140. package/src/modules/design_system/AGENTS.md +34 -0
  141. package/src/modules/design_system/acl.ts +5 -0
  142. package/src/modules/design_system/backend/design-system/page.meta.ts +11 -0
  143. package/src/modules/design_system/backend/design-system/page.tsx +14 -0
  144. package/src/modules/design_system/gallery/components/CodeSnippet.tsx +55 -0
  145. package/src/modules/design_system/gallery/components/EntryCard.tsx +95 -0
  146. package/src/modules/design_system/gallery/components/GalleryShell.tsx +206 -0
  147. package/src/modules/design_system/gallery/components/VariantPreview.tsx +15 -0
  148. package/src/modules/design_system/gallery/entries/banners.tsx +226 -0
  149. package/src/modules/design_system/gallery/entries/buttons.tsx +422 -0
  150. package/src/modules/design_system/gallery/entries/charts.tsx +518 -0
  151. package/src/modules/design_system/gallery/entries/dates.tsx +325 -0
  152. package/src/modules/design_system/gallery/entries/detail.tsx +403 -0
  153. package/src/modules/design_system/gallery/entries/display.tsx +893 -0
  154. package/src/modules/design_system/gallery/entries/feedback.tsx +746 -0
  155. package/src/modules/design_system/gallery/entries/filters.tsx +570 -0
  156. package/src/modules/design_system/gallery/entries/foundations.tsx +529 -0
  157. package/src/modules/design_system/gallery/entries/icons.tsx +168 -0
  158. package/src/modules/design_system/gallery/entries/inputs.tsx +1516 -0
  159. package/src/modules/design_system/gallery/entries/messages.tsx +339 -0
  160. package/src/modules/design_system/gallery/entries/navigation.tsx +573 -0
  161. package/src/modules/design_system/gallery/entries/notifications.tsx +285 -0
  162. package/src/modules/design_system/gallery/entries/overlays.tsx +677 -0
  163. package/src/modules/design_system/gallery/entries/scaffolding.tsx +430 -0
  164. package/src/modules/design_system/gallery/entries/schedule.tsx +307 -0
  165. package/src/modules/design_system/gallery/registry.ts +152 -0
  166. package/src/modules/design_system/gallery/types.ts +32 -0
  167. package/src/modules/design_system/i18n/.hardcoded-allowlist.json +71 -0
  168. package/src/modules/design_system/i18n/de.json +46 -0
  169. package/src/modules/design_system/i18n/en.json +46 -0
  170. package/src/modules/design_system/i18n/es.json +46 -0
  171. package/src/modules/design_system/i18n/pl.json +46 -0
  172. package/src/modules/design_system/index.ts +7 -0
  173. package/src/modules/design_system/setup.ts +12 -0
  174. package/src/modules/inbox_ops/i18n/de.json +3 -0
  175. package/src/modules/inbox_ops/i18n/en.json +3 -0
  176. package/src/modules/inbox_ops/i18n/es.json +3 -0
  177. package/src/modules/inbox_ops/i18n/pl.json +3 -0
  178. package/src/modules/inbox_ops/search.ts +20 -4
  179. package/src/modules/integrations/api/[id]/credentials/route.ts +11 -5
  180. package/src/modules/integrations/api/[id]/health/route.ts +6 -3
  181. package/src/modules/integrations/api/[id]/route.ts +6 -3
  182. package/src/modules/integrations/api/[id]/state/route.ts +6 -3
  183. package/src/modules/integrations/api/[id]/version/route.ts +6 -3
  184. package/src/modules/integrations/api/logs/route.ts +6 -3
  185. package/src/modules/integrations/api/route.ts +6 -3
  186. package/src/modules/integrations/api/umes-read.ts +2 -2
  187. package/src/modules/integrations/lib/organization-scope.ts +5 -25
  188. package/src/modules/messages/i18n/de.json +2 -0
  189. package/src/modules/messages/i18n/en.json +2 -0
  190. package/src/modules/messages/i18n/es.json +2 -0
  191. package/src/modules/messages/i18n/pl.json +2 -0
  192. package/src/modules/messages/search.ts +11 -5
  193. package/src/modules/query_index/data/entities.ts +7 -0
  194. package/src/modules/query_index/lib/engine.ts +40 -63
  195. package/src/modules/query_index/migrations/.snapshot-open-mercato.json +12 -0
  196. package/src/modules/query_index/migrations/Migration20260731105052_query_index.ts +35 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/integrations/api/umes-read.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport type { AwilixContainer } from 'awilix'\nimport { runApiInterceptorsAfter, runApiInterceptorsBefore, type RunInterceptorsBeforeResult } from '@open-mercato/shared/lib/crud/interceptor-runner'\nimport { applyResponseEnrichers, applyResponseEnricherToRecord } from '@open-mercato/shared/lib/crud/enricher-runner'\nimport type { ApiInterceptorMethod, InterceptorRequest } from '@open-mercato/shared/lib/crud/api-interceptor'\nimport { resolveIntegrationsOrganizationId } from '../lib/organization-scope'\n\nexport const integrationApiRoutePaths = {\n list: 'integrations',\n detail: 'integrations/detail',\n logs: 'integrations/logs',\n} as const\n\ntype ReadRouteFieldConfig = {\n targetEntity: string\n recordKeys?: string[]\n listKeys?: string[]\n}\n\ntype ReadRouteContext = {\n routePath: string\n request: Request\n auth: {\n tenantId: string | null\n orgId: string | null\n sub?: string | null\n features?: unknown\n }\n container: AwilixContainer\n}\n\nfunction headersToObject(headers: Headers): Record<string, string> {\n return Object.fromEntries(headers.entries())\n}\n\nfunction searchParamsToObject(url: URL): Record<string, string> {\n const entries: Record<string, string> = {}\n url.searchParams.forEach((value, key) => {\n entries[key] = value\n })\n return entries\n}\n\nfunction toUserFeatures(features: unknown): string[] {\n if (!Array.isArray(features)) return []\n return features.filter((value): value is string => typeof value === 'string')\n}\n\nfunction mergeAdditiveRecord<T extends Record<string, unknown>>(base: T, candidate: T): T {\n const additions = Object.fromEntries(\n Object.entries(candidate).filter(([key]) => !(key in base)),\n ) as Partial<T>\n\n return {\n ...base,\n ...additions,\n }\n}\n\nfunction mergeAdditiveBody(\n baseBody: Record<string, unknown>,\n candidateBody: Record<string, unknown>,\n): Record<string, unknown> {\n return mergeAdditiveRecord(baseBody, candidateBody)\n}\n\nfunction getEnricherContext(input: ReadRouteContext) {\n return {\n organizationId: resolveIntegrationsOrganizationId(input.auth) as string,\n tenantId: input.auth.tenantId as string,\n userId: input.auth.sub ?? '',\n em: input.container.resolve('em') as EntityManager,\n container: input.container,\n userFeatures: toUserFeatures(input.auth.features),\n }\n}\n\nexport async function runIntegrationsReadBeforeInterceptors(\n input: ReadRouteContext,\n): Promise<RunInterceptorsBeforeResult> {\n const url = new URL(input.request.url)\n\n return runApiInterceptorsBefore({\n routePath: input.routePath,\n method: 'GET',\n request: {\n method: 'GET',\n url: input.request.url,\n headers: headersToObject(input.request.headers),\n query: searchParamsToObject(url),\n },\n context: getEnricherContext(input),\n })\n}\n\nasync function applySafeRecordEnrichment(\n record: Record<string, unknown>,\n targetEntity: string,\n context: ReturnType<typeof getEnricherContext>,\n): Promise<Record<string, unknown>> {\n const enriched = await applyResponseEnricherToRecord(record, targetEntity, context)\n return mergeAdditiveRecord(record, enriched.record)\n}\n\nasync function applySafeListEnrichment(\n items: Record<string, unknown>[],\n targetEntity: string,\n context: ReturnType<typeof getEnricherContext>,\n): Promise<Record<string, unknown>[]> {\n const enriched = await applyResponseEnrichers(items, targetEntity, context)\n return items.map((item, index) => {\n const enrichedItem = enriched.items[index]\n if (!enrichedItem) return item\n return mergeAdditiveRecord(item, enrichedItem)\n })\n}\n\nexport async function finalizeIntegrationsReadResponse(\n input: ReadRouteContext & {\n interceptorRequest: InterceptorRequest\n body: Record<string, unknown>\n enrich?: ReadRouteFieldConfig\n method?: ApiInterceptorMethod\n beforeMetadata?: Record<string, Record<string, unknown> | undefined>\n },\n): Promise<NextResponse> {\n const method = input.method ?? 'GET'\n const baseBody = input.body\n const context = getEnricherContext(input)\n\n const intercepted = await runApiInterceptorsAfter({\n routePath: input.routePath,\n method,\n request: input.interceptorRequest,\n response: {\n statusCode: 200,\n body: baseBody,\n headers: {},\n },\n context,\n metadataByInterceptor: input.beforeMetadata,\n })\n\n if (!intercepted.ok) {\n return NextResponse.json(intercepted.body, { status: intercepted.statusCode, headers: intercepted.headers })\n }\n\n let responseBody = mergeAdditiveBody(baseBody, intercepted.body)\n\n if (input.enrich) {\n const { targetEntity, recordKeys = [], listKeys = [] } = input.enrich\n\n for (const key of recordKeys) {\n const current = responseBody[key]\n if (!current || typeof current !== 'object' || Array.isArray(current)) continue\n responseBody = {\n ...responseBody,\n [key]: await applySafeRecordEnrichment(current as Record<string, unknown>, targetEntity, context),\n }\n }\n\n for (const key of listKeys) {\n const current = responseBody[key]\n if (!Array.isArray(current)) continue\n const records = current.filter(\n (item): item is Record<string, unknown> => typeof item === 'object' && item !== null && !Array.isArray(item),\n )\n if (records.length !== current.length) continue\n responseBody = {\n ...responseBody,\n [key]: await applySafeListEnrichment(records, targetEntity, context),\n }\n }\n }\n\n return NextResponse.json(responseBody, { headers: intercepted.headers })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport type { AwilixContainer } from 'awilix'\nimport { runApiInterceptorsAfter, runApiInterceptorsBefore, type RunInterceptorsBeforeResult } from '@open-mercato/shared/lib/crud/interceptor-runner'\nimport { applyResponseEnrichers, applyResponseEnricherToRecord } from '@open-mercato/shared/lib/crud/enricher-runner'\nimport type { ApiInterceptorMethod, InterceptorRequest } from '@open-mercato/shared/lib/crud/api-interceptor'\nimport { resolveActiveOrganizationId } from '@open-mercato/shared/lib/auth/organizationScope'\n\nexport const integrationApiRoutePaths = {\n list: 'integrations',\n detail: 'integrations/detail',\n logs: 'integrations/logs',\n} as const\n\ntype ReadRouteFieldConfig = {\n targetEntity: string\n recordKeys?: string[]\n listKeys?: string[]\n}\n\ntype ReadRouteContext = {\n routePath: string\n request: Request\n auth: {\n tenantId: string | null\n orgId: string | null\n sub?: string | null\n features?: unknown\n }\n container: AwilixContainer\n}\n\nfunction headersToObject(headers: Headers): Record<string, string> {\n return Object.fromEntries(headers.entries())\n}\n\nfunction searchParamsToObject(url: URL): Record<string, string> {\n const entries: Record<string, string> = {}\n url.searchParams.forEach((value, key) => {\n entries[key] = value\n })\n return entries\n}\n\nfunction toUserFeatures(features: unknown): string[] {\n if (!Array.isArray(features)) return []\n return features.filter((value): value is string => typeof value === 'string')\n}\n\nfunction mergeAdditiveRecord<T extends Record<string, unknown>>(base: T, candidate: T): T {\n const additions = Object.fromEntries(\n Object.entries(candidate).filter(([key]) => !(key in base)),\n ) as Partial<T>\n\n return {\n ...base,\n ...additions,\n }\n}\n\nfunction mergeAdditiveBody(\n baseBody: Record<string, unknown>,\n candidateBody: Record<string, unknown>,\n): Record<string, unknown> {\n return mergeAdditiveRecord(baseBody, candidateBody)\n}\n\nfunction getEnricherContext(input: ReadRouteContext) {\n return {\n organizationId: resolveActiveOrganizationId(input.auth) as string,\n tenantId: input.auth.tenantId as string,\n userId: input.auth.sub ?? '',\n em: input.container.resolve('em') as EntityManager,\n container: input.container,\n userFeatures: toUserFeatures(input.auth.features),\n }\n}\n\nexport async function runIntegrationsReadBeforeInterceptors(\n input: ReadRouteContext,\n): Promise<RunInterceptorsBeforeResult> {\n const url = new URL(input.request.url)\n\n return runApiInterceptorsBefore({\n routePath: input.routePath,\n method: 'GET',\n request: {\n method: 'GET',\n url: input.request.url,\n headers: headersToObject(input.request.headers),\n query: searchParamsToObject(url),\n },\n context: getEnricherContext(input),\n })\n}\n\nasync function applySafeRecordEnrichment(\n record: Record<string, unknown>,\n targetEntity: string,\n context: ReturnType<typeof getEnricherContext>,\n): Promise<Record<string, unknown>> {\n const enriched = await applyResponseEnricherToRecord(record, targetEntity, context)\n return mergeAdditiveRecord(record, enriched.record)\n}\n\nasync function applySafeListEnrichment(\n items: Record<string, unknown>[],\n targetEntity: string,\n context: ReturnType<typeof getEnricherContext>,\n): Promise<Record<string, unknown>[]> {\n const enriched = await applyResponseEnrichers(items, targetEntity, context)\n return items.map((item, index) => {\n const enrichedItem = enriched.items[index]\n if (!enrichedItem) return item\n return mergeAdditiveRecord(item, enrichedItem)\n })\n}\n\nexport async function finalizeIntegrationsReadResponse(\n input: ReadRouteContext & {\n interceptorRequest: InterceptorRequest\n body: Record<string, unknown>\n enrich?: ReadRouteFieldConfig\n method?: ApiInterceptorMethod\n beforeMetadata?: Record<string, Record<string, unknown> | undefined>\n },\n): Promise<NextResponse> {\n const method = input.method ?? 'GET'\n const baseBody = input.body\n const context = getEnricherContext(input)\n\n const intercepted = await runApiInterceptorsAfter({\n routePath: input.routePath,\n method,\n request: input.interceptorRequest,\n response: {\n statusCode: 200,\n body: baseBody,\n headers: {},\n },\n context,\n metadataByInterceptor: input.beforeMetadata,\n })\n\n if (!intercepted.ok) {\n return NextResponse.json(intercepted.body, { status: intercepted.statusCode, headers: intercepted.headers })\n }\n\n let responseBody = mergeAdditiveBody(baseBody, intercepted.body)\n\n if (input.enrich) {\n const { targetEntity, recordKeys = [], listKeys = [] } = input.enrich\n\n for (const key of recordKeys) {\n const current = responseBody[key]\n if (!current || typeof current !== 'object' || Array.isArray(current)) continue\n responseBody = {\n ...responseBody,\n [key]: await applySafeRecordEnrichment(current as Record<string, unknown>, targetEntity, context),\n }\n }\n\n for (const key of listKeys) {\n const current = responseBody[key]\n if (!Array.isArray(current)) continue\n const records = current.filter(\n (item): item is Record<string, unknown> => typeof item === 'object' && item !== null && !Array.isArray(item),\n )\n if (records.length !== current.length) continue\n responseBody = {\n ...responseBody,\n [key]: await applySafeListEnrichment(records, targetEntity, context),\n }\n }\n }\n\n return NextResponse.json(responseBody, { headers: intercepted.headers })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,12 +1,5 @@
1
- function resolveIntegrationsOrganizationId(auth) {
2
- if (!auth) return null;
3
- const selected = auth.orgId;
4
- if (typeof selected === "string" && selected.trim().length > 0) return selected;
5
- const actorOrgId = auth.actorOrgId;
6
- if (typeof actorOrgId === "string" && actorOrgId.trim().length > 0) return actorOrgId;
7
- return null;
8
- }
1
+ import { resolveActiveOrganizationId } from "@open-mercato/shared/lib/auth/organizationScope";
9
2
  export {
10
- resolveIntegrationsOrganizationId
3
+ resolveActiveOrganizationId as resolveIntegrationsOrganizationId
11
4
  };
12
5
  //# sourceMappingURL=organization-scope.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/integrations/lib/organization-scope.ts"],
4
- "sourcesContent": ["type OrganizationScopedAuth = {\n orgId?: string | null\n actorOrgId?: unknown\n} | null | undefined\n\n/**\n * Resolves the organization an integrations request is scoped to.\n *\n * Integrations are configured per organization \u2014 `integration_credentials` and\n * `integration_states` both require a non-null `organization_id` \u2014 so there is no\n * meaningful \"all organizations\" view of this module. When an operator selects that\n * option the super-admin cookie override clears `auth.orgId` and preserves the actor's\n * own organization in `actorOrgId`; fall back to it so the module keeps showing the\n * operator's own configuration instead of failing.\n *\n * Answering 401 for that case is not merely wrong but self-perpetuating: `apiFetch`\n * reads 401 as an expired session and redirects through `/api/auth/session/refresh`,\n * which succeeds and returns to the same page, reloading forever.\n */\nexport function resolveIntegrationsOrganizationId(auth: OrganizationScopedAuth): string | null {\n if (!auth) return null\n const selected = auth.orgId\n if (typeof selected === 'string' && selected.trim().length > 0) return selected\n const actorOrgId = auth.actorOrgId\n if (typeof actorOrgId === 'string' && actorOrgId.trim().length > 0) return actorOrgId\n return null\n}\n"],
5
- "mappings": "AAmBO,SAAS,kCAAkC,MAA6C;AAC7F,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,WAAW,KAAK;AACtB,MAAI,OAAO,aAAa,YAAY,SAAS,KAAK,EAAE,SAAS,EAAG,QAAO;AACvE,QAAM,aAAa,KAAK;AACxB,MAAI,OAAO,eAAe,YAAY,WAAW,KAAK,EAAE,SAAS,EAAG,QAAO;AAC3E,SAAO;AACT;",
4
+ "sourcesContent": ["/**\n * @deprecated Moved to `@open-mercato/shared/lib/auth/organizationScope` as\n * `resolveActiveOrganizationId`, because `data_sync` needs the same resolution and\n * `integrations` is not the right owner for it. This alias stays for third-party\n * modules that already import from here.\n */\nexport { resolveActiveOrganizationId as resolveIntegrationsOrganizationId } from '@open-mercato/shared/lib/auth/organizationScope'\n"],
5
+ "mappings": "AAMA,SAAwC,mCAAyC;",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
1
2
  function appendLine(lines, label, value) {
2
3
  if (value === null || value === void 0) return;
3
4
  const text = Array.isArray(value) ? value.map((item) => item === null || item === void 0 ? "" : String(item)).filter(Boolean).join(", ") : typeof value === "object" ? JSON.stringify(value) : String(value);
@@ -19,8 +20,8 @@ function snippet(value, max = 140) {
19
20
  if (trimmed.length <= max) return trimmed;
20
21
  return `${trimmed.slice(0, max - 3)}...`;
21
22
  }
22
- function buildMessagePresenter(record) {
23
- const title = pickString(record.subject) ?? "Message";
23
+ function buildMessagePresenter(translate, record) {
24
+ const title = pickString(record.subject) ?? translate("messages.search.fallback.title", "Message");
24
25
  const body = snippet(record.body);
25
26
  const externalName = pickString(record.external_name, record.externalName);
26
27
  const subtitle = [externalName, body].filter(Boolean).join(" \xB7 ") || void 0;
@@ -28,7 +29,7 @@ function buildMessagePresenter(record) {
28
29
  title: String(title),
29
30
  subtitle,
30
31
  icon: "mail",
31
- badge: "Message"
32
+ badge: translate("messages.search.badge.message", "Message")
32
33
  };
33
34
  }
34
35
  const searchConfig = {
@@ -38,6 +39,7 @@ const searchConfig = {
38
39
  enabled: true,
39
40
  priority: 5,
40
41
  buildSource: async (ctx) => {
42
+ const { t } = await resolveTranslations();
41
43
  const record = ctx.record;
42
44
  const lines = [];
43
45
  appendLine(lines, "Subject", record.subject);
@@ -46,7 +48,7 @@ const searchConfig = {
46
48
  if (!lines.length) return null;
47
49
  return {
48
50
  text: lines,
49
- presenter: buildMessagePresenter(record),
51
+ presenter: buildMessagePresenter(t, record),
50
52
  checksumSource: {
51
53
  record: {
52
54
  subject: record.subject,
@@ -57,7 +59,10 @@ const searchConfig = {
57
59
  }
58
60
  };
59
61
  },
60
- formatResult: async (ctx) => buildMessagePresenter(ctx.record),
62
+ formatResult: async (ctx) => {
63
+ const { t } = await resolveTranslations();
64
+ return buildMessagePresenter(t, ctx.record);
65
+ },
61
66
  resolveUrl: async (ctx) => {
62
67
  const id = pickString(ctx.record.id);
63
68
  return id ? `/backend/messages/${encodeURIComponent(id)}` : null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/messages/search.ts"],
4
- "sourcesContent": ["import type {\n SearchModuleConfig,\n SearchBuildContext,\n SearchIndexSource,\n SearchResultPresenter,\n} from '@open-mercato/shared/modules/search'\n\nfunction appendLine(lines: string[], label: string, value: unknown) {\n if (value === null || value === undefined) return\n const text = Array.isArray(value)\n ? value.map((item) => (item === null || item === undefined ? '' : String(item))).filter(Boolean).join(', ')\n : (typeof value === 'object' ? JSON.stringify(value) : String(value))\n if (!text.trim()) return\n lines.push(`${label}: ${text}`)\n}\n\nfunction pickString(...candidates: Array<unknown>): string | null {\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') continue\n const trimmed = candidate.trim()\n if (trimmed.length > 0) return trimmed\n }\n return null\n}\n\nfunction snippet(value: unknown, max = 140): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n if (!trimmed.length) return undefined\n if (trimmed.length <= max) return trimmed\n return `${trimmed.slice(0, max - 3)}...`\n}\n\nfunction buildMessagePresenter(record: Record<string, unknown>): SearchResultPresenter {\n const title = pickString(record.subject) ?? 'Message'\n const body = snippet(record.body)\n const externalName = pickString(record.external_name, record.externalName)\n const subtitle = [externalName, body].filter(Boolean).join(' \u00B7 ') || undefined\n return {\n title: String(title),\n subtitle,\n icon: 'mail',\n badge: 'Message',\n }\n}\n\nexport const searchConfig: SearchModuleConfig = {\n entities: [\n {\n entityId: 'messages:message',\n enabled: true,\n priority: 5,\n buildSource: async (ctx: SearchBuildContext): Promise<SearchIndexSource | null> => {\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Subject', record.subject)\n appendLine(lines, 'Body', record.body)\n appendLine(lines, 'From name', record.external_name ?? record.externalName)\n if (!lines.length) return null\n return {\n text: lines,\n presenter: buildMessagePresenter(record),\n checksumSource: {\n record: {\n subject: record.subject,\n body: record.body,\n external_name: record.external_name ?? record.externalName,\n external_email_hash: record.external_email_hash ?? record.externalEmailHash,\n },\n },\n }\n },\n formatResult: async (ctx) => buildMessagePresenter(ctx.record),\n resolveUrl: async (ctx) => {\n const id = pickString(ctx.record.id)\n return id ? `/backend/messages/${encodeURIComponent(id)}` : null\n },\n fieldPolicy: {\n searchable: ['subject', 'body', 'external_name'],\n hashOnly: ['external_email'],\n excluded: ['action_data', 'action_result'],\n },\n },\n ],\n}\n\nexport default searchConfig\nexport const config = searchConfig\n"],
5
- "mappings": "AAOA,SAAS,WAAW,OAAiB,OAAe,OAAgB;AAClE,MAAI,UAAU,QAAQ,UAAU,OAAW;AAC3C,QAAM,OAAO,MAAM,QAAQ,KAAK,IAC5B,MAAM,IAAI,CAAC,SAAU,SAAS,QAAQ,SAAS,SAAY,KAAK,OAAO,IAAI,CAAE,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,IACvG,OAAO,UAAU,WAAW,KAAK,UAAU,KAAK,IAAI,OAAO,KAAK;AACrE,MAAI,CAAC,KAAK,KAAK,EAAG;AAClB,QAAM,KAAK,GAAG,KAAK,KAAK,IAAI,EAAE;AAChC;AAEA,SAAS,cAAc,YAA2C;AAChE,aAAW,aAAa,YAAY;AAClC,QAAI,OAAO,cAAc,SAAU;AACnC,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,QAAQ,SAAS,EAAG,QAAO;AAAA,EACjC;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,OAAgB,MAAM,KAAyB;AAC9D,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,MAAI,QAAQ,UAAU,IAAK,QAAO;AAClC,SAAO,GAAG,QAAQ,MAAM,GAAG,MAAM,CAAC,CAAC;AACrC;AAEA,SAAS,sBAAsB,QAAwD;AACrF,QAAM,QAAQ,WAAW,OAAO,OAAO,KAAK;AAC5C,QAAM,OAAO,QAAQ,OAAO,IAAI;AAChC,QAAM,eAAe,WAAW,OAAO,eAAe,OAAO,YAAY;AACzE,QAAM,WAAW,CAAC,cAAc,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,QAAK,KAAK;AACrE,SAAO;AAAA,IACL,OAAO,OAAO,KAAK;AAAA,IACnB;AAAA,IACA,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AACF;AAEO,MAAM,eAAmC;AAAA,EAC9C,UAAU;AAAA,IACR;AAAA,MACE,UAAU;AAAA,MACV,SAAS;AAAA,MACT,UAAU;AAAA,MACV,aAAa,OAAO,QAA+D;AACjF,cAAM,SAAS,IAAI;AACnB,cAAM,QAAkB,CAAC;AACzB,mBAAW,OAAO,WAAW,OAAO,OAAO;AAC3C,mBAAW,OAAO,QAAQ,OAAO,IAAI;AACrC,mBAAW,OAAO,aAAa,OAAO,iBAAiB,OAAO,YAAY;AAC1E,YAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,eAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAW,sBAAsB,MAAM;AAAA,UACvC,gBAAgB;AAAA,YACd,QAAQ;AAAA,cACN,SAAS,OAAO;AAAA,cAChB,MAAM,OAAO;AAAA,cACb,eAAe,OAAO,iBAAiB,OAAO;AAAA,cAC9C,qBAAqB,OAAO,uBAAuB,OAAO;AAAA,YAC5D;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,cAAc,OAAO,QAAQ,sBAAsB,IAAI,MAAM;AAAA,MAC7D,YAAY,OAAO,QAAQ;AACzB,cAAM,KAAK,WAAW,IAAI,OAAO,EAAE;AACnC,eAAO,KAAK,qBAAqB,mBAAmB,EAAE,CAAC,KAAK;AAAA,MAC9D;AAAA,MACA,aAAa;AAAA,QACX,YAAY,CAAC,WAAW,QAAQ,eAAe;AAAA,QAC/C,UAAU,CAAC,gBAAgB;AAAA,QAC3B,UAAU,CAAC,eAAe,eAAe;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;AACR,MAAM,SAAS;",
4
+ "sourcesContent": ["import type {\n SearchModuleConfig,\n SearchBuildContext,\n SearchIndexSource,\n SearchResultPresenter,\n} from '@open-mercato/shared/modules/search'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nfunction appendLine(lines: string[], label: string, value: unknown) {\n if (value === null || value === undefined) return\n const text = Array.isArray(value)\n ? value.map((item) => (item === null || item === undefined ? '' : String(item))).filter(Boolean).join(', ')\n : (typeof value === 'object' ? JSON.stringify(value) : String(value))\n if (!text.trim()) return\n lines.push(`${label}: ${text}`)\n}\n\nfunction pickString(...candidates: Array<unknown>): string | null {\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') continue\n const trimmed = candidate.trim()\n if (trimmed.length > 0) return trimmed\n }\n return null\n}\n\nfunction snippet(value: unknown, max = 140): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n if (!trimmed.length) return undefined\n if (trimmed.length <= max) return trimmed\n return `${trimmed.slice(0, max - 3)}...`\n}\n\nfunction buildMessagePresenter(translate: TranslateFn, record: Record<string, unknown>): SearchResultPresenter {\n const title = pickString(record.subject) ?? translate('messages.search.fallback.title', 'Message')\n const body = snippet(record.body)\n const externalName = pickString(record.external_name, record.externalName)\n const subtitle = [externalName, body].filter(Boolean).join(' \u00B7 ') || undefined\n return {\n title: String(title),\n subtitle,\n icon: 'mail',\n badge: translate('messages.search.badge.message', 'Message'),\n }\n}\n\nexport const searchConfig: SearchModuleConfig = {\n entities: [\n {\n entityId: 'messages:message',\n enabled: true,\n priority: 5,\n buildSource: async (ctx: SearchBuildContext): Promise<SearchIndexSource | null> => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Subject', record.subject)\n appendLine(lines, 'Body', record.body)\n appendLine(lines, 'From name', record.external_name ?? record.externalName)\n if (!lines.length) return null\n return {\n text: lines,\n presenter: buildMessagePresenter(t, record),\n checksumSource: {\n record: {\n subject: record.subject,\n body: record.body,\n external_name: record.external_name ?? record.externalName,\n external_email_hash: record.external_email_hash ?? record.externalEmailHash,\n },\n },\n }\n },\n formatResult: async (ctx) => {\n const { t } = await resolveTranslations()\n return buildMessagePresenter(t, ctx.record)\n },\n resolveUrl: async (ctx) => {\n const id = pickString(ctx.record.id)\n return id ? `/backend/messages/${encodeURIComponent(id)}` : null\n },\n fieldPolicy: {\n searchable: ['subject', 'body', 'external_name'],\n hashOnly: ['external_email'],\n excluded: ['action_data', 'action_result'],\n },\n },\n ],\n}\n\nexport default searchConfig\nexport const config = searchConfig\n"],
5
+ "mappings": "AAOA,SAAS,2BAA2B;AAEpC,SAAS,WAAW,OAAiB,OAAe,OAAgB;AAClE,MAAI,UAAU,QAAQ,UAAU,OAAW;AAC3C,QAAM,OAAO,MAAM,QAAQ,KAAK,IAC5B,MAAM,IAAI,CAAC,SAAU,SAAS,QAAQ,SAAS,SAAY,KAAK,OAAO,IAAI,CAAE,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,IACvG,OAAO,UAAU,WAAW,KAAK,UAAU,KAAK,IAAI,OAAO,KAAK;AACrE,MAAI,CAAC,KAAK,KAAK,EAAG;AAClB,QAAM,KAAK,GAAG,KAAK,KAAK,IAAI,EAAE;AAChC;AAEA,SAAS,cAAc,YAA2C;AAChE,aAAW,aAAa,YAAY;AAClC,QAAI,OAAO,cAAc,SAAU;AACnC,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,QAAQ,SAAS,EAAG,QAAO;AAAA,EACjC;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,OAAgB,MAAM,KAAyB;AAC9D,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,MAAI,QAAQ,UAAU,IAAK,QAAO;AAClC,SAAO,GAAG,QAAQ,MAAM,GAAG,MAAM,CAAC,CAAC;AACrC;AAEA,SAAS,sBAAsB,WAAwB,QAAwD;AAC7G,QAAM,QAAQ,WAAW,OAAO,OAAO,KAAK,UAAU,kCAAkC,SAAS;AACjG,QAAM,OAAO,QAAQ,OAAO,IAAI;AAChC,QAAM,eAAe,WAAW,OAAO,eAAe,OAAO,YAAY;AACzE,QAAM,WAAW,CAAC,cAAc,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,QAAK,KAAK;AACrE,SAAO;AAAA,IACL,OAAO,OAAO,KAAK;AAAA,IACnB;AAAA,IACA,MAAM;AAAA,IACN,OAAO,UAAU,iCAAiC,SAAS;AAAA,EAC7D;AACF;AAEO,MAAM,eAAmC;AAAA,EAC9C,UAAU;AAAA,IACR;AAAA,MACE,UAAU;AAAA,MACV,SAAS;AAAA,MACT,UAAU;AAAA,MACV,aAAa,OAAO,QAA+D;AACjF,cAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,cAAM,SAAS,IAAI;AACnB,cAAM,QAAkB,CAAC;AACzB,mBAAW,OAAO,WAAW,OAAO,OAAO;AAC3C,mBAAW,OAAO,QAAQ,OAAO,IAAI;AACrC,mBAAW,OAAO,aAAa,OAAO,iBAAiB,OAAO,YAAY;AAC1E,YAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,eAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAW,sBAAsB,GAAG,MAAM;AAAA,UAC1C,gBAAgB;AAAA,YACd,QAAQ;AAAA,cACN,SAAS,OAAO;AAAA,cAChB,MAAM,OAAO;AAAA,cACb,eAAe,OAAO,iBAAiB,OAAO;AAAA,cAC9C,qBAAqB,OAAO,uBAAuB,OAAO;AAAA,YAC5D;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,cAAc,OAAO,QAAQ;AAC3B,cAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,eAAO,sBAAsB,GAAG,IAAI,MAAM;AAAA,MAC5C;AAAA,MACA,YAAY,OAAO,QAAQ;AACzB,cAAM,KAAK,WAAW,IAAI,OAAO,EAAE;AACnC,eAAO,KAAK,qBAAqB,mBAAmB,EAAE,CAAC,KAAK;AAAA,MAC9D;AAAA,MACA,aAAa;AAAA,QACX,YAAY,CAAC,WAAW,QAAQ,eAAe;AAAA,QAC/C,UAAU,CAAC,gBAAgB;AAAA,QAC3B,UAAU,CAAC,eAAe,eAAe;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;AACR,MAAM,SAAS;",
6
6
  "names": []
7
7
  }
@@ -308,7 +308,8 @@ SearchToken = __decorateClass([
308
308
  Entity({ tableName: "search_tokens" }),
309
309
  Index({ name: "search_tokens_lookup_idx", properties: ["entityType", "field", "tokenHash", "tenantId", "organizationId"] }),
310
310
  Index({ name: "search_tokens_entity_idx", properties: ["entityType", "entityId"] }),
311
- Index({ name: "search_tokens_tenant_token_hash_idx", properties: ["tenantId", "tokenHash"] })
311
+ Index({ name: "search_tokens_tenant_token_hash_idx", properties: ["tenantId", "tokenHash"] }),
312
+ Index({ name: "search_tokens_presence_idx", properties: ["entityType", "tenantId", "organizationId"] })
312
313
  ], SearchToken);
313
314
  export {
314
315
  EntityIndexCoverage,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/query_index/data/entities.ts"],
4
- "sourcesContent": ["import { Entity, Index, PrimaryKey, Property, Unique } from '@mikro-orm/decorators/legacy'\n\n// Generic JSONB-backed index rows for any entity ('<module>:<entity>')\n@Entity({ tableName: 'entity_indexes' })\n@Index({\n name: 'entity_indexes_customer_entity_doc_idx',\n expression:\n `create index \"entity_indexes_customer_entity_doc_idx\" on \"entity_indexes\" (\"entity_id\", \"organization_id\", \"tenant_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_entity' and organization_id is not null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_person_profile_doc_idx',\n expression:\n `create index \"entity_indexes_customer_person_profile_doc_idx\" on \"entity_indexes\" (\"entity_id\", \"organization_id\", \"tenant_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_person_profile' and organization_id is not null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_company_profile_doc_idx',\n expression:\n `create index \"entity_indexes_customer_company_profile_doc_idx\" on \"entity_indexes\" (\"entity_id\", \"organization_id\", \"tenant_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_company_profile' and organization_id is not null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_entity_tenant_doc_idx',\n expression:\n `create index \"entity_indexes_customer_entity_tenant_doc_idx\" on \"entity_indexes\" (\"tenant_id\", \"entity_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_entity' and organization_id is null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_person_profile_tenant_doc_idx',\n expression:\n `create index \"entity_indexes_customer_person_profile_tenant_doc_idx\" on \"entity_indexes\" (\"tenant_id\", \"entity_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_person_profile' and organization_id is null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_company_profile_tenant_doc_idx',\n expression:\n `create index \"entity_indexes_customer_company_profile_tenant_doc_idx\" on \"entity_indexes\" (\"tenant_id\", \"entity_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_company_profile' and organization_id is null and tenant_id is not null`,\n})\n@Index({ name: 'entity_indexes_type_tenant_idx', properties: ['entityType', 'tenantId'] })\n@Unique({\n name: 'entity_indexes_type_entity_org_coalesced_unique',\n properties: ['entityType', 'entityId', 'organizationIdCoalesced'],\n})\nexport class EntityIndexRow {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n // Entity identifier: '<module>:<entity>'\n @Property({ name: 'entity_type', type: 'text' })\n @Index({ name: 'entity_indexes_type_idx' })\n entityType!: string\n\n // Record id as text for compatibility with uuid/int\n @Property({ name: 'entity_id', type: 'text' })\n @Index({ name: 'entity_indexes_entity_idx' })\n entityId!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n @Index({ name: 'entity_indexes_org_idx' })\n organizationId?: string | null\n\n @Property({\n name: 'organization_id_coalesced',\n type: 'uuid',\n generated: (cols) => `(coalesce(${cols.organizationId}, '00000000-0000-0000-0000-000000000000'::uuid)) stored`,\n hidden: true,\n })\n organizationIdCoalesced!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n // Flattened document of base fields and custom fields\n @Property({ name: 'doc', type: 'json' })\n doc!: any\n\n // Optional embedding vector or metadata produced by secondary indexers\n @Property({ name: 'embedding', type: 'json', nullable: true })\n embedding?: any | null\n\n @Property({ name: 'index_version', type: 'int', default: 1 })\n indexVersion: number = 1\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// Track long-running index jobs (reindex/purge) per entity and org scope\n@Entity({ tableName: 'entity_index_jobs' })\n// Coalesced-scope unique index so prepareJob can upsert atomically and two\n// concurrent schedulers cannot insert duplicate rows for the same scope (#2739).\n// Declared via @Unique with a raw expression (not properties) because the scope\n// columns are nullable and NULLs must collapse to one bucket via coalesce; mirrors\n// the entity_indexes coalesced uniqueness and the domain_mappings expression unique.\n@Unique({\n name: 'entity_index_jobs_scope_unique',\n expression:\n `create unique index \"entity_index_jobs_scope_unique\" on \"entity_index_jobs\" (\"entity_type\", coalesce(\"organization_id\", '00000000-0000-0000-0000-000000000000'::uuid), coalesce(\"tenant_id\", '00000000-0000-0000-0000-000000000000'::uuid), coalesce(\"partition_index\", -1), coalesce(\"partition_count\", -1))`,\n})\nexport class EntityIndexJob {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'entity_type', type: 'text' })\n @Index({ name: 'entity_index_jobs_type_idx' })\n entityType!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n @Index({ name: 'entity_index_jobs_org_idx' })\n organizationId?: string | null\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'partition_index', type: 'int', nullable: true })\n partitionIndex?: number | null\n\n @Property({ name: 'partition_count', type: 'int', nullable: true })\n partitionCount?: number | null\n\n @Property({ name: 'processed_count', type: 'int', nullable: true })\n processedCount?: number | null\n\n @Property({ name: 'total_count', type: 'int', nullable: true })\n totalCount?: number | null\n\n @Property({ name: 'heartbeat_at', type: Date, nullable: true })\n heartbeatAt?: Date | null\n\n // 'reindexing' | 'purging'\n @Property({ name: 'status', type: 'text' })\n status!: string\n\n @Property({ name: 'started_at', type: Date, onCreate: () => new Date() })\n startedAt: Date = new Date()\n\n @Property({ name: 'finished_at', type: Date, nullable: true })\n finishedAt?: Date | null\n}\n\n// Snapshot counts for coverage checks (per entity / tenant / org / withDeleted scope)\n@Entity({ tableName: 'entity_index_coverage' })\n@Unique({\n name: 'entity_index_coverage_scope_idx',\n properties: ['entityType', 'tenantId', 'organizationId', 'withDeleted'],\n})\nexport class EntityIndexCoverage {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'entity_type', type: 'text' })\n entityType!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'with_deleted', type: 'boolean', default: false })\n withDeleted: boolean = false\n\n @Property({ name: 'base_count', type: 'int', unsigned: true, default: 0 })\n baseCount: number = 0\n\n @Property({ name: 'indexed_count', type: 'int', unsigned: true, default: 0 })\n indexedCount: number = 0\n\n @Property({ name: 'vector_indexed_count', type: 'int', unsigned: true, default: 0 })\n vectorIndexedCount: number = 0\n\n @Property({ name: 'refreshed_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n refreshedAt: Date = new Date()\n}\n\n@Entity({ tableName: 'indexer_error_logs' })\n@Index({ name: 'indexer_error_logs_source_idx', properties: ['source'] })\n@Index({ name: 'indexer_error_logs_occurred_idx', properties: ['occurredAt'] })\nexport class IndexerErrorLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'source', type: 'text' })\n source!: string\n\n @Property({ name: 'handler', type: 'text' })\n handler!: string\n\n @Property({ name: 'entity_type', type: 'text', nullable: true })\n entityType?: string | null\n\n @Property({ name: 'record_id', type: 'text', nullable: true })\n recordId?: string | null\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'payload', type: 'json', nullable: true })\n payload?: any | null\n\n @Property({ name: 'message', type: 'text' })\n message!: string\n\n @Property({ name: 'stack', type: 'text', nullable: true })\n stack?: string | null\n\n@Property({ name: 'occurred_at', type: Date, onCreate: () => new Date() })\n occurredAt: Date = new Date()\n}\n\n@Entity({ tableName: 'indexer_status_logs' })\n@Index({ name: 'indexer_status_logs_source_idx', properties: ['source'] })\n@Index({ name: 'indexer_status_logs_occurred_idx', properties: ['occurredAt'] })\nexport class IndexerStatusLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'source', type: 'text' })\n source!: string\n\n @Property({ name: 'handler', type: 'text' })\n handler!: string\n\n @Property({ name: 'level', type: 'text' })\n level: string = 'info'\n\n @Property({ name: 'entity_type', type: 'text', nullable: true })\n entityType?: string | null\n\n @Property({ name: 'record_id', type: 'text', nullable: true })\n recordId?: string | null\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'message', type: 'text' })\n message!: string\n\n @Property({ name: 'details', type: 'json', nullable: true })\n details?: any | null\n\n @Property({ name: 'occurred_at', type: Date, onCreate: () => new Date() })\n occurredAt: Date = new Date()\n}\n\n@Entity({ tableName: 'search_tokens' })\n@Index({ name: 'search_tokens_lookup_idx', properties: ['entityType', 'field', 'tokenHash', 'tenantId', 'organizationId'] })\n@Index({ name: 'search_tokens_entity_idx', properties: ['entityType', 'entityId'] })\n@Index({ name: 'search_tokens_tenant_token_hash_idx', properties: ['tenantId', 'tokenHash'] })\nexport class SearchToken {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'entity_type', type: 'text' })\n entityType!: string\n\n @Property({ name: 'entity_id', type: 'text' })\n entityId!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'field', type: 'text' })\n field!: string\n\n @Property({ name: 'token_hash', type: 'text' })\n tokenHash!: string\n\n @Property({ name: 'token', type: 'text', nullable: true })\n token?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Entity, Index, PrimaryKey, Property, Unique } from '@mikro-orm/decorators/legacy'\n\n// Generic JSONB-backed index rows for any entity ('<module>:<entity>')\n@Entity({ tableName: 'entity_indexes' })\n@Index({\n name: 'entity_indexes_customer_entity_doc_idx',\n expression:\n `create index \"entity_indexes_customer_entity_doc_idx\" on \"entity_indexes\" (\"entity_id\", \"organization_id\", \"tenant_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_entity' and organization_id is not null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_person_profile_doc_idx',\n expression:\n `create index \"entity_indexes_customer_person_profile_doc_idx\" on \"entity_indexes\" (\"entity_id\", \"organization_id\", \"tenant_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_person_profile' and organization_id is not null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_company_profile_doc_idx',\n expression:\n `create index \"entity_indexes_customer_company_profile_doc_idx\" on \"entity_indexes\" (\"entity_id\", \"organization_id\", \"tenant_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_company_profile' and organization_id is not null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_entity_tenant_doc_idx',\n expression:\n `create index \"entity_indexes_customer_entity_tenant_doc_idx\" on \"entity_indexes\" (\"tenant_id\", \"entity_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_entity' and organization_id is null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_person_profile_tenant_doc_idx',\n expression:\n `create index \"entity_indexes_customer_person_profile_tenant_doc_idx\" on \"entity_indexes\" (\"tenant_id\", \"entity_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_person_profile' and organization_id is null and tenant_id is not null`,\n})\n@Index({\n name: 'entity_indexes_customer_company_profile_tenant_doc_idx',\n expression:\n `create index \"entity_indexes_customer_company_profile_tenant_doc_idx\" on \"entity_indexes\" (\"tenant_id\", \"entity_id\") include (\"doc\") where deleted_at is null and entity_type = 'customers:customer_company_profile' and organization_id is null and tenant_id is not null`,\n})\n@Index({ name: 'entity_indexes_type_tenant_idx', properties: ['entityType', 'tenantId'] })\n@Unique({\n name: 'entity_indexes_type_entity_org_coalesced_unique',\n properties: ['entityType', 'entityId', 'organizationIdCoalesced'],\n})\nexport class EntityIndexRow {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n // Entity identifier: '<module>:<entity>'\n @Property({ name: 'entity_type', type: 'text' })\n @Index({ name: 'entity_indexes_type_idx' })\n entityType!: string\n\n // Record id as text for compatibility with uuid/int\n @Property({ name: 'entity_id', type: 'text' })\n @Index({ name: 'entity_indexes_entity_idx' })\n entityId!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n @Index({ name: 'entity_indexes_org_idx' })\n organizationId?: string | null\n\n @Property({\n name: 'organization_id_coalesced',\n type: 'uuid',\n generated: (cols) => `(coalesce(${cols.organizationId}, '00000000-0000-0000-0000-000000000000'::uuid)) stored`,\n hidden: true,\n })\n organizationIdCoalesced!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n // Flattened document of base fields and custom fields\n @Property({ name: 'doc', type: 'json' })\n doc!: any\n\n // Optional embedding vector or metadata produced by secondary indexers\n @Property({ name: 'embedding', type: 'json', nullable: true })\n embedding?: any | null\n\n @Property({ name: 'index_version', type: 'int', default: 1 })\n indexVersion: number = 1\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// Track long-running index jobs (reindex/purge) per entity and org scope\n@Entity({ tableName: 'entity_index_jobs' })\n// Coalesced-scope unique index so prepareJob can upsert atomically and two\n// concurrent schedulers cannot insert duplicate rows for the same scope (#2739).\n// Declared via @Unique with a raw expression (not properties) because the scope\n// columns are nullable and NULLs must collapse to one bucket via coalesce; mirrors\n// the entity_indexes coalesced uniqueness and the domain_mappings expression unique.\n@Unique({\n name: 'entity_index_jobs_scope_unique',\n expression:\n `create unique index \"entity_index_jobs_scope_unique\" on \"entity_index_jobs\" (\"entity_type\", coalesce(\"organization_id\", '00000000-0000-0000-0000-000000000000'::uuid), coalesce(\"tenant_id\", '00000000-0000-0000-0000-000000000000'::uuid), coalesce(\"partition_index\", -1), coalesce(\"partition_count\", -1))`,\n})\nexport class EntityIndexJob {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'entity_type', type: 'text' })\n @Index({ name: 'entity_index_jobs_type_idx' })\n entityType!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n @Index({ name: 'entity_index_jobs_org_idx' })\n organizationId?: string | null\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'partition_index', type: 'int', nullable: true })\n partitionIndex?: number | null\n\n @Property({ name: 'partition_count', type: 'int', nullable: true })\n partitionCount?: number | null\n\n @Property({ name: 'processed_count', type: 'int', nullable: true })\n processedCount?: number | null\n\n @Property({ name: 'total_count', type: 'int', nullable: true })\n totalCount?: number | null\n\n @Property({ name: 'heartbeat_at', type: Date, nullable: true })\n heartbeatAt?: Date | null\n\n // 'reindexing' | 'purging'\n @Property({ name: 'status', type: 'text' })\n status!: string\n\n @Property({ name: 'started_at', type: Date, onCreate: () => new Date() })\n startedAt: Date = new Date()\n\n @Property({ name: 'finished_at', type: Date, nullable: true })\n finishedAt?: Date | null\n}\n\n// Snapshot counts for coverage checks (per entity / tenant / org / withDeleted scope)\n@Entity({ tableName: 'entity_index_coverage' })\n@Unique({\n name: 'entity_index_coverage_scope_idx',\n properties: ['entityType', 'tenantId', 'organizationId', 'withDeleted'],\n})\nexport class EntityIndexCoverage {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'entity_type', type: 'text' })\n entityType!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'with_deleted', type: 'boolean', default: false })\n withDeleted: boolean = false\n\n @Property({ name: 'base_count', type: 'int', unsigned: true, default: 0 })\n baseCount: number = 0\n\n @Property({ name: 'indexed_count', type: 'int', unsigned: true, default: 0 })\n indexedCount: number = 0\n\n @Property({ name: 'vector_indexed_count', type: 'int', unsigned: true, default: 0 })\n vectorIndexedCount: number = 0\n\n @Property({ name: 'refreshed_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n refreshedAt: Date = new Date()\n}\n\n@Entity({ tableName: 'indexer_error_logs' })\n@Index({ name: 'indexer_error_logs_source_idx', properties: ['source'] })\n@Index({ name: 'indexer_error_logs_occurred_idx', properties: ['occurredAt'] })\nexport class IndexerErrorLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'source', type: 'text' })\n source!: string\n\n @Property({ name: 'handler', type: 'text' })\n handler!: string\n\n @Property({ name: 'entity_type', type: 'text', nullable: true })\n entityType?: string | null\n\n @Property({ name: 'record_id', type: 'text', nullable: true })\n recordId?: string | null\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'payload', type: 'json', nullable: true })\n payload?: any | null\n\n @Property({ name: 'message', type: 'text' })\n message!: string\n\n @Property({ name: 'stack', type: 'text', nullable: true })\n stack?: string | null\n\n@Property({ name: 'occurred_at', type: Date, onCreate: () => new Date() })\n occurredAt: Date = new Date()\n}\n\n@Entity({ tableName: 'indexer_status_logs' })\n@Index({ name: 'indexer_status_logs_source_idx', properties: ['source'] })\n@Index({ name: 'indexer_status_logs_occurred_idx', properties: ['occurredAt'] })\nexport class IndexerStatusLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'source', type: 'text' })\n source!: string\n\n @Property({ name: 'handler', type: 'text' })\n handler!: string\n\n @Property({ name: 'level', type: 'text' })\n level: string = 'info'\n\n @Property({ name: 'entity_type', type: 'text', nullable: true })\n entityType?: string | null\n\n @Property({ name: 'record_id', type: 'text', nullable: true })\n recordId?: string | null\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'message', type: 'text' })\n message!: string\n\n @Property({ name: 'details', type: 'json', nullable: true })\n details?: any | null\n\n @Property({ name: 'occurred_at', type: Date, onCreate: () => new Date() })\n occurredAt: Date = new Date()\n}\n\n@Entity({ tableName: 'search_tokens' })\n@Index({ name: 'search_tokens_lookup_idx', properties: ['entityType', 'field', 'tokenHash', 'tenantId', 'organizationId'] })\n@Index({ name: 'search_tokens_entity_idx', properties: ['entityType', 'entityId'] })\n@Index({ name: 'search_tokens_tenant_token_hash_idx', properties: ['tenantId', 'tokenHash'] })\n// Serves the availability probe (\"does this scope have any tokens?\") as a pure\n// B-tree prefix seek, making the MISS as cheap as the hit \u2014 neither existing\n// index can (lookup_idx buries tenant_id behind field/token_hash), which is how\n// the probe degraded to a seq scan on large tables (#4723). Requires the\n// index-usable `= / IS NULL` predicates the availability resolver emits; see\n// .ai/specs/2026-07-31-search-token-probe-index.md.\n@Index({ name: 'search_tokens_presence_idx', properties: ['entityType', 'tenantId', 'organizationId'] })\nexport class SearchToken {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'entity_type', type: 'text' })\n entityType!: string\n\n @Property({ name: 'entity_id', type: 'text' })\n entityId!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'field', type: 'text' })\n field!: string\n\n @Property({ name: 'token_hash', type: 'text' })\n tokenHash!: string\n\n @Property({ name: 'token', type: 'text', nullable: true })\n token?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -13,6 +13,11 @@ import {
13
13
  resolveJoins
14
14
  } from "@open-mercato/shared/lib/query/join-utils";
15
15
  import { resolveSearchConfig } from "@open-mercato/shared/lib/search/config";
16
+ import {
17
+ createSearchTokenAvailability,
18
+ isSearchFilterOp,
19
+ hasSearchFilter
20
+ } from "@open-mercato/shared/lib/search/availability";
16
21
  import { tokenizeText } from "@open-mercato/shared/lib/search/tokenize";
17
22
  import { runBeforeQueryPipeline, runAfterQueryPipeline } from "@open-mercato/shared/lib/query/query-extension-runner";
18
23
  import { warnOnCiphertextLikeFallback } from "@open-mercato/shared/lib/query/ciphertext-search-warning";
@@ -85,6 +90,7 @@ class HybridQueryEngine {
85
90
  this.autoReindexEnabled = null;
86
91
  this.coverageOptimizationEnabled = null;
87
92
  this.pendingCoverageRefreshKeys = /* @__PURE__ */ new Set();
93
+ this.searchAvailabilityInstance = null;
88
94
  const coverageTtl = Number.parseInt(process.env.QUERY_INDEX_COVERAGE_CACHE_MS ?? "", 10);
89
95
  this.coverageStatsTtlMs = Number.isFinite(coverageTtl) && coverageTtl >= 0 ? coverageTtl : 5 * 60 * 1e3;
90
96
  const cfTtl = Number.parseInt(process.env.QUERY_INDEX_CF_KEYS_CACHE_MS ?? "", 10);
@@ -102,6 +108,21 @@ class HybridQueryEngine {
102
108
  if (typeof emAny.getKysely === "function") return emAny.getKysely();
103
109
  throw new Error("HybridQueryEngine requires an EntityManager exposing getKysely() (MikroORM v7)");
104
110
  }
111
+ searchAvailability() {
112
+ if (!this.searchAvailabilityInstance) {
113
+ this.searchAvailabilityInstance = createSearchTokenAvailability({
114
+ getDb: () => this.getDb(),
115
+ getConfig: resolveSearchConfig,
116
+ applyOrganizationScope: (query, column, scope) => this.applyOrganizationScope(
117
+ query,
118
+ column,
119
+ scope
120
+ ),
121
+ logDebug: (event, payload) => this.logSearchDebug(event, payload)
122
+ });
123
+ }
124
+ return this.searchAvailabilityInstance;
125
+ }
105
126
  async query(entity, opts = {}) {
106
127
  const ext = opts.extensions;
107
128
  let hybridExtCtx = null;
@@ -173,7 +194,7 @@ class HybridQueryEngine {
173
194
  profiler.mark("query:base_table_resolved");
174
195
  const searchConfig = resolveSearchConfig();
175
196
  const orgScope = this.resolveOrganizationScope(opts);
176
- const searchEnabled = searchConfig.enabled && await this.tableExists("search_tokens");
197
+ const searchEnabled = await this.searchAvailability().staticEnabled();
177
198
  const baseExists = await profiler.measure("base_table_exists", () => this.tableExists(baseTable));
178
199
  if (!baseExists) {
179
200
  if (debugEnabled) this.debug("query:fallback:missing-base", { entity, baseTable });
@@ -317,10 +338,13 @@ class HybridQueryEngine {
317
338
  }
318
339
  }
319
340
  const searchSources = indexSources.map((src) => ({ entity: String(src.entityId), recordIdColumn: src.recordIdColumn })).filter((src) => src.recordIdColumn && src.entity);
320
- const searchFilters = normalizedFilters.filter((filter) => filter.op === "like" || filter.op === "ilike");
321
- const hasSearchTokens = searchEnabled && searchSources.length && searchFilters.length ? await this.searchSourcesHaveTokens(searchSources, opts.tenantId ?? null, orgScope) : false;
341
+ const searchFilters = normalizedFilters.filter((filter) => isSearchFilterOp(filter.op));
342
+ const sourceSearchFilters = [
343
+ ...baseFilters,
344
+ ...normalizedFilters.filter((filter) => String(filter.field).startsWith("cf:"))
345
+ ].filter((filter) => isSearchFilterOp(filter.op));
346
+ const hasSearchTokens = searchEnabled && searchSources.length && sourceSearchFilters.length ? await this.searchAvailability().anySourceHasTokens(searchSources, opts.tenantId ?? null, orgScope) : false;
322
347
  const searchRuntime = { ...searchRuntimeBase, searchSources, enabled: searchEnabled && hasSearchTokens };
323
- const joinSearchAvailability = /* @__PURE__ */ new Map();
324
348
  if (searchFilters.length) {
325
349
  this.logSearchDebug("search:init", {
326
350
  entity,
@@ -366,8 +390,7 @@ class HybridQueryEngine {
366
390
  if (!filters.length) continue;
367
391
  const join = joinMap.get(alias);
368
392
  if (!join?.entityId) continue;
369
- const hasJoinedTokens = searchEnabled ? await this.hasSearchTokens(String(join.entityId), opts.tenantId ?? null, orgScope) : false;
370
- joinSearchAvailability.set(String(join.entityId), hasJoinedTokens);
393
+ const hasJoinedTokens = searchEnabled ? await this.searchAvailability().hasTokens(String(join.entityId), opts.tenantId ?? null, orgScope) : false;
371
394
  const fallbackFields = filters.filter((filter) => !hasJoinedTokens || typeof filter.value !== "string" || tokenizeText(filter.value, searchConfig).hashes.length === 0).map((filter) => filter.column);
372
395
  if (!fallbackFields.length) continue;
373
396
  await warnOnCiphertextLikeFallback({
@@ -636,11 +659,7 @@ class HybridQueryEngine {
636
659
  if (!searchEnabled || !join.entityId) return false;
637
660
  if (!["like", "ilike"].includes(filter.op)) return false;
638
661
  if (typeof filter.value !== "string" || filter.value.trim().length === 0) return false;
639
- let searchAvailable = joinSearchAvailability.get(join.entityId);
640
- if (searchAvailable === void 0) {
641
- searchAvailable = await this.hasSearchTokens(String(join.entityId), opts.tenantId ?? null, orgScope);
642
- joinSearchAvailability.set(join.entityId, searchAvailable);
643
- }
662
+ const searchAvailable = await this.searchAvailability().hasTokens(String(join.entityId), opts.tenantId ?? null, orgScope);
644
663
  if (!searchAvailable) return false;
645
664
  const tokens = tokenizeText(String(filter.value), searchConfig);
646
665
  if (!tokens.hashes.length) return false;
@@ -1388,10 +1407,9 @@ class HybridQueryEngine {
1388
1407
  const orgScope = this.resolveOrganizationScope(opts);
1389
1408
  if (!opts.tenantId) throw new Error("QueryEngine: tenantId is required");
1390
1409
  const normalizedFilters = normalizeFilters(opts.filters);
1391
- const hasSearchFilter = normalizedFilters.some((filter) => filter.op === "like" || filter.op === "ilike");
1392
1410
  const searchConfig = resolveSearchConfig();
1393
- const searchEnabled = searchConfig.enabled && await this.tableExists("search_tokens");
1394
- const hasSearchTokens = searchEnabled && hasSearchFilter ? await this.hasSearchTokens(entity, opts.tenantId ?? null, orgScope) : false;
1411
+ const searchEnabled = await this.searchAvailability().staticEnabled();
1412
+ const hasSearchTokens = searchEnabled && hasSearchFilter(normalizedFilters) ? await this.searchAvailability().hasTokens(entity, opts.tenantId ?? null, orgScope) : false;
1395
1413
  const searchRuntime = {
1396
1414
  enabled: searchEnabled && hasSearchTokens,
1397
1415
  config: searchConfig,
@@ -1511,42 +1529,6 @@ class HybridQueryEngine {
1511
1529
  const exists = await db.selectFrom("information_schema.tables").select(sql`1`.as("one")).where("table_name", "=", table).executeTakeFirst();
1512
1530
  return !!exists;
1513
1531
  }
1514
- async hasSearchTokens(entity, tenantId, orgScope) {
1515
- try {
1516
- const db = this.getDb();
1517
- let query = db.selectFrom("search_tokens").select(sql`1`.as("one")).where("entity_type", "=", entity);
1518
- if (tenantId !== void 0) {
1519
- query = query.where(sql`tenant_id is not distinct from ${tenantId}`);
1520
- }
1521
- if (orgScope) {
1522
- query = this.applyOrganizationScope(query, "search_tokens.organization_id", orgScope);
1523
- }
1524
- const row = await query.limit(1).executeTakeFirst();
1525
- return !!row;
1526
- } catch (err) {
1527
- this.logSearchDebug("search:has-tokens-error", {
1528
- entity,
1529
- tenantId,
1530
- organizationScope: orgScope,
1531
- error: err instanceof Error ? err.message : String(err)
1532
- });
1533
- return false;
1534
- }
1535
- }
1536
- async searchSourcesHaveTokens(sources, tenantId, orgScope) {
1537
- for (const source of sources) {
1538
- const ok = await this.hasSearchTokens(source.entity, tenantId, orgScope);
1539
- this.logSearchDebug("search:source-has-tokens", {
1540
- entity: source.entity,
1541
- recordIdColumn: source.recordIdColumn,
1542
- tenantId,
1543
- organizationScope: orgScope,
1544
- hasTokens: ok
1545
- });
1546
- if (ok) return true;
1547
- }
1548
- return false;
1549
- }
1550
1532
  async resolveAvailableCustomFieldKeys(entityIds, tenantId) {
1551
1533
  if (!entityIds.length) return [];
1552
1534
  const cacheKey = this.customFieldKeysCacheKey(entityIds, tenantId);