@open-mercato/search 0.7.1-develop.7136.1.0f76137a1d → 0.7.1-develop.7148.1.3076e5ccf7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import { parseDecryptedFieldValue } from "@open-mercato/shared/lib/encryption/tenantDataEncryptionService";
1
2
  import { computeChecksum } from "./checksum.js";
2
3
  import { logVectorOperation } from "../lib/vector-logs.js";
3
4
  import { searchDebug, searchDebugWarn } from "../../lib/debug.js";
@@ -165,6 +166,8 @@ class VectorIndexService {
165
166
  args.tenantId,
166
167
  args.organizationId
167
168
  );
169
+ const decryptedLinks = decrypted.links ?? args.links;
170
+ const decryptedPayload = decrypted.payload ?? args.payload;
168
171
  return {
169
172
  resultTitle: String(decrypted.resultTitle ?? args.resultTitle),
170
173
  resultSubtitle: decrypted.resultSubtitle ?? args.resultSubtitle,
@@ -172,8 +175,8 @@ class VectorIndexService {
172
175
  resultSnapshot: decrypted.resultSnapshot ?? args.resultSnapshot,
173
176
  primaryLinkHref: decrypted.primaryLinkHref ?? args.primaryLinkHref,
174
177
  primaryLinkLabel: decrypted.primaryLinkLabel ?? args.primaryLinkLabel,
175
- links: decrypted.links ?? args.links,
176
- payload: decrypted.payload ?? args.payload
178
+ links: typeof decryptedLinks === "string" ? parseDecryptedFieldValue(decryptedLinks) : decryptedLinks,
179
+ payload: typeof decryptedPayload === "string" ? parseDecryptedFieldValue(decryptedPayload) : decryptedPayload
177
180
  };
178
181
  } catch (err) {
179
182
  searchDebugWarn("vector.index", "Vector entry decryption failed; refusing to return ciphertext as plaintext", {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/vector/services/vector-index.service.ts"],
4
- "sourcesContent": ["import type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport {\n type VectorModuleConfig,\n type VectorEntityConfig,\n type VectorQueryRequest,\n type VectorSearchHit,\n type VectorIndexSource,\n type VectorDriverId,\n type VectorLinkDescriptor,\n type VectorResultPresenter,\n type VectorIndexEntry,\n} from '../types'\nimport type { VectorDriver } from '../types'\nimport type { SearchFieldPolicy } from '@open-mercato/shared/modules/search'\nimport { computeChecksum } from './checksum'\nimport { EmbeddingService } from './embedding'\nimport { logVectorOperation } from '../lib/vector-logs'\nimport { searchDebug, searchDebugWarn } from '../../lib/debug'\n\ntype ContainerResolver = () => unknown\nconst VECTOR_ENTRY_ENCRYPTION_ENTITY_ID = 'vector:vector_search'\n\nconst DEFAULT_SOURCE_SCOPE_FIELDS = new Set<string>([\n 'id',\n 'tenant_id',\n 'tenantId',\n 'organization_id',\n 'organizationId',\n 'created_at',\n 'createdAt',\n 'updated_at',\n 'updatedAt',\n 'deleted_at',\n 'deletedAt',\n])\n\nconst ENRICHMENT_FIELD_HINTS: Record<EntityId, string[]> = {\n 'customers:customer_entity': [\n 'id',\n 'organization_id',\n 'tenant_id',\n 'display_name',\n 'description',\n 'status',\n 'lifecycle_stage',\n 'primary_email',\n 'primary_phone',\n 'kind',\n 'customer_kind',\n ],\n 'customers:customer_comment': ['id', 'organization_id', 'tenant_id', 'entity_id', 'body', 'appearance_icon', 'appearance_color'],\n 'customers:customer_activity': ['id', 'organization_id', 'tenant_id', 'entity_id', 'activity_type', 'subject', 'body', 'deal_id'],\n 'customers:customer_deal': ['id', 'organization_id', 'tenant_id', 'title', 'pipeline_stage', 'status', 'value_amount', 'value_currency'],\n 'customers:customer_todo_link': ['id', 'organization_id', 'tenant_id', 'entity_id', 'todo_id', 'todo_source'],\n 'customers:customer_person_profile': [\n 'id',\n 'organization_id',\n 'tenant_id',\n 'entity_id',\n 'first_name',\n 'last_name',\n 'preferred_name',\n 'job_title',\n 'department',\n ],\n 'customers:customer_company_profile': [\n 'id',\n 'organization_id',\n 'tenant_id',\n 'entity_id',\n 'brand_name',\n 'legal_name',\n 'domain',\n 'industry',\n 'size_bucket',\n ],\n}\n\nexport type VectorIndexServiceOptions = {\n drivers: VectorDriver[]\n embeddingService: EmbeddingService\n queryEngine: QueryEngine\n moduleConfigs: VectorModuleConfig[]\n defaultDriverId?: VectorDriverId\n containerResolver?: ContainerResolver\n eventBus?: {\n emitEvent(event: string, payload: any, options?: any): Promise<void>\n }\n}\n\ntype IndexRecordArgs = {\n entityId: EntityId\n recordId: string\n tenantId: string\n organizationId?: string | null\n}\n\ntype DeleteRecordArgs = {\n entityId: EntityId\n recordId: string\n tenantId: string\n organizationId?: string | null\n}\n\nexport type VectorIndexOperationResult = {\n action: 'indexed' | 'deleted' | 'skipped'\n created?: boolean\n existed?: boolean\n tenantId: string\n organizationId: string | null\n reason?: 'unsupported' | 'missing_record' | 'checksum_match'\n}\n\nexport class VectorIndexService {\n private readonly driverMap = new Map<VectorDriverId, VectorDriver>()\n private readonly entityConfig = new Map<EntityId, { config: VectorEntityConfig; driverId: VectorDriverId }>()\n private readonly defaultDriverId: VectorDriverId\n\n constructor(private readonly opts: VectorIndexServiceOptions) {\n for (const driver of opts.drivers) {\n this.driverMap.set(driver.id, driver)\n }\n this.defaultDriverId = opts.defaultDriverId ?? 'pgvector'\n for (const moduleConfig of opts.moduleConfigs) {\n const driverId = moduleConfig.defaultDriverId ?? this.defaultDriverId\n for (const entity of moduleConfig.entities ?? []) {\n if (!entity?.entityId) continue\n if (entity.enabled === false) continue\n const targetDriver = entity.driverId ?? driverId\n this.entityConfig.set(entity.entityId, { config: entity, driverId: targetDriver })\n }\n }\n }\n\n private resolveEncryptionService(): TenantDataEncryptionService | null {\n if (!this.opts.containerResolver) return null\n try {\n const container = this.opts.containerResolver() as any\n if (!container || typeof container.resolve !== 'function') return null\n return container.resolve('tenantEncryptionService') as TenantDataEncryptionService\n } catch {\n return null\n }\n }\n\n private async encryptResultFields(args: {\n tenantId: string\n organizationId: string | null\n resultTitle: string\n resultSubtitle: string | null\n resultIcon: string | null\n resultSnapshot: string | null\n primaryLinkHref: string | null\n primaryLinkLabel: string | null\n links: VectorLinkDescriptor[] | string | null\n payload: Record<string, unknown> | string | null\n }): Promise<{\n resultTitle: string\n resultSubtitle: string | null\n resultIcon: string | null\n resultSnapshot: string | null\n primaryLinkHref: string | null\n primaryLinkLabel: string | null\n links: VectorLinkDescriptor[] | string | null\n payload: Record<string, unknown> | string | null\n }> {\n const service = this.resolveEncryptionService()\n if (!service || !service.isEnabled?.()) {\n return {\n resultTitle: args.resultTitle,\n resultSubtitle: args.resultSubtitle,\n resultIcon: args.resultIcon,\n resultSnapshot: args.resultSnapshot,\n primaryLinkHref: args.primaryLinkHref,\n primaryLinkLabel: args.primaryLinkLabel,\n links: args.links,\n payload: args.payload,\n }\n }\n try {\n const encrypted = await service.encryptEntityPayload(\n VECTOR_ENTRY_ENCRYPTION_ENTITY_ID,\n {\n resultTitle: args.resultTitle,\n resultSubtitle: args.resultSubtitle,\n resultIcon: args.resultIcon,\n resultSnapshot: args.resultSnapshot,\n primaryLinkHref: args.primaryLinkHref,\n primaryLinkLabel: args.primaryLinkLabel,\n links: args.links,\n payload: args.payload,\n },\n args.tenantId,\n args.organizationId,\n )\n return {\n resultTitle: String((encrypted as any).resultTitle ?? args.resultTitle),\n resultSubtitle: ((encrypted as any).resultSubtitle ?? args.resultSubtitle) as any,\n resultIcon: ((encrypted as any).resultIcon ?? args.resultIcon) as any,\n resultSnapshot: ((encrypted as any).resultSnapshot ?? args.resultSnapshot) as any,\n primaryLinkHref: ((encrypted as any).primaryLinkHref ?? args.primaryLinkHref) as any,\n primaryLinkLabel: ((encrypted as any).primaryLinkLabel ?? args.primaryLinkLabel) as any,\n links: ((encrypted as any).links ?? args.links) as any,\n payload: ((encrypted as any).payload ?? args.payload) as any,\n }\n } catch (err) {\n searchDebugWarn('vector.index', 'Vector entry encryption failed; refusing to persist plaintext', {\n entityId: VECTOR_ENTRY_ENCRYPTION_ENTITY_ID,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n error: err instanceof Error ? err.message : String(err),\n })\n throw err instanceof Error\n ? err\n : new Error('[internal] Vector entry encryption failed')\n }\n }\n\n private async decryptResultFields(args: {\n tenantId: string\n organizationId: string | null\n resultTitle: string\n resultSubtitle: string | null\n resultIcon: string | null\n resultSnapshot: string | null\n primaryLinkHref: string | null\n primaryLinkLabel: string | null\n links: VectorLinkDescriptor[] | string | null\n payload: Record<string, unknown> | string | null\n }): Promise<{\n resultTitle: string\n resultSubtitle: string | null\n resultIcon: string | null\n resultSnapshot: string | null\n primaryLinkHref: string | null\n primaryLinkLabel: string | null\n links: VectorLinkDescriptor[] | string | null\n payload: Record<string, unknown> | string | null\n }> {\n const service = this.resolveEncryptionService()\n if (!service || !service.isEnabled?.() || typeof service.decryptEntityPayload !== 'function') {\n return {\n resultTitle: args.resultTitle,\n resultSubtitle: args.resultSubtitle,\n resultIcon: args.resultIcon,\n resultSnapshot: args.resultSnapshot,\n primaryLinkHref: args.primaryLinkHref,\n primaryLinkLabel: args.primaryLinkLabel,\n links: args.links,\n payload: args.payload,\n }\n }\n try {\n const decrypted = await service.decryptEntityPayload(\n VECTOR_ENTRY_ENCRYPTION_ENTITY_ID,\n {\n resultTitle: args.resultTitle,\n resultSubtitle: args.resultSubtitle,\n resultIcon: args.resultIcon,\n resultSnapshot: args.resultSnapshot,\n primaryLinkHref: args.primaryLinkHref,\n primaryLinkLabel: args.primaryLinkLabel,\n links: args.links,\n payload: args.payload,\n },\n args.tenantId,\n args.organizationId,\n )\n return {\n resultTitle: String((decrypted as any).resultTitle ?? args.resultTitle),\n resultSubtitle: ((decrypted as any).resultSubtitle ?? args.resultSubtitle) as any,\n resultIcon: ((decrypted as any).resultIcon ?? args.resultIcon) as any,\n resultSnapshot: ((decrypted as any).resultSnapshot ?? args.resultSnapshot) as any,\n primaryLinkHref: ((decrypted as any).primaryLinkHref ?? args.primaryLinkHref) as any,\n primaryLinkLabel: ((decrypted as any).primaryLinkLabel ?? args.primaryLinkLabel) as any,\n links: ((decrypted as any).links ?? args.links) as any,\n payload: ((decrypted as any).payload ?? args.payload) as any,\n }\n } catch (err) {\n searchDebugWarn('vector.index', 'Vector entry decryption failed; refusing to return ciphertext as plaintext', {\n entityId: VECTOR_ENTRY_ENCRYPTION_ENTITY_ID,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n error: err instanceof Error ? err.message : String(err),\n })\n throw err instanceof Error\n ? err\n : new Error('[internal] Vector entry decryption failed')\n }\n }\n\n listEnabledEntities(): EntityId[] {\n return Array.from(this.entityConfig.keys())\n }\n\n async ensureDriverReady(entityId?: EntityId): Promise<void> {\n if (entityId) {\n const entry = this.entityConfig.get(entityId)\n if (!entry) return\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n return\n }\n const uniqueDrivers = new Set<VectorDriverId>()\n for (const entry of this.entityConfig.values()) {\n uniqueDrivers.add(entry.driverId)\n }\n if (!uniqueDrivers.size) uniqueDrivers.add(this.defaultDriverId)\n await Promise.all(Array.from(uniqueDrivers).map(async (driverId) => {\n try {\n const driver = this.getDriver(driverId)\n await driver.ensureReady()\n } catch (err) {\n searchDebugWarn('vector', 'Failed to ensure driver readiness', { driverId, error: err instanceof Error ? err.message : err })\n }\n }))\n }\n\n private getDriver(driverId: VectorDriverId): VectorDriver {\n const driver = this.driverMap.get(driverId)\n if (!driver) {\n throw new Error(`[vector] Driver ${driverId} is not registered`)\n }\n return driver\n }\n\n private getEnrichmentFields(entityId: EntityId): string[] | undefined {\n const hints = ENRICHMENT_FIELD_HINTS[entityId]\n if (!hints) return undefined\n const unique = new Set<string>(['id', ...hints])\n return Array.from(unique)\n }\n\n private async fetchRecord(entityId: EntityId, recordIds: string[], tenantId: string, organizationId?: string | null) {\n const filters: Record<string, any> = { id: { $in: recordIds } }\n const result = await this.opts.queryEngine.query(entityId, {\n tenantId,\n organizationId: organizationId ?? undefined,\n filters,\n includeCustomFields: true,\n fields: this.getEnrichmentFields(entityId),\n skipAutoReindex: true,\n })\n const byId = new Map<string, Record<string, any>>()\n for (const item of result.items) {\n const key = String((item as any).id ?? '')\n if (!key) continue\n byId.set(key, item as Record<string, any>)\n }\n return byId\n }\n\n private extractRecordPayload(entityId: EntityId, raw: Record<string, any>) {\n const record: Record<string, any> = {}\n const customFields: Record<string, any> = {}\n const multiMap = new Map<string, boolean>()\n\n for (const [key, value] of Object.entries(raw)) {\n if (key.startsWith('cf:') && key.endsWith('__is_multi')) {\n const base = key.replace(/__is_multi$/, '')\n multiMap.set(base, Boolean(value))\n continue\n }\n if (key.startsWith('cf:')) {\n customFields[key.slice(3)] = value\n continue\n }\n record[key] = value\n }\n\n for (const [key, isMulti] of multiMap.entries()) {\n const bare = key.slice(3)\n if (bare && customFields[bare] != null && !Array.isArray(customFields[bare]) && isMulti) {\n customFields[bare] = [customFields[bare]]\n }\n }\n\n if (record.entity_id == null && record.entityId == null && entityId.endsWith('_company_profile')) {\n searchDebugWarn('vector.index', 'company profile missing entity id in payload', {\n id: record.id,\n keys: Object.keys(record),\n })\n }\n\n return { record, customFields }\n }\n\n private async indexExisting(\n entry: { config: VectorEntityConfig; driverId: VectorDriverId },\n driver: VectorDriver,\n args: IndexRecordArgs,\n raw: Record<string, any>,\n opts: { skipDelete?: boolean } = {},\n ): Promise<VectorIndexOperationResult> {\n const scopeOrg = args.organizationId ?? null\n const { record, customFields } = this.extractRecordPayload(args.entityId, raw)\n const resolvedOrgId = scopeOrg ?? (record.organization_id ?? record.organizationId ?? null)\n const source = await this.resolveSource(args.entityId, entry.config, {\n record,\n customFields,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n })\n if (!source) {\n const existing = await driver.getChecksum(args.entityId, args.recordId, args.tenantId)\n if (!opts.skipDelete && existing) {\n await driver.delete(args.entityId, args.recordId, args.tenantId)\n return {\n action: 'deleted',\n existed: true,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n }\n }\n return {\n action: 'skipped',\n existed: Boolean(existing),\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n reason: 'missing_record',\n }\n }\n\n const checksumSource = source.checksumSource ?? { record, customFields }\n const checksum = computeChecksum(checksumSource)\n const current = await driver.getChecksum(args.entityId, args.recordId, args.tenantId)\n if (current && current === checksum) {\n return {\n action: 'skipped',\n existed: true,\n tenantId: args.tenantId,\n organizationId: scopeOrg,\n reason: 'checksum_match',\n }\n }\n if (!this.opts.embeddingService.available) {\n throw new Error('[vector] Embedding service unavailable (missing OPENAI_API_KEY)')\n }\n const embedding = await this.opts.embeddingService.createEmbedding(source.input)\n const presenter = await this.resolvePresenter(entry.config, {\n record,\n customFields,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n }, source.presenter ?? null)\n if (!presenter?.title) {\n searchDebugWarn('vector.index', 'missing presenter title', {\n entityId: args.entityId,\n recordId: args.recordId,\n recordSample: {\n display_name: record.display_name,\n displayName: record.displayName,\n name: record.name,\n title: record.title,\n subject: record.subject,\n kind: record.kind,\n },\n })\n }\n const links = await this.resolveLinks(entry.config, {\n record,\n customFields,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n }, source.links ?? null)\n const url = await this.resolveUrl(entry.config, {\n record,\n customFields,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n })\n\n const normalizedPresenter = this.ensurePresenter(presenter, record, customFields, args.recordId, args.entityId)\n const normalizedLinks = Array.isArray(links) && links.length ? links : null\n const snapshot = this.deriveSnapshot(record, customFields)\n const rawResultTitle = this.resolveResultTitle(normalizedPresenter, record, customFields, args.recordId)\n const rawResultSubtitle = normalizedPresenter.subtitle ?? snapshot ?? null\n const rawResultIcon = normalizedPresenter.icon ?? this.mapDefaultIcon(args.entityId)\n const resultBadge = normalizedPresenter.badge ?? null\n const primaryLink = this.resolvePrimaryLink(normalizedLinks, url, rawResultTitle)\n\n const encryptedResult = await this.encryptResultFields({\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n resultTitle: rawResultTitle,\n resultSubtitle: rawResultSubtitle,\n resultIcon: rawResultIcon ?? null,\n resultSnapshot: snapshot ?? null,\n primaryLinkHref: primaryLink?.href ?? null,\n primaryLinkLabel: primaryLink?.label ?? null,\n links: normalizedLinks,\n payload: source.payload ?? null,\n })\n\n const presenterForStorage: VectorResultPresenter = {\n title: encryptedResult.resultTitle,\n subtitle: encryptedResult.resultSubtitle ?? undefined,\n icon: encryptedResult.resultIcon ?? undefined,\n badge: resultBadge ?? undefined,\n }\n\n searchDebug('VectorIndexService', 'Storing vector index entry', {\n entityId: args.entityId,\n recordId: args.recordId,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n })\n await driver.upsert({\n driverId: entry.driverId,\n entityId: args.entityId,\n recordId: args.recordId,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n checksum,\n embedding,\n url: url ?? null,\n presenter: presenterForStorage,\n links: (encryptedResult.links as any) ?? normalizedLinks,\n payload: (encryptedResult.payload as any) ?? source.payload ?? null,\n resultTitle: encryptedResult.resultTitle,\n resultSubtitle: encryptedResult.resultSubtitle,\n resultIcon: encryptedResult.resultIcon ?? null,\n resultBadge,\n resultSnapshot: encryptedResult.resultSnapshot ?? snapshot,\n primaryLinkHref: encryptedResult.primaryLinkHref ?? primaryLink?.href ?? null,\n primaryLinkLabel: encryptedResult.primaryLinkLabel ?? null,\n })\n\n return {\n action: 'indexed',\n created: !current,\n existed: true,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n }\n }\n\n private ensurePresenter(\n presenter: VectorResultPresenter | null,\n record: Record<string, any>,\n customFields: Record<string, any>,\n recordId: string,\n entityId: EntityId,\n ): VectorResultPresenter {\n if (presenter?.title) return presenter\n const fallback = this.buildFallbackPresenter(record, customFields, recordId, entityId)\n return fallback\n }\n\n private resolveResultTitle(\n presenter: VectorResultPresenter,\n record: Record<string, any>,\n customFields: Record<string, any>,\n recordId: string,\n ): string {\n const candidate =\n presenter.title ??\n record.display_name ??\n record.displayName ??\n record.name ??\n record.title ??\n record.subject ??\n customFields.title ??\n customFields.name ??\n recordId\n const text = String(candidate).trim()\n return text.length ? text : recordId\n }\n\n private buildFallbackPresenter(\n record: Record<string, any>,\n customFields: Record<string, any>,\n recordId: string,\n entityId: EntityId,\n ): VectorResultPresenter {\n const titleCandidate =\n record.display_name ??\n record.displayName ??\n record.name ??\n record.title ??\n record.subject ??\n recordId\n const subtitleCandidate =\n record.description ??\n record.summary ??\n record.body ??\n customFields.summary ??\n customFields.description ??\n null\n const icon = typeof record.kind === 'string'\n ? this.mapEntityIcon(record.kind)\n : this.mapDefaultIcon(entityId)\n return {\n title: String(titleCandidate),\n subtitle: subtitleCandidate ? String(subtitleCandidate) : undefined,\n icon: icon ?? undefined,\n }\n }\n\n private mapEntityIcon(kind?: string | null): string | null {\n if (!kind) return null\n const normalized = kind.toLowerCase()\n if (normalized === 'person') return 'user'\n if (normalized === 'company' || normalized === 'organization') return 'building'\n return null\n }\n\n private mapDefaultIcon(entityId: EntityId): string | null {\n if (entityId.startsWith('customers:customer_deal')) return 'briefcase'\n if (entityId.startsWith('customers:customer_comment')) return 'sticky-note'\n if (entityId.startsWith('customers:customer_activity')) return 'bolt'\n if (entityId.startsWith('customers:customer_todo')) return 'check-square'\n return null\n }\n\n private resolvePrimaryLink(\n links: VectorLinkDescriptor[] | null,\n url: string | null,\n fallbackLabel: string,\n ): { href: string; label: string } | null {\n if (links?.length) {\n const primary = links.find((link) => link.kind === 'primary') ?? links[0]\n if (primary?.href) {\n return { href: primary.href, label: primary.label ?? fallbackLabel }\n }\n }\n if (url) {\n return { href: url, label: fallbackLabel }\n }\n return null\n }\n\n private deriveSnapshot(\n record: Record<string, any>,\n customFields: Record<string, any>,\n ): string | null {\n const candidates = [\n record.summary,\n record.description,\n record.body,\n customFields.summary,\n customFields.description,\n customFields.body,\n ]\n for (const value of candidates) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed.length) return trimmed\n }\n }\n return null\n }\n\n private buildDefaultSource(\n entityId: EntityId,\n payload: { record: Record<string, any>; customFields: Record<string, any> },\n fieldPolicy?: SearchFieldPolicy,\n ): VectorIndexSource {\n const { record, customFields } = payload\n const lines: string[] = []\n\n const searchableWhitelist = fieldPolicy?.searchable ? new Set(fieldPolicy.searchable) : null\n const excludedFields = new Set<string>([\n ...(fieldPolicy?.excluded ?? []),\n ...(fieldPolicy?.hashOnly ?? []),\n ])\n\n const isEligibleField = (field: string): boolean => {\n if (DEFAULT_SOURCE_SCOPE_FIELDS.has(field)) return false\n if (excludedFields.has(field)) return false\n if (searchableWhitelist && !searchableWhitelist.has(field)) return false\n return true\n }\n\n const pushEntry = (label: string, value: unknown) => {\n if (value === null || value === undefined) return\n if (typeof value === 'string' && value.trim().length === 0) return\n if (typeof value === 'object') {\n lines.push(`${label}: ${JSON.stringify(value)}`)\n } else {\n lines.push(`${label}: ${value}`)\n }\n }\n\n const preferredFields = ['title', 'name', 'displayName', 'summary', 'subject']\n for (const field of preferredFields) {\n if (!isEligibleField(field)) continue\n if (record[field] != null) pushEntry(field, record[field])\n }\n\n for (const [key, value] of Object.entries(record)) {\n if (preferredFields.includes(key)) continue\n if (!isEligibleField(key)) continue\n pushEntry(key, value)\n }\n\n for (const [key, value] of Object.entries(customFields)) {\n if (!isEligibleField(key)) continue\n pushEntry(`custom.${key}`, value)\n }\n\n if (lines.length === 0) {\n lines.push(`${entityId}#${record.id ?? ''}`)\n }\n\n return {\n input: lines,\n payload: null,\n checksumSource: { record, customFields },\n }\n }\n\n private async resolveSource(entityId: EntityId, config: VectorEntityConfig, ctx: {\n record: Record<string, any>\n customFields: Record<string, any>\n organizationId?: string | null\n tenantId: string\n }): Promise<VectorIndexSource | null> {\n const baseCtx = {\n record: ctx.record,\n customFields: ctx.customFields,\n organizationId: ctx.organizationId ?? null,\n tenantId: ctx.tenantId,\n queryEngine: this.opts.queryEngine,\n container: this.opts.containerResolver ? this.opts.containerResolver() : undefined,\n }\n if (config.buildSource) {\n const built = await config.buildSource(baseCtx)\n if (built) return built\n return null\n }\n return this.buildDefaultSource(entityId, { record: ctx.record, customFields: ctx.customFields }, config.fieldPolicy)\n }\n\n private async resolvePresenter(\n config: VectorEntityConfig,\n ctx: {\n record: Record<string, any>\n customFields: Record<string, any>\n organizationId?: string | null\n tenantId: string\n },\n fallback?: VectorResultPresenter | null,\n ): Promise<VectorResultPresenter | null> {\n const baseCtx = {\n record: ctx.record,\n customFields: ctx.customFields,\n organizationId: ctx.organizationId ?? null,\n tenantId: ctx.tenantId,\n queryEngine: this.opts.queryEngine,\n container: this.opts.containerResolver ? this.opts.containerResolver() : undefined,\n }\n if (config.formatResult) {\n const formatted = await config.formatResult(baseCtx)\n if (formatted) return formatted\n }\n if (fallback) return fallback\n const nameLike = ctx.record.displayName || ctx.record.title || ctx.record.name\n if (typeof nameLike === 'string' && nameLike.trim().length > 0) {\n const subtitle = ctx.record.description || ctx.record.summary\n return {\n title: nameLike,\n subtitle: typeof subtitle === 'string' ? subtitle : undefined,\n }\n }\n return null\n }\n\n private async resolveLinks(\n config: VectorEntityConfig,\n ctx: {\n record: Record<string, any>\n customFields: Record<string, any>\n organizationId?: string | null\n tenantId: string\n },\n fallback?: VectorLinkDescriptor[] | null,\n ): Promise<VectorLinkDescriptor[] | null> {\n const baseCtx = {\n record: ctx.record,\n customFields: ctx.customFields,\n organizationId: ctx.organizationId ?? null,\n tenantId: ctx.tenantId,\n queryEngine: this.opts.queryEngine,\n container: this.opts.containerResolver ? this.opts.containerResolver() : undefined,\n }\n if (config.resolveLinks) {\n const resolved = await config.resolveLinks(baseCtx)\n if (resolved?.length) return resolved\n }\n return fallback ?? null\n }\n\n private async resolveUrl(\n config: VectorEntityConfig,\n ctx: {\n record: Record<string, any>\n customFields: Record<string, any>\n organizationId?: string | null\n tenantId: string\n },\n fallback?: string | null,\n ): Promise<string | null> {\n if (config.resolveUrl) {\n const candidate = await config.resolveUrl({\n record: ctx.record,\n customFields: ctx.customFields,\n organizationId: ctx.organizationId ?? null,\n tenantId: ctx.tenantId,\n queryEngine: this.opts.queryEngine,\n container: this.opts.containerResolver ? this.opts.containerResolver() : undefined,\n })\n if (candidate) return candidate\n }\n return fallback ?? null\n }\n\n async indexRecord(args: IndexRecordArgs): Promise<VectorIndexOperationResult> {\n const entry = this.entityConfig.get(args.entityId)\n if (!entry) {\n return {\n action: 'skipped',\n existed: false,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n reason: 'unsupported',\n }\n }\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n\n const records = await this.fetchRecord(args.entityId, [args.recordId], args.tenantId, args.organizationId)\n const raw = records.get(args.recordId)\n if (!raw) {\n const existing = await driver.getChecksum(args.entityId, args.recordId, args.tenantId)\n if (existing) {\n await driver.delete(args.entityId, args.recordId, args.tenantId)\n return {\n action: 'deleted',\n existed: true,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n }\n }\n return {\n action: 'skipped',\n existed: false,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n reason: 'missing_record',\n }\n }\n return this.indexExisting(entry, driver, args, raw as Record<string, any>)\n }\n\n async deleteRecord(args: DeleteRecordArgs): Promise<VectorIndexOperationResult> {\n const entry = this.entityConfig.get(args.entityId)\n if (!entry) {\n return {\n action: 'skipped',\n existed: false,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n reason: 'unsupported',\n }\n }\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n const scopeOrg = args.organizationId ?? null\n const existing = await driver.getChecksum(args.entityId, args.recordId, args.tenantId)\n if (existing) {\n await driver.delete(args.entityId, args.recordId, args.tenantId)\n return {\n action: 'deleted',\n existed: true,\n tenantId: args.tenantId,\n organizationId: scopeOrg,\n }\n }\n return {\n action: 'skipped',\n existed: false,\n tenantId: args.tenantId,\n organizationId: scopeOrg,\n reason: 'missing_record',\n }\n }\n\n async reindexEntity(args: { entityId: EntityId; tenantId?: string | null; organizationId?: string | null; purgeFirst?: boolean }): Promise<void> {\n const entry = this.entityConfig.get(args.entityId)\n if (!entry) return\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n\n const shouldPurge = args.purgeFirst === true\n const reindexStartedAt = new Date()\n\n if (this.opts.eventBus) {\n if (shouldPurge && driver.purge && args.tenantId) {\n await driver.purge(args.entityId, args.tenantId, args.organizationId)\n } else if (shouldPurge && !args.tenantId) {\n searchDebugWarn('vector', 'Skipping purge for multi-tenant reindex (tenant not provided)')\n }\n const payload: Record<string, unknown> = {\n entityType: args.entityId,\n }\n if (shouldPurge) {\n payload.force = true\n payload.resetCoverage = true\n }\n if (args.tenantId !== undefined) payload.tenantId = args.tenantId\n else payload.allowAllTenants = true\n if (args.organizationId !== undefined) payload.organizationId = args.organizationId\n await this.opts.eventBus.emitEvent('query_index.reindex', payload)\n return\n }\n\n if (!args.tenantId) {\n throw new Error('[vector] Reindex without tenantId requires event bus integration')\n }\n\n if (shouldPurge && driver.purge) {\n await driver.purge(args.entityId, args.tenantId, args.organizationId)\n }\n\n const pageSize = 50\n let page = 1\n const loggingEm = this.resolveEntityManager()\n for (;;) {\n const result = await this.opts.queryEngine.query(args.entityId, {\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? undefined,\n page: { page, pageSize },\n includeCustomFields: true,\n fields: this.getEnrichmentFields(args.entityId),\n skipAutoReindex: true,\n })\n if (!result.items.length) break\n for (const raw of result.items) {\n const recordId = String((raw as any).id ?? '')\n if (!recordId) continue\n const opResult = await this.indexExisting(\n entry,\n driver,\n {\n entityId: args.entityId,\n recordId,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n },\n raw as Record<string, any>,\n { skipDelete: true },\n )\n await logVectorOperation({\n em: loggingEm,\n handler: 'service:vector.reindex',\n entityType: args.entityId,\n recordId,\n result: opResult,\n })\n }\n if (result.items.length < pageSize) break\n page += 1\n }\n\n if (shouldPurge) {\n await this.removeOrphans({\n entityId: args.entityId,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n olderThan: reindexStartedAt,\n })\n }\n }\n\n async reindexAll(args: { tenantId?: string | null; organizationId?: string | null; purgeFirst?: boolean }): Promise<void> {\n for (const entityId of this.listEnabledEntities()) {\n await this.reindexEntity({ entityId, tenantId: args.tenantId, organizationId: args.organizationId ?? null, purgeFirst: args.purgeFirst })\n }\n }\n\n private resolveEntityManager(): EntityManager | null {\n if (!this.opts.containerResolver) return null\n try {\n const container = this.opts.containerResolver()\n if (!container || typeof (container as any).resolve !== 'function') return null\n const resolver = container as { resolve(name: string): unknown }\n const em = resolver.resolve('em')\n return (em as EntityManager | null) ?? null\n } catch {\n return null\n }\n }\n\n async purgeIndex(args: { tenantId: string; organizationId?: string | null; entityId?: EntityId | null }): Promise<void> {\n const targets = args.entityId ? [args.entityId] : this.listEnabledEntities()\n if (!targets.length) return\n\n const grouped = new Map<VectorDriverId, EntityId[]>()\n for (const entityId of targets) {\n const cfg = this.entityConfig.get(entityId)\n if (!cfg) continue\n const driver = this.getDriver(cfg.driverId)\n if (typeof driver.purge !== 'function') {\n throw new Error(`[vector] Driver ${cfg.driverId} does not support purging entities`)\n }\n if (!grouped.has(cfg.driverId)) grouped.set(cfg.driverId, [])\n grouped.get(cfg.driverId)!.push(entityId)\n }\n\n for (const [driverId, entityIds] of grouped.entries()) {\n const driver = this.getDriver(driverId)\n await driver.ensureReady()\n for (const entityId of entityIds) {\n await driver.purge!(entityId, args.tenantId)\n }\n }\n }\n\n async removeOrphans(args: {\n entityId: EntityId\n tenantId?: string | null\n organizationId?: string | null\n olderThan: Date\n }): Promise<number> {\n const entry = this.entityConfig.get(args.entityId)\n if (!entry) return 0\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n\n const driverAny = driver as VectorDriver & {\n removeOrphans?: (params: {\n entityId: EntityId\n tenantId?: string | null\n organizationId?: string | null\n olderThan: Date\n }) => Promise<number | void>\n }\n\n if (typeof driverAny.removeOrphans === 'function') {\n const deleted = await driverAny.removeOrphans({\n entityId: args.entityId,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n olderThan: args.olderThan,\n })\n return typeof deleted === 'number' ? deleted : 0\n }\n\n searchDebugWarn('vector', 'Driver does not support orphan cleanup', { driverId: entry.driverId })\n return 0\n }\n\n async countIndexEntries(args: {\n tenantId: string\n organizationId?: string | null\n entityId?: EntityId\n driverId?: VectorDriverId\n }): Promise<number> {\n if (!args.tenantId) return 0\n const targetEntity = args.entityId ? this.entityConfig.get(args.entityId) : null\n if (args.entityId && !targetEntity) {\n return 0\n }\n const driverId =\n args.driverId ??\n (targetEntity ? targetEntity.driverId : this.defaultDriverId)\n const driver = this.getDriver(driverId)\n await driver.ensureReady()\n const countParams = {\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n entityId: args.entityId,\n }\n if (typeof driver.count === 'function') {\n try {\n return await driver.count(countParams)\n } catch (err) {\n searchDebugWarn('vector', 'Driver count failed, falling back to list', {\n driverId,\n error: err instanceof Error ? err.message : err,\n })\n }\n }\n if (typeof driver.list === 'function') {\n const limit = 1000\n let offset = 0\n let total = 0\n for (;;) {\n const batch = await driver.list({\n tenantId: countParams.tenantId,\n organizationId: countParams.organizationId,\n entityId: countParams.entityId,\n limit,\n offset,\n orderBy: 'created',\n })\n const size = batch.length\n total += size\n if (size < limit) break\n offset += limit\n }\n return total\n }\n searchDebugWarn('vector', 'Driver does not support counting or listing index entries', { driverId })\n return 0\n }\n\n async listIndexEntries(args: {\n tenantId: string\n organizationId?: string | null\n entityId?: EntityId\n limit?: number\n offset?: number\n driverId?: VectorDriverId\n }): Promise<VectorIndexEntry[]> {\n const targetEntity = args.entityId ? this.entityConfig.get(args.entityId) : null\n if (args.entityId && !targetEntity) {\n return []\n }\n const driverId =\n args.driverId ??\n (targetEntity ? targetEntity.driverId : this.defaultDriverId)\n const driver = this.getDriver(driverId)\n if (typeof driver.list !== 'function') {\n throw new Error(`[vector] Driver ${driverId} does not support listing index entries`)\n }\n await driver.ensureReady()\n const list = await driver.list({\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n entityId: args.entityId,\n limit: args.limit,\n offset: args.offset,\n orderBy: 'updated',\n })\n if (!list.length) {\n return []\n }\n\n const decrypted = await Promise.all(\n list.map(async (entry) => {\n const decryptedResult = await this.decryptResultFields({\n tenantId: args.tenantId,\n organizationId: entry.organizationId ?? null,\n resultTitle: entry.resultTitle,\n resultSubtitle: entry.resultSubtitle ?? null,\n resultIcon: entry.resultIcon ?? null,\n resultSnapshot: entry.resultSnapshot ?? null,\n primaryLinkHref: entry.primaryLinkHref ?? null,\n primaryLinkLabel: entry.primaryLinkLabel ?? null,\n links: (entry.links as any) ?? null,\n payload: (entry.payload as any) ?? null,\n })\n return {\n ...entry,\n resultTitle: decryptedResult.resultTitle,\n resultSubtitle: decryptedResult.resultSubtitle,\n resultIcon: decryptedResult.resultIcon,\n resultSnapshot: decryptedResult.resultSnapshot,\n primaryLinkHref: decryptedResult.primaryLinkHref,\n primaryLinkLabel: decryptedResult.primaryLinkLabel,\n links: decryptedResult.links as any,\n payload: decryptedResult.payload as any,\n metadata: (decryptedResult.payload as any) ?? null,\n }\n }),\n )\n\n return decrypted.map((entry) => {\n const presenter = {\n title: entry.resultTitle,\n subtitle: entry.resultSubtitle ?? undefined,\n icon: entry.resultIcon ?? undefined,\n badge: entry.presenter?.badge ?? entry.resultBadge ?? undefined,\n }\n const links = entry.links ?? (entry.primaryLinkHref\n ? [{ href: entry.primaryLinkHref, label: entry.primaryLinkLabel ?? entry.resultTitle, kind: 'primary' as const }]\n : null)\n const url = entry.url ?? entry.primaryLinkHref ?? null\n const metadata = entry.metadata ?? (entry.resultSnapshot ? { snapshot: entry.resultSnapshot } : null)\n return {\n ...entry,\n driverId,\n presenter,\n links,\n url,\n metadata,\n score: entry.score ?? null,\n }\n })\n }\n\n async search(request: VectorQueryRequest): Promise<VectorSearchHit[]> {\n const driverId = request.driverId ?? this.defaultDriverId\n const driver = this.getDriver(driverId)\n await driver.ensureReady()\n if (!this.opts.embeddingService.available) {\n throw new Error('[vector] Embedding service unavailable (missing OPENAI_API_KEY)')\n }\n const embedding = await this.opts.embeddingService.createEmbedding(request.query)\n const hits = await driver.query({\n vector: embedding,\n limit: request.limit ?? 10,\n filter: {\n tenantId: request.tenantId,\n organizationId: request.organizationId ?? null,\n entityIds: undefined,\n },\n })\n\n if (!hits.length) return []\n\n const decrypted = await Promise.all(\n hits.map(async (hit) => {\n const decryptedResult = await this.decryptResultFields({\n tenantId: request.tenantId,\n organizationId: hit.organizationId ?? request.organizationId ?? null,\n resultTitle: hit.resultTitle,\n resultSubtitle: hit.resultSubtitle ?? null,\n resultIcon: hit.resultIcon ?? null,\n resultSnapshot: hit.resultSnapshot ?? null,\n primaryLinkHref: hit.primaryLinkHref ?? null,\n primaryLinkLabel: hit.primaryLinkLabel ?? null,\n links: (hit.links as any) ?? null,\n payload: (hit.payload as any) ?? null,\n })\n return {\n ...hit,\n resultTitle: decryptedResult.resultTitle,\n resultSubtitle: decryptedResult.resultSubtitle,\n resultIcon: decryptedResult.resultIcon,\n resultSnapshot: decryptedResult.resultSnapshot,\n primaryLinkHref: decryptedResult.primaryLinkHref,\n primaryLinkLabel: decryptedResult.primaryLinkLabel,\n links: decryptedResult.links as any,\n payload: decryptedResult.payload as any,\n }\n }),\n )\n\n return decrypted.map((hit) => {\n const presenter = {\n title: hit.resultTitle,\n subtitle: hit.resultSubtitle ?? undefined,\n icon: hit.resultIcon ?? undefined,\n badge: hit.presenter?.badge ?? hit.resultBadge ?? undefined,\n }\n const links = hit.links ?? (hit.primaryLinkHref\n ? [{ href: hit.primaryLinkHref, label: hit.primaryLinkLabel ?? hit.resultTitle, kind: 'primary' as const }]\n : null)\n const url = hit.url ?? hit.primaryLinkHref ?? null\n const metadata = hit.payload ?? (hit.resultSnapshot ? { snapshot: hit.resultSnapshot } : null)\n return {\n entityId: hit.entityId,\n recordId: hit.recordId,\n score: hit.score,\n url,\n presenter,\n links,\n driverId,\n metadata,\n }\n })\n }\n}\n"],
5
- "mappings": "AAiBA,SAAS,uBAAuB;AAEhC,SAAS,0BAA0B;AACnC,SAAS,aAAa,uBAAuB;AAG7C,MAAM,oCAAoC;AAE1C,MAAM,8BAA8B,oBAAI,IAAY;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,MAAM,yBAAqD;AAAA,EACzD,6BAA6B;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,8BAA8B,CAAC,MAAM,mBAAmB,aAAa,aAAa,QAAQ,mBAAmB,kBAAkB;AAAA,EAC/H,+BAA+B,CAAC,MAAM,mBAAmB,aAAa,aAAa,iBAAiB,WAAW,QAAQ,SAAS;AAAA,EAChI,2BAA2B,CAAC,MAAM,mBAAmB,aAAa,SAAS,kBAAkB,UAAU,gBAAgB,gBAAgB;AAAA,EACvI,gCAAgC,CAAC,MAAM,mBAAmB,aAAa,aAAa,WAAW,aAAa;AAAA,EAC5G,qCAAqC;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,sCAAsC;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAqCO,MAAM,mBAAmB;AAAA,EAK9B,YAA6B,MAAiC;AAAjC;AAJ7B,SAAiB,YAAY,oBAAI,IAAkC;AACnE,SAAiB,eAAe,oBAAI,IAAwE;AAI1G,eAAW,UAAU,KAAK,SAAS;AACjC,WAAK,UAAU,IAAI,OAAO,IAAI,MAAM;AAAA,IACtC;AACA,SAAK,kBAAkB,KAAK,mBAAmB;AAC/C,eAAW,gBAAgB,KAAK,eAAe;AAC7C,YAAM,WAAW,aAAa,mBAAmB,KAAK;AACtD,iBAAW,UAAU,aAAa,YAAY,CAAC,GAAG;AAChD,YAAI,CAAC,QAAQ,SAAU;AACvB,YAAI,OAAO,YAAY,MAAO;AAC9B,cAAM,eAAe,OAAO,YAAY;AACxC,aAAK,aAAa,IAAI,OAAO,UAAU,EAAE,QAAQ,QAAQ,UAAU,aAAa,CAAC;AAAA,MACnF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,2BAA+D;AACrE,QAAI,CAAC,KAAK,KAAK,kBAAmB,QAAO;AACzC,QAAI;AACF,YAAM,YAAY,KAAK,KAAK,kBAAkB;AAC9C,UAAI,CAAC,aAAa,OAAO,UAAU,YAAY,WAAY,QAAO;AAClE,aAAO,UAAU,QAAQ,yBAAyB;AAAA,IACpD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,MAoB/B;AACD,UAAM,UAAU,KAAK,yBAAyB;AAC9C,QAAI,CAAC,WAAW,CAAC,QAAQ,YAAY,GAAG;AACtC,aAAO;AAAA,QACL,aAAa,KAAK;AAAA,QAClB,gBAAgB,KAAK;AAAA,QACrB,YAAY,KAAK;AAAA,QACjB,gBAAgB,KAAK;AAAA,QACrB,iBAAiB,KAAK;AAAA,QACtB,kBAAkB,KAAK;AAAA,QACvB,OAAO,KAAK;AAAA,QACZ,SAAS,KAAK;AAAA,MAChB;AAAA,IACF;AACA,QAAI;AACF,YAAM,YAAY,MAAM,QAAQ;AAAA,QAC9B;AAAA,QACA;AAAA,UACE,aAAa,KAAK;AAAA,UAClB,gBAAgB,KAAK;AAAA,UACrB,YAAY,KAAK;AAAA,UACjB,gBAAgB,KAAK;AAAA,UACrB,iBAAiB,KAAK;AAAA,UACtB,kBAAkB,KAAK;AAAA,UACvB,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,QAChB;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,aAAO;AAAA,QACL,aAAa,OAAQ,UAAkB,eAAe,KAAK,WAAW;AAAA,QACtE,gBAAkB,UAAkB,kBAAkB,KAAK;AAAA,QAC3D,YAAc,UAAkB,cAAc,KAAK;AAAA,QACnD,gBAAkB,UAAkB,kBAAkB,KAAK;AAAA,QAC3D,iBAAmB,UAAkB,mBAAmB,KAAK;AAAA,QAC7D,kBAAoB,UAAkB,oBAAoB,KAAK;AAAA,QAC/D,OAAS,UAAkB,SAAS,KAAK;AAAA,QACzC,SAAW,UAAkB,WAAW,KAAK;AAAA,MAC/C;AAAA,IACF,SAAS,KAAK;AACZ,sBAAgB,gBAAgB,iEAAiE;AAAA,QAC/F,UAAU;AAAA,QACV,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK;AAAA,QACrB,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AACD,YAAM,eAAe,QACjB,MACA,IAAI,MAAM,2CAA2C;AAAA,IAC3D;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,MAoB/B;AACD,UAAM,UAAU,KAAK,yBAAyB;AAC9C,QAAI,CAAC,WAAW,CAAC,QAAQ,YAAY,KAAK,OAAO,QAAQ,yBAAyB,YAAY;AAC5F,aAAO;AAAA,QACL,aAAa,KAAK;AAAA,QAClB,gBAAgB,KAAK;AAAA,QACrB,YAAY,KAAK;AAAA,QACjB,gBAAgB,KAAK;AAAA,QACrB,iBAAiB,KAAK;AAAA,QACtB,kBAAkB,KAAK;AAAA,QACvB,OAAO,KAAK;AAAA,QACZ,SAAS,KAAK;AAAA,MAChB;AAAA,IACF;AACA,QAAI;AACF,YAAM,YAAY,MAAM,QAAQ;AAAA,QAC9B;AAAA,QACA;AAAA,UACE,aAAa,KAAK;AAAA,UAClB,gBAAgB,KAAK;AAAA,UACrB,YAAY,KAAK;AAAA,UACjB,gBAAgB,KAAK;AAAA,UACrB,iBAAiB,KAAK;AAAA,UACtB,kBAAkB,KAAK;AAAA,UACvB,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,QAChB;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,aAAO;AAAA,QACL,aAAa,OAAQ,UAAkB,eAAe,KAAK,WAAW;AAAA,QACtE,gBAAkB,UAAkB,kBAAkB,KAAK;AAAA,QAC3D,YAAc,UAAkB,cAAc,KAAK;AAAA,QACnD,gBAAkB,UAAkB,kBAAkB,KAAK;AAAA,QAC3D,iBAAmB,UAAkB,mBAAmB,KAAK;AAAA,QAC7D,kBAAoB,UAAkB,oBAAoB,KAAK;AAAA,QAC/D,OAAS,UAAkB,SAAS,KAAK;AAAA,QACzC,SAAW,UAAkB,WAAW,KAAK;AAAA,MAC/C;AAAA,IACF,SAAS,KAAK;AACZ,sBAAgB,gBAAgB,8EAA8E;AAAA,QAC5G,UAAU;AAAA,QACV,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK;AAAA,QACrB,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AACD,YAAM,eAAe,QACjB,MACA,IAAI,MAAM,2CAA2C;AAAA,IAC3D;AAAA,EACF;AAAA,EAEA,sBAAkC;AAChC,WAAO,MAAM,KAAK,KAAK,aAAa,KAAK,CAAC;AAAA,EAC5C;AAAA,EAEA,MAAM,kBAAkB,UAAoC;AAC1D,QAAI,UAAU;AACZ,YAAM,QAAQ,KAAK,aAAa,IAAI,QAAQ;AAC5C,UAAI,CAAC,MAAO;AACZ,YAAM,SAAS,KAAK,UAAU,MAAM,QAAQ;AAC5C,YAAM,OAAO,YAAY;AACzB;AAAA,IACF;AACA,UAAM,gBAAgB,oBAAI,IAAoB;AAC9C,eAAW,SAAS,KAAK,aAAa,OAAO,GAAG;AAC9C,oBAAc,IAAI,MAAM,QAAQ;AAAA,IAClC;AACA,QAAI,CAAC,cAAc,KAAM,eAAc,IAAI,KAAK,eAAe;AAC/D,UAAM,QAAQ,IAAI,MAAM,KAAK,aAAa,EAAE,IAAI,OAAO,aAAa;AAClE,UAAI;AACF,cAAM,SAAS,KAAK,UAAU,QAAQ;AACtC,cAAM,OAAO,YAAY;AAAA,MAC3B,SAAS,KAAK;AACZ,wBAAgB,UAAU,qCAAqC,EAAE,UAAU,OAAO,eAAe,QAAQ,IAAI,UAAU,IAAI,CAAC;AAAA,MAC9H;AAAA,IACF,CAAC,CAAC;AAAA,EACJ;AAAA,EAEQ,UAAU,UAAwC;AACxD,UAAM,SAAS,KAAK,UAAU,IAAI,QAAQ;AAC1C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,mBAAmB,QAAQ,oBAAoB;AAAA,IACjE;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,oBAAoB,UAA0C;AACpE,UAAM,QAAQ,uBAAuB,QAAQ;AAC7C,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,SAAS,oBAAI,IAAY,CAAC,MAAM,GAAG,KAAK,CAAC;AAC/C,WAAO,MAAM,KAAK,MAAM;AAAA,EAC1B;AAAA,EAEA,MAAc,YAAY,UAAoB,WAAqB,UAAkB,gBAAgC;AACnH,UAAM,UAA+B,EAAE,IAAI,EAAE,KAAK,UAAU,EAAE;AAC9D,UAAM,SAAS,MAAM,KAAK,KAAK,YAAY,MAAM,UAAU;AAAA,MACzD;AAAA,MACA,gBAAgB,kBAAkB;AAAA,MAClC;AAAA,MACA,qBAAqB;AAAA,MACrB,QAAQ,KAAK,oBAAoB,QAAQ;AAAA,MACzC,iBAAiB;AAAA,IACnB,CAAC;AACD,UAAM,OAAO,oBAAI,IAAiC;AAClD,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,MAAM,OAAQ,KAAa,MAAM,EAAE;AACzC,UAAI,CAAC,IAAK;AACV,WAAK,IAAI,KAAK,IAA2B;AAAA,IAC3C;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,qBAAqB,UAAoB,KAA0B;AACzE,UAAM,SAA8B,CAAC;AACrC,UAAM,eAAoC,CAAC;AAC3C,UAAM,WAAW,oBAAI,IAAqB;AAE1C,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,UAAI,IAAI,WAAW,KAAK,KAAK,IAAI,SAAS,YAAY,GAAG;AACvD,cAAM,OAAO,IAAI,QAAQ,eAAe,EAAE;AAC1C,iBAAS,IAAI,MAAM,QAAQ,KAAK,CAAC;AACjC;AAAA,MACF;AACA,UAAI,IAAI,WAAW,KAAK,GAAG;AACzB,qBAAa,IAAI,MAAM,CAAC,CAAC,IAAI;AAC7B;AAAA,MACF;AACA,aAAO,GAAG,IAAI;AAAA,IAChB;AAEA,eAAW,CAAC,KAAK,OAAO,KAAK,SAAS,QAAQ,GAAG;AAC/C,YAAM,OAAO,IAAI,MAAM,CAAC;AACxB,UAAI,QAAQ,aAAa,IAAI,KAAK,QAAQ,CAAC,MAAM,QAAQ,aAAa,IAAI,CAAC,KAAK,SAAS;AACvF,qBAAa,IAAI,IAAI,CAAC,aAAa,IAAI,CAAC;AAAA,MAC1C;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,QAAQ,OAAO,YAAY,QAAQ,SAAS,SAAS,kBAAkB,GAAG;AAChG,sBAAgB,gBAAgB,gDAAgD;AAAA,QAC9E,IAAI,OAAO;AAAA,QACX,MAAM,OAAO,KAAK,MAAM;AAAA,MAC1B,CAAC;AAAA,IACH;AAEA,WAAO,EAAE,QAAQ,aAAa;AAAA,EAChC;AAAA,EAEA,MAAc,cACZ,OACA,QACA,MACA,KACA,OAAiC,CAAC,GACG;AACrC,UAAM,WAAW,KAAK,kBAAkB;AACxC,UAAM,EAAE,QAAQ,aAAa,IAAI,KAAK,qBAAqB,KAAK,UAAU,GAAG;AAC7E,UAAM,gBAAgB,aAAa,OAAO,mBAAmB,OAAO,kBAAkB;AACtF,UAAM,SAAS,MAAM,KAAK,cAAc,KAAK,UAAU,MAAM,QAAQ;AAAA,MACnE;AAAA,MACA;AAAA,MACA,UAAU,KAAK;AAAA,MACf,gBAAgB;AAAA,IAClB,CAAC;AACD,QAAI,CAAC,QAAQ;AACX,YAAM,WAAW,MAAM,OAAO,YAAY,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ;AACrF,UAAI,CAAC,KAAK,cAAc,UAAU;AAChC,cAAM,OAAO,OAAO,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ;AAC/D,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,UAAU,KAAK;AAAA,UACf,gBAAgB;AAAA,QAClB;AAAA,MACF;AACA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS,QAAQ,QAAQ;AAAA,QACzB,UAAU,KAAK;AAAA,QACf,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,UAAM,iBAAiB,OAAO,kBAAkB,EAAE,QAAQ,aAAa;AACvE,UAAM,WAAW,gBAAgB,cAAc;AAC/C,UAAM,UAAU,MAAM,OAAO,YAAY,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ;AACpF,QAAI,WAAW,YAAY,UAAU;AACnC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,UAAU,KAAK;AAAA,QACf,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV;AAAA,IACF;AACA,QAAI,CAAC,KAAK,KAAK,iBAAiB,WAAW;AACzC,YAAM,IAAI,MAAM,iEAAiE;AAAA,IACnF;AACA,UAAM,YAAY,MAAM,KAAK,KAAK,iBAAiB,gBAAgB,OAAO,KAAK;AAC/E,UAAM,YAAY,MAAM,KAAK,iBAAiB,MAAM,QAAQ;AAAA,MAC1D;AAAA,MACA;AAAA,MACA,UAAU,KAAK;AAAA,MACf,gBAAgB;AAAA,IAClB,GAAG,OAAO,aAAa,IAAI;AAC3B,QAAI,CAAC,WAAW,OAAO;AACrB,sBAAgB,gBAAgB,2BAA2B;AAAA,QACzD,UAAU,KAAK;AAAA,QACf,UAAU,KAAK;AAAA,QACf,cAAc;AAAA,UACZ,cAAc,OAAO;AAAA,UACrB,aAAa,OAAO;AAAA,UACpB,MAAM,OAAO;AAAA,UACb,OAAO,OAAO;AAAA,UACd,SAAS,OAAO;AAAA,UAChB,MAAM,OAAO;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,QAAQ,MAAM,KAAK,aAAa,MAAM,QAAQ;AAAA,MAClD;AAAA,MACA;AAAA,MACA,UAAU,KAAK;AAAA,MACf,gBAAgB;AAAA,IAClB,GAAG,OAAO,SAAS,IAAI;AACvB,UAAM,MAAM,MAAM,KAAK,WAAW,MAAM,QAAQ;AAAA,MAC9C;AAAA,MACA;AAAA,MACA,UAAU,KAAK;AAAA,MACf,gBAAgB;AAAA,IAClB,CAAC;AAED,UAAM,sBAAsB,KAAK,gBAAgB,WAAW,QAAQ,cAAc,KAAK,UAAU,KAAK,QAAQ;AAC9G,UAAM,kBAAkB,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,QAAQ;AACvE,UAAM,WAAW,KAAK,eAAe,QAAQ,YAAY;AACzD,UAAM,iBAAiB,KAAK,mBAAmB,qBAAqB,QAAQ,cAAc,KAAK,QAAQ;AACvG,UAAM,oBAAoB,oBAAoB,YAAY,YAAY;AACtE,UAAM,gBAAgB,oBAAoB,QAAQ,KAAK,eAAe,KAAK,QAAQ;AACnF,UAAM,cAAc,oBAAoB,SAAS;AACjD,UAAM,cAAc,KAAK,mBAAmB,iBAAiB,KAAK,cAAc;AAEhF,UAAM,kBAAkB,MAAM,KAAK,oBAAoB;AAAA,MACrD,UAAU,KAAK;AAAA,MACf,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,YAAY,iBAAiB;AAAA,MAC7B,gBAAgB,YAAY;AAAA,MAC5B,iBAAiB,aAAa,QAAQ;AAAA,MACtC,kBAAkB,aAAa,SAAS;AAAA,MACxC,OAAO;AAAA,MACP,SAAS,OAAO,WAAW;AAAA,IAC7B,CAAC;AAED,UAAM,sBAA6C;AAAA,MACjD,OAAO,gBAAgB;AAAA,MACvB,UAAU,gBAAgB,kBAAkB;AAAA,MAC5C,MAAM,gBAAgB,cAAc;AAAA,MACpC,OAAO,eAAe;AAAA,IACxB;AAEA,gBAAY,sBAAsB,8BAA8B;AAAA,MAC9D,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,gBAAgB;AAAA,IAClB,CAAC;AACD,UAAM,OAAO,OAAO;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,WAAW;AAAA,MACX,OAAQ,gBAAgB,SAAiB;AAAA,MACzC,SAAU,gBAAgB,WAAmB,OAAO,WAAW;AAAA,MAC/D,aAAa,gBAAgB;AAAA,MAC7B,gBAAgB,gBAAgB;AAAA,MAChC,YAAY,gBAAgB,cAAc;AAAA,MAC1C;AAAA,MACA,gBAAgB,gBAAgB,kBAAkB;AAAA,MAClD,iBAAiB,gBAAgB,mBAAmB,aAAa,QAAQ;AAAA,MACzE,kBAAkB,gBAAgB,oBAAoB;AAAA,IACxD,CAAC;AAED,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS,CAAC;AAAA,MACV,SAAS;AAAA,MACT,UAAU,KAAK;AAAA,MACf,gBAAgB;AAAA,IAClB;AAAA,EACF;AAAA,EAEQ,gBACN,WACA,QACA,cACA,UACA,UACuB;AACvB,QAAI,WAAW,MAAO,QAAO;AAC7B,UAAM,WAAW,KAAK,uBAAuB,QAAQ,cAAc,UAAU,QAAQ;AACrF,WAAO;AAAA,EACT;AAAA,EAEQ,mBACN,WACA,QACA,cACA,UACQ;AACR,UAAM,YACJ,UAAU,SACV,OAAO,gBACP,OAAO,eACP,OAAO,QACP,OAAO,SACP,OAAO,WACP,aAAa,SACb,aAAa,QACb;AACF,UAAM,OAAO,OAAO,SAAS,EAAE,KAAK;AACpC,WAAO,KAAK,SAAS,OAAO;AAAA,EAC9B;AAAA,EAEQ,uBACN,QACA,cACA,UACA,UACuB;AACvB,UAAM,iBACJ,OAAO,gBACP,OAAO,eACP,OAAO,QACP,OAAO,SACP,OAAO,WACP;AACF,UAAM,oBACJ,OAAO,eACP,OAAO,WACP,OAAO,QACP,aAAa,WACb,aAAa,eACb;AACF,UAAM,OAAO,OAAO,OAAO,SAAS,WAChC,KAAK,cAAc,OAAO,IAAI,IAC9B,KAAK,eAAe,QAAQ;AAChC,WAAO;AAAA,MACL,OAAO,OAAO,cAAc;AAAA,MAC5B,UAAU,oBAAoB,OAAO,iBAAiB,IAAI;AAAA,MAC1D,MAAM,QAAQ;AAAA,IAChB;AAAA,EACF;AAAA,EAEQ,cAAc,MAAqC;AACzD,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,aAAa,KAAK,YAAY;AACpC,QAAI,eAAe,SAAU,QAAO;AACpC,QAAI,eAAe,aAAa,eAAe,eAAgB,QAAO;AACtE,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe,UAAmC;AACxD,QAAI,SAAS,WAAW,yBAAyB,EAAG,QAAO;AAC3D,QAAI,SAAS,WAAW,4BAA4B,EAAG,QAAO;AAC9D,QAAI,SAAS,WAAW,6BAA6B,EAAG,QAAO;AAC/D,QAAI,SAAS,WAAW,yBAAyB,EAAG,QAAO;AAC3D,WAAO;AAAA,EACT;AAAA,EAEQ,mBACN,OACA,KACA,eACwC;AACxC,QAAI,OAAO,QAAQ;AACjB,YAAM,UAAU,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,SAAS,KAAK,MAAM,CAAC;AACxE,UAAI,SAAS,MAAM;AACjB,eAAO,EAAE,MAAM,QAAQ,MAAM,OAAO,QAAQ,SAAS,cAAc;AAAA,MACrE;AAAA,IACF;AACA,QAAI,KAAK;AACP,aAAO,EAAE,MAAM,KAAK,OAAO,cAAc;AAAA,IAC3C;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,eACN,QACA,cACe;AACf,UAAM,aAAa;AAAA,MACjB,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AACA,eAAW,SAAS,YAAY;AAC9B,UAAI,OAAO,UAAU,UAAU;AAC7B,cAAM,UAAU,MAAM,KAAK;AAC3B,YAAI,QAAQ,OAAQ,QAAO;AAAA,MAC7B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,mBACN,UACA,SACA,aACmB;AACnB,UAAM,EAAE,QAAQ,aAAa,IAAI;AACjC,UAAM,QAAkB,CAAC;AAEzB,UAAM,sBAAsB,aAAa,aAAa,IAAI,IAAI,YAAY,UAAU,IAAI;AACxF,UAAM,iBAAiB,oBAAI,IAAY;AAAA,MACrC,GAAI,aAAa,YAAY,CAAC;AAAA,MAC9B,GAAI,aAAa,YAAY,CAAC;AAAA,IAChC,CAAC;AAED,UAAM,kBAAkB,CAAC,UAA2B;AAClD,UAAI,4BAA4B,IAAI,KAAK,EAAG,QAAO;AACnD,UAAI,eAAe,IAAI,KAAK,EAAG,QAAO;AACtC,UAAI,uBAAuB,CAAC,oBAAoB,IAAI,KAAK,EAAG,QAAO;AACnE,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,CAAC,OAAe,UAAmB;AACnD,UAAI,UAAU,QAAQ,UAAU,OAAW;AAC3C,UAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,EAAG;AAC5D,UAAI,OAAO,UAAU,UAAU;AAC7B,cAAM,KAAK,GAAG,KAAK,KAAK,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,MACjD,OAAO;AACL,cAAM,KAAK,GAAG,KAAK,KAAK,KAAK,EAAE;AAAA,MACjC;AAAA,IACF;AAEA,UAAM,kBAAkB,CAAC,SAAS,QAAQ,eAAe,WAAW,SAAS;AAC7E,eAAW,SAAS,iBAAiB;AACnC,UAAI,CAAC,gBAAgB,KAAK,EAAG;AAC7B,UAAI,OAAO,KAAK,KAAK,KAAM,WAAU,OAAO,OAAO,KAAK,CAAC;AAAA,IAC3D;AAEA,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAI,gBAAgB,SAAS,GAAG,EAAG;AACnC,UAAI,CAAC,gBAAgB,GAAG,EAAG;AAC3B,gBAAU,KAAK,KAAK;AAAA,IACtB;AAEA,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,YAAY,GAAG;AACvD,UAAI,CAAC,gBAAgB,GAAG,EAAG;AAC3B,gBAAU,UAAU,GAAG,IAAI,KAAK;AAAA,IAClC;AAEA,QAAI,MAAM,WAAW,GAAG;AACtB,YAAM,KAAK,GAAG,QAAQ,IAAI,OAAO,MAAM,EAAE,EAAE;AAAA,IAC7C;AAEA,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS;AAAA,MACT,gBAAgB,EAAE,QAAQ,aAAa;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,UAAoB,QAA4B,KAKtC;AACpC,UAAM,UAAU;AAAA,MACd,QAAQ,IAAI;AAAA,MACZ,cAAc,IAAI;AAAA,MAClB,gBAAgB,IAAI,kBAAkB;AAAA,MACtC,UAAU,IAAI;AAAA,MACd,aAAa,KAAK,KAAK;AAAA,MACvB,WAAW,KAAK,KAAK,oBAAoB,KAAK,KAAK,kBAAkB,IAAI;AAAA,IAC3E;AACA,QAAI,OAAO,aAAa;AACtB,YAAM,QAAQ,MAAM,OAAO,YAAY,OAAO;AAC9C,UAAI,MAAO,QAAO;AAClB,aAAO;AAAA,IACT;AACA,WAAO,KAAK,mBAAmB,UAAU,EAAE,QAAQ,IAAI,QAAQ,cAAc,IAAI,aAAa,GAAG,OAAO,WAAW;AAAA,EACrH;AAAA,EAEA,MAAc,iBACZ,QACA,KAMA,UACuC;AACvC,UAAM,UAAU;AAAA,MACd,QAAQ,IAAI;AAAA,MACZ,cAAc,IAAI;AAAA,MAClB,gBAAgB,IAAI,kBAAkB;AAAA,MACtC,UAAU,IAAI;AAAA,MACd,aAAa,KAAK,KAAK;AAAA,MACvB,WAAW,KAAK,KAAK,oBAAoB,KAAK,KAAK,kBAAkB,IAAI;AAAA,IAC3E;AACA,QAAI,OAAO,cAAc;AACvB,YAAM,YAAY,MAAM,OAAO,aAAa,OAAO;AACnD,UAAI,UAAW,QAAO;AAAA,IACxB;AACA,QAAI,SAAU,QAAO;AACrB,UAAM,WAAW,IAAI,OAAO,eAAe,IAAI,OAAO,SAAS,IAAI,OAAO;AAC1E,QAAI,OAAO,aAAa,YAAY,SAAS,KAAK,EAAE,SAAS,GAAG;AAC9D,YAAM,WAAW,IAAI,OAAO,eAAe,IAAI,OAAO;AACtD,aAAO;AAAA,QACL,OAAO;AAAA,QACP,UAAU,OAAO,aAAa,WAAW,WAAW;AAAA,MACtD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,aACZ,QACA,KAMA,UACwC;AACxC,UAAM,UAAU;AAAA,MACd,QAAQ,IAAI;AAAA,MACZ,cAAc,IAAI;AAAA,MAClB,gBAAgB,IAAI,kBAAkB;AAAA,MACtC,UAAU,IAAI;AAAA,MACd,aAAa,KAAK,KAAK;AAAA,MACvB,WAAW,KAAK,KAAK,oBAAoB,KAAK,KAAK,kBAAkB,IAAI;AAAA,IAC3E;AACA,QAAI,OAAO,cAAc;AACvB,YAAM,WAAW,MAAM,OAAO,aAAa,OAAO;AAClD,UAAI,UAAU,OAAQ,QAAO;AAAA,IAC/B;AACA,WAAO,YAAY;AAAA,EACrB;AAAA,EAEA,MAAc,WACZ,QACA,KAMA,UACwB;AACxB,QAAI,OAAO,YAAY;AACrB,YAAM,YAAY,MAAM,OAAO,WAAW;AAAA,QACxC,QAAQ,IAAI;AAAA,QACZ,cAAc,IAAI;AAAA,QAClB,gBAAgB,IAAI,kBAAkB;AAAA,QACtC,UAAU,IAAI;AAAA,QACd,aAAa,KAAK,KAAK;AAAA,QACvB,WAAW,KAAK,KAAK,oBAAoB,KAAK,KAAK,kBAAkB,IAAI;AAAA,MAC3E,CAAC;AACD,UAAI,UAAW,QAAO;AAAA,IACxB;AACA,WAAO,YAAY;AAAA,EACrB;AAAA,EAEA,MAAM,YAAY,MAA4D;AAC5E,UAAM,QAAQ,KAAK,aAAa,IAAI,KAAK,QAAQ;AACjD,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK,kBAAkB;AAAA,QACvC,QAAQ;AAAA,MACV;AAAA,IACF;AACA,UAAM,SAAS,KAAK,UAAU,MAAM,QAAQ;AAC5C,UAAM,OAAO,YAAY;AAEzB,UAAM,UAAU,MAAM,KAAK,YAAY,KAAK,UAAU,CAAC,KAAK,QAAQ,GAAG,KAAK,UAAU,KAAK,cAAc;AACzG,UAAM,MAAM,QAAQ,IAAI,KAAK,QAAQ;AACrC,QAAI,CAAC,KAAK;AACR,YAAM,WAAW,MAAM,OAAO,YAAY,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ;AACrF,UAAI,UAAU;AACZ,cAAM,OAAO,OAAO,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ;AAC/D,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,UAAU,KAAK;AAAA,UACf,gBAAgB,KAAK,kBAAkB;AAAA,QACzC;AAAA,MACF;AACA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK,kBAAkB;AAAA,QACvC,QAAQ;AAAA,MACV;AAAA,IACF;AACA,WAAO,KAAK,cAAc,OAAO,QAAQ,MAAM,GAA0B;AAAA,EAC3E;AAAA,EAEA,MAAM,aAAa,MAA6D;AAC9E,UAAM,QAAQ,KAAK,aAAa,IAAI,KAAK,QAAQ;AACjD,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK,kBAAkB;AAAA,QACvC,QAAQ;AAAA,MACV;AAAA,IACF;AACA,UAAM,SAAS,KAAK,UAAU,MAAM,QAAQ;AAC5C,UAAM,OAAO,YAAY;AACzB,UAAM,WAAW,KAAK,kBAAkB;AACxC,UAAM,WAAW,MAAM,OAAO,YAAY,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ;AACrF,QAAI,UAAU;AACZ,YAAM,OAAO,OAAO,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ;AAC/D,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,UAAU,KAAK;AAAA,QACf,gBAAgB;AAAA,MAClB;AAAA,IACF;AACA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,UAAU,KAAK;AAAA,MACf,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,MAA6H;AAC/I,UAAM,QAAQ,KAAK,aAAa,IAAI,KAAK,QAAQ;AACjD,QAAI,CAAC,MAAO;AACZ,UAAM,SAAS,KAAK,UAAU,MAAM,QAAQ;AAC5C,UAAM,OAAO,YAAY;AAEzB,UAAM,cAAc,KAAK,eAAe;AACxC,UAAM,mBAAmB,oBAAI,KAAK;AAElC,QAAI,KAAK,KAAK,UAAU;AACtB,UAAI,eAAe,OAAO,SAAS,KAAK,UAAU;AAChD,cAAM,OAAO,MAAM,KAAK,UAAU,KAAK,UAAU,KAAK,cAAc;AAAA,MACtE,WAAW,eAAe,CAAC,KAAK,UAAU;AACxC,wBAAgB,UAAU,+DAA+D;AAAA,MAC3F;AACA,YAAM,UAAmC;AAAA,QACvC,YAAY,KAAK;AAAA,MACnB;AACA,UAAI,aAAa;AACf,gBAAQ,QAAQ;AAChB,gBAAQ,gBAAgB;AAAA,MAC1B;AACA,UAAI,KAAK,aAAa,OAAW,SAAQ,WAAW,KAAK;AAAA,UACpD,SAAQ,kBAAkB;AAC/B,UAAI,KAAK,mBAAmB,OAAW,SAAQ,iBAAiB,KAAK;AACrE,YAAM,KAAK,KAAK,SAAS,UAAU,uBAAuB,OAAO;AACjE;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,UAAU;AAClB,YAAM,IAAI,MAAM,kEAAkE;AAAA,IACpF;AAEA,QAAI,eAAe,OAAO,OAAO;AAC/B,YAAM,OAAO,MAAM,KAAK,UAAU,KAAK,UAAU,KAAK,cAAc;AAAA,IACtE;AAEA,UAAM,WAAW;AACjB,QAAI,OAAO;AACX,UAAM,YAAY,KAAK,qBAAqB;AAC5C,eAAS;AACP,YAAM,SAAS,MAAM,KAAK,KAAK,YAAY,MAAM,KAAK,UAAU;AAAA,QAC9D,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK,kBAAkB;AAAA,QACvC,MAAM,EAAE,MAAM,SAAS;AAAA,QACvB,qBAAqB;AAAA,QACrB,QAAQ,KAAK,oBAAoB,KAAK,QAAQ;AAAA,QAC9C,iBAAiB;AAAA,MACnB,CAAC;AACD,UAAI,CAAC,OAAO,MAAM,OAAQ;AAC1B,iBAAW,OAAO,OAAO,OAAO;AAC9B,cAAM,WAAW,OAAQ,IAAY,MAAM,EAAE;AAC7C,YAAI,CAAC,SAAU;AACf,cAAM,WAAW,MAAM,KAAK;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,YACE,UAAU,KAAK;AAAA,YACf;AAAA,YACA,UAAU,KAAK;AAAA,YACf,gBAAgB,KAAK,kBAAkB;AAAA,UACzC;AAAA,UACA;AAAA,UACA,EAAE,YAAY,KAAK;AAAA,QACrB;AACA,cAAM,mBAAmB;AAAA,UACvB,IAAI;AAAA,UACJ,SAAS;AAAA,UACT,YAAY,KAAK;AAAA,UACjB;AAAA,UACA,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AACA,UAAI,OAAO,MAAM,SAAS,SAAU;AACpC,cAAQ;AAAA,IACV;AAEA,QAAI,aAAa;AACf,YAAM,KAAK,cAAc;AAAA,QACvB,UAAU,KAAK;AAAA,QACf,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK;AAAA,QACrB,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,MAAyG;AACxH,eAAW,YAAY,KAAK,oBAAoB,GAAG;AACjD,YAAM,KAAK,cAAc,EAAE,UAAU,UAAU,KAAK,UAAU,gBAAgB,KAAK,kBAAkB,MAAM,YAAY,KAAK,WAAW,CAAC;AAAA,IAC1I;AAAA,EACF;AAAA,EAEQ,uBAA6C;AACnD,QAAI,CAAC,KAAK,KAAK,kBAAmB,QAAO;AACzC,QAAI;AACF,YAAM,YAAY,KAAK,KAAK,kBAAkB;AAC9C,UAAI,CAAC,aAAa,OAAQ,UAAkB,YAAY,WAAY,QAAO;AAC3E,YAAM,WAAW;AACjB,YAAM,KAAK,SAAS,QAAQ,IAAI;AAChC,aAAQ,MAA+B;AAAA,IACzC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,MAAuG;AACtH,UAAM,UAAU,KAAK,WAAW,CAAC,KAAK,QAAQ,IAAI,KAAK,oBAAoB;AAC3E,QAAI,CAAC,QAAQ,OAAQ;AAErB,UAAM,UAAU,oBAAI,IAAgC;AACpD,eAAW,YAAY,SAAS;AAC9B,YAAM,MAAM,KAAK,aAAa,IAAI,QAAQ;AAC1C,UAAI,CAAC,IAAK;AACV,YAAM,SAAS,KAAK,UAAU,IAAI,QAAQ;AAC1C,UAAI,OAAO,OAAO,UAAU,YAAY;AACtC,cAAM,IAAI,MAAM,mBAAmB,IAAI,QAAQ,oCAAoC;AAAA,MACrF;AACA,UAAI,CAAC,QAAQ,IAAI,IAAI,QAAQ,EAAG,SAAQ,IAAI,IAAI,UAAU,CAAC,CAAC;AAC5D,cAAQ,IAAI,IAAI,QAAQ,EAAG,KAAK,QAAQ;AAAA,IAC1C;AAEA,eAAW,CAAC,UAAU,SAAS,KAAK,QAAQ,QAAQ,GAAG;AACrD,YAAM,SAAS,KAAK,UAAU,QAAQ;AACtC,YAAM,OAAO,YAAY;AACzB,iBAAW,YAAY,WAAW;AAChC,cAAM,OAAO,MAAO,UAAU,KAAK,QAAQ;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,MAKA;AAClB,UAAM,QAAQ,KAAK,aAAa,IAAI,KAAK,QAAQ;AACjD,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,SAAS,KAAK,UAAU,MAAM,QAAQ;AAC5C,UAAM,OAAO,YAAY;AAEzB,UAAM,YAAY;AASlB,QAAI,OAAO,UAAU,kBAAkB,YAAY;AACjD,YAAM,UAAU,MAAM,UAAU,cAAc;AAAA,QAC5C,UAAU,KAAK;AAAA,QACf,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK;AAAA,QACrB,WAAW,KAAK;AAAA,MAClB,CAAC;AACD,aAAO,OAAO,YAAY,WAAW,UAAU;AAAA,IACjD;AAEA,oBAAgB,UAAU,0CAA0C,EAAE,UAAU,MAAM,SAAS,CAAC;AAChG,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,kBAAkB,MAKJ;AAClB,QAAI,CAAC,KAAK,SAAU,QAAO;AAC3B,UAAM,eAAe,KAAK,WAAW,KAAK,aAAa,IAAI,KAAK,QAAQ,IAAI;AAC5E,QAAI,KAAK,YAAY,CAAC,cAAc;AAClC,aAAO;AAAA,IACT;AACA,UAAM,WACJ,KAAK,aACJ,eAAe,aAAa,WAAW,KAAK;AAC/C,UAAM,SAAS,KAAK,UAAU,QAAQ;AACtC,UAAM,OAAO,YAAY;AACzB,UAAM,cAAc;AAAA,MAClB,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,IACjB;AACA,QAAI,OAAO,OAAO,UAAU,YAAY;AACtC,UAAI;AACF,eAAO,MAAM,OAAO,MAAM,WAAW;AAAA,MACvC,SAAS,KAAK;AACZ,wBAAgB,UAAU,6CAA6C;AAAA,UACrE;AAAA,UACA,OAAO,eAAe,QAAQ,IAAI,UAAU;AAAA,QAC9C,CAAC;AAAA,MACH;AAAA,IACF;AACA,QAAI,OAAO,OAAO,SAAS,YAAY;AACrC,YAAM,QAAQ;AACd,UAAI,SAAS;AACb,UAAI,QAAQ;AACZ,iBAAS;AACP,cAAM,QAAQ,MAAM,OAAO,KAAK;AAAA,UAC9B,UAAU,YAAY;AAAA,UACtB,gBAAgB,YAAY;AAAA,UAC5B,UAAU,YAAY;AAAA,UACtB;AAAA,UACA;AAAA,UACA,SAAS;AAAA,QACX,CAAC;AACD,cAAM,OAAO,MAAM;AACnB,iBAAS;AACT,YAAI,OAAO,MAAO;AAClB,kBAAU;AAAA,MACZ;AACA,aAAO;AAAA,IACT;AACA,oBAAgB,UAAU,6DAA6D,EAAE,SAAS,CAAC;AACnG,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,iBAAiB,MAOS;AAC9B,UAAM,eAAe,KAAK,WAAW,KAAK,aAAa,IAAI,KAAK,QAAQ,IAAI;AAC5E,QAAI,KAAK,YAAY,CAAC,cAAc;AAClC,aAAO,CAAC;AAAA,IACV;AACA,UAAM,WACJ,KAAK,aACJ,eAAe,aAAa,WAAW,KAAK;AAC/C,UAAM,SAAS,KAAK,UAAU,QAAQ;AACtC,QAAI,OAAO,OAAO,SAAS,YAAY;AACrC,YAAM,IAAI,MAAM,mBAAmB,QAAQ,yCAAyC;AAAA,IACtF;AACA,UAAM,OAAO,YAAY;AACzB,UAAM,OAAO,MAAM,OAAO,KAAK;AAAA,MAC7B,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,MACf,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,MACb,SAAS;AAAA,IACX,CAAC;AACD,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B,KAAK,IAAI,OAAO,UAAU;AACxB,cAAM,kBAAkB,MAAM,KAAK,oBAAoB;AAAA,UACrD,UAAU,KAAK;AAAA,UACf,gBAAgB,MAAM,kBAAkB;AAAA,UACxC,aAAa,MAAM;AAAA,UACnB,gBAAgB,MAAM,kBAAkB;AAAA,UACxC,YAAY,MAAM,cAAc;AAAA,UAChC,gBAAgB,MAAM,kBAAkB;AAAA,UACxC,iBAAiB,MAAM,mBAAmB;AAAA,UAC1C,kBAAkB,MAAM,oBAAoB;AAAA,UAC5C,OAAQ,MAAM,SAAiB;AAAA,UAC/B,SAAU,MAAM,WAAmB;AAAA,QACrC,CAAC;AACD,eAAO;AAAA,UACL,GAAG;AAAA,UACH,aAAa,gBAAgB;AAAA,UAC7B,gBAAgB,gBAAgB;AAAA,UAChC,YAAY,gBAAgB;AAAA,UAC5B,gBAAgB,gBAAgB;AAAA,UAChC,iBAAiB,gBAAgB;AAAA,UACjC,kBAAkB,gBAAgB;AAAA,UAClC,OAAO,gBAAgB;AAAA,UACvB,SAAS,gBAAgB;AAAA,UACzB,UAAW,gBAAgB,WAAmB;AAAA,QAChD;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO,UAAU,IAAI,CAAC,UAAU;AAC9B,YAAM,YAAY;AAAA,QAChB,OAAO,MAAM;AAAA,QACb,UAAU,MAAM,kBAAkB;AAAA,QAClC,MAAM,MAAM,cAAc;AAAA,QAC1B,OAAO,MAAM,WAAW,SAAS,MAAM,eAAe;AAAA,MACxD;AACA,YAAM,QAAQ,MAAM,UAAU,MAAM,kBAChC,CAAC,EAAE,MAAM,MAAM,iBAAiB,OAAO,MAAM,oBAAoB,MAAM,aAAa,MAAM,UAAmB,CAAC,IAC9G;AACJ,YAAM,MAAM,MAAM,OAAO,MAAM,mBAAmB;AAClD,YAAM,WAAW,MAAM,aAAa,MAAM,iBAAiB,EAAE,UAAU,MAAM,eAAe,IAAI;AAChG,aAAO;AAAA,QACL,GAAG;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,MAAM,SAAS;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAO,SAAyD;AACpE,UAAM,WAAW,QAAQ,YAAY,KAAK;AAC1C,UAAM,SAAS,KAAK,UAAU,QAAQ;AACtC,UAAM,OAAO,YAAY;AACzB,QAAI,CAAC,KAAK,KAAK,iBAAiB,WAAW;AACzC,YAAM,IAAI,MAAM,iEAAiE;AAAA,IACnF;AACA,UAAM,YAAY,MAAM,KAAK,KAAK,iBAAiB,gBAAgB,QAAQ,KAAK;AAChF,UAAM,OAAO,MAAM,OAAO,MAAM;AAAA,MAC9B,QAAQ;AAAA,MACR,OAAO,QAAQ,SAAS;AAAA,MACxB,QAAQ;AAAA,QACN,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ,kBAAkB;AAAA,QAC1C,WAAW;AAAA,MACb;AAAA,IACF,CAAC;AAED,QAAI,CAAC,KAAK,OAAQ,QAAO,CAAC;AAE1B,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B,KAAK,IAAI,OAAO,QAAQ;AACtB,cAAM,kBAAkB,MAAM,KAAK,oBAAoB;AAAA,UACrD,UAAU,QAAQ;AAAA,UAClB,gBAAgB,IAAI,kBAAkB,QAAQ,kBAAkB;AAAA,UAChE,aAAa,IAAI;AAAA,UACjB,gBAAgB,IAAI,kBAAkB;AAAA,UACtC,YAAY,IAAI,cAAc;AAAA,UAC9B,gBAAgB,IAAI,kBAAkB;AAAA,UACtC,iBAAiB,IAAI,mBAAmB;AAAA,UACxC,kBAAkB,IAAI,oBAAoB;AAAA,UAC1C,OAAQ,IAAI,SAAiB;AAAA,UAC7B,SAAU,IAAI,WAAmB;AAAA,QACnC,CAAC;AACD,eAAO;AAAA,UACL,GAAG;AAAA,UACH,aAAa,gBAAgB;AAAA,UAC7B,gBAAgB,gBAAgB;AAAA,UAChC,YAAY,gBAAgB;AAAA,UAC5B,gBAAgB,gBAAgB;AAAA,UAChC,iBAAiB,gBAAgB;AAAA,UACjC,kBAAkB,gBAAgB;AAAA,UAClC,OAAO,gBAAgB;AAAA,UACvB,SAAS,gBAAgB;AAAA,QAC3B;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO,UAAU,IAAI,CAAC,QAAQ;AAC5B,YAAM,YAAY;AAAA,QAChB,OAAO,IAAI;AAAA,QACX,UAAU,IAAI,kBAAkB;AAAA,QAChC,MAAM,IAAI,cAAc;AAAA,QACxB,OAAO,IAAI,WAAW,SAAS,IAAI,eAAe;AAAA,MACpD;AACA,YAAM,QAAQ,IAAI,UAAU,IAAI,kBAC5B,CAAC,EAAE,MAAM,IAAI,iBAAiB,OAAO,IAAI,oBAAoB,IAAI,aAAa,MAAM,UAAmB,CAAC,IACxG;AACJ,YAAM,MAAM,IAAI,OAAO,IAAI,mBAAmB;AAC9C,YAAM,WAAW,IAAI,YAAY,IAAI,iBAAiB,EAAE,UAAU,IAAI,eAAe,IAAI;AACzF,aAAO;AAAA,QACL,UAAU,IAAI;AAAA,QACd,UAAU,IAAI;AAAA,QACd,OAAO,IAAI;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
4
+ "sourcesContent": ["import type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { parseDecryptedFieldValue } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport {\n type VectorModuleConfig,\n type VectorEntityConfig,\n type VectorQueryRequest,\n type VectorSearchHit,\n type VectorIndexSource,\n type VectorDriverId,\n type VectorLinkDescriptor,\n type VectorResultPresenter,\n type VectorIndexEntry,\n} from '../types'\nimport type { VectorDriver } from '../types'\nimport type { SearchFieldPolicy } from '@open-mercato/shared/modules/search'\nimport { computeChecksum } from './checksum'\nimport { EmbeddingService } from './embedding'\nimport { logVectorOperation } from '../lib/vector-logs'\nimport { searchDebug, searchDebugWarn } from '../../lib/debug'\n\ntype ContainerResolver = () => unknown\nconst VECTOR_ENTRY_ENCRYPTION_ENTITY_ID = 'vector:vector_search'\n\nconst DEFAULT_SOURCE_SCOPE_FIELDS = new Set<string>([\n 'id',\n 'tenant_id',\n 'tenantId',\n 'organization_id',\n 'organizationId',\n 'created_at',\n 'createdAt',\n 'updated_at',\n 'updatedAt',\n 'deleted_at',\n 'deletedAt',\n])\n\nconst ENRICHMENT_FIELD_HINTS: Record<EntityId, string[]> = {\n 'customers:customer_entity': [\n 'id',\n 'organization_id',\n 'tenant_id',\n 'display_name',\n 'description',\n 'status',\n 'lifecycle_stage',\n 'primary_email',\n 'primary_phone',\n 'kind',\n 'customer_kind',\n ],\n 'customers:customer_comment': ['id', 'organization_id', 'tenant_id', 'entity_id', 'body', 'appearance_icon', 'appearance_color'],\n 'customers:customer_activity': ['id', 'organization_id', 'tenant_id', 'entity_id', 'activity_type', 'subject', 'body', 'deal_id'],\n 'customers:customer_deal': ['id', 'organization_id', 'tenant_id', 'title', 'pipeline_stage', 'status', 'value_amount', 'value_currency'],\n 'customers:customer_todo_link': ['id', 'organization_id', 'tenant_id', 'entity_id', 'todo_id', 'todo_source'],\n 'customers:customer_person_profile': [\n 'id',\n 'organization_id',\n 'tenant_id',\n 'entity_id',\n 'first_name',\n 'last_name',\n 'preferred_name',\n 'job_title',\n 'department',\n ],\n 'customers:customer_company_profile': [\n 'id',\n 'organization_id',\n 'tenant_id',\n 'entity_id',\n 'brand_name',\n 'legal_name',\n 'domain',\n 'industry',\n 'size_bucket',\n ],\n}\n\nexport type VectorIndexServiceOptions = {\n drivers: VectorDriver[]\n embeddingService: EmbeddingService\n queryEngine: QueryEngine\n moduleConfigs: VectorModuleConfig[]\n defaultDriverId?: VectorDriverId\n containerResolver?: ContainerResolver\n eventBus?: {\n emitEvent(event: string, payload: any, options?: any): Promise<void>\n }\n}\n\ntype IndexRecordArgs = {\n entityId: EntityId\n recordId: string\n tenantId: string\n organizationId?: string | null\n}\n\ntype DeleteRecordArgs = {\n entityId: EntityId\n recordId: string\n tenantId: string\n organizationId?: string | null\n}\n\nexport type VectorIndexOperationResult = {\n action: 'indexed' | 'deleted' | 'skipped'\n created?: boolean\n existed?: boolean\n tenantId: string\n organizationId: string | null\n reason?: 'unsupported' | 'missing_record' | 'checksum_match'\n}\n\nexport class VectorIndexService {\n private readonly driverMap = new Map<VectorDriverId, VectorDriver>()\n private readonly entityConfig = new Map<EntityId, { config: VectorEntityConfig; driverId: VectorDriverId }>()\n private readonly defaultDriverId: VectorDriverId\n\n constructor(private readonly opts: VectorIndexServiceOptions) {\n for (const driver of opts.drivers) {\n this.driverMap.set(driver.id, driver)\n }\n this.defaultDriverId = opts.defaultDriverId ?? 'pgvector'\n for (const moduleConfig of opts.moduleConfigs) {\n const driverId = moduleConfig.defaultDriverId ?? this.defaultDriverId\n for (const entity of moduleConfig.entities ?? []) {\n if (!entity?.entityId) continue\n if (entity.enabled === false) continue\n const targetDriver = entity.driverId ?? driverId\n this.entityConfig.set(entity.entityId, { config: entity, driverId: targetDriver })\n }\n }\n }\n\n private resolveEncryptionService(): TenantDataEncryptionService | null {\n if (!this.opts.containerResolver) return null\n try {\n const container = this.opts.containerResolver() as any\n if (!container || typeof container.resolve !== 'function') return null\n return container.resolve('tenantEncryptionService') as TenantDataEncryptionService\n } catch {\n return null\n }\n }\n\n private async encryptResultFields(args: {\n tenantId: string\n organizationId: string | null\n resultTitle: string\n resultSubtitle: string | null\n resultIcon: string | null\n resultSnapshot: string | null\n primaryLinkHref: string | null\n primaryLinkLabel: string | null\n links: VectorLinkDescriptor[] | string | null\n payload: Record<string, unknown> | string | null\n }): Promise<{\n resultTitle: string\n resultSubtitle: string | null\n resultIcon: string | null\n resultSnapshot: string | null\n primaryLinkHref: string | null\n primaryLinkLabel: string | null\n links: VectorLinkDescriptor[] | string | null\n payload: Record<string, unknown> | string | null\n }> {\n const service = this.resolveEncryptionService()\n if (!service || !service.isEnabled?.()) {\n return {\n resultTitle: args.resultTitle,\n resultSubtitle: args.resultSubtitle,\n resultIcon: args.resultIcon,\n resultSnapshot: args.resultSnapshot,\n primaryLinkHref: args.primaryLinkHref,\n primaryLinkLabel: args.primaryLinkLabel,\n links: args.links,\n payload: args.payload,\n }\n }\n try {\n const encrypted = await service.encryptEntityPayload(\n VECTOR_ENTRY_ENCRYPTION_ENTITY_ID,\n {\n resultTitle: args.resultTitle,\n resultSubtitle: args.resultSubtitle,\n resultIcon: args.resultIcon,\n resultSnapshot: args.resultSnapshot,\n primaryLinkHref: args.primaryLinkHref,\n primaryLinkLabel: args.primaryLinkLabel,\n links: args.links,\n payload: args.payload,\n },\n args.tenantId,\n args.organizationId,\n )\n return {\n resultTitle: String((encrypted as any).resultTitle ?? args.resultTitle),\n resultSubtitle: ((encrypted as any).resultSubtitle ?? args.resultSubtitle) as any,\n resultIcon: ((encrypted as any).resultIcon ?? args.resultIcon) as any,\n resultSnapshot: ((encrypted as any).resultSnapshot ?? args.resultSnapshot) as any,\n primaryLinkHref: ((encrypted as any).primaryLinkHref ?? args.primaryLinkHref) as any,\n primaryLinkLabel: ((encrypted as any).primaryLinkLabel ?? args.primaryLinkLabel) as any,\n links: ((encrypted as any).links ?? args.links) as any,\n payload: ((encrypted as any).payload ?? args.payload) as any,\n }\n } catch (err) {\n searchDebugWarn('vector.index', 'Vector entry encryption failed; refusing to persist plaintext', {\n entityId: VECTOR_ENTRY_ENCRYPTION_ENTITY_ID,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n error: err instanceof Error ? err.message : String(err),\n })\n throw err instanceof Error\n ? err\n : new Error('[internal] Vector entry encryption failed')\n }\n }\n\n private async decryptResultFields(args: {\n tenantId: string\n organizationId: string | null\n resultTitle: string\n resultSubtitle: string | null\n resultIcon: string | null\n resultSnapshot: string | null\n primaryLinkHref: string | null\n primaryLinkLabel: string | null\n links: VectorLinkDescriptor[] | string | null\n payload: Record<string, unknown> | string | null\n }): Promise<{\n resultTitle: string\n resultSubtitle: string | null\n resultIcon: string | null\n resultSnapshot: string | null\n primaryLinkHref: string | null\n primaryLinkLabel: string | null\n links: VectorLinkDescriptor[] | string | null\n payload: Record<string, unknown> | string | null\n }> {\n const service = this.resolveEncryptionService()\n if (!service || !service.isEnabled?.() || typeof service.decryptEntityPayload !== 'function') {\n return {\n resultTitle: args.resultTitle,\n resultSubtitle: args.resultSubtitle,\n resultIcon: args.resultIcon,\n resultSnapshot: args.resultSnapshot,\n primaryLinkHref: args.primaryLinkHref,\n primaryLinkLabel: args.primaryLinkLabel,\n links: args.links,\n payload: args.payload,\n }\n }\n try {\n const decrypted = await service.decryptEntityPayload(\n VECTOR_ENTRY_ENCRYPTION_ENTITY_ID,\n {\n resultTitle: args.resultTitle,\n resultSubtitle: args.resultSubtitle,\n resultIcon: args.resultIcon,\n resultSnapshot: args.resultSnapshot,\n primaryLinkHref: args.primaryLinkHref,\n primaryLinkLabel: args.primaryLinkLabel,\n links: args.links,\n payload: args.payload,\n },\n args.tenantId,\n args.organizationId,\n )\n const decryptedLinks = (decrypted as any).links ?? args.links\n const decryptedPayload = (decrypted as any).payload ?? args.payload\n return {\n resultTitle: String((decrypted as any).resultTitle ?? args.resultTitle),\n resultSubtitle: ((decrypted as any).resultSubtitle ?? args.resultSubtitle) as any,\n resultIcon: ((decrypted as any).resultIcon ?? args.resultIcon) as any,\n resultSnapshot: ((decrypted as any).resultSnapshot ?? args.resultSnapshot) as any,\n primaryLinkHref: ((decrypted as any).primaryLinkHref ?? args.primaryLinkHref) as any,\n primaryLinkLabel: ((decrypted as any).primaryLinkLabel ?? args.primaryLinkLabel) as any,\n links: (typeof decryptedLinks === 'string' ? parseDecryptedFieldValue(decryptedLinks) : decryptedLinks) as any,\n payload: (typeof decryptedPayload === 'string' ? parseDecryptedFieldValue(decryptedPayload) : decryptedPayload) as any,\n }\n } catch (err) {\n searchDebugWarn('vector.index', 'Vector entry decryption failed; refusing to return ciphertext as plaintext', {\n entityId: VECTOR_ENTRY_ENCRYPTION_ENTITY_ID,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n error: err instanceof Error ? err.message : String(err),\n })\n throw err instanceof Error\n ? err\n : new Error('[internal] Vector entry decryption failed')\n }\n }\n\n listEnabledEntities(): EntityId[] {\n return Array.from(this.entityConfig.keys())\n }\n\n async ensureDriverReady(entityId?: EntityId): Promise<void> {\n if (entityId) {\n const entry = this.entityConfig.get(entityId)\n if (!entry) return\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n return\n }\n const uniqueDrivers = new Set<VectorDriverId>()\n for (const entry of this.entityConfig.values()) {\n uniqueDrivers.add(entry.driverId)\n }\n if (!uniqueDrivers.size) uniqueDrivers.add(this.defaultDriverId)\n await Promise.all(Array.from(uniqueDrivers).map(async (driverId) => {\n try {\n const driver = this.getDriver(driverId)\n await driver.ensureReady()\n } catch (err) {\n searchDebugWarn('vector', 'Failed to ensure driver readiness', { driverId, error: err instanceof Error ? err.message : err })\n }\n }))\n }\n\n private getDriver(driverId: VectorDriverId): VectorDriver {\n const driver = this.driverMap.get(driverId)\n if (!driver) {\n throw new Error(`[vector] Driver ${driverId} is not registered`)\n }\n return driver\n }\n\n private getEnrichmentFields(entityId: EntityId): string[] | undefined {\n const hints = ENRICHMENT_FIELD_HINTS[entityId]\n if (!hints) return undefined\n const unique = new Set<string>(['id', ...hints])\n return Array.from(unique)\n }\n\n private async fetchRecord(entityId: EntityId, recordIds: string[], tenantId: string, organizationId?: string | null) {\n const filters: Record<string, any> = { id: { $in: recordIds } }\n const result = await this.opts.queryEngine.query(entityId, {\n tenantId,\n organizationId: organizationId ?? undefined,\n filters,\n includeCustomFields: true,\n fields: this.getEnrichmentFields(entityId),\n skipAutoReindex: true,\n })\n const byId = new Map<string, Record<string, any>>()\n for (const item of result.items) {\n const key = String((item as any).id ?? '')\n if (!key) continue\n byId.set(key, item as Record<string, any>)\n }\n return byId\n }\n\n private extractRecordPayload(entityId: EntityId, raw: Record<string, any>) {\n const record: Record<string, any> = {}\n const customFields: Record<string, any> = {}\n const multiMap = new Map<string, boolean>()\n\n for (const [key, value] of Object.entries(raw)) {\n if (key.startsWith('cf:') && key.endsWith('__is_multi')) {\n const base = key.replace(/__is_multi$/, '')\n multiMap.set(base, Boolean(value))\n continue\n }\n if (key.startsWith('cf:')) {\n customFields[key.slice(3)] = value\n continue\n }\n record[key] = value\n }\n\n for (const [key, isMulti] of multiMap.entries()) {\n const bare = key.slice(3)\n if (bare && customFields[bare] != null && !Array.isArray(customFields[bare]) && isMulti) {\n customFields[bare] = [customFields[bare]]\n }\n }\n\n if (record.entity_id == null && record.entityId == null && entityId.endsWith('_company_profile')) {\n searchDebugWarn('vector.index', 'company profile missing entity id in payload', {\n id: record.id,\n keys: Object.keys(record),\n })\n }\n\n return { record, customFields }\n }\n\n private async indexExisting(\n entry: { config: VectorEntityConfig; driverId: VectorDriverId },\n driver: VectorDriver,\n args: IndexRecordArgs,\n raw: Record<string, any>,\n opts: { skipDelete?: boolean } = {},\n ): Promise<VectorIndexOperationResult> {\n const scopeOrg = args.organizationId ?? null\n const { record, customFields } = this.extractRecordPayload(args.entityId, raw)\n const resolvedOrgId = scopeOrg ?? (record.organization_id ?? record.organizationId ?? null)\n const source = await this.resolveSource(args.entityId, entry.config, {\n record,\n customFields,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n })\n if (!source) {\n const existing = await driver.getChecksum(args.entityId, args.recordId, args.tenantId)\n if (!opts.skipDelete && existing) {\n await driver.delete(args.entityId, args.recordId, args.tenantId)\n return {\n action: 'deleted',\n existed: true,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n }\n }\n return {\n action: 'skipped',\n existed: Boolean(existing),\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n reason: 'missing_record',\n }\n }\n\n const checksumSource = source.checksumSource ?? { record, customFields }\n const checksum = computeChecksum(checksumSource)\n const current = await driver.getChecksum(args.entityId, args.recordId, args.tenantId)\n if (current && current === checksum) {\n return {\n action: 'skipped',\n existed: true,\n tenantId: args.tenantId,\n organizationId: scopeOrg,\n reason: 'checksum_match',\n }\n }\n if (!this.opts.embeddingService.available) {\n throw new Error('[vector] Embedding service unavailable (missing OPENAI_API_KEY)')\n }\n const embedding = await this.opts.embeddingService.createEmbedding(source.input)\n const presenter = await this.resolvePresenter(entry.config, {\n record,\n customFields,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n }, source.presenter ?? null)\n if (!presenter?.title) {\n searchDebugWarn('vector.index', 'missing presenter title', {\n entityId: args.entityId,\n recordId: args.recordId,\n recordSample: {\n display_name: record.display_name,\n displayName: record.displayName,\n name: record.name,\n title: record.title,\n subject: record.subject,\n kind: record.kind,\n },\n })\n }\n const links = await this.resolveLinks(entry.config, {\n record,\n customFields,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n }, source.links ?? null)\n const url = await this.resolveUrl(entry.config, {\n record,\n customFields,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n })\n\n const normalizedPresenter = this.ensurePresenter(presenter, record, customFields, args.recordId, args.entityId)\n const normalizedLinks = Array.isArray(links) && links.length ? links : null\n const snapshot = this.deriveSnapshot(record, customFields)\n const rawResultTitle = this.resolveResultTitle(normalizedPresenter, record, customFields, args.recordId)\n const rawResultSubtitle = normalizedPresenter.subtitle ?? snapshot ?? null\n const rawResultIcon = normalizedPresenter.icon ?? this.mapDefaultIcon(args.entityId)\n const resultBadge = normalizedPresenter.badge ?? null\n const primaryLink = this.resolvePrimaryLink(normalizedLinks, url, rawResultTitle)\n\n const encryptedResult = await this.encryptResultFields({\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n resultTitle: rawResultTitle,\n resultSubtitle: rawResultSubtitle,\n resultIcon: rawResultIcon ?? null,\n resultSnapshot: snapshot ?? null,\n primaryLinkHref: primaryLink?.href ?? null,\n primaryLinkLabel: primaryLink?.label ?? null,\n links: normalizedLinks,\n payload: source.payload ?? null,\n })\n\n const presenterForStorage: VectorResultPresenter = {\n title: encryptedResult.resultTitle,\n subtitle: encryptedResult.resultSubtitle ?? undefined,\n icon: encryptedResult.resultIcon ?? undefined,\n badge: resultBadge ?? undefined,\n }\n\n searchDebug('VectorIndexService', 'Storing vector index entry', {\n entityId: args.entityId,\n recordId: args.recordId,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n })\n await driver.upsert({\n driverId: entry.driverId,\n entityId: args.entityId,\n recordId: args.recordId,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n checksum,\n embedding,\n url: url ?? null,\n presenter: presenterForStorage,\n links: (encryptedResult.links as any) ?? normalizedLinks,\n payload: (encryptedResult.payload as any) ?? source.payload ?? null,\n resultTitle: encryptedResult.resultTitle,\n resultSubtitle: encryptedResult.resultSubtitle,\n resultIcon: encryptedResult.resultIcon ?? null,\n resultBadge,\n resultSnapshot: encryptedResult.resultSnapshot ?? snapshot,\n primaryLinkHref: encryptedResult.primaryLinkHref ?? primaryLink?.href ?? null,\n primaryLinkLabel: encryptedResult.primaryLinkLabel ?? null,\n })\n\n return {\n action: 'indexed',\n created: !current,\n existed: true,\n tenantId: args.tenantId,\n organizationId: resolvedOrgId,\n }\n }\n\n private ensurePresenter(\n presenter: VectorResultPresenter | null,\n record: Record<string, any>,\n customFields: Record<string, any>,\n recordId: string,\n entityId: EntityId,\n ): VectorResultPresenter {\n if (presenter?.title) return presenter\n const fallback = this.buildFallbackPresenter(record, customFields, recordId, entityId)\n return fallback\n }\n\n private resolveResultTitle(\n presenter: VectorResultPresenter,\n record: Record<string, any>,\n customFields: Record<string, any>,\n recordId: string,\n ): string {\n const candidate =\n presenter.title ??\n record.display_name ??\n record.displayName ??\n record.name ??\n record.title ??\n record.subject ??\n customFields.title ??\n customFields.name ??\n recordId\n const text = String(candidate).trim()\n return text.length ? text : recordId\n }\n\n private buildFallbackPresenter(\n record: Record<string, any>,\n customFields: Record<string, any>,\n recordId: string,\n entityId: EntityId,\n ): VectorResultPresenter {\n const titleCandidate =\n record.display_name ??\n record.displayName ??\n record.name ??\n record.title ??\n record.subject ??\n recordId\n const subtitleCandidate =\n record.description ??\n record.summary ??\n record.body ??\n customFields.summary ??\n customFields.description ??\n null\n const icon = typeof record.kind === 'string'\n ? this.mapEntityIcon(record.kind)\n : this.mapDefaultIcon(entityId)\n return {\n title: String(titleCandidate),\n subtitle: subtitleCandidate ? String(subtitleCandidate) : undefined,\n icon: icon ?? undefined,\n }\n }\n\n private mapEntityIcon(kind?: string | null): string | null {\n if (!kind) return null\n const normalized = kind.toLowerCase()\n if (normalized === 'person') return 'user'\n if (normalized === 'company' || normalized === 'organization') return 'building'\n return null\n }\n\n private mapDefaultIcon(entityId: EntityId): string | null {\n if (entityId.startsWith('customers:customer_deal')) return 'briefcase'\n if (entityId.startsWith('customers:customer_comment')) return 'sticky-note'\n if (entityId.startsWith('customers:customer_activity')) return 'bolt'\n if (entityId.startsWith('customers:customer_todo')) return 'check-square'\n return null\n }\n\n private resolvePrimaryLink(\n links: VectorLinkDescriptor[] | null,\n url: string | null,\n fallbackLabel: string,\n ): { href: string; label: string } | null {\n if (links?.length) {\n const primary = links.find((link) => link.kind === 'primary') ?? links[0]\n if (primary?.href) {\n return { href: primary.href, label: primary.label ?? fallbackLabel }\n }\n }\n if (url) {\n return { href: url, label: fallbackLabel }\n }\n return null\n }\n\n private deriveSnapshot(\n record: Record<string, any>,\n customFields: Record<string, any>,\n ): string | null {\n const candidates = [\n record.summary,\n record.description,\n record.body,\n customFields.summary,\n customFields.description,\n customFields.body,\n ]\n for (const value of candidates) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed.length) return trimmed\n }\n }\n return null\n }\n\n private buildDefaultSource(\n entityId: EntityId,\n payload: { record: Record<string, any>; customFields: Record<string, any> },\n fieldPolicy?: SearchFieldPolicy,\n ): VectorIndexSource {\n const { record, customFields } = payload\n const lines: string[] = []\n\n const searchableWhitelist = fieldPolicy?.searchable ? new Set(fieldPolicy.searchable) : null\n const excludedFields = new Set<string>([\n ...(fieldPolicy?.excluded ?? []),\n ...(fieldPolicy?.hashOnly ?? []),\n ])\n\n const isEligibleField = (field: string): boolean => {\n if (DEFAULT_SOURCE_SCOPE_FIELDS.has(field)) return false\n if (excludedFields.has(field)) return false\n if (searchableWhitelist && !searchableWhitelist.has(field)) return false\n return true\n }\n\n const pushEntry = (label: string, value: unknown) => {\n if (value === null || value === undefined) return\n if (typeof value === 'string' && value.trim().length === 0) return\n if (typeof value === 'object') {\n lines.push(`${label}: ${JSON.stringify(value)}`)\n } else {\n lines.push(`${label}: ${value}`)\n }\n }\n\n const preferredFields = ['title', 'name', 'displayName', 'summary', 'subject']\n for (const field of preferredFields) {\n if (!isEligibleField(field)) continue\n if (record[field] != null) pushEntry(field, record[field])\n }\n\n for (const [key, value] of Object.entries(record)) {\n if (preferredFields.includes(key)) continue\n if (!isEligibleField(key)) continue\n pushEntry(key, value)\n }\n\n for (const [key, value] of Object.entries(customFields)) {\n if (!isEligibleField(key)) continue\n pushEntry(`custom.${key}`, value)\n }\n\n if (lines.length === 0) {\n lines.push(`${entityId}#${record.id ?? ''}`)\n }\n\n return {\n input: lines,\n payload: null,\n checksumSource: { record, customFields },\n }\n }\n\n private async resolveSource(entityId: EntityId, config: VectorEntityConfig, ctx: {\n record: Record<string, any>\n customFields: Record<string, any>\n organizationId?: string | null\n tenantId: string\n }): Promise<VectorIndexSource | null> {\n const baseCtx = {\n record: ctx.record,\n customFields: ctx.customFields,\n organizationId: ctx.organizationId ?? null,\n tenantId: ctx.tenantId,\n queryEngine: this.opts.queryEngine,\n container: this.opts.containerResolver ? this.opts.containerResolver() : undefined,\n }\n if (config.buildSource) {\n const built = await config.buildSource(baseCtx)\n if (built) return built\n return null\n }\n return this.buildDefaultSource(entityId, { record: ctx.record, customFields: ctx.customFields }, config.fieldPolicy)\n }\n\n private async resolvePresenter(\n config: VectorEntityConfig,\n ctx: {\n record: Record<string, any>\n customFields: Record<string, any>\n organizationId?: string | null\n tenantId: string\n },\n fallback?: VectorResultPresenter | null,\n ): Promise<VectorResultPresenter | null> {\n const baseCtx = {\n record: ctx.record,\n customFields: ctx.customFields,\n organizationId: ctx.organizationId ?? null,\n tenantId: ctx.tenantId,\n queryEngine: this.opts.queryEngine,\n container: this.opts.containerResolver ? this.opts.containerResolver() : undefined,\n }\n if (config.formatResult) {\n const formatted = await config.formatResult(baseCtx)\n if (formatted) return formatted\n }\n if (fallback) return fallback\n const nameLike = ctx.record.displayName || ctx.record.title || ctx.record.name\n if (typeof nameLike === 'string' && nameLike.trim().length > 0) {\n const subtitle = ctx.record.description || ctx.record.summary\n return {\n title: nameLike,\n subtitle: typeof subtitle === 'string' ? subtitle : undefined,\n }\n }\n return null\n }\n\n private async resolveLinks(\n config: VectorEntityConfig,\n ctx: {\n record: Record<string, any>\n customFields: Record<string, any>\n organizationId?: string | null\n tenantId: string\n },\n fallback?: VectorLinkDescriptor[] | null,\n ): Promise<VectorLinkDescriptor[] | null> {\n const baseCtx = {\n record: ctx.record,\n customFields: ctx.customFields,\n organizationId: ctx.organizationId ?? null,\n tenantId: ctx.tenantId,\n queryEngine: this.opts.queryEngine,\n container: this.opts.containerResolver ? this.opts.containerResolver() : undefined,\n }\n if (config.resolveLinks) {\n const resolved = await config.resolveLinks(baseCtx)\n if (resolved?.length) return resolved\n }\n return fallback ?? null\n }\n\n private async resolveUrl(\n config: VectorEntityConfig,\n ctx: {\n record: Record<string, any>\n customFields: Record<string, any>\n organizationId?: string | null\n tenantId: string\n },\n fallback?: string | null,\n ): Promise<string | null> {\n if (config.resolveUrl) {\n const candidate = await config.resolveUrl({\n record: ctx.record,\n customFields: ctx.customFields,\n organizationId: ctx.organizationId ?? null,\n tenantId: ctx.tenantId,\n queryEngine: this.opts.queryEngine,\n container: this.opts.containerResolver ? this.opts.containerResolver() : undefined,\n })\n if (candidate) return candidate\n }\n return fallback ?? null\n }\n\n async indexRecord(args: IndexRecordArgs): Promise<VectorIndexOperationResult> {\n const entry = this.entityConfig.get(args.entityId)\n if (!entry) {\n return {\n action: 'skipped',\n existed: false,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n reason: 'unsupported',\n }\n }\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n\n const records = await this.fetchRecord(args.entityId, [args.recordId], args.tenantId, args.organizationId)\n const raw = records.get(args.recordId)\n if (!raw) {\n const existing = await driver.getChecksum(args.entityId, args.recordId, args.tenantId)\n if (existing) {\n await driver.delete(args.entityId, args.recordId, args.tenantId)\n return {\n action: 'deleted',\n existed: true,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n }\n }\n return {\n action: 'skipped',\n existed: false,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n reason: 'missing_record',\n }\n }\n return this.indexExisting(entry, driver, args, raw as Record<string, any>)\n }\n\n async deleteRecord(args: DeleteRecordArgs): Promise<VectorIndexOperationResult> {\n const entry = this.entityConfig.get(args.entityId)\n if (!entry) {\n return {\n action: 'skipped',\n existed: false,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n reason: 'unsupported',\n }\n }\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n const scopeOrg = args.organizationId ?? null\n const existing = await driver.getChecksum(args.entityId, args.recordId, args.tenantId)\n if (existing) {\n await driver.delete(args.entityId, args.recordId, args.tenantId)\n return {\n action: 'deleted',\n existed: true,\n tenantId: args.tenantId,\n organizationId: scopeOrg,\n }\n }\n return {\n action: 'skipped',\n existed: false,\n tenantId: args.tenantId,\n organizationId: scopeOrg,\n reason: 'missing_record',\n }\n }\n\n async reindexEntity(args: { entityId: EntityId; tenantId?: string | null; organizationId?: string | null; purgeFirst?: boolean }): Promise<void> {\n const entry = this.entityConfig.get(args.entityId)\n if (!entry) return\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n\n const shouldPurge = args.purgeFirst === true\n const reindexStartedAt = new Date()\n\n if (this.opts.eventBus) {\n if (shouldPurge && driver.purge && args.tenantId) {\n await driver.purge(args.entityId, args.tenantId, args.organizationId)\n } else if (shouldPurge && !args.tenantId) {\n searchDebugWarn('vector', 'Skipping purge for multi-tenant reindex (tenant not provided)')\n }\n const payload: Record<string, unknown> = {\n entityType: args.entityId,\n }\n if (shouldPurge) {\n payload.force = true\n payload.resetCoverage = true\n }\n if (args.tenantId !== undefined) payload.tenantId = args.tenantId\n else payload.allowAllTenants = true\n if (args.organizationId !== undefined) payload.organizationId = args.organizationId\n await this.opts.eventBus.emitEvent('query_index.reindex', payload)\n return\n }\n\n if (!args.tenantId) {\n throw new Error('[vector] Reindex without tenantId requires event bus integration')\n }\n\n if (shouldPurge && driver.purge) {\n await driver.purge(args.entityId, args.tenantId, args.organizationId)\n }\n\n const pageSize = 50\n let page = 1\n const loggingEm = this.resolveEntityManager()\n for (;;) {\n const result = await this.opts.queryEngine.query(args.entityId, {\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? undefined,\n page: { page, pageSize },\n includeCustomFields: true,\n fields: this.getEnrichmentFields(args.entityId),\n skipAutoReindex: true,\n })\n if (!result.items.length) break\n for (const raw of result.items) {\n const recordId = String((raw as any).id ?? '')\n if (!recordId) continue\n const opResult = await this.indexExisting(\n entry,\n driver,\n {\n entityId: args.entityId,\n recordId,\n tenantId: args.tenantId,\n organizationId: args.organizationId ?? null,\n },\n raw as Record<string, any>,\n { skipDelete: true },\n )\n await logVectorOperation({\n em: loggingEm,\n handler: 'service:vector.reindex',\n entityType: args.entityId,\n recordId,\n result: opResult,\n })\n }\n if (result.items.length < pageSize) break\n page += 1\n }\n\n if (shouldPurge) {\n await this.removeOrphans({\n entityId: args.entityId,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n olderThan: reindexStartedAt,\n })\n }\n }\n\n async reindexAll(args: { tenantId?: string | null; organizationId?: string | null; purgeFirst?: boolean }): Promise<void> {\n for (const entityId of this.listEnabledEntities()) {\n await this.reindexEntity({ entityId, tenantId: args.tenantId, organizationId: args.organizationId ?? null, purgeFirst: args.purgeFirst })\n }\n }\n\n private resolveEntityManager(): EntityManager | null {\n if (!this.opts.containerResolver) return null\n try {\n const container = this.opts.containerResolver()\n if (!container || typeof (container as any).resolve !== 'function') return null\n const resolver = container as { resolve(name: string): unknown }\n const em = resolver.resolve('em')\n return (em as EntityManager | null) ?? null\n } catch {\n return null\n }\n }\n\n async purgeIndex(args: { tenantId: string; organizationId?: string | null; entityId?: EntityId | null }): Promise<void> {\n const targets = args.entityId ? [args.entityId] : this.listEnabledEntities()\n if (!targets.length) return\n\n const grouped = new Map<VectorDriverId, EntityId[]>()\n for (const entityId of targets) {\n const cfg = this.entityConfig.get(entityId)\n if (!cfg) continue\n const driver = this.getDriver(cfg.driverId)\n if (typeof driver.purge !== 'function') {\n throw new Error(`[vector] Driver ${cfg.driverId} does not support purging entities`)\n }\n if (!grouped.has(cfg.driverId)) grouped.set(cfg.driverId, [])\n grouped.get(cfg.driverId)!.push(entityId)\n }\n\n for (const [driverId, entityIds] of grouped.entries()) {\n const driver = this.getDriver(driverId)\n await driver.ensureReady()\n for (const entityId of entityIds) {\n await driver.purge!(entityId, args.tenantId)\n }\n }\n }\n\n async removeOrphans(args: {\n entityId: EntityId\n tenantId?: string | null\n organizationId?: string | null\n olderThan: Date\n }): Promise<number> {\n const entry = this.entityConfig.get(args.entityId)\n if (!entry) return 0\n const driver = this.getDriver(entry.driverId)\n await driver.ensureReady()\n\n const driverAny = driver as VectorDriver & {\n removeOrphans?: (params: {\n entityId: EntityId\n tenantId?: string | null\n organizationId?: string | null\n olderThan: Date\n }) => Promise<number | void>\n }\n\n if (typeof driverAny.removeOrphans === 'function') {\n const deleted = await driverAny.removeOrphans({\n entityId: args.entityId,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n olderThan: args.olderThan,\n })\n return typeof deleted === 'number' ? deleted : 0\n }\n\n searchDebugWarn('vector', 'Driver does not support orphan cleanup', { driverId: entry.driverId })\n return 0\n }\n\n async countIndexEntries(args: {\n tenantId: string\n organizationId?: string | null\n entityId?: EntityId\n driverId?: VectorDriverId\n }): Promise<number> {\n if (!args.tenantId) return 0\n const targetEntity = args.entityId ? this.entityConfig.get(args.entityId) : null\n if (args.entityId && !targetEntity) {\n return 0\n }\n const driverId =\n args.driverId ??\n (targetEntity ? targetEntity.driverId : this.defaultDriverId)\n const driver = this.getDriver(driverId)\n await driver.ensureReady()\n const countParams = {\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n entityId: args.entityId,\n }\n if (typeof driver.count === 'function') {\n try {\n return await driver.count(countParams)\n } catch (err) {\n searchDebugWarn('vector', 'Driver count failed, falling back to list', {\n driverId,\n error: err instanceof Error ? err.message : err,\n })\n }\n }\n if (typeof driver.list === 'function') {\n const limit = 1000\n let offset = 0\n let total = 0\n for (;;) {\n const batch = await driver.list({\n tenantId: countParams.tenantId,\n organizationId: countParams.organizationId,\n entityId: countParams.entityId,\n limit,\n offset,\n orderBy: 'created',\n })\n const size = batch.length\n total += size\n if (size < limit) break\n offset += limit\n }\n return total\n }\n searchDebugWarn('vector', 'Driver does not support counting or listing index entries', { driverId })\n return 0\n }\n\n async listIndexEntries(args: {\n tenantId: string\n organizationId?: string | null\n entityId?: EntityId\n limit?: number\n offset?: number\n driverId?: VectorDriverId\n }): Promise<VectorIndexEntry[]> {\n const targetEntity = args.entityId ? this.entityConfig.get(args.entityId) : null\n if (args.entityId && !targetEntity) {\n return []\n }\n const driverId =\n args.driverId ??\n (targetEntity ? targetEntity.driverId : this.defaultDriverId)\n const driver = this.getDriver(driverId)\n if (typeof driver.list !== 'function') {\n throw new Error(`[vector] Driver ${driverId} does not support listing index entries`)\n }\n await driver.ensureReady()\n const list = await driver.list({\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n entityId: args.entityId,\n limit: args.limit,\n offset: args.offset,\n orderBy: 'updated',\n })\n if (!list.length) {\n return []\n }\n\n const decrypted = await Promise.all(\n list.map(async (entry) => {\n const decryptedResult = await this.decryptResultFields({\n tenantId: args.tenantId,\n organizationId: entry.organizationId ?? null,\n resultTitle: entry.resultTitle,\n resultSubtitle: entry.resultSubtitle ?? null,\n resultIcon: entry.resultIcon ?? null,\n resultSnapshot: entry.resultSnapshot ?? null,\n primaryLinkHref: entry.primaryLinkHref ?? null,\n primaryLinkLabel: entry.primaryLinkLabel ?? null,\n links: (entry.links as any) ?? null,\n payload: (entry.payload as any) ?? null,\n })\n return {\n ...entry,\n resultTitle: decryptedResult.resultTitle,\n resultSubtitle: decryptedResult.resultSubtitle,\n resultIcon: decryptedResult.resultIcon,\n resultSnapshot: decryptedResult.resultSnapshot,\n primaryLinkHref: decryptedResult.primaryLinkHref,\n primaryLinkLabel: decryptedResult.primaryLinkLabel,\n links: decryptedResult.links as any,\n payload: decryptedResult.payload as any,\n metadata: (decryptedResult.payload as any) ?? null,\n }\n }),\n )\n\n return decrypted.map((entry) => {\n const presenter = {\n title: entry.resultTitle,\n subtitle: entry.resultSubtitle ?? undefined,\n icon: entry.resultIcon ?? undefined,\n badge: entry.presenter?.badge ?? entry.resultBadge ?? undefined,\n }\n const links = entry.links ?? (entry.primaryLinkHref\n ? [{ href: entry.primaryLinkHref, label: entry.primaryLinkLabel ?? entry.resultTitle, kind: 'primary' as const }]\n : null)\n const url = entry.url ?? entry.primaryLinkHref ?? null\n const metadata = entry.metadata ?? (entry.resultSnapshot ? { snapshot: entry.resultSnapshot } : null)\n return {\n ...entry,\n driverId,\n presenter,\n links,\n url,\n metadata,\n score: entry.score ?? null,\n }\n })\n }\n\n async search(request: VectorQueryRequest): Promise<VectorSearchHit[]> {\n const driverId = request.driverId ?? this.defaultDriverId\n const driver = this.getDriver(driverId)\n await driver.ensureReady()\n if (!this.opts.embeddingService.available) {\n throw new Error('[vector] Embedding service unavailable (missing OPENAI_API_KEY)')\n }\n const embedding = await this.opts.embeddingService.createEmbedding(request.query)\n const hits = await driver.query({\n vector: embedding,\n limit: request.limit ?? 10,\n filter: {\n tenantId: request.tenantId,\n organizationId: request.organizationId ?? null,\n entityIds: undefined,\n },\n })\n\n if (!hits.length) return []\n\n const decrypted = await Promise.all(\n hits.map(async (hit) => {\n const decryptedResult = await this.decryptResultFields({\n tenantId: request.tenantId,\n organizationId: hit.organizationId ?? request.organizationId ?? null,\n resultTitle: hit.resultTitle,\n resultSubtitle: hit.resultSubtitle ?? null,\n resultIcon: hit.resultIcon ?? null,\n resultSnapshot: hit.resultSnapshot ?? null,\n primaryLinkHref: hit.primaryLinkHref ?? null,\n primaryLinkLabel: hit.primaryLinkLabel ?? null,\n links: (hit.links as any) ?? null,\n payload: (hit.payload as any) ?? null,\n })\n return {\n ...hit,\n resultTitle: decryptedResult.resultTitle,\n resultSubtitle: decryptedResult.resultSubtitle,\n resultIcon: decryptedResult.resultIcon,\n resultSnapshot: decryptedResult.resultSnapshot,\n primaryLinkHref: decryptedResult.primaryLinkHref,\n primaryLinkLabel: decryptedResult.primaryLinkLabel,\n links: decryptedResult.links as any,\n payload: decryptedResult.payload as any,\n }\n }),\n )\n\n return decrypted.map((hit) => {\n const presenter = {\n title: hit.resultTitle,\n subtitle: hit.resultSubtitle ?? undefined,\n icon: hit.resultIcon ?? undefined,\n badge: hit.presenter?.badge ?? hit.resultBadge ?? undefined,\n }\n const links = hit.links ?? (hit.primaryLinkHref\n ? [{ href: hit.primaryLinkHref, label: hit.primaryLinkLabel ?? hit.resultTitle, kind: 'primary' as const }]\n : null)\n const url = hit.url ?? hit.primaryLinkHref ?? null\n const metadata = hit.payload ?? (hit.resultSnapshot ? { snapshot: hit.resultSnapshot } : null)\n return {\n entityId: hit.entityId,\n recordId: hit.recordId,\n score: hit.score,\n url,\n presenter,\n links,\n driverId,\n metadata,\n }\n })\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/search",
3
- "version": "0.7.1-develop.7136.1.0f76137a1d",
3
+ "version": "0.7.1-develop.7148.1.3076e5ccf7",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -127,9 +127,9 @@
127
127
  "zod": "^4.4.3"
128
128
  },
129
129
  "peerDependencies": {
130
- "@open-mercato/core": "0.7.1-develop.7136.1.0f76137a1d",
131
- "@open-mercato/queue": "0.7.1-develop.7136.1.0f76137a1d",
132
- "@open-mercato/shared": "0.7.1-develop.7136.1.0f76137a1d"
130
+ "@open-mercato/core": "0.7.1-develop.7148.1.3076e5ccf7",
131
+ "@open-mercato/queue": "0.7.1-develop.7148.1.3076e5ccf7",
132
+ "@open-mercato/shared": "0.7.1-develop.7148.1.3076e5ccf7"
133
133
  },
134
134
  "devDependencies": {
135
135
  "@types/jest": "^30.0.0",
@@ -228,3 +228,48 @@ describe('VectorIndexService encryption fail-closed (issue #2716)', () => {
228
228
  expect(driver.upsertCalls[0].resultTitle).toBe('Title')
229
229
  })
230
230
  })
231
+
232
+ describe('VectorIndexService decrypted result parse-back (issue #5789)', () => {
233
+ const passthroughEncryptionService = (): TenantDataEncryptionService =>
234
+ ({
235
+ isEnabled: () => true,
236
+ decryptEntityPayload: async (_entityId: string, payload: Record<string, unknown>) => payload,
237
+ }) as unknown as TenantDataEncryptionService
238
+
239
+ it('parses links/payload back to objects when decryptEntityPayload returns sealed JSON strings', async () => {
240
+ const driver = createDriver({
241
+ query: async () => [
242
+ {
243
+ entityId: 'demo:item',
244
+ recordId: 'rec-6',
245
+ organizationId: null,
246
+ score: 0.9,
247
+ checksum: 'abc',
248
+ resultTitle: 'Visible Title',
249
+ resultSubtitle: null,
250
+ resultIcon: null,
251
+ resultBadge: null,
252
+ resultSnapshot: null,
253
+ primaryLinkHref: null,
254
+ primaryLinkLabel: null,
255
+ links: '[{"href":"/backend/demo/rec-6","label":"View","kind":"primary"}]',
256
+ payload: '{"foo":"bar"}',
257
+ },
258
+ ],
259
+ })
260
+ const captured: CapturedEmbeddingInput = { value: null }
261
+ const service = createService({
262
+ driver,
263
+ embeddingService: createEmbeddingService(captured),
264
+ queryEngine: createQueryEngine({ id: 'rec-6' }),
265
+ moduleConfigs: [{ entities: [{ entityId: 'demo:item' }] }],
266
+ encryptionService: passthroughEncryptionService(),
267
+ })
268
+
269
+ const results = await service.search({ tenantId: 'tenant-1', query: 'anything' })
270
+
271
+ expect(results).toHaveLength(1)
272
+ expect(results[0].links).toEqual([{ href: '/backend/demo/rec-6', label: 'View', kind: 'primary' }])
273
+ expect(results[0].metadata).toEqual({ foo: 'bar' })
274
+ })
275
+ })
@@ -2,6 +2,7 @@ import type { QueryEngine } from '@open-mercato/shared/lib/query/types'
2
2
  import type { EntityManager } from '@mikro-orm/postgresql'
3
3
  import type { EntityId } from '@open-mercato/shared/modules/entities'
4
4
  import type { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'
5
+ import { parseDecryptedFieldValue } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'
5
6
  import {
6
7
  type VectorModuleConfig,
7
8
  type VectorEntityConfig,
@@ -269,6 +270,8 @@ export class VectorIndexService {
269
270
  args.tenantId,
270
271
  args.organizationId,
271
272
  )
273
+ const decryptedLinks = (decrypted as any).links ?? args.links
274
+ const decryptedPayload = (decrypted as any).payload ?? args.payload
272
275
  return {
273
276
  resultTitle: String((decrypted as any).resultTitle ?? args.resultTitle),
274
277
  resultSubtitle: ((decrypted as any).resultSubtitle ?? args.resultSubtitle) as any,
@@ -276,8 +279,8 @@ export class VectorIndexService {
276
279
  resultSnapshot: ((decrypted as any).resultSnapshot ?? args.resultSnapshot) as any,
277
280
  primaryLinkHref: ((decrypted as any).primaryLinkHref ?? args.primaryLinkHref) as any,
278
281
  primaryLinkLabel: ((decrypted as any).primaryLinkLabel ?? args.primaryLinkLabel) as any,
279
- links: ((decrypted as any).links ?? args.links) as any,
280
- payload: ((decrypted as any).payload ?? args.payload) as any,
282
+ links: (typeof decryptedLinks === 'string' ? parseDecryptedFieldValue(decryptedLinks) : decryptedLinks) as any,
283
+ payload: (typeof decryptedPayload === 'string' ? parseDecryptedFieldValue(decryptedPayload) : decryptedPayload) as any,
281
284
  }
282
285
  } catch (err) {
283
286
  searchDebugWarn('vector.index', 'Vector entry decryption failed; refusing to return ciphertext as plaintext', {