@open-mercato/core 0.6.6-develop.6465.1.019f0fb26f → 0.6.6-develop.6471.1.9ab5bdd79a

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 (1213) hide show
  1. package/dist/bootstrap.js +13 -11
  2. package/dist/bootstrap.js.map +2 -2
  3. package/dist/helpers/integration/queue-runner.js +4 -2
  4. package/dist/helpers/integration/queue-runner.js.map +2 -2
  5. package/dist/modules/api_keys/services/apiKeyService.js +5 -3
  6. package/dist/modules/api_keys/services/apiKeyService.js.map +2 -2
  7. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +4 -2
  8. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
  9. package/dist/modules/attachments/api/library/[id]/route.js +3 -1
  10. package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
  11. package/dist/modules/attachments/api/route.js +8 -6
  12. package/dist/modules/attachments/api/route.js.map +2 -2
  13. package/dist/modules/attachments/components/AttachmentPartitionSettings.js +5 -3
  14. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
  15. package/dist/modules/attachments/lib/assignmentDetails.js +3 -1
  16. package/dist/modules/attachments/lib/assignmentDetails.js.map +2 -2
  17. package/dist/modules/attachments/lib/drivers/driverFactory.js +3 -1
  18. package/dist/modules/attachments/lib/drivers/driverFactory.js.map +2 -2
  19. package/dist/modules/attachments/lib/ocrQueue.js +10 -11
  20. package/dist/modules/attachments/lib/ocrQueue.js.map +2 -2
  21. package/dist/modules/attachments/lib/ocrService.js +6 -4
  22. package/dist/modules/attachments/lib/ocrService.js.map +2 -2
  23. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +3 -1
  24. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
  25. package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js +3 -1
  26. package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js.map +2 -2
  27. package/dist/modules/audit_logs/backend/audit-logs/page.js +3 -1
  28. package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
  29. package/dist/modules/audit_logs/components/AuditLogsActions.js +4 -2
  30. package/dist/modules/audit_logs/components/AuditLogsActions.js.map +2 -2
  31. package/dist/modules/audit_logs/services/accessLogService.js +4 -2
  32. package/dist/modules/audit_logs/services/accessLogService.js.map +2 -2
  33. package/dist/modules/audit_logs/services/actionLogService.js +7 -5
  34. package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
  35. package/dist/modules/auth/api/profile/route.js +4 -2
  36. package/dist/modules/auth/api/profile/route.js.map +2 -2
  37. package/dist/modules/auth/api/reset/confirm.js +3 -1
  38. package/dist/modules/auth/api/reset/confirm.js.map +2 -2
  39. package/dist/modules/auth/api/reset.js +4 -2
  40. package/dist/modules/auth/api/reset.js.map +2 -2
  41. package/dist/modules/auth/api/roles/route.js +3 -1
  42. package/dist/modules/auth/api/roles/route.js.map +2 -2
  43. package/dist/modules/auth/api/sidebar/variants/route.js +3 -1
  44. package/dist/modules/auth/api/sidebar/variants/route.js.map +2 -2
  45. package/dist/modules/auth/api/users/resend-invite/route.js +4 -2
  46. package/dist/modules/auth/api/users/resend-invite/route.js.map +2 -2
  47. package/dist/modules/auth/api/users/route.js +3 -1
  48. package/dist/modules/auth/api/users/route.js.map +2 -2
  49. package/dist/modules/auth/backend/auth/profile/page.js +3 -1
  50. package/dist/modules/auth/backend/auth/profile/page.js.map +2 -2
  51. package/dist/modules/auth/backend/profile/change-password/page.js +3 -1
  52. package/dist/modules/auth/backend/profile/change-password/page.js.map +2 -2
  53. package/dist/modules/auth/backend/users/[id]/edit/page.js +5 -3
  54. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  55. package/dist/modules/auth/backend/users/create/page.js +4 -2
  56. package/dist/modules/auth/backend/users/create/page.js.map +2 -2
  57. package/dist/modules/auth/cli.js +10 -0
  58. package/dist/modules/auth/cli.js.map +2 -2
  59. package/dist/modules/auth/commands/users.js +4 -2
  60. package/dist/modules/auth/commands/users.js.map +2 -2
  61. package/dist/modules/auth/lib/consentIntegrity.js +3 -3
  62. package/dist/modules/auth/lib/consentIntegrity.js.map +2 -2
  63. package/dist/modules/auth/lib/setup-app.js +9 -10
  64. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  65. package/dist/modules/auth/lib/tenantAccess.js +3 -1
  66. package/dist/modules/auth/lib/tenantAccess.js.map +2 -2
  67. package/dist/modules/auth/lib/tokenHash.js +3 -3
  68. package/dist/modules/auth/lib/tokenHash.js.map +2 -2
  69. package/dist/modules/business_rules/api/rules/route.js +4 -2
  70. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  71. package/dist/modules/business_rules/lib/expression-evaluator.js +7 -5
  72. package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
  73. package/dist/modules/business_rules/lib/rule-engine.js +5 -3
  74. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  75. package/dist/modules/business_rules/lib/rule-evaluator.js +11 -12
  76. package/dist/modules/business_rules/lib/rule-evaluator.js.map +2 -2
  77. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js +3 -1
  78. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js.map +2 -2
  79. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +3 -1
  80. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +2 -2
  81. package/dist/modules/catalog/ai-agents-context.js +4 -5
  82. package/dist/modules/catalog/ai-agents-context.js.map +2 -2
  83. package/dist/modules/catalog/ai-tools/_shared.js +3 -1
  84. package/dist/modules/catalog/ai-tools/_shared.js.map +2 -2
  85. package/dist/modules/catalog/ai-tools/authoring-pack.js +3 -1
  86. package/dist/modules/catalog/ai-tools/authoring-pack.js.map +2 -2
  87. package/dist/modules/catalog/ai-tools/merchandising-pack.js +3 -1
  88. package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +2 -2
  89. package/dist/modules/catalog/api/dictionaries/[key]/route.js +3 -1
  90. package/dist/modules/catalog/api/dictionaries/[key]/route.js.map +2 -2
  91. package/dist/modules/catalog/api/prices/route.js +3 -1
  92. package/dist/modules/catalog/api/prices/route.js.map +2 -2
  93. package/dist/modules/catalog/api/products/route.js +5 -3
  94. package/dist/modules/catalog/api/products/route.js.map +2 -2
  95. package/dist/modules/catalog/api/settings/route.js +4 -2
  96. package/dist/modules/catalog/api/settings/route.js.map +2 -2
  97. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +14 -12
  98. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  99. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +8 -6
  100. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  101. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js +5 -3
  102. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js.map +2 -2
  103. package/dist/modules/catalog/backend/catalog/products/create/page.js +5 -6
  104. package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
  105. package/dist/modules/catalog/backend/catalog/products/optionSchemaClient.js +3 -1
  106. package/dist/modules/catalog/backend/catalog/products/optionSchemaClient.js.map +2 -2
  107. package/dist/modules/catalog/components/PriceKindSettings.js +5 -3
  108. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  109. package/dist/modules/catalog/components/UnitPriceDisplaySettings.js +4 -2
  110. package/dist/modules/catalog/components/UnitPriceDisplaySettings.js.map +2 -2
  111. package/dist/modules/catalog/inbox-actions.js +4 -2
  112. package/dist/modules/catalog/inbox-actions.js.map +2 -2
  113. package/dist/modules/catalog/seed/examples.js +4 -5
  114. package/dist/modules/catalog/seed/examples.js.map +2 -2
  115. package/dist/modules/catalog/subscribers/low-stock-notification.js +3 -1
  116. package/dist/modules/catalog/subscribers/low-stock-notification.js.map +2 -2
  117. package/dist/modules/catalog/widgets/injection/product-seo/state.js +3 -1
  118. package/dist/modules/catalog/widgets/injection/product-seo/state.js.map +2 -2
  119. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +12 -11
  120. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +2 -2
  121. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +3 -1
  122. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +2 -2
  123. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js +3 -1
  124. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js.map +2 -2
  125. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +5 -3
  126. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +2 -2
  127. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +6 -6
  128. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +2 -2
  129. package/dist/modules/communication_channels/commands/connect-credential-channel.js +4 -6
  130. package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +2 -2
  131. package/dist/modules/communication_channels/commands/delete-channel.js +3 -3
  132. package/dist/modules/communication_channels/commands/delete-channel.js.map +2 -2
  133. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +4 -5
  134. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
  135. package/dist/modules/communication_channels/commands/disconnect-channel.js +3 -3
  136. package/dist/modules/communication_channels/commands/disconnect-channel.js.map +2 -2
  137. package/dist/modules/communication_channels/commands/ingest-inbound-message.js +5 -12
  138. package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
  139. package/dist/modules/communication_channels/commands/push-unregister.js +3 -3
  140. package/dist/modules/communication_channels/commands/push-unregister.js.map +2 -2
  141. package/dist/modules/communication_channels/commands/reassign-conversation.js +5 -3
  142. package/dist/modules/communication_channels/commands/reassign-conversation.js.map +2 -2
  143. package/dist/modules/communication_channels/lib/dead-letter.js +3 -3
  144. package/dist/modules/communication_channels/lib/dead-letter.js.map +2 -2
  145. package/dist/modules/communication_channels/lib/email-mime.js +4 -6
  146. package/dist/modules/communication_channels/lib/email-mime.js.map +2 -2
  147. package/dist/modules/communication_channels/lib/oauth-client-config.js +5 -3
  148. package/dist/modules/communication_channels/lib/oauth-client-config.js.map +2 -2
  149. package/dist/modules/communication_channels/lib/send-as-user.js +3 -1
  150. package/dist/modules/communication_channels/lib/send-as-user.js.map +2 -2
  151. package/dist/modules/communication_channels/lib/thread-token.js +4 -2
  152. package/dist/modules/communication_channels/lib/thread-token.js.map +2 -2
  153. package/dist/modules/communication_channels/setup.js +3 -4
  154. package/dist/modules/communication_channels/setup.js.map +2 -2
  155. package/dist/modules/communication_channels/workers/channel-import-history.js +4 -6
  156. package/dist/modules/communication_channels/workers/channel-import-history.js.map +2 -2
  157. package/dist/modules/communication_channels/workers/gmail-history-sync.js +8 -16
  158. package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +2 -2
  159. package/dist/modules/communication_channels/workers/gmail-renew-watch.js +5 -7
  160. package/dist/modules/communication_channels/workers/gmail-renew-watch.js.map +2 -2
  161. package/dist/modules/communication_channels/workers/outbound-delivery.js +6 -12
  162. package/dist/modules/communication_channels/workers/outbound-delivery.js.map +2 -2
  163. package/dist/modules/communication_channels/workers/poll-channel.js +7 -15
  164. package/dist/modules/communication_channels/workers/poll-channel.js.map +2 -2
  165. package/dist/modules/communication_channels/workers/poll-tick.js +5 -6
  166. package/dist/modules/communication_channels/workers/poll-tick.js.map +2 -2
  167. package/dist/modules/communication_channels/workers/reaction-processor.js +5 -9
  168. package/dist/modules/communication_channels/workers/reaction-processor.js.map +2 -2
  169. package/dist/modules/configs/api/cache/route.js +4 -2
  170. package/dist/modules/configs/api/cache/route.js.map +2 -2
  171. package/dist/modules/configs/api/system-status/route.js +4 -2
  172. package/dist/modules/configs/api/system-status/route.js.map +2 -2
  173. package/dist/modules/configs/api/upgrade-actions/route.js +4 -2
  174. package/dist/modules/configs/api/upgrade-actions/route.js.map +2 -2
  175. package/dist/modules/configs/components/ModuleTelemetryPanel.js +3 -1
  176. package/dist/modules/configs/components/ModuleTelemetryPanel.js.map +2 -2
  177. package/dist/modules/configs/lib/touchGeneratedBarrels.js +2 -1
  178. package/dist/modules/configs/lib/touchGeneratedBarrels.js.map +2 -2
  179. package/dist/modules/configs/services/upgradeActionsService.js +6 -3
  180. package/dist/modules/configs/services/upgradeActionsService.js.map +2 -2
  181. package/dist/modules/currencies/components/CurrencyFetchingConfig.js +3 -1
  182. package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
  183. package/dist/modules/currencies/lib/exchangeRateFormConfig.js +3 -1
  184. package/dist/modules/currencies/lib/exchangeRateFormConfig.js.map +2 -2
  185. package/dist/modules/currencies/services/providers/nbp.js +6 -4
  186. package/dist/modules/currencies/services/providers/nbp.js.map +2 -2
  187. package/dist/modules/currencies/services/providers/raiffeisen.js +7 -7
  188. package/dist/modules/currencies/services/providers/raiffeisen.js.map +2 -2
  189. package/dist/modules/customer_accounts/api/domain-resolve/all.js +6 -5
  190. package/dist/modules/customer_accounts/api/domain-resolve/all.js.map +2 -2
  191. package/dist/modules/customer_accounts/api/signup.js +4 -2
  192. package/dist/modules/customer_accounts/api/signup.js.map +2 -2
  193. package/dist/modules/customer_accounts/lib/customerAuthServer.js +3 -1
  194. package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +2 -2
  195. package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js +3 -1
  196. package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js.map +2 -2
  197. package/dist/modules/customer_accounts/subscribers/autoLinkCrmReverse.js +3 -1
  198. package/dist/modules/customer_accounts/subscribers/autoLinkCrmReverse.js.map +2 -2
  199. package/dist/modules/customer_accounts/subscribers/unlinkOnCompanyDelete.js +3 -1
  200. package/dist/modules/customer_accounts/subscribers/unlinkOnCompanyDelete.js.map +2 -2
  201. package/dist/modules/customer_accounts/subscribers/unlinkOnPersonDelete.js +3 -1
  202. package/dist/modules/customer_accounts/subscribers/unlinkOnPersonDelete.js.map +2 -2
  203. package/dist/modules/customers/ai-agents-context.js +4 -5
  204. package/dist/modules/customers/ai-agents-context.js.map +2 -2
  205. package/dist/modules/customers/api/activities/route.js +6 -4
  206. package/dist/modules/customers/api/activities/route.js.map +2 -2
  207. package/dist/modules/customers/api/comments/route.js +3 -1
  208. package/dist/modules/customers/api/comments/route.js.map +2 -2
  209. package/dist/modules/customers/api/companies/[id]/people/route.js +3 -1
  210. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  211. package/dist/modules/customers/api/companies/[id]/route.js +5 -3
  212. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  213. package/dist/modules/customers/api/companies/route.js +5 -3
  214. package/dist/modules/customers/api/companies/route.js.map +2 -2
  215. package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js +3 -1
  216. package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js.map +2 -2
  217. package/dist/modules/customers/api/dashboard/widgets/new-customers/route.js +3 -1
  218. package/dist/modules/customers/api/dashboard/widgets/new-customers/route.js.map +2 -2
  219. package/dist/modules/customers/api/dashboard/widgets/new-deals/route.js +3 -1
  220. package/dist/modules/customers/api/dashboard/widgets/new-deals/route.js.map +2 -2
  221. package/dist/modules/customers/api/dashboard/widgets/next-interactions/route.js +3 -1
  222. package/dist/modules/customers/api/dashboard/widgets/next-interactions/route.js.map +2 -2
  223. package/dist/modules/customers/api/deals/[id]/companies/route.js +3 -1
  224. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  225. package/dist/modules/customers/api/deals/[id]/people/route.js +3 -1
  226. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  227. package/dist/modules/customers/api/deals/aggregate/route.js +3 -1
  228. package/dist/modules/customers/api/deals/aggregate/route.js.map +2 -2
  229. package/dist/modules/customers/api/deals/bulk-update-owner/route.js +7 -5
  230. package/dist/modules/customers/api/deals/bulk-update-owner/route.js.map +2 -2
  231. package/dist/modules/customers/api/deals/bulk-update-stage/route.js +7 -5
  232. package/dist/modules/customers/api/deals/bulk-update-stage/route.js.map +2 -2
  233. package/dist/modules/customers/api/deals/route.js +3 -1
  234. package/dist/modules/customers/api/deals/route.js.map +2 -2
  235. package/dist/modules/customers/api/deals/summary/route.js +3 -1
  236. package/dist/modules/customers/api/deals/summary/route.js.map +2 -2
  237. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +4 -2
  238. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  239. package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -3
  240. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  241. package/dist/modules/customers/api/dictionaries/cache.js +3 -1
  242. package/dist/modules/customers/api/dictionaries/cache.js.map +2 -2
  243. package/dist/modules/customers/api/dictionaries/context.js +3 -1
  244. package/dist/modules/customers/api/dictionaries/context.js.map +2 -2
  245. package/dist/modules/customers/api/dictionaries/currency/route.js +3 -1
  246. package/dist/modules/customers/api/dictionaries/currency/route.js.map +2 -2
  247. package/dist/modules/customers/api/dictionaries/kind-settings/route.js +4 -2
  248. package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
  249. package/dist/modules/customers/api/entity-roles-factory.js +7 -5
  250. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  251. package/dist/modules/customers/api/interactions/cancel/route.js +3 -1
  252. package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
  253. package/dist/modules/customers/api/interactions/complete/route.js +3 -1
  254. package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
  255. package/dist/modules/customers/api/interactions/conflicts/route.js +3 -1
  256. package/dist/modules/customers/api/interactions/conflicts/route.js.map +2 -2
  257. package/dist/modules/customers/api/interactions/counts/route.js +3 -1
  258. package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
  259. package/dist/modules/customers/api/interactions/route.js +7 -4
  260. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  261. package/dist/modules/customers/api/interactions/tasks/route.js +3 -1
  262. package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
  263. package/dist/modules/customers/api/labels/assign/route.js +3 -1
  264. package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
  265. package/dist/modules/customers/api/labels/route.js +6 -3
  266. package/dist/modules/customers/api/labels/route.js.map +2 -2
  267. package/dist/modules/customers/api/labels/unassign/route.js +3 -1
  268. package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
  269. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +3 -1
  270. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  271. package/dist/modules/customers/api/people/[id]/route.js +5 -3
  272. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  273. package/dist/modules/customers/api/people/route.js +5 -3
  274. package/dist/modules/customers/api/people/route.js.map +2 -2
  275. package/dist/modules/customers/api/pipeline-stages/reorder/route.js +3 -1
  276. package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
  277. package/dist/modules/customers/api/pipeline-stages/route.js +6 -4
  278. package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
  279. package/dist/modules/customers/api/pipelines/route.js +6 -4
  280. package/dist/modules/customers/api/pipelines/route.js.map +2 -2
  281. package/dist/modules/customers/api/settings/address-format/route.js +4 -2
  282. package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
  283. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +4 -2
  284. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
  285. package/dist/modules/customers/api/settings/stuck-threshold/route.js +4 -2
  286. package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
  287. package/dist/modules/customers/api/tags/assign/route.js +3 -1
  288. package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
  289. package/dist/modules/customers/api/tags/unassign/route.js +3 -1
  290. package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
  291. package/dist/modules/customers/api/todos/route.js +6 -4
  292. package/dist/modules/customers/api/todos/route.js.map +2 -2
  293. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +5 -3
  294. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  295. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +3 -1
  296. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
  297. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js +3 -1
  298. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js.map +2 -2
  299. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.js +4 -2
  300. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.js.map +2 -2
  301. package/dist/modules/customers/backend/customers/deals/page.js +4 -2
  302. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  303. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +5 -3
  304. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  305. package/dist/modules/customers/commands/deals.js +3 -1
  306. package/dist/modules/customers/commands/deals.js.map +2 -2
  307. package/dist/modules/customers/commands/dictionaries.js +3 -1
  308. package/dist/modules/customers/commands/dictionaries.js.map +2 -2
  309. package/dist/modules/customers/commands/interactions.js +4 -2
  310. package/dist/modules/customers/commands/interactions.js.map +2 -2
  311. package/dist/modules/customers/commands/shared.js +4 -2
  312. package/dist/modules/customers/commands/shared.js.map +2 -2
  313. package/dist/modules/customers/commands/todos.js +3 -1
  314. package/dist/modules/customers/commands/todos.js.map +2 -2
  315. package/dist/modules/customers/components/DictionarySettings.js +5 -3
  316. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  317. package/dist/modules/customers/components/DictionarySortSettings.js +3 -1
  318. package/dist/modules/customers/components/DictionarySortSettings.js.map +2 -2
  319. package/dist/modules/customers/components/detail/ActivitiesCard.js +3 -1
  320. package/dist/modules/customers/components/detail/ActivitiesCard.js.map +2 -2
  321. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js +3 -1
  322. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js.map +2 -2
  323. package/dist/modules/customers/components/detail/ActivitiesSection.js +6 -4
  324. package/dist/modules/customers/components/detail/ActivitiesSection.js.map +2 -2
  325. package/dist/modules/customers/components/detail/ActivityCard.js +3 -1
  326. package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
  327. package/dist/modules/customers/components/detail/ChangelogTab.js +3 -1
  328. package/dist/modules/customers/components/detail/ChangelogTab.js.map +2 -2
  329. package/dist/modules/customers/components/detail/CompanyCard.js +3 -1
  330. package/dist/modules/customers/components/detail/CompanyCard.js.map +2 -2
  331. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +3 -1
  332. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
  333. package/dist/modules/customers/components/detail/EntityTagsDialog.js +3 -1
  334. package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
  335. package/dist/modules/customers/components/detail/InlineActivityComposer.js +3 -1
  336. package/dist/modules/customers/components/detail/InlineActivityComposer.js.map +2 -2
  337. package/dist/modules/customers/components/detail/ManageTagsDialog.js +3 -1
  338. package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +2 -2
  339. package/dist/modules/customers/components/detail/RoleAssignmentRow.js +5 -3
  340. package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
  341. package/dist/modules/customers/components/detail/RolesSection.js +4 -2
  342. package/dist/modules/customers/components/detail/RolesSection.js.map +2 -2
  343. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js +5 -3
  344. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js.map +2 -2
  345. package/dist/modules/customers/components/linking/LinkEntityDialog.js +4 -2
  346. package/dist/modules/customers/components/linking/LinkEntityDialog.js.map +2 -2
  347. package/dist/modules/customers/inbox-actions.js +3 -1
  348. package/dist/modules/customers/inbox-actions.js.map +2 -2
  349. package/dist/modules/customers/lib/bulkDeals.js +3 -1
  350. package/dist/modules/customers/lib/bulkDeals.js.map +2 -2
  351. package/dist/modules/customers/lib/dealClosureNotification.js +4 -2
  352. package/dist/modules/customers/lib/dealClosureNotification.js.map +2 -2
  353. package/dist/modules/customers/lib/dealStageTransitionTable.js +3 -1
  354. package/dist/modules/customers/lib/dealStageTransitionTable.js.map +2 -2
  355. package/dist/modules/customers/lib/interactionFeatureFlags.js +3 -1
  356. package/dist/modules/customers/lib/interactionFeatureFlags.js.map +2 -2
  357. package/dist/modules/customers/lib/todoCompatibility.js +3 -1
  358. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  359. package/dist/modules/customers/search.js +14 -7
  360. package/dist/modules/customers/search.js.map +2 -2
  361. package/dist/modules/customers/widgets/dashboard/customer-todos/widget.client.js +3 -1
  362. package/dist/modules/customers/widgets/dashboard/customer-todos/widget.client.js.map +2 -2
  363. package/dist/modules/customers/widgets/dashboard/new-customers/widget.client.js +3 -1
  364. package/dist/modules/customers/widgets/dashboard/new-customers/widget.client.js.map +2 -2
  365. package/dist/modules/customers/widgets/dashboard/new-deals/widget.client.js +3 -1
  366. package/dist/modules/customers/widgets/dashboard/new-deals/widget.client.js.map +2 -2
  367. package/dist/modules/customers/widgets/dashboard/next-interactions/widget.client.js +3 -1
  368. package/dist/modules/customers/widgets/dashboard/next-interactions/widget.client.js.map +2 -2
  369. package/dist/modules/dashboards/api/widgets/data/batch/route.js +3 -1
  370. package/dist/modules/dashboards/api/widgets/data/batch/route.js.map +2 -2
  371. package/dist/modules/dashboards/api/widgets/data/route.js +3 -1
  372. package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
  373. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +5 -3
  374. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
  375. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js +3 -1
  376. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js.map +2 -2
  377. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js +3 -1
  378. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js.map +2 -2
  379. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js +3 -1
  380. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js.map +2 -2
  381. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js +3 -1
  382. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js.map +2 -2
  383. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js +3 -1
  384. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js.map +2 -2
  385. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js +3 -1
  386. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js.map +2 -2
  387. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js +3 -1
  388. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js.map +2 -2
  389. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js +3 -1
  390. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js.map +2 -2
  391. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js +3 -1
  392. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js.map +2 -2
  393. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js +3 -1
  394. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js.map +2 -2
  395. package/dist/modules/data_sync/api/run.js +3 -1
  396. package/dist/modules/data_sync/api/run.js.map +2 -2
  397. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +1 -1
  398. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
  399. package/dist/modules/data_sync/lib/adapter-registry.js +11 -4
  400. package/dist/modules/data_sync/lib/adapter-registry.js.map +2 -2
  401. package/dist/modules/data_sync/lib/sync-engine.js +23 -2
  402. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  403. package/dist/modules/data_sync/workers/sync-export.js +3 -1
  404. package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
  405. package/dist/modules/data_sync/workers/sync-import.js +3 -1
  406. package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
  407. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +4 -2
  408. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  409. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +3 -1
  410. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
  411. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +4 -2
  412. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
  413. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +3 -1
  414. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
  415. package/dist/modules/dictionaries/api/[dictionaryId]/route.js +5 -3
  416. package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
  417. package/dist/modules/dictionaries/api/context.js +3 -1
  418. package/dist/modules/dictionaries/api/context.js.map +2 -2
  419. package/dist/modules/dictionaries/api/route.js +4 -2
  420. package/dist/modules/dictionaries/api/route.js.map +2 -2
  421. package/dist/modules/dictionaries/components/DictionariesManager.js +5 -3
  422. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  423. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +4 -2
  424. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
  425. package/dist/modules/dictionaries/components/DictionaryEntrySelect.js +4 -2
  426. package/dist/modules/dictionaries/components/DictionaryEntrySelect.js.map +2 -2
  427. package/dist/modules/dictionaries/components/DictionarySelectControl.js +3 -1
  428. package/dist/modules/dictionaries/components/DictionarySelectControl.js.map +2 -2
  429. package/dist/modules/dictionaries/fields/dictionary.js +3 -1
  430. package/dist/modules/dictionaries/fields/dictionary.js.map +2 -2
  431. package/dist/modules/directory/api/organization-branding/route.js +3 -1
  432. package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
  433. package/dist/modules/directory/api/organization-switcher/route.js +3 -1
  434. package/dist/modules/directory/api/organization-switcher/route.js.map +2 -2
  435. package/dist/modules/directory/commands/tenants.js +5 -3
  436. package/dist/modules/directory/commands/tenants.js.map +2 -2
  437. package/dist/modules/directory/utils/organizationScope.js +6 -4
  438. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  439. package/dist/modules/entities/api/definitions.cache.js +3 -1
  440. package/dist/modules/entities/api/definitions.cache.js.map +2 -2
  441. package/dist/modules/entities/api/definitions.js +7 -5
  442. package/dist/modules/entities/api/definitions.js.map +2 -2
  443. package/dist/modules/entities/api/records.js +4 -8
  444. package/dist/modules/entities/api/records.js.map +2 -2
  445. package/dist/modules/entities/components/EncryptionManager.js +3 -1
  446. package/dist/modules/entities/components/EncryptionManager.js.map +2 -2
  447. package/dist/modules/entities/lib/makeActivityRoute.js +3 -1
  448. package/dist/modules/entities/lib/makeActivityRoute.js.map +2 -2
  449. package/dist/modules/feature_toggles/api/overrides/route.js +3 -1
  450. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  451. package/dist/modules/feature_toggles/lib/feature-flag-check.js +5 -9
  452. package/dist/modules/feature_toggles/lib/feature-flag-check.js.map +2 -2
  453. package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js +4 -2
  454. package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js.map +2 -2
  455. package/dist/modules/inbox_ops/api/emails/[id]/route.js +4 -2
  456. package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
  457. package/dist/modules/inbox_ops/api/emails/route.js +3 -1
  458. package/dist/modules/inbox_ops/api/emails/route.js.map +2 -2
  459. package/dist/modules/inbox_ops/api/extract/route.js +3 -1
  460. package/dist/modules/inbox_ops/api/extract/route.js.map +2 -2
  461. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +3 -1
  462. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
  463. package/dist/modules/inbox_ops/api/proposals/[id]/replies/[replyId]/send/route.js +4 -2
  464. package/dist/modules/inbox_ops/api/proposals/[id]/replies/[replyId]/send/route.js.map +2 -2
  465. package/dist/modules/inbox_ops/api/proposals/counts/route.js +4 -2
  466. package/dist/modules/inbox_ops/api/proposals/counts/route.js.map +2 -2
  467. package/dist/modules/inbox_ops/api/proposals/route.js +3 -1
  468. package/dist/modules/inbox_ops/api/proposals/route.js.map +2 -2
  469. package/dist/modules/inbox_ops/api/routeHelpers.js +3 -1
  470. package/dist/modules/inbox_ops/api/routeHelpers.js.map +2 -2
  471. package/dist/modules/inbox_ops/api/settings/route.js +3 -1
  472. package/dist/modules/inbox_ops/api/settings/route.js.map +2 -2
  473. package/dist/modules/inbox_ops/api/webhook/inbound.js +7 -5
  474. package/dist/modules/inbox_ops/api/webhook/inbound.js.map +2 -2
  475. package/dist/modules/inbox_ops/lib/cache.js +4 -2
  476. package/dist/modules/inbox_ops/lib/cache.js.map +2 -2
  477. package/dist/modules/inbox_ops/lib/catalogLookup.js +3 -1
  478. package/dist/modules/inbox_ops/lib/catalogLookup.js.map +2 -2
  479. package/dist/modules/inbox_ops/lib/eventBus.js +3 -4
  480. package/dist/modules/inbox_ops/lib/eventBus.js.map +2 -2
  481. package/dist/modules/inbox_ops/lib/messagesIntegration.js +4 -8
  482. package/dist/modules/inbox_ops/lib/messagesIntegration.js.map +2 -2
  483. package/dist/modules/inbox_ops/subscribers/executionAuditor.js +3 -1
  484. package/dist/modules/inbox_ops/subscribers/executionAuditor.js.map +2 -2
  485. package/dist/modules/inbox_ops/subscribers/extractionWorker.js +10 -8
  486. package/dist/modules/inbox_ops/subscribers/extractionWorker.js.map +2 -2
  487. package/dist/modules/inbox_ops/subscribers/proposalNotifier.js +3 -1
  488. package/dist/modules/inbox_ops/subscribers/proposalNotifier.js.map +2 -2
  489. package/dist/modules/messages/api/[id]/route.js +3 -4
  490. package/dist/modules/messages/api/[id]/route.js.map +2 -2
  491. package/dist/modules/messages/commands/actions.js +3 -1
  492. package/dist/modules/messages/commands/actions.js.map +2 -2
  493. package/dist/modules/messages/components/MessagesInboxPageClient.js +3 -1
  494. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  495. package/dist/modules/messages/lib/email-sender.js +3 -7
  496. package/dist/modules/messages/lib/email-sender.js.map +2 -2
  497. package/dist/modules/messages/lib/message-objects-registry.js +3 -1
  498. package/dist/modules/messages/lib/message-objects-registry.js.map +2 -2
  499. package/dist/modules/messages/lib/message-types-registry.js +3 -1
  500. package/dist/modules/messages/lib/message-types-registry.js.map +2 -2
  501. package/dist/modules/messages/workers/send-email.worker.js +6 -4
  502. package/dist/modules/messages/workers/send-email.worker.js.map +2 -2
  503. package/dist/modules/notifications/lib/notificationMapper.js +3 -4
  504. package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
  505. package/dist/modules/notifications/lib/notificationService.js +4 -5
  506. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  507. package/dist/modules/notifications/subscribers/deliver-notification.js +8 -13
  508. package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
  509. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +3 -1
  510. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
  511. package/dist/modules/perspectives/services/perspectiveService.js +3 -3
  512. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  513. package/dist/modules/planner/api/access.js +3 -3
  514. package/dist/modules/planner/api/access.js.map +2 -2
  515. package/dist/modules/planner/api/availability-date-specific.js +3 -1
  516. package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
  517. package/dist/modules/planner/api/availability-weekly.js +3 -1
  518. package/dist/modules/planner/api/availability-weekly.js.map +2 -2
  519. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +4 -2
  520. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  521. package/dist/modules/planner/components/AvailabilityRulesEditor.js +3 -1
  522. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  523. package/dist/modules/progress/api/jobs/route.js +3 -1
  524. package/dist/modules/progress/api/jobs/route.js.map +2 -2
  525. package/dist/modules/query_index/components/QueryIndexesTable.js +5 -3
  526. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  527. package/dist/modules/query_index/lib/batch.js +4 -2
  528. package/dist/modules/query_index/lib/batch.js.map +2 -2
  529. package/dist/modules/query_index/lib/coverage.js +3 -1
  530. package/dist/modules/query_index/lib/coverage.js.map +2 -2
  531. package/dist/modules/query_index/lib/engine.js +13 -18
  532. package/dist/modules/query_index/lib/engine.js.map +2 -2
  533. package/dist/modules/query_index/lib/reindexer.js +8 -6
  534. package/dist/modules/query_index/lib/reindexer.js.map +2 -2
  535. package/dist/modules/query_index/lib/search-tokens.js +3 -1
  536. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  537. package/dist/modules/query_index/subscribers/coverage_refresh.js +3 -1
  538. package/dist/modules/query_index/subscribers/coverage_refresh.js.map +2 -2
  539. package/dist/modules/resources/api/comments.js +3 -1
  540. package/dist/modules/resources/api/comments.js.map +2 -2
  541. package/dist/modules/resources/api/resources/tags/assign/route.js +3 -1
  542. package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
  543. package/dist/modules/resources/api/resources/tags/unassign/route.js +3 -1
  544. package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
  545. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +3 -1
  546. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
  547. package/dist/modules/resources/backend/resources/resource-types/page.js +4 -2
  548. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  549. package/dist/modules/resources/commands/activities.js +6 -5
  550. package/dist/modules/resources/commands/activities.js.map +2 -2
  551. package/dist/modules/resources/commands/comments.js +6 -4
  552. package/dist/modules/resources/commands/comments.js.map +2 -2
  553. package/dist/modules/resources/commands/shared.js +24 -1
  554. package/dist/modules/resources/commands/shared.js.map +2 -2
  555. package/dist/modules/sales/api/channels/route.js +3 -1
  556. package/dist/modules/sales/api/channels/route.js.map +2 -2
  557. package/dist/modules/sales/api/document-history/route.js +3 -1
  558. package/dist/modules/sales/api/document-history/route.js.map +2 -2
  559. package/dist/modules/sales/api/document-numbers/route.js +3 -1
  560. package/dist/modules/sales/api/document-numbers/route.js.map +2 -2
  561. package/dist/modules/sales/api/quotes/accept/route.js +4 -2
  562. package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
  563. package/dist/modules/sales/api/quotes/convert/route.js +3 -1
  564. package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
  565. package/dist/modules/sales/api/quotes/public/[token]/route.js +3 -1
  566. package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
  567. package/dist/modules/sales/api/quotes/send/route.js +3 -1
  568. package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
  569. package/dist/modules/sales/api/returns/[id]/route.js +3 -1
  570. package/dist/modules/sales/api/returns/[id]/route.js.map +2 -2
  571. package/dist/modules/sales/api/settings/document-numbers/route.js +4 -2
  572. package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
  573. package/dist/modules/sales/api/settings/order-editing/route.js +4 -2
  574. package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
  575. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +3 -1
  576. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  577. package/dist/modules/sales/backend/sales/channels/offers/page.js +6 -4
  578. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  579. package/dist/modules/sales/backend/sales/channels/page.js +4 -2
  580. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  581. package/dist/modules/sales/backend/sales/documents/[id]/page.js +20 -18
  582. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  583. package/dist/modules/sales/commands/documents.js +4 -8
  584. package/dist/modules/sales/commands/documents.js.map +2 -2
  585. package/dist/modules/sales/commands/payments.js +3 -1
  586. package/dist/modules/sales/commands/payments.js.map +2 -2
  587. package/dist/modules/sales/commands/shipments.js +3 -1
  588. package/dist/modules/sales/commands/shipments.js.map +2 -2
  589. package/dist/modules/sales/components/AdjustmentKindSettings.js +5 -3
  590. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  591. package/dist/modules/sales/components/DocumentNumberSettings.js +4 -2
  592. package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
  593. package/dist/modules/sales/components/OrderEditingSettings.js +4 -2
  594. package/dist/modules/sales/components/OrderEditingSettings.js.map +2 -2
  595. package/dist/modules/sales/components/PaymentMethodsSettings.js +5 -3
  596. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  597. package/dist/modules/sales/components/ShippingMethodsSettings.js +5 -3
  598. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  599. package/dist/modules/sales/components/StatusSettings.js +5 -3
  600. package/dist/modules/sales/components/StatusSettings.js.map +2 -2
  601. package/dist/modules/sales/components/TaxRatesSettings.js +5 -3
  602. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  603. package/dist/modules/sales/components/channels/ChannelOfferForm.js +16 -14
  604. package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
  605. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +4 -2
  606. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  607. package/dist/modules/sales/components/documents/AddressesSection.js +5 -3
  608. package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
  609. package/dist/modules/sales/components/documents/AdjustmentDialog.js +3 -1
  610. package/dist/modules/sales/components/documents/AdjustmentDialog.js.map +2 -2
  611. package/dist/modules/sales/components/documents/AdjustmentsSection.js +5 -3
  612. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  613. package/dist/modules/sales/components/documents/ItemsSection.js +6 -4
  614. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  615. package/dist/modules/sales/components/documents/LineItemDialog.js +8 -6
  616. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  617. package/dist/modules/sales/components/documents/PaymentDialog.js +5 -3
  618. package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
  619. package/dist/modules/sales/components/documents/PaymentsSection.js +4 -2
  620. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  621. package/dist/modules/sales/components/documents/SalesDocumentForm.js +14 -12
  622. package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
  623. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +5 -3
  624. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  625. package/dist/modules/sales/components/documents/ShipmentDialog.js +9 -7
  626. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  627. package/dist/modules/sales/components/documents/ShipmentsSection.js +4 -2
  628. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  629. package/dist/modules/sales/frontend/quote/[token]/page.js +4 -2
  630. package/dist/modules/sales/frontend/quote/[token]/page.js.map +2 -2
  631. package/dist/modules/sales/seed/examples.js +7 -9
  632. package/dist/modules/sales/seed/examples.js.map +2 -2
  633. package/dist/modules/sales/subscribers/quote-expiring-notification.js +3 -1
  634. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +2 -2
  635. package/dist/modules/sales/subscribers/reconcileOnAddressDelete.js +3 -1
  636. package/dist/modules/sales/subscribers/reconcileOnAddressDelete.js.map +2 -2
  637. package/dist/modules/sales/subscribers/reconcileOnCompanyDelete.js +3 -1
  638. package/dist/modules/sales/subscribers/reconcileOnCompanyDelete.js.map +2 -2
  639. package/dist/modules/sales/subscribers/reconcileOnCustomerDelete.js +3 -1
  640. package/dist/modules/sales/subscribers/reconcileOnCustomerDelete.js.map +2 -2
  641. package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js +5 -3
  642. package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js.map +2 -2
  643. package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js +3 -1
  644. package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js.map +2 -2
  645. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js +3 -1
  646. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js.map +2 -2
  647. package/dist/modules/shipping_carriers/api/tracking/route.js +5 -0
  648. package/dist/modules/shipping_carriers/api/tracking/route.js.map +2 -2
  649. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +3 -1
  650. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +2 -2
  651. package/dist/modules/shipping_carriers/lib/shipping-service.js +41 -19
  652. package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
  653. package/dist/modules/shipping_carriers/lib/upstream-error-response.js +3 -1
  654. package/dist/modules/shipping_carriers/lib/upstream-error-response.js.map +2 -2
  655. package/dist/modules/shipping_carriers/workers/webhook-processor.js +3 -5
  656. package/dist/modules/shipping_carriers/workers/webhook-processor.js.map +2 -2
  657. package/dist/modules/staff/api/comments.js +3 -1
  658. package/dist/modules/staff/api/comments.js.map +2 -2
  659. package/dist/modules/staff/api/leave-requests/accept/route.js +3 -1
  660. package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
  661. package/dist/modules/staff/api/leave-requests/reject/route.js +3 -1
  662. package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
  663. package/dist/modules/staff/api/team-members/assignable/route.js +3 -1
  664. package/dist/modules/staff/api/team-members/assignable/route.js.map +2 -2
  665. package/dist/modules/staff/api/team-members/self/route.js +4 -2
  666. package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
  667. package/dist/modules/staff/api/team-members/tags/assign/route.js +3 -1
  668. package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
  669. package/dist/modules/staff/api/team-members/tags/unassign/route.js +3 -1
  670. package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
  671. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +3 -1
  672. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +2 -2
  673. package/dist/modules/staff/api/timesheets/my-projects/route.js +3 -1
  674. package/dist/modules/staff/api/timesheets/my-projects/route.js.map +2 -2
  675. package/dist/modules/staff/api/timesheets/projects/kpis/route.js +3 -1
  676. package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +2 -2
  677. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +3 -1
  678. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +2 -2
  679. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +4 -2
  680. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
  681. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +4 -2
  682. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +2 -2
  683. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +3 -1
  684. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +2 -2
  685. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +3 -1
  686. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
  687. package/dist/modules/staff/backend/staff/team-members/page.js +4 -2
  688. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  689. package/dist/modules/staff/backend/staff/team-roles/page.js +4 -2
  690. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  691. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +4 -2
  692. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  693. package/dist/modules/staff/backend/staff/teams/page.js +4 -2
  694. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  695. package/dist/modules/staff/backend/staff/timesheets/page.js +7 -5
  696. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  697. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +3 -1
  698. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +2 -2
  699. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +4 -2
  700. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  701. package/dist/modules/staff/commands/timesheets-entries.js +3 -1
  702. package/dist/modules/staff/commands/timesheets-entries.js.map +2 -2
  703. package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js +3 -1
  704. package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js.map +2 -2
  705. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js +5 -3
  706. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js.map +2 -2
  707. package/dist/modules/sync_excel/api/import/route.js +3 -1
  708. package/dist/modules/sync_excel/api/import/route.js.map +2 -2
  709. package/dist/modules/translations/api/[entityType]/[entityId]/route.js +5 -3
  710. package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
  711. package/dist/modules/translations/api/get/locales.js +3 -1
  712. package/dist/modules/translations/api/get/locales.js.map +2 -2
  713. package/dist/modules/translations/api/put/locales.js +3 -1
  714. package/dist/modules/translations/api/put/locales.js.map +2 -2
  715. package/dist/modules/translations/components/TranslationManager.js +3 -1
  716. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  717. package/dist/modules/translations/subscribers/cleanup.js +3 -1
  718. package/dist/modules/translations/subscribers/cleanup.js.map +2 -2
  719. package/dist/modules/workflows/api/definitions/[id]/customize/route.js +4 -2
  720. package/dist/modules/workflows/api/definitions/[id]/customize/route.js.map +2 -2
  721. package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js +4 -2
  722. package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js.map +2 -2
  723. package/dist/modules/workflows/api/definitions/[id]/route.js +6 -4
  724. package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
  725. package/dist/modules/workflows/api/definitions/route.js +4 -2
  726. package/dist/modules/workflows/api/definitions/route.js.map +2 -2
  727. package/dist/modules/workflows/api/events/[id]/route.js +3 -1
  728. package/dist/modules/workflows/api/events/[id]/route.js.map +2 -2
  729. package/dist/modules/workflows/api/events/route.js +3 -1
  730. package/dist/modules/workflows/api/events/route.js.map +2 -2
  731. package/dist/modules/workflows/api/instances/[id]/advance/route.js +3 -1
  732. package/dist/modules/workflows/api/instances/[id]/advance/route.js.map +2 -2
  733. package/dist/modules/workflows/api/instances/[id]/cancel/route.js +3 -1
  734. package/dist/modules/workflows/api/instances/[id]/cancel/route.js.map +2 -2
  735. package/dist/modules/workflows/api/instances/[id]/events/route.js +3 -1
  736. package/dist/modules/workflows/api/instances/[id]/events/route.js.map +2 -2
  737. package/dist/modules/workflows/api/instances/[id]/retry/route.js +3 -1
  738. package/dist/modules/workflows/api/instances/[id]/retry/route.js.map +2 -2
  739. package/dist/modules/workflows/api/instances/[id]/route.js +3 -1
  740. package/dist/modules/workflows/api/instances/[id]/route.js.map +2 -2
  741. package/dist/modules/workflows/api/instances/[id]/signal/route.js +5 -7
  742. package/dist/modules/workflows/api/instances/[id]/signal/route.js.map +2 -2
  743. package/dist/modules/workflows/api/instances/route.js +5 -3
  744. package/dist/modules/workflows/api/instances/route.js.map +2 -2
  745. package/dist/modules/workflows/api/instances/validate-start/route.js +3 -1
  746. package/dist/modules/workflows/api/instances/validate-start/route.js.map +2 -2
  747. package/dist/modules/workflows/api/signals/route.js +3 -1
  748. package/dist/modules/workflows/api/signals/route.js.map +2 -2
  749. package/dist/modules/workflows/api/tasks/[id]/claim/route.js +3 -1
  750. package/dist/modules/workflows/api/tasks/[id]/claim/route.js.map +2 -2
  751. package/dist/modules/workflows/api/tasks/[id]/complete/route.js +3 -1
  752. package/dist/modules/workflows/api/tasks/[id]/complete/route.js.map +2 -2
  753. package/dist/modules/workflows/api/tasks/[id]/route.js +3 -1
  754. package/dist/modules/workflows/api/tasks/[id]/route.js.map +2 -2
  755. package/dist/modules/workflows/api/tasks/route.js +3 -1
  756. package/dist/modules/workflows/api/tasks/route.js.map +2 -2
  757. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +4 -2
  758. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  759. package/dist/modules/workflows/backend/instances/[id]/page.js +5 -3
  760. package/dist/modules/workflows/backend/instances/[id]/page.js.map +2 -2
  761. package/dist/modules/workflows/backend/tasks/[id]/page.js +3 -1
  762. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  763. package/dist/modules/workflows/components/BusinessRulesSelector.js +5 -3
  764. package/dist/modules/workflows/components/BusinessRulesSelector.js.map +2 -2
  765. package/dist/modules/workflows/components/WorkflowSelector.js +5 -3
  766. package/dist/modules/workflows/components/WorkflowSelector.js.map +2 -2
  767. package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js +3 -1
  768. package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js.map +2 -2
  769. package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js +3 -1
  770. package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js.map +2 -2
  771. package/dist/modules/workflows/components/fields/WorkflowSelectorField.js +3 -1
  772. package/dist/modules/workflows/components/fields/WorkflowSelectorField.js.map +2 -2
  773. package/dist/modules/workflows/frontend/checkout-demo/page.js +8 -7
  774. package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
  775. package/dist/modules/workflows/lib/activity-executor.js +25 -21
  776. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  777. package/dist/modules/workflows/lib/activity-worker-handler.js +8 -15
  778. package/dist/modules/workflows/lib/activity-worker-handler.js.map +2 -2
  779. package/dist/modules/workflows/lib/code-registry.js +7 -7
  780. package/dist/modules/workflows/lib/code-registry.js.map +2 -2
  781. package/dist/modules/workflows/lib/compensation-handler.js +4 -2
  782. package/dist/modules/workflows/lib/compensation-handler.js.map +2 -2
  783. package/dist/modules/workflows/lib/edgeFormTransforms.js +3 -1
  784. package/dist/modules/workflows/lib/edgeFormTransforms.js.map +2 -2
  785. package/dist/modules/workflows/lib/event-trigger-service.js +5 -3
  786. package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
  787. package/dist/modules/workflows/lib/nodeFormTransforms.js +3 -1
  788. package/dist/modules/workflows/lib/nodeFormTransforms.js.map +2 -2
  789. package/dist/modules/workflows/lib/seeds.js +16 -2
  790. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  791. package/dist/modules/workflows/lib/signal-handler.js +3 -1
  792. package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
  793. package/dist/modules/workflows/lib/start-validator.js +9 -3
  794. package/dist/modules/workflows/lib/start-validator.js.map +2 -2
  795. package/dist/modules/workflows/lib/step-handler.js +3 -1
  796. package/dist/modules/workflows/lib/step-handler.js.map +2 -2
  797. package/dist/modules/workflows/lib/task-handler.js +3 -1
  798. package/dist/modules/workflows/lib/task-handler.js.map +2 -2
  799. package/dist/modules/workflows/lib/transition-handler.js +5 -3
  800. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  801. package/dist/modules/workflows/lib/workflow-executor.js +27 -15
  802. package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
  803. package/dist/modules/workflows/subscribers/event-trigger.js +21 -18
  804. package/dist/modules/workflows/subscribers/event-trigger.js.map +2 -2
  805. package/dist/modules/workflows/subscribers/task-assigned-notification.js +3 -1
  806. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +2 -2
  807. package/dist/modules/workflows/workers/workflow-activities.worker.js +30 -20
  808. package/dist/modules/workflows/workers/workflow-activities.worker.js.map +2 -2
  809. package/package.json +7 -7
  810. package/src/bootstrap.ts +14 -11
  811. package/src/helpers/integration/queue-runner.ts +2 -2
  812. package/src/modules/api_keys/services/apiKeyService.ts +6 -3
  813. package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +5 -2
  814. package/src/modules/attachments/api/library/[id]/route.ts +4 -1
  815. package/src/modules/attachments/api/route.ts +9 -6
  816. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +6 -3
  817. package/src/modules/attachments/lib/assignmentDetails.ts +4 -1
  818. package/src/modules/attachments/lib/drivers/driverFactory.ts +4 -1
  819. package/src/modules/attachments/lib/ocrQueue.ts +11 -11
  820. package/src/modules/attachments/lib/ocrService.ts +7 -4
  821. package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +4 -1
  822. package/src/modules/audit_logs/api/audit-logs/actions/undo/route.ts +4 -1
  823. package/src/modules/audit_logs/backend/audit-logs/page.tsx +4 -1
  824. package/src/modules/audit_logs/components/AuditLogsActions.tsx +5 -2
  825. package/src/modules/audit_logs/services/accessLogService.ts +5 -4
  826. package/src/modules/audit_logs/services/actionLogService.ts +5 -2
  827. package/src/modules/auth/api/profile/route.ts +5 -2
  828. package/src/modules/auth/api/reset/confirm.ts +4 -1
  829. package/src/modules/auth/api/reset.ts +5 -2
  830. package/src/modules/auth/api/roles/route.ts +4 -1
  831. package/src/modules/auth/api/sidebar/variants/route.ts +4 -2
  832. package/src/modules/auth/api/users/resend-invite/route.ts +5 -2
  833. package/src/modules/auth/api/users/route.ts +4 -1
  834. package/src/modules/auth/backend/auth/profile/page.tsx +4 -1
  835. package/src/modules/auth/backend/profile/change-password/page.tsx +4 -1
  836. package/src/modules/auth/backend/users/[id]/edit/page.tsx +6 -3
  837. package/src/modules/auth/backend/users/create/page.tsx +5 -2
  838. package/src/modules/auth/cli.ts +10 -0
  839. package/src/modules/auth/commands/users.ts +5 -2
  840. package/src/modules/auth/lib/consentIntegrity.ts +4 -4
  841. package/src/modules/auth/lib/setup-app.ts +10 -10
  842. package/src/modules/auth/lib/tenantAccess.ts +4 -1
  843. package/src/modules/auth/lib/tokenHash.ts +4 -4
  844. package/src/modules/business_rules/api/rules/route.ts +5 -2
  845. package/src/modules/business_rules/lib/expression-evaluator.ts +8 -6
  846. package/src/modules/business_rules/lib/rule-engine.ts +6 -3
  847. package/src/modules/business_rules/lib/rule-evaluator.ts +11 -11
  848. package/src/modules/business_rules/subscribers/crud-rule-trigger.ts +4 -1
  849. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +4 -1
  850. package/src/modules/catalog/ai-agents-context.ts +5 -5
  851. package/src/modules/catalog/ai-tools/_shared.ts +4 -1
  852. package/src/modules/catalog/ai-tools/authoring-pack.ts +4 -1
  853. package/src/modules/catalog/ai-tools/merchandising-pack.ts +4 -1
  854. package/src/modules/catalog/api/dictionaries/[key]/route.ts +4 -1
  855. package/src/modules/catalog/api/prices/route.ts +4 -1
  856. package/src/modules/catalog/api/products/route.ts +6 -3
  857. package/src/modules/catalog/api/settings/route.ts +5 -2
  858. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +15 -12
  859. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +9 -6
  860. package/src/modules/catalog/backend/catalog/products/[productId]/variants/create/page.tsx +6 -3
  861. package/src/modules/catalog/backend/catalog/products/create/page.tsx +6 -6
  862. package/src/modules/catalog/backend/catalog/products/optionSchemaClient.ts +4 -1
  863. package/src/modules/catalog/components/PriceKindSettings.tsx +6 -3
  864. package/src/modules/catalog/components/UnitPriceDisplaySettings.tsx +5 -2
  865. package/src/modules/catalog/inbox-actions.ts +5 -2
  866. package/src/modules/catalog/seed/examples.ts +5 -5
  867. package/src/modules/catalog/subscribers/low-stock-notification.ts +4 -1
  868. package/src/modules/catalog/widgets/injection/product-seo/state.ts +4 -1
  869. package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +13 -13
  870. package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +4 -1
  871. package/src/modules/communication_channels/api/post/channels/[id]/push/register/route.ts +4 -1
  872. package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +6 -3
  873. package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +7 -6
  874. package/src/modules/communication_channels/commands/connect-credential-channel.ts +5 -10
  875. package/src/modules/communication_channels/commands/delete-channel.ts +4 -5
  876. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +5 -5
  877. package/src/modules/communication_channels/commands/disconnect-channel.ts +4 -5
  878. package/src/modules/communication_channels/commands/ingest-inbound-message.ts +6 -12
  879. package/src/modules/communication_channels/commands/push-unregister.ts +4 -3
  880. package/src/modules/communication_channels/commands/reassign-conversation.ts +6 -3
  881. package/src/modules/communication_channels/lib/dead-letter.ts +4 -5
  882. package/src/modules/communication_channels/lib/email-mime.ts +5 -6
  883. package/src/modules/communication_channels/lib/oauth-client-config.ts +7 -3
  884. package/src/modules/communication_channels/lib/send-as-user.ts +4 -1
  885. package/src/modules/communication_channels/lib/thread-token.ts +5 -2
  886. package/src/modules/communication_channels/setup.ts +4 -4
  887. package/src/modules/communication_channels/workers/channel-import-history.ts +5 -8
  888. package/src/modules/communication_channels/workers/gmail-history-sync.ts +9 -18
  889. package/src/modules/communication_channels/workers/gmail-renew-watch.ts +6 -9
  890. package/src/modules/communication_channels/workers/outbound-delivery.ts +7 -12
  891. package/src/modules/communication_channels/workers/poll-channel.ts +8 -15
  892. package/src/modules/communication_channels/workers/poll-tick.ts +6 -6
  893. package/src/modules/communication_channels/workers/reaction-processor.ts +6 -9
  894. package/src/modules/configs/api/cache/route.ts +5 -2
  895. package/src/modules/configs/api/system-status/route.ts +5 -2
  896. package/src/modules/configs/api/upgrade-actions/route.ts +5 -2
  897. package/src/modules/configs/components/ModuleTelemetryPanel.tsx +4 -1
  898. package/src/modules/configs/lib/touchGeneratedBarrels.ts +1 -1
  899. package/src/modules/configs/services/upgradeActionsService.ts +7 -3
  900. package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +4 -1
  901. package/src/modules/currencies/lib/exchangeRateFormConfig.ts +4 -1
  902. package/src/modules/currencies/services/providers/nbp.ts +7 -4
  903. package/src/modules/currencies/services/providers/raiffeisen.ts +8 -7
  904. package/src/modules/customer_accounts/api/domain-resolve/all.ts +7 -5
  905. package/src/modules/customer_accounts/api/signup.ts +5 -2
  906. package/src/modules/customer_accounts/lib/customerAuthServer.ts +4 -1
  907. package/src/modules/customer_accounts/subscribers/autoLinkCrm.ts +4 -1
  908. package/src/modules/customer_accounts/subscribers/autoLinkCrmReverse.ts +4 -1
  909. package/src/modules/customer_accounts/subscribers/unlinkOnCompanyDelete.ts +4 -1
  910. package/src/modules/customer_accounts/subscribers/unlinkOnPersonDelete.ts +4 -1
  911. package/src/modules/customers/ai-agents-context.ts +5 -5
  912. package/src/modules/customers/api/activities/route.ts +7 -4
  913. package/src/modules/customers/api/comments/route.ts +4 -1
  914. package/src/modules/customers/api/companies/[id]/people/route.ts +4 -1
  915. package/src/modules/customers/api/companies/[id]/route.ts +6 -3
  916. package/src/modules/customers/api/companies/route.ts +6 -3
  917. package/src/modules/customers/api/dashboard/widgets/customer-todos/route.ts +4 -1
  918. package/src/modules/customers/api/dashboard/widgets/new-customers/route.ts +4 -1
  919. package/src/modules/customers/api/dashboard/widgets/new-deals/route.ts +4 -1
  920. package/src/modules/customers/api/dashboard/widgets/next-interactions/route.ts +4 -1
  921. package/src/modules/customers/api/deals/[id]/companies/route.ts +4 -1
  922. package/src/modules/customers/api/deals/[id]/people/route.ts +4 -1
  923. package/src/modules/customers/api/deals/aggregate/route.ts +4 -1
  924. package/src/modules/customers/api/deals/bulk-update-owner/route.ts +8 -5
  925. package/src/modules/customers/api/deals/bulk-update-stage/route.ts +8 -5
  926. package/src/modules/customers/api/deals/route.ts +4 -1
  927. package/src/modules/customers/api/deals/summary/route.ts +4 -1
  928. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +5 -2
  929. package/src/modules/customers/api/dictionaries/[kind]/route.ts +6 -3
  930. package/src/modules/customers/api/dictionaries/cache.ts +4 -1
  931. package/src/modules/customers/api/dictionaries/context.ts +4 -1
  932. package/src/modules/customers/api/dictionaries/currency/route.ts +4 -1
  933. package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -2
  934. package/src/modules/customers/api/entity-roles-factory.ts +8 -5
  935. package/src/modules/customers/api/interactions/cancel/route.ts +4 -1
  936. package/src/modules/customers/api/interactions/complete/route.ts +4 -1
  937. package/src/modules/customers/api/interactions/conflicts/route.ts +4 -1
  938. package/src/modules/customers/api/interactions/counts/route.ts +4 -1
  939. package/src/modules/customers/api/interactions/route.ts +8 -4
  940. package/src/modules/customers/api/interactions/tasks/route.ts +4 -1
  941. package/src/modules/customers/api/labels/assign/route.ts +4 -1
  942. package/src/modules/customers/api/labels/route.ts +7 -3
  943. package/src/modules/customers/api/labels/unassign/route.ts +4 -1
  944. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +4 -1
  945. package/src/modules/customers/api/people/[id]/route.ts +6 -3
  946. package/src/modules/customers/api/people/route.ts +6 -3
  947. package/src/modules/customers/api/pipeline-stages/reorder/route.ts +4 -1
  948. package/src/modules/customers/api/pipeline-stages/route.ts +7 -4
  949. package/src/modules/customers/api/pipelines/route.ts +7 -4
  950. package/src/modules/customers/api/settings/address-format/route.ts +5 -2
  951. package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -2
  952. package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -2
  953. package/src/modules/customers/api/tags/assign/route.ts +4 -1
  954. package/src/modules/customers/api/tags/unassign/route.ts +4 -1
  955. package/src/modules/customers/api/todos/route.ts +7 -4
  956. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +6 -3
  957. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +4 -1
  958. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.ts +4 -1
  959. package/src/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.tsx +5 -3
  960. package/src/modules/customers/backend/customers/deals/page.tsx +5 -2
  961. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +6 -3
  962. package/src/modules/customers/commands/deals.ts +4 -1
  963. package/src/modules/customers/commands/dictionaries.ts +4 -1
  964. package/src/modules/customers/commands/interactions.ts +5 -2
  965. package/src/modules/customers/commands/shared.ts +5 -2
  966. package/src/modules/customers/commands/todos.ts +4 -1
  967. package/src/modules/customers/components/DictionarySettings.tsx +6 -3
  968. package/src/modules/customers/components/DictionarySortSettings.tsx +4 -1
  969. package/src/modules/customers/components/detail/ActivitiesCard.tsx +4 -1
  970. package/src/modules/customers/components/detail/ActivitiesDayStrip.tsx +4 -1
  971. package/src/modules/customers/components/detail/ActivitiesSection.tsx +7 -4
  972. package/src/modules/customers/components/detail/ActivityCard.tsx +4 -1
  973. package/src/modules/customers/components/detail/ChangelogTab.tsx +4 -1
  974. package/src/modules/customers/components/detail/CompanyCard.tsx +4 -1
  975. package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +4 -1
  976. package/src/modules/customers/components/detail/EntityTagsDialog.tsx +4 -1
  977. package/src/modules/customers/components/detail/InlineActivityComposer.tsx +4 -1
  978. package/src/modules/customers/components/detail/ManageTagsDialog.tsx +4 -1
  979. package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +6 -3
  980. package/src/modules/customers/components/detail/RolesSection.tsx +5 -2
  981. package/src/modules/customers/components/detail/hooks/useInteractionMutations.ts +6 -3
  982. package/src/modules/customers/components/linking/LinkEntityDialog.tsx +5 -2
  983. package/src/modules/customers/inbox-actions.ts +4 -1
  984. package/src/modules/customers/lib/bulkDeals.ts +4 -1
  985. package/src/modules/customers/lib/dealClosureNotification.ts +5 -2
  986. package/src/modules/customers/lib/dealStageTransitionTable.ts +4 -1
  987. package/src/modules/customers/lib/interactionFeatureFlags.ts +5 -1
  988. package/src/modules/customers/lib/todoCompatibility.ts +4 -1
  989. package/src/modules/customers/search.ts +15 -7
  990. package/src/modules/customers/widgets/dashboard/customer-todos/widget.client.tsx +4 -1
  991. package/src/modules/customers/widgets/dashboard/new-customers/widget.client.tsx +4 -1
  992. package/src/modules/customers/widgets/dashboard/new-deals/widget.client.tsx +4 -1
  993. package/src/modules/customers/widgets/dashboard/next-interactions/widget.client.tsx +4 -1
  994. package/src/modules/dashboards/api/widgets/data/batch/route.ts +4 -1
  995. package/src/modules/dashboards/api/widgets/data/route.ts +4 -1
  996. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +6 -3
  997. package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.tsx +4 -1
  998. package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.tsx +4 -1
  999. package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.tsx +4 -1
  1000. package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.tsx +4 -1
  1001. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.tsx +4 -1
  1002. package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.tsx +4 -1
  1003. package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.tsx +4 -1
  1004. package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.tsx +4 -1
  1005. package/src/modules/dashboards/widgets/dashboard/top-customers/widget.client.tsx +4 -1
  1006. package/src/modules/dashboards/widgets/dashboard/top-products/widget.client.tsx +4 -1
  1007. package/src/modules/data_sync/api/run.ts +4 -1
  1008. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +3 -1
  1009. package/src/modules/data_sync/lib/adapter-registry.ts +16 -4
  1010. package/src/modules/data_sync/lib/sync-engine.ts +31 -2
  1011. package/src/modules/data_sync/workers/sync-export.ts +4 -1
  1012. package/src/modules/data_sync/workers/sync-import.ts +4 -1
  1013. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +5 -2
  1014. package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +4 -1
  1015. package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -2
  1016. package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +4 -1
  1017. package/src/modules/dictionaries/api/[dictionaryId]/route.ts +6 -3
  1018. package/src/modules/dictionaries/api/context.ts +4 -1
  1019. package/src/modules/dictionaries/api/route.ts +5 -2
  1020. package/src/modules/dictionaries/components/DictionariesManager.tsx +6 -3
  1021. package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +5 -2
  1022. package/src/modules/dictionaries/components/DictionaryEntrySelect.tsx +5 -2
  1023. package/src/modules/dictionaries/components/DictionarySelectControl.tsx +4 -1
  1024. package/src/modules/dictionaries/fields/dictionary.tsx +4 -1
  1025. package/src/modules/directory/api/organization-branding/route.ts +4 -1
  1026. package/src/modules/directory/api/organization-switcher/route.ts +4 -1
  1027. package/src/modules/directory/commands/tenants.ts +6 -3
  1028. package/src/modules/directory/utils/organizationScope.ts +7 -4
  1029. package/src/modules/entities/api/definitions.cache.ts +4 -1
  1030. package/src/modules/entities/api/definitions.ts +8 -5
  1031. package/src/modules/entities/api/records.ts +5 -2
  1032. package/src/modules/entities/components/EncryptionManager.tsx +4 -1
  1033. package/src/modules/entities/lib/makeActivityRoute.ts +4 -1
  1034. package/src/modules/feature_toggles/api/overrides/route.ts +4 -1
  1035. package/src/modules/feature_toggles/lib/feature-flag-check.ts +6 -9
  1036. package/src/modules/inbox_ops/api/emails/[id]/reprocess/route.ts +5 -2
  1037. package/src/modules/inbox_ops/api/emails/[id]/route.ts +5 -2
  1038. package/src/modules/inbox_ops/api/emails/route.ts +4 -1
  1039. package/src/modules/inbox_ops/api/extract/route.ts +4 -1
  1040. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +4 -1
  1041. package/src/modules/inbox_ops/api/proposals/[id]/replies/[replyId]/send/route.ts +5 -2
  1042. package/src/modules/inbox_ops/api/proposals/counts/route.ts +5 -2
  1043. package/src/modules/inbox_ops/api/proposals/route.ts +4 -1
  1044. package/src/modules/inbox_ops/api/routeHelpers.ts +4 -1
  1045. package/src/modules/inbox_ops/api/settings/route.ts +4 -1
  1046. package/src/modules/inbox_ops/api/webhook/inbound.ts +8 -5
  1047. package/src/modules/inbox_ops/lib/cache.ts +5 -2
  1048. package/src/modules/inbox_ops/lib/catalogLookup.ts +4 -1
  1049. package/src/modules/inbox_ops/lib/eventBus.ts +4 -4
  1050. package/src/modules/inbox_ops/lib/messagesIntegration.ts +5 -8
  1051. package/src/modules/inbox_ops/subscribers/executionAuditor.ts +5 -1
  1052. package/src/modules/inbox_ops/subscribers/extractionWorker.ts +11 -8
  1053. package/src/modules/inbox_ops/subscribers/proposalNotifier.ts +4 -1
  1054. package/src/modules/messages/api/[id]/route.ts +4 -4
  1055. package/src/modules/messages/commands/actions.ts +4 -1
  1056. package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -1
  1057. package/src/modules/messages/lib/email-sender.ts +4 -7
  1058. package/src/modules/messages/lib/message-objects-registry.ts +4 -1
  1059. package/src/modules/messages/lib/message-types-registry.ts +4 -1
  1060. package/src/modules/messages/workers/send-email.worker.ts +7 -4
  1061. package/src/modules/notifications/lib/notificationMapper.ts +4 -4
  1062. package/src/modules/notifications/lib/notificationService.ts +4 -5
  1063. package/src/modules/notifications/subscribers/deliver-notification.ts +9 -14
  1064. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +4 -1
  1065. package/src/modules/perspectives/services/perspectiveService.ts +4 -3
  1066. package/src/modules/planner/api/access.ts +4 -3
  1067. package/src/modules/planner/api/availability-date-specific.ts +4 -1
  1068. package/src/modules/planner/api/availability-weekly.ts +4 -1
  1069. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +5 -2
  1070. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +4 -1
  1071. package/src/modules/progress/api/jobs/route.ts +4 -1
  1072. package/src/modules/query_index/components/QueryIndexesTable.tsx +6 -3
  1073. package/src/modules/query_index/lib/batch.ts +5 -2
  1074. package/src/modules/query_index/lib/coverage.ts +4 -1
  1075. package/src/modules/query_index/lib/engine.ts +14 -17
  1076. package/src/modules/query_index/lib/reindexer.ts +9 -6
  1077. package/src/modules/query_index/lib/search-tokens.ts +4 -2
  1078. package/src/modules/query_index/subscribers/coverage_refresh.ts +4 -1
  1079. package/src/modules/resources/api/comments.ts +4 -1
  1080. package/src/modules/resources/api/resources/tags/assign/route.ts +4 -1
  1081. package/src/modules/resources/api/resources/tags/unassign/route.ts +4 -1
  1082. package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +4 -1
  1083. package/src/modules/resources/backend/resources/resource-types/page.tsx +5 -2
  1084. package/src/modules/resources/commands/activities.ts +5 -4
  1085. package/src/modules/resources/commands/comments.ts +6 -4
  1086. package/src/modules/resources/commands/shared.ts +36 -0
  1087. package/src/modules/sales/api/channels/route.ts +4 -1
  1088. package/src/modules/sales/api/document-history/route.ts +4 -1
  1089. package/src/modules/sales/api/document-numbers/route.ts +4 -1
  1090. package/src/modules/sales/api/quotes/accept/route.ts +5 -2
  1091. package/src/modules/sales/api/quotes/convert/route.ts +4 -1
  1092. package/src/modules/sales/api/quotes/public/[token]/route.ts +4 -1
  1093. package/src/modules/sales/api/quotes/send/route.ts +4 -1
  1094. package/src/modules/sales/api/returns/[id]/route.ts +4 -1
  1095. package/src/modules/sales/api/settings/document-numbers/route.ts +5 -2
  1096. package/src/modules/sales/api/settings/order-editing/route.ts +5 -2
  1097. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +4 -1
  1098. package/src/modules/sales/backend/sales/channels/offers/page.tsx +7 -4
  1099. package/src/modules/sales/backend/sales/channels/page.tsx +5 -2
  1100. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +21 -18
  1101. package/src/modules/sales/commands/documents.ts +5 -8
  1102. package/src/modules/sales/commands/payments.ts +4 -1
  1103. package/src/modules/sales/commands/shipments.ts +4 -2
  1104. package/src/modules/sales/components/AdjustmentKindSettings.tsx +6 -3
  1105. package/src/modules/sales/components/DocumentNumberSettings.tsx +5 -2
  1106. package/src/modules/sales/components/OrderEditingSettings.tsx +5 -2
  1107. package/src/modules/sales/components/PaymentMethodsSettings.tsx +6 -3
  1108. package/src/modules/sales/components/ShippingMethodsSettings.tsx +6 -3
  1109. package/src/modules/sales/components/StatusSettings.tsx +6 -3
  1110. package/src/modules/sales/components/TaxRatesSettings.tsx +6 -3
  1111. package/src/modules/sales/components/channels/ChannelOfferForm.tsx +17 -14
  1112. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +5 -2
  1113. package/src/modules/sales/components/documents/AddressesSection.tsx +6 -3
  1114. package/src/modules/sales/components/documents/AdjustmentDialog.tsx +4 -1
  1115. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +6 -3
  1116. package/src/modules/sales/components/documents/ItemsSection.tsx +7 -4
  1117. package/src/modules/sales/components/documents/LineItemDialog.tsx +9 -6
  1118. package/src/modules/sales/components/documents/PaymentDialog.tsx +6 -3
  1119. package/src/modules/sales/components/documents/PaymentsSection.tsx +5 -2
  1120. package/src/modules/sales/components/documents/SalesDocumentForm.tsx +15 -12
  1121. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +6 -3
  1122. package/src/modules/sales/components/documents/ShipmentDialog.tsx +10 -7
  1123. package/src/modules/sales/components/documents/ShipmentsSection.tsx +5 -2
  1124. package/src/modules/sales/frontend/quote/[token]/page.tsx +5 -2
  1125. package/src/modules/sales/seed/examples.ts +8 -9
  1126. package/src/modules/sales/subscribers/quote-expiring-notification.ts +4 -1
  1127. package/src/modules/sales/subscribers/reconcileOnAddressDelete.ts +4 -1
  1128. package/src/modules/sales/subscribers/reconcileOnCompanyDelete.ts +4 -1
  1129. package/src/modules/sales/subscribers/reconcileOnCustomerDelete.ts +4 -1
  1130. package/src/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.ts +6 -3
  1131. package/src/modules/sales/widgets/dashboard/new-orders/widget.client.tsx +274 -271
  1132. package/src/modules/sales/widgets/dashboard/new-quotes/widget.client.tsx +280 -277
  1133. package/src/modules/shipping_carriers/api/tracking/route.ts +5 -1
  1134. package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +4 -1
  1135. package/src/modules/shipping_carriers/lib/shipping-service.ts +47 -22
  1136. package/src/modules/shipping_carriers/lib/upstream-error-response.ts +4 -1
  1137. package/src/modules/shipping_carriers/workers/webhook-processor.ts +4 -5
  1138. package/src/modules/staff/api/comments.ts +4 -1
  1139. package/src/modules/staff/api/leave-requests/accept/route.ts +4 -1
  1140. package/src/modules/staff/api/leave-requests/reject/route.ts +4 -1
  1141. package/src/modules/staff/api/team-members/assignable/route.ts +4 -1
  1142. package/src/modules/staff/api/team-members/self/route.ts +5 -2
  1143. package/src/modules/staff/api/team-members/tags/assign/route.ts +4 -1
  1144. package/src/modules/staff/api/team-members/tags/unassign/route.ts +4 -1
  1145. package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +4 -1
  1146. package/src/modules/staff/api/timesheets/my-projects/route.ts +4 -1
  1147. package/src/modules/staff/api/timesheets/projects/kpis/route.ts +4 -1
  1148. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +4 -1
  1149. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +5 -2
  1150. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +5 -2
  1151. package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +4 -1
  1152. package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +4 -1
  1153. package/src/modules/staff/backend/staff/team-members/page.tsx +5 -2
  1154. package/src/modules/staff/backend/staff/team-roles/page.tsx +5 -2
  1155. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +5 -2
  1156. package/src/modules/staff/backend/staff/teams/page.tsx +5 -2
  1157. package/src/modules/staff/backend/staff/timesheets/page.tsx +8 -5
  1158. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -1
  1159. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +5 -2
  1160. package/src/modules/staff/commands/timesheets-entries.ts +4 -1
  1161. package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.tsx +4 -1
  1162. package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.tsx +6 -3
  1163. package/src/modules/sync_excel/api/import/route.ts +4 -1
  1164. package/src/modules/translations/api/[entityType]/[entityId]/route.ts +6 -3
  1165. package/src/modules/translations/api/get/locales.ts +4 -1
  1166. package/src/modules/translations/api/put/locales.ts +4 -1
  1167. package/src/modules/translations/components/TranslationManager.tsx +4 -1
  1168. package/src/modules/translations/subscribers/cleanup.ts +4 -1
  1169. package/src/modules/workflows/api/definitions/[id]/customize/route.ts +5 -2
  1170. package/src/modules/workflows/api/definitions/[id]/reset-to-code/route.ts +5 -2
  1171. package/src/modules/workflows/api/definitions/[id]/route.ts +7 -4
  1172. package/src/modules/workflows/api/definitions/route.ts +5 -2
  1173. package/src/modules/workflows/api/events/[id]/route.ts +4 -1
  1174. package/src/modules/workflows/api/events/route.ts +4 -1
  1175. package/src/modules/workflows/api/instances/[id]/advance/route.ts +4 -1
  1176. package/src/modules/workflows/api/instances/[id]/cancel/route.ts +4 -1
  1177. package/src/modules/workflows/api/instances/[id]/events/route.ts +4 -1
  1178. package/src/modules/workflows/api/instances/[id]/retry/route.ts +4 -1
  1179. package/src/modules/workflows/api/instances/[id]/route.ts +4 -1
  1180. package/src/modules/workflows/api/instances/[id]/signal/route.ts +6 -7
  1181. package/src/modules/workflows/api/instances/route.ts +6 -3
  1182. package/src/modules/workflows/api/instances/validate-start/route.ts +4 -1
  1183. package/src/modules/workflows/api/signals/route.ts +4 -1
  1184. package/src/modules/workflows/api/tasks/[id]/claim/route.ts +4 -1
  1185. package/src/modules/workflows/api/tasks/[id]/complete/route.ts +4 -1
  1186. package/src/modules/workflows/api/tasks/[id]/route.ts +4 -1
  1187. package/src/modules/workflows/api/tasks/route.ts +4 -1
  1188. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +5 -2
  1189. package/src/modules/workflows/backend/instances/[id]/page.tsx +6 -3
  1190. package/src/modules/workflows/backend/tasks/[id]/page.tsx +4 -1
  1191. package/src/modules/workflows/components/BusinessRulesSelector.tsx +6 -3
  1192. package/src/modules/workflows/components/WorkflowSelector.tsx +6 -3
  1193. package/src/modules/workflows/components/fields/BusinessRuleConditionsEditor.tsx +4 -1
  1194. package/src/modules/workflows/components/fields/StartPreConditionsEditor.tsx +4 -1
  1195. package/src/modules/workflows/components/fields/WorkflowSelectorField.tsx +4 -1
  1196. package/src/modules/workflows/frontend/checkout-demo/page.tsx +9 -7
  1197. package/src/modules/workflows/lib/activity-executor.ts +26 -21
  1198. package/src/modules/workflows/lib/activity-worker-handler.ts +9 -15
  1199. package/src/modules/workflows/lib/code-registry.ts +8 -7
  1200. package/src/modules/workflows/lib/compensation-handler.ts +5 -2
  1201. package/src/modules/workflows/lib/edgeFormTransforms.ts +4 -1
  1202. package/src/modules/workflows/lib/event-trigger-service.ts +6 -3
  1203. package/src/modules/workflows/lib/nodeFormTransforms.ts +4 -1
  1204. package/src/modules/workflows/lib/seeds.ts +17 -2
  1205. package/src/modules/workflows/lib/signal-handler.ts +4 -1
  1206. package/src/modules/workflows/lib/start-validator.ts +10 -3
  1207. package/src/modules/workflows/lib/step-handler.ts +4 -1
  1208. package/src/modules/workflows/lib/task-handler.ts +4 -1
  1209. package/src/modules/workflows/lib/transition-handler.ts +6 -3
  1210. package/src/modules/workflows/lib/workflow-executor.ts +28 -15
  1211. package/src/modules/workflows/subscribers/event-trigger.ts +22 -24
  1212. package/src/modules/workflows/subscribers/task-assigned-notification.ts +4 -1
  1213. package/src/modules/workflows/workers/workflow-activities.worker.ts +31 -20
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/communication_channels/workers/poll-channel.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, getCommunicationChannelsQueue } from '../lib/queue'\nimport { preservePushState } from '../lib/push-state'\nimport { writeIngestDeadLetter } from '../lib/dead-letter'\nimport { classifyOutboundError, computeBackoffMs, isReauthError } from '../lib/error-classification'\nimport { refreshCredentialsIfNeeded } from '../lib/credential-refresh'\nimport { emitCommunicationChannelsEvent } from '../events'\nimport type { ChannelAdapterRegistry } from '../lib/registry'\nimport type { NormalizedInboundMessage } from '../lib/adapter'\n\n/**\n * Job payload for the `communication-channels-poll` queue.\n *\n * One job per channel per scheduler tick. The poll-tick worker (sibling file)\n * enumerates due channels and enqueues these jobs.\n */\nexport type PollChannelJobPayload = {\n channelId: string\n scope: {\n tenantId: string\n organizationId: string | null\n }\n /** Attempt count, 1-based; used for retry-backoff decisions. */\n attempt?: number\n /** Self-re-enqueue drain counter (bounds the multi-page `hasMore` drain loop). */\n drainPage?: number\n}\n\nexport const POLL_CHANNEL_MAX_ATTEMPTS = 3\n\n/**\n * Hard cap on `hasMore` self-re-enqueue drain pages \u2014 guards against an adapter\n * that returns `hasMore: true` with a non-advancing (pinned) cursor, which would\n * otherwise spin a tight, unthrottled re-enqueue loop. Mirrors the same guard in\n * `gmail-history-sync`.\n */\nconst MAX_DRAIN_PAGES = 100\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.poll,\n id: 'communication_channels:poll-channel',\n concurrency: 10,\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\n/**\n * Poll a single channel for inbound messages.\n *\n * Per SPEC-045d \u00A7 6 (with email-spec \u00A7 Hub Deltas \u2192 Delta 6 polling extensions):\n * 1. Load the channel; skip when `is_active === false` or `status !== 'connected'`.\n * 2. Skip when the adapter declares `realtimePush !== false` (it doesn't want polling).\n * 3. Refresh credentials if OAuth and within the expiry window.\n * 4. Call `adapter.fetchHistory({ channelId, credentials, since: lastPolledAt })`.\n * 5. For each normalized message, dispatch `ingest_inbound_message` (idempotent on\n * `(channel_id, external_message_id)`).\n * 6. Update `channel.lastPolledAt = NOW()` on success.\n * 7. On error: classify, set `channel.status` accordingly, set `last_error`, emit\n * `channel.requires_reauth` on 401, retry transient failures up to MAX.\n *\n * The hub doesn't drain a remote mailbox indefinitely \u2014 `fetchHistory` returns a\n * single page (provider decides the size). If the provider has more, the next tick\n * picks it up after the configured `poll_interval_seconds`.\n */\nexport default async function handle(\n job: QueuedJob<PollChannelJobPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n const { channelId, scope, attempt = 1, drainPage = 0 } = 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(\n `[communication_channels:poll-channel] channel ${channelId} not found (skipping)`,\n )\n return\n }\n if (!channel.isActive) return\n // Allow `connected` (normal poll) and `error` (Spec B \u00A7 B5 auto-recovery\n // sweep enqueues these intentionally \u2014 on a successful poll below we\n // flip them back to `connected`). `requires_reauth` and `disconnected`\n // are owned by the credential-refresh and disconnect flows.\n if (channel.status !== 'connected' && channel.status !== 'error') return\n\n const adapter = adapterRegistry?.get(channel.providerKey)\n if (!adapter) {\n console.warn(\n `[communication_channels:poll-channel] no adapter for provider '${channel.providerKey}' (channel ${channelId})`,\n )\n return\n }\n // Adapter opted out of polling \u2014 webhook providers.\n const capabilities = (channel.capabilities as { realtimePush?: boolean } | null) ?? null\n if (capabilities?.realtimePush !== false) {\n // realtimePush is `true` (default for back-compat) \u2014 don't poll push providers.\n return\n }\n if (typeof adapter.fetchHistory !== 'function') {\n // Adapter doesn't implement history fetching \u2014 nothing we can do.\n return\n }\n\n // Credentials.\n let credentialsService: CredentialsServiceLike | null = null\n try {\n credentialsService = ctx.resolve<CredentialsServiceLike>('integrationCredentialsService')\n } catch {\n credentialsService = null\n }\n // Per-user credentials scope: pass `channel.userId` so the credentials\n // service returns this user's row, not whoever connected the provider last.\n // See review R2-C1 / N1 (2026-05-26).\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 {\n adapter,\n channelId: channel.id,\n credentials,\n scope: credentialsScope,\n },\n { credentialsService },\n )\n credentials = refreshed.credentials\n\n // Fetch a single page of history.\n // `channelState` is the provider-specific resumption cursor \u2014 Gmail historyId,\n // IMAP UIDVALIDITY+UIDNEXT, etc. We persist it across\n // ticks on `channel.channelState` so each poll resumes from the prior one\n // instead of running a full mailbox resync. Empty / NULL = \"first poll;\n // bootstrap the cursor from the provider\".\n let normalized: NormalizedInboundMessage[] = []\n let nextCursor: string | undefined\n let hasMore = false\n try {\n const result = await adapter.fetchHistory({\n conversationId: channel.externalIdentifier ?? channel.id,\n credentials,\n cursor: channel.lastPolledAt ? channel.lastPolledAt.toISOString() : undefined,\n channelState: (channel.channelState as Record<string, unknown> | null) ?? undefined,\n scope: {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? scope.tenantId,\n },\n // `contactFilter.sinceDays` is a hint to provider adapters about how far\n // back to look on first-poll bootstrap. For UID-incremental polls (every\n // tick after the first), adapters ignore this and just fetch new mail\n // since the persisted cursor.\n contactFilter: { addresses: [], sinceDays: 7 },\n })\n normalized = Array.isArray(result?.messages) ? result.messages : []\n nextCursor = result?.nextCursor\n hasMore = result?.hasMore === true\n } catch (err) {\n await handlePollError(err, em, channel, scope, attempt, job.payload)\n return\n }\n\n // Dispatch ingest commands for each message.\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 // Spec B \u00A7 Per-message commit + dead-letter:\n // - Permanent failure (malformed MIME, schema, contract violation):\n // write to channel_ingest_dead_letter, log, advance cursor anyway so\n // the bad blob never stalls the channel again.\n // - Transient failure (DB drop, network blip): abort the loop without\n // advancing the cursor. The next tick re-fetches the same page;\n // idempotency via the (channel_id, external_message_id) unique\n // constraint means already-ingested messages no-op on the retry.\n let transientIngestAbort = false\n for (const message of normalized) {\n try {\n const input: IngestInboundMessageInput = {\n channelId: channel.id,\n providerKey: channel.providerKey,\n channelType: channel.channelType,\n scope: {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? null,\n },\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 console.warn(\n `[communication_channels:poll-channel] transient ingest failure for channel ${channel.id}; aborting page so cursor is NOT advanced. Reason: ${classification.message}`,\n )\n transientIngestAbort = true\n break\n }\n // Permanent \u2014 write to dead-letter so an operator can replay later.\n // The shared helper is best-effort (never throws) and idempotent on\n // `(channelId, externalMessageId)`, so a replayed page that fails the\n // same message again does not insert a duplicate row.\n await writeIngestDeadLetter({\n em,\n scope,\n channel,\n message,\n err,\n errorMessage: classification.message,\n })\n console.warn(\n `[communication_channels:poll-channel] permanent ingest failure for channel ${channel.id}; recorded in dead-letter and advancing cursor past message ${message.externalMessageId}. Reason: ${classification.message}`,\n )\n }\n }\n\n // Transient abort: keep prior cursor + lastPolledAt so the next tick\n // re-fetches the same page (idempotent at the DB layer).\n if (transientIngestAbort) {\n channel.lastError = 'transient_ingest_failure'\n await em.flush()\n return\n }\n\n // Update poll cursor + clear any stale error state.\n channel.lastPolledAt = new Date()\n if (channel.lastError) channel.lastError = null\n // Recover the channel from any prior non-fatal error state. The previous\n // poll(s) may have set status='error' after exhausting transient-retry\n // attempts, but a fresh successful poll means the upstream is healthy\n // again \u2014 flip it back to 'connected' so the scheduler keeps it in\n // rotation and the user doesn't have to manually reconnect.\n // We DON'T touch 'requires_reauth' here (that lifecycle state is owned\n // by the credential-refresh / OAuth flow) or 'disconnected' (owned by\n // the cascade-on-user-delete subscriber).\n if (channel.status === 'error') {\n channel.status = 'connected'\n }\n // Providers encode their cursor as base64-encoded JSON in `nextCursor`; we\n // decode it back to an object so the next tick can pass it straight into\n // `fetchHistory` as `channelState`. Decode failures fall back to the prior\n // state (next tick bootstraps from there).\n //\n // Push-delivery state (Spec C) \u2014 watch/subscription identifiers and expiry \u2014 is\n // owned by the push register/renew commands, not the sync cursor. A provider's\n // `fetchHistory` returns only sync-cursor fields, so persisting the decoded\n // cursor as a full replace would silently wipe push state and stop\n // `gmail-renew-watch` from renewing it. Carry\n // the hub-owned push keys forward whenever the new cursor omits them.\n if (typeof nextCursor === 'string' && nextCursor.length > 0) {\n const decoded = decodeChannelStateCursor(nextCursor)\n if (decoded) {\n channel.channelState = preservePushState(channel.channelState, decoded)\n }\n }\n await em.flush()\n\n // Drain contract (review H1, 2026-05-26): adapters that have additional\n // pages beyond this tick's batch (large mailboxes, mid-deltaLink walks,\n // UID overflows) signal `hasMore: true`. The persisted `channelState`\n // already encodes the mid-drain resumption token (Gmail `pendingHistoryPageToken`,\n // IMAP non-terminal `uidNext`). Re-enqueue with\n // a small delay so we keep draining without overrunning rate limits.\n if (hasMore) {\n // Bound the drain: an adapter that returns `hasMore: true` with a\n // non-advancing cursor (e.g. a persistently-failing message that pins the\n // Gmail cursor via `hardFailed`) must not spin an unthrottled loop. Stop\n // at MAX_DRAIN_PAGES; the next scheduled poll tick re-checks the channel.\n if (drainPage < MAX_DRAIN_PAGES) {\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.poll)\n await queue.enqueue(\n { channelId: channel.id, scope, attempt: 1, drainPage: drainPage + 1 } as unknown as Record<string, unknown>,\n { delayMs: 250 },\n )\n } else {\n console.warn(\n `[communication_channels:poll-channel] drain page cap (${MAX_DRAIN_PAGES}) reached for channel ${channel.id}; stopping re-enqueue until the next scheduled tick`,\n )\n }\n }\n}\n\nfunction decodeChannelStateCursor(cursor: string): Record<string, unknown> | null {\n try {\n const decoded = Buffer.from(cursor, 'base64').toString('utf8')\n const parsed = JSON.parse(decoded) as unknown\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>\n }\n return null\n } catch {\n return null\n }\n}\n\nasync function handlePollError(\n err: unknown,\n em: EntityManager,\n channel: CommunicationChannel,\n scope: PollChannelJobPayload['scope'],\n attempt: number,\n payload: PollChannelJobPayload,\n): Promise<void> {\n const classification = classifyOutboundError(err)\n channel.lastError = classification.message\n\n if (isReauthError(classification)) {\n channel.status = 'requires_reauth'\n await em.flush()\n await emitCommunicationChannelsEvent(\n 'communication_channels.channel.requires_reauth',\n {\n channelId: channel.id,\n providerKey: channel.providerKey,\n channelType: channel.channelType,\n reason: classification.message,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? null,\n },\n { persistent: true },\n )\n return\n }\n\n if (classification.transient && attempt < POLL_CHANNEL_MAX_ATTEMPTS) {\n // Transient error \u2014 re-enqueue with backoff. Channel status stays\n // `connected` so the scheduler keeps it in rotation.\n await em.flush()\n const next: PollChannelJobPayload = { ...payload, attempt: attempt + 1 }\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.poll)\n await queue.enqueue(next as unknown as Record<string, unknown>, {\n delayMs: computeBackoffMs(attempt),\n })\n return\n }\n\n // Permanent or attempts exhausted \u2014 mark channel as error and stop the loop.\n channel.status = 'error'\n await em.flush()\n}\n"],
5
- "mappings": "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",
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, getCommunicationChannelsQueue } from '../lib/queue'\nimport { preservePushState } from '../lib/push-state'\nimport { writeIngestDeadLetter } from '../lib/dead-letter'\nimport { classifyOutboundError, computeBackoffMs, isReauthError } from '../lib/error-classification'\nimport { refreshCredentialsIfNeeded } from '../lib/credential-refresh'\nimport { emitCommunicationChannelsEvent } from '../events'\nimport type { ChannelAdapterRegistry } from '../lib/registry'\nimport type { NormalizedInboundMessage } from '../lib/adapter'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('communication_channels').child({ component: 'poll-channel' })\n\n/**\n * Job payload for the `communication-channels-poll` queue.\n *\n * One job per channel per scheduler tick. The poll-tick worker (sibling file)\n * enumerates due channels and enqueues these jobs.\n */\nexport type PollChannelJobPayload = {\n channelId: string\n scope: {\n tenantId: string\n organizationId: string | null\n }\n /** Attempt count, 1-based; used for retry-backoff decisions. */\n attempt?: number\n /** Self-re-enqueue drain counter (bounds the multi-page `hasMore` drain loop). */\n drainPage?: number\n}\n\nexport const POLL_CHANNEL_MAX_ATTEMPTS = 3\n\n/**\n * Hard cap on `hasMore` self-re-enqueue drain pages \u2014 guards against an adapter\n * that returns `hasMore: true` with a non-advancing (pinned) cursor, which would\n * otherwise spin a tight, unthrottled re-enqueue loop. Mirrors the same guard in\n * `gmail-history-sync`.\n */\nconst MAX_DRAIN_PAGES = 100\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.poll,\n id: 'communication_channels:poll-channel',\n concurrency: 10,\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\n/**\n * Poll a single channel for inbound messages.\n *\n * Per SPEC-045d \u00A7 6 (with email-spec \u00A7 Hub Deltas \u2192 Delta 6 polling extensions):\n * 1. Load the channel; skip when `is_active === false` or `status !== 'connected'`.\n * 2. Skip when the adapter declares `realtimePush !== false` (it doesn't want polling).\n * 3. Refresh credentials if OAuth and within the expiry window.\n * 4. Call `adapter.fetchHistory({ channelId, credentials, since: lastPolledAt })`.\n * 5. For each normalized message, dispatch `ingest_inbound_message` (idempotent on\n * `(channel_id, external_message_id)`).\n * 6. Update `channel.lastPolledAt = NOW()` on success.\n * 7. On error: classify, set `channel.status` accordingly, set `last_error`, emit\n * `channel.requires_reauth` on 401, retry transient failures up to MAX.\n *\n * The hub doesn't drain a remote mailbox indefinitely \u2014 `fetchHistory` returns a\n * single page (provider decides the size). If the provider has more, the next tick\n * picks it up after the configured `poll_interval_seconds`.\n */\nexport default async function handle(\n job: QueuedJob<PollChannelJobPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n const { channelId, scope, attempt = 1, drainPage = 0 } = 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 logger.warn('channel not found (skipping)', { channelId })\n return\n }\n if (!channel.isActive) return\n // Allow `connected` (normal poll) and `error` (Spec B \u00A7 B5 auto-recovery\n // sweep enqueues these intentionally \u2014 on a successful poll below we\n // flip them back to `connected`). `requires_reauth` and `disconnected`\n // are owned by the credential-refresh and disconnect flows.\n if (channel.status !== 'connected' && channel.status !== 'error') return\n\n const adapter = adapterRegistry?.get(channel.providerKey)\n if (!adapter) {\n logger.warn('no adapter for provider', { providerKey: channel.providerKey, channelId })\n return\n }\n // Adapter opted out of polling \u2014 webhook providers.\n const capabilities = (channel.capabilities as { realtimePush?: boolean } | null) ?? null\n if (capabilities?.realtimePush !== false) {\n // realtimePush is `true` (default for back-compat) \u2014 don't poll push providers.\n return\n }\n if (typeof adapter.fetchHistory !== 'function') {\n // Adapter doesn't implement history fetching \u2014 nothing we can do.\n return\n }\n\n // Credentials.\n let credentialsService: CredentialsServiceLike | null = null\n try {\n credentialsService = ctx.resolve<CredentialsServiceLike>('integrationCredentialsService')\n } catch {\n credentialsService = null\n }\n // Per-user credentials scope: pass `channel.userId` so the credentials\n // service returns this user's row, not whoever connected the provider last.\n // See review R2-C1 / N1 (2026-05-26).\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 {\n adapter,\n channelId: channel.id,\n credentials,\n scope: credentialsScope,\n },\n { credentialsService },\n )\n credentials = refreshed.credentials\n\n // Fetch a single page of history.\n // `channelState` is the provider-specific resumption cursor \u2014 Gmail historyId,\n // IMAP UIDVALIDITY+UIDNEXT, etc. We persist it across\n // ticks on `channel.channelState` so each poll resumes from the prior one\n // instead of running a full mailbox resync. Empty / NULL = \"first poll;\n // bootstrap the cursor from the provider\".\n let normalized: NormalizedInboundMessage[] = []\n let nextCursor: string | undefined\n let hasMore = false\n try {\n const result = await adapter.fetchHistory({\n conversationId: channel.externalIdentifier ?? channel.id,\n credentials,\n cursor: channel.lastPolledAt ? channel.lastPolledAt.toISOString() : undefined,\n channelState: (channel.channelState as Record<string, unknown> | null) ?? undefined,\n scope: {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? scope.tenantId,\n },\n // `contactFilter.sinceDays` is a hint to provider adapters about how far\n // back to look on first-poll bootstrap. For UID-incremental polls (every\n // tick after the first), adapters ignore this and just fetch new mail\n // since the persisted cursor.\n contactFilter: { addresses: [], sinceDays: 7 },\n })\n normalized = Array.isArray(result?.messages) ? result.messages : []\n nextCursor = result?.nextCursor\n hasMore = result?.hasMore === true\n } catch (err) {\n await handlePollError(err, em, channel, scope, attempt, job.payload)\n return\n }\n\n // Dispatch ingest commands for each message.\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 // Spec B \u00A7 Per-message commit + dead-letter:\n // - Permanent failure (malformed MIME, schema, contract violation):\n // write to channel_ingest_dead_letter, log, advance cursor anyway so\n // the bad blob never stalls the channel again.\n // - Transient failure (DB drop, network blip): abort the loop without\n // advancing the cursor. The next tick re-fetches the same page;\n // idempotency via the (channel_id, external_message_id) unique\n // constraint means already-ingested messages no-op on the retry.\n let transientIngestAbort = false\n for (const message of normalized) {\n try {\n const input: IngestInboundMessageInput = {\n channelId: channel.id,\n providerKey: channel.providerKey,\n channelType: channel.channelType,\n scope: {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? null,\n },\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 logger.warn('transient ingest failure; aborting page so cursor is not advanced', { channelId: channel.id, reason: classification.message })\n transientIngestAbort = true\n break\n }\n // Permanent \u2014 write to dead-letter so an operator can replay later.\n // The shared helper is best-effort (never throws) and idempotent on\n // `(channelId, externalMessageId)`, so a replayed page that fails the\n // same message again does not insert a duplicate row.\n await writeIngestDeadLetter({\n em,\n scope,\n channel,\n message,\n err,\n errorMessage: classification.message,\n })\n logger.warn('permanent ingest failure; recorded in dead-letter and advancing cursor', { channelId: channel.id, externalMessageId: message.externalMessageId, reason: classification.message })\n }\n }\n\n // Transient abort: keep prior cursor + lastPolledAt so the next tick\n // re-fetches the same page (idempotent at the DB layer).\n if (transientIngestAbort) {\n channel.lastError = 'transient_ingest_failure'\n await em.flush()\n return\n }\n\n // Update poll cursor + clear any stale error state.\n channel.lastPolledAt = new Date()\n if (channel.lastError) channel.lastError = null\n // Recover the channel from any prior non-fatal error state. The previous\n // poll(s) may have set status='error' after exhausting transient-retry\n // attempts, but a fresh successful poll means the upstream is healthy\n // again \u2014 flip it back to 'connected' so the scheduler keeps it in\n // rotation and the user doesn't have to manually reconnect.\n // We DON'T touch 'requires_reauth' here (that lifecycle state is owned\n // by the credential-refresh / OAuth flow) or 'disconnected' (owned by\n // the cascade-on-user-delete subscriber).\n if (channel.status === 'error') {\n channel.status = 'connected'\n }\n // Providers encode their cursor as base64-encoded JSON in `nextCursor`; we\n // decode it back to an object so the next tick can pass it straight into\n // `fetchHistory` as `channelState`. Decode failures fall back to the prior\n // state (next tick bootstraps from there).\n //\n // Push-delivery state (Spec C) \u2014 watch/subscription identifiers and expiry \u2014 is\n // owned by the push register/renew commands, not the sync cursor. A provider's\n // `fetchHistory` returns only sync-cursor fields, so persisting the decoded\n // cursor as a full replace would silently wipe push state and stop\n // `gmail-renew-watch` from renewing it. Carry\n // the hub-owned push keys forward whenever the new cursor omits them.\n if (typeof nextCursor === 'string' && nextCursor.length > 0) {\n const decoded = decodeChannelStateCursor(nextCursor)\n if (decoded) {\n channel.channelState = preservePushState(channel.channelState, decoded)\n }\n }\n await em.flush()\n\n // Drain contract (review H1, 2026-05-26): adapters that have additional\n // pages beyond this tick's batch (large mailboxes, mid-deltaLink walks,\n // UID overflows) signal `hasMore: true`. The persisted `channelState`\n // already encodes the mid-drain resumption token (Gmail `pendingHistoryPageToken`,\n // IMAP non-terminal `uidNext`). Re-enqueue with\n // a small delay so we keep draining without overrunning rate limits.\n if (hasMore) {\n // Bound the drain: an adapter that returns `hasMore: true` with a\n // non-advancing cursor (e.g. a persistently-failing message that pins the\n // Gmail cursor via `hardFailed`) must not spin an unthrottled loop. Stop\n // at MAX_DRAIN_PAGES; the next scheduled poll tick re-checks the channel.\n if (drainPage < MAX_DRAIN_PAGES) {\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.poll)\n await queue.enqueue(\n { channelId: channel.id, scope, attempt: 1, drainPage: drainPage + 1 } as unknown as Record<string, unknown>,\n { delayMs: 250 },\n )\n } else {\n logger.warn('drain page cap reached; stopping re-enqueue until the next scheduled tick', { maxDrainPages: MAX_DRAIN_PAGES, channelId: channel.id })\n }\n }\n}\n\nfunction decodeChannelStateCursor(cursor: string): Record<string, unknown> | null {\n try {\n const decoded = Buffer.from(cursor, 'base64').toString('utf8')\n const parsed = JSON.parse(decoded) as unknown\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>\n }\n return null\n } catch {\n return null\n }\n}\n\nasync function handlePollError(\n err: unknown,\n em: EntityManager,\n channel: CommunicationChannel,\n scope: PollChannelJobPayload['scope'],\n attempt: number,\n payload: PollChannelJobPayload,\n): Promise<void> {\n const classification = classifyOutboundError(err)\n channel.lastError = classification.message\n\n if (isReauthError(classification)) {\n channel.status = 'requires_reauth'\n await em.flush()\n await emitCommunicationChannelsEvent(\n 'communication_channels.channel.requires_reauth',\n {\n channelId: channel.id,\n providerKey: channel.providerKey,\n channelType: channel.channelType,\n reason: classification.message,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? null,\n },\n { persistent: true },\n )\n return\n }\n\n if (classification.transient && attempt < POLL_CHANNEL_MAX_ATTEMPTS) {\n // Transient error \u2014 re-enqueue with backoff. Channel status stays\n // `connected` so the scheduler keeps it in rotation.\n await em.flush()\n const next: PollChannelJobPayload = { ...payload, attempt: attempt + 1 }\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.poll)\n await queue.enqueue(next as unknown as Record<string, unknown>, {\n delayMs: computeBackoffMs(attempt),\n })\n return\n }\n\n // Permanent or attempts exhausted \u2014 mark channel as error and stop the loop.\n channel.status = 'error'\n await em.flush()\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,8 @@
1
1
  import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
2
  import { CommunicationChannel } from "../data/entities.js";
3
3
  import { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from "../lib/queue.js";
4
+ import { createLogger } from "@open-mercato/shared/lib/logger";
5
+ const logger = createLogger("communication_channels").child({ component: "poll-tick" });
4
6
  const POLL_ENUMERATION_CAP = Math.max(
5
7
  1,
6
8
  Number.parseInt(process.env.COMMUNICATION_CHANNELS_POLL_ENUMERATION_CAP ?? "500", 10) || 500
@@ -16,9 +18,8 @@ async function handle(job, ctx) {
16
18
  const tenantId = raw.scope?.tenantId ?? raw.tenantId ?? null;
17
19
  const organizationId = raw.scope?.organizationId ?? raw.organizationId ?? null;
18
20
  if (!tenantId) {
19
- console.warn(
20
- "[communication_channels:poll-tick] skipping tick \u2014 payload has no tenantId",
21
- { payload: raw }
21
+ logger.warn(
22
+ "skipping tick \u2014 payload has no tenantId"
22
23
  );
23
24
  return;
24
25
  }
@@ -115,9 +116,7 @@ async function handle(job, ctx) {
115
116
  }
116
117
  if (recovered > 0) await em.flush();
117
118
  if (enqueued > 0 || recovered > 0) {
118
- console.log(
119
- `[communication_channels:poll-tick] enqueued ${enqueued} normal + ${recovered} auto-recover poll job(s) for tenant ${scope.tenantId}`
120
- );
119
+ logger.info("enqueued poll jobs for tenant", { enqueued, recovered, tenantId: scope.tenantId });
121
120
  }
122
121
  }
123
122
  function isDue(lastPolledAt, intervalSeconds, now) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/communication_channels/workers/poll-tick.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, getCommunicationChannelsQueue } from '../lib/queue'\nimport type { PollChannelJobPayload } from './poll-channel'\n\n/**\n * Scheduler tick payload. Fired by the `@open-mercato/scheduler` cron entry\n * registered in the hub's `setup.ts` (`communication_channels:poll-tick` schedule).\n */\nexport type PollTickPayload = {\n scope: {\n tenantId: string\n organizationId: string | null\n }\n}\n\nconst POLL_ENUMERATION_CAP = Math.max(\n 1,\n Number.parseInt(process.env.COMMUNICATION_CHANNELS_POLL_ENUMERATION_CAP ?? '500', 10) || 500,\n)\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.pollTick,\n id: 'communication_channels:poll-tick',\n concurrency: 1, // single-flight per tenant \u2014 one tick at a time\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\n/**\n * Enumerate channels due for polling and enqueue per-channel jobs.\n *\n * Per email integration spec \u00A7 Hub Deltas \u2192 Delta 6:\n * SELECT id FROM communication_channels\n * WHERE is_active = true\n * AND deleted_at IS NULL\n * AND status = 'connected'\n * AND poll_interval_seconds IS NOT NULL\n * AND (last_polled_at IS NULL\n * OR last_polled_at + poll_interval_seconds * '1 sec' <= NOW())\n * ORDER BY last_polled_at NULLS FIRST\n * LIMIT 500\n *\n * Implementation note: MikroORM's QueryBuilder is the canonical entry point;\n * the raw SQL above is the conceptual query. We use the entity-level filter to\n * keep things portable and let MikroORM compile it.\n */\nexport default async function handle(\n job: QueuedJob<PollTickPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n // The scheduler module (`@open-mercato/scheduler`) spreads the configured\n // `targetPayload` and then adds `tenantId` / `organizationId` at the TOP\n // level of the enqueued payload (see\n // packages/scheduler/.../execute-schedule.worker.ts). Our setup.ts originally\n // stored `{ scope: { tenantId, organizationId } }` under targetPayload, so\n // at runtime the payload looks like:\n // { scope: { tenantId, organizationId }, tenantId, organizationId, _idempotencyKey }\n // Accept either path so the handler is robust to both how operators originally\n // configured the schedule (nested `scope`) and how the scheduler flattens it.\n const raw = (job?.payload ?? {}) as Partial<PollTickPayload> & {\n tenantId?: string | null\n organizationId?: string | null\n }\n const tenantId = raw.scope?.tenantId ?? raw.tenantId ?? null\n const organizationId =\n raw.scope?.organizationId ?? raw.organizationId ?? null\n if (!tenantId) {\n console.warn(\n '[communication_channels:poll-tick] skipping tick \u2014 payload has no tenantId',\n { payload: raw },\n )\n return\n }\n const scope = { tenantId, organizationId }\n const em = (ctx.resolve('em') as EntityManager).fork()\n\n const now = new Date()\n // Find candidate channels \u2014 we enumerate two pools:\n // (1) status='connected' channels due for their normal poll cycle.\n // (2) Spec B \u00A7 Auto-recovery sweep: status='error' channels whose\n // `lastPolledAt` is older than OM_CHANNEL_AUTO_RECOVER_MINUTES\n // (default 30 min). At most one retry per recovery window per\n // channel \u2014 when we enqueue a recovery job below we bump that\n // channel's `lastPolledAt` to `now` so it falls back under the\n // cutoff and is NOT re-selected on the immediately-following ticks\n // (poll-channel only advances `lastPolledAt` on a SUCCESSFUL poll,\n // so without this a persistently-failing channel would re-enqueue\n // every tick). On success `poll-channel` flips the status back to\n // 'connected' so the channel rejoins the normal pool.\n //\n // Due-ness for (1) is computed in JS to avoid cross-DB interval\n // arithmetic; the (2) cutoff is a single timestamp compare.\n const connectedCandidates = await findWithDecryption(\n em,\n CommunicationChannel,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? null,\n isActive: true,\n deletedAt: null,\n status: 'connected',\n pollIntervalSeconds: { $ne: null },\n },\n {\n limit: POLL_ENUMERATION_CAP,\n orderBy: { lastPolledAt: 'asc' },\n },\n scope,\n )\n\n const recoverMinutesRaw = Number.parseInt(\n process.env.OM_CHANNEL_AUTO_RECOVER_MINUTES ?? '',\n 10,\n )\n // `0` is a valid override meaning \"recover on the very next tick\" (used by\n // TC-CHANNEL-EMAIL-027 and operators who want aggressive recovery). Only a\n // negative or non-numeric value falls back to the 30-minute default.\n const recoverMinutes =\n Number.isFinite(recoverMinutesRaw) && recoverMinutesRaw >= 0 ? recoverMinutesRaw : 30\n const recoverCutoff = new Date(now.getTime() - recoverMinutes * 60 * 1000)\n const errorCandidates = await findWithDecryption(\n em,\n CommunicationChannel,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? null,\n isActive: true,\n deletedAt: null,\n status: 'error',\n // Polling channels only. Push-only channels (Gmail) have\n // `pollIntervalSeconds = null` and are intentionally excluded: poll-channel\n // returns early for push providers, so their recovery is owner-driven\n // (re-register push / reconnect), not this poll-recovery sweep.\n pollIntervalSeconds: { $ne: null },\n // `lastPolledAt` advances only on a SUCCESSFUL poll (poll-channel does\n // not touch it in `handlePollError`). To keep recovery to one retry per\n // window \u2014 rather than re-enqueuing a persistently-failing channel every\n // tick \u2014 the recovery-enqueue loop below bumps `lastPolledAt` to `now`\n // when it schedules a recovery job, so the channel only re-enters this\n // pool after another `recoverMinutes` have elapsed.\n //\n // A channel that fails its FIRST poll (before any success) still has\n // `lastPolledAt = null`; a bare `$lt` would exclude it forever (SQL\n // `NULL < ts` is NULL, not true), stranding it in `error`. Include the\n // null case so never-polled error channels get their first recovery\n // attempt on the next tick (the enqueue bump below then throttles it).\n $or: [{ lastPolledAt: null }, { lastPolledAt: { $lt: recoverCutoff } }],\n },\n {\n limit: POLL_ENUMERATION_CAP,\n orderBy: { lastPolledAt: 'asc' },\n },\n scope,\n )\n\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.poll)\n let enqueued = 0\n let recovered = 0\n for (const channel of connectedCandidates as CommunicationChannel[]) {\n const intervalSeconds = channel.pollIntervalSeconds\n if (!intervalSeconds || intervalSeconds <= 0) continue\n if (!isDue(channel.lastPolledAt ?? null, intervalSeconds, now)) continue\n const payload: PollChannelJobPayload = {\n channelId: channel.id,\n scope: {\n tenantId: channel.tenantId,\n organizationId: channel.organizationId ?? scope.organizationId ?? null,\n },\n attempt: 1,\n }\n await queue.enqueue(payload as unknown as Record<string, unknown>)\n enqueued += 1\n }\n // Auto-recovery: at most one retry per recovery window per error-state\n // channel. We bump `lastPolledAt` to `now` as we enqueue so the same channel\n // drops back under `recoverCutoff` and is NOT re-selected on the next ticks\n // (poll-channel leaves `lastPolledAt` untouched on failure, so without this a\n // persistently-failing channel would be re-enqueued every tick).\n for (const channel of errorCandidates as CommunicationChannel[]) {\n const payload: PollChannelJobPayload = {\n channelId: channel.id,\n scope: {\n tenantId: channel.tenantId,\n organizationId: channel.organizationId ?? scope.organizationId ?? null,\n },\n attempt: 1,\n }\n await queue.enqueue(payload as unknown as Record<string, unknown>)\n channel.lastPolledAt = now\n recovered += 1\n }\n if (recovered > 0) await em.flush()\n\n if (enqueued > 0 || recovered > 0) {\n console.log(\n `[communication_channels:poll-tick] enqueued ${enqueued} normal + ${recovered} auto-recover poll job(s) for tenant ${scope.tenantId}`,\n )\n }\n}\n\nfunction isDue(lastPolledAt: Date | null, intervalSeconds: number, now: Date): boolean {\n if (!lastPolledAt) return true\n const dueAt = new Date(lastPolledAt.getTime() + intervalSeconds * 1000)\n return now >= dueAt\n}\n"],
5
- "mappings": "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",
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, getCommunicationChannelsQueue } from '../lib/queue'\nimport type { PollChannelJobPayload } from './poll-channel'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('communication_channels').child({ component: 'poll-tick' })\n\n/**\n * Scheduler tick payload. Fired by the `@open-mercato/scheduler` cron entry\n * registered in the hub's `setup.ts` (`communication_channels:poll-tick` schedule).\n */\nexport type PollTickPayload = {\n scope: {\n tenantId: string\n organizationId: string | null\n }\n}\n\nconst POLL_ENUMERATION_CAP = Math.max(\n 1,\n Number.parseInt(process.env.COMMUNICATION_CHANNELS_POLL_ENUMERATION_CAP ?? '500', 10) || 500,\n)\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.pollTick,\n id: 'communication_channels:poll-tick',\n concurrency: 1, // single-flight per tenant \u2014 one tick at a time\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\n/**\n * Enumerate channels due for polling and enqueue per-channel jobs.\n *\n * Per email integration spec \u00A7 Hub Deltas \u2192 Delta 6:\n * SELECT id FROM communication_channels\n * WHERE is_active = true\n * AND deleted_at IS NULL\n * AND status = 'connected'\n * AND poll_interval_seconds IS NOT NULL\n * AND (last_polled_at IS NULL\n * OR last_polled_at + poll_interval_seconds * '1 sec' <= NOW())\n * ORDER BY last_polled_at NULLS FIRST\n * LIMIT 500\n *\n * Implementation note: MikroORM's QueryBuilder is the canonical entry point;\n * the raw SQL above is the conceptual query. We use the entity-level filter to\n * keep things portable and let MikroORM compile it.\n */\nexport default async function handle(\n job: QueuedJob<PollTickPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n // The scheduler module (`@open-mercato/scheduler`) spreads the configured\n // `targetPayload` and then adds `tenantId` / `organizationId` at the TOP\n // level of the enqueued payload (see\n // packages/scheduler/.../execute-schedule.worker.ts). Our setup.ts originally\n // stored `{ scope: { tenantId, organizationId } }` under targetPayload, so\n // at runtime the payload looks like:\n // { scope: { tenantId, organizationId }, tenantId, organizationId, _idempotencyKey }\n // Accept either path so the handler is robust to both how operators originally\n // configured the schedule (nested `scope`) and how the scheduler flattens it.\n const raw = (job?.payload ?? {}) as Partial<PollTickPayload> & {\n tenantId?: string | null\n organizationId?: string | null\n }\n const tenantId = raw.scope?.tenantId ?? raw.tenantId ?? null\n const organizationId =\n raw.scope?.organizationId ?? raw.organizationId ?? null\n if (!tenantId) {\n logger.warn(\n 'skipping tick \u2014 payload has no tenantId',\n )\n return\n }\n const scope = { tenantId, organizationId }\n const em = (ctx.resolve('em') as EntityManager).fork()\n\n const now = new Date()\n // Find candidate channels \u2014 we enumerate two pools:\n // (1) status='connected' channels due for their normal poll cycle.\n // (2) Spec B \u00A7 Auto-recovery sweep: status='error' channels whose\n // `lastPolledAt` is older than OM_CHANNEL_AUTO_RECOVER_MINUTES\n // (default 30 min). At most one retry per recovery window per\n // channel \u2014 when we enqueue a recovery job below we bump that\n // channel's `lastPolledAt` to `now` so it falls back under the\n // cutoff and is NOT re-selected on the immediately-following ticks\n // (poll-channel only advances `lastPolledAt` on a SUCCESSFUL poll,\n // so without this a persistently-failing channel would re-enqueue\n // every tick). On success `poll-channel` flips the status back to\n // 'connected' so the channel rejoins the normal pool.\n //\n // Due-ness for (1) is computed in JS to avoid cross-DB interval\n // arithmetic; the (2) cutoff is a single timestamp compare.\n const connectedCandidates = await findWithDecryption(\n em,\n CommunicationChannel,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? null,\n isActive: true,\n deletedAt: null,\n status: 'connected',\n pollIntervalSeconds: { $ne: null },\n },\n {\n limit: POLL_ENUMERATION_CAP,\n orderBy: { lastPolledAt: 'asc' },\n },\n scope,\n )\n\n const recoverMinutesRaw = Number.parseInt(\n process.env.OM_CHANNEL_AUTO_RECOVER_MINUTES ?? '',\n 10,\n )\n // `0` is a valid override meaning \"recover on the very next tick\" (used by\n // TC-CHANNEL-EMAIL-027 and operators who want aggressive recovery). Only a\n // negative or non-numeric value falls back to the 30-minute default.\n const recoverMinutes =\n Number.isFinite(recoverMinutesRaw) && recoverMinutesRaw >= 0 ? recoverMinutesRaw : 30\n const recoverCutoff = new Date(now.getTime() - recoverMinutes * 60 * 1000)\n const errorCandidates = await findWithDecryption(\n em,\n CommunicationChannel,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId ?? null,\n isActive: true,\n deletedAt: null,\n status: 'error',\n // Polling channels only. Push-only channels (Gmail) have\n // `pollIntervalSeconds = null` and are intentionally excluded: poll-channel\n // returns early for push providers, so their recovery is owner-driven\n // (re-register push / reconnect), not this poll-recovery sweep.\n pollIntervalSeconds: { $ne: null },\n // `lastPolledAt` advances only on a SUCCESSFUL poll (poll-channel does\n // not touch it in `handlePollError`). To keep recovery to one retry per\n // window \u2014 rather than re-enqueuing a persistently-failing channel every\n // tick \u2014 the recovery-enqueue loop below bumps `lastPolledAt` to `now`\n // when it schedules a recovery job, so the channel only re-enters this\n // pool after another `recoverMinutes` have elapsed.\n //\n // A channel that fails its FIRST poll (before any success) still has\n // `lastPolledAt = null`; a bare `$lt` would exclude it forever (SQL\n // `NULL < ts` is NULL, not true), stranding it in `error`. Include the\n // null case so never-polled error channels get their first recovery\n // attempt on the next tick (the enqueue bump below then throttles it).\n $or: [{ lastPolledAt: null }, { lastPolledAt: { $lt: recoverCutoff } }],\n },\n {\n limit: POLL_ENUMERATION_CAP,\n orderBy: { lastPolledAt: 'asc' },\n },\n scope,\n )\n\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.poll)\n let enqueued = 0\n let recovered = 0\n for (const channel of connectedCandidates as CommunicationChannel[]) {\n const intervalSeconds = channel.pollIntervalSeconds\n if (!intervalSeconds || intervalSeconds <= 0) continue\n if (!isDue(channel.lastPolledAt ?? null, intervalSeconds, now)) continue\n const payload: PollChannelJobPayload = {\n channelId: channel.id,\n scope: {\n tenantId: channel.tenantId,\n organizationId: channel.organizationId ?? scope.organizationId ?? null,\n },\n attempt: 1,\n }\n await queue.enqueue(payload as unknown as Record<string, unknown>)\n enqueued += 1\n }\n // Auto-recovery: at most one retry per recovery window per error-state\n // channel. We bump `lastPolledAt` to `now` as we enqueue so the same channel\n // drops back under `recoverCutoff` and is NOT re-selected on the next ticks\n // (poll-channel leaves `lastPolledAt` untouched on failure, so without this a\n // persistently-failing channel would be re-enqueued every tick).\n for (const channel of errorCandidates as CommunicationChannel[]) {\n const payload: PollChannelJobPayload = {\n channelId: channel.id,\n scope: {\n tenantId: channel.tenantId,\n organizationId: channel.organizationId ?? scope.organizationId ?? null,\n },\n attempt: 1,\n }\n await queue.enqueue(payload as unknown as Record<string, unknown>)\n channel.lastPolledAt = now\n recovered += 1\n }\n if (recovered > 0) await em.flush()\n\n if (enqueued > 0 || recovered > 0) {\n logger.info('enqueued poll jobs for tenant', { enqueued, recovered, tenantId: scope.tenantId })\n }\n}\n\nfunction isDue(lastPolledAt: Date | null, intervalSeconds: number, now: Date): boolean {\n if (!lastPolledAt) return true\n const dueAt = new Date(lastPolledAt.getTime() + intervalSeconds * 1000)\n return now >= dueAt\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -9,6 +9,8 @@ import {
9
9
  REACTION_PROCESSOR_MAX_ATTEMPTS
10
10
  } from "../lib/reaction-processor-types.js";
11
11
  import { refreshCredentialsIfNeeded } from "../lib/credential-refresh.js";
12
+ import { createLogger } from "@open-mercato/shared/lib/logger";
13
+ const logger = createLogger("communication_channels").child({ component: "reaction-processor" });
12
14
  const metadata = {
13
15
  queue: COMMUNICATION_CHANNELS_QUEUES.reactions,
14
16
  id: "communication_channels:reaction-processor",
@@ -161,22 +163,16 @@ async function callAdapterOutbound(payload, ctx, action) {
161
163
  async function maybeRetry(result, payload, ctx) {
162
164
  if (result.status === "ok") return;
163
165
  if (result.status === "no_adapter" || result.status === "channel_inactive") {
164
- console.error(
165
- `[communication_channels:reaction-processor] ${result.status}: ${result.message}`
166
- );
166
+ logger.error("reaction processing failed", { status: result.status, reason: result.message });
167
167
  return;
168
168
  }
169
169
  if (!result.transient) {
170
- console.error(
171
- `[communication_channels:reaction-processor] permanent failure: ${result.message}`
172
- );
170
+ logger.error("permanent reaction processing failure", { reason: result.message });
173
171
  return;
174
172
  }
175
173
  const attempt = payload.attempt ?? 1;
176
174
  if (attempt >= REACTION_PROCESSOR_MAX_ATTEMPTS) {
177
- console.error(
178
- `[communication_channels:reaction-processor] giving up after attempt ${attempt}: ${result.message}`
179
- );
175
+ logger.error("giving up on reaction processing", { attempt, reason: result.message });
180
176
  return;
181
177
  }
182
178
  const next = { ...payload, attempt: attempt + 1 };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/communication_channels/workers/reaction-processor.ts"],
4
- "sourcesContent": ["import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n COMMUNICATION_CHANNELS_PROCESS_INBOUND_REACTION_COMMAND_ID,\n type ProcessInboundReactionInput,\n type ProcessInboundReactionResult,\n} from '../commands/process-inbound-reaction'\nimport { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from '../lib/queue'\nimport { classifyOutboundError, computeBackoffMs } from '../lib/error-classification'\nimport { CommunicationChannel } from '../data/entities'\nimport {\n REACTION_PROCESSOR_MAX_ATTEMPTS,\n type ReactionInboundJob,\n type ReactionOutboundRemoveJob,\n type ReactionOutboundSendJob,\n type ReactionProcessorPayload,\n} from '../lib/reaction-processor-types'\nimport { refreshCredentialsIfNeeded } from '../lib/credential-refresh'\nimport type { ChannelAdapterRegistry } from '../lib/registry'\n\nexport type { ReactionProcessorPayload }\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.reactions,\n id: 'communication_channels:reaction-processor',\n concurrency: 10,\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\n/**\n * Unified reaction worker \u2014 handles inbound, outbound-send, and outbound-remove\n * jobs from the `communication-channels-reactions` queue via a discriminated payload.\n *\n * - `inbound` \u2014 dispatches the `process_inbound_reaction` command which\n * applies provider semantics and emits `.reaction.added/.removed`.\n * - `outbound_send` \u2014 calls `adapter.sendReaction?(...)`. The local\n * `MessageReaction` row was already created by the API/command layer\n * for UX responsiveness; this worker just notifies the provider.\n * - `outbound_remove` \u2014 calls `adapter.removeReaction?(...)`. The local\n * row was already deleted by the API/command layer.\n *\n * Retry: transient failures re-enqueue with exponential backoff up to\n * `REACTION_PROCESSOR_MAX_ATTEMPTS = 3`. Permanent failures stop. Reactions\n * are inherently low-stakes (a missed reaction is annoying, not data loss),\n * so we do not surface failures via a dedicated event; provider errors are\n * logged through `integrationLogService` when available.\n */\nexport default async function handle(\n job: QueuedJob<ReactionProcessorPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n switch (job.payload.kind) {\n case 'inbound':\n await handleInbound(job.payload, ctx)\n return\n case 'outbound_send':\n await handleOutboundSend(job.payload, ctx)\n return\n case 'outbound_remove':\n await handleOutboundRemove(job.payload, ctx)\n return\n default: {\n // exhaustiveness check\n const exhaustive: never = job.payload\n throw new Error(`Unknown reaction job kind: ${JSON.stringify(exhaustive)}`)\n }\n }\n}\n\nasync function handleInbound(payload: ReactionInboundJob, ctx: HandlerContext): Promise<void> {\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: payload.scope.organizationId ?? null,\n organizationIds: payload.scope.organizationId ? [payload.scope.organizationId] : null,\n }\n const input: ProcessInboundReactionInput = {\n channelId: payload.channelId,\n providerKey: payload.providerKey,\n channelType: payload.channelType,\n scope: payload.scope,\n event: payload.event,\n }\n await commandBus.execute<ProcessInboundReactionInput, ProcessInboundReactionResult>(\n COMMUNICATION_CHANNELS_PROCESS_INBOUND_REACTION_COMMAND_ID,\n { input, ctx: commandCtx as never },\n )\n}\n\nasync function handleOutboundSend(payload: ReactionOutboundSendJob, ctx: HandlerContext): Promise<void> {\n const result = await callAdapterOutbound(payload, ctx, 'send')\n await maybeRetry(result, payload, ctx)\n}\n\nasync function handleOutboundRemove(\n payload: ReactionOutboundRemoveJob,\n ctx: HandlerContext,\n): Promise<void> {\n const result = await callAdapterOutbound(payload, ctx, 'remove')\n await maybeRetry(result, payload, ctx)\n}\n\ntype AdapterCallResult =\n | { status: 'ok' }\n | { status: 'no_adapter'; message: string }\n | { status: 'channel_inactive'; message: string }\n | { status: 'failed'; transient: boolean; message: string }\n\nasync function callAdapterOutbound(\n payload: ReactionOutboundSendJob | ReactionOutboundRemoveJob,\n ctx: HandlerContext,\n action: 'send' | 'remove',\n): Promise<AdapterCallResult> {\n const adapterRegistry = ctx.resolve<ChannelAdapterRegistry>('channelAdapterRegistry')\n const adapter = adapterRegistry?.get(payload.providerKey)\n if (!adapter) {\n return { status: 'no_adapter', message: `No adapter for provider '${payload.providerKey}'` }\n }\n const em = (ctx.resolve('em') as EntityManager).fork()\n const channel = await findOneWithDecryption(\n em,\n CommunicationChannel,\n {\n id: payload.channelId,\n tenantId: payload.scope.tenantId,\n organizationId: payload.scope.organizationId ?? null,\n deletedAt: null,\n },\n undefined,\n payload.scope,\n )\n if (!channel) {\n return { status: 'no_adapter', message: `Channel ${payload.channelId} not found` }\n }\n if (!channel.isActive) {\n return { status: 'channel_inactive', message: `Channel ${payload.channelId} is inactive` }\n }\n\n // Credentials. Per-user scoping via `channel.userId` (review R2-C1 / N1).\n type 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 let credentials: Record<string, unknown> = {}\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: payload.scope.tenantId,\n organizationId: payload.scope.organizationId ?? payload.scope.tenantId,\n userId: channel.userId ?? null,\n }\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 {\n adapter,\n channelId: channel.id,\n credentials,\n scope: credentialsScope,\n },\n { credentialsService },\n )\n credentials = refreshed.credentials\n\n try {\n if (action === 'send') {\n if (typeof adapter.sendReaction !== 'function') {\n return { status: 'no_adapter', message: `Adapter '${adapter.providerKey}' has no sendReaction` }\n }\n const sendPayload = payload as ReactionOutboundSendJob\n await adapter.sendReaction({\n externalMessageId: sendPayload.messageId, // platform message id; the adapter maps to provider id via its own channel-link lookup if needed\n conversationId: sendPayload.conversationId ?? '',\n emoji: sendPayload.emoji,\n credentials,\n scope: {\n tenantId: payload.scope.tenantId,\n organizationId: payload.scope.organizationId ?? payload.scope.tenantId,\n },\n })\n } else {\n if (typeof adapter.removeReaction !== 'function') {\n return { status: 'no_adapter', message: `Adapter '${adapter.providerKey}' has no removeReaction` }\n }\n const removePayload = payload as ReactionOutboundRemoveJob\n await adapter.removeReaction({\n externalMessageId: removePayload.externalReactionId ?? removePayload.messageId,\n conversationId: removePayload.conversationId ?? '',\n emoji: removePayload.emoji,\n credentials,\n scope: {\n tenantId: payload.scope.tenantId,\n organizationId: payload.scope.organizationId ?? payload.scope.tenantId,\n },\n })\n }\n return { status: 'ok' }\n } catch (err) {\n const classification = classifyOutboundError(err)\n return {\n status: 'failed',\n transient: classification.transient,\n message: classification.message,\n }\n }\n}\n\nasync function maybeRetry(\n result: AdapterCallResult,\n payload: ReactionProcessorPayload,\n ctx: HandlerContext,\n): Promise<void> {\n if (result.status === 'ok') return\n if (result.status === 'no_adapter' || result.status === 'channel_inactive') {\n // Permanent \u2014 log and stop.\n console.error(\n `[communication_channels:reaction-processor] ${result.status}: ${result.message}`,\n )\n return\n }\n if (!result.transient) {\n console.error(\n `[communication_channels:reaction-processor] permanent failure: ${result.message}`,\n )\n return\n }\n const attempt = payload.attempt ?? 1\n if (attempt >= REACTION_PROCESSOR_MAX_ATTEMPTS) {\n console.error(\n `[communication_channels:reaction-processor] giving up after attempt ${attempt}: ${result.message}`,\n )\n return\n }\n const next: ReactionProcessorPayload = { ...payload, attempt: attempt + 1 }\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.reactions)\n await queue.enqueue(next as unknown as Record<string, unknown>, {\n delayMs: computeBackoffMs(attempt),\n })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n COMMUNICATION_CHANNELS_PROCESS_INBOUND_REACTION_COMMAND_ID,\n type ProcessInboundReactionInput,\n type ProcessInboundReactionResult,\n} from '../commands/process-inbound-reaction'\nimport { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from '../lib/queue'\nimport { classifyOutboundError, computeBackoffMs } from '../lib/error-classification'\nimport { CommunicationChannel } from '../data/entities'\nimport {\n REACTION_PROCESSOR_MAX_ATTEMPTS,\n type ReactionInboundJob,\n type ReactionOutboundRemoveJob,\n type ReactionOutboundSendJob,\n type ReactionProcessorPayload,\n} from '../lib/reaction-processor-types'\nimport { refreshCredentialsIfNeeded } from '../lib/credential-refresh'\nimport type { ChannelAdapterRegistry } from '../lib/registry'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('communication_channels').child({ component: 'reaction-processor' })\n\nexport type { ReactionProcessorPayload }\n\nexport const metadata: WorkerMeta = {\n queue: COMMUNICATION_CHANNELS_QUEUES.reactions,\n id: 'communication_channels:reaction-processor',\n concurrency: 10,\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\n/**\n * Unified reaction worker \u2014 handles inbound, outbound-send, and outbound-remove\n * jobs from the `communication-channels-reactions` queue via a discriminated payload.\n *\n * - `inbound` \u2014 dispatches the `process_inbound_reaction` command which\n * applies provider semantics and emits `.reaction.added/.removed`.\n * - `outbound_send` \u2014 calls `adapter.sendReaction?(...)`. The local\n * `MessageReaction` row was already created by the API/command layer\n * for UX responsiveness; this worker just notifies the provider.\n * - `outbound_remove` \u2014 calls `adapter.removeReaction?(...)`. The local\n * row was already deleted by the API/command layer.\n *\n * Retry: transient failures re-enqueue with exponential backoff up to\n * `REACTION_PROCESSOR_MAX_ATTEMPTS = 3`. Permanent failures stop. Reactions\n * are inherently low-stakes (a missed reaction is annoying, not data loss),\n * so we do not surface failures via a dedicated event; provider errors are\n * logged through `integrationLogService` when available.\n */\nexport default async function handle(\n job: QueuedJob<ReactionProcessorPayload>,\n ctx: HandlerContext,\n): Promise<void> {\n switch (job.payload.kind) {\n case 'inbound':\n await handleInbound(job.payload, ctx)\n return\n case 'outbound_send':\n await handleOutboundSend(job.payload, ctx)\n return\n case 'outbound_remove':\n await handleOutboundRemove(job.payload, ctx)\n return\n default: {\n // exhaustiveness check\n const exhaustive: never = job.payload\n throw new Error(`Unknown reaction job kind: ${JSON.stringify(exhaustive)}`)\n }\n }\n}\n\nasync function handleInbound(payload: ReactionInboundJob, ctx: HandlerContext): Promise<void> {\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: payload.scope.organizationId ?? null,\n organizationIds: payload.scope.organizationId ? [payload.scope.organizationId] : null,\n }\n const input: ProcessInboundReactionInput = {\n channelId: payload.channelId,\n providerKey: payload.providerKey,\n channelType: payload.channelType,\n scope: payload.scope,\n event: payload.event,\n }\n await commandBus.execute<ProcessInboundReactionInput, ProcessInboundReactionResult>(\n COMMUNICATION_CHANNELS_PROCESS_INBOUND_REACTION_COMMAND_ID,\n { input, ctx: commandCtx as never },\n )\n}\n\nasync function handleOutboundSend(payload: ReactionOutboundSendJob, ctx: HandlerContext): Promise<void> {\n const result = await callAdapterOutbound(payload, ctx, 'send')\n await maybeRetry(result, payload, ctx)\n}\n\nasync function handleOutboundRemove(\n payload: ReactionOutboundRemoveJob,\n ctx: HandlerContext,\n): Promise<void> {\n const result = await callAdapterOutbound(payload, ctx, 'remove')\n await maybeRetry(result, payload, ctx)\n}\n\ntype AdapterCallResult =\n | { status: 'ok' }\n | { status: 'no_adapter'; message: string }\n | { status: 'channel_inactive'; message: string }\n | { status: 'failed'; transient: boolean; message: string }\n\nasync function callAdapterOutbound(\n payload: ReactionOutboundSendJob | ReactionOutboundRemoveJob,\n ctx: HandlerContext,\n action: 'send' | 'remove',\n): Promise<AdapterCallResult> {\n const adapterRegistry = ctx.resolve<ChannelAdapterRegistry>('channelAdapterRegistry')\n const adapter = adapterRegistry?.get(payload.providerKey)\n if (!adapter) {\n return { status: 'no_adapter', message: `No adapter for provider '${payload.providerKey}'` }\n }\n const em = (ctx.resolve('em') as EntityManager).fork()\n const channel = await findOneWithDecryption(\n em,\n CommunicationChannel,\n {\n id: payload.channelId,\n tenantId: payload.scope.tenantId,\n organizationId: payload.scope.organizationId ?? null,\n deletedAt: null,\n },\n undefined,\n payload.scope,\n )\n if (!channel) {\n return { status: 'no_adapter', message: `Channel ${payload.channelId} not found` }\n }\n if (!channel.isActive) {\n return { status: 'channel_inactive', message: `Channel ${payload.channelId} is inactive` }\n }\n\n // Credentials. Per-user scoping via `channel.userId` (review R2-C1 / N1).\n type 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 let credentials: Record<string, unknown> = {}\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: payload.scope.tenantId,\n organizationId: payload.scope.organizationId ?? payload.scope.tenantId,\n userId: channel.userId ?? null,\n }\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 {\n adapter,\n channelId: channel.id,\n credentials,\n scope: credentialsScope,\n },\n { credentialsService },\n )\n credentials = refreshed.credentials\n\n try {\n if (action === 'send') {\n if (typeof adapter.sendReaction !== 'function') {\n return { status: 'no_adapter', message: `Adapter '${adapter.providerKey}' has no sendReaction` }\n }\n const sendPayload = payload as ReactionOutboundSendJob\n await adapter.sendReaction({\n externalMessageId: sendPayload.messageId, // platform message id; the adapter maps to provider id via its own channel-link lookup if needed\n conversationId: sendPayload.conversationId ?? '',\n emoji: sendPayload.emoji,\n credentials,\n scope: {\n tenantId: payload.scope.tenantId,\n organizationId: payload.scope.organizationId ?? payload.scope.tenantId,\n },\n })\n } else {\n if (typeof adapter.removeReaction !== 'function') {\n return { status: 'no_adapter', message: `Adapter '${adapter.providerKey}' has no removeReaction` }\n }\n const removePayload = payload as ReactionOutboundRemoveJob\n await adapter.removeReaction({\n externalMessageId: removePayload.externalReactionId ?? removePayload.messageId,\n conversationId: removePayload.conversationId ?? '',\n emoji: removePayload.emoji,\n credentials,\n scope: {\n tenantId: payload.scope.tenantId,\n organizationId: payload.scope.organizationId ?? payload.scope.tenantId,\n },\n })\n }\n return { status: 'ok' }\n } catch (err) {\n const classification = classifyOutboundError(err)\n return {\n status: 'failed',\n transient: classification.transient,\n message: classification.message,\n }\n }\n}\n\nasync function maybeRetry(\n result: AdapterCallResult,\n payload: ReactionProcessorPayload,\n ctx: HandlerContext,\n): Promise<void> {\n if (result.status === 'ok') return\n if (result.status === 'no_adapter' || result.status === 'channel_inactive') {\n // Permanent \u2014 log and stop.\n logger.error('reaction processing failed', { status: result.status, reason: result.message })\n return\n }\n if (!result.transient) {\n logger.error('permanent reaction processing failure', { reason: result.message })\n return\n }\n const attempt = payload.attempt ?? 1\n if (attempt >= REACTION_PROCESSOR_MAX_ATTEMPTS) {\n logger.error('giving up on reaction processing', { attempt, reason: result.message })\n return\n }\n const next: ReactionProcessorPayload = { ...payload, attempt: attempt + 1 }\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.reactions)\n await queue.enqueue(next as unknown as Record<string, unknown>, {\n delayMs: computeBackoffMs(attempt),\n })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -16,6 +16,8 @@ import {
16
16
  cachePurgeSegmentResponseSchema,
17
17
  configErrorSchema
18
18
  } from "../openapi.js";
19
+ import { createLogger } from "@open-mercato/shared/lib/logger";
20
+ const logger = createLogger("configs");
19
21
  const metadata = {
20
22
  GET: { requireAuth: true, requireFeatures: ["configs.cache.view"] },
21
23
  POST: { requireAuth: true, requireFeatures: ["configs.cache.manage"] }
@@ -44,7 +46,7 @@ async function GET(req) {
44
46
  const stats = await runWithCacheTenant(auth.tenantId ?? null, () => collectCrudCacheStats(cache));
45
47
  return NextResponse.json(stats);
46
48
  } catch (error) {
47
- console.error("[configs.cache] failed to resolve cache stats", error);
49
+ logger.error("failed to resolve cache stats", { component: "cache", err: error });
48
50
  return NextResponse.json(
49
51
  { error: translate("configs.cache.unavailable", "Cache service is unavailable.") },
50
52
  { status: 500 }
@@ -134,7 +136,7 @@ async function POST(req) {
134
136
  }
135
137
  return NextResponse.json(responseBody);
136
138
  } catch (error) {
137
- console.error("[configs.cache] failed to purge cache", error);
139
+ logger.error("failed to purge cache", { component: "cache", err: error });
138
140
  return NextResponse.json(
139
141
  { error: translate("configs.cache.purgeError", "Failed to purge cache segment.") },
140
142
  { status: 500 }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/configs/api/cache/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport { collectCrudCacheStats, purgeCrudCacheSegment } from '@open-mercato/shared/lib/crud/cache-stats'\nimport {\n bridgeLegacyGuard,\n runMutationGuards,\n type MutationGuardInput,\n} from '@open-mercato/shared/lib/crud/mutation-guard-registry'\nimport {\n configsTag,\n cacheStatsResponseSchema,\n cachePurgeRequestSchema,\n cachePurgeAllResponseSchema,\n cachePurgeSegmentResponseSchema,\n configErrorSchema,\n} from '../openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['configs.cache.view'] },\n POST: { requireAuth: true, requireFeatures: ['configs.cache.manage'] },\n} as const\n\nfunction resolveUserFeatures(auth: unknown): string[] {\n const features = (auth as { features?: unknown } | null)?.features\n return Array.isArray(features) ? features.filter((value): value is string => typeof value === 'string') : []\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const { translate } = await resolveTranslations()\n\n try {\n const container = await createRequestContainer()\n let cache: CacheStrategy\n try {\n cache = (container.resolve('cache') as CacheStrategy)\n } catch {\n return NextResponse.json(\n { error: translate('configs.cache.unavailable', 'Cache service is unavailable.') },\n { status: 503 },\n )\n }\n const stats = await runWithCacheTenant(auth.tenantId ?? null, () => collectCrudCacheStats(cache))\n return NextResponse.json(stats)\n } catch (error) {\n console.error('[configs.cache] failed to resolve cache stats', error)\n return NextResponse.json(\n { error: translate('configs.cache.unavailable', 'Cache service is unavailable.') },\n { status: 500 },\n )\n }\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const { translate } = await resolveTranslations()\n\n let body: any = null\n try {\n body = await req.json()\n } catch {\n body = null\n }\n\n const action = typeof body?.action === 'string' ? body.action : 'purgeAll'\n\n try {\n const container = await createRequestContainer()\n let cache: CacheStrategy\n try {\n cache = (container.resolve('cache') as CacheStrategy)\n } catch {\n return NextResponse.json(\n { error: translate('configs.cache.unavailable', 'Cache service is unavailable.') },\n { status: 503 },\n )\n }\n const tenantScope = auth.tenantId ?? null\n\n const segment =\n action === 'purgeSegment'\n ? (typeof body?.segment === 'string' ? body.segment.trim() : '')\n : null\n if (action === 'purgeSegment' && !segment) {\n return NextResponse.json(\n { error: translate('configs.cache.purgeError', 'Failed to purge cache segment.') },\n { status: 400 },\n )\n }\n\n // Mutation-guard contract for this custom write route (packages/core/AGENTS.md\n // \u2192 API Routes). A cache purge carries no per-record optimistic-lock version, so\n // the default OSS guard short-circuits; wiring it keeps the route on the shared\n // write-guard interception path for any tenant-registered guard.\n const guardInput: MutationGuardInput = {\n tenantId: tenantScope ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'configs.cache',\n resourceId: segment ?? tenantScope ?? 'all',\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { action, ...(segment ? { segment } : {}) },\n }\n const guard = bridgeLegacyGuard(container)\n let afterSuccessCallbacks: Awaited<ReturnType<typeof runMutationGuards>>['afterSuccessCallbacks'] = []\n if (guard) {\n const guardResult = await runMutationGuards([guard], guardInput, {\n userFeatures: resolveUserFeatures(auth),\n })\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: translate('configs.cache.purgeError', 'Failed to purge cache segment.') },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n afterSuccessCallbacks = guardResult.afterSuccessCallbacks\n }\n\n let responseBody: Record<string, unknown>\n if (action === 'purgeSegment') {\n const result = await runWithCacheTenant(tenantScope, () => purgeCrudCacheSegment(cache, segment!))\n const stats = await runWithCacheTenant(tenantScope, () => collectCrudCacheStats(cache))\n responseBody = { action: 'purgeSegment', segment, deleted: result.deleted, stats }\n } else {\n await runWithCacheTenant(tenantScope, () => cache.clear())\n const stats = await runWithCacheTenant(tenantScope, () => collectCrudCacheStats(cache))\n responseBody = { action: 'purgeAll', stats }\n }\n\n for (const callback of afterSuccessCallbacks) {\n if (!callback.guard.afterSuccess) continue\n await callback.guard.afterSuccess({\n tenantId: tenantScope ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'configs.cache',\n resourceId: segment ?? tenantScope ?? 'all',\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: callback.metadata ?? null,\n })\n }\n\n return NextResponse.json(responseBody)\n } catch (error) {\n console.error('[configs.cache] failed to purge cache', error)\n return NextResponse.json(\n { error: translate('configs.cache.purgeError', 'Failed to purge cache segment.') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: configsTag,\n summary: 'Cache management',\n methods: {\n GET: {\n summary: 'Get cache statistics',\n description: 'Returns detailed cache statistics including total entries and breakdown by cache segments. Requires cache service to be available.',\n responses: [\n { status: 200, description: 'Cache statistics', schema: cacheStatsResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to resolve cache stats', schema: configErrorSchema },\n { status: 503, description: 'Cache service unavailable', schema: configErrorSchema },\n ],\n },\n POST: {\n summary: 'Purge cache',\n description: 'Purges cache entries. Supports two actions: purgeAll (clears entire cache) or purgeSegment (clears specific segment). Returns updated cache statistics after purge.',\n requestBody: {\n contentType: 'application/json',\n schema: cachePurgeRequestSchema,\n },\n responses: [\n { status: 200, description: 'All cache cleared successfully', schema: cachePurgeAllResponseSchema },\n { status: 200, description: 'Cache segment cleared successfully', schema: cachePurgeSegmentResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid request - missing segment identifier for purgeSegment action', schema: configErrorSchema },\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to purge cache', schema: configErrorSchema },\n { status: 503, description: 'Cache service unavailable', schema: configErrorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport { collectCrudCacheStats, purgeCrudCacheSegment } from '@open-mercato/shared/lib/crud/cache-stats'\nimport {\n bridgeLegacyGuard,\n runMutationGuards,\n type MutationGuardInput,\n} from '@open-mercato/shared/lib/crud/mutation-guard-registry'\nimport {\n configsTag,\n cacheStatsResponseSchema,\n cachePurgeRequestSchema,\n cachePurgeAllResponseSchema,\n cachePurgeSegmentResponseSchema,\n configErrorSchema,\n} from '../openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('configs')\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['configs.cache.view'] },\n POST: { requireAuth: true, requireFeatures: ['configs.cache.manage'] },\n} as const\n\nfunction resolveUserFeatures(auth: unknown): string[] {\n const features = (auth as { features?: unknown } | null)?.features\n return Array.isArray(features) ? features.filter((value): value is string => typeof value === 'string') : []\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const { translate } = await resolveTranslations()\n\n try {\n const container = await createRequestContainer()\n let cache: CacheStrategy\n try {\n cache = (container.resolve('cache') as CacheStrategy)\n } catch {\n return NextResponse.json(\n { error: translate('configs.cache.unavailable', 'Cache service is unavailable.') },\n { status: 503 },\n )\n }\n const stats = await runWithCacheTenant(auth.tenantId ?? null, () => collectCrudCacheStats(cache))\n return NextResponse.json(stats)\n } catch (error) {\n logger.error('failed to resolve cache stats', { component: 'cache', err: error })\n return NextResponse.json(\n { error: translate('configs.cache.unavailable', 'Cache service is unavailable.') },\n { status: 500 },\n )\n }\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const { translate } = await resolveTranslations()\n\n let body: any = null\n try {\n body = await req.json()\n } catch {\n body = null\n }\n\n const action = typeof body?.action === 'string' ? body.action : 'purgeAll'\n\n try {\n const container = await createRequestContainer()\n let cache: CacheStrategy\n try {\n cache = (container.resolve('cache') as CacheStrategy)\n } catch {\n return NextResponse.json(\n { error: translate('configs.cache.unavailable', 'Cache service is unavailable.') },\n { status: 503 },\n )\n }\n const tenantScope = auth.tenantId ?? null\n\n const segment =\n action === 'purgeSegment'\n ? (typeof body?.segment === 'string' ? body.segment.trim() : '')\n : null\n if (action === 'purgeSegment' && !segment) {\n return NextResponse.json(\n { error: translate('configs.cache.purgeError', 'Failed to purge cache segment.') },\n { status: 400 },\n )\n }\n\n // Mutation-guard contract for this custom write route (packages/core/AGENTS.md\n // \u2192 API Routes). A cache purge carries no per-record optimistic-lock version, so\n // the default OSS guard short-circuits; wiring it keeps the route on the shared\n // write-guard interception path for any tenant-registered guard.\n const guardInput: MutationGuardInput = {\n tenantId: tenantScope ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'configs.cache',\n resourceId: segment ?? tenantScope ?? 'all',\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { action, ...(segment ? { segment } : {}) },\n }\n const guard = bridgeLegacyGuard(container)\n let afterSuccessCallbacks: Awaited<ReturnType<typeof runMutationGuards>>['afterSuccessCallbacks'] = []\n if (guard) {\n const guardResult = await runMutationGuards([guard], guardInput, {\n userFeatures: resolveUserFeatures(auth),\n })\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: translate('configs.cache.purgeError', 'Failed to purge cache segment.') },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n afterSuccessCallbacks = guardResult.afterSuccessCallbacks\n }\n\n let responseBody: Record<string, unknown>\n if (action === 'purgeSegment') {\n const result = await runWithCacheTenant(tenantScope, () => purgeCrudCacheSegment(cache, segment!))\n const stats = await runWithCacheTenant(tenantScope, () => collectCrudCacheStats(cache))\n responseBody = { action: 'purgeSegment', segment, deleted: result.deleted, stats }\n } else {\n await runWithCacheTenant(tenantScope, () => cache.clear())\n const stats = await runWithCacheTenant(tenantScope, () => collectCrudCacheStats(cache))\n responseBody = { action: 'purgeAll', stats }\n }\n\n for (const callback of afterSuccessCallbacks) {\n if (!callback.guard.afterSuccess) continue\n await callback.guard.afterSuccess({\n tenantId: tenantScope ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'configs.cache',\n resourceId: segment ?? tenantScope ?? 'all',\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: callback.metadata ?? null,\n })\n }\n\n return NextResponse.json(responseBody)\n } catch (error) {\n logger.error('failed to purge cache', { component: 'cache', err: error })\n return NextResponse.json(\n { error: translate('configs.cache.purgeError', 'Failed to purge cache segment.') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: configsTag,\n summary: 'Cache management',\n methods: {\n GET: {\n summary: 'Get cache statistics',\n description: 'Returns detailed cache statistics including total entries and breakdown by cache segments. Requires cache service to be available.',\n responses: [\n { status: 200, description: 'Cache statistics', schema: cacheStatsResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to resolve cache stats', schema: configErrorSchema },\n { status: 503, description: 'Cache service unavailable', schema: configErrorSchema },\n ],\n },\n POST: {\n summary: 'Purge cache',\n description: 'Purges cache entries. Supports two actions: purgeAll (clears entire cache) or purgeSegment (clears specific segment). Returns updated cache statistics after purge.',\n requestBody: {\n contentType: 'application/json',\n schema: cachePurgeRequestSchema,\n },\n responses: [\n { status: 200, description: 'All cache cleared successfully', schema: cachePurgeAllResponseSchema },\n { status: 200, description: 'Cache segment cleared successfully', schema: cachePurgeSegmentResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid request - missing segment identifier for purgeSegment action', schema: configErrorSchema },\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to purge cache', schema: configErrorSchema },\n { status: 503, description: 'Cache service unavailable', schema: configErrorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,0BAA8C;AACvD,SAAS,uBAAuB,6BAA6B;AAC7D;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,SAAS;AAE9B,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,oBAAoB,EAAE;AAAA,EAClE,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,sBAAsB,EAAE;AACvE;AAEA,SAAS,oBAAoB,MAAyB;AACpD,QAAM,WAAY,MAAwC;AAC1D,SAAO,MAAM,QAAQ,QAAQ,IAAI,SAAS,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ,IAAI,CAAC;AAC7G;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,KAAK;AACd,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAEhD,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAI;AACJ,QAAI;AACF,cAAS,UAAU,QAAQ,OAAO;AAAA,IACpC,QAAQ;AACN,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,UAAU,6BAA6B,+BAA+B,EAAE;AAAA,QACjF,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,mBAAmB,KAAK,YAAY,MAAM,MAAM,sBAAsB,KAAK,CAAC;AAChG,WAAO,aAAa,KAAK,KAAK;AAAA,EAChC,SAAS,OAAO;AACd,WAAO,MAAM,iCAAiC,EAAE,WAAW,SAAS,KAAK,MAAM,CAAC;AAChF,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,UAAU,6BAA6B,+BAA+B,EAAE;AAAA,MACjF,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,KAAK;AACd,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAEhD,MAAI,OAAY;AAChB,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS;AAEhE,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAI;AACJ,QAAI;AACF,cAAS,UAAU,QAAQ,OAAO;AAAA,IACpC,QAAQ;AACN,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,UAAU,6BAA6B,+BAA+B,EAAE;AAAA,QACjF,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AACA,UAAM,cAAc,KAAK,YAAY;AAErC,UAAM,UACJ,WAAW,iBACN,OAAO,MAAM,YAAY,WAAW,KAAK,QAAQ,KAAK,IAAI,KAC3D;AACN,QAAI,WAAW,kBAAkB,CAAC,SAAS;AACzC,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,UAAU,4BAA4B,gCAAgC,EAAE;AAAA,QACjF,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAMA,UAAM,aAAiC;AAAA,MACrC,UAAU,eAAe;AAAA,MACzB,gBAAgB,KAAK,SAAS;AAAA,MAC9B,QAAQ,KAAK;AAAA,MACb,cAAc;AAAA,MACd,YAAY,WAAW,eAAe;AAAA,MACtC,WAAW;AAAA,MACX,eAAe,IAAI;AAAA,MACnB,gBAAgB,IAAI;AAAA,MACpB,iBAAiB,EAAE,QAAQ,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,IAC7D;AACA,UAAM,QAAQ,kBAAkB,SAAS;AACzC,QAAI,wBAAgG,CAAC;AACrG,QAAI,OAAO;AACT,YAAM,cAAc,MAAM,kBAAkB,CAAC,KAAK,GAAG,YAAY;AAAA,QAC/D,cAAc,oBAAoB,IAAI;AAAA,MACxC,CAAC;AACD,UAAI,CAAC,YAAY,IAAI;AACnB,eAAO,aAAa;AAAA,UAClB,YAAY,aAAa,EAAE,OAAO,UAAU,4BAA4B,gCAAgC,EAAE;AAAA,UAC1G,EAAE,QAAQ,YAAY,eAAe,IAAI;AAAA,QAC3C;AAAA,MACF;AACA,8BAAwB,YAAY;AAAA,IACtC;AAEA,QAAI;AACJ,QAAI,WAAW,gBAAgB;AAC7B,YAAM,SAAS,MAAM,mBAAmB,aAAa,MAAM,sBAAsB,OAAO,OAAQ,CAAC;AACjG,YAAM,QAAQ,MAAM,mBAAmB,aAAa,MAAM,sBAAsB,KAAK,CAAC;AACtF,qBAAe,EAAE,QAAQ,gBAAgB,SAAS,SAAS,OAAO,SAAS,MAAM;AAAA,IACnF,OAAO;AACL,YAAM,mBAAmB,aAAa,MAAM,MAAM,MAAM,CAAC;AACzD,YAAM,QAAQ,MAAM,mBAAmB,aAAa,MAAM,sBAAsB,KAAK,CAAC;AACtF,qBAAe,EAAE,QAAQ,YAAY,MAAM;AAAA,IAC7C;AAEA,eAAW,YAAY,uBAAuB;AAC5C,UAAI,CAAC,SAAS,MAAM,aAAc;AAClC,YAAM,SAAS,MAAM,aAAa;AAAA,QAChC,UAAU,eAAe;AAAA,QACzB,gBAAgB,KAAK,SAAS;AAAA,QAC9B,QAAQ,KAAK;AAAA,QACb,cAAc;AAAA,QACd,YAAY,WAAW,eAAe;AAAA,QACtC,WAAW;AAAA,QACX,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,QACpB,UAAU,SAAS,YAAY;AAAA,MACjC,CAAC;AAAA,IACH;AAEA,WAAO,aAAa,KAAK,YAAY;AAAA,EACvC,SAAS,OAAO;AACd,WAAO,MAAM,yBAAyB,EAAE,WAAW,SAAS,KAAK,MAAM,CAAC;AACxE,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,UAAU,4BAA4B,gCAAgC,EAAE;AAAA,MACjF,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,MACH,SAAS;AAAA,MACT,aAAa;AAAA,MACb,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,yBAAyB;AAAA,MACnF;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,kBAAkB;AAAA,QACtE,EAAE,QAAQ,KAAK,aAAa,iCAAiC,QAAQ,kBAAkB;AAAA,QACvF,EAAE,QAAQ,KAAK,aAAa,6BAA6B,QAAQ,kBAAkB;AAAA,MACrF;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa;AAAA,QACX,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,kCAAkC,QAAQ,4BAA4B;AAAA,QAClG,EAAE,QAAQ,KAAK,aAAa,sCAAsC,QAAQ,gCAAgC;AAAA,MAC5G;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,wEAAwE,QAAQ,kBAAkB;AAAA,QAC9H,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,kBAAkB;AAAA,QACtE,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,kBAAkB;AAAA,QAC/E,EAAE,QAAQ,KAAK,aAAa,6BAA6B,QAAQ,kBAAkB;AAAA,MACrF;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -10,6 +10,8 @@ import {
10
10
  purgeCacheResponseSchema,
11
11
  configErrorSchema
12
12
  } from "../openapi.js";
13
+ import { createLogger } from "@open-mercato/shared/lib/logger";
14
+ const logger = createLogger("configs");
13
15
  const metadata = {
14
16
  GET: { requireAuth: true, requireFeatures: ["configs.system_status.view"] },
15
17
  POST: { requireAuth: true, requireFeatures: ["configs.manage"] }
@@ -23,7 +25,7 @@ async function GET(req) {
23
25
  const snapshot = buildSystemStatusSnapshot();
24
26
  return NextResponse.json(snapshot);
25
27
  } catch (error) {
26
- console.error("[configs.system-status] failed to build environment snapshot", error);
28
+ logger.error("failed to build environment snapshot", { component: "system-status", err: error });
27
29
  const { translate } = await resolveTranslations();
28
30
  return NextResponse.json(
29
31
  { error: translate("configs.systemStatus.error", "Failed to load system status") },
@@ -55,7 +57,7 @@ async function POST(req) {
55
57
  const cleared = await runWithCacheTenant(tenantScope, () => cache.clear());
56
58
  return NextResponse.json({ cleared });
57
59
  } catch (error) {
58
- console.error("[configs.system-status] failed to purge cache", error);
60
+ logger.error("failed to purge cache", { component: "system-status", err: error });
59
61
  return NextResponse.json(
60
62
  { error: translate("configs.systemStatus.actions.purgeCacheError", "Failed to purge cache.") },
61
63
  { status: 500 }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/configs/api/system-status/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { buildSystemStatusSnapshot } from '../../lib/system-status'\nimport type { SystemStatusSnapshot } from '../../lib/system-status.types'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport {\n configsTag,\n systemStatusResponseSchema,\n purgeCacheResponseSchema,\n configErrorSchema,\n} from '../openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['configs.system_status.view'] },\n POST: { requireAuth: true, requireFeatures: ['configs.manage'] },\n} as const\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n try {\n const snapshot: SystemStatusSnapshot = buildSystemStatusSnapshot()\n return NextResponse.json(snapshot)\n } catch (error) {\n console.error('[configs.system-status] failed to build environment snapshot', error)\n const { translate } = await resolveTranslations()\n return NextResponse.json(\n { error: translate('configs.systemStatus.error', 'Failed to load system status') },\n { status: 500 }\n )\n }\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const { translate } = await resolveTranslations()\n\n try {\n const container = await createRequestContainer()\n let cache: CacheStrategy | null = null\n try {\n cache = (container.resolve('cache') as CacheStrategy)\n } catch {\n cache = null\n }\n\n if (!cache || typeof cache.clear !== 'function') {\n return NextResponse.json(\n { error: translate('configs.systemStatus.actions.purgeCacheUnavailable', 'Cache service is unavailable.') },\n { status: 503 }\n )\n }\n\n const tenantScope = auth.tenantId ?? null\n const cleared = await runWithCacheTenant(tenantScope, () => cache.clear())\n return NextResponse.json({ cleared })\n } catch (error) {\n console.error('[configs.system-status] failed to purge cache', error)\n return NextResponse.json(\n { error: translate('configs.systemStatus.actions.purgeCacheError', 'Failed to purge cache.') },\n { status: 500 }\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: configsTag,\n summary: 'System status and health',\n methods: {\n GET: {\n summary: 'Get system health status',\n description: 'Returns comprehensive system health information including environment details, version, resource usage, and service connectivity status.',\n responses: [\n { status: 200, description: 'System status snapshot', schema: systemStatusResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to load system status', schema: configErrorSchema },\n ],\n },\n POST: {\n summary: 'Clear system cache',\n description: 'Purges the entire cache for the current tenant. Useful for troubleshooting or forcing fresh data loading.',\n responses: [\n { status: 200, description: 'Cache cleared successfully', schema: purgeCacheResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 503, description: 'Cache service unavailable', schema: configErrorSchema },\n { status: 500, description: 'Failed to purge cache', schema: configErrorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,iCAAiC;AAE1C,SAAS,8BAA8B;AACvC,SAAS,0BAA8C;AACvD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAAA,EAC1E,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,gBAAgB,EAAE;AACjE;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,KAAK;AACd,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,MAAI;AACF,UAAM,WAAiC,0BAA0B;AACjE,WAAO,aAAa,KAAK,QAAQ;AAAA,EACnC,SAAS,OAAO;AACd,YAAQ,MAAM,gEAAgE,KAAK;AACnF,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,UAAU,8BAA8B,8BAA8B,EAAE;AAAA,MACjF,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,KAAK;AACd,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAEhD,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAI,QAA8B;AAClC,QAAI;AACF,cAAS,UAAU,QAAQ,OAAO;AAAA,IACpC,QAAQ;AACN,cAAQ;AAAA,IACV;AAEA,QAAI,CAAC,SAAS,OAAO,MAAM,UAAU,YAAY;AAC/C,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,UAAU,sDAAsD,+BAA+B,EAAE;AAAA,QAC1G,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAEA,UAAM,cAAc,KAAK,YAAY;AACrC,UAAM,UAAU,MAAM,mBAAmB,aAAa,MAAM,MAAM,MAAM,CAAC;AACzE,WAAO,aAAa,KAAK,EAAE,QAAQ,CAAC;AAAA,EACtC,SAAS,OAAO;AACd,YAAQ,MAAM,iDAAiD,KAAK;AACpE,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,UAAU,gDAAgD,wBAAwB,EAAE;AAAA,MAC7F,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,MACH,SAAS;AAAA,MACT,aAAa;AAAA,MACb,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,0BAA0B,QAAQ,2BAA2B;AAAA,MAC3F;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,kBAAkB;AAAA,QACtE,EAAE,QAAQ,KAAK,aAAa,gCAAgC,QAAQ,kBAAkB;AAAA,MACxF;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,8BAA8B,QAAQ,yBAAyB;AAAA,MAC7F;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,kBAAkB;AAAA,QACtE,EAAE,QAAQ,KAAK,aAAa,6BAA6B,QAAQ,kBAAkB;AAAA,QACnF,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,kBAAkB;AAAA,MACjF;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { buildSystemStatusSnapshot } from '../../lib/system-status'\nimport type { SystemStatusSnapshot } from '../../lib/system-status.types'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport {\n configsTag,\n systemStatusResponseSchema,\n purgeCacheResponseSchema,\n configErrorSchema,\n} from '../openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('configs')\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['configs.system_status.view'] },\n POST: { requireAuth: true, requireFeatures: ['configs.manage'] },\n} as const\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n try {\n const snapshot: SystemStatusSnapshot = buildSystemStatusSnapshot()\n return NextResponse.json(snapshot)\n } catch (error) {\n logger.error('failed to build environment snapshot', { component: 'system-status', err: error })\n const { translate } = await resolveTranslations()\n return NextResponse.json(\n { error: translate('configs.systemStatus.error', 'Failed to load system status') },\n { status: 500 }\n )\n }\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const { translate } = await resolveTranslations()\n\n try {\n const container = await createRequestContainer()\n let cache: CacheStrategy | null = null\n try {\n cache = (container.resolve('cache') as CacheStrategy)\n } catch {\n cache = null\n }\n\n if (!cache || typeof cache.clear !== 'function') {\n return NextResponse.json(\n { error: translate('configs.systemStatus.actions.purgeCacheUnavailable', 'Cache service is unavailable.') },\n { status: 503 }\n )\n }\n\n const tenantScope = auth.tenantId ?? null\n const cleared = await runWithCacheTenant(tenantScope, () => cache.clear())\n return NextResponse.json({ cleared })\n } catch (error) {\n logger.error('failed to purge cache', { component: 'system-status', err: error })\n return NextResponse.json(\n { error: translate('configs.systemStatus.actions.purgeCacheError', 'Failed to purge cache.') },\n { status: 500 }\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: configsTag,\n summary: 'System status and health',\n methods: {\n GET: {\n summary: 'Get system health status',\n description: 'Returns comprehensive system health information including environment details, version, resource usage, and service connectivity status.',\n responses: [\n { status: 200, description: 'System status snapshot', schema: systemStatusResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to load system status', schema: configErrorSchema },\n ],\n },\n POST: {\n summary: 'Clear system cache',\n description: 'Purges the entire cache for the current tenant. Useful for troubleshooting or forcing fresh data loading.',\n responses: [\n { status: 200, description: 'Cache cleared successfully', schema: purgeCacheResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 503, description: 'Cache service unavailable', schema: configErrorSchema },\n { status: 500, description: 'Failed to purge cache', schema: configErrorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -12,6 +12,8 @@ import {
12
12
  executeUpgradeActionResponseSchema,
13
13
  configErrorSchema
14
14
  } from "../openapi.js";
15
+ import { createLogger } from "@open-mercato/shared/lib/logger";
16
+ const logger = createLogger("configs");
15
17
  const metadata = {
16
18
  GET: { requireAuth: true, requireFeatures: ["configs.manage"] },
17
19
  POST: { requireAuth: true, requireFeatures: ["configs.manage"] }
@@ -74,7 +76,7 @@ async function GET(req) {
74
76
  }] : [];
75
77
  return NextResponse.json({ version, actions: payload });
76
78
  } catch (error) {
77
- console.error("[configs.upgrade-actions] failed to load upgrade actions", error);
79
+ logger.error("failed to load upgrade actions", { component: "upgrade-actions", err: error });
78
80
  return NextResponse.json({ error: "Failed to load upgrade actions" }, { status: 500 });
79
81
  }
80
82
  }
@@ -115,7 +117,7 @@ async function POST(req) {
115
117
  version
116
118
  });
117
119
  } catch (error) {
118
- console.error("[configs.upgrade-actions] failed to execute upgrade action", error);
120
+ logger.error("failed to execute upgrade action", { component: "upgrade-actions", err: error });
119
121
  const message = translate("upgrades.runFailed", "We could not run this upgrade action.");
120
122
  const details = error instanceof Error ? error.message : null;
121
123
  return NextResponse.json({ error: message, details }, { status: 500 });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/configs/api/upgrade-actions/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { resolveFeatureCheckContext } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { executeUpgradeAction, getCurrentVersion, isUpgradeActionsEnabled, listPendingUpgradeActions } from '../../services/upgradeActionsService'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n configsTag,\n upgradeActionsListResponseSchema,\n executeUpgradeActionRequestSchema,\n executeUpgradeActionResponseSchema,\n configErrorSchema,\n} from '../openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['configs.manage'] },\n POST: { requireAuth: true, requireFeatures: ['configs.manage'] },\n} as const\n\nconst runSchema = z.object({\n actionId: z.string().trim().min(1, 'action id required'),\n})\n\ntype Scope = { tenantId: string; organizationId: string }\n\nasync function resolveScope(\n req: Request,\n translate: (key: string, fallback?: string, params?: Record<string, unknown>) => string,\n): Promise<{ scope: Scope | null; container: Awaited<ReturnType<typeof createRequestContainer>> | null; error?: Response }> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return { scope: null, container: null, error: NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) }\n }\n const container = await createRequestContainer()\n const featureContext = await resolveFeatureCheckContext({ container, auth, request: req })\n const tenantId = featureContext.scope.tenantId ?? auth.tenantId ?? null\n const organizationId = featureContext.organizationId ?? featureContext.scope.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n const message = translate(\n 'upgrades.scopeRequired',\n 'Select an organization and tenant to apply this upgrade.'\n )\n return { scope: null, container, error: NextResponse.json({ error: message }, { status: 400 }) }\n }\n return {\n scope: { tenantId, organizationId },\n container,\n }\n}\n\nexport async function GET(req: Request) {\n if (!isUpgradeActionsEnabled()) {\n return NextResponse.json({ version: getCurrentVersion(), actions: [] })\n }\n const { translate } = await resolveTranslations()\n const scopedTranslate = (key: string, fallback?: string, params?: Record<string, unknown>) =>\n translate(key, fallback, params as any)\n const scopeResult = await resolveScope(req, scopedTranslate)\n if (!scopeResult.scope) return scopeResult.error!\n\n const container = scopeResult.container ?? (await createRequestContainer())\n const em = container.resolve<EntityManager>('em')\n const version = getCurrentVersion()\n try {\n const actions = await listPendingUpgradeActions(em, { ...scopeResult.scope, version })\n const nextAction = actions[0]\n const payload = nextAction\n ? [{\n id: nextAction.id,\n version: nextAction.version,\n message: translate(\n nextAction.messageKey,\n 'The version {{version}} has been installed. To fully use all the features from this edition please do \"Install example catalog products and categories\".',\n { version: nextAction.version }\n ),\n ctaLabel: translate(\n nextAction.ctaKey,\n 'Install example catalog products and categories'\n ),\n successMessage: translate(\n nextAction.successKey,\n 'Example catalog products and categories installed.'\n ),\n loadingLabel: nextAction.loadingKey\n ? translate(nextAction.loadingKey, 'Installing\u2026')\n : translate('upgrades.loading', 'Installing\u2026'),\n }]\n : []\n return NextResponse.json({ version, actions: payload })\n } catch (error) {\n console.error('[configs.upgrade-actions] failed to load upgrade actions', error)\n return NextResponse.json({ error: 'Failed to load upgrade actions' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n if (!isUpgradeActionsEnabled()) {\n return NextResponse.json({ error: 'Upgrade actions are disabled' }, { status: 403 })\n }\n const { translate } = await resolveTranslations()\n const scopedTranslate = (key: string, fallback?: string, params?: Record<string, unknown>) =>\n translate(key, fallback, params as any)\n const scopeResult = await resolveScope(req, scopedTranslate)\n if (!scopeResult.scope) return scopeResult.error!\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n parsedBody = null\n }\n const parsed = runSchema.safeParse(parsedBody || {})\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid request body' }, { status: 400 })\n }\n\n const container = scopeResult.container ?? (await createRequestContainer())\n const version = getCurrentVersion()\n\n try {\n const result = await executeUpgradeAction(container, {\n actionId: parsed.data.actionId,\n tenantId: scopeResult.scope.tenantId,\n organizationId: scopeResult.scope.organizationId,\n version,\n })\n const successMessage = translate(\n result.action.successKey,\n 'Example catalog products and categories installed.'\n )\n return NextResponse.json({\n status: result.status,\n message: successMessage,\n version,\n })\n } catch (error) {\n console.error('[configs.upgrade-actions] failed to execute upgrade action', error)\n const message = translate('upgrades.runFailed', 'We could not run this upgrade action.')\n const details = error instanceof Error ? error.message : null\n return NextResponse.json({ error: message, details }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: configsTag,\n summary: 'Upgrade actions management',\n methods: {\n GET: {\n summary: 'List pending upgrade actions',\n description: 'Returns a list of pending upgrade actions for the current version. These are one-time setup tasks that need to be executed after upgrading to a new version. Requires organization and tenant context.',\n responses: [\n { status: 200, description: 'List of pending upgrade actions', schema: upgradeActionsListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Missing organization or tenant context', schema: configErrorSchema },\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to load upgrade actions', schema: configErrorSchema },\n ],\n },\n POST: {\n summary: 'Execute upgrade action',\n description: 'Executes a specific upgrade action by ID. Typically used for one-time setup tasks like seeding example data after version upgrade. Returns execution status and localized success message.',\n requestBody: {\n contentType: 'application/json',\n schema: executeUpgradeActionRequestSchema,\n },\n responses: [\n { status: 200, description: 'Upgrade action executed successfully', schema: executeUpgradeActionResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid request body or missing context', schema: configErrorSchema },\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 403, description: 'Upgrade actions are disabled', schema: configErrorSchema },\n { status: 500, description: 'Failed to execute upgrade action', schema: configErrorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { resolveFeatureCheckContext } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { executeUpgradeAction, getCurrentVersion, isUpgradeActionsEnabled, listPendingUpgradeActions } from '../../services/upgradeActionsService'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n configsTag,\n upgradeActionsListResponseSchema,\n executeUpgradeActionRequestSchema,\n executeUpgradeActionResponseSchema,\n configErrorSchema,\n} from '../openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('configs')\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['configs.manage'] },\n POST: { requireAuth: true, requireFeatures: ['configs.manage'] },\n} as const\n\nconst runSchema = z.object({\n actionId: z.string().trim().min(1, 'action id required'),\n})\n\ntype Scope = { tenantId: string; organizationId: string }\n\nasync function resolveScope(\n req: Request,\n translate: (key: string, fallback?: string, params?: Record<string, unknown>) => string,\n): Promise<{ scope: Scope | null; container: Awaited<ReturnType<typeof createRequestContainer>> | null; error?: Response }> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return { scope: null, container: null, error: NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) }\n }\n const container = await createRequestContainer()\n const featureContext = await resolveFeatureCheckContext({ container, auth, request: req })\n const tenantId = featureContext.scope.tenantId ?? auth.tenantId ?? null\n const organizationId = featureContext.organizationId ?? featureContext.scope.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n const message = translate(\n 'upgrades.scopeRequired',\n 'Select an organization and tenant to apply this upgrade.'\n )\n return { scope: null, container, error: NextResponse.json({ error: message }, { status: 400 }) }\n }\n return {\n scope: { tenantId, organizationId },\n container,\n }\n}\n\nexport async function GET(req: Request) {\n if (!isUpgradeActionsEnabled()) {\n return NextResponse.json({ version: getCurrentVersion(), actions: [] })\n }\n const { translate } = await resolveTranslations()\n const scopedTranslate = (key: string, fallback?: string, params?: Record<string, unknown>) =>\n translate(key, fallback, params as any)\n const scopeResult = await resolveScope(req, scopedTranslate)\n if (!scopeResult.scope) return scopeResult.error!\n\n const container = scopeResult.container ?? (await createRequestContainer())\n const em = container.resolve<EntityManager>('em')\n const version = getCurrentVersion()\n try {\n const actions = await listPendingUpgradeActions(em, { ...scopeResult.scope, version })\n const nextAction = actions[0]\n const payload = nextAction\n ? [{\n id: nextAction.id,\n version: nextAction.version,\n message: translate(\n nextAction.messageKey,\n 'The version {{version}} has been installed. To fully use all the features from this edition please do \"Install example catalog products and categories\".',\n { version: nextAction.version }\n ),\n ctaLabel: translate(\n nextAction.ctaKey,\n 'Install example catalog products and categories'\n ),\n successMessage: translate(\n nextAction.successKey,\n 'Example catalog products and categories installed.'\n ),\n loadingLabel: nextAction.loadingKey\n ? translate(nextAction.loadingKey, 'Installing\u2026')\n : translate('upgrades.loading', 'Installing\u2026'),\n }]\n : []\n return NextResponse.json({ version, actions: payload })\n } catch (error) {\n logger.error('failed to load upgrade actions', { component: 'upgrade-actions', err: error })\n return NextResponse.json({ error: 'Failed to load upgrade actions' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n if (!isUpgradeActionsEnabled()) {\n return NextResponse.json({ error: 'Upgrade actions are disabled' }, { status: 403 })\n }\n const { translate } = await resolveTranslations()\n const scopedTranslate = (key: string, fallback?: string, params?: Record<string, unknown>) =>\n translate(key, fallback, params as any)\n const scopeResult = await resolveScope(req, scopedTranslate)\n if (!scopeResult.scope) return scopeResult.error!\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n parsedBody = null\n }\n const parsed = runSchema.safeParse(parsedBody || {})\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid request body' }, { status: 400 })\n }\n\n const container = scopeResult.container ?? (await createRequestContainer())\n const version = getCurrentVersion()\n\n try {\n const result = await executeUpgradeAction(container, {\n actionId: parsed.data.actionId,\n tenantId: scopeResult.scope.tenantId,\n organizationId: scopeResult.scope.organizationId,\n version,\n })\n const successMessage = translate(\n result.action.successKey,\n 'Example catalog products and categories installed.'\n )\n return NextResponse.json({\n status: result.status,\n message: successMessage,\n version,\n })\n } catch (error) {\n logger.error('failed to execute upgrade action', { component: 'upgrade-actions', err: error })\n const message = translate('upgrades.runFailed', 'We could not run this upgrade action.')\n const details = error instanceof Error ? error.message : null\n return NextResponse.json({ error: message, details }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: configsTag,\n summary: 'Upgrade actions management',\n methods: {\n GET: {\n summary: 'List pending upgrade actions',\n description: 'Returns a list of pending upgrade actions for the current version. These are one-time setup tasks that need to be executed after upgrading to a new version. Requires organization and tenant context.',\n responses: [\n { status: 200, description: 'List of pending upgrade actions', schema: upgradeActionsListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Missing organization or tenant context', schema: configErrorSchema },\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to load upgrade actions', schema: configErrorSchema },\n ],\n },\n POST: {\n summary: 'Execute upgrade action',\n description: 'Executes a specific upgrade action by ID. Typically used for one-time setup tasks like seeding example data after version upgrade. Returns execution status and localized success message.',\n requestBody: {\n contentType: 'application/json',\n schema: executeUpgradeActionRequestSchema,\n },\n responses: [\n { status: 200, description: 'Upgrade action executed successfully', schema: executeUpgradeActionResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid request body or missing context', schema: configErrorSchema },\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 403, description: 'Upgrade actions are disabled', schema: configErrorSchema },\n { status: 500, description: 'Failed to execute upgrade action', schema: configErrorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }