@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,55 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260526134719_communication_channels extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ this.addSql(`create table "channel_thread_mappings" ("id" uuid not null default gen_random_uuid(), "external_conversation_id" uuid not null, "message_thread_id" uuid not null, "channel_id" uuid not null, "provider_key" text not null, "external_thread_ref" text not null, "assigned_user_id" uuid null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, "updated_at" timestamptz not null, primary key ("id"));`);
7
+ this.addSql(`create index "channel_thread_mappings_thread_idx" on "channel_thread_mappings" ("message_thread_id", "tenant_id");`);
8
+ this.addSql(`create index "channel_thread_mappings_ext_conv_idx" on "channel_thread_mappings" ("external_conversation_id", "tenant_id");`);
9
+ this.addSql(`alter table "channel_thread_mappings" add constraint "channel_thread_mappings_ext_conv_uq" unique ("external_conversation_id", "tenant_id");`);
10
+
11
+ this.addSql(`create table "communication_channels" ("id" uuid not null default gen_random_uuid(), "provider_key" text not null, "channel_type" text not null, "display_name" text not null, "external_identifier" text null, "credentials_ref" uuid null, "capabilities" jsonb null, "is_active" boolean not null default true, "user_id" uuid null, "is_primary" boolean not null default false, "poll_interval_seconds" int null, "last_polled_at" timestamptz null, "status" text not null default 'connected', "last_error" text null, "channel_state" jsonb null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, primary key ("id"));`);
12
+ this.addSql(`create index "communication_channels_tenant_type_active_idx" on "communication_channels" ("tenant_id", "channel_type", "is_active");`);
13
+ this.addSql(`create index "communication_channels_tenant_provider_idx" on "communication_channels" ("tenant_id", "provider_key");`);
14
+ this.addSql(`create unique index "communication_channels_one_primary_per_user_uq" on "communication_channels" ("user_id") where "is_primary" and "user_id" is not null and "deleted_at" is null;`);
15
+ this.addSql(`create index "communication_channels_poll_due_idx" on "communication_channels" ("is_active", "last_polled_at") where "deleted_at" is null;`);
16
+ this.addSql(`create index "communication_channels_user_lookup_idx" on "communication_channels" ("user_id", "channel_type", "deleted_at");`);
17
+
18
+ this.addSql(`create table "external_conversations" ("id" uuid not null default gen_random_uuid(), "channel_id" uuid not null, "external_conversation_id" text not null, "subject" text null, "contact_person_id" uuid null, "assigned_user_id" uuid null, "last_message_at" timestamptz null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, "updated_at" timestamptz not null, primary key ("id"));`);
19
+ this.addSql(`create index "external_conversations_assigned_user_idx" on "external_conversations" ("assigned_user_id");`);
20
+ this.addSql(`create index "external_conversations_contact_person_idx" on "external_conversations" ("contact_person_id");`);
21
+ this.addSql(`create index "external_conversations_channel_idx" on "external_conversations" ("channel_id", "external_conversation_id");`);
22
+ this.addSql(`alter table "external_conversations" add constraint "external_conversations_channel_external_uq" unique ("channel_id", "external_conversation_id");`);
23
+
24
+ this.addSql(`create table "external_messages" ("id" uuid not null default gen_random_uuid(), "channel_id" uuid not null, "conversation_id" uuid not null, "external_message_id" text not null, "direction" text not null, "sender_identifier" text null, "sender_display_name" text null, "provider_timestamp" timestamptz null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, primary key ("id"));`);
25
+ this.addSql(`create index "external_messages_channel_external_idx" on "external_messages" ("channel_id", "external_message_id");`);
26
+ this.addSql(`create index "external_messages_conversation_idx" on "external_messages" ("conversation_id");`);
27
+ this.addSql(`alter table "external_messages" add constraint "external_messages_channel_external_uq" unique ("channel_id", "external_message_id");`);
28
+
29
+ this.addSql(`create table "message_channel_links" ("id" uuid not null default gen_random_uuid(), "message_id" uuid not null, "external_conversation_id" uuid not null, "external_message_id" uuid null, "provider_key" text not null, "channel_type" text not null, "direction" text not null, "delivery_status" text not null default 'pending', "channel_payload" jsonb null, "channel_content_type" text null, "interactive_state" jsonb null, "channel_metadata" jsonb null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, primary key ("id"));`);
30
+ this.addSql(`create index "message_channel_links_ext_msg_idx" on "message_channel_links" ("external_message_id");`);
31
+ this.addSql(`create index "message_channel_links_ext_conv_idx" on "message_channel_links" ("external_conversation_id");`);
32
+ this.addSql(`create index "message_channel_links_message_idx" on "message_channel_links" ("message_id");`);
33
+ this.addSql(`alter table "message_channel_links" add constraint "message_channel_links_message_uq" unique ("message_id");`);
34
+
35
+ this.addSql(`create table "message_reactions" ("id" uuid not null default gen_random_uuid(), "message_id" uuid not null, "emoji" text not null, "reacted_by_user_id" uuid null, "reacted_by_external_id" text null, "reacted_by_display_name" text null, "provider_key" text null, "external_reaction_id" text null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, primary key ("id"));`);
36
+ this.addSql(`alter table "message_reactions" add constraint "message_reactions_exactly_one_actor_chk" check (("reacted_by_user_id" is null) <> ("reacted_by_external_id" is null));`);
37
+ this.addSql(`create index "message_reactions_message_emoji_idx" on "message_reactions" ("message_id", "emoji");`);
38
+ this.addSql(`create index "message_reactions_message_idx" on "message_reactions" ("message_id");`);
39
+ this.addSql(`create unique index "message_reactions_external_actor_uq" on "message_reactions" ("tenant_id", "message_id", "emoji", "reacted_by_external_id") where "reacted_by_external_id" is not null;`);
40
+ this.addSql(`create unique index "message_reactions_internal_actor_uq" on "message_reactions" ("tenant_id", "message_id", "emoji", "reacted_by_user_id") where "reacted_by_user_id" is not null;`);
41
+ }
42
+
43
+ override down(): void | Promise<void> {
44
+ this.addSql(`drop table if exists "message_reactions";`);
45
+ this.addSql(`drop table if exists "message_channel_links";`);
46
+ this.addSql(`drop table if exists "external_messages";`);
47
+ this.addSql(`drop table if exists "external_conversations";`);
48
+ this.addSql(`drop index if exists "communication_channels_user_lookup_idx";`);
49
+ this.addSql(`drop index if exists "communication_channels_poll_due_idx";`);
50
+ this.addSql(`drop index if exists "communication_channels_one_primary_per_user_uq";`);
51
+ this.addSql(`drop table if exists "communication_channels";`);
52
+ this.addSql(`drop table if exists "channel_thread_mappings";`);
53
+ }
54
+
55
+ }
@@ -0,0 +1,20 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260527195446_communication_channels extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ this.addSql(`create table "channel_ingest_dead_letters" ("id" uuid not null default gen_random_uuid(), "tenant_id" uuid not null, "organization_id" uuid null, "channel_id" uuid not null, "provider_key" text not null, "external_uid" text null, "external_message_id" text null, "error_class" text not null, "error_message" text not null, "raw_body" text null, "created_at" timestamptz not null, primary key ("id"));`);
7
+ this.addSql(`create index "channel_ingest_dead_letters_created_idx" on "channel_ingest_dead_letters" ("tenant_id", "created_at");`);
8
+ this.addSql(`create index "channel_ingest_dead_letters_channel_idx" on "channel_ingest_dead_letters" ("channel_id", "tenant_id");`);
9
+
10
+ this.addSql(`create table "channel_thread_tokens" ("id" uuid not null default gen_random_uuid(), "tenant_id" uuid not null, "organization_id" uuid null, "message_thread_id" uuid not null, "token" text not null, "created_at" timestamptz not null, "last_seen_at" timestamptz null, primary key ("id"));`);
11
+ this.addSql(`create index "channel_thread_tokens_thread_idx" on "channel_thread_tokens" ("message_thread_id", "tenant_id");`);
12
+ this.addSql(`alter table "channel_thread_tokens" add constraint "channel_thread_tokens_token_uq" unique ("tenant_id", "token");`);
13
+ }
14
+
15
+ override down(): void | Promise<void> {
16
+ this.addSql(`drop table if exists "channel_ingest_dead_letters";`);
17
+ this.addSql(`drop table if exists "channel_thread_tokens";`);
18
+ }
19
+
20
+ }
@@ -0,0 +1,13 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260529231848_communication_channels extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ this.addSql(`create index "communication_channels_provider_external_idx" on "communication_channels" ("provider_key", "external_identifier") where "deleted_at" is null;`);
7
+ }
8
+
9
+ override down(): void | Promise<void> {
10
+ this.addSql(`drop index "communication_channels_provider_external_idx";`);
11
+ }
12
+
13
+ }
@@ -0,0 +1,24 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260531120000_communication_channels extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ // Enforce one thread token per (tenant, message_thread_id). Replaces the
7
+ // earlier non-unique `channel_thread_tokens_thread_idx` so `getOrCreateThreadToken`
8
+ // is idempotent under concurrency (insert-on-conflict). The new unique index
9
+ // also serves the `WHERE tenant_id = ? AND message_thread_id = ?` lookup.
10
+ this.addSql(`drop index if exists "channel_thread_tokens_thread_idx";`);
11
+ this.addSql(`alter table "channel_thread_tokens" add constraint "channel_thread_tokens_thread_uq" unique ("tenant_id", "message_thread_id");`);
12
+ // Enforce one channel per (tenant, user, provider, mailbox) so a reconnect
13
+ // heals the existing row instead of inserting a duplicate. Partial: tenant-wide
14
+ // (null user_id) and identifier-less channels are exempt.
15
+ this.addSql(`create unique index "communication_channels_user_provider_external_uq" on "communication_channels" ("tenant_id", "user_id", "provider_key", "external_identifier") where "deleted_at" is null and "user_id" is not null and "external_identifier" is not null;`);
16
+ }
17
+
18
+ override down(): void | Promise<void> {
19
+ this.addSql(`drop index if exists "communication_channels_user_provider_external_uq";`);
20
+ this.addSql(`alter table "channel_thread_tokens" drop constraint if exists "channel_thread_tokens_thread_uq";`);
21
+ this.addSql(`create index "channel_thread_tokens_thread_idx" on "channel_thread_tokens" ("message_thread_id", "tenant_id");`);
22
+ }
23
+
24
+ }
@@ -0,0 +1,50 @@
1
+ 'use client'
2
+
3
+ import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
4
+ import { MessageReceivedRenderer } from './widgets/notifications/MessageReceivedRenderer'
5
+ import { ChannelRequiresReauthRenderer } from './widgets/notifications/ChannelRequiresReauthRenderer'
6
+
7
+ export const communicationChannelsNotificationTypes: NotificationTypeDefinition[] = [
8
+ {
9
+ type: 'communication_channels.message.received',
10
+ module: 'communication_channels',
11
+ titleKey: 'communication_channels.notifications.message_received.title',
12
+ bodyKey: 'communication_channels.notifications.message_received.body',
13
+ icon: 'message-circle',
14
+ severity: 'info',
15
+ actions: [
16
+ {
17
+ id: 'view',
18
+ labelKey: 'common.view',
19
+ variant: 'outline',
20
+ href: '/backend/messages/{sourceEntityId}',
21
+ icon: 'external-link',
22
+ },
23
+ ],
24
+ linkHref: '/backend/messages/{sourceEntityId}',
25
+ Renderer: MessageReceivedRenderer,
26
+ expiresAfterHours: 168,
27
+ },
28
+ {
29
+ type: 'communication_channels.channel.requires_reauth',
30
+ module: 'communication_channels',
31
+ titleKey: 'communication_channels.notifications.channel_requires_reauth.title',
32
+ bodyKey: 'communication_channels.notifications.channel_requires_reauth.body',
33
+ icon: 'alert-triangle',
34
+ severity: 'warning',
35
+ actions: [
36
+ {
37
+ id: 'reconnect',
38
+ labelKey: 'communication_channels.notifications.channel_requires_reauth.reconnect',
39
+ variant: 'outline',
40
+ href: '/backend/profile/communication-channels?reconnect={sourceEntityId}',
41
+ icon: 'refresh-cw',
42
+ },
43
+ ],
44
+ linkHref: '/backend/profile/communication-channels?reconnect={sourceEntityId}',
45
+ Renderer: ChannelRequiresReauthRenderer,
46
+ expiresAfterHours: 720,
47
+ },
48
+ ]
49
+
50
+ export default communicationChannelsNotificationTypes
@@ -0,0 +1,86 @@
1
+ import type { NotificationHandler } from '@open-mercato/shared/modules/notifications/handler'
2
+
3
+ /**
4
+ * Reactive notification handlers for the Communications Hub (Phase 4 of the
5
+ * email integration spec).
6
+ *
7
+ * These handlers fire on the browser when the matching notification arrives via
8
+ * the notifications stream. They run in addition to (not instead of) the static
9
+ * UI surfaces:
10
+ *
11
+ * - The notification appears in the user's bell dropdown via
12
+ * `notifications.client.ts` renderers (already wired in slice 2a).
13
+ * - The dropdown's primary action navigates to the reconnect URL.
14
+ *
15
+ * What the handlers add:
16
+ * - **Toast**: surfaces the message even when the user isn't looking at the
17
+ * bell. The reauth toast is the highest-signal interruption — without
18
+ * reauth the channel goes silent.
19
+ * - **Event emit**: lets the profile page / channel admin DataTable hook in
20
+ * via `useAppEvent(...)` (DOM Event Bridge) so they auto-refresh when the
21
+ * state flips without requiring a manual reload.
22
+ * - **Refresh notifications**: keeps the bell badge accurate.
23
+ *
24
+ * Custom event names emitted here on the DOM Event Bridge. They are published
25
+ * for any page that opts in via `useAppEvent(...)`; no surface subscribes to
26
+ * them today (the unified inbox auto-refreshes from the `messages.message.*`
27
+ * bridge, and the reconnect flow is driven by the bell notification + its
28
+ * reconnect action). Kept as forward-compatible hooks for future
29
+ * row-highlight / refetch UX.
30
+ */
31
+
32
+ export const CHANNEL_REQUIRES_REAUTH_EVENT = 'om:communication_channels:channel-requires-reauth'
33
+ export const MESSAGE_RECEIVED_EVENT = 'om:communication_channels:message-received'
34
+
35
+ export const notificationHandlers: NotificationHandler[] = [
36
+ {
37
+ id: 'communication_channels.channel-requires-reauth-toast',
38
+ notificationType: 'communication_channels.channel.requires_reauth',
39
+ features: ['communication_channels.connect_user_channel'],
40
+ priority: 110,
41
+ handle(notification, context) {
42
+ context.toast({
43
+ title: notification.title,
44
+ body: notification.body ?? undefined,
45
+ severity: 'warning',
46
+ action: {
47
+ label:
48
+ context.t?.(
49
+ 'communication_channels.notifications.channel_requires_reauth.reconnect',
50
+ 'Reconnect',
51
+ ) ?? 'Reconnect',
52
+ onClick: () => {
53
+ const channelId = notification.sourceEntityId
54
+ const target = channelId
55
+ ? `/backend/profile/communication-channels?reconnect=${encodeURIComponent(channelId)}`
56
+ : '/backend/profile/communication-channels'
57
+ context.navigate(target)
58
+ },
59
+ },
60
+ })
61
+ context.emitEvent(CHANNEL_REQUIRES_REAUTH_EVENT, {
62
+ notificationId: notification.id,
63
+ channelId: notification.sourceEntityId ?? null,
64
+ })
65
+ context.refreshNotifications()
66
+ },
67
+ },
68
+ {
69
+ id: 'communication_channels.message-received-event',
70
+ notificationType: 'communication_channels.message.received',
71
+ features: ['communication_channels.view'],
72
+ priority: 100,
73
+ handle(notification, context) {
74
+ // Inbox refresh is the only side effect — the user's bell dropdown already
75
+ // shows the notification entry. A toast here would be noisy because the
76
+ // bell badge increments anyway, and inbound email lands in the unified
77
+ // inbox where the user expects it.
78
+ context.emitEvent(MESSAGE_RECEIVED_EVENT, {
79
+ notificationId: notification.id,
80
+ messageId: notification.sourceEntityId ?? null,
81
+ })
82
+ },
83
+ },
84
+ ]
85
+
86
+ export default notificationHandlers
@@ -0,0 +1,52 @@
1
+ import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+
3
+ export const notificationTypes: NotificationTypeDefinition[] = [
4
+ {
5
+ type: 'communication_channels.message.received',
6
+ module: 'communication_channels',
7
+ titleKey: 'communication_channels.notifications.message_received.title',
8
+ bodyKey: 'communication_channels.notifications.message_received.body',
9
+ icon: 'message-circle',
10
+ severity: 'info',
11
+ actions: [
12
+ {
13
+ id: 'view',
14
+ labelKey: 'common.view',
15
+ variant: 'outline',
16
+ href: '/backend/messages/{sourceEntityId}',
17
+ icon: 'external-link',
18
+ },
19
+ ],
20
+ linkHref: '/backend/messages/{sourceEntityId}',
21
+ expiresAfterHours: 168, // 7 days
22
+ },
23
+ {
24
+ /**
25
+ * Channel-agnostic notification raised when an adapter loses authorization
26
+ * (OAuth refresh token revoked, IMAP/SMTP password rotated, WhatsApp token expired).
27
+ * Persisted by the `channel-requires-reauth-notification` subscriber in
28
+ * response to the `communication_channels.channel.requires_reauth` event
29
+ * (emitted by the poll worker and outbound delivery). Consumed by the email
30
+ * integration spec's reconnect flow.
31
+ */
32
+ type: 'communication_channels.channel.requires_reauth',
33
+ module: 'communication_channels',
34
+ titleKey: 'communication_channels.notifications.channel_requires_reauth.title',
35
+ bodyKey: 'communication_channels.notifications.channel_requires_reauth.body',
36
+ icon: 'alert-triangle',
37
+ severity: 'warning',
38
+ actions: [
39
+ {
40
+ id: 'reconnect',
41
+ labelKey: 'communication_channels.notifications.channel_requires_reauth.reconnect',
42
+ variant: 'outline',
43
+ href: '/backend/profile/communication-channels?reconnect={sourceEntityId}',
44
+ icon: 'refresh-cw',
45
+ },
46
+ ],
47
+ linkHref: '/backend/profile/communication-channels?reconnect={sourceEntityId}',
48
+ expiresAfterHours: 720, // 30 days — auth issues should be addressed promptly
49
+ },
50
+ ]
51
+
52
+ export default notificationTypes
@@ -0,0 +1,158 @@
1
+ import { createHash } from 'node:crypto'
2
+ import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
3
+ import { COMMUNICATION_CHANNELS_QUEUES } from './lib/queue'
4
+
5
+ type SchedulerServiceLike = {
6
+ register: (registration: {
7
+ id: string
8
+ name: string
9
+ scopeType: 'system' | 'organization' | 'tenant'
10
+ organizationId?: string
11
+ tenantId?: string
12
+ scheduleType: 'cron' | 'interval'
13
+ scheduleValue: string
14
+ timezone?: string
15
+ targetType: 'queue' | 'command'
16
+ targetQueue?: string
17
+ targetPayload?: unknown
18
+ sourceType?: 'user' | 'module'
19
+ sourceModule?: string
20
+ isEnabled?: boolean
21
+ description?: string
22
+ }) => Promise<void>
23
+ }
24
+
25
+ /**
26
+ * Tick interval in seconds. Default 60s per email integration spec
27
+ * § Hub Deltas → Delta 6 (scheduler mechanism).
28
+ */
29
+ const POLL_TICK_INTERVAL_SECONDS = Math.max(
30
+ 10,
31
+ Number.parseInt(process.env.OM_HUB_POLL_SCHEDULER_TICK_SECONDS ?? '60', 10) || 60,
32
+ )
33
+
34
+ /**
35
+ * `scheduled_jobs.id` is a uuid column, so a module-owned schedule's stable
36
+ * registration key must be hashed into a uuid rather than used verbatim — this
37
+ * keeps `schedulerService.register()` an idempotent upsert across re-runs of
38
+ * seedDefaults instead of trying to insert a raw string into the uuid PK.
39
+ */
40
+ function stableScheduleUuid(stableKey: string): string {
41
+ const hex = createHash('sha256').update(stableKey).digest('hex')
42
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`
43
+ }
44
+
45
+ function stablePollTickScheduleId(organizationId: string): string {
46
+ return stableScheduleUuid(`communication_channels:poll-tick:${organizationId}`)
47
+ }
48
+
49
+ export const setup: ModuleSetupConfig = {
50
+ defaultRoleFeatures: {
51
+ superadmin: [
52
+ 'communication_channels.view',
53
+ 'communication_channels.manage',
54
+ 'communication_channels.react',
55
+ 'communication_channels.assign',
56
+ 'communication_channels.connect_user_channel',
57
+ 'communication_channels.admin',
58
+ 'communication_channels.channel.import_history',
59
+ 'communication_channels.channel.push.manage',
60
+ ],
61
+ admin: [
62
+ 'communication_channels.view',
63
+ 'communication_channels.manage',
64
+ 'communication_channels.react',
65
+ 'communication_channels.assign',
66
+ 'communication_channels.connect_user_channel',
67
+ 'communication_channels.admin',
68
+ 'communication_channels.channel.import_history',
69
+ 'communication_channels.channel.push.manage',
70
+ ],
71
+ manager: [
72
+ 'communication_channels.view',
73
+ 'communication_channels.manage',
74
+ 'communication_channels.react',
75
+ 'communication_channels.assign',
76
+ 'communication_channels.connect_user_channel',
77
+ ],
78
+ employee: [
79
+ 'communication_channels.view',
80
+ 'communication_channels.react',
81
+ 'communication_channels.connect_user_channel',
82
+ ],
83
+ },
84
+
85
+ async seedDefaults({ container, organizationId, tenantId }) {
86
+ /**
87
+ * Register the per-channel polling tick with `@open-mercato/scheduler`.
88
+ *
89
+ * Per email integration spec § Hub Deltas → Delta 6: every
90
+ * `POLL_TICK_INTERVAL_SECONDS` (default 60s), enqueue a single
91
+ * `communication-channels-poll-tick` job that enumerates due channels and
92
+ * fans out to the `communication-channels-poll` queue.
93
+ *
94
+ * The registration is per-organization (one tick row per org/tenant pair)
95
+ * so multi-tenant deploys schedule independently. Skipped silently when the
96
+ * scheduler module isn't enabled — keeps the hub usable in scheduler-less
97
+ * test harnesses.
98
+ */
99
+ const cradle = container as { hasRegistration?: (name: string) => boolean }
100
+ if (typeof cradle.hasRegistration !== 'function' || !cradle.hasRegistration('schedulerService')) {
101
+ return
102
+ }
103
+ const schedulerService = container.resolve('schedulerService') as SchedulerServiceLike
104
+ // Best-effort: a scheduler failure must not abort tenant initialization for
105
+ // every other module (mirrors the ai_assistant setup pattern). The schedule
106
+ // ids are deterministic uuids so re-runs upsert idempotently.
107
+ try {
108
+ await schedulerService.register({
109
+ id: stablePollTickScheduleId(organizationId),
110
+ name: 'Communication channels poll tick',
111
+ description:
112
+ `Enumerates active polling channels every ${POLL_TICK_INTERVAL_SECONDS}s and enqueues per-channel poll jobs.`,
113
+ scopeType: 'organization',
114
+ organizationId,
115
+ tenantId,
116
+ scheduleType: 'interval',
117
+ scheduleValue: `${POLL_TICK_INTERVAL_SECONDS}s`,
118
+ timezone: 'UTC',
119
+ targetType: 'queue',
120
+ targetQueue: COMMUNICATION_CHANNELS_QUEUES.pollTick,
121
+ targetPayload: {
122
+ scope: { tenantId, organizationId },
123
+ },
124
+ sourceType: 'module',
125
+ sourceModule: 'communication_channels',
126
+ isEnabled: true,
127
+ })
128
+
129
+ // Spec C § Phase C4 — Gmail watch renewal cron, per-org so multi-tenant
130
+ // deploys schedule independently.
131
+ await schedulerService.register({
132
+ id: stableScheduleUuid(`communication_channels:gmail-renew-watch:${organizationId}`),
133
+ name: 'Gmail watch renewal',
134
+ description:
135
+ 'Daily 04:00 UTC. Re-issues gmail.users.watch for channels within OM_PUSH_RENEWAL_GMAIL_LEAD_HOURS of expiry.',
136
+ scopeType: 'organization',
137
+ organizationId,
138
+ tenantId,
139
+ scheduleType: 'cron',
140
+ scheduleValue: '0 4 * * *',
141
+ timezone: 'UTC',
142
+ targetType: 'queue',
143
+ targetQueue: COMMUNICATION_CHANNELS_QUEUES.gmailRenewWatch,
144
+ targetPayload: { scope: { tenantId, organizationId } },
145
+ sourceType: 'module',
146
+ sourceModule: 'communication_channels',
147
+ isEnabled: true,
148
+ })
149
+ } catch (error) {
150
+ console.warn(
151
+ '[communication_channels] Failed to register module schedules:',
152
+ error instanceof Error ? error.message : error,
153
+ )
154
+ }
155
+ },
156
+ }
157
+
158
+ export default setup
@@ -0,0 +1,118 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
+ import { CommunicationChannel } from '../data/entities'
4
+ import { notificationTypes } from '../notifications'
5
+ import {
6
+ buildNotificationFromType,
7
+ buildFeatureNotificationFromType,
8
+ } from '../../notifications/lib/notificationBuilder'
9
+ import { resolveNotificationService } from '../../notifications/lib/notificationService'
10
+
11
+ /**
12
+ * Subscriber: re-authentication notification.
13
+ *
14
+ * Listens to `communication_channels.channel.requires_reauth` (emitted by the
15
+ * poll worker and outbound delivery when an
16
+ * adapter loses authorization) and raises the in-app
17
+ * `communication_channels.channel.requires_reauth` notification so the affected
18
+ * user sees a bell entry + reconnect CTA and the reactive toast handler
19
+ * (`notifications.handlers.ts`) fires. This is the producer half of the
20
+ * notification contract declared in `notifications.ts` /
21
+ * `notifications.client.ts` — without it the notification is never persisted.
22
+ *
23
+ * Recipient: per-user channels notify the channel owner (`channel.userId`) — the
24
+ * only person who can complete their own OAuth/credential reconnect. Tenant-wide
25
+ * (shared) channels have no owner, so operators holding
26
+ * `communication_channels.manage` are notified instead.
27
+ *
28
+ * Idempotency: `groupKey = channelId` collapses repeated reauth events for the
29
+ * same channel onto one notification (the notification service dedupes by
30
+ * tenant/org/recipient/type/groupKey under an advisory lock), so a flapping
31
+ * channel or a subscriber retry never spams the bell.
32
+ */
33
+ export const metadata = {
34
+ event: 'communication_channels.channel.requires_reauth',
35
+ persistent: true,
36
+ id: 'communication_channels:channel-requires-reauth-notification',
37
+ }
38
+
39
+ const NOTIFICATION_TYPE = 'communication_channels.channel.requires_reauth'
40
+ const MANAGE_FEATURE = 'communication_channels.manage'
41
+
42
+ type RequiresReauthPayload = {
43
+ channelId?: string
44
+ providerKey?: string
45
+ channelType?: string
46
+ reason?: string
47
+ tenantId?: string
48
+ organizationId?: string | null
49
+ }
50
+
51
+ type SubscriberContext = {
52
+ resolve: <T = unknown>(name: string) => T
53
+ container?: { resolve: <T = unknown>(name: string) => T }
54
+ }
55
+
56
+ function resolveFromCtx<T = unknown>(ctx: SubscriberContext, name: string): T {
57
+ if (typeof ctx?.resolve === 'function') return ctx.resolve<T>(name)
58
+ if (ctx?.container && typeof ctx.container.resolve === 'function') {
59
+ return ctx.container.resolve<T>(name)
60
+ }
61
+ throw new Error(
62
+ `channel-requires-reauth-notification: subscriber context has no resolver (looking for '${name}')`,
63
+ )
64
+ }
65
+
66
+ export default async function handler(
67
+ payload: RequiresReauthPayload,
68
+ ctx: SubscriberContext,
69
+ ): Promise<void> {
70
+ if (!payload?.channelId || !payload.tenantId) {
71
+ return
72
+ }
73
+
74
+ const typeDef = notificationTypes.find((type) => type.type === NOTIFICATION_TYPE)
75
+ if (!typeDef) return
76
+
77
+ const scope = {
78
+ tenantId: payload.tenantId,
79
+ organizationId: payload.organizationId ?? null,
80
+ }
81
+
82
+ const em = resolveFromCtx<EntityManager>(ctx, 'em').fork()
83
+ const channel = await findOneWithDecryption(
84
+ em,
85
+ CommunicationChannel,
86
+ {
87
+ id: payload.channelId,
88
+ tenantId: payload.tenantId,
89
+ organizationId: payload.organizationId ?? null,
90
+ deletedAt: null,
91
+ },
92
+ undefined,
93
+ scope,
94
+ )
95
+ if (!channel) return
96
+
97
+ const notificationService = resolveNotificationService(ctx)
98
+ const common = {
99
+ sourceEntityType: 'communication_channel',
100
+ sourceEntityId: payload.channelId,
101
+ groupKey: payload.channelId,
102
+ }
103
+
104
+ if (channel.userId) {
105
+ await notificationService.create(
106
+ buildNotificationFromType(typeDef, { ...common, recipientUserId: channel.userId }),
107
+ scope,
108
+ )
109
+ return
110
+ }
111
+
112
+ // Tenant-wide (shared) channel — no single owner. Notify operators who can
113
+ // reconnect it on behalf of the tenant.
114
+ await notificationService.createForFeature(
115
+ buildFeatureNotificationFromType(typeDef, { ...common, requiredFeature: MANAGE_FEATURE }),
116
+ scope,
117
+ )
118
+ }