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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (533) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/channel_ingest_dead_letter/index.js +25 -0
  3. package/dist/generated/entities/channel_ingest_dead_letter/index.js.map +7 -0
  4. package/dist/generated/entities/channel_thread_mapping/index.js +25 -0
  5. package/dist/generated/entities/channel_thread_mapping/index.js.map +7 -0
  6. package/dist/generated/entities/channel_thread_token/index.js +17 -0
  7. package/dist/generated/entities/channel_thread_token/index.js.map +7 -0
  8. package/dist/generated/entities/communication_channel/index.js +43 -0
  9. package/dist/generated/entities/communication_channel/index.js.map +7 -0
  10. package/dist/generated/entities/customer_interaction/index.js +4 -0
  11. package/dist/generated/entities/customer_interaction/index.js.map +2 -2
  12. package/dist/generated/entities/external_conversation/index.js +25 -0
  13. package/dist/generated/entities/external_conversation/index.js.map +7 -0
  14. package/dist/generated/entities/external_message/index.js +25 -0
  15. package/dist/generated/entities/external_message/index.js.map +7 -0
  16. package/dist/generated/entities/integration_credentials/index.js +3 -1
  17. package/dist/generated/entities/integration_credentials/index.js.map +2 -2
  18. package/dist/generated/entities/message/index.js +2 -0
  19. package/dist/generated/entities/message/index.js.map +2 -2
  20. package/dist/generated/entities/message_channel_link/index.js +33 -0
  21. package/dist/generated/entities/message_channel_link/index.js.map +7 -0
  22. package/dist/generated/entities/message_reaction/index.js +25 -0
  23. package/dist/generated/entities/message_reaction/index.js.map +7 -0
  24. package/dist/generated/entities.ids.generated.js +11 -0
  25. package/dist/generated/entities.ids.generated.js.map +2 -2
  26. package/dist/generated/entity-fields-registry.js +117 -0
  27. package/dist/generated/entity-fields-registry.js.map +2 -2
  28. package/dist/helpers/integration/authFixtures.js +2 -1
  29. package/dist/helpers/integration/authFixtures.js.map +2 -2
  30. package/dist/helpers/integration/communicationChannelsFixtures.js +58 -0
  31. package/dist/helpers/integration/communicationChannelsFixtures.js.map +7 -0
  32. package/dist/modules/communication_channels/acl.js +47 -0
  33. package/dist/modules/communication_channels/acl.js.map +7 -0
  34. package/dist/modules/communication_channels/api/delete/channels/[id]/route.js +133 -0
  35. package/dist/modules/communication_channels/api/delete/channels/[id]/route.js.map +7 -0
  36. package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js +113 -0
  37. package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js.map +7 -0
  38. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +138 -0
  39. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +7 -0
  40. package/dist/modules/communication_channels/api/get/channels/[id]/route.js +93 -0
  41. package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +7 -0
  42. package/dist/modules/communication_channels/api/get/channels/route.js +96 -0
  43. package/dist/modules/communication_channels/api/get/channels/route.js.map +7 -0
  44. package/dist/modules/communication_channels/api/get/me/channels/route.js +82 -0
  45. package/dist/modules/communication_channels/api/get/me/channels/route.js.map +7 -0
  46. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +274 -0
  47. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +7 -0
  48. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +168 -0
  49. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +7 -0
  50. package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js +143 -0
  51. package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js.map +7 -0
  52. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js +127 -0
  53. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js.map +7 -0
  54. package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js +99 -0
  55. package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js.map +7 -0
  56. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +197 -0
  57. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +7 -0
  58. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +124 -0
  59. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +7 -0
  60. package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js +120 -0
  61. package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js.map +7 -0
  62. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +157 -0
  63. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +7 -0
  64. package/dist/modules/communication_channels/api/post/send-as-user/route.js +115 -0
  65. package/dist/modules/communication_channels/api/post/send-as-user/route.js.map +7 -0
  66. package/dist/modules/communication_channels/api/post/test-seed/route.js +217 -0
  67. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +7 -0
  68. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +175 -0
  69. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +7 -0
  70. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +123 -0
  71. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +7 -0
  72. package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js +117 -0
  73. package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js.map +7 -0
  74. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js +180 -0
  75. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js.map +7 -0
  76. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js +36 -0
  77. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js.map +7 -0
  78. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +107 -0
  79. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +7 -0
  80. package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js +38 -0
  81. package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js.map +7 -0
  82. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +727 -0
  83. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +7 -0
  84. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js +38 -0
  85. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js.map +7 -0
  86. package/dist/modules/communication_channels/commands/connect-credential-channel.js +154 -0
  87. package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +7 -0
  88. package/dist/modules/communication_channels/commands/delete-channel.js +137 -0
  89. package/dist/modules/communication_channels/commands/delete-channel.js.map +7 -0
  90. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +400 -0
  91. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +7 -0
  92. package/dist/modules/communication_channels/commands/disconnect-channel.js +163 -0
  93. package/dist/modules/communication_channels/commands/disconnect-channel.js.map +7 -0
  94. package/dist/modules/communication_channels/commands/ingest-inbound-message.js +413 -0
  95. package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +7 -0
  96. package/dist/modules/communication_channels/commands/interceptors.js +68 -0
  97. package/dist/modules/communication_channels/commands/interceptors.js.map +7 -0
  98. package/dist/modules/communication_channels/commands/process-inbound-reaction.js +198 -0
  99. package/dist/modules/communication_channels/commands/process-inbound-reaction.js.map +7 -0
  100. package/dist/modules/communication_channels/commands/push-register.js +146 -0
  101. package/dist/modules/communication_channels/commands/push-register.js.map +7 -0
  102. package/dist/modules/communication_channels/commands/push-renew.js +23 -0
  103. package/dist/modules/communication_channels/commands/push-renew.js.map +7 -0
  104. package/dist/modules/communication_channels/commands/push-unregister.js +108 -0
  105. package/dist/modules/communication_channels/commands/push-unregister.js.map +7 -0
  106. package/dist/modules/communication_channels/commands/queue-import-history.js +113 -0
  107. package/dist/modules/communication_channels/commands/queue-import-history.js.map +7 -0
  108. package/dist/modules/communication_channels/commands/reassign-conversation.js +193 -0
  109. package/dist/modules/communication_channels/commands/reassign-conversation.js.map +7 -0
  110. package/dist/modules/communication_channels/commands/set-primary-channel.js +114 -0
  111. package/dist/modules/communication_channels/commands/set-primary-channel.js.map +7 -0
  112. package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js +260 -0
  113. package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js.map +7 -0
  114. package/dist/modules/communication_channels/data/enrichers.js +286 -0
  115. package/dist/modules/communication_channels/data/enrichers.js.map +7 -0
  116. package/dist/modules/communication_channels/data/entities.js +447 -0
  117. package/dist/modules/communication_channels/data/entities.js.map +7 -0
  118. package/dist/modules/communication_channels/data/extensions.js +67 -0
  119. package/dist/modules/communication_channels/data/extensions.js.map +7 -0
  120. package/dist/modules/communication_channels/data/validators.js +123 -0
  121. package/dist/modules/communication_channels/data/validators.js.map +7 -0
  122. package/dist/modules/communication_channels/di.js +35 -0
  123. package/dist/modules/communication_channels/di.js.map +7 -0
  124. package/dist/modules/communication_channels/encryption.js +12 -0
  125. package/dist/modules/communication_channels/encryption.js.map +7 -0
  126. package/dist/modules/communication_channels/events.js +124 -0
  127. package/dist/modules/communication_channels/events.js.map +7 -0
  128. package/dist/modules/communication_channels/index.js +20 -0
  129. package/dist/modules/communication_channels/index.js.map +7 -0
  130. package/dist/modules/communication_channels/lib/access-control.js +43 -0
  131. package/dist/modules/communication_channels/lib/access-control.js.map +7 -0
  132. package/dist/modules/communication_channels/lib/adapter-compat.js +36 -0
  133. package/dist/modules/communication_channels/lib/adapter-compat.js.map +7 -0
  134. package/dist/modules/communication_channels/lib/adapter-registry-singleton.js +22 -0
  135. package/dist/modules/communication_channels/lib/adapter-registry-singleton.js.map +7 -0
  136. package/dist/modules/communication_channels/lib/adapter.js +1 -0
  137. package/dist/modules/communication_channels/lib/adapter.js.map +7 -0
  138. package/dist/modules/communication_channels/lib/connect-channel.js +95 -0
  139. package/dist/modules/communication_channels/lib/connect-channel.js.map +7 -0
  140. package/dist/modules/communication_channels/lib/contact-resolver.js +79 -0
  141. package/dist/modules/communication_channels/lib/contact-resolver.js.map +7 -0
  142. package/dist/modules/communication_channels/lib/credential-refresh.js +97 -0
  143. package/dist/modules/communication_channels/lib/credential-refresh.js.map +7 -0
  144. package/dist/modules/communication_channels/lib/dead-letter.js +62 -0
  145. package/dist/modules/communication_channels/lib/dead-letter.js.map +7 -0
  146. package/dist/modules/communication_channels/lib/email-capabilities.js +47 -0
  147. package/dist/modules/communication_channels/lib/email-capabilities.js.map +7 -0
  148. package/dist/modules/communication_channels/lib/email-contact.js +14 -0
  149. package/dist/modules/communication_channels/lib/email-contact.js.map +7 -0
  150. package/dist/modules/communication_channels/lib/email-mime.js +269 -0
  151. package/dist/modules/communication_channels/lib/email-mime.js.map +7 -0
  152. package/dist/modules/communication_channels/lib/error-classification.js +101 -0
  153. package/dist/modules/communication_channels/lib/error-classification.js.map +7 -0
  154. package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js +185 -0
  155. package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js.map +7 -0
  156. package/dist/modules/communication_channels/lib/mutation-guards.js +114 -0
  157. package/dist/modules/communication_channels/lib/mutation-guards.js.map +7 -0
  158. package/dist/modules/communication_channels/lib/oauth-client-config.js +32 -0
  159. package/dist/modules/communication_channels/lib/oauth-client-config.js.map +7 -0
  160. package/dist/modules/communication_channels/lib/oauth-state.js +128 -0
  161. package/dist/modules/communication_channels/lib/oauth-state.js.map +7 -0
  162. package/dist/modules/communication_channels/lib/oauth-token.js +45 -0
  163. package/dist/modules/communication_channels/lib/oauth-token.js.map +7 -0
  164. package/dist/modules/communication_channels/lib/pg-errors.js +11 -0
  165. package/dist/modules/communication_channels/lib/pg-errors.js.map +7 -0
  166. package/dist/modules/communication_channels/lib/provider-health.js +24 -0
  167. package/dist/modules/communication_channels/lib/provider-health.js.map +7 -0
  168. package/dist/modules/communication_channels/lib/push-state.js +19 -0
  169. package/dist/modules/communication_channels/lib/push-state.js.map +7 -0
  170. package/dist/modules/communication_channels/lib/queue.js +54 -0
  171. package/dist/modules/communication_channels/lib/queue.js.map +7 -0
  172. package/dist/modules/communication_channels/lib/reaction-processor-types.js +5 -0
  173. package/dist/modules/communication_channels/lib/reaction-processor-types.js.map +7 -0
  174. package/dist/modules/communication_channels/lib/reaction-semantics.js +11 -0
  175. package/dist/modules/communication_channels/lib/reaction-semantics.js.map +7 -0
  176. package/dist/modules/communication_channels/lib/registry.js +67 -0
  177. package/dist/modules/communication_channels/lib/registry.js.map +7 -0
  178. package/dist/modules/communication_channels/lib/route-mutation-guard.js +43 -0
  179. package/dist/modules/communication_channels/lib/route-mutation-guard.js.map +7 -0
  180. package/dist/modules/communication_channels/lib/sanitize-channel-html.js +96 -0
  181. package/dist/modules/communication_channels/lib/sanitize-channel-html.js.map +7 -0
  182. package/dist/modules/communication_channels/lib/send-as-user.js +194 -0
  183. package/dist/modules/communication_channels/lib/send-as-user.js.map +7 -0
  184. package/dist/modules/communication_channels/lib/system-user.js +22 -0
  185. package/dist/modules/communication_channels/lib/system-user.js.map +7 -0
  186. package/dist/modules/communication_channels/lib/test-seed.js +68 -0
  187. package/dist/modules/communication_channels/lib/test-seed.js.map +7 -0
  188. package/dist/modules/communication_channels/lib/thread-matcher.js +263 -0
  189. package/dist/modules/communication_channels/lib/thread-matcher.js.map +7 -0
  190. package/dist/modules/communication_channels/lib/thread-token.js +219 -0
  191. package/dist/modules/communication_channels/lib/thread-token.js.map +7 -0
  192. package/dist/modules/communication_channels/lib/use-connect-channel.js +61 -0
  193. package/dist/modules/communication_channels/lib/use-connect-channel.js.map +7 -0
  194. package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js +50 -0
  195. package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js.map +7 -0
  196. package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js +19 -0
  197. package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js.map +7 -0
  198. package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js +13 -0
  199. package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js.map +7 -0
  200. package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js +17 -0
  201. package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js.map +7 -0
  202. package/dist/modules/communication_channels/notifications.client.js +51 -0
  203. package/dist/modules/communication_channels/notifications.client.js.map +7 -0
  204. package/dist/modules/communication_channels/notifications.handlers.js +53 -0
  205. package/dist/modules/communication_channels/notifications.handlers.js.map +7 -0
  206. package/dist/modules/communication_channels/notifications.js +56 -0
  207. package/dist/modules/communication_channels/notifications.js.map +7 -0
  208. package/dist/modules/communication_channels/setup.js +105 -0
  209. package/dist/modules/communication_channels/setup.js.map +7 -0
  210. package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js +71 -0
  211. package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js.map +7 -0
  212. package/dist/modules/communication_channels/subscribers/outbound-bridge.js +103 -0
  213. package/dist/modules/communication_channels/subscribers/outbound-bridge.js.map +7 -0
  214. package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js +51 -0
  215. package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js.map +7 -0
  216. package/dist/modules/communication_channels/widgets/components.js +7 -0
  217. package/dist/modules/communication_channels/widgets/components.js.map +7 -0
  218. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js +18 -0
  219. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js.map +7 -0
  220. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js +30 -0
  221. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js.map +7 -0
  222. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js +185 -0
  223. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js.map +7 -0
  224. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js +17 -0
  225. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js.map +7 -0
  226. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +44 -0
  227. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +7 -0
  228. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js +17 -0
  229. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js.map +7 -0
  230. package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js +23 -0
  231. package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js.map +7 -0
  232. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js +141 -0
  233. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js.map +7 -0
  234. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js +17 -0
  235. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js.map +7 -0
  236. package/dist/modules/communication_channels/widgets/injection-table.js +38 -0
  237. package/dist/modules/communication_channels/widgets/injection-table.js.map +7 -0
  238. package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js +25 -0
  239. package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js.map +7 -0
  240. package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js +19 -0
  241. package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js.map +7 -0
  242. package/dist/modules/communication_channels/widgets/notifications/index.js +7 -0
  243. package/dist/modules/communication_channels/widgets/notifications/index.js.map +7 -0
  244. package/dist/modules/communication_channels/workers/channel-import-history.js +185 -0
  245. package/dist/modules/communication_channels/workers/channel-import-history.js.map +7 -0
  246. package/dist/modules/communication_channels/workers/gmail-history-sync.js +154 -0
  247. package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +7 -0
  248. package/dist/modules/communication_channels/workers/gmail-renew-watch.js +95 -0
  249. package/dist/modules/communication_channels/workers/gmail-renew-watch.js.map +7 -0
  250. package/dist/modules/communication_channels/workers/inbound-processor.js +56 -0
  251. package/dist/modules/communication_channels/workers/inbound-processor.js.map +7 -0
  252. package/dist/modules/communication_channels/workers/outbound-delivery.js +85 -0
  253. package/dist/modules/communication_channels/workers/outbound-delivery.js.map +7 -0
  254. package/dist/modules/communication_channels/workers/poll-channel.js +240 -0
  255. package/dist/modules/communication_channels/workers/poll-channel.js.map +7 -0
  256. package/dist/modules/communication_channels/workers/poll-tick.js +132 -0
  257. package/dist/modules/communication_channels/workers/poll-tick.js.map +7 -0
  258. package/dist/modules/communication_channels/workers/reaction-processor.js +192 -0
  259. package/dist/modules/communication_channels/workers/reaction-processor.js.map +7 -0
  260. package/dist/modules/customers/acl.js +18 -0
  261. package/dist/modules/customers/acl.js.map +2 -2
  262. package/dist/modules/customers/api/activities/route.js +9 -0
  263. package/dist/modules/customers/api/activities/route.js.map +2 -2
  264. package/dist/modules/customers/api/companies/[id]/route.js +18 -7
  265. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  266. package/dist/modules/customers/api/interactions/[id]/visibility/route.js +151 -0
  267. package/dist/modules/customers/api/interactions/[id]/visibility/route.js.map +7 -0
  268. package/dist/modules/customers/api/interactions/counts/route.js +6 -0
  269. package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
  270. package/dist/modules/customers/api/interactions/route.js +26 -7
  271. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  272. package/dist/modules/customers/api/people/[id]/email-threads/route.js +82 -0
  273. package/dist/modules/customers/api/people/[id]/email-threads/route.js.map +7 -0
  274. package/dist/modules/customers/api/people/[id]/emails/route.js +157 -0
  275. package/dist/modules/customers/api/people/[id]/emails/route.js.map +7 -0
  276. package/dist/modules/customers/api/people/[id]/route.js +12 -4
  277. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  278. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +10 -0
  279. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  280. package/dist/modules/customers/commands/deals.js +46 -5
  281. package/dist/modules/customers/commands/deals.js.map +2 -2
  282. package/dist/modules/customers/commands/interactions.js +16 -0
  283. package/dist/modules/customers/commands/interactions.js.map +2 -2
  284. package/dist/modules/customers/components/detail/ActivityCard.js +32 -0
  285. package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
  286. package/dist/modules/customers/components/detail/ComposeEmailDialog.js +242 -0
  287. package/dist/modules/customers/components/detail/ComposeEmailDialog.js.map +7 -0
  288. package/dist/modules/customers/components/detail/DealForm.js +2 -1
  289. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  290. package/dist/modules/customers/components/detail/DealsSection.js +10 -0
  291. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  292. package/dist/modules/customers/components/detail/EmailCardActions.js +179 -0
  293. package/dist/modules/customers/components/detail/EmailCardActions.js.map +7 -0
  294. package/dist/modules/customers/components/detail/EmailReplyForwardActions.js +52 -0
  295. package/dist/modules/customers/components/detail/EmailReplyForwardActions.js.map +7 -0
  296. package/dist/modules/customers/components/detail/PersonDetailTabs.js +7 -1
  297. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  298. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js +366 -0
  299. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js.map +7 -0
  300. package/dist/modules/customers/data/enrichers.js +133 -2
  301. package/dist/modules/customers/data/enrichers.js.map +2 -2
  302. package/dist/modules/customers/data/entities.js +18 -0
  303. package/dist/modules/customers/data/entities.js.map +2 -2
  304. package/dist/modules/customers/data/extensions.js +16 -0
  305. package/dist/modules/customers/data/extensions.js.map +7 -0
  306. package/dist/modules/customers/encryption.js +11 -0
  307. package/dist/modules/customers/encryption.js.map +2 -2
  308. package/dist/modules/customers/events.js +4 -1
  309. package/dist/modules/customers/events.js.map +2 -2
  310. package/dist/modules/customers/lib/findPeopleByAddresses.js +64 -0
  311. package/dist/modules/customers/lib/findPeopleByAddresses.js.map +7 -0
  312. package/dist/modules/customers/lib/kysely.js.map +2 -2
  313. package/dist/modules/customers/lib/link-channel-message-handler.js +303 -0
  314. package/dist/modules/customers/lib/link-channel-message-handler.js.map +7 -0
  315. package/dist/modules/customers/lib/personEmailThreads.js +205 -0
  316. package/dist/modules/customers/lib/personEmailThreads.js.map +7 -0
  317. package/dist/modules/customers/lib/visibilityFilter.js +51 -0
  318. package/dist/modules/customers/lib/visibilityFilter.js.map +7 -0
  319. package/dist/modules/customers/migrations/Migration20260527012240_customers.js +20 -0
  320. package/dist/modules/customers/migrations/Migration20260527012240_customers.js.map +7 -0
  321. package/dist/modules/customers/setup.js +2 -1
  322. package/dist/modules/customers/setup.js.map +2 -2
  323. package/dist/modules/customers/subscribers/link-channel-message-received.js +12 -0
  324. package/dist/modules/customers/subscribers/link-channel-message-received.js.map +7 -0
  325. package/dist/modules/customers/subscribers/link-channel-message-sent.js +12 -0
  326. package/dist/modules/customers/subscribers/link-channel-message-sent.js.map +7 -0
  327. package/dist/modules/integrations/data/entities.js +8 -1
  328. package/dist/modules/integrations/data/entities.js.map +2 -2
  329. package/dist/modules/integrations/lib/credentials-service.js +29 -14
  330. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  331. package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js +15 -0
  332. package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js.map +7 -0
  333. package/dist/modules/messages/commands/messages.js +70 -8
  334. package/dist/modules/messages/commands/messages.js.map +2 -2
  335. package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -13
  336. package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
  337. package/dist/modules/messages/components/MessageDetailPageClient.js +39 -2
  338. package/dist/modules/messages/components/MessageDetailPageClient.js.map +2 -2
  339. package/dist/modules/messages/components/MessagesInboxPageClient.js +1 -0
  340. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  341. package/dist/modules/messages/data/entities.js +8 -1
  342. package/dist/modules/messages/data/entities.js.map +2 -2
  343. package/dist/modules/messages/migrations/Migration20260531130000.js +15 -0
  344. package/dist/modules/messages/migrations/Migration20260531130000.js.map +7 -0
  345. package/dist/modules/messages/widgets/injection-table.js +7 -0
  346. package/dist/modules/messages/widgets/injection-table.js.map +7 -0
  347. package/generated/entities/channel_ingest_dead_letter/index.ts +11 -0
  348. package/generated/entities/channel_thread_mapping/index.ts +11 -0
  349. package/generated/entities/channel_thread_token/index.ts +7 -0
  350. package/generated/entities/communication_channel/index.ts +20 -0
  351. package/generated/entities/customer_interaction/index.ts +2 -0
  352. package/generated/entities/external_conversation/index.ts +11 -0
  353. package/generated/entities/external_message/index.ts +11 -0
  354. package/generated/entities/integration_credentials/index.ts +1 -0
  355. package/generated/entities/message/index.ts +1 -0
  356. package/generated/entities/message_channel_link/index.ts +15 -0
  357. package/generated/entities/message_reaction/index.ts +11 -0
  358. package/generated/entities.ids.generated.ts +11 -0
  359. package/generated/entity-fields-registry.ts +117 -0
  360. package/package.json +9 -7
  361. package/src/helpers/integration/authFixtures.ts +4 -1
  362. package/src/helpers/integration/communicationChannelsFixtures.ts +124 -0
  363. package/src/modules/communication_channels/acl.ts +43 -0
  364. package/src/modules/communication_channels/api/delete/channels/[id]/route.ts +163 -0
  365. package/src/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.ts +143 -0
  366. package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +173 -0
  367. package/src/modules/communication_channels/api/get/channels/[id]/route.ts +111 -0
  368. package/src/modules/communication_channels/api/get/channels/route.ts +109 -0
  369. package/src/modules/communication_channels/api/get/me/channels/route.ts +100 -0
  370. package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +355 -0
  371. package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +206 -0
  372. package/src/modules/communication_channels/api/post/channels/[id]/poll-now/route.ts +174 -0
  373. package/src/modules/communication_channels/api/post/channels/[id]/push/register/route.ts +158 -0
  374. package/src/modules/communication_channels/api/post/channels/[id]/set-primary/route.ts +114 -0
  375. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +241 -0
  376. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +134 -0
  377. package/src/modules/communication_channels/api/post/messages/[messageId]/reactions/route.ts +143 -0
  378. package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +192 -0
  379. package/src/modules/communication_channels/api/post/send-as-user/route.ts +125 -0
  380. package/src/modules/communication_channels/api/post/test-seed/route.ts +267 -0
  381. package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +227 -0
  382. package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +161 -0
  383. package/src/modules/communication_channels/api/put/threads/[threadId]/assign/route.ts +132 -0
  384. package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.ts +34 -0
  385. package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.tsx +250 -0
  386. package/src/modules/communication_channels/backend/communication_channels/channels/page.meta.ts +36 -0
  387. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +137 -0
  388. package/src/modules/communication_channels/backend/profile/communication-channels/page.meta.ts +36 -0
  389. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +907 -0
  390. package/src/modules/communication_channels/commands/connect-credential-channel.ts +243 -0
  391. package/src/modules/communication_channels/commands/delete-channel.ts +193 -0
  392. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +579 -0
  393. package/src/modules/communication_channels/commands/disconnect-channel.ts +241 -0
  394. package/src/modules/communication_channels/commands/ingest-inbound-message.ts +602 -0
  395. package/src/modules/communication_channels/commands/interceptors.ts +104 -0
  396. package/src/modules/communication_channels/commands/process-inbound-reaction.ts +265 -0
  397. package/src/modules/communication_channels/commands/push-register.ts +203 -0
  398. package/src/modules/communication_channels/commands/push-renew.ts +49 -0
  399. package/src/modules/communication_channels/commands/push-unregister.ts +168 -0
  400. package/src/modules/communication_channels/commands/queue-import-history.ts +180 -0
  401. package/src/modules/communication_channels/commands/reassign-conversation.ts +273 -0
  402. package/src/modules/communication_channels/commands/set-primary-channel.ts +154 -0
  403. package/src/modules/communication_channels/commands/toggle-outbound-reaction.ts +347 -0
  404. package/src/modules/communication_channels/data/enrichers.ts +413 -0
  405. package/src/modules/communication_channels/data/entities.ts +546 -0
  406. package/src/modules/communication_channels/data/extensions.ts +76 -0
  407. package/src/modules/communication_channels/data/validators.ts +138 -0
  408. package/src/modules/communication_channels/di.ts +40 -0
  409. package/src/modules/communication_channels/encryption.ts +44 -0
  410. package/src/modules/communication_channels/events.ts +122 -0
  411. package/src/modules/communication_channels/i18n/de.json +138 -0
  412. package/src/modules/communication_channels/i18n/en.json +138 -0
  413. package/src/modules/communication_channels/i18n/es.json +138 -0
  414. package/src/modules/communication_channels/i18n/pl.json +138 -0
  415. package/src/modules/communication_channels/index.ts +19 -0
  416. package/src/modules/communication_channels/lib/access-control.ts +110 -0
  417. package/src/modules/communication_channels/lib/adapter-compat.ts +57 -0
  418. package/src/modules/communication_channels/lib/adapter-registry-singleton.ts +35 -0
  419. package/src/modules/communication_channels/lib/adapter.ts +605 -0
  420. package/src/modules/communication_channels/lib/connect-channel.ts +163 -0
  421. package/src/modules/communication_channels/lib/contact-resolver.ts +162 -0
  422. package/src/modules/communication_channels/lib/credential-refresh.ts +197 -0
  423. package/src/modules/communication_channels/lib/dead-letter.ts +87 -0
  424. package/src/modules/communication_channels/lib/email-capabilities.ts +60 -0
  425. package/src/modules/communication_channels/lib/email-contact.ts +17 -0
  426. package/src/modules/communication_channels/lib/email-mime.ts +442 -0
  427. package/src/modules/communication_channels/lib/error-classification.ts +144 -0
  428. package/src/modules/communication_channels/lib/gmail-pubsub-jwt.ts +278 -0
  429. package/src/modules/communication_channels/lib/mutation-guards.ts +215 -0
  430. package/src/modules/communication_channels/lib/oauth-client-config.ts +79 -0
  431. package/src/modules/communication_channels/lib/oauth-state.ts +228 -0
  432. package/src/modules/communication_channels/lib/oauth-token.ts +81 -0
  433. package/src/modules/communication_channels/lib/pg-errors.ts +12 -0
  434. package/src/modules/communication_channels/lib/provider-health.ts +47 -0
  435. package/src/modules/communication_channels/lib/push-state.ts +38 -0
  436. package/src/modules/communication_channels/lib/queue.ts +66 -0
  437. package/src/modules/communication_channels/lib/reaction-processor-types.ts +51 -0
  438. package/src/modules/communication_channels/lib/reaction-semantics.ts +48 -0
  439. package/src/modules/communication_channels/lib/registry.ts +99 -0
  440. package/src/modules/communication_channels/lib/route-mutation-guard.ts +68 -0
  441. package/src/modules/communication_channels/lib/sanitize-channel-html.ts +129 -0
  442. package/src/modules/communication_channels/lib/send-as-user.ts +284 -0
  443. package/src/modules/communication_channels/lib/system-user.ts +74 -0
  444. package/src/modules/communication_channels/lib/test-seed.ts +140 -0
  445. package/src/modules/communication_channels/lib/thread-matcher.ts +430 -0
  446. package/src/modules/communication_channels/lib/thread-token.ts +355 -0
  447. package/src/modules/communication_channels/lib/use-connect-channel.ts +73 -0
  448. package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +2142 -0
  449. package/src/modules/communication_channels/migrations/Migration20260526134719_communication_channels.ts +55 -0
  450. package/src/modules/communication_channels/migrations/Migration20260527195446_communication_channels.ts +20 -0
  451. package/src/modules/communication_channels/migrations/Migration20260529231848_communication_channels.ts +13 -0
  452. package/src/modules/communication_channels/migrations/Migration20260531120000_communication_channels.ts +24 -0
  453. package/src/modules/communication_channels/notifications.client.ts +50 -0
  454. package/src/modules/communication_channels/notifications.handlers.ts +86 -0
  455. package/src/modules/communication_channels/notifications.ts +52 -0
  456. package/src/modules/communication_channels/setup.ts +158 -0
  457. package/src/modules/communication_channels/subscribers/channel-requires-reauth-notification.ts +118 -0
  458. package/src/modules/communication_channels/subscribers/outbound-bridge.ts +175 -0
  459. package/src/modules/communication_channels/subscribers/user-deleted-cascade.ts +100 -0
  460. package/src/modules/communication_channels/widgets/components.ts +36 -0
  461. package/src/modules/communication_channels/widgets/injection/channel-badge/widget.client.tsx +38 -0
  462. package/src/modules/communication_channels/widgets/injection/channel-badge/widget.ts +51 -0
  463. package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.tsx +278 -0
  464. package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.ts +24 -0
  465. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +63 -0
  466. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.ts +29 -0
  467. package/src/modules/communication_channels/widgets/injection/profile-channels-menu/widget.ts +34 -0
  468. package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.client.tsx +177 -0
  469. package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.ts +26 -0
  470. package/src/modules/communication_channels/widgets/injection-table.ts +47 -0
  471. package/src/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.tsx +48 -0
  472. package/src/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.tsx +45 -0
  473. package/src/modules/communication_channels/widgets/notifications/index.ts +2 -0
  474. package/src/modules/communication_channels/workers/channel-import-history.ts +252 -0
  475. package/src/modules/communication_channels/workers/gmail-history-sync.ts +223 -0
  476. package/src/modules/communication_channels/workers/gmail-renew-watch.ts +141 -0
  477. package/src/modules/communication_channels/workers/inbound-processor.ts +114 -0
  478. package/src/modules/communication_channels/workers/outbound-delivery.ts +155 -0
  479. package/src/modules/communication_channels/workers/poll-channel.ts +391 -0
  480. package/src/modules/communication_channels/workers/poll-tick.ts +210 -0
  481. package/src/modules/communication_channels/workers/reaction-processor.ts +264 -0
  482. package/src/modules/customers/acl.ts +18 -0
  483. package/src/modules/customers/api/activities/route.ts +13 -0
  484. package/src/modules/customers/api/companies/[id]/route.ts +21 -1
  485. package/src/modules/customers/api/interactions/[id]/visibility/route.ts +179 -0
  486. package/src/modules/customers/api/interactions/counts/route.ts +10 -0
  487. package/src/modules/customers/api/interactions/route.ts +51 -5
  488. package/src/modules/customers/api/people/[id]/email-threads/route.ts +92 -0
  489. package/src/modules/customers/api/people/[id]/emails/route.ts +184 -0
  490. package/src/modules/customers/api/people/[id]/route.ts +17 -2
  491. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +11 -1
  492. package/src/modules/customers/commands/deals.ts +65 -6
  493. package/src/modules/customers/commands/interactions.ts +30 -0
  494. package/src/modules/customers/components/detail/ActivityCard.tsx +48 -0
  495. package/src/modules/customers/components/detail/ComposeEmailDialog.tsx +329 -0
  496. package/src/modules/customers/components/detail/DealForm.tsx +2 -1
  497. package/src/modules/customers/components/detail/DealsSection.tsx +26 -0
  498. package/src/modules/customers/components/detail/EmailCardActions.tsx +258 -0
  499. package/src/modules/customers/components/detail/EmailReplyForwardActions.tsx +53 -0
  500. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +8 -1
  501. package/src/modules/customers/components/detail/PersonEmailThreadsTab.tsx +448 -0
  502. package/src/modules/customers/data/enrichers.ts +252 -1
  503. package/src/modules/customers/data/entities.ts +46 -1
  504. package/src/modules/customers/data/extensions.ts +26 -0
  505. package/src/modules/customers/encryption.ts +11 -0
  506. package/src/modules/customers/events.ts +4 -0
  507. package/src/modules/customers/i18n/de.json +41 -0
  508. package/src/modules/customers/i18n/en.json +41 -0
  509. package/src/modules/customers/i18n/es.json +41 -0
  510. package/src/modules/customers/i18n/pl.json +41 -0
  511. package/src/modules/customers/lib/findPeopleByAddresses.ts +107 -0
  512. package/src/modules/customers/lib/kysely.ts +16 -0
  513. package/src/modules/customers/lib/link-channel-message-handler.ts +571 -0
  514. package/src/modules/customers/lib/personEmailThreads.ts +325 -0
  515. package/src/modules/customers/lib/visibilityFilter.ts +152 -0
  516. package/src/modules/customers/migrations/.snapshot-open-mercato.json +61 -0
  517. package/src/modules/customers/migrations/Migration20260527012240_customers.ts +23 -0
  518. package/src/modules/customers/setup.ts +1 -0
  519. package/src/modules/customers/subscribers/link-channel-message-received.ts +21 -0
  520. package/src/modules/customers/subscribers/link-channel-message-sent.ts +21 -0
  521. package/src/modules/integrations/AGENTS.md +9 -0
  522. package/src/modules/integrations/data/entities.ts +21 -1
  523. package/src/modules/integrations/lib/credentials-service.ts +49 -13
  524. package/src/modules/integrations/migrations/.snapshot-open-mercato.json +26 -1
  525. package/src/modules/integrations/migrations/Migration20260526154136_integrations.ts +15 -0
  526. package/src/modules/messages/commands/messages.ts +101 -8
  527. package/src/modules/messages/components/ComposeMessagePageClient.tsx +17 -0
  528. package/src/modules/messages/components/MessageDetailPageClient.tsx +43 -0
  529. package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -0
  530. package/src/modules/messages/data/entities.ts +11 -0
  531. package/src/modules/messages/migrations/.snapshot-open-mercato.json +18 -0
  532. package/src/modules/messages/migrations/Migration20260531130000.ts +15 -0
  533. package/src/modules/messages/widgets/injection-table.ts +29 -0
@@ -0,0 +1,154 @@
1
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
+ import { CommunicationChannel } from "../data/entities.js";
3
+ import {
4
+ COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID
5
+ } from "../commands/ingest-inbound-message.js";
6
+ import { COMMUNICATION_CHANNELS_QUEUES } from "../lib/queue.js";
7
+ import { preservePushState } from "../lib/push-state.js";
8
+ import { refreshCredentialsIfNeeded } from "../lib/credential-refresh.js";
9
+ import { classifyOutboundError } from "../lib/error-classification.js";
10
+ import { writeIngestDeadLetter } from "../lib/dead-letter.js";
11
+ const MAX_DRAIN_PAGES = 100;
12
+ const metadata = {
13
+ queue: COMMUNICATION_CHANNELS_QUEUES.gmailHistorySync,
14
+ id: "communication_channels:gmail-history-sync",
15
+ concurrency: 5
16
+ };
17
+ async function handle(job, ctx) {
18
+ const { channelId, scope } = job.payload;
19
+ const em = ctx.resolve("em").fork();
20
+ const adapterRegistry = ctx.resolve("channelAdapterRegistry");
21
+ const channel = await findOneWithDecryption(
22
+ em,
23
+ CommunicationChannel,
24
+ {
25
+ id: channelId,
26
+ tenantId: scope.tenantId,
27
+ organizationId: scope.organizationId ?? null,
28
+ deletedAt: null
29
+ },
30
+ void 0,
31
+ scope
32
+ );
33
+ if (!channel) {
34
+ console.warn(`[gmail-history-sync] channel ${channelId} not found (skip)`);
35
+ return;
36
+ }
37
+ if (!channel.isActive || channel.status !== "connected") return;
38
+ const adapter = adapterRegistry?.get(channel.providerKey);
39
+ if (!adapter || typeof adapter.applyPushNotification !== "function") {
40
+ console.warn(
41
+ `[gmail-history-sync] adapter for '${channel.providerKey}' does not support applyPushNotification`
42
+ );
43
+ return;
44
+ }
45
+ let credentialsService = null;
46
+ try {
47
+ credentialsService = ctx.resolve("integrationCredentialsService");
48
+ } catch {
49
+ credentialsService = null;
50
+ }
51
+ const credentialsScope = {
52
+ tenantId: scope.tenantId,
53
+ organizationId: scope.organizationId ?? scope.tenantId,
54
+ userId: channel.userId ?? null
55
+ };
56
+ let credentials = {};
57
+ if (channel.credentialsRef && credentialsService) {
58
+ try {
59
+ credentials = await credentialsService.resolve(`channel_${channel.providerKey}`, credentialsScope) ?? {};
60
+ } catch {
61
+ credentials = {};
62
+ }
63
+ }
64
+ const refreshed = await refreshCredentialsIfNeeded(
65
+ { adapter, channelId: channel.id, credentials, scope: credentialsScope },
66
+ { credentialsService }
67
+ );
68
+ credentials = refreshed.credentials;
69
+ let page;
70
+ try {
71
+ page = await adapter.applyPushNotification({
72
+ credentials,
73
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId ?? scope.tenantId },
74
+ channelState: channel.channelState ?? {},
75
+ notification: job.payload.notification
76
+ });
77
+ } catch (err) {
78
+ const classification = classifyOutboundError(err);
79
+ if (classification.transient) throw err;
80
+ console.warn(
81
+ `[gmail-history-sync] permanent failure applying push for channel ${channel.id}: ${classification.message}`
82
+ );
83
+ return;
84
+ }
85
+ const messages = Array.isArray(page?.messages) ? page.messages : [];
86
+ if (messages.length > 0) {
87
+ const commandBus = ctx.resolve("commandBus");
88
+ const containerProxy = { resolve: ctx.resolve.bind(ctx) };
89
+ const commandCtx = {
90
+ container: containerProxy,
91
+ auth: null,
92
+ organizationScope: null,
93
+ selectedOrganizationId: scope.organizationId ?? null,
94
+ organizationIds: scope.organizationId ? [scope.organizationId] : null
95
+ };
96
+ for (const message of messages) {
97
+ try {
98
+ const input = {
99
+ channelId: channel.id,
100
+ providerKey: channel.providerKey,
101
+ channelType: channel.channelType,
102
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId ?? null },
103
+ message
104
+ };
105
+ await commandBus.execute(COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID, {
106
+ input,
107
+ ctx: commandCtx
108
+ });
109
+ } catch (err) {
110
+ const classification = classifyOutboundError(err);
111
+ if (classification.transient) {
112
+ throw err;
113
+ }
114
+ console.warn(
115
+ `[gmail-history-sync] permanent ingest failure for channel ${channel.id}: ${classification.message}`
116
+ );
117
+ await writeIngestDeadLetter({ em, scope, channel, message, err, errorMessage: classification.message });
118
+ }
119
+ }
120
+ }
121
+ if (page?.nextCursor) {
122
+ try {
123
+ const decoded = JSON.parse(Buffer.from(page.nextCursor, "base64").toString("utf-8"));
124
+ channel.channelState = preservePushState(channel.channelState, decoded);
125
+ channel.lastPolledAt = /* @__PURE__ */ new Date();
126
+ await em.flush();
127
+ } catch (err) {
128
+ console.warn(
129
+ `[gmail-history-sync] failed to persist next cursor for channel ${channel.id}: ${err instanceof Error ? err.message : String(err)}`
130
+ );
131
+ }
132
+ }
133
+ if (page?.hasMore && page?.nextCursor) {
134
+ const drainPage = job.payload.drainPage ?? 0;
135
+ if (drainPage < MAX_DRAIN_PAGES) {
136
+ const queue = (await import("../lib/queue.js")).getCommunicationChannelsQueue(
137
+ COMMUNICATION_CHANNELS_QUEUES.gmailHistorySync
138
+ );
139
+ await queue.enqueue(
140
+ { ...job.payload, drainPage: drainPage + 1 },
141
+ { delayMs: 250 }
142
+ );
143
+ } else {
144
+ console.warn(
145
+ `[gmail-history-sync] drain page cap (${MAX_DRAIN_PAGES}) reached for channel ${channelId}; stopping re-enqueue`
146
+ );
147
+ }
148
+ }
149
+ }
150
+ export {
151
+ handle as default,
152
+ metadata
153
+ };
154
+ //# sourceMappingURL=gmail-history-sync.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/communication_channels/workers/gmail-history-sync.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CommunicationChannel } from '../data/entities'\nimport {\n COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID,\n type IngestInboundMessageInput,\n} from '../commands/ingest-inbound-message'\nimport { COMMUNICATION_CHANNELS_QUEUES } from '../lib/queue'\nimport { preservePushState } from '../lib/push-state'\nimport { refreshCredentialsIfNeeded } from '../lib/credential-refresh'\nimport { classifyOutboundError } from '../lib/error-classification'\nimport { writeIngestDeadLetter } from '../lib/dead-letter'\nimport type { ChannelAdapterRegistry } from '../lib/registry'\n\n/**\n * Spec C \u00A7 Phase C2 \u2014 Gmail Pub/Sub push delivery worker.\n *\n * One job per verified webhook notification. Calls\n * `adapter.applyPushNotification(...)` to walk `users.history.list` from the\n * channel's stored `historyId`, then dispatches each message through\n * `ingest-inbound-message` (same path as the polling worker).\n *\n * Concurrency: bounded by the queue's default (10). Per-channel ordering is\n * NOT guaranteed across notifications \u2014 but the ingest command is idempotent\n * on `(channel_id, external_message_id)`, and Gmail's history cursor advances\n * monotonically per channel, so out-of-order replay is safe.\n */\nexport type GmailHistorySyncJobPayload = {\n channelId: string\n scope: { tenantId: string; organizationId: string | null }\n notification: { emailAddress: string; historyId: string }\n /** Self-re-enqueue drain counter (bounds the multi-page drain loop). */\n drainPage?: number\n}\n\n/** Hard cap on self-re-enqueue drain pages \u2014 guards against an adapter that\n * returns `hasMore` with a non-advancing cursor (a tight, unbounded loop). */\nconst MAX_DRAIN_PAGES = 100\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.gmailHistorySync,\n id: 'communication_channels:gmail-history-sync',\n concurrency: 5,\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\ntype CredentialsServiceLike = {\n resolve: (\n integrationId: string,\n scope: { organizationId: string; tenantId: string; userId?: string | null },\n ) => Promise<Record<string, unknown> | null>\n save?: (\n integrationId: string,\n credentials: Record<string, unknown>,\n scope: { organizationId: string; tenantId: string; userId?: string | null },\n ) => Promise<void>\n}\n\nexport default async function handle(\n job: QueuedJob<GmailHistorySyncJobPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n const { channelId, scope } = job.payload\n const em = (ctx.resolve('em') as EntityManager).fork()\n const adapterRegistry = ctx.resolve<ChannelAdapterRegistry>('channelAdapterRegistry')\n\n const channel = await findOneWithDecryption(\n em,\n CommunicationChannel,\n {\n id: channelId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? null,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!channel) {\n console.warn(`[gmail-history-sync] channel ${channelId} not found (skip)`)\n return\n }\n if (!channel.isActive || channel.status !== 'connected') return\n\n const adapter = adapterRegistry?.get(channel.providerKey)\n if (!adapter || typeof adapter.applyPushNotification !== 'function') {\n console.warn(\n `[gmail-history-sync] adapter for '${channel.providerKey}' does not support applyPushNotification`,\n )\n return\n }\n\n let credentialsService: CredentialsServiceLike | null = null\n try {\n credentialsService = ctx.resolve<CredentialsServiceLike>('integrationCredentialsService')\n } catch {\n credentialsService = null\n }\n const credentialsScope = {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? scope.tenantId,\n userId: channel.userId ?? null,\n }\n let credentials: Record<string, unknown> = {}\n if (channel.credentialsRef && credentialsService) {\n try {\n credentials =\n (await credentialsService.resolve(`channel_${channel.providerKey}`, credentialsScope)) ?? {}\n } catch {\n credentials = {}\n }\n }\n const refreshed = await refreshCredentialsIfNeeded(\n { adapter, channelId: channel.id, credentials, scope: credentialsScope },\n { credentialsService },\n )\n credentials = refreshed.credentials\n\n let page\n try {\n page = await adapter.applyPushNotification({\n credentials,\n scope: { tenantId: scope.tenantId, organizationId: scope.organizationId ?? scope.tenantId },\n channelState: (channel.channelState as Record<string, unknown> | null) ?? {},\n notification: job.payload.notification as unknown as Record<string, unknown>,\n })\n } catch (err) {\n const classification = classifyOutboundError(err)\n if (classification.transient) throw err\n console.warn(\n `[gmail-history-sync] permanent failure applying push for channel ${channel.id}: ${classification.message}`,\n )\n return\n }\n\n const messages = Array.isArray(page?.messages) ? page.messages : []\n if (messages.length > 0) {\n const commandBus = ctx.resolve<CommandBus>('commandBus')\n const containerProxy = { resolve: ctx.resolve.bind(ctx) }\n const commandCtx = {\n container: containerProxy as never,\n auth: null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId ?? null,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n }\n for (const message of messages) {\n try {\n const input: IngestInboundMessageInput = {\n channelId: channel.id,\n providerKey: channel.providerKey,\n channelType: channel.channelType,\n scope: { tenantId: scope.tenantId, organizationId: scope.organizationId ?? null },\n message,\n }\n await commandBus.execute(COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID, {\n input,\n ctx: commandCtx as never,\n })\n } catch (err) {\n const classification = classifyOutboundError(err)\n if (classification.transient) {\n // Per-message commit semantics from Spec B: re-throw so queue\n // retry re-runs from a safe point.\n throw err\n }\n console.warn(\n `[gmail-history-sync] permanent ingest failure for channel ${channel.id}: ${classification.message}`,\n )\n await writeIngestDeadLetter({ em, scope, channel, message, err, errorMessage: classification.message })\n }\n }\n }\n\n // Persist the adapter's advanced cursor (encoded in nextCursor) back to\n // channel.channelState. fetchHistory's nextCursor is a base64-JSON blob\n // mirroring the new GmailChannelState \u2014 same as the polling worker writes.\n if (page?.nextCursor) {\n try {\n const decoded = JSON.parse(Buffer.from(page.nextCursor, 'base64').toString('utf-8')) as Record<string, unknown>\n // Carry the push keys forward via preservePushState rather than spreading\n // the decoded cursor wholesale: a blind spread would retain a stale\n // `pendingHistoryPageToken` after a completed drain and mis-route the next\n // push notification.\n channel.channelState = preservePushState(channel.channelState, decoded)\n channel.lastPolledAt = new Date()\n await em.flush()\n } catch (err) {\n console.warn(\n `[gmail-history-sync] failed to persist next cursor for channel ${channel.id}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n }\n\n if (page?.hasMore && page?.nextCursor) {\n // Re-enqueue self so the drain continues. The Pub/Sub notification fired\n // once, but `history.list` may need multiple pages on a busy mailbox.\n // Bound the drain and add a small delay so an adapter that returns\n // `hasMore: true` with a non-advancing cursor cannot spin a tight,\n // unthrottled re-enqueue loop against the provider/queue.\n const drainPage = job.payload.drainPage ?? 0\n if (drainPage < MAX_DRAIN_PAGES) {\n const queue = (await import('../lib/queue')).getCommunicationChannelsQueue(\n COMMUNICATION_CHANNELS_QUEUES.gmailHistorySync,\n )\n await queue.enqueue(\n { ...job.payload, drainPage: drainPage + 1 } as unknown as Record<string, unknown>,\n { delayMs: 250 },\n )\n } else {\n console.warn(\n `[gmail-history-sync] drain page cap (${MAX_DRAIN_PAGES}) reached for channel ${channelId}; stopping re-enqueue`,\n )\n }\n }\n}\n"],
5
+ "mappings": "AAGA,SAAS,6BAA6B;AACtC,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,OAEK;AACP,SAAS,qCAAqC;AAC9C,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,6BAA6B;AACtC,SAAS,6BAA6B;AA0BtC,MAAM,kBAAkB;AAEjB,MAAM,WAAuB;AAAA,EAClC,OAAO,8BAA8B;AAAA,EACrC,IAAI;AAAA,EACJ,aAAa;AACf;AAkBA,eAAO,OACL,KACA,KACe;AACf,QAAM,EAAE,WAAW,MAAM,IAAI,IAAI;AACjC,QAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,QAAM,kBAAkB,IAAI,QAAgC,wBAAwB;AAEpF,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM,kBAAkB;AAAA,MACxC,WAAW;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,CAAC,SAAS;AACZ,YAAQ,KAAK,gCAAgC,SAAS,mBAAmB;AACzE;AAAA,EACF;AACA,MAAI,CAAC,QAAQ,YAAY,QAAQ,WAAW,YAAa;AAEzD,QAAM,UAAU,iBAAiB,IAAI,QAAQ,WAAW;AACxD,MAAI,CAAC,WAAW,OAAO,QAAQ,0BAA0B,YAAY;AACnE,YAAQ;AAAA,MACN,qCAAqC,QAAQ,WAAW;AAAA,IAC1D;AACA;AAAA,EACF;AAEA,MAAI,qBAAoD;AACxD,MAAI;AACF,yBAAqB,IAAI,QAAgC,+BAA+B;AAAA,EAC1F,QAAQ;AACN,yBAAqB;AAAA,EACvB;AACA,QAAM,mBAAmB;AAAA,IACvB,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM,kBAAkB,MAAM;AAAA,IAC9C,QAAQ,QAAQ,UAAU;AAAA,EAC5B;AACA,MAAI,cAAuC,CAAC;AAC5C,MAAI,QAAQ,kBAAkB,oBAAoB;AAChD,QAAI;AACF,oBACG,MAAM,mBAAmB,QAAQ,WAAW,QAAQ,WAAW,IAAI,gBAAgB,KAAM,CAAC;AAAA,IAC/F,QAAQ;AACN,oBAAc,CAAC;AAAA,IACjB;AAAA,EACF;AACA,QAAM,YAAY,MAAM;AAAA,IACtB,EAAE,SAAS,WAAW,QAAQ,IAAI,aAAa,OAAO,iBAAiB;AAAA,IACvE,EAAE,mBAAmB;AAAA,EACvB;AACA,gBAAc,UAAU;AAExB,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,sBAAsB;AAAA,MACzC;AAAA,MACA,OAAO,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,kBAAkB,MAAM,SAAS;AAAA,MAC1F,cAAe,QAAQ,gBAAmD,CAAC;AAAA,MAC3E,cAAc,IAAI,QAAQ;AAAA,IAC5B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,iBAAiB,sBAAsB,GAAG;AAChD,QAAI,eAAe,UAAW,OAAM;AACpC,YAAQ;AAAA,MACN,oEAAoE,QAAQ,EAAE,KAAK,eAAe,OAAO;AAAA,IAC3G;AACA;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,QAAQ,MAAM,QAAQ,IAAI,KAAK,WAAW,CAAC;AAClE,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,aAAa,IAAI,QAAoB,YAAY;AACvD,UAAM,iBAAiB,EAAE,SAAS,IAAI,QAAQ,KAAK,GAAG,EAAE;AACxD,UAAM,aAAa;AAAA,MACjB,WAAW;AAAA,MACX,MAAM;AAAA,MACN,mBAAmB;AAAA,MACnB,wBAAwB,MAAM,kBAAkB;AAAA,MAChD,iBAAiB,MAAM,iBAAiB,CAAC,MAAM,cAAc,IAAI;AAAA,IACnE;AACA,eAAW,WAAW,UAAU;AAC9B,UAAI;AACF,cAAM,QAAmC;AAAA,UACvC,WAAW,QAAQ;AAAA,UACnB,aAAa,QAAQ;AAAA,UACrB,aAAa,QAAQ;AAAA,UACrB,OAAO,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,kBAAkB,KAAK;AAAA,UAChF;AAAA,QACF;AACA,cAAM,WAAW,QAAQ,kDAAkD;AAAA,UACzE;AAAA,UACA,KAAK;AAAA,QACP,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,cAAM,iBAAiB,sBAAsB,GAAG;AAChD,YAAI,eAAe,WAAW;AAG5B,gBAAM;AAAA,QACR;AACA,gBAAQ;AAAA,UACN,6DAA6D,QAAQ,EAAE,KAAK,eAAe,OAAO;AAAA,QACpG;AACA,cAAM,sBAAsB,EAAE,IAAI,OAAO,SAAS,SAAS,KAAK,cAAc,eAAe,QAAQ,CAAC;AAAA,MACxG;AAAA,IACF;AAAA,EACF;AAKA,MAAI,MAAM,YAAY;AACpB,QAAI;AACF,YAAM,UAAU,KAAK,MAAM,OAAO,KAAK,KAAK,YAAY,QAAQ,EAAE,SAAS,OAAO,CAAC;AAKnF,cAAQ,eAAe,kBAAkB,QAAQ,cAAc,OAAO;AACtE,cAAQ,eAAe,oBAAI,KAAK;AAChC,YAAM,GAAG,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,cAAQ;AAAA,QACN,kEAAkE,QAAQ,EAAE,KAC1E,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CACjD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,MAAM,YAAY;AAMrC,UAAM,YAAY,IAAI,QAAQ,aAAa;AAC3C,QAAI,YAAY,iBAAiB;AAC/B,YAAM,SAAS,MAAM,OAAO,cAAc,GAAG;AAAA,QAC3C,8BAA8B;AAAA,MAChC;AACA,YAAM,MAAM;AAAA,QACV,EAAE,GAAG,IAAI,SAAS,WAAW,YAAY,EAAE;AAAA,QAC3C,EAAE,SAAS,IAAI;AAAA,MACjB;AAAA,IACF,OAAO;AACL,cAAQ;AAAA,QACN,wCAAwC,eAAe,yBAAyB,SAAS;AAAA,MAC3F;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,95 @@
1
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
+ import { CommunicationChannel } from "../data/entities.js";
3
+ import { COMMUNICATION_CHANNELS_QUEUES } from "../lib/queue.js";
4
+ import { pushRenew } from "../commands/push-renew.js";
5
+ import { emitCommunicationChannelsEvent } from "../events.js";
6
+ const metadata = {
7
+ queue: COMMUNICATION_CHANNELS_QUEUES.gmailRenewWatch,
8
+ id: "communication_channels:gmail-renew-watch",
9
+ concurrency: 1
10
+ };
11
+ const LEAD_HOURS = Math.max(
12
+ 1,
13
+ Number.parseInt(process.env.OM_PUSH_RENEWAL_GMAIL_LEAD_HOURS ?? "24", 10) || 24
14
+ );
15
+ async function handle(job, ctx) {
16
+ const em = ctx.resolve("em").fork();
17
+ const cutoffMs = Date.now() + LEAD_HOURS * 60 * 60 * 1e3;
18
+ const scope = job?.payload?.scope ?? null;
19
+ const where = {
20
+ providerKey: "gmail",
21
+ isActive: true,
22
+ deletedAt: null
23
+ };
24
+ if (scope?.tenantId) where.tenantId = scope.tenantId;
25
+ if (scope?.organizationId) where.organizationId = scope.organizationId;
26
+ const channels = await findWithDecryption(
27
+ em,
28
+ CommunicationChannel,
29
+ where,
30
+ void 0,
31
+ scope ? { tenantId: scope.tenantId, organizationId: scope.organizationId ?? null } : void 0
32
+ );
33
+ const containerProxy = ctx.container;
34
+ let renewed = 0;
35
+ let failed = 0;
36
+ for (const channel of channels) {
37
+ const state = channel.channelState ?? null;
38
+ if (!state || state.pushStatus !== "active") continue;
39
+ if (typeof state.watchExpirationMs !== "number") continue;
40
+ if (state.watchExpirationMs > cutoffMs) continue;
41
+ const organizationId = channel.organizationId;
42
+ if (!organizationId) {
43
+ console.warn(
44
+ `[gmail-renew-watch] skipping channel ${channel.id} \u2014 no organizationId on row`
45
+ );
46
+ continue;
47
+ }
48
+ try {
49
+ const container = resolveContainer(containerProxy, ctx);
50
+ const result = await pushRenew({
51
+ container,
52
+ scope: {
53
+ tenantId: channel.tenantId,
54
+ organizationId,
55
+ userId: channel.userId ?? null
56
+ },
57
+ input: { channelId: channel.id }
58
+ });
59
+ if (result.pushStatus === "active") {
60
+ renewed += 1;
61
+ await emitCommunicationChannelsEvent(
62
+ "communication_channels.push.renewed",
63
+ {
64
+ channelId: channel.id,
65
+ providerKey: channel.providerKey,
66
+ tenantId: channel.tenantId,
67
+ organizationId
68
+ },
69
+ { persistent: false }
70
+ );
71
+ } else {
72
+ failed += 1;
73
+ }
74
+ } catch (err) {
75
+ failed += 1;
76
+ console.warn(
77
+ `[gmail-renew-watch] failed to renew channel ${channel.id}: ${err instanceof Error ? err.message : String(err)}`
78
+ );
79
+ }
80
+ }
81
+ if (renewed > 0 || failed > 0) {
82
+ console.info(`[gmail-renew-watch] renewed=${renewed} failed=${failed}`);
83
+ }
84
+ }
85
+ function resolveContainer(containerProxy, ctx) {
86
+ if (containerProxy && typeof containerProxy === "object" && "resolve" in containerProxy) {
87
+ return containerProxy;
88
+ }
89
+ return { resolve: ctx.resolve.bind(ctx) };
90
+ }
91
+ export {
92
+ handle as default,
93
+ metadata
94
+ };
95
+ //# sourceMappingURL=gmail-renew-watch.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/communication_channels/workers/gmail-renew-watch.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CommunicationChannel } from '../data/entities'\nimport { COMMUNICATION_CHANNELS_QUEUES } from '../lib/queue'\nimport { pushRenew } from '../commands/push-renew'\nimport { emitCommunicationChannelsEvent } from '../events'\n\n/**\n * Spec C \u00A7 Phase C4 \u2014 Daily cron that re-issues `gmail.users.watch` for\n * channels whose `watchExpirationMs` is within the renewal lead window.\n *\n * Gmail watch expires after ~7 days. We renew with a lead time of\n * `OM_PUSH_RENEWAL_GMAIL_LEAD_HOURS` (default 24h) so a missed cron tick\n * doesn't drop us into the polling fallback.\n *\n * The actual renewal is delegated to `pushRenew` (which in turn calls\n * `pushRegister`) \u2014 Gmail's `users.watch` is idempotent, so re-calling it\n * just returns a fresh `historyId` + `expiration` and persists them.\n *\n * Scoped to `job.payload.scope` (the cron is registered per-org in\n * `setup.ts`); each per-org cron tick only walks channels for its own\n * `(tenantId, organizationId)`. A missing scope means the legacy global\n * sweep variant \u2014 supported for backwards compatibility, but new\n * registrations always carry a scope.\n */\nexport type GmailRenewWatchPayload = {\n scope?: { tenantId: string; organizationId: string | null }\n}\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.gmailRenewWatch,\n id: 'communication_channels:gmail-renew-watch',\n concurrency: 1,\n}\n\ntype HandlerContext = JobContext & { resolve: <T = unknown>(name: string) => T }\n\nconst LEAD_HOURS = Math.max(\n 1,\n Number.parseInt(process.env.OM_PUSH_RENEWAL_GMAIL_LEAD_HOURS ?? '24', 10) || 24,\n)\n\nexport default async function handle(\n job: QueuedJob<GmailRenewWatchPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const cutoffMs = Date.now() + LEAD_HOURS * 60 * 60 * 1000\n const scope = job?.payload?.scope ?? null\n\n // Per-org cron: filter by the scope embedded in the payload. The unscoped\n // fallback is preserved so a deploy that hand-enqueues a renewal sweep\n // (e.g. an operator-triggered backfill) still works.\n const where: Record<string, unknown> = {\n providerKey: 'gmail',\n isActive: true,\n deletedAt: null,\n }\n if (scope?.tenantId) where.tenantId = scope.tenantId\n if (scope?.organizationId) where.organizationId = scope.organizationId\n const channels = await findWithDecryption(\n em,\n CommunicationChannel,\n where,\n undefined,\n scope ? { tenantId: scope.tenantId, organizationId: scope.organizationId ?? null } : undefined,\n )\n\n const containerProxy = (ctx as unknown as { container?: unknown }).container\n let renewed = 0\n let failed = 0\n for (const channel of channels) {\n const state =\n (channel.channelState as { watchExpirationMs?: number; pushStatus?: string } | null) ?? null\n if (!state || state.pushStatus !== 'active') continue\n if (typeof state.watchExpirationMs !== 'number') continue\n if (state.watchExpirationMs > cutoffMs) continue\n\n const organizationId = channel.organizationId\n if (!organizationId) {\n console.warn(\n `[gmail-renew-watch] skipping channel ${channel.id} \u2014 no organizationId on row`,\n )\n continue\n }\n\n try {\n const container = resolveContainer(containerProxy, ctx)\n const result = await pushRenew({\n container,\n scope: {\n tenantId: channel.tenantId,\n organizationId,\n userId: channel.userId ?? null,\n },\n input: { channelId: channel.id },\n })\n if (result.pushStatus === 'active') {\n renewed += 1\n await emitCommunicationChannelsEvent(\n 'communication_channels.push.renewed',\n {\n channelId: channel.id,\n providerKey: channel.providerKey,\n tenantId: channel.tenantId,\n organizationId,\n },\n { persistent: false },\n )\n } else {\n failed += 1\n }\n } catch (err) {\n failed += 1\n console.warn(\n `[gmail-renew-watch] failed to renew channel ${channel.id}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n }\n if (renewed > 0 || failed > 0) {\n console.info(`[gmail-renew-watch] renewed=${renewed} failed=${failed}`)\n }\n}\n\nfunction resolveContainer(\n containerProxy: unknown,\n ctx: { resolve: <T = unknown>(name: string) => T },\n): import('awilix').AwilixContainer {\n // The queue runtime exposes container access via `ctx.container` in\n // dedicated worker harnesses, but the bare-worker contract only provides\n // `ctx.resolve(name)`. Both shapes appear in production (test harness,\n // AUTO_SPAWN_WORKERS) so we accept either.\n if (containerProxy && typeof containerProxy === 'object' && 'resolve' in containerProxy) {\n return containerProxy as import('awilix').AwilixContainer\n }\n // Synthesize a minimal container shape from `resolve`.\n return { resolve: ctx.resolve.bind(ctx) } as unknown as import('awilix').AwilixContainer\n}\n"],
5
+ "mappings": "AAEA,SAAS,0BAA0B;AACnC,SAAS,4BAA4B;AACrC,SAAS,qCAAqC;AAC9C,SAAS,iBAAiB;AAC1B,SAAS,sCAAsC;AAwBxC,MAAM,WAAuB;AAAA,EAClC,OAAO,8BAA8B;AAAA,EACrC,IAAI;AAAA,EACJ,aAAa;AACf;AAIA,MAAM,aAAa,KAAK;AAAA,EACtB;AAAA,EACA,OAAO,SAAS,QAAQ,IAAI,oCAAoC,MAAM,EAAE,KAAK;AAC/E;AAEA,eAAO,OACL,KACA,KACe;AACf,QAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,QAAM,WAAW,KAAK,IAAI,IAAI,aAAa,KAAK,KAAK;AACrD,QAAM,QAAQ,KAAK,SAAS,SAAS;AAKrC,QAAM,QAAiC;AAAA,IACrC,aAAa;AAAA,IACb,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AACA,MAAI,OAAO,SAAU,OAAM,WAAW,MAAM;AAC5C,MAAI,OAAO,eAAgB,OAAM,iBAAiB,MAAM;AACxD,QAAM,WAAW,MAAM;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,kBAAkB,KAAK,IAAI;AAAA,EACvF;AAEA,QAAM,iBAAkB,IAA2C;AACnE,MAAI,UAAU;AACd,MAAI,SAAS;AACb,aAAW,WAAW,UAAU;AAC9B,UAAM,QACH,QAAQ,gBAA+E;AAC1F,QAAI,CAAC,SAAS,MAAM,eAAe,SAAU;AAC7C,QAAI,OAAO,MAAM,sBAAsB,SAAU;AACjD,QAAI,MAAM,oBAAoB,SAAU;AAExC,UAAM,iBAAiB,QAAQ;AAC/B,QAAI,CAAC,gBAAgB;AACnB,cAAQ;AAAA,QACN,wCAAwC,QAAQ,EAAE;AAAA,MACpD;AACA;AAAA,IACF;AAEA,QAAI;AACF,YAAM,YAAY,iBAAiB,gBAAgB,GAAG;AACtD,YAAM,SAAS,MAAM,UAAU;AAAA,QAC7B;AAAA,QACA,OAAO;AAAA,UACL,UAAU,QAAQ;AAAA,UAClB;AAAA,UACA,QAAQ,QAAQ,UAAU;AAAA,QAC5B;AAAA,QACA,OAAO,EAAE,WAAW,QAAQ,GAAG;AAAA,MACjC,CAAC;AACD,UAAI,OAAO,eAAe,UAAU;AAClC,mBAAW;AACX,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,YACE,WAAW,QAAQ;AAAA,YACnB,aAAa,QAAQ;AAAA,YACrB,UAAU,QAAQ;AAAA,YAClB;AAAA,UACF;AAAA,UACA,EAAE,YAAY,MAAM;AAAA,QACtB;AAAA,MACF,OAAO;AACL,kBAAU;AAAA,MACZ;AAAA,IACF,SAAS,KAAK;AACZ,gBAAU;AACV,cAAQ;AAAA,QACN,+CAA+C,QAAQ,EAAE,KACvD,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CACjD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,KAAK,SAAS,GAAG;AAC7B,YAAQ,KAAK,+BAA+B,OAAO,WAAW,MAAM,EAAE;AAAA,EACxE;AACF;AAEA,SAAS,iBACP,gBACA,KACkC;AAKlC,MAAI,kBAAkB,OAAO,mBAAmB,YAAY,aAAa,gBAAgB;AACvF,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,SAAS,IAAI,QAAQ,KAAK,GAAG,EAAE;AAC1C;",
6
+ "names": []
7
+ }
@@ -0,0 +1,56 @@
1
+ import {
2
+ COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID
3
+ } from "../commands/ingest-inbound-message.js";
4
+ import { COMMUNICATION_CHANNELS_QUEUES } from "../lib/queue.js";
5
+ const metadata = {
6
+ queue: COMMUNICATION_CHANNELS_QUEUES.inbound,
7
+ id: "communication_channels:inbound-processor",
8
+ concurrency: 10
9
+ };
10
+ async function handle(job, ctx) {
11
+ const { providerKey, channelId, channelType, raw, scope } = job.payload;
12
+ const adapterRegistry = ctx.resolve("channelAdapterRegistry");
13
+ const adapter = adapterRegistry?.get(providerKey);
14
+ if (!adapter) {
15
+ throw new Error(
16
+ `No ChannelAdapter registered for providerKey '${providerKey}' (worker job ${job.id}). Check that the provider package is enabled in modules.ts.`
17
+ );
18
+ }
19
+ const normalized = await adapter.normalizeInbound(raw);
20
+ if (!normalized?.externalMessageId || !normalized?.externalConversationId) {
21
+ throw new Error(
22
+ `Adapter '${providerKey}' returned a normalized message missing required fields (externalMessageId, externalConversationId) for worker job ${job.id}`
23
+ );
24
+ }
25
+ const commandBus = ctx.resolve("commandBus");
26
+ const containerProxy = { resolve: ctx.resolve.bind(ctx) };
27
+ const commandCtx = {
28
+ container: containerProxy,
29
+ auth: null,
30
+ organizationScope: null,
31
+ selectedOrganizationId: scope.organizationId ?? null,
32
+ organizationIds: scope.organizationId ? [scope.organizationId] : null
33
+ };
34
+ const input = {
35
+ channelId,
36
+ providerKey,
37
+ channelType,
38
+ scope,
39
+ message: normalized
40
+ };
41
+ const { result } = await commandBus.execute(
42
+ COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID,
43
+ {
44
+ input,
45
+ ctx: commandCtx
46
+ }
47
+ );
48
+ if (result.status === "duplicate") {
49
+ return;
50
+ }
51
+ }
52
+ export {
53
+ handle as default,
54
+ metadata
55
+ };
56
+ //# sourceMappingURL=inbound-processor.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/communication_channels/workers/inbound-processor.ts"],
4
+ "sourcesContent": ["import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { ChannelAdapterRegistry } from '../lib/registry'\nimport type { InboundMessage, NormalizedInboundMessage } from '../lib/adapter'\nimport {\n COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID,\n type IngestInboundMessageInput,\n type IngestInboundMessageResult,\n} from '../commands/ingest-inbound-message'\nimport { COMMUNICATION_CHANNELS_QUEUES } from '../lib/queue'\n\n/**\n * Job payload enqueued by the webhook route after successful signature verification.\n *\n * The route does the cheap signature-verification step synchronously and immediately\n * returns 200. Normalization (`adapter.normalizeInbound`) + DB writes run here, in\n * the worker, so a slow database or a long contact-resolution path can't time-out\n * the provider's webhook.\n */\nexport type InboundProcessorPayload = {\n providerKey: string\n channelId: string\n channelType: string\n raw: InboundMessage\n scope: {\n tenantId: string\n organizationId: string | null\n }\n}\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.inbound,\n id: 'communication_channels:inbound-processor',\n concurrency: 10,\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\n/**\n * Inbound channel message processor.\n *\n * Per SPEC-045d \u00A76:\n * - Resolves the channel adapter for the inbound providerKey.\n * - Calls `adapter.normalizeInbound(raw)` to produce a `NormalizedInboundMessage`.\n * - Hands off to the `communication_channels.message.ingest_inbound` command,\n * which idempotently creates ExternalConversation, ChannelThreadMapping,\n * ExternalMessage, MessageChannelLink, and composes the platform Message.\n *\n * Idempotency: handled by the command (dedup on `(channel_id, external_message_id)`).\n * The worker can safely retry on transient failures.\n */\nexport default async function handle(\n job: QueuedJob<InboundProcessorPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n const { providerKey, channelId, channelType, raw, scope } = job.payload\n\n const adapterRegistry = ctx.resolve<ChannelAdapterRegistry>('channelAdapterRegistry')\n const adapter = adapterRegistry?.get(providerKey)\n if (!adapter) {\n throw new Error(\n `No ChannelAdapter registered for providerKey '${providerKey}' (worker job ${job.id}). ` +\n 'Check that the provider package is enabled in modules.ts.',\n )\n }\n\n // Normalize the raw inbound payload into the hub's canonical shape.\n const normalized: NormalizedInboundMessage = await adapter.normalizeInbound(raw)\n if (!normalized?.externalMessageId || !normalized?.externalConversationId) {\n throw new Error(\n `Adapter '${providerKey}' returned a normalized message missing required fields ` +\n `(externalMessageId, externalConversationId) for worker job ${job.id}`,\n )\n }\n\n const commandBus = ctx.resolve<CommandBus>('commandBus')\n\n // The worker's JobContext exposes `.resolve(name)` \u2014 duck-type-compatible with the\n // shape CommandBus uses (`container.resolve(...)`). We cast to AwilixContainer because\n // CommandRuntimeContext requires that exact type; in practice CommandBus only touches\n // `.resolve`, mirroring the pattern in `inbox_ops/lib/messagesIntegration.ts:148`.\n const containerProxy = { resolve: ctx.resolve.bind(ctx) }\n const commandCtx = {\n container: containerProxy as never,\n auth: null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId ?? null,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n }\n\n const input: IngestInboundMessageInput = {\n channelId,\n providerKey,\n channelType,\n scope,\n message: normalized,\n }\n\n const { result } = await commandBus.execute<IngestInboundMessageInput, IngestInboundMessageResult>(\n COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID,\n {\n input,\n ctx: commandCtx as never,\n },\n )\n\n if (result.status === 'duplicate') {\n // Idempotent skip \u2014 provider sent the same webhook twice (common with at-least-once\n // delivery semantics). Nothing to do; we already ingested this message earlier.\n return\n }\n}\n"],
5
+ "mappings": "AAIA;AAAA,EACE;AAAA,OAGK;AACP,SAAS,qCAAqC;AAqBvC,MAAM,WAAuB;AAAA,EAClC,OAAO,8BAA8B;AAAA,EACrC,IAAI;AAAA,EACJ,aAAa;AACf;AAmBA,eAAO,OACL,KACA,KACe;AACf,QAAM,EAAE,aAAa,WAAW,aAAa,KAAK,MAAM,IAAI,IAAI;AAEhE,QAAM,kBAAkB,IAAI,QAAgC,wBAAwB;AACpF,QAAM,UAAU,iBAAiB,IAAI,WAAW;AAChD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR,iDAAiD,WAAW,iBAAiB,IAAI,EAAE;AAAA,IAErF;AAAA,EACF;AAGA,QAAM,aAAuC,MAAM,QAAQ,iBAAiB,GAAG;AAC/E,MAAI,CAAC,YAAY,qBAAqB,CAAC,YAAY,wBAAwB;AACzE,UAAM,IAAI;AAAA,MACR,YAAY,WAAW,sHACyC,IAAI,EAAE;AAAA,IACxE;AAAA,EACF;AAEA,QAAM,aAAa,IAAI,QAAoB,YAAY;AAMvD,QAAM,iBAAiB,EAAE,SAAS,IAAI,QAAQ,KAAK,GAAG,EAAE;AACxD,QAAM,aAAa;AAAA,IACjB,WAAW;AAAA,IACX,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,wBAAwB,MAAM,kBAAkB;AAAA,IAChD,iBAAiB,MAAM,iBAAiB,CAAC,MAAM,cAAc,IAAI;AAAA,EACnE;AAEA,QAAM,QAAmC;AAAA,IACvC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EACX;AAEA,QAAM,EAAE,OAAO,IAAI,MAAM,WAAW;AAAA,IAClC;AAAA,IACA;AAAA,MACE;AAAA,MACA,KAAK;AAAA,IACP;AAAA,EACF;AAEA,MAAI,OAAO,WAAW,aAAa;AAGjC;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,85 @@
1
+ import {
2
+ COMMUNICATION_CHANNELS_DELIVER_OUTBOUND_COMMAND_ID
3
+ } from "../commands/deliver-outbound-message.js";
4
+ import { computeBackoffMs } from "../lib/error-classification.js";
5
+ import { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from "../lib/queue.js";
6
+ const OUTBOUND_DELIVERY_MAX_ATTEMPTS = 3;
7
+ const metadata = {
8
+ queue: COMMUNICATION_CHANNELS_QUEUES.outbound,
9
+ id: "communication_channels:outbound-delivery",
10
+ concurrency: 10
11
+ };
12
+ async function handle(job, ctx) {
13
+ const { messageId, scope, attempt = 1, forceCredentialRefresh } = job.payload;
14
+ const commandBus = ctx.resolve("commandBus");
15
+ const containerProxy = { resolve: ctx.resolve.bind(ctx) };
16
+ const commandCtx = {
17
+ container: containerProxy,
18
+ auth: null,
19
+ organizationScope: null,
20
+ selectedOrganizationId: scope.organizationId ?? null,
21
+ organizationIds: scope.organizationId ? [scope.organizationId] : null
22
+ };
23
+ let outcome;
24
+ try {
25
+ const { result } = await commandBus.execute(
26
+ COMMUNICATION_CHANNELS_DELIVER_OUTBOUND_COMMAND_ID,
27
+ {
28
+ input: { messageId, scope, forceCredentialRefresh },
29
+ ctx: commandCtx
30
+ }
31
+ );
32
+ outcome = result;
33
+ } catch (err) {
34
+ const errorMessage = err instanceof Error ? err.message : String(err);
35
+ console.warn(
36
+ `[communication_channels:outbound-delivery] command threw on attempt ${attempt} for message ${messageId}: ${errorMessage}`
37
+ );
38
+ if (attempt < OUTBOUND_DELIVERY_MAX_ATTEMPTS) {
39
+ await reenqueue(job.payload, attempt);
40
+ return;
41
+ }
42
+ throw err;
43
+ }
44
+ switch (outcome.status) {
45
+ case "delivered":
46
+ case "already_delivered":
47
+ case "no_channel_link":
48
+ return;
49
+ case "failed": {
50
+ if (outcome.requiresReauth && !forceCredentialRefresh && attempt < OUTBOUND_DELIVERY_MAX_ATTEMPTS) {
51
+ console.warn(
52
+ `[communication_channels:outbound-delivery] reauth failure on attempt ${attempt} for message ${messageId} (${outcome.providerKey}): ${outcome.error}. Retrying once with a forced credential refresh.`
53
+ );
54
+ await reenqueue({ ...job.payload, forceCredentialRefresh: true }, attempt);
55
+ return;
56
+ }
57
+ if (outcome.transient && attempt < OUTBOUND_DELIVERY_MAX_ATTEMPTS) {
58
+ console.warn(
59
+ `[communication_channels:outbound-delivery] transient failure on attempt ${attempt} for message ${messageId} (${outcome.providerKey}): ${outcome.error}. Re-enqueueing.`
60
+ );
61
+ await reenqueue(job.payload, attempt);
62
+ return;
63
+ }
64
+ console.error(
65
+ `[communication_channels:outbound-delivery] giving up on message ${messageId} after attempt ${attempt} (${outcome.providerKey}): ${outcome.error}`
66
+ );
67
+ return;
68
+ }
69
+ }
70
+ }
71
+ async function reenqueue(payload, attempt) {
72
+ const next = {
73
+ ...payload,
74
+ attempt: attempt + 1
75
+ };
76
+ const delayMs = computeBackoffMs(attempt);
77
+ const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.outbound);
78
+ await queue.enqueue(next, { delayMs });
79
+ }
80
+ export {
81
+ OUTBOUND_DELIVERY_MAX_ATTEMPTS,
82
+ handle as default,
83
+ metadata
84
+ };
85
+ //# sourceMappingURL=outbound-delivery.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/communication_channels/workers/outbound-delivery.ts"],
4
+ "sourcesContent": ["import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport {\n COMMUNICATION_CHANNELS_DELIVER_OUTBOUND_COMMAND_ID,\n type DeliverOutboundMessageInput,\n type DeliverOutboundMessageResult,\n} from '../commands/deliver-outbound-message'\nimport { computeBackoffMs } from '../lib/error-classification'\nimport { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from '../lib/queue'\n\n/**\n * Worker payload \u2014 the subscriber enqueues `{ messageId, scope, attempt? }`.\n *\n * `attempt` starts at 1 on first enqueue. The worker increments it when\n * re-enqueueing for retry, until `MAX_ATTEMPTS` is reached.\n */\nexport type OutboundDeliveryPayload = {\n messageId: string\n scope: {\n tenantId: string\n organizationId: string | null\n }\n /** Attempt number, 1-based. Set by the subscriber for the first try; the worker increments on retry. */\n attempt?: number\n /** Force credential refresh on this attempt \u2014 used after a 401 from the provider. */\n forceCredentialRefresh?: boolean\n}\n\nexport const OUTBOUND_DELIVERY_MAX_ATTEMPTS = 3\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.outbound,\n id: 'communication_channels:outbound-delivery',\n concurrency: 10,\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\n/**\n * Outbound delivery worker.\n *\n * Dispatches the `deliver_outbound_message` command which performs the actual\n * send. The command returns a classified result:\n * - `delivered` / `already_delivered` / `no_channel_link` \u2192 success, return.\n * - `failed` with `transient: true` \u2192 re-enqueue with exponential backoff,\n * incremented attempt, up to `OUTBOUND_DELIVERY_MAX_ATTEMPTS`.\n * - `failed` with `transient: false` \u2192 permanent failure, no retry.\n *\n * The command already wrote the failure record + emitted `.delivery_failed`,\n * so the worker just decides whether to schedule another attempt.\n *\n * We DO NOT throw on a recorded delivery-failure outcome \u2014 that would let the\n * queue apply its own retry policy on top of ours, double-retrying. Explicit\n * re-enqueue with delayMs is the portable, controllable pattern. The one\n * exception is an *unexpected* exception from the command itself (e.g. a DB blip\n * that stopped it from recording anything): we re-enqueue up to our max and then\n * rethrow so the infrastructure failure surfaces to the queue's dead-letter\n * instead of vanishing. The command's idempotency prevents a double-send.\n */\nexport default async function handle(\n job: QueuedJob<OutboundDeliveryPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n const { messageId, scope, attempt = 1, forceCredentialRefresh } = job.payload\n\n const commandBus = ctx.resolve<CommandBus>('commandBus')\n const containerProxy = { resolve: ctx.resolve.bind(ctx) }\n const commandCtx = {\n container: containerProxy as never,\n auth: null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId ?? null,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n }\n\n let outcome: DeliverOutboundMessageResult\n try {\n const { result } = await commandBus.execute<\n DeliverOutboundMessageInput,\n DeliverOutboundMessageResult\n >(\n COMMUNICATION_CHANNELS_DELIVER_OUTBOUND_COMMAND_ID,\n {\n input: { messageId, scope, forceCredentialRefresh },\n ctx: commandCtx as never,\n },\n )\n outcome = result\n } catch (err) {\n // Unexpected error inside the command itself (e.g. DB blip). Re-enqueue\n // up to MAX_ATTEMPTS so we don't lose deliveries due to infrastructure flakes.\n const errorMessage = err instanceof Error ? err.message : String(err)\n console.warn(\n `[communication_channels:outbound-delivery] command threw on attempt ${attempt} for message ${messageId}: ${errorMessage}`,\n )\n if (attempt < OUTBOUND_DELIVERY_MAX_ATTEMPTS) {\n await reenqueue(job.payload, attempt)\n return\n }\n // Attempts exhausted on an unexpected command exception \u2014 rethrow so the\n // failure reaches the queue's dead-letter / observability (see header note).\n throw err\n }\n\n switch (outcome.status) {\n case 'delivered':\n case 'already_delivered':\n case 'no_channel_link':\n return\n case 'failed': {\n // Reauth (401 / invalid_grant): the command already flipped the channel to\n // `requires_reauth`. Give the credentials exactly one forced-refresh retry\n // before giving up \u2014 a near-expiry access token whose proactive refresh\n // was skipped can still recover here. If we already forced a refresh and\n // still got a reauth error, the token is unrecoverable: stop (the operator\n // must reconnect).\n if (\n outcome.requiresReauth &&\n !forceCredentialRefresh &&\n attempt < OUTBOUND_DELIVERY_MAX_ATTEMPTS\n ) {\n console.warn(\n `[communication_channels:outbound-delivery] reauth failure on attempt ${attempt} for message ${messageId} (${outcome.providerKey}): ${outcome.error}. Retrying once with a forced credential refresh.`,\n )\n await reenqueue({ ...job.payload, forceCredentialRefresh: true }, attempt)\n return\n }\n if (outcome.transient && attempt < OUTBOUND_DELIVERY_MAX_ATTEMPTS) {\n console.warn(\n `[communication_channels:outbound-delivery] transient failure on attempt ${attempt} for message ${messageId} (${outcome.providerKey}): ${outcome.error}. Re-enqueueing.`,\n )\n await reenqueue(job.payload, attempt)\n return\n }\n // Permanent or attempts exhausted \u2014 `.delivery_failed` was already emitted\n // by the command, so we stop here.\n console.error(\n `[communication_channels:outbound-delivery] giving up on message ${messageId} after attempt ${attempt} (${outcome.providerKey}): ${outcome.error}`,\n )\n return\n }\n }\n}\n\nasync function reenqueue(payload: OutboundDeliveryPayload, attempt: number): Promise<void> {\n const next: OutboundDeliveryPayload = {\n ...payload,\n attempt: attempt + 1,\n }\n const delayMs = computeBackoffMs(attempt)\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.outbound)\n await queue.enqueue(next as unknown as Record<string, unknown>, { delayMs })\n}\n"],
5
+ "mappings": "AAEA;AAAA,EACE;AAAA,OAGK;AACP,SAAS,wBAAwB;AACjC,SAAS,+BAA+B,qCAAqC;AAoBtE,MAAM,iCAAiC;AAEvC,MAAM,WAAuB;AAAA,EAClC,OAAO,8BAA8B;AAAA,EACrC,IAAI;AAAA,EACJ,aAAa;AACf;AA2BA,eAAO,OACL,KACA,KACe;AACf,QAAM,EAAE,WAAW,OAAO,UAAU,GAAG,uBAAuB,IAAI,IAAI;AAEtE,QAAM,aAAa,IAAI,QAAoB,YAAY;AACvD,QAAM,iBAAiB,EAAE,SAAS,IAAI,QAAQ,KAAK,GAAG,EAAE;AACxD,QAAM,aAAa;AAAA,IACjB,WAAW;AAAA,IACX,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,wBAAwB,MAAM,kBAAkB;AAAA,IAChD,iBAAiB,MAAM,iBAAiB,CAAC,MAAM,cAAc,IAAI;AAAA,EACnE;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,WAAW;AAAA,MAIlC;AAAA,MACA;AAAA,QACE,OAAO,EAAE,WAAW,OAAO,uBAAuB;AAAA,QAClD,KAAK;AAAA,MACP;AAAA,IACF;AACA,cAAU;AAAA,EACZ,SAAS,KAAK;AAGZ,UAAM,eAAe,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACpE,YAAQ;AAAA,MACN,uEAAuE,OAAO,gBAAgB,SAAS,KAAK,YAAY;AAAA,IAC1H;AACA,QAAI,UAAU,gCAAgC;AAC5C,YAAM,UAAU,IAAI,SAAS,OAAO;AACpC;AAAA,IACF;AAGA,UAAM;AAAA,EACR;AAEA,UAAQ,QAAQ,QAAQ;AAAA,IACtB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH;AAAA,IACF,KAAK,UAAU;AAOb,UACE,QAAQ,kBACR,CAAC,0BACD,UAAU,gCACV;AACA,gBAAQ;AAAA,UACN,wEAAwE,OAAO,gBAAgB,SAAS,KAAK,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAAA,QACrJ;AACA,cAAM,UAAU,EAAE,GAAG,IAAI,SAAS,wBAAwB,KAAK,GAAG,OAAO;AACzE;AAAA,MACF;AACA,UAAI,QAAQ,aAAa,UAAU,gCAAgC;AACjE,gBAAQ;AAAA,UACN,2EAA2E,OAAO,gBAAgB,SAAS,KAAK,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAAA,QACxJ;AACA,cAAM,UAAU,IAAI,SAAS,OAAO;AACpC;AAAA,MACF;AAGA,cAAQ;AAAA,QACN,mEAAmE,SAAS,kBAAkB,OAAO,KAAK,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAAA,MAClJ;AACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,UAAU,SAAkC,SAAgC;AACzF,QAAM,OAAgC;AAAA,IACpC,GAAG;AAAA,IACH,SAAS,UAAU;AAAA,EACrB;AACA,QAAM,UAAU,iBAAiB,OAAO;AACxC,QAAM,QAAQ,8BAA8B,8BAA8B,QAAQ;AAClF,QAAM,MAAM,QAAQ,MAA4C,EAAE,QAAQ,CAAC;AAC7E;",
6
+ "names": []
7
+ }