@open-mercato/core 0.6.7-develop.6795.1.8a3f27921c → 0.6.7-develop.6824.1.21fff90682

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 (228) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/customer_deal_person_link/index.js +2 -0
  3. package/dist/generated/entities/customer_deal_person_link/index.js.map +2 -2
  4. package/dist/generated/entities/customer_user_invitation/index.js +2 -0
  5. package/dist/generated/entities/customer_user_invitation/index.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +2 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/auth/lib/backendChrome.js +10 -9
  9. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  10. package/dist/modules/auth/lib/tenantAccess.js +7 -0
  11. package/dist/modules/auth/lib/tenantAccess.js.map +2 -2
  12. package/dist/modules/business_rules/api/openmercato-call-options/route.js +85 -0
  13. package/dist/modules/business_rules/api/openmercato-call-options/route.js.map +7 -0
  14. package/dist/modules/business_rules/api/rules/route.js +71 -4
  15. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  16. package/dist/modules/business_rules/components/ActionBuilder.js +47 -4
  17. package/dist/modules/business_rules/components/ActionBuilder.js.map +3 -3
  18. package/dist/modules/business_rules/components/ActionRow.js +106 -16
  19. package/dist/modules/business_rules/components/ActionRow.js.map +2 -2
  20. package/dist/modules/business_rules/components/utils/actionValidation.js +6 -0
  21. package/dist/modules/business_rules/components/utils/actionValidation.js.map +2 -2
  22. package/dist/modules/business_rules/lib/action-executor.js +148 -0
  23. package/dist/modules/business_rules/lib/action-executor.js.map +2 -2
  24. package/dist/modules/business_rules/lib/openmercato-call-options-types.js +5 -0
  25. package/dist/modules/business_rules/lib/openmercato-call-options-types.js.map +7 -0
  26. package/dist/modules/business_rules/lib/openmercato-call-options.js +231 -0
  27. package/dist/modules/business_rules/lib/openmercato-call-options.js.map +7 -0
  28. package/dist/modules/business_rules/lib/rule-engine.js +5 -1
  29. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  30. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +11 -1
  31. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +2 -2
  32. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +11 -6
  33. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +2 -2
  34. package/dist/modules/configs/lib/upgrade-actions.js +12 -0
  35. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  36. package/dist/modules/customer_accounts/api/admin/users/[id].js +10 -1
  37. package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
  38. package/dist/modules/customer_accounts/api/admin/users-invite.js +61 -4
  39. package/dist/modules/customer_accounts/api/admin/users-invite.js.map +2 -2
  40. package/dist/modules/customer_accounts/api/portal/users-invite.js +20 -2
  41. package/dist/modules/customer_accounts/api/portal/users-invite.js.map +2 -2
  42. package/dist/modules/customer_accounts/data/entities.js +3 -0
  43. package/dist/modules/customer_accounts/data/entities.js.map +2 -2
  44. package/dist/modules/customer_accounts/data/validators.js +1 -0
  45. package/dist/modules/customer_accounts/data/validators.js.map +2 -2
  46. package/dist/modules/customer_accounts/emails/CustomerInvitationEmail.js +26 -0
  47. package/dist/modules/customer_accounts/emails/CustomerInvitationEmail.js.map +7 -0
  48. package/dist/modules/customer_accounts/lib/customerEntityOwnership.js +29 -3
  49. package/dist/modules/customer_accounts/lib/customerEntityOwnership.js.map +2 -2
  50. package/dist/modules/customer_accounts/lib/customerUrl.js +19 -10
  51. package/dist/modules/customer_accounts/lib/customerUrl.js.map +2 -2
  52. package/dist/modules/customer_accounts/lib/invitationEmail.js +35 -0
  53. package/dist/modules/customer_accounts/lib/invitationEmail.js.map +7 -0
  54. package/dist/modules/customer_accounts/migrations/Migration20260723120000_customer_accounts.js +13 -0
  55. package/dist/modules/customer_accounts/migrations/Migration20260723120000_customer_accounts.js.map +7 -0
  56. package/dist/modules/customer_accounts/services/customerInvitationService.js +32 -2
  57. package/dist/modules/customer_accounts/services/customerInvitationService.js.map +2 -2
  58. package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js +47 -7
  59. package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js.map +2 -2
  60. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +2 -2
  61. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
  62. package/dist/modules/customer_accounts/widgets/injection-table.js +1 -1
  63. package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
  64. package/dist/modules/customers/api/deals/[id]/people/route.js +4 -2
  65. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  66. package/dist/modules/customers/api/deals/[id]/route.js +7 -3
  67. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  68. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +11 -1
  69. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  70. package/dist/modules/customers/backend/customers/deals/[id]/page.js +14 -8
  71. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  72. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +21 -6
  73. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  74. package/dist/modules/customers/commands/deals.js +48 -10
  75. package/dist/modules/customers/commands/deals.js.map +2 -2
  76. package/dist/modules/customers/commands/people.js +2 -0
  77. package/dist/modules/customers/commands/people.js.map +2 -2
  78. package/dist/modules/customers/components/detail/CompanyDetailTabs.js +26 -6
  79. package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +2 -2
  80. package/dist/modules/customers/components/detail/DealDetailTabs.js +30 -8
  81. package/dist/modules/customers/components/detail/DealDetailTabs.js.map +2 -2
  82. package/dist/modules/customers/components/detail/PersonDetailTabs.js +30 -8
  83. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  84. package/dist/modules/customers/data/entities.js +8 -0
  85. package/dist/modules/customers/data/entities.js.map +2 -2
  86. package/dist/modules/customers/data/validators.js +2 -1
  87. package/dist/modules/customers/data/validators.js.map +2 -2
  88. package/dist/modules/customers/migrations/Migration20260723210000_customers.js +15 -0
  89. package/dist/modules/customers/migrations/Migration20260723210000_customers.js.map +7 -0
  90. package/dist/modules/directory/api/tenants/route.js +8 -2
  91. package/dist/modules/directory/api/tenants/route.js.map +2 -2
  92. package/dist/modules/directory/commands/tenants.js +6 -0
  93. package/dist/modules/directory/commands/tenants.js.map +2 -2
  94. package/dist/modules/entities/api/entity-settings.js +59 -9
  95. package/dist/modules/entities/api/entity-settings.js.map +2 -2
  96. package/dist/modules/entities/backend/config/settings/page.js +10 -0
  97. package/dist/modules/entities/backend/config/settings/page.js.map +7 -0
  98. package/dist/modules/entities/backend/config/settings/page.meta.js +36 -0
  99. package/dist/modules/entities/backend/config/settings/page.meta.js.map +7 -0
  100. package/dist/modules/entities/backend/entities/user/create/page.js +16 -7
  101. package/dist/modules/entities/backend/entities/user/create/page.js.map +2 -2
  102. package/dist/modules/entities/components/EntitySettingsManager.js +136 -0
  103. package/dist/modules/entities/components/EntitySettingsManager.js.map +7 -0
  104. package/dist/modules/inbox_ops/api/webhook/inbound.js +14 -10
  105. package/dist/modules/inbox_ops/api/webhook/inbound.js.map +2 -2
  106. package/dist/modules/payment_gateways/api/sessions/route.js +1 -3
  107. package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
  108. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +11 -1
  109. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
  110. package/dist/modules/payment_gateways/data/entities.js +4 -0
  111. package/dist/modules/payment_gateways/data/entities.js.map +2 -2
  112. package/dist/modules/payment_gateways/lib/gateway-service.js +15 -6
  113. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  114. package/dist/modules/payment_gateways/lib/session-idempotency.js +33 -0
  115. package/dist/modules/payment_gateways/lib/session-idempotency.js.map +2 -2
  116. package/dist/modules/payment_gateways/migrations/Migration20260803103035_payment_gateways.js +18 -0
  117. package/dist/modules/payment_gateways/migrations/Migration20260803103035_payment_gateways.js.map +7 -0
  118. package/dist/modules/payment_gateways/setup.js +47 -0
  119. package/dist/modules/payment_gateways/setup.js.map +2 -2
  120. package/dist/modules/payment_gateways/workers/session-initialization-prune.js +50 -0
  121. package/dist/modules/payment_gateways/workers/session-initialization-prune.js.map +7 -0
  122. package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js +134 -0
  123. package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js.map +7 -0
  124. package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.meta.js +11 -0
  125. package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.meta.js.map +7 -0
  126. package/dist/modules/portal/lib/navigation.js +7 -0
  127. package/dist/modules/portal/lib/navigation.js.map +7 -0
  128. package/dist/modules/query_index/lib/engine.js +43 -0
  129. package/dist/modules/query_index/lib/engine.js.map +2 -2
  130. package/dist/modules/query_index/lib/search-tokens.js +15 -5
  131. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  132. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +11 -1
  133. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +2 -2
  134. package/generated/entities/customer_deal_person_link/index.ts +1 -0
  135. package/generated/entities/customer_user_invitation/index.ts +1 -0
  136. package/generated/entity-fields-registry.ts +2 -0
  137. package/package.json +7 -7
  138. package/src/modules/auth/lib/backendChrome.tsx +15 -9
  139. package/src/modules/auth/lib/tenantAccess.ts +8 -0
  140. package/src/modules/business_rules/api/openmercato-call-options/route.ts +94 -0
  141. package/src/modules/business_rules/api/rules/route.ts +92 -4
  142. package/src/modules/business_rules/components/ActionBuilder.tsx +54 -5
  143. package/src/modules/business_rules/components/ActionRow.tsx +156 -18
  144. package/src/modules/business_rules/components/utils/actionValidation.ts +6 -0
  145. package/src/modules/business_rules/i18n/de.json +12 -0
  146. package/src/modules/business_rules/i18n/en.json +12 -0
  147. package/src/modules/business_rules/i18n/es.json +12 -0
  148. package/src/modules/business_rules/i18n/pl.json +12 -0
  149. package/src/modules/business_rules/lib/action-executor.ts +198 -0
  150. package/src/modules/business_rules/lib/openmercato-call-options-types.ts +31 -0
  151. package/src/modules/business_rules/lib/openmercato-call-options.ts +297 -0
  152. package/src/modules/business_rules/lib/rule-engine.ts +4 -0
  153. package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +11 -1
  154. package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +17 -11
  155. package/src/modules/configs/lib/upgrade-actions.ts +14 -0
  156. package/src/modules/customer_accounts/api/admin/users/[id].ts +16 -1
  157. package/src/modules/customer_accounts/api/admin/users-invite.ts +78 -3
  158. package/src/modules/customer_accounts/api/portal/users-invite.ts +22 -1
  159. package/src/modules/customer_accounts/data/entities.ts +3 -0
  160. package/src/modules/customer_accounts/data/validators.ts +1 -0
  161. package/src/modules/customer_accounts/emails/CustomerInvitationEmail.tsx +45 -0
  162. package/src/modules/customer_accounts/i18n/de.json +6 -0
  163. package/src/modules/customer_accounts/i18n/en.json +6 -0
  164. package/src/modules/customer_accounts/i18n/es.json +6 -0
  165. package/src/modules/customer_accounts/i18n/pl.json +6 -0
  166. package/src/modules/customer_accounts/lib/customerEntityOwnership.ts +68 -2
  167. package/src/modules/customer_accounts/lib/customerUrl.ts +27 -11
  168. package/src/modules/customer_accounts/lib/invitationEmail.ts +42 -0
  169. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +16 -0
  170. package/src/modules/customer_accounts/migrations/Migration20260723120000_customer_accounts.ts +13 -0
  171. package/src/modules/customer_accounts/services/customerInvitationService.ts +56 -3
  172. package/src/modules/customer_accounts/subscribers/autoLinkCrm.ts +75 -8
  173. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +2 -2
  174. package/src/modules/customer_accounts/widgets/injection-table.ts +4 -1
  175. package/src/modules/customers/api/deals/[id]/people/route.ts +3 -0
  176. package/src/modules/customers/api/deals/[id]/route.ts +10 -2
  177. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +12 -1
  178. package/src/modules/customers/backend/customers/deals/[id]/hooks/types.ts +1 -0
  179. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +15 -9
  180. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +24 -6
  181. package/src/modules/customers/commands/deals.ts +50 -9
  182. package/src/modules/customers/commands/people.ts +3 -0
  183. package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +30 -7
  184. package/src/modules/customers/components/detail/DealDetailTabs.tsx +34 -9
  185. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +34 -9
  186. package/src/modules/customers/data/entities.ts +9 -1
  187. package/src/modules/customers/data/validators.ts +1 -0
  188. package/src/modules/customers/i18n/de.json +1 -0
  189. package/src/modules/customers/i18n/en.json +1 -0
  190. package/src/modules/customers/i18n/es.json +1 -0
  191. package/src/modules/customers/i18n/pl.json +1 -0
  192. package/src/modules/customers/migrations/.snapshot-open-mercato.json +27 -1
  193. package/src/modules/customers/migrations/Migration20260723210000_customers.ts +15 -0
  194. package/src/modules/directory/api/tenants/route.ts +9 -1
  195. package/src/modules/directory/commands/tenants.ts +6 -0
  196. package/src/modules/entities/api/entity-settings.ts +66 -22
  197. package/src/modules/entities/backend/config/settings/page.meta.ts +34 -0
  198. package/src/modules/entities/backend/config/settings/page.tsx +12 -0
  199. package/src/modules/entities/backend/entities/user/create/page.tsx +16 -7
  200. package/src/modules/entities/components/EntitySettingsManager.tsx +160 -0
  201. package/src/modules/entities/i18n/de.json +9 -0
  202. package/src/modules/entities/i18n/en.json +9 -0
  203. package/src/modules/entities/i18n/es.json +9 -0
  204. package/src/modules/entities/i18n/pl.json +9 -0
  205. package/src/modules/inbox_ops/api/webhook/inbound.ts +14 -12
  206. package/src/modules/payment_gateways/api/sessions/route.ts +1 -3
  207. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +13 -1
  208. package/src/modules/payment_gateways/data/entities.ts +5 -0
  209. package/src/modules/payment_gateways/i18n/de.json +5 -1
  210. package/src/modules/payment_gateways/i18n/en.json +5 -1
  211. package/src/modules/payment_gateways/i18n/es.json +5 -1
  212. package/src/modules/payment_gateways/i18n/pl.json +5 -1
  213. package/src/modules/payment_gateways/lib/gateway-service.ts +17 -8
  214. package/src/modules/payment_gateways/lib/session-idempotency.ts +36 -0
  215. package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +10 -1
  216. package/src/modules/payment_gateways/migrations/Migration20260803103035_payment_gateways.ts +17 -0
  217. package/src/modules/payment_gateways/setup.ts +62 -0
  218. package/src/modules/payment_gateways/workers/session-initialization-prune.ts +63 -0
  219. package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.meta.ts +9 -0
  220. package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.tsx +164 -0
  221. package/src/modules/portal/i18n/de.json +18 -0
  222. package/src/modules/portal/i18n/en.json +18 -0
  223. package/src/modules/portal/i18n/es.json +18 -0
  224. package/src/modules/portal/i18n/pl.json +18 -0
  225. package/src/modules/portal/lib/navigation.ts +3 -0
  226. package/src/modules/query_index/lib/engine.ts +52 -0
  227. package/src/modules/query_index/lib/search-tokens.ts +16 -4
  228. package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +11 -1
@@ -34,14 +34,14 @@ function groupBackendRoutesByModule(routes) {
34
34
  ).map(([id, backendRoutes]) => ({ id, backendRoutes }));
35
35
  }
36
36
  const settingsSectionOrder = {
37
- system: 1,
38
- auth: 2,
39
- "customer-portal": 3,
40
- "data-designer": 4,
41
- "module-configs": 5,
42
- currencies: 6,
43
- directory: 7,
44
- "feature-toggles": 8
37
+ "settings.sections.system": 1,
38
+ "settings.sections.auth": 2,
39
+ "customer_accounts.settings.section": 3,
40
+ "settings.sections.dataDesigner": 4,
41
+ "settings.sections.moduleConfigs": 5,
42
+ "currencies.nav.group": 6,
43
+ "settings.sections.directory": 7,
44
+ "settings.sections.featureToggles": 8
45
45
  };
46
46
  let renderToStaticMarkupPromise = null;
47
47
  async function serializeIconMarkup(icon) {
@@ -338,6 +338,7 @@ async function resolveBackendChromePayload({
338
338
  }
339
339
  export {
340
340
  groupBackendRoutesByModule,
341
- resolveBackendChromePayload
341
+ resolveBackendChromePayload,
342
+ settingsSectionOrder
342
343
  };
343
344
  //# sourceMappingURL=backendChrome.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/lib/backendChrome.tsx"],
4
- "sourcesContent": ["import * as React from 'react'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport type { BackendRouteManifestEntry } from '@open-mercato/shared/modules/registry'\nimport type {\n BackendChromePayload,\n BackendChromeNavGroup,\n BackendChromeNavItem,\n BackendChromeSectionGroup,\n BackendChromeSectionItem,\n} from '@open-mercato/shared/modules/navigation/backendChrome'\nimport {\n buildAdminNav,\n buildSettingsSections,\n computeSettingsPathPrefixes,\n convertToSectionNavGroups,\n type AdminNavItem,\n} from '@open-mercato/ui/backend/utils/nav'\nimport { resolveRegisteredLucideIconNode } from '@open-mercato/ui/backend/icons/lucideRegistry'\nimport { profilePathPrefixes, profileSections } from './profile-sections'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getNavGroupOrderOverride } from '@open-mercato/shared/modules/overrides'\nimport {\n getSelectedOrganizationFromRequest,\n resolveFeatureCheckContext,\n} from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { CustomEntity } from '@open-mercato/core/modules/entities/data/entities'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n applySidebarPreference,\n loadFirstRoleSidebarPreference,\n loadSidebarPreference,\n} from '@open-mercato/core/modules/auth/services/sidebarPreferencesService'\nimport type { SidebarPreferencesSettings } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\n\ntype TranslationFn = (key: string | undefined, fallback: string) => string\n\ntype RouteModule = {\n id: string\n backendRoutes?: BackendRouteManifestEntry[]\n}\n\nexport function groupBackendRoutesByModule(routes: BackendRouteManifestEntry[]): RouteModule[] {\n return Array.from(\n routes.reduce((grouped, route) => {\n const list = grouped.get(route.moduleId) ?? []\n list.push(route)\n grouped.set(route.moduleId, list)\n return grouped\n }, new Map<string, BackendRouteManifestEntry[]>()),\n ).map(([id, backendRoutes]) => ({ id, backendRoutes }))\n}\n\ntype SerializableSectionItem = {\n id: string\n label: string\n labelKey?: string\n href: string\n icon?: React.ReactNode\n order?: number\n children?: SerializableSectionItem[]\n}\n\ntype SerializableSectionGroup = {\n id: string\n label: string\n labelKey?: string\n order?: number\n items: SerializableSectionItem[]\n}\n\ntype ResolvedNavItem = Omit<BackendChromeNavItem, 'defaultTitle' | 'children'> & {\n defaultTitle: string\n children?: ResolvedNavItem[]\n}\n\ntype ResolveBackendChromePayloadArgs = {\n auth: Exclude<AuthContext, null>\n locale: string\n modules: RouteModule[]\n translate: TranslationFn\n request?: Request\n selectedOrganizationId?: string | null\n selectedTenantId?: string | null\n}\n\nconst settingsSectionOrder: Record<string, number> = {\n system: 1,\n auth: 2,\n 'customer-portal': 3,\n 'data-designer': 4,\n 'module-configs': 5,\n currencies: 6,\n directory: 7,\n 'feature-toggles': 8,\n}\n\ntype NavGroupWithWeight = Omit<BackendChromeNavGroup, 'id' | 'defaultName' | 'items'> & {\n id: string\n defaultName: string\n items: ResolvedNavItem[]\n weight: number\n}\n\nlet renderToStaticMarkupPromise: Promise<typeof import('react-dom/server')> | null = null\n\nasync function serializeIconMarkup(icon: React.ReactNode | undefined): Promise<string | undefined> {\n if (!icon) return undefined\n if (!renderToStaticMarkupPromise) {\n renderToStaticMarkupPromise = import('react-dom/server')\n }\n const { renderToStaticMarkup } = await renderToStaticMarkupPromise\n\n const normalizedIcon = typeof icon === 'string'\n ? resolveRegisteredLucideIconNode(icon, 'size-4')\n : icon\n\n if (!normalizedIcon) return undefined\n\n try {\n const markup = renderToStaticMarkup(<>{normalizedIcon}</>)\n return markup.trim().length > 0 ? markup : undefined\n } catch {\n // Some icon values may be client-only component references after dependency upgrades.\n // Avoid taking down the entire nav payload because one icon cannot be rendered server-side.\n return undefined\n }\n}\n\nasync function serializeNavItem(item: AdminNavItem): Promise<ResolvedNavItem> {\n return {\n id: item.href,\n href: item.href,\n title: item.title,\n defaultTitle: item.defaultTitle,\n enabled: item.enabled,\n hidden: item.hidden,\n pageContext: item.pageContext,\n iconName: typeof item.icon === 'string' ? item.icon : undefined,\n iconMarkup: await serializeIconMarkup(item.icon),\n children: item.children ? await Promise.all(item.children.map((child) => serializeNavItem(child))) : undefined,\n }\n}\n\nconst defaultGroupOrder = [\n 'customers.nav.group',\n 'catalog.nav.group',\n 'customers~sales.nav.group',\n 'wms.nav.group',\n 'resources.nav.group',\n 'staff.nav.group',\n 'entities.nav.group',\n 'directory.nav.group',\n 'attachments.nav.group',\n]\n\n/**\n * Group ids ranked ahead of everything else, most significant first.\n *\n * An app may prepend its own ids via `overrides.nav.groupOrder` in `modules.ts`; ids it does not name\n * keep the ordering they have today. With no override configured this returns `defaultGroupOrder`\n * itself, so ordering is unchanged for every existing install.\n */\nfunction resolveGroupOrder(): string[] {\n const override = getNavGroupOrderOverride()\n if (!override || override.length === 0) return defaultGroupOrder\n const overridden = new Set(override)\n return [...override, ...defaultGroupOrder.filter((id) => !overridden.has(id))]\n}\n\nfunction normalizeGroupWeights(groups: NavGroupWithWeight[]): NavGroupWithWeight[] {\n const groupOrder = resolveGroupOrder()\n const groupOrderIndex = new Map(groupOrder.map((id, index) => [id, index]))\n groups.sort((a, b) => {\n const aIndex = groupOrderIndex.get(a.id)\n const bIndex = groupOrderIndex.get(b.id)\n if (aIndex !== undefined || bIndex !== undefined) {\n if (aIndex === undefined) return 1\n if (bIndex === undefined) return -1\n if (aIndex !== bIndex) return aIndex - bIndex\n }\n if (a.weight !== b.weight) return a.weight - b.weight\n return a.name.localeCompare(b.name)\n })\n const defaultGroupCount = groupOrder.length\n groups.forEach((group, index) => {\n const rank = groupOrderIndex.get(group.id)\n const fallbackWeight = typeof group.weight === 'number' ? group.weight : 10_000\n group.weight =\n (rank !== undefined ? rank : defaultGroupCount + index) * 1_000_000 +\n Math.min(Math.max(fallbackWeight, 0), 999_999)\n })\n return groups\n}\n\nasync function groupEntries(entries: AdminNavItem[]): Promise<NavGroupWithWeight[]> {\n const groupMap = new Map<string, NavGroupWithWeight>()\n for (const entry of entries) {\n const weight = entry.priority ?? entry.order ?? 10_000\n const serializedItem = await serializeNavItem(entry)\n const existing = groupMap.get(entry.groupId)\n if (existing) {\n existing.items.push(serializedItem)\n if (weight < existing.weight) existing.weight = weight\n continue\n }\n groupMap.set(entry.groupId, {\n id: entry.groupId,\n name: entry.group,\n defaultName: entry.groupDefaultName,\n items: [serializedItem],\n weight,\n })\n }\n return normalizeGroupWeights(Array.from(groupMap.values()))\n}\n\nfunction adoptSidebarDefaults(groups: NavGroupWithWeight[]): NavGroupWithWeight[] {\n const adoptItems = (items: ResolvedNavItem[]): ResolvedNavItem[] =>\n items.map((item) => ({\n ...item,\n defaultTitle: item.title,\n children: item.children ? adoptItems(item.children) : undefined,\n }))\n\n return groups.map((group) => ({\n ...group,\n defaultName: group.name,\n items: adoptItems(group.items),\n }))\n}\n\nasync function serializeSectionItem(item: {\n id: string\n label: string\n labelKey?: string\n href: string\n icon?: React.ReactNode\n order?: number\n children?: SerializableSectionItem[]\n}): Promise<BackendChromeSectionItem> {\n return {\n id: item.id,\n label: item.label,\n labelKey: item.labelKey,\n href: item.href,\n order: item.order,\n iconName: typeof item.icon === 'string' ? item.icon : undefined,\n iconMarkup: await serializeIconMarkup(item.icon),\n children: item.children ? await Promise.all(item.children.map((child) => serializeSectionItem(child))) : undefined,\n }\n}\n\nasync function serializeSectionGroups(groups: SerializableSectionGroup[]): Promise<BackendChromeSectionGroup[]> {\n return Promise.all(groups.map(async (group) => ({\n id: group.id,\n label: group.label,\n labelKey: group.labelKey,\n order: group.order,\n items: await Promise.all(group.items.map((item) => serializeSectionItem(item))),\n })))\n}\n\nasync function loadScopedContainer(): Promise<AwilixContainer> {\n return createRequestContainer()\n}\n\nexport async function resolveBackendChromePayload({\n auth,\n locale,\n modules,\n translate,\n request,\n selectedOrganizationId,\n selectedTenantId,\n}: ResolveBackendChromePayloadArgs): Promise<BackendChromePayload> {\n const container = await loadScopedContainer()\n const em = container.resolve('em') as EntityManager\n const rbac = container.resolve('rbacService') as {\n getEffectiveFeatures: (userId: string, scope: { tenantId: string | null; organizationId: string | null }) => Promise<string[]>\n userHasAllFeatures: (userId: string, required: string[], scope: { tenantId: string | null; organizationId: string | null }) => Promise<boolean>\n }\n\n let scopedOrganizationId: string | null = auth.orgId ?? null\n let scopedTenantId: string | null = auth.tenantId ?? null\n // The organization the caller actually *selected*, as distinct from the one the scope resolver fell\n // back to. `resolveFeatureCheckContext` resolves `organizationId` to `auth.orgId` when no concrete\n // organization is selected \u2014 which is precisely what an all-organizations view produces \u2014 so the\n // resolved id cannot answer \"which organization am I viewing\".\n let concretelySelectedOrganizationId: string | null = null\n let allowNavigation = true\n\n try {\n const { organizationId, scope, allowedOrganizationIds } = await resolveFeatureCheckContext({\n container,\n auth,\n request,\n selectedId: selectedOrganizationId,\n tenantId: selectedTenantId,\n })\n scopedOrganizationId = organizationId\n scopedTenantId = scope.tenantId ?? auth.tenantId ?? null\n concretelySelectedOrganizationId = scope.selectedId ?? null\n if (Array.isArray(allowedOrganizationIds) && allowedOrganizationIds.length === 0) {\n allowNavigation = false\n }\n } catch {\n scopedOrganizationId = auth.orgId ?? null\n scopedTenantId = auth.tenantId ?? null\n concretelySelectedOrganizationId = null\n }\n\n const grantedFeatures = allowNavigation\n ? await rbac.getEffectiveFeatures(auth.sub, {\n tenantId: scopedTenantId,\n organizationId: scopedOrganizationId,\n })\n : []\n const featureChecker = async (features: string[]): Promise<string[]> => {\n if (!allowNavigation || !features.length) return []\n const context = {\n tenantId: scopedTenantId ?? auth.tenantId ?? null,\n organizationId: scopedOrganizationId ?? null,\n }\n const hasAll = await rbac.userHasAllFeatures(auth.sub, features, context)\n if (hasAll) return features\n\n const granted: string[] = []\n for (const feature of features) {\n const hasFeature = await rbac.userHasAllFeatures(auth.sub, [feature], context)\n if (hasFeature) granted.push(feature)\n }\n return granted\n }\n\n let userEntities: Array<{ entityId: string; label: string; href: string }> = []\n if (allowNavigation) {\n try {\n const where: FilterQuery<CustomEntity> = {\n isActive: true,\n showInSidebar: true,\n }\n where.$and = [\n { $or: [{ organizationId: scopedOrganizationId ?? undefined }, { organizationId: null }] },\n { $or: [{ tenantId: scopedTenantId ?? undefined }, { tenantId: null }] },\n ]\n const entities = await em.find(CustomEntity, where, { orderBy: { label: 'asc' } })\n userEntities = entities.map((entity) => ({\n entityId: entity.entityId,\n label: entity.label,\n href: `/backend/entities/user/${encodeURIComponent(entity.entityId)}/records`,\n }))\n } catch {\n userEntities = []\n }\n }\n\n const ctxAuth = {\n roles: auth.roles || [],\n sub: auth.sub,\n tenantId: scopedTenantId,\n orgId: scopedOrganizationId,\n }\n const entries = allowNavigation\n ? await buildAdminNav(\n modules,\n { auth: ctxAuth },\n userEntities,\n translate,\n { checkFeatures: featureChecker },\n )\n : []\n\n let rolePreference: SidebarPreferencesSettings | null = null\n let userPreference: SidebarPreferencesSettings | null = null\n\n if (Array.isArray(auth.roles) && auth.roles.length > 0) {\n const roleRecords = scopedTenantId\n ? await em.find(Role, {\n name: { $in: auth.roles },\n tenantId: scopedTenantId,\n })\n : []\n const roleIds = Array.isArray(roleRecords) ? roleRecords.map((role) => role.id) : []\n if (roleIds.length > 0) {\n rolePreference = await loadFirstRoleSidebarPreference(em, {\n roleIds,\n tenantId: scopedTenantId,\n locale,\n })\n }\n }\n\n const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub\n if (effectiveUserId) {\n userPreference = await loadSidebarPreference(em, {\n userId: effectiveUserId,\n tenantId: scopedTenantId,\n organizationId: scopedOrganizationId,\n locale,\n })\n }\n\n const baseGroups = await groupEntries(entries)\n const groupsWithRole = rolePreference\n ? applySidebarPreference<NavGroupWithWeight>(baseGroups, rolePreference)\n : baseGroups\n const baseForUser = adoptSidebarDefaults(groupsWithRole)\n const appliedGroups = userPreference\n ? applySidebarPreference<NavGroupWithWeight>(baseForUser, userPreference)\n : baseForUser\n\n const settingsSections = await serializeSectionGroups(\n convertToSectionNavGroups(\n buildSettingsSections(entries, settingsSectionOrder),\n translate,\n ),\n )\n\n const requestOrganizationId = request ? getSelectedOrganizationFromRequest(request) : null\n const fallbackOrganizationId = selectedOrganizationId ?? requestOrganizationId ?? auth.orgId ?? null\n const brandOrganizationId = scopedOrganizationId\n ?? (fallbackOrganizationId && !isAllOrganizationsSelection(fallbackOrganizationId) ? fallbackOrganizationId : null)\n\n let brand: BackendChromePayload['brand'] = null\n // Resolved here rather than left to callers. `brand` only populates when the organization has a\n // logo, so it is a branding channel, not a dependable \"which organization am I viewing\" source.\n // Without this field every downstream app has to fetch `/api/directory/organization-switcher` and\n // walk its tree for the selected id. The row is already loaded below, so the name costs nothing.\n let currentOrganization: BackendChromePayload['currentOrganization'] = null\n if (brandOrganizationId && scopedTenantId) {\n try {\n const organization = await findOneWithDecryption(\n em,\n Organization,\n { id: brandOrganizationId, tenant: scopedTenantId, deletedAt: null },\n undefined,\n { tenantId: scopedTenantId, organizationId: brandOrganizationId },\n )\n // Only when a concrete organization was selected. Under an all-organizations view\n // `brandOrganizationId` still resolves (to the caller's own organization, which is what keeps\n // branding working), so gating on the loaded row alone would misreport the scope.\n if (organization && concretelySelectedOrganizationId === brandOrganizationId) {\n currentOrganization = { id: String(organization.id), name: organization.name }\n }\n if (organization?.logoUrl) {\n brand = {\n name: organization.name,\n logo: {\n src: organization.logoUrl,\n alt: `${organization.name} logo`,\n preserveAspectRatio: !!organization.logoPreserveAspectRatio,\n },\n }\n }\n } catch {\n // Fail soft, as before: a failed organization lookup must not take down the nav payload.\n brand = null\n currentOrganization = null\n }\n }\n\n return {\n groups: appliedGroups.map(({ weight: _weight, ...group }) => group),\n settingsSections,\n settingsPathPrefixes: computeSettingsPathPrefixes(buildSettingsSections(entries, settingsSectionOrder)),\n profileSections: await serializeSectionGroups(profileSections),\n profilePathPrefixes,\n grantedFeatures,\n roles: Array.isArray(auth.roles) ? auth.roles : [],\n brand,\n currentOrganization,\n }\n}\n"],
5
- "mappings": "AA6HwC;AAhHxC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,uCAAuC;AAChD,SAAS,qBAAqB,uBAAuB;AACrD,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,mCAAmC;AAC5C,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,YAAY;AACrB,SAAS,6BAA6B;AACtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAUA,SAAS,2BAA2B,QAAoD;AAC7F,SAAO,MAAM;AAAA,IACX,OAAO,OAAO,CAAC,SAAS,UAAU;AAChC,YAAM,OAAO,QAAQ,IAAI,MAAM,QAAQ,KAAK,CAAC;AAC7C,WAAK,KAAK,KAAK;AACf,cAAQ,IAAI,MAAM,UAAU,IAAI;AAChC,aAAO;AAAA,IACT,GAAG,oBAAI,IAAyC,CAAC;AAAA,EACnD,EAAE,IAAI,CAAC,CAAC,IAAI,aAAa,OAAO,EAAE,IAAI,cAAc,EAAE;AACxD;AAmCA,MAAM,uBAA+C;AAAA,EACnD,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,mBAAmB;AACrB;AASA,IAAI,8BAAiF;AAErF,eAAe,oBAAoB,MAAgE;AACjG,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,CAAC,6BAA6B;AAChC,kCAA8B,OAAO,kBAAkB;AAAA,EACzD;AACA,QAAM,EAAE,qBAAqB,IAAI,MAAM;AAEvC,QAAM,iBAAiB,OAAO,SAAS,WACnC,gCAAgC,MAAM,QAAQ,IAC9C;AAEJ,MAAI,CAAC,eAAgB,QAAO;AAE5B,MAAI;AACF,UAAM,SAAS,qBAAqB,gCAAG,0BAAe,CAAG;AACzD,WAAO,OAAO,KAAK,EAAE,SAAS,IAAI,SAAS;AAAA,EAC7C,QAAQ;AAGN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,iBAAiB,MAA8C;AAC5E,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,IACX,OAAO,KAAK;AAAA,IACZ,cAAc,KAAK;AAAA,IACnB,SAAS,KAAK;AAAA,IACd,QAAQ,KAAK;AAAA,IACb,aAAa,KAAK;AAAA,IAClB,UAAU,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AAAA,IACtD,YAAY,MAAM,oBAAoB,KAAK,IAAI;AAAA,IAC/C,UAAU,KAAK,WAAW,MAAM,QAAQ,IAAI,KAAK,SAAS,IAAI,CAAC,UAAU,iBAAiB,KAAK,CAAC,CAAC,IAAI;AAAA,EACvG;AACF;AAEA,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASA,SAAS,oBAA8B;AACrC,QAAM,WAAW,yBAAyB;AAC1C,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG,QAAO;AAC/C,QAAM,aAAa,IAAI,IAAI,QAAQ;AACnC,SAAO,CAAC,GAAG,UAAU,GAAG,kBAAkB,OAAO,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;AAC/E;AAEA,SAAS,sBAAsB,QAAoD;AACjF,QAAM,aAAa,kBAAkB;AACrC,QAAM,kBAAkB,IAAI,IAAI,WAAW,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,CAAC;AAC1E,SAAO,KAAK,CAAC,GAAG,MAAM;AACpB,UAAM,SAAS,gBAAgB,IAAI,EAAE,EAAE;AACvC,UAAM,SAAS,gBAAgB,IAAI,EAAE,EAAE;AACvC,QAAI,WAAW,UAAa,WAAW,QAAW;AAChD,UAAI,WAAW,OAAW,QAAO;AACjC,UAAI,WAAW,OAAW,QAAO;AACjC,UAAI,WAAW,OAAQ,QAAO,SAAS;AAAA,IACzC;AACA,QAAI,EAAE,WAAW,EAAE,OAAQ,QAAO,EAAE,SAAS,EAAE;AAC/C,WAAO,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,EACpC,CAAC;AACD,QAAM,oBAAoB,WAAW;AACrC,SAAO,QAAQ,CAAC,OAAO,UAAU;AAC/B,UAAM,OAAO,gBAAgB,IAAI,MAAM,EAAE;AACzC,UAAM,iBAAiB,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;AACzE,UAAM,UACH,SAAS,SAAY,OAAO,oBAAoB,SAAS,MAC1D,KAAK,IAAI,KAAK,IAAI,gBAAgB,CAAC,GAAG,MAAO;AAAA,EACjD,CAAC;AACD,SAAO;AACT;AAEA,eAAe,aAAa,SAAwD;AAClF,QAAM,WAAW,oBAAI,IAAgC;AACrD,aAAW,SAAS,SAAS;AAC3B,UAAM,SAAS,MAAM,YAAY,MAAM,SAAS;AAChD,UAAM,iBAAiB,MAAM,iBAAiB,KAAK;AACnD,UAAM,WAAW,SAAS,IAAI,MAAM,OAAO;AAC3C,QAAI,UAAU;AACZ,eAAS,MAAM,KAAK,cAAc;AAClC,UAAI,SAAS,SAAS,OAAQ,UAAS,SAAS;AAChD;AAAA,IACF;AACA,aAAS,IAAI,MAAM,SAAS;AAAA,MAC1B,IAAI,MAAM;AAAA,MACV,MAAM,MAAM;AAAA,MACZ,aAAa,MAAM;AAAA,MACnB,OAAO,CAAC,cAAc;AAAA,MACtB;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO,sBAAsB,MAAM,KAAK,SAAS,OAAO,CAAC,CAAC;AAC5D;AAEA,SAAS,qBAAqB,QAAoD;AAChF,QAAM,aAAa,CAAC,UAClB,MAAM,IAAI,CAAC,UAAU;AAAA,IACnB,GAAG;AAAA,IACH,cAAc,KAAK;AAAA,IACnB,UAAU,KAAK,WAAW,WAAW,KAAK,QAAQ,IAAI;AAAA,EACxD,EAAE;AAEJ,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B,GAAG;AAAA,IACH,aAAa,MAAM;AAAA,IACnB,OAAO,WAAW,MAAM,KAAK;AAAA,EAC/B,EAAE;AACJ;AAEA,eAAe,qBAAqB,MAQE;AACpC,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,OAAO,KAAK;AAAA,IACZ,UAAU,KAAK;AAAA,IACf,MAAM,KAAK;AAAA,IACX,OAAO,KAAK;AAAA,IACZ,UAAU,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AAAA,IACtD,YAAY,MAAM,oBAAoB,KAAK,IAAI;AAAA,IAC/C,UAAU,KAAK,WAAW,MAAM,QAAQ,IAAI,KAAK,SAAS,IAAI,CAAC,UAAU,qBAAqB,KAAK,CAAC,CAAC,IAAI;AAAA,EAC3G;AACF;AAEA,eAAe,uBAAuB,QAA0E;AAC9G,SAAO,QAAQ,IAAI,OAAO,IAAI,OAAO,WAAW;AAAA,IAC9C,IAAI,MAAM;AAAA,IACV,OAAO,MAAM;AAAA,IACb,UAAU,MAAM;AAAA,IAChB,OAAO,MAAM;AAAA,IACb,OAAO,MAAM,QAAQ,IAAI,MAAM,MAAM,IAAI,CAAC,SAAS,qBAAqB,IAAI,CAAC,CAAC;AAAA,EAChF,EAAE,CAAC;AACL;AAEA,eAAe,sBAAgD;AAC7D,SAAO,uBAAuB;AAChC;AAEA,eAAsB,4BAA4B;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmE;AACjE,QAAM,YAAY,MAAM,oBAAoB;AAC5C,QAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,QAAM,OAAO,UAAU,QAAQ,aAAa;AAK5C,MAAI,uBAAsC,KAAK,SAAS;AACxD,MAAI,iBAAgC,KAAK,YAAY;AAKrD,MAAI,mCAAkD;AACtD,MAAI,kBAAkB;AAEtB,MAAI;AACF,UAAM,EAAE,gBAAgB,OAAO,uBAAuB,IAAI,MAAM,2BAA2B;AAAA,MACzF;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ,CAAC;AACD,2BAAuB;AACvB,qBAAiB,MAAM,YAAY,KAAK,YAAY;AACpD,uCAAmC,MAAM,cAAc;AACvD,QAAI,MAAM,QAAQ,sBAAsB,KAAK,uBAAuB,WAAW,GAAG;AAChF,wBAAkB;AAAA,IACpB;AAAA,EACF,QAAQ;AACN,2BAAuB,KAAK,SAAS;AACrC,qBAAiB,KAAK,YAAY;AAClC,uCAAmC;AAAA,EACrC;AAEA,QAAM,kBAAkB,kBACpB,MAAM,KAAK,qBAAqB,KAAK,KAAK;AAAA,IACxC,UAAU;AAAA,IACV,gBAAgB;AAAA,EAClB,CAAC,IACD,CAAC;AACL,QAAM,iBAAiB,OAAO,aAA0C;AACtE,QAAI,CAAC,mBAAmB,CAAC,SAAS,OAAQ,QAAO,CAAC;AAClD,UAAM,UAAU;AAAA,MACd,UAAU,kBAAkB,KAAK,YAAY;AAAA,MAC7C,gBAAgB,wBAAwB;AAAA,IAC1C;AACA,UAAM,SAAS,MAAM,KAAK,mBAAmB,KAAK,KAAK,UAAU,OAAO;AACxE,QAAI,OAAQ,QAAO;AAEnB,UAAM,UAAoB,CAAC;AAC3B,eAAW,WAAW,UAAU;AAC9B,YAAM,aAAa,MAAM,KAAK,mBAAmB,KAAK,KAAK,CAAC,OAAO,GAAG,OAAO;AAC7E,UAAI,WAAY,SAAQ,KAAK,OAAO;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAEA,MAAI,eAAyE,CAAC;AAC9E,MAAI,iBAAiB;AACnB,QAAI;AACF,YAAM,QAAmC;AAAA,QACvC,UAAU;AAAA,QACV,eAAe;AAAA,MACjB;AACA,YAAM,OAAO;AAAA,QACX,EAAE,KAAK,CAAC,EAAE,gBAAgB,wBAAwB,OAAU,GAAG,EAAE,gBAAgB,KAAK,CAAC,EAAE;AAAA,QACzF,EAAE,KAAK,CAAC,EAAE,UAAU,kBAAkB,OAAU,GAAG,EAAE,UAAU,KAAK,CAAC,EAAE;AAAA,MACzE;AACA,YAAM,WAAW,MAAM,GAAG,KAAK,cAAc,OAAO,EAAE,SAAS,EAAE,OAAO,MAAM,EAAE,CAAC;AACjF,qBAAe,SAAS,IAAI,CAAC,YAAY;AAAA,QACvC,UAAU,OAAO;AAAA,QACjB,OAAO,OAAO;AAAA,QACd,MAAM,0BAA0B,mBAAmB,OAAO,QAAQ,CAAC;AAAA,MACrE,EAAE;AAAA,IACJ,QAAQ;AACN,qBAAe,CAAC;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,UAAU;AAAA,IACd,OAAO,KAAK,SAAS,CAAC;AAAA,IACtB,KAAK,KAAK;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,EACT;AACA,QAAM,UAAU,kBACZ,MAAM;AAAA,IACJ;AAAA,IACA,EAAE,MAAM,QAAQ;AAAA,IAChB;AAAA,IACA;AAAA,IACA,EAAE,eAAe,eAAe;AAAA,EAClC,IACA,CAAC;AAEL,MAAI,iBAAoD;AACxD,MAAI,iBAAoD;AAExD,MAAI,MAAM,QAAQ,KAAK,KAAK,KAAK,KAAK,MAAM,SAAS,GAAG;AACtD,UAAM,cAAc,iBAChB,MAAM,GAAG,KAAK,MAAM;AAAA,MAClB,MAAM,EAAE,KAAK,KAAK,MAAM;AAAA,MACxB,UAAU;AAAA,IACZ,CAAC,IACD,CAAC;AACL,UAAM,UAAU,MAAM,QAAQ,WAAW,IAAI,YAAY,IAAI,CAAC,SAAS,KAAK,EAAE,IAAI,CAAC;AACnF,QAAI,QAAQ,SAAS,GAAG;AACtB,uBAAiB,MAAM,+BAA+B,IAAI;AAAA,QACxD;AAAA,QACA,UAAU;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,kBAAkB,KAAK,WAAW,KAAK,SAAS,KAAK;AAC3D,MAAI,iBAAiB;AACnB,qBAAiB,MAAM,sBAAsB,IAAI;AAAA,MAC/C,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,gBAAgB;AAAA,MAChB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,aAAa,MAAM,aAAa,OAAO;AAC7C,QAAM,iBAAiB,iBACnB,uBAA2C,YAAY,cAAc,IACrE;AACJ,QAAM,cAAc,qBAAqB,cAAc;AACvD,QAAM,gBAAgB,iBAClB,uBAA2C,aAAa,cAAc,IACtE;AAEJ,QAAM,mBAAmB,MAAM;AAAA,IAC7B;AAAA,MACE,sBAAsB,SAAS,oBAAoB;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,wBAAwB,UAAU,mCAAmC,OAAO,IAAI;AACtF,QAAM,yBAAyB,0BAA0B,yBAAyB,KAAK,SAAS;AAChG,QAAM,sBAAsB,yBACtB,0BAA0B,CAAC,4BAA4B,sBAAsB,IAAI,yBAAyB;AAEhH,MAAI,QAAuC;AAK3C,MAAI,sBAAmE;AACvE,MAAI,uBAAuB,gBAAgB;AACzC,QAAI;AACF,YAAM,eAAe,MAAM;AAAA,QACzB;AAAA,QACA;AAAA,QACA,EAAE,IAAI,qBAAqB,QAAQ,gBAAgB,WAAW,KAAK;AAAA,QACnE;AAAA,QACA,EAAE,UAAU,gBAAgB,gBAAgB,oBAAoB;AAAA,MAClE;AAIA,UAAI,gBAAgB,qCAAqC,qBAAqB;AAC5E,8BAAsB,EAAE,IAAI,OAAO,aAAa,EAAE,GAAG,MAAM,aAAa,KAAK;AAAA,MAC/E;AACA,UAAI,cAAc,SAAS;AACzB,gBAAQ;AAAA,UACN,MAAM,aAAa;AAAA,UACnB,MAAM;AAAA,YACJ,KAAK,aAAa;AAAA,YAClB,KAAK,GAAG,aAAa,IAAI;AAAA,YACzB,qBAAqB,CAAC,CAAC,aAAa;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAEN,cAAQ;AACR,4BAAsB;AAAA,IACxB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,cAAc,IAAI,CAAC,EAAE,QAAQ,SAAS,GAAG,MAAM,MAAM,KAAK;AAAA,IAClE;AAAA,IACA,sBAAsB,4BAA4B,sBAAsB,SAAS,oBAAoB,CAAC;AAAA,IACtG,iBAAiB,MAAM,uBAAuB,eAAe;AAAA,IAC7D;AAAA,IACA;AAAA,IACA,OAAO,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC;AAAA,IACjD;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import * as React from 'react'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport type { BackendRouteManifestEntry } from '@open-mercato/shared/modules/registry'\nimport type {\n BackendChromePayload,\n BackendChromeNavGroup,\n BackendChromeNavItem,\n BackendChromeSectionGroup,\n BackendChromeSectionItem,\n} from '@open-mercato/shared/modules/navigation/backendChrome'\nimport {\n buildAdminNav,\n buildSettingsSections,\n computeSettingsPathPrefixes,\n convertToSectionNavGroups,\n type AdminNavItem,\n} from '@open-mercato/ui/backend/utils/nav'\nimport { resolveRegisteredLucideIconNode } from '@open-mercato/ui/backend/icons/lucideRegistry'\nimport { profilePathPrefixes, profileSections } from './profile-sections'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getNavGroupOrderOverride } from '@open-mercato/shared/modules/overrides'\nimport {\n getSelectedOrganizationFromRequest,\n resolveFeatureCheckContext,\n} from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { CustomEntity } from '@open-mercato/core/modules/entities/data/entities'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n applySidebarPreference,\n loadFirstRoleSidebarPreference,\n loadSidebarPreference,\n} from '@open-mercato/core/modules/auth/services/sidebarPreferencesService'\nimport type { SidebarPreferencesSettings } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\n\ntype TranslationFn = (key: string | undefined, fallback: string) => string\n\ntype RouteModule = {\n id: string\n backendRoutes?: BackendRouteManifestEntry[]\n}\n\nexport function groupBackendRoutesByModule(routes: BackendRouteManifestEntry[]): RouteModule[] {\n return Array.from(\n routes.reduce((grouped, route) => {\n const list = grouped.get(route.moduleId) ?? []\n list.push(route)\n grouped.set(route.moduleId, list)\n return grouped\n }, new Map<string, BackendRouteManifestEntry[]>()),\n ).map(([id, backendRoutes]) => ({ id, backendRoutes }))\n}\n\ntype SerializableSectionItem = {\n id: string\n label: string\n labelKey?: string\n href: string\n icon?: React.ReactNode\n order?: number\n children?: SerializableSectionItem[]\n}\n\ntype SerializableSectionGroup = {\n id: string\n label: string\n labelKey?: string\n order?: number\n items: SerializableSectionItem[]\n}\n\ntype ResolvedNavItem = Omit<BackendChromeNavItem, 'defaultTitle' | 'children'> & {\n defaultTitle: string\n children?: ResolvedNavItem[]\n}\n\ntype ResolveBackendChromePayloadArgs = {\n auth: Exclude<AuthContext, null>\n locale: string\n modules: RouteModule[]\n translate: TranslationFn\n request?: Request\n selectedOrganizationId?: string | null\n selectedTenantId?: string | null\n}\n\n/**\n * Settings section weights, keyed by the untranslated group id each page declares as `pageGroupKey`.\n *\n * Mirrors `defaultGroupOrder` above: an id, never a rendered label, so the panel keeps its intended\n * order in every locale and an app-side module can place its own section deterministically (#4843).\n */\nexport const settingsSectionOrder: Record<string, number> = {\n 'settings.sections.system': 1,\n 'settings.sections.auth': 2,\n 'customer_accounts.settings.section': 3,\n 'settings.sections.dataDesigner': 4,\n 'settings.sections.moduleConfigs': 5,\n 'currencies.nav.group': 6,\n 'settings.sections.directory': 7,\n 'settings.sections.featureToggles': 8,\n}\n\ntype NavGroupWithWeight = Omit<BackendChromeNavGroup, 'id' | 'defaultName' | 'items'> & {\n id: string\n defaultName: string\n items: ResolvedNavItem[]\n weight: number\n}\n\nlet renderToStaticMarkupPromise: Promise<typeof import('react-dom/server')> | null = null\n\nasync function serializeIconMarkup(icon: React.ReactNode | undefined): Promise<string | undefined> {\n if (!icon) return undefined\n if (!renderToStaticMarkupPromise) {\n renderToStaticMarkupPromise = import('react-dom/server')\n }\n const { renderToStaticMarkup } = await renderToStaticMarkupPromise\n\n const normalizedIcon = typeof icon === 'string'\n ? resolveRegisteredLucideIconNode(icon, 'size-4')\n : icon\n\n if (!normalizedIcon) return undefined\n\n try {\n const markup = renderToStaticMarkup(<>{normalizedIcon}</>)\n return markup.trim().length > 0 ? markup : undefined\n } catch {\n // Some icon values may be client-only component references after dependency upgrades.\n // Avoid taking down the entire nav payload because one icon cannot be rendered server-side.\n return undefined\n }\n}\n\nasync function serializeNavItem(item: AdminNavItem): Promise<ResolvedNavItem> {\n return {\n id: item.href,\n href: item.href,\n title: item.title,\n defaultTitle: item.defaultTitle,\n enabled: item.enabled,\n hidden: item.hidden,\n pageContext: item.pageContext,\n iconName: typeof item.icon === 'string' ? item.icon : undefined,\n iconMarkup: await serializeIconMarkup(item.icon),\n children: item.children ? await Promise.all(item.children.map((child) => serializeNavItem(child))) : undefined,\n }\n}\n\nconst defaultGroupOrder = [\n 'customers.nav.group',\n 'catalog.nav.group',\n 'customers~sales.nav.group',\n 'wms.nav.group',\n 'resources.nav.group',\n 'staff.nav.group',\n 'entities.nav.group',\n 'directory.nav.group',\n 'attachments.nav.group',\n]\n\n/**\n * Group ids ranked ahead of everything else, most significant first.\n *\n * An app may prepend its own ids via `overrides.nav.groupOrder` in `modules.ts`; ids it does not name\n * keep the ordering they have today. With no override configured this returns `defaultGroupOrder`\n * itself, so ordering is unchanged for every existing install.\n */\nfunction resolveGroupOrder(): string[] {\n const override = getNavGroupOrderOverride()\n if (!override || override.length === 0) return defaultGroupOrder\n const overridden = new Set(override)\n return [...override, ...defaultGroupOrder.filter((id) => !overridden.has(id))]\n}\n\nfunction normalizeGroupWeights(groups: NavGroupWithWeight[]): NavGroupWithWeight[] {\n const groupOrder = resolveGroupOrder()\n const groupOrderIndex = new Map(groupOrder.map((id, index) => [id, index]))\n groups.sort((a, b) => {\n const aIndex = groupOrderIndex.get(a.id)\n const bIndex = groupOrderIndex.get(b.id)\n if (aIndex !== undefined || bIndex !== undefined) {\n if (aIndex === undefined) return 1\n if (bIndex === undefined) return -1\n if (aIndex !== bIndex) return aIndex - bIndex\n }\n if (a.weight !== b.weight) return a.weight - b.weight\n return a.name.localeCompare(b.name)\n })\n const defaultGroupCount = groupOrder.length\n groups.forEach((group, index) => {\n const rank = groupOrderIndex.get(group.id)\n const fallbackWeight = typeof group.weight === 'number' ? group.weight : 10_000\n group.weight =\n (rank !== undefined ? rank : defaultGroupCount + index) * 1_000_000 +\n Math.min(Math.max(fallbackWeight, 0), 999_999)\n })\n return groups\n}\n\nasync function groupEntries(entries: AdminNavItem[]): Promise<NavGroupWithWeight[]> {\n const groupMap = new Map<string, NavGroupWithWeight>()\n for (const entry of entries) {\n const weight = entry.priority ?? entry.order ?? 10_000\n const serializedItem = await serializeNavItem(entry)\n const existing = groupMap.get(entry.groupId)\n if (existing) {\n existing.items.push(serializedItem)\n if (weight < existing.weight) existing.weight = weight\n continue\n }\n groupMap.set(entry.groupId, {\n id: entry.groupId,\n name: entry.group,\n defaultName: entry.groupDefaultName,\n items: [serializedItem],\n weight,\n })\n }\n return normalizeGroupWeights(Array.from(groupMap.values()))\n}\n\nfunction adoptSidebarDefaults(groups: NavGroupWithWeight[]): NavGroupWithWeight[] {\n const adoptItems = (items: ResolvedNavItem[]): ResolvedNavItem[] =>\n items.map((item) => ({\n ...item,\n defaultTitle: item.title,\n children: item.children ? adoptItems(item.children) : undefined,\n }))\n\n return groups.map((group) => ({\n ...group,\n defaultName: group.name,\n items: adoptItems(group.items),\n }))\n}\n\nasync function serializeSectionItem(item: {\n id: string\n label: string\n labelKey?: string\n href: string\n icon?: React.ReactNode\n order?: number\n children?: SerializableSectionItem[]\n}): Promise<BackendChromeSectionItem> {\n return {\n id: item.id,\n label: item.label,\n labelKey: item.labelKey,\n href: item.href,\n order: item.order,\n iconName: typeof item.icon === 'string' ? item.icon : undefined,\n iconMarkup: await serializeIconMarkup(item.icon),\n children: item.children ? await Promise.all(item.children.map((child) => serializeSectionItem(child))) : undefined,\n }\n}\n\nasync function serializeSectionGroups(groups: SerializableSectionGroup[]): Promise<BackendChromeSectionGroup[]> {\n return Promise.all(groups.map(async (group) => ({\n id: group.id,\n label: group.label,\n labelKey: group.labelKey,\n order: group.order,\n items: await Promise.all(group.items.map((item) => serializeSectionItem(item))),\n })))\n}\n\nasync function loadScopedContainer(): Promise<AwilixContainer> {\n return createRequestContainer()\n}\n\nexport async function resolveBackendChromePayload({\n auth,\n locale,\n modules,\n translate,\n request,\n selectedOrganizationId,\n selectedTenantId,\n}: ResolveBackendChromePayloadArgs): Promise<BackendChromePayload> {\n const container = await loadScopedContainer()\n const em = container.resolve('em') as EntityManager\n const rbac = container.resolve('rbacService') as {\n getEffectiveFeatures: (userId: string, scope: { tenantId: string | null; organizationId: string | null }) => Promise<string[]>\n userHasAllFeatures: (userId: string, required: string[], scope: { tenantId: string | null; organizationId: string | null }) => Promise<boolean>\n }\n\n let scopedOrganizationId: string | null = auth.orgId ?? null\n let scopedTenantId: string | null = auth.tenantId ?? null\n // The organization the caller actually *selected*, as distinct from the one the scope resolver fell\n // back to. `resolveFeatureCheckContext` resolves `organizationId` to `auth.orgId` when no concrete\n // organization is selected \u2014 which is precisely what an all-organizations view produces \u2014 so the\n // resolved id cannot answer \"which organization am I viewing\".\n let concretelySelectedOrganizationId: string | null = null\n let allowNavigation = true\n\n try {\n const { organizationId, scope, allowedOrganizationIds } = await resolveFeatureCheckContext({\n container,\n auth,\n request,\n selectedId: selectedOrganizationId,\n tenantId: selectedTenantId,\n })\n scopedOrganizationId = organizationId\n scopedTenantId = scope.tenantId ?? auth.tenantId ?? null\n concretelySelectedOrganizationId = scope.selectedId ?? null\n if (Array.isArray(allowedOrganizationIds) && allowedOrganizationIds.length === 0) {\n allowNavigation = false\n }\n } catch {\n scopedOrganizationId = auth.orgId ?? null\n scopedTenantId = auth.tenantId ?? null\n concretelySelectedOrganizationId = null\n }\n\n const grantedFeatures = allowNavigation\n ? await rbac.getEffectiveFeatures(auth.sub, {\n tenantId: scopedTenantId,\n organizationId: scopedOrganizationId,\n })\n : []\n const featureChecker = async (features: string[]): Promise<string[]> => {\n if (!allowNavigation || !features.length) return []\n const context = {\n tenantId: scopedTenantId ?? auth.tenantId ?? null,\n organizationId: scopedOrganizationId ?? null,\n }\n const hasAll = await rbac.userHasAllFeatures(auth.sub, features, context)\n if (hasAll) return features\n\n const granted: string[] = []\n for (const feature of features) {\n const hasFeature = await rbac.userHasAllFeatures(auth.sub, [feature], context)\n if (hasFeature) granted.push(feature)\n }\n return granted\n }\n\n let userEntities: Array<{ entityId: string; label: string; href: string }> = []\n if (allowNavigation) {\n try {\n const where: FilterQuery<CustomEntity> = {\n isActive: true,\n showInSidebar: true,\n }\n where.$and = [\n { $or: [{ organizationId: scopedOrganizationId ?? undefined }, { organizationId: null }] },\n { $or: [{ tenantId: scopedTenantId ?? undefined }, { tenantId: null }] },\n ]\n const entities = await em.find(CustomEntity, where, { orderBy: { label: 'asc' } })\n userEntities = entities.map((entity) => ({\n entityId: entity.entityId,\n label: entity.label,\n href: `/backend/entities/user/${encodeURIComponent(entity.entityId)}/records`,\n }))\n } catch {\n userEntities = []\n }\n }\n\n const ctxAuth = {\n roles: auth.roles || [],\n sub: auth.sub,\n tenantId: scopedTenantId,\n orgId: scopedOrganizationId,\n }\n const entries = allowNavigation\n ? await buildAdminNav(\n modules,\n { auth: ctxAuth },\n userEntities,\n translate,\n { checkFeatures: featureChecker },\n )\n : []\n\n let rolePreference: SidebarPreferencesSettings | null = null\n let userPreference: SidebarPreferencesSettings | null = null\n\n if (Array.isArray(auth.roles) && auth.roles.length > 0) {\n const roleRecords = scopedTenantId\n ? await em.find(Role, {\n name: { $in: auth.roles },\n tenantId: scopedTenantId,\n })\n : []\n const roleIds = Array.isArray(roleRecords) ? roleRecords.map((role) => role.id) : []\n if (roleIds.length > 0) {\n rolePreference = await loadFirstRoleSidebarPreference(em, {\n roleIds,\n tenantId: scopedTenantId,\n locale,\n })\n }\n }\n\n const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub\n if (effectiveUserId) {\n userPreference = await loadSidebarPreference(em, {\n userId: effectiveUserId,\n tenantId: scopedTenantId,\n organizationId: scopedOrganizationId,\n locale,\n })\n }\n\n const baseGroups = await groupEntries(entries)\n const groupsWithRole = rolePreference\n ? applySidebarPreference<NavGroupWithWeight>(baseGroups, rolePreference)\n : baseGroups\n const baseForUser = adoptSidebarDefaults(groupsWithRole)\n const appliedGroups = userPreference\n ? applySidebarPreference<NavGroupWithWeight>(baseForUser, userPreference)\n : baseForUser\n\n const settingsSections = await serializeSectionGroups(\n convertToSectionNavGroups(\n buildSettingsSections(entries, settingsSectionOrder),\n translate,\n ),\n )\n\n const requestOrganizationId = request ? getSelectedOrganizationFromRequest(request) : null\n const fallbackOrganizationId = selectedOrganizationId ?? requestOrganizationId ?? auth.orgId ?? null\n const brandOrganizationId = scopedOrganizationId\n ?? (fallbackOrganizationId && !isAllOrganizationsSelection(fallbackOrganizationId) ? fallbackOrganizationId : null)\n\n let brand: BackendChromePayload['brand'] = null\n // Resolved here rather than left to callers. `brand` only populates when the organization has a\n // logo, so it is a branding channel, not a dependable \"which organization am I viewing\" source.\n // Without this field every downstream app has to fetch `/api/directory/organization-switcher` and\n // walk its tree for the selected id. The row is already loaded below, so the name costs nothing.\n let currentOrganization: BackendChromePayload['currentOrganization'] = null\n if (brandOrganizationId && scopedTenantId) {\n try {\n const organization = await findOneWithDecryption(\n em,\n Organization,\n { id: brandOrganizationId, tenant: scopedTenantId, deletedAt: null },\n undefined,\n { tenantId: scopedTenantId, organizationId: brandOrganizationId },\n )\n // Only when a concrete organization was selected. Under an all-organizations view\n // `brandOrganizationId` still resolves (to the caller's own organization, which is what keeps\n // branding working), so gating on the loaded row alone would misreport the scope.\n if (organization && concretelySelectedOrganizationId === brandOrganizationId) {\n currentOrganization = { id: String(organization.id), name: organization.name }\n }\n if (organization?.logoUrl) {\n brand = {\n name: organization.name,\n logo: {\n src: organization.logoUrl,\n alt: `${organization.name} logo`,\n preserveAspectRatio: !!organization.logoPreserveAspectRatio,\n },\n }\n }\n } catch {\n // Fail soft, as before: a failed organization lookup must not take down the nav payload.\n brand = null\n currentOrganization = null\n }\n }\n\n return {\n groups: appliedGroups.map(({ weight: _weight, ...group }) => group),\n settingsSections,\n settingsPathPrefixes: computeSettingsPathPrefixes(buildSettingsSections(entries, settingsSectionOrder)),\n profileSections: await serializeSectionGroups(profileSections),\n profilePathPrefixes,\n grantedFeatures,\n roles: Array.isArray(auth.roles) ? auth.roles : [],\n brand,\n currentOrganization,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -57,9 +57,16 @@ async function enforceTenantSelection(ctx, requested) {
57
57
  if (normalized === actorTenant) return actorTenant;
58
58
  throw forbidden("Not authorized to target this tenant.");
59
59
  }
60
+ async function requireSuperAdmin(ctx) {
61
+ const isSuperAdmin = await resolveIsSuperAdmin(ctx);
62
+ if (!isSuperAdmin) {
63
+ throw forbidden("Forbidden");
64
+ }
65
+ }
60
66
  export {
61
67
  enforceTenantSelection,
62
68
  normalizeTenantId,
69
+ requireSuperAdmin,
63
70
  resolveIsSuperAdmin
64
71
  };
65
72
  //# sourceMappingURL=tenantAccess.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/lib/tenantAccess.ts"],
4
- "sourcesContent": ["import { forbidden } from '@open-mercato/shared/lib/crud/errors'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('auth').child({ component: 'tenant-access' })\n\ntype TenantGuardCtx = {\n auth: {\n sub?: string | null\n tenantId?: string | null\n orgId?: string | null\n roles?: string[]\n isSuperAdmin?: boolean\n } | null\n container: { resolve<T = unknown>(name: string): T }\n}\n\nconst SUPER_ADMIN_SYMBOL = Symbol.for('auth.tenantGuard.isSuperAdmin')\n\nexport function normalizeTenantId(value: unknown): string | null | undefined {\n if (value === undefined) return undefined\n if (value === null) return null\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n }\n return null\n}\n\nexport async function resolveIsSuperAdmin(ctx: TenantGuardCtx): Promise<boolean> {\n const cacheStore = ctx as { [SUPER_ADMIN_SYMBOL]?: boolean }\n const cached = cacheStore[SUPER_ADMIN_SYMBOL]\n if (typeof cached === 'boolean') return cached\n const auth = ctx.auth\n if (!auth?.sub) {\n cacheStore[SUPER_ADMIN_SYMBOL] = false\n return false\n }\n if (auth.isSuperAdmin === true) {\n cacheStore[SUPER_ADMIN_SYMBOL] = true\n return true\n }\n try {\n const rbac = (ctx.container.resolve('rbacService') as RbacService)\n const acl = await rbac.loadAcl(auth.sub, {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n })\n const value = !!acl?.isSuperAdmin\n cacheStore[SUPER_ADMIN_SYMBOL] = value\n return value\n } catch (error) {\n logger.error('Failed to resolve rbac', { err: error })\n cacheStore[SUPER_ADMIN_SYMBOL] = false\n return false\n }\n}\n\nexport async function enforceTenantSelection(ctx: TenantGuardCtx, requested: unknown): Promise<string | null> {\n const normalized = normalizeTenantId(requested)\n const actorTenant = normalizeTenantId(ctx.auth?.tenantId ?? null) ?? null\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n\n if (isSuperAdmin) {\n if (normalized === undefined) return actorTenant\n return normalized ?? null\n }\n\n if (!actorTenant) {\n if (normalized && normalized !== null) {\n throw forbidden('Not authorized to target this tenant.')\n }\n return actorTenant\n }\n\n if (normalized === undefined) return actorTenant\n if (normalized === actorTenant) return actorTenant\n throw forbidden('Not authorized to target this tenant.')\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAiB;AAE1B,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,MAAM,EAAE,MAAM,EAAE,WAAW,gBAAgB,CAAC;AAaxE,MAAM,qBAAqB,uBAAO,IAAI,+BAA+B;AAE9D,SAAS,kBAAkB,OAA2C;AAC3E,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,UAAU,MAAM,KAAK;AAC3B,WAAO,QAAQ,SAAS,IAAI,UAAU;AAAA,EACxC;AACA,SAAO;AACT;AAEA,eAAsB,oBAAoB,KAAuC;AAC/E,QAAM,aAAa;AACnB,QAAM,SAAS,WAAW,kBAAkB;AAC5C,MAAI,OAAO,WAAW,UAAW,QAAO;AACxC,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,MAAM,KAAK;AACd,eAAW,kBAAkB,IAAI;AACjC,WAAO;AAAA,EACT;AACA,MAAI,KAAK,iBAAiB,MAAM;AAC9B,eAAW,kBAAkB,IAAI;AACjC,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,OAAQ,IAAI,UAAU,QAAQ,aAAa;AACjD,UAAM,MAAM,MAAM,KAAK,QAAQ,KAAK,KAAK;AAAA,MACvC,UAAU,KAAK,YAAY;AAAA,MAC3B,gBAAgB,KAAK,SAAS;AAAA,IAChC,CAAC;AACD,UAAM,QAAQ,CAAC,CAAC,KAAK;AACrB,eAAW,kBAAkB,IAAI;AACjC,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO,MAAM,0BAA0B,EAAE,KAAK,MAAM,CAAC;AACrD,eAAW,kBAAkB,IAAI;AACjC,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,uBAAuB,KAAqB,WAA4C;AAC5G,QAAM,aAAa,kBAAkB,SAAS;AAC9C,QAAM,cAAc,kBAAkB,IAAI,MAAM,YAAY,IAAI,KAAK;AACrE,QAAM,eAAe,MAAM,oBAAoB,GAAG;AAElD,MAAI,cAAc;AAChB,QAAI,eAAe,OAAW,QAAO;AACrC,WAAO,cAAc;AAAA,EACvB;AAEA,MAAI,CAAC,aAAa;AAChB,QAAI,cAAc,eAAe,MAAM;AACrC,YAAM,UAAU,uCAAuC;AAAA,IACzD;AACA,WAAO;AAAA,EACT;AAEA,MAAI,eAAe,OAAW,QAAO;AACrC,MAAI,eAAe,YAAa,QAAO;AACvC,QAAM,UAAU,uCAAuC;AACzD;",
4
+ "sourcesContent": ["import { forbidden } from '@open-mercato/shared/lib/crud/errors'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('auth').child({ component: 'tenant-access' })\n\ntype TenantGuardCtx = {\n auth: {\n sub?: string | null\n tenantId?: string | null\n orgId?: string | null\n roles?: string[]\n isSuperAdmin?: boolean\n } | null\n container: { resolve<T = unknown>(name: string): T }\n}\n\nconst SUPER_ADMIN_SYMBOL = Symbol.for('auth.tenantGuard.isSuperAdmin')\n\nexport function normalizeTenantId(value: unknown): string | null | undefined {\n if (value === undefined) return undefined\n if (value === null) return null\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n }\n return null\n}\n\nexport async function resolveIsSuperAdmin(ctx: TenantGuardCtx): Promise<boolean> {\n const cacheStore = ctx as { [SUPER_ADMIN_SYMBOL]?: boolean }\n const cached = cacheStore[SUPER_ADMIN_SYMBOL]\n if (typeof cached === 'boolean') return cached\n const auth = ctx.auth\n if (!auth?.sub) {\n cacheStore[SUPER_ADMIN_SYMBOL] = false\n return false\n }\n if (auth.isSuperAdmin === true) {\n cacheStore[SUPER_ADMIN_SYMBOL] = true\n return true\n }\n try {\n const rbac = (ctx.container.resolve('rbacService') as RbacService)\n const acl = await rbac.loadAcl(auth.sub, {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n })\n const value = !!acl?.isSuperAdmin\n cacheStore[SUPER_ADMIN_SYMBOL] = value\n return value\n } catch (error) {\n logger.error('Failed to resolve rbac', { err: error })\n cacheStore[SUPER_ADMIN_SYMBOL] = false\n return false\n }\n}\n\nexport async function enforceTenantSelection(ctx: TenantGuardCtx, requested: unknown): Promise<string | null> {\n const normalized = normalizeTenantId(requested)\n const actorTenant = normalizeTenantId(ctx.auth?.tenantId ?? null) ?? null\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n\n if (isSuperAdmin) {\n if (normalized === undefined) return actorTenant\n return normalized ?? null\n }\n\n if (!actorTenant) {\n if (normalized && normalized !== null) {\n throw forbidden('Not authorized to target this tenant.')\n }\n return actorTenant\n }\n\n if (normalized === undefined) return actorTenant\n if (normalized === actorTenant) return actorTenant\n throw forbidden('Not authorized to target this tenant.')\n}\n\nexport async function requireSuperAdmin(ctx: TenantGuardCtx): Promise<void> {\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n if (!isSuperAdmin) {\n throw forbidden('Forbidden')\n }\n}\n\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAE1B,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,MAAM,EAAE,MAAM,EAAE,WAAW,gBAAgB,CAAC;AAaxE,MAAM,qBAAqB,uBAAO,IAAI,+BAA+B;AAE9D,SAAS,kBAAkB,OAA2C;AAC3E,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,UAAU,MAAM,KAAK;AAC3B,WAAO,QAAQ,SAAS,IAAI,UAAU;AAAA,EACxC;AACA,SAAO;AACT;AAEA,eAAsB,oBAAoB,KAAuC;AAC/E,QAAM,aAAa;AACnB,QAAM,SAAS,WAAW,kBAAkB;AAC5C,MAAI,OAAO,WAAW,UAAW,QAAO;AACxC,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,MAAM,KAAK;AACd,eAAW,kBAAkB,IAAI;AACjC,WAAO;AAAA,EACT;AACA,MAAI,KAAK,iBAAiB,MAAM;AAC9B,eAAW,kBAAkB,IAAI;AACjC,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,OAAQ,IAAI,UAAU,QAAQ,aAAa;AACjD,UAAM,MAAM,MAAM,KAAK,QAAQ,KAAK,KAAK;AAAA,MACvC,UAAU,KAAK,YAAY;AAAA,MAC3B,gBAAgB,KAAK,SAAS;AAAA,IAChC,CAAC;AACD,UAAM,QAAQ,CAAC,CAAC,KAAK;AACrB,eAAW,kBAAkB,IAAI;AACjC,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO,MAAM,0BAA0B,EAAE,KAAK,MAAM,CAAC;AACrD,eAAW,kBAAkB,IAAI;AACjC,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,uBAAuB,KAAqB,WAA4C;AAC5G,QAAM,aAAa,kBAAkB,SAAS;AAC9C,QAAM,cAAc,kBAAkB,IAAI,MAAM,YAAY,IAAI,KAAK;AACrE,QAAM,eAAe,MAAM,oBAAoB,GAAG;AAElD,MAAI,cAAc;AAChB,QAAI,eAAe,OAAW,QAAO;AACrC,WAAO,cAAc;AAAA,EACvB;AAEA,MAAI,CAAC,aAAa;AAChB,QAAI,cAAc,eAAe,MAAM;AACrC,YAAM,UAAU,uCAAuC;AAAA,IACzD;AACA,WAAO;AAAA,EACT;AAEA,MAAI,eAAe,OAAW,QAAO;AACrC,MAAI,eAAe,YAAa,QAAO;AACvC,QAAM,UAAU,uCAAuC;AACzD;AAEA,eAAsB,kBAAkB,KAAoC;AAC1E,QAAM,eAAe,MAAM,oBAAoB,GAAG;AAClD,MAAI,CAAC,cAAc;AACjB,UAAM,UAAU,WAAW;AAAA,EAC7B;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,85 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import {
6
+ getCurrentOpenMercatoEndpointOptions,
7
+ listOpenMercatoApiKeyOptions
8
+ } from "../../lib/openmercato-call-options.js";
9
+ const roleOptionSchema = z.object({
10
+ id: z.string(),
11
+ name: z.string().nullable()
12
+ });
13
+ const endpointOptionSchema = z.object({
14
+ id: z.string(),
15
+ path: z.string(),
16
+ method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
17
+ label: z.string(),
18
+ summary: z.string().nullable(),
19
+ operationId: z.string().nullable()
20
+ });
21
+ const apiKeyOptionSchema = z.object({
22
+ id: z.string(),
23
+ name: z.string(),
24
+ keyPrefix: z.string(),
25
+ organizationId: z.string().nullable(),
26
+ organizationName: z.string().nullable(),
27
+ roles: z.array(roleOptionSchema)
28
+ });
29
+ const optionsResponseSchema = z.object({
30
+ endpoints: z.array(endpointOptionSchema),
31
+ apiKeys: z.array(apiKeyOptionSchema)
32
+ });
33
+ const errorResponseSchema = z.object({
34
+ error: z.string()
35
+ });
36
+ const metadata = {
37
+ GET: { requireAuth: true, requireFeatures: ["business_rules.manage", "api_keys.create"] }
38
+ };
39
+ async function GET(req) {
40
+ const auth = await getAuthFromRequest(req);
41
+ if (!auth) {
42
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
43
+ }
44
+ if (!auth.tenantId) {
45
+ return NextResponse.json({ error: "Tenant context required" }, { status: 400 });
46
+ }
47
+ const container = await createRequestContainer();
48
+ const em = container.resolve("em");
49
+ const [endpoints, apiKeys] = await Promise.all([
50
+ getCurrentOpenMercatoEndpointOptions(),
51
+ listOpenMercatoApiKeyOptions(em, {
52
+ tenantId: auth.tenantId,
53
+ organizationId: auth.orgId ?? null
54
+ })
55
+ ]);
56
+ return NextResponse.json({ endpoints, apiKeys });
57
+ }
58
+ const openApi = {
59
+ tag: "Business Rules",
60
+ summary: "List OpenMercato call action options",
61
+ methods: {
62
+ GET: {
63
+ summary: "List OpenMercato call action options",
64
+ description: "Returns executable Open Mercato API endpoint options and safe API key profile metadata for business-rule actions.",
65
+ responses: [
66
+ {
67
+ status: 200,
68
+ description: "OpenMercato call options",
69
+ schema: optionsResponseSchema
70
+ }
71
+ ],
72
+ errors: [
73
+ { status: 400, description: "Tenant context missing", schema: errorResponseSchema },
74
+ { status: 401, description: "Unauthorized", schema: errorResponseSchema },
75
+ { status: 403, description: "Missing required features", schema: errorResponseSchema }
76
+ ]
77
+ }
78
+ }
79
+ };
80
+ export {
81
+ GET,
82
+ metadata,
83
+ openApi
84
+ };
85
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/business_rules/api/openmercato-call-options/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport {\n getCurrentOpenMercatoEndpointOptions,\n listOpenMercatoApiKeyOptions,\n} from '../../lib/openmercato-call-options'\n\nconst roleOptionSchema = z.object({\n id: z.string(),\n name: z.string().nullable(),\n})\n\nconst endpointOptionSchema = z.object({\n id: z.string(),\n path: z.string(),\n method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),\n label: z.string(),\n summary: z.string().nullable(),\n operationId: z.string().nullable(),\n})\n\nconst apiKeyOptionSchema = z.object({\n id: z.string(),\n name: z.string(),\n keyPrefix: z.string(),\n organizationId: z.string().nullable(),\n organizationName: z.string().nullable(),\n roles: z.array(roleOptionSchema),\n})\n\nconst optionsResponseSchema = z.object({\n endpoints: z.array(endpointOptionSchema),\n apiKeys: z.array(apiKeyOptionSchema),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['business_rules.manage', 'api_keys.create'] },\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n if (!auth.tenantId) {\n return NextResponse.json({ error: 'Tenant context required' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const [endpoints, apiKeys] = await Promise.all([\n getCurrentOpenMercatoEndpointOptions(),\n listOpenMercatoApiKeyOptions(em, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n }),\n ])\n\n return NextResponse.json({ endpoints, apiKeys })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Business Rules',\n summary: 'List OpenMercato call action options',\n methods: {\n GET: {\n summary: 'List OpenMercato call action options',\n description:\n 'Returns executable Open Mercato API endpoint options and safe API key profile metadata for business-rule actions.',\n responses: [\n {\n status: 200,\n description: 'OpenMercato call options',\n schema: optionsResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Tenant context missing', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Missing required features', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAGlB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,MAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,IAAI,EAAE,OAAO;AAAA,EACb,MAAM,EAAE,OAAO,EAAE,SAAS;AAC5B,CAAC;AAED,MAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,IAAI,EAAE,OAAO;AAAA,EACb,MAAM,EAAE,OAAO;AAAA,EACf,QAAQ,EAAE,KAAK,CAAC,OAAO,QAAQ,OAAO,SAAS,QAAQ,CAAC;AAAA,EACxD,OAAO,EAAE,OAAO;AAAA,EAChB,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAED,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,IAAI,EAAE,OAAO;AAAA,EACb,MAAM,EAAE,OAAO;AAAA,EACf,WAAW,EAAE,OAAO;AAAA,EACpB,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,EACpC,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,EACtC,OAAO,EAAE,MAAM,gBAAgB;AACjC,CAAC;AAED,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,WAAW,EAAE,MAAM,oBAAoB;AAAA,EACvC,SAAS,EAAE,MAAM,kBAAkB;AACrC,CAAC;AAED,MAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,OAAO,EAAE,OAAO;AAClB,CAAC;AAEM,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,iBAAiB,EAAE;AAC1F;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,MAAI,CAAC,KAAK,UAAU;AAClB,WAAO,aAAa,KAAK,EAAE,OAAO,0BAA0B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAChF;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAK,UAAU,QAAQ,IAAI;AAEjC,QAAM,CAAC,WAAW,OAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC7C,qCAAqC;AAAA,IACrC,6BAA6B,IAAI;AAAA,MAC/B,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK,SAAS;AAAA,IAChC,CAAC;AAAA,EACH,CAAC;AAED,SAAO,aAAa,KAAK,EAAE,WAAW,QAAQ,CAAC;AACjD;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,MACH,SAAS;AAAA,MACT,aACE;AAAA,MACF,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,0BAA0B,QAAQ,oBAAoB;AAAA,QAClF,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,oBAAoB;AAAA,QACxE,EAAE,QAAQ,KAAK,aAAa,6BAA6B,QAAQ,oBAAoB;AAAA,MACvF;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -18,6 +18,7 @@ import {
18
18
  invalidateBusinessRuleDiscoveryCache,
19
19
  resolveBusinessRuleDiscoveryCache
20
20
  } from "../../lib/rule-engine.js";
21
+ import { validateOpenMercatoCallActions } from "../../lib/openmercato-call-options.js";
21
22
  import { createLogger } from "@open-mercato/shared/lib/logger";
22
23
  const logger = createLogger("business_rules").child({ component: "rules-api" });
23
24
  const querySchema = z.looseObject({
@@ -74,6 +75,27 @@ const routeMetadata = {
74
75
  DELETE: { requireAuth: true, requireFeatures: ["business_rules.manage"] }
75
76
  };
76
77
  const metadata = routeMetadata;
78
+ function hasOpenMercatoCallAction(actions) {
79
+ return Array.isArray(actions) && actions.some((action) => action && typeof action === "object" && action.type === "CALL_OPEN_MERCATO");
80
+ }
81
+ async function requireOpenMercatoCallConfiguratorAccess(container, auth) {
82
+ if (!auth.sub) {
83
+ return NextResponse.json(
84
+ { error: "Forbidden", requiredFeatures: ["api_keys.create"] },
85
+ { status: 403 }
86
+ );
87
+ }
88
+ const rbac = container.resolve("rbacService");
89
+ const allowed = await rbac?.userHasAllFeatures?.(auth.sub, ["api_keys.create"], {
90
+ tenantId: auth.tenantId ?? null,
91
+ organizationId: auth.orgId ?? null
92
+ });
93
+ if (allowed) return null;
94
+ return NextResponse.json(
95
+ { error: "Forbidden", requiredFeatures: ["api_keys.create"] },
96
+ { status: 403 }
97
+ );
98
+ }
77
99
  async function GET(req) {
78
100
  const auth = await getAuthFromRequest(req);
79
101
  if (!auth) {
@@ -160,6 +182,11 @@ async function POST(req) {
160
182
  if (!auth) {
161
183
  return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
162
184
  }
185
+ if (!auth.tenantId || !auth.orgId) {
186
+ return NextResponse.json({ error: "Tenant and organization context required" }, { status: 400 });
187
+ }
188
+ const tenantId = auth.tenantId;
189
+ const organizationId = auth.orgId;
163
190
  const container = await createRequestContainer();
164
191
  const em = container.resolve("em");
165
192
  const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container));
@@ -171,8 +198,8 @@ async function POST(req) {
171
198
  }
172
199
  const payload = {
173
200
  ...body,
174
- tenantId: auth.tenantId,
175
- organizationId: auth.orgId,
201
+ tenantId,
202
+ organizationId,
176
203
  createdBy: auth.sub ?? auth.email ?? null
177
204
  };
178
205
  const { t } = await resolveTranslations();
@@ -182,6 +209,23 @@ async function POST(req) {
182
209
  const errors = parsed.error.issues.map((e) => `${e.path.join(".")}: ${e.message}`);
183
210
  return NextResponse.json({ error: `Validation failed: ${errors.join(", ")}` }, { status: 400 });
184
211
  }
212
+ if (hasOpenMercatoCallAction(parsed.data.successActions) || hasOpenMercatoCallAction(parsed.data.failureActions)) {
213
+ const accessError = await requireOpenMercatoCallConfiguratorAccess(container, auth);
214
+ if (accessError) return accessError;
215
+ }
216
+ const actionErrors = [
217
+ ...await validateOpenMercatoCallActions(em, parsed.data.successActions, {
218
+ tenantId,
219
+ organizationId
220
+ }),
221
+ ...await validateOpenMercatoCallActions(em, parsed.data.failureActions, {
222
+ tenantId,
223
+ organizationId
224
+ })
225
+ ];
226
+ if (actionErrors.length > 0) {
227
+ return NextResponse.json({ error: `Validation failed: ${actionErrors.join(", ")}` }, { status: 400 });
228
+ }
185
229
  const data = {
186
230
  ...parsed.data,
187
231
  conditionExpression: parsed.data.conditionExpression ?? null
@@ -204,6 +248,11 @@ async function PUT(req) {
204
248
  if (!auth) {
205
249
  return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
206
250
  }
251
+ if (!auth.tenantId || !auth.orgId) {
252
+ return NextResponse.json({ error: "Tenant and organization context required" }, { status: 400 });
253
+ }
254
+ const tenantId = auth.tenantId;
255
+ const organizationId = auth.orgId;
207
256
  const container = await createRequestContainer();
208
257
  const em = container.resolve("em");
209
258
  const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container));
@@ -232,13 +281,31 @@ async function PUT(req) {
232
281
  }
233
282
  const rule = await em.findOne(BusinessRule, {
234
283
  id: parsed.data.id,
235
- tenantId: auth.tenantId,
236
- organizationId: auth.orgId,
284
+ tenantId,
285
+ organizationId,
237
286
  deletedAt: null
238
287
  });
239
288
  if (!rule) {
240
289
  return NextResponse.json({ error: "Rule not found" }, { status: 404 });
241
290
  }
291
+ const touchesOpenMercatoCall = hasOpenMercatoCallAction(parsed.data.successActions) || hasOpenMercatoCallAction(parsed.data.failureActions) || hasOpenMercatoCallAction(rule.successActions) || hasOpenMercatoCallAction(rule.failureActions);
292
+ if (touchesOpenMercatoCall) {
293
+ const accessError = await requireOpenMercatoCallConfiguratorAccess(container, auth);
294
+ if (accessError) return accessError;
295
+ }
296
+ const actionErrors = [
297
+ ...await validateOpenMercatoCallActions(em, parsed.data.successActions, {
298
+ tenantId,
299
+ organizationId
300
+ }),
301
+ ...await validateOpenMercatoCallActions(em, parsed.data.failureActions, {
302
+ tenantId,
303
+ organizationId
304
+ })
305
+ ];
306
+ if (actionErrors.length > 0) {
307
+ return NextResponse.json({ error: `Validation failed: ${actionErrors.join(", ")}` }, { status: 400 });
308
+ }
242
309
  try {
243
310
  await enforceCommandOptimisticLockWithGuards(container, {
244
311
  resourceKind: "business_rules.rule",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/business_rules/api/rules/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { BusinessRule } from '../../data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport {\n createBusinessRuleSchema,\n updateBusinessRuleSchema,\n createLocalizedBusinessRuleSchema,\n createLocalizedUpdateBusinessRuleSchema,\n businessRuleFilterSchema,\n ruleTypeSchema,\n} from '../../data/validators'\nimport {\n invalidateBusinessRuleDiscoveryCache,\n resolveBusinessRuleDiscoveryCache,\n} from '../../lib/rule-engine'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('business_rules').child({ component: 'rules-api' })\n\nconst querySchema = z.looseObject({\n id: z.uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n ruleId: z.string().optional(),\n ruleType: ruleTypeSchema.optional(),\n entityType: z.string().optional(),\n eventType: z.string().optional(),\n enabled: z.coerce.boolean().optional(),\n ruleCategory: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional().default('desc'),\n})\n\nconst ruleListItemSchema = z.object({\n id: z.string().uuid(),\n ruleId: z.string(),\n ruleName: z.string(),\n description: z.string().nullable(),\n ruleType: ruleTypeSchema,\n ruleCategory: z.string().nullable(),\n entityType: z.string(),\n eventType: z.string().nullable(),\n enabled: z.boolean(),\n priority: z.number(),\n version: z.number(),\n effectiveFrom: z.string().nullable(),\n effectiveTo: z.string().nullable(),\n tenantId: z.string().uuid(),\n organizationId: z.string().uuid(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\nconst ruleListResponseSchema = z.object({\n items: z.array(ruleListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n})\n\nconst ruleCreateResponseSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst okResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['business_rules.view'] },\n POST: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n ruleId: url.searchParams.get('ruleId') || undefined,\n ruleType: url.searchParams.get('ruleType') || undefined,\n entityType: url.searchParams.get('entityType') || undefined,\n eventType: url.searchParams.get('eventType') || undefined,\n enabled: url.searchParams.get('enabled') || undefined,\n ruleCategory: url.searchParams.get('ruleCategory') || undefined,\n sortField: url.searchParams.get('sortField') || undefined,\n sortDir: url.searchParams.get('sortDir') || undefined,\n })\n\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query parameters' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const { id, page, pageSize, search, ruleId, ruleType, entityType, eventType, enabled, ruleCategory, sortField, sortDir } = parsed.data\n\n const filters: Record<string, any> = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n }\n\n if (id) filters.id = id\n if (ruleId) filters.ruleId = { $ilike: `%${escapeLikePattern(ruleId)}%` }\n if (search) filters.ruleName = { $ilike: `%${escapeLikePattern(search)}%` }\n if (ruleType) filters.ruleType = ruleType\n if (entityType) filters.entityType = entityType\n if (eventType) filters.eventType = eventType\n if (enabled !== undefined) filters.enabled = enabled\n if (ruleCategory) filters.ruleCategory = ruleCategory\n\n const sortFieldMap: Record<string, string> = {\n ruleId: 'ruleId',\n ruleName: 'ruleName',\n ruleType: 'ruleType',\n entityType: 'entityType',\n priority: 'priority',\n createdAt: 'createdAt',\n updatedAt: 'updatedAt',\n }\n\n const orderByField = sortField && sortFieldMap[sortField] ? sortFieldMap[sortField] : 'priority'\n const orderBy = { [orderByField]: sortDir, ruleId: 'asc' as const }\n\n const [rows, count] = await em.findAndCount(\n BusinessRule,\n filters,\n {\n limit: pageSize,\n offset: (page - 1) * pageSize,\n orderBy,\n }\n )\n\n const items = rows.map((rule) => ({\n id: rule.id,\n ruleId: rule.ruleId,\n ruleName: rule.ruleName,\n description: rule.description ?? null,\n ruleType: rule.ruleType,\n ruleCategory: rule.ruleCategory ?? null,\n entityType: rule.entityType,\n eventType: rule.eventType ?? null,\n enabled: rule.enabled,\n priority: rule.priority,\n version: rule.version,\n effectiveFrom: rule.effectiveFrom ? rule.effectiveFrom.toISOString() : null,\n effectiveTo: rule.effectiveTo ? rule.effectiveTo.toISOString() : null,\n tenantId: rule.tenantId,\n organizationId: rule.organizationId,\n createdAt: rule.createdAt.toISOString(),\n updatedAt: rule.updatedAt.toISOString(),\n }))\n\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n\n return NextResponse.json({ items, total: count, totalPages })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const payload = {\n ...body,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n createdBy: auth.sub ?? auth.email ?? null,\n }\n\n const { t } = await resolveTranslations()\n const schema = createLocalizedBusinessRuleSchema(t)\n const parsed = schema.safeParse(payload)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const data = {\n ...parsed.data,\n conditionExpression: parsed.data.conditionExpression ?? null,\n }\n\n const rule = em.create(BusinessRule, data)\n\n try {\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)\n } catch (error) {\n logger.error('Failed to persist new rule', { err: error })\n return NextResponse.json(\n { error: t('business_rules.errors.createFailed') },\n { status: 500 },\n )\n }\n\n return NextResponse.json({ id: rule.id }, { status: 201 })\n}\n\nexport async function PUT(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n if (!body.id) {\n return NextResponse.json({ error: 'Rule id is required' }, { status: 400 })\n }\n\n const payload = {\n ...body,\n updatedBy: auth.sub ?? auth.email ?? null,\n }\n delete (payload as Record<string, unknown>).tenantId\n delete (payload as Record<string, unknown>).organizationId\n delete (payload as Record<string, unknown>).createdBy\n\n const { t } = await resolveTranslations()\n const schema = createLocalizedUpdateBusinessRuleSchema(t)\n const parsed = schema.safeParse(payload)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const rule = await em.findOne(BusinessRule, {\n id: parsed.data.id,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n })\n\n if (!rule) {\n return NextResponse.json({ error: 'Rule not found' }, { status: 404 })\n }\n\n try {\n await enforceCommandOptimisticLockWithGuards(container, {\n resourceKind: 'business_rules.rule',\n resourceId: rule.id,\n current: rule.updatedAt ?? null,\n request: req,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n em.assign(rule, parsed.data)\n\n try {\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)\n } catch (error) {\n logger.error('Failed to persist rule update', { err: error })\n return NextResponse.json(\n { error: t('business_rules.errors.updateFailed') },\n { status: 500 },\n )\n }\n\n return NextResponse.json({ ok: true })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const id = url.searchParams.get('id')\n\n if (!id) {\n return NextResponse.json({ error: 'Rule id is required' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n\n const rule = await em.findOne(BusinessRule, {\n id,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n })\n\n if (!rule) {\n return NextResponse.json({ error: 'Rule not found' }, { status: 404 })\n }\n\n try {\n await enforceCommandOptimisticLockWithGuards(container, {\n resourceKind: 'business_rules.rule',\n resourceId: rule.id,\n current: rule.updatedAt ?? null,\n request: req,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n rule.deletedAt = new Date()\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)\n\n return NextResponse.json({ ok: true })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Business Rules',\n summary: 'Business rule management',\n methods: {\n GET: {\n summary: 'List business rules',\n description: 'Returns business rules for the current tenant and organization with filtering and pagination.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Business rules collection', schema: ruleListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid query parameters', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n ],\n },\n POST: {\n summary: 'Create business rule',\n description: 'Creates a new business rule for the current tenant and organization.',\n requestBody: {\n contentType: 'application/json',\n schema: createBusinessRuleSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Business rule created',\n schema: ruleCreateResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update business rule',\n description: 'Updates an existing business rule.',\n requestBody: {\n contentType: 'application/json',\n schema: updateBusinessRuleSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Business rule updated',\n schema: okResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'Business rule not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete business rule',\n description: 'Soft deletes a business rule by identifier.',\n query: z.object({ id: z.string().uuid().describe('Business rule identifier') }),\n responses: [\n { status: 200, description: 'Business rule deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid identifier', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'Business rule not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { BusinessRule } from '../../data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport {\n createBusinessRuleSchema,\n updateBusinessRuleSchema,\n createLocalizedBusinessRuleSchema,\n createLocalizedUpdateBusinessRuleSchema,\n businessRuleFilterSchema,\n ruleTypeSchema,\n} from '../../data/validators'\nimport {\n invalidateBusinessRuleDiscoveryCache,\n resolveBusinessRuleDiscoveryCache,\n} from '../../lib/rule-engine'\nimport { validateOpenMercatoCallActions } from '../../lib/openmercato-call-options'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('business_rules').child({ component: 'rules-api' })\n\nconst querySchema = z.looseObject({\n id: z.uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n ruleId: z.string().optional(),\n ruleType: ruleTypeSchema.optional(),\n entityType: z.string().optional(),\n eventType: z.string().optional(),\n enabled: z.coerce.boolean().optional(),\n ruleCategory: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional().default('desc'),\n})\n\nconst ruleListItemSchema = z.object({\n id: z.string().uuid(),\n ruleId: z.string(),\n ruleName: z.string(),\n description: z.string().nullable(),\n ruleType: ruleTypeSchema,\n ruleCategory: z.string().nullable(),\n entityType: z.string(),\n eventType: z.string().nullable(),\n enabled: z.boolean(),\n priority: z.number(),\n version: z.number(),\n effectiveFrom: z.string().nullable(),\n effectiveTo: z.string().nullable(),\n tenantId: z.string().uuid(),\n organizationId: z.string().uuid(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\nconst ruleListResponseSchema = z.object({\n items: z.array(ruleListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n})\n\nconst ruleCreateResponseSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst okResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['business_rules.view'] },\n POST: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nfunction hasOpenMercatoCallAction(actions: unknown): boolean {\n return Array.isArray(actions)\n && actions.some((action) => action && typeof action === 'object' && (action as { type?: unknown }).type === 'CALL_OPEN_MERCATO')\n}\n\nasync function requireOpenMercatoCallConfiguratorAccess(\n container: ReturnType<typeof createRequestContainer> extends Promise<infer T> ? T : never,\n auth: NonNullable<Awaited<ReturnType<typeof getAuthFromRequest>>>,\n): Promise<Response | null> {\n if (!auth.sub) {\n return NextResponse.json(\n { error: 'Forbidden', requiredFeatures: ['api_keys.create'] },\n { status: 403 },\n )\n }\n\n const rbac = container.resolve('rbacService') as {\n userHasAllFeatures?: (\n userId: string,\n required: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n } | undefined\n const allowed = await rbac?.userHasAllFeatures?.(auth.sub, ['api_keys.create'], {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n })\n if (allowed) return null\n\n return NextResponse.json(\n { error: 'Forbidden', requiredFeatures: ['api_keys.create'] },\n { status: 403 },\n )\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n ruleId: url.searchParams.get('ruleId') || undefined,\n ruleType: url.searchParams.get('ruleType') || undefined,\n entityType: url.searchParams.get('entityType') || undefined,\n eventType: url.searchParams.get('eventType') || undefined,\n enabled: url.searchParams.get('enabled') || undefined,\n ruleCategory: url.searchParams.get('ruleCategory') || undefined,\n sortField: url.searchParams.get('sortField') || undefined,\n sortDir: url.searchParams.get('sortDir') || undefined,\n })\n\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query parameters' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const { id, page, pageSize, search, ruleId, ruleType, entityType, eventType, enabled, ruleCategory, sortField, sortDir } = parsed.data\n\n const filters: Record<string, any> = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n }\n\n if (id) filters.id = id\n if (ruleId) filters.ruleId = { $ilike: `%${escapeLikePattern(ruleId)}%` }\n if (search) filters.ruleName = { $ilike: `%${escapeLikePattern(search)}%` }\n if (ruleType) filters.ruleType = ruleType\n if (entityType) filters.entityType = entityType\n if (eventType) filters.eventType = eventType\n if (enabled !== undefined) filters.enabled = enabled\n if (ruleCategory) filters.ruleCategory = ruleCategory\n\n const sortFieldMap: Record<string, string> = {\n ruleId: 'ruleId',\n ruleName: 'ruleName',\n ruleType: 'ruleType',\n entityType: 'entityType',\n priority: 'priority',\n createdAt: 'createdAt',\n updatedAt: 'updatedAt',\n }\n\n const orderByField = sortField && sortFieldMap[sortField] ? sortFieldMap[sortField] : 'priority'\n const orderBy = { [orderByField]: sortDir, ruleId: 'asc' as const }\n\n const [rows, count] = await em.findAndCount(\n BusinessRule,\n filters,\n {\n limit: pageSize,\n offset: (page - 1) * pageSize,\n orderBy,\n }\n )\n\n const items = rows.map((rule) => ({\n id: rule.id,\n ruleId: rule.ruleId,\n ruleName: rule.ruleName,\n description: rule.description ?? null,\n ruleType: rule.ruleType,\n ruleCategory: rule.ruleCategory ?? null,\n entityType: rule.entityType,\n eventType: rule.eventType ?? null,\n enabled: rule.enabled,\n priority: rule.priority,\n version: rule.version,\n effectiveFrom: rule.effectiveFrom ? rule.effectiveFrom.toISOString() : null,\n effectiveTo: rule.effectiveTo ? rule.effectiveTo.toISOString() : null,\n tenantId: rule.tenantId,\n organizationId: rule.organizationId,\n createdAt: rule.createdAt.toISOString(),\n updatedAt: rule.updatedAt.toISOString(),\n }))\n\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n\n return NextResponse.json({ items, total: count, totalPages })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n if (!auth.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Tenant and organization context required' }, { status: 400 })\n }\n const tenantId = auth.tenantId\n const organizationId = auth.orgId\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const payload = {\n ...body,\n tenantId,\n organizationId,\n createdBy: auth.sub ?? auth.email ?? null,\n }\n\n const { t } = await resolveTranslations()\n const schema = createLocalizedBusinessRuleSchema(t)\n const parsed = schema.safeParse(payload)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n if (hasOpenMercatoCallAction(parsed.data.successActions) || hasOpenMercatoCallAction(parsed.data.failureActions)) {\n const accessError = await requireOpenMercatoCallConfiguratorAccess(container, auth)\n if (accessError) return accessError\n }\n\n const actionErrors = [\n ...await validateOpenMercatoCallActions(em as EntityManager, parsed.data.successActions, {\n tenantId,\n organizationId,\n }),\n ...await validateOpenMercatoCallActions(em as EntityManager, parsed.data.failureActions, {\n tenantId,\n organizationId,\n }),\n ]\n if (actionErrors.length > 0) {\n return NextResponse.json({ error: `Validation failed: ${actionErrors.join(', ')}` }, { status: 400 })\n }\n\n const data = {\n ...parsed.data,\n conditionExpression: parsed.data.conditionExpression ?? null,\n }\n\n const rule = em.create(BusinessRule, data)\n\n try {\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)\n } catch (error) {\n logger.error('Failed to persist new rule', { err: error })\n return NextResponse.json(\n { error: t('business_rules.errors.createFailed') },\n { status: 500 },\n )\n }\n\n return NextResponse.json({ id: rule.id }, { status: 201 })\n}\n\nexport async function PUT(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n if (!auth.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Tenant and organization context required' }, { status: 400 })\n }\n const tenantId = auth.tenantId\n const organizationId = auth.orgId\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n if (!body.id) {\n return NextResponse.json({ error: 'Rule id is required' }, { status: 400 })\n }\n\n const payload = {\n ...body,\n updatedBy: auth.sub ?? auth.email ?? null,\n }\n delete (payload as Record<string, unknown>).tenantId\n delete (payload as Record<string, unknown>).organizationId\n delete (payload as Record<string, unknown>).createdBy\n\n const { t } = await resolveTranslations()\n const schema = createLocalizedUpdateBusinessRuleSchema(t)\n const parsed = schema.safeParse(payload)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const rule = await em.findOne(BusinessRule, {\n id: parsed.data.id,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n\n if (!rule) {\n return NextResponse.json({ error: 'Rule not found' }, { status: 404 })\n }\n\n const touchesOpenMercatoCall = hasOpenMercatoCallAction(parsed.data.successActions)\n || hasOpenMercatoCallAction(parsed.data.failureActions)\n || hasOpenMercatoCallAction(rule.successActions)\n || hasOpenMercatoCallAction(rule.failureActions)\n if (touchesOpenMercatoCall) {\n const accessError = await requireOpenMercatoCallConfiguratorAccess(container, auth)\n if (accessError) return accessError\n }\n\n const actionErrors = [\n ...await validateOpenMercatoCallActions(em as EntityManager, parsed.data.successActions, {\n tenantId,\n organizationId,\n }),\n ...await validateOpenMercatoCallActions(em as EntityManager, parsed.data.failureActions, {\n tenantId,\n organizationId,\n }),\n ]\n if (actionErrors.length > 0) {\n return NextResponse.json({ error: `Validation failed: ${actionErrors.join(', ')}` }, { status: 400 })\n }\n\n try {\n await enforceCommandOptimisticLockWithGuards(container, {\n resourceKind: 'business_rules.rule',\n resourceId: rule.id,\n current: rule.updatedAt ?? null,\n request: req,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n em.assign(rule, parsed.data)\n\n try {\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)\n } catch (error) {\n logger.error('Failed to persist rule update', { err: error })\n return NextResponse.json(\n { error: t('business_rules.errors.updateFailed') },\n { status: 500 },\n )\n }\n\n return NextResponse.json({ ok: true })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const id = url.searchParams.get('id')\n\n if (!id) {\n return NextResponse.json({ error: 'Rule id is required' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n\n const rule = await em.findOne(BusinessRule, {\n id,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n })\n\n if (!rule) {\n return NextResponse.json({ error: 'Rule not found' }, { status: 404 })\n }\n\n try {\n await enforceCommandOptimisticLockWithGuards(container, {\n resourceKind: 'business_rules.rule',\n resourceId: rule.id,\n current: rule.updatedAt ?? null,\n request: req,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n rule.deletedAt = new Date()\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)\n\n return NextResponse.json({ ok: true })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Business Rules',\n summary: 'Business rule management',\n methods: {\n GET: {\n summary: 'List business rules',\n description: 'Returns business rules for the current tenant and organization with filtering and pagination.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Business rules collection', schema: ruleListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid query parameters', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n ],\n },\n POST: {\n summary: 'Create business rule',\n description: 'Creates a new business rule for the current tenant and organization.',\n requestBody: {\n contentType: 'application/json',\n schema: createBusinessRuleSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Business rule created',\n schema: ruleCreateResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update business rule',\n description: 'Updates an existing business rule.',\n requestBody: {\n contentType: 'application/json',\n schema: updateBusinessRuleSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Business rule updated',\n schema: okResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'Business rule not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete business rule',\n description: 'Soft deletes a business rule by identifier.',\n query: z.object({ id: z.string().uuid().describe('Business rule identifier') }),\n responses: [\n { status: 200, description: 'Business rule deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid identifier', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'Business rule not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }