@open-mercato/search 0.4.2-canary-c02407ff85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +678 -0
- package/build.mjs +92 -0
- package/dist/di.js +157 -0
- package/dist/di.js.map +7 -0
- package/dist/fulltext/drivers/index.js +21 -0
- package/dist/fulltext/drivers/index.js.map +7 -0
- package/dist/fulltext/drivers/meilisearch/index.js +320 -0
- package/dist/fulltext/drivers/meilisearch/index.js.map +7 -0
- package/dist/fulltext/index.js +7 -0
- package/dist/fulltext/index.js.map +7 -0
- package/dist/fulltext/types.js +1 -0
- package/dist/fulltext/types.js.map +7 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +7 -0
- package/dist/indexer/index.js +8 -0
- package/dist/indexer/index.js.map +7 -0
- package/dist/indexer/search-indexer.js +848 -0
- package/dist/indexer/search-indexer.js.map +7 -0
- package/dist/indexer/subscribers/delete.js +41 -0
- package/dist/indexer/subscribers/delete.js.map +7 -0
- package/dist/lib/debug.js +34 -0
- package/dist/lib/debug.js.map +7 -0
- package/dist/lib/fallback-presenter.js +107 -0
- package/dist/lib/fallback-presenter.js.map +7 -0
- package/dist/lib/field-policy.js +75 -0
- package/dist/lib/field-policy.js.map +7 -0
- package/dist/lib/index.js +19 -0
- package/dist/lib/index.js.map +7 -0
- package/dist/lib/merger.js +93 -0
- package/dist/lib/merger.js.map +7 -0
- package/dist/lib/presenter-enricher.js +192 -0
- package/dist/lib/presenter-enricher.js.map +7 -0
- package/dist/modules/search/acl.js +14 -0
- package/dist/modules/search/acl.js.map +7 -0
- package/dist/modules/search/ai-tools.js +284 -0
- package/dist/modules/search/ai-tools.js.map +7 -0
- package/dist/modules/search/api/embeddings/reindex/cancel/route.js +65 -0
- package/dist/modules/search/api/embeddings/reindex/cancel/route.js.map +7 -0
- package/dist/modules/search/api/embeddings/reindex/route.js +165 -0
- package/dist/modules/search/api/embeddings/reindex/route.js.map +7 -0
- package/dist/modules/search/api/embeddings/route.js +246 -0
- package/dist/modules/search/api/embeddings/route.js.map +7 -0
- package/dist/modules/search/api/index/route.js +245 -0
- package/dist/modules/search/api/index/route.js.map +7 -0
- package/dist/modules/search/api/reindex/cancel/route.js +65 -0
- package/dist/modules/search/api/reindex/cancel/route.js.map +7 -0
- package/dist/modules/search/api/reindex/route.js +332 -0
- package/dist/modules/search/api/reindex/route.js.map +7 -0
- package/dist/modules/search/api/search/global/route.js +100 -0
- package/dist/modules/search/api/search/global/route.js.map +7 -0
- package/dist/modules/search/api/search/route.js +101 -0
- package/dist/modules/search/api/search/route.js.map +7 -0
- package/dist/modules/search/api/settings/fulltext/route.js +55 -0
- package/dist/modules/search/api/settings/fulltext/route.js.map +7 -0
- package/dist/modules/search/api/settings/global-search/route.js +80 -0
- package/dist/modules/search/api/settings/global-search/route.js.map +7 -0
- package/dist/modules/search/api/settings/route.js +118 -0
- package/dist/modules/search/api/settings/route.js.map +7 -0
- package/dist/modules/search/api/settings/vector-store/route.js +77 -0
- package/dist/modules/search/api/settings/vector-store/route.js.map +7 -0
- package/dist/modules/search/backend/config/search/page.js +10 -0
- package/dist/modules/search/backend/config/search/page.js.map +7 -0
- package/dist/modules/search/backend/config/search/page.meta.js +24 -0
- package/dist/modules/search/backend/config/search/page.meta.js.map +7 -0
- package/dist/modules/search/cli.js +698 -0
- package/dist/modules/search/cli.js.map +7 -0
- package/dist/modules/search/di.js +32 -0
- package/dist/modules/search/di.js.map +7 -0
- package/dist/modules/search/frontend/components/GlobalSearchDialog.js +357 -0
- package/dist/modules/search/frontend/components/GlobalSearchDialog.js.map +7 -0
- package/dist/modules/search/frontend/components/HybridSearchTable.js +343 -0
- package/dist/modules/search/frontend/components/HybridSearchTable.js.map +7 -0
- package/dist/modules/search/frontend/components/SearchSettingsPageClient.js +303 -0
- package/dist/modules/search/frontend/components/SearchSettingsPageClient.js.map +7 -0
- package/dist/modules/search/frontend/components/sections/FulltextSearchSection.js +360 -0
- package/dist/modules/search/frontend/components/sections/FulltextSearchSection.js.map +7 -0
- package/dist/modules/search/frontend/components/sections/GlobalSearchSection.js +101 -0
- package/dist/modules/search/frontend/components/sections/GlobalSearchSection.js.map +7 -0
- package/dist/modules/search/frontend/components/sections/VectorSearchSection.js +608 -0
- package/dist/modules/search/frontend/components/sections/VectorSearchSection.js.map +7 -0
- package/dist/modules/search/frontend/index.js +9 -0
- package/dist/modules/search/frontend/index.js.map +7 -0
- package/dist/modules/search/frontend/utils.js +41 -0
- package/dist/modules/search/frontend/utils.js.map +7 -0
- package/dist/modules/search/i18n/de.json +61 -0
- package/dist/modules/search/i18n/en.json +72 -0
- package/dist/modules/search/i18n/es.json +61 -0
- package/dist/modules/search/i18n/pl.json +61 -0
- package/dist/modules/search/index.js +11 -0
- package/dist/modules/search/index.js.map +7 -0
- package/dist/modules/search/lib/auto-indexing.js +29 -0
- package/dist/modules/search/lib/auto-indexing.js.map +7 -0
- package/dist/modules/search/lib/embedding-config.js +131 -0
- package/dist/modules/search/lib/embedding-config.js.map +7 -0
- package/dist/modules/search/lib/global-search-config.js +45 -0
- package/dist/modules/search/lib/global-search-config.js.map +7 -0
- package/dist/modules/search/lib/reindex-lock.js +99 -0
- package/dist/modules/search/lib/reindex-lock.js.map +7 -0
- package/dist/modules/search/subscribers/fulltext_upsert.js +64 -0
- package/dist/modules/search/subscribers/fulltext_upsert.js.map +7 -0
- package/dist/modules/search/subscribers/vector_delete.js +58 -0
- package/dist/modules/search/subscribers/vector_delete.js.map +7 -0
- package/dist/modules/search/subscribers/vector_purge.js +142 -0
- package/dist/modules/search/subscribers/vector_purge.js.map +7 -0
- package/dist/modules/search/subscribers/vector_upsert.js +58 -0
- package/dist/modules/search/subscribers/vector_upsert.js.map +7 -0
- package/dist/modules/search/workers/fulltext-index.worker.js +240 -0
- package/dist/modules/search/workers/fulltext-index.worker.js.map +7 -0
- package/dist/modules/search/workers/vector-index.worker.js +234 -0
- package/dist/modules/search/workers/vector-index.worker.js.map +7 -0
- package/dist/queue/fulltext-indexing.js +15 -0
- package/dist/queue/fulltext-indexing.js.map +7 -0
- package/dist/queue/index.js +3 -0
- package/dist/queue/index.js.map +7 -0
- package/dist/queue/vector-indexing.js +15 -0
- package/dist/queue/vector-indexing.js.map +7 -0
- package/dist/service.js +286 -0
- package/dist/service.js.map +7 -0
- package/dist/strategies/fulltext.strategy.js +116 -0
- package/dist/strategies/fulltext.strategy.js.map +7 -0
- package/dist/strategies/index.js +12 -0
- package/dist/strategies/index.js.map +7 -0
- package/dist/strategies/token.strategy.js +80 -0
- package/dist/strategies/token.strategy.js.map +7 -0
- package/dist/strategies/vector.strategy.js +137 -0
- package/dist/strategies/vector.strategy.js.map +7 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +7 -0
- package/dist/vector/drivers/chromadb/index.js +44 -0
- package/dist/vector/drivers/chromadb/index.js.map +7 -0
- package/dist/vector/drivers/index.js +9 -0
- package/dist/vector/drivers/index.js.map +7 -0
- package/dist/vector/drivers/pgvector/index.js +509 -0
- package/dist/vector/drivers/pgvector/index.js.map +7 -0
- package/dist/vector/drivers/qdrant/index.js +44 -0
- package/dist/vector/drivers/qdrant/index.js.map +7 -0
- package/dist/vector/index.js +4 -0
- package/dist/vector/index.js.map +7 -0
- package/dist/vector/lib/vector-logs.js +33 -0
- package/dist/vector/lib/vector-logs.js.map +7 -0
- package/dist/vector/services/checksum.js +20 -0
- package/dist/vector/services/checksum.js.map +7 -0
- package/dist/vector/services/embedding.js +222 -0
- package/dist/vector/services/embedding.js.map +7 -0
- package/dist/vector/services/index.js +4 -0
- package/dist/vector/services/index.js.map +7 -0
- package/dist/vector/services/vector-index.service.js +960 -0
- package/dist/vector/services/vector-index.service.js.map +7 -0
- package/dist/vector/types/pg.d.js +1 -0
- package/dist/vector/types/pg.d.js.map +7 -0
- package/dist/vector/types.js +75 -0
- package/dist/vector/types.js.map +7 -0
- package/jest.config.cjs +19 -0
- package/package.json +142 -0
- package/src/__tests__/queue.test.ts +148 -0
- package/src/__tests__/service.test.ts +345 -0
- package/src/__tests__/workers.test.ts +319 -0
- package/src/di.ts +291 -0
- package/src/fulltext/drivers/index.ts +41 -0
- package/src/fulltext/drivers/meilisearch/index.ts +410 -0
- package/src/fulltext/index.ts +13 -0
- package/src/fulltext/types.ts +115 -0
- package/src/index.ts +36 -0
- package/src/indexer/index.ts +13 -0
- package/src/indexer/search-indexer.ts +1141 -0
- package/src/indexer/subscribers/delete.ts +49 -0
- package/src/lib/debug.ts +46 -0
- package/src/lib/fallback-presenter.ts +106 -0
- package/src/lib/field-policy.ts +169 -0
- package/src/lib/index.ts +13 -0
- package/src/lib/merger.ts +159 -0
- package/src/lib/presenter-enricher.ts +323 -0
- package/src/modules/search/README.md +694 -0
- package/src/modules/search/acl.ts +10 -0
- package/src/modules/search/ai-tools.ts +467 -0
- package/src/modules/search/api/embeddings/reindex/cancel/route.ts +77 -0
- package/src/modules/search/api/embeddings/reindex/route.ts +197 -0
- package/src/modules/search/api/embeddings/route.ts +304 -0
- package/src/modules/search/api/index/route.ts +297 -0
- package/src/modules/search/api/reindex/cancel/route.ts +77 -0
- package/src/modules/search/api/reindex/route.ts +419 -0
- package/src/modules/search/api/search/global/route.ts +120 -0
- package/src/modules/search/api/search/route.ts +121 -0
- package/src/modules/search/api/settings/fulltext/route.ts +82 -0
- package/src/modules/search/api/settings/global-search/route.ts +91 -0
- package/src/modules/search/api/settings/route.ts +187 -0
- package/src/modules/search/api/settings/vector-store/route.ts +105 -0
- package/src/modules/search/backend/config/search/page.meta.ts +22 -0
- package/src/modules/search/backend/config/search/page.tsx +12 -0
- package/src/modules/search/cli.ts +818 -0
- package/src/modules/search/di.ts +50 -0
- package/src/modules/search/frontend/components/GlobalSearchDialog.tsx +436 -0
- package/src/modules/search/frontend/components/HybridSearchTable.tsx +418 -0
- package/src/modules/search/frontend/components/SearchSettingsPageClient.tsx +476 -0
- package/src/modules/search/frontend/components/sections/FulltextSearchSection.tsx +624 -0
- package/src/modules/search/frontend/components/sections/GlobalSearchSection.tsx +124 -0
- package/src/modules/search/frontend/components/sections/VectorSearchSection.tsx +943 -0
- package/src/modules/search/frontend/index.ts +3 -0
- package/src/modules/search/frontend/utils.ts +82 -0
- package/src/modules/search/i18n/de.json +61 -0
- package/src/modules/search/i18n/en.json +72 -0
- package/src/modules/search/i18n/es.json +61 -0
- package/src/modules/search/i18n/pl.json +61 -0
- package/src/modules/search/index.ts +9 -0
- package/src/modules/search/lib/auto-indexing.ts +35 -0
- package/src/modules/search/lib/embedding-config.ts +161 -0
- package/src/modules/search/lib/global-search-config.ts +69 -0
- package/src/modules/search/lib/reindex-lock.ts +201 -0
- package/src/modules/search/subscribers/fulltext_upsert.ts +83 -0
- package/src/modules/search/subscribers/vector_delete.ts +75 -0
- package/src/modules/search/subscribers/vector_purge.ts +161 -0
- package/src/modules/search/subscribers/vector_upsert.ts +75 -0
- package/src/modules/search/workers/fulltext-index.worker.ts +318 -0
- package/src/modules/search/workers/vector-index.worker.ts +292 -0
- package/src/queue/fulltext-indexing.ts +87 -0
- package/src/queue/index.ts +2 -0
- package/src/queue/vector-indexing.ts +66 -0
- package/src/service.ts +397 -0
- package/src/strategies/fulltext.strategy.ts +155 -0
- package/src/strategies/index.ts +17 -0
- package/src/strategies/token.strategy.ts +153 -0
- package/src/strategies/vector.strategy.ts +234 -0
- package/src/types.ts +38 -0
- package/src/vector/drivers/chromadb/index.ts +49 -0
- package/src/vector/drivers/index.ts +4 -0
- package/src/vector/drivers/pgvector/index.ts +627 -0
- package/src/vector/drivers/qdrant/index.ts +49 -0
- package/src/vector/index.ts +3 -0
- package/src/vector/lib/vector-logs.ts +46 -0
- package/src/vector/services/checksum.ts +18 -0
- package/src/vector/services/embedding.ts +275 -0
- package/src/vector/services/index.ts +3 -0
- package/src/vector/services/vector-index.service.ts +1234 -0
- package/src/vector/types/pg.d.ts +1 -0
- package/src/vector/types.ts +220 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/search/api/embeddings/reindex/cancel/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport type { Queue } from '@open-mercato/queue'\nimport type { Knex } from 'knex'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { clearReindexLock } from '../../../../lib/reindex-lock'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['search.embeddings.manage'] },\n}\n\nexport async function POST(req: Request) {\n const { t } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const knex = (em.getConnection() as unknown as { getKnex: () => Knex }).getKnex()\n\n let queue: Queue | undefined\n try {\n queue = container.resolve<Queue>('vectorIndexQueue')\n } catch {\n // Queue not available - just clear the lock\n }\n\n let jobsRemoved = 0\n if (queue) {\n try {\n const countsBefore = await queue.getJobCounts()\n jobsRemoved = countsBefore.waiting + countsBefore.active\n await queue.clear()\n } catch {\n // Queue clear failed - continue to clear lock\n }\n }\n\n await clearReindexLock(knex, auth.tenantId, 'vector', auth.orgId ?? null)\n\n // Log the cancellation\n try {\n const em = container.resolve('em')\n await recordIndexerLog(\n { em },\n {\n source: 'vector',\n handler: 'api:search.embeddings.reindex.cancel',\n message: `Cancelled vector reindex operation (${jobsRemoved} jobs removed)`,\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n details: { jobsRemoved },\n },\n )\n } catch {\n // Logging failure should not fail the cancel operation\n }\n\n try {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n } catch {\n // Ignore disposal errors\n }\n\n return NextResponse.json({\n ok: true,\n jobsRemoved,\n })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AAInC,SAAS,wBAAwB;AACjC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAE1B,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,0BAA0B,EAAE;AAC3E;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,UAAU;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,EAAE,2BAA2B,cAAc,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnG;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,QAAM,OAAQ,GAAG,cAAc,EAAyC,QAAQ;AAEhF,MAAI;AACJ,MAAI;AACF,YAAQ,UAAU,QAAe,kBAAkB;AAAA,EACrD,QAAQ;AAAA,EAER;AAEA,MAAI,cAAc;AAClB,MAAI,OAAO;AACT,QAAI;AACF,YAAM,eAAe,MAAM,MAAM,aAAa;AAC9C,oBAAc,aAAa,UAAU,aAAa;AAClD,YAAM,MAAM,MAAM;AAAA,IACpB,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,iBAAiB,MAAM,KAAK,UAAU,UAAU,KAAK,SAAS,IAAI;AAGxE,MAAI;AACF,UAAMA,MAAK,UAAU,QAAQ,IAAI;AACjC,UAAM;AAAA,MACJ,EAAE,IAAAA,IAAG;AAAA,MACL;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,SAAS,uCAAuC,WAAW;AAAA,QAC3D,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK,SAAS;AAAA,QAC9B,SAAS,EAAE,YAAY;AAAA,MACzB;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,MAAI;AACF,UAAM,aAAa;AACnB,QAAI,OAAO,WAAW,YAAY,YAAY;AAC5C,YAAM,WAAW,QAAQ;AAAA,IAC3B;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO,aAAa,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ;AAAA,EACF,CAAC;AACH;",
|
|
6
|
+
"names": ["em"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { recordIndexerLog } from "@open-mercato/shared/lib/indexers/status-log";
|
|
5
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
6
|
+
import { resolveEmbeddingConfig } from "../../../lib/embedding-config.js";
|
|
7
|
+
import { searchDebug, searchDebugWarn, searchError } from "../../../../../lib/debug.js";
|
|
8
|
+
import { acquireReindexLock, getReindexLockStatus } from "../../../lib/reindex-lock.js";
|
|
9
|
+
const metadata = {
|
|
10
|
+
POST: { requireAuth: true, requireFeatures: ["search.embeddings.manage"] }
|
|
11
|
+
};
|
|
12
|
+
async function POST(req) {
|
|
13
|
+
const { t } = await resolveTranslations();
|
|
14
|
+
const auth = await getAuthFromRequest(req);
|
|
15
|
+
if (!auth?.tenantId) {
|
|
16
|
+
return NextResponse.json({ error: t("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
17
|
+
}
|
|
18
|
+
let payload = {};
|
|
19
|
+
try {
|
|
20
|
+
payload = await req.json();
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
const entityId = typeof payload?.entityId === "string" ? payload.entityId : void 0;
|
|
24
|
+
const purgeFirst = payload?.purgeFirst === true;
|
|
25
|
+
const container = await createRequestContainer();
|
|
26
|
+
const em = container.resolve("em");
|
|
27
|
+
const knex = em.getConnection().getKnex();
|
|
28
|
+
const existingLock = await getReindexLockStatus(knex, auth.tenantId, { type: "vector" });
|
|
29
|
+
if (existingLock) {
|
|
30
|
+
const startedAt = new Date(existingLock.startedAt);
|
|
31
|
+
return NextResponse.json(
|
|
32
|
+
{
|
|
33
|
+
error: t("search.api.errors.reindexInProgress", "A reindex operation is already in progress"),
|
|
34
|
+
lock: {
|
|
35
|
+
type: existingLock.type,
|
|
36
|
+
action: existingLock.action,
|
|
37
|
+
startedAt: existingLock.startedAt,
|
|
38
|
+
elapsedMinutes: Math.round((Date.now() - startedAt.getTime()) / 6e4),
|
|
39
|
+
processedCount: existingLock.processedCount,
|
|
40
|
+
totalCount: existingLock.totalCount
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{ status: 409 }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
const { acquired: lockAcquired } = await acquireReindexLock(knex, {
|
|
47
|
+
type: "vector",
|
|
48
|
+
action: entityId ? `reindex:${entityId}` : "reindex:all",
|
|
49
|
+
tenantId: auth.tenantId,
|
|
50
|
+
organizationId: auth.orgId ?? null
|
|
51
|
+
});
|
|
52
|
+
if (!lockAcquired) {
|
|
53
|
+
return NextResponse.json(
|
|
54
|
+
{ error: t("search.api.errors.lockFailed", "Failed to acquire reindex lock") },
|
|
55
|
+
{ status: 409 }
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
let em2 = null;
|
|
60
|
+
try {
|
|
61
|
+
em2 = container.resolve("em");
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
let searchIndexer;
|
|
65
|
+
try {
|
|
66
|
+
searchIndexer = container.resolve("searchIndexer");
|
|
67
|
+
} catch {
|
|
68
|
+
return NextResponse.json(
|
|
69
|
+
{ error: t("search.api.errors.indexUnavailable", "Search indexer unavailable") },
|
|
70
|
+
{ status: 503 }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const embeddingConfig = await resolveEmbeddingConfig(container, { defaultValue: null });
|
|
75
|
+
if (embeddingConfig) {
|
|
76
|
+
const embeddingService = container.resolve("vectorEmbeddingService");
|
|
77
|
+
embeddingService.updateConfig(embeddingConfig);
|
|
78
|
+
searchDebug("search.embeddings.reindex", "using embedding config", {
|
|
79
|
+
providerId: embeddingConfig.providerId,
|
|
80
|
+
model: embeddingConfig.model,
|
|
81
|
+
dimension: embeddingConfig.dimension
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
} catch (err) {
|
|
85
|
+
searchDebugWarn("search.embeddings.reindex", "failed to load embedding config, using defaults", {
|
|
86
|
+
error: err instanceof Error ? err.message : err
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
await recordIndexerLog(
|
|
90
|
+
{ em: em2 ?? void 0 },
|
|
91
|
+
{
|
|
92
|
+
source: "vector",
|
|
93
|
+
handler: "api:search.embeddings.reindex",
|
|
94
|
+
message: entityId ? `Vector reindex requested for ${entityId}` : "Vector reindex requested for all entities",
|
|
95
|
+
entityType: entityId ?? null,
|
|
96
|
+
tenantId: auth.tenantId ?? null,
|
|
97
|
+
organizationId: auth.orgId ?? null,
|
|
98
|
+
details: { purgeFirst }
|
|
99
|
+
}
|
|
100
|
+
).catch(() => void 0);
|
|
101
|
+
let result;
|
|
102
|
+
if (entityId) {
|
|
103
|
+
result = await searchIndexer.reindexEntityToVector({
|
|
104
|
+
entityId,
|
|
105
|
+
tenantId: auth.tenantId,
|
|
106
|
+
organizationId: auth.orgId ?? null,
|
|
107
|
+
purgeFirst,
|
|
108
|
+
useQueue: true
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
result = await searchIndexer.reindexAllToVector({
|
|
112
|
+
tenantId: auth.tenantId,
|
|
113
|
+
organizationId: auth.orgId ?? null,
|
|
114
|
+
purgeFirst,
|
|
115
|
+
useQueue: true
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
await recordIndexerLog(
|
|
119
|
+
{ em: em2 ?? void 0 },
|
|
120
|
+
{
|
|
121
|
+
source: "vector",
|
|
122
|
+
handler: "api:search.embeddings.reindex",
|
|
123
|
+
message: result.jobsEnqueued ? `Vector reindex enqueued ${result.jobsEnqueued} jobs for ${entityId ?? "all entities"}` : `Vector reindex completed for ${entityId ?? "all entities"}`,
|
|
124
|
+
entityType: entityId ?? null,
|
|
125
|
+
tenantId: auth.tenantId ?? null,
|
|
126
|
+
organizationId: auth.orgId ?? null,
|
|
127
|
+
details: {
|
|
128
|
+
purgeFirst,
|
|
129
|
+
recordsIndexed: result.recordsIndexed,
|
|
130
|
+
jobsEnqueued: result.jobsEnqueued,
|
|
131
|
+
success: result.success
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
).catch(() => void 0);
|
|
135
|
+
return NextResponse.json({
|
|
136
|
+
ok: result.success,
|
|
137
|
+
recordsIndexed: result.recordsIndexed,
|
|
138
|
+
jobsEnqueued: result.jobsEnqueued,
|
|
139
|
+
entitiesProcessed: result.entitiesProcessed,
|
|
140
|
+
errors: result.errors.length > 0 ? result.errors : void 0
|
|
141
|
+
});
|
|
142
|
+
} catch (error) {
|
|
143
|
+
const err = error;
|
|
144
|
+
const status = typeof err?.status === "number" ? err.status : typeof err?.statusCode === "number" ? err.statusCode : 500;
|
|
145
|
+
searchError("search.embeddings.reindex", "failed", {
|
|
146
|
+
error: error instanceof Error ? error.message : error,
|
|
147
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
148
|
+
status
|
|
149
|
+
});
|
|
150
|
+
return NextResponse.json(
|
|
151
|
+
{ error: t("search.api.errors.reindexFailed", "Vector reindex failed. Please try again or contact support.") },
|
|
152
|
+
{ status: status >= 400 ? status : 500 }
|
|
153
|
+
);
|
|
154
|
+
} finally {
|
|
155
|
+
const disposable = container;
|
|
156
|
+
if (typeof disposable.dispose === "function") {
|
|
157
|
+
await disposable.dispose();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export {
|
|
162
|
+
POST,
|
|
163
|
+
metadata
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/search/api/embeddings/reindex/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport type { SearchIndexer } from '../../../../../indexer/search-indexer'\nimport type { EmbeddingService } from '../../../../../vector'\nimport type { Knex } from 'knex'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { resolveEmbeddingConfig } from '../../../lib/embedding-config'\nimport type { EntityId } from '@open-mercato/shared/modules/entities'\nimport { searchDebug, searchDebugWarn, searchError } from '../../../../../lib/debug'\nimport { acquireReindexLock, clearReindexLock, getReindexLockStatus } from '../../../lib/reindex-lock'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['search.embeddings.manage'] },\n}\n\nexport async function POST(req: Request) {\n const { t } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n\n let payload: { entityId?: string; purgeFirst?: boolean } = {}\n try {\n payload = await req.json()\n } catch {\n // Default values\n }\n\n const entityId = typeof payload?.entityId === 'string' ? payload.entityId : undefined\n const purgeFirst = payload?.purgeFirst === true\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const knex = (em.getConnection() as unknown as { getKnex: () => Knex }).getKnex()\n\n // Check if another vector reindex operation is already in progress\n const existingLock = await getReindexLockStatus(knex, auth.tenantId, { type: 'vector' })\n if (existingLock) {\n const startedAt = new Date(existingLock.startedAt)\n return NextResponse.json(\n {\n error: t('search.api.errors.reindexInProgress', 'A reindex operation is already in progress'),\n lock: {\n type: existingLock.type,\n action: existingLock.action,\n startedAt: existingLock.startedAt,\n elapsedMinutes: Math.round((Date.now() - startedAt.getTime()) / 60000),\n processedCount: existingLock.processedCount,\n totalCount: existingLock.totalCount,\n },\n },\n { status: 409 }\n )\n }\n\n // Acquire lock before starting the operation\n const { acquired: lockAcquired } = await acquireReindexLock(knex, {\n type: 'vector',\n action: entityId ? `reindex:${entityId}` : 'reindex:all',\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n })\n\n if (!lockAcquired) {\n return NextResponse.json(\n { error: t('search.api.errors.lockFailed', 'Failed to acquire reindex lock') },\n { status: 409 }\n )\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let em: any = null\n try {\n em = container.resolve('em')\n } catch {\n // em not available\n }\n\n let searchIndexer: SearchIndexer\n try {\n searchIndexer = container.resolve('searchIndexer') as SearchIndexer\n } catch {\n return NextResponse.json(\n { error: t('search.api.errors.indexUnavailable', 'Search indexer unavailable') },\n { status: 503 }\n )\n }\n\n // Load saved embedding config and update the embedding service\n try {\n const embeddingConfig = await resolveEmbeddingConfig(container, { defaultValue: null })\n if (embeddingConfig) {\n const embeddingService = container.resolve<EmbeddingService>('vectorEmbeddingService')\n embeddingService.updateConfig(embeddingConfig)\n searchDebug('search.embeddings.reindex', 'using embedding config', {\n providerId: embeddingConfig.providerId,\n model: embeddingConfig.model,\n dimension: embeddingConfig.dimension,\n })\n }\n } catch (err) {\n searchDebugWarn('search.embeddings.reindex', 'failed to load embedding config, using defaults', {\n error: err instanceof Error ? err.message : err,\n })\n }\n\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'vector',\n handler: 'api:search.embeddings.reindex',\n message: entityId\n ? `Vector reindex requested for ${entityId}`\n : 'Vector reindex requested for all entities',\n entityType: entityId ?? null,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: { purgeFirst },\n },\n ).catch(() => undefined)\n\n // Use queue-based vector reindexing (similar to fulltext)\n // This enqueues batches for background processing by workers\n let result\n if (entityId) {\n result = await searchIndexer.reindexEntityToVector({\n entityId: entityId as EntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n purgeFirst,\n useQueue: true,\n })\n } else {\n result = await searchIndexer.reindexAllToVector({\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n purgeFirst,\n useQueue: true,\n })\n }\n\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'vector',\n handler: 'api:search.embeddings.reindex',\n message: result.jobsEnqueued\n ? `Vector reindex enqueued ${result.jobsEnqueued} jobs for ${entityId ?? 'all entities'}`\n : `Vector reindex completed for ${entityId ?? 'all entities'}`,\n entityType: entityId ?? null,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n purgeFirst,\n recordsIndexed: result.recordsIndexed,\n jobsEnqueued: result.jobsEnqueued,\n success: result.success,\n },\n },\n ).catch(() => undefined)\n\n return NextResponse.json({\n ok: result.success,\n recordsIndexed: result.recordsIndexed,\n jobsEnqueued: result.jobsEnqueued,\n entitiesProcessed: result.entitiesProcessed,\n errors: result.errors.length > 0 ? result.errors : undefined,\n })\n } catch (error: unknown) {\n const err = error as { message?: string; status?: number; statusCode?: number }\n const status = typeof err?.status === 'number'\n ? err.status\n : (typeof err?.statusCode === 'number' ? err.statusCode : 500)\n searchError('search.embeddings.reindex', 'failed', {\n error: error instanceof Error ? error.message : error,\n stack: error instanceof Error ? error.stack : undefined,\n status,\n })\n return NextResponse.json(\n { error: t('search.api.errors.reindexFailed', 'Vector reindex failed. Please try again or contact support.') },\n { status: status >= 400 ? status : 500 }\n )\n } finally {\n // Do NOT clear lock here - vector reindex always uses queue mode\n // Workers update heartbeat and stale detection handles cleanup when done\n\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["em"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
|
+
import { envDisablesAutoIndexing, resolveAutoIndexingEnabled, SEARCH_AUTO_INDEX_CONFIG_KEY } from "../../lib/auto-indexing.js";
|
|
6
|
+
import {
|
|
7
|
+
resolveEmbeddingConfig,
|
|
8
|
+
saveEmbeddingConfig,
|
|
9
|
+
getConfiguredProviders,
|
|
10
|
+
detectConfigChange,
|
|
11
|
+
getEffectiveDimension
|
|
12
|
+
} from "../../lib/embedding-config.js";
|
|
13
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
14
|
+
import { EMBEDDING_PROVIDERS, DEFAULT_EMBEDDING_CONFIG } from "../../../../vector/index.js";
|
|
15
|
+
import { searchDebug, searchDebugWarn, searchError } from "../../../../lib/debug.js";
|
|
16
|
+
const embeddingConfigSchema = z.object({
|
|
17
|
+
providerId: z.enum(["openai", "google", "mistral", "cohere", "bedrock", "ollama"]),
|
|
18
|
+
model: z.string(),
|
|
19
|
+
dimension: z.number(),
|
|
20
|
+
outputDimensionality: z.number().optional(),
|
|
21
|
+
baseUrl: z.string().optional()
|
|
22
|
+
});
|
|
23
|
+
const updateSchema = z.object({
|
|
24
|
+
autoIndexingEnabled: z.boolean().optional(),
|
|
25
|
+
embeddingConfig: embeddingConfigSchema.optional()
|
|
26
|
+
});
|
|
27
|
+
const metadata = {
|
|
28
|
+
GET: { requireAuth: true, requireFeatures: ["search.embeddings.view"] },
|
|
29
|
+
POST: { requireAuth: true, requireFeatures: ["search.embeddings.manage"] }
|
|
30
|
+
};
|
|
31
|
+
const openAiConfigured = () => Boolean(process.env.OPENAI_API_KEY && process.env.OPENAI_API_KEY.trim().length > 0);
|
|
32
|
+
const toJson = (payload, init) => NextResponse.json(payload, init);
|
|
33
|
+
const unauthorized = async () => {
|
|
34
|
+
const { t } = await resolveTranslations();
|
|
35
|
+
return NextResponse.json({ error: t("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
36
|
+
};
|
|
37
|
+
const configUnavailable = async () => {
|
|
38
|
+
const { t } = await resolveTranslations();
|
|
39
|
+
return NextResponse.json({ error: t("search.api.errors.configUnavailable", "Configuration service unavailable") }, { status: 503 });
|
|
40
|
+
};
|
|
41
|
+
async function getIndexedDimension(container) {
|
|
42
|
+
try {
|
|
43
|
+
const drivers = container.resolve("vectorDrivers");
|
|
44
|
+
const pgvectorDriver = drivers.find((d) => d.id === "pgvector");
|
|
45
|
+
if (pgvectorDriver?.getTableDimension) {
|
|
46
|
+
return await pgvectorDriver.getTableDimension();
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function getVectorDocumentCount(container, tenantId, organizationId) {
|
|
54
|
+
try {
|
|
55
|
+
const drivers = container.resolve("vectorDrivers");
|
|
56
|
+
const pgvectorDriver = drivers.find((d) => d.id === "pgvector");
|
|
57
|
+
if (pgvectorDriver?.count) {
|
|
58
|
+
return await pgvectorDriver.count({ tenantId, organizationId: organizationId ?? void 0 });
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function GET(req) {
|
|
66
|
+
const auth = await getAuthFromRequest(req);
|
|
67
|
+
if (!auth?.sub) return await unauthorized();
|
|
68
|
+
const container = await createRequestContainer();
|
|
69
|
+
try {
|
|
70
|
+
const lockedByEnv = envDisablesAutoIndexing();
|
|
71
|
+
let autoIndexingEnabled = !lockedByEnv;
|
|
72
|
+
if (!lockedByEnv) {
|
|
73
|
+
try {
|
|
74
|
+
autoIndexingEnabled = await resolveAutoIndexingEnabled(container, { defaultValue: true });
|
|
75
|
+
} catch {
|
|
76
|
+
autoIndexingEnabled = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const embeddingConfig = await resolveEmbeddingConfig(container, { defaultValue: null });
|
|
80
|
+
const configuredProviders = getConfiguredProviders();
|
|
81
|
+
const indexedDimension = await getIndexedDimension(container);
|
|
82
|
+
const effectiveDimension = embeddingConfig ? getEffectiveDimension(embeddingConfig) : DEFAULT_EMBEDDING_CONFIG.dimension;
|
|
83
|
+
const reindexRequired = Boolean(
|
|
84
|
+
indexedDimension && embeddingConfig && indexedDimension !== effectiveDimension
|
|
85
|
+
);
|
|
86
|
+
const documentCount = auth.tenantId ? await getVectorDocumentCount(container, auth.tenantId, auth.orgId) : null;
|
|
87
|
+
return toJson({
|
|
88
|
+
settings: {
|
|
89
|
+
openaiConfigured: openAiConfigured(),
|
|
90
|
+
autoIndexingEnabled: lockedByEnv ? false : autoIndexingEnabled,
|
|
91
|
+
autoIndexingLocked: lockedByEnv,
|
|
92
|
+
lockReason: lockedByEnv ? "env" : null,
|
|
93
|
+
embeddingConfig,
|
|
94
|
+
configuredProviders,
|
|
95
|
+
indexedDimension,
|
|
96
|
+
reindexRequired,
|
|
97
|
+
documentCount
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
} finally {
|
|
101
|
+
const disposable = container;
|
|
102
|
+
if (typeof disposable.dispose === "function") {
|
|
103
|
+
await disposable.dispose();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function POST(req) {
|
|
108
|
+
const { t } = await resolveTranslations();
|
|
109
|
+
const auth = await getAuthFromRequest(req);
|
|
110
|
+
if (!auth?.sub) return await unauthorized();
|
|
111
|
+
let body;
|
|
112
|
+
try {
|
|
113
|
+
body = await req.json();
|
|
114
|
+
} catch {
|
|
115
|
+
return NextResponse.json({ error: t("api.errors.invalidJson", "Invalid JSON payload.") }, { status: 400 });
|
|
116
|
+
}
|
|
117
|
+
const parsed = updateSchema.safeParse(body);
|
|
118
|
+
if (!parsed.success) {
|
|
119
|
+
return NextResponse.json({ error: t("api.errors.invalidPayload", "Invalid payload.") }, { status: 400 });
|
|
120
|
+
}
|
|
121
|
+
const container = await createRequestContainer();
|
|
122
|
+
try {
|
|
123
|
+
let service;
|
|
124
|
+
try {
|
|
125
|
+
service = container.resolve("moduleConfigService");
|
|
126
|
+
} catch {
|
|
127
|
+
return await configUnavailable();
|
|
128
|
+
}
|
|
129
|
+
if (parsed.data.autoIndexingEnabled !== void 0) {
|
|
130
|
+
if (envDisablesAutoIndexing()) {
|
|
131
|
+
return NextResponse.json(
|
|
132
|
+
{ error: t("search.api.errors.autoIndexingDisabled", "Auto-indexing is disabled via DISABLE_VECTOR_SEARCH_AUTOINDEXING.") },
|
|
133
|
+
{ status: 409 }
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
await service.setValue("vector", SEARCH_AUTO_INDEX_CONFIG_KEY, parsed.data.autoIndexingEnabled);
|
|
137
|
+
}
|
|
138
|
+
let embeddingConfig = await resolveEmbeddingConfig(container, { defaultValue: null });
|
|
139
|
+
let reindexRequired = false;
|
|
140
|
+
let indexedDimension = await getIndexedDimension(container);
|
|
141
|
+
if (parsed.data.embeddingConfig) {
|
|
142
|
+
const newConfig = parsed.data.embeddingConfig;
|
|
143
|
+
const providerInfo = EMBEDDING_PROVIDERS[newConfig.providerId];
|
|
144
|
+
if (!providerInfo) {
|
|
145
|
+
return NextResponse.json(
|
|
146
|
+
{ error: t("search.api.errors.invalidProvider", "Invalid embedding provider.") },
|
|
147
|
+
{ status: 400 }
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
const configuredProviders = getConfiguredProviders();
|
|
151
|
+
if (!configuredProviders.includes(newConfig.providerId)) {
|
|
152
|
+
return NextResponse.json(
|
|
153
|
+
{ error: t("search.api.errors.providerNotConfigured", `Provider ${providerInfo.name} is not configured. Set ${providerInfo.envKeyRequired} environment variable.`) },
|
|
154
|
+
{ status: 400 }
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const change = detectConfigChange(
|
|
158
|
+
embeddingConfig,
|
|
159
|
+
{
|
|
160
|
+
...newConfig,
|
|
161
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
162
|
+
},
|
|
163
|
+
indexedDimension
|
|
164
|
+
);
|
|
165
|
+
if (change.requiresReindex) {
|
|
166
|
+
const newDimension = getEffectiveDimension(change.newConfig);
|
|
167
|
+
searchDebug("search.embeddings.update", "config change detected, recreating table", {
|
|
168
|
+
requiresReindex: change.requiresReindex,
|
|
169
|
+
reason: change.reason,
|
|
170
|
+
oldDimension: indexedDimension,
|
|
171
|
+
newDimension
|
|
172
|
+
});
|
|
173
|
+
try {
|
|
174
|
+
const drivers = container.resolve("vectorDrivers");
|
|
175
|
+
const pgvectorDriver = drivers.find((d) => d.id === "pgvector");
|
|
176
|
+
if (pgvectorDriver?.recreateWithDimension) {
|
|
177
|
+
await pgvectorDriver.recreateWithDimension(newDimension);
|
|
178
|
+
if (pgvectorDriver.getTableDimension) {
|
|
179
|
+
indexedDimension = await pgvectorDriver.getTableDimension();
|
|
180
|
+
} else {
|
|
181
|
+
indexedDimension = newDimension;
|
|
182
|
+
}
|
|
183
|
+
searchDebug("search.embeddings.update", "table recreated successfully", { indexedDimension });
|
|
184
|
+
} else {
|
|
185
|
+
searchDebugWarn("search.embeddings.update", "pgvector driver does not have recreateWithDimension method");
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
searchError("search.embeddings.update", "failed to recreate table", {
|
|
189
|
+
error: error instanceof Error ? error.message : error
|
|
190
|
+
});
|
|
191
|
+
return NextResponse.json(
|
|
192
|
+
{ error: t("search.api.errors.recreateFailed", "Failed to recreate vector table with new dimension.") },
|
|
193
|
+
{ status: 500 }
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
await saveEmbeddingConfig(container, change.newConfig);
|
|
198
|
+
embeddingConfig = change.newConfig;
|
|
199
|
+
try {
|
|
200
|
+
const embeddingService = container.resolve("vectorEmbeddingService");
|
|
201
|
+
embeddingService.updateConfig(embeddingConfig);
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
reindexRequired = change.requiresReindex;
|
|
205
|
+
}
|
|
206
|
+
const lockedByEnv = envDisablesAutoIndexing();
|
|
207
|
+
let autoIndexingEnabled = !lockedByEnv;
|
|
208
|
+
if (!lockedByEnv) {
|
|
209
|
+
try {
|
|
210
|
+
autoIndexingEnabled = await resolveAutoIndexingEnabled(container, { defaultValue: true });
|
|
211
|
+
} catch {
|
|
212
|
+
autoIndexingEnabled = true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const updatedDocumentCount = auth.tenantId ? await getVectorDocumentCount(container, auth.tenantId, auth.orgId) : null;
|
|
216
|
+
return toJson({
|
|
217
|
+
settings: {
|
|
218
|
+
openaiConfigured: openAiConfigured(),
|
|
219
|
+
autoIndexingEnabled: lockedByEnv ? false : autoIndexingEnabled,
|
|
220
|
+
autoIndexingLocked: lockedByEnv,
|
|
221
|
+
lockReason: lockedByEnv ? "env" : null,
|
|
222
|
+
embeddingConfig,
|
|
223
|
+
configuredProviders: getConfiguredProviders(),
|
|
224
|
+
indexedDimension,
|
|
225
|
+
reindexRequired,
|
|
226
|
+
documentCount: updatedDocumentCount
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
} catch (error) {
|
|
230
|
+
searchError("search.embeddings.update", "failed", {
|
|
231
|
+
error: error instanceof Error ? error.message : error
|
|
232
|
+
});
|
|
233
|
+
return NextResponse.json({ error: t("search.api.errors.updateFailed", "Failed to update embedding settings.") }, { status: 500 });
|
|
234
|
+
} finally {
|
|
235
|
+
const disposable = container;
|
|
236
|
+
if (typeof disposable.dispose === "function") {
|
|
237
|
+
await disposable.dispose();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
export {
|
|
242
|
+
GET,
|
|
243
|
+
POST,
|
|
244
|
+
metadata
|
|
245
|
+
};
|
|
246
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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'\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"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AAEnC,SAAS,yBAAyB,4BAA4B,oCAAoC;AAClG;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAEpC,SAAS,qBAAqB,gCAAkD;AAChF,SAAS,aAAa,iBAAiB,mBAAmB;AAE1D,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,YAAY,EAAE,KAAK,CAAC,UAAU,UAAU,WAAW,UAAU,WAAW,QAAQ,CAAC;AAAA,EACjF,OAAO,EAAE,OAAO;AAAA,EAChB,WAAW,EAAE,OAAO;AAAA,EACpB,sBAAsB,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1C,SAAS,EAAE,OAAO,EAAE,SAAS;AAC/B,CAAC;AAED,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,qBAAqB,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC1C,iBAAiB,sBAAsB,SAAS;AAClD,CAAC;AAEM,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,wBAAwB,EAAE;AAAA,EACtE,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,0BAA0B,EAAE;AAC3E;AAgBA,MAAM,mBAAmB,MAAM,QAAQ,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,eAAe,KAAK,EAAE,SAAS,CAAC;AAEjH,MAAM,SAAS,CAAC,SAA2B,SAAwB,aAAa,KAAK,SAAS,IAAI;AAElG,MAAM,eAAe,YAAY;AAC/B,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,SAAO,aAAa,KAAK,EAAE,OAAO,EAAE,2BAA2B,cAAc,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AACnG;AAEA,MAAM,oBAAoB,YAAY;AACpC,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,SAAO,aAAa,KAAK,EAAE,OAAO,EAAE,uCAAuC,mCAAmC,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AACpI;AAEA,eAAe,oBAAoB,WAAkF;AACnH,MAAI;AACF,UAAM,UAAU,UAAU,QAAwB,eAAe;AACjE,UAAM,iBAAiB,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU;AAC9D,QAAI,gBAAgB,mBAAmB;AACrC,aAAO,MAAM,eAAe,kBAAkB;AAAA,IAChD;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,uBACb,WACA,UACA,gBACwB;AACxB,MAAI;AACF,UAAM,UAAU,UAAU,QAAwB,eAAe;AACjE,UAAM,iBAAiB,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU;AAC9D,QAAI,gBAAgB,OAAO;AACzB,aAAO,MAAM,eAAe,MAAM,EAAE,UAAU,gBAAgB,kBAAkB,OAAU,CAAC;AAAA,IAC7F;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,IAAK,QAAO,MAAM,aAAa;AAE1C,QAAM,YAAY,MAAM,uBAAuB;AAC/C,MAAI;AACF,UAAM,cAAc,wBAAwB;AAC5C,QAAI,sBAAsB,CAAC;AAC3B,QAAI,CAAC,aAAa;AAChB,UAAI;AACF,8BAAsB,MAAM,2BAA2B,WAAW,EAAE,cAAc,KAAK,CAAC;AAAA,MAC1F,QAAQ;AACN,8BAAsB;AAAA,MACxB;AAAA,IACF;AAEA,UAAM,kBAAkB,MAAM,uBAAuB,WAAW,EAAE,cAAc,KAAK,CAAC;AACtF,UAAM,sBAAsB,uBAAuB;AACnD,UAAM,mBAAmB,MAAM,oBAAoB,SAAS;AAE5D,UAAM,qBAAqB,kBACvB,sBAAsB,eAAe,IACrC,yBAAyB;AAE7B,UAAM,kBAAkB;AAAA,MACtB,oBACA,mBACA,qBAAqB;AAAA,IACvB;AAGA,UAAM,gBAAgB,KAAK,WACvB,MAAM,uBAAuB,WAAW,KAAK,UAAU,KAAK,KAAK,IACjE;AAEJ,WAAO,OAAO;AAAA,MACZ,UAAU;AAAA,QACR,kBAAkB,iBAAiB;AAAA,QACnC,qBAAqB,cAAc,QAAQ;AAAA,QAC3C,oBAAoB;AAAA,QACpB,YAAY,cAAc,QAAQ;AAAA,QAClC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,UAAE;AACA,UAAM,aAAa;AACnB,QAAI,OAAO,WAAW,YAAY,YAAY;AAC5C,YAAM,WAAW,QAAQ;AAAA,IAC3B;AAAA,EACF;AACF;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,IAAK,QAAO,MAAM,aAAa;AAE1C,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AACN,WAAO,aAAa,KAAK,EAAE,OAAO,EAAE,0BAA0B,uBAAuB,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3G;AACA,QAAM,SAAS,aAAa,UAAU,IAAI;AAC1C,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,EAAE,6BAA6B,kBAAkB,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzG;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,MAAI;AACF,QAAI;AACJ,QAAI;AACF,gBAAW,UAAU,QAAQ,qBAAqB;AAAA,IACpD,QAAQ;AACN,aAAO,MAAM,kBAAkB;AAAA,IACjC;AAEA,QAAI,OAAO,KAAK,wBAAwB,QAAW;AACjD,UAAI,wBAAwB,GAAG;AAC7B,eAAO,aAAa;AAAA,UAClB,EAAE,OAAO,EAAE,0CAA0C,mEAAmE,EAAE;AAAA,UAC1H,EAAE,QAAQ,IAAI;AAAA,QAChB;AAAA,MACF;AACA,YAAM,QAAQ,SAAS,UAAU,8BAA8B,OAAO,KAAK,mBAAmB;AAAA,IAChG;AAEA,QAAI,kBAAkB,MAAM,uBAAuB,WAAW,EAAE,cAAc,KAAK,CAAC;AACpF,QAAI,kBAAkB;AACtB,QAAI,mBAAmB,MAAM,oBAAoB,SAAS;AAE1D,QAAI,OAAO,KAAK,iBAAiB;AAC/B,YAAM,YAAY,OAAO,KAAK;AAC9B,YAAM,eAAe,oBAAoB,UAAU,UAAU;AAE7D,UAAI,CAAC,cAAc;AACjB,eAAO,aAAa;AAAA,UAClB,EAAE,OAAO,EAAE,qCAAqC,6BAA6B,EAAE;AAAA,UAC/E,EAAE,QAAQ,IAAI;AAAA,QAChB;AAAA,MACF;AAEA,YAAM,sBAAsB,uBAAuB;AACnD,UAAI,CAAC,oBAAoB,SAAS,UAAU,UAAU,GAAG;AACvD,eAAO,aAAa;AAAA,UAClB,EAAE,OAAO,EAAE,2CAA2C,YAAY,aAAa,IAAI,2BAA2B,aAAa,cAAc,wBAAwB,EAAE;AAAA,UACnK,EAAE,QAAQ,IAAI;AAAA,QAChB;AAAA,MACF;AAEA,YAAM,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACE,GAAG;AAAA,UACH,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QACpC;AAAA,QACA;AAAA,MACF;AAEA,UAAI,OAAO,iBAAiB;AAC1B,cAAM,eAAe,sBAAsB,OAAO,SAAS;AAC3D,oBAAY,4BAA4B,4CAA4C;AAAA,UAClF,iBAAiB,OAAO;AAAA,UACxB,QAAQ,OAAO;AAAA,UACf,cAAc;AAAA,UACd;AAAA,QACF,CAAC;AACD,YAAI;AACF,gBAAM,UAAU,UAAU,QAAwB,eAAe;AACjE,gBAAM,iBAAiB,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU;AAC9D,cAAI,gBAAgB,uBAAuB;AACzC,kBAAM,eAAe,sBAAsB,YAAY;AAEvD,gBAAI,eAAe,mBAAmB;AACpC,iCAAmB,MAAM,eAAe,kBAAkB;AAAA,YAC5D,OAAO;AACL,iCAAmB;AAAA,YACrB;AACA,wBAAY,4BAA4B,gCAAgC,EAAE,iBAAiB,CAAC;AAAA,UAC9F,OAAO;AACL,4BAAgB,4BAA4B,4DAA4D;AAAA,UAC1G;AAAA,QACF,SAAS,OAAO;AACd,sBAAY,4BAA4B,4BAA4B;AAAA,YAClE,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAClD,CAAC;AACD,iBAAO,aAAa;AAAA,YAClB,EAAE,OAAO,EAAE,oCAAoC,qDAAqD,EAAE;AAAA,YACtG,EAAE,QAAQ,IAAI;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AAEA,YAAM,oBAAoB,WAAW,OAAO,SAAS;AACrD,wBAAkB,OAAO;AAEzB,UAAI;AACF,cAAM,mBAAmB,UAAU,QAA0B,wBAAwB;AACrF,yBAAiB,aAAa,eAAe;AAAA,MAC/C,QAAQ;AAAA,MAER;AAEA,wBAAkB,OAAO;AAAA,IAC3B;AAEA,UAAM,cAAc,wBAAwB;AAC5C,QAAI,sBAAsB,CAAC;AAC3B,QAAI,CAAC,aAAa;AAChB,UAAI;AACF,8BAAsB,MAAM,2BAA2B,WAAW,EAAE,cAAc,KAAK,CAAC;AAAA,MAC1F,QAAQ;AACN,8BAAsB;AAAA,MACxB;AAAA,IACF;AAGA,UAAM,uBAAuB,KAAK,WAC9B,MAAM,uBAAuB,WAAW,KAAK,UAAU,KAAK,KAAK,IACjE;AAEJ,WAAO,OAAO;AAAA,MACZ,UAAU;AAAA,QACR,kBAAkB,iBAAiB;AAAA,QACnC,qBAAqB,cAAc,QAAQ;AAAA,QAC3C,oBAAoB;AAAA,QACpB,YAAY,cAAc,QAAQ;AAAA,QAClC;AAAA,QACA,qBAAqB,uBAAuB;AAAA,QAC5C;AAAA,QACA;AAAA,QACA,eAAe;AAAA,MACjB;AAAA,IACF,CAAC;AAAA,EACH,SAAS,OAAO;AACd,gBAAY,4BAA4B,UAAU;AAAA,MAChD,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD,CAAC;AACD,WAAO,aAAa,KAAK,EAAE,OAAO,EAAE,kCAAkC,sCAAsC,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClI,UAAE;AACA,UAAM,aAAa;AACnB,QAAI,OAAO,WAAW,YAAY,YAAY;AAC5C,YAAM,WAAW,QAAQ;AAAA,IAC3B;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|