@open-mercato/search 0.4.11-develop.2236.83f5fdcdeb → 0.4.11-develop.2238.3f713b35d4

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.
@@ -1,5 +1,5 @@
1
1
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
2
- import { getRedisUrl } from "@open-mercato/shared/lib/redis/connection";
2
+ import { getRedisUrlOrThrow } from "@open-mercato/shared/lib/redis/connection";
3
3
  import { recordIndexerError } from "@open-mercato/shared/lib/indexers/error-log";
4
4
  import { recordIndexerLog } from "@open-mercato/shared/lib/indexers/status-log";
5
5
  import { createProgressBar } from "@open-mercato/shared/lib/cli/progress";
@@ -672,7 +672,7 @@ async function workerCommand(rest) {
672
672
  console.error("Set QUEUE_STRATEGY=async and configure REDIS_URL in your environment.\n");
673
673
  return;
674
674
  }
675
- const redisUrl = getRedisUrl("QUEUE");
675
+ const redisUrl = getRedisUrlOrThrow("QUEUE");
676
676
  const { runWorker } = await import("@open-mercato/queue/worker");
677
677
  console.log(`
678
678
  Starting ${queueName} worker...`);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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 { getRedisUrl } from '@open-mercato/shared/lib/redis/connection'\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\ntype PartitionProgressInfo = { processed: number; total: number }\n\nfunction isIndexerVerbose(): boolean {\n const parsed = parseBooleanToken(process.env.OM_INDEXER_VERBOSE ?? '')\n return parsed === true\n}\n\nfunction createGroupedProgress(label: string, partitionTargets: number[]) {\n const totals = new Map<number, number>()\n const processed = new Map<number, number>()\n let bar: CliProgressBar | null = null\n\n const getTotals = () => {\n let total = 0\n let done = 0\n for (const value of totals.values()) total += value\n for (const value of processed.values()) done += value\n return { total, done }\n }\n\n const tryInitBar = () => {\n if (bar) return\n if (totals.size < partitionTargets.length) return\n const { total } = getTotals()\n if (total <= 0) return\n bar = createProgressBar(label, total)\n }\n\n return {\n onProgress(partition: number, info: PartitionProgressInfo) {\n processed.set(partition, info.processed)\n if (!totals.has(partition)) totals.set(partition, info.total)\n tryInitBar()\n if (!bar) return\n const { done } = getTotals()\n bar.update(done)\n },\n complete() {\n if (bar) bar.complete()\n },\n getTotals,\n }\n}\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 verbose = isIndexerVerbose()\n const progressState = verbose ? new Map<number, { last: number }>() : null\n const groupedProgress =\n !verbose && partitionTargets.length > 1\n ? createGroupedProgress(`Reindexing ${entityType}`, partitionTargets)\n : null\n const renderProgress = (part: number, info: PartitionProgressInfo) => {\n if (!progressState) return\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 (verbose && 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 if (groupedProgress) {\n groupedProgress.onProgress(part, info)\n } else {\n renderProgress(part, info)\n }\n },\n })\n if (progressBar) (progressBar as CliProgressBar).complete()\n if (!useBar && groupedProgress) {\n groupedProgress.onProgress(part, { processed: stats.processed, total: stats.total })\n } else 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 groupedProgress?.complete()\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 = getRedisUrl('QUEUE')\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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",
4
+ "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getRedisUrlOrThrow } from '@open-mercato/shared/lib/redis/connection'\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\ntype PartitionProgressInfo = { processed: number; total: number }\n\nfunction isIndexerVerbose(): boolean {\n const parsed = parseBooleanToken(process.env.OM_INDEXER_VERBOSE ?? '')\n return parsed === true\n}\n\nfunction createGroupedProgress(label: string, partitionTargets: number[]) {\n const totals = new Map<number, number>()\n const processed = new Map<number, number>()\n let bar: CliProgressBar | null = null\n\n const getTotals = () => {\n let total = 0\n let done = 0\n for (const value of totals.values()) total += value\n for (const value of processed.values()) done += value\n return { total, done }\n }\n\n const tryInitBar = () => {\n if (bar) return\n if (totals.size < partitionTargets.length) return\n const { total } = getTotals()\n if (total <= 0) return\n bar = createProgressBar(label, total)\n }\n\n return {\n onProgress(partition: number, info: PartitionProgressInfo) {\n processed.set(partition, info.processed)\n if (!totals.has(partition)) totals.set(partition, info.total)\n tryInitBar()\n if (!bar) return\n const { done } = getTotals()\n bar.update(done)\n },\n complete() {\n if (bar) bar.complete()\n },\n getTotals,\n }\n}\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 verbose = isIndexerVerbose()\n const progressState = verbose ? new Map<number, { last: number }>() : null\n const groupedProgress =\n !verbose && partitionTargets.length > 1\n ? createGroupedProgress(`Reindexing ${entityType}`, partitionTargets)\n : null\n const renderProgress = (part: number, info: PartitionProgressInfo) => {\n if (!progressState) return\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 (verbose && 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 if (groupedProgress) {\n groupedProgress.onProgress(part, info)\n } else {\n renderProgress(part, info)\n }\n },\n })\n if (progressBar) (progressBar as CliProgressBar).complete()\n if (!useBar && groupedProgress) {\n groupedProgress.onProgress(part, { processed: stats.processed, total: stats.total })\n } else 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 groupedProgress?.complete()\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 = getRedisUrlOrThrow('QUEUE')\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
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import { asValue } from "awilix";
2
- import { getRedisUrl } from "@open-mercato/shared/lib/redis/connection";
2
+ import { getRedisUrlOrThrow } from "@open-mercato/shared/lib/redis/connection";
3
3
  import { EmbeddingService, createPgVectorDriver, createChromaDbDriver, createQdrantDriver } from "../../vector/index.js";
4
4
  import { createVectorIndexingQueue } from "../../queue/vector-indexing.js";
5
5
  import { createFulltextIndexingQueue } from "../../queue/fulltext-indexing.js";
@@ -11,7 +11,7 @@ function register(container) {
11
11
  createQdrantDriver()
12
12
  ];
13
13
  const queueStrategy = process.env.QUEUE_STRATEGY || "local";
14
- const queueConnection = queueStrategy === "async" ? { connection: { url: getRedisUrl("QUEUE") } } : void 0;
14
+ const queueConnection = queueStrategy === "async" ? { connection: { url: getRedisUrlOrThrow("QUEUE") } } : void 0;
15
15
  const vectorIndexQueue = createVectorIndexingQueue(
16
16
  queueStrategy,
17
17
  queueConnection
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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 { getRedisUrl } from '@open-mercato/shared/lib/redis/connection'\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: getRedisUrl('QUEUE') } }\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,mBAAmB;AAC5B,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,YAAY,OAAO,EAAE,EAAE,IAC5C;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;",
4
+ "sourcesContent": ["import { asValue } from 'awilix'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { getRedisUrlOrThrow } from '@open-mercato/shared/lib/redis/connection'\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: getRedisUrlOrThrow('QUEUE') } }\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,0BAA0B;AACnC,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,mBAAmB,OAAO,EAAE,EAAE,IACnD;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
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/search",
3
- "version": "0.4.11-develop.2236.83f5fdcdeb",
3
+ "version": "0.4.11-develop.2238.3f713b35d4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -126,9 +126,9 @@
126
126
  "zod": "^4.0.0"
127
127
  },
128
128
  "peerDependencies": {
129
- "@open-mercato/core": "0.4.11-develop.2236.83f5fdcdeb",
130
- "@open-mercato/queue": "0.4.11-develop.2236.83f5fdcdeb",
131
- "@open-mercato/shared": "0.4.11-develop.2236.83f5fdcdeb"
129
+ "@open-mercato/core": "0.4.11-develop.2238.3f713b35d4",
130
+ "@open-mercato/queue": "0.4.11-develop.2238.3f713b35d4",
131
+ "@open-mercato/shared": "0.4.11-develop.2238.3f713b35d4"
132
132
  },
133
133
  "devDependencies": {
134
134
  "@types/jest": "^30.0.0",
@@ -1,6 +1,6 @@
1
1
  import type { ModuleCli } from '@open-mercato/shared/modules/registry'
2
2
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
3
- import { getRedisUrl } from '@open-mercato/shared/lib/redis/connection'
3
+ import { getRedisUrlOrThrow } from '@open-mercato/shared/lib/redis/connection'
4
4
  import { recordIndexerError } from '@open-mercato/shared/lib/indexers/error-log'
5
5
  import { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'
6
6
  import { createProgressBar } from '@open-mercato/shared/lib/cli/progress'
@@ -798,7 +798,7 @@ async function workerCommand(rest: string[]): Promise<void> {
798
798
  return
799
799
  }
800
800
 
801
- const redisUrl = getRedisUrl('QUEUE')
801
+ const redisUrl = getRedisUrlOrThrow('QUEUE')
802
802
 
803
803
  // Dynamically import runWorker to avoid loading BullMQ unless needed
804
804
  const { runWorker } = await import('@open-mercato/queue/worker')
@@ -1,6 +1,6 @@
1
1
  import { asValue } from 'awilix'
2
2
  import type { AppContainer } from '@open-mercato/shared/lib/di/container'
3
- import { getRedisUrl } from '@open-mercato/shared/lib/redis/connection'
3
+ import { getRedisUrlOrThrow } from '@open-mercato/shared/lib/redis/connection'
4
4
  import { EmbeddingService, createPgVectorDriver, createChromaDbDriver, createQdrantDriver } from '../../vector'
5
5
  import { createVectorIndexingQueue, type VectorIndexJobPayload } from '../../queue/vector-indexing'
6
6
  import { createFulltextIndexingQueue, type FulltextIndexJobPayload } from '../../queue/fulltext-indexing'
@@ -29,7 +29,7 @@ export function register(container: AppContainer) {
29
29
  // Create queues based on environment strategy
30
30
  const queueStrategy = (process.env.QUEUE_STRATEGY || 'local') as 'local' | 'async'
31
31
  const queueConnection = queueStrategy === 'async'
32
- ? { connection: { url: getRedisUrl('QUEUE') } }
32
+ ? { connection: { url: getRedisUrlOrThrow('QUEUE') } }
33
33
  : undefined
34
34
 
35
35
  const vectorIndexQueue: Queue<VectorIndexJobPayload> = createVectorIndexingQueue(