@open-mercato/core 0.6.6-develop.6535.1.5cf43724de → 0.6.6-develop.6540.1.ae856df1ec

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/api_keys/cli.js +2 -1
  3. package/dist/modules/api_keys/cli.js.map +2 -2
  4. package/dist/modules/attachments/api/file/[id]/route.js +9 -7
  5. package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
  6. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +9 -7
  7. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
  8. package/dist/modules/attachments/api/route.js +21 -17
  9. package/dist/modules/attachments/api/route.js.map +2 -2
  10. package/dist/modules/attachments/lib/reconcileOrganization.js +110 -0
  11. package/dist/modules/attachments/lib/reconcileOrganization.js.map +7 -0
  12. package/dist/modules/attachments/lib/requestScope.js +10 -0
  13. package/dist/modules/attachments/lib/requestScope.js.map +7 -0
  14. package/dist/modules/auth/api/users/route.js +6 -2
  15. package/dist/modules/auth/api/users/route.js.map +2 -2
  16. package/dist/modules/auth/cli.js +3 -2
  17. package/dist/modules/auth/cli.js.map +2 -2
  18. package/dist/modules/configs/lib/upgrade-actions.js +22 -0
  19. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  20. package/dist/modules/dashboards/cli.js +4 -3
  21. package/dist/modules/dashboards/cli.js.map +2 -2
  22. package/dist/modules/directory/utils/organizationScope.js +10 -3
  23. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  24. package/dist/modules/entities/api/records.js +5 -4
  25. package/dist/modules/entities/api/records.js.map +2 -2
  26. package/dist/modules/entities/cli.js +2 -1
  27. package/dist/modules/entities/cli.js.map +2 -2
  28. package/package.json +7 -7
  29. package/src/modules/api_keys/cli.ts +2 -3
  30. package/src/modules/attachments/api/file/[id]/route.ts +11 -7
  31. package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +11 -7
  32. package/src/modules/attachments/api/route.ts +26 -21
  33. package/src/modules/attachments/lib/reconcileOrganization.ts +188 -0
  34. package/src/modules/attachments/lib/requestScope.ts +27 -0
  35. package/src/modules/auth/api/users/route.ts +11 -2
  36. package/src/modules/auth/cli.ts +3 -2
  37. package/src/modules/configs/i18n/de.json +5 -1
  38. package/src/modules/configs/i18n/en.json +5 -1
  39. package/src/modules/configs/i18n/es.json +5 -1
  40. package/src/modules/configs/i18n/pl.json +5 -1
  41. package/src/modules/configs/lib/upgrade-actions.ts +24 -0
  42. package/src/modules/dashboards/cli.ts +4 -9
  43. package/src/modules/directory/utils/organizationScope.ts +36 -2
  44. package/src/modules/entities/api/records.ts +5 -7
  45. package/src/modules/entities/cli.ts +2 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/entities/cli.ts"],
4
- "sourcesContent": ["import { getCliModules, getDefaultEncryptionMaps, type Module, type ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CacheStrategy } from '@open-mercato/cache/types'\nimport { CustomEntity, CustomFieldDef, EncryptionMap } from './data/entities'\nimport {\n installCustomEntitiesFromModules,\n getAggregatedCustomEntityConfigs,\n} from './lib/install-from-ce'\nimport readline from 'node:readline/promises'\nimport { stdin as input, stdout as output } from 'node:process'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { createKmsService, type KmsService, type TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport {\n decryptWithAesGcm,\n decryptWithAesGcmStrict,\n TenantDataEncryptionError,\n TenantDataEncryptionErrorCode,\n} from '@open-mercato/shared/lib/encryption/aes'\nimport {\n TenantDataEncryptionService,\n parseDecryptedFieldValue,\n} from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { resolveEntityIdFromMetadata } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { Organization } from '../directory/data/entities'\nimport crypto from 'node:crypto'\n\nfunction parseArgs(rest: string[]) {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]\n if (!a) continue\n if (a.startsWith('--')) {\n const [k, v] = a.replace(/^--/, '').split('=')\n if (v !== undefined) args[k] = v\n else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) { args[k] = rest[i + 1]!; i++ }\n else args[k] = true\n }\n }\n return args\n}\n\nconst seedDefs: ModuleCli = {\n command: 'install',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string)\n const globalOnly = Boolean(args.global)\n const dry = Boolean(args['dry-run'] || args.dry)\n const force = Boolean(args.force)\n const includeGlobal = args['no-global'] ? false : true\n\n if (globalOnly && includeGlobal === false) {\n console.error('Cannot combine --global with --no-global.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n let cache: CacheStrategy | null = null\n try { cache = resolve('cache') as CacheStrategy } catch {}\n\n const tenantIds = tenantIdArg\n ? [tenantIdArg]\n : (globalOnly ? [] : undefined)\n\n const logger = (message: string) => {\n const prefix = dry ? '[dry-run] ' : ''\n console.log(`${prefix}${message}`)\n }\n\n const result = await installCustomEntitiesFromModules(em, cache, {\n tenantIds,\n includeGlobal,\n dryRun: dry,\n force,\n logger,\n })\n const label = dry ? 'Dry-run' : 'Sync'\n console.log(`\u2705 ${label} complete: processed=${result.processed}, updated=${result.synchronized}, fieldsChanged=${result.fieldChanges}, skipped=${result.skipped}`)\n },\n}\n\n// Reinstall: remove existing definitions for target scope and re-seed from modules\nconst reinstallDefs: ModuleCli = {\n command: 'reinstall',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string)\n const globalOnly = Boolean(args.global)\n const dry = Boolean(args['dry-run'] || args.dry)\n const includeGlobal = globalOnly ? true : (args['no-global'] ? false : true)\n\n if (globalOnly && includeGlobal === false) {\n console.error('Cannot combine --global with --no-global.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n let cache: CacheStrategy | null = null\n try { cache = resolve('cache') as CacheStrategy } catch {}\n\n const tenantIds = tenantIdArg\n ? [tenantIdArg]\n : (globalOnly ? [] : undefined)\n\n const aggregates = getAggregatedCustomEntityConfigs()\n const relevant = aggregates.filter((entry) => (globalOnly ? entry.spec?.global === true : true))\n if (!relevant.length) {\n console.log('No custom entities or fields discovered. Nothing to reinstall.')\n return\n }\n const entityIds = Array.from(new Set(relevant.map((entry) => entry.entityId)))\n if (!entityIds.length) {\n console.log('No entity ids discovered. Nothing to reinstall.')\n return\n }\n\n const logger = (message: string) => {\n const prefix = dry ? '[dry-run] ' : ''\n console.log(`${prefix}${message}`)\n }\n\n if (dry) {\n console.log('Dry-run: would remove existing custom entity definitions before reinstall.')\n } else {\n const fieldWhere: any = { entityId: { $in: entityIds } }\n if (tenantIds !== undefined) {\n if (tenantIds.length === 0) fieldWhere.tenantId = null\n else fieldWhere.tenantId = { $in: tenantIds }\n }\n const removedFields = await em.nativeDelete(CustomFieldDef, fieldWhere)\n\n const entityWhere: any = { entityId: { $in: entityIds } }\n if (tenantIds !== undefined) {\n if (tenantIds.length === 0) entityWhere.tenantId = null\n else entityWhere.tenantId = { $in: tenantIds }\n }\n const removedEntities = await em.nativeDelete(CustomEntity, entityWhere)\n\n if (cache && entityIds.length) {\n try {\n await cache.deleteByTags(entityIds.map((id) => `custom-entity:${id}`))\n } catch {}\n }\n console.log(`Cleared definitions: fields=${removedFields}, entities=${removedEntities}`)\n }\n\n const result = await installCustomEntitiesFromModules(em, cache, {\n tenantIds,\n includeGlobal,\n dryRun: dry,\n force: true,\n logger,\n })\n const label = dry ? 'Dry-run' : 'Reinstall'\n console.log(`\u2705 ${label} complete: processed=${result.processed}, updated=${result.synchronized}, fieldsChanged=${result.fieldChanges}, skipped=${result.skipped}`)\n },\n}\n\n// Interactive: add a single custom field definition\nconst addField: ModuleCli = {\n command: 'add-field',\n async run(rest) {\n const args = parseArgs(rest)\n const rl = readline.createInterface({ input, output })\n const ask = async (q: string, d?: string) => {\n const a = (await rl.question(d ? `${q} [${d}]: ` : `${q}: `)).trim()\n return a || (d ?? '')\n }\n const askBool = async (q: string, d = false) => {\n const a = (await ask(q, d ? 'y' : 'n')).toLowerCase()\n return parseBooleanToken(a) === true\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n const entityId = (args.entity as string) || (args.e as string) || await ask('Entity ID (e.g., example:todo)')\n const isGlobal = args.global ? true : await askBool('Global (no organization)?', false)\n const orgId = isGlobal ? null : ((args.org as string) || (args.organizationId as string) || await ask('Organization ID'))\n const tenantId = isGlobal ? null : ((args.tenant as string) || (args.tenantId as string) || await ask('Tenant ID'))\n const key = (args.key as string) || await ask('Field key (snake_case)')\n let kind = (args.kind as string) || await ask(\"Kind (text|multiline|integer|float|boolean|select|currency|relation|attachment)\", 'text')\n kind = kind.toLowerCase()\n if (!['text','multiline','integer','float','boolean','select','currency','relation','attachment'].includes(kind)) throw new Error('Invalid kind')\n const label = (args.label as string) || (await ask('Label', key))\n const description = (args.description as string) || ''\n const required = args.required !== undefined ? Boolean(args.required) : await askBool('Required?', false)\n const multi = args.multi !== undefined ? Boolean(args.multi) : await askBool('Allow multiple?', false)\n let options: string[] | undefined\n if (kind === 'select') {\n const raw = (args.options as string) || await ask('Options (comma-separated)', 'low,medium,high')\n options = raw.split(',').map((s) => s.trim()).filter(Boolean)\n }\n let defaultValue: any = undefined\n const defRaw = (args.default as string) ?? (args.defaultValue as string)\n const needDefault = defRaw !== undefined ? defRaw : await ask('Default value (leave empty for none)', '')\n if (needDefault !== '') {\n switch (kind) {\n case 'integer': defaultValue = Number(needDefault); break\n case 'float': defaultValue = Number(needDefault); break\n case 'boolean': defaultValue = parseBooleanToken(String(needDefault)) === true; break\n default: defaultValue = String(needDefault)\n }\n }\n const filterable = args.filterable !== undefined ? Boolean(args.filterable) : await askBool('Filterable?', true)\n const listVisible = args.listVisible !== undefined ? Boolean(args.listVisible) : await askBool('Visible in list?', true)\n const formEditable = args.formEditable !== undefined ? Boolean(args.formEditable) : await askBool('Editable in forms?', true)\n const indexed = args.indexed !== undefined ? Boolean(args.indexed) : await askBool('Indexed?', false)\n\n const where = { entityId, organizationId: orgId, tenantId: tenantId, key }\n const existing = await em.findOne(CustomFieldDef, where)\n const configJson: any = {}\n if (options) configJson.options = options\n if (defaultValue !== undefined) configJson.defaultValue = defaultValue\n if (required !== undefined) configJson.required = required\n if (multi !== undefined) configJson.multi = multi\n if (filterable !== undefined) configJson.filterable = filterable\n if (indexed !== undefined) configJson.indexed = indexed\n if (listVisible !== undefined) configJson.listVisible = listVisible\n if (formEditable !== undefined) configJson.formEditable = formEditable\n if (label !== undefined) configJson.label = label\n if (description !== undefined) configJson.description = description\n\n if (!existing) {\n await em.persist(em.create(CustomFieldDef, {\n entityId,\n organizationId: orgId,\n tenantId: tenantId,\n key,\n kind,\n configJson,\n isActive: true,\n })).flush()\n console.log(`Created custom field: ${entityId}.${key} (${kind})${orgId == null ? ' [global]' : ` [org=${orgId}, tenant=${tenantId}]`}`)\n } else {\n existing.kind = kind as any\n existing.configJson = configJson\n existing.isActive = true\n await em.flush()\n console.log(`Updated custom field: ${entityId}.${key} (${kind})${orgId == null ? ' [global]' : ` [org=${orgId}, tenant=${tenantId}]`}`)\n }\n } catch (e: any) {\n console.error('Failed:', e?.message || e)\n } finally {\n await rl.close()\n }\n },\n}\n\nfunction resolveEncryptionMapModules(): Module[] {\n const cliModules = getCliModules()\n if (cliModules.length > 0) return cliModules\n try {\n const { getModules } = require('@open-mercato/shared/lib/modules/registry')\n return getModules()\n } catch {\n return []\n }\n}\n\nasync function upsertEncryptionMaps(em: any, tenantId: string, organizationId: string | null, logger: (msg: string) => void) {\n for (const spec of getDefaultEncryptionMaps(resolveEncryptionMapModules())) {\n const existing = await em.findOne(EncryptionMap, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (existing) {\n existing.fieldsJson = spec.fields\n existing.isActive = true\n existing.updatedAt = new Date()\n logger(`\uD83D\uDD12 Updated encryption map for ${spec.entityId} \u2728`)\n await em.persist(existing).flush()\n continue\n }\n const map = em.create(EncryptionMap, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n fieldsJson: spec.fields,\n isActive: true,\n })\n await em.persist(map).flush()\n logger(`Created encryption map for ${spec.entityId}`)\n }\n}\n\nconst seedEncryptionMaps: ModuleCli = {\n command: 'seed-encryption',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenant as string) || (args.tenantId as string)\n const organizationId = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n\n if (!tenantId) {\n console.error('tenant id is required (use --tenant <uuid>)')\n return\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.warn('TENANT_DATA_ENCRYPTION is disabled; skipping encryption map seeding.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const logger = (msg: string) => console.log(msg)\n await upsertEncryptionMaps(em, tenantId, organizationId, logger)\n console.log('\u2705 Encryption maps seeded')\n },\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/(?:^['\"]|['\"]$)/g, '')\n}\n\nclass DerivedKeyKmsService implements KmsService {\n private root: Buffer\n constructor(secret: string) {\n this.root = crypto.createHash('sha256').update(normalizeKeyInput(secret)).digest()\n }\n\n isHealthy(): boolean {\n return true\n }\n\n private deriveKey(tenantId: string): string {\n const iterations = 310_000\n const keyLength = 32\n const derived = crypto.pbkdf2Sync(this.root, tenantId, iterations, keyLength, 'sha512')\n return derived.toString('base64')\n }\n\n async getTenantDek(tenantId: string): Promise<TenantDek | null> {\n if (!tenantId) return null\n return { tenantId, key: this.deriveKey(tenantId), fetchedAt: Date.now() }\n }\n\n async createTenantDek(tenantId: string): Promise<TenantDek | null> {\n return this.getTenantDek(tenantId)\n }\n}\n\nfunction fingerprintDek(dek: TenantDek | null): string | null {\n if (!dek?.key) return null\n return crypto.createHash('sha256').update(dek.key).digest('hex').slice(0, 12)\n}\n\nfunction decryptWithOldKey(\n payload: string,\n dek: TenantDek | null,\n): string | null {\n if (!dek?.key) return null\n return decryptWithAesGcm(payload, dek.key)\n}\n\nfunction resolveProperty(meta: any, field: string): { columnName: string | null; prop: any | null } {\n if (!meta?.properties) return { columnName: null, prop: null }\n const candidates = [\n field,\n field.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),\n field.replace(/([A-Z])/g, '_$1').toLowerCase(),\n ]\n for (const candidate of candidates) {\n const prop = meta.properties[candidate]\n const fieldName =\n prop?.fieldName ??\n (Array.isArray(prop?.fieldNames) && prop.fieldNames.length ? prop.fieldNames[0] : undefined)\n if (typeof fieldName === 'string' && fieldName.length) return { columnName: fieldName, prop }\n if (prop?.name) return { columnName: prop.name, prop }\n }\n return { columnName: null, prop: null }\n}\n\nfunction buildEntityMetaRegistry(em: any): Map<string, any> {\n const registry = em?.getMetadata?.()\n const allMetaRaw = typeof registry?.getAll === 'function' ? registry.getAll() : []\n const allMeta = Array.isArray(allMetaRaw) ? allMetaRaw : Object.values(allMetaRaw ?? {})\n const metaByEntityId = new Map<string, any>()\n for (const meta of allMeta) {\n const resolved = resolveEntityIdFromMetadata(meta)\n if (resolved) metaByEntityId.set(resolved, meta)\n }\n return metaByEntityId\n}\n\ninterface EncryptionMapMeta {\n entityId: string\n meta: any\n fields: Array<{ field: string; hashField?: string | null }>\n tenantId: string\n}\n\nfunction resolveMapMeta(\n map: EncryptionMap,\n metaByEntityId: Map<string, any>,\n warn: (msg: string) => void = () => {},\n): EncryptionMapMeta | null {\n const entityId = String(map.entityId)\n const meta = metaByEntityId.get(entityId)\n if (!meta) {\n warn(`Skipping ${entityId}: metadata not found.`)\n return null\n }\n const fields = Array.isArray(map.fieldsJson) ? map.fieldsJson : []\n if (!fields.length) return null\n const tenantId = map.tenantId ? String(map.tenantId) : null\n if (!tenantId) return null\n return { entityId, meta, fields, tenantId }\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string) || null\n const organizationIdArg = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n const oldKey = (args['old-key'] as string) || (args.oldKey as string) || null\n const dryRun = Boolean(args['dry-run'] || args.dry)\n const debug = Boolean(args.debug)\n const rotate = Boolean(oldKey)\n if (rotate && !tenantIdArg) {\n console.warn(\n '\u26A0\uFE0F Rotating with --old-key across all tenants. A single old key should normally target one tenant; consider --tenant.',\n )\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const conn: any = em?.getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantIdArg ?? null,\n organizationId: organizationIdArg ?? null,\n })\n if (tenantIdArg) {\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(tenantIdArg) ?? Promise.resolve(null),\n encryptionService.getDek(tenantIdArg),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n } else {\n console.log('[rotate-encryption-key] dek fingerprints skipped (no tenantId)')\n }\n }\n\n const isEncryptedPayload = (value: unknown): boolean => {\n if (typeof value !== 'string') return false\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n }\n\n const metaByEntityId = buildEntityMetaRegistry(em)\n\n const where: any = { deletedAt: null }\n if (tenantIdArg) where.tenantId = tenantIdArg\n if (organizationIdArg) where.organizationId = organizationIdArg\n const maps = await em.find(EncryptionMap, where)\n if (!maps.length) {\n console.log('No encryption maps found for the selected scope.')\n return\n }\n\n const formatValueForColumn = (prop: any, value: unknown): unknown => {\n if (value === null || value === undefined) return value\n const types = Array.isArray(prop?.columnTypes) ? prop.columnTypes : []\n const type = String(prop?.type ?? '').toLowerCase()\n const isJson = types.some((entry: string) => entry.toLowerCase().includes('json')) || type === 'json' || type === 'jsonb'\n if (!isJson) return value\n return JSON.stringify(value)\n }\n\n const resolveScopes = async (tenantId: string, organizationId: string | null) => {\n if (organizationId) return [{ tenantId, organizationId }]\n const orgs = await em.find(Organization, { tenant: tenantId })\n const scopes = orgs.map((org: Organization) => ({\n tenantId,\n organizationId: String(org.id),\n }))\n scopes.push({ tenantId, organizationId: null })\n return scopes\n }\n\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (\n entityId: string,\n meta: any,\n fields: Array<{ field: string; hashField?: string | null }>,\n scope: { tenantId: string; organizationId: string | null },\n ): Promise<number> => {\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const columns = new Set<string>()\n columns.add(pk)\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n if (resolved?.columnName) columns.add(resolved.columnName)\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n if (resolvedHash?.columnName) columns.add(resolvedHash.columnName)\n }\n }\n const columnList = Array.from(columns)\n if (!columnList.length) return 0\n const tableName = meta?.tableName\n if (!tableName) return 0\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const selectSql = `select ${columnList.map((c) => `\"${c}\"`).join(', ')} from ${qualifiedTable} where tenant_id = ? and organization_id is not distinct from ?`\n const rows = await conn.execute(selectSql, [scope.tenantId, scope.organizationId])\n const list = Array.isArray(rows) ? rows : []\n if (!list.length) return 0\n let updated = 0\n for (const row of list) {\n const payload: Record<string, unknown> = {}\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rotate && !isEncryptedPayload(rawValue)) {\n continue\n }\n payload[rule.field] = rawValue\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n const hashCol = resolvedHash?.columnName\n if (hashCol) payload[rule.hashField] = row[hashCol]\n }\n }\n if (rotate && !Object.keys(payload).length) {\n continue\n }\n if (rotate && oldKms) {\n let oldDek = oldDekCache.get(scope.tenantId) ?? null\n if (!oldDekCache.has(scope.tenantId)) {\n oldDek = await oldKms.getTenantDek(scope.tenantId)\n oldDekCache.set(scope.tenantId, oldDek)\n }\n for (const rule of fields) {\n const value = payload[rule.field]\n if (typeof value !== 'string' || !isEncryptedPayload(value)) continue\n const decrypted = decryptWithOldKey(value, oldDek)\n if (decrypted === null) continue\n payload[rule.field] = parseDecryptedFieldValue(decrypted)\n }\n }\n const encrypted = await encryptionService.encryptEntityPayload(\n entityId,\n payload,\n scope.tenantId,\n scope.organizationId,\n )\n const updates: Record<string, unknown> = {}\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n const col = resolved?.columnName\n if (!col) continue\n const nextValue = (encrypted as any)[rule.field]\n if (nextValue !== undefined && nextValue !== row[col]) {\n if (!rotate && isEncryptedPayload(row[col])) continue\n updates[col] = formatValueForColumn(resolved?.prop, nextValue)\n }\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n const hashCol = resolvedHash?.columnName\n const hashValue = (encrypted as any)[rule.hashField]\n if (hashCol && hashValue !== undefined && hashValue !== row[hashCol]) {\n updates[hashCol] = formatValueForColumn(resolvedHash?.prop, hashValue)\n }\n }\n }\n if (!Object.keys(updates).length) continue\n if (!dryRun) {\n const setSql = Object.keys(updates).map((col) => `\"${col}\" = ?`).join(', ')\n await conn.execute(\n `update ${qualifiedTable} set ${setSql} where \"${pk}\" = ?`,\n [...Object.values(updates), row[pk]],\n )\n }\n updated += 1\n }\n return updated\n }\n\n let total = 0\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId, console.warn)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const scopes = await resolveScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n for (const scope of scopes) {\n const updated = await processScope(entityId, meta, fields, scope)\n if (updated > 0) {\n console.log(\n `${dryRun ? '[dry-run] ' : ''}Encrypted ${updated} record(s) for ${entityId} org=${scope.organizationId ?? 'null'}`\n )\n }\n total += updated\n }\n }\n\n if (total > 0) {\n console.log(`Encrypted ${total} record(s) across mapped entities.`)\n } else {\n console.log('All mapped entity fields already encrypted for the selected scope.')\n }\n },\n}\n\nconst decryptDatabase: ModuleCli = {\n command: 'decrypt-database',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string) || null\n const organizationIdArg = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n const entityIdArg = (args.entity as string) || null\n const checkMode = Boolean(args.check)\n const dryRun = Boolean(args['dry-run'] || args.dry) || checkMode\n const deactivateMaps = Boolean(args['deactivate-maps'])\n const confirm = (args.confirm as string) || null\n const batchSize = Math.max(1, parseInt(String(args['batch-size'] || args.batchSize || '500'), 10) || 500)\n const sleepMs = Math.max(0, parseInt(String(args['sleep-ms'] || args.sleepMs || '0'), 10) || 0)\n const debug = Boolean(args.debug)\n\n if (!tenantIdArg) {\n console.error('--tenant <uuid> is required.')\n return\n }\n\n if (!checkMode) {\n if (!confirm) {\n console.error('--confirm <tenantUuid> is required (safety gate). Pass the exact tenant UUID to confirm the operation.')\n return\n }\n if (confirm !== tenantIdArg) {\n console.error(`--confirm value \"${confirm}\" does not match --tenant \"${tenantIdArg}\". Aborting.`)\n return\n }\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const conn: any = em?.getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw database connection; aborting.')\n return\n }\n\n if (!checkMode && !isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting. Data may already be decrypted.')\n return\n }\n\n const metaByEntityId = buildEntityMetaRegistry(em)\n\n const resolveDecryptScopes = async (\n tenantId: string,\n organizationId: string | null,\n ): Promise<Array<{ tenantId: string; organizationId: string | null }>> => {\n if (organizationId) return [{ tenantId, organizationId }]\n const rows = await conn.execute(\n `SELECT DISTINCT organization_id FROM encryption_maps WHERE tenant_id = ? AND deleted_at IS NULL`,\n [tenantId],\n )\n const orgIds = new Set<string | null>()\n for (const row of Array.isArray(rows) ? rows : []) {\n orgIds.add(row.organization_id ?? null)\n }\n orgIds.add(null)\n return Array.from(orgIds).map((orgId) => ({ tenantId, organizationId: orgId }))\n }\n\n const mapWhere: any = { tenantId: tenantIdArg, deletedAt: null, isActive: true }\n if (organizationIdArg) mapWhere.organizationId = organizationIdArg\n if (entityIdArg) mapWhere.entityId = entityIdArg\n const maps = await em.find(EncryptionMap, mapWhere)\n\n if (!maps.length) {\n console.log('No active encryption maps found for the selected scope.')\n return\n }\n\n const kms = createKmsService()\n const dekCache = new Map<string, TenantDek | null>()\n const getDek = async (tenantId: string): Promise<TenantDek | null> => {\n if (dekCache.has(tenantId)) return dekCache.get(tenantId) ?? null\n const dek = await kms.getTenantDek(tenantId)\n dekCache.set(tenantId, dek)\n return dek\n }\n\n if (checkMode) {\n const envValue = process.env.TENANT_DATA_ENCRYPTION ?? '(not set)'\n console.log(`TENANT_DATA_ENCRYPTION = ${envValue}`)\n console.log(`Active EncryptionMap records for scope: ${maps.length}`)\n let encryptedCandidatesSampled = 0\n let malformedPayloadCountSampled = 0\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const dek = await getDek(tenantId).catch(() => null)\n if (!dek) continue\n const scopes = await resolveDecryptScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const tableName = meta?.tableName\n if (!tableName) continue\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const fieldCols = fields.flatMap((f: any) => {\n const r = resolveProperty(meta, f.field)\n return r.columnName ? [r.columnName] : []\n })\n const colList = Array.from(new Set([pk, ...fieldCols]))\n for (const scope of scopes) {\n const sampleRows = await conn.execute(\n `SELECT ${colList.map((c: string) => `\"${c}\"`).join(', ')} FROM ${qualifiedTable} WHERE tenant_id = ? AND organization_id IS NOT DISTINCT FROM ? LIMIT 100`,\n [scope.tenantId, scope.organizationId],\n ).catch(() => [])\n for (const row of Array.isArray(sampleRows) ? sampleRows : []) {\n let rowHasEncrypted = false\n for (const fieldRule of fields) {\n const resolved = resolveProperty(meta, fieldRule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rawValue === null || rawValue === undefined) continue\n try {\n decryptWithAesGcmStrict(String(rawValue), dek.key)\n rowHasEncrypted = true\n } catch (e: any) {\n if (e instanceof TenantDataEncryptionError && e.code === TenantDataEncryptionErrorCode.MALFORMED_PAYLOAD) {\n malformedPayloadCountSampled++\n }\n }\n }\n if (rowHasEncrypted) encryptedCandidatesSampled++\n }\n }\n }\n console.log(`estimated encrypted candidates (sampled): ${encryptedCandidatesSampled}`)\n if (malformedPayloadCountSampled > 0) {\n console.warn(`\u26A0 malformed payloads (sampled): ${malformedPayloadCountSampled} \u2014 may indicate corruption`)\n } else {\n console.log(`malformed payloads (sampled): ${malformedPayloadCountSampled}`)\n }\n console.log('not a proof of absence \u2014 run full command + rerun --check to confirm')\n return\n }\n\n let totalRowsFetched = 0\n let totalRowsUpdated = 0\n let totalHashFieldsCleared = 0\n const totalHashFieldsSkipped = new Set<string>()\n let totalMalformedPayloadCount = 0\n const malformedByLocation = new Map<string, number>()\n let totalEntitiesProcessed = 0\n\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId, console.warn)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const dek = await getDek(tenantId)\n if (!dek) {\n console.warn(`No DEK available for tenant ${tenantId}; skipping ${entityId}.`)\n continue\n }\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const tableName = meta?.tableName\n if (!tableName) {\n console.warn(`Skipping ${entityId}: table name not found.`)\n continue\n }\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const fieldCols = fields.flatMap((f: any) => {\n const r = resolveProperty(meta, f.field)\n return r.columnName ? [r.columnName] : []\n })\n const colList = Array.from(new Set([pk, ...fieldCols]))\n totalEntitiesProcessed++\n\n const scopes = await resolveDecryptScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n\n for (const scope of scopes) {\n let lastId: string | null = null\n let scopeMalformedCount = 0\n\n while (true) {\n let selectSql = `SELECT ${colList.map((c: string) => `\"${c}\"`).join(', ')} FROM ${qualifiedTable} WHERE tenant_id = ? AND organization_id IS NOT DISTINCT FROM ?`\n const selectParams: unknown[] = [scope.tenantId, scope.organizationId]\n if (lastId !== null) {\n selectSql += ` AND \"${pk}\" > ?`\n selectParams.push(lastId)\n }\n selectSql += ` ORDER BY \"${pk}\" LIMIT ?`\n selectParams.push(batchSize)\n\n const batchRows = await conn.execute(selectSql, selectParams)\n const batch = Array.isArray(batchRows) ? batchRows : []\n if (!batch.length) break\n\n lastId = String(batch[batch.length - 1]![pk])\n totalRowsFetched += batch.length\n\n const batchStart = Date.now()\n await conn.execute('BEGIN')\n let batchCommitted = false\n try {\n for (const row of batch) {\n const updates: Record<string, unknown> = {}\n let rowDecrypted = false\n\n for (const fieldRule of fields) {\n const resolved = resolveProperty(meta, fieldRule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rawValue === null || rawValue === undefined) continue\n try {\n const decrypted = decryptWithAesGcmStrict(String(rawValue), dek.key)\n let valueToWrite: string\n try {\n const parsed = JSON.parse(decrypted)\n valueToWrite = typeof parsed === 'string' ? parsed : decrypted\n } catch {\n valueToWrite = decrypted\n }\n updates[col] = valueToWrite\n rowDecrypted = true\n } catch (e: any) {\n if (e instanceof TenantDataEncryptionError) {\n if (e.code === TenantDataEncryptionErrorCode.AUTH_FAILED) {\n // Value is plaintext \u2014 skip silently\n } else if (e.code === TenantDataEncryptionErrorCode.MALFORMED_PAYLOAD) {\n scopeMalformedCount++\n const locationKey = `${tableName}:${col}`\n malformedByLocation.set(locationKey, (malformedByLocation.get(locationKey) ?? 0) + 1)\n console.warn(`\u26A0 MALFORMED_PAYLOAD for ${entityId} field \"${col}\" row ${row[pk]}; skipping field.`)\n } else {\n throw e\n }\n } else {\n throw e\n }\n }\n }\n\n if (rowDecrypted) {\n for (const fieldRule of fields) {\n if (!fieldRule.hashField) continue\n const resolvedHash = resolveProperty(meta, fieldRule.hashField)\n const hashCol = resolvedHash?.columnName\n if (!hashCol) {\n const skippedKey = `${tableName}:${fieldRule.hashField}`\n if (!totalHashFieldsSkipped.has(skippedKey)) {\n console.warn(`\u26A0 Hash column \"${fieldRule.hashField}\" not found in metadata for ${entityId}; skipping.`)\n totalHashFieldsSkipped.add(skippedKey)\n }\n continue\n }\n updates[hashCol] = null\n totalHashFieldsCleared++\n }\n }\n\n if (Object.keys(updates).length > 0) {\n if (!dryRun) {\n const setSql = Object.keys(updates).map((col) => `\"${col}\" = ?`).join(', ')\n await conn.execute(\n `UPDATE ${qualifiedTable} SET ${setSql} WHERE \"${pk}\" = ?`,\n [...Object.values(updates), row[pk]],\n )\n }\n totalRowsUpdated++\n }\n }\n await conn.execute('COMMIT')\n batchCommitted = true\n } catch (fatalErr: any) {\n if (!batchCommitted) {\n try { await conn.execute('ROLLBACK') } catch {}\n }\n console.error(`Fatal error during batch processing for ${entityId}: ${(fatalErr as Error)?.message || String(fatalErr)}`)\n throw fatalErr\n }\n\n const batchDurationMs = Date.now() - batchStart\n if (debug) {\n console.log(\n `[debug] Batch ${entityId} org=${scope.organizationId ?? 'null'}: ${batch.length} rows in ${batchDurationMs}ms, scopeMalformed=${scopeMalformedCount}`,\n )\n if (batchDurationMs > 30_000) {\n console.warn('\u26A0 Batch took >30s; consider reducing --batch-size.')\n }\n }\n if (sleepMs > 0) {\n await new Promise<void>((r) => setTimeout(r, sleepMs))\n }\n }\n\n totalMalformedPayloadCount += scopeMalformedCount\n }\n }\n\n if (deactivateMaps && !dryRun) {\n let deactivateSql = `UPDATE encryption_maps SET is_active = false, deleted_at = now() WHERE tenant_id = ? AND deleted_at IS NULL`\n const deactivateParams: unknown[] = [tenantIdArg]\n if (organizationIdArg) {\n deactivateSql += ` AND (organization_id = ? OR organization_id IS NULL)`\n deactivateParams.push(organizationIdArg)\n }\n if (entityIdArg) {\n deactivateSql += ` AND entity_id = ?`\n deactivateParams.push(entityIdArg)\n }\n await conn.execute(deactivateSql, deactivateParams)\n console.warn('\u26A0 Restart all application replicas \u2014 in-process map caches may still be active.')\n if (isTenantDataEncryptionEnabled()) {\n console.warn('\u26A0 Env TENANT_DATA_ENCRYPTION is still true \u2014 new writes will be re-encrypted until env is updated and replicas restarted.')\n }\n }\n\n if (deactivateMaps && dryRun) {\n console.log(`[dry-run] Would deactivate ${maps.length} EncryptionMap record(s).`)\n }\n\n const prefix = dryRun ? '[dry-run] ' : ''\n console.log(`\\n${prefix}Decryption summary:`)\n console.log(` Rows fetched: ${totalRowsFetched}`)\n console.log(` Rows updated: ${totalRowsUpdated}`)\n console.log(` Entities processed: ${totalEntitiesProcessed}`)\n console.log(` Hash fields cleared: ${totalHashFieldsCleared}`)\n if (totalHashFieldsSkipped.size > 0) {\n console.log(` Hash fields skipped (missing columns): ${Array.from(totalHashFieldsSkipped).join(', ')}`)\n }\n if (totalMalformedPayloadCount > 0) {\n console.warn(\n ` \u26A0 ${totalMalformedPayloadCount} field value(s) returned MALFORMED_PAYLOAD and were skipped; these may be corrupted ciphertexts. Investigate before assuming decryption is complete.`,\n )\n if (debug && malformedByLocation.size > 0) {\n const top = Array.from(malformedByLocation.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n console.log(' Top malformed locations:')\n for (const [loc, count] of top) {\n console.log(` ${loc}: ${count}`)\n }\n }\n }\n\n if (!dryRun) {\n console.log(`\\n\u2705 Decryption complete. Required next steps:`)\n console.log(` 1. Set TENANT_DATA_ENCRYPTION=false in your environment / secrets`)\n console.log(` 2. Restart all application replicas`)\n console.log(` 3. Run: mercato query_index reindex --tenant ${tenantIdArg} \u2190 run after env flip + restart; search/filter degraded until this completes`)\n console.log(` 4. Run: mercato entities decrypt-database --tenant ${tenantIdArg} --check to confirm no encrypted values remain`)\n console.log(` NOTE: if the run was long and concurrent inserts occurred, run again before step 4 \u2014 it is idempotent.`)\n }\n },\n}\n\n// Keep default export stable (install first for help listing)\nexport default [seedDefs, reinstallDefs, addField, seedEncryptionMaps, rotateEncryptionKey, decryptDatabase]\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { getCliModules, getDefaultEncryptionMaps, type Module, type ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CacheStrategy } from '@open-mercato/cache/types'\nimport { CustomEntity, CustomFieldDef, EncryptionMap } from './data/entities'\nimport {\n installCustomEntitiesFromModules,\n getAggregatedCustomEntityConfigs,\n} from './lib/install-from-ce'\nimport readline from 'node:readline/promises'\nimport { stdin as input, stdout as output } from 'node:process'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { parseCommaSeparatedList } from '@open-mercato/shared/lib/string'\nimport { createKmsService, type KmsService, type TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport {\n decryptWithAesGcm,\n decryptWithAesGcmStrict,\n TenantDataEncryptionError,\n TenantDataEncryptionErrorCode,\n} from '@open-mercato/shared/lib/encryption/aes'\nimport {\n TenantDataEncryptionService,\n parseDecryptedFieldValue,\n} from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { resolveEntityIdFromMetadata } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { Organization } from '../directory/data/entities'\nimport crypto from 'node:crypto'\n\nfunction parseArgs(rest: string[]) {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]\n if (!a) continue\n if (a.startsWith('--')) {\n const [k, v] = a.replace(/^--/, '').split('=')\n if (v !== undefined) args[k] = v\n else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) { args[k] = rest[i + 1]!; i++ }\n else args[k] = true\n }\n }\n return args\n}\n\nconst seedDefs: ModuleCli = {\n command: 'install',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string)\n const globalOnly = Boolean(args.global)\n const dry = Boolean(args['dry-run'] || args.dry)\n const force = Boolean(args.force)\n const includeGlobal = args['no-global'] ? false : true\n\n if (globalOnly && includeGlobal === false) {\n console.error('Cannot combine --global with --no-global.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n let cache: CacheStrategy | null = null\n try { cache = resolve('cache') as CacheStrategy } catch {}\n\n const tenantIds = tenantIdArg\n ? [tenantIdArg]\n : (globalOnly ? [] : undefined)\n\n const logger = (message: string) => {\n const prefix = dry ? '[dry-run] ' : ''\n console.log(`${prefix}${message}`)\n }\n\n const result = await installCustomEntitiesFromModules(em, cache, {\n tenantIds,\n includeGlobal,\n dryRun: dry,\n force,\n logger,\n })\n const label = dry ? 'Dry-run' : 'Sync'\n console.log(`\u2705 ${label} complete: processed=${result.processed}, updated=${result.synchronized}, fieldsChanged=${result.fieldChanges}, skipped=${result.skipped}`)\n },\n}\n\n// Reinstall: remove existing definitions for target scope and re-seed from modules\nconst reinstallDefs: ModuleCli = {\n command: 'reinstall',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string)\n const globalOnly = Boolean(args.global)\n const dry = Boolean(args['dry-run'] || args.dry)\n const includeGlobal = globalOnly ? true : (args['no-global'] ? false : true)\n\n if (globalOnly && includeGlobal === false) {\n console.error('Cannot combine --global with --no-global.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n let cache: CacheStrategy | null = null\n try { cache = resolve('cache') as CacheStrategy } catch {}\n\n const tenantIds = tenantIdArg\n ? [tenantIdArg]\n : (globalOnly ? [] : undefined)\n\n const aggregates = getAggregatedCustomEntityConfigs()\n const relevant = aggregates.filter((entry) => (globalOnly ? entry.spec?.global === true : true))\n if (!relevant.length) {\n console.log('No custom entities or fields discovered. Nothing to reinstall.')\n return\n }\n const entityIds = Array.from(new Set(relevant.map((entry) => entry.entityId)))\n if (!entityIds.length) {\n console.log('No entity ids discovered. Nothing to reinstall.')\n return\n }\n\n const logger = (message: string) => {\n const prefix = dry ? '[dry-run] ' : ''\n console.log(`${prefix}${message}`)\n }\n\n if (dry) {\n console.log('Dry-run: would remove existing custom entity definitions before reinstall.')\n } else {\n const fieldWhere: any = { entityId: { $in: entityIds } }\n if (tenantIds !== undefined) {\n if (tenantIds.length === 0) fieldWhere.tenantId = null\n else fieldWhere.tenantId = { $in: tenantIds }\n }\n const removedFields = await em.nativeDelete(CustomFieldDef, fieldWhere)\n\n const entityWhere: any = { entityId: { $in: entityIds } }\n if (tenantIds !== undefined) {\n if (tenantIds.length === 0) entityWhere.tenantId = null\n else entityWhere.tenantId = { $in: tenantIds }\n }\n const removedEntities = await em.nativeDelete(CustomEntity, entityWhere)\n\n if (cache && entityIds.length) {\n try {\n await cache.deleteByTags(entityIds.map((id) => `custom-entity:${id}`))\n } catch {}\n }\n console.log(`Cleared definitions: fields=${removedFields}, entities=${removedEntities}`)\n }\n\n const result = await installCustomEntitiesFromModules(em, cache, {\n tenantIds,\n includeGlobal,\n dryRun: dry,\n force: true,\n logger,\n })\n const label = dry ? 'Dry-run' : 'Reinstall'\n console.log(`\u2705 ${label} complete: processed=${result.processed}, updated=${result.synchronized}, fieldsChanged=${result.fieldChanges}, skipped=${result.skipped}`)\n },\n}\n\n// Interactive: add a single custom field definition\nconst addField: ModuleCli = {\n command: 'add-field',\n async run(rest) {\n const args = parseArgs(rest)\n const rl = readline.createInterface({ input, output })\n const ask = async (q: string, d?: string) => {\n const a = (await rl.question(d ? `${q} [${d}]: ` : `${q}: `)).trim()\n return a || (d ?? '')\n }\n const askBool = async (q: string, d = false) => {\n const a = (await ask(q, d ? 'y' : 'n')).toLowerCase()\n return parseBooleanToken(a) === true\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n const entityId = (args.entity as string) || (args.e as string) || await ask('Entity ID (e.g., example:todo)')\n const isGlobal = args.global ? true : await askBool('Global (no organization)?', false)\n const orgId = isGlobal ? null : ((args.org as string) || (args.organizationId as string) || await ask('Organization ID'))\n const tenantId = isGlobal ? null : ((args.tenant as string) || (args.tenantId as string) || await ask('Tenant ID'))\n const key = (args.key as string) || await ask('Field key (snake_case)')\n let kind = (args.kind as string) || await ask(\"Kind (text|multiline|integer|float|boolean|select|currency|relation|attachment)\", 'text')\n kind = kind.toLowerCase()\n if (!['text','multiline','integer','float','boolean','select','currency','relation','attachment'].includes(kind)) throw new Error('Invalid kind')\n const label = (args.label as string) || (await ask('Label', key))\n const description = (args.description as string) || ''\n const required = args.required !== undefined ? Boolean(args.required) : await askBool('Required?', false)\n const multi = args.multi !== undefined ? Boolean(args.multi) : await askBool('Allow multiple?', false)\n let options: string[] | undefined\n if (kind === 'select') {\n const raw = (args.options as string) || await ask('Options (comma-separated)', 'low,medium,high')\n options = parseCommaSeparatedList(raw)\n }\n let defaultValue: any = undefined\n const defRaw = (args.default as string) ?? (args.defaultValue as string)\n const needDefault = defRaw !== undefined ? defRaw : await ask('Default value (leave empty for none)', '')\n if (needDefault !== '') {\n switch (kind) {\n case 'integer': defaultValue = Number(needDefault); break\n case 'float': defaultValue = Number(needDefault); break\n case 'boolean': defaultValue = parseBooleanToken(String(needDefault)) === true; break\n default: defaultValue = String(needDefault)\n }\n }\n const filterable = args.filterable !== undefined ? Boolean(args.filterable) : await askBool('Filterable?', true)\n const listVisible = args.listVisible !== undefined ? Boolean(args.listVisible) : await askBool('Visible in list?', true)\n const formEditable = args.formEditable !== undefined ? Boolean(args.formEditable) : await askBool('Editable in forms?', true)\n const indexed = args.indexed !== undefined ? Boolean(args.indexed) : await askBool('Indexed?', false)\n\n const where = { entityId, organizationId: orgId, tenantId: tenantId, key }\n const existing = await em.findOne(CustomFieldDef, where)\n const configJson: any = {}\n if (options) configJson.options = options\n if (defaultValue !== undefined) configJson.defaultValue = defaultValue\n if (required !== undefined) configJson.required = required\n if (multi !== undefined) configJson.multi = multi\n if (filterable !== undefined) configJson.filterable = filterable\n if (indexed !== undefined) configJson.indexed = indexed\n if (listVisible !== undefined) configJson.listVisible = listVisible\n if (formEditable !== undefined) configJson.formEditable = formEditable\n if (label !== undefined) configJson.label = label\n if (description !== undefined) configJson.description = description\n\n if (!existing) {\n await em.persist(em.create(CustomFieldDef, {\n entityId,\n organizationId: orgId,\n tenantId: tenantId,\n key,\n kind,\n configJson,\n isActive: true,\n })).flush()\n console.log(`Created custom field: ${entityId}.${key} (${kind})${orgId == null ? ' [global]' : ` [org=${orgId}, tenant=${tenantId}]`}`)\n } else {\n existing.kind = kind as any\n existing.configJson = configJson\n existing.isActive = true\n await em.flush()\n console.log(`Updated custom field: ${entityId}.${key} (${kind})${orgId == null ? ' [global]' : ` [org=${orgId}, tenant=${tenantId}]`}`)\n }\n } catch (e: any) {\n console.error('Failed:', e?.message || e)\n } finally {\n await rl.close()\n }\n },\n}\n\nfunction resolveEncryptionMapModules(): Module[] {\n const cliModules = getCliModules()\n if (cliModules.length > 0) return cliModules\n try {\n const { getModules } = require('@open-mercato/shared/lib/modules/registry')\n return getModules()\n } catch {\n return []\n }\n}\n\nasync function upsertEncryptionMaps(em: any, tenantId: string, organizationId: string | null, logger: (msg: string) => void) {\n for (const spec of getDefaultEncryptionMaps(resolveEncryptionMapModules())) {\n const existing = await em.findOne(EncryptionMap, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (existing) {\n existing.fieldsJson = spec.fields\n existing.isActive = true\n existing.updatedAt = new Date()\n logger(`\uD83D\uDD12 Updated encryption map for ${spec.entityId} \u2728`)\n await em.persist(existing).flush()\n continue\n }\n const map = em.create(EncryptionMap, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n fieldsJson: spec.fields,\n isActive: true,\n })\n await em.persist(map).flush()\n logger(`Created encryption map for ${spec.entityId}`)\n }\n}\n\nconst seedEncryptionMaps: ModuleCli = {\n command: 'seed-encryption',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenant as string) || (args.tenantId as string)\n const organizationId = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n\n if (!tenantId) {\n console.error('tenant id is required (use --tenant <uuid>)')\n return\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.warn('TENANT_DATA_ENCRYPTION is disabled; skipping encryption map seeding.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const logger = (msg: string) => console.log(msg)\n await upsertEncryptionMaps(em, tenantId, organizationId, logger)\n console.log('\u2705 Encryption maps seeded')\n },\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/(?:^['\"]|['\"]$)/g, '')\n}\n\nclass DerivedKeyKmsService implements KmsService {\n private root: Buffer\n constructor(secret: string) {\n this.root = crypto.createHash('sha256').update(normalizeKeyInput(secret)).digest()\n }\n\n isHealthy(): boolean {\n return true\n }\n\n private deriveKey(tenantId: string): string {\n const iterations = 310_000\n const keyLength = 32\n const derived = crypto.pbkdf2Sync(this.root, tenantId, iterations, keyLength, 'sha512')\n return derived.toString('base64')\n }\n\n async getTenantDek(tenantId: string): Promise<TenantDek | null> {\n if (!tenantId) return null\n return { tenantId, key: this.deriveKey(tenantId), fetchedAt: Date.now() }\n }\n\n async createTenantDek(tenantId: string): Promise<TenantDek | null> {\n return this.getTenantDek(tenantId)\n }\n}\n\nfunction fingerprintDek(dek: TenantDek | null): string | null {\n if (!dek?.key) return null\n return crypto.createHash('sha256').update(dek.key).digest('hex').slice(0, 12)\n}\n\nfunction decryptWithOldKey(\n payload: string,\n dek: TenantDek | null,\n): string | null {\n if (!dek?.key) return null\n return decryptWithAesGcm(payload, dek.key)\n}\n\nfunction resolveProperty(meta: any, field: string): { columnName: string | null; prop: any | null } {\n if (!meta?.properties) return { columnName: null, prop: null }\n const candidates = [\n field,\n field.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),\n field.replace(/([A-Z])/g, '_$1').toLowerCase(),\n ]\n for (const candidate of candidates) {\n const prop = meta.properties[candidate]\n const fieldName =\n prop?.fieldName ??\n (Array.isArray(prop?.fieldNames) && prop.fieldNames.length ? prop.fieldNames[0] : undefined)\n if (typeof fieldName === 'string' && fieldName.length) return { columnName: fieldName, prop }\n if (prop?.name) return { columnName: prop.name, prop }\n }\n return { columnName: null, prop: null }\n}\n\nfunction buildEntityMetaRegistry(em: any): Map<string, any> {\n const registry = em?.getMetadata?.()\n const allMetaRaw = typeof registry?.getAll === 'function' ? registry.getAll() : []\n const allMeta = Array.isArray(allMetaRaw) ? allMetaRaw : Object.values(allMetaRaw ?? {})\n const metaByEntityId = new Map<string, any>()\n for (const meta of allMeta) {\n const resolved = resolveEntityIdFromMetadata(meta)\n if (resolved) metaByEntityId.set(resolved, meta)\n }\n return metaByEntityId\n}\n\ninterface EncryptionMapMeta {\n entityId: string\n meta: any\n fields: Array<{ field: string; hashField?: string | null }>\n tenantId: string\n}\n\nfunction resolveMapMeta(\n map: EncryptionMap,\n metaByEntityId: Map<string, any>,\n warn: (msg: string) => void = () => {},\n): EncryptionMapMeta | null {\n const entityId = String(map.entityId)\n const meta = metaByEntityId.get(entityId)\n if (!meta) {\n warn(`Skipping ${entityId}: metadata not found.`)\n return null\n }\n const fields = Array.isArray(map.fieldsJson) ? map.fieldsJson : []\n if (!fields.length) return null\n const tenantId = map.tenantId ? String(map.tenantId) : null\n if (!tenantId) return null\n return { entityId, meta, fields, tenantId }\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string) || null\n const organizationIdArg = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n const oldKey = (args['old-key'] as string) || (args.oldKey as string) || null\n const dryRun = Boolean(args['dry-run'] || args.dry)\n const debug = Boolean(args.debug)\n const rotate = Boolean(oldKey)\n if (rotate && !tenantIdArg) {\n console.warn(\n '\u26A0\uFE0F Rotating with --old-key across all tenants. A single old key should normally target one tenant; consider --tenant.',\n )\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const conn: any = em?.getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantIdArg ?? null,\n organizationId: organizationIdArg ?? null,\n })\n if (tenantIdArg) {\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(tenantIdArg) ?? Promise.resolve(null),\n encryptionService.getDek(tenantIdArg),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n } else {\n console.log('[rotate-encryption-key] dek fingerprints skipped (no tenantId)')\n }\n }\n\n const isEncryptedPayload = (value: unknown): boolean => {\n if (typeof value !== 'string') return false\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n }\n\n const metaByEntityId = buildEntityMetaRegistry(em)\n\n const where: any = { deletedAt: null }\n if (tenantIdArg) where.tenantId = tenantIdArg\n if (organizationIdArg) where.organizationId = organizationIdArg\n const maps = await em.find(EncryptionMap, where)\n if (!maps.length) {\n console.log('No encryption maps found for the selected scope.')\n return\n }\n\n const formatValueForColumn = (prop: any, value: unknown): unknown => {\n if (value === null || value === undefined) return value\n const types = Array.isArray(prop?.columnTypes) ? prop.columnTypes : []\n const type = String(prop?.type ?? '').toLowerCase()\n const isJson = types.some((entry: string) => entry.toLowerCase().includes('json')) || type === 'json' || type === 'jsonb'\n if (!isJson) return value\n return JSON.stringify(value)\n }\n\n const resolveScopes = async (tenantId: string, organizationId: string | null) => {\n if (organizationId) return [{ tenantId, organizationId }]\n const orgs = await em.find(Organization, { tenant: tenantId })\n const scopes = orgs.map((org: Organization) => ({\n tenantId,\n organizationId: String(org.id),\n }))\n scopes.push({ tenantId, organizationId: null })\n return scopes\n }\n\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (\n entityId: string,\n meta: any,\n fields: Array<{ field: string; hashField?: string | null }>,\n scope: { tenantId: string; organizationId: string | null },\n ): Promise<number> => {\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const columns = new Set<string>()\n columns.add(pk)\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n if (resolved?.columnName) columns.add(resolved.columnName)\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n if (resolvedHash?.columnName) columns.add(resolvedHash.columnName)\n }\n }\n const columnList = Array.from(columns)\n if (!columnList.length) return 0\n const tableName = meta?.tableName\n if (!tableName) return 0\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const selectSql = `select ${columnList.map((c) => `\"${c}\"`).join(', ')} from ${qualifiedTable} where tenant_id = ? and organization_id is not distinct from ?`\n const rows = await conn.execute(selectSql, [scope.tenantId, scope.organizationId])\n const list = Array.isArray(rows) ? rows : []\n if (!list.length) return 0\n let updated = 0\n for (const row of list) {\n const payload: Record<string, unknown> = {}\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rotate && !isEncryptedPayload(rawValue)) {\n continue\n }\n payload[rule.field] = rawValue\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n const hashCol = resolvedHash?.columnName\n if (hashCol) payload[rule.hashField] = row[hashCol]\n }\n }\n if (rotate && !Object.keys(payload).length) {\n continue\n }\n if (rotate && oldKms) {\n let oldDek = oldDekCache.get(scope.tenantId) ?? null\n if (!oldDekCache.has(scope.tenantId)) {\n oldDek = await oldKms.getTenantDek(scope.tenantId)\n oldDekCache.set(scope.tenantId, oldDek)\n }\n for (const rule of fields) {\n const value = payload[rule.field]\n if (typeof value !== 'string' || !isEncryptedPayload(value)) continue\n const decrypted = decryptWithOldKey(value, oldDek)\n if (decrypted === null) continue\n payload[rule.field] = parseDecryptedFieldValue(decrypted)\n }\n }\n const encrypted = await encryptionService.encryptEntityPayload(\n entityId,\n payload,\n scope.tenantId,\n scope.organizationId,\n )\n const updates: Record<string, unknown> = {}\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n const col = resolved?.columnName\n if (!col) continue\n const nextValue = (encrypted as any)[rule.field]\n if (nextValue !== undefined && nextValue !== row[col]) {\n if (!rotate && isEncryptedPayload(row[col])) continue\n updates[col] = formatValueForColumn(resolved?.prop, nextValue)\n }\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n const hashCol = resolvedHash?.columnName\n const hashValue = (encrypted as any)[rule.hashField]\n if (hashCol && hashValue !== undefined && hashValue !== row[hashCol]) {\n updates[hashCol] = formatValueForColumn(resolvedHash?.prop, hashValue)\n }\n }\n }\n if (!Object.keys(updates).length) continue\n if (!dryRun) {\n const setSql = Object.keys(updates).map((col) => `\"${col}\" = ?`).join(', ')\n await conn.execute(\n `update ${qualifiedTable} set ${setSql} where \"${pk}\" = ?`,\n [...Object.values(updates), row[pk]],\n )\n }\n updated += 1\n }\n return updated\n }\n\n let total = 0\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId, console.warn)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const scopes = await resolveScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n for (const scope of scopes) {\n const updated = await processScope(entityId, meta, fields, scope)\n if (updated > 0) {\n console.log(\n `${dryRun ? '[dry-run] ' : ''}Encrypted ${updated} record(s) for ${entityId} org=${scope.organizationId ?? 'null'}`\n )\n }\n total += updated\n }\n }\n\n if (total > 0) {\n console.log(`Encrypted ${total} record(s) across mapped entities.`)\n } else {\n console.log('All mapped entity fields already encrypted for the selected scope.')\n }\n },\n}\n\nconst decryptDatabase: ModuleCli = {\n command: 'decrypt-database',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string) || null\n const organizationIdArg = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n const entityIdArg = (args.entity as string) || null\n const checkMode = Boolean(args.check)\n const dryRun = Boolean(args['dry-run'] || args.dry) || checkMode\n const deactivateMaps = Boolean(args['deactivate-maps'])\n const confirm = (args.confirm as string) || null\n const batchSize = Math.max(1, parseInt(String(args['batch-size'] || args.batchSize || '500'), 10) || 500)\n const sleepMs = Math.max(0, parseInt(String(args['sleep-ms'] || args.sleepMs || '0'), 10) || 0)\n const debug = Boolean(args.debug)\n\n if (!tenantIdArg) {\n console.error('--tenant <uuid> is required.')\n return\n }\n\n if (!checkMode) {\n if (!confirm) {\n console.error('--confirm <tenantUuid> is required (safety gate). Pass the exact tenant UUID to confirm the operation.')\n return\n }\n if (confirm !== tenantIdArg) {\n console.error(`--confirm value \"${confirm}\" does not match --tenant \"${tenantIdArg}\". Aborting.`)\n return\n }\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const conn: any = em?.getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw database connection; aborting.')\n return\n }\n\n if (!checkMode && !isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting. Data may already be decrypted.')\n return\n }\n\n const metaByEntityId = buildEntityMetaRegistry(em)\n\n const resolveDecryptScopes = async (\n tenantId: string,\n organizationId: string | null,\n ): Promise<Array<{ tenantId: string; organizationId: string | null }>> => {\n if (organizationId) return [{ tenantId, organizationId }]\n const rows = await conn.execute(\n `SELECT DISTINCT organization_id FROM encryption_maps WHERE tenant_id = ? AND deleted_at IS NULL`,\n [tenantId],\n )\n const orgIds = new Set<string | null>()\n for (const row of Array.isArray(rows) ? rows : []) {\n orgIds.add(row.organization_id ?? null)\n }\n orgIds.add(null)\n return Array.from(orgIds).map((orgId) => ({ tenantId, organizationId: orgId }))\n }\n\n const mapWhere: any = { tenantId: tenantIdArg, deletedAt: null, isActive: true }\n if (organizationIdArg) mapWhere.organizationId = organizationIdArg\n if (entityIdArg) mapWhere.entityId = entityIdArg\n const maps = await em.find(EncryptionMap, mapWhere)\n\n if (!maps.length) {\n console.log('No active encryption maps found for the selected scope.')\n return\n }\n\n const kms = createKmsService()\n const dekCache = new Map<string, TenantDek | null>()\n const getDek = async (tenantId: string): Promise<TenantDek | null> => {\n if (dekCache.has(tenantId)) return dekCache.get(tenantId) ?? null\n const dek = await kms.getTenantDek(tenantId)\n dekCache.set(tenantId, dek)\n return dek\n }\n\n if (checkMode) {\n const envValue = process.env.TENANT_DATA_ENCRYPTION ?? '(not set)'\n console.log(`TENANT_DATA_ENCRYPTION = ${envValue}`)\n console.log(`Active EncryptionMap records for scope: ${maps.length}`)\n let encryptedCandidatesSampled = 0\n let malformedPayloadCountSampled = 0\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const dek = await getDek(tenantId).catch(() => null)\n if (!dek) continue\n const scopes = await resolveDecryptScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const tableName = meta?.tableName\n if (!tableName) continue\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const fieldCols = fields.flatMap((f: any) => {\n const r = resolveProperty(meta, f.field)\n return r.columnName ? [r.columnName] : []\n })\n const colList = Array.from(new Set([pk, ...fieldCols]))\n for (const scope of scopes) {\n const sampleRows = await conn.execute(\n `SELECT ${colList.map((c: string) => `\"${c}\"`).join(', ')} FROM ${qualifiedTable} WHERE tenant_id = ? AND organization_id IS NOT DISTINCT FROM ? LIMIT 100`,\n [scope.tenantId, scope.organizationId],\n ).catch(() => [])\n for (const row of Array.isArray(sampleRows) ? sampleRows : []) {\n let rowHasEncrypted = false\n for (const fieldRule of fields) {\n const resolved = resolveProperty(meta, fieldRule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rawValue === null || rawValue === undefined) continue\n try {\n decryptWithAesGcmStrict(String(rawValue), dek.key)\n rowHasEncrypted = true\n } catch (e: any) {\n if (e instanceof TenantDataEncryptionError && e.code === TenantDataEncryptionErrorCode.MALFORMED_PAYLOAD) {\n malformedPayloadCountSampled++\n }\n }\n }\n if (rowHasEncrypted) encryptedCandidatesSampled++\n }\n }\n }\n console.log(`estimated encrypted candidates (sampled): ${encryptedCandidatesSampled}`)\n if (malformedPayloadCountSampled > 0) {\n console.warn(`\u26A0 malformed payloads (sampled): ${malformedPayloadCountSampled} \u2014 may indicate corruption`)\n } else {\n console.log(`malformed payloads (sampled): ${malformedPayloadCountSampled}`)\n }\n console.log('not a proof of absence \u2014 run full command + rerun --check to confirm')\n return\n }\n\n let totalRowsFetched = 0\n let totalRowsUpdated = 0\n let totalHashFieldsCleared = 0\n const totalHashFieldsSkipped = new Set<string>()\n let totalMalformedPayloadCount = 0\n const malformedByLocation = new Map<string, number>()\n let totalEntitiesProcessed = 0\n\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId, console.warn)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const dek = await getDek(tenantId)\n if (!dek) {\n console.warn(`No DEK available for tenant ${tenantId}; skipping ${entityId}.`)\n continue\n }\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const tableName = meta?.tableName\n if (!tableName) {\n console.warn(`Skipping ${entityId}: table name not found.`)\n continue\n }\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const fieldCols = fields.flatMap((f: any) => {\n const r = resolveProperty(meta, f.field)\n return r.columnName ? [r.columnName] : []\n })\n const colList = Array.from(new Set([pk, ...fieldCols]))\n totalEntitiesProcessed++\n\n const scopes = await resolveDecryptScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n\n for (const scope of scopes) {\n let lastId: string | null = null\n let scopeMalformedCount = 0\n\n while (true) {\n let selectSql = `SELECT ${colList.map((c: string) => `\"${c}\"`).join(', ')} FROM ${qualifiedTable} WHERE tenant_id = ? AND organization_id IS NOT DISTINCT FROM ?`\n const selectParams: unknown[] = [scope.tenantId, scope.organizationId]\n if (lastId !== null) {\n selectSql += ` AND \"${pk}\" > ?`\n selectParams.push(lastId)\n }\n selectSql += ` ORDER BY \"${pk}\" LIMIT ?`\n selectParams.push(batchSize)\n\n const batchRows = await conn.execute(selectSql, selectParams)\n const batch = Array.isArray(batchRows) ? batchRows : []\n if (!batch.length) break\n\n lastId = String(batch[batch.length - 1]![pk])\n totalRowsFetched += batch.length\n\n const batchStart = Date.now()\n await conn.execute('BEGIN')\n let batchCommitted = false\n try {\n for (const row of batch) {\n const updates: Record<string, unknown> = {}\n let rowDecrypted = false\n\n for (const fieldRule of fields) {\n const resolved = resolveProperty(meta, fieldRule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rawValue === null || rawValue === undefined) continue\n try {\n const decrypted = decryptWithAesGcmStrict(String(rawValue), dek.key)\n let valueToWrite: string\n try {\n const parsed = JSON.parse(decrypted)\n valueToWrite = typeof parsed === 'string' ? parsed : decrypted\n } catch {\n valueToWrite = decrypted\n }\n updates[col] = valueToWrite\n rowDecrypted = true\n } catch (e: any) {\n if (e instanceof TenantDataEncryptionError) {\n if (e.code === TenantDataEncryptionErrorCode.AUTH_FAILED) {\n // Value is plaintext \u2014 skip silently\n } else if (e.code === TenantDataEncryptionErrorCode.MALFORMED_PAYLOAD) {\n scopeMalformedCount++\n const locationKey = `${tableName}:${col}`\n malformedByLocation.set(locationKey, (malformedByLocation.get(locationKey) ?? 0) + 1)\n console.warn(`\u26A0 MALFORMED_PAYLOAD for ${entityId} field \"${col}\" row ${row[pk]}; skipping field.`)\n } else {\n throw e\n }\n } else {\n throw e\n }\n }\n }\n\n if (rowDecrypted) {\n for (const fieldRule of fields) {\n if (!fieldRule.hashField) continue\n const resolvedHash = resolveProperty(meta, fieldRule.hashField)\n const hashCol = resolvedHash?.columnName\n if (!hashCol) {\n const skippedKey = `${tableName}:${fieldRule.hashField}`\n if (!totalHashFieldsSkipped.has(skippedKey)) {\n console.warn(`\u26A0 Hash column \"${fieldRule.hashField}\" not found in metadata for ${entityId}; skipping.`)\n totalHashFieldsSkipped.add(skippedKey)\n }\n continue\n }\n updates[hashCol] = null\n totalHashFieldsCleared++\n }\n }\n\n if (Object.keys(updates).length > 0) {\n if (!dryRun) {\n const setSql = Object.keys(updates).map((col) => `\"${col}\" = ?`).join(', ')\n await conn.execute(\n `UPDATE ${qualifiedTable} SET ${setSql} WHERE \"${pk}\" = ?`,\n [...Object.values(updates), row[pk]],\n )\n }\n totalRowsUpdated++\n }\n }\n await conn.execute('COMMIT')\n batchCommitted = true\n } catch (fatalErr: any) {\n if (!batchCommitted) {\n try { await conn.execute('ROLLBACK') } catch {}\n }\n console.error(`Fatal error during batch processing for ${entityId}: ${(fatalErr as Error)?.message || String(fatalErr)}`)\n throw fatalErr\n }\n\n const batchDurationMs = Date.now() - batchStart\n if (debug) {\n console.log(\n `[debug] Batch ${entityId} org=${scope.organizationId ?? 'null'}: ${batch.length} rows in ${batchDurationMs}ms, scopeMalformed=${scopeMalformedCount}`,\n )\n if (batchDurationMs > 30_000) {\n console.warn('\u26A0 Batch took >30s; consider reducing --batch-size.')\n }\n }\n if (sleepMs > 0) {\n await new Promise<void>((r) => setTimeout(r, sleepMs))\n }\n }\n\n totalMalformedPayloadCount += scopeMalformedCount\n }\n }\n\n if (deactivateMaps && !dryRun) {\n let deactivateSql = `UPDATE encryption_maps SET is_active = false, deleted_at = now() WHERE tenant_id = ? AND deleted_at IS NULL`\n const deactivateParams: unknown[] = [tenantIdArg]\n if (organizationIdArg) {\n deactivateSql += ` AND (organization_id = ? OR organization_id IS NULL)`\n deactivateParams.push(organizationIdArg)\n }\n if (entityIdArg) {\n deactivateSql += ` AND entity_id = ?`\n deactivateParams.push(entityIdArg)\n }\n await conn.execute(deactivateSql, deactivateParams)\n console.warn('\u26A0 Restart all application replicas \u2014 in-process map caches may still be active.')\n if (isTenantDataEncryptionEnabled()) {\n console.warn('\u26A0 Env TENANT_DATA_ENCRYPTION is still true \u2014 new writes will be re-encrypted until env is updated and replicas restarted.')\n }\n }\n\n if (deactivateMaps && dryRun) {\n console.log(`[dry-run] Would deactivate ${maps.length} EncryptionMap record(s).`)\n }\n\n const prefix = dryRun ? '[dry-run] ' : ''\n console.log(`\\n${prefix}Decryption summary:`)\n console.log(` Rows fetched: ${totalRowsFetched}`)\n console.log(` Rows updated: ${totalRowsUpdated}`)\n console.log(` Entities processed: ${totalEntitiesProcessed}`)\n console.log(` Hash fields cleared: ${totalHashFieldsCleared}`)\n if (totalHashFieldsSkipped.size > 0) {\n console.log(` Hash fields skipped (missing columns): ${Array.from(totalHashFieldsSkipped).join(', ')}`)\n }\n if (totalMalformedPayloadCount > 0) {\n console.warn(\n ` \u26A0 ${totalMalformedPayloadCount} field value(s) returned MALFORMED_PAYLOAD and were skipped; these may be corrupted ciphertexts. Investigate before assuming decryption is complete.`,\n )\n if (debug && malformedByLocation.size > 0) {\n const top = Array.from(malformedByLocation.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n console.log(' Top malformed locations:')\n for (const [loc, count] of top) {\n console.log(` ${loc}: ${count}`)\n }\n }\n }\n\n if (!dryRun) {\n console.log(`\\n\u2705 Decryption complete. Required next steps:`)\n console.log(` 1. Set TENANT_DATA_ENCRYPTION=false in your environment / secrets`)\n console.log(` 2. Restart all application replicas`)\n console.log(` 3. Run: mercato query_index reindex --tenant ${tenantIdArg} \u2190 run after env flip + restart; search/filter degraded until this completes`)\n console.log(` 4. Run: mercato entities decrypt-database --tenant ${tenantIdArg} --check to confirm no encrypted values remain`)\n console.log(` NOTE: if the run was long and concurrent inserts occurred, run again before step 4 \u2014 it is idempotent.`)\n }\n },\n}\n\n// Keep default export stable (install first for help listing)\nexport default [seedDefs, reinstallDefs, addField, seedEncryptionMaps, rotateEncryptionKey, decryptDatabase]\n"],
5
+ "mappings": "AAAA,SAAS,eAAe,gCAA6D;AACrF,SAAS,8BAA8B;AAEvC,SAAS,cAAc,gBAAgB,qBAAqB;AAC5D;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,OAAO,cAAc;AACrB,SAAS,SAAS,OAAO,UAAU,cAAc;AACjD,SAAS,qCAAqC;AAC9C,SAAS,yBAAyB;AAClC,SAAS,+BAA+B;AACxC,SAAS,wBAAyD;AAClE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,mCAAmC;AAC5C,SAAS,oBAAoB;AAC7B,OAAO,YAAY;AAEnB,SAAS,UAAU,MAAgB;AACjC,QAAM,OAAyC,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,CAAC,EAAG;AACR,QAAI,EAAE,WAAW,IAAI,GAAG;AACtB,YAAM,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,OAAO,EAAE,EAAE,MAAM,GAAG;AAC7C,UAAI,MAAM,OAAW,MAAK,CAAC,IAAI;AAAA,eACtB,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,EAAG,WAAW,IAAI,GAAG;AAAE,aAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AAAI;AAAA,MAAI,MACjF,MAAK,CAAC,IAAI;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;AAEA,MAAM,WAAsB;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,cAAe,KAAK,UAAsB,KAAK;AACrD,UAAM,aAAa,QAAQ,KAAK,MAAM;AACtC,UAAM,MAAM,QAAQ,KAAK,SAAS,KAAK,KAAK,GAAG;AAC/C,UAAM,QAAQ,QAAQ,KAAK,KAAK;AAChC,UAAM,gBAAgB,KAAK,WAAW,IAAI,QAAQ;AAElD,QAAI,cAAc,kBAAkB,OAAO;AACzC,cAAQ,MAAM,2CAA2C;AACzD;AAAA,IACF;AAEA,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AACvB,QAAI,QAA8B;AAClC,QAAI;AAAE,cAAQ,QAAQ,OAAO;AAAA,IAAmB,QAAQ;AAAA,IAAC;AAEzD,UAAM,YAAY,cACd,CAAC,WAAW,IACX,aAAa,CAAC,IAAI;AAEvB,UAAM,SAAS,CAAC,YAAoB;AAClC,YAAM,SAAS,MAAM,eAAe;AACpC,cAAQ,IAAI,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,IACnC;AAEA,UAAM,SAAS,MAAM,iCAAiC,IAAI,OAAO;AAAA,MAC/D;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,QAAQ,MAAM,YAAY;AAChC,YAAQ,IAAI,UAAK,KAAK,wBAAwB,OAAO,SAAS,aAAa,OAAO,YAAY,mBAAmB,OAAO,YAAY,aAAa,OAAO,OAAO,EAAE;AAAA,EACnK;AACF;AAGA,MAAM,gBAA2B;AAAA,EAC/B,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,cAAe,KAAK,UAAsB,KAAK;AACrD,UAAM,aAAa,QAAQ,KAAK,MAAM;AACtC,UAAM,MAAM,QAAQ,KAAK,SAAS,KAAK,KAAK,GAAG;AAC/C,UAAM,gBAAgB,aAAa,OAAQ,KAAK,WAAW,IAAI,QAAQ;AAEvE,QAAI,cAAc,kBAAkB,OAAO;AACzC,cAAQ,MAAM,2CAA2C;AACzD;AAAA,IACF;AAEA,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AACvB,QAAI,QAA8B;AAClC,QAAI;AAAE,cAAQ,QAAQ,OAAO;AAAA,IAAmB,QAAQ;AAAA,IAAC;AAEzD,UAAM,YAAY,cACd,CAAC,WAAW,IACX,aAAa,CAAC,IAAI;AAEvB,UAAM,aAAa,iCAAiC;AACpD,UAAM,WAAW,WAAW,OAAO,CAAC,UAAW,aAAa,MAAM,MAAM,WAAW,OAAO,IAAK;AAC/F,QAAI,CAAC,SAAS,QAAQ;AACpB,cAAQ,IAAI,gEAAgE;AAC5E;AAAA,IACF;AACA,UAAM,YAAY,MAAM,KAAK,IAAI,IAAI,SAAS,IAAI,CAAC,UAAU,MAAM,QAAQ,CAAC,CAAC;AAC7E,QAAI,CAAC,UAAU,QAAQ;AACrB,cAAQ,IAAI,iDAAiD;AAC7D;AAAA,IACF;AAEA,UAAM,SAAS,CAAC,YAAoB;AAClC,YAAM,SAAS,MAAM,eAAe;AACpC,cAAQ,IAAI,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,IACnC;AAEA,QAAI,KAAK;AACP,cAAQ,IAAI,4EAA4E;AAAA,IAC1F,OAAO;AACL,YAAM,aAAkB,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE;AACvD,UAAI,cAAc,QAAW;AAC3B,YAAI,UAAU,WAAW,EAAG,YAAW,WAAW;AAAA,YAC7C,YAAW,WAAW,EAAE,KAAK,UAAU;AAAA,MAC9C;AACA,YAAM,gBAAgB,MAAM,GAAG,aAAa,gBAAgB,UAAU;AAEtE,YAAM,cAAmB,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE;AACxD,UAAI,cAAc,QAAW;AAC3B,YAAI,UAAU,WAAW,EAAG,aAAY,WAAW;AAAA,YAC9C,aAAY,WAAW,EAAE,KAAK,UAAU;AAAA,MAC/C;AACA,YAAM,kBAAkB,MAAM,GAAG,aAAa,cAAc,WAAW;AAEvE,UAAI,SAAS,UAAU,QAAQ;AAC7B,YAAI;AACF,gBAAM,MAAM,aAAa,UAAU,IAAI,CAAC,OAAO,iBAAiB,EAAE,EAAE,CAAC;AAAA,QACvE,QAAQ;AAAA,QAAC;AAAA,MACX;AACA,cAAQ,IAAI,+BAA+B,aAAa,cAAc,eAAe,EAAE;AAAA,IACzF;AAEA,UAAM,SAAS,MAAM,iCAAiC,IAAI,OAAO;AAAA,MAC/D;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,OAAO;AAAA,MACP;AAAA,IACF,CAAC;AACD,UAAM,QAAQ,MAAM,YAAY;AAChC,YAAQ,IAAI,UAAK,KAAK,wBAAwB,OAAO,SAAS,aAAa,OAAO,YAAY,mBAAmB,OAAO,YAAY,aAAa,OAAO,OAAO,EAAE;AAAA,EACnK;AACF;AAGA,MAAM,WAAsB;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,KAAK,SAAS,gBAAgB,EAAE,OAAO,OAAO,CAAC;AACrD,UAAM,MAAM,OAAO,GAAW,MAAe;AAC3C,YAAM,KAAK,MAAM,GAAG,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,IAAI,GAAG,KAAK;AACnE,aAAO,MAAM,KAAK;AAAA,IACpB;AACA,UAAM,UAAU,OAAO,GAAW,IAAI,UAAU;AAC9C,YAAM,KAAK,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,YAAY;AACpD,aAAO,kBAAkB,CAAC,MAAM;AAAA,IAClC;AAEA,QAAI;AACF,YAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,YAAM,KAAK,QAAQ,IAAI;AAEvB,YAAM,WAAY,KAAK,UAAsB,KAAK,KAAgB,MAAM,IAAI,gCAAgC;AAC5G,YAAM,WAAW,KAAK,SAAS,OAAO,MAAM,QAAQ,6BAA6B,KAAK;AACtF,YAAM,QAAQ,WAAW,OAAS,KAAK,OAAmB,KAAK,kBAA6B,MAAM,IAAI,iBAAiB;AACvH,YAAM,WAAW,WAAW,OAAS,KAAK,UAAsB,KAAK,YAAuB,MAAM,IAAI,WAAW;AACjH,YAAM,MAAO,KAAK,OAAkB,MAAM,IAAI,wBAAwB;AACtE,UAAI,OAAQ,KAAK,QAAmB,MAAM,IAAI,mFAAmF,MAAM;AACvI,aAAO,KAAK,YAAY;AACxB,UAAI,CAAC,CAAC,QAAO,aAAY,WAAU,SAAQ,WAAU,UAAS,YAAW,YAAW,YAAY,EAAE,SAAS,IAAI,EAAG,OAAM,IAAI,MAAM,cAAc;AAChJ,YAAM,QAAS,KAAK,SAAqB,MAAM,IAAI,SAAS,GAAG;AAC/D,YAAM,cAAe,KAAK,eAA0B;AACpD,YAAM,WAAW,KAAK,aAAa,SAAY,QAAQ,KAAK,QAAQ,IAAI,MAAM,QAAQ,aAAa,KAAK;AACxG,YAAM,QAAQ,KAAK,UAAU,SAAY,QAAQ,KAAK,KAAK,IAAI,MAAM,QAAQ,mBAAmB,KAAK;AACrG,UAAI;AACJ,UAAI,SAAS,UAAU;AACrB,cAAM,MAAO,KAAK,WAAsB,MAAM,IAAI,6BAA6B,iBAAiB;AAChG,kBAAU,wBAAwB,GAAG;AAAA,MACvC;AACA,UAAI,eAAoB;AACxB,YAAM,SAAU,KAAK,WAAuB,KAAK;AACjD,YAAM,cAAc,WAAW,SAAY,SAAS,MAAM,IAAI,wCAAwC,EAAE;AACxG,UAAI,gBAAgB,IAAI;AACtB,gBAAQ,MAAM;AAAA,UACZ,KAAK;AAAW,2BAAe,OAAO,WAAW;AAAG;AAAA,UACpD,KAAK;AAAS,2BAAe,OAAO,WAAW;AAAG;AAAA,UAClD,KAAK;AAAW,2BAAe,kBAAkB,OAAO,WAAW,CAAC,MAAM;AAAM;AAAA,UAChF;AAAS,2BAAe,OAAO,WAAW;AAAA,QAC5C;AAAA,MACF;AACA,YAAM,aAAa,KAAK,eAAe,SAAY,QAAQ,KAAK,UAAU,IAAI,MAAM,QAAQ,eAAe,IAAI;AAC/G,YAAM,cAAc,KAAK,gBAAgB,SAAY,QAAQ,KAAK,WAAW,IAAI,MAAM,QAAQ,oBAAoB,IAAI;AACvH,YAAM,eAAe,KAAK,iBAAiB,SAAY,QAAQ,KAAK,YAAY,IAAI,MAAM,QAAQ,sBAAsB,IAAI;AAC5H,YAAM,UAAU,KAAK,YAAY,SAAY,QAAQ,KAAK,OAAO,IAAI,MAAM,QAAQ,YAAY,KAAK;AAEpG,YAAM,QAAQ,EAAE,UAAU,gBAAgB,OAAO,UAAoB,IAAI;AACzE,YAAM,WAAW,MAAM,GAAG,QAAQ,gBAAgB,KAAK;AACvD,YAAM,aAAkB,CAAC;AACzB,UAAI,QAAS,YAAW,UAAU;AAClC,UAAI,iBAAiB,OAAW,YAAW,eAAe;AAC1D,UAAI,aAAa,OAAW,YAAW,WAAW;AAClD,UAAI,UAAU,OAAW,YAAW,QAAQ;AAC5C,UAAI,eAAe,OAAW,YAAW,aAAa;AACtD,UAAI,YAAY,OAAW,YAAW,UAAU;AAChD,UAAI,gBAAgB,OAAW,YAAW,cAAc;AACxD,UAAI,iBAAiB,OAAW,YAAW,eAAe;AAC1D,UAAI,UAAU,OAAW,YAAW,QAAQ;AAC5C,UAAI,gBAAgB,OAAW,YAAW,cAAc;AAExD,UAAI,CAAC,UAAU;AACb,cAAM,GAAG,QAAQ,GAAG,OAAO,gBAAgB;AAAA,UACzC;AAAA,UACA,gBAAgB;AAAA,UAChB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,UAAU;AAAA,QACZ,CAAC,CAAC,EAAE,MAAM;AACV,gBAAQ,IAAI,yBAAyB,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,SAAS,OAAO,cAAc,SAAS,KAAK,YAAY,QAAQ,GAAG,EAAE;AAAA,MACxI,OAAO;AACL,iBAAS,OAAO;AAChB,iBAAS,aAAa;AACtB,iBAAS,WAAW;AACpB,cAAM,GAAG,MAAM;AACf,gBAAQ,IAAI,yBAAyB,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,SAAS,OAAO,cAAc,SAAS,KAAK,YAAY,QAAQ,GAAG,EAAE;AAAA,MACxI;AAAA,IACF,SAAS,GAAQ;AACf,cAAQ,MAAM,WAAW,GAAG,WAAW,CAAC;AAAA,IAC1C,UAAE;AACA,YAAM,GAAG,MAAM;AAAA,IACjB;AAAA,EACF;AACF;AAEA,SAAS,8BAAwC;AAC/C,QAAM,aAAa,cAAc;AACjC,MAAI,WAAW,SAAS,EAAG,QAAO;AAClC,MAAI;AACF,UAAM,EAAE,WAAW,IAAI,QAAQ,2CAA2C;AAC1E,WAAO,WAAW;AAAA,EACpB,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,qBAAqB,IAAS,UAAkB,gBAA+B,QAA+B;AAC3H,aAAW,QAAQ,yBAAyB,4BAA4B,CAAC,GAAG;AAC1E,UAAM,WAAW,MAAM,GAAG,QAAQ,eAAe;AAAA,MAC/C,UAAU,KAAK;AAAA,MACf;AAAA,MACA;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AACD,QAAI,UAAU;AACZ,eAAS,aAAa,KAAK;AAC3B,eAAS,WAAW;AACpB,eAAS,YAAY,oBAAI,KAAK;AAC9B,aAAO,wCAAiC,KAAK,QAAQ,SAAI;AACzD,YAAM,GAAG,QAAQ,QAAQ,EAAE,MAAM;AACjC;AAAA,IACF;AACA,UAAM,MAAM,GAAG,OAAO,eAAe;AAAA,MACnC,UAAU,KAAK;AAAA,MACf;AAAA,MACA;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,UAAU;AAAA,IACZ,CAAC;AACD,UAAM,GAAG,QAAQ,GAAG,EAAE,MAAM;AAC5B,WAAO,8BAA8B,KAAK,QAAQ,EAAE;AAAA,EACtD;AACF;AAEA,MAAM,qBAAgC;AAAA,EACpC,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,WAAY,KAAK,UAAsB,KAAK;AAClD,UAAM,iBAAkB,KAAK,OAAmB,KAAK,gBAA4B,KAAK,kBAA6B;AAEnH,QAAI,CAAC,UAAU;AACb,cAAQ,MAAM,6CAA6C;AAC3D;AAAA,IACF;AACA,QAAI,CAAC,8BAA8B,GAAG;AACpC,cAAQ,KAAK,sEAAsE;AACnF;AAAA,IACF;AAEA,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AACvB,UAAM,SAAS,CAAC,QAAgB,QAAQ,IAAI,GAAG;AAC/C,UAAM,qBAAqB,IAAI,UAAU,gBAAgB,MAAM;AAC/D,YAAQ,IAAI,+BAA0B;AAAA,EACxC;AACF;AAEA,SAAS,kBAAkB,OAAuB;AAChD,SAAO,MAAM,KAAK,EAAE,QAAQ,oBAAoB,EAAE;AACpD;AAEA,MAAM,qBAA2C;AAAA,EAE/C,YAAY,QAAgB;AAC1B,SAAK,OAAO,OAAO,WAAW,QAAQ,EAAE,OAAO,kBAAkB,MAAM,CAAC,EAAE,OAAO;AAAA,EACnF;AAAA,EAEA,YAAqB;AACnB,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,UAA0B;AAC1C,UAAM,aAAa;AACnB,UAAM,YAAY;AAClB,UAAM,UAAU,OAAO,WAAW,KAAK,MAAM,UAAU,YAAY,WAAW,QAAQ;AACtF,WAAO,QAAQ,SAAS,QAAQ;AAAA,EAClC;AAAA,EAEA,MAAM,aAAa,UAA6C;AAC9D,QAAI,CAAC,SAAU,QAAO;AACtB,WAAO,EAAE,UAAU,KAAK,KAAK,UAAU,QAAQ,GAAG,WAAW,KAAK,IAAI,EAAE;AAAA,EAC1E;AAAA,EAEA,MAAM,gBAAgB,UAA6C;AACjE,WAAO,KAAK,aAAa,QAAQ;AAAA,EACnC;AACF;AAEA,SAAS,eAAe,KAAsC;AAC5D,MAAI,CAAC,KAAK,IAAK,QAAO;AACtB,SAAO,OAAO,WAAW,QAAQ,EAAE,OAAO,IAAI,GAAG,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC9E;AAEA,SAAS,kBACP,SACA,KACe;AACf,MAAI,CAAC,KAAK,IAAK,QAAO;AACtB,SAAO,kBAAkB,SAAS,IAAI,GAAG;AAC3C;AAEA,SAAS,gBAAgB,MAAW,OAAgE;AAClG,MAAI,CAAC,MAAM,WAAY,QAAO,EAAE,YAAY,MAAM,MAAM,KAAK;AAC7D,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,MAAM,QAAQ,aAAa,CAAC,GAAG,MAAM,EAAE,YAAY,CAAC;AAAA,IACpD,MAAM,QAAQ,YAAY,KAAK,EAAE,YAAY;AAAA,EAC/C;AACA,aAAW,aAAa,YAAY;AAClC,UAAM,OAAO,KAAK,WAAW,SAAS;AACtC,UAAM,YACJ,MAAM,cACL,MAAM,QAAQ,MAAM,UAAU,KAAK,KAAK,WAAW,SAAS,KAAK,WAAW,CAAC,IAAI;AACpF,QAAI,OAAO,cAAc,YAAY,UAAU,OAAQ,QAAO,EAAE,YAAY,WAAW,KAAK;AAC5F,QAAI,MAAM,KAAM,QAAO,EAAE,YAAY,KAAK,MAAM,KAAK;AAAA,EACvD;AACA,SAAO,EAAE,YAAY,MAAM,MAAM,KAAK;AACxC;AAEA,SAAS,wBAAwB,IAA2B;AAC1D,QAAM,WAAW,IAAI,cAAc;AACnC,QAAM,aAAa,OAAO,UAAU,WAAW,aAAa,SAAS,OAAO,IAAI,CAAC;AACjF,QAAM,UAAU,MAAM,QAAQ,UAAU,IAAI,aAAa,OAAO,OAAO,cAAc,CAAC,CAAC;AACvF,QAAM,iBAAiB,oBAAI,IAAiB;AAC5C,aAAW,QAAQ,SAAS;AAC1B,UAAM,WAAW,4BAA4B,IAAI;AACjD,QAAI,SAAU,gBAAe,IAAI,UAAU,IAAI;AAAA,EACjD;AACA,SAAO;AACT;AASA,SAAS,eACP,KACA,gBACA,OAA8B,MAAM;AAAC,GACX;AAC1B,QAAM,WAAW,OAAO,IAAI,QAAQ;AACpC,QAAM,OAAO,eAAe,IAAI,QAAQ;AACxC,MAAI,CAAC,MAAM;AACT,SAAK,YAAY,QAAQ,uBAAuB;AAChD,WAAO;AAAA,EACT;AACA,QAAM,SAAS,MAAM,QAAQ,IAAI,UAAU,IAAI,IAAI,aAAa,CAAC;AACjE,MAAI,CAAC,OAAO,OAAQ,QAAO;AAC3B,QAAM,WAAW,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AACvD,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,EAAE,UAAU,MAAM,QAAQ,SAAS;AAC5C;AAEA,MAAM,sBAAiC;AAAA,EACrC,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,cAAe,KAAK,UAAsB,KAAK,YAAuB;AAC5E,UAAM,oBAAqB,KAAK,OAAmB,KAAK,gBAA4B,KAAK,kBAA6B;AACtH,UAAM,SAAU,KAAK,SAAS,KAAiB,KAAK,UAAqB;AACzE,UAAM,SAAS,QAAQ,KAAK,SAAS,KAAK,KAAK,GAAG;AAClD,UAAM,QAAQ,QAAQ,KAAK,KAAK;AAChC,UAAM,SAAS,QAAQ,MAAM;AAC7B,QAAI,UAAU,CAAC,aAAa;AAC1B,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,8BAA8B,GAAG;AACpC,cAAQ,MAAM,+CAA+C;AAC7D;AAAA,IACF;AAEA,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AACvB,UAAM,OAAY,IAAI,gBAAgB;AACtC,QAAI,CAAC,QAAQ,OAAO,KAAK,YAAY,YAAY;AAC/C,cAAQ,MAAM,4CAA4C;AAC1D;AAAA,IACF;AAEA,UAAM,oBAAoB,IAAI,4BAA4B,IAAW,EAAE,KAAK,iBAAiB,EAAE,CAAC;AAChG,UAAM,SAAS,UAAU,SAAS,IAAI,qBAAqB,MAAM,IAAI;AACrE,QAAI,CAAC,kBAAkB,UAAU,GAAG;AAClC,cAAQ,MAAM,wEAAwE;AACtF;AAAA,IACF;AAEA,QAAI,OAAO;AACT,cAAQ,IAAI,2BAA2B;AAAA,QACrC,WAAW,QAAQ,MAAM;AAAA,QACzB;AAAA,QACA,UAAU,eAAe;AAAA,QACzB,gBAAgB,qBAAqB;AAAA,MACvC,CAAC;AACD,UAAI,aAAa;AACf,cAAM,CAAC,QAAQ,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,UACzC,QAAQ,aAAa,WAAW,KAAK,QAAQ,QAAQ,IAAI;AAAA,UACzD,kBAAkB,OAAO,WAAW;AAAA,QACtC,CAAC;AACD,gBAAQ,IAAI,4CAA4C;AAAA,UACtD,QAAQ,eAAe,MAAM;AAAA,UAC7B,YAAY,eAAe,MAAM;AAAA,QACnC,CAAC;AAAA,MACH,OAAO;AACL,gBAAQ,IAAI,gEAAgE;AAAA,MAC9E;AAAA,IACF;AAEA,UAAM,qBAAqB,CAAC,UAA4B;AACtD,UAAI,OAAO,UAAU,SAAU,QAAO;AACtC,YAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,aAAO,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM;AAAA,IAC5C;AAEA,UAAM,iBAAiB,wBAAwB,EAAE;AAEjD,UAAM,QAAa,EAAE,WAAW,KAAK;AACrC,QAAI,YAAa,OAAM,WAAW;AAClC,QAAI,kBAAmB,OAAM,iBAAiB;AAC9C,UAAM,OAAO,MAAM,GAAG,KAAK,eAAe,KAAK;AAC/C,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ,IAAI,kDAAkD;AAC9D;AAAA,IACF;AAEA,UAAM,uBAAuB,CAAC,MAAW,UAA4B;AACnE,UAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,YAAM,QAAQ,MAAM,QAAQ,MAAM,WAAW,IAAI,KAAK,cAAc,CAAC;AACrE,YAAM,OAAO,OAAO,MAAM,QAAQ,EAAE,EAAE,YAAY;AAClD,YAAM,SAAS,MAAM,KAAK,CAAC,UAAkB,MAAM,YAAY,EAAE,SAAS,MAAM,CAAC,KAAK,SAAS,UAAU,SAAS;AAClH,UAAI,CAAC,OAAQ,QAAO;AACpB,aAAO,KAAK,UAAU,KAAK;AAAA,IAC7B;AAEA,UAAM,gBAAgB,OAAO,UAAkB,mBAAkC;AAC/E,UAAI,eAAgB,QAAO,CAAC,EAAE,UAAU,eAAe,CAAC;AACxD,YAAM,OAAO,MAAM,GAAG,KAAK,cAAc,EAAE,QAAQ,SAAS,CAAC;AAC7D,YAAM,SAAS,KAAK,IAAI,CAAC,SAAuB;AAAA,QAC9C;AAAA,QACA,gBAAgB,OAAO,IAAI,EAAE;AAAA,MAC/B,EAAE;AACF,aAAO,KAAK,EAAE,UAAU,gBAAgB,KAAK,CAAC;AAC9C,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,oBAAI,IAA8B;AACtD,UAAM,eAAe,OACnB,UACA,MACA,QACA,UACoB;AACpB,YAAM,KAAK,MAAM,QAAQ,MAAM,WAAW,KAAK,KAAK,YAAY,SAAS,KAAK,YAAY,CAAC,IAAI;AAC/F,YAAM,UAAU,oBAAI,IAAY;AAChC,cAAQ,IAAI,EAAE;AACd,iBAAW,QAAQ,QAAQ;AACzB,cAAM,WAAW,gBAAgB,MAAM,KAAK,KAAK;AACjD,YAAI,UAAU,WAAY,SAAQ,IAAI,SAAS,UAAU;AACzD,YAAI,KAAK,WAAW;AAClB,gBAAM,eAAe,gBAAgB,MAAM,KAAK,SAAS;AACzD,cAAI,cAAc,WAAY,SAAQ,IAAI,aAAa,UAAU;AAAA,QACnE;AAAA,MACF;AACA,YAAM,aAAa,MAAM,KAAK,OAAO;AACrC,UAAI,CAAC,WAAW,OAAQ,QAAO;AAC/B,YAAM,YAAY,MAAM;AACxB,UAAI,CAAC,UAAW,QAAO;AACvB,YAAM,SAAS,MAAM;AACrB,YAAM,iBAAiB,SAAS,IAAI,MAAM,MAAM,SAAS,MAAM,IAAI,SAAS;AAC5E,YAAM,YAAY,UAAU,WAAW,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,SAAS,cAAc;AAC7F,YAAM,OAAO,MAAM,KAAK,QAAQ,WAAW,CAAC,MAAM,UAAU,MAAM,cAAc,CAAC;AACjF,YAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;AAC3C,UAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,UAAI,UAAU;AACd,iBAAW,OAAO,MAAM;AACtB,cAAM,UAAmC,CAAC;AAC1C,mBAAW,QAAQ,QAAQ;AACzB,gBAAM,WAAW,gBAAgB,MAAM,KAAK,KAAK;AACjD,gBAAM,MAAM,UAAU;AACtB,cAAI,CAAC,IAAK;AACV,gBAAM,WAAW,IAAI,GAAG;AACxB,cAAI,UAAU,CAAC,mBAAmB,QAAQ,GAAG;AAC3C;AAAA,UACF;AACA,kBAAQ,KAAK,KAAK,IAAI;AACtB,cAAI,KAAK,WAAW;AAClB,kBAAM,eAAe,gBAAgB,MAAM,KAAK,SAAS;AACzD,kBAAM,UAAU,cAAc;AAC9B,gBAAI,QAAS,SAAQ,KAAK,SAAS,IAAI,IAAI,OAAO;AAAA,UACpD;AAAA,QACF;AACA,YAAI,UAAU,CAAC,OAAO,KAAK,OAAO,EAAE,QAAQ;AAC1C;AAAA,QACF;AACA,YAAI,UAAU,QAAQ;AACpB,cAAI,SAAS,YAAY,IAAI,MAAM,QAAQ,KAAK;AAChD,cAAI,CAAC,YAAY,IAAI,MAAM,QAAQ,GAAG;AACpC,qBAAS,MAAM,OAAO,aAAa,MAAM,QAAQ;AACjD,wBAAY,IAAI,MAAM,UAAU,MAAM;AAAA,UACxC;AACA,qBAAW,QAAQ,QAAQ;AACzB,kBAAM,QAAQ,QAAQ,KAAK,KAAK;AAChC,gBAAI,OAAO,UAAU,YAAY,CAAC,mBAAmB,KAAK,EAAG;AAC7D,kBAAM,YAAY,kBAAkB,OAAO,MAAM;AACjD,gBAAI,cAAc,KAAM;AACxB,oBAAQ,KAAK,KAAK,IAAI,yBAAyB,SAAS;AAAA,UAC1D;AAAA,QACF;AACA,cAAM,YAAY,MAAM,kBAAkB;AAAA,UACxC;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN,MAAM;AAAA,QACR;AACA,cAAM,UAAmC,CAAC;AAC1C,mBAAW,QAAQ,QAAQ;AACzB,gBAAM,WAAW,gBAAgB,MAAM,KAAK,KAAK;AACjD,gBAAM,MAAM,UAAU;AACtB,cAAI,CAAC,IAAK;AACV,gBAAM,YAAa,UAAkB,KAAK,KAAK;AAC/C,cAAI,cAAc,UAAa,cAAc,IAAI,GAAG,GAAG;AACrD,gBAAI,CAAC,UAAU,mBAAmB,IAAI,GAAG,CAAC,EAAG;AAC7C,oBAAQ,GAAG,IAAI,qBAAqB,UAAU,MAAM,SAAS;AAAA,UAC/D;AACA,cAAI,KAAK,WAAW;AAClB,kBAAM,eAAe,gBAAgB,MAAM,KAAK,SAAS;AACzD,kBAAM,UAAU,cAAc;AAC9B,kBAAM,YAAa,UAAkB,KAAK,SAAS;AACnD,gBAAI,WAAW,cAAc,UAAa,cAAc,IAAI,OAAO,GAAG;AACpE,sBAAQ,OAAO,IAAI,qBAAqB,cAAc,MAAM,SAAS;AAAA,YACvE;AAAA,UACF;AAAA,QACF;AACA,YAAI,CAAC,OAAO,KAAK,OAAO,EAAE,OAAQ;AAClC,YAAI,CAAC,QAAQ;AACX,gBAAM,SAAS,OAAO,KAAK,OAAO,EAAE,IAAI,CAAC,QAAQ,IAAI,GAAG,OAAO,EAAE,KAAK,IAAI;AAC1E,gBAAM,KAAK;AAAA,YACT,UAAU,cAAc,QAAQ,MAAM,WAAW,EAAE;AAAA,YACnD,CAAC,GAAG,OAAO,OAAO,OAAO,GAAG,IAAI,EAAE,CAAC;AAAA,UACrC;AAAA,QACF;AACA,mBAAW;AAAA,MACb;AACA,aAAO;AAAA,IACT;AAEA,QAAI,QAAQ;AACZ,eAAW,OAAO,MAAM;AACtB,YAAM,UAAU,eAAe,KAAK,gBAAgB,QAAQ,IAAI;AAChE,UAAI,CAAC,QAAS;AACd,YAAM,EAAE,UAAU,MAAM,QAAQ,SAAS,IAAI;AAC7C,YAAM,SAAS,MAAM,cAAc,UAAU,IAAI,iBAAiB,OAAO,IAAI,cAAc,IAAI,IAAI;AACnG,iBAAW,SAAS,QAAQ;AAC1B,cAAM,UAAU,MAAM,aAAa,UAAU,MAAM,QAAQ,KAAK;AAChE,YAAI,UAAU,GAAG;AACf,kBAAQ;AAAA,YACN,GAAG,SAAS,eAAe,EAAE,aAAa,OAAO,kBAAkB,QAAQ,QAAQ,MAAM,kBAAkB,MAAM;AAAA,UACnH;AAAA,QACF;AACA,iBAAS;AAAA,MACX;AAAA,IACF;AAEA,QAAI,QAAQ,GAAG;AACb,cAAQ,IAAI,aAAa,KAAK,oCAAoC;AAAA,IACpE,OAAO;AACL,cAAQ,IAAI,oEAAoE;AAAA,IAClF;AAAA,EACF;AACF;AAEA,MAAM,kBAA6B;AAAA,EACjC,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,cAAe,KAAK,UAAsB,KAAK,YAAuB;AAC5E,UAAM,oBAAqB,KAAK,OAAmB,KAAK,gBAA4B,KAAK,kBAA6B;AACtH,UAAM,cAAe,KAAK,UAAqB;AAC/C,UAAM,YAAY,QAAQ,KAAK,KAAK;AACpC,UAAM,SAAS,QAAQ,KAAK,SAAS,KAAK,KAAK,GAAG,KAAK;AACvD,UAAM,iBAAiB,QAAQ,KAAK,iBAAiB,CAAC;AACtD,UAAM,UAAW,KAAK,WAAsB;AAC5C,UAAM,YAAY,KAAK,IAAI,GAAG,SAAS,OAAO,KAAK,YAAY,KAAK,KAAK,aAAa,KAAK,GAAG,EAAE,KAAK,GAAG;AACxG,UAAM,UAAU,KAAK,IAAI,GAAG,SAAS,OAAO,KAAK,UAAU,KAAK,KAAK,WAAW,GAAG,GAAG,EAAE,KAAK,CAAC;AAC9F,UAAM,QAAQ,QAAQ,KAAK,KAAK;AAEhC,QAAI,CAAC,aAAa;AAChB,cAAQ,MAAM,8BAA8B;AAC5C;AAAA,IACF;AAEA,QAAI,CAAC,WAAW;AACd,UAAI,CAAC,SAAS;AACZ,gBAAQ,MAAM,wGAAwG;AACtH;AAAA,MACF;AACA,UAAI,YAAY,aAAa;AAC3B,gBAAQ,MAAM,oBAAoB,OAAO,8BAA8B,WAAW,cAAc;AAChG;AAAA,MACF;AAAA,IACF;AAEA,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AACvB,UAAM,OAAY,IAAI,gBAAgB;AACtC,QAAI,CAAC,QAAQ,OAAO,KAAK,YAAY,YAAY;AAC/C,cAAQ,MAAM,qDAAqD;AACnE;AAAA,IACF;AAEA,QAAI,CAAC,aAAa,CAAC,8BAA8B,GAAG;AAClD,cAAQ,MAAM,8EAA8E;AAC5F;AAAA,IACF;AAEA,UAAM,iBAAiB,wBAAwB,EAAE;AAEjD,UAAM,uBAAuB,OAC3B,UACA,mBACwE;AACxE,UAAI,eAAgB,QAAO,CAAC,EAAE,UAAU,eAAe,CAAC;AACxD,YAAM,OAAO,MAAM,KAAK;AAAA,QACtB;AAAA,QACA,CAAC,QAAQ;AAAA,MACX;AACA,YAAM,SAAS,oBAAI,IAAmB;AACtC,iBAAW,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,GAAG;AACjD,eAAO,IAAI,IAAI,mBAAmB,IAAI;AAAA,MACxC;AACA,aAAO,IAAI,IAAI;AACf,aAAO,MAAM,KAAK,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,gBAAgB,MAAM,EAAE;AAAA,IAChF;AAEA,UAAM,WAAgB,EAAE,UAAU,aAAa,WAAW,MAAM,UAAU,KAAK;AAC/E,QAAI,kBAAmB,UAAS,iBAAiB;AACjD,QAAI,YAAa,UAAS,WAAW;AACrC,UAAM,OAAO,MAAM,GAAG,KAAK,eAAe,QAAQ;AAElD,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ,IAAI,yDAAyD;AACrE;AAAA,IACF;AAEA,UAAM,MAAM,iBAAiB;AAC7B,UAAM,WAAW,oBAAI,IAA8B;AACnD,UAAM,SAAS,OAAO,aAAgD;AACpE,UAAI,SAAS,IAAI,QAAQ,EAAG,QAAO,SAAS,IAAI,QAAQ,KAAK;AAC7D,YAAM,MAAM,MAAM,IAAI,aAAa,QAAQ;AAC3C,eAAS,IAAI,UAAU,GAAG;AAC1B,aAAO;AAAA,IACT;AAEA,QAAI,WAAW;AACb,YAAM,WAAW,QAAQ,IAAI,0BAA0B;AACvD,cAAQ,IAAI,4BAA4B,QAAQ,EAAE;AAClD,cAAQ,IAAI,2CAA2C,KAAK,MAAM,EAAE;AACpE,UAAI,6BAA6B;AACjC,UAAI,+BAA+B;AACnC,iBAAW,OAAO,MAAM;AACtB,cAAM,UAAU,eAAe,KAAK,cAAc;AAClD,YAAI,CAAC,QAAS;AACd,cAAM,EAAE,UAAU,MAAM,QAAQ,SAAS,IAAI;AAC7C,cAAM,MAAM,MAAM,OAAO,QAAQ,EAAE,MAAM,MAAM,IAAI;AACnD,YAAI,CAAC,IAAK;AACV,cAAM,SAAS,MAAM,qBAAqB,UAAU,IAAI,iBAAiB,OAAO,IAAI,cAAc,IAAI,IAAI;AAC1G,cAAM,KAAK,MAAM,QAAQ,MAAM,WAAW,KAAK,KAAK,YAAY,SAAS,KAAK,YAAY,CAAC,IAAI;AAC/F,cAAM,YAAY,MAAM;AACxB,YAAI,CAAC,UAAW;AAChB,cAAM,SAAS,MAAM;AACrB,cAAM,iBAAiB,SAAS,IAAI,MAAM,MAAM,SAAS,MAAM,IAAI,SAAS;AAC5E,cAAM,YAAY,OAAO,QAAQ,CAAC,MAAW;AAC3C,gBAAM,IAAI,gBAAgB,MAAM,EAAE,KAAK;AACvC,iBAAO,EAAE,aAAa,CAAC,EAAE,UAAU,IAAI,CAAC;AAAA,QAC1C,CAAC;AACD,cAAM,UAAU,MAAM,KAAK,oBAAI,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;AACtD,mBAAW,SAAS,QAAQ;AAC1B,gBAAM,aAAa,MAAM,KAAK;AAAA,YAC5B,UAAU,QAAQ,IAAI,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,SAAS,cAAc;AAAA,YAChF,CAAC,MAAM,UAAU,MAAM,cAAc;AAAA,UACvC,EAAE,MAAM,MAAM,CAAC,CAAC;AAChB,qBAAW,OAAO,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC,GAAG;AAC7D,gBAAI,kBAAkB;AACtB,uBAAW,aAAa,QAAQ;AAC9B,oBAAM,WAAW,gBAAgB,MAAM,UAAU,KAAK;AACtD,oBAAM,MAAM,UAAU;AACtB,kBAAI,CAAC,IAAK;AACV,oBAAM,WAAW,IAAI,GAAG;AACxB,kBAAI,aAAa,QAAQ,aAAa,OAAW;AACjD,kBAAI;AACF,wCAAwB,OAAO,QAAQ,GAAG,IAAI,GAAG;AACjD,kCAAkB;AAAA,cACpB,SAAS,GAAQ;AACf,oBAAI,aAAa,6BAA6B,EAAE,SAAS,8BAA8B,mBAAmB;AACxG;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AACA,gBAAI,gBAAiB;AAAA,UACvB;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,6CAA6C,0BAA0B,EAAE;AACrF,UAAI,+BAA+B,GAAG;AACpC,gBAAQ,KAAK,wCAAmC,4BAA4B,iCAA4B;AAAA,MAC1G,OAAO;AACL,gBAAQ,IAAI,iCAAiC,4BAA4B,EAAE;AAAA,MAC7E;AACA,cAAQ,IAAI,2EAAsE;AAClF;AAAA,IACF;AAEA,QAAI,mBAAmB;AACvB,QAAI,mBAAmB;AACvB,QAAI,yBAAyB;AAC7B,UAAM,yBAAyB,oBAAI,IAAY;AAC/C,QAAI,6BAA6B;AACjC,UAAM,sBAAsB,oBAAI,IAAoB;AACpD,QAAI,yBAAyB;AAE7B,eAAW,OAAO,MAAM;AACtB,YAAM,UAAU,eAAe,KAAK,gBAAgB,QAAQ,IAAI;AAChE,UAAI,CAAC,QAAS;AACd,YAAM,EAAE,UAAU,MAAM,QAAQ,SAAS,IAAI;AAC7C,YAAM,MAAM,MAAM,OAAO,QAAQ;AACjC,UAAI,CAAC,KAAK;AACR,gBAAQ,KAAK,+BAA+B,QAAQ,cAAc,QAAQ,GAAG;AAC7E;AAAA,MACF;AACA,YAAM,KAAK,MAAM,QAAQ,MAAM,WAAW,KAAK,KAAK,YAAY,SAAS,KAAK,YAAY,CAAC,IAAI;AAC/F,YAAM,YAAY,MAAM;AACxB,UAAI,CAAC,WAAW;AACd,gBAAQ,KAAK,YAAY,QAAQ,yBAAyB;AAC1D;AAAA,MACF;AACA,YAAM,SAAS,MAAM;AACrB,YAAM,iBAAiB,SAAS,IAAI,MAAM,MAAM,SAAS,MAAM,IAAI,SAAS;AAC5E,YAAM,YAAY,OAAO,QAAQ,CAAC,MAAW;AAC3C,cAAM,IAAI,gBAAgB,MAAM,EAAE,KAAK;AACvC,eAAO,EAAE,aAAa,CAAC,EAAE,UAAU,IAAI,CAAC;AAAA,MAC1C,CAAC;AACD,YAAM,UAAU,MAAM,KAAK,oBAAI,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;AACtD;AAEA,YAAM,SAAS,MAAM,qBAAqB,UAAU,IAAI,iBAAiB,OAAO,IAAI,cAAc,IAAI,IAAI;AAE1G,iBAAW,SAAS,QAAQ;AAC1B,YAAI,SAAwB;AAC5B,YAAI,sBAAsB;AAE1B,eAAO,MAAM;AACX,cAAI,YAAY,UAAU,QAAQ,IAAI,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,SAAS,cAAc;AAChG,gBAAM,eAA0B,CAAC,MAAM,UAAU,MAAM,cAAc;AACrE,cAAI,WAAW,MAAM;AACnB,yBAAa,SAAS,EAAE;AACxB,yBAAa,KAAK,MAAM;AAAA,UAC1B;AACA,uBAAa,cAAc,EAAE;AAC7B,uBAAa,KAAK,SAAS;AAE3B,gBAAM,YAAY,MAAM,KAAK,QAAQ,WAAW,YAAY;AAC5D,gBAAM,QAAQ,MAAM,QAAQ,SAAS,IAAI,YAAY,CAAC;AACtD,cAAI,CAAC,MAAM,OAAQ;AAEnB,mBAAS,OAAO,MAAM,MAAM,SAAS,CAAC,EAAG,EAAE,CAAC;AAC5C,8BAAoB,MAAM;AAE1B,gBAAM,aAAa,KAAK,IAAI;AAC5B,gBAAM,KAAK,QAAQ,OAAO;AAC1B,cAAI,iBAAiB;AACrB,cAAI;AACF,uBAAW,OAAO,OAAO;AACvB,oBAAM,UAAmC,CAAC;AAC1C,kBAAI,eAAe;AAEnB,yBAAW,aAAa,QAAQ;AAC9B,sBAAM,WAAW,gBAAgB,MAAM,UAAU,KAAK;AACtD,sBAAM,MAAM,UAAU;AACtB,oBAAI,CAAC,IAAK;AACV,sBAAM,WAAW,IAAI,GAAG;AACxB,oBAAI,aAAa,QAAQ,aAAa,OAAW;AACjD,oBAAI;AACF,wBAAM,YAAY,wBAAwB,OAAO,QAAQ,GAAG,IAAI,GAAG;AACnE,sBAAI;AACJ,sBAAI;AACF,0BAAM,SAAS,KAAK,MAAM,SAAS;AACnC,mCAAe,OAAO,WAAW,WAAW,SAAS;AAAA,kBACvD,QAAQ;AACN,mCAAe;AAAA,kBACjB;AACA,0BAAQ,GAAG,IAAI;AACf,iCAAe;AAAA,gBACjB,SAAS,GAAQ;AACf,sBAAI,aAAa,2BAA2B;AAC1C,wBAAI,EAAE,SAAS,8BAA8B,aAAa;AAAA,oBAE1D,WAAW,EAAE,SAAS,8BAA8B,mBAAmB;AACrE;AACA,4BAAM,cAAc,GAAG,SAAS,IAAI,GAAG;AACvC,0CAAoB,IAAI,cAAc,oBAAoB,IAAI,WAAW,KAAK,KAAK,CAAC;AACpF,8BAAQ,KAAK,gCAA2B,QAAQ,WAAW,GAAG,SAAS,IAAI,EAAE,CAAC,mBAAmB;AAAA,oBACnG,OAAO;AACL,4BAAM;AAAA,oBACR;AAAA,kBACF,OAAO;AACL,0BAAM;AAAA,kBACR;AAAA,gBACF;AAAA,cACF;AAEA,kBAAI,cAAc;AAChB,2BAAW,aAAa,QAAQ;AAC9B,sBAAI,CAAC,UAAU,UAAW;AAC1B,wBAAM,eAAe,gBAAgB,MAAM,UAAU,SAAS;AAC9D,wBAAM,UAAU,cAAc;AAC9B,sBAAI,CAAC,SAAS;AACZ,0BAAM,aAAa,GAAG,SAAS,IAAI,UAAU,SAAS;AACtD,wBAAI,CAAC,uBAAuB,IAAI,UAAU,GAAG;AAC3C,8BAAQ,KAAK,uBAAkB,UAAU,SAAS,+BAA+B,QAAQ,aAAa;AACtG,6CAAuB,IAAI,UAAU;AAAA,oBACvC;AACA;AAAA,kBACF;AACA,0BAAQ,OAAO,IAAI;AACnB;AAAA,gBACF;AAAA,cACF;AAEA,kBAAI,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG;AACnC,oBAAI,CAAC,QAAQ;AACX,wBAAM,SAAS,OAAO,KAAK,OAAO,EAAE,IAAI,CAAC,QAAQ,IAAI,GAAG,OAAO,EAAE,KAAK,IAAI;AAC1E,wBAAM,KAAK;AAAA,oBACT,UAAU,cAAc,QAAQ,MAAM,WAAW,EAAE;AAAA,oBACnD,CAAC,GAAG,OAAO,OAAO,OAAO,GAAG,IAAI,EAAE,CAAC;AAAA,kBACrC;AAAA,gBACF;AACA;AAAA,cACF;AAAA,YACF;AACA,kBAAM,KAAK,QAAQ,QAAQ;AAC3B,6BAAiB;AAAA,UACnB,SAAS,UAAe;AACtB,gBAAI,CAAC,gBAAgB;AACnB,kBAAI;AAAE,sBAAM,KAAK,QAAQ,UAAU;AAAA,cAAE,QAAQ;AAAA,cAAC;AAAA,YAChD;AACA,oBAAQ,MAAM,2CAA2C,QAAQ,KAAM,UAAoB,WAAW,OAAO,QAAQ,CAAC,EAAE;AACxH,kBAAM;AAAA,UACR;AAEA,gBAAM,kBAAkB,KAAK,IAAI,IAAI;AACrC,cAAI,OAAO;AACT,oBAAQ;AAAA,cACN,iBAAiB,QAAQ,QAAQ,MAAM,kBAAkB,MAAM,KAAK,MAAM,MAAM,YAAY,eAAe,sBAAsB,mBAAmB;AAAA,YACtJ;AACA,gBAAI,kBAAkB,KAAQ;AAC5B,sBAAQ,KAAK,yDAAoD;AAAA,YACnE;AAAA,UACF;AACA,cAAI,UAAU,GAAG;AACf,kBAAM,IAAI,QAAc,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AAAA,UACvD;AAAA,QACF;AAEA,sCAA8B;AAAA,MAChC;AAAA,IACF;AAEA,QAAI,kBAAkB,CAAC,QAAQ;AAC7B,UAAI,gBAAgB;AACpB,YAAM,mBAA8B,CAAC,WAAW;AAChD,UAAI,mBAAmB;AACrB,yBAAiB;AACjB,yBAAiB,KAAK,iBAAiB;AAAA,MACzC;AACA,UAAI,aAAa;AACf,yBAAiB;AACjB,yBAAiB,KAAK,WAAW;AAAA,MACnC;AACA,YAAM,KAAK,QAAQ,eAAe,gBAAgB;AAClD,cAAQ,KAAK,2FAAiF;AAC9F,UAAI,8BAA8B,GAAG;AACnC,gBAAQ,KAAK,qIAA2H;AAAA,MAC1I;AAAA,IACF;AAEA,QAAI,kBAAkB,QAAQ;AAC5B,cAAQ,IAAI,8BAA8B,KAAK,MAAM,2BAA2B;AAAA,IAClF;AAEA,UAAM,SAAS,SAAS,eAAe;AACvC,YAAQ,IAAI;AAAA,EAAK,MAAM,qBAAqB;AAC5C,YAAQ,IAAI,0BAA0B,gBAAgB,EAAE;AACxD,YAAQ,IAAI,0BAA0B,gBAAgB,EAAE;AACxD,YAAQ,IAAI,0BAA0B,sBAAsB,EAAE;AAC9D,YAAQ,IAAI,0BAA0B,sBAAsB,EAAE;AAC9D,QAAI,uBAAuB,OAAO,GAAG;AACnC,cAAQ,IAAI,4CAA4C,MAAM,KAAK,sBAAsB,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,IACzG;AACA,QAAI,6BAA6B,GAAG;AAClC,cAAQ;AAAA,QACN,YAAO,0BAA0B;AAAA,MACnC;AACA,UAAI,SAAS,oBAAoB,OAAO,GAAG;AACzC,cAAM,MAAM,MAAM,KAAK,oBAAoB,QAAQ,CAAC,EACjD,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAC1B,MAAM,GAAG,EAAE;AACd,gBAAQ,IAAI,4BAA4B;AACxC,mBAAW,CAAC,KAAK,KAAK,KAAK,KAAK;AAC9B,kBAAQ,IAAI,OAAO,GAAG,KAAK,KAAK,EAAE;AAAA,QACpC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ;AACX,cAAQ,IAAI;AAAA,iDAA+C;AAC3D,cAAQ,IAAI,sEAAsE;AAClF,cAAQ,IAAI,wCAAwC;AACpD,cAAQ,IAAI,mDAAmD,WAAW,oFAA+E;AACzJ,cAAQ,IAAI,yDAAyD,WAAW,iDAAiD;AACjI,cAAQ,IAAI,gHAA2G;AAAA,IACzH;AAAA,EACF;AACF;AAGA,IAAO,cAAQ,CAAC,UAAU,eAAe,UAAU,oBAAoB,qBAAqB,eAAe;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.6-develop.6535.1.5cf43724de",
3
+ "version": "0.6.6-develop.6540.1.ae856df1ec",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -253,16 +253,16 @@
253
253
  "zod": "^4.4.3"
254
254
  },
255
255
  "peerDependencies": {
256
- "@open-mercato/ai-assistant": "0.6.6-develop.6535.1.5cf43724de",
257
- "@open-mercato/shared": "0.6.6-develop.6535.1.5cf43724de",
258
- "@open-mercato/ui": "0.6.6-develop.6535.1.5cf43724de",
256
+ "@open-mercato/ai-assistant": "0.6.6-develop.6540.1.ae856df1ec",
257
+ "@open-mercato/shared": "0.6.6-develop.6540.1.ae856df1ec",
258
+ "@open-mercato/ui": "0.6.6-develop.6540.1.ae856df1ec",
259
259
  "react": "^19.0.0",
260
260
  "react-dom": "^19.0.0"
261
261
  },
262
262
  "devDependencies": {
263
- "@open-mercato/ai-assistant": "0.6.6-develop.6535.1.5cf43724de",
264
- "@open-mercato/shared": "0.6.6-develop.6535.1.5cf43724de",
265
- "@open-mercato/ui": "0.6.6-develop.6535.1.5cf43724de",
263
+ "@open-mercato/ai-assistant": "0.6.6-develop.6540.1.ae856df1ec",
264
+ "@open-mercato/shared": "0.6.6-develop.6540.1.ae856df1ec",
265
+ "@open-mercato/ui": "0.6.6-develop.6540.1.ae856df1ec",
266
266
  "@testing-library/dom": "^10.4.1",
267
267
  "@testing-library/jest-dom": "^6.9.1",
268
268
  "@testing-library/react": "^16.3.1",
@@ -3,6 +3,7 @@ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
3
3
  import { createApiKey } from './services/apiKeyService'
4
4
  import { Organization } from '@open-mercato/core/modules/directory/data/entities'
5
5
  import { Role } from '@open-mercato/core/modules/auth/data/entities'
6
+ import { parseCommaSeparatedList } from '@open-mercato/shared/lib/string'
6
7
 
7
8
  function parseArgs(rest: string[]): Record<string, string> {
8
9
  const args: Record<string, string> = {}
@@ -47,9 +48,7 @@ const addApiKey: ModuleCli = {
47
48
  return
48
49
  }
49
50
 
50
- const roleInputs = rolesCsv
51
- ? rolesCsv.split(',').map((value) => value.trim()).filter(Boolean)
52
- : []
51
+ const roleInputs = parseCommaSeparatedList(rolesCsv)
53
52
  const roleIds: string[] = []
54
53
  if (roleInputs.length) {
55
54
  for (const token of roleInputs) {
@@ -15,6 +15,7 @@ import {
15
15
  canRenderInlineAttachment,
16
16
  } from "@open-mercato/core/modules/attachments/lib/security";
17
17
  import { StorageDriverFactory } from '../../../lib/drivers';
18
+ import { resolveAttachmentOrganizationId } from '@open-mercato/core/modules/attachments/lib/requestScope';
18
19
 
19
20
  export const metadata = {
20
21
  GET: { requireAuth: false },
@@ -32,16 +33,19 @@ export async function GET(
32
33
  );
33
34
  }
34
35
  const auth = await getAuthFromRequest(req);
35
- const { resolve } = await createRequestContainer();
36
- const em = resolve("em") as EntityManager;
36
+ const container = await createRequestContainer();
37
+ const em = container.resolve("em") as EntityManager;
37
38
  const storageDriverFactory =
38
- (resolve("storageDriverFactory") as StorageDriverFactory | null) ??
39
+ (container.resolve("storageDriverFactory") as StorageDriverFactory | null) ??
39
40
  new StorageDriverFactory(em);
40
41
 
42
+ const scopedAuth = auth
43
+ ? { ...auth, orgId: await resolveAttachmentOrganizationId(container, auth, req) }
44
+ : auth;
41
45
  const findFilter: Record<string, unknown> = { id };
42
- if (auth && !isSuperAdminAuth(auth)) {
43
- if (auth.tenantId) findFilter.tenantId = auth.tenantId;
44
- if (auth.orgId) findFilter.organizationId = auth.orgId;
46
+ if (scopedAuth && !isSuperAdminAuth(scopedAuth)) {
47
+ if (scopedAuth.tenantId) findFilter.tenantId = scopedAuth.tenantId;
48
+ if (scopedAuth.orgId) findFilter.organizationId = scopedAuth.orgId;
45
49
  }
46
50
  const attachment = await em.findOne(Attachment, findFilter);
47
51
  if (!attachment) {
@@ -60,7 +64,7 @@ export async function GET(
60
64
  );
61
65
  }
62
66
 
63
- const access = checkAttachmentAccess(auth, attachment, partition);
67
+ const access = checkAttachmentAccess(scopedAuth, attachment, partition);
64
68
  if (!access.ok) {
65
69
  const message = access.status === 401 ? "Unauthorized" : "Forbidden";
66
70
  return NextResponse.json({ error: message }, { status: access.status });
@@ -20,6 +20,7 @@ import {
20
20
  validateImageMagicBytes,
21
21
  } from '@open-mercato/core/modules/attachments/lib/imageSafety'
22
22
  import { StorageDriverFactory } from '../../../../lib/drivers'
23
+ import { resolveAttachmentOrganizationId } from '@open-mercato/core/modules/attachments/lib/requestScope'
23
24
  import { createLogger } from '@open-mercato/shared/lib/logger'
24
25
 
25
26
  const logger = createLogger('attachments').child({ component: 'image' })
@@ -51,15 +52,18 @@ export async function GET(
51
52
  }
52
53
  const { width, height, cropType } = parsedQuery.data
53
54
 
54
- const { resolve } = await createRequestContainer()
55
- const em = resolve('em') as EntityManager
55
+ const container = await createRequestContainer()
56
+ const em = container.resolve('em') as EntityManager
56
57
  const storageDriverFactory =
57
- (resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
58
+ (container.resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
58
59
 
60
+ const scopedAuth = auth
61
+ ? { ...auth, orgId: await resolveAttachmentOrganizationId(container, auth, req) }
62
+ : auth
59
63
  const findFilter: Record<string, unknown> = { id }
60
- if (auth && !isSuperAdminAuth(auth)) {
61
- if (auth.tenantId) findFilter.tenantId = auth.tenantId
62
- if (auth.orgId) findFilter.organizationId = auth.orgId
64
+ if (scopedAuth && !isSuperAdminAuth(scopedAuth)) {
65
+ if (scopedAuth.tenantId) findFilter.tenantId = scopedAuth.tenantId
66
+ if (scopedAuth.orgId) findFilter.organizationId = scopedAuth.orgId
63
67
  }
64
68
  const attachment = await em.findOne(Attachment, findFilter)
65
69
  if (!attachment) {
@@ -72,7 +76,7 @@ export async function GET(
72
76
  if (!partition) {
73
77
  return NextResponse.json({ error: 'Partition misconfigured' }, { status: 500 })
74
78
  }
75
- const access = checkAttachmentAccess(auth, attachment, partition)
79
+ const access = checkAttachmentAccess(scopedAuth, attachment, partition)
76
80
  if (!access.ok) {
77
81
  const message = access.status === 401 ? 'Unauthorized' : 'Forbidden'
78
82
  return NextResponse.json({ error: message }, { status: access.status })
@@ -13,6 +13,7 @@ import { StorageDriverFactory } from '../lib/drivers'
13
13
  import { OcrService, shouldUseLlmOcr } from '../lib/ocrService'
14
14
  import { clearAttachmentThumbnailCache } from '../lib/thumbnailCache'
15
15
  import { assertAttachmentScopeInvariant } from '../lib/access'
16
+ import { resolveAttachmentOrganizationId } from '../lib/requestScope'
16
17
  import {
17
18
  mergeAttachmentMetadata,
18
19
  normalizeAttachmentAssignments,
@@ -198,10 +199,11 @@ export async function GET(req: Request) {
198
199
  }
199
200
  const { entityId, recordId, page, pageSize } = parsedQuery.data
200
201
 
201
- const { resolve } = await createRequestContainer()
202
- const em = resolve('em') as EntityManager
202
+ const container = await createRequestContainer()
203
+ const em = container.resolve('em') as EntityManager
204
+ const orgId = await resolveAttachmentOrganizationId(container, auth, req)
203
205
  const filter: Record<string, unknown> = { entityId, recordId, tenantId: auth.tenantId! }
204
- if (auth.orgId) filter.organizationId = auth.orgId
206
+ if (orgId) filter.organizationId = orgId
205
207
  const orderBy: Record<string, 'ASC' | 'DESC'> = { createdAt: 'DESC' }
206
208
  const usePaging = typeof page === 'number' && typeof pageSize === 'number'
207
209
  const total = usePaging ? await em.count(Attachment, filter) : null
@@ -224,7 +226,7 @@ export async function GET(req: Request) {
224
226
  },
225
227
  {
226
228
  tenantId: auth.tenantId ?? null,
227
- organizationId: auth.orgId ?? null,
229
+ organizationId: orgId ?? null,
228
230
  },
229
231
  )
230
232
  return NextResponse.json({
@@ -275,7 +277,6 @@ export async function POST(req: Request) {
275
277
  }, { status: 400 })
276
278
  }
277
279
  const tenantId = auth.tenantId
278
- const orgId = auth.orgId
279
280
 
280
281
  const contentType = req.headers.get('content-type') || ''
281
282
  if (!contentType.toLowerCase().includes('multipart/form-data')) {
@@ -301,11 +302,13 @@ export async function POST(req: Request) {
301
302
  const tags = parseFormTags(form.get('tags'))
302
303
  const assignmentsFromForm = parseFormAssignments(form.get('assignments'))
303
304
 
304
- const { resolve } = await createRequestContainer()
305
- const em = resolve('em') as EntityManager
306
- const dataEngine = resolve('dataEngine')
305
+ const container = await createRequestContainer()
306
+ const em = container.resolve('em') as EntityManager
307
+ const dataEngine = container.resolve('dataEngine')
307
308
  const storageDriverFactory =
308
- (resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
309
+ (container.resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
310
+ const orgId = await resolveAttachmentOrganizationId(container, auth, req)
311
+ if (!orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
309
312
  await ensureDefaultPartitions(em)
310
313
  // Optional per-field validations
311
314
  let partitionFromField: string | null = null
@@ -436,12 +439,12 @@ export async function POST(req: Request) {
436
439
  }
437
440
  const metadata = mergeAttachmentMetadata(null, { assignments, tags })
438
441
  const attachmentId = randomUUID()
439
- assertAttachmentScopeInvariant({ tenantId: auth.tenantId, organizationId: auth.orgId })
442
+ assertAttachmentScopeInvariant({ tenantId: auth.tenantId, organizationId: orgId })
440
443
  const att = em.create(Attachment, {
441
444
  id: attachmentId,
442
445
  entityId,
443
446
  recordId,
444
- organizationId: auth.orgId!,
447
+ organizationId: orgId,
445
448
  tenantId: auth.tenantId!,
446
449
  fileName: safeName,
447
450
  mimeType: fileMimeType,
@@ -545,17 +548,19 @@ export async function DELETE(req: Request) {
545
548
  const url = new URL(req.url)
546
549
  const id = url.searchParams.get('id') || ''
547
550
  if (!id) return NextResponse.json({ error: 'Attachment id is required' }, { status: 400 })
548
- const { resolve } = await createRequestContainer()
549
- const em = resolve('em') as EntityManager
550
- const dataEngine = resolve('dataEngine')
551
+ const container = await createRequestContainer()
552
+ const em = container.resolve('em') as EntityManager
553
+ const dataEngine = container.resolve('dataEngine')
551
554
  const storageDriverFactory =
552
- (resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
553
- // Mirror the GET handler's superadmin bypass: a superadmin browsing with "All
554
- // organizations" selected has no concrete org, so scope the delete by tenant
555
- // only and let them remove any attachment in the tenant. Non-superadmins stay
556
- // scoped to their own organization (#3764).
555
+ (container.resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
556
+ // Resolve the currently selected organization (#3765) so a multi-org admin who
557
+ // switched the header org deletes within that org, not their pinned home org.
558
+ // A superadmin browsing with "All organizations" selected has no concrete org,
559
+ // so resolution returns null and the delete falls back to a tenant-only scope
560
+ // (#3764) — letting them remove any attachment in the tenant.
561
+ const orgId = await resolveAttachmentOrganizationId(container, auth, req)
557
562
  const deleteFilter: Record<string, unknown> = { id, tenantId: auth.tenantId! }
558
- if (auth.orgId) deleteFilter.organizationId = auth.orgId
563
+ if (orgId) deleteFilter.organizationId = orgId
559
564
  const record = await em.findOne(Attachment, deleteFilter)
560
565
  if (!record) return NextResponse.json({ error: 'Attachment not found' }, { status: 404 })
561
566
  await em.remove(record).flush()
@@ -565,7 +570,7 @@ export async function DELETE(req: Request) {
565
570
  if (record.storagePath) {
566
571
  const delDriver = await storageDriverFactory.resolveForPartition(record.partitionCode, {
567
572
  tenantId: record.tenantId ?? auth.tenantId!,
568
- organizationId: record.organizationId ?? auth.orgId ?? '',
573
+ organizationId: record.organizationId ?? orgId ?? '',
569
574
  })
570
575
  await delDriver.delete(record.partitionCode, record.storagePath)
571
576
  }