@open-mercato/search 0.4.2-canary-c02407ff85

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 (237) hide show
  1. package/AGENTS.md +678 -0
  2. package/build.mjs +92 -0
  3. package/dist/di.js +157 -0
  4. package/dist/di.js.map +7 -0
  5. package/dist/fulltext/drivers/index.js +21 -0
  6. package/dist/fulltext/drivers/index.js.map +7 -0
  7. package/dist/fulltext/drivers/meilisearch/index.js +320 -0
  8. package/dist/fulltext/drivers/meilisearch/index.js.map +7 -0
  9. package/dist/fulltext/index.js +7 -0
  10. package/dist/fulltext/index.js.map +7 -0
  11. package/dist/fulltext/types.js +1 -0
  12. package/dist/fulltext/types.js.map +7 -0
  13. package/dist/index.js +12 -0
  14. package/dist/index.js.map +7 -0
  15. package/dist/indexer/index.js +8 -0
  16. package/dist/indexer/index.js.map +7 -0
  17. package/dist/indexer/search-indexer.js +848 -0
  18. package/dist/indexer/search-indexer.js.map +7 -0
  19. package/dist/indexer/subscribers/delete.js +41 -0
  20. package/dist/indexer/subscribers/delete.js.map +7 -0
  21. package/dist/lib/debug.js +34 -0
  22. package/dist/lib/debug.js.map +7 -0
  23. package/dist/lib/fallback-presenter.js +107 -0
  24. package/dist/lib/fallback-presenter.js.map +7 -0
  25. package/dist/lib/field-policy.js +75 -0
  26. package/dist/lib/field-policy.js.map +7 -0
  27. package/dist/lib/index.js +19 -0
  28. package/dist/lib/index.js.map +7 -0
  29. package/dist/lib/merger.js +93 -0
  30. package/dist/lib/merger.js.map +7 -0
  31. package/dist/lib/presenter-enricher.js +192 -0
  32. package/dist/lib/presenter-enricher.js.map +7 -0
  33. package/dist/modules/search/acl.js +14 -0
  34. package/dist/modules/search/acl.js.map +7 -0
  35. package/dist/modules/search/ai-tools.js +284 -0
  36. package/dist/modules/search/ai-tools.js.map +7 -0
  37. package/dist/modules/search/api/embeddings/reindex/cancel/route.js +65 -0
  38. package/dist/modules/search/api/embeddings/reindex/cancel/route.js.map +7 -0
  39. package/dist/modules/search/api/embeddings/reindex/route.js +165 -0
  40. package/dist/modules/search/api/embeddings/reindex/route.js.map +7 -0
  41. package/dist/modules/search/api/embeddings/route.js +246 -0
  42. package/dist/modules/search/api/embeddings/route.js.map +7 -0
  43. package/dist/modules/search/api/index/route.js +245 -0
  44. package/dist/modules/search/api/index/route.js.map +7 -0
  45. package/dist/modules/search/api/reindex/cancel/route.js +65 -0
  46. package/dist/modules/search/api/reindex/cancel/route.js.map +7 -0
  47. package/dist/modules/search/api/reindex/route.js +332 -0
  48. package/dist/modules/search/api/reindex/route.js.map +7 -0
  49. package/dist/modules/search/api/search/global/route.js +100 -0
  50. package/dist/modules/search/api/search/global/route.js.map +7 -0
  51. package/dist/modules/search/api/search/route.js +101 -0
  52. package/dist/modules/search/api/search/route.js.map +7 -0
  53. package/dist/modules/search/api/settings/fulltext/route.js +55 -0
  54. package/dist/modules/search/api/settings/fulltext/route.js.map +7 -0
  55. package/dist/modules/search/api/settings/global-search/route.js +80 -0
  56. package/dist/modules/search/api/settings/global-search/route.js.map +7 -0
  57. package/dist/modules/search/api/settings/route.js +118 -0
  58. package/dist/modules/search/api/settings/route.js.map +7 -0
  59. package/dist/modules/search/api/settings/vector-store/route.js +77 -0
  60. package/dist/modules/search/api/settings/vector-store/route.js.map +7 -0
  61. package/dist/modules/search/backend/config/search/page.js +10 -0
  62. package/dist/modules/search/backend/config/search/page.js.map +7 -0
  63. package/dist/modules/search/backend/config/search/page.meta.js +24 -0
  64. package/dist/modules/search/backend/config/search/page.meta.js.map +7 -0
  65. package/dist/modules/search/cli.js +698 -0
  66. package/dist/modules/search/cli.js.map +7 -0
  67. package/dist/modules/search/di.js +32 -0
  68. package/dist/modules/search/di.js.map +7 -0
  69. package/dist/modules/search/frontend/components/GlobalSearchDialog.js +357 -0
  70. package/dist/modules/search/frontend/components/GlobalSearchDialog.js.map +7 -0
  71. package/dist/modules/search/frontend/components/HybridSearchTable.js +343 -0
  72. package/dist/modules/search/frontend/components/HybridSearchTable.js.map +7 -0
  73. package/dist/modules/search/frontend/components/SearchSettingsPageClient.js +303 -0
  74. package/dist/modules/search/frontend/components/SearchSettingsPageClient.js.map +7 -0
  75. package/dist/modules/search/frontend/components/sections/FulltextSearchSection.js +360 -0
  76. package/dist/modules/search/frontend/components/sections/FulltextSearchSection.js.map +7 -0
  77. package/dist/modules/search/frontend/components/sections/GlobalSearchSection.js +101 -0
  78. package/dist/modules/search/frontend/components/sections/GlobalSearchSection.js.map +7 -0
  79. package/dist/modules/search/frontend/components/sections/VectorSearchSection.js +608 -0
  80. package/dist/modules/search/frontend/components/sections/VectorSearchSection.js.map +7 -0
  81. package/dist/modules/search/frontend/index.js +9 -0
  82. package/dist/modules/search/frontend/index.js.map +7 -0
  83. package/dist/modules/search/frontend/utils.js +41 -0
  84. package/dist/modules/search/frontend/utils.js.map +7 -0
  85. package/dist/modules/search/i18n/de.json +61 -0
  86. package/dist/modules/search/i18n/en.json +72 -0
  87. package/dist/modules/search/i18n/es.json +61 -0
  88. package/dist/modules/search/i18n/pl.json +61 -0
  89. package/dist/modules/search/index.js +11 -0
  90. package/dist/modules/search/index.js.map +7 -0
  91. package/dist/modules/search/lib/auto-indexing.js +29 -0
  92. package/dist/modules/search/lib/auto-indexing.js.map +7 -0
  93. package/dist/modules/search/lib/embedding-config.js +131 -0
  94. package/dist/modules/search/lib/embedding-config.js.map +7 -0
  95. package/dist/modules/search/lib/global-search-config.js +45 -0
  96. package/dist/modules/search/lib/global-search-config.js.map +7 -0
  97. package/dist/modules/search/lib/reindex-lock.js +99 -0
  98. package/dist/modules/search/lib/reindex-lock.js.map +7 -0
  99. package/dist/modules/search/subscribers/fulltext_upsert.js +64 -0
  100. package/dist/modules/search/subscribers/fulltext_upsert.js.map +7 -0
  101. package/dist/modules/search/subscribers/vector_delete.js +58 -0
  102. package/dist/modules/search/subscribers/vector_delete.js.map +7 -0
  103. package/dist/modules/search/subscribers/vector_purge.js +142 -0
  104. package/dist/modules/search/subscribers/vector_purge.js.map +7 -0
  105. package/dist/modules/search/subscribers/vector_upsert.js +58 -0
  106. package/dist/modules/search/subscribers/vector_upsert.js.map +7 -0
  107. package/dist/modules/search/workers/fulltext-index.worker.js +240 -0
  108. package/dist/modules/search/workers/fulltext-index.worker.js.map +7 -0
  109. package/dist/modules/search/workers/vector-index.worker.js +234 -0
  110. package/dist/modules/search/workers/vector-index.worker.js.map +7 -0
  111. package/dist/queue/fulltext-indexing.js +15 -0
  112. package/dist/queue/fulltext-indexing.js.map +7 -0
  113. package/dist/queue/index.js +3 -0
  114. package/dist/queue/index.js.map +7 -0
  115. package/dist/queue/vector-indexing.js +15 -0
  116. package/dist/queue/vector-indexing.js.map +7 -0
  117. package/dist/service.js +286 -0
  118. package/dist/service.js.map +7 -0
  119. package/dist/strategies/fulltext.strategy.js +116 -0
  120. package/dist/strategies/fulltext.strategy.js.map +7 -0
  121. package/dist/strategies/index.js +12 -0
  122. package/dist/strategies/index.js.map +7 -0
  123. package/dist/strategies/token.strategy.js +80 -0
  124. package/dist/strategies/token.strategy.js.map +7 -0
  125. package/dist/strategies/vector.strategy.js +137 -0
  126. package/dist/strategies/vector.strategy.js.map +7 -0
  127. package/dist/types.js +1 -0
  128. package/dist/types.js.map +7 -0
  129. package/dist/vector/drivers/chromadb/index.js +44 -0
  130. package/dist/vector/drivers/chromadb/index.js.map +7 -0
  131. package/dist/vector/drivers/index.js +9 -0
  132. package/dist/vector/drivers/index.js.map +7 -0
  133. package/dist/vector/drivers/pgvector/index.js +509 -0
  134. package/dist/vector/drivers/pgvector/index.js.map +7 -0
  135. package/dist/vector/drivers/qdrant/index.js +44 -0
  136. package/dist/vector/drivers/qdrant/index.js.map +7 -0
  137. package/dist/vector/index.js +4 -0
  138. package/dist/vector/index.js.map +7 -0
  139. package/dist/vector/lib/vector-logs.js +33 -0
  140. package/dist/vector/lib/vector-logs.js.map +7 -0
  141. package/dist/vector/services/checksum.js +20 -0
  142. package/dist/vector/services/checksum.js.map +7 -0
  143. package/dist/vector/services/embedding.js +222 -0
  144. package/dist/vector/services/embedding.js.map +7 -0
  145. package/dist/vector/services/index.js +4 -0
  146. package/dist/vector/services/index.js.map +7 -0
  147. package/dist/vector/services/vector-index.service.js +960 -0
  148. package/dist/vector/services/vector-index.service.js.map +7 -0
  149. package/dist/vector/types/pg.d.js +1 -0
  150. package/dist/vector/types/pg.d.js.map +7 -0
  151. package/dist/vector/types.js +75 -0
  152. package/dist/vector/types.js.map +7 -0
  153. package/jest.config.cjs +19 -0
  154. package/package.json +142 -0
  155. package/src/__tests__/queue.test.ts +148 -0
  156. package/src/__tests__/service.test.ts +345 -0
  157. package/src/__tests__/workers.test.ts +319 -0
  158. package/src/di.ts +291 -0
  159. package/src/fulltext/drivers/index.ts +41 -0
  160. package/src/fulltext/drivers/meilisearch/index.ts +410 -0
  161. package/src/fulltext/index.ts +13 -0
  162. package/src/fulltext/types.ts +115 -0
  163. package/src/index.ts +36 -0
  164. package/src/indexer/index.ts +13 -0
  165. package/src/indexer/search-indexer.ts +1141 -0
  166. package/src/indexer/subscribers/delete.ts +49 -0
  167. package/src/lib/debug.ts +46 -0
  168. package/src/lib/fallback-presenter.ts +106 -0
  169. package/src/lib/field-policy.ts +169 -0
  170. package/src/lib/index.ts +13 -0
  171. package/src/lib/merger.ts +159 -0
  172. package/src/lib/presenter-enricher.ts +323 -0
  173. package/src/modules/search/README.md +694 -0
  174. package/src/modules/search/acl.ts +10 -0
  175. package/src/modules/search/ai-tools.ts +467 -0
  176. package/src/modules/search/api/embeddings/reindex/cancel/route.ts +77 -0
  177. package/src/modules/search/api/embeddings/reindex/route.ts +197 -0
  178. package/src/modules/search/api/embeddings/route.ts +304 -0
  179. package/src/modules/search/api/index/route.ts +297 -0
  180. package/src/modules/search/api/reindex/cancel/route.ts +77 -0
  181. package/src/modules/search/api/reindex/route.ts +419 -0
  182. package/src/modules/search/api/search/global/route.ts +120 -0
  183. package/src/modules/search/api/search/route.ts +121 -0
  184. package/src/modules/search/api/settings/fulltext/route.ts +82 -0
  185. package/src/modules/search/api/settings/global-search/route.ts +91 -0
  186. package/src/modules/search/api/settings/route.ts +187 -0
  187. package/src/modules/search/api/settings/vector-store/route.ts +105 -0
  188. package/src/modules/search/backend/config/search/page.meta.ts +22 -0
  189. package/src/modules/search/backend/config/search/page.tsx +12 -0
  190. package/src/modules/search/cli.ts +818 -0
  191. package/src/modules/search/di.ts +50 -0
  192. package/src/modules/search/frontend/components/GlobalSearchDialog.tsx +436 -0
  193. package/src/modules/search/frontend/components/HybridSearchTable.tsx +418 -0
  194. package/src/modules/search/frontend/components/SearchSettingsPageClient.tsx +476 -0
  195. package/src/modules/search/frontend/components/sections/FulltextSearchSection.tsx +624 -0
  196. package/src/modules/search/frontend/components/sections/GlobalSearchSection.tsx +124 -0
  197. package/src/modules/search/frontend/components/sections/VectorSearchSection.tsx +943 -0
  198. package/src/modules/search/frontend/index.ts +3 -0
  199. package/src/modules/search/frontend/utils.ts +82 -0
  200. package/src/modules/search/i18n/de.json +61 -0
  201. package/src/modules/search/i18n/en.json +72 -0
  202. package/src/modules/search/i18n/es.json +61 -0
  203. package/src/modules/search/i18n/pl.json +61 -0
  204. package/src/modules/search/index.ts +9 -0
  205. package/src/modules/search/lib/auto-indexing.ts +35 -0
  206. package/src/modules/search/lib/embedding-config.ts +161 -0
  207. package/src/modules/search/lib/global-search-config.ts +69 -0
  208. package/src/modules/search/lib/reindex-lock.ts +201 -0
  209. package/src/modules/search/subscribers/fulltext_upsert.ts +83 -0
  210. package/src/modules/search/subscribers/vector_delete.ts +75 -0
  211. package/src/modules/search/subscribers/vector_purge.ts +161 -0
  212. package/src/modules/search/subscribers/vector_upsert.ts +75 -0
  213. package/src/modules/search/workers/fulltext-index.worker.ts +318 -0
  214. package/src/modules/search/workers/vector-index.worker.ts +292 -0
  215. package/src/queue/fulltext-indexing.ts +87 -0
  216. package/src/queue/index.ts +2 -0
  217. package/src/queue/vector-indexing.ts +66 -0
  218. package/src/service.ts +397 -0
  219. package/src/strategies/fulltext.strategy.ts +155 -0
  220. package/src/strategies/index.ts +17 -0
  221. package/src/strategies/token.strategy.ts +153 -0
  222. package/src/strategies/vector.strategy.ts +234 -0
  223. package/src/types.ts +38 -0
  224. package/src/vector/drivers/chromadb/index.ts +49 -0
  225. package/src/vector/drivers/index.ts +4 -0
  226. package/src/vector/drivers/pgvector/index.ts +627 -0
  227. package/src/vector/drivers/qdrant/index.ts +49 -0
  228. package/src/vector/index.ts +3 -0
  229. package/src/vector/lib/vector-logs.ts +46 -0
  230. package/src/vector/services/checksum.ts +18 -0
  231. package/src/vector/services/embedding.ts +275 -0
  232. package/src/vector/services/index.ts +3 -0
  233. package/src/vector/services/vector-index.service.ts +1234 -0
  234. package/src/vector/types/pg.d.ts +1 -0
  235. package/src/vector/types.ts +220 -0
  236. package/tsconfig.json +9 -0
  237. package/watch.mjs +6 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/search/cli.ts"],
4
+ "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { recordIndexerError } from '@open-mercato/shared/lib/indexers/error-log'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\nimport { createProgressBar } from '@open-mercato/shared/lib/cli/progress'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { reindexEntity, DEFAULT_REINDEX_PARTITIONS } from '@open-mercato/core/modules/query_index/lib/reindexer'\nimport { writeCoverageCounts } from '@open-mercato/core/modules/query_index/lib/coverage'\nimport type { SearchService } from '../../service'\nimport type { SearchIndexer } from '../../indexer/search-indexer'\nimport { VECTOR_INDEXING_QUEUE_NAME, type VectorIndexJobPayload } from '../../queue/vector-indexing'\nimport { FULLTEXT_INDEXING_QUEUE_NAME, type FulltextIndexJobPayload } from '../../queue/fulltext-indexing'\nimport type { QueuedJob, JobContext } from '@open-mercato/queue'\nimport type { EntityId } from '@open-mercato/shared/modules/entities'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\ntype CliProgressBar = {\n update(completed: number): void\n complete(): void\n}\n\ntype ParsedArgs = Record<string, string | boolean>\n\nfunction parseArgs(rest: string[]): ParsedArgs {\n const args: ParsedArgs = {}\n for (let i = 0; i < rest.length; i += 1) {\n const part = rest[i]\n if (!part?.startsWith('--')) continue\n const [rawKey, rawValue] = part.slice(2).split('=')\n if (!rawKey) continue\n if (rawValue !== undefined) {\n args[rawKey] = rawValue\n } else if (i + 1 < rest.length && !rest[i + 1]!.startsWith('--')) {\n args[rawKey] = rest[i + 1]!\n i += 1\n } else {\n args[rawKey] = true\n }\n }\n return args\n}\n\nfunction stringOpt(args: ParsedArgs, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (typeof raw !== 'string') continue\n const trimmed = raw.trim()\n if (trimmed.length > 0) return trimmed\n }\n return undefined\n}\n\nfunction numberOpt(args: ParsedArgs, ...keys: string[]): number | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (typeof raw === 'number') return raw\n if (typeof raw === 'string') {\n const parsed = Number(raw)\n if (Number.isFinite(parsed)) return parsed\n }\n }\n return undefined\n}\n\nfunction flagOpt(args: ParsedArgs, ...keys: string[]): boolean | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (raw === undefined) continue\n if (raw === true) return true\n if (raw === false) return false\n if (typeof raw === 'string') {\n const trimmed = raw.trim()\n if (!trimmed) return true\n const parsed = parseBooleanToken(trimmed)\n return parsed === null ? true : parsed\n }\n }\n return undefined\n}\n\nfunction toPositiveInt(value: number | undefined): number | undefined {\n if (value === undefined) return undefined\n const n = Math.floor(value)\n if (!Number.isFinite(n) || n <= 0) return undefined\n return n\n}\n\nfunction toNonNegativeInt(value: number | undefined, fallback = 0): number {\n if (value === undefined) return fallback\n const n = Math.floor(value)\n if (!Number.isFinite(n) || n < 0) return fallback\n return n\n}\n\n/**\n * Test search functionality with a query\n */\nasync function searchCommand(rest: string[]): Promise<void> {\n const args = parseArgs(rest)\n const query = stringOpt(args, 'query', 'q')\n const tenantId = stringOpt(args, 'tenant', 'tenantId')\n const organizationId = stringOpt(args, 'org', 'organizationId')\n const entityTypes = stringOpt(args, 'entity', 'entities')\n const strategies = stringOpt(args, 'strategy', 'strategies')\n const limit = numberOpt(args, 'limit') ?? 20\n\n if (!query) {\n console.error('Usage: yarn mercato search query --query \"search terms\" --tenant <id> [options]')\n console.error(' --query, -q Search query (required)')\n console.error(' --tenant Tenant ID (required)')\n console.error(' --org Organization ID (optional)')\n console.error(' --entity Entity types to search (comma-separated)')\n console.error(' --strategy Strategies to use (comma-separated: meilisearch,vector,tokens)')\n console.error(' --limit Max results (default: 20)')\n return\n }\n\n if (!tenantId) {\n console.error('Error: --tenant is required')\n return\n }\n\n const container = await createRequestContainer()\n\n try {\n const searchService = container.resolve('searchService') as SearchService | undefined\n\n if (!searchService) {\n console.error('Error: SearchService not available. Make sure the search module is registered.')\n return\n }\n\n console.log(`\\nSearching for: \"${query}\"`)\n console.log(`Tenant: ${tenantId}`)\n if (organizationId) console.log(`Organization: ${organizationId}`)\n console.log('---')\n\n const results = await searchService.search(query, {\n tenantId,\n organizationId,\n entityTypes: entityTypes?.split(',').map(s => s.trim()),\n strategies: strategies?.split(',').map(s => s.trim()) as any,\n limit,\n })\n\n if (results.length === 0) {\n console.log('No results found.')\n return\n }\n\n console.log(`\\nFound ${results.length} result(s):\\n`)\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i]\n console.log(`${i + 1}. [${result.source}] ${result.entityId}`)\n console.log(` Record ID: ${result.recordId}`)\n console.log(` Score: ${result.score.toFixed(4)}`)\n if (result.presenter) {\n console.log(` Title: ${result.presenter.title}`)\n if (result.presenter.subtitle) console.log(` Subtitle: ${result.presenter.subtitle}`)\n }\n if (result.url) console.log(` URL: ${result.url}`)\n console.log('')\n }\n } finally {\n try {\n const em = container.resolve('em') as any\n await em?.getConnection?.()?.close?.()\n } catch {}\n }\n}\n\n/**\n * Show status of search strategies\n */\nasync function statusCommand(): Promise<void> {\n const container = await createRequestContainer()\n\n try {\n const searchService = container.resolve('searchService') as SearchService | undefined\n const strategies = container.resolve('searchStrategies') as any[] | undefined\n\n console.log('\\n=== Search Module Status ===\\n')\n\n if (!searchService) {\n console.log('SearchService: NOT REGISTERED')\n return\n }\n\n console.log('SearchService: ACTIVE')\n console.log('')\n\n if (!strategies || strategies.length === 0) {\n console.log('Strategies: NONE CONFIGURED')\n return\n }\n\n console.log('Strategies:')\n console.log('-----------')\n\n for (const strategy of strategies) {\n const available = await strategy.isAvailable?.() ?? true\n const status = available ? 'AVAILABLE' : 'UNAVAILABLE'\n const icon = available ? '\u2713' : '\u2717'\n console.log(` ${icon} ${strategy.name ?? strategy.id} (${strategy.id})`)\n console.log(` Status: ${status}`)\n console.log(` Priority: ${strategy.priority ?? 'N/A'}`)\n console.log('')\n }\n\n // Check environment variables\n console.log('Environment:')\n console.log('------------')\n console.log(` MEILISEARCH_HOST: ${process.env.MEILISEARCH_HOST ?? '(not set)'}`)\n console.log(` MEILISEARCH_API_KEY: ${process.env.MEILISEARCH_API_KEY ? '(set)' : '(not set)'}`)\n console.log(` OPENAI_API_KEY: ${process.env.OPENAI_API_KEY ? '(set)' : '(not set)'}`)\n console.log(` OM_SEARCH_ENABLED: ${process.env.OM_SEARCH_ENABLED ?? 'true (default)'}`)\n console.log('')\n } finally {\n try {\n const em = container.resolve('em') as any\n await em?.getConnection?.()?.close?.()\n } catch {}\n }\n}\n\n/**\n * Index a specific record for testing\n */\nasync function indexCommand(rest: string[]): Promise<void> {\n const args = parseArgs(rest)\n const entityId = stringOpt(args, 'entity', 'entityId')\n const recordId = stringOpt(args, 'record', 'recordId')\n const tenantId = stringOpt(args, 'tenant', 'tenantId')\n const organizationId = stringOpt(args, 'org', 'organizationId')\n\n if (!entityId || !recordId || !tenantId) {\n console.error('Usage: yarn mercato search index --entity <entityId> --record <recordId> --tenant <tenantId>')\n console.error(' --entity Entity ID (e.g., customers:customer_person_profile)')\n console.error(' --record Record ID')\n console.error(' --tenant Tenant ID')\n console.error(' --org Organization ID (optional)')\n return\n }\n\n const container = await createRequestContainer()\n\n try {\n const searchIndexer = container.resolve('searchIndexer') as SearchIndexer | undefined\n\n if (!searchIndexer) {\n console.error('Error: SearchIndexer not available.')\n return\n }\n\n // Load record from query engine\n const queryEngine = container.resolve('queryEngine') as any\n\n console.log(`\\nLoading record: ${entityId} / ${recordId}`)\n\n const result = await queryEngine.query(entityId, {\n tenantId,\n organizationId,\n filters: { id: recordId },\n includeCustomFields: true,\n page: { page: 1, pageSize: 1 },\n })\n\n const record = result.items[0]\n\n if (!record) {\n console.error('Error: Record not found')\n return\n }\n\n console.log('Record loaded, indexing...')\n\n // Extract custom fields\n const customFields: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(record)) {\n if (key.startsWith('cf:') || key.startsWith('cf_')) {\n const cfKey = key.slice(3) // Remove 'cf:' or 'cf_' prefix (both are 3 chars)\n customFields[cfKey] = value\n }\n }\n\n await searchIndexer.indexRecord({\n entityId,\n recordId,\n tenantId,\n organizationId,\n record,\n customFields,\n })\n\n console.log('Record indexed successfully!')\n } finally {\n try {\n const em = container.resolve('em') as any\n await em?.getConnection?.()?.close?.()\n } catch {}\n }\n}\n\n/**\n * Test Meilisearch connection directly\n */\nasync function testMeilisearchCommand(): Promise<void> {\n const host = process.env.MEILISEARCH_HOST\n const apiKey = process.env.MEILISEARCH_API_KEY\n\n console.log('\\n=== Meilisearch Connection Test ===\\n')\n\n if (!host) {\n console.log('MEILISEARCH_HOST: NOT SET')\n console.log('\\nMeilisearch is not configured. Set MEILISEARCH_HOST in your .env file.')\n return\n }\n\n console.log(`Host: ${host}`)\n console.log(`API Key: ${apiKey ? '(configured)' : '(not set)'}`)\n console.log('')\n\n try {\n const { MeiliSearch } = await import('meilisearch')\n const client = new MeiliSearch({ host, apiKey })\n\n console.log('Testing connection...')\n const health = await client.health()\n console.log(`Health: ${health.status}`)\n\n console.log('\\nListing indexes...')\n const indexes = await client.getIndexes()\n\n if (indexes.results.length === 0) {\n console.log('No indexes found.')\n } else {\n console.log(`Found ${indexes.results.length} index(es):`)\n for (const index of indexes.results) {\n const stats = await client.index(index.uid).getStats()\n console.log(` - ${index.uid}: ${stats.numberOfDocuments} documents`)\n }\n }\n\n console.log('\\nMeilisearch connection successful!')\n } catch (error) {\n console.error('Connection failed:', error instanceof Error ? error.message : error)\n }\n}\n\nconst searchCli: ModuleCli = {\n command: 'query',\n async run(rest) {\n await searchCommand(rest)\n },\n}\n\nconst statusCli: ModuleCli = {\n command: 'status',\n async run() {\n await statusCommand()\n },\n}\n\nconst indexCli: ModuleCli = {\n command: 'index',\n async run(rest) {\n await indexCommand(rest)\n },\n}\n\nconst testMeilisearchCli: ModuleCli = {\n command: 'test-meilisearch',\n async run() {\n await testMeilisearchCommand()\n },\n}\n\nasync function resetVectorCoverageAfterPurge(\n em: EntityManager | null,\n entityId: string,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<void> {\n if (!em || !entityId) return\n try {\n const scopes = new Set<string>()\n scopes.add('__null__')\n if (organizationId) scopes.add(organizationId)\n for (const scope of scopes) {\n const orgValue = scope === '__null__' ? null : scope\n await writeCoverageCounts(\n em,\n {\n entityType: entityId,\n tenantId,\n organizationId: orgValue,\n withDeleted: false,\n },\n { vectorCount: 0 },\n )\n }\n } catch (error) {\n console.warn('[search.cli] Failed to reset vector coverage after purge', error instanceof Error ? error.message : error)\n }\n}\n\nasync function reindexCommand(rest: string[]): Promise<void> {\n const args = parseArgs(rest)\n const tenantId = stringOpt(args, 'tenant', 'tenantId')\n const organizationId = stringOpt(args, 'org', 'orgId', 'organizationId')\n const entityId = stringOpt(args, 'entity', 'entityId')\n const force = flagOpt(args, 'force', 'full') === true\n const batchSize = toPositiveInt(numberOpt(args, 'batch', 'chunk', 'size'))\n const partitionsOption = toPositiveInt(numberOpt(args, 'partitions', 'partitionCount', 'parallel'))\n const partitionIndexRaw = numberOpt(args, 'partition', 'partitionIndex')\n const partitionIndexOption = partitionIndexRaw === undefined ? undefined : toNonNegativeInt(partitionIndexRaw, 0)\n const resetCoverageFlag = flagOpt(args, 'resetCoverage') === true\n const skipResetCoverageFlag = flagOpt(args, 'skipResetCoverage', 'noResetCoverage') === true\n const skipPurgeFlag = flagOpt(args, 'skipPurge', 'noPurge') === true\n const purgeFlag = flagOpt(args, 'purge', 'purgeFirst')\n\n const container = await createRequestContainer()\n let baseEm: EntityManager | null = null\n try {\n baseEm = (container.resolve('em') as EntityManager)\n } catch {\n baseEm = null\n }\n\n const disposeContainer = async () => {\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n }\n\n const recordError = async (error: Error) => {\n await recordIndexerLog(\n { em: baseEm ?? undefined },\n {\n source: 'vector',\n handler: 'cli:search.reindex',\n level: 'warn',\n message: `Reindex failed${entityId ? ` for ${entityId}` : ''}`,\n entityType: entityId ?? null,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n details: { error: error.message },\n },\n ).catch(() => undefined)\n await recordIndexerError(\n { em: baseEm ?? undefined },\n {\n source: 'vector',\n handler: 'cli:search.reindex',\n error,\n entityType: entityId ?? null,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n payload: {\n args,\n force,\n batchSize,\n partitionsOption,\n partitionIndexOption,\n resetCoverageFlag,\n skipResetCoverageFlag,\n skipPurgeFlag,\n purgeFlag,\n },\n },\n )\n }\n\n try {\n const searchIndexer = container.resolve<SearchIndexer>('searchIndexer')\n const enabledEntities = new Set(searchIndexer.listEnabledEntities())\n const baseEventBus = (() => {\n try {\n return container.resolve('eventBus') as {\n emitEvent(event: string, payload: any, options?: any): Promise<void>\n }\n } catch {\n return null\n }\n })()\n if (!baseEventBus) {\n console.warn('[search.cli] eventBus unavailable; vector embeddings may not be refreshed. Run bootstrap or ensure event bus configuration.')\n }\n\n const partitionCount = Math.max(1, partitionsOption ?? DEFAULT_REINDEX_PARTITIONS)\n if (partitionIndexOption !== undefined && partitionIndexOption >= partitionCount) {\n console.error(`partitionIndex (${partitionIndexOption}) must be < partitionCount (${partitionCount})`)\n return\n }\n const partitionTargets =\n partitionIndexOption !== undefined\n ? [partitionIndexOption]\n : Array.from({ length: partitionCount }, (_, idx) => idx)\n\n const shouldResetCoverage = (partition: number): boolean => {\n if (resetCoverageFlag) return true\n if (skipResetCoverageFlag) return false\n if (partitionIndexOption !== undefined) return partitionIndexOption === 0\n return partition === partitionTargets[0]\n }\n\n const runReindex = async (entityType: string, purgeFirst: boolean) => {\n const scopeLabel = tenantId\n ? `tenant=${tenantId}${organizationId ? `, org=${organizationId}` : ''}`\n : 'all tenants'\n console.log(`Reindexing vectors for ${entityType} (${scopeLabel})${purgeFirst ? ' [purge]' : ''}`)\n await recordIndexerLog(\n { em: baseEm ?? undefined },\n {\n source: 'vector',\n handler: 'cli:search.reindex',\n message: `Reindex started for ${entityType}`,\n entityType,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n details: {\n purgeFirst,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n batchSize,\n },\n },\n ).catch(() => undefined)\n\n if (purgeFirst && tenantId) {\n try {\n console.log(' -> purging existing vector index rows...')\n await searchIndexer.purgeEntity({ entityId: entityType as EntityId, tenantId })\n await resetVectorCoverageAfterPurge(baseEm, entityType, tenantId ?? null, organizationId ?? null)\n if (baseEventBus) {\n const scopes = new Set<string>()\n scopes.add('__null__')\n if (organizationId) scopes.add(organizationId)\n await Promise.all(\n Array.from(scopes).map((scope) => {\n const orgValue = scope === '__null__' ? null : scope\n return baseEventBus!\n .emitEvent(\n 'query_index.coverage.refresh',\n {\n entityType,\n tenantId: tenantId ?? null,\n organizationId: orgValue,\n delayMs: 0,\n },\n )\n .catch(() => undefined)\n }),\n )\n }\n } catch (err) {\n console.warn(' -> purge failed, continuing with reindex', err instanceof Error ? err.message : err)\n }\n } else if (purgeFirst && !tenantId) {\n console.warn(' -> skipping purge: tenant scope not provided')\n }\n\n const progressState = new Map<number, { last: number }>()\n const renderProgress = (part: number, info: { processed: number; total: number }) => {\n const state = progressState.get(part) ?? { last: 0 }\n const now = Date.now()\n if (now - state.last < 1000 && info.processed < info.total) return\n state.last = now\n progressState.set(part, state)\n const percent = info.total > 0 ? ((info.processed / info.total) * 100).toFixed(2) : '0.00'\n console.log(\n ` [${entityType}] partition ${part + 1}/${partitionCount}: ${info.processed.toLocaleString()} / ${info.total.toLocaleString()} (${percent}%)`,\n )\n }\n\n const processed = await Promise.all(\n partitionTargets.map(async (part, idx) => {\n const label = partitionTargets.length > 1 ? ` [partition ${part + 1}/${partitionCount}]` : ''\n if (partitionTargets.length === 1) {\n console.log(` -> processing${label}`)\n } else if (idx === 0) {\n console.log(` -> processing partitions in parallel (count=${partitionTargets.length})`)\n }\n\n const partitionContainer = await createRequestContainer()\n const partitionEm = partitionContainer.resolve<EntityManager>('em')\n try {\n let progressBar: CliProgressBar | null = null\n const useBar = partitionTargets.length === 1\n const stats = await reindexEntity(partitionEm, {\n entityType,\n tenantId: tenantId ?? undefined,\n organizationId: organizationId ?? undefined,\n force,\n batchSize,\n eventBus: baseEventBus ?? undefined,\n emitVectorizeEvents: true,\n partitionCount,\n partitionIndex: part,\n resetCoverage: shouldResetCoverage(part),\n onProgress(info) {\n if (useBar) {\n if (info.total > 0 && !progressBar) {\n progressBar = createProgressBar(`Reindexing ${entityType}${label}`, info.total)\n }\n progressBar?.update(info.processed)\n } else {\n renderProgress(part, info)\n }\n },\n })\n if (progressBar) (progressBar as CliProgressBar).complete()\n if (!useBar) {\n renderProgress(part, { processed: stats.processed, total: stats.total })\n } else {\n console.log(\n ` processed ${stats.processed} row(s)${stats.total ? ` (base ${stats.total})` : ''}`,\n )\n }\n return stats.processed\n } finally {\n if (typeof (partitionContainer as any)?.dispose === 'function') {\n await (partitionContainer as any).dispose()\n }\n }\n }),\n )\n\n const totalProcessed = processed.reduce((acc, value) => acc + value, 0)\n console.log(`Finished ${entityType}: processed ${totalProcessed} row(s) across ${partitionTargets.length} partition(s)`)\n await recordIndexerLog(\n { em: baseEm ?? undefined },\n {\n source: 'vector',\n handler: 'cli:search.reindex',\n message: `Reindex completed for ${entityType}`,\n entityType,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n details: {\n processed: totalProcessed,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n batchSize,\n },\n },\n ).catch(() => undefined)\n return totalProcessed\n }\n\n const defaultPurge = purgeFlag === true && !skipPurgeFlag\n\n if (entityId) {\n if (!enabledEntities.has(entityId)) {\n console.error(`Entity ${entityId} is not enabled for vector search.`)\n return\n }\n const purgeFirst = defaultPurge\n await runReindex(entityId, purgeFirst)\n console.log('Vector reindex completed.')\n return\n }\n\n const entityIds = searchIndexer.listEnabledEntities()\n if (!entityIds.length) {\n console.log('No entities enabled for vector search.')\n return\n }\n console.log(`Reindexing ${entityIds.length} vector-enabled entities...`)\n let processedOverall = 0\n for (let idx = 0; idx < entityIds.length; idx += 1) {\n const id = entityIds[idx]!\n console.log(`[${idx + 1}/${entityIds.length}] Preparing ${id}...`)\n processedOverall += await runReindex(id, defaultPurge)\n }\n console.log(`Vector reindex completed. Total processed rows: ${processedOverall.toLocaleString()}`)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n console.error('[search.cli] Reindex failed:', err.stack ?? err.message)\n await recordError(err)\n throw err\n } finally {\n await disposeContainer()\n }\n}\n\nconst reindexCli: ModuleCli = {\n command: 'reindex',\n async run(rest) {\n await reindexCommand(rest)\n },\n}\n\nconst reindexHelpCli: ModuleCli = {\n command: 'reindex-help',\n async run() {\n console.log('Usage: yarn mercato search reindex [options]')\n console.log(' --tenant <id> Optional tenant scope (required for purge & coverage).')\n console.log(' --org <id> Optional organization scope (requires tenant).')\n console.log(' --entity <module:entity> Reindex a single entity (defaults to all enabled entities).')\n console.log(' --partitions <n> Number of partitions to process in parallel (default from query index).')\n console.log(' --partition <idx> Restrict to a specific partition index.')\n console.log(' --batch <n> Override batch size per chunk.')\n console.log(' --force Force reindex even if another job is running.')\n console.log(' --purgeFirst Purge vector rows before reindexing (defaults to skip).')\n console.log(' --skipPurge Explicitly skip purging vector rows.')\n console.log(' --skipResetCoverage Keep existing coverage snapshots.')\n },\n}\n\n/**\n * Start a queue worker for processing search indexing jobs.\n */\nasync function workerCommand(rest: string[]): Promise<void> {\n const queueName = rest[0]\n const args = parseArgs(rest)\n const concurrency = toPositiveInt(numberOpt(args, 'concurrency')) ?? 1\n\n const validQueues = [VECTOR_INDEXING_QUEUE_NAME, FULLTEXT_INDEXING_QUEUE_NAME]\n\n if (!queueName || !validQueues.includes(queueName)) {\n console.error('\\nUsage: yarn mercato search worker <queue-name> [options]\\n')\n console.error('Available queues:')\n console.error(` ${VECTOR_INDEXING_QUEUE_NAME} Process vector embedding indexing jobs`)\n console.error(` ${FULLTEXT_INDEXING_QUEUE_NAME} Process fulltext indexing jobs`)\n console.error('\\nOptions:')\n console.error(' --concurrency <n> Number of concurrent jobs to process (default: 1)')\n console.error('\\nExamples:')\n console.error(` yarn mercato search worker ${VECTOR_INDEXING_QUEUE_NAME} --concurrency=10`)\n console.error(` yarn mercato search worker ${FULLTEXT_INDEXING_QUEUE_NAME} --concurrency=5`)\n return\n }\n\n // Check if Redis is configured for async queue\n const queueStrategy = process.env.QUEUE_STRATEGY || 'local'\n if (queueStrategy !== 'async') {\n console.error('\\nError: Queue workers require QUEUE_STRATEGY=async')\n console.error('Set QUEUE_STRATEGY=async and configure REDIS_URL in your environment.\\n')\n return\n }\n\n const redisUrl = process.env.REDIS_URL || process.env.QUEUE_REDIS_URL\n if (!redisUrl) {\n console.error('\\nError: Redis connection not configured')\n console.error('Set REDIS_URL or QUEUE_REDIS_URL in your environment.\\n')\n return\n }\n\n // Dynamically import runWorker to avoid loading BullMQ unless needed\n const { runWorker } = await import('@open-mercato/queue/worker')\n\n console.log(`\\nStarting ${queueName} worker...`)\n console.log(` Concurrency: ${concurrency}`)\n console.log(` Redis: ${redisUrl.replace(/\\/\\/[^:]+:[^@]+@/, '//<credentials>@')}`)\n console.log('')\n\n if (queueName === VECTOR_INDEXING_QUEUE_NAME) {\n const { handleVectorIndexJob } = await import('./workers/vector-index.worker')\n const container = await createRequestContainer()\n\n await runWorker<VectorIndexJobPayload>({\n queueName: VECTOR_INDEXING_QUEUE_NAME,\n handler: async (job: QueuedJob<VectorIndexJobPayload>, ctx: JobContext) => {\n await handleVectorIndexJob(job, ctx, { resolve: container.resolve.bind(container) })\n },\n connection: { url: redisUrl },\n concurrency,\n })\n } else if (queueName === FULLTEXT_INDEXING_QUEUE_NAME) {\n const { handleFulltextIndexJob } = await import('./workers/fulltext-index.worker')\n const container = await createRequestContainer()\n\n await runWorker<FulltextIndexJobPayload>({\n queueName: FULLTEXT_INDEXING_QUEUE_NAME,\n handler: async (job: QueuedJob<FulltextIndexJobPayload>, ctx: JobContext) => {\n await handleFulltextIndexJob(job, ctx, { resolve: container.resolve.bind(container) })\n },\n connection: { url: redisUrl },\n concurrency,\n })\n }\n}\n\nconst workerCli: ModuleCli = {\n command: 'worker',\n async run(rest) {\n await workerCommand(rest)\n },\n}\n\nconst helpCli: ModuleCli = {\n command: 'help',\n async run() {\n console.log('\\nUsage: yarn mercato search <command> [options]\\n')\n console.log('Commands:')\n console.log(' status Show search module status and available strategies')\n console.log(' query Execute a search query')\n console.log(' index Index a specific record')\n console.log(' reindex Reindex vector embeddings for entities')\n console.log(' reindex-help Show reindex command options')\n console.log(' test-meilisearch Test Meilisearch connection')\n console.log(' worker Start a queue worker for search indexing')\n console.log(' help Show this help message')\n console.log('\\nExamples:')\n console.log(' yarn mercato search status')\n console.log(' yarn mercato search query --query \"john doe\" --tenant tenant-123')\n console.log(' yarn mercato search index --entity customers:customer_person_profile --record abc123 --tenant tenant-123')\n console.log(' yarn mercato search reindex --tenant tenant-123 --entity customers:customer_person_profile')\n console.log(' yarn mercato search test-meilisearch')\n console.log(` yarn mercato search worker ${VECTOR_INDEXING_QUEUE_NAME} --concurrency=10`)\n console.log(` yarn mercato search worker ${FULLTEXT_INDEXING_QUEUE_NAME} --concurrency=5`)\n },\n}\n\nexport default [searchCli, statusCli, indexCli, reindexCli, reindexHelpCli, testMeilisearchCli, workerCli, helpCli]\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,32 @@
1
+ import { asValue } from "awilix";
2
+ import { EmbeddingService, createPgVectorDriver, createChromaDbDriver, createQdrantDriver } from "../../vector/index.js";
3
+ import { createVectorIndexingQueue } from "../../queue/vector-indexing.js";
4
+ import { createFulltextIndexingQueue } from "../../queue/fulltext-indexing.js";
5
+ function register(container) {
6
+ const embeddingService = new EmbeddingService();
7
+ const drivers = [
8
+ createPgVectorDriver(),
9
+ createChromaDbDriver(),
10
+ createQdrantDriver()
11
+ ];
12
+ const queueStrategy = process.env.QUEUE_STRATEGY || "local";
13
+ const queueConnection = queueStrategy === "async" ? { connection: { url: process.env.REDIS_URL || process.env.QUEUE_REDIS_URL } } : void 0;
14
+ const vectorIndexQueue = createVectorIndexingQueue(
15
+ queueStrategy,
16
+ queueConnection
17
+ );
18
+ const fulltextIndexQueue = createFulltextIndexingQueue(
19
+ queueStrategy,
20
+ queueConnection
21
+ );
22
+ container.register({
23
+ vectorEmbeddingService: asValue(embeddingService),
24
+ vectorDrivers: asValue(drivers),
25
+ vectorIndexQueue: asValue(vectorIndexQueue),
26
+ fulltextIndexQueue: asValue(fulltextIndexQueue)
27
+ });
28
+ }
29
+ export {
30
+ register
31
+ };
32
+ //# sourceMappingURL=di.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/search/di.ts"],
4
+ "sourcesContent": ["import { asValue } from 'awilix'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { EmbeddingService, createPgVectorDriver, createChromaDbDriver, createQdrantDriver } from '../../vector'\nimport { createVectorIndexingQueue, type VectorIndexJobPayload } from '../../queue/vector-indexing'\nimport { createFulltextIndexingQueue, type FulltextIndexJobPayload } from '../../queue/fulltext-indexing'\nimport type { Queue } from '@open-mercato/queue'\n\n/**\n * Register search module dependencies.\n *\n * This registers:\n * - vectorEmbeddingService: EmbeddingService for creating embeddings\n * - vectorDrivers: Array of vector database drivers (pgvector, chromadb, qdrant)\n * - vectorIndexQueue: Queue for vector indexing jobs\n * - fulltextIndexQueue: Queue for fulltext indexing jobs\n *\n * Note: VectorIndexService is no longer registered here. Use SearchIndexer instead,\n * which is registered in the main search module DI (packages/search/src/di.ts).\n */\nexport function register(container: AppContainer) {\n const embeddingService = new EmbeddingService()\n const drivers = [\n createPgVectorDriver(),\n createChromaDbDriver(),\n createQdrantDriver(),\n ]\n\n // Create queues based on environment strategy\n const queueStrategy = (process.env.QUEUE_STRATEGY || 'local') as 'local' | 'async'\n const queueConnection = queueStrategy === 'async'\n ? { connection: { url: process.env.REDIS_URL || process.env.QUEUE_REDIS_URL } }\n : undefined\n\n const vectorIndexQueue: Queue<VectorIndexJobPayload> = createVectorIndexingQueue(\n queueStrategy,\n queueConnection,\n )\n\n const fulltextIndexQueue: Queue<FulltextIndexJobPayload> = createFulltextIndexingQueue(\n queueStrategy,\n queueConnection,\n )\n\n container.register({\n vectorEmbeddingService: asValue(embeddingService),\n vectorDrivers: asValue(drivers),\n vectorIndexQueue: asValue(vectorIndexQueue),\n fulltextIndexQueue: asValue(fulltextIndexQueue),\n })\n}\n"],
5
+ "mappings": "AAAA,SAAS,eAAe;AAExB,SAAS,kBAAkB,sBAAsB,sBAAsB,0BAA0B;AACjG,SAAS,iCAA6D;AACtE,SAAS,mCAAiE;AAenE,SAAS,SAAS,WAAyB;AAChD,QAAM,mBAAmB,IAAI,iBAAiB;AAC9C,QAAM,UAAU;AAAA,IACd,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,EACrB;AAGA,QAAM,gBAAiB,QAAQ,IAAI,kBAAkB;AACrD,QAAM,kBAAkB,kBAAkB,UACtC,EAAE,YAAY,EAAE,KAAK,QAAQ,IAAI,aAAa,QAAQ,IAAI,gBAAgB,EAAE,IAC5E;AAEJ,QAAM,mBAAiD;AAAA,IACrD;AAAA,IACA;AAAA,EACF;AAEA,QAAM,qBAAqD;AAAA,IACzD;AAAA,IACA;AAAA,EACF;AAEA,YAAU,SAAS;AAAA,IACjB,wBAAwB,QAAQ,gBAAgB;AAAA,IAChD,eAAe,QAAQ,OAAO;AAAA,IAC9B,kBAAkB,QAAQ,gBAAgB;AAAA,IAC1C,oBAAoB,QAAQ,kBAAkB;AAAA,EAChD,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1,357 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import {
6
+ Search,
7
+ Loader2,
8
+ Zap,
9
+ User,
10
+ Users,
11
+ Building,
12
+ StickyNote,
13
+ Briefcase,
14
+ CheckSquare,
15
+ FileText,
16
+ Mail,
17
+ Phone,
18
+ Calendar,
19
+ Clock,
20
+ Star,
21
+ Tag,
22
+ Flag,
23
+ Heart,
24
+ Bookmark,
25
+ Package,
26
+ Truck,
27
+ ShoppingCart,
28
+ CreditCard,
29
+ DollarSign,
30
+ Target,
31
+ Award,
32
+ Trophy,
33
+ Rocket,
34
+ Lightbulb,
35
+ MessageSquare,
36
+ Bell,
37
+ Settings,
38
+ Globe,
39
+ MapPin,
40
+ Link,
41
+ Folder,
42
+ Database,
43
+ Activity
44
+ } from "lucide-react";
45
+ import { Dialog, DialogContent } from "@open-mercato/ui/primitives/dialog";
46
+ import { Input } from "@open-mercato/ui/primitives/input";
47
+ import { Button } from "@open-mercato/ui/primitives/button";
48
+ import { cn } from "@open-mercato/shared/lib/utils";
49
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
50
+ import { fetchGlobalSearchResults } from "../utils.js";
51
+ const MIN_QUERY_LENGTH = 2;
52
+ const DEFAULT_STRATEGIES = ["fulltext", "vector", "tokens"];
53
+ function normalizeLinks(links) {
54
+ if (!Array.isArray(links)) return [];
55
+ return links.filter((link) => typeof link?.href === "string");
56
+ }
57
+ function pickPrimaryLink(result) {
58
+ if (result.url) return result.url;
59
+ const links = normalizeLinks(result.links);
60
+ if (!links.length) return null;
61
+ const primary = links.find((link) => link.kind === "primary");
62
+ return (primary ?? links[0]).href;
63
+ }
64
+ function humanizeSegment(segment) {
65
+ return segment.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
66
+ }
67
+ const ICON_MAP = {
68
+ bolt: Zap,
69
+ zap: Zap,
70
+ user: User,
71
+ users: Users,
72
+ building: Building,
73
+ "sticky-note": StickyNote,
74
+ briefcase: Briefcase,
75
+ "check-square": CheckSquare,
76
+ "file-text": FileText,
77
+ mail: Mail,
78
+ phone: Phone,
79
+ calendar: Calendar,
80
+ clock: Clock,
81
+ star: Star,
82
+ tag: Tag,
83
+ flag: Flag,
84
+ heart: Heart,
85
+ bookmark: Bookmark,
86
+ package: Package,
87
+ truck: Truck,
88
+ "shopping-cart": ShoppingCart,
89
+ "credit-card": CreditCard,
90
+ "dollar-sign": DollarSign,
91
+ target: Target,
92
+ award: Award,
93
+ trophy: Trophy,
94
+ rocket: Rocket,
95
+ lightbulb: Lightbulb,
96
+ "message-square": MessageSquare,
97
+ bell: Bell,
98
+ settings: Settings,
99
+ globe: Globe,
100
+ "map-pin": MapPin,
101
+ link: Link,
102
+ folder: Folder,
103
+ database: Database,
104
+ activity: Activity
105
+ };
106
+ function resolveIcon(name) {
107
+ if (!name) return null;
108
+ return ICON_MAP[name.toLowerCase()] ?? null;
109
+ }
110
+ function formatEntityId(entityId) {
111
+ if (!entityId.includes(":")) return humanizeSegment(entityId);
112
+ const [module, entity] = entityId.split(":");
113
+ return `${humanizeSegment(module)} \xB7 ${humanizeSegment(entity)}`;
114
+ }
115
+ function GlobalSearchDialog({
116
+ embeddingConfigured,
117
+ missingConfigMessage,
118
+ enabledStrategies: propStrategies
119
+ }) {
120
+ const router = useRouter();
121
+ const [open, setOpen] = React.useState(false);
122
+ const [query, setQuery] = React.useState("");
123
+ const [results, setResults] = React.useState([]);
124
+ const [loading, setLoading] = React.useState(false);
125
+ const [error, setError] = React.useState(null);
126
+ const [selectedIndex, setSelectedIndex] = React.useState(0);
127
+ const inputRef = React.useRef(null);
128
+ const abortRef = React.useRef(null);
129
+ const t = useT();
130
+ const enabledStrategies = React.useMemo(() => {
131
+ if (propStrategies && propStrategies.length > 0) {
132
+ return propStrategies;
133
+ }
134
+ return DEFAULT_STRATEGIES;
135
+ }, [propStrategies]);
136
+ const resetState = React.useCallback(() => {
137
+ setQuery("");
138
+ setResults([]);
139
+ setError(null);
140
+ setSelectedIndex(0);
141
+ setLoading(false);
142
+ }, []);
143
+ React.useEffect(() => {
144
+ if (!open) {
145
+ resetState();
146
+ return;
147
+ }
148
+ const handler = (event) => {
149
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
150
+ event.preventDefault();
151
+ }
152
+ };
153
+ window.addEventListener("keydown", handler);
154
+ return () => window.removeEventListener("keydown", handler);
155
+ }, [open, resetState]);
156
+ React.useEffect(() => {
157
+ const shortcut = (event) => {
158
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
159
+ event.preventDefault();
160
+ setOpen((prev) => !prev);
161
+ }
162
+ };
163
+ window.addEventListener("keydown", shortcut);
164
+ return () => window.removeEventListener("keydown", shortcut);
165
+ }, []);
166
+ React.useEffect(() => {
167
+ if (!open) return;
168
+ const focusTimer = setTimeout(() => inputRef.current?.focus(), 50);
169
+ return () => clearTimeout(focusTimer);
170
+ }, [open]);
171
+ React.useEffect(() => {
172
+ if (!open) return;
173
+ abortRef.current?.abort();
174
+ if (query.trim().length < MIN_QUERY_LENGTH) {
175
+ setResults([]);
176
+ setError(null);
177
+ setLoading(false);
178
+ return;
179
+ }
180
+ const controller = new AbortController();
181
+ abortRef.current = controller;
182
+ setLoading(true);
183
+ const handle = setTimeout(async () => {
184
+ try {
185
+ const data = await fetchGlobalSearchResults(query, {
186
+ limit: 10,
187
+ signal: controller.signal
188
+ });
189
+ setResults(data.results);
190
+ setError(data.error ?? null);
191
+ setSelectedIndex(0);
192
+ } catch (err) {
193
+ if (controller.signal.aborted) return;
194
+ const abortError = err;
195
+ if (abortError?.name === "AbortError") return;
196
+ setError(err instanceof Error ? err.message : t("search.dialog.errors.searchFailed"));
197
+ setResults([]);
198
+ } finally {
199
+ if (!controller.signal.aborted) setLoading(false);
200
+ }
201
+ }, 220);
202
+ return () => {
203
+ clearTimeout(handle);
204
+ controller.abort();
205
+ };
206
+ }, [open, query, enabledStrategies, t]);
207
+ const openResult = React.useCallback((result) => {
208
+ if (!result) return;
209
+ const href = pickPrimaryLink(result);
210
+ if (!href) return;
211
+ router.push(href);
212
+ setOpen(false);
213
+ }, [router]);
214
+ const handleKeyDown = React.useCallback((event) => {
215
+ if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
216
+ event.preventDefault();
217
+ openResult(results[selectedIndex]);
218
+ return;
219
+ }
220
+ if (event.key === "ArrowDown") {
221
+ event.preventDefault();
222
+ setSelectedIndex((prev) => (prev + 1) % Math.max(results.length || 1, 1));
223
+ return;
224
+ }
225
+ if (event.key === "ArrowUp") {
226
+ event.preventDefault();
227
+ setSelectedIndex((prev) => {
228
+ if (!results.length) return 0;
229
+ return prev <= 0 ? results.length - 1 : prev - 1;
230
+ });
231
+ return;
232
+ }
233
+ if (event.key === "Escape") {
234
+ event.preventDefault();
235
+ setOpen(false);
236
+ return;
237
+ }
238
+ if (event.key === "Enter") {
239
+ event.preventDefault();
240
+ const target = results[selectedIndex];
241
+ openResult(target);
242
+ return;
243
+ }
244
+ }, [results, selectedIndex, openResult]);
245
+ const showVectorWarning = !embeddingConfigured && enabledStrategies.includes("vector") && !error;
246
+ const selectedResult = results[selectedIndex];
247
+ const selectedHasLink = selectedResult ? pickPrimaryLink(selectedResult) !== null : false;
248
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
249
+ /* @__PURE__ */ jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setOpen(true), className: "hidden sm:inline-flex items-center gap-2", children: [
250
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4" }),
251
+ /* @__PURE__ */ jsx("span", { children: t("search.dialog.actions.search") }),
252
+ /* @__PURE__ */ jsx("span", { className: "ml-2 rounded border px-1 text-xs text-muted-foreground", children: "\u2318K" })
253
+ ] }),
254
+ /* @__PURE__ */ jsx(
255
+ Button,
256
+ {
257
+ type: "button",
258
+ variant: "ghost",
259
+ size: "icon",
260
+ className: "sm:hidden",
261
+ onClick: () => setOpen(true),
262
+ "aria-label": t("search.dialog.actions.openGlobalSearch"),
263
+ children: /* @__PURE__ */ jsx(Search, { className: "h-4 w-4" })
264
+ }
265
+ ),
266
+ /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-xl p-0", "aria-describedby": "global-search-description", children: [
267
+ /* @__PURE__ */ jsx("span", { id: "global-search-description", className: "sr-only", children: t("search.dialog.instructions") }),
268
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 border-b px-4 pb-3 pt-4", children: [
269
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded border bg-background px-3 py-2 focus-within:ring-2 focus-within:ring-ring", children: [
270
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 text-muted-foreground" }),
271
+ /* @__PURE__ */ jsx(
272
+ TypedInput,
273
+ {
274
+ ref: inputRef,
275
+ value: query,
276
+ onChange: (event) => setQuery(event.target.value),
277
+ onKeyDown: handleKeyDown,
278
+ placeholder: t("search.dialog.input.placeholder"),
279
+ className: "border-none px-0 shadow-none focus-visible:ring-0",
280
+ autoFocus: true
281
+ }
282
+ ),
283
+ loading && /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" })
284
+ ] }),
285
+ error ? /* @__PURE__ */ jsx("p", { className: "rounded bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error }) : null,
286
+ showVectorWarning ? /* @__PURE__ */ jsx("p", { className: "rounded bg-amber-100 dark:bg-amber-900/20 px-3 py-2 text-sm text-amber-800 dark:text-amber-200", children: missingConfigMessage }) : null
287
+ ] }),
288
+ /* @__PURE__ */ jsxs("div", { className: "max-h-96 overflow-y-auto px-2 pb-3", children: [
289
+ results.length === 0 && !loading && !error ? /* @__PURE__ */ jsx("div", { className: "px-4 py-6 text-sm text-muted-foreground", children: query.trim().length < MIN_QUERY_LENGTH ? t("search.dialog.empty.hint") : t("search.dialog.empty.none") }) : null,
290
+ /* @__PURE__ */ jsx("ul", { className: "flex flex-col", children: results.map((result, index) => {
291
+ const presenter = result.presenter;
292
+ const isActive = index === selectedIndex;
293
+ const hasLink = pickPrimaryLink(result) !== null;
294
+ const Icon = presenter?.icon ? resolveIcon(presenter.icon) : null;
295
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
296
+ "button",
297
+ {
298
+ type: "button",
299
+ onClick: () => openResult(result),
300
+ onMouseEnter: () => setSelectedIndex(index),
301
+ className: cn(
302
+ "w-full rounded-lg px-4 py-3 text-left transition border",
303
+ isActive ? "border-primary bg-primary/10 text-foreground shadow-sm" : "border-transparent hover:border-muted-foreground/30 hover:bg-muted/60",
304
+ !hasLink && "opacity-60"
305
+ ),
306
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
307
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
308
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
309
+ /* @__PURE__ */ jsx("span", { className: cn("font-medium text-base whitespace-normal break-all", !hasLink && "text-muted-foreground"), children: presenter?.title ?? result.recordId }),
310
+ /* @__PURE__ */ jsx("span", { className: "rounded-full border border-muted-foreground/30 px-2 py-0.5 text-xs text-muted-foreground", children: formatEntityId(result.entityId) }),
311
+ !hasLink && /* @__PURE__ */ jsx("span", { className: "rounded-full border border-amber-500/50 bg-amber-50 dark:bg-amber-900/20 px-2 py-0.5 text-xs text-amber-700 dark:text-amber-400", children: t("search.dialog.noLink") })
312
+ ] }),
313
+ presenter?.subtitle ? /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground whitespace-normal break-words", children: presenter.subtitle }) : null,
314
+ normalizeLinks(result.links).length ? /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap items-center gap-2", children: normalizeLinks(result.links).map((link) => /* @__PURE__ */ jsx(
315
+ "span",
316
+ {
317
+ className: cn(
318
+ "rounded-full border px-2 py-0.5 text-xs",
319
+ link.kind === "primary" ? "border-primary text-primary" : "border-muted-foreground/40 text-muted-foreground"
320
+ ),
321
+ children: link.label ?? link.href
322
+ },
323
+ `${link.href}`
324
+ )) }) : null
325
+ ] }),
326
+ Icon ? /* @__PURE__ */ jsx("div", { className: "flex flex-col items-end gap-2", children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-muted-foreground" }) }) : null
327
+ ] })
328
+ }
329
+ ) }, `${result.entityId}:${result.recordId}`);
330
+ }) })
331
+ ] }),
332
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t px-4 py-3", children: [
333
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: selectedResult && !selectedHasLink ? t("search.dialog.noLinkHint") : t("search.dialog.shortcuts.hint") }),
334
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
335
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setOpen(false), children: t("search.dialog.actions.cancel") }),
336
+ /* @__PURE__ */ jsx(
337
+ Button,
338
+ {
339
+ type: "button",
340
+ size: "sm",
341
+ onClick: () => openResult(results[selectedIndex]),
342
+ disabled: !results.length || !selectedHasLink,
343
+ children: t("search.dialog.actions.openSelected")
344
+ }
345
+ )
346
+ ] })
347
+ ] })
348
+ ] }) })
349
+ ] });
350
+ }
351
+ var GlobalSearchDialog_default = GlobalSearchDialog;
352
+ const TypedInput = Input;
353
+ export {
354
+ GlobalSearchDialog,
355
+ GlobalSearchDialog_default as default
356
+ };
357
+ //# sourceMappingURL=GlobalSearchDialog.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/search/frontend/components/GlobalSearchDialog.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport {\n Search,\n Loader2,\n Zap,\n User,\n Users,\n Building,\n StickyNote,\n Briefcase,\n CheckSquare,\n FileText,\n Mail,\n Phone,\n Calendar,\n Clock,\n Star,\n Tag,\n Flag,\n Heart,\n Bookmark,\n Package,\n Truck,\n ShoppingCart,\n CreditCard,\n DollarSign,\n Target,\n Award,\n Trophy,\n Rocket,\n Lightbulb,\n MessageSquare,\n Bell,\n Settings,\n Globe,\n MapPin,\n Link,\n Folder,\n Database,\n Activity,\n} from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { Dialog, DialogContent } from '@open-mercato/ui/primitives/dialog'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport type { SearchResult, SearchResultLink, SearchStrategyId } from '@open-mercato/shared/modules/search'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { fetchGlobalSearchResults } from '../utils'\n\nconst MIN_QUERY_LENGTH = 2\n\n/** Default strategies used when none are configured */\nconst DEFAULT_STRATEGIES: SearchStrategyId[] = ['fulltext', 'vector', 'tokens']\n\nfunction normalizeLinks(links?: SearchResultLink[] | null): SearchResultLink[] {\n if (!Array.isArray(links)) return []\n return links.filter((link) => typeof link?.href === 'string')\n}\n\nfunction pickPrimaryLink(result: SearchResult): string | null {\n if (result.url) return result.url\n const links = normalizeLinks(result.links)\n if (!links.length) return null\n const primary = links.find((link) => link.kind === 'primary')\n return (primary ?? links[0]).href\n}\n\nfunction humanizeSegment(segment: string): string {\n return segment\n .split(/[_-]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(' ')\n}\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n bolt: Zap,\n zap: Zap,\n user: User,\n users: Users,\n building: Building,\n 'sticky-note': StickyNote,\n briefcase: Briefcase,\n 'check-square': CheckSquare,\n 'file-text': FileText,\n mail: Mail,\n phone: Phone,\n calendar: Calendar,\n clock: Clock,\n star: Star,\n tag: Tag,\n flag: Flag,\n heart: Heart,\n bookmark: Bookmark,\n package: Package,\n truck: Truck,\n 'shopping-cart': ShoppingCart,\n 'credit-card': CreditCard,\n 'dollar-sign': DollarSign,\n target: Target,\n award: Award,\n trophy: Trophy,\n rocket: Rocket,\n lightbulb: Lightbulb,\n 'message-square': MessageSquare,\n bell: Bell,\n settings: Settings,\n globe: Globe,\n 'map-pin': MapPin,\n link: Link,\n folder: Folder,\n database: Database,\n activity: Activity,\n}\n\nfunction resolveIcon(name?: string): LucideIcon | null {\n if (!name) return null\n return ICON_MAP[name.toLowerCase()] ?? null\n}\n\nfunction formatEntityId(entityId: string): string {\n if (!entityId.includes(':')) return humanizeSegment(entityId)\n const [module, entity] = entityId.split(':')\n return `${humanizeSegment(module)} \u00B7 ${humanizeSegment(entity)}`\n}\n\nexport type GlobalSearchDialogProps = {\n /** Whether embedding provider is configured for vector search */\n embeddingConfigured: boolean\n /** Message to show when embedding is not configured */\n missingConfigMessage: string\n /** Enabled strategies from tenant configuration (optional - uses defaults if not provided) */\n enabledStrategies?: SearchStrategyId[]\n}\n\nexport function GlobalSearchDialog({\n embeddingConfigured,\n missingConfigMessage,\n enabledStrategies: propStrategies,\n}: GlobalSearchDialogProps) {\n const router = useRouter()\n const [open, setOpen] = React.useState(false)\n const [query, setQuery] = React.useState('')\n const [results, setResults] = React.useState<SearchResult[]>([])\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [selectedIndex, setSelectedIndex] = React.useState(0)\n const inputRef = React.useRef<HTMLInputElement | null>(null)\n const abortRef = React.useRef<AbortController | null>(null)\n const t = useT()\n\n // Use configured strategies or fall back to defaults\n const enabledStrategies = React.useMemo(() => {\n if (propStrategies && propStrategies.length > 0) {\n return propStrategies\n }\n return DEFAULT_STRATEGIES\n }, [propStrategies])\n\n const resetState = React.useCallback(() => {\n setQuery('')\n setResults([])\n setError(null)\n setSelectedIndex(0)\n setLoading(false)\n }, [])\n\n React.useEffect(() => {\n if (!open) {\n resetState()\n return\n }\n const handler = (event: KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {\n event.preventDefault()\n }\n }\n window.addEventListener('keydown', handler)\n return () => window.removeEventListener('keydown', handler)\n }, [open, resetState])\n\n React.useEffect(() => {\n const shortcut = (event: KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {\n event.preventDefault()\n setOpen((prev) => !prev)\n }\n }\n window.addEventListener('keydown', shortcut)\n return () => window.removeEventListener('keydown', shortcut)\n }, [])\n\n React.useEffect(() => {\n if (!open) return\n const focusTimer = setTimeout(() => inputRef.current?.focus(), 50)\n return () => clearTimeout(focusTimer)\n }, [open])\n\n React.useEffect(() => {\n if (!open) return\n\n abortRef.current?.abort()\n if (query.trim().length < MIN_QUERY_LENGTH) {\n setResults([])\n setError(null)\n setLoading(false)\n return\n }\n\n const controller = new AbortController()\n abortRef.current = controller\n setLoading(true)\n\n const handle = setTimeout(async () => {\n try {\n const data = await fetchGlobalSearchResults(query, {\n limit: 10,\n signal: controller.signal,\n })\n setResults(data.results)\n setError(data.error ?? null)\n setSelectedIndex(0)\n } catch (err: unknown) {\n if (controller.signal.aborted) return\n const abortError = err as { name?: string }\n if (abortError?.name === 'AbortError') return\n setError(err instanceof Error ? err.message : t('search.dialog.errors.searchFailed'))\n setResults([])\n } finally {\n if (!controller.signal.aborted) setLoading(false)\n }\n }, 220)\n\n return () => {\n clearTimeout(handle)\n controller.abort()\n }\n }, [open, query, enabledStrategies, t])\n\n const openResult = React.useCallback((result: SearchResult | undefined) => {\n if (!result) return\n const href = pickPrimaryLink(result)\n if (!href) return\n router.push(href)\n setOpen(false)\n }, [router])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLInputElement>) => {\n if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {\n event.preventDefault()\n openResult(results[selectedIndex])\n return\n }\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n setSelectedIndex((prev) => (prev + 1) % Math.max(results.length || 1, 1))\n return\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault()\n setSelectedIndex((prev) => {\n if (!results.length) return 0\n return prev <= 0 ? results.length - 1 : prev - 1\n })\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n setOpen(false)\n return\n }\n if (event.key === 'Enter') {\n event.preventDefault()\n const target = results[selectedIndex]\n openResult(target)\n return\n }\n }, [results, selectedIndex, openResult])\n\n // Check if vector search is enabled but not configured\n const showVectorWarning = !embeddingConfigured && enabledStrategies.includes('vector') && !error\n\n // Check if selected result has a navigable link\n const selectedResult = results[selectedIndex]\n const selectedHasLink = selectedResult ? pickPrimaryLink(selectedResult) !== null : false\n\n return (\n <>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => setOpen(true)} className=\"hidden sm:inline-flex items-center gap-2\">\n <Search className=\"h-4 w-4\" />\n <span>{t('search.dialog.actions.search')}</span>\n <span className=\"ml-2 rounded border px-1 text-xs text-muted-foreground\">\u2318K</span>\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"sm:hidden\"\n onClick={() => setOpen(true)}\n aria-label={t('search.dialog.actions.openGlobalSearch')}\n >\n <Search className=\"h-4 w-4\" />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-w-xl p-0\" aria-describedby=\"global-search-description\">\n <span id=\"global-search-description\" className=\"sr-only\">\n {t('search.dialog.instructions')}\n </span>\n <div className=\"flex flex-col gap-3 border-b px-4 pb-3 pt-4\">\n <div className=\"flex items-center gap-2 rounded border bg-background px-3 py-2 focus-within:ring-2 focus-within:ring-ring\">\n <Search className=\"h-4 w-4 text-muted-foreground\" />\n <TypedInput\n ref={inputRef}\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={t('search.dialog.input.placeholder')}\n className=\"border-none px-0 shadow-none focus-visible:ring-0\"\n autoFocus\n />\n {loading && <Loader2 className=\"h-4 w-4 animate-spin text-muted-foreground\" />}\n </div>\n\n {error ? (\n <p className=\"rounded bg-destructive/10 px-3 py-2 text-sm text-destructive\">{error}</p>\n ) : null}\n {showVectorWarning ? (\n <p className=\"rounded bg-amber-100 dark:bg-amber-900/20 px-3 py-2 text-sm text-amber-800 dark:text-amber-200\">{missingConfigMessage}</p>\n ) : null}\n </div>\n <div className=\"max-h-96 overflow-y-auto px-2 pb-3\">\n {results.length === 0 && !loading && !error ? (\n <div className=\"px-4 py-6 text-sm text-muted-foreground\">\n {query.trim().length < MIN_QUERY_LENGTH\n ? t('search.dialog.empty.hint')\n : t('search.dialog.empty.none')}\n </div>\n ) : null}\n <ul className=\"flex flex-col\">\n {results.map((result, index) => {\n const presenter = result.presenter\n const isActive = index === selectedIndex\n const hasLink = pickPrimaryLink(result) !== null\n const Icon = presenter?.icon ? resolveIcon(presenter.icon) : null\n return (\n <li key={`${result.entityId}:${result.recordId}`}>\n <button\n type=\"button\"\n onClick={() => openResult(result)}\n onMouseEnter={() => setSelectedIndex(index)}\n className={cn(\n 'w-full rounded-lg px-4 py-3 text-left transition border',\n isActive\n ? 'border-primary bg-primary/10 text-foreground shadow-sm'\n : 'border-transparent hover:border-muted-foreground/30 hover:bg-muted/60',\n !hasLink && 'opacity-60'\n )}\n >\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className={cn('font-medium text-base whitespace-normal break-all', !hasLink && 'text-muted-foreground')}>{presenter?.title ?? result.recordId}</span>\n <span className=\"rounded-full border border-muted-foreground/30 px-2 py-0.5 text-xs text-muted-foreground\">\n {formatEntityId(result.entityId)}\n </span>\n {!hasLink && (\n <span className=\"rounded-full border border-amber-500/50 bg-amber-50 dark:bg-amber-900/20 px-2 py-0.5 text-xs text-amber-700 dark:text-amber-400\">\n {t('search.dialog.noLink')}\n </span>\n )}\n </div>\n {presenter?.subtitle ? (\n <div className=\"text-sm text-muted-foreground whitespace-normal break-words\">{presenter.subtitle}</div>\n ) : null}\n {normalizeLinks(result.links).length ? (\n <div className=\"mt-1 flex flex-wrap items-center gap-2\">\n {normalizeLinks(result.links).map((link) => (\n <span\n key={`${link.href}`}\n className={cn(\n 'rounded-full border px-2 py-0.5 text-xs',\n link.kind === 'primary'\n ? 'border-primary text-primary'\n : 'border-muted-foreground/40 text-muted-foreground'\n )}\n >\n {link.label ?? link.href}\n </span>\n ))}\n </div>\n ) : null}\n </div>\n {Icon ? (\n <div className=\"flex flex-col items-end gap-2\">\n <Icon className=\"h-5 w-5 text-muted-foreground\" />\n </div>\n ) : null}\n </div>\n </button>\n </li>\n )\n })}\n </ul>\n </div>\n <div className=\"flex items-center justify-between border-t px-4 py-3\">\n <span className=\"text-xs text-muted-foreground\">\n {selectedResult && !selectedHasLink\n ? t('search.dialog.noLinkHint')\n : t('search.dialog.shortcuts.hint')}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => setOpen(false)}>\n {t('search.dialog.actions.cancel')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => openResult(results[selectedIndex])}\n disabled={!results.length || !selectedHasLink}\n >\n {t('search.dialog.actions.openSelected')}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\nexport default GlobalSearchDialog\nconst TypedInput = Input as React.ForwardRefExoticComponent<React.InputHTMLAttributes<HTMLInputElement> & React.RefAttributes<HTMLInputElement>>\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }