@open-mercato/core 0.6.5-develop.4384.1.ce2ec6eaaa → 0.6.5-develop.4397.1.9a65481757

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 (533) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/channel_ingest_dead_letter/index.js +25 -0
  3. package/dist/generated/entities/channel_ingest_dead_letter/index.js.map +7 -0
  4. package/dist/generated/entities/channel_thread_mapping/index.js +25 -0
  5. package/dist/generated/entities/channel_thread_mapping/index.js.map +7 -0
  6. package/dist/generated/entities/channel_thread_token/index.js +17 -0
  7. package/dist/generated/entities/channel_thread_token/index.js.map +7 -0
  8. package/dist/generated/entities/communication_channel/index.js +43 -0
  9. package/dist/generated/entities/communication_channel/index.js.map +7 -0
  10. package/dist/generated/entities/customer_interaction/index.js +4 -0
  11. package/dist/generated/entities/customer_interaction/index.js.map +2 -2
  12. package/dist/generated/entities/external_conversation/index.js +25 -0
  13. package/dist/generated/entities/external_conversation/index.js.map +7 -0
  14. package/dist/generated/entities/external_message/index.js +25 -0
  15. package/dist/generated/entities/external_message/index.js.map +7 -0
  16. package/dist/generated/entities/integration_credentials/index.js +3 -1
  17. package/dist/generated/entities/integration_credentials/index.js.map +2 -2
  18. package/dist/generated/entities/message/index.js +2 -0
  19. package/dist/generated/entities/message/index.js.map +2 -2
  20. package/dist/generated/entities/message_channel_link/index.js +33 -0
  21. package/dist/generated/entities/message_channel_link/index.js.map +7 -0
  22. package/dist/generated/entities/message_reaction/index.js +25 -0
  23. package/dist/generated/entities/message_reaction/index.js.map +7 -0
  24. package/dist/generated/entities.ids.generated.js +11 -0
  25. package/dist/generated/entities.ids.generated.js.map +2 -2
  26. package/dist/generated/entity-fields-registry.js +117 -0
  27. package/dist/generated/entity-fields-registry.js.map +2 -2
  28. package/dist/helpers/integration/authFixtures.js +2 -1
  29. package/dist/helpers/integration/authFixtures.js.map +2 -2
  30. package/dist/helpers/integration/communicationChannelsFixtures.js +58 -0
  31. package/dist/helpers/integration/communicationChannelsFixtures.js.map +7 -0
  32. package/dist/modules/communication_channels/acl.js +47 -0
  33. package/dist/modules/communication_channels/acl.js.map +7 -0
  34. package/dist/modules/communication_channels/api/delete/channels/[id]/route.js +133 -0
  35. package/dist/modules/communication_channels/api/delete/channels/[id]/route.js.map +7 -0
  36. package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js +113 -0
  37. package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js.map +7 -0
  38. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +138 -0
  39. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +7 -0
  40. package/dist/modules/communication_channels/api/get/channels/[id]/route.js +93 -0
  41. package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +7 -0
  42. package/dist/modules/communication_channels/api/get/channels/route.js +96 -0
  43. package/dist/modules/communication_channels/api/get/channels/route.js.map +7 -0
  44. package/dist/modules/communication_channels/api/get/me/channels/route.js +82 -0
  45. package/dist/modules/communication_channels/api/get/me/channels/route.js.map +7 -0
  46. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +274 -0
  47. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +7 -0
  48. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +168 -0
  49. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +7 -0
  50. package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js +143 -0
  51. package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js.map +7 -0
  52. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js +127 -0
  53. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js.map +7 -0
  54. package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js +99 -0
  55. package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js.map +7 -0
  56. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +197 -0
  57. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +7 -0
  58. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +124 -0
  59. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +7 -0
  60. package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js +120 -0
  61. package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js.map +7 -0
  62. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +157 -0
  63. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +7 -0
  64. package/dist/modules/communication_channels/api/post/send-as-user/route.js +115 -0
  65. package/dist/modules/communication_channels/api/post/send-as-user/route.js.map +7 -0
  66. package/dist/modules/communication_channels/api/post/test-seed/route.js +217 -0
  67. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +7 -0
  68. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +175 -0
  69. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +7 -0
  70. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +123 -0
  71. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +7 -0
  72. package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js +117 -0
  73. package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js.map +7 -0
  74. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js +180 -0
  75. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js.map +7 -0
  76. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js +36 -0
  77. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js.map +7 -0
  78. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +107 -0
  79. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +7 -0
  80. package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js +38 -0
  81. package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js.map +7 -0
  82. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +727 -0
  83. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +7 -0
  84. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js +38 -0
  85. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js.map +7 -0
  86. package/dist/modules/communication_channels/commands/connect-credential-channel.js +154 -0
  87. package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +7 -0
  88. package/dist/modules/communication_channels/commands/delete-channel.js +137 -0
  89. package/dist/modules/communication_channels/commands/delete-channel.js.map +7 -0
  90. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +400 -0
  91. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +7 -0
  92. package/dist/modules/communication_channels/commands/disconnect-channel.js +163 -0
  93. package/dist/modules/communication_channels/commands/disconnect-channel.js.map +7 -0
  94. package/dist/modules/communication_channels/commands/ingest-inbound-message.js +413 -0
  95. package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +7 -0
  96. package/dist/modules/communication_channels/commands/interceptors.js +68 -0
  97. package/dist/modules/communication_channels/commands/interceptors.js.map +7 -0
  98. package/dist/modules/communication_channels/commands/process-inbound-reaction.js +198 -0
  99. package/dist/modules/communication_channels/commands/process-inbound-reaction.js.map +7 -0
  100. package/dist/modules/communication_channels/commands/push-register.js +146 -0
  101. package/dist/modules/communication_channels/commands/push-register.js.map +7 -0
  102. package/dist/modules/communication_channels/commands/push-renew.js +23 -0
  103. package/dist/modules/communication_channels/commands/push-renew.js.map +7 -0
  104. package/dist/modules/communication_channels/commands/push-unregister.js +108 -0
  105. package/dist/modules/communication_channels/commands/push-unregister.js.map +7 -0
  106. package/dist/modules/communication_channels/commands/queue-import-history.js +113 -0
  107. package/dist/modules/communication_channels/commands/queue-import-history.js.map +7 -0
  108. package/dist/modules/communication_channels/commands/reassign-conversation.js +193 -0
  109. package/dist/modules/communication_channels/commands/reassign-conversation.js.map +7 -0
  110. package/dist/modules/communication_channels/commands/set-primary-channel.js +114 -0
  111. package/dist/modules/communication_channels/commands/set-primary-channel.js.map +7 -0
  112. package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js +260 -0
  113. package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js.map +7 -0
  114. package/dist/modules/communication_channels/data/enrichers.js +286 -0
  115. package/dist/modules/communication_channels/data/enrichers.js.map +7 -0
  116. package/dist/modules/communication_channels/data/entities.js +447 -0
  117. package/dist/modules/communication_channels/data/entities.js.map +7 -0
  118. package/dist/modules/communication_channels/data/extensions.js +67 -0
  119. package/dist/modules/communication_channels/data/extensions.js.map +7 -0
  120. package/dist/modules/communication_channels/data/validators.js +123 -0
  121. package/dist/modules/communication_channels/data/validators.js.map +7 -0
  122. package/dist/modules/communication_channels/di.js +35 -0
  123. package/dist/modules/communication_channels/di.js.map +7 -0
  124. package/dist/modules/communication_channels/encryption.js +12 -0
  125. package/dist/modules/communication_channels/encryption.js.map +7 -0
  126. package/dist/modules/communication_channels/events.js +124 -0
  127. package/dist/modules/communication_channels/events.js.map +7 -0
  128. package/dist/modules/communication_channels/index.js +20 -0
  129. package/dist/modules/communication_channels/index.js.map +7 -0
  130. package/dist/modules/communication_channels/lib/access-control.js +43 -0
  131. package/dist/modules/communication_channels/lib/access-control.js.map +7 -0
  132. package/dist/modules/communication_channels/lib/adapter-compat.js +36 -0
  133. package/dist/modules/communication_channels/lib/adapter-compat.js.map +7 -0
  134. package/dist/modules/communication_channels/lib/adapter-registry-singleton.js +22 -0
  135. package/dist/modules/communication_channels/lib/adapter-registry-singleton.js.map +7 -0
  136. package/dist/modules/communication_channels/lib/adapter.js +1 -0
  137. package/dist/modules/communication_channels/lib/adapter.js.map +7 -0
  138. package/dist/modules/communication_channels/lib/connect-channel.js +95 -0
  139. package/dist/modules/communication_channels/lib/connect-channel.js.map +7 -0
  140. package/dist/modules/communication_channels/lib/contact-resolver.js +79 -0
  141. package/dist/modules/communication_channels/lib/contact-resolver.js.map +7 -0
  142. package/dist/modules/communication_channels/lib/credential-refresh.js +97 -0
  143. package/dist/modules/communication_channels/lib/credential-refresh.js.map +7 -0
  144. package/dist/modules/communication_channels/lib/dead-letter.js +62 -0
  145. package/dist/modules/communication_channels/lib/dead-letter.js.map +7 -0
  146. package/dist/modules/communication_channels/lib/email-capabilities.js +47 -0
  147. package/dist/modules/communication_channels/lib/email-capabilities.js.map +7 -0
  148. package/dist/modules/communication_channels/lib/email-contact.js +14 -0
  149. package/dist/modules/communication_channels/lib/email-contact.js.map +7 -0
  150. package/dist/modules/communication_channels/lib/email-mime.js +269 -0
  151. package/dist/modules/communication_channels/lib/email-mime.js.map +7 -0
  152. package/dist/modules/communication_channels/lib/error-classification.js +101 -0
  153. package/dist/modules/communication_channels/lib/error-classification.js.map +7 -0
  154. package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js +185 -0
  155. package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js.map +7 -0
  156. package/dist/modules/communication_channels/lib/mutation-guards.js +114 -0
  157. package/dist/modules/communication_channels/lib/mutation-guards.js.map +7 -0
  158. package/dist/modules/communication_channels/lib/oauth-client-config.js +32 -0
  159. package/dist/modules/communication_channels/lib/oauth-client-config.js.map +7 -0
  160. package/dist/modules/communication_channels/lib/oauth-state.js +128 -0
  161. package/dist/modules/communication_channels/lib/oauth-state.js.map +7 -0
  162. package/dist/modules/communication_channels/lib/oauth-token.js +45 -0
  163. package/dist/modules/communication_channels/lib/oauth-token.js.map +7 -0
  164. package/dist/modules/communication_channels/lib/pg-errors.js +11 -0
  165. package/dist/modules/communication_channels/lib/pg-errors.js.map +7 -0
  166. package/dist/modules/communication_channels/lib/provider-health.js +24 -0
  167. package/dist/modules/communication_channels/lib/provider-health.js.map +7 -0
  168. package/dist/modules/communication_channels/lib/push-state.js +19 -0
  169. package/dist/modules/communication_channels/lib/push-state.js.map +7 -0
  170. package/dist/modules/communication_channels/lib/queue.js +54 -0
  171. package/dist/modules/communication_channels/lib/queue.js.map +7 -0
  172. package/dist/modules/communication_channels/lib/reaction-processor-types.js +5 -0
  173. package/dist/modules/communication_channels/lib/reaction-processor-types.js.map +7 -0
  174. package/dist/modules/communication_channels/lib/reaction-semantics.js +11 -0
  175. package/dist/modules/communication_channels/lib/reaction-semantics.js.map +7 -0
  176. package/dist/modules/communication_channels/lib/registry.js +67 -0
  177. package/dist/modules/communication_channels/lib/registry.js.map +7 -0
  178. package/dist/modules/communication_channels/lib/route-mutation-guard.js +43 -0
  179. package/dist/modules/communication_channels/lib/route-mutation-guard.js.map +7 -0
  180. package/dist/modules/communication_channels/lib/sanitize-channel-html.js +96 -0
  181. package/dist/modules/communication_channels/lib/sanitize-channel-html.js.map +7 -0
  182. package/dist/modules/communication_channels/lib/send-as-user.js +194 -0
  183. package/dist/modules/communication_channels/lib/send-as-user.js.map +7 -0
  184. package/dist/modules/communication_channels/lib/system-user.js +22 -0
  185. package/dist/modules/communication_channels/lib/system-user.js.map +7 -0
  186. package/dist/modules/communication_channels/lib/test-seed.js +68 -0
  187. package/dist/modules/communication_channels/lib/test-seed.js.map +7 -0
  188. package/dist/modules/communication_channels/lib/thread-matcher.js +263 -0
  189. package/dist/modules/communication_channels/lib/thread-matcher.js.map +7 -0
  190. package/dist/modules/communication_channels/lib/thread-token.js +219 -0
  191. package/dist/modules/communication_channels/lib/thread-token.js.map +7 -0
  192. package/dist/modules/communication_channels/lib/use-connect-channel.js +61 -0
  193. package/dist/modules/communication_channels/lib/use-connect-channel.js.map +7 -0
  194. package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js +50 -0
  195. package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js.map +7 -0
  196. package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js +19 -0
  197. package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js.map +7 -0
  198. package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js +13 -0
  199. package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js.map +7 -0
  200. package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js +17 -0
  201. package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js.map +7 -0
  202. package/dist/modules/communication_channels/notifications.client.js +51 -0
  203. package/dist/modules/communication_channels/notifications.client.js.map +7 -0
  204. package/dist/modules/communication_channels/notifications.handlers.js +53 -0
  205. package/dist/modules/communication_channels/notifications.handlers.js.map +7 -0
  206. package/dist/modules/communication_channels/notifications.js +56 -0
  207. package/dist/modules/communication_channels/notifications.js.map +7 -0
  208. package/dist/modules/communication_channels/setup.js +105 -0
  209. package/dist/modules/communication_channels/setup.js.map +7 -0
  210. package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js +71 -0
  211. package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js.map +7 -0
  212. package/dist/modules/communication_channels/subscribers/outbound-bridge.js +103 -0
  213. package/dist/modules/communication_channels/subscribers/outbound-bridge.js.map +7 -0
  214. package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js +51 -0
  215. package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js.map +7 -0
  216. package/dist/modules/communication_channels/widgets/components.js +7 -0
  217. package/dist/modules/communication_channels/widgets/components.js.map +7 -0
  218. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js +18 -0
  219. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js.map +7 -0
  220. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js +30 -0
  221. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js.map +7 -0
  222. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js +185 -0
  223. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js.map +7 -0
  224. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js +17 -0
  225. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js.map +7 -0
  226. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +44 -0
  227. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +7 -0
  228. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js +17 -0
  229. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js.map +7 -0
  230. package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js +23 -0
  231. package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js.map +7 -0
  232. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js +141 -0
  233. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js.map +7 -0
  234. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js +17 -0
  235. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js.map +7 -0
  236. package/dist/modules/communication_channels/widgets/injection-table.js +38 -0
  237. package/dist/modules/communication_channels/widgets/injection-table.js.map +7 -0
  238. package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js +25 -0
  239. package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js.map +7 -0
  240. package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js +19 -0
  241. package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js.map +7 -0
  242. package/dist/modules/communication_channels/widgets/notifications/index.js +7 -0
  243. package/dist/modules/communication_channels/widgets/notifications/index.js.map +7 -0
  244. package/dist/modules/communication_channels/workers/channel-import-history.js +185 -0
  245. package/dist/modules/communication_channels/workers/channel-import-history.js.map +7 -0
  246. package/dist/modules/communication_channels/workers/gmail-history-sync.js +154 -0
  247. package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +7 -0
  248. package/dist/modules/communication_channels/workers/gmail-renew-watch.js +95 -0
  249. package/dist/modules/communication_channels/workers/gmail-renew-watch.js.map +7 -0
  250. package/dist/modules/communication_channels/workers/inbound-processor.js +56 -0
  251. package/dist/modules/communication_channels/workers/inbound-processor.js.map +7 -0
  252. package/dist/modules/communication_channels/workers/outbound-delivery.js +85 -0
  253. package/dist/modules/communication_channels/workers/outbound-delivery.js.map +7 -0
  254. package/dist/modules/communication_channels/workers/poll-channel.js +240 -0
  255. package/dist/modules/communication_channels/workers/poll-channel.js.map +7 -0
  256. package/dist/modules/communication_channels/workers/poll-tick.js +132 -0
  257. package/dist/modules/communication_channels/workers/poll-tick.js.map +7 -0
  258. package/dist/modules/communication_channels/workers/reaction-processor.js +192 -0
  259. package/dist/modules/communication_channels/workers/reaction-processor.js.map +7 -0
  260. package/dist/modules/customers/acl.js +18 -0
  261. package/dist/modules/customers/acl.js.map +2 -2
  262. package/dist/modules/customers/api/activities/route.js +9 -0
  263. package/dist/modules/customers/api/activities/route.js.map +2 -2
  264. package/dist/modules/customers/api/companies/[id]/route.js +18 -7
  265. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  266. package/dist/modules/customers/api/interactions/[id]/visibility/route.js +151 -0
  267. package/dist/modules/customers/api/interactions/[id]/visibility/route.js.map +7 -0
  268. package/dist/modules/customers/api/interactions/counts/route.js +6 -0
  269. package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
  270. package/dist/modules/customers/api/interactions/route.js +26 -7
  271. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  272. package/dist/modules/customers/api/people/[id]/email-threads/route.js +82 -0
  273. package/dist/modules/customers/api/people/[id]/email-threads/route.js.map +7 -0
  274. package/dist/modules/customers/api/people/[id]/emails/route.js +157 -0
  275. package/dist/modules/customers/api/people/[id]/emails/route.js.map +7 -0
  276. package/dist/modules/customers/api/people/[id]/route.js +12 -4
  277. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  278. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +10 -0
  279. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  280. package/dist/modules/customers/commands/deals.js +46 -5
  281. package/dist/modules/customers/commands/deals.js.map +2 -2
  282. package/dist/modules/customers/commands/interactions.js +16 -0
  283. package/dist/modules/customers/commands/interactions.js.map +2 -2
  284. package/dist/modules/customers/components/detail/ActivityCard.js +32 -0
  285. package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
  286. package/dist/modules/customers/components/detail/ComposeEmailDialog.js +242 -0
  287. package/dist/modules/customers/components/detail/ComposeEmailDialog.js.map +7 -0
  288. package/dist/modules/customers/components/detail/DealForm.js +2 -1
  289. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  290. package/dist/modules/customers/components/detail/DealsSection.js +10 -0
  291. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  292. package/dist/modules/customers/components/detail/EmailCardActions.js +179 -0
  293. package/dist/modules/customers/components/detail/EmailCardActions.js.map +7 -0
  294. package/dist/modules/customers/components/detail/EmailReplyForwardActions.js +52 -0
  295. package/dist/modules/customers/components/detail/EmailReplyForwardActions.js.map +7 -0
  296. package/dist/modules/customers/components/detail/PersonDetailTabs.js +7 -1
  297. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  298. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js +366 -0
  299. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js.map +7 -0
  300. package/dist/modules/customers/data/enrichers.js +133 -2
  301. package/dist/modules/customers/data/enrichers.js.map +2 -2
  302. package/dist/modules/customers/data/entities.js +18 -0
  303. package/dist/modules/customers/data/entities.js.map +2 -2
  304. package/dist/modules/customers/data/extensions.js +16 -0
  305. package/dist/modules/customers/data/extensions.js.map +7 -0
  306. package/dist/modules/customers/encryption.js +11 -0
  307. package/dist/modules/customers/encryption.js.map +2 -2
  308. package/dist/modules/customers/events.js +4 -1
  309. package/dist/modules/customers/events.js.map +2 -2
  310. package/dist/modules/customers/lib/findPeopleByAddresses.js +64 -0
  311. package/dist/modules/customers/lib/findPeopleByAddresses.js.map +7 -0
  312. package/dist/modules/customers/lib/kysely.js.map +2 -2
  313. package/dist/modules/customers/lib/link-channel-message-handler.js +303 -0
  314. package/dist/modules/customers/lib/link-channel-message-handler.js.map +7 -0
  315. package/dist/modules/customers/lib/personEmailThreads.js +205 -0
  316. package/dist/modules/customers/lib/personEmailThreads.js.map +7 -0
  317. package/dist/modules/customers/lib/visibilityFilter.js +51 -0
  318. package/dist/modules/customers/lib/visibilityFilter.js.map +7 -0
  319. package/dist/modules/customers/migrations/Migration20260527012240_customers.js +20 -0
  320. package/dist/modules/customers/migrations/Migration20260527012240_customers.js.map +7 -0
  321. package/dist/modules/customers/setup.js +2 -1
  322. package/dist/modules/customers/setup.js.map +2 -2
  323. package/dist/modules/customers/subscribers/link-channel-message-received.js +12 -0
  324. package/dist/modules/customers/subscribers/link-channel-message-received.js.map +7 -0
  325. package/dist/modules/customers/subscribers/link-channel-message-sent.js +12 -0
  326. package/dist/modules/customers/subscribers/link-channel-message-sent.js.map +7 -0
  327. package/dist/modules/integrations/data/entities.js +8 -1
  328. package/dist/modules/integrations/data/entities.js.map +2 -2
  329. package/dist/modules/integrations/lib/credentials-service.js +29 -14
  330. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  331. package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js +15 -0
  332. package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js.map +7 -0
  333. package/dist/modules/messages/commands/messages.js +70 -8
  334. package/dist/modules/messages/commands/messages.js.map +2 -2
  335. package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -13
  336. package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
  337. package/dist/modules/messages/components/MessageDetailPageClient.js +39 -2
  338. package/dist/modules/messages/components/MessageDetailPageClient.js.map +2 -2
  339. package/dist/modules/messages/components/MessagesInboxPageClient.js +1 -0
  340. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  341. package/dist/modules/messages/data/entities.js +8 -1
  342. package/dist/modules/messages/data/entities.js.map +2 -2
  343. package/dist/modules/messages/migrations/Migration20260531130000.js +15 -0
  344. package/dist/modules/messages/migrations/Migration20260531130000.js.map +7 -0
  345. package/dist/modules/messages/widgets/injection-table.js +7 -0
  346. package/dist/modules/messages/widgets/injection-table.js.map +7 -0
  347. package/generated/entities/channel_ingest_dead_letter/index.ts +11 -0
  348. package/generated/entities/channel_thread_mapping/index.ts +11 -0
  349. package/generated/entities/channel_thread_token/index.ts +7 -0
  350. package/generated/entities/communication_channel/index.ts +20 -0
  351. package/generated/entities/customer_interaction/index.ts +2 -0
  352. package/generated/entities/external_conversation/index.ts +11 -0
  353. package/generated/entities/external_message/index.ts +11 -0
  354. package/generated/entities/integration_credentials/index.ts +1 -0
  355. package/generated/entities/message/index.ts +1 -0
  356. package/generated/entities/message_channel_link/index.ts +15 -0
  357. package/generated/entities/message_reaction/index.ts +11 -0
  358. package/generated/entities.ids.generated.ts +11 -0
  359. package/generated/entity-fields-registry.ts +117 -0
  360. package/package.json +9 -7
  361. package/src/helpers/integration/authFixtures.ts +4 -1
  362. package/src/helpers/integration/communicationChannelsFixtures.ts +124 -0
  363. package/src/modules/communication_channels/acl.ts +43 -0
  364. package/src/modules/communication_channels/api/delete/channels/[id]/route.ts +163 -0
  365. package/src/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.ts +143 -0
  366. package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +173 -0
  367. package/src/modules/communication_channels/api/get/channels/[id]/route.ts +111 -0
  368. package/src/modules/communication_channels/api/get/channels/route.ts +109 -0
  369. package/src/modules/communication_channels/api/get/me/channels/route.ts +100 -0
  370. package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +355 -0
  371. package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +206 -0
  372. package/src/modules/communication_channels/api/post/channels/[id]/poll-now/route.ts +174 -0
  373. package/src/modules/communication_channels/api/post/channels/[id]/push/register/route.ts +158 -0
  374. package/src/modules/communication_channels/api/post/channels/[id]/set-primary/route.ts +114 -0
  375. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +241 -0
  376. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +134 -0
  377. package/src/modules/communication_channels/api/post/messages/[messageId]/reactions/route.ts +143 -0
  378. package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +192 -0
  379. package/src/modules/communication_channels/api/post/send-as-user/route.ts +125 -0
  380. package/src/modules/communication_channels/api/post/test-seed/route.ts +267 -0
  381. package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +227 -0
  382. package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +161 -0
  383. package/src/modules/communication_channels/api/put/threads/[threadId]/assign/route.ts +132 -0
  384. package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.ts +34 -0
  385. package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.tsx +250 -0
  386. package/src/modules/communication_channels/backend/communication_channels/channels/page.meta.ts +36 -0
  387. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +137 -0
  388. package/src/modules/communication_channels/backend/profile/communication-channels/page.meta.ts +36 -0
  389. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +907 -0
  390. package/src/modules/communication_channels/commands/connect-credential-channel.ts +243 -0
  391. package/src/modules/communication_channels/commands/delete-channel.ts +193 -0
  392. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +579 -0
  393. package/src/modules/communication_channels/commands/disconnect-channel.ts +241 -0
  394. package/src/modules/communication_channels/commands/ingest-inbound-message.ts +602 -0
  395. package/src/modules/communication_channels/commands/interceptors.ts +104 -0
  396. package/src/modules/communication_channels/commands/process-inbound-reaction.ts +265 -0
  397. package/src/modules/communication_channels/commands/push-register.ts +203 -0
  398. package/src/modules/communication_channels/commands/push-renew.ts +49 -0
  399. package/src/modules/communication_channels/commands/push-unregister.ts +168 -0
  400. package/src/modules/communication_channels/commands/queue-import-history.ts +180 -0
  401. package/src/modules/communication_channels/commands/reassign-conversation.ts +273 -0
  402. package/src/modules/communication_channels/commands/set-primary-channel.ts +154 -0
  403. package/src/modules/communication_channels/commands/toggle-outbound-reaction.ts +347 -0
  404. package/src/modules/communication_channels/data/enrichers.ts +413 -0
  405. package/src/modules/communication_channels/data/entities.ts +546 -0
  406. package/src/modules/communication_channels/data/extensions.ts +76 -0
  407. package/src/modules/communication_channels/data/validators.ts +138 -0
  408. package/src/modules/communication_channels/di.ts +40 -0
  409. package/src/modules/communication_channels/encryption.ts +44 -0
  410. package/src/modules/communication_channels/events.ts +122 -0
  411. package/src/modules/communication_channels/i18n/de.json +138 -0
  412. package/src/modules/communication_channels/i18n/en.json +138 -0
  413. package/src/modules/communication_channels/i18n/es.json +138 -0
  414. package/src/modules/communication_channels/i18n/pl.json +138 -0
  415. package/src/modules/communication_channels/index.ts +19 -0
  416. package/src/modules/communication_channels/lib/access-control.ts +110 -0
  417. package/src/modules/communication_channels/lib/adapter-compat.ts +57 -0
  418. package/src/modules/communication_channels/lib/adapter-registry-singleton.ts +35 -0
  419. package/src/modules/communication_channels/lib/adapter.ts +605 -0
  420. package/src/modules/communication_channels/lib/connect-channel.ts +163 -0
  421. package/src/modules/communication_channels/lib/contact-resolver.ts +162 -0
  422. package/src/modules/communication_channels/lib/credential-refresh.ts +197 -0
  423. package/src/modules/communication_channels/lib/dead-letter.ts +87 -0
  424. package/src/modules/communication_channels/lib/email-capabilities.ts +60 -0
  425. package/src/modules/communication_channels/lib/email-contact.ts +17 -0
  426. package/src/modules/communication_channels/lib/email-mime.ts +442 -0
  427. package/src/modules/communication_channels/lib/error-classification.ts +144 -0
  428. package/src/modules/communication_channels/lib/gmail-pubsub-jwt.ts +278 -0
  429. package/src/modules/communication_channels/lib/mutation-guards.ts +215 -0
  430. package/src/modules/communication_channels/lib/oauth-client-config.ts +79 -0
  431. package/src/modules/communication_channels/lib/oauth-state.ts +228 -0
  432. package/src/modules/communication_channels/lib/oauth-token.ts +81 -0
  433. package/src/modules/communication_channels/lib/pg-errors.ts +12 -0
  434. package/src/modules/communication_channels/lib/provider-health.ts +47 -0
  435. package/src/modules/communication_channels/lib/push-state.ts +38 -0
  436. package/src/modules/communication_channels/lib/queue.ts +66 -0
  437. package/src/modules/communication_channels/lib/reaction-processor-types.ts +51 -0
  438. package/src/modules/communication_channels/lib/reaction-semantics.ts +48 -0
  439. package/src/modules/communication_channels/lib/registry.ts +99 -0
  440. package/src/modules/communication_channels/lib/route-mutation-guard.ts +68 -0
  441. package/src/modules/communication_channels/lib/sanitize-channel-html.ts +129 -0
  442. package/src/modules/communication_channels/lib/send-as-user.ts +284 -0
  443. package/src/modules/communication_channels/lib/system-user.ts +74 -0
  444. package/src/modules/communication_channels/lib/test-seed.ts +140 -0
  445. package/src/modules/communication_channels/lib/thread-matcher.ts +430 -0
  446. package/src/modules/communication_channels/lib/thread-token.ts +355 -0
  447. package/src/modules/communication_channels/lib/use-connect-channel.ts +73 -0
  448. package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +2142 -0
  449. package/src/modules/communication_channels/migrations/Migration20260526134719_communication_channels.ts +55 -0
  450. package/src/modules/communication_channels/migrations/Migration20260527195446_communication_channels.ts +20 -0
  451. package/src/modules/communication_channels/migrations/Migration20260529231848_communication_channels.ts +13 -0
  452. package/src/modules/communication_channels/migrations/Migration20260531120000_communication_channels.ts +24 -0
  453. package/src/modules/communication_channels/notifications.client.ts +50 -0
  454. package/src/modules/communication_channels/notifications.handlers.ts +86 -0
  455. package/src/modules/communication_channels/notifications.ts +52 -0
  456. package/src/modules/communication_channels/setup.ts +158 -0
  457. package/src/modules/communication_channels/subscribers/channel-requires-reauth-notification.ts +118 -0
  458. package/src/modules/communication_channels/subscribers/outbound-bridge.ts +175 -0
  459. package/src/modules/communication_channels/subscribers/user-deleted-cascade.ts +100 -0
  460. package/src/modules/communication_channels/widgets/components.ts +36 -0
  461. package/src/modules/communication_channels/widgets/injection/channel-badge/widget.client.tsx +38 -0
  462. package/src/modules/communication_channels/widgets/injection/channel-badge/widget.ts +51 -0
  463. package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.tsx +278 -0
  464. package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.ts +24 -0
  465. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +63 -0
  466. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.ts +29 -0
  467. package/src/modules/communication_channels/widgets/injection/profile-channels-menu/widget.ts +34 -0
  468. package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.client.tsx +177 -0
  469. package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.ts +26 -0
  470. package/src/modules/communication_channels/widgets/injection-table.ts +47 -0
  471. package/src/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.tsx +48 -0
  472. package/src/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.tsx +45 -0
  473. package/src/modules/communication_channels/widgets/notifications/index.ts +2 -0
  474. package/src/modules/communication_channels/workers/channel-import-history.ts +252 -0
  475. package/src/modules/communication_channels/workers/gmail-history-sync.ts +223 -0
  476. package/src/modules/communication_channels/workers/gmail-renew-watch.ts +141 -0
  477. package/src/modules/communication_channels/workers/inbound-processor.ts +114 -0
  478. package/src/modules/communication_channels/workers/outbound-delivery.ts +155 -0
  479. package/src/modules/communication_channels/workers/poll-channel.ts +391 -0
  480. package/src/modules/communication_channels/workers/poll-tick.ts +210 -0
  481. package/src/modules/communication_channels/workers/reaction-processor.ts +264 -0
  482. package/src/modules/customers/acl.ts +18 -0
  483. package/src/modules/customers/api/activities/route.ts +13 -0
  484. package/src/modules/customers/api/companies/[id]/route.ts +21 -1
  485. package/src/modules/customers/api/interactions/[id]/visibility/route.ts +179 -0
  486. package/src/modules/customers/api/interactions/counts/route.ts +10 -0
  487. package/src/modules/customers/api/interactions/route.ts +51 -5
  488. package/src/modules/customers/api/people/[id]/email-threads/route.ts +92 -0
  489. package/src/modules/customers/api/people/[id]/emails/route.ts +184 -0
  490. package/src/modules/customers/api/people/[id]/route.ts +17 -2
  491. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +11 -1
  492. package/src/modules/customers/commands/deals.ts +65 -6
  493. package/src/modules/customers/commands/interactions.ts +30 -0
  494. package/src/modules/customers/components/detail/ActivityCard.tsx +48 -0
  495. package/src/modules/customers/components/detail/ComposeEmailDialog.tsx +329 -0
  496. package/src/modules/customers/components/detail/DealForm.tsx +2 -1
  497. package/src/modules/customers/components/detail/DealsSection.tsx +26 -0
  498. package/src/modules/customers/components/detail/EmailCardActions.tsx +258 -0
  499. package/src/modules/customers/components/detail/EmailReplyForwardActions.tsx +53 -0
  500. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +8 -1
  501. package/src/modules/customers/components/detail/PersonEmailThreadsTab.tsx +448 -0
  502. package/src/modules/customers/data/enrichers.ts +252 -1
  503. package/src/modules/customers/data/entities.ts +46 -1
  504. package/src/modules/customers/data/extensions.ts +26 -0
  505. package/src/modules/customers/encryption.ts +11 -0
  506. package/src/modules/customers/events.ts +4 -0
  507. package/src/modules/customers/i18n/de.json +41 -0
  508. package/src/modules/customers/i18n/en.json +41 -0
  509. package/src/modules/customers/i18n/es.json +41 -0
  510. package/src/modules/customers/i18n/pl.json +41 -0
  511. package/src/modules/customers/lib/findPeopleByAddresses.ts +107 -0
  512. package/src/modules/customers/lib/kysely.ts +16 -0
  513. package/src/modules/customers/lib/link-channel-message-handler.ts +571 -0
  514. package/src/modules/customers/lib/personEmailThreads.ts +325 -0
  515. package/src/modules/customers/lib/visibilityFilter.ts +152 -0
  516. package/src/modules/customers/migrations/.snapshot-open-mercato.json +61 -0
  517. package/src/modules/customers/migrations/Migration20260527012240_customers.ts +23 -0
  518. package/src/modules/customers/setup.ts +1 -0
  519. package/src/modules/customers/subscribers/link-channel-message-received.ts +21 -0
  520. package/src/modules/customers/subscribers/link-channel-message-sent.ts +21 -0
  521. package/src/modules/integrations/AGENTS.md +9 -0
  522. package/src/modules/integrations/data/entities.ts +21 -1
  523. package/src/modules/integrations/lib/credentials-service.ts +49 -13
  524. package/src/modules/integrations/migrations/.snapshot-open-mercato.json +26 -1
  525. package/src/modules/integrations/migrations/Migration20260526154136_integrations.ts +15 -0
  526. package/src/modules/messages/commands/messages.ts +101 -8
  527. package/src/modules/messages/components/ComposeMessagePageClient.tsx +17 -0
  528. package/src/modules/messages/components/MessageDetailPageClient.tsx +43 -0
  529. package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -0
  530. package/src/modules/messages/data/entities.ts +11 -0
  531. package/src/modules/messages/migrations/.snapshot-open-mercato.json +18 -0
  532. package/src/modules/messages/migrations/Migration20260531130000.ts +15 -0
  533. package/src/modules/messages/widgets/injection-table.ts +29 -0
@@ -0,0 +1,175 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
+ import { ChannelThreadMapping, CommunicationChannel, MessageChannelLink } from '../data/entities'
4
+ import { Message } from '../../messages/data/entities'
5
+ import { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from '../lib/queue'
6
+ import type { OutboundDeliveryPayload } from '../workers/outbound-delivery'
7
+
8
+ /**
9
+ * Subscriber: outbound bridge.
10
+ *
11
+ * Listens to `messages.message.sent` and, when the Message lives in a channel-linked
12
+ * thread, enqueues a delivery job to the `communication-channels-outbound` queue.
13
+ *
14
+ * Per the pre-implementation analysis we **re-fetch the Message by ID** rather than
15
+ * trusting the event payload — this keeps the subscriber decoupled from the
16
+ * `messages.message.sent` payload shape, so any future addition/removal of fields
17
+ * in the messages module doesn't break this bridge.
18
+ *
19
+ * Idempotency: we check for an existing `MessageChannelLink` with `direction='outbound'`
20
+ * and `deliveryStatus IN ('queued','pending','sent','delivered','read')`. If found, we
21
+ * skip — the message is already delivered or has a delivery in flight. Including the
22
+ * in-flight states ('queued'/'pending') stops a replayed `messages.message.sent` from
23
+ * enqueueing a second delivery job while the worker is mid-send. The command-side check
24
+ * is the authoritative gate, but this cheap subscriber-level check avoids redundant jobs.
25
+ *
26
+ * Internal-only messages (no `ChannelThreadMapping` for the threadId) are skipped
27
+ * silently — this is the expected steady-state for the majority of platform messages.
28
+ */
29
+ export const metadata = {
30
+ event: 'messages.message.sent',
31
+ persistent: true,
32
+ id: 'communication_channels:outbound-bridge',
33
+ }
34
+
35
+ type MessageSentPayload = {
36
+ messageId: string
37
+ senderUserId?: string
38
+ recipientUserIds?: string[]
39
+ sendViaEmail?: boolean
40
+ externalEmail?: string | null
41
+ tenantId: string
42
+ organizationId?: string | null
43
+ }
44
+
45
+ type SubscriberContext = {
46
+ /** Canonical event-bus context: `resolve` is exposed directly (no `.container` wrapper). */
47
+ resolve: <T = unknown>(name: string) => T
48
+ /** Some callers wrap in a `container` — supported for forward-compat. */
49
+ container?: { resolve: <T = unknown>(name: string) => T }
50
+ }
51
+
52
+ function resolveFromCtx<T = unknown>(ctx: SubscriberContext, name: string): T {
53
+ if (typeof ctx?.resolve === 'function') return ctx.resolve<T>(name)
54
+ if (ctx?.container && typeof ctx.container.resolve === 'function') {
55
+ return ctx.container.resolve<T>(name)
56
+ }
57
+ throw new Error(`outbound-bridge: subscriber context has no resolver (looking for '${name}')`)
58
+ }
59
+
60
+ export default async function handler(
61
+ payload: MessageSentPayload,
62
+ ctx: SubscriberContext,
63
+ ): Promise<void> {
64
+ if (!payload?.messageId || !payload.tenantId) {
65
+ return
66
+ }
67
+
68
+ const em = (resolveFromCtx<EntityManager>(ctx, 'em')).fork()
69
+ const dscope = {
70
+ tenantId: payload.tenantId,
71
+ organizationId: payload.organizationId ?? null,
72
+ }
73
+
74
+ // (a) Re-fetch the Message — no payload-shape coupling.
75
+ const message = await findOneWithDecryption(
76
+ em,
77
+ Message,
78
+ {
79
+ id: payload.messageId,
80
+ tenantId: payload.tenantId,
81
+ organizationId: payload.organizationId ?? null,
82
+ deletedAt: null,
83
+ },
84
+ undefined,
85
+ dscope,
86
+ )
87
+ if (!message) return
88
+ if (message.sourceEntityType === 'communication_channels.send_as_user') {
89
+ return
90
+ }
91
+ // Inbound ingest path: when `ingest-inbound-message` composes a new platform
92
+ // Message for an incoming email, the messages module also emits
93
+ // `messages.message.sent` (the Message row is fresh and marked sent). Without
94
+ // this guard, we'd treat it as outbound and queue a redundant SMTP delivery —
95
+ // which fails because inbound MCLs carry recipient info in `channelPayload`,
96
+ // not `channelMetadata`, and the failure marker then leaks back onto the
97
+ // inbound link itself.
98
+ if (message.sourceEntityType === 'communication_channels.external_conversation') {
99
+ return
100
+ }
101
+ if (!message.threadId) return // Internal-only; no channel routing.
102
+
103
+ // (b) Look up the channel mapping by threadId.
104
+ const mapping = await findOneWithDecryption(
105
+ em,
106
+ ChannelThreadMapping,
107
+ {
108
+ messageThreadId: message.threadId,
109
+ tenantId: payload.tenantId,
110
+ organizationId: payload.organizationId ?? null,
111
+ },
112
+ undefined,
113
+ dscope,
114
+ )
115
+ if (!mapping) return // Internal-only thread; skip silently.
116
+
117
+ // (c) Idempotency — skip if already delivered.
118
+ const existingLink = await findOneWithDecryption(
119
+ em,
120
+ MessageChannelLink,
121
+ {
122
+ messageId: message.id,
123
+ tenantId: payload.tenantId,
124
+ organizationId: payload.organizationId ?? null,
125
+ },
126
+ undefined,
127
+ dscope,
128
+ )
129
+ if (
130
+ existingLink &&
131
+ (existingLink.deliveryStatus === 'queued' ||
132
+ existingLink.deliveryStatus === 'pending' ||
133
+ existingLink.deliveryStatus === 'sent' ||
134
+ existingLink.deliveryStatus === 'delivered' ||
135
+ existingLink.deliveryStatus === 'read')
136
+ ) {
137
+ return
138
+ }
139
+
140
+ // (c2) Per-user ownership gate. Outbound delivery sends with the CHANNEL
141
+ // OWNER's credentials (workers/outbound-delivery → deliver-outbound-message),
142
+ // so we may only bridge a platform message into a per-user channel when the
143
+ // message's sender OWNS that channel. Tenant-wide channels (userId == null —
144
+ // shared inboxes) accept any sender. Without this, composing into another
145
+ // user's channel-linked thread would send from their connected account
146
+ // (impersonation). Mirrors lib/send-as-user and the reaction ownership gate.
147
+ const channel = await findOneWithDecryption(
148
+ em,
149
+ CommunicationChannel,
150
+ {
151
+ id: mapping.channelId,
152
+ tenantId: payload.tenantId,
153
+ organizationId: payload.organizationId ?? null,
154
+ deletedAt: null,
155
+ },
156
+ undefined,
157
+ dscope,
158
+ )
159
+ if (!channel) return
160
+ if (channel.userId != null && channel.userId !== message.senderUserId) {
161
+ return
162
+ }
163
+
164
+ // (d) Enqueue the delivery worker.
165
+ const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.outbound)
166
+ const job: OutboundDeliveryPayload = {
167
+ messageId: message.id,
168
+ scope: {
169
+ tenantId: payload.tenantId,
170
+ organizationId: payload.organizationId ?? null,
171
+ },
172
+ attempt: 1,
173
+ }
174
+ await queue.enqueue(job as unknown as Record<string, unknown>)
175
+ }
@@ -0,0 +1,100 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
+ import { CommunicationChannel } from '../data/entities'
4
+
5
+ /**
6
+ * Subscriber: cascade-disconnect a user's communication channels when the user
7
+ * is deleted (`auth.user.deleted` event).
8
+ *
9
+ * Why a subscriber, not a foreign-key CASCADE: per root `AGENTS.md`, modules
10
+ * don't form direct ORM relationships across the auth module boundary —
11
+ * `CommunicationChannel.userId` is a plain `uuid` column, not a FK. The
12
+ * cascade must therefore happen at the event-bridge layer, here, in response
13
+ * to the auth module's lifecycle event.
14
+ *
15
+ * Effects per matched channel:
16
+ * 1. `status = 'disconnected'` — stops the polling worker (slice 3b
17
+ * skips channels not in `connected`).
18
+ * 2. `is_active = false` — hides the channel from the hub admin
19
+ * UI and stops adapter resolution.
20
+ * 3. `credentials_ref = null` — orphans the encrypted credentials
21
+ * blob in `integration_credentials`
22
+ * (the integrations module's own
23
+ * retention policy then sweeps it).
24
+ * 4. `last_error = 'user-deleted'` — diagnostic breadcrumb.
25
+ *
26
+ * The channel row itself is NOT hard-deleted — keeping it preserves the audit
27
+ * trail of which user owned it and the conversation history that still lives
28
+ * in `external_messages` / `messages`. A future tenant-level GDPR sweep can
29
+ * hard-delete on a schedule.
30
+ *
31
+ * Idempotency: the update is conditional on `status != 'disconnected'`, so
32
+ * replays from the event bus are no-ops.
33
+ */
34
+ export const metadata = {
35
+ event: 'auth.user.deleted',
36
+ persistent: true,
37
+ id: 'communication_channels:user-deleted-cascade',
38
+ }
39
+
40
+ type AuthUserDeletedPayload = {
41
+ userId: string
42
+ tenantId?: string
43
+ organizationId?: string | null
44
+ }
45
+
46
+ type SubscriberContext = {
47
+ resolve: <T = unknown>(name: string) => T
48
+ }
49
+
50
+ export default async function handler(
51
+ payload: AuthUserDeletedPayload,
52
+ ctx: SubscriberContext,
53
+ ): Promise<void> {
54
+ if (!payload || typeof payload.userId !== 'string' || !payload.userId) {
55
+ return
56
+ }
57
+ // tenantId MUST be present: an unscoped cascade would touch rows in arbitrary
58
+ // tenants if two tenants ever happened to share a userId (impossible under
59
+ // UUIDv4 but possible after backup-restore). Fail-closed: skip when missing
60
+ // and let an out-of-band sweep clean up.
61
+ if (typeof payload.tenantId !== 'string' || !payload.tenantId) {
62
+ return
63
+ }
64
+ const em = (ctx.resolve('em') as EntityManager).fork()
65
+
66
+ const channels = await findWithDecryption(
67
+ em,
68
+ CommunicationChannel,
69
+ {
70
+ userId: payload.userId,
71
+ tenantId: payload.tenantId,
72
+ ...(payload.organizationId !== undefined
73
+ ? { organizationId: payload.organizationId ?? null }
74
+ : {}),
75
+ deletedAt: null,
76
+ },
77
+ undefined,
78
+ {
79
+ tenantId: payload.tenantId,
80
+ organizationId: payload.organizationId ?? null,
81
+ },
82
+ )
83
+ if (channels.length === 0) return
84
+
85
+ let touched = 0
86
+ for (const channel of channels) {
87
+ // Already disconnected → idempotent skip.
88
+ if (channel.status === 'disconnected' && channel.isActive === false) continue
89
+ channel.status = 'disconnected'
90
+ channel.isActive = false
91
+ channel.credentialsRef = null
92
+ channel.lastError = 'user-deleted'
93
+ channel.isPrimary = false
94
+ channel.lastPolledAt = new Date()
95
+ touched += 1
96
+ }
97
+ if (touched > 0) {
98
+ await em.flush()
99
+ }
100
+ }
@@ -0,0 +1,36 @@
1
+ import type { ComponentOverride } from '@open-mercato/shared/modules/widgets/component-registry'
2
+
3
+ /**
4
+ * Component override scaffold for the Communications Hub.
5
+ *
6
+ * Phase 4 of the email integration spec (`2026-05-21-email-integration-foundation`)
7
+ * adds a `MessagesThreadDetailHeader` override for `channelType=email`. The shipping
8
+ * Messages module renders the header via `MainMessageHeader` and does not yet expose
9
+ * a registered component id — the header is rendered as a regular React component
10
+ * in `MessageDetailPageClient.tsx`. The override mechanism (this file, plus the
11
+ * `widgets/components.ts` auto-discovery convention) is wired and ready for the
12
+ * Messages module to opt in by registering the component.
13
+ *
14
+ * Today, email-specific affordances (subject / Cc list / attachment count / Gmail
15
+ * labels) are delivered to the Messages thread view via the
16
+ * existing `detail:messages:message:body:after` injection spot's
17
+ * `channel-payload-renderer` widget (slice 2e/2f). That widget renders the same
18
+ * affordances the spec lists as header content, just below the header rather than
19
+ * inside it. Surfacing them inside the header is the v2 upgrade path.
20
+ *
21
+ * Forward-compatible handle ids the hub will target once Messages exposes them:
22
+ * - `section:messages.detail.header` — primary slot; the email override
23
+ * wraps it with the email banner above
24
+ * the default header.
25
+ * - `data-table:messages:row` — secondary slot; email-channel rows
26
+ * get a small inline preview of the
27
+ * first-from name + envelope icon.
28
+ *
29
+ * Until Messages calls `registerComponent({ id: 'section:messages.detail.header', ... })`,
30
+ * this file exports an empty array — the hub honors the override-mechanism
31
+ * contract (downstream apps can target hub-side handles) without inventing
32
+ * fictional component ids the Messages module doesn't surface.
33
+ */
34
+ export const componentOverrides: ComponentOverride[] = []
35
+
36
+ export default componentOverrides
@@ -0,0 +1,38 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Tag } from '@open-mercato/ui/primitives/tag'
5
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
6
+
7
+ type ChannelEnrichment = {
8
+ providerKey?: string
9
+ channelType?: string
10
+ direction?: 'inbound' | 'outbound' | string
11
+ deliveryStatus?: string | null
12
+ }
13
+
14
+ export type ChannelBadgeProps = {
15
+ channel: ChannelEnrichment | null | undefined
16
+ }
17
+
18
+ /**
19
+ * Pure presentational badge — renders the channel provider/type as a `<Tag>`
20
+ * pill. The host (declarative column injection — see `widget.ts`) feeds the
21
+ * `_channel` enrichment in.
22
+ */
23
+ export default function ChannelBadgeWidget({ channel }: ChannelBadgeProps) {
24
+ const t = useT()
25
+ if (!channel) return null
26
+
27
+ const providerLabel = channel.providerKey ?? channel.channelType ?? ''
28
+ const variant: 'success' | 'info' = channel.direction === 'outbound' ? 'info' : 'success'
29
+
30
+ return (
31
+ <Tag variant={variant} dot>
32
+ {t(
33
+ `communication_channels.channel.providers.${providerLabel}`,
34
+ providerLabel,
35
+ )}
36
+ </Tag>
37
+ )
38
+ }
@@ -0,0 +1,51 @@
1
+ import * as React from 'react'
2
+ import type { InjectionColumnWidget } from '@open-mercato/shared/modules/widgets/injection'
3
+ import ChannelBadgeWidget from './widget.client'
4
+
5
+ /**
6
+ * Channel badge — renders the channel type/provider icon + label as a `<Tag>`
7
+ * pill in the messages DataTable's columns spot (`data-table:messages:columns`).
8
+ *
9
+ * DataTable's column-injection contract requires a declarative `columns` array
10
+ * of `InjectionColumnDefinition` — the registry filters out widgets that don't
11
+ * expose `columns`. The cell renderer is a React component that reads the
12
+ * `_channel` enrichment field added by `messageChannelEnricher`.
13
+ *
14
+ * Round-1/round-2 review trail: round-1 shipped this as an
15
+ * `InjectionWidgetModule` with a top-level `Widget` component, which DataTable
16
+ * silently skipped (`if (!('columns' in widget)) continue`). Round-2 R2-H6 / F7
17
+ * (2026-05-26) converted it to the declarative column shape.
18
+ *
19
+ * Provider packages can override this widget via UMES component replacement
20
+ * (`section:communication_channels.channel-badge`) to render brand-specific
21
+ * badges (Slack logo, WhatsApp green badge, etc.).
22
+ */
23
+ const widget: InjectionColumnWidget = {
24
+ metadata: {
25
+ id: 'communication_channels.injection.channel-badge',
26
+ title: 'Channel badge',
27
+ description:
28
+ 'Renders a channel-type/provider badge inline in the messages list. Visible whenever a message has an associated MessageChannelLink (= the message was bridged from or to an external channel).',
29
+ features: ['communication_channels.view'],
30
+ priority: 100,
31
+ enabled: true,
32
+ },
33
+ columns: [
34
+ {
35
+ id: 'communication_channels.channel-badge',
36
+ header: 'communication_channels.columns.provider',
37
+ accessorKey: '_channel',
38
+ size: 140,
39
+ cell: ({ getValue }) => {
40
+ const channel = getValue() as
41
+ | { providerKey?: string; channelType?: string; direction?: string; deliveryStatus?: string | null }
42
+ | null
43
+ | undefined
44
+ if (!channel) return null
45
+ return React.createElement(ChannelBadgeWidget, { channel })
46
+ },
47
+ },
48
+ ],
49
+ }
50
+
51
+ export default widget
@@ -0,0 +1,278 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'
5
+ import { hasFeature } from '@open-mercato/shared/security/features'
6
+ import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
7
+ import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
8
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
9
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
10
+ import {
11
+ Select,
12
+ SelectContent,
13
+ SelectItem,
14
+ SelectTrigger,
15
+ SelectValue,
16
+ } from '@open-mercato/ui/primitives/select'
17
+
18
+ type ChannelEnrichment = {
19
+ providerKey: string
20
+ channelType: string
21
+ direction: 'inbound' | 'outbound' | string
22
+ deliveryStatus: string | null
23
+ }
24
+
25
+ type ChannelContactEnrichment = {
26
+ contactPersonId: string | null
27
+ assignedUserId: string | null
28
+ subject: string | null
29
+ }
30
+
31
+ type MessageWithChannelContext = Record<string, unknown> & {
32
+ id?: string
33
+ threadId?: string | null
34
+ _channel?: ChannelEnrichment | null
35
+ _channelContact?: ChannelContactEnrichment | null
36
+ }
37
+
38
+ type WidgetContext = Record<string, unknown> & {
39
+ /**
40
+ * Set when the host (e.g. message detail page) provides the current user's
41
+ * feature grants. The reassignment editor is hidden when
42
+ * `communication_channels.assign` is not present. Wildcard grants
43
+ * (`*` and `communication_channels.*`) are honored too.
44
+ */
45
+ userFeatures?: string[]
46
+ }
47
+
48
+ type UserOption = { id: string; label: string }
49
+
50
+ const FEATURE_GATE = 'communication_channels.assign'
51
+ const CHANNEL_INFO_MUTATION_CONTEXT_ID = 'communication-channels-info-panel'
52
+ // Radix Select reserves '' for "no selection"; use a sentinel for Unassigned.
53
+ const UNASSIGNED_VALUE = '__unassigned__'
54
+
55
+ type ChannelInfoMutationContext = {
56
+ formId: string
57
+ resourceKind: string
58
+ resourceId: string
59
+ retryLastMutation: () => Promise<boolean>
60
+ }
61
+
62
+ export default function ChannelInfoPanelWidget({
63
+ data,
64
+ context,
65
+ }: InjectionWidgetComponentProps<WidgetContext, MessageWithChannelContext>) {
66
+ const t = useT()
67
+ const channel = data?._channel ?? null
68
+ const contact = data?._channelContact ?? null
69
+ const { runMutation, retryLastMutation } = useGuardedMutation<ChannelInfoMutationContext>({
70
+ contextId: CHANNEL_INFO_MUTATION_CONTEXT_ID,
71
+ blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),
72
+ })
73
+
74
+ const canReassign = hasFeature(context?.userFeatures ?? [], FEATURE_GATE)
75
+
76
+ const [assignedUserId, setAssignedUserId] = React.useState<string | null>(
77
+ contact?.assignedUserId ?? null,
78
+ )
79
+ const [savingAssignee, setSavingAssignee] = React.useState(false)
80
+ const [users, setUsers] = React.useState<UserOption[]>([])
81
+ const [usersLoaded, setUsersLoaded] = React.useState(false)
82
+
83
+ React.useEffect(
84
+ () => setAssignedUserId(contact?.assignedUserId ?? null),
85
+ [contact?.assignedUserId],
86
+ )
87
+
88
+ const loadUsers = React.useCallback(async () => {
89
+ if (usersLoaded || !canReassign) return
90
+ const response = await apiCall<{
91
+ items?: Array<{ id: string; email?: string | null; name?: string | null }>
92
+ }>('/api/auth/users?page=1&pageSize=50', {
93
+ // Opportunistic dropdown-options fetch: a user may hold
94
+ // `communication_channels.assign` without `auth.users.list`. Suppress the
95
+ // global 403/401 redirect + forbidden flash so the page degrades to an
96
+ // empty assignee list instead of bouncing the whole browser to /login.
97
+ headers: { 'x-om-forbidden-redirect': '0', 'x-om-unauthorized-redirect': '0' },
98
+ }).catch(() => null)
99
+ if (!response || !response.ok) {
100
+ setUsersLoaded(true)
101
+ return
102
+ }
103
+ const items = (response.result?.items ?? []).flatMap<UserOption>((u) => {
104
+ if (!u?.id) return []
105
+ const label =
106
+ (typeof u.name === 'string' && u.name.trim().length > 0 && u.name.trim()) ||
107
+ (typeof u.email === 'string' && u.email.trim().length > 0 && u.email.trim()) ||
108
+ u.id
109
+ return [{ id: u.id, label }]
110
+ })
111
+ setUsers(items)
112
+ setUsersLoaded(true)
113
+ }, [canReassign, usersLoaded])
114
+
115
+ const reassign = React.useCallback(
116
+ async (nextAssignedUserId: string | null) => {
117
+ const threadId = data?.threadId
118
+ if (!threadId) {
119
+ flash(
120
+ t(
121
+ 'communication_channels.infoPanel.noThread',
122
+ 'This message is not on a thread; reassignment unavailable.',
123
+ ),
124
+ 'error',
125
+ )
126
+ return
127
+ }
128
+ setSavingAssignee(true)
129
+ try {
130
+ const response = await runMutation({
131
+ operation: () => apiCall<{ assignedUserId: string | null }>(
132
+ `/api/communication_channels/threads/${encodeURIComponent(threadId)}/assign`,
133
+ {
134
+ method: 'PUT',
135
+ headers: { 'content-type': 'application/json' },
136
+ body: JSON.stringify({ assignedUserId: nextAssignedUserId }),
137
+ },
138
+ ),
139
+ context: {
140
+ formId: CHANNEL_INFO_MUTATION_CONTEXT_ID,
141
+ resourceKind: 'communication_channels.thread',
142
+ resourceId: threadId,
143
+ retryLastMutation,
144
+ },
145
+ mutationPayload: { assignedUserId: nextAssignedUserId },
146
+ })
147
+ if (!response.ok) {
148
+ const body = response.result as { error?: string } | undefined
149
+ flash(
150
+ body?.error ??
151
+ t('communication_channels.infoPanel.reassignError', 'Reassignment failed'),
152
+ 'error',
153
+ )
154
+ return
155
+ }
156
+ setAssignedUserId(nextAssignedUserId)
157
+ flash(
158
+ t('communication_channels.infoPanel.reassignSuccess', 'Conversation reassigned.'),
159
+ 'success',
160
+ )
161
+ } catch (err) {
162
+ flash(
163
+ err instanceof Error
164
+ ? err.message
165
+ : t('communication_channels.infoPanel.reassignError', 'Reassignment failed'),
166
+ 'error',
167
+ )
168
+ } finally {
169
+ setSavingAssignee(false)
170
+ }
171
+ },
172
+ [data?.threadId, retryLastMutation, runMutation, t],
173
+ )
174
+
175
+ if (!channel) return null
176
+
177
+ return (
178
+ <aside
179
+ className="rounded-md border bg-card p-4 text-sm"
180
+ aria-label={t('communication_channels.infoPanel.aria', 'Channel info')}
181
+ >
182
+ <header className="mb-2 text-overline text-muted-foreground">
183
+ {t('communication_channels.infoPanel.title', 'Channel info')}
184
+ </header>
185
+ <dl className="grid grid-cols-2 gap-1 text-xs">
186
+ <dt className="text-muted-foreground">
187
+ {t('communication_channels.infoPanel.provider', 'Provider')}
188
+ </dt>
189
+ <dd>{channel.providerKey}</dd>
190
+
191
+ <dt className="text-muted-foreground">
192
+ {t('communication_channels.infoPanel.type', 'Type')}
193
+ </dt>
194
+ <dd>{channel.channelType}</dd>
195
+
196
+ <dt className="text-muted-foreground">
197
+ {t('communication_channels.infoPanel.direction', 'Direction')}
198
+ </dt>
199
+ <dd>{channel.direction}</dd>
200
+
201
+ {channel.deliveryStatus ? (
202
+ <>
203
+ <dt className="text-muted-foreground">
204
+ {t('communication_channels.infoPanel.status', 'Status')}
205
+ </dt>
206
+ <dd>{channel.deliveryStatus}</dd>
207
+ </>
208
+ ) : null}
209
+
210
+ {contact?.contactPersonId ? (
211
+ <>
212
+ <dt className="text-muted-foreground">
213
+ {t('communication_channels.infoPanel.contactPerson', 'CRM contact')}
214
+ </dt>
215
+ <dd className="truncate" title={contact.contactPersonId}>
216
+ {contact.contactPersonId}
217
+ </dd>
218
+ </>
219
+ ) : null}
220
+
221
+ {contact?.subject ? (
222
+ <>
223
+ <dt className="text-muted-foreground">
224
+ {t('communication_channels.infoPanel.subject', 'Subject')}
225
+ </dt>
226
+ <dd className="col-span-2 truncate" title={contact.subject}>
227
+ {contact.subject}
228
+ </dd>
229
+ </>
230
+ ) : null}
231
+
232
+ <dt className="text-muted-foreground">
233
+ {t('communication_channels.infoPanel.assignedTo', 'Assigned to')}
234
+ </dt>
235
+ <dd className="col-span-1">
236
+ {canReassign ? (
237
+ <Select
238
+ value={assignedUserId ?? UNASSIGNED_VALUE}
239
+ onValueChange={(value) =>
240
+ void reassign(value === UNASSIGNED_VALUE ? null : value)
241
+ }
242
+ onOpenChange={(isOpen) => {
243
+ if (isOpen) void loadUsers()
244
+ }}
245
+ disabled={savingAssignee || !data?.threadId}
246
+ >
247
+ <SelectTrigger
248
+ size="xs"
249
+ aria-label={t('communication_channels.infoPanel.assignedTo', 'Assigned to')}
250
+ >
251
+ <SelectValue
252
+ placeholder={t('communication_channels.infoPanel.unassigned', 'Unassigned')}
253
+ />
254
+ </SelectTrigger>
255
+ <SelectContent>
256
+ <SelectItem value={UNASSIGNED_VALUE}>
257
+ {t('communication_channels.infoPanel.unassigned', 'Unassigned')}
258
+ </SelectItem>
259
+ {assignedUserId && !users.some((u) => u.id === assignedUserId) ? (
260
+ <SelectItem value={assignedUserId}>{assignedUserId}</SelectItem>
261
+ ) : null}
262
+ {users.map((user) => (
263
+ <SelectItem key={user.id} value={user.id}>
264
+ {user.label}
265
+ </SelectItem>
266
+ ))}
267
+ </SelectContent>
268
+ </Select>
269
+ ) : (
270
+ <span className="truncate" title={assignedUserId ?? ''}>
271
+ {assignedUserId ?? t('communication_channels.infoPanel.unassigned', 'Unassigned')}
272
+ </span>
273
+ )}
274
+ </dd>
275
+ </dl>
276
+ </aside>
277
+ )
278
+ }