@open-mercato/core 0.4.6-develop-e321a4e2a1 → 0.4.6-main-24e64eef39

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 (180) hide show
  1. package/AGENTS.md +0 -22
  2. package/dist/modules/api_docs/frontend/docs/api/page.js +1 -1
  3. package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
  4. package/dist/modules/attachments/api/library/[id]/route.js +1 -0
  5. package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
  6. package/dist/modules/attachments/components/AttachmentLibrary.js +1 -1
  7. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  8. package/dist/modules/attachments/lib/partitionEnv.js +1 -1
  9. package/dist/modules/attachments/lib/partitionEnv.js.map +2 -2
  10. package/dist/modules/auth/backend/users/page.js +1 -1
  11. package/dist/modules/auth/backend/users/page.js.map +2 -2
  12. package/dist/modules/auth/cli.js +1 -1
  13. package/dist/modules/auth/cli.js.map +2 -2
  14. package/dist/modules/auth/commands/users.js +1 -1
  15. package/dist/modules/auth/commands/users.js.map +2 -2
  16. package/dist/modules/business_rules/components/utils/formHelpers.js +1 -1
  17. package/dist/modules/business_rules/components/utils/formHelpers.js.map +2 -2
  18. package/dist/modules/catalog/backend/catalog/products/create/page.js +1 -1
  19. package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
  20. package/dist/modules/catalog/commands/products.js +1 -1
  21. package/dist/modules/catalog/commands/products.js.map +2 -2
  22. package/dist/modules/catalog/commands/shared.js +1 -1
  23. package/dist/modules/catalog/commands/shared.js.map +2 -2
  24. package/dist/modules/catalog/components/PriceKindSettings.js +1 -1
  25. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  26. package/dist/modules/catalog/components/products/productForm.js +1 -1
  27. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  28. package/dist/modules/configs/lib/upgrade-actions.js.map +1 -1
  29. package/dist/modules/currencies/services/providers/raiffeisen.js +1 -1
  30. package/dist/modules/currencies/services/providers/raiffeisen.js.map +2 -2
  31. package/dist/modules/customers/backend/customers/companies/page.js +3 -3
  32. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  33. package/dist/modules/customers/backend/customers/deals/page.js +3 -3
  34. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  35. package/dist/modules/customers/backend/customers/people/page.js +3 -3
  36. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  37. package/dist/modules/customers/cli.js +2 -2
  38. package/dist/modules/customers/cli.js.map +2 -2
  39. package/dist/modules/customers/lib/detailHelpers.js +1 -1
  40. package/dist/modules/customers/lib/detailHelpers.js.map +2 -2
  41. package/dist/modules/entities/cli.js +1 -1
  42. package/dist/modules/entities/cli.js.map +2 -2
  43. package/dist/modules/entities/lib/field-definitions.js +1 -1
  44. package/dist/modules/entities/lib/field-definitions.js.map +2 -2
  45. package/dist/modules/entities/lib/install-from-ce.js +1 -1
  46. package/dist/modules/entities/lib/install-from-ce.js.map +2 -2
  47. package/dist/modules/inbox_ops/lib/emailParser.js +1 -1
  48. package/dist/modules/inbox_ops/lib/emailParser.js.map +2 -2
  49. package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js +0 -8
  50. package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js.map +2 -2
  51. package/dist/modules/perspectives/services/perspectiveService.js +1 -1
  52. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  53. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +3 -3
  54. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  55. package/dist/modules/query_index/components/QueryIndexesTable.js +7 -7
  56. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  57. package/dist/modules/query_index/lib/engine.js +1 -1
  58. package/dist/modules/query_index/lib/engine.js.map +2 -2
  59. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
  60. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  61. package/dist/modules/resources/backend/resources/resources/page.js +3 -3
  62. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  63. package/dist/modules/resources/commands/resources.js +1 -1
  64. package/dist/modules/resources/commands/resources.js.map +2 -2
  65. package/dist/modules/resources/lib/seeds.js +1 -1
  66. package/dist/modules/resources/lib/seeds.js.map +2 -2
  67. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js +1 -1
  68. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js.map +2 -2
  69. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js +1 -1
  70. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js.map +2 -2
  71. package/dist/modules/sales/backend/sales/channels/page.js +3 -3
  72. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  73. package/dist/modules/sales/commands/documents.js +2 -2
  74. package/dist/modules/sales/commands/documents.js.map +2 -2
  75. package/dist/modules/sales/components/channels/offerTableUtils.js +2 -3
  76. package/dist/modules/sales/components/channels/offerTableUtils.js.map +2 -2
  77. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +4 -4
  78. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  79. package/dist/modules/sales/components/documents/ShipmentDialog.js +1 -1
  80. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  81. package/dist/modules/sales/lib/shipments/snapshots.js.map +1 -1
  82. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +0 -8
  83. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +2 -2
  84. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +0 -8
  85. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +2 -2
  86. package/dist/modules/staff/backend/staff/leave-requests/page.js +3 -3
  87. package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
  88. package/dist/modules/staff/backend/staff/my-leave-requests/page.js +3 -3
  89. package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
  90. package/dist/modules/staff/backend/staff/team-members/page.js +3 -3
  91. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  92. package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
  93. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  94. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +3 -3
  95. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  96. package/dist/modules/staff/backend/staff/teams/page.js +2 -2
  97. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  98. package/dist/modules/workflows/backend/instances/page.js +2 -2
  99. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  100. package/dist/modules/workflows/backend/tasks/page.js +1 -1
  101. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  102. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -1
  103. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
  104. package/dist/modules/workflows/lib/graph-utils.js +1 -1
  105. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  106. package/package.json +2 -2
  107. package/src/modules/api_docs/frontend/docs/api/page.tsx +1 -1
  108. package/src/modules/attachments/api/library/[id]/route.ts +1 -0
  109. package/src/modules/attachments/components/AttachmentLibrary.tsx +1 -1
  110. package/src/modules/attachments/lib/partitionEnv.ts +1 -1
  111. package/src/modules/auth/backend/users/page.tsx +1 -1
  112. package/src/modules/auth/cli.ts +1 -1
  113. package/src/modules/auth/commands/users.ts +1 -1
  114. package/src/modules/business_rules/components/utils/formHelpers.ts +1 -1
  115. package/src/modules/catalog/backend/catalog/products/create/page.tsx +1 -1
  116. package/src/modules/catalog/commands/products.ts +1 -1
  117. package/src/modules/catalog/commands/shared.ts +1 -1
  118. package/src/modules/catalog/components/PriceKindSettings.tsx +1 -1
  119. package/src/modules/catalog/components/products/productForm.ts +1 -1
  120. package/src/modules/configs/lib/upgrade-actions.ts +1 -1
  121. package/src/modules/currencies/services/providers/raiffeisen.ts +1 -1
  122. package/src/modules/customers/backend/customers/companies/page.tsx +3 -3
  123. package/src/modules/customers/backend/customers/deals/page.tsx +3 -3
  124. package/src/modules/customers/backend/customers/people/page.tsx +3 -3
  125. package/src/modules/customers/cli.ts +2 -2
  126. package/src/modules/customers/lib/detailHelpers.ts +1 -1
  127. package/src/modules/entities/cli.ts +1 -1
  128. package/src/modules/entities/lib/field-definitions.ts +1 -1
  129. package/src/modules/entities/lib/install-from-ce.ts +1 -1
  130. package/src/modules/inbox_ops/lib/emailParser.ts +1 -1
  131. package/src/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.tsx +0 -8
  132. package/src/modules/perspectives/services/perspectiveService.ts +1 -1
  133. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +4 -3
  134. package/src/modules/query_index/components/QueryIndexesTable.tsx +7 -7
  135. package/src/modules/query_index/lib/engine.ts +1 -1
  136. package/src/modules/resources/backend/resources/resource-types/page.tsx +3 -2
  137. package/src/modules/resources/backend/resources/resources/page.tsx +3 -3
  138. package/src/modules/resources/commands/resources.ts +1 -1
  139. package/src/modules/resources/lib/seeds.ts +1 -1
  140. package/src/modules/sales/api/dashboard/widgets/new-orders/route.ts +1 -1
  141. package/src/modules/sales/api/dashboard/widgets/new-quotes/route.ts +1 -1
  142. package/src/modules/sales/backend/sales/channels/page.tsx +3 -3
  143. package/src/modules/sales/commands/documents.ts +2 -2
  144. package/src/modules/sales/components/channels/offerTableUtils.tsx +2 -3
  145. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +4 -4
  146. package/src/modules/sales/components/documents/ShipmentDialog.tsx +1 -1
  147. package/src/modules/sales/lib/shipments/snapshots.ts +1 -1
  148. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +0 -8
  149. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +0 -8
  150. package/src/modules/staff/backend/staff/leave-requests/page.tsx +3 -3
  151. package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +3 -3
  152. package/src/modules/staff/backend/staff/team-members/page.tsx +3 -3
  153. package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
  154. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +4 -3
  155. package/src/modules/staff/backend/staff/teams/page.tsx +3 -2
  156. package/src/modules/workflows/backend/instances/page.tsx +2 -2
  157. package/src/modules/workflows/backend/tasks/page.tsx +1 -1
  158. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -1
  159. package/src/modules/workflows/lib/graph-utils.ts +1 -1
  160. package/dist/modules/integrations/acl.js +0 -8
  161. package/dist/modules/integrations/acl.js.map +0 -7
  162. package/dist/modules/integrations/data/enrichers.js +0 -72
  163. package/dist/modules/integrations/data/enrichers.js.map +0 -7
  164. package/dist/modules/integrations/data/entities.js +0 -63
  165. package/dist/modules/integrations/data/entities.js.map +0 -7
  166. package/dist/modules/integrations/index.js +0 -9
  167. package/dist/modules/integrations/index.js.map +0 -7
  168. package/dist/modules/integrations/setup.js +0 -13
  169. package/dist/modules/integrations/setup.js.map +0 -7
  170. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js +0 -69
  171. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js.map +0 -7
  172. package/dist/modules/integrations/widgets/injection-table.js +0 -13
  173. package/dist/modules/integrations/widgets/injection-table.js.map +0 -7
  174. package/src/modules/integrations/acl.ts +0 -4
  175. package/src/modules/integrations/data/enrichers.ts +0 -98
  176. package/src/modules/integrations/data/entities.ts +0 -46
  177. package/src/modules/integrations/index.ts +0 -5
  178. package/src/modules/integrations/setup.ts +0 -11
  179. package/src/modules/integrations/widgets/injection/external-ids/widget.client.tsx +0 -94
  180. package/src/modules/integrations/widgets/injection-table.ts +0 -17
@@ -25,7 +25,7 @@ function isValidSocialUrl(rawValue, options) {
25
25
  if (!pathRequired) {
26
26
  return true;
27
27
  }
28
- const normalizedPath = parsed.pathname.replace(/\/+/g, "/").replace(/(?:^\/|\/$)/g, "");
28
+ const normalizedPath = parsed.pathname.replace(/\/+/g, "/").replace(/^\/|\/$/g, "");
29
29
  return normalizedPath.length > 0;
30
30
  }
31
31
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customers/lib/detailHelpers.ts"],
4
- "sourcesContent": ["import { slugifyTagLabel } from '@open-mercato/shared/lib/utils'\n\nexport function generateTempId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `tmp-${Math.random().toString(36).slice(2)}`\n}\n\nexport function isValidSocialUrl(\n rawValue: string,\n options: { hosts: string[]; pathRequired?: boolean },\n): boolean {\n const { hosts, pathRequired = false } = options\n let parsed: URL\n try {\n parsed = new URL(rawValue)\n } catch {\n return false\n }\n const protocol = parsed.protocol.toLowerCase()\n if (protocol !== 'https:' && protocol !== 'http:') {\n return false\n }\n const hostname = parsed.hostname.toLowerCase()\n const matchesHost = hosts.some((host) => hostname === host || hostname.endsWith(`.${host}`))\n if (!matchesHost) {\n return false\n }\n if (!pathRequired) {\n return true\n }\n const normalizedPath = parsed.pathname.replace(/\\/+/g, '/').replace(/(?:^\\/|\\/$)/g, '')\n return normalizedPath.length > 0\n}\n\nexport { slugifyTagLabel }\n"],
5
- "mappings": "AAAA,SAAS,uBAAuB;AAEzB,SAAS,iBAAyB;AACvC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACnD;AAEO,SAAS,iBACd,UACA,SACS;AACT,QAAM,EAAE,OAAO,eAAe,MAAM,IAAI;AACxC,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,QAAQ;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,WAAW,OAAO,SAAS,YAAY;AAC7C,MAAI,aAAa,YAAY,aAAa,SAAS;AACjD,WAAO;AAAA,EACT;AACA,QAAM,WAAW,OAAO,SAAS,YAAY;AAC7C,QAAM,cAAc,MAAM,KAAK,CAAC,SAAS,aAAa,QAAQ,SAAS,SAAS,IAAI,IAAI,EAAE,CAAC;AAC3F,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AACA,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AACA,QAAM,iBAAiB,OAAO,SAAS,QAAQ,QAAQ,GAAG,EAAE,QAAQ,gBAAgB,EAAE;AACtF,SAAO,eAAe,SAAS;AACjC;",
4
+ "sourcesContent": ["import { slugifyTagLabel } from '@open-mercato/shared/lib/utils'\n\nexport function generateTempId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `tmp-${Math.random().toString(36).slice(2)}`\n}\n\nexport function isValidSocialUrl(\n rawValue: string,\n options: { hosts: string[]; pathRequired?: boolean },\n): boolean {\n const { hosts, pathRequired = false } = options\n let parsed: URL\n try {\n parsed = new URL(rawValue)\n } catch {\n return false\n }\n const protocol = parsed.protocol.toLowerCase()\n if (protocol !== 'https:' && protocol !== 'http:') {\n return false\n }\n const hostname = parsed.hostname.toLowerCase()\n const matchesHost = hosts.some((host) => hostname === host || hostname.endsWith(`.${host}`))\n if (!matchesHost) {\n return false\n }\n if (!pathRequired) {\n return true\n }\n const normalizedPath = parsed.pathname.replace(/\\/+/g, '/').replace(/^\\/|\\/$/g, '')\n return normalizedPath.length > 0\n}\n\nexport { slugifyTagLabel }\n"],
5
+ "mappings": "AAAA,SAAS,uBAAuB;AAEzB,SAAS,iBAAyB;AACvC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACnD;AAEO,SAAS,iBACd,UACA,SACS;AACT,QAAM,EAAE,OAAO,eAAe,MAAM,IAAI;AACxC,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,QAAQ;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,WAAW,OAAO,SAAS,YAAY;AAC7C,MAAI,aAAa,YAAY,aAAa,SAAS;AACjD,WAAO;AAAA,EACT;AACA,QAAM,WAAW,OAAO,SAAS,YAAY;AAC7C,QAAM,cAAc,MAAM,KAAK,CAAC,SAAS,aAAa,QAAQ,SAAS,SAAS,IAAI,IAAI,EAAE,CAAC;AAC3F,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AACA,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AACA,QAAM,iBAAiB,OAAO,SAAS,QAAQ,QAAQ,GAAG,EAAE,QAAQ,YAAY,EAAE;AAClF,SAAO,eAAe,SAAS;AACjC;",
6
6
  "names": []
7
7
  }
@@ -283,7 +283,7 @@ const seedEncryptionMaps = {
283
283
  }
284
284
  };
285
285
  function normalizeKeyInput(value) {
286
- return value.trim().replace(/(?:^['"]|['"]$)/g, "");
286
+ return value.trim().replace(/^['"]|['"]$/g, "");
287
287
  }
288
288
  class DerivedKeyKmsService {
289
289
  constructor(secret) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/entities/cli.ts"],
4
- "sourcesContent": ["import 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 '@open-mercato/core/modules/entities/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 { DEFAULT_ENCRYPTION_MAPS } from '@open-mercato/core/modules/entities/lib/encryptionDefaults'\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 { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { resolveEntityIdFromMetadata } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { Organization } from '@open-mercato/core/modules/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.persistAndFlush(em.create(CustomFieldDef, {\n entityId,\n organizationId: orgId,\n tenantId: tenantId,\n key,\n kind,\n configJson,\n isActive: true,\n }))\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\nasync function upsertEncryptionMaps(em: any, tenantId: string, organizationId: string | null, logger: (msg: string) => void) {\n for (const spec of DEFAULT_ENCRYPTION_MAPS) {\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.persistAndFlush(existing)\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.persistAndFlush(map)\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 try {\n payload[rule.field] = JSON.parse(decrypted)\n } catch {\n payload[rule.field] = decrypted\n }\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 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 '@open-mercato/core/modules/entities/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 { DEFAULT_ENCRYPTION_MAPS } from '@open-mercato/core/modules/entities/lib/encryptionDefaults'\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 { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { resolveEntityIdFromMetadata } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { Organization } from '@open-mercato/core/modules/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.persistAndFlush(em.create(CustomFieldDef, {\n entityId,\n organizationId: orgId,\n tenantId: tenantId,\n key,\n kind,\n configJson,\n isActive: true,\n }))\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\nasync function upsertEncryptionMaps(em: any, tenantId: string, organizationId: string | null, logger: (msg: string) => void) {\n for (const spec of DEFAULT_ENCRYPTION_MAPS) {\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.persistAndFlush(existing)\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.persistAndFlush(map)\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 try {\n payload[rule.field] = JSON.parse(decrypted)\n } catch {\n payload[rule.field] = decrypted\n }\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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",
6
6
  "names": []
7
7
  }
@@ -25,7 +25,7 @@ const CONFIG_PASSTHROUGH_KEYS = [
25
25
  function normalizeValue(value) {
26
26
  if (Array.isArray(value)) return value.map((item) => normalizeValue(item));
27
27
  if (value && typeof value === "object") {
28
- return Object.keys(value).sort((a, b) => a.localeCompare(b)).reduce((acc, key) => {
28
+ return Object.keys(value).sort().reduce((acc, key) => {
29
29
  acc[key] = normalizeValue(value[key]);
30
30
  return acc;
31
31
  }, {});
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/entities/lib/field-definitions.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomFieldDef } from '../data/entities'\nimport type { CustomFieldDefinition } from '@open-mercato/shared/modules/entities'\n\nexport type FieldSetInput = {\n entity: string\n fields: CustomFieldDefinition[]\n source?: string\n}\n\nexport type EnsureFieldDefinitionsOptions = {\n organizationId: string | null\n tenantId: string | null\n dryRun?: boolean\n}\n\nexport type EnsureFieldDefinitionsResult = {\n created: number\n updated: number\n unchanged: number\n}\n\nconst CONFIG_PASSTHROUGH_KEYS: Array<keyof CustomFieldDefinition> = [\n 'label',\n 'description',\n 'fieldset',\n 'group',\n 'options',\n 'optionsUrl',\n 'defaultValue',\n 'required',\n 'multi',\n 'filterable',\n 'formEditable',\n 'listVisible',\n 'indexed',\n 'editor',\n 'input',\n 'relatedEntityId',\n 'dictionaryId',\n 'dictionaryInlineCreate',\n 'validation',\n 'maxAttachmentSizeMb',\n 'acceptExtensions',\n]\n\nfunction normalizeValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map((item) => normalizeValue(item))\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>)\n .sort((a, b) => a.localeCompare(b))\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = normalizeValue((value as Record<string, unknown>)[key])\n return acc\n }, {})\n }\n return value\n}\n\nfunction configEquals(a: unknown, b: unknown): boolean {\n return JSON.stringify(normalizeValue(a ?? null)) === JSON.stringify(normalizeValue(b ?? null))\n}\n\nexport async function ensureCustomFieldDefinitions(\n em: EntityManager,\n sets: FieldSetInput[],\n scope: EnsureFieldDefinitionsOptions\n): Promise<EnsureFieldDefinitionsResult> {\n let created = 0\n let updated = 0\n let unchanged = 0\n\n for (const set of sets) {\n for (const field of set.fields) {\n const where = {\n entityId: set.entity,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n key: field.key,\n }\n const existing = await em.findOne(CustomFieldDef, where)\n const configJson: Record<string, unknown> = {}\n\n for (const key of CONFIG_PASSTHROUGH_KEYS) {\n const value = field[key]\n if (value !== undefined) configJson[key] = value as unknown\n }\n\n if (!existing) {\n if (!scope.dryRun) {\n await em.persistAndFlush(\n em.create(CustomFieldDef, {\n entityId: set.entity,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n key: field.key,\n kind: field.kind,\n configJson,\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n created++\n continue\n }\n\n const kindChanged = existing.kind !== field.kind\n const configChanged = !configEquals(existing.configJson ?? null, configJson)\n const needsActivation = existing.isActive !== true || existing.deletedAt != null\n if (!kindChanged && !configChanged && !needsActivation) {\n unchanged++\n continue\n }\n\n if (!scope.dryRun) {\n existing.kind = field.kind\n ;(existing as any).configJson = configJson\n existing.isActive = true\n existing.updatedAt = new Date()\n if (existing.deletedAt) existing.deletedAt = null\n await em.flush()\n }\n updated++\n }\n }\n\n return { created, updated, unchanged }\n}\n"],
5
- "mappings": "AACA,SAAS,sBAAsB;AAqB/B,MAAM,0BAA8D;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,eAAe,OAAyB;AAC/C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,CAAC,SAAS,eAAe,IAAI,CAAC;AACzE,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,WAAO,OAAO,KAAK,KAAgC,EAChD,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC,EACjC,OAAgC,CAAC,KAAK,QAAQ;AAC7C,UAAI,GAAG,IAAI,eAAgB,MAAkC,GAAG,CAAC;AACjE,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,aAAa,GAAY,GAAqB;AACrD,SAAO,KAAK,UAAU,eAAe,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,eAAe,KAAK,IAAI,CAAC;AAC/F;AAEA,eAAsB,6BACpB,IACA,MACA,OACuC;AACvC,MAAI,UAAU;AACd,MAAI,UAAU;AACd,MAAI,YAAY;AAEhB,aAAW,OAAO,MAAM;AACtB,eAAW,SAAS,IAAI,QAAQ;AAC9B,YAAM,QAAQ;AAAA,QACZ,UAAU,IAAI;AAAA,QACd,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,QAChB,KAAK,MAAM;AAAA,MACb;AACA,YAAM,WAAW,MAAM,GAAG,QAAQ,gBAAgB,KAAK;AACvD,YAAM,aAAsC,CAAC;AAE7C,iBAAW,OAAO,yBAAyB;AACzC,cAAM,QAAQ,MAAM,GAAG;AACvB,YAAI,UAAU,OAAW,YAAW,GAAG,IAAI;AAAA,MAC7C;AAEA,UAAI,CAAC,UAAU;AACb,YAAI,CAAC,MAAM,QAAQ;AACjB,gBAAM,GAAG;AAAA,YACP,GAAG,OAAO,gBAAgB;AAAA,cACxB,UAAU,IAAI;AAAA,cACd,gBAAgB,MAAM;AAAA,cACtB,UAAU,MAAM;AAAA,cAChB,KAAK,MAAM;AAAA,cACX,MAAM,MAAM;AAAA,cACZ;AAAA,cACA,UAAU;AAAA,cACV,WAAW,oBAAI,KAAK;AAAA,cACpB,WAAW,oBAAI,KAAK;AAAA,YACtB,CAAC;AAAA,UACH;AAAA,QACF;AACA;AACA;AAAA,MACF;AAEA,YAAM,cAAc,SAAS,SAAS,MAAM;AAC5C,YAAM,gBAAgB,CAAC,aAAa,SAAS,cAAc,MAAM,UAAU;AAC3E,YAAM,kBAAkB,SAAS,aAAa,QAAQ,SAAS,aAAa;AAC5E,UAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,iBAAiB;AACtD;AACA;AAAA,MACF;AAEA,UAAI,CAAC,MAAM,QAAQ;AACjB,iBAAS,OAAO,MAAM;AACrB,QAAC,SAAiB,aAAa;AAChC,iBAAS,WAAW;AACpB,iBAAS,YAAY,oBAAI,KAAK;AAC9B,YAAI,SAAS,UAAW,UAAS,YAAY;AAC7C,cAAM,GAAG,MAAM;AAAA,MACjB;AACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,SAAS,UAAU;AACvC;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomFieldDef } from '../data/entities'\nimport type { CustomFieldDefinition } from '@open-mercato/shared/modules/entities'\n\nexport type FieldSetInput = {\n entity: string\n fields: CustomFieldDefinition[]\n source?: string\n}\n\nexport type EnsureFieldDefinitionsOptions = {\n organizationId: string | null\n tenantId: string | null\n dryRun?: boolean\n}\n\nexport type EnsureFieldDefinitionsResult = {\n created: number\n updated: number\n unchanged: number\n}\n\nconst CONFIG_PASSTHROUGH_KEYS: Array<keyof CustomFieldDefinition> = [\n 'label',\n 'description',\n 'fieldset',\n 'group',\n 'options',\n 'optionsUrl',\n 'defaultValue',\n 'required',\n 'multi',\n 'filterable',\n 'formEditable',\n 'listVisible',\n 'indexed',\n 'editor',\n 'input',\n 'relatedEntityId',\n 'dictionaryId',\n 'dictionaryInlineCreate',\n 'validation',\n 'maxAttachmentSizeMb',\n 'acceptExtensions',\n]\n\nfunction normalizeValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map((item) => normalizeValue(item))\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>)\n .sort()\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = normalizeValue((value as Record<string, unknown>)[key])\n return acc\n }, {})\n }\n return value\n}\n\nfunction configEquals(a: unknown, b: unknown): boolean {\n return JSON.stringify(normalizeValue(a ?? null)) === JSON.stringify(normalizeValue(b ?? null))\n}\n\nexport async function ensureCustomFieldDefinitions(\n em: EntityManager,\n sets: FieldSetInput[],\n scope: EnsureFieldDefinitionsOptions\n): Promise<EnsureFieldDefinitionsResult> {\n let created = 0\n let updated = 0\n let unchanged = 0\n\n for (const set of sets) {\n for (const field of set.fields) {\n const where = {\n entityId: set.entity,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n key: field.key,\n }\n const existing = await em.findOne(CustomFieldDef, where)\n const configJson: Record<string, unknown> = {}\n\n for (const key of CONFIG_PASSTHROUGH_KEYS) {\n const value = field[key]\n if (value !== undefined) configJson[key] = value as unknown\n }\n\n if (!existing) {\n if (!scope.dryRun) {\n await em.persistAndFlush(\n em.create(CustomFieldDef, {\n entityId: set.entity,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n key: field.key,\n kind: field.kind,\n configJson,\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n created++\n continue\n }\n\n const kindChanged = existing.kind !== field.kind\n const configChanged = !configEquals(existing.configJson ?? null, configJson)\n const needsActivation = existing.isActive !== true || existing.deletedAt != null\n if (!kindChanged && !configChanged && !needsActivation) {\n unchanged++\n continue\n }\n\n if (!scope.dryRun) {\n existing.kind = field.kind\n ;(existing as any).configJson = configJson\n existing.isActive = true\n existing.updatedAt = new Date()\n if (existing.deletedAt) existing.deletedAt = null\n await em.flush()\n }\n updated++\n }\n }\n\n return { created, updated, unchanged }\n}\n"],
5
+ "mappings": "AACA,SAAS,sBAAsB;AAqB/B,MAAM,0BAA8D;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,eAAe,OAAyB;AAC/C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,CAAC,SAAS,eAAe,IAAI,CAAC;AACzE,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,WAAO,OAAO,KAAK,KAAgC,EAChD,KAAK,EACL,OAAgC,CAAC,KAAK,QAAQ;AAC7C,UAAI,GAAG,IAAI,eAAgB,MAAkC,GAAG,CAAC;AACjE,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,aAAa,GAAY,GAAqB;AACrD,SAAO,KAAK,UAAU,eAAe,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,eAAe,KAAK,IAAI,CAAC;AAC/F;AAEA,eAAsB,6BACpB,IACA,MACA,OACuC;AACvC,MAAI,UAAU;AACd,MAAI,UAAU;AACd,MAAI,YAAY;AAEhB,aAAW,OAAO,MAAM;AACtB,eAAW,SAAS,IAAI,QAAQ;AAC9B,YAAM,QAAQ;AAAA,QACZ,UAAU,IAAI;AAAA,QACd,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,QAChB,KAAK,MAAM;AAAA,MACb;AACA,YAAM,WAAW,MAAM,GAAG,QAAQ,gBAAgB,KAAK;AACvD,YAAM,aAAsC,CAAC;AAE7C,iBAAW,OAAO,yBAAyB;AACzC,cAAM,QAAQ,MAAM,GAAG;AACvB,YAAI,UAAU,OAAW,YAAW,GAAG,IAAI;AAAA,MAC7C;AAEA,UAAI,CAAC,UAAU;AACb,YAAI,CAAC,MAAM,QAAQ;AACjB,gBAAM,GAAG;AAAA,YACP,GAAG,OAAO,gBAAgB;AAAA,cACxB,UAAU,IAAI;AAAA,cACd,gBAAgB,MAAM;AAAA,cACtB,UAAU,MAAM;AAAA,cAChB,KAAK,MAAM;AAAA,cACX,MAAM,MAAM;AAAA,cACZ;AAAA,cACA,UAAU;AAAA,cACV,WAAW,oBAAI,KAAK;AAAA,cACpB,WAAW,oBAAI,KAAK;AAAA,YACtB,CAAC;AAAA,UACH;AAAA,QACF;AACA;AACA;AAAA,MACF;AAEA,YAAM,cAAc,SAAS,SAAS,MAAM;AAC5C,YAAM,gBAAgB,CAAC,aAAa,SAAS,cAAc,MAAM,UAAU;AAC3E,YAAM,kBAAkB,SAAS,aAAa,QAAQ,SAAS,aAAa;AAC5E,UAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,iBAAiB;AACtD;AACA;AAAA,MACF;AAEA,UAAI,CAAC,MAAM,QAAQ;AACjB,iBAAS,OAAO,MAAM;AACrB,QAAC,SAAiB,aAAa;AAChC,iBAAS,WAAW;AACpB,iBAAS,YAAY,oBAAI,KAAK;AAC9B,YAAI,SAAS,UAAW,UAAS,YAAY;AAC7C,cAAM,GAAG,MAAM;AAAA,MACjB;AACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,SAAS,UAAU;AACvC;",
6
6
  "names": []
7
7
  }
@@ -26,7 +26,7 @@ const FIELD_DETAIL_KEYS = [
26
26
  function normalizeValue(value) {
27
27
  if (Array.isArray(value)) return value.map((item) => normalizeValue(item));
28
28
  if (value && typeof value === "object") {
29
- return Object.keys(value).sort((a, b) => a.localeCompare(b)).reduce((acc, key) => {
29
+ return Object.keys(value).sort().reduce((acc, key) => {
30
30
  acc[key] = normalizeValue(value[key]);
31
31
  return acc;
32
32
  }, {});
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/entities/lib/install-from-ce.ts"],
4
- "sourcesContent": ["import crypto from 'node:crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CacheStrategy } from '@open-mercato/cache/types'\nimport type { CustomFieldDefinition, CustomFieldSet, CustomEntitySpec } from '@open-mercato/shared/modules/entities'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { ensureCustomFieldDefinitions } from './field-definitions'\nimport { upsertCustomEntity, type UpsertCustomEntityResult } from './register'\n\ntype InstallScope = {\n tenantId: string | null\n}\n\nexport type InstallEntitiesOptions = {\n tenantIds?: string[]\n includeGlobal?: boolean\n dryRun?: boolean\n force?: boolean\n logger?: (message: string) => void\n}\n\nexport type InstallEntitiesResult = {\n processed: number\n synchronized: number\n skipped: number\n fieldChanges: number\n}\n\nexport type AggregatedEntityConfig = {\n entityId: string\n moduleIds: Set<string>\n spec?: CustomEntitySpec\n fieldSets: CustomFieldSet[]\n}\n\nconst FIELD_DETAIL_KEYS: Array<keyof CustomFieldDefinition> = [\n 'label',\n 'description',\n 'options',\n 'optionsUrl',\n 'defaultValue',\n 'required',\n 'multi',\n 'filterable',\n 'formEditable',\n 'listVisible',\n 'indexed',\n 'editor',\n 'input',\n 'relatedEntityId',\n 'validation',\n 'maxAttachmentSizeMb',\n 'acceptExtensions',\n]\n\nfunction normalizeValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map((item) => normalizeValue(item))\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>)\n .sort((a, b) => a.localeCompare(b))\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = normalizeValue((value as Record<string, unknown>)[key])\n return acc\n }, {})\n }\n return value\n}\n\nfunction computeChecksum(payload: unknown): string {\n return crypto.createHash('md5').update(JSON.stringify(normalizeValue(payload))).digest('hex')\n}\n\nfunction systemEntityIds(): Set<string> {\n const ids = new Set<string>()\n const GeneratedEntities = getEntityIds()\n for (const moduleEntities of Object.values(GeneratedEntities)) {\n for (const id of Object.values(moduleEntities as Record<string, string>)) {\n ids.add(id)\n }\n }\n return ids\n}\n\nfunction buildAggregatedConfigs(): AggregatedEntityConfig[] {\n const map = new Map<string, AggregatedEntityConfig>()\n const modules = getModules()\n for (const mod of modules) {\n const moduleId = mod.id\n const entitySpecs = ((mod as any).customEntities as CustomEntitySpec[] | undefined) ?? []\n for (const spec of entitySpecs) {\n const existing = map.get(spec.id) ?? { entityId: spec.id, moduleIds: new Set<string>(), spec: undefined, fieldSets: [] }\n existing.moduleIds.add(moduleId)\n if (!existing.spec) existing.spec = spec\n map.set(spec.id, existing)\n }\n const fieldSets = ((mod as any).customFieldSets as CustomFieldSet[] | undefined) ?? []\n for (const set of fieldSets) {\n const existing = map.get(set.entity) ?? { entityId: set.entity, moduleIds: new Set<string>(), spec: undefined, fieldSets: [] }\n existing.moduleIds.add(moduleId)\n existing.fieldSets.push(set)\n map.set(set.entity, existing)\n }\n }\n return Array.from(map.values())\n}\n\nfunction resolveFields(fieldSets: CustomFieldSet[]): CustomFieldDefinition[] {\n const byKey = new Map<string, CustomFieldDefinition>()\n for (const set of fieldSets) {\n for (const field of set.fields ?? []) {\n byKey.set(field.key, { ...field })\n }\n }\n return Array.from(byKey.values()).sort((a, b) => a.key.localeCompare(b.key))\n}\n\nfunction normalizeField(field: CustomFieldDefinition) {\n const payload: Record<string, unknown> = {\n key: field.key,\n kind: field.kind,\n }\n for (const key of FIELD_DETAIL_KEYS) {\n const value = field[key]\n if (value !== undefined) payload[key] = value as unknown\n }\n if (field.id) payload.id = field.id\n return payload\n}\n\nfunction buildChecksumPayload(params: {\n entityId: string\n scope: InstallScope\n spec: CustomEntitySpec | undefined\n global: boolean\n fields: CustomFieldDefinition[]\n}) {\n const { entityId, scope, spec, global, fields } = params\n return {\n entityId,\n scope,\n global,\n label: spec?.label ?? null,\n description: spec?.description ?? null,\n labelField: spec?.labelField ?? null,\n defaultEditor: spec?.defaultEditor ?? null,\n showInSidebar: spec?.showInSidebar ?? false,\n fields: fields.map((f) => normalizeField(f)),\n }\n}\n\nexport async function installCustomEntitiesFromModules(\n em: EntityManager,\n cache: CacheStrategy | null | undefined,\n options: InstallEntitiesOptions = {}\n): Promise<InstallEntitiesResult> {\n const aggregated = buildAggregatedConfigs()\n const systemIds = systemEntityIds()\n const includeGlobal = options.includeGlobal !== false\n const dryRun = options.dryRun === true\n const force = options.force === true\n const logger = options.logger\n\n let tenantIds: string[] | undefined\n if (options.tenantIds !== undefined) {\n tenantIds = Array.from(new Set(options.tenantIds.filter((id): id is string => typeof id === 'string' && id.length > 0)))\n }\n\n const ensureTenantIds = async (): Promise<string[]> => {\n if (tenantIds !== undefined) return tenantIds\n const rows = await em.find(Tenant, { deletedAt: null } as any, { fields: ['id'] as any })\n tenantIds = rows.map((row) => row.id)\n return tenantIds ?? []\n }\n\n let processed = 0\n let synchronized = 0\n let skipped = 0\n let fieldChanges = 0\n\n for (const entry of aggregated) {\n const { entityId } = entry\n const spec = entry.spec\n const fields = resolveFields(entry.fieldSets)\n const isSystem = systemIds.has(entityId)\n const registerEntity = !isSystem && !!spec\n const isGlobal = spec?.global === true\n\n const scopes: InstallScope[] = []\n if (isGlobal) {\n if (includeGlobal) scopes.push({ tenantId: null })\n } else {\n const ids = await ensureTenantIds()\n if (!ids.length) {\n skipped++\n continue\n }\n for (const tenantId of ids) scopes.push({ tenantId })\n }\n\n if (!scopes.length && fields.length === 0 && !registerEntity) {\n skipped++\n continue\n }\n\n for (const scope of scopes) {\n processed++\n const scopeKey = scope.tenantId ? `tenant:${scope.tenantId}` : 'global'\n const checksumPayload = buildChecksumPayload({\n entityId,\n scope,\n spec,\n global: isGlobal,\n fields,\n })\n const checksum = computeChecksum(checksumPayload)\n const cacheKey = `custom-entities:v1:${scopeKey}:${entityId}`\n\n if (!dryRun && !force && cache) {\n try {\n const cached = await cache.get(cacheKey)\n if (typeof cached === 'string' && cached === checksum) {\n skipped++\n continue\n }\n } catch {}\n }\n\n let entityResult: UpsertCustomEntityResult = 'unchanged'\n if (registerEntity) {\n entityResult = await upsertCustomEntity(em, entityId, {\n label: spec?.label ?? entityId,\n description: spec?.description ?? null,\n organizationId: null,\n tenantId: scope.tenantId,\n showInSidebar: spec?.showInSidebar ?? false,\n labelField: spec?.labelField ?? null,\n defaultEditor: spec?.defaultEditor ?? null,\n isActive: true,\n dryRun,\n })\n }\n\n let fieldResult = { created: 0, updated: 0, unchanged: 0 }\n if (fields.length) {\n fieldResult = await ensureCustomFieldDefinitions(\n em,\n [{ entity: entityId, fields }],\n { organizationId: null, tenantId: scope.tenantId, dryRun }\n )\n }\n\n const changed = (entityResult !== 'unchanged') || fieldResult.created > 0 || fieldResult.updated > 0\n if (changed) {\n synchronized++\n fieldChanges += fieldResult.created + fieldResult.updated\n if (!dryRun && cache) {\n try {\n await cache.set(cacheKey, checksum, { tags: [`custom-entity:${entityId}`, `custom-entity-scope:${scopeKey}`] })\n } catch {}\n }\n if (logger) {\n const parts: string[] = []\n if (entityResult !== 'unchanged') parts.push(`entity ${entityResult}`)\n if (fieldResult.created || fieldResult.updated) {\n parts.push(`fields +${fieldResult.created} / ~${fieldResult.updated}`)\n }\n logger(`Synced ${entityId} for ${scopeKey}${parts.length ? ` (${parts.join(', ')})` : ''}`)\n }\n } else {\n skipped++\n if (!dryRun && cache) {\n try {\n await cache.set(cacheKey, checksum, { tags: [`custom-entity:${entityId}`, `custom-entity-scope:${scopeKey}`] })\n } catch {}\n }\n }\n }\n }\n\n return { processed, synchronized, skipped, fieldChanges }\n}\n\nexport function listCustomEntityIds(): string[] {\n return buildAggregatedConfigs().map((entry) => entry.entityId)\n}\n\nexport function getAggregatedCustomEntityConfigs(): AggregatedEntityConfig[] {\n return buildAggregatedConfigs()\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import crypto from 'node:crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CacheStrategy } from '@open-mercato/cache/types'\nimport type { CustomFieldDefinition, CustomFieldSet, CustomEntitySpec } from '@open-mercato/shared/modules/entities'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { ensureCustomFieldDefinitions } from './field-definitions'\nimport { upsertCustomEntity, type UpsertCustomEntityResult } from './register'\n\ntype InstallScope = {\n tenantId: string | null\n}\n\nexport type InstallEntitiesOptions = {\n tenantIds?: string[]\n includeGlobal?: boolean\n dryRun?: boolean\n force?: boolean\n logger?: (message: string) => void\n}\n\nexport type InstallEntitiesResult = {\n processed: number\n synchronized: number\n skipped: number\n fieldChanges: number\n}\n\nexport type AggregatedEntityConfig = {\n entityId: string\n moduleIds: Set<string>\n spec?: CustomEntitySpec\n fieldSets: CustomFieldSet[]\n}\n\nconst FIELD_DETAIL_KEYS: Array<keyof CustomFieldDefinition> = [\n 'label',\n 'description',\n 'options',\n 'optionsUrl',\n 'defaultValue',\n 'required',\n 'multi',\n 'filterable',\n 'formEditable',\n 'listVisible',\n 'indexed',\n 'editor',\n 'input',\n 'relatedEntityId',\n 'validation',\n 'maxAttachmentSizeMb',\n 'acceptExtensions',\n]\n\nfunction normalizeValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map((item) => normalizeValue(item))\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>)\n .sort()\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = normalizeValue((value as Record<string, unknown>)[key])\n return acc\n }, {})\n }\n return value\n}\n\nfunction computeChecksum(payload: unknown): string {\n return crypto.createHash('md5').update(JSON.stringify(normalizeValue(payload))).digest('hex')\n}\n\nfunction systemEntityIds(): Set<string> {\n const ids = new Set<string>()\n const GeneratedEntities = getEntityIds()\n for (const moduleEntities of Object.values(GeneratedEntities)) {\n for (const id of Object.values(moduleEntities as Record<string, string>)) {\n ids.add(id)\n }\n }\n return ids\n}\n\nfunction buildAggregatedConfigs(): AggregatedEntityConfig[] {\n const map = new Map<string, AggregatedEntityConfig>()\n const modules = getModules()\n for (const mod of modules) {\n const moduleId = mod.id\n const entitySpecs = ((mod as any).customEntities as CustomEntitySpec[] | undefined) ?? []\n for (const spec of entitySpecs) {\n const existing = map.get(spec.id) ?? { entityId: spec.id, moduleIds: new Set<string>(), spec: undefined, fieldSets: [] }\n existing.moduleIds.add(moduleId)\n if (!existing.spec) existing.spec = spec\n map.set(spec.id, existing)\n }\n const fieldSets = ((mod as any).customFieldSets as CustomFieldSet[] | undefined) ?? []\n for (const set of fieldSets) {\n const existing = map.get(set.entity) ?? { entityId: set.entity, moduleIds: new Set<string>(), spec: undefined, fieldSets: [] }\n existing.moduleIds.add(moduleId)\n existing.fieldSets.push(set)\n map.set(set.entity, existing)\n }\n }\n return Array.from(map.values())\n}\n\nfunction resolveFields(fieldSets: CustomFieldSet[]): CustomFieldDefinition[] {\n const byKey = new Map<string, CustomFieldDefinition>()\n for (const set of fieldSets) {\n for (const field of set.fields ?? []) {\n byKey.set(field.key, { ...field })\n }\n }\n return Array.from(byKey.values()).sort((a, b) => a.key.localeCompare(b.key))\n}\n\nfunction normalizeField(field: CustomFieldDefinition) {\n const payload: Record<string, unknown> = {\n key: field.key,\n kind: field.kind,\n }\n for (const key of FIELD_DETAIL_KEYS) {\n const value = field[key]\n if (value !== undefined) payload[key] = value as unknown\n }\n if (field.id) payload.id = field.id\n return payload\n}\n\nfunction buildChecksumPayload(params: {\n entityId: string\n scope: InstallScope\n spec: CustomEntitySpec | undefined\n global: boolean\n fields: CustomFieldDefinition[]\n}) {\n const { entityId, scope, spec, global, fields } = params\n return {\n entityId,\n scope,\n global,\n label: spec?.label ?? null,\n description: spec?.description ?? null,\n labelField: spec?.labelField ?? null,\n defaultEditor: spec?.defaultEditor ?? null,\n showInSidebar: spec?.showInSidebar ?? false,\n fields: fields.map((f) => normalizeField(f)),\n }\n}\n\nexport async function installCustomEntitiesFromModules(\n em: EntityManager,\n cache: CacheStrategy | null | undefined,\n options: InstallEntitiesOptions = {}\n): Promise<InstallEntitiesResult> {\n const aggregated = buildAggregatedConfigs()\n const systemIds = systemEntityIds()\n const includeGlobal = options.includeGlobal !== false\n const dryRun = options.dryRun === true\n const force = options.force === true\n const logger = options.logger\n\n let tenantIds: string[] | undefined\n if (options.tenantIds !== undefined) {\n tenantIds = Array.from(new Set(options.tenantIds.filter((id): id is string => typeof id === 'string' && id.length > 0)))\n }\n\n const ensureTenantIds = async (): Promise<string[]> => {\n if (tenantIds !== undefined) return tenantIds\n const rows = await em.find(Tenant, { deletedAt: null } as any, { fields: ['id'] as any })\n tenantIds = rows.map((row) => row.id)\n return tenantIds ?? []\n }\n\n let processed = 0\n let synchronized = 0\n let skipped = 0\n let fieldChanges = 0\n\n for (const entry of aggregated) {\n const { entityId } = entry\n const spec = entry.spec\n const fields = resolveFields(entry.fieldSets)\n const isSystem = systemIds.has(entityId)\n const registerEntity = !isSystem && !!spec\n const isGlobal = spec?.global === true\n\n const scopes: InstallScope[] = []\n if (isGlobal) {\n if (includeGlobal) scopes.push({ tenantId: null })\n } else {\n const ids = await ensureTenantIds()\n if (!ids.length) {\n skipped++\n continue\n }\n for (const tenantId of ids) scopes.push({ tenantId })\n }\n\n if (!scopes.length && fields.length === 0 && !registerEntity) {\n skipped++\n continue\n }\n\n for (const scope of scopes) {\n processed++\n const scopeKey = scope.tenantId ? `tenant:${scope.tenantId}` : 'global'\n const checksumPayload = buildChecksumPayload({\n entityId,\n scope,\n spec,\n global: isGlobal,\n fields,\n })\n const checksum = computeChecksum(checksumPayload)\n const cacheKey = `custom-entities:v1:${scopeKey}:${entityId}`\n\n if (!dryRun && !force && cache) {\n try {\n const cached = await cache.get(cacheKey)\n if (typeof cached === 'string' && cached === checksum) {\n skipped++\n continue\n }\n } catch {}\n }\n\n let entityResult: UpsertCustomEntityResult = 'unchanged'\n if (registerEntity) {\n entityResult = await upsertCustomEntity(em, entityId, {\n label: spec?.label ?? entityId,\n description: spec?.description ?? null,\n organizationId: null,\n tenantId: scope.tenantId,\n showInSidebar: spec?.showInSidebar ?? false,\n labelField: spec?.labelField ?? null,\n defaultEditor: spec?.defaultEditor ?? null,\n isActive: true,\n dryRun,\n })\n }\n\n let fieldResult = { created: 0, updated: 0, unchanged: 0 }\n if (fields.length) {\n fieldResult = await ensureCustomFieldDefinitions(\n em,\n [{ entity: entityId, fields }],\n { organizationId: null, tenantId: scope.tenantId, dryRun }\n )\n }\n\n const changed = (entityResult !== 'unchanged') || fieldResult.created > 0 || fieldResult.updated > 0\n if (changed) {\n synchronized++\n fieldChanges += fieldResult.created + fieldResult.updated\n if (!dryRun && cache) {\n try {\n await cache.set(cacheKey, checksum, { tags: [`custom-entity:${entityId}`, `custom-entity-scope:${scopeKey}`] })\n } catch {}\n }\n if (logger) {\n const parts: string[] = []\n if (entityResult !== 'unchanged') parts.push(`entity ${entityResult}`)\n if (fieldResult.created || fieldResult.updated) {\n parts.push(`fields +${fieldResult.created} / ~${fieldResult.updated}`)\n }\n logger(`Synced ${entityId} for ${scopeKey}${parts.length ? ` (${parts.join(', ')})` : ''}`)\n }\n } else {\n skipped++\n if (!dryRun && cache) {\n try {\n await cache.set(cacheKey, checksum, { tags: [`custom-entity:${entityId}`, `custom-entity-scope:${scopeKey}`] })\n } catch {}\n }\n }\n }\n }\n\n return { processed, synchronized, skipped, fieldChanges }\n}\n\nexport function listCustomEntityIds(): string[] {\n return buildAggregatedConfigs().map((entry) => entry.entityId)\n}\n\nexport function getAggregatedCustomEntityConfigs(): AggregatedEntityConfig[] {\n return buildAggregatedConfigs()\n}\n"],
5
+ "mappings": "AAAA,OAAO,YAAY;AAInB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,oCAAoC;AAC7C,SAAS,0BAAyD;AA4BlE,MAAM,oBAAwD;AAAA,EAC5D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,eAAe,OAAyB;AAC/C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,CAAC,SAAS,eAAe,IAAI,CAAC;AACzE,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,WAAO,OAAO,KAAK,KAAgC,EAChD,KAAK,EACL,OAAgC,CAAC,KAAK,QAAQ;AAC7C,UAAI,GAAG,IAAI,eAAgB,MAAkC,GAAG,CAAC;AACjE,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,SAA0B;AACjD,SAAO,OAAO,WAAW,KAAK,EAAE,OAAO,KAAK,UAAU,eAAe,OAAO,CAAC,CAAC,EAAE,OAAO,KAAK;AAC9F;AAEA,SAAS,kBAA+B;AACtC,QAAM,MAAM,oBAAI,IAAY;AAC5B,QAAM,oBAAoB,aAAa;AACvC,aAAW,kBAAkB,OAAO,OAAO,iBAAiB,GAAG;AAC7D,eAAW,MAAM,OAAO,OAAO,cAAwC,GAAG;AACxE,UAAI,IAAI,EAAE;AAAA,IACZ;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,yBAAmD;AAC1D,QAAM,MAAM,oBAAI,IAAoC;AACpD,QAAM,UAAU,WAAW;AAC3B,aAAW,OAAO,SAAS;AACzB,UAAM,WAAW,IAAI;AACrB,UAAM,cAAgB,IAAY,kBAAqD,CAAC;AACxF,eAAW,QAAQ,aAAa;AAC9B,YAAM,WAAW,IAAI,IAAI,KAAK,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,WAAW,oBAAI,IAAY,GAAG,MAAM,QAAW,WAAW,CAAC,EAAE;AACvH,eAAS,UAAU,IAAI,QAAQ;AAC/B,UAAI,CAAC,SAAS,KAAM,UAAS,OAAO;AACpC,UAAI,IAAI,KAAK,IAAI,QAAQ;AAAA,IAC3B;AACA,UAAM,YAAc,IAAY,mBAAoD,CAAC;AACrF,eAAW,OAAO,WAAW;AAC3B,YAAM,WAAW,IAAI,IAAI,IAAI,MAAM,KAAK,EAAE,UAAU,IAAI,QAAQ,WAAW,oBAAI,IAAY,GAAG,MAAM,QAAW,WAAW,CAAC,EAAE;AAC7H,eAAS,UAAU,IAAI,QAAQ;AAC/B,eAAS,UAAU,KAAK,GAAG;AAC3B,UAAI,IAAI,IAAI,QAAQ,QAAQ;AAAA,IAC9B;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI,OAAO,CAAC;AAChC;AAEA,SAAS,cAAc,WAAsD;AAC3E,QAAM,QAAQ,oBAAI,IAAmC;AACrD,aAAW,OAAO,WAAW;AAC3B,eAAW,SAAS,IAAI,UAAU,CAAC,GAAG;AACpC,YAAM,IAAI,MAAM,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,IACnC;AAAA,EACF;AACA,SAAO,MAAM,KAAK,MAAM,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,cAAc,EAAE,GAAG,CAAC;AAC7E;AAEA,SAAS,eAAe,OAA8B;AACpD,QAAM,UAAmC;AAAA,IACvC,KAAK,MAAM;AAAA,IACX,MAAM,MAAM;AAAA,EACd;AACA,aAAW,OAAO,mBAAmB;AACnC,UAAM,QAAQ,MAAM,GAAG;AACvB,QAAI,UAAU,OAAW,SAAQ,GAAG,IAAI;AAAA,EAC1C;AACA,MAAI,MAAM,GAAI,SAAQ,KAAK,MAAM;AACjC,SAAO;AACT;AAEA,SAAS,qBAAqB,QAM3B;AACD,QAAM,EAAE,UAAU,OAAO,MAAM,QAAQ,OAAO,IAAI;AAClD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,MAAM,SAAS;AAAA,IACtB,aAAa,MAAM,eAAe;AAAA,IAClC,YAAY,MAAM,cAAc;AAAA,IAChC,eAAe,MAAM,iBAAiB;AAAA,IACtC,eAAe,MAAM,iBAAiB;AAAA,IACtC,QAAQ,OAAO,IAAI,CAAC,MAAM,eAAe,CAAC,CAAC;AAAA,EAC7C;AACF;AAEA,eAAsB,iCACpB,IACA,OACA,UAAkC,CAAC,GACH;AAChC,QAAM,aAAa,uBAAuB;AAC1C,QAAM,YAAY,gBAAgB;AAClC,QAAM,gBAAgB,QAAQ,kBAAkB;AAChD,QAAM,SAAS,QAAQ,WAAW;AAClC,QAAM,QAAQ,QAAQ,UAAU;AAChC,QAAM,SAAS,QAAQ;AAEvB,MAAI;AACJ,MAAI,QAAQ,cAAc,QAAW;AACnC,gBAAY,MAAM,KAAK,IAAI,IAAI,QAAQ,UAAU,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC;AAAA,EACzH;AAEA,QAAM,kBAAkB,YAA+B;AACrD,QAAI,cAAc,OAAW,QAAO;AACpC,UAAM,OAAO,MAAM,GAAG,KAAK,QAAQ,EAAE,WAAW,KAAK,GAAU,EAAE,QAAQ,CAAC,IAAI,EAAS,CAAC;AACxF,gBAAY,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE;AACpC,WAAO,aAAa,CAAC;AAAA,EACvB;AAEA,MAAI,YAAY;AAChB,MAAI,eAAe;AACnB,MAAI,UAAU;AACd,MAAI,eAAe;AAEnB,aAAW,SAAS,YAAY;AAC9B,UAAM,EAAE,SAAS,IAAI;AACrB,UAAM,OAAO,MAAM;AACnB,UAAM,SAAS,cAAc,MAAM,SAAS;AAC5C,UAAM,WAAW,UAAU,IAAI,QAAQ;AACvC,UAAM,iBAAiB,CAAC,YAAY,CAAC,CAAC;AACtC,UAAM,WAAW,MAAM,WAAW;AAElC,UAAM,SAAyB,CAAC;AAChC,QAAI,UAAU;AACZ,UAAI,cAAe,QAAO,KAAK,EAAE,UAAU,KAAK,CAAC;AAAA,IACnD,OAAO;AACL,YAAM,MAAM,MAAM,gBAAgB;AAClC,UAAI,CAAC,IAAI,QAAQ;AACf;AACA;AAAA,MACF;AACA,iBAAW,YAAY,IAAK,QAAO,KAAK,EAAE,SAAS,CAAC;AAAA,IACtD;AAEA,QAAI,CAAC,OAAO,UAAU,OAAO,WAAW,KAAK,CAAC,gBAAgB;AAC5D;AACA;AAAA,IACF;AAEA,eAAW,SAAS,QAAQ;AAC1B;AACA,YAAM,WAAW,MAAM,WAAW,UAAU,MAAM,QAAQ,KAAK;AAC/D,YAAM,kBAAkB,qBAAqB;AAAA,QAC3C;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AACD,YAAM,WAAW,gBAAgB,eAAe;AAChD,YAAM,WAAW,sBAAsB,QAAQ,IAAI,QAAQ;AAE3D,UAAI,CAAC,UAAU,CAAC,SAAS,OAAO;AAC9B,YAAI;AACF,gBAAM,SAAS,MAAM,MAAM,IAAI,QAAQ;AACvC,cAAI,OAAO,WAAW,YAAY,WAAW,UAAU;AACrD;AACA;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAAC;AAAA,MACX;AAEA,UAAI,eAAyC;AAC7C,UAAI,gBAAgB;AAClB,uBAAe,MAAM,mBAAmB,IAAI,UAAU;AAAA,UACpD,OAAO,MAAM,SAAS;AAAA,UACtB,aAAa,MAAM,eAAe;AAAA,UAClC,gBAAgB;AAAA,UAChB,UAAU,MAAM;AAAA,UAChB,eAAe,MAAM,iBAAiB;AAAA,UACtC,YAAY,MAAM,cAAc;AAAA,UAChC,eAAe,MAAM,iBAAiB;AAAA,UACtC,UAAU;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH;AAEA,UAAI,cAAc,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,EAAE;AACzD,UAAI,OAAO,QAAQ;AACjB,sBAAc,MAAM;AAAA,UAClB;AAAA,UACA,CAAC,EAAE,QAAQ,UAAU,OAAO,CAAC;AAAA,UAC7B,EAAE,gBAAgB,MAAM,UAAU,MAAM,UAAU,OAAO;AAAA,QAC3D;AAAA,MACF;AAEA,YAAM,UAAW,iBAAiB,eAAgB,YAAY,UAAU,KAAK,YAAY,UAAU;AACnG,UAAI,SAAS;AACX;AACA,wBAAgB,YAAY,UAAU,YAAY;AAClD,YAAI,CAAC,UAAU,OAAO;AACpB,cAAI;AACF,kBAAM,MAAM,IAAI,UAAU,UAAU,EAAE,MAAM,CAAC,iBAAiB,QAAQ,IAAI,uBAAuB,QAAQ,EAAE,EAAE,CAAC;AAAA,UAChH,QAAQ;AAAA,UAAC;AAAA,QACX;AACA,YAAI,QAAQ;AACV,gBAAM,QAAkB,CAAC;AACzB,cAAI,iBAAiB,YAAa,OAAM,KAAK,UAAU,YAAY,EAAE;AACrE,cAAI,YAAY,WAAW,YAAY,SAAS;AAC9C,kBAAM,KAAK,WAAW,YAAY,OAAO,OAAO,YAAY,OAAO,EAAE;AAAA,UACvE;AACA,iBAAO,UAAU,QAAQ,QAAQ,QAAQ,GAAG,MAAM,SAAS,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,QAC5F;AAAA,MACF,OAAO;AACL;AACA,YAAI,CAAC,UAAU,OAAO;AACpB,cAAI;AACF,kBAAM,MAAM,IAAI,UAAU,UAAU,EAAE,MAAM,CAAC,iBAAiB,QAAQ,IAAI,uBAAuB,QAAQ,EAAE,EAAE,CAAC;AAAA,UAChH,QAAQ;AAAA,UAAC;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,WAAW,cAAc,SAAS,aAAa;AAC1D;AAEO,SAAS,sBAAgC;AAC9C,SAAO,uBAAuB,EAAE,IAAI,CAAC,UAAU,MAAM,QAAQ;AAC/D;AAEO,SAAS,mCAA6D;AAC3E,SAAO,uBAAuB;AAChC;",
6
6
  "names": []
7
7
  }
@@ -72,7 +72,7 @@ function parseAddressField(value) {
72
72
  if (!value) return { email: "" };
73
73
  const match = value.match(/^(.+?)\s*<([^>]+)>$/);
74
74
  if (match) {
75
- return { name: match[1].trim().replace(/(?:^["']|["']$)/g, ""), email: match[2].trim().toLowerCase() };
75
+ return { name: match[1].trim().replace(/^["']|["']$/g, ""), email: match[2].trim().toLowerCase() };
76
76
  }
77
77
  return { email: value.trim().toLowerCase() };
78
78
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/inbox_ops/lib/emailParser.ts"],
4
- "sourcesContent": ["import { createHash } from 'node:crypto'\nimport type { InboxEmail, ThreadMessage } from '../data/entities'\nimport { htmlToPlainText } from './htmlToPlainText'\n\nexport interface ParsedEmail {\n messageId?: string | null\n from: { name?: string; email: string }\n to: { name?: string; email: string }[]\n subject: string\n replyTo?: string | null\n inReplyTo?: string | null\n references?: string[] | null\n rawText?: string | null\n rawHtml?: string | null\n cleanedText: string\n threadMessages: ThreadMessage[]\n detectedLanguage?: string | null\n contentHash: string\n}\n\nconst SIGNATURE_PATTERNS = [\n /^--\\s*$/m,\n /^Sent from my (iPhone|iPad|Android|Galaxy|Samsung|Pixel)/m,\n /^Get Outlook for/m,\n /^_{10,}/m,\n /^Regards,?\\s*$/m,\n /^Best,?\\s*$/m,\n /^Thanks,?\\s*$/m,\n /^Cheers,?\\s*$/m,\n /^Kind regards,?\\s*$/m,\n /^Best regards,?\\s*$/m,\n]\n\nconst QUOTE_PATTERNS = [\n /^On .+ wrote:\\s*$/m,\n /^>+\\s/m,\n /^From:\\s/m,\n /^-{3,}\\s*Original Message\\s*-{3,}/m,\n /^-{3,}\\s*Forwarded message\\s*-{3,}/m,\n /^Begin forwarded message:/m,\n]\n\nconst DATE_HEADER_PATTERN = /^Date:\\s*(.+)$/m\n\nfunction stripSignature(text: string): string {\n const lines = text.split('\\n')\n let cutIndex = lines.length\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]\n if (SIGNATURE_PATTERNS.some((p) => p.test(line))) {\n cutIndex = i\n break\n }\n }\n return lines.slice(0, cutIndex).join('\\n').trimEnd()\n}\n\nfunction stripQuotedReplies(text: string): string {\n const lines = text.split('\\n')\n const cleanLines: string[] = []\n let inQuote = false\n\n for (const line of lines) {\n if (QUOTE_PATTERNS.some((p) => p.test(line))) {\n inQuote = true\n continue\n }\n if (inQuote && line.startsWith('>')) {\n continue\n }\n if (inQuote && line.trim() === '') {\n continue\n }\n if (inQuote && !line.startsWith('>') && line.trim().length > 0) {\n inQuote = false\n }\n if (!inQuote) {\n cleanLines.push(line)\n }\n }\n\n return cleanLines.join('\\n').trimEnd()\n}\n\nfunction stripHtml(html: string): string {\n return htmlToPlainText(html)\n}\n\nfunction normalizeText(text: string): string {\n return text\n .replace(/\\r\\n/g, '\\n')\n .replace(/\\r/g, '\\n')\n .replace(/\\t/g, ' ')\n .replace(/ {2,}/g, ' ')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n}\n\nfunction generateContentHash(subject: string, from: string, text: string): string {\n const input = `${subject}|${from}|${text.slice(0, 500)}`\n const normalized = input.toLowerCase().replace(/\\s+/g, ' ').trim()\n return createHash('sha256').update(normalized).digest('hex')\n}\n\nfunction parseAddressField(value: string | undefined | null): { name?: string; email: string } {\n if (!value) return { email: '' }\n const match = value.match(/^(.+?)\\s*<([^>]+)>$/)\n if (match) {\n return { name: match[1].trim().replace(/(?:^[\"']|[\"']$)/g, ''), email: match[2].trim().toLowerCase() }\n }\n return { email: value.trim().toLowerCase() }\n}\n\nfunction parseAddressListField(value: string | string[] | undefined | null): { name?: string; email: string }[] {\n if (!value) return []\n const list = Array.isArray(value) ? value : value.split(',')\n return list.map((v) => parseAddressField(v.trim())).filter((a) => a.email)\n}\n\nfunction parseDateFromBlock(block: string): string {\n const match = block.match(DATE_HEADER_PATTERN)\n if (match) {\n const parsed = new Date(match[1].trim())\n if (!Number.isNaN(parsed.getTime())) {\n return parsed.toISOString()\n }\n }\n return new Date().toISOString()\n}\n\nfunction parseInlineHeaders(block: string): {\n from: { name?: string; email: string }\n to: { name?: string; email: string }[]\n subject?: string\n bodyStart: number\n} {\n const lines = block.split('\\n')\n let from: { name?: string; email: string } = { email: '' }\n let to: { name?: string; email: string }[] = []\n let subject: string | undefined\n let lastHeaderLine = -1\n\n for (let i = 0; i < lines.length && i < 10; i++) {\n const line = lines[i]\n const fromMatch = line.match(/^From:\\s*(.+)$/i)\n if (fromMatch) {\n from = parseAddressField(fromMatch[1].trim())\n lastHeaderLine = i\n continue\n }\n const toMatch = line.match(/^To:\\s*(.+)$/i)\n if (toMatch) {\n to = parseAddressListField(toMatch[1].trim())\n lastHeaderLine = i\n continue\n }\n const subjectMatch = line.match(/^Subject:\\s*(.+)$/i)\n if (subjectMatch) {\n subject = subjectMatch[1].trim()\n lastHeaderLine = i\n continue\n }\n if (line.match(/^Date:\\s/i) || line.match(/^CC:\\s/i)) {\n lastHeaderLine = i\n continue\n }\n if (lastHeaderLine >= 0 && line.trim() === '') {\n lastHeaderLine = i\n break\n }\n if (lastHeaderLine >= 0) break\n }\n\n return { from, to, subject, bodyStart: lastHeaderLine + 1 }\n}\n\nfunction splitThread(text: string): ThreadMessage[] {\n const separator = /(?:^|\\n)(?:-{3,}\\s*(?:Original Message|Forwarded message)\\s*-{3,}|(?:On .+ wrote:))\\s*\\n/gm\n const parts = text.split(separator).filter((p) => p.trim())\n\n if (parts.length <= 1) {\n return [{\n from: { email: '' },\n to: [],\n date: new Date().toISOString(),\n body: normalizeText(text),\n contentType: 'text',\n isForwarded: false,\n }]\n }\n\n return parts.map((part, index) => {\n if (index === 0) {\n return {\n from: { email: '' } as { name?: string; email: string },\n to: [] as { name?: string; email: string }[],\n date: parseDateFromBlock(part),\n body: normalizeText(part),\n contentType: 'text' as const,\n isForwarded: false,\n }\n }\n\n const headers = parseInlineHeaders(part)\n const bodyLines = part.split('\\n').slice(headers.bodyStart)\n return {\n from: headers.from,\n to: headers.to,\n subject: headers.subject,\n date: parseDateFromBlock(part),\n body: normalizeText(bodyLines.join('\\n')),\n contentType: 'text' as const,\n isForwarded: true,\n }\n })\n}\n\nexport function parseInboundEmail(payload: {\n from?: string\n to?: string | string[]\n subject?: string\n text?: string\n html?: string\n messageId?: string\n replyTo?: string\n inReplyTo?: string\n references?: string | string[]\n}): ParsedEmail {\n const fromParsed = parseAddressField(payload.from)\n const toParsed = parseAddressListField(payload.to)\n const subject = payload.subject?.trim() || '(no subject)'\n\n const rawText = payload.text || null\n const rawHtml = payload.html || null\n\n let textContent = rawText || ''\n if (!textContent && rawHtml) {\n textContent = stripHtml(rawHtml)\n }\n\n const normalized = normalizeText(textContent)\n const withoutSignature = stripSignature(normalized)\n const cleanedText = stripQuotedReplies(withoutSignature)\n\n const threadMessages = splitThread(normalized)\n if (threadMessages.length > 0 && fromParsed.email) {\n threadMessages[0].from = fromParsed\n threadMessages[0].to = toParsed\n threadMessages[0].subject = subject\n }\n\n const contentHash = generateContentHash(\n subject,\n fromParsed.email,\n textContent,\n )\n\n const references = payload.references\n ? (Array.isArray(payload.references) ? payload.references : payload.references.split(/\\s+/))\n : null\n\n return {\n messageId: payload.messageId || null,\n from: fromParsed,\n to: toParsed,\n subject,\n replyTo: payload.replyTo || null,\n inReplyTo: payload.inReplyTo || null,\n references,\n rawText,\n rawHtml,\n cleanedText,\n threadMessages,\n // TODO: Phase 2 \u2014 detect language via LLM or `franc` library\n detectedLanguage: null,\n contentHash,\n }\n}\n\nexport function extractParticipantsFromThread(\n email: InboxEmail,\n): { name: string; email: string; role: string }[] {\n const seen = new Set<string>()\n const participants: { name: string; email: string; role: string }[] = []\n\n // Pre-seed with the inbox's own address so it's excluded from participants\n const inboxAddress = (email.toAddress || '').toLowerCase()\n if (inboxAddress) seen.add(inboxAddress)\n\n const addParticipant = (name: string, addr: string, role: string) => {\n const key = addr.toLowerCase()\n if (!key || seen.has(key)) return\n seen.add(key)\n participants.push({ name, email: key, role })\n }\n\n if (email.threadMessages) {\n for (const msg of email.threadMessages) {\n if (msg.from?.email) {\n addParticipant(msg.from.name || '', msg.from.email, 'other')\n }\n if (msg.to) {\n for (const to of msg.to) {\n addParticipant(to.name || '', to.email, 'other')\n }\n }\n if (msg.cc) {\n for (const cc of msg.cc) {\n addParticipant(cc.name || '', cc.email, 'other')\n }\n }\n }\n }\n\n if (email.forwardedByAddress) {\n addParticipant(email.forwardedByName || '', email.forwardedByAddress, 'seller')\n }\n\n return participants\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { createHash } from 'node:crypto'\nimport type { InboxEmail, ThreadMessage } from '../data/entities'\nimport { htmlToPlainText } from './htmlToPlainText'\n\nexport interface ParsedEmail {\n messageId?: string | null\n from: { name?: string; email: string }\n to: { name?: string; email: string }[]\n subject: string\n replyTo?: string | null\n inReplyTo?: string | null\n references?: string[] | null\n rawText?: string | null\n rawHtml?: string | null\n cleanedText: string\n threadMessages: ThreadMessage[]\n detectedLanguage?: string | null\n contentHash: string\n}\n\nconst SIGNATURE_PATTERNS = [\n /^--\\s*$/m,\n /^Sent from my (iPhone|iPad|Android|Galaxy|Samsung|Pixel)/m,\n /^Get Outlook for/m,\n /^_{10,}/m,\n /^Regards,?\\s*$/m,\n /^Best,?\\s*$/m,\n /^Thanks,?\\s*$/m,\n /^Cheers,?\\s*$/m,\n /^Kind regards,?\\s*$/m,\n /^Best regards,?\\s*$/m,\n]\n\nconst QUOTE_PATTERNS = [\n /^On .+ wrote:\\s*$/m,\n /^>+\\s/m,\n /^From:\\s/m,\n /^-{3,}\\s*Original Message\\s*-{3,}/m,\n /^-{3,}\\s*Forwarded message\\s*-{3,}/m,\n /^Begin forwarded message:/m,\n]\n\nconst DATE_HEADER_PATTERN = /^Date:\\s*(.+)$/m\n\nfunction stripSignature(text: string): string {\n const lines = text.split('\\n')\n let cutIndex = lines.length\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]\n if (SIGNATURE_PATTERNS.some((p) => p.test(line))) {\n cutIndex = i\n break\n }\n }\n return lines.slice(0, cutIndex).join('\\n').trimEnd()\n}\n\nfunction stripQuotedReplies(text: string): string {\n const lines = text.split('\\n')\n const cleanLines: string[] = []\n let inQuote = false\n\n for (const line of lines) {\n if (QUOTE_PATTERNS.some((p) => p.test(line))) {\n inQuote = true\n continue\n }\n if (inQuote && line.startsWith('>')) {\n continue\n }\n if (inQuote && line.trim() === '') {\n continue\n }\n if (inQuote && !line.startsWith('>') && line.trim().length > 0) {\n inQuote = false\n }\n if (!inQuote) {\n cleanLines.push(line)\n }\n }\n\n return cleanLines.join('\\n').trimEnd()\n}\n\nfunction stripHtml(html: string): string {\n return htmlToPlainText(html)\n}\n\nfunction normalizeText(text: string): string {\n return text\n .replace(/\\r\\n/g, '\\n')\n .replace(/\\r/g, '\\n')\n .replace(/\\t/g, ' ')\n .replace(/ {2,}/g, ' ')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n}\n\nfunction generateContentHash(subject: string, from: string, text: string): string {\n const input = `${subject}|${from}|${text.slice(0, 500)}`\n const normalized = input.toLowerCase().replace(/\\s+/g, ' ').trim()\n return createHash('sha256').update(normalized).digest('hex')\n}\n\nfunction parseAddressField(value: string | undefined | null): { name?: string; email: string } {\n if (!value) return { email: '' }\n const match = value.match(/^(.+?)\\s*<([^>]+)>$/)\n if (match) {\n return { name: match[1].trim().replace(/^[\"']|[\"']$/g, ''), email: match[2].trim().toLowerCase() }\n }\n return { email: value.trim().toLowerCase() }\n}\n\nfunction parseAddressListField(value: string | string[] | undefined | null): { name?: string; email: string }[] {\n if (!value) return []\n const list = Array.isArray(value) ? value : value.split(',')\n return list.map((v) => parseAddressField(v.trim())).filter((a) => a.email)\n}\n\nfunction parseDateFromBlock(block: string): string {\n const match = block.match(DATE_HEADER_PATTERN)\n if (match) {\n const parsed = new Date(match[1].trim())\n if (!Number.isNaN(parsed.getTime())) {\n return parsed.toISOString()\n }\n }\n return new Date().toISOString()\n}\n\nfunction parseInlineHeaders(block: string): {\n from: { name?: string; email: string }\n to: { name?: string; email: string }[]\n subject?: string\n bodyStart: number\n} {\n const lines = block.split('\\n')\n let from: { name?: string; email: string } = { email: '' }\n let to: { name?: string; email: string }[] = []\n let subject: string | undefined\n let lastHeaderLine = -1\n\n for (let i = 0; i < lines.length && i < 10; i++) {\n const line = lines[i]\n const fromMatch = line.match(/^From:\\s*(.+)$/i)\n if (fromMatch) {\n from = parseAddressField(fromMatch[1].trim())\n lastHeaderLine = i\n continue\n }\n const toMatch = line.match(/^To:\\s*(.+)$/i)\n if (toMatch) {\n to = parseAddressListField(toMatch[1].trim())\n lastHeaderLine = i\n continue\n }\n const subjectMatch = line.match(/^Subject:\\s*(.+)$/i)\n if (subjectMatch) {\n subject = subjectMatch[1].trim()\n lastHeaderLine = i\n continue\n }\n if (line.match(/^Date:\\s/i) || line.match(/^CC:\\s/i)) {\n lastHeaderLine = i\n continue\n }\n if (lastHeaderLine >= 0 && line.trim() === '') {\n lastHeaderLine = i\n break\n }\n if (lastHeaderLine >= 0) break\n }\n\n return { from, to, subject, bodyStart: lastHeaderLine + 1 }\n}\n\nfunction splitThread(text: string): ThreadMessage[] {\n const separator = /(?:^|\\n)(?:-{3,}\\s*(?:Original Message|Forwarded message)\\s*-{3,}|(?:On .+ wrote:))\\s*\\n/gm\n const parts = text.split(separator).filter((p) => p.trim())\n\n if (parts.length <= 1) {\n return [{\n from: { email: '' },\n to: [],\n date: new Date().toISOString(),\n body: normalizeText(text),\n contentType: 'text',\n isForwarded: false,\n }]\n }\n\n return parts.map((part, index) => {\n if (index === 0) {\n return {\n from: { email: '' } as { name?: string; email: string },\n to: [] as { name?: string; email: string }[],\n date: parseDateFromBlock(part),\n body: normalizeText(part),\n contentType: 'text' as const,\n isForwarded: false,\n }\n }\n\n const headers = parseInlineHeaders(part)\n const bodyLines = part.split('\\n').slice(headers.bodyStart)\n return {\n from: headers.from,\n to: headers.to,\n subject: headers.subject,\n date: parseDateFromBlock(part),\n body: normalizeText(bodyLines.join('\\n')),\n contentType: 'text' as const,\n isForwarded: true,\n }\n })\n}\n\nexport function parseInboundEmail(payload: {\n from?: string\n to?: string | string[]\n subject?: string\n text?: string\n html?: string\n messageId?: string\n replyTo?: string\n inReplyTo?: string\n references?: string | string[]\n}): ParsedEmail {\n const fromParsed = parseAddressField(payload.from)\n const toParsed = parseAddressListField(payload.to)\n const subject = payload.subject?.trim() || '(no subject)'\n\n const rawText = payload.text || null\n const rawHtml = payload.html || null\n\n let textContent = rawText || ''\n if (!textContent && rawHtml) {\n textContent = stripHtml(rawHtml)\n }\n\n const normalized = normalizeText(textContent)\n const withoutSignature = stripSignature(normalized)\n const cleanedText = stripQuotedReplies(withoutSignature)\n\n const threadMessages = splitThread(normalized)\n if (threadMessages.length > 0 && fromParsed.email) {\n threadMessages[0].from = fromParsed\n threadMessages[0].to = toParsed\n threadMessages[0].subject = subject\n }\n\n const contentHash = generateContentHash(\n subject,\n fromParsed.email,\n textContent,\n )\n\n const references = payload.references\n ? (Array.isArray(payload.references) ? payload.references : payload.references.split(/\\s+/))\n : null\n\n return {\n messageId: payload.messageId || null,\n from: fromParsed,\n to: toParsed,\n subject,\n replyTo: payload.replyTo || null,\n inReplyTo: payload.inReplyTo || null,\n references,\n rawText,\n rawHtml,\n cleanedText,\n threadMessages,\n // TODO: Phase 2 \u2014 detect language via LLM or `franc` library\n detectedLanguage: null,\n contentHash,\n }\n}\n\nexport function extractParticipantsFromThread(\n email: InboxEmail,\n): { name: string; email: string; role: string }[] {\n const seen = new Set<string>()\n const participants: { name: string; email: string; role: string }[] = []\n\n // Pre-seed with the inbox's own address so it's excluded from participants\n const inboxAddress = (email.toAddress || '').toLowerCase()\n if (inboxAddress) seen.add(inboxAddress)\n\n const addParticipant = (name: string, addr: string, role: string) => {\n const key = addr.toLowerCase()\n if (!key || seen.has(key)) return\n seen.add(key)\n participants.push({ name, email: key, role })\n }\n\n if (email.threadMessages) {\n for (const msg of email.threadMessages) {\n if (msg.from?.email) {\n addParticipant(msg.from.name || '', msg.from.email, 'other')\n }\n if (msg.to) {\n for (const to of msg.to) {\n addParticipant(to.name || '', to.email, 'other')\n }\n }\n if (msg.cc) {\n for (const cc of msg.cc) {\n addParticipant(cc.name || '', cc.email, 'other')\n }\n }\n }\n }\n\n if (email.forwardedByAddress) {\n addParticipant(email.forwardedByName || '', email.forwardedByAddress, 'seller')\n }\n\n return participants\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }