@open-mercato/search 0.5.1-develop.2709.b6bdd776ac → 0.5.1-develop.2744.9c8be0dd93

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.
@@ -130,7 +130,12 @@ async function POST(req) {
130
130
  if (parsed.data.autoIndexingEnabled !== void 0) {
131
131
  if (envDisablesAutoIndexing()) {
132
132
  return NextResponse.json(
133
- { error: t("search.api.errors.autoIndexingDisabled", "Auto-indexing is disabled via DISABLE_VECTOR_SEARCH_AUTOINDEXING.") },
133
+ {
134
+ error: t(
135
+ "search.api.errors.autoIndexingDisabled",
136
+ "Auto-indexing is disabled via OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING (legacy alias: DISABLE_VECTOR_SEARCH_AUTOINDEXING)."
137
+ )
138
+ },
134
139
  { status: 409 }
135
140
  );
136
141
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/search/api/embeddings/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { envDisablesAutoIndexing, resolveAutoIndexingEnabled, SEARCH_AUTO_INDEX_CONFIG_KEY } from '../../lib/auto-indexing'\nimport {\n resolveEmbeddingConfig,\n saveEmbeddingConfig,\n getConfiguredProviders,\n detectConfigChange,\n getEffectiveDimension,\n} from '../../lib/embedding-config'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { EmbeddingProviderConfig, EmbeddingProviderId, VectorDriver } from '../../../../vector'\nimport { EMBEDDING_PROVIDERS, DEFAULT_EMBEDDING_CONFIG, EmbeddingService } from '../../../../vector'\nimport { searchDebug, searchDebugWarn, searchError } from '../../../../lib/debug'\nimport { embeddingsOpenApi } from '../openapi'\n\nconst embeddingConfigSchema = z.object({\n providerId: z.enum(['openai', 'google', 'mistral', 'cohere', 'bedrock', 'ollama']),\n model: z.string(),\n dimension: z.number(),\n outputDimensionality: z.number().optional(),\n baseUrl: z.string().optional(),\n})\n\nconst updateSchema = z.object({\n autoIndexingEnabled: z.boolean().optional(),\n embeddingConfig: embeddingConfigSchema.optional(),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['search.embeddings.view'] },\n POST: { requireAuth: true, requireFeatures: ['search.embeddings.manage'] },\n}\n\ntype SettingsResponse = {\n settings: {\n openaiConfigured: boolean\n autoIndexingEnabled: boolean\n autoIndexingLocked: boolean\n lockReason: string | null\n embeddingConfig: EmbeddingProviderConfig | null\n configuredProviders: EmbeddingProviderId[]\n indexedDimension: number | null\n reindexRequired: boolean\n documentCount: number | null\n }\n}\n\nconst openAiConfigured = () => Boolean(process.env.OPENAI_API_KEY && process.env.OPENAI_API_KEY.trim().length > 0)\n\nconst toJson = (payload: SettingsResponse, init?: ResponseInit) => NextResponse.json(payload, init)\n\nconst unauthorized = async () => {\n const { t } = await resolveTranslations()\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n}\n\nconst configUnavailable = async () => {\n const { t } = await resolveTranslations()\n return NextResponse.json({ error: t('search.api.errors.configUnavailable', 'Configuration service unavailable') }, { status: 503 })\n}\n\nasync function getIndexedDimension(container: { resolve: <T = unknown>(name: string) => T }): Promise<number | null> {\n try {\n const drivers = container.resolve<VectorDriver[]>('vectorDrivers')\n const pgvectorDriver = drivers.find((d) => d.id === 'pgvector')\n if (pgvectorDriver?.getTableDimension) {\n return await pgvectorDriver.getTableDimension()\n }\n return null\n } catch {\n return null\n }\n}\n\nasync function getVectorDocumentCount(\n container: { resolve: <T = unknown>(name: string) => T },\n tenantId: string,\n organizationId?: string | null,\n): Promise<number | null> {\n try {\n const drivers = container.resolve<VectorDriver[]>('vectorDrivers')\n const pgvectorDriver = drivers.find((d) => d.id === 'pgvector')\n if (pgvectorDriver?.count) {\n return await pgvectorDriver.count({ tenantId, organizationId: organizationId ?? undefined })\n }\n return null\n } catch {\n return null\n }\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) return await unauthorized()\n\n const container = await createRequestContainer()\n try {\n const lockedByEnv = envDisablesAutoIndexing()\n let autoIndexingEnabled = !lockedByEnv\n if (!lockedByEnv) {\n try {\n autoIndexingEnabled = await resolveAutoIndexingEnabled(container, { defaultValue: true })\n } catch {\n autoIndexingEnabled = true\n }\n }\n\n const embeddingConfig = await resolveEmbeddingConfig(container, { defaultValue: null })\n const configuredProviders = getConfiguredProviders()\n const indexedDimension = await getIndexedDimension(container)\n\n const effectiveDimension = embeddingConfig\n ? getEffectiveDimension(embeddingConfig)\n : DEFAULT_EMBEDDING_CONFIG.dimension\n\n const reindexRequired = Boolean(\n indexedDimension &&\n embeddingConfig &&\n indexedDimension !== effectiveDimension\n )\n\n // Get document count for vector index\n const documentCount = auth.tenantId\n ? await getVectorDocumentCount(container, auth.tenantId, auth.orgId)\n : null\n\n return toJson({\n settings: {\n openaiConfigured: openAiConfigured(),\n autoIndexingEnabled: lockedByEnv ? false : autoIndexingEnabled,\n autoIndexingLocked: lockedByEnv,\n lockReason: lockedByEnv ? 'env' : null,\n embeddingConfig,\n configuredProviders,\n indexedDimension,\n reindexRequired,\n documentCount,\n },\n })\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n}\n\nexport async function POST(req: Request) {\n const { t } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) return await unauthorized()\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: t('api.errors.invalidJson', 'Invalid JSON payload.') }, { status: 400 })\n }\n const parsed = updateSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ error: t('api.errors.invalidPayload', 'Invalid payload.') }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n try {\n let service: ModuleConfigService\n try {\n service = (container.resolve('moduleConfigService') as ModuleConfigService)\n } catch {\n return await configUnavailable()\n }\n\n if (parsed.data.autoIndexingEnabled !== undefined) {\n if (envDisablesAutoIndexing()) {\n return NextResponse.json(\n { error: t('search.api.errors.autoIndexingDisabled', 'Auto-indexing is disabled via DISABLE_VECTOR_SEARCH_AUTOINDEXING.') },\n { status: 409 },\n )\n }\n await service.setValue('vector', SEARCH_AUTO_INDEX_CONFIG_KEY, parsed.data.autoIndexingEnabled)\n }\n\n let embeddingConfig = await resolveEmbeddingConfig(container, { defaultValue: null })\n let reindexRequired = false\n let indexedDimension = await getIndexedDimension(container)\n\n if (parsed.data.embeddingConfig) {\n const newConfig = parsed.data.embeddingConfig\n const providerInfo = EMBEDDING_PROVIDERS[newConfig.providerId]\n\n if (!providerInfo) {\n return NextResponse.json(\n { error: t('search.api.errors.invalidProvider', 'Invalid embedding provider.') },\n { status: 400 },\n )\n }\n\n const configuredProviders = getConfiguredProviders()\n if (!configuredProviders.includes(newConfig.providerId)) {\n return NextResponse.json(\n { error: t('search.api.errors.providerNotConfigured', `Provider ${providerInfo.name} is not configured. Set ${providerInfo.envKeyRequired} environment variable.`) },\n { status: 400 },\n )\n }\n\n const change = detectConfigChange(\n embeddingConfig,\n {\n ...newConfig,\n updatedAt: new Date().toISOString(),\n },\n indexedDimension\n )\n\n if (change.requiresReindex) {\n const newDimension = getEffectiveDimension(change.newConfig)\n searchDebug('search.embeddings.update', 'config change detected, recreating table', {\n requiresReindex: change.requiresReindex,\n reason: change.reason,\n oldDimension: indexedDimension,\n newDimension,\n })\n try {\n const drivers = container.resolve<VectorDriver[]>('vectorDrivers')\n const pgvectorDriver = drivers.find((d) => d.id === 'pgvector')\n if (pgvectorDriver?.recreateWithDimension) {\n await pgvectorDriver.recreateWithDimension(newDimension)\n // Query the actual dimension from the database to confirm\n if (pgvectorDriver.getTableDimension) {\n indexedDimension = await pgvectorDriver.getTableDimension()\n } else {\n indexedDimension = newDimension\n }\n searchDebug('search.embeddings.update', 'table recreated successfully', { indexedDimension })\n } else {\n searchDebugWarn('search.embeddings.update', 'pgvector driver does not have recreateWithDimension method')\n }\n } catch (error) {\n searchError('search.embeddings.update', 'failed to recreate table', {\n error: error instanceof Error ? error.message : error,\n })\n return NextResponse.json(\n { error: t('search.api.errors.recreateFailed', 'Failed to recreate vector table with new dimension.') },\n { status: 500 },\n )\n }\n }\n\n await saveEmbeddingConfig(container, change.newConfig)\n embeddingConfig = change.newConfig\n\n try {\n const embeddingService = container.resolve<EmbeddingService>('vectorEmbeddingService')\n embeddingService.updateConfig(embeddingConfig)\n } catch {\n // Embedding service may not be available in all contexts\n }\n\n reindexRequired = change.requiresReindex\n }\n\n const lockedByEnv = envDisablesAutoIndexing()\n let autoIndexingEnabled = !lockedByEnv\n if (!lockedByEnv) {\n try {\n autoIndexingEnabled = await resolveAutoIndexingEnabled(container, { defaultValue: true })\n } catch {\n autoIndexingEnabled = true\n }\n }\n\n // Get updated document count\n const updatedDocumentCount = auth.tenantId\n ? await getVectorDocumentCount(container, auth.tenantId, auth.orgId)\n : null\n\n return toJson({\n settings: {\n openaiConfigured: openAiConfigured(),\n autoIndexingEnabled: lockedByEnv ? false : autoIndexingEnabled,\n autoIndexingLocked: lockedByEnv,\n lockReason: lockedByEnv ? 'env' : null,\n embeddingConfig,\n configuredProviders: getConfiguredProviders(),\n indexedDimension,\n reindexRequired,\n documentCount: updatedDocumentCount,\n },\n })\n } catch (error) {\n searchError('search.embeddings.update', 'failed', {\n error: error instanceof Error ? error.message : error,\n })\n return NextResponse.json({ error: t('search.api.errors.updateFailed', 'Failed to update embedding settings.') }, { status: 500 })\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n}\n\nexport const openApi = embeddingsOpenApi\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { envDisablesAutoIndexing, resolveAutoIndexingEnabled, SEARCH_AUTO_INDEX_CONFIG_KEY } from '../../lib/auto-indexing'\nimport {\n resolveEmbeddingConfig,\n saveEmbeddingConfig,\n getConfiguredProviders,\n detectConfigChange,\n getEffectiveDimension,\n} from '../../lib/embedding-config'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { EmbeddingProviderConfig, EmbeddingProviderId, VectorDriver } from '../../../../vector'\nimport { EMBEDDING_PROVIDERS, DEFAULT_EMBEDDING_CONFIG, EmbeddingService } from '../../../../vector'\nimport { searchDebug, searchDebugWarn, searchError } from '../../../../lib/debug'\nimport { embeddingsOpenApi } from '../openapi'\n\nconst embeddingConfigSchema = z.object({\n providerId: z.enum(['openai', 'google', 'mistral', 'cohere', 'bedrock', 'ollama']),\n model: z.string(),\n dimension: z.number(),\n outputDimensionality: z.number().optional(),\n baseUrl: z.string().optional(),\n})\n\nconst updateSchema = z.object({\n autoIndexingEnabled: z.boolean().optional(),\n embeddingConfig: embeddingConfigSchema.optional(),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['search.embeddings.view'] },\n POST: { requireAuth: true, requireFeatures: ['search.embeddings.manage'] },\n}\n\ntype SettingsResponse = {\n settings: {\n openaiConfigured: boolean\n autoIndexingEnabled: boolean\n autoIndexingLocked: boolean\n lockReason: string | null\n embeddingConfig: EmbeddingProviderConfig | null\n configuredProviders: EmbeddingProviderId[]\n indexedDimension: number | null\n reindexRequired: boolean\n documentCount: number | null\n }\n}\n\nconst openAiConfigured = () => Boolean(process.env.OPENAI_API_KEY && process.env.OPENAI_API_KEY.trim().length > 0)\n\nconst toJson = (payload: SettingsResponse, init?: ResponseInit) => NextResponse.json(payload, init)\n\nconst unauthorized = async () => {\n const { t } = await resolveTranslations()\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n}\n\nconst configUnavailable = async () => {\n const { t } = await resolveTranslations()\n return NextResponse.json({ error: t('search.api.errors.configUnavailable', 'Configuration service unavailable') }, { status: 503 })\n}\n\nasync function getIndexedDimension(container: { resolve: <T = unknown>(name: string) => T }): Promise<number | null> {\n try {\n const drivers = container.resolve<VectorDriver[]>('vectorDrivers')\n const pgvectorDriver = drivers.find((d) => d.id === 'pgvector')\n if (pgvectorDriver?.getTableDimension) {\n return await pgvectorDriver.getTableDimension()\n }\n return null\n } catch {\n return null\n }\n}\n\nasync function getVectorDocumentCount(\n container: { resolve: <T = unknown>(name: string) => T },\n tenantId: string,\n organizationId?: string | null,\n): Promise<number | null> {\n try {\n const drivers = container.resolve<VectorDriver[]>('vectorDrivers')\n const pgvectorDriver = drivers.find((d) => d.id === 'pgvector')\n if (pgvectorDriver?.count) {\n return await pgvectorDriver.count({ tenantId, organizationId: organizationId ?? undefined })\n }\n return null\n } catch {\n return null\n }\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) return await unauthorized()\n\n const container = await createRequestContainer()\n try {\n const lockedByEnv = envDisablesAutoIndexing()\n let autoIndexingEnabled = !lockedByEnv\n if (!lockedByEnv) {\n try {\n autoIndexingEnabled = await resolveAutoIndexingEnabled(container, { defaultValue: true })\n } catch {\n autoIndexingEnabled = true\n }\n }\n\n const embeddingConfig = await resolveEmbeddingConfig(container, { defaultValue: null })\n const configuredProviders = getConfiguredProviders()\n const indexedDimension = await getIndexedDimension(container)\n\n const effectiveDimension = embeddingConfig\n ? getEffectiveDimension(embeddingConfig)\n : DEFAULT_EMBEDDING_CONFIG.dimension\n\n const reindexRequired = Boolean(\n indexedDimension &&\n embeddingConfig &&\n indexedDimension !== effectiveDimension\n )\n\n // Get document count for vector index\n const documentCount = auth.tenantId\n ? await getVectorDocumentCount(container, auth.tenantId, auth.orgId)\n : null\n\n return toJson({\n settings: {\n openaiConfigured: openAiConfigured(),\n autoIndexingEnabled: lockedByEnv ? false : autoIndexingEnabled,\n autoIndexingLocked: lockedByEnv,\n lockReason: lockedByEnv ? 'env' : null,\n embeddingConfig,\n configuredProviders,\n indexedDimension,\n reindexRequired,\n documentCount,\n },\n })\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n}\n\nexport async function POST(req: Request) {\n const { t } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) return await unauthorized()\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: t('api.errors.invalidJson', 'Invalid JSON payload.') }, { status: 400 })\n }\n const parsed = updateSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ error: t('api.errors.invalidPayload', 'Invalid payload.') }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n try {\n let service: ModuleConfigService\n try {\n service = (container.resolve('moduleConfigService') as ModuleConfigService)\n } catch {\n return await configUnavailable()\n }\n\n if (parsed.data.autoIndexingEnabled !== undefined) {\n if (envDisablesAutoIndexing()) {\n return NextResponse.json(\n {\n error: t(\n 'search.api.errors.autoIndexingDisabled',\n 'Auto-indexing is disabled via OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING (legacy alias: DISABLE_VECTOR_SEARCH_AUTOINDEXING).',\n ),\n },\n { status: 409 },\n )\n }\n await service.setValue('vector', SEARCH_AUTO_INDEX_CONFIG_KEY, parsed.data.autoIndexingEnabled)\n }\n\n let embeddingConfig = await resolveEmbeddingConfig(container, { defaultValue: null })\n let reindexRequired = false\n let indexedDimension = await getIndexedDimension(container)\n\n if (parsed.data.embeddingConfig) {\n const newConfig = parsed.data.embeddingConfig\n const providerInfo = EMBEDDING_PROVIDERS[newConfig.providerId]\n\n if (!providerInfo) {\n return NextResponse.json(\n { error: t('search.api.errors.invalidProvider', 'Invalid embedding provider.') },\n { status: 400 },\n )\n }\n\n const configuredProviders = getConfiguredProviders()\n if (!configuredProviders.includes(newConfig.providerId)) {\n return NextResponse.json(\n { error: t('search.api.errors.providerNotConfigured', `Provider ${providerInfo.name} is not configured. Set ${providerInfo.envKeyRequired} environment variable.`) },\n { status: 400 },\n )\n }\n\n const change = detectConfigChange(\n embeddingConfig,\n {\n ...newConfig,\n updatedAt: new Date().toISOString(),\n },\n indexedDimension\n )\n\n if (change.requiresReindex) {\n const newDimension = getEffectiveDimension(change.newConfig)\n searchDebug('search.embeddings.update', 'config change detected, recreating table', {\n requiresReindex: change.requiresReindex,\n reason: change.reason,\n oldDimension: indexedDimension,\n newDimension,\n })\n try {\n const drivers = container.resolve<VectorDriver[]>('vectorDrivers')\n const pgvectorDriver = drivers.find((d) => d.id === 'pgvector')\n if (pgvectorDriver?.recreateWithDimension) {\n await pgvectorDriver.recreateWithDimension(newDimension)\n // Query the actual dimension from the database to confirm\n if (pgvectorDriver.getTableDimension) {\n indexedDimension = await pgvectorDriver.getTableDimension()\n } else {\n indexedDimension = newDimension\n }\n searchDebug('search.embeddings.update', 'table recreated successfully', { indexedDimension })\n } else {\n searchDebugWarn('search.embeddings.update', 'pgvector driver does not have recreateWithDimension method')\n }\n } catch (error) {\n searchError('search.embeddings.update', 'failed to recreate table', {\n error: error instanceof Error ? error.message : error,\n })\n return NextResponse.json(\n { error: t('search.api.errors.recreateFailed', 'Failed to recreate vector table with new dimension.') },\n { status: 500 },\n )\n }\n }\n\n await saveEmbeddingConfig(container, change.newConfig)\n embeddingConfig = change.newConfig\n\n try {\n const embeddingService = container.resolve<EmbeddingService>('vectorEmbeddingService')\n embeddingService.updateConfig(embeddingConfig)\n } catch {\n // Embedding service may not be available in all contexts\n }\n\n reindexRequired = change.requiresReindex\n }\n\n const lockedByEnv = envDisablesAutoIndexing()\n let autoIndexingEnabled = !lockedByEnv\n if (!lockedByEnv) {\n try {\n autoIndexingEnabled = await resolveAutoIndexingEnabled(container, { defaultValue: true })\n } catch {\n autoIndexingEnabled = true\n }\n }\n\n // Get updated document count\n const updatedDocumentCount = auth.tenantId\n ? await getVectorDocumentCount(container, auth.tenantId, auth.orgId)\n : null\n\n return toJson({\n settings: {\n openaiConfigured: openAiConfigured(),\n autoIndexingEnabled: lockedByEnv ? false : autoIndexingEnabled,\n autoIndexingLocked: lockedByEnv,\n lockReason: lockedByEnv ? 'env' : null,\n embeddingConfig,\n configuredProviders: getConfiguredProviders(),\n indexedDimension,\n reindexRequired,\n documentCount: updatedDocumentCount,\n },\n })\n } catch (error) {\n searchError('search.embeddings.update', 'failed', {\n error: error instanceof Error ? error.message : error,\n })\n return NextResponse.json({ error: t('search.api.errors.updateFailed', 'Failed to update embedding settings.') }, { status: 500 })\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n}\n\nexport const openApi = embeddingsOpenApi\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "search.api.errors.autoIndexingDisabled": "Automatische Indizierung ist deaktiviert.",
2
+ "search.api.errors.autoIndexingDisabled": "Die Auto-Indizierung ist über OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING deaktiviert (Legacy-Alias: DISABLE_VECTOR_SEARCH_AUTOINDEXING).",
3
3
  "search.api.errors.configUnavailable": "Konfigurationsdienst nicht verfügbar.",
4
4
  "search.api.errors.confirmAllRequired": "Bestätigung ist für alle Einträge erforderlich.",
5
5
  "search.api.errors.indexFetchFailed": "Indexdaten konnten nicht abgerufen werden.",
@@ -1,5 +1,5 @@
1
1
  {
2
- "search.api.errors.autoIndexingDisabled": "Auto-indexing is disabled.",
2
+ "search.api.errors.autoIndexingDisabled": "Auto-indexing is disabled via OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING (legacy alias: DISABLE_VECTOR_SEARCH_AUTOINDEXING).",
3
3
  "search.api.errors.configUnavailable": "Configuration service unavailable.",
4
4
  "search.api.errors.confirmAllRequired": "Confirmation is required for all items.",
5
5
  "search.api.errors.indexFetchFailed": "Failed to fetch index data.",
@@ -1,5 +1,5 @@
1
1
  {
2
- "search.api.errors.autoIndexingDisabled": "La indexación automática está deshabilitada.",
2
+ "search.api.errors.autoIndexingDisabled": "La indexación automática está deshabilitada mediante OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING (alias heredado: DISABLE_VECTOR_SEARCH_AUTOINDEXING).",
3
3
  "search.api.errors.configUnavailable": "Servicio de configuración no disponible.",
4
4
  "search.api.errors.confirmAllRequired": "Se requiere confirmación para todos los elementos.",
5
5
  "search.api.errors.indexFetchFailed": "Error al obtener los datos del índice.",
@@ -1,5 +1,5 @@
1
1
  {
2
- "search.api.errors.autoIndexingDisabled": "Automatyczne indeksowanie jest wyłączone.",
2
+ "search.api.errors.autoIndexingDisabled": "Automatyczne indeksowanie jest wyłączone przez OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING (stary alias: DISABLE_VECTOR_SEARCH_AUTOINDEXING).",
3
3
  "search.api.errors.configUnavailable": "Usługa konfiguracji jest niedostępna.",
4
4
  "search.api.errors.confirmAllRequired": "Wymagane jest potwierdzenie dla wszystkich elementów.",
5
5
  "search.api.errors.indexFetchFailed": "Nie udało się pobrać danych indeksu.",
@@ -1,7 +1,7 @@
1
1
  import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
2
2
  const SEARCH_AUTO_INDEX_CONFIG_KEY = "auto_index_enabled";
3
3
  function envDisablesAutoIndexing() {
4
- const raw = process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING;
4
+ const raw = process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING ?? process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING;
5
5
  if (!raw) return false;
6
6
  return parseBooleanToken(raw) === true;
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/search/lib/auto-indexing.ts"],
4
- "sourcesContent": ["import type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\nexport const SEARCH_AUTO_INDEX_CONFIG_KEY = 'auto_index_enabled'\n\nexport function envDisablesAutoIndexing(): boolean {\n const raw = process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING\n if (!raw) return false\n return parseBooleanToken(raw) === true\n}\n\ntype Resolver = {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport async function resolveAutoIndexingEnabled(\n resolver: Resolver,\n options?: { defaultValue?: boolean },\n): Promise<boolean> {\n if (envDisablesAutoIndexing()) return false\n const fallback = options?.defaultValue ?? true\n let service: ModuleConfigService\n try {\n service = resolver.resolve<ModuleConfigService>('moduleConfigService')\n } catch {\n return fallback\n }\n try {\n // Still use 'vector' module key for backwards compatibility\n const value = await service.getValue<boolean>('vector', SEARCH_AUTO_INDEX_CONFIG_KEY, { defaultValue: fallback })\n return value !== false\n } catch {\n return fallback\n }\n}\n"],
5
- "mappings": "AACA,SAAS,yBAAyB;AAE3B,MAAM,+BAA+B;AAErC,SAAS,0BAAmC;AACjD,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,kBAAkB,GAAG,MAAM;AACpC;AAMA,eAAsB,2BACpB,UACA,SACkB;AAClB,MAAI,wBAAwB,EAAG,QAAO;AACtC,QAAM,WAAW,SAAS,gBAAgB;AAC1C,MAAI;AACJ,MAAI;AACF,cAAU,SAAS,QAA6B,qBAAqB;AAAA,EACvE,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AAEF,UAAM,QAAQ,MAAM,QAAQ,SAAkB,UAAU,8BAA8B,EAAE,cAAc,SAAS,CAAC;AAChH,WAAO,UAAU;AAAA,EACnB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
4
+ "sourcesContent": ["import type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\nexport const SEARCH_AUTO_INDEX_CONFIG_KEY = 'auto_index_enabled'\n\nexport function envDisablesAutoIndexing(): boolean {\n const raw =\n process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING ??\n process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING\n if (!raw) return false\n return parseBooleanToken(raw) === true\n}\n\ntype Resolver = {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport async function resolveAutoIndexingEnabled(\n resolver: Resolver,\n options?: { defaultValue?: boolean },\n): Promise<boolean> {\n if (envDisablesAutoIndexing()) return false\n const fallback = options?.defaultValue ?? true\n let service: ModuleConfigService\n try {\n service = resolver.resolve<ModuleConfigService>('moduleConfigService')\n } catch {\n return fallback\n }\n try {\n // Still use 'vector' module key for backwards compatibility\n const value = await service.getValue<boolean>('vector', SEARCH_AUTO_INDEX_CONFIG_KEY, { defaultValue: fallback })\n return value !== false\n } catch {\n return fallback\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,yBAAyB;AAE3B,MAAM,+BAA+B;AAErC,SAAS,0BAAmC;AACjD,QAAM,MACJ,QAAQ,IAAI,yCACZ,QAAQ,IAAI;AACd,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,kBAAkB,GAAG,MAAM;AACpC;AAMA,eAAsB,2BACpB,UACA,SACkB;AAClB,MAAI,wBAAwB,EAAG,QAAO;AACtC,QAAM,WAAW,SAAS,gBAAgB;AAC1C,MAAI;AACJ,MAAI;AACF,cAAU,SAAS,QAA6B,qBAAqB;AAAA,EACvE,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AAEF,UAAM,QAAQ,MAAM,QAAQ,SAAkB,UAAU,8BAA8B,EAAE,cAAc,SAAS,CAAC;AAChH,WAAO,UAAU;AAAA,EACnB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/search",
3
- "version": "0.5.1-develop.2709.b6bdd776ac",
3
+ "version": "0.5.1-develop.2744.9c8be0dd93",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -126,9 +126,9 @@
126
126
  "zod": "^4.3.6"
127
127
  },
128
128
  "peerDependencies": {
129
- "@open-mercato/core": "0.5.1-develop.2709.b6bdd776ac",
130
- "@open-mercato/queue": "0.5.1-develop.2709.b6bdd776ac",
131
- "@open-mercato/shared": "0.5.1-develop.2709.b6bdd776ac"
129
+ "@open-mercato/core": "0.5.1-develop.2744.9c8be0dd93",
130
+ "@open-mercato/queue": "0.5.1-develop.2744.9c8be0dd93",
131
+ "@open-mercato/shared": "0.5.1-develop.2744.9c8be0dd93"
132
132
  },
133
133
  "devDependencies": {
134
134
  "@types/jest": "^30.0.0",
@@ -177,7 +177,12 @@ export async function POST(req: Request) {
177
177
  if (parsed.data.autoIndexingEnabled !== undefined) {
178
178
  if (envDisablesAutoIndexing()) {
179
179
  return NextResponse.json(
180
- { error: t('search.api.errors.autoIndexingDisabled', 'Auto-indexing is disabled via DISABLE_VECTOR_SEARCH_AUTOINDEXING.') },
180
+ {
181
+ error: t(
182
+ 'search.api.errors.autoIndexingDisabled',
183
+ 'Auto-indexing is disabled via OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING (legacy alias: DISABLE_VECTOR_SEARCH_AUTOINDEXING).',
184
+ ),
185
+ },
181
186
  { status: 409 },
182
187
  )
183
188
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "search.api.errors.autoIndexingDisabled": "Automatische Indizierung ist deaktiviert.",
2
+ "search.api.errors.autoIndexingDisabled": "Die Auto-Indizierung ist über OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING deaktiviert (Legacy-Alias: DISABLE_VECTOR_SEARCH_AUTOINDEXING).",
3
3
  "search.api.errors.configUnavailable": "Konfigurationsdienst nicht verfügbar.",
4
4
  "search.api.errors.confirmAllRequired": "Bestätigung ist für alle Einträge erforderlich.",
5
5
  "search.api.errors.indexFetchFailed": "Indexdaten konnten nicht abgerufen werden.",
@@ -1,5 +1,5 @@
1
1
  {
2
- "search.api.errors.autoIndexingDisabled": "Auto-indexing is disabled.",
2
+ "search.api.errors.autoIndexingDisabled": "Auto-indexing is disabled via OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING (legacy alias: DISABLE_VECTOR_SEARCH_AUTOINDEXING).",
3
3
  "search.api.errors.configUnavailable": "Configuration service unavailable.",
4
4
  "search.api.errors.confirmAllRequired": "Confirmation is required for all items.",
5
5
  "search.api.errors.indexFetchFailed": "Failed to fetch index data.",
@@ -1,5 +1,5 @@
1
1
  {
2
- "search.api.errors.autoIndexingDisabled": "La indexación automática está deshabilitada.",
2
+ "search.api.errors.autoIndexingDisabled": "La indexación automática está deshabilitada mediante OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING (alias heredado: DISABLE_VECTOR_SEARCH_AUTOINDEXING).",
3
3
  "search.api.errors.configUnavailable": "Servicio de configuración no disponible.",
4
4
  "search.api.errors.confirmAllRequired": "Se requiere confirmación para todos los elementos.",
5
5
  "search.api.errors.indexFetchFailed": "Error al obtener los datos del índice.",
@@ -1,5 +1,5 @@
1
1
  {
2
- "search.api.errors.autoIndexingDisabled": "Automatyczne indeksowanie jest wyłączone.",
2
+ "search.api.errors.autoIndexingDisabled": "Automatyczne indeksowanie jest wyłączone przez OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING (stary alias: DISABLE_VECTOR_SEARCH_AUTOINDEXING).",
3
3
  "search.api.errors.configUnavailable": "Usługa konfiguracji jest niedostępna.",
4
4
  "search.api.errors.confirmAllRequired": "Wymagane jest potwierdzenie dla wszystkich elementów.",
5
5
  "search.api.errors.indexFetchFailed": "Nie udało się pobrać danych indeksu.",
@@ -0,0 +1,44 @@
1
+ import { envDisablesAutoIndexing, resolveAutoIndexingEnabled } from '../auto-indexing'
2
+
3
+ describe('search auto-indexing env overrides', () => {
4
+ const originalEnv = process.env
5
+
6
+ beforeEach(() => {
7
+ process.env = { ...originalEnv }
8
+ delete process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING
9
+ delete process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING
10
+ })
11
+
12
+ afterAll(() => {
13
+ process.env = originalEnv
14
+ })
15
+
16
+ it('uses the OM_ env name to disable auto-indexing', () => {
17
+ process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING = 'true'
18
+
19
+ expect(envDisablesAutoIndexing()).toBe(true)
20
+ })
21
+
22
+ it('keeps the legacy env alias working', () => {
23
+ process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING = '1'
24
+
25
+ expect(envDisablesAutoIndexing()).toBe(true)
26
+ })
27
+
28
+ it('prefers the OM_ env name when both aliases are set', () => {
29
+ process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING = 'false'
30
+ process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING = '1'
31
+
32
+ expect(envDisablesAutoIndexing()).toBe(false)
33
+ })
34
+
35
+ it('forces auto-indexing off before module config is consulted', async () => {
36
+ process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING = 'true'
37
+ const resolve = jest.fn().mockReturnValue({
38
+ getValue: jest.fn().mockResolvedValue(true),
39
+ })
40
+
41
+ await expect(resolveAutoIndexingEnabled({ resolve })).resolves.toBe(false)
42
+ expect(resolve).not.toHaveBeenCalled()
43
+ })
44
+ })
@@ -4,7 +4,9 @@ import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
4
4
  export const SEARCH_AUTO_INDEX_CONFIG_KEY = 'auto_index_enabled'
5
5
 
6
6
  export function envDisablesAutoIndexing(): boolean {
7
- const raw = process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING
7
+ const raw =
8
+ process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING ??
9
+ process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING
8
10
  if (!raw) return false
9
11
  return parseBooleanToken(raw) === true
10
12
  }