@open-mercato/core 0.6.8-develop.6983.1.e4f794099e → 0.6.8-develop.6985.1.fb93574faa

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 (597) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/notification/index.js +6 -0
  3. package/dist/generated/entities/notification/index.js.map +2 -2
  4. package/dist/generated/entities/notification_preference/index.js +19 -0
  5. package/dist/generated/entities/notification_preference/index.js.map +7 -0
  6. package/dist/generated/entities/notification_type/index.js +21 -0
  7. package/dist/generated/entities/notification_type/index.js.map +7 -0
  8. package/dist/generated/entities/notification_type_override/index.js +17 -0
  9. package/dist/generated/entities/notification_type_override/index.js.map +7 -0
  10. package/dist/generated/entities/push_notification_delivery/index.js +41 -0
  11. package/dist/generated/entities/push_notification_delivery/index.js.map +7 -0
  12. package/dist/generated/entities/user_device/index.js +35 -0
  13. package/dist/generated/entities/user_device/index.js.map +7 -0
  14. package/dist/generated/entities.ids.generated.js +12 -1
  15. package/dist/generated/entities.ids.generated.js.map +2 -2
  16. package/dist/generated/entity-fields-registry.js +72 -0
  17. package/dist/generated/entity-fields-registry.js.map +2 -2
  18. package/dist/helpers/integration/api.js +2 -1
  19. package/dist/helpers/integration/api.js.map +2 -2
  20. package/dist/helpers/integration/appRoot.js +9 -0
  21. package/dist/helpers/integration/appRoot.js.map +7 -0
  22. package/dist/helpers/integration/dbFixtures.js +1 -4
  23. package/dist/helpers/integration/dbFixtures.js.map +2 -2
  24. package/dist/helpers/integration/pushFake.js +126 -0
  25. package/dist/helpers/integration/pushFake.js.map +7 -0
  26. package/dist/modules/auth/notifications.js +16 -0
  27. package/dist/modules/auth/notifications.js.map +2 -2
  28. package/dist/modules/business_rules/notifications.js +2 -0
  29. package/dist/modules/business_rules/notifications.js.map +2 -2
  30. package/dist/modules/catalog/notifications.js +2 -0
  31. package/dist/modules/catalog/notifications.js.map +2 -2
  32. package/dist/modules/communication_channels/acl.js +9 -0
  33. package/dist/modules/communication_channels/acl.js.map +2 -2
  34. package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js +133 -0
  35. package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js.map +7 -0
  36. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +6 -2
  37. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +2 -2
  38. package/dist/modules/communication_channels/api/get/channels/[id]/route.js +6 -2
  39. package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +2 -2
  40. package/dist/modules/communication_channels/api/get/channels/route.js +2 -1
  41. package/dist/modules/communication_channels/api/get/channels/route.js.map +2 -2
  42. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +10 -5
  43. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
  44. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +29 -1
  45. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
  46. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +157 -0
  47. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +7 -0
  48. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +8 -1
  49. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
  50. package/dist/modules/communication_channels/commands/admin-delete-channel.js +141 -0
  51. package/dist/modules/communication_channels/commands/admin-delete-channel.js.map +7 -0
  52. package/dist/modules/communication_channels/commands/connect-credential-channel.js +25 -8
  53. package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +2 -2
  54. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +2 -2
  55. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
  56. package/dist/modules/communication_channels/commands/push-register.js +1 -1
  57. package/dist/modules/communication_channels/commands/push-register.js.map +2 -2
  58. package/dist/modules/communication_channels/commands/push-unregister.js +4 -3
  59. package/dist/modules/communication_channels/commands/push-unregister.js.map +2 -2
  60. package/dist/modules/communication_channels/data/entities.js +4 -0
  61. package/dist/modules/communication_channels/data/entities.js.map +2 -2
  62. package/dist/modules/communication_channels/data/validators.js +2 -1
  63. package/dist/modules/communication_channels/data/validators.js.map +2 -2
  64. package/dist/modules/communication_channels/lib/access-control.js +11 -1
  65. package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
  66. package/dist/modules/communication_channels/lib/connect-channel.js +20 -12
  67. package/dist/modules/communication_channels/lib/connect-channel.js.map +2 -2
  68. package/dist/modules/communication_channels/lib/push-adapter.js +68 -0
  69. package/dist/modules/communication_channels/lib/push-adapter.js.map +7 -0
  70. package/dist/modules/communication_channels/lib/push-capabilities.js +31 -0
  71. package/dist/modules/communication_channels/lib/push-capabilities.js.map +7 -0
  72. package/dist/modules/communication_channels/lib/push-connect-error.js +20 -0
  73. package/dist/modules/communication_channels/lib/push-connect-error.js.map +7 -0
  74. package/dist/modules/communication_channels/lib/push-credential-errors.js +41 -0
  75. package/dist/modules/communication_channels/lib/push-credential-errors.js.map +7 -0
  76. package/dist/modules/communication_channels/lib/push-envelope.js +46 -0
  77. package/dist/modules/communication_channels/lib/push-envelope.js.map +7 -0
  78. package/dist/modules/communication_channels/lib/refcounted-client-cache.js +64 -0
  79. package/dist/modules/communication_channels/lib/refcounted-client-cache.js.map +7 -0
  80. package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js +13 -0
  81. package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js.map +7 -0
  82. package/dist/modules/communication_channels/notifications.js +4 -0
  83. package/dist/modules/communication_channels/notifications.js.map +2 -2
  84. package/dist/modules/communication_channels/setup.js +2 -0
  85. package/dist/modules/communication_channels/setup.js.map +2 -2
  86. package/dist/modules/configs/lib/upgrade-actions.js +21 -0
  87. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  88. package/dist/modules/customer_accounts/notifications.js +12 -0
  89. package/dist/modules/customer_accounts/notifications.js.map +2 -2
  90. package/dist/modules/customers/notifications.js +4 -0
  91. package/dist/modules/customers/notifications.js.map +2 -2
  92. package/dist/modules/devices/acl.js +11 -0
  93. package/dist/modules/devices/acl.js.map +7 -0
  94. package/dist/modules/devices/api/[id]/route.js +167 -0
  95. package/dist/modules/devices/api/[id]/route.js.map +7 -0
  96. package/dist/modules/devices/api/admin/devices/[id]/route.js +219 -0
  97. package/dist/modules/devices/api/admin/devices/[id]/route.js.map +7 -0
  98. package/dist/modules/devices/api/admin/devices/route.js +140 -0
  99. package/dist/modules/devices/api/admin/devices/route.js.map +7 -0
  100. package/dist/modules/devices/api/auth.js +9 -0
  101. package/dist/modules/devices/api/auth.js.map +7 -0
  102. package/dist/modules/devices/api/deviceList.js +53 -0
  103. package/dist/modules/devices/api/deviceList.js.map +7 -0
  104. package/dist/modules/devices/api/deviceOps.js +118 -0
  105. package/dist/modules/devices/api/deviceOps.js.map +7 -0
  106. package/dist/modules/devices/api/openapi.js +19 -0
  107. package/dist/modules/devices/api/openapi.js.map +7 -0
  108. package/dist/modules/devices/api/route.js +143 -0
  109. package/dist/modules/devices/api/route.js.map +7 -0
  110. package/dist/modules/devices/backend/devices/[id]/page.js +135 -0
  111. package/dist/modules/devices/backend/devices/[id]/page.js.map +7 -0
  112. package/dist/modules/devices/backend/devices/[id]/page.meta.js +16 -0
  113. package/dist/modules/devices/backend/devices/[id]/page.meta.js.map +7 -0
  114. package/dist/modules/devices/backend/devices/create/page.js +68 -0
  115. package/dist/modules/devices/backend/devices/create/page.js.map +7 -0
  116. package/dist/modules/devices/backend/devices/create/page.meta.js +16 -0
  117. package/dist/modules/devices/backend/devices/create/page.meta.js.map +7 -0
  118. package/dist/modules/devices/backend/devices/page.js +235 -0
  119. package/dist/modules/devices/backend/devices/page.js.map +7 -0
  120. package/dist/modules/devices/backend/devices/page.meta.js +23 -0
  121. package/dist/modules/devices/backend/devices/page.meta.js.map +7 -0
  122. package/dist/modules/devices/commands/deactivate.js +107 -0
  123. package/dist/modules/devices/commands/deactivate.js.map +7 -0
  124. package/dist/modules/devices/commands/index.js +4 -0
  125. package/dist/modules/devices/commands/index.js.map +7 -0
  126. package/dist/modules/devices/commands/register.js +183 -0
  127. package/dist/modules/devices/commands/register.js.map +7 -0
  128. package/dist/modules/devices/commands/shared.js +74 -0
  129. package/dist/modules/devices/commands/shared.js.map +7 -0
  130. package/dist/modules/devices/commands/update.js +126 -0
  131. package/dist/modules/devices/commands/update.js.map +7 -0
  132. package/dist/modules/devices/data/entities.js +83 -0
  133. package/dist/modules/devices/data/entities.js.map +7 -0
  134. package/dist/modules/devices/data/validators.js +49 -0
  135. package/dist/modules/devices/data/validators.js.map +7 -0
  136. package/dist/modules/devices/di.js +11 -0
  137. package/dist/modules/devices/di.js.map +7 -0
  138. package/dist/modules/devices/encryption.js +12 -0
  139. package/dist/modules/devices/encryption.js.map +7 -0
  140. package/dist/modules/devices/events.js +19 -0
  141. package/dist/modules/devices/events.js.map +7 -0
  142. package/dist/modules/devices/index.js +17 -0
  143. package/dist/modules/devices/index.js.map +7 -0
  144. package/dist/modules/devices/lib/operationMetadata.js +20 -0
  145. package/dist/modules/devices/lib/operationMetadata.js.map +7 -0
  146. package/dist/modules/devices/migrations/Migration20260602120000.js +15 -0
  147. package/dist/modules/devices/migrations/Migration20260602120000.js.map +7 -0
  148. package/dist/modules/devices/migrations/Migration20260630120000.js +15 -0
  149. package/dist/modules/devices/migrations/Migration20260630120000.js.map +7 -0
  150. package/dist/modules/devices/migrations/Migration20260701120000.js +13 -0
  151. package/dist/modules/devices/migrations/Migration20260701120000.js.map +7 -0
  152. package/dist/modules/devices/migrations/Migration20260721120000.js +15 -0
  153. package/dist/modules/devices/migrations/Migration20260721120000.js.map +7 -0
  154. package/dist/modules/devices/setup.js +13 -0
  155. package/dist/modules/devices/setup.js.map +7 -0
  156. package/dist/modules/entities/cli.js +8 -3
  157. package/dist/modules/entities/cli.js.map +2 -2
  158. package/dist/modules/entities/migrations/Migration20260722120000.js +28 -0
  159. package/dist/modules/entities/migrations/Migration20260722120000.js.map +7 -0
  160. package/dist/modules/inbox_ops/notifications.js +2 -0
  161. package/dist/modules/inbox_ops/notifications.js.map +2 -2
  162. package/dist/modules/messages/notifications.js +2 -0
  163. package/dist/modules/messages/notifications.js.map +2 -2
  164. package/dist/modules/notifications/acl.js +3 -1
  165. package/dist/modules/notifications/acl.js.map +2 -2
  166. package/dist/modules/notifications/api/admin/preferences/route.js +167 -0
  167. package/dist/modules/notifications/api/admin/preferences/route.js.map +7 -0
  168. package/dist/modules/notifications/api/channels/route.js +54 -0
  169. package/dist/modules/notifications/api/channels/route.js.map +7 -0
  170. package/dist/modules/notifications/api/preferences/route.js +126 -0
  171. package/dist/modules/notifications/api/preferences/route.js.map +7 -0
  172. package/dist/modules/notifications/api/route.js +8 -1
  173. package/dist/modules/notifications/api/route.js.map +2 -2
  174. package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js +187 -0
  175. package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js.map +7 -0
  176. package/dist/modules/notifications/api/types/route.js +225 -0
  177. package/dist/modules/notifications/api/types/route.js.map +7 -0
  178. package/dist/modules/notifications/api/unread-count/route.js +5 -1
  179. package/dist/modules/notifications/api/unread-count/route.js.map +2 -2
  180. package/dist/modules/notifications/backend/notifications/user-preferences/page.js +10 -0
  181. package/dist/modules/notifications/backend/notifications/user-preferences/page.js.map +7 -0
  182. package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js +28 -0
  183. package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js.map +7 -0
  184. package/dist/modules/notifications/backend/profile/notification-preferences/page.js +10 -0
  185. package/dist/modules/notifications/backend/profile/notification-preferences/page.js.map +7 -0
  186. package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js +33 -0
  187. package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js.map +7 -0
  188. package/dist/modules/notifications/data/entities.js +128 -1
  189. package/dist/modules/notifications/data/entities.js.map +2 -2
  190. package/dist/modules/notifications/data/validators.js +95 -2
  191. package/dist/modules/notifications/data/validators.js.map +2 -2
  192. package/dist/modules/notifications/di.js +7 -0
  193. package/dist/modules/notifications/di.js.map +2 -2
  194. package/dist/modules/notifications/events.js +17 -0
  195. package/dist/modules/notifications/events.js.map +2 -2
  196. package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js +190 -0
  197. package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js.map +7 -0
  198. package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js +124 -0
  199. package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js.map +7 -0
  200. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +166 -1
  201. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
  202. package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js +172 -0
  203. package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js.map +7 -0
  204. package/dist/modules/notifications/generators.js +63 -0
  205. package/dist/modules/notifications/generators.js.map +7 -0
  206. package/dist/modules/notifications/lib/delivery-strategies-registry.js +19 -0
  207. package/dist/modules/notifications/lib/delivery-strategies-registry.js.map +7 -0
  208. package/dist/modules/notifications/lib/deliveryStrategies.js.map +2 -2
  209. package/dist/modules/notifications/lib/derive-category.js +8 -0
  210. package/dist/modules/notifications/lib/derive-category.js.map +7 -0
  211. package/dist/modules/notifications/lib/notification-channel-registry.js +35 -0
  212. package/dist/modules/notifications/lib/notification-channel-registry.js.map +7 -0
  213. package/dist/modules/notifications/lib/notification-type-registry.js +87 -0
  214. package/dist/modules/notifications/lib/notification-type-registry.js.map +7 -0
  215. package/dist/modules/notifications/lib/notificationCopy.js +14 -0
  216. package/dist/modules/notifications/lib/notificationCopy.js.map +7 -0
  217. package/dist/modules/notifications/lib/notificationFactory.js +4 -1
  218. package/dist/modules/notifications/lib/notificationFactory.js.map +2 -2
  219. package/dist/modules/notifications/lib/notificationMapper.js +2 -0
  220. package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
  221. package/dist/modules/notifications/lib/notificationPreferenceService.js +83 -0
  222. package/dist/modules/notifications/lib/notificationPreferenceService.js.map +7 -0
  223. package/dist/modules/notifications/lib/notificationService.js +83 -28
  224. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  225. package/dist/modules/notifications/lib/notificationVisibility.js +24 -0
  226. package/dist/modules/notifications/lib/notificationVisibility.js.map +7 -0
  227. package/dist/modules/notifications/lib/routeHelpers.js +11 -1
  228. package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
  229. package/dist/modules/notifications/lib/shouldDeliver.js +46 -0
  230. package/dist/modules/notifications/lib/shouldDeliver.js.map +7 -0
  231. package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js +54 -0
  232. package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js.map +7 -0
  233. package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js +12 -0
  234. package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js.map +7 -0
  235. package/dist/modules/notifications/lib/typeCatalogue.js +71 -0
  236. package/dist/modules/notifications/lib/typeCatalogue.js.map +7 -0
  237. package/dist/modules/notifications/lib/typeChannelSettings.js +12 -0
  238. package/dist/modules/notifications/lib/typeChannelSettings.js.map +7 -0
  239. package/dist/modules/notifications/lib/typeOverrides.js +19 -0
  240. package/dist/modules/notifications/lib/typeOverrides.js.map +7 -0
  241. package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js +14 -0
  242. package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js.map +7 -0
  243. package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js +15 -0
  244. package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js.map +7 -0
  245. package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js +13 -0
  246. package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js.map +7 -0
  247. package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js +15 -0
  248. package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js.map +7 -0
  249. package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js +13 -0
  250. package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js.map +7 -0
  251. package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js +14 -0
  252. package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js.map +7 -0
  253. package/dist/modules/notifications/notification-channels.js +26 -0
  254. package/dist/modules/notifications/notification-channels.js.map +7 -0
  255. package/dist/modules/notifications/notifications.delivery-strategies.js +12 -0
  256. package/dist/modules/notifications/notifications.delivery-strategies.js.map +7 -0
  257. package/dist/modules/notifications/setup.js +16 -1
  258. package/dist/modules/notifications/setup.js.map +2 -2
  259. package/dist/modules/notifications/subscribers/deliver-notification.js +66 -57
  260. package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
  261. package/dist/modules/notifications/subscribers/sync-notification-types.js +16 -0
  262. package/dist/modules/notifications/subscribers/sync-notification-types.js.map +7 -0
  263. package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js +23 -0
  264. package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js.map +7 -0
  265. package/dist/modules/notifications/widgets/injection-table.js +14 -0
  266. package/dist/modules/notifications/widgets/injection-table.js.map +7 -0
  267. package/dist/modules/push_notifications/acl.js +13 -0
  268. package/dist/modules/push_notifications/acl.js.map +7 -0
  269. package/dist/modules/push_notifications/api/custom-send/route.js +141 -0
  270. package/dist/modules/push_notifications/api/custom-send/route.js.map +7 -0
  271. package/dist/modules/push_notifications/api/deliveries/[id]/route.js +110 -0
  272. package/dist/modules/push_notifications/api/deliveries/[id]/route.js.map +7 -0
  273. package/dist/modules/push_notifications/api/deliveries/route.js +56 -0
  274. package/dist/modules/push_notifications/api/deliveries/route.js.map +7 -0
  275. package/dist/modules/push_notifications/api/openapi.js +19 -0
  276. package/dist/modules/push_notifications/api/openapi.js.map +7 -0
  277. package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js +103 -0
  278. package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js.map +7 -0
  279. package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js +13 -0
  280. package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js.map +7 -0
  281. package/dist/modules/push_notifications/backend/push_notifications/page.js +213 -0
  282. package/dist/modules/push_notifications/backend/push_notifications/page.js.map +7 -0
  283. package/dist/modules/push_notifications/backend/push_notifications/page.meta.js +23 -0
  284. package/dist/modules/push_notifications/backend/push_notifications/page.meta.js.map +7 -0
  285. package/dist/modules/push_notifications/backend/push_notifications/send/page.js +245 -0
  286. package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +7 -0
  287. package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js +26 -0
  288. package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js.map +7 -0
  289. package/dist/modules/push_notifications/data/entities.js +92 -0
  290. package/dist/modules/push_notifications/data/entities.js.map +7 -0
  291. package/dist/modules/push_notifications/data/extensions.js +23 -0
  292. package/dist/modules/push_notifications/data/extensions.js.map +7 -0
  293. package/dist/modules/push_notifications/data/validators.js +89 -0
  294. package/dist/modules/push_notifications/data/validators.js.map +7 -0
  295. package/dist/modules/push_notifications/di.js +17 -0
  296. package/dist/modules/push_notifications/di.js.map +7 -0
  297. package/dist/modules/push_notifications/events.js +24 -0
  298. package/dist/modules/push_notifications/events.js.map +7 -0
  299. package/dist/modules/push_notifications/index.js +16 -0
  300. package/dist/modules/push_notifications/index.js.map +7 -0
  301. package/dist/modules/push_notifications/lib/fake-provider-recorder.js +80 -0
  302. package/dist/modules/push_notifications/lib/fake-provider-recorder.js.map +7 -0
  303. package/dist/modules/push_notifications/lib/push-delivery-strategy.js +54 -0
  304. package/dist/modules/push_notifications/lib/push-delivery-strategy.js.map +7 -0
  305. package/dist/modules/push_notifications/lib/push-delivery.js +297 -0
  306. package/dist/modules/push_notifications/lib/push-delivery.js.map +7 -0
  307. package/dist/modules/push_notifications/lib/push-fanout.js +138 -0
  308. package/dist/modules/push_notifications/lib/push-fanout.js.map +7 -0
  309. package/dist/modules/push_notifications/lib/push-health.js +32 -0
  310. package/dist/modules/push_notifications/lib/push-health.js.map +7 -0
  311. package/dist/modules/push_notifications/lib/push-reaper.js +87 -0
  312. package/dist/modules/push_notifications/lib/push-reaper.js.map +7 -0
  313. package/dist/modules/push_notifications/lib/push-receipt-reaper.js +136 -0
  314. package/dist/modules/push_notifications/lib/push-receipt-reaper.js.map +7 -0
  315. package/dist/modules/push_notifications/lib/push-stub-adapter.js +63 -0
  316. package/dist/modules/push_notifications/lib/push-stub-adapter.js.map +7 -0
  317. package/dist/modules/push_notifications/lib/queue.js +53 -0
  318. package/dist/modules/push_notifications/lib/queue.js.map +7 -0
  319. package/dist/modules/push_notifications/lib/reclaim-window.js +13 -0
  320. package/dist/modules/push_notifications/lib/reclaim-window.js.map +7 -0
  321. package/dist/modules/push_notifications/lib/send-custom-push.js +48 -0
  322. package/dist/modules/push_notifications/lib/send-custom-push.js.map +7 -0
  323. package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js +16 -0
  324. package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js.map +7 -0
  325. package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js +13 -0
  326. package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js.map +7 -0
  327. package/dist/modules/push_notifications/notifications.delivery-strategies.js +8 -0
  328. package/dist/modules/push_notifications/notifications.delivery-strategies.js.map +7 -0
  329. package/dist/modules/push_notifications/notifications.js +40 -0
  330. package/dist/modules/push_notifications/notifications.js.map +7 -0
  331. package/dist/modules/push_notifications/setup.js +52 -0
  332. package/dist/modules/push_notifications/setup.js.map +7 -0
  333. package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js +57 -0
  334. package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js.map +7 -0
  335. package/dist/modules/push_notifications/workers/send-push.worker.js +27 -0
  336. package/dist/modules/push_notifications/workers/send-push.worker.js.map +7 -0
  337. package/dist/modules/sales/notifications.js +8 -0
  338. package/dist/modules/sales/notifications.js.map +2 -2
  339. package/dist/modules/staff/notifications.js +6 -0
  340. package/dist/modules/staff/notifications.js.map +2 -2
  341. package/dist/modules/workflows/notifications.js +2 -0
  342. package/dist/modules/workflows/notifications.js.map +2 -2
  343. package/generated/entities/notification/index.ts +3 -0
  344. package/generated/entities/notification_preference/index.ts +8 -0
  345. package/generated/entities/notification_type/index.ts +9 -0
  346. package/generated/entities/notification_type_override/index.ts +7 -0
  347. package/generated/entities/push_notification_delivery/index.ts +19 -0
  348. package/generated/entities/user_device/index.ts +16 -0
  349. package/generated/entities.ids.generated.ts +12 -1
  350. package/generated/entity-fields-registry.ts +72 -0
  351. package/package.json +7 -7
  352. package/src/helpers/integration/api.ts +2 -1
  353. package/src/helpers/integration/appRoot.ts +13 -0
  354. package/src/helpers/integration/dbFixtures.ts +1 -5
  355. package/src/helpers/integration/pushFake.ts +219 -0
  356. package/src/modules/auth/i18n/de.json +1 -0
  357. package/src/modules/auth/i18n/en.json +1 -0
  358. package/src/modules/auth/i18n/es.json +1 -0
  359. package/src/modules/auth/i18n/ko.json +1 -0
  360. package/src/modules/auth/i18n/pl.json +1 -0
  361. package/src/modules/auth/notifications.ts +16 -0
  362. package/src/modules/business_rules/i18n/de.json +2 -1
  363. package/src/modules/business_rules/i18n/en.json +2 -1
  364. package/src/modules/business_rules/i18n/es.json +2 -1
  365. package/src/modules/business_rules/i18n/ko.json +2 -1
  366. package/src/modules/business_rules/i18n/pl.json +2 -1
  367. package/src/modules/business_rules/notifications.ts +2 -0
  368. package/src/modules/catalog/i18n/de.json +2 -1
  369. package/src/modules/catalog/i18n/en.json +2 -1
  370. package/src/modules/catalog/i18n/es.json +2 -1
  371. package/src/modules/catalog/i18n/ko.json +2 -1
  372. package/src/modules/catalog/i18n/pl.json +2 -1
  373. package/src/modules/catalog/notifications.ts +2 -0
  374. package/src/modules/communication_channels/acl.ts +9 -0
  375. package/src/modules/communication_channels/api/delete/admin/channels/[id]/route.ts +168 -0
  376. package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +6 -2
  377. package/src/modules/communication_channels/api/get/channels/[id]/route.ts +6 -2
  378. package/src/modules/communication_channels/api/get/channels/route.ts +2 -1
  379. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +16 -5
  380. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +37 -1
  381. package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +175 -0
  382. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +11 -1
  383. package/src/modules/communication_channels/commands/admin-delete-channel.ts +203 -0
  384. package/src/modules/communication_channels/commands/connect-credential-channel.ts +63 -15
  385. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +10 -2
  386. package/src/modules/communication_channels/commands/push-register.ts +7 -1
  387. package/src/modules/communication_channels/commands/push-unregister.ts +14 -3
  388. package/src/modules/communication_channels/data/entities.ts +10 -0
  389. package/src/modules/communication_channels/data/validators.ts +1 -0
  390. package/src/modules/communication_channels/i18n/de.json +35 -1
  391. package/src/modules/communication_channels/i18n/en.json +35 -1
  392. package/src/modules/communication_channels/i18n/es.json +35 -1
  393. package/src/modules/communication_channels/i18n/ko.json +35 -1
  394. package/src/modules/communication_channels/i18n/pl.json +35 -1
  395. package/src/modules/communication_channels/lib/access-control.ts +42 -0
  396. package/src/modules/communication_channels/lib/adapter.ts +25 -0
  397. package/src/modules/communication_channels/lib/connect-channel.ts +61 -26
  398. package/src/modules/communication_channels/lib/push-adapter.ts +139 -0
  399. package/src/modules/communication_channels/lib/push-capabilities.ts +42 -0
  400. package/src/modules/communication_channels/lib/push-connect-error.ts +52 -0
  401. package/src/modules/communication_channels/lib/push-credential-errors.ts +58 -0
  402. package/src/modules/communication_channels/lib/push-envelope.ts +104 -0
  403. package/src/modules/communication_channels/lib/refcounted-client-cache.ts +123 -0
  404. package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +9 -0
  405. package/src/modules/communication_channels/migrations/Migration20260703123630_communication_channels.ts +20 -0
  406. package/src/modules/communication_channels/notifications.ts +4 -0
  407. package/src/modules/communication_channels/setup.ts +2 -0
  408. package/src/modules/configs/i18n/de.json +5 -1
  409. package/src/modules/configs/i18n/en.json +5 -1
  410. package/src/modules/configs/i18n/es.json +5 -1
  411. package/src/modules/configs/i18n/ko.json +5 -1
  412. package/src/modules/configs/i18n/pl.json +5 -1
  413. package/src/modules/configs/lib/upgrade-actions.ts +21 -0
  414. package/src/modules/customer_accounts/i18n/de.json +2 -1
  415. package/src/modules/customer_accounts/i18n/en.json +2 -1
  416. package/src/modules/customer_accounts/i18n/es.json +2 -1
  417. package/src/modules/customer_accounts/i18n/ko.json +2 -1
  418. package/src/modules/customer_accounts/i18n/pl.json +2 -1
  419. package/src/modules/customer_accounts/notifications.ts +12 -0
  420. package/src/modules/customers/i18n/de.json +2 -1
  421. package/src/modules/customers/i18n/en.json +2 -1
  422. package/src/modules/customers/i18n/es.json +2 -1
  423. package/src/modules/customers/i18n/ko.json +2 -1
  424. package/src/modules/customers/i18n/pl.json +2 -1
  425. package/src/modules/customers/notifications.ts +4 -0
  426. package/src/modules/devices/AGENTS.md +101 -0
  427. package/src/modules/devices/acl.ts +7 -0
  428. package/src/modules/devices/api/[id]/route.ts +180 -0
  429. package/src/modules/devices/api/admin/devices/[id]/route.ts +235 -0
  430. package/src/modules/devices/api/admin/devices/route.ts +160 -0
  431. package/src/modules/devices/api/auth.ts +13 -0
  432. package/src/modules/devices/api/deviceList.ts +54 -0
  433. package/src/modules/devices/api/deviceOps.ts +186 -0
  434. package/src/modules/devices/api/openapi.ts +23 -0
  435. package/src/modules/devices/api/route.ts +165 -0
  436. package/src/modules/devices/backend/devices/[id]/page.meta.ts +12 -0
  437. package/src/modules/devices/backend/devices/[id]/page.tsx +164 -0
  438. package/src/modules/devices/backend/devices/create/page.meta.ts +12 -0
  439. package/src/modules/devices/backend/devices/create/page.tsx +81 -0
  440. package/src/modules/devices/backend/devices/page.meta.ts +21 -0
  441. package/src/modules/devices/backend/devices/page.tsx +259 -0
  442. package/src/modules/devices/commands/deactivate.ts +126 -0
  443. package/src/modules/devices/commands/index.ts +3 -0
  444. package/src/modules/devices/commands/register.ts +199 -0
  445. package/src/modules/devices/commands/shared.ts +119 -0
  446. package/src/modules/devices/commands/update.ts +142 -0
  447. package/src/modules/devices/data/entities.ts +79 -0
  448. package/src/modules/devices/data/validators.ts +65 -0
  449. package/src/modules/devices/di.ts +9 -0
  450. package/src/modules/devices/encryption.ts +15 -0
  451. package/src/modules/devices/events.ts +16 -0
  452. package/src/modules/devices/i18n/de.json +45 -0
  453. package/src/modules/devices/i18n/en.json +45 -0
  454. package/src/modules/devices/i18n/es.json +45 -0
  455. package/src/modules/devices/i18n/ko.json +45 -0
  456. package/src/modules/devices/i18n/pl.json +45 -0
  457. package/src/modules/devices/index.ts +15 -0
  458. package/src/modules/devices/lib/operationMetadata.ts +35 -0
  459. package/src/modules/devices/migrations/.snapshot-open-mercato.json +200 -0
  460. package/src/modules/devices/migrations/Migration20260602120000.ts +15 -0
  461. package/src/modules/devices/migrations/Migration20260630120000.ts +20 -0
  462. package/src/modules/devices/migrations/Migration20260701120000.ts +13 -0
  463. package/src/modules/devices/migrations/Migration20260721120000.ts +18 -0
  464. package/src/modules/devices/setup.ts +11 -0
  465. package/src/modules/entities/cli.ts +16 -2
  466. package/src/modules/entities/migrations/Migration20260722120000.ts +38 -0
  467. package/src/modules/inbox_ops/i18n/de.json +2 -1
  468. package/src/modules/inbox_ops/i18n/en.json +2 -1
  469. package/src/modules/inbox_ops/i18n/es.json +2 -1
  470. package/src/modules/inbox_ops/i18n/ko.json +2 -1
  471. package/src/modules/inbox_ops/i18n/pl.json +2 -1
  472. package/src/modules/inbox_ops/notifications.ts +2 -0
  473. package/src/modules/messages/i18n/de.json +2 -1
  474. package/src/modules/messages/i18n/en.json +2 -1
  475. package/src/modules/messages/i18n/es.json +2 -1
  476. package/src/modules/messages/i18n/ko.json +2 -1
  477. package/src/modules/messages/i18n/pl.json +2 -1
  478. package/src/modules/messages/notifications.ts +2 -0
  479. package/src/modules/notifications/AGENTS.md +54 -0
  480. package/src/modules/notifications/acl.ts +2 -0
  481. package/src/modules/notifications/api/admin/preferences/route.ts +193 -0
  482. package/src/modules/notifications/api/channels/route.ts +54 -0
  483. package/src/modules/notifications/api/preferences/route.ts +131 -0
  484. package/src/modules/notifications/api/route.ts +8 -1
  485. package/src/modules/notifications/api/types/[id]/channels/[channel]/route.ts +226 -0
  486. package/src/modules/notifications/api/types/route.ts +239 -0
  487. package/src/modules/notifications/api/unread-count/route.ts +5 -1
  488. package/src/modules/notifications/backend/notifications/user-preferences/page.meta.ts +26 -0
  489. package/src/modules/notifications/backend/notifications/user-preferences/page.tsx +12 -0
  490. package/src/modules/notifications/backend/profile/notification-preferences/page.meta.ts +31 -0
  491. package/src/modules/notifications/backend/profile/notification-preferences/page.tsx +12 -0
  492. package/src/modules/notifications/data/entities.ts +144 -0
  493. package/src/modules/notifications/data/validators.ts +116 -0
  494. package/src/modules/notifications/di.ts +7 -0
  495. package/src/modules/notifications/events.ts +23 -0
  496. package/src/modules/notifications/frontend/NotificationPreferenceMatrix.tsx +287 -0
  497. package/src/modules/notifications/frontend/NotificationPreferencesPageClient.tsx +155 -0
  498. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +273 -2
  499. package/src/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.tsx +214 -0
  500. package/src/modules/notifications/generators.ts +82 -0
  501. package/src/modules/notifications/i18n/de.json +43 -0
  502. package/src/modules/notifications/i18n/en.json +43 -0
  503. package/src/modules/notifications/i18n/es.json +43 -0
  504. package/src/modules/notifications/i18n/ko.json +43 -0
  505. package/src/modules/notifications/i18n/pl.json +44 -1
  506. package/src/modules/notifications/lib/delivery-strategies-registry.ts +30 -0
  507. package/src/modules/notifications/lib/deliveryStrategies.ts +40 -4
  508. package/src/modules/notifications/lib/derive-category.ts +15 -0
  509. package/src/modules/notifications/lib/notification-channel-registry.ts +59 -0
  510. package/src/modules/notifications/lib/notification-type-registry.ts +182 -0
  511. package/src/modules/notifications/lib/notificationCopy.ts +34 -0
  512. package/src/modules/notifications/lib/notificationFactory.ts +5 -1
  513. package/src/modules/notifications/lib/notificationMapper.ts +2 -0
  514. package/src/modules/notifications/lib/notificationPreferenceService.ts +138 -0
  515. package/src/modules/notifications/lib/notificationService.ts +129 -25
  516. package/src/modules/notifications/lib/notificationVisibility.ts +48 -0
  517. package/src/modules/notifications/lib/routeHelpers.ts +14 -1
  518. package/src/modules/notifications/lib/shouldDeliver.ts +112 -0
  519. package/src/modules/notifications/lib/strategies/email-delivery-strategy.ts +63 -0
  520. package/src/modules/notifications/lib/strategies/in-app-delivery-strategy.ts +21 -0
  521. package/src/modules/notifications/lib/typeCatalogue.ts +141 -0
  522. package/src/modules/notifications/lib/typeChannelSettings.ts +29 -0
  523. package/src/modules/notifications/lib/typeOverrides.ts +46 -0
  524. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +874 -100
  525. package/src/modules/notifications/migrations/Migration20260625122947_notifications.ts +14 -0
  526. package/src/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.ts +15 -0
  527. package/src/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.ts +13 -0
  528. package/src/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.ts +15 -0
  529. package/src/modules/notifications/migrations/Migration20260706120000_notifications_channels.ts +13 -0
  530. package/src/modules/notifications/migrations/Migration20260716123121_notifications.ts +14 -0
  531. package/src/modules/notifications/notification-channels.ts +30 -0
  532. package/src/modules/notifications/notifications.delivery-strategies.ts +15 -0
  533. package/src/modules/notifications/setup.ts +23 -1
  534. package/src/modules/notifications/subscribers/deliver-notification.ts +105 -71
  535. package/src/modules/notifications/subscribers/sync-notification-types.ts +18 -0
  536. package/src/modules/notifications/widgets/injection/profile-preferences-menu/widget.ts +35 -0
  537. package/src/modules/notifications/widgets/injection-table.ts +18 -0
  538. package/src/modules/push_notifications/AGENTS.md +92 -0
  539. package/src/modules/push_notifications/acl.ts +9 -0
  540. package/src/modules/push_notifications/api/custom-send/route.ts +175 -0
  541. package/src/modules/push_notifications/api/deliveries/[id]/route.ts +122 -0
  542. package/src/modules/push_notifications/api/deliveries/route.ts +58 -0
  543. package/src/modules/push_notifications/api/openapi.ts +22 -0
  544. package/src/modules/push_notifications/backend/push_notifications/[id]/page.meta.ts +9 -0
  545. package/src/modules/push_notifications/backend/push_notifications/[id]/page.tsx +149 -0
  546. package/src/modules/push_notifications/backend/push_notifications/page.meta.ts +21 -0
  547. package/src/modules/push_notifications/backend/push_notifications/page.tsx +239 -0
  548. package/src/modules/push_notifications/backend/push_notifications/send/page.meta.ts +24 -0
  549. package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +327 -0
  550. package/src/modules/push_notifications/data/entities.ts +111 -0
  551. package/src/modules/push_notifications/data/extensions.ts +29 -0
  552. package/src/modules/push_notifications/data/validators.ts +102 -0
  553. package/src/modules/push_notifications/di.ts +20 -0
  554. package/src/modules/push_notifications/events.ts +21 -0
  555. package/src/modules/push_notifications/i18n/de.json +79 -0
  556. package/src/modules/push_notifications/i18n/en.json +79 -0
  557. package/src/modules/push_notifications/i18n/es.json +79 -0
  558. package/src/modules/push_notifications/i18n/ko.json +79 -0
  559. package/src/modules/push_notifications/i18n/pl.json +79 -0
  560. package/src/modules/push_notifications/index.ts +15 -0
  561. package/src/modules/push_notifications/lib/fake-provider-recorder.ts +154 -0
  562. package/src/modules/push_notifications/lib/push-delivery-strategy.ts +90 -0
  563. package/src/modules/push_notifications/lib/push-delivery.ts +455 -0
  564. package/src/modules/push_notifications/lib/push-fanout.ts +237 -0
  565. package/src/modules/push_notifications/lib/push-health.ts +47 -0
  566. package/src/modules/push_notifications/lib/push-reaper.ts +140 -0
  567. package/src/modules/push_notifications/lib/push-receipt-reaper.ts +210 -0
  568. package/src/modules/push_notifications/lib/push-stub-adapter.ts +80 -0
  569. package/src/modules/push_notifications/lib/queue.ts +74 -0
  570. package/src/modules/push_notifications/lib/reclaim-window.ts +16 -0
  571. package/src/modules/push_notifications/lib/send-custom-push.ts +100 -0
  572. package/src/modules/push_notifications/migrations/.snapshot-open-mercato.json +386 -0
  573. package/src/modules/push_notifications/migrations/Migration20260625150049_push_notifications.ts +16 -0
  574. package/src/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.ts +13 -0
  575. package/src/modules/push_notifications/notifications.delivery-strategies.ts +8 -0
  576. package/src/modules/push_notifications/notifications.ts +50 -0
  577. package/src/modules/push_notifications/setup.ts +92 -0
  578. package/src/modules/push_notifications/workers/reclaim-stuck.worker.ts +78 -0
  579. package/src/modules/push_notifications/workers/send-push.worker.ts +34 -0
  580. package/src/modules/sales/i18n/de.json +1 -0
  581. package/src/modules/sales/i18n/en.json +1 -0
  582. package/src/modules/sales/i18n/es.json +1 -0
  583. package/src/modules/sales/i18n/ko.json +1 -0
  584. package/src/modules/sales/i18n/pl.json +1 -0
  585. package/src/modules/sales/notifications.ts +8 -0
  586. package/src/modules/staff/i18n/de.json +1 -0
  587. package/src/modules/staff/i18n/en.json +1 -0
  588. package/src/modules/staff/i18n/es.json +1 -0
  589. package/src/modules/staff/i18n/ko.json +1 -0
  590. package/src/modules/staff/i18n/pl.json +1 -0
  591. package/src/modules/staff/notifications.ts +6 -0
  592. package/src/modules/workflows/i18n/de.json +1 -0
  593. package/src/modules/workflows/i18n/en.json +1 -0
  594. package/src/modules/workflows/i18n/es.json +1 -0
  595. package/src/modules/workflows/i18n/ko.json +1 -0
  596. package/src/modules/workflows/i18n/pl.json +1 -0
  597. package/src/modules/workflows/notifications.ts +2 -0
@@ -0,0 +1,71 @@
1
+ import { LockMode } from "@mikro-orm/core";
2
+ import { detectLocale, loadDictionary } from "@open-mercato/shared/lib/i18n/server";
3
+ import { createFallbackTranslator } from "@open-mercato/shared/lib/i18n/translate";
4
+ import { resolveSupportedLocale } from "@open-mercato/shared/lib/i18n/locale";
5
+ import { resolveLocaleFromRequest } from "../../translations/lib/locale.js";
6
+ import { NotificationTypeOverride } from "../data/entities.js";
7
+ import { getNotificationDeliveryStrategies } from "./deliveryStrategies.js";
8
+ import { getNotificationType } from "./notification-type-registry.js";
9
+ const CATEGORY_LABEL_KEY_PREFIX = "notifications.categories.";
10
+ async function resolveCatalogueTranslate(req) {
11
+ const requested = resolveSupportedLocale(resolveLocaleFromRequest(req));
12
+ const locale = requested ?? await detectLocale();
13
+ return createFallbackTranslator(await loadDictionary(locale));
14
+ }
15
+ const typeItem = (row, override, translate) => ({
16
+ id: row.id,
17
+ labelKey: row.labelKey,
18
+ descriptionKey: row.descriptionKey ?? null,
19
+ category: row.category ?? null,
20
+ categoryLabel: row.category ? translate(`${CATEGORY_LABEL_KEY_PREFIX}${row.category}`, row.category) : null,
21
+ label: row.labelKey ? translate(row.labelKey, row.id) : null,
22
+ description: row.descriptionKey ? translate(row.descriptionKey, "") : null,
23
+ silent: row.silent === true,
24
+ nonOptOut: (override?.nonOptOut ?? row.nonOptOut) === true,
25
+ channels: override?.channels ?? getNotificationType(row.id)?.channels ?? null,
26
+ storedChannels: override?.channels ?? null,
27
+ storedNonOptOut: override?.nonOptOut ?? null,
28
+ updatedAt: override?.updatedAt ? override.updatedAt.toISOString() : null
29
+ });
30
+ function resolveRegisteredChannelIds() {
31
+ return getNotificationDeliveryStrategies().map((strategy) => strategy.id);
32
+ }
33
+ function resolveEffectiveChannels(typeId, storedChannels, registeredChannelIds) {
34
+ return storedChannels ?? getNotificationType(typeId)?.channels ?? registeredChannelIds;
35
+ }
36
+ function loadTypeOverrideForUpdate(em, tenantId, notificationTypeId) {
37
+ return em.findOne(
38
+ NotificationTypeOverride,
39
+ { tenantId, notificationTypeId },
40
+ { lockMode: LockMode.PESSIMISTIC_WRITE }
41
+ );
42
+ }
43
+ function applyTypeOverride(em, input) {
44
+ const { tenantId, notificationTypeId, existing, nextChannels, nextNonOptOut } = input;
45
+ if (nextChannels === null && nextNonOptOut === null) {
46
+ if (existing) em.remove(existing);
47
+ return null;
48
+ }
49
+ if (existing) {
50
+ existing.channels = nextChannels;
51
+ existing.nonOptOut = nextNonOptOut;
52
+ return existing;
53
+ }
54
+ const created = em.create(NotificationTypeOverride, {
55
+ tenantId,
56
+ notificationTypeId,
57
+ channels: nextChannels,
58
+ nonOptOut: nextNonOptOut
59
+ });
60
+ em.persist(created);
61
+ return created;
62
+ }
63
+ export {
64
+ applyTypeOverride,
65
+ loadTypeOverrideForUpdate,
66
+ resolveCatalogueTranslate,
67
+ resolveEffectiveChannels,
68
+ resolveRegisteredChannelIds,
69
+ typeItem
70
+ };
71
+ //# sourceMappingURL=typeCatalogue.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/lib/typeCatalogue.ts"],
4
+ "sourcesContent": ["import { LockMode } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { detectLocale, loadDictionary } from '@open-mercato/shared/lib/i18n/server'\nimport { createFallbackTranslator } from '@open-mercato/shared/lib/i18n/translate'\nimport { resolveSupportedLocale } from '@open-mercato/shared/lib/i18n/locale'\nimport { resolveLocaleFromRequest } from '../../translations/lib/locale'\nimport { NotificationType, NotificationTypeOverride } from '../data/entities'\nimport { getNotificationDeliveryStrategies } from './deliveryStrategies'\nimport { getNotificationType } from './notification-type-registry'\n\nconst CATEGORY_LABEL_KEY_PREFIX = 'notifications.categories.'\n\nexport type CatalogueTranslate = (key: string, fallback: string) => string\n\n/**\n * Locale for the display strings, following the repo-wide request-locale convention\n * (`resolveLocaleFromRequest`: `?locale=` \u2192 `x-locale` \u2192 cookie \u2192 `Accept-Language`).\n * Only the `Accept-Language` branch of that helper validates its input, so the result is\n * re-checked against the supported set here \u2014 an unsupported `?locale=zz` would otherwise\n * load an empty dictionary instead of degrading. Falls back to ambient detection, which\n * also honours `OM_FORCE_LOCALE`.\n */\nexport async function resolveCatalogueTranslate(req: Request): Promise<CatalogueTranslate> {\n const requested = resolveSupportedLocale(resolveLocaleFromRequest(req))\n const locale = requested ?? (await detectLocale())\n return createFallbackTranslator(await loadDictionary(locale))\n}\n\n/**\n * Catalogue item with the caller tenant's stored overrides merged in \u2014 matching\n * `resolveEligibleChannels` in the delivery gate, so preference UIs lock exactly the cells\n * delivery would reject. `channels: null` = no restriction (every registered channel).\n * `updatedAt` is the override row's version token for optimistic locking (`null` when the\n * tenant stores no override yet).\n *\n * `label` / `description` / `categoryLabel` are resolved server-side so clients without the\n * Open Mercato dictionary (the mobile app) can render the screen directly. Group on\n * `category` \u2014 the raw key, stable across locales \u2014 and display `categoryLabel`; grouping on\n * the localized string re-partitions the list whenever the language changes.\n *\n * A category whose owning module ships no `notifications.categories.<key>` entry falls back\n * to the raw key, so `categoryLabel === category` signals to a client that no server-side\n * translation exists and it may apply its own presentation.\n */\nexport const typeItem = (\n row: NotificationType,\n override: NotificationTypeOverride | null | undefined,\n translate: CatalogueTranslate,\n) => ({\n id: row.id,\n labelKey: row.labelKey,\n descriptionKey: row.descriptionKey ?? null,\n category: row.category ?? null,\n categoryLabel: row.category\n ? translate(`${CATEGORY_LABEL_KEY_PREFIX}${row.category}`, row.category)\n : null,\n label: row.labelKey ? translate(row.labelKey, row.id) : null,\n description: row.descriptionKey ? translate(row.descriptionKey, '') : null,\n silent: row.silent === true,\n nonOptOut: (override?.nonOptOut ?? row.nonOptOut) === true,\n channels: override?.channels ?? getNotificationType(row.id)?.channels ?? null,\n storedChannels: override?.channels ?? null,\n storedNonOptOut: override?.nonOptOut ?? null,\n updatedAt: override?.updatedAt ? override.updatedAt.toISOString() : null,\n})\n\n/** Ids of every registered delivery channel (`in_app`, `email`, `push`, \u2026). */\nexport function resolveRegisteredChannelIds(): string[] {\n return getNotificationDeliveryStrategies().map((strategy) => strategy.id)\n}\n\n/**\n * The channel set a per-channel toggle applies to, resolved server-side so a client never\n * has to send a whole replacing array. Mirrors what the catalogue exposes as the effective\n * set \u2014 stored override, else the code declaration, else every registered channel (the\n * `channels: null` \"no restriction\" case, which a toggle has to materialize before it can\n * remove one entry from it).\n */\nexport function resolveEffectiveChannels(\n typeId: string,\n storedChannels: string[] | null | undefined,\n registeredChannelIds: string[],\n): string[] {\n return storedChannels ?? getNotificationType(typeId)?.channels ?? registeredChannelIds\n}\n\n/**\n * Load the tenant's override row for a type under a row lock, so a read-modify-write of the\n * `channels` array serializes against a concurrent one instead of last-write-wins. Returns\n * `null` when no override is stored yet (nothing exists to lock \u2014 that first-write race is\n * caught by the partial unique index instead).\n */\nexport function loadTypeOverrideForUpdate(\n em: EntityManager,\n tenantId: string,\n notificationTypeId: string,\n): Promise<NotificationTypeOverride | null> {\n return em.findOne(\n NotificationTypeOverride,\n { tenantId, notificationTypeId },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n )\n}\n\n/**\n * Apply the resolved override values to the tenant's row, creating, updating or dropping it.\n * Shared by the full-array `PATCH` and the per-channel add/remove sub-resource so both agree\n * on the \"all overrides cleared \u21D2 remove the row\" rule. Does NOT flush \u2014 the caller owns the\n * transaction boundary and the unique-violation handling.\n */\nexport function applyTypeOverride(\n em: EntityManager,\n input: {\n tenantId: string\n notificationTypeId: string\n existing: NotificationTypeOverride | null\n nextChannels: string[] | null\n nextNonOptOut: boolean | null\n },\n): NotificationTypeOverride | null {\n const { tenantId, notificationTypeId, existing, nextChannels, nextNonOptOut } = input\n if (nextChannels === null && nextNonOptOut === null) {\n // Both overrides cleared \u21D2 the code declarations apply again; drop the row\n // instead of keeping an all-null husk.\n if (existing) em.remove(existing)\n return null\n }\n if (existing) {\n existing.channels = nextChannels\n existing.nonOptOut = nextNonOptOut\n return existing\n }\n const created = em.create(NotificationTypeOverride, {\n tenantId,\n notificationTypeId,\n channels: nextChannels,\n nonOptOut: nextNonOptOut,\n })\n em.persist(created)\n return created\n}\n"],
5
+ "mappings": "AAAA,SAAS,gBAAgB;AAEzB,SAAS,cAAc,sBAAsB;AAC7C,SAAS,gCAAgC;AACzC,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC,SAA2B,gCAAgC;AAC3D,SAAS,yCAAyC;AAClD,SAAS,2BAA2B;AAEpC,MAAM,4BAA4B;AAYlC,eAAsB,0BAA0B,KAA2C;AACzF,QAAM,YAAY,uBAAuB,yBAAyB,GAAG,CAAC;AACtE,QAAM,SAAS,aAAc,MAAM,aAAa;AAChD,SAAO,yBAAyB,MAAM,eAAe,MAAM,CAAC;AAC9D;AAkBO,MAAM,WAAW,CACtB,KACA,UACA,eACI;AAAA,EACJ,IAAI,IAAI;AAAA,EACR,UAAU,IAAI;AAAA,EACd,gBAAgB,IAAI,kBAAkB;AAAA,EACtC,UAAU,IAAI,YAAY;AAAA,EAC1B,eAAe,IAAI,WACf,UAAU,GAAG,yBAAyB,GAAG,IAAI,QAAQ,IAAI,IAAI,QAAQ,IACrE;AAAA,EACJ,OAAO,IAAI,WAAW,UAAU,IAAI,UAAU,IAAI,EAAE,IAAI;AAAA,EACxD,aAAa,IAAI,iBAAiB,UAAU,IAAI,gBAAgB,EAAE,IAAI;AAAA,EACtE,QAAQ,IAAI,WAAW;AAAA,EACvB,YAAY,UAAU,aAAa,IAAI,eAAe;AAAA,EACtD,UAAU,UAAU,YAAY,oBAAoB,IAAI,EAAE,GAAG,YAAY;AAAA,EACzE,gBAAgB,UAAU,YAAY;AAAA,EACtC,iBAAiB,UAAU,aAAa;AAAA,EACxC,WAAW,UAAU,YAAY,SAAS,UAAU,YAAY,IAAI;AACtE;AAGO,SAAS,8BAAwC;AACtD,SAAO,kCAAkC,EAAE,IAAI,CAAC,aAAa,SAAS,EAAE;AAC1E;AASO,SAAS,yBACd,QACA,gBACA,sBACU;AACV,SAAO,kBAAkB,oBAAoB,MAAM,GAAG,YAAY;AACpE;AAQO,SAAS,0BACd,IACA,UACA,oBAC0C;AAC1C,SAAO,GAAG;AAAA,IACR;AAAA,IACA,EAAE,UAAU,mBAAmB;AAAA,IAC/B,EAAE,UAAU,SAAS,kBAAkB;AAAA,EACzC;AACF;AAQO,SAAS,kBACd,IACA,OAOiC;AACjC,QAAM,EAAE,UAAU,oBAAoB,UAAU,cAAc,cAAc,IAAI;AAChF,MAAI,iBAAiB,QAAQ,kBAAkB,MAAM;AAGnD,QAAI,SAAU,IAAG,OAAO,QAAQ;AAChC,WAAO;AAAA,EACT;AACA,MAAI,UAAU;AACZ,aAAS,WAAW;AACpB,aAAS,YAAY;AACrB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,GAAG,OAAO,0BAA0B;AAAA,IAClD;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,WAAW;AAAA,EACb,CAAC;AACD,KAAG,QAAQ,OAAO;AAClB,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ function computeNextChannels(effective, channelId, enabled) {
2
+ return enabled ? Array.from(/* @__PURE__ */ new Set([...effective, channelId])) : effective.filter((channel) => channel !== channelId);
3
+ }
4
+ function computeChannelsPatch(effective, channelId, enabled) {
5
+ const next = computeNextChannels(effective, channelId, enabled);
6
+ return next.length > 0 ? next : null;
7
+ }
8
+ export {
9
+ computeChannelsPatch,
10
+ computeNextChannels
11
+ };
12
+ //# sourceMappingURL=typeChannelSettings.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/lib/typeChannelSettings.ts"],
4
+ "sourcesContent": ["/**\n * Next eligibility set after toggling one channel in the admin type-catalogue\n * table, based on the EFFECTIVE set (tenant override ?? code ?? all registered\n * channels) \u2014 the stored array replaces the code-declared set wholesale.\n */\nexport function computeNextChannels(\n effective: string[],\n channelId: string,\n enabled: boolean,\n): string[] {\n return enabled\n ? Array.from(new Set([...effective, channelId]))\n : effective.filter((channel) => channel !== channelId)\n}\n\n/**\n * The `channels` value to PATCH after one toggle: the next eligibility set, or `null` when the\n * toggle empties it (unchecking the last channel). Persisting `[]` would black-hole the type\n * (invisible + undelivered) and is rejected by the API \u2014 `null` clears the override so the\n * code-declared default reapplies.\n */\nexport function computeChannelsPatch(\n effective: string[],\n channelId: string,\n enabled: boolean,\n): string[] | null {\n const next = computeNextChannels(effective, channelId, enabled)\n return next.length > 0 ? next : null\n}\n"],
5
+ "mappings": "AAKO,SAAS,oBACd,WACA,WACA,SACU;AACV,SAAO,UACH,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,WAAW,SAAS,CAAC,CAAC,IAC7C,UAAU,OAAO,CAAC,YAAY,YAAY,SAAS;AACzD;AAQO,SAAS,qBACd,WACA,WACA,SACiB;AACjB,QAAM,OAAO,oBAAoB,WAAW,WAAW,OAAO;AAC9D,SAAO,KAAK,SAAS,IAAI,OAAO;AAClC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,19 @@
1
+ async function getNotificationTypeOverrides(em, tenantId, typeIds) {
2
+ const result = /* @__PURE__ */ new Map();
3
+ const uniqueIds = Array.from(new Set(typeIds)).filter((id) => id.length > 0);
4
+ if (!uniqueIds.length || !tenantId) return result;
5
+ const db = em.getKysely();
6
+ const rows = await db.selectFrom("notification_type_overrides").select(["notification_type_id", "channels", "non_opt_out"]).where("tenant_id", "=", tenantId).where("notification_type_id", "in", uniqueIds).execute();
7
+ for (const row of rows) {
8
+ const channels = Array.isArray(row.channels) ? row.channels : null;
9
+ const nonOptOut = typeof row.non_opt_out === "boolean" ? row.non_opt_out : null;
10
+ if (channels !== null || nonOptOut !== null) {
11
+ result.set(row.notification_type_id, { channels, nonOptOut });
12
+ }
13
+ }
14
+ return result;
15
+ }
16
+ export {
17
+ getNotificationTypeOverrides
18
+ };
19
+ //# sourceMappingURL=typeOverrides.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/lib/typeOverrides.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Kysely } from 'kysely'\n\n/**\n * Tenant-scoped operator overrides of a notification type's delivery contract,\n * read from `notification_type_overrides`. Types without a row (or whose row\n * stores neither field) are absent from the map \u2014 the code declarations apply.\n * Used by the create-time gate, the deliver-subscriber recompute, and the\n * preference write path so `shouldDeliver` applies the same effective\n * eligibility + opt-out governance the types API reports.\n */\nexport type NotificationTypeOverrides = {\n /** Stored eligibility override; `null` \u21D2 inherit the code-declared `type.channels`. */\n channels: string[] | null\n /** Stored `nonOptOut` override; `null` \u21D2 inherit the code-declared flag. */\n nonOptOut: boolean | null\n}\n\nexport async function getNotificationTypeOverrides(\n em: EntityManager,\n tenantId: string,\n typeIds: string[],\n): Promise<Map<string, NotificationTypeOverrides>> {\n const result = new Map<string, NotificationTypeOverrides>()\n const uniqueIds = Array.from(new Set(typeIds)).filter((id) => id.length > 0)\n if (!uniqueIds.length || !tenantId) return result\n const db = em.getKysely<any>() as Kysely<any>\n const rows = (await db\n .selectFrom('notification_type_overrides')\n .select(['notification_type_id', 'channels', 'non_opt_out'])\n .where('tenant_id', '=', tenantId)\n .where('notification_type_id', 'in', uniqueIds)\n .execute()) as Array<{\n notification_type_id: string\n channels: string[] | null\n non_opt_out: boolean | null\n }>\n for (const row of rows) {\n const channels = Array.isArray(row.channels) ? row.channels : null\n const nonOptOut = typeof row.non_opt_out === 'boolean' ? row.non_opt_out : null\n if (channels !== null || nonOptOut !== null) {\n result.set(row.notification_type_id, { channels, nonOptOut })\n }\n }\n return result\n}\n"],
5
+ "mappings": "AAkBA,eAAsB,6BACpB,IACA,UACA,SACiD;AACjD,QAAM,SAAS,oBAAI,IAAuC;AAC1D,QAAM,YAAY,MAAM,KAAK,IAAI,IAAI,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;AAC3E,MAAI,CAAC,UAAU,UAAU,CAAC,SAAU,QAAO;AAC3C,QAAM,KAAK,GAAG,UAAe;AAC7B,QAAM,OAAQ,MAAM,GACjB,WAAW,6BAA6B,EACxC,OAAO,CAAC,wBAAwB,YAAY,aAAa,CAAC,EAC1D,MAAM,aAAa,KAAK,QAAQ,EAChC,MAAM,wBAAwB,MAAM,SAAS,EAC7C,QAAQ;AAKX,aAAW,OAAO,MAAM;AACtB,UAAM,WAAW,MAAM,QAAQ,IAAI,QAAQ,IAAI,IAAI,WAAW;AAC9D,UAAM,YAAY,OAAO,IAAI,gBAAgB,YAAY,IAAI,cAAc;AAC3E,QAAI,aAAa,QAAQ,cAAc,MAAM;AAC3C,aAAO,IAAI,IAAI,sBAAsB,EAAE,UAAU,UAAU,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,14 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260625122947_notifications extends Migration {
3
+ up() {
4
+ this.addSql(`create table "notification_preferences" ("id" uuid not null default gen_random_uuid(), "tenant_id" uuid not null, "user_id" uuid not null, "notification_type_id" text not null, "channel" text not null, "enabled" boolean not null default true, "created_at" timestamptz not null, "updated_at" timestamptz not null, primary key ("id"));`);
5
+ this.addSql(`create unique index "notification_preferences_unique" on "notification_preferences" ("tenant_id", "user_id", "notification_type_id", "channel");`);
6
+ this.addSql(`create index "notification_preferences_tenant_user_idx" on "notification_preferences" ("tenant_id", "user_id");`);
7
+ this.addSql(`create table "notification_types" ("id" text not null, "tenant_id" uuid null, "label_key" text not null, "description_key" text null, "created_at" timestamptz not null, "updated_at" timestamptz not null, primary key ("id"));`);
8
+ this.addSql(`create index "notification_types_tenant_idx" on "notification_types" ("tenant_id");`);
9
+ }
10
+ }
11
+ export {
12
+ Migration20260625122947_notifications
13
+ };
14
+ //# sourceMappingURL=Migration20260625122947_notifications.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/migrations/Migration20260625122947_notifications.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260625122947_notifications extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`create table \"notification_preferences\" (\"id\" uuid not null default gen_random_uuid(), \"tenant_id\" uuid not null, \"user_id\" uuid not null, \"notification_type_id\" text not null, \"channel\" text not null, \"enabled\" boolean not null default true, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, primary key (\"id\"));`);\n this.addSql(`create unique index \"notification_preferences_unique\" on \"notification_preferences\" (\"tenant_id\", \"user_id\", \"notification_type_id\", \"channel\");`);\n this.addSql(`create index \"notification_preferences_tenant_user_idx\" on \"notification_preferences\" (\"tenant_id\", \"user_id\");`);\n\n this.addSql(`create table \"notification_types\" (\"id\" text not null, \"tenant_id\" uuid null, \"label_key\" text not null, \"description_key\" text null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, primary key (\"id\"));`);\n this.addSql(`create index \"notification_types_tenant_idx\" on \"notification_types\" (\"tenant_id\");`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,8CAA8C,UAAU;AAAA,EAE1D,KAA2B;AAClC,SAAK,OAAO,+UAA+U;AAC3V,SAAK,OAAO,kJAAkJ;AAC9J,SAAK,OAAO,iHAAiH;AAE7H,SAAK,OAAO,kOAAkO;AAC9O,SAAK,OAAO,qFAAqF;AAAA,EACnG;AAEF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,15 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260626120500_notifications_push_payload extends Migration {
3
+ up() {
4
+ this.addSql(`alter table "notifications" add column "data" jsonb null;`);
5
+ this.addSql(`alter table "notifications" add column "push_options" jsonb null;`);
6
+ }
7
+ down() {
8
+ this.addSql(`alter table "notifications" drop column "push_options";`);
9
+ this.addSql(`alter table "notifications" drop column "data";`);
10
+ }
11
+ }
12
+ export {
13
+ Migration20260626120500_notifications_push_payload
14
+ };
15
+ //# sourceMappingURL=Migration20260626120500_notifications_push_payload.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260626120500_notifications_push_payload extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`alter table \"notifications\" add column \"data\" jsonb null;`);\n this.addSql(`alter table \"notifications\" add column \"push_options\" jsonb null;`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`alter table \"notifications\" drop column \"push_options\";`);\n this.addSql(`alter table \"notifications\" drop column \"data\";`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,2DAA2D,UAAU;AAAA,EAEvE,KAA2B;AAClC,SAAK,OAAO,2DAA2D;AACvE,SAAK,OAAO,mEAAmE;AAAA,EACjF;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,yDAAyD;AACrE,SAAK,OAAO,iDAAiD;AAAA,EAC/D;AAEF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260626130000_notifications_non_opt_out extends Migration {
3
+ up() {
4
+ this.addSql(`alter table "notification_types" add column "non_opt_out" boolean not null default false;`);
5
+ }
6
+ down() {
7
+ this.addSql(`alter table "notification_types" drop column "non_opt_out";`);
8
+ }
9
+ }
10
+ export {
11
+ Migration20260626130000_notifications_non_opt_out
12
+ };
13
+ //# sourceMappingURL=Migration20260626130000_notifications_non_opt_out.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260626130000_notifications_non_opt_out extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`alter table \"notification_types\" add column \"non_opt_out\" boolean not null default false;`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`alter table \"notification_types\" drop column \"non_opt_out\";`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,0DAA0D,UAAU;AAAA,EAEtE,KAA2B;AAClC,SAAK,OAAO,2FAA2F;AAAA,EACzG;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,6DAA6D;AAAA,EAC3E;AAEF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,15 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260626140000_notifications_type_category_silent extends Migration {
3
+ up() {
4
+ this.addSql(`alter table "notification_types" add column "category" text null;`);
5
+ this.addSql(`alter table "notification_types" add column "silent" boolean not null default false;`);
6
+ }
7
+ down() {
8
+ this.addSql(`alter table "notification_types" drop column "silent";`);
9
+ this.addSql(`alter table "notification_types" drop column "category";`);
10
+ }
11
+ }
12
+ export {
13
+ Migration20260626140000_notifications_type_category_silent
14
+ };
15
+ //# sourceMappingURL=Migration20260626140000_notifications_type_category_silent.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260626140000_notifications_type_category_silent extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`alter table \"notification_types\" add column \"category\" text null;`);\n this.addSql(`alter table \"notification_types\" add column \"silent\" boolean not null default false;`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`alter table \"notification_types\" drop column \"silent\";`);\n this.addSql(`alter table \"notification_types\" drop column \"category\";`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,mEAAmE,UAAU;AAAA,EAE/E,KAA2B;AAClC,SAAK,OAAO,mEAAmE;AAC/E,SAAK,OAAO,sFAAsF;AAAA,EACpG;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,wDAAwD;AACpE,SAAK,OAAO,0DAA0D;AAAA,EACxE;AAEF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260706120000_notifications_channels extends Migration {
3
+ up() {
4
+ this.addSql(`alter table "notifications" add column "channels" jsonb null;`);
5
+ }
6
+ down() {
7
+ this.addSql(`alter table "notifications" drop column "channels";`);
8
+ }
9
+ }
10
+ export {
11
+ Migration20260706120000_notifications_channels
12
+ };
13
+ //# sourceMappingURL=Migration20260706120000_notifications_channels.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/migrations/Migration20260706120000_notifications_channels.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260706120000_notifications_channels extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`alter table \"notifications\" add column \"channels\" jsonb null;`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`alter table \"notifications\" drop column \"channels\";`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,uDAAuD,UAAU;AAAA,EAEnE,KAA2B;AAClC,SAAK,OAAO,+DAA+D;AAAA,EAC7E;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,qDAAqD;AAAA,EACnE;AAEF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,14 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260716123121_notifications extends Migration {
3
+ up() {
4
+ this.addSql(`create table "notification_type_overrides" ("id" uuid not null default gen_random_uuid(), "tenant_id" uuid not null, "notification_type_id" text not null, "channels" jsonb null, "non_opt_out" boolean null, "created_at" timestamptz not null, "updated_at" timestamptz not null, primary key ("id"));`);
5
+ this.addSql(`create unique index "notification_type_overrides_unique" on "notification_type_overrides" ("tenant_id", "notification_type_id");`);
6
+ }
7
+ down() {
8
+ this.addSql(`drop table if exists "notification_type_overrides" cascade;`);
9
+ }
10
+ }
11
+ export {
12
+ Migration20260716123121_notifications
13
+ };
14
+ //# sourceMappingURL=Migration20260716123121_notifications.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/migrations/Migration20260716123121_notifications.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260716123121_notifications extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`create table \"notification_type_overrides\" (\"id\" uuid not null default gen_random_uuid(), \"tenant_id\" uuid not null, \"notification_type_id\" text not null, \"channels\" jsonb null, \"non_opt_out\" boolean null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, primary key (\"id\"));`);\n this.addSql(`create unique index \"notification_type_overrides_unique\" on \"notification_type_overrides\" (\"tenant_id\", \"notification_type_id\");`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`drop table if exists \"notification_type_overrides\" cascade;`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,8CAA8C,UAAU;AAAA,EAE1D,KAA2B;AAClC,SAAK,OAAO,0SAA0S;AACtT,SAAK,OAAO,kIAAkI;AAAA,EAChJ;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,6DAA6D;AAAA,EAC3E;AAEF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,26 @@
1
+ const notificationChannels = [
2
+ {
3
+ id: "in_app",
4
+ labelKey: "notifications.preferences.channels.inApp",
5
+ descriptionKey: "notifications.preferences.channels.inAppHint",
6
+ order: 10
7
+ },
8
+ {
9
+ id: "email",
10
+ labelKey: "notifications.preferences.channels.email",
11
+ descriptionKey: "notifications.preferences.channels.emailHint",
12
+ order: 20
13
+ },
14
+ {
15
+ id: "push",
16
+ labelKey: "notifications.preferences.channels.push",
17
+ descriptionKey: "notifications.preferences.channels.pushHint",
18
+ order: 30
19
+ }
20
+ ];
21
+ var notification_channels_default = notificationChannels;
22
+ export {
23
+ notification_channels_default as default,
24
+ notificationChannels
25
+ };
26
+ //# sourceMappingURL=notification-channels.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/notifications/notification-channels.ts"],
4
+ "sourcesContent": ["import type { NotificationChannelDefinition } from '@open-mercato/shared/modules/notifications/types'\n\n/**\n * Core-owned delivery channels. `notifications` is the channel-metadata authority (labels shown in the\n * preferences UI + the `/api/notifications/channels` catalogue); the delivery *behavior* for each lives\n * in its own strategy (`in_app`/`email` here, `push` in `push_notifications`). Third-party modules add\n * further channels by shipping their own generator-discovered `notification-channels.ts`.\n */\nexport const notificationChannels: NotificationChannelDefinition[] = [\n {\n id: 'in_app',\n labelKey: 'notifications.preferences.channels.inApp',\n descriptionKey: 'notifications.preferences.channels.inAppHint',\n order: 10,\n },\n {\n id: 'email',\n labelKey: 'notifications.preferences.channels.email',\n descriptionKey: 'notifications.preferences.channels.emailHint',\n order: 20,\n },\n {\n id: 'push',\n labelKey: 'notifications.preferences.channels.push',\n descriptionKey: 'notifications.preferences.channels.pushHint',\n order: 30,\n },\n]\n\nexport default notificationChannels\n"],
5
+ "mappings": "AAQO,MAAM,uBAAwD;AAAA,EACnE;AAAA,IACE,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,OAAO;AAAA,EACT;AACF;AAEA,IAAO,gCAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { inAppDeliveryStrategy } from "./lib/strategies/in-app-delivery-strategy.js";
2
+ import { emailDeliveryStrategy } from "./lib/strategies/email-delivery-strategy.js";
3
+ const deliveryStrategies = [
4
+ inAppDeliveryStrategy,
5
+ emailDeliveryStrategy
6
+ ];
7
+ var notifications_delivery_strategies_default = deliveryStrategies;
8
+ export {
9
+ notifications_delivery_strategies_default as default,
10
+ deliveryStrategies
11
+ };
12
+ //# sourceMappingURL=notifications.delivery-strategies.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/notifications/notifications.delivery-strategies.ts"],
4
+ "sourcesContent": ["import type { NotificationDeliveryStrategy } from './lib/deliveryStrategies'\nimport { inAppDeliveryStrategy } from './lib/strategies/in-app-delivery-strategy'\nimport { emailDeliveryStrategy } from './lib/strategies/email-delivery-strategy'\n\n/**\n * Core-owned delivery strategies, discovered by the notifications `delivery-strategies` generator\n * plugin (see `generators.ts`) and registered at bootstrap. `push` is contributed separately by the\n * `push_notifications` module through its own `notifications.delivery-strategies.ts`.\n */\nexport const deliveryStrategies: NotificationDeliveryStrategy[] = [\n inAppDeliveryStrategy,\n emailDeliveryStrategy,\n]\n\nexport default deliveryStrategies\n"],
5
+ "mappings": "AACA,SAAS,6BAA6B;AACtC,SAAS,6BAA6B;AAO/B,MAAM,qBAAqD;AAAA,EAChE;AAAA,EACA;AACF;AAEA,IAAO,4CAAQ;",
6
+ "names": []
7
+ }
@@ -1,8 +1,23 @@
1
+ import { createLogger } from "@open-mercato/shared/lib/logger";
2
+ import { TYPE_REGISTRY_SYNC_EVENT } from "./events.js";
3
+ const logger = createLogger("notifications");
1
4
  const setup = {
2
5
  defaultRoleFeatures: {
3
6
  superadmin: ["notifications.*"],
4
7
  admin: ["notifications.*"],
5
- employee: ["notifications.view"]
8
+ employee: ["notifications.view", "notifications.manage_preferences"]
9
+ },
10
+ async seedDefaults({ container, tenantId, organizationId }) {
11
+ try {
12
+ const eventBus = container.resolve("eventBus");
13
+ await eventBus.emit(
14
+ TYPE_REGISTRY_SYNC_EVENT,
15
+ { tenantId, organizationId },
16
+ { tenantId, organizationId, persistent: true }
17
+ );
18
+ } catch (err) {
19
+ logger.warn("type_registry.sync emit skipped during seedDefaults", { err });
20
+ }
6
21
  }
7
22
  };
8
23
  var setup_default = setup;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/notifications/setup.ts"],
4
- "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n superadmin: ['notifications.*'],\n admin: ['notifications.*'],\n employee: ['notifications.view'],\n },\n}\n\nexport default setup\n"],
5
- "mappings": "AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,YAAY,CAAC,iBAAiB;AAAA,IAC9B,OAAO,CAAC,iBAAiB;AAAA,IACzB,UAAU,CAAC,oBAAoB;AAAA,EACjC;AACF;AAEA,IAAO,gBAAQ;",
4
+ "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { TYPE_REGISTRY_SYNC_EVENT } from './events'\n\nconst logger = createLogger('notifications')\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n superadmin: ['notifications.*'],\n admin: ['notifications.*'],\n employee: ['notifications.view', 'notifications.manage_preferences'],\n },\n\n async seedDefaults({ container, tenantId, organizationId }) {\n // Best-effort: nudge the type catalogue into the DB at init time. The\n // `GET /api/notifications/types` lazy reconcile is the reliable fallback, so\n // a failure here must never break tenant initialization.\n try {\n const eventBus = container.resolve('eventBus') as {\n emit: (event: string, payload: unknown, options?: unknown) => Promise<void>\n }\n await eventBus.emit(\n TYPE_REGISTRY_SYNC_EVENT,\n { tenantId, organizationId },\n { tenantId, organizationId, persistent: true },\n )\n } catch (err) {\n logger.warn('type_registry.sync emit skipped during seedDefaults', { err })\n }\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,gCAAgC;AAEzC,MAAM,SAAS,aAAa,eAAe;AAEpC,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,YAAY,CAAC,iBAAiB;AAAA,IAC9B,OAAO,CAAC,iBAAiB;AAAA,IACzB,UAAU,CAAC,sBAAsB,kCAAkC;AAAA,EACrE;AAAA,EAEA,MAAM,aAAa,EAAE,WAAW,UAAU,eAAe,GAAG;AAI1D,QAAI;AACF,YAAM,WAAW,UAAU,QAAQ,UAAU;AAG7C,YAAM,SAAS;AAAA,QACb;AAAA,QACA,EAAE,UAAU,eAAe;AAAA,QAC3B,EAAE,UAAU,gBAAgB,YAAY,KAAK;AAAA,MAC/C;AAAA,IACF,SAAS,KAAK;AACZ,aAAO,KAAK,uDAAuD,EAAE,IAAI,CAAC;AAAA,IAC5E;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;",
6
6
  "names": []
7
7
  }
@@ -2,11 +2,11 @@ import { Notification } from "../data/entities.js";
2
2
  import { NOTIFICATION_EVENTS } from "../lib/events.js";
3
3
  import { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, resolveNotificationDeliveryConfig, resolveNotificationPanelUrl } from "../lib/deliveryConfig.js";
4
4
  import { getNotificationDeliveryStrategies } from "../lib/deliveryStrategies.js";
5
- import { sendEmail } from "@open-mercato/shared/lib/email/send";
6
- import NotificationEmail from "../emails/NotificationEmail.js";
7
- import { loadDictionary } from "@open-mercato/shared/lib/i18n/server";
8
- import { createFallbackTranslator } from "@open-mercato/shared/lib/i18n/translate";
9
- import { defaultLocale } from "@open-mercato/shared/lib/i18n/config";
5
+ import { resolveEffectiveChannels } from "../lib/shouldDeliver.js";
6
+ import { getNotificationType } from "../lib/notification-type-registry.js";
7
+ import { getNotificationTypeOverrides } from "../lib/typeOverrides.js";
8
+ import { resolveNotificationPreferenceService } from "../lib/notificationPreferenceService.js";
9
+ import { resolveNotificationCopy } from "../lib/notificationCopy.js";
10
10
  import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
11
11
  import { User } from "../../auth/data/entities.js";
12
12
  import { createLogger } from "@open-mercato/shared/lib/logger";
@@ -28,13 +28,6 @@ const buildPanelLink = (panelUrl, notificationId) => {
28
28
  const separator = panelUrl.includes("?") ? "&" : "?";
29
29
  return `${panelUrl}${separator}notificationId=${encodeURIComponent(notificationId)}`;
30
30
  };
31
- const resolveNotificationCopy = async (notification) => {
32
- const dict = await loadDictionary(defaultLocale);
33
- const t = createFallbackTranslator(dict);
34
- const title = notification.titleKey ? t(notification.titleKey, notification.title ?? notification.titleKey, notification.titleVariables ?? void 0) : notification.title;
35
- const body = notification.bodyKey ? t(notification.bodyKey, notification.body ?? notification.bodyKey ?? "", notification.bodyVariables ?? void 0) : notification.body ?? null;
36
- return { title, body, t };
37
- };
38
31
  const resolveRecipient = async (em, notification, encryptionService) => {
39
32
  const where = {
40
33
  id: notification.recipientUserId,
@@ -64,9 +57,6 @@ const resolveRecipient = async (em, notification, encryptionService) => {
64
57
  async function handle(payload, ctx) {
65
58
  debug("deliver notification event", payload);
66
59
  const deliveryConfig = await resolveNotificationDeliveryConfig(ctx, { defaultValue: DEFAULT_NOTIFICATION_DELIVERY_CONFIG });
67
- if (!deliveryConfig.strategies.email.enabled) {
68
- debug("email delivery disabled");
69
- }
70
60
  const em = ctx.resolve("em");
71
61
  const notification = await findOneWithDecryption(
72
62
  em,
@@ -118,59 +108,78 @@ async function handle(payload, ctx) {
118
108
  href: fullHref
119
109
  };
120
110
  }).filter((action) => action !== null);
121
- if (deliveryConfig.strategies.email.enabled && recipient?.email && panelLink) {
122
- const subjectPrefix = deliveryConfig.strategies.email.subjectPrefix?.trim();
123
- const subject = subjectPrefix ? `${subjectPrefix} ${title}` : title;
124
- const copy = {
125
- preview: t("notifications.delivery.email.preview", "New notification"),
126
- heading: t("notifications.delivery.email.heading", "You have a new notification"),
127
- bodyIntro: t("notifications.delivery.email.bodyIntro", "Review the notification details and take any required actions."),
128
- actionNotice: t("notifications.delivery.email.actionNotice", "Actions are available in Open Mercato and are read-only in this email."),
129
- openCta: t("notifications.delivery.email.openCta", "Open notification center"),
130
- footer: t("notifications.delivery.email.footer", "Open Mercato notifications")
131
- };
111
+ const strategyConfigs = deliveryConfig.strategies.custom ?? {};
112
+ const strategies = getNotificationDeliveryStrategies();
113
+ const persistedChannels = notification.channels;
114
+ const registeredStrategyIds = strategies.map((strategy) => strategy.id);
115
+ let targetChannels = persistedChannels ?? null;
116
+ if (persistedChannels == null && registeredStrategyIds.length > 0) {
132
117
  try {
133
- debug("sending email", { recipientUserId: notification.recipientUserId });
134
- await sendEmail({
135
- to: recipient.email,
136
- subject,
137
- from: deliveryConfig.strategies.email.from,
138
- replyTo: deliveryConfig.strategies.email.replyTo,
139
- react: NotificationEmail({
140
- title,
141
- body,
142
- actions: actionLinks,
143
- panelUrl: panelLink,
144
- copy
145
- })
118
+ const typeOverrides = (await getNotificationTypeOverrides(em, notification.tenantId, [notification.type])).get(notification.type);
119
+ targetChannels = await resolveEffectiveChannels({
120
+ typeId: notification.type,
121
+ type: getNotificationType(notification.type),
122
+ scope: { tenantId: notification.tenantId, userId: notification.recipientUserId },
123
+ targetChannels: null,
124
+ registeredChannels: registeredStrategyIds,
125
+ preferences: resolveNotificationPreferenceService({ resolve: ctx.resolve }),
126
+ channelsOverride: typeOverrides?.channels ?? null,
127
+ nonOptOutOverride: typeOverrides?.nonOptOut ?? null
146
128
  });
147
- } catch (error) {
148
- logger.error("Email delivery failed", { err: error });
129
+ } catch (err) {
130
+ logger.error("failed to recompute delivery channels; retrying via persistent subscriber", {
131
+ notificationId: notification.id,
132
+ type: notification.type,
133
+ err
134
+ });
135
+ throw err;
136
+ }
137
+ }
138
+ if (persistedChannels == null && targetChannels != null) {
139
+ try {
140
+ await em.fork().nativeUpdate(
141
+ Notification,
142
+ { id: notification.id, tenantId: notification.tenantId },
143
+ { channels: targetChannels }
144
+ );
145
+ } catch (err) {
146
+ debug("failed to persist recomputed channels", err);
149
147
  }
150
148
  }
151
- const strategyConfigs = deliveryConfig.strategies.custom ?? {};
152
- const strategies = getNotificationDeliveryStrategies();
153
149
  for (const strategy of strategies) {
150
+ if (targetChannels && !targetChannels.includes(strategy.id)) {
151
+ debug("channel not targeted", strategy.id);
152
+ continue;
153
+ }
154
154
  const strategyConfig = strategyConfigs[strategy.id];
155
155
  const enabled = strategyConfig?.enabled ?? strategy.defaultEnabled ?? false;
156
156
  if (!enabled) {
157
- debug("custom delivery disabled", strategy.id);
157
+ debug("delivery disabled", strategy.id);
158
158
  continue;
159
159
  }
160
+ if (strategy.supports && !strategy.supports(notification)) {
161
+ debug("strategy does not support notification", strategy.id);
162
+ continue;
163
+ }
164
+ const context = {
165
+ notification,
166
+ recipient,
167
+ title,
168
+ body,
169
+ panelUrl,
170
+ panelLink,
171
+ actionLinks,
172
+ deliveryConfig,
173
+ config: strategyConfig ?? {},
174
+ resolve: ctx.resolve,
175
+ t
176
+ };
160
177
  try {
161
- await strategy.deliver({
162
- notification,
163
- recipient,
164
- title,
165
- body,
166
- panelUrl,
167
- panelLink,
168
- actionLinks,
169
- deliveryConfig,
170
- config: strategyConfig ?? {},
171
- resolve: ctx.resolve,
172
- t
173
- });
178
+ if (strategy.isConfigured && !await strategy.isConfigured(context)) {
179
+ debug("strategy not configured", strategy.id);
180
+ continue;
181
+ }
182
+ await strategy.deliver(context);
174
183
  } catch (error) {
175
184
  logger.error("Delivery strategy failed", { strategyId: strategy.id, err: error });
176
185
  }