@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,168 @@
1
+ import { z } from 'zod'
2
+ import type { AwilixContainer } from 'awilix'
3
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
+ import type { EntityManager } from '@mikro-orm/postgresql'
5
+ import { CommunicationChannel } from '../data/entities'
6
+ import { getChannelAdapterRegistry } from '../lib/adapter-registry-singleton'
7
+ import { refreshCredentialsIfNeeded } from '../lib/credential-refresh'
8
+ import { POLLING_ONLY_DEFAULT_INTERVAL_SECONDS } from '../lib/connect-channel'
9
+ import { emitCommunicationChannelsEvent } from '../events'
10
+
11
+ /**
12
+ * Spec C § Phase C5 — Tear down a previously-registered push delivery.
13
+ *
14
+ * Called from `disconnect-channel.ts` (per-user disconnect path) so the
15
+ * provider-side `users.watch` subscription doesn't keep firing
16
+ * notifications at a webhook that no longer recognises the channel.
17
+ *
18
+ * Idempotent + best-effort: a missing registration, a 404 from the provider,
19
+ * or any adapter error is logged but never raised. The caller still proceeds
20
+ * with the disconnect (the channel row is cleared regardless of whether the
21
+ * provider-side teardown succeeded).
22
+ *
23
+ * Companion to `commands/push-register.ts` — same lib-style helper shape,
24
+ * not a `registerCommand`-style command (push lifecycle has no undo).
25
+ */
26
+
27
+ export const pushUnregisterSchema = z.object({
28
+ channelId: z.string().uuid(),
29
+ })
30
+
31
+ export interface PushUnregisterScope {
32
+ tenantId: string
33
+ organizationId: string
34
+ userId?: string | null
35
+ }
36
+
37
+ export interface PushUnregisterResult {
38
+ channelId: string
39
+ status: 'unregistered' | 'noop' | 'failed'
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 pushUnregister(params: {
56
+ container: AwilixContainer
57
+ scope: PushUnregisterScope
58
+ input: { channelId: string }
59
+ }): Promise<PushUnregisterResult> {
60
+ const input = pushUnregisterSchema.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
+ },
74
+ undefined,
75
+ dscope,
76
+ )
77
+ if (!channel) {
78
+ return { channelId: input.channelId, status: 'noop' }
79
+ }
80
+
81
+ const adapter = getChannelAdapterRegistry().get(channel.providerKey)
82
+ if (!adapter || typeof adapter.unregisterPush !== 'function') {
83
+ return { channelId: channel.id, status: 'noop' }
84
+ }
85
+
86
+ const channelState = (channel.channelState as Record<string, unknown> | null) ?? {}
87
+ const pushStatus = typeof channelState.pushStatus === 'string' ? channelState.pushStatus : null
88
+ if (!pushStatus || pushStatus === 'inactive') {
89
+ return { channelId: channel.id, status: 'noop' }
90
+ }
91
+
92
+ let credentialsService: CredentialsServiceLike | null = null
93
+ try {
94
+ credentialsService = container.resolve('integrationCredentialsService') as CredentialsServiceLike
95
+ } catch {
96
+ credentialsService = null
97
+ }
98
+ const credentialsScope = {
99
+ tenantId: scope.tenantId,
100
+ organizationId: scope.organizationId,
101
+ userId: channel.userId ?? null,
102
+ }
103
+ let credentials: Record<string, unknown> = {}
104
+ if (channel.credentialsRef && credentialsService) {
105
+ try {
106
+ credentials =
107
+ (await credentialsService.resolve(`channel_${channel.providerKey}`, credentialsScope)) ?? {}
108
+ } catch {
109
+ credentials = {}
110
+ }
111
+ }
112
+ try {
113
+ const refreshed = await refreshCredentialsIfNeeded(
114
+ { adapter, channelId: channel.id, credentials, scope: credentialsScope },
115
+ { credentialsService },
116
+ )
117
+ credentials = refreshed.credentials
118
+ } catch {
119
+ // Refresh failure is non-fatal — unregister tolerates expired tokens
120
+ // because the provider will simply return 401, which the adapter swallows
121
+ // or we ignore below.
122
+ }
123
+
124
+ try {
125
+ await adapter.unregisterPush({
126
+ channelId: channel.id,
127
+ credentials,
128
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId },
129
+ channelState,
130
+ })
131
+ } catch (err) {
132
+ const message = err instanceof Error ? err.message : String(err)
133
+ console.warn(
134
+ `[push-unregister] adapter.unregisterPush failed for channel ${channel.id}: ${message}`,
135
+ )
136
+ return {
137
+ channelId: channel.id,
138
+ status: 'failed',
139
+ error: { code: 'adapter_unregister_failed', message: message.slice(0, 500) },
140
+ }
141
+ }
142
+
143
+ // Clear push markers from channel state so a subsequent reconnect starts
144
+ // fresh.
145
+ channel.channelState = {
146
+ ...channelState,
147
+ pushStatus: 'inactive',
148
+ watchExpirationMs: null,
149
+ }
150
+ // Restore the polling-only default cadence (matching connect) so the channel
151
+ // keeps working until disconnect itself flips `isActive: false`.
152
+ channel.pollIntervalSeconds = POLLING_ONLY_DEFAULT_INTERVAL_SECONDS
153
+ await em.flush()
154
+
155
+ await emitCommunicationChannelsEvent(
156
+ 'communication_channels.push.deactivated',
157
+ {
158
+ channelId: channel.id,
159
+ providerKey: channel.providerKey,
160
+ tenantId: scope.tenantId,
161
+ organizationId: scope.organizationId,
162
+ reason: 'unregistered',
163
+ },
164
+ { persistent: true },
165
+ )
166
+
167
+ return { channelId: channel.id, status: 'unregistered' }
168
+ }
@@ -0,0 +1,180 @@
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 type {
7
+ ProgressService,
8
+ ProgressServiceContext,
9
+ } from '../../progress/lib/progressService'
10
+ import { CommunicationChannel } from '../data/entities'
11
+ import { getChannelAdapterRegistry } from '../lib/adapter-registry-singleton'
12
+ import { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from '../lib/queue'
13
+
14
+ /**
15
+ * Spec B § Phase B6 — operator-triggered backlog import.
16
+ *
17
+ * Validates the channel + adapter capability, creates a `ProgressJob`, then
18
+ * enqueues a `channel-import-history` job. Returns the job id so the UI can
19
+ * track progress via the existing ProgressTopBar.
20
+ *
21
+ * Concurrency guard: at most one in-flight import per channel — a second call
22
+ * while the first is still running returns a 429 envelope via
23
+ * `createCrudFormError`. The worker itself runs with `concurrency: 1` so a
24
+ * dropped guard is still safe.
25
+ */
26
+
27
+ export const queueImportHistorySchema = z.object({
28
+ channelId: z.string().uuid(),
29
+ sinceDays: z.number().int().min(1).max(365).default(30),
30
+ contactEmails: z
31
+ .array(z.string().email().max(255))
32
+ .max(200)
33
+ .optional(),
34
+ maxMessages: z.number().int().min(1).max(5000).default(1000),
35
+ })
36
+
37
+ export type QueueImportHistoryInput = z.infer<typeof queueImportHistorySchema>
38
+
39
+ export interface QueueImportHistoryScope {
40
+ tenantId: string
41
+ organizationId: string
42
+ userId?: string | null
43
+ }
44
+
45
+ export interface QueueImportHistoryResult {
46
+ progressJobId: string
47
+ totalCountHint: number
48
+ }
49
+
50
+ export const CHANNEL_IMPORT_HISTORY_JOB_TYPE = 'communication_channels.channel.import_history'
51
+
52
+ export interface ChannelImportHistoryJobPayload {
53
+ progressJobId: string
54
+ channelId: string
55
+ sinceDays: number
56
+ contactEmails?: string[]
57
+ maxMessages: number
58
+ scope: { tenantId: string; organizationId: string }
59
+ }
60
+
61
+ export async function queueImportHistory(params: {
62
+ container: AwilixContainer
63
+ scope: QueueImportHistoryScope
64
+ input: QueueImportHistoryInput
65
+ }): Promise<QueueImportHistoryResult> {
66
+ const input = queueImportHistorySchema.parse(params.input)
67
+ const { container, scope } = params
68
+
69
+ const em = (container.resolve('em') as EntityManager).fork()
70
+ const dscope = { tenantId: scope.tenantId, organizationId: scope.organizationId }
71
+
72
+ const channel = await findOneWithDecryption(
73
+ em,
74
+ CommunicationChannel,
75
+ {
76
+ id: input.channelId,
77
+ tenantId: scope.tenantId,
78
+ organizationId: scope.organizationId,
79
+ deletedAt: null,
80
+ },
81
+ undefined,
82
+ dscope,
83
+ )
84
+ if (!channel) {
85
+ throw createCrudFormError(
86
+ 'Channel not found',
87
+ { channelId: 'Channel not found' },
88
+ { status: 404 },
89
+ )
90
+ }
91
+ if (!channel.isActive || channel.status !== 'connected') {
92
+ // 409 (not 400): the request is well-formed but the channel is in a
93
+ // conflicting state (requires_reauth / error / disconnected). The UI maps
94
+ // this to the localized "reconnect first" flash; see spec § API Contracts.
95
+ throw createCrudFormError(
96
+ 'Channel is not connected',
97
+ { channelId: 'Channel must be connected to import history' },
98
+ { status: 409 },
99
+ )
100
+ }
101
+
102
+ // Adapter must declare `importHistory` — Gmail / IMAP adapters gain it
103
+ // in Spec C. Other providers (chat, SMS) cannot do historical inbox sweeps.
104
+ const adapter = getChannelAdapterRegistry().get(channel.providerKey)
105
+ if (!adapter) {
106
+ throw createCrudFormError(
107
+ 'Channel provider is not available',
108
+ { channelId: `No adapter registered for provider "${channel.providerKey}"` },
109
+ { status: 400 },
110
+ )
111
+ }
112
+ if (typeof adapter.importHistory !== 'function') {
113
+ throw createCrudFormError(
114
+ 'History import is not supported on this provider',
115
+ { channelId: `Provider "${channel.providerKey}" does not support history import yet` },
116
+ { status: 400 },
117
+ )
118
+ }
119
+
120
+ const progressService = container.resolve('progressService') as ProgressService
121
+ const progressContext: ProgressServiceContext = {
122
+ tenantId: scope.tenantId,
123
+ organizationId: scope.organizationId,
124
+ userId: scope.userId ?? null,
125
+ }
126
+
127
+ // Concurrency guard (best-effort): refuse if another import is already
128
+ // in-flight for the same channel. We can't query by meta in a portable way
129
+ // without DB-specific JSON operators, so we scan the small active-jobs window.
130
+ // This check and `createJob` below are NOT atomic — two near-simultaneous
131
+ // requests for the same channel can both pass and enqueue. That is acceptable,
132
+ // not corrupting: the import worker runs at concurrency 1 (duplicate jobs run
133
+ // sequentially, never in parallel) and inbound ingest dedups by the
134
+ // `(channel_id, external_message_id)` unique index, so the worst case is
135
+ // wasted re-fetch work, never duplicate rows.
136
+ const active = await progressService.getActiveJobs(progressContext)
137
+ const conflict = active.find((job) => {
138
+ if (job.jobType !== CHANNEL_IMPORT_HISTORY_JOB_TYPE) return false
139
+ const meta = (job.meta ?? {}) as Record<string, unknown>
140
+ return meta.channelId === channel.id
141
+ })
142
+ if (conflict) {
143
+ throw createCrudFormError(
144
+ 'An import is already in progress for this channel',
145
+ { channelId: 'Another history import is already running' },
146
+ { status: 429 },
147
+ )
148
+ }
149
+
150
+ const progressJob = await progressService.createJob(
151
+ {
152
+ jobType: CHANNEL_IMPORT_HISTORY_JOB_TYPE,
153
+ name: `Import history: ${channel.displayName ?? channel.externalIdentifier ?? channel.providerKey}`,
154
+ description: `Import up to ${input.maxMessages} messages from the last ${input.sinceDays} days`,
155
+ totalCount: input.maxMessages,
156
+ cancellable: true,
157
+ meta: {
158
+ channelId: channel.id,
159
+ providerKey: channel.providerKey,
160
+ sinceDays: input.sinceDays,
161
+ contactEmailsCount: input.contactEmails?.length ?? 0,
162
+ maxMessages: input.maxMessages,
163
+ },
164
+ },
165
+ progressContext,
166
+ )
167
+
168
+ const payload: ChannelImportHistoryJobPayload = {
169
+ progressJobId: progressJob.id,
170
+ channelId: channel.id,
171
+ sinceDays: input.sinceDays,
172
+ contactEmails: input.contactEmails,
173
+ maxMessages: input.maxMessages,
174
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId },
175
+ }
176
+ const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.importHistory)
177
+ await queue.enqueue(payload as unknown as Record<string, unknown>)
178
+
179
+ return { progressJobId: progressJob.id, totalCountHint: input.maxMessages }
180
+ }
@@ -0,0 +1,273 @@
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 { extractUndoPayload as extractSharedUndoPayload } from '@open-mercato/shared/lib/commands/undo'
6
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
7
+ import { ChannelThreadMapping, ExternalConversation } from '../data/entities'
8
+ import { emitCommunicationChannelsEvent } from '../events'
9
+
10
+ const reassignConversationSchema = z.object({
11
+ threadId: z.string().uuid(),
12
+ /** Set to `null` to unassign the conversation. */
13
+ assignedUserId: z.string().uuid().nullable(),
14
+ scope: z.object({
15
+ tenantId: z.string().uuid(),
16
+ organizationId: z.string().uuid().nullable(),
17
+ }),
18
+ })
19
+
20
+ export type ReassignConversationInput = z.infer<typeof reassignConversationSchema>
21
+
22
+ export type ReassignConversationResult =
23
+ | {
24
+ status: 'reassigned'
25
+ threadId: string
26
+ previousAssignedUserId: string | null
27
+ nextAssignedUserId: string | null
28
+ conversationId: string
29
+ undo: ReassignConversationUndoSnapshot
30
+ }
31
+ | { status: 'no_channel_link'; reason: string }
32
+ | { status: 'invalid_assignee'; reason: string }
33
+ | { status: 'noop'; reason: string }
34
+
35
+ export interface ReassignConversationUndoSnapshot {
36
+ threadMappingId: string
37
+ conversationId: string
38
+ // Optional for backward compatibility with log entries written before tenant
39
+ // scoping was added to the undo lookup; new snapshots always set it.
40
+ tenantId?: string
41
+ previousAssignedUserId: string | null
42
+ newAssignedUserId: string | null
43
+ }
44
+
45
+ export const COMMUNICATION_CHANNELS_REASSIGN_CONVERSATION_COMMAND_ID =
46
+ 'communication_channels.conversation.reassign'
47
+
48
+ /**
49
+ * Reassign the owning user of a channel-linked conversation.
50
+ *
51
+ * Updates both `ChannelThreadMapping.assignedUserId` and the linked
52
+ * `ExternalConversation.assignedUserId` so subscribers (notification handlers,
53
+ * future dashboards) see a consistent owner. No external provider call —
54
+ * reassignment is an internal-routing concern.
55
+ *
56
+ * Idempotent: when the new owner matches the existing one, returns `noop`.
57
+ *
58
+ * The command is undoable: the `before` snapshot captures the previous owner on
59
+ * both rows so undo can restore them atomically.
60
+ */
61
+ const reassignConversationCommand: CommandHandler<
62
+ ReassignConversationInput,
63
+ ReassignConversationResult
64
+ > = {
65
+ id: COMMUNICATION_CHANNELS_REASSIGN_CONVERSATION_COMMAND_ID,
66
+ // Explicitly undoable (the bus also infers this from `undo` below, but
67
+ // declaring it keeps undoability from silently dropping under a refactor).
68
+ isUndoable: true,
69
+ async execute(rawInput, ctx) {
70
+ const input = reassignConversationSchema.parse(rawInput) as ReassignConversationInput
71
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
72
+ const dscope = {
73
+ tenantId: input.scope.tenantId,
74
+ organizationId: input.scope.organizationId ?? null,
75
+ }
76
+
77
+ const mapping = await findOneWithDecryption(
78
+ em,
79
+ ChannelThreadMapping,
80
+ {
81
+ messageThreadId: input.threadId,
82
+ tenantId: input.scope.tenantId,
83
+ organizationId: input.scope.organizationId ?? null,
84
+ },
85
+ // `message_thread_id` is non-unique (only `(external_conversation_id,
86
+ // tenant_id)` is unique). The 1:1 thread↔mapping invariant holds in v1,
87
+ // but order deterministically so a future many-conversations-per-thread
88
+ // merge can never resolve an arbitrary mapping here.
89
+ { orderBy: { createdAt: 'asc' } },
90
+ dscope,
91
+ )
92
+ if (!mapping) {
93
+ return {
94
+ status: 'no_channel_link',
95
+ reason: `no ChannelThreadMapping for thread ${input.threadId}`,
96
+ }
97
+ }
98
+
99
+ const previousAssignedUserId = mapping.assignedUserId ?? null
100
+ if (previousAssignedUserId === input.assignedUserId) {
101
+ return { status: 'noop', reason: 'assigned user unchanged' }
102
+ }
103
+
104
+ // Reject an assignee that is not a live user of this tenant — a UUID-shaped
105
+ // body alone must not create a cross-tenant / dangling owner reference.
106
+ if (input.assignedUserId) {
107
+ // Reference the `auth` user row by string entity name so this command does
108
+ // not import the auth module's entities (module independence); `as never`
109
+ // matches the codebase pattern for cross-module decrypted reads.
110
+ const assignee = await findOneWithDecryption(
111
+ em,
112
+ 'User' as never,
113
+ { id: input.assignedUserId, tenantId: input.scope.tenantId, deletedAt: null } as never,
114
+ undefined,
115
+ dscope,
116
+ )
117
+ if (!assignee) {
118
+ return {
119
+ status: 'invalid_assignee',
120
+ reason: 'assigned user is not a member of this tenant',
121
+ }
122
+ }
123
+ }
124
+
125
+ const conversation = await findOneWithDecryption(
126
+ em,
127
+ ExternalConversation,
128
+ {
129
+ id: mapping.externalConversationId,
130
+ tenantId: input.scope.tenantId,
131
+ organizationId: input.scope.organizationId ?? null,
132
+ },
133
+ undefined,
134
+ dscope,
135
+ )
136
+ if (!conversation) {
137
+ return {
138
+ status: 'no_channel_link',
139
+ reason: `no ExternalConversation for thread ${input.threadId}`,
140
+ }
141
+ }
142
+
143
+ // `assignedUserId` is an advisory routing pointer, not a DB foreign key
144
+ // (modules don't share ORM relations). The assignee-existence check above
145
+ // and this write are not atomic, so a user deleted in between leaves a
146
+ // harmless dangling pointer — consistent with the module's no-FK design.
147
+ mapping.assignedUserId = input.assignedUserId
148
+ conversation.assignedUserId = input.assignedUserId
149
+ await em.flush()
150
+
151
+ try {
152
+ await emitCommunicationChannelsEvent(
153
+ 'communication_channels.conversation.reassigned',
154
+ {
155
+ conversationId: conversation.id,
156
+ channelId: mapping.channelId,
157
+ messageThreadId: input.threadId,
158
+ previousAssignedUserId,
159
+ assignedUserId: input.assignedUserId,
160
+ tenantId: input.scope.tenantId,
161
+ organizationId: input.scope.organizationId ?? null,
162
+ },
163
+ { persistent: true },
164
+ )
165
+ } catch (emitErr) {
166
+ // Best-effort lifecycle/workflow-trigger signal — a bus failure must not
167
+ // abort the reassignment (the rows are already committed above).
168
+ console.warn(
169
+ '[communication_channels:reassign-conversation] reassigned event emit failed:',
170
+ emitErr instanceof Error ? emitErr.message : emitErr,
171
+ )
172
+ }
173
+
174
+ const undo: ReassignConversationUndoSnapshot = {
175
+ threadMappingId: mapping.id,
176
+ conversationId: conversation.id,
177
+ tenantId: mapping.tenantId,
178
+ previousAssignedUserId,
179
+ newAssignedUserId: input.assignedUserId,
180
+ }
181
+
182
+ return {
183
+ status: 'reassigned',
184
+ threadId: input.threadId,
185
+ previousAssignedUserId,
186
+ nextAssignedUserId: input.assignedUserId,
187
+ conversationId: conversation.id,
188
+ undo,
189
+ }
190
+ },
191
+ // Persist the undo snapshot into the action log. Without this, the command bus
192
+ // mints an undo token (so the UI offers "Undo") but the snapshot returned from
193
+ // execute() is never stored, and undo() would silently no-op.
194
+ async buildLog({ input, result }) {
195
+ if (result.status !== 'reassigned') return null
196
+ return {
197
+ resourceKind: 'communication_channels.channel',
198
+ resourceId: result.conversationId,
199
+ tenantId: result.undo.tenantId ?? input.scope.tenantId,
200
+ organizationId: input.scope.organizationId ?? null,
201
+ payload: { undo: result.undo },
202
+ snapshotBefore: result.undo,
203
+ }
204
+ },
205
+ async undo({ ctx, logEntry }) {
206
+ const snapshot = extractSnapshotFromLog(logEntry)
207
+ if (!snapshot) return
208
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
209
+ // Never resolve by bare id (cross-tenant). New snapshots always carry
210
+ // tenantId; refuse the undo if a legacy snapshot lacks it.
211
+ if (!snapshot.tenantId) return
212
+ const dscope = { tenantId: snapshot.tenantId, organizationId: null }
213
+
214
+ const mapping = await findOneWithDecryption(
215
+ em,
216
+ ChannelThreadMapping,
217
+ { id: snapshot.threadMappingId, tenantId: snapshot.tenantId },
218
+ undefined,
219
+ dscope,
220
+ )
221
+ const conversation = await findOneWithDecryption(
222
+ em,
223
+ ExternalConversation,
224
+ { id: snapshot.conversationId, tenantId: snapshot.tenantId },
225
+ undefined,
226
+ dscope,
227
+ )
228
+ if (mapping) mapping.assignedUserId = snapshot.previousAssignedUserId
229
+ if (conversation) conversation.assignedUserId = snapshot.previousAssignedUserId
230
+ if (mapping || conversation) await em.flush()
231
+ },
232
+ }
233
+
234
+ /**
235
+ * Read the undo payload defensively — wraps the shared
236
+ * `@open-mercato/shared/lib/commands/undo.ts` helper with a narrow-by-shape
237
+ * validation so callers get a strongly-typed snapshot or `null`.
238
+ *
239
+ * Kept as a separate export for test ergonomics (tests can mock the snapshot
240
+ * shape directly without round-tripping through a CommandLogEntry).
241
+ */
242
+ export function extractUndoPayload(value: unknown): ReassignConversationUndoSnapshot | null {
243
+ if (!value || typeof value !== 'object') return null
244
+ const candidate = (value as { undo?: unknown }).undo ?? value
245
+ if (!candidate || typeof candidate !== 'object') return null
246
+ const obj = candidate as Record<string, unknown>
247
+ if (typeof obj.threadMappingId !== 'string' || typeof obj.conversationId !== 'string') return null
248
+ return {
249
+ threadMappingId: obj.threadMappingId,
250
+ conversationId: obj.conversationId,
251
+ tenantId: typeof obj.tenantId === 'string' ? obj.tenantId : undefined,
252
+ previousAssignedUserId:
253
+ typeof obj.previousAssignedUserId === 'string' ? obj.previousAssignedUserId : null,
254
+ newAssignedUserId: typeof obj.newAssignedUserId === 'string' ? obj.newAssignedUserId : null,
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Pulls the reassignment snapshot from a command log entry — first via the
260
+ * shared `extractUndoPayload` helper, then through the local shape-validator.
261
+ * Always falls back to `null` so the undo handler can no-op safely.
262
+ */
263
+ export function extractSnapshotFromLog(logEntry: unknown): ReassignConversationUndoSnapshot | null {
264
+ const undo = extractSharedUndoPayload<ReassignConversationUndoSnapshot>(
265
+ (logEntry ?? null) as never,
266
+ )
267
+ if (undo) return extractUndoPayload(undo)
268
+ return extractUndoPayload(logEntry)
269
+ }
270
+
271
+ registerCommand(reassignConversationCommand)
272
+
273
+ export default reassignConversationCommand