@open-mercato/core 0.4.8-develop-28cee031d6 → 0.4.8-develop-15259be22b

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 (333) hide show
  1. package/agentic/standalone-guide.md +235 -0
  2. package/dist/generated/entities/customer_role/index.js +27 -0
  3. package/dist/generated/entities/customer_role/index.js.map +7 -0
  4. package/dist/generated/entities/customer_role_acl/index.js +19 -0
  5. package/dist/generated/entities/customer_role_acl/index.js.map +7 -0
  6. package/dist/generated/entities/customer_user/index.js +37 -0
  7. package/dist/generated/entities/customer_user/index.js.map +7 -0
  8. package/dist/generated/entities/customer_user_acl/index.js +19 -0
  9. package/dist/generated/entities/customer_user_acl/index.js.map +7 -0
  10. package/dist/generated/entities/customer_user_email_verification/index.js +17 -0
  11. package/dist/generated/entities/customer_user_email_verification/index.js.map +7 -0
  12. package/dist/generated/entities/customer_user_invitation/index.js +33 -0
  13. package/dist/generated/entities/customer_user_invitation/index.js.map +7 -0
  14. package/dist/generated/entities/customer_user_password_reset/index.js +15 -0
  15. package/dist/generated/entities/customer_user_password_reset/index.js.map +7 -0
  16. package/dist/generated/entities/customer_user_role/index.js +13 -0
  17. package/dist/generated/entities/customer_user_role/index.js.map +7 -0
  18. package/dist/generated/entities/customer_user_session/index.js +21 -0
  19. package/dist/generated/entities/customer_user_session/index.js.map +7 -0
  20. package/dist/generated/entities/organization/index.js +2 -0
  21. package/dist/generated/entities/organization/index.js.map +2 -2
  22. package/dist/generated/entities.ids.generated.js +14 -1
  23. package/dist/generated/entities.ids.generated.js.map +2 -2
  24. package/dist/generated/entity-fields-registry.js +18 -0
  25. package/dist/generated/entity-fields-registry.js.map +2 -2
  26. package/dist/modules/auth/services/rbacService.js +3 -9
  27. package/dist/modules/auth/services/rbacService.js.map +2 -2
  28. package/dist/modules/customer_accounts/acl.js +12 -0
  29. package/dist/modules/customer_accounts/acl.js.map +7 -0
  30. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +87 -0
  31. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +7 -0
  32. package/dist/modules/customer_accounts/api/admin/roles/[id].js +216 -0
  33. package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +7 -0
  34. package/dist/modules/customer_accounts/api/admin/roles.js +189 -0
  35. package/dist/modules/customer_accounts/api/admin/roles.js.map +7 -0
  36. package/dist/modules/customer_accounts/api/admin/users/[id]/reset-password.js +69 -0
  37. package/dist/modules/customer_accounts/api/admin/users/[id]/reset-password.js.map +7 -0
  38. package/dist/modules/customer_accounts/api/admin/users/[id]/verify-email.js +64 -0
  39. package/dist/modules/customer_accounts/api/admin/users/[id]/verify-email.js.map +7 -0
  40. package/dist/modules/customer_accounts/api/admin/users/[id].js +253 -0
  41. package/dist/modules/customer_accounts/api/admin/users/[id].js.map +7 -0
  42. package/dist/modules/customer_accounts/api/admin/users-invite.js +78 -0
  43. package/dist/modules/customer_accounts/api/admin/users-invite.js.map +7 -0
  44. package/dist/modules/customer_accounts/api/admin/users.js +251 -0
  45. package/dist/modules/customer_accounts/api/admin/users.js.map +7 -0
  46. package/dist/modules/customer_accounts/api/email/verify.js +59 -0
  47. package/dist/modules/customer_accounts/api/email/verify.js.map +7 -0
  48. package/dist/modules/customer_accounts/api/interceptors.js +5 -0
  49. package/dist/modules/customer_accounts/api/interceptors.js.map +7 -0
  50. package/dist/modules/customer_accounts/api/invitations/accept.js +114 -0
  51. package/dist/modules/customer_accounts/api/invitations/accept.js.map +7 -0
  52. package/dist/modules/customer_accounts/api/login.js +143 -0
  53. package/dist/modules/customer_accounts/api/login.js.map +7 -0
  54. package/dist/modules/customer_accounts/api/magic-link/request.js +78 -0
  55. package/dist/modules/customer_accounts/api/magic-link/request.js.map +7 -0
  56. package/dist/modules/customer_accounts/api/magic-link/verify.js +114 -0
  57. package/dist/modules/customer_accounts/api/magic-link/verify.js.map +7 -0
  58. package/dist/modules/customer_accounts/api/password/reset-confirm.js +59 -0
  59. package/dist/modules/customer_accounts/api/password/reset-confirm.js.map +7 -0
  60. package/dist/modules/customer_accounts/api/password/reset-request.js +77 -0
  61. package/dist/modules/customer_accounts/api/password/reset-request.js.map +7 -0
  62. package/dist/modules/customer_accounts/api/portal/events/stream.js +163 -0
  63. package/dist/modules/customer_accounts/api/portal/events/stream.js.map +7 -0
  64. package/dist/modules/customer_accounts/api/portal/feature-check.js +57 -0
  65. package/dist/modules/customer_accounts/api/portal/feature-check.js.map +7 -0
  66. package/dist/modules/customer_accounts/api/portal/logout.js +64 -0
  67. package/dist/modules/customer_accounts/api/portal/logout.js.map +7 -0
  68. package/dist/modules/customer_accounts/api/portal/notifications/[id]/dismiss.js +49 -0
  69. package/dist/modules/customer_accounts/api/portal/notifications/[id]/dismiss.js.map +7 -0
  70. package/dist/modules/customer_accounts/api/portal/notifications/[id]/read.js +49 -0
  71. package/dist/modules/customer_accounts/api/portal/notifications/[id]/read.js.map +7 -0
  72. package/dist/modules/customer_accounts/api/portal/notifications/mark-all-read.js +46 -0
  73. package/dist/modules/customer_accounts/api/portal/notifications/mark-all-read.js.map +7 -0
  74. package/dist/modules/customer_accounts/api/portal/notifications/unread-count.js +42 -0
  75. package/dist/modules/customer_accounts/api/portal/notifications/unread-count.js.map +7 -0
  76. package/dist/modules/customer_accounts/api/portal/notifications.js +105 -0
  77. package/dist/modules/customer_accounts/api/portal/notifications.js.map +7 -0
  78. package/dist/modules/customer_accounts/api/portal/password-change.js +57 -0
  79. package/dist/modules/customer_accounts/api/portal/password-change.js.map +7 -0
  80. package/dist/modules/customer_accounts/api/portal/profile.js +135 -0
  81. package/dist/modules/customer_accounts/api/portal/profile.js.map +7 -0
  82. package/dist/modules/customer_accounts/api/portal/sessions/[id].js +62 -0
  83. package/dist/modules/customer_accounts/api/portal/sessions/[id].js.map +7 -0
  84. package/dist/modules/customer_accounts/api/portal/sessions-refresh.js +75 -0
  85. package/dist/modules/customer_accounts/api/portal/sessions-refresh.js.map +7 -0
  86. package/dist/modules/customer_accounts/api/portal/sessions.js +77 -0
  87. package/dist/modules/customer_accounts/api/portal/sessions.js.map +7 -0
  88. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js +90 -0
  89. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js.map +7 -0
  90. package/dist/modules/customer_accounts/api/portal/users/[id].js +71 -0
  91. package/dist/modules/customer_accounts/api/portal/users/[id].js.map +7 -0
  92. package/dist/modules/customer_accounts/api/portal/users-invite.js +92 -0
  93. package/dist/modules/customer_accounts/api/portal/users-invite.js.map +7 -0
  94. package/dist/modules/customer_accounts/api/portal/users.js +79 -0
  95. package/dist/modules/customer_accounts/api/portal/users.js.map +7 -0
  96. package/dist/modules/customer_accounts/api/signup.js +121 -0
  97. package/dist/modules/customer_accounts/api/signup.js.map +7 -0
  98. package/dist/modules/customer_accounts/backend/customer_accounts/[id]/page.js +491 -0
  99. package/dist/modules/customer_accounts/backend/customer_accounts/[id]/page.js.map +7 -0
  100. package/dist/modules/customer_accounts/backend/customer_accounts/[id]/page.meta.js +15 -0
  101. package/dist/modules/customer_accounts/backend/customer_accounts/[id]/page.meta.js.map +7 -0
  102. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +343 -0
  103. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +7 -0
  104. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.meta.js +16 -0
  105. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.meta.js.map +7 -0
  106. package/dist/modules/customer_accounts/backend/customer_accounts/roles/create/page.js +180 -0
  107. package/dist/modules/customer_accounts/backend/customer_accounts/roles/create/page.js.map +7 -0
  108. package/dist/modules/customer_accounts/backend/customer_accounts/roles/create/page.meta.js +16 -0
  109. package/dist/modules/customer_accounts/backend/customer_accounts/roles/create/page.meta.js.map +7 -0
  110. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +176 -0
  111. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +7 -0
  112. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.meta.js +33 -0
  113. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.meta.js.map +7 -0
  114. package/dist/modules/customer_accounts/backend/page.js +466 -0
  115. package/dist/modules/customer_accounts/backend/page.js.map +7 -0
  116. package/dist/modules/customer_accounts/backend/page.meta.js +35 -0
  117. package/dist/modules/customer_accounts/backend/page.meta.js.map +7 -0
  118. package/dist/modules/customer_accounts/ce.js +26 -0
  119. package/dist/modules/customer_accounts/ce.js.map +7 -0
  120. package/dist/modules/customer_accounts/data/enrichers.js +85 -0
  121. package/dist/modules/customer_accounts/data/enrichers.js.map +7 -0
  122. package/dist/modules/customer_accounts/data/entities.js +377 -0
  123. package/dist/modules/customer_accounts/data/entities.js.map +7 -0
  124. package/dist/modules/customer_accounts/data/extensions.js +8 -0
  125. package/dist/modules/customer_accounts/data/extensions.js.map +7 -0
  126. package/dist/modules/customer_accounts/data/validators.js +111 -0
  127. package/dist/modules/customer_accounts/data/validators.js.map +7 -0
  128. package/dist/modules/customer_accounts/di.js +17 -0
  129. package/dist/modules/customer_accounts/di.js.map +7 -0
  130. package/dist/modules/customer_accounts/events.js +28 -0
  131. package/dist/modules/customer_accounts/events.js.map +7 -0
  132. package/dist/modules/customer_accounts/index.js +15 -0
  133. package/dist/modules/customer_accounts/index.js.map +7 -0
  134. package/dist/modules/customer_accounts/lib/customerAuth.js +71 -0
  135. package/dist/modules/customer_accounts/lib/customerAuth.js.map +7 -0
  136. package/dist/modules/customer_accounts/lib/customerAuthServer.js +29 -0
  137. package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +7 -0
  138. package/dist/modules/customer_accounts/lib/rateLimiter.js +63 -0
  139. package/dist/modules/customer_accounts/lib/rateLimiter.js.map +7 -0
  140. package/dist/modules/customer_accounts/lib/tokenGenerator.js +12 -0
  141. package/dist/modules/customer_accounts/lib/tokenGenerator.js.map +7 -0
  142. package/dist/modules/customer_accounts/migrations/Migration20260313222043.js +49 -0
  143. package/dist/modules/customer_accounts/migrations/Migration20260313222043.js.map +7 -0
  144. package/dist/modules/customer_accounts/notifications.client.js +47 -0
  145. package/dist/modules/customer_accounts/notifications.client.js.map +7 -0
  146. package/dist/modules/customer_accounts/notifications.js +46 -0
  147. package/dist/modules/customer_accounts/notifications.js.map +7 -0
  148. package/dist/modules/customer_accounts/search.js +120 -0
  149. package/dist/modules/customer_accounts/search.js.map +7 -0
  150. package/dist/modules/customer_accounts/services/customerInvitationService.js +87 -0
  151. package/dist/modules/customer_accounts/services/customerInvitationService.js.map +7 -0
  152. package/dist/modules/customer_accounts/services/customerRbacService.js +109 -0
  153. package/dist/modules/customer_accounts/services/customerRbacService.js.map +7 -0
  154. package/dist/modules/customer_accounts/services/customerSessionService.js +75 -0
  155. package/dist/modules/customer_accounts/services/customerSessionService.js.map +7 -0
  156. package/dist/modules/customer_accounts/services/customerTokenService.js +91 -0
  157. package/dist/modules/customer_accounts/services/customerTokenService.js.map +7 -0
  158. package/dist/modules/customer_accounts/services/customerUserService.js +92 -0
  159. package/dist/modules/customer_accounts/services/customerUserService.js.map +7 -0
  160. package/dist/modules/customer_accounts/setup.js +179 -0
  161. package/dist/modules/customer_accounts/setup.js.map +7 -0
  162. package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js +54 -0
  163. package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js.map +7 -0
  164. package/dist/modules/customer_accounts/subscribers/autoLinkCrmReverse.js +68 -0
  165. package/dist/modules/customer_accounts/subscribers/autoLinkCrmReverse.js.map +7 -0
  166. package/dist/modules/customer_accounts/subscribers/notifyStaffOnSignup.js +29 -0
  167. package/dist/modules/customer_accounts/subscribers/notifyStaffOnSignup.js.map +7 -0
  168. package/dist/modules/customer_accounts/translations.js +9 -0
  169. package/dist/modules/customer_accounts/translations.js.map +7 -0
  170. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +63 -0
  171. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +7 -0
  172. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.js +17 -0
  173. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.js.map +7 -0
  174. package/dist/modules/customer_accounts/widgets/injection/company-users/widget.client.js +55 -0
  175. package/dist/modules/customer_accounts/widgets/injection/company-users/widget.client.js.map +7 -0
  176. package/dist/modules/customer_accounts/widgets/injection/company-users/widget.js +17 -0
  177. package/dist/modules/customer_accounts/widgets/injection/company-users/widget.js.map +7 -0
  178. package/dist/modules/customer_accounts/widgets/injection-table.js +26 -0
  179. package/dist/modules/customer_accounts/widgets/injection-table.js.map +7 -0
  180. package/dist/modules/customer_accounts/workers/cleanupExpiredSessions.js +23 -0
  181. package/dist/modules/customer_accounts/workers/cleanupExpiredSessions.js.map +7 -0
  182. package/dist/modules/customer_accounts/workers/cleanupExpiredTokens.js +38 -0
  183. package/dist/modules/customer_accounts/workers/cleanupExpiredTokens.js.map +7 -0
  184. package/dist/modules/customers/components/AddressTiles.js +1 -1
  185. package/dist/modules/customers/components/AddressTiles.js.map +2 -2
  186. package/dist/modules/directory/api/get/organizations/lookup.js +83 -0
  187. package/dist/modules/directory/api/get/organizations/lookup.js.map +7 -0
  188. package/dist/modules/directory/commands/organizations.js +32 -1
  189. package/dist/modules/directory/commands/organizations.js.map +2 -2
  190. package/dist/modules/directory/data/entities.js +6 -2
  191. package/dist/modules/directory/data/entities.js.map +2 -2
  192. package/dist/modules/directory/data/validators.js +3 -0
  193. package/dist/modules/directory/data/validators.js.map +2 -2
  194. package/dist/modules/directory/migrations/Migration20260314143323.js +15 -0
  195. package/dist/modules/directory/migrations/Migration20260314143323.js.map +7 -0
  196. package/dist/modules/directory/setup.js +36 -0
  197. package/dist/modules/directory/setup.js.map +2 -2
  198. package/dist/modules/payment_gateways/migrations/Migration20260313222043.js +15 -0
  199. package/dist/modules/payment_gateways/migrations/Migration20260313222043.js.map +7 -0
  200. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +131 -0
  201. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +7 -0
  202. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +96 -0
  203. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +7 -0
  204. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js +94 -0
  205. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js.map +7 -0
  206. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js +89 -0
  207. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js.map +7 -0
  208. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js +104 -0
  209. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js.map +7 -0
  210. package/dist/modules/portal/index.js +11 -0
  211. package/dist/modules/portal/index.js.map +7 -0
  212. package/dist/modules/portal/setup.js +23 -0
  213. package/dist/modules/portal/setup.js.map +7 -0
  214. package/generated/entities/customer_role/index.ts +12 -0
  215. package/generated/entities/customer_role_acl/index.ts +8 -0
  216. package/generated/entities/customer_user/index.ts +17 -0
  217. package/generated/entities/customer_user_acl/index.ts +8 -0
  218. package/generated/entities/customer_user_email_verification/index.ts +7 -0
  219. package/generated/entities/customer_user_invitation/index.ts +15 -0
  220. package/generated/entities/customer_user_password_reset/index.ts +6 -0
  221. package/generated/entities/customer_user_role/index.ts +5 -0
  222. package/generated/entities/customer_user_session/index.ts +9 -0
  223. package/generated/entities/organization/index.ts +1 -0
  224. package/generated/entities.ids.generated.ts +14 -1
  225. package/generated/entity-fields-registry.ts +18 -0
  226. package/package.json +3 -3
  227. package/src/modules/auth/services/rbacService.ts +3 -9
  228. package/src/modules/customer_accounts/AGENTS.md +377 -0
  229. package/src/modules/customer_accounts/acl.ts +8 -0
  230. package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +98 -0
  231. package/src/modules/customer_accounts/api/admin/roles/[id].ts +246 -0
  232. package/src/modules/customer_accounts/api/admin/roles.ts +212 -0
  233. package/src/modules/customer_accounts/api/admin/users/[id]/reset-password.ts +78 -0
  234. package/src/modules/customer_accounts/api/admin/users/[id]/verify-email.ts +72 -0
  235. package/src/modules/customer_accounts/api/admin/users/[id].ts +289 -0
  236. package/src/modules/customer_accounts/api/admin/users-invite.ts +86 -0
  237. package/src/modules/customer_accounts/api/admin/users.ts +280 -0
  238. package/src/modules/customer_accounts/api/email/verify.ts +66 -0
  239. package/src/modules/customer_accounts/api/interceptors.ts +3 -0
  240. package/src/modules/customer_accounts/api/invitations/accept.ts +128 -0
  241. package/src/modules/customer_accounts/api/login.ts +163 -0
  242. package/src/modules/customer_accounts/api/magic-link/request.ts +87 -0
  243. package/src/modules/customer_accounts/api/magic-link/verify.ts +132 -0
  244. package/src/modules/customer_accounts/api/password/reset-confirm.ts +69 -0
  245. package/src/modules/customer_accounts/api/password/reset-request.ts +87 -0
  246. package/src/modules/customer_accounts/api/portal/events/stream.ts +209 -0
  247. package/src/modules/customer_accounts/api/portal/feature-check.ts +60 -0
  248. package/src/modules/customer_accounts/api/portal/logout.ts +71 -0
  249. package/src/modules/customer_accounts/api/portal/notifications/[id]/dismiss.ts +54 -0
  250. package/src/modules/customer_accounts/api/portal/notifications/[id]/read.ts +54 -0
  251. package/src/modules/customer_accounts/api/portal/notifications/mark-all-read.ts +49 -0
  252. package/src/modules/customer_accounts/api/portal/notifications/unread-count.ts +45 -0
  253. package/src/modules/customer_accounts/api/portal/notifications.ts +115 -0
  254. package/src/modules/customer_accounts/api/portal/password-change.ts +65 -0
  255. package/src/modules/customer_accounts/api/portal/profile.ts +151 -0
  256. package/src/modules/customer_accounts/api/portal/sessions/[id].ts +70 -0
  257. package/src/modules/customer_accounts/api/portal/sessions-refresh.ts +87 -0
  258. package/src/modules/customer_accounts/api/portal/sessions.ts +84 -0
  259. package/src/modules/customer_accounts/api/portal/users/[id]/roles.ts +106 -0
  260. package/src/modules/customer_accounts/api/portal/users/[id].ts +81 -0
  261. package/src/modules/customer_accounts/api/portal/users-invite.ts +103 -0
  262. package/src/modules/customer_accounts/api/portal/users.ts +86 -0
  263. package/src/modules/customer_accounts/api/signup.ts +136 -0
  264. package/src/modules/customer_accounts/backend/customer_accounts/[id]/page.meta.ts +11 -0
  265. package/src/modules/customer_accounts/backend/customer_accounts/[id]/page.tsx +607 -0
  266. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.meta.ts +12 -0
  267. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +385 -0
  268. package/src/modules/customer_accounts/backend/customer_accounts/roles/create/page.meta.ts +12 -0
  269. package/src/modules/customer_accounts/backend/customer_accounts/roles/create/page.tsx +203 -0
  270. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.meta.ts +31 -0
  271. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +217 -0
  272. package/src/modules/customer_accounts/backend/page.meta.ts +33 -0
  273. package/src/modules/customer_accounts/backend/page.tsx +535 -0
  274. package/src/modules/customer_accounts/ce.ts +22 -0
  275. package/src/modules/customer_accounts/data/enrichers.ts +117 -0
  276. package/src/modules/customer_accounts/data/entities.ts +302 -0
  277. package/src/modules/customer_accounts/data/extensions.ts +4 -0
  278. package/src/modules/customer_accounts/data/validators.ts +128 -0
  279. package/src/modules/customer_accounts/di.ts +15 -0
  280. package/src/modules/customer_accounts/events.ts +28 -0
  281. package/src/modules/customer_accounts/i18n/de.json +176 -0
  282. package/src/modules/customer_accounts/i18n/en.json +176 -0
  283. package/src/modules/customer_accounts/i18n/es.json +176 -0
  284. package/src/modules/customer_accounts/i18n/pl.json +176 -0
  285. package/src/modules/customer_accounts/index.ts +13 -0
  286. package/src/modules/customer_accounts/lib/customerAuth.ts +85 -0
  287. package/src/modules/customer_accounts/lib/customerAuthServer.ts +54 -0
  288. package/src/modules/customer_accounts/lib/rateLimiter.ts +36 -0
  289. package/src/modules/customer_accounts/lib/tokenGenerator.ts +9 -0
  290. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +1255 -0
  291. package/src/modules/customer_accounts/migrations/Migration20260313222043.ts +62 -0
  292. package/src/modules/customer_accounts/notifications.client.ts +46 -0
  293. package/src/modules/customer_accounts/notifications.ts +44 -0
  294. package/src/modules/customer_accounts/search.ts +134 -0
  295. package/src/modules/customer_accounts/services/customerInvitationService.ts +109 -0
  296. package/src/modules/customer_accounts/services/customerRbacService.ts +144 -0
  297. package/src/modules/customer_accounts/services/customerSessionService.ts +90 -0
  298. package/src/modules/customer_accounts/services/customerTokenService.ts +98 -0
  299. package/src/modules/customer_accounts/services/customerUserService.ts +105 -0
  300. package/src/modules/customer_accounts/setup.ts +212 -0
  301. package/src/modules/customer_accounts/subscribers/autoLinkCrm.ts +65 -0
  302. package/src/modules/customer_accounts/subscribers/autoLinkCrmReverse.ts +78 -0
  303. package/src/modules/customer_accounts/subscribers/notifyStaffOnSignup.ts +32 -0
  304. package/src/modules/customer_accounts/translations.ts +5 -0
  305. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +89 -0
  306. package/src/modules/customer_accounts/widgets/injection/account-status/widget.ts +16 -0
  307. package/src/modules/customer_accounts/widgets/injection/company-users/widget.client.tsx +78 -0
  308. package/src/modules/customer_accounts/widgets/injection/company-users/widget.ts +16 -0
  309. package/src/modules/customer_accounts/widgets/injection-table.ts +24 -0
  310. package/src/modules/customer_accounts/workers/cleanupExpiredSessions.ts +33 -0
  311. package/src/modules/customer_accounts/workers/cleanupExpiredTokens.ts +51 -0
  312. package/src/modules/customers/components/AddressTiles.tsx +1 -1
  313. package/src/modules/directory/api/get/organizations/lookup.ts +92 -0
  314. package/src/modules/directory/commands/organizations.ts +34 -1
  315. package/src/modules/directory/data/entities.ts +5 -1
  316. package/src/modules/directory/data/validators.ts +4 -0
  317. package/src/modules/directory/migrations/.snapshot-open-mercato.json +20 -1
  318. package/src/modules/directory/migrations/Migration20260314143323.ts +15 -0
  319. package/src/modules/directory/setup.ts +41 -0
  320. package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +4 -1
  321. package/src/modules/payment_gateways/migrations/Migration20260313222043.ts +17 -0
  322. package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +158 -0
  323. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +120 -0
  324. package/src/modules/portal/frontend/[orgSlug]/portal/page.tsx +118 -0
  325. package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.tsx +112 -0
  326. package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.tsx +138 -0
  327. package/src/modules/portal/i18n/de.json +93 -0
  328. package/src/modules/portal/i18n/en.json +93 -0
  329. package/src/modules/portal/i18n/es.json +93 -0
  330. package/src/modules/portal/i18n/pl.json +93 -0
  331. package/src/modules/portal/index.ts +9 -0
  332. package/src/modules/portal/setup.ts +23 -0
  333. package/src/modules/shipping_carriers/migrations/.snapshot-open-mercato.json +226 -0
@@ -0,0 +1,92 @@
1
+ import { hash, compare } from "bcryptjs";
2
+ import { CustomerUser } from "@open-mercato/core/modules/customer_accounts/data/entities";
3
+ import { hashForLookup } from "@open-mercato/shared/lib/encryption/aes";
4
+ const BCRYPT_COST = 10;
5
+ const MAX_FAILED_ATTEMPTS = 5;
6
+ const LOCKOUT_DURATION_MS = 15 * 60 * 1e3;
7
+ class CustomerUserService {
8
+ constructor(em) {
9
+ this.em = em;
10
+ }
11
+ async createUser(email, password, displayName, scope) {
12
+ const passwordHash = await hash(password, BCRYPT_COST);
13
+ const emailHash = hashForLookup(email);
14
+ const user = this.em.create(CustomerUser, {
15
+ email: email.toLowerCase().trim(),
16
+ emailHash,
17
+ passwordHash,
18
+ displayName,
19
+ tenantId: scope.tenantId,
20
+ organizationId: scope.organizationId,
21
+ isActive: true,
22
+ failedLoginAttempts: 0,
23
+ createdAt: /* @__PURE__ */ new Date()
24
+ });
25
+ return user;
26
+ }
27
+ async findByEmail(email, tenantId) {
28
+ const emailHash = hashForLookup(email);
29
+ return this.em.findOne(CustomerUser, {
30
+ emailHash,
31
+ tenantId,
32
+ deletedAt: null
33
+ });
34
+ }
35
+ async findById(id, tenantId) {
36
+ return this.em.findOne(CustomerUser, { id, tenantId, deletedAt: null });
37
+ }
38
+ async verifyPassword(user, password) {
39
+ if (!user.passwordHash) return false;
40
+ return compare(password, user.passwordHash);
41
+ }
42
+ async updateLastLoginAt(user) {
43
+ const now = /* @__PURE__ */ new Date();
44
+ await this.em.nativeUpdate(CustomerUser, { id: user.id }, { lastLoginAt: now });
45
+ user.lastLoginAt = now;
46
+ }
47
+ checkLockout(user) {
48
+ if (!user.lockedUntil) return false;
49
+ if (user.lockedUntil.getTime() > Date.now()) return true;
50
+ return false;
51
+ }
52
+ async incrementFailedAttempts(user) {
53
+ const newCount = (user.failedLoginAttempts || 0) + 1;
54
+ const updates = { failedLoginAttempts: newCount };
55
+ if (newCount >= MAX_FAILED_ATTEMPTS) {
56
+ updates.lockedUntil = new Date(Date.now() + LOCKOUT_DURATION_MS);
57
+ }
58
+ await this.em.nativeUpdate(CustomerUser, { id: user.id }, updates);
59
+ user.failedLoginAttempts = newCount;
60
+ if (updates.lockedUntil) user.lockedUntil = updates.lockedUntil;
61
+ }
62
+ async resetFailedAttempts(user) {
63
+ await this.em.nativeUpdate(CustomerUser, { id: user.id }, {
64
+ failedLoginAttempts: 0,
65
+ lockedUntil: null
66
+ });
67
+ user.failedLoginAttempts = 0;
68
+ user.lockedUntil = null;
69
+ }
70
+ async updatePassword(user, newPassword) {
71
+ const passwordHash = await hash(newPassword, BCRYPT_COST);
72
+ await this.em.nativeUpdate(CustomerUser, { id: user.id }, { passwordHash });
73
+ user.passwordHash = passwordHash;
74
+ }
75
+ async updateProfile(user, data) {
76
+ const updates = {};
77
+ if (data.displayName !== void 0) updates.displayName = data.displayName;
78
+ if (Object.keys(updates).length === 0) return;
79
+ await this.em.nativeUpdate(CustomerUser, { id: user.id }, updates);
80
+ if (data.displayName !== void 0) user.displayName = data.displayName;
81
+ }
82
+ async softDelete(userId) {
83
+ await this.em.nativeUpdate(CustomerUser, { id: userId }, {
84
+ deletedAt: /* @__PURE__ */ new Date(),
85
+ isActive: false
86
+ });
87
+ }
88
+ }
89
+ export {
90
+ CustomerUserService
91
+ };
92
+ //# sourceMappingURL=customerUserService.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/customer_accounts/services/customerUserService.ts"],
4
+ "sourcesContent": ["import { EntityManager } from '@mikro-orm/postgresql'\nimport { hash, compare } from 'bcryptjs'\nimport { CustomerUser } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { hashForLookup } from '@open-mercato/shared/lib/encryption/aes'\n\nconst BCRYPT_COST = 10\nconst MAX_FAILED_ATTEMPTS = 5\nconst LOCKOUT_DURATION_MS = 15 * 60 * 1000 // 15 minutes\n\nexport class CustomerUserService {\n constructor(private em: EntityManager) {}\n\n async createUser(\n email: string,\n password: string,\n displayName: string,\n scope: { tenantId: string; organizationId: string },\n ): Promise<CustomerUser> {\n const passwordHash = await hash(password, BCRYPT_COST)\n const emailHash = hashForLookup(email)\n const user = this.em.create(CustomerUser, {\n email: email.toLowerCase().trim(),\n emailHash,\n passwordHash,\n displayName,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n isActive: true,\n failedLoginAttempts: 0,\n createdAt: new Date(),\n } as any)\n return user as CustomerUser\n }\n\n async findByEmail(email: string, tenantId: string): Promise<CustomerUser | null> {\n const emailHash = hashForLookup(email)\n return this.em.findOne(CustomerUser, {\n emailHash,\n tenantId,\n deletedAt: null,\n })\n }\n\n async findById(id: string, tenantId: string): Promise<CustomerUser | null> {\n return this.em.findOne(CustomerUser, { id, tenantId, deletedAt: null })\n }\n\n async verifyPassword(user: CustomerUser, password: string): Promise<boolean> {\n if (!user.passwordHash) return false\n return compare(password, user.passwordHash)\n }\n\n async updateLastLoginAt(user: CustomerUser): Promise<void> {\n const now = new Date()\n await this.em.nativeUpdate(CustomerUser, { id: user.id }, { lastLoginAt: now })\n user.lastLoginAt = now\n }\n\n checkLockout(user: CustomerUser): boolean {\n if (!user.lockedUntil) return false\n if (user.lockedUntil.getTime() > Date.now()) return true\n return false\n }\n\n async incrementFailedAttempts(user: CustomerUser): Promise<void> {\n const newCount = (user.failedLoginAttempts || 0) + 1\n const updates: Record<string, unknown> = { failedLoginAttempts: newCount }\n if (newCount >= MAX_FAILED_ATTEMPTS) {\n updates.lockedUntil = new Date(Date.now() + LOCKOUT_DURATION_MS)\n }\n await this.em.nativeUpdate(CustomerUser, { id: user.id }, updates)\n user.failedLoginAttempts = newCount\n if (updates.lockedUntil) user.lockedUntil = updates.lockedUntil as Date\n }\n\n async resetFailedAttempts(user: CustomerUser): Promise<void> {\n await this.em.nativeUpdate(CustomerUser, { id: user.id }, {\n failedLoginAttempts: 0,\n lockedUntil: null,\n })\n user.failedLoginAttempts = 0\n user.lockedUntil = null\n }\n\n async updatePassword(user: CustomerUser, newPassword: string): Promise<void> {\n const passwordHash = await hash(newPassword, BCRYPT_COST)\n await this.em.nativeUpdate(CustomerUser, { id: user.id }, { passwordHash })\n user.passwordHash = passwordHash\n }\n\n async updateProfile(user: CustomerUser, data: { displayName?: string }): Promise<void> {\n const updates: Record<string, unknown> = {}\n if (data.displayName !== undefined) updates.displayName = data.displayName\n if (Object.keys(updates).length === 0) return\n await this.em.nativeUpdate(CustomerUser, { id: user.id }, updates)\n if (data.displayName !== undefined) user.displayName = data.displayName\n }\n\n async softDelete(userId: string): Promise<void> {\n await this.em.nativeUpdate(CustomerUser, { id: userId }, {\n deletedAt: new Date(),\n isActive: false,\n })\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,MAAM,eAAe;AAC9B,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAE9B,MAAM,cAAc;AACpB,MAAM,sBAAsB;AAC5B,MAAM,sBAAsB,KAAK,KAAK;AAE/B,MAAM,oBAAoB;AAAA,EAC/B,YAAoB,IAAmB;AAAnB;AAAA,EAAoB;AAAA,EAExC,MAAM,WACJ,OACA,UACA,aACA,OACuB;AACvB,UAAM,eAAe,MAAM,KAAK,UAAU,WAAW;AACrD,UAAM,YAAY,cAAc,KAAK;AACrC,UAAM,OAAO,KAAK,GAAG,OAAO,cAAc;AAAA,MACxC,OAAO,MAAM,YAAY,EAAE,KAAK;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,UAAU;AAAA,MACV,qBAAqB;AAAA,MACrB,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAQ;AACR,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,OAAe,UAAgD;AAC/E,UAAM,YAAY,cAAc,KAAK;AACrC,WAAO,KAAK,GAAG,QAAQ,cAAc;AAAA,MACnC;AAAA,MACA;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAS,IAAY,UAAgD;AACzE,WAAO,KAAK,GAAG,QAAQ,cAAc,EAAE,IAAI,UAAU,WAAW,KAAK,CAAC;AAAA,EACxE;AAAA,EAEA,MAAM,eAAe,MAAoB,UAAoC;AAC3E,QAAI,CAAC,KAAK,aAAc,QAAO;AAC/B,WAAO,QAAQ,UAAU,KAAK,YAAY;AAAA,EAC5C;AAAA,EAEA,MAAM,kBAAkB,MAAmC;AACzD,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,KAAK,GAAG,aAAa,cAAc,EAAE,IAAI,KAAK,GAAG,GAAG,EAAE,aAAa,IAAI,CAAC;AAC9E,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,aAAa,MAA6B;AACxC,QAAI,CAAC,KAAK,YAAa,QAAO;AAC9B,QAAI,KAAK,YAAY,QAAQ,IAAI,KAAK,IAAI,EAAG,QAAO;AACpD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,wBAAwB,MAAmC;AAC/D,UAAM,YAAY,KAAK,uBAAuB,KAAK;AACnD,UAAM,UAAmC,EAAE,qBAAqB,SAAS;AACzE,QAAI,YAAY,qBAAqB;AACnC,cAAQ,cAAc,IAAI,KAAK,KAAK,IAAI,IAAI,mBAAmB;AAAA,IACjE;AACA,UAAM,KAAK,GAAG,aAAa,cAAc,EAAE,IAAI,KAAK,GAAG,GAAG,OAAO;AACjE,SAAK,sBAAsB;AAC3B,QAAI,QAAQ,YAAa,MAAK,cAAc,QAAQ;AAAA,EACtD;AAAA,EAEA,MAAM,oBAAoB,MAAmC;AAC3D,UAAM,KAAK,GAAG,aAAa,cAAc,EAAE,IAAI,KAAK,GAAG,GAAG;AAAA,MACxD,qBAAqB;AAAA,MACrB,aAAa;AAAA,IACf,CAAC;AACD,SAAK,sBAAsB;AAC3B,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,MAAM,eAAe,MAAoB,aAAoC;AAC3E,UAAM,eAAe,MAAM,KAAK,aAAa,WAAW;AACxD,UAAM,KAAK,GAAG,aAAa,cAAc,EAAE,IAAI,KAAK,GAAG,GAAG,EAAE,aAAa,CAAC;AAC1E,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,MAAM,cAAc,MAAoB,MAA+C;AACrF,UAAM,UAAmC,CAAC;AAC1C,QAAI,KAAK,gBAAgB,OAAW,SAAQ,cAAc,KAAK;AAC/D,QAAI,OAAO,KAAK,OAAO,EAAE,WAAW,EAAG;AACvC,UAAM,KAAK,GAAG,aAAa,cAAc,EAAE,IAAI,KAAK,GAAG,GAAG,OAAO;AACjE,QAAI,KAAK,gBAAgB,OAAW,MAAK,cAAc,KAAK;AAAA,EAC9D;AAAA,EAEA,MAAM,WAAW,QAA+B;AAC9C,UAAM,KAAK,GAAG,aAAa,cAAc,EAAE,IAAI,OAAO,GAAG;AAAA,MACvD,WAAW,oBAAI,KAAK;AAAA,MACpB,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,179 @@
1
+ import { hash } from "bcryptjs";
2
+ import { hashForLookup } from "@open-mercato/shared/lib/encryption/aes";
3
+ import {
4
+ CustomerRole,
5
+ CustomerRoleAcl,
6
+ CustomerUser,
7
+ CustomerUserRole
8
+ } from "@open-mercato/core/modules/customer_accounts/data/entities";
9
+ const DEFAULT_ROLES = [
10
+ {
11
+ name: "Portal Admin",
12
+ slug: "portal_admin",
13
+ description: "Full portal administration access",
14
+ isSystem: true,
15
+ customerAssignable: false,
16
+ isDefault: false,
17
+ acl: {
18
+ isPortalAdmin: true,
19
+ features: ["portal.*"]
20
+ }
21
+ },
22
+ {
23
+ name: "Buyer",
24
+ slug: "buyer",
25
+ description: "Standard buyer access with ordering capabilities",
26
+ isSystem: true,
27
+ customerAssignable: true,
28
+ isDefault: true,
29
+ acl: {
30
+ isPortalAdmin: false,
31
+ features: [
32
+ "portal.account.manage",
33
+ "portal.orders.view",
34
+ "portal.orders.create",
35
+ "portal.quotes.view",
36
+ "portal.quotes.request",
37
+ "portal.invoices.view",
38
+ "portal.catalog.view"
39
+ ]
40
+ }
41
+ },
42
+ {
43
+ name: "Viewer",
44
+ slug: "viewer",
45
+ description: "Read-only portal access",
46
+ isSystem: true,
47
+ customerAssignable: true,
48
+ isDefault: false,
49
+ acl: {
50
+ isPortalAdmin: false,
51
+ features: [
52
+ "portal.account.manage",
53
+ "portal.orders.view",
54
+ "portal.invoices.view",
55
+ "portal.catalog.view"
56
+ ]
57
+ }
58
+ }
59
+ ];
60
+ async function ensureDefaultCustomerRoleAcls(em, tenantId, modules) {
61
+ const featuresByRole = {};
62
+ for (const mod of modules) {
63
+ const customerRoleFeatures = mod.setup?.defaultCustomerRoleFeatures;
64
+ if (!customerRoleFeatures) continue;
65
+ for (const [roleSlug, features] of Object.entries(customerRoleFeatures)) {
66
+ if (!features || !features.length) continue;
67
+ if (!featuresByRole[roleSlug]) featuresByRole[roleSlug] = [];
68
+ featuresByRole[roleSlug].push(...features);
69
+ }
70
+ }
71
+ const roleSlugs = Object.keys(featuresByRole);
72
+ if (!roleSlugs.length) return;
73
+ for (const roleSlug of roleSlugs) {
74
+ const role = await em.findOne(CustomerRole, { tenantId, slug: roleSlug, deletedAt: null });
75
+ if (!role) continue;
76
+ const acl = await em.findOne(CustomerRoleAcl, { role: role.id, tenantId });
77
+ if (!acl) continue;
78
+ const currentFeatures = Array.isArray(acl.featuresJson) ? acl.featuresJson : [];
79
+ const merged = Array.from(/* @__PURE__ */ new Set([...currentFeatures, ...featuresByRole[roleSlug]]));
80
+ const changed = merged.length !== currentFeatures.length || merged.some((value, index) => value !== currentFeatures[index]);
81
+ if (changed) {
82
+ acl.featuresJson = merged;
83
+ em.persist(acl);
84
+ }
85
+ }
86
+ await em.flush();
87
+ }
88
+ async function seedDefaultRoles(em, scope) {
89
+ for (const roleDef of DEFAULT_ROLES) {
90
+ const existing = await em.findOne(CustomerRole, {
91
+ tenantId: scope.tenantId,
92
+ slug: roleDef.slug,
93
+ deletedAt: null
94
+ });
95
+ if (existing) continue;
96
+ const role = em.create(CustomerRole, {
97
+ tenantId: scope.tenantId,
98
+ organizationId: scope.organizationId,
99
+ name: roleDef.name,
100
+ slug: roleDef.slug,
101
+ description: roleDef.description,
102
+ isSystem: roleDef.isSystem,
103
+ customerAssignable: roleDef.customerAssignable,
104
+ isDefault: roleDef.isDefault,
105
+ createdAt: /* @__PURE__ */ new Date()
106
+ });
107
+ em.persist(role);
108
+ const acl = em.create(CustomerRoleAcl, {
109
+ role,
110
+ tenantId: scope.tenantId,
111
+ featuresJson: roleDef.acl.features,
112
+ isPortalAdmin: roleDef.acl.isPortalAdmin,
113
+ createdAt: /* @__PURE__ */ new Date()
114
+ });
115
+ em.persist(acl);
116
+ }
117
+ await em.flush();
118
+ }
119
+ const setup = {
120
+ defaultRoleFeatures: {
121
+ superadmin: ["customer_accounts.*"],
122
+ admin: ["customer_accounts.*"]
123
+ },
124
+ async onTenantCreated({ em, tenantId, organizationId }) {
125
+ await seedDefaultRoles(em, { tenantId, organizationId });
126
+ },
127
+ async seedDefaults({ em, tenantId, organizationId }) {
128
+ await seedDefaultRoles(em, { tenantId, organizationId });
129
+ try {
130
+ const { getModules } = await import("@open-mercato/shared/lib/modules/registry");
131
+ const allModules = getModules();
132
+ await ensureDefaultCustomerRoleAcls(em, tenantId, allModules);
133
+ } catch {
134
+ }
135
+ },
136
+ async seedExamples({ em, tenantId, organizationId }) {
137
+ const BCRYPT_COST = 10;
138
+ const exampleUsers = [
139
+ { email: "alice.johnson@example.com", displayName: "Alice Johnson", password: "Password123!", roleSlug: "portal_admin" },
140
+ { email: "bob.smith@example.com", displayName: "Bob Smith", password: "Password123!", roleSlug: "buyer" },
141
+ { email: "carol.white@example.com", displayName: "Carol White", password: "Password123!", roleSlug: "viewer" }
142
+ ];
143
+ for (const entry of exampleUsers) {
144
+ const emailHash = hashForLookup(entry.email);
145
+ const existing = await em.findOne(CustomerUser, { emailHash, tenantId, deletedAt: null });
146
+ if (existing) continue;
147
+ const passwordHash = await hash(entry.password, BCRYPT_COST);
148
+ const user = em.create(CustomerUser, {
149
+ email: entry.email,
150
+ emailHash,
151
+ passwordHash,
152
+ displayName: entry.displayName,
153
+ tenantId,
154
+ organizationId,
155
+ isActive: true,
156
+ failedLoginAttempts: 0,
157
+ emailVerifiedAt: /* @__PURE__ */ new Date(),
158
+ createdAt: /* @__PURE__ */ new Date()
159
+ });
160
+ em.persist(user);
161
+ const role = await em.findOne(CustomerRole, { tenantId, slug: entry.roleSlug, deletedAt: null });
162
+ if (role) {
163
+ const userRole = em.create(CustomerUserRole, {
164
+ user,
165
+ role,
166
+ createdAt: /* @__PURE__ */ new Date()
167
+ });
168
+ em.persist(userRole);
169
+ }
170
+ }
171
+ await em.flush();
172
+ }
173
+ };
174
+ var setup_default = setup;
175
+ export {
176
+ setup_default as default,
177
+ setup
178
+ };
179
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/customer_accounts/setup.ts"],
4
+ "sourcesContent": ["import type { ModuleSetupConfig, DefaultCustomerRoleFeatures } from '@open-mercato/shared/modules/setup'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Module } from '@open-mercato/shared/modules/registry'\nimport { hash } from 'bcryptjs'\nimport { hashForLookup } from '@open-mercato/shared/lib/encryption/aes'\nimport {\n CustomerRole,\n CustomerRoleAcl,\n CustomerUser,\n CustomerUserRole,\n} from '@open-mercato/core/modules/customer_accounts/data/entities'\n\ninterface SeedScope {\n tenantId: string\n organizationId: string\n}\n\nconst DEFAULT_ROLES = [\n {\n name: 'Portal Admin',\n slug: 'portal_admin',\n description: 'Full portal administration access',\n isSystem: true,\n customerAssignable: false,\n isDefault: false,\n acl: {\n isPortalAdmin: true,\n features: ['portal.*'],\n },\n },\n {\n name: 'Buyer',\n slug: 'buyer',\n description: 'Standard buyer access with ordering capabilities',\n isSystem: true,\n customerAssignable: true,\n isDefault: true,\n acl: {\n isPortalAdmin: false,\n features: [\n 'portal.account.manage',\n 'portal.orders.view',\n 'portal.orders.create',\n 'portal.quotes.view',\n 'portal.quotes.request',\n 'portal.invoices.view',\n 'portal.catalog.view',\n ],\n },\n },\n {\n name: 'Viewer',\n slug: 'viewer',\n description: 'Read-only portal access',\n isSystem: true,\n customerAssignable: true,\n isDefault: false,\n acl: {\n isPortalAdmin: false,\n features: [\n 'portal.account.manage',\n 'portal.orders.view',\n 'portal.invoices.view',\n 'portal.catalog.view',\n ],\n },\n },\n]\n\n/**\n * Collect defaultCustomerRoleFeatures from all enabled modules and merge\n * them into the corresponding CustomerRoleAcl records.\n */\nasync function ensureDefaultCustomerRoleAcls(\n em: EntityManager,\n tenantId: string,\n modules: Module[],\n): Promise<void> {\n const featuresByRole: Record<string, string[]> = {}\n\n for (const mod of modules) {\n const customerRoleFeatures = mod.setup?.defaultCustomerRoleFeatures\n if (!customerRoleFeatures) continue\n for (const [roleSlug, features] of Object.entries(customerRoleFeatures)) {\n if (!features || !features.length) continue\n if (!featuresByRole[roleSlug]) featuresByRole[roleSlug] = []\n featuresByRole[roleSlug].push(...features)\n }\n }\n\n const roleSlugs = Object.keys(featuresByRole)\n if (!roleSlugs.length) return\n\n for (const roleSlug of roleSlugs) {\n const role = await em.findOne(CustomerRole, { tenantId, slug: roleSlug, deletedAt: null })\n if (!role) continue\n\n const acl = await em.findOne(CustomerRoleAcl, { role: role.id as any, tenantId })\n if (!acl) continue\n\n const currentFeatures = Array.isArray(acl.featuresJson) ? acl.featuresJson : []\n const merged = Array.from(new Set([...currentFeatures, ...featuresByRole[roleSlug]]))\n const changed =\n merged.length !== currentFeatures.length ||\n merged.some((value, index) => value !== currentFeatures[index])\n if (changed) {\n acl.featuresJson = merged\n em.persist(acl)\n }\n }\n await em.flush()\n}\n\nasync function seedDefaultRoles(em: EntityManager, scope: SeedScope): Promise<void> {\n for (const roleDef of DEFAULT_ROLES) {\n const existing = await em.findOne(CustomerRole, {\n tenantId: scope.tenantId,\n slug: roleDef.slug,\n deletedAt: null,\n })\n if (existing) continue\n\n const role = em.create(CustomerRole, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: roleDef.name,\n slug: roleDef.slug,\n description: roleDef.description,\n isSystem: roleDef.isSystem,\n customerAssignable: roleDef.customerAssignable,\n isDefault: roleDef.isDefault,\n createdAt: new Date(),\n } as any)\n em.persist(role)\n\n const acl = em.create(CustomerRoleAcl, {\n role,\n tenantId: scope.tenantId,\n featuresJson: roleDef.acl.features,\n isPortalAdmin: roleDef.acl.isPortalAdmin,\n createdAt: new Date(),\n } as any)\n em.persist(acl)\n }\n await em.flush()\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n superadmin: ['customer_accounts.*'],\n admin: ['customer_accounts.*'],\n },\n\n async onTenantCreated({ em, tenantId, organizationId }) {\n await seedDefaultRoles(em, { tenantId, organizationId })\n },\n\n async seedDefaults({ em, tenantId, organizationId }) {\n await seedDefaultRoles(em, { tenantId, organizationId })\n // Merge defaultCustomerRoleFeatures from all enabled modules\n try {\n const { getModules } = await import('@open-mercato/shared/lib/modules/registry')\n const allModules = getModules()\n await ensureDefaultCustomerRoleAcls(em, tenantId, allModules)\n } catch {\n // Modules may not be registered yet during initial setup\n }\n },\n\n async seedExamples({ em, tenantId, organizationId }) {\n const BCRYPT_COST = 10\n const exampleUsers = [\n { email: 'alice.johnson@example.com', displayName: 'Alice Johnson', password: 'Password123!', roleSlug: 'portal_admin' },\n { email: 'bob.smith@example.com', displayName: 'Bob Smith', password: 'Password123!', roleSlug: 'buyer' },\n { email: 'carol.white@example.com', displayName: 'Carol White', password: 'Password123!', roleSlug: 'viewer' },\n ]\n\n for (const entry of exampleUsers) {\n const emailHash = hashForLookup(entry.email)\n const existing = await em.findOne(CustomerUser, { emailHash, tenantId, deletedAt: null })\n if (existing) continue\n\n const passwordHash = await hash(entry.password, BCRYPT_COST)\n const user = em.create(CustomerUser, {\n email: entry.email,\n emailHash,\n passwordHash,\n displayName: entry.displayName,\n tenantId,\n organizationId,\n isActive: true,\n failedLoginAttempts: 0,\n emailVerifiedAt: new Date(),\n createdAt: new Date(),\n } as any)\n em.persist(user)\n\n const role = await em.findOne(CustomerRole, { tenantId, slug: entry.roleSlug, deletedAt: null })\n if (role) {\n const userRole = em.create(CustomerUserRole, {\n user,\n role,\n createdAt: new Date(),\n } as any)\n em.persist(userRole)\n }\n }\n await em.flush()\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "AAGA,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAOP,MAAM,gBAAgB;AAAA,EACpB;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,WAAW;AAAA,IACX,KAAK;AAAA,MACH,eAAe;AAAA,MACf,UAAU,CAAC,UAAU;AAAA,IACvB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,WAAW;AAAA,IACX,KAAK;AAAA,MACH,eAAe;AAAA,MACf,UAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,WAAW;AAAA,IACX,KAAK;AAAA,MACH,eAAe;AAAA,MACf,UAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAMA,eAAe,8BACb,IACA,UACA,SACe;AACf,QAAM,iBAA2C,CAAC;AAElD,aAAW,OAAO,SAAS;AACzB,UAAM,uBAAuB,IAAI,OAAO;AACxC,QAAI,CAAC,qBAAsB;AAC3B,eAAW,CAAC,UAAU,QAAQ,KAAK,OAAO,QAAQ,oBAAoB,GAAG;AACvE,UAAI,CAAC,YAAY,CAAC,SAAS,OAAQ;AACnC,UAAI,CAAC,eAAe,QAAQ,EAAG,gBAAe,QAAQ,IAAI,CAAC;AAC3D,qBAAe,QAAQ,EAAE,KAAK,GAAG,QAAQ;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,YAAY,OAAO,KAAK,cAAc;AAC5C,MAAI,CAAC,UAAU,OAAQ;AAEvB,aAAW,YAAY,WAAW;AAChC,UAAM,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,UAAU,MAAM,UAAU,WAAW,KAAK,CAAC;AACzF,QAAI,CAAC,KAAM;AAEX,UAAM,MAAM,MAAM,GAAG,QAAQ,iBAAiB,EAAE,MAAM,KAAK,IAAW,SAAS,CAAC;AAChF,QAAI,CAAC,IAAK;AAEV,UAAM,kBAAkB,MAAM,QAAQ,IAAI,YAAY,IAAI,IAAI,eAAe,CAAC;AAC9E,UAAM,SAAS,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,eAAe,QAAQ,CAAC,CAAC,CAAC;AACpF,UAAM,UACJ,OAAO,WAAW,gBAAgB,UAClC,OAAO,KAAK,CAAC,OAAO,UAAU,UAAU,gBAAgB,KAAK,CAAC;AAChE,QAAI,SAAS;AACX,UAAI,eAAe;AACnB,SAAG,QAAQ,GAAG;AAAA,IAChB;AAAA,EACF;AACA,QAAM,GAAG,MAAM;AACjB;AAEA,eAAe,iBAAiB,IAAmB,OAAiC;AAClF,aAAW,WAAW,eAAe;AACnC,UAAM,WAAW,MAAM,GAAG,QAAQ,cAAc;AAAA,MAC9C,UAAU,MAAM;AAAA,MAChB,MAAM,QAAQ;AAAA,MACd,WAAW;AAAA,IACb,CAAC;AACD,QAAI,SAAU;AAEd,UAAM,OAAO,GAAG,OAAO,cAAc;AAAA,MACnC,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,MAAM,QAAQ;AAAA,MACd,MAAM,QAAQ;AAAA,MACd,aAAa,QAAQ;AAAA,MACrB,UAAU,QAAQ;AAAA,MAClB,oBAAoB,QAAQ;AAAA,MAC5B,WAAW,QAAQ;AAAA,MACnB,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAQ;AACR,OAAG,QAAQ,IAAI;AAEf,UAAM,MAAM,GAAG,OAAO,iBAAiB;AAAA,MACrC;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,cAAc,QAAQ,IAAI;AAAA,MAC1B,eAAe,QAAQ,IAAI;AAAA,MAC3B,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAQ;AACR,OAAG,QAAQ,GAAG;AAAA,EAChB;AACA,QAAM,GAAG,MAAM;AACjB;AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,YAAY,CAAC,qBAAqB;AAAA,IAClC,OAAO,CAAC,qBAAqB;AAAA,EAC/B;AAAA,EAEA,MAAM,gBAAgB,EAAE,IAAI,UAAU,eAAe,GAAG;AACtD,UAAM,iBAAiB,IAAI,EAAE,UAAU,eAAe,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,aAAa,EAAE,IAAI,UAAU,eAAe,GAAG;AACnD,UAAM,iBAAiB,IAAI,EAAE,UAAU,eAAe,CAAC;AAEvD,QAAI;AACF,YAAM,EAAE,WAAW,IAAI,MAAM,OAAO,2CAA2C;AAC/E,YAAM,aAAa,WAAW;AAC9B,YAAM,8BAA8B,IAAI,UAAU,UAAU;AAAA,IAC9D,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,EAAE,IAAI,UAAU,eAAe,GAAG;AACnD,UAAM,cAAc;AACpB,UAAM,eAAe;AAAA,MACnB,EAAE,OAAO,6BAA6B,aAAa,iBAAiB,UAAU,gBAAgB,UAAU,eAAe;AAAA,MACvH,EAAE,OAAO,yBAAyB,aAAa,aAAa,UAAU,gBAAgB,UAAU,QAAQ;AAAA,MACxG,EAAE,OAAO,2BAA2B,aAAa,eAAe,UAAU,gBAAgB,UAAU,SAAS;AAAA,IAC/G;AAEA,eAAW,SAAS,cAAc;AAChC,YAAM,YAAY,cAAc,MAAM,KAAK;AAC3C,YAAM,WAAW,MAAM,GAAG,QAAQ,cAAc,EAAE,WAAW,UAAU,WAAW,KAAK,CAAC;AACxF,UAAI,SAAU;AAEd,YAAM,eAAe,MAAM,KAAK,MAAM,UAAU,WAAW;AAC3D,YAAM,OAAO,GAAG,OAAO,cAAc;AAAA,QACnC,OAAO,MAAM;AAAA,QACb;AAAA,QACA;AAAA,QACA,aAAa,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,qBAAqB;AAAA,QACrB,iBAAiB,oBAAI,KAAK;AAAA,QAC1B,WAAW,oBAAI,KAAK;AAAA,MACtB,CAAQ;AACR,SAAG,QAAQ,IAAI;AAEf,YAAM,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,UAAU,MAAM,MAAM,UAAU,WAAW,KAAK,CAAC;AAC/F,UAAI,MAAM;AACR,cAAM,WAAW,GAAG,OAAO,kBAAkB;AAAA,UAC3C;AAAA,UACA;AAAA,UACA,WAAW,oBAAI,KAAK;AAAA,QACtB,CAAQ;AACR,WAAG,QAAQ,QAAQ;AAAA,MACrB;AAAA,IACF;AACA,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,IAAO,gBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,54 @@
1
+ import { CustomerUser } from "@open-mercato/core/modules/customer_accounts/data/entities";
2
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
3
+ const metadata = {
4
+ event: "customer_accounts.user.created",
5
+ persistent: true,
6
+ id: "customer_accounts:auto-link-crm"
7
+ };
8
+ async function handle(payload, ctx) {
9
+ const data = payload;
10
+ const userId = data?.id;
11
+ const tenantId = data?.tenantId;
12
+ const organizationId = data?.organizationId;
13
+ if (!userId || !tenantId) return;
14
+ const em = ctx.resolve("em");
15
+ try {
16
+ let email;
17
+ if (data?.email) {
18
+ email = data.email.toLowerCase().trim();
19
+ } else {
20
+ const user = await em.findOne(CustomerUser, { id: userId, tenantId, deletedAt: null });
21
+ if (user) email = user.email?.toLowerCase().trim();
22
+ }
23
+ if (!email) return;
24
+ const { CustomerEntity } = await import("@open-mercato/core/modules/customers/data/entities");
25
+ const personEntities = await findWithDecryption(
26
+ em,
27
+ CustomerEntity,
28
+ { tenantId, kind: "person", deletedAt: null },
29
+ { limit: 500 },
30
+ { tenantId, organizationId }
31
+ );
32
+ const matchingEntity = personEntities.find(
33
+ (e) => e.primaryEmail && e.primaryEmail.toLowerCase().trim() === email
34
+ );
35
+ if (!matchingEntity) return;
36
+ const profileRows = await em.getConnection().execute(
37
+ `SELECT company_entity_id FROM customer_people WHERE entity_id = ? LIMIT 1`,
38
+ [matchingEntity.id]
39
+ );
40
+ const companyEntityId = profileRows?.[0]?.company_entity_id;
41
+ const updates = { personEntityId: matchingEntity.id };
42
+ if (companyEntityId) {
43
+ updates.customerEntityId = companyEntityId;
44
+ }
45
+ await em.nativeUpdate(CustomerUser, { id: userId }, updates);
46
+ } catch (err) {
47
+ console.error("[customer_accounts:auto-link-crm] Failed to link customer user to CRM person:", err);
48
+ }
49
+ }
50
+ export {
51
+ handle as default,
52
+ metadata
53
+ };
54
+ //# sourceMappingURL=autoLinkCrm.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/customer_accounts/subscribers/autoLinkCrm.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerUser } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { hashForLookup } from '@open-mercato/shared/lib/encryption/aes'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nexport const metadata = {\n event: 'customer_accounts.user.created',\n persistent: true,\n id: 'customer_accounts:auto-link-crm',\n}\n\nexport default async function handle(\n payload: unknown,\n ctx: { resolve: <T = unknown>(name: string) => T; eventName?: string },\n): Promise<void> {\n const data = payload as Record<string, unknown>\n const userId = data?.id as string\n const tenantId = data?.tenantId as string\n const organizationId = data?.organizationId as string | undefined\n if (!userId || !tenantId) return\n\n const em = ctx.resolve<EntityManager>('em')\n\n try {\n let email: string | undefined\n if (data?.email) {\n email = (data.email as string).toLowerCase().trim()\n } else {\n const user = await em.findOne(CustomerUser, { id: userId, tenantId, deletedAt: null })\n if (user) email = user.email?.toLowerCase().trim()\n }\n if (!email) return\n\n const { CustomerEntity } = await import('@open-mercato/core/modules/customers/data/entities')\n\n const personEntities = await findWithDecryption(\n em,\n CustomerEntity,\n { tenantId, kind: 'person', deletedAt: null } as any,\n { limit: 500 } as any,\n { tenantId, organizationId },\n )\n\n const matchingEntity = personEntities.find(\n (e: any) => e.primaryEmail && e.primaryEmail.toLowerCase().trim() === email,\n ) as any\n\n if (!matchingEntity) return\n\n const profileRows = await em.getConnection().execute(\n `SELECT company_entity_id FROM customer_people WHERE entity_id = ? LIMIT 1`,\n [matchingEntity.id],\n )\n const companyEntityId = profileRows?.[0]?.company_entity_id as string | undefined\n\n const updates: Record<string, unknown> = { personEntityId: matchingEntity.id }\n if (companyEntityId) {\n updates.customerEntityId = companyEntityId\n }\n\n await em.nativeUpdate(CustomerUser, { id: userId }, updates)\n } catch (err) {\n console.error('[customer_accounts:auto-link-crm] Failed to link customer user to CRM person:', err)\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAE7B,SAAS,0BAA0B;AAE5B,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,eAAO,OACL,SACA,KACe;AACf,QAAM,OAAO;AACb,QAAM,SAAS,MAAM;AACrB,QAAM,WAAW,MAAM;AACvB,QAAM,iBAAiB,MAAM;AAC7B,MAAI,CAAC,UAAU,CAAC,SAAU;AAE1B,QAAM,KAAK,IAAI,QAAuB,IAAI;AAE1C,MAAI;AACF,QAAI;AACJ,QAAI,MAAM,OAAO;AACf,cAAS,KAAK,MAAiB,YAAY,EAAE,KAAK;AAAA,IACpD,OAAO;AACL,YAAM,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,IAAI,QAAQ,UAAU,WAAW,KAAK,CAAC;AACrF,UAAI,KAAM,SAAQ,KAAK,OAAO,YAAY,EAAE,KAAK;AAAA,IACnD;AACA,QAAI,CAAC,MAAO;AAEZ,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,oDAAoD;AAE5F,UAAM,iBAAiB,MAAM;AAAA,MAC3B;AAAA,MACA;AAAA,MACA,EAAE,UAAU,MAAM,UAAU,WAAW,KAAK;AAAA,MAC5C,EAAE,OAAO,IAAI;AAAA,MACb,EAAE,UAAU,eAAe;AAAA,IAC7B;AAEA,UAAM,iBAAiB,eAAe;AAAA,MACpC,CAAC,MAAW,EAAE,gBAAgB,EAAE,aAAa,YAAY,EAAE,KAAK,MAAM;AAAA,IACxE;AAEA,QAAI,CAAC,eAAgB;AAErB,UAAM,cAAc,MAAM,GAAG,cAAc,EAAE;AAAA,MAC3C;AAAA,MACA,CAAC,eAAe,EAAE;AAAA,IACpB;AACA,UAAM,kBAAkB,cAAc,CAAC,GAAG;AAE1C,UAAM,UAAmC,EAAE,gBAAgB,eAAe,GAAG;AAC7E,QAAI,iBAAiB;AACnB,cAAQ,mBAAmB;AAAA,IAC7B;AAEA,UAAM,GAAG,aAAa,cAAc,EAAE,IAAI,OAAO,GAAG,OAAO;AAAA,EAC7D,SAAS,KAAK;AACZ,YAAQ,MAAM,iFAAiF,GAAG;AAAA,EACpG;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,68 @@
1
+ import { CustomerUser } from "@open-mercato/core/modules/customer_accounts/data/entities";
2
+ import { hashForLookup } from "@open-mercato/shared/lib/encryption/aes";
3
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
4
+ const metadata = {
5
+ event: "customers.person.created",
6
+ persistent: true,
7
+ id: "customer_accounts:auto-link-crm-reverse"
8
+ };
9
+ async function handle(payload, ctx) {
10
+ const data = payload;
11
+ const eventId = data?.id;
12
+ const tenantId = data?.tenantId;
13
+ const organizationId = data?.organizationId;
14
+ if (!eventId || !tenantId) return;
15
+ const em = ctx.resolve("em");
16
+ try {
17
+ const { CustomerEntity } = await import("@open-mercato/core/modules/customers/data/entities");
18
+ let entity = await findOneWithDecryption(
19
+ em,
20
+ CustomerEntity,
21
+ { id: eventId, tenantId, kind: "person", deletedAt: null },
22
+ void 0,
23
+ { tenantId, organizationId }
24
+ );
25
+ if (!entity) {
26
+ const { CustomerPersonProfile } = await import("@open-mercato/core/modules/customers/data/entities");
27
+ const profile = await em.findOne(CustomerPersonProfile, { id: eventId }, { populate: ["entity"] });
28
+ if (profile?.entity) {
29
+ entity = await findOneWithDecryption(
30
+ em,
31
+ CustomerEntity,
32
+ { id: profile.entity.id ?? profile.entity, tenantId, deletedAt: null },
33
+ void 0,
34
+ { tenantId, organizationId }
35
+ );
36
+ }
37
+ }
38
+ if (!entity) return;
39
+ const email = entity.primaryEmail;
40
+ if (!email) return;
41
+ const emailHash = hashForLookup(email.toLowerCase().trim());
42
+ const personProfile = await em.getConnection().execute(
43
+ `SELECT company_entity_id FROM customer_people WHERE entity_id = ? LIMIT 1`,
44
+ [entity.id]
45
+ );
46
+ const companyEntityId = personProfile?.[0]?.company_entity_id;
47
+ const customerUser = await em.findOne(CustomerUser, {
48
+ emailHash,
49
+ tenantId,
50
+ deletedAt: null,
51
+ personEntityId: null
52
+ });
53
+ if (customerUser) {
54
+ const updates = { personEntityId: entity.id };
55
+ if (companyEntityId && !customerUser.customerEntityId) {
56
+ updates.customerEntityId = companyEntityId;
57
+ }
58
+ await em.nativeUpdate(CustomerUser, { id: customerUser.id }, updates);
59
+ }
60
+ } catch (err) {
61
+ console.error("[customer_accounts:auto-link-crm-reverse] Failed to link CRM person to customer user:", err);
62
+ }
63
+ }
64
+ export {
65
+ handle as default,
66
+ metadata
67
+ };
68
+ //# sourceMappingURL=autoLinkCrmReverse.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/customer_accounts/subscribers/autoLinkCrmReverse.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerUser } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { hashForLookup } from '@open-mercato/shared/lib/encryption/aes'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nexport const metadata = {\n event: 'customers.person.created',\n persistent: true,\n id: 'customer_accounts:auto-link-crm-reverse',\n}\n\nexport default async function handle(\n payload: unknown,\n ctx: { resolve: <T = unknown>(name: string) => T; eventName?: string },\n): Promise<void> {\n const data = payload as Record<string, unknown>\n const eventId = data?.id as string\n const tenantId = data?.tenantId as string\n const organizationId = data?.organizationId as string | undefined\n if (!eventId || !tenantId) return\n\n const em = ctx.resolve<EntityManager>('em')\n\n try {\n const { CustomerEntity } = await import('@open-mercato/core/modules/customers/data/entities')\n\n let entity = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: eventId, tenantId, kind: 'person', deletedAt: null } as any,\n undefined,\n { tenantId, organizationId },\n )\n\n if (!entity) {\n const { CustomerPersonProfile } = await import('@open-mercato/core/modules/customers/data/entities')\n const profile = await em.findOne(CustomerPersonProfile as any, { id: eventId } as any, { populate: ['entity'] }) as any\n if (profile?.entity) {\n entity = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: profile.entity.id ?? profile.entity, tenantId, deletedAt: null } as any,\n undefined,\n { tenantId, organizationId },\n )\n }\n }\n\n if (!entity) return\n const email = (entity as any).primaryEmail as string | null\n if (!email) return\n\n const emailHash = hashForLookup(email.toLowerCase().trim())\n\n const personProfile = await em.getConnection().execute(\n `SELECT company_entity_id FROM customer_people WHERE entity_id = ? LIMIT 1`,\n [(entity as any).id],\n )\n const companyEntityId = personProfile?.[0]?.company_entity_id as string | undefined\n\n const customerUser = await em.findOne(CustomerUser, {\n emailHash,\n tenantId,\n deletedAt: null,\n personEntityId: null,\n })\n\n if (customerUser) {\n const updates: Record<string, unknown> = { personEntityId: (entity as any).id }\n if (companyEntityId && !customerUser.customerEntityId) {\n updates.customerEntityId = companyEntityId\n }\n await em.nativeUpdate(CustomerUser, { id: customerUser.id }, updates)\n }\n } catch (err) {\n console.error('[customer_accounts:auto-link-crm-reverse] Failed to link CRM person to customer user:', err)\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AAE/B,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,eAAO,OACL,SACA,KACe;AACf,QAAM,OAAO;AACb,QAAM,UAAU,MAAM;AACtB,QAAM,WAAW,MAAM;AACvB,QAAM,iBAAiB,MAAM;AAC7B,MAAI,CAAC,WAAW,CAAC,SAAU;AAE3B,QAAM,KAAK,IAAI,QAAuB,IAAI;AAE1C,MAAI;AACF,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,oDAAoD;AAE5F,QAAI,SAAS,MAAM;AAAA,MACjB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,SAAS,UAAU,MAAM,UAAU,WAAW,KAAK;AAAA,MACzD;AAAA,MACA,EAAE,UAAU,eAAe;AAAA,IAC7B;AAEA,QAAI,CAAC,QAAQ;AACX,YAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,oDAAoD;AACnG,YAAM,UAAU,MAAM,GAAG,QAAQ,uBAA8B,EAAE,IAAI,QAAQ,GAAU,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC;AAC/G,UAAI,SAAS,QAAQ;AACnB,iBAAS,MAAM;AAAA,UACb;AAAA,UACA;AAAA,UACA,EAAE,IAAI,QAAQ,OAAO,MAAM,QAAQ,QAAQ,UAAU,WAAW,KAAK;AAAA,UACrE;AAAA,UACA,EAAE,UAAU,eAAe;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,OAAQ;AACb,UAAM,QAAS,OAAe;AAC9B,QAAI,CAAC,MAAO;AAEZ,UAAM,YAAY,cAAc,MAAM,YAAY,EAAE,KAAK,CAAC;AAE1D,UAAM,gBAAgB,MAAM,GAAG,cAAc,EAAE;AAAA,MAC7C;AAAA,MACA,CAAE,OAAe,EAAE;AAAA,IACrB;AACA,UAAM,kBAAkB,gBAAgB,CAAC,GAAG;AAE5C,UAAM,eAAe,MAAM,GAAG,QAAQ,cAAc;AAAA,MAClD;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,gBAAgB;AAAA,IAClB,CAAC;AAED,QAAI,cAAc;AAChB,YAAM,UAAmC,EAAE,gBAAiB,OAAe,GAAG;AAC9E,UAAI,mBAAmB,CAAC,aAAa,kBAAkB;AACrD,gBAAQ,mBAAmB;AAAA,MAC7B;AACA,YAAM,GAAG,aAAa,cAAc,EAAE,IAAI,aAAa,GAAG,GAAG,OAAO;AAAA,IACtE;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,yFAAyF,GAAG;AAAA,EAC5G;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,29 @@
1
+ const metadata = {
2
+ event: "customer_accounts.user.created",
3
+ persistent: true,
4
+ id: "customer_accounts:notify-staff-on-signup"
5
+ };
6
+ async function handle(payload, ctx) {
7
+ const data = payload;
8
+ const userId = data?.id;
9
+ const email = data?.email;
10
+ const tenantId = data?.tenantId;
11
+ const organizationId = data?.organizationId;
12
+ if (!userId || !email || !tenantId || !organizationId) return;
13
+ try {
14
+ const eventBus = ctx.resolve("eventBus");
15
+ await eventBus.emitEvent("notifications.create", {
16
+ type: "customer_accounts.user.signup",
17
+ tenantId,
18
+ organizationId,
19
+ sourceEntityId: userId,
20
+ data: { userId, email }
21
+ });
22
+ } catch {
23
+ }
24
+ }
25
+ export {
26
+ handle as default,
27
+ metadata
28
+ };
29
+ //# sourceMappingURL=notifyStaffOnSignup.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/customer_accounts/subscribers/notifyStaffOnSignup.ts"],
4
+ "sourcesContent": ["import type { EventBus } from '@open-mercato/events/types'\n\nexport const metadata = {\n event: 'customer_accounts.user.created',\n persistent: true,\n id: 'customer_accounts:notify-staff-on-signup',\n}\n\nexport default async function handle(\n payload: unknown,\n ctx: { resolve: <T = unknown>(name: string) => T; eventName?: string },\n): Promise<void> {\n const data = payload as Record<string, unknown>\n const userId = data?.id as string\n const email = data?.email as string\n const tenantId = data?.tenantId as string\n const organizationId = data?.organizationId as string\n if (!userId || !email || !tenantId || !organizationId) return\n\n try {\n const eventBus = ctx.resolve<EventBus>('eventBus')\n await eventBus.emitEvent('notifications.create', {\n type: 'customer_accounts.user.signup',\n tenantId,\n organizationId,\n sourceEntityId: userId,\n data: { userId, email },\n })\n } catch {\n // Notifications module may not be available\n }\n}\n"],
5
+ "mappings": "AAEO,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,eAAO,OACL,SACA,KACe;AACf,QAAM,OAAO;AACb,QAAM,SAAS,MAAM;AACrB,QAAM,QAAQ,MAAM;AACpB,QAAM,WAAW,MAAM;AACvB,QAAM,iBAAiB,MAAM;AAC7B,MAAI,CAAC,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC,eAAgB;AAEvD,MAAI;AACF,UAAM,WAAW,IAAI,QAAkB,UAAU;AACjD,UAAM,SAAS,UAAU,wBAAwB;AAAA,MAC/C,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,gBAAgB;AAAA,MAChB,MAAM,EAAE,QAAQ,MAAM;AAAA,IACxB,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,9 @@
1
+ const translatableFields = {
2
+ "customer_accounts:customer_role": ["name", "description"]
3
+ };
4
+ var translations_default = translatableFields;
5
+ export {
6
+ translations_default as default,
7
+ translatableFields
8
+ };
9
+ //# sourceMappingURL=translations.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/customer_accounts/translations.ts"],
4
+ "sourcesContent": ["export const translatableFields: Record<string, string[]> = {\n 'customer_accounts:customer_role': ['name', 'description'],\n}\n\nexport default translatableFields\n"],
5
+ "mappings": "AAAO,MAAM,qBAA+C;AAAA,EAC1D,mCAAmC,CAAC,QAAQ,aAAa;AAC3D;AAEA,IAAO,uBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useQuery } from "@tanstack/react-query";
4
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ function AccountStatusWidget({ context }) {
7
+ const t = useT();
8
+ const personEntityId = context?.recordId;
9
+ const { data, isLoading } = useQuery({
10
+ queryKey: ["customer-account-status", personEntityId],
11
+ queryFn: async () => {
12
+ if (!personEntityId) return null;
13
+ const result = await apiCall(`/api/customer_accounts/admin/users?personEntityId=${personEntityId}&pageSize=1`);
14
+ if (!result.ok) return null;
15
+ const json = result.result;
16
+ const items = json?.items;
17
+ return items?.[0] || null;
18
+ },
19
+ enabled: !!personEntityId
20
+ });
21
+ if (isLoading) {
22
+ return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("common.loading", "Loading...") });
23
+ }
24
+ if (!data) {
25
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md border p-3", children: [
26
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium mb-1", children: t("customer_accounts.widgets.accountStatus", "Portal Account") }),
27
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("customer_accounts.widgets.noAccount", "No portal account linked") })
28
+ ] });
29
+ }
30
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md border p-3", children: [
31
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium mb-2", children: t("customer_accounts.widgets.accountStatus", "Portal Account") }),
32
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm", children: [
33
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
34
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("common.status", "Status") }),
35
+ /* @__PURE__ */ jsx("span", { className: data.isActive ? "text-green-600" : "text-red-600", children: data.isActive ? t("common.active", "Active") : t("common.inactive", "Inactive") })
36
+ ] }),
37
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
38
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("common.email", "Email") }),
39
+ /* @__PURE__ */ jsx("span", { children: data.email })
40
+ ] }),
41
+ data.emailVerified !== void 0 && /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
42
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("customer_accounts.widgets.emailVerified", "Email Verified") }),
43
+ /* @__PURE__ */ jsx("span", { children: data.emailVerified ? "\u2713" : "\u2717" })
44
+ ] }),
45
+ data.lastLoginAt && /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
46
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("customer_accounts.widgets.lastLogin", "Last Login") }),
47
+ /* @__PURE__ */ jsx("span", { children: new Date(data.lastLoginAt).toLocaleDateString() })
48
+ ] })
49
+ ] }),
50
+ /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
51
+ "a",
52
+ {
53
+ href: `/backend/customer_accounts/${data.id}`,
54
+ className: "text-xs text-primary hover:underline",
55
+ children: t("customer_accounts.widgets.viewAccount", "View account details \u2192")
56
+ }
57
+ ) })
58
+ ] });
59
+ }
60
+ export {
61
+ AccountStatusWidget as default
62
+ };
63
+ //# sourceMappingURL=widget.client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport { useQuery } from '@tanstack/react-query'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ninterface AccountStatusData {\n id: string\n email: string\n isActive: boolean\n emailVerified: boolean\n lastLoginAt: string | null\n}\n\ninterface AccountStatusProps {\n context?: {\n entityId?: string\n recordId?: string\n }\n}\n\nexport default function AccountStatusWidget({ context }: AccountStatusProps) {\n const t = useT()\n const personEntityId = context?.recordId\n\n const { data, isLoading } = useQuery({\n queryKey: ['customer-account-status', personEntityId],\n queryFn: async (): Promise<AccountStatusData | null> => {\n if (!personEntityId) return null\n const result = await apiCall(`/api/customer_accounts/admin/users?personEntityId=${personEntityId}&pageSize=1`)\n if (!result.ok) return null\n const json = result.result as Record<string, unknown> | null\n const items = json?.items as AccountStatusData[] | undefined\n return items?.[0] || null\n },\n enabled: !!personEntityId,\n })\n\n if (isLoading) {\n return <div className=\"text-sm text-muted-foreground\">{t('common.loading', 'Loading...')}</div>\n }\n\n if (!data) {\n return (\n <div className=\"rounded-md border p-3\">\n <div className=\"text-sm font-medium mb-1\">{t('customer_accounts.widgets.accountStatus', 'Portal Account')}</div>\n <div className=\"text-sm text-muted-foreground\">{t('customer_accounts.widgets.noAccount', 'No portal account linked')}</div>\n </div>\n )\n }\n\n return (\n <div className=\"rounded-md border p-3\">\n <div className=\"text-sm font-medium mb-2\">{t('customer_accounts.widgets.accountStatus', 'Portal Account')}</div>\n <div className=\"space-y-1 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('common.status', 'Status')}</span>\n <span className={data.isActive ? 'text-green-600' : 'text-red-600'}>\n {data.isActive ? t('common.active', 'Active') : t('common.inactive', 'Inactive')}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('common.email', 'Email')}</span>\n <span>{data.email}</span>\n </div>\n {data.emailVerified !== undefined && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('customer_accounts.widgets.emailVerified', 'Email Verified')}</span>\n <span>{data.emailVerified ? '\u2713' : '\u2717'}</span>\n </div>\n )}\n {data.lastLoginAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('customer_accounts.widgets.lastLogin', 'Last Login')}</span>\n <span>{new Date(data.lastLoginAt).toLocaleDateString()}</span>\n </div>\n )}\n </div>\n <div className=\"mt-2\">\n <a\n href={`/backend/customer_accounts/${data.id}`}\n className=\"text-xs text-primary hover:underline\"\n >\n {t('customer_accounts.widgets.viewAccount', 'View account details \u2192')}\n </a>\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": ";AAuCW,cAKL,YALK;AArCX,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,YAAY;AAiBN,SAAR,oBAAqC,EAAE,QAAQ,GAAuB;AAC3E,QAAM,IAAI,KAAK;AACf,QAAM,iBAAiB,SAAS;AAEhC,QAAM,EAAE,MAAM,UAAU,IAAI,SAAS;AAAA,IACnC,UAAU,CAAC,2BAA2B,cAAc;AAAA,IACpD,SAAS,YAA+C;AACtD,UAAI,CAAC,eAAgB,QAAO;AAC5B,YAAM,SAAS,MAAM,QAAQ,qDAAqD,cAAc,aAAa;AAC7G,UAAI,CAAC,OAAO,GAAI,QAAO;AACvB,YAAM,OAAO,OAAO;AACpB,YAAM,QAAQ,MAAM;AACpB,aAAO,QAAQ,CAAC,KAAK;AAAA,IACvB;AAAA,IACA,SAAS,CAAC,CAAC;AAAA,EACb,CAAC;AAED,MAAI,WAAW;AACb,WAAO,oBAAC,SAAI,WAAU,iCAAiC,YAAE,kBAAkB,YAAY,GAAE;AAAA,EAC3F;AAEA,MAAI,CAAC,MAAM;AACT,WACE,qBAAC,SAAI,WAAU,yBACb;AAAA,0BAAC,SAAI,WAAU,4BAA4B,YAAE,2CAA2C,gBAAgB,GAAE;AAAA,MAC1G,oBAAC,SAAI,WAAU,iCAAiC,YAAE,uCAAuC,0BAA0B,GAAE;AAAA,OACvH;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,yBACb;AAAA,wBAAC,SAAI,WAAU,4BAA4B,YAAE,2CAA2C,gBAAgB,GAAE;AAAA,IAC1G,qBAAC,SAAI,WAAU,qBACb;AAAA,2BAAC,SAAI,WAAU,wBACb;AAAA,4BAAC,UAAK,WAAU,yBAAyB,YAAE,iBAAiB,QAAQ,GAAE;AAAA,QACtE,oBAAC,UAAK,WAAW,KAAK,WAAW,mBAAmB,gBACjD,eAAK,WAAW,EAAE,iBAAiB,QAAQ,IAAI,EAAE,mBAAmB,UAAU,GACjF;AAAA,SACF;AAAA,MACA,qBAAC,SAAI,WAAU,wBACb;AAAA,4BAAC,UAAK,WAAU,yBAAyB,YAAE,gBAAgB,OAAO,GAAE;AAAA,QACpE,oBAAC,UAAM,eAAK,OAAM;AAAA,SACpB;AAAA,MACC,KAAK,kBAAkB,UACtB,qBAAC,SAAI,WAAU,wBACb;AAAA,4BAAC,UAAK,WAAU,yBAAyB,YAAE,2CAA2C,gBAAgB,GAAE;AAAA,QACxG,oBAAC,UAAM,eAAK,gBAAgB,WAAM,UAAI;AAAA,SACxC;AAAA,MAED,KAAK,eACJ,qBAAC,SAAI,WAAU,wBACb;AAAA,4BAAC,UAAK,WAAU,yBAAyB,YAAE,uCAAuC,YAAY,GAAE;AAAA,QAChG,oBAAC,UAAM,cAAI,KAAK,KAAK,WAAW,EAAE,mBAAmB,GAAE;AAAA,SACzD;AAAA,OAEJ;AAAA,IACA,oBAAC,SAAI,WAAU,QACb;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,8BAA8B,KAAK,EAAE;AAAA,QAC3C,WAAU;AAAA,QAET,YAAE,yCAAyC,6BAAwB;AAAA;AAAA,IACtE,GACF;AAAA,KACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ import AccountStatusWidget from "./widget.client.js";
2
+ const widget = {
3
+ metadata: {
4
+ id: "customer_accounts.injection.account-status",
5
+ title: "Customer Account Status",
6
+ description: "Shows customer portal account status on CRM person detail",
7
+ features: ["customer_accounts.view"],
8
+ priority: 100,
9
+ enabled: true
10
+ },
11
+ Widget: AccountStatusWidget
12
+ };
13
+ var widget_default = widget;
14
+ export {
15
+ widget_default as default
16
+ };
17
+ //# sourceMappingURL=widget.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customer_accounts/widgets/injection/account-status/widget.ts"],
4
+ "sourcesContent": ["import type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'\nimport AccountStatusWidget from './widget.client'\n\nconst widget: InjectionWidgetModule<Record<string, unknown>, Record<string, unknown>> = {\n metadata: {\n id: 'customer_accounts.injection.account-status',\n title: 'Customer Account Status',\n description: 'Shows customer portal account status on CRM person detail',\n features: ['customer_accounts.view'],\n priority: 100,\n enabled: true,\n },\n Widget: AccountStatusWidget,\n}\n\nexport default widget\n"],
5
+ "mappings": "AACA,OAAO,yBAAyB;AAEhC,MAAM,SAAkF;AAAA,EACtF,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU,CAAC,wBAAwB;AAAA,IACnC,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAAA,EACA,QAAQ;AACV;AAEA,IAAO,iBAAQ;",
6
+ "names": []
7
+ }