@open-mercato/core 0.6.5-develop.4384.1.ce2ec6eaaa → 0.6.5-develop.4393.1.de282b5dfd

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 +259 -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 +425 -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,104 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import type {
3
+ CommandInterceptor,
4
+ CommandInterceptorBeforeResult,
5
+ CommandInterceptorUndoContext,
6
+ } from '@open-mercato/shared/lib/commands/command-interceptor'
7
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
8
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
9
+ import { CommunicationChannel } from '../data/entities'
10
+ import {
11
+ COMMUNICATION_CHANNELS_DISCONNECT_CHANNEL_COMMAND_ID,
12
+ extractSnapshotFromLog,
13
+ type DisconnectChannelUndoSnapshot,
14
+ } from './disconnect-channel'
15
+
16
+ /**
17
+ * Command interceptors for the Communications Hub.
18
+ *
19
+ * Phase 4 deliverable 5 of the email integration spec:
20
+ * - `beforeUndo` interceptor on `communication_channels.channel.disconnect`.
21
+ * If a different channel became primary while this one was disconnected,
22
+ * blocking the undo is the right call — restoring `is_primary=true` would
23
+ * violate the partial unique index `communication_channels_one_primary_per_user_uq`,
24
+ * and silently demoting the *other* channel would surprise the user.
25
+ *
26
+ * Auto-discovered by `commands/interceptors.ts` convention; no DI registration
27
+ * required.
28
+ */
29
+ export const interceptors: CommandInterceptor[] = [
30
+ {
31
+ id: 'communication_channels.disconnect-channel-before-undo',
32
+ targetCommand: COMMUNICATION_CHANNELS_DISCONNECT_CHANNEL_COMMAND_ID,
33
+ priority: 50,
34
+ async beforeUndo(
35
+ undoContext: CommandInterceptorUndoContext,
36
+ ctxRuntime,
37
+ ): Promise<CommandInterceptorBeforeResult | void> {
38
+ // Read the snapshot exactly as the command's own undo() handler does — from
39
+ // the persisted action-log `commandPayload.undo` (written by buildLog). The
40
+ // earlier `result.undo`/`resultJson`/`resultBody` shapes do not exist on the
41
+ // ActionLog entity, so this guard previously never fired.
42
+ const snapshot: DisconnectChannelUndoSnapshot | null = extractSnapshotFromLog(
43
+ undoContext.logEntry,
44
+ )
45
+ if (!snapshot) {
46
+ // No snapshot we can interpret → let undo proceed; the command's own
47
+ // undo() handler will no-op if the channel can't be re-resolved.
48
+ return
49
+ }
50
+ // Only enforce when the disconnected channel was the primary.
51
+ if (!snapshot.previousIsPrimary) {
52
+ return
53
+ }
54
+
55
+ const em = (ctxRuntime.container.resolve('em') as EntityManager).fork()
56
+ // Resolve only within the snapshot's tenant — never by bare id, which
57
+ // would cross tenant boundaries. New snapshots always carry tenantId.
58
+ if (!snapshot.tenantId) return
59
+ const ownedChannel = await findOneWithDecryption(
60
+ em,
61
+ CommunicationChannel,
62
+ { id: snapshot.channelId, tenantId: snapshot.tenantId },
63
+ undefined,
64
+ { tenantId: snapshot.tenantId as string, organizationId: null },
65
+ )
66
+ if (!ownedChannel) return // command's undo() will silently no-op
67
+
68
+ const otherPrimary = await findOneWithDecryption(
69
+ em,
70
+ CommunicationChannel,
71
+ {
72
+ tenantId: ownedChannel.tenantId,
73
+ organizationId: ownedChannel.organizationId ?? null,
74
+ userId: ownedChannel.userId,
75
+ isPrimary: true,
76
+ deletedAt: null,
77
+ },
78
+ undefined,
79
+ {
80
+ tenantId: ownedChannel.tenantId,
81
+ organizationId: ownedChannel.organizationId ?? null,
82
+ },
83
+ )
84
+ if (otherPrimary && otherPrimary.id !== snapshot.channelId) {
85
+ // Operator-facing block reason. Localize via the request locale when
86
+ // available, but undo can also run outside a request (queue worker / CLI)
87
+ // where the i18n module registry is uninitialized and resolveTranslations()
88
+ // throws — fall back to the English string rather than failing the block.
89
+ const fallback =
90
+ 'Another channel is now primary for this user. Set it as non-primary before restoring the disconnected channel as primary.'
91
+ let message = fallback
92
+ try {
93
+ const { translate } = await resolveTranslations()
94
+ message = translate('communication_channels.errors.undoBlockedPrimaryConflict', fallback)
95
+ } catch {
96
+ message = fallback
97
+ }
98
+ return { ok: false, message }
99
+ }
100
+ },
101
+ },
102
+ ]
103
+
104
+ export default interceptors
@@ -0,0 +1,265 @@
1
+ import { z } from 'zod'
2
+ import type { EntityManager } from '@mikro-orm/postgresql'
3
+ import type { CommandHandler } from '@open-mercato/shared/lib/commands'
4
+ import { registerCommand } from '@open-mercato/shared/lib/commands'
5
+ import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
+ import { emitCommunicationChannelsEvent } from '../events'
7
+ import {
8
+ CommunicationChannel,
9
+ ExternalMessage,
10
+ MessageChannelLink,
11
+ MessageReaction,
12
+ } from '../data/entities'
13
+ import { inboundReactionEventSchema } from '../data/validators'
14
+ import {
15
+ allowsMultipleReactionsPerUser,
16
+ resolveInboundAddMutation,
17
+ } from '../lib/reaction-semantics'
18
+ import type { ChannelCapabilities } from '../lib/adapter'
19
+ import { isUniqueViolation } from '../lib/pg-errors'
20
+
21
+ const processInboundReactionInputSchema = z.object({
22
+ channelId: z.string().uuid(),
23
+ providerKey: z.string().min(1),
24
+ channelType: z.string().min(1),
25
+ scope: z.object({
26
+ tenantId: z.string().uuid(),
27
+ organizationId: z.string().uuid().nullable(),
28
+ }),
29
+ event: inboundReactionEventSchema,
30
+ })
31
+
32
+ export type ProcessInboundReactionInput = z.infer<typeof processInboundReactionInputSchema>
33
+
34
+ export type ProcessInboundReactionResult =
35
+ | { status: 'no_message_link' }
36
+ | {
37
+ status: 'added'
38
+ reactionId: string
39
+ messageId: string
40
+ emoji: string
41
+ replaced: number
42
+ }
43
+ | {
44
+ status: 'removed'
45
+ messageId: string
46
+ emoji: string
47
+ deleted: number
48
+ }
49
+ | { status: 'noop' }
50
+
51
+ export const COMMUNICATION_CHANNELS_PROCESS_INBOUND_REACTION_COMMAND_ID =
52
+ 'communication_channels.reaction.process_inbound'
53
+
54
+ /**
55
+ * Inbound reaction processor command.
56
+ *
57
+ * Per SPEC-045d §5.2:
58
+ * 1. Look up `MessageChannelLink` by `(channelId, externalMessageId)` via the
59
+ * `ExternalMessage` row (which has the unique constraint on that pair).
60
+ * 2. For `added`: apply per-provider semantics
61
+ * - `multiReactionPerUser=true` (Slack): insert; existing reactions from the
62
+ * same reactor are preserved.
63
+ * - `multiReactionPerUser=false` (WhatsApp): delete all existing reactions
64
+ * from the same reactor for the same message, then insert the new one.
65
+ * 3. For `removed`: delete matching `MessageReaction` rows for
66
+ * `(messageId, emoji, reactedByExternalId)`. If `externalReactionId` is
67
+ * provided, prefer that as the lookup key.
68
+ * 4. Emit `communication_channels.reaction.added` or `.removed`.
69
+ *
70
+ * The command is idempotent: re-running it for the same `externalReactionId` will
71
+ * be a no-op for `added` (unique constraint blocks duplicates) and harmless for
72
+ * `removed` (delete-where-not-found returns 0).
73
+ */
74
+ const processInboundReactionCommand: CommandHandler<
75
+ ProcessInboundReactionInput,
76
+ ProcessInboundReactionResult
77
+ > = {
78
+ id: COMMUNICATION_CHANNELS_PROCESS_INBOUND_REACTION_COMMAND_ID,
79
+ async execute(rawInput, ctx) {
80
+ const input = processInboundReactionInputSchema.parse(rawInput) as ProcessInboundReactionInput
81
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
82
+ const dscope = {
83
+ tenantId: input.scope.tenantId,
84
+ organizationId: input.scope.organizationId ?? null,
85
+ }
86
+
87
+ // (1) Find the platform Message via ExternalMessage → MessageChannelLink.
88
+ const externalMessage = await findOneWithDecryption(
89
+ em,
90
+ ExternalMessage,
91
+ {
92
+ channelId: input.channelId,
93
+ externalMessageId: input.event.externalMessageId,
94
+ tenantId: input.scope.tenantId,
95
+ organizationId: input.scope.organizationId ?? null,
96
+ },
97
+ undefined,
98
+ dscope,
99
+ )
100
+ if (!externalMessage) {
101
+ // The reaction targets a message we never ingested (e.g., reaction to a
102
+ // history-only message we skipped). Skip silently.
103
+ return { status: 'no_message_link' }
104
+ }
105
+
106
+ const channelLink = await findOneWithDecryption(
107
+ em,
108
+ MessageChannelLink,
109
+ {
110
+ externalMessageId: externalMessage.id,
111
+ tenantId: input.scope.tenantId,
112
+ organizationId: input.scope.organizationId ?? null,
113
+ },
114
+ undefined,
115
+ dscope,
116
+ )
117
+ if (!channelLink) {
118
+ return { status: 'no_message_link' }
119
+ }
120
+
121
+ const messageId = channelLink.messageId
122
+
123
+ // (2) Resolve channel capabilities for semantics decision.
124
+ const channel = await findOneWithDecryption(
125
+ em,
126
+ CommunicationChannel,
127
+ {
128
+ id: input.channelId,
129
+ tenantId: input.scope.tenantId,
130
+ organizationId: input.scope.organizationId ?? null,
131
+ deletedAt: null,
132
+ },
133
+ undefined,
134
+ dscope,
135
+ )
136
+ const capabilities = (channel?.capabilities as ChannelCapabilities | null) ?? null
137
+
138
+ if (input.event.action === 'added') {
139
+ const mutation = resolveInboundAddMutation(capabilities)
140
+ let replaced = 0
141
+
142
+ if (mutation === 'replace') {
143
+ // WhatsApp-style: delete prior reactions from the same reactor.
144
+ const existing = await findWithDecryption(
145
+ em,
146
+ MessageReaction,
147
+ {
148
+ messageId,
149
+ reactedByExternalId: input.event.userIdentifier,
150
+ tenantId: input.scope.tenantId,
151
+ organizationId: input.scope.organizationId ?? null,
152
+ },
153
+ undefined,
154
+ dscope,
155
+ )
156
+ replaced = existing.length
157
+ for (const row of existing) em.remove(row)
158
+ await em.flush()
159
+ }
160
+
161
+ // Insert the new reaction. On a partial actor unique-index conflict,
162
+ // treat as no-op — provider sent the same reaction twice.
163
+ let reaction: MessageReaction
164
+ try {
165
+ reaction = em.create(MessageReaction, {
166
+ messageId,
167
+ emoji: input.event.emoji,
168
+ reactedByUserId: null,
169
+ reactedByExternalId: input.event.userIdentifier,
170
+ reactedByDisplayName: input.event.userDisplayName ?? null,
171
+ providerKey: input.providerKey,
172
+ externalReactionId: input.event.externalReactionId ?? null,
173
+ tenantId: input.scope.tenantId,
174
+ organizationId: input.scope.organizationId ?? null,
175
+ })
176
+ em.persist(reaction)
177
+ await em.flush()
178
+ } catch (err) {
179
+ // Unique constraint violation → idempotent skip.
180
+ if (isUniqueViolation(err)) {
181
+ return { status: 'noop' }
182
+ }
183
+ throw err
184
+ }
185
+
186
+ await emitCommunicationChannelsEvent(
187
+ 'communication_channels.reaction.added',
188
+ {
189
+ reactionId: reaction.id,
190
+ messageId,
191
+ channelLinkId: channelLink.id,
192
+ channelId: input.channelId,
193
+ providerKey: input.providerKey,
194
+ channelType: input.channelType,
195
+ emoji: input.event.emoji,
196
+ reactedByExternalId: input.event.userIdentifier,
197
+ reactedByDisplayName: input.event.userDisplayName ?? null,
198
+ allowsMultiplePerUser: allowsMultipleReactionsPerUser(capabilities),
199
+ tenantId: input.scope.tenantId,
200
+ organizationId: input.scope.organizationId ?? null,
201
+ },
202
+ { persistent: true },
203
+ )
204
+
205
+ return {
206
+ status: 'added',
207
+ reactionId: reaction.id,
208
+ messageId,
209
+ emoji: input.event.emoji,
210
+ replaced,
211
+ }
212
+ }
213
+
214
+ // input.event.action === 'removed'
215
+ const filter: Record<string, unknown> = {
216
+ messageId,
217
+ reactedByExternalId: input.event.userIdentifier,
218
+ emoji: input.event.emoji,
219
+ tenantId: input.scope.tenantId,
220
+ organizationId: input.scope.organizationId ?? null,
221
+ }
222
+ if (input.event.externalReactionId) {
223
+ filter.externalReactionId = input.event.externalReactionId
224
+ }
225
+ const toDelete = await findWithDecryption(
226
+ em,
227
+ MessageReaction,
228
+ filter,
229
+ undefined,
230
+ dscope,
231
+ )
232
+ for (const row of toDelete) em.remove(row)
233
+ await em.flush()
234
+
235
+ if (toDelete.length > 0) {
236
+ await emitCommunicationChannelsEvent(
237
+ 'communication_channels.reaction.removed',
238
+ {
239
+ messageId,
240
+ channelLinkId: channelLink.id,
241
+ channelId: input.channelId,
242
+ providerKey: input.providerKey,
243
+ channelType: input.channelType,
244
+ emoji: input.event.emoji,
245
+ reactedByExternalId: input.event.userIdentifier,
246
+ deletedCount: toDelete.length,
247
+ tenantId: input.scope.tenantId,
248
+ organizationId: input.scope.organizationId ?? null,
249
+ },
250
+ { persistent: true },
251
+ )
252
+ }
253
+
254
+ return {
255
+ status: 'removed',
256
+ messageId,
257
+ emoji: input.event.emoji,
258
+ deleted: toDelete.length,
259
+ }
260
+ },
261
+ }
262
+
263
+ registerCommand(processInboundReactionCommand)
264
+
265
+ export default processInboundReactionCommand
@@ -0,0 +1,203 @@
1
+ import { z } from 'zod'
2
+ import type { AwilixContainer } from 'awilix'
3
+ import type { EntityManager } from '@mikro-orm/postgresql'
4
+ import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
5
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
+ import { CommunicationChannel } from '../data/entities'
7
+ import { getChannelAdapterRegistry } from '../lib/adapter-registry-singleton'
8
+ import { refreshCredentialsIfNeeded } from '../lib/credential-refresh'
9
+ import { emitCommunicationChannelsEvent } from '../events'
10
+ import type { PushRegistration } from '../lib/adapter'
11
+
12
+ /**
13
+ * Spec C § Phase C5 — Register provider push delivery for a channel.
14
+ *
15
+ * Resolves credentials + tenant OAuth client config, then calls
16
+ * `adapter.registerPush(...)`. Applies the returned state patch to
17
+ * `channel.channelState` and flips `pollIntervalSeconds` to the
18
+ * adapter's recommendation (or leaves it as-is on failure so polling
19
+ * fallback continues).
20
+ *
21
+ * Idempotent: calling on a channel that already has `pushStatus='active'`
22
+ * is OK — the adapter re-issues the underlying registration and we update
23
+ * the cursor / expiry to the new value.
24
+ */
25
+
26
+ export const pushRegisterSchema = z.object({
27
+ channelId: z.string().uuid(),
28
+ })
29
+
30
+ export interface PushRegisterScope {
31
+ tenantId: string
32
+ organizationId: string
33
+ userId?: string | null
34
+ }
35
+
36
+ export interface PushRegisterResult {
37
+ channelId: string
38
+ pushStatus: 'active' | 'failed'
39
+ channelState: Record<string, unknown>
40
+ error?: { code: string; message: string }
41
+ }
42
+
43
+ type CredentialsServiceLike = {
44
+ resolve: (
45
+ integrationId: string,
46
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
47
+ ) => Promise<Record<string, unknown> | null>
48
+ save?: (
49
+ integrationId: string,
50
+ credentials: Record<string, unknown>,
51
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
52
+ ) => Promise<void>
53
+ }
54
+
55
+ export async function pushRegister(params: {
56
+ container: AwilixContainer
57
+ scope: PushRegisterScope
58
+ input: { channelId: string }
59
+ }): Promise<PushRegisterResult> {
60
+ const input = pushRegisterSchema.parse(params.input)
61
+ const { container, scope } = params
62
+
63
+ const em = (container.resolve('em') as EntityManager).fork()
64
+ const dscope = { tenantId: scope.tenantId, organizationId: scope.organizationId }
65
+
66
+ const channel = await findOneWithDecryption(
67
+ em,
68
+ CommunicationChannel,
69
+ {
70
+ id: input.channelId,
71
+ tenantId: scope.tenantId,
72
+ organizationId: scope.organizationId,
73
+ deletedAt: null,
74
+ },
75
+ undefined,
76
+ dscope,
77
+ )
78
+ if (!channel) {
79
+ throw createCrudFormError(
80
+ 'Channel not found',
81
+ { channelId: 'Channel not found' },
82
+ { status: 404 },
83
+ )
84
+ }
85
+ if (!channel.isActive || channel.status !== 'connected') {
86
+ throw createCrudFormError(
87
+ 'Channel must be connected to register push',
88
+ { channelId: `Channel status is ${channel.status}` },
89
+ { status: 400 },
90
+ )
91
+ }
92
+
93
+ const adapter = getChannelAdapterRegistry().get(channel.providerKey)
94
+ if (!adapter) {
95
+ throw createCrudFormError(
96
+ 'Channel provider is not available',
97
+ { channelId: `No adapter for provider "${channel.providerKey}"` },
98
+ { status: 400 },
99
+ )
100
+ }
101
+ if (typeof adapter.registerPush !== 'function') {
102
+ throw createCrudFormError(
103
+ 'Push delivery is not supported on this provider',
104
+ { channelId: `Provider "${channel.providerKey}" does not implement registerPush` },
105
+ { status: 409 },
106
+ )
107
+ }
108
+
109
+ // Credentials.
110
+ let credentialsService: CredentialsServiceLike | null = null
111
+ try {
112
+ credentialsService = container.resolve('integrationCredentialsService') as CredentialsServiceLike
113
+ } catch {
114
+ credentialsService = null
115
+ }
116
+ const credentialsScope = {
117
+ tenantId: scope.tenantId,
118
+ organizationId: scope.organizationId,
119
+ userId: channel.userId ?? null,
120
+ }
121
+ let credentials: Record<string, unknown> = {}
122
+ if (channel.credentialsRef && credentialsService) {
123
+ try {
124
+ credentials =
125
+ (await credentialsService.resolve(`channel_${channel.providerKey}`, credentialsScope)) ?? {}
126
+ } catch {
127
+ credentials = {}
128
+ }
129
+ }
130
+ const refreshed = await refreshCredentialsIfNeeded(
131
+ { adapter, channelId: channel.id, credentials, scope: credentialsScope },
132
+ { credentialsService },
133
+ )
134
+ credentials = refreshed.credentials
135
+
136
+ // Per-provider provider config.
137
+ let providerConfig: Record<string, unknown> | undefined
138
+ let notificationUrl = ''
139
+
140
+ if (channel.providerKey === 'gmail') {
141
+ const topic = process.env.OM_GMAIL_PUBSUB_TOPIC
142
+ providerConfig = { pubsubTopic: topic ?? '' }
143
+ notificationUrl = '' // Gmail does not use a per-call URL; Pub/Sub subscription is preconfigured.
144
+ }
145
+
146
+ let registration: PushRegistration
147
+ try {
148
+ registration = await adapter.registerPush({
149
+ channelId: channel.id,
150
+ credentials,
151
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId },
152
+ notificationUrl,
153
+ providerConfig,
154
+ })
155
+ } catch (err) {
156
+ const message = err instanceof Error ? err.message : 'registerPush threw'
157
+ throw createCrudFormError(
158
+ 'Push registration failed',
159
+ { channelId: message.slice(0, 500) },
160
+ { status: 502 },
161
+ )
162
+ }
163
+
164
+ // Persist results.
165
+ const previousState = (channel.channelState as Record<string, unknown> | null) ?? {}
166
+ channel.channelState = { ...previousState, ...registration.channelStatePatch }
167
+ if (registration.status === 'active' && typeof registration.recommendedPollIntervalSeconds === 'number') {
168
+ channel.pollIntervalSeconds = registration.recommendedPollIntervalSeconds
169
+ }
170
+ await em.flush()
171
+
172
+ if (registration.status === 'active') {
173
+ await emitCommunicationChannelsEvent(
174
+ 'communication_channels.push.registered',
175
+ {
176
+ channelId: channel.id,
177
+ providerKey: channel.providerKey,
178
+ tenantId: scope.tenantId,
179
+ organizationId: scope.organizationId,
180
+ },
181
+ { persistent: true },
182
+ )
183
+ } else {
184
+ await emitCommunicationChannelsEvent(
185
+ 'communication_channels.push.failed',
186
+ {
187
+ channelId: channel.id,
188
+ providerKey: channel.providerKey,
189
+ tenantId: scope.tenantId,
190
+ organizationId: scope.organizationId,
191
+ error: registration.error ?? null,
192
+ },
193
+ { persistent: true },
194
+ )
195
+ }
196
+
197
+ return {
198
+ channelId: channel.id,
199
+ pushStatus: registration.status,
200
+ channelState: (channel.channelState as Record<string, unknown> | null) ?? {},
201
+ error: registration.error,
202
+ }
203
+ }
@@ -0,0 +1,49 @@
1
+ import { z } from 'zod'
2
+ import type { AwilixContainer } from 'awilix'
3
+ import { pushRegister } from './push-register'
4
+
5
+ /**
6
+ * Spec C § Phase C4 — Renew a push registration.
7
+ *
8
+ * The renewal action is to call the same `registerPush` adapter method again —
9
+ * Gmail's `users.watch` is idempotent (it returns a fresh `expiration` and
10
+ * `historyId`).
11
+ *
12
+ * Callers: the daily renewal cron worker (`gmail-renew-watch.ts`). The
13
+ * operator-facing `POST /push/register` route invokes `pushRegister` directly
14
+ * without needing this helper.
15
+ */
16
+
17
+ export const pushRenewSchema = z.object({
18
+ channelId: z.string().uuid(),
19
+ })
20
+
21
+ export interface PushRenewScope {
22
+ tenantId: string
23
+ organizationId: string
24
+ userId?: string | null
25
+ }
26
+
27
+ export interface PushRenewResult {
28
+ channelId: string
29
+ pushStatus: 'active' | 'failed'
30
+ error?: { code: string; message: string }
31
+ }
32
+
33
+ export async function pushRenew(params: {
34
+ container: AwilixContainer
35
+ scope: PushRenewScope
36
+ input: { channelId: string }
37
+ }): Promise<PushRenewResult> {
38
+ const input = pushRenewSchema.parse(params.input)
39
+ const result = await pushRegister({
40
+ container: params.container,
41
+ scope: params.scope,
42
+ input: { channelId: input.channelId },
43
+ })
44
+ return {
45
+ channelId: result.channelId,
46
+ pushStatus: result.pushStatus,
47
+ error: result.error,
48
+ }
49
+ }