@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,14 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260625122947_notifications extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ 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"));`);
7
+ this.addSql(`create unique index "notification_preferences_unique" on "notification_preferences" ("tenant_id", "user_id", "notification_type_id", "channel");`);
8
+ this.addSql(`create index "notification_preferences_tenant_user_idx" on "notification_preferences" ("tenant_id", "user_id");`);
9
+
10
+ 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"));`);
11
+ this.addSql(`create index "notification_types_tenant_idx" on "notification_types" ("tenant_id");`);
12
+ }
13
+
14
+ }
@@ -0,0 +1,15 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260626120500_notifications_push_payload extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ this.addSql(`alter table "notifications" add column "data" jsonb null;`);
7
+ this.addSql(`alter table "notifications" add column "push_options" jsonb null;`);
8
+ }
9
+
10
+ override down(): void | Promise<void> {
11
+ this.addSql(`alter table "notifications" drop column "push_options";`);
12
+ this.addSql(`alter table "notifications" drop column "data";`);
13
+ }
14
+
15
+ }
@@ -0,0 +1,13 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260626130000_notifications_non_opt_out extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ this.addSql(`alter table "notification_types" add column "non_opt_out" boolean not null default false;`);
7
+ }
8
+
9
+ override down(): void | Promise<void> {
10
+ this.addSql(`alter table "notification_types" drop column "non_opt_out";`);
11
+ }
12
+
13
+ }
@@ -0,0 +1,15 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260626140000_notifications_type_category_silent extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ this.addSql(`alter table "notification_types" add column "category" text null;`);
7
+ this.addSql(`alter table "notification_types" add column "silent" boolean not null default false;`);
8
+ }
9
+
10
+ override down(): void | Promise<void> {
11
+ this.addSql(`alter table "notification_types" drop column "silent";`);
12
+ this.addSql(`alter table "notification_types" drop column "category";`);
13
+ }
14
+
15
+ }
@@ -0,0 +1,13 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260706120000_notifications_channels extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ this.addSql(`alter table "notifications" add column "channels" jsonb null;`);
7
+ }
8
+
9
+ override down(): void | Promise<void> {
10
+ this.addSql(`alter table "notifications" drop column "channels";`);
11
+ }
12
+
13
+ }
@@ -0,0 +1,14 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260716123121_notifications extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ 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"));`);
7
+ this.addSql(`create unique index "notification_type_overrides_unique" on "notification_type_overrides" ("tenant_id", "notification_type_id");`);
8
+ }
9
+
10
+ override down(): void | Promise<void> {
11
+ this.addSql(`drop table if exists "notification_type_overrides" cascade;`);
12
+ }
13
+
14
+ }
@@ -0,0 +1,30 @@
1
+ import type { NotificationChannelDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+
3
+ /**
4
+ * Core-owned delivery channels. `notifications` is the channel-metadata authority (labels shown in the
5
+ * preferences UI + the `/api/notifications/channels` catalogue); the delivery *behavior* for each lives
6
+ * in its own strategy (`in_app`/`email` here, `push` in `push_notifications`). Third-party modules add
7
+ * further channels by shipping their own generator-discovered `notification-channels.ts`.
8
+ */
9
+ export const notificationChannels: NotificationChannelDefinition[] = [
10
+ {
11
+ id: 'in_app',
12
+ labelKey: 'notifications.preferences.channels.inApp',
13
+ descriptionKey: 'notifications.preferences.channels.inAppHint',
14
+ order: 10,
15
+ },
16
+ {
17
+ id: 'email',
18
+ labelKey: 'notifications.preferences.channels.email',
19
+ descriptionKey: 'notifications.preferences.channels.emailHint',
20
+ order: 20,
21
+ },
22
+ {
23
+ id: 'push',
24
+ labelKey: 'notifications.preferences.channels.push',
25
+ descriptionKey: 'notifications.preferences.channels.pushHint',
26
+ order: 30,
27
+ },
28
+ ]
29
+
30
+ export default notificationChannels
@@ -0,0 +1,15 @@
1
+ import type { NotificationDeliveryStrategy } from './lib/deliveryStrategies'
2
+ import { inAppDeliveryStrategy } from './lib/strategies/in-app-delivery-strategy'
3
+ import { emailDeliveryStrategy } from './lib/strategies/email-delivery-strategy'
4
+
5
+ /**
6
+ * Core-owned delivery strategies, discovered by the notifications `delivery-strategies` generator
7
+ * plugin (see `generators.ts`) and registered at bootstrap. `push` is contributed separately by the
8
+ * `push_notifications` module through its own `notifications.delivery-strategies.ts`.
9
+ */
10
+ export const deliveryStrategies: NotificationDeliveryStrategy[] = [
11
+ inAppDeliveryStrategy,
12
+ emailDeliveryStrategy,
13
+ ]
14
+
15
+ export default deliveryStrategies
@@ -1,10 +1,32 @@
1
1
  import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
2
+ import { createLogger } from '@open-mercato/shared/lib/logger'
3
+ import { TYPE_REGISTRY_SYNC_EVENT } from './events'
4
+
5
+ const logger = createLogger('notifications')
2
6
 
3
7
  export const setup: ModuleSetupConfig = {
4
8
  defaultRoleFeatures: {
5
9
  superadmin: ['notifications.*'],
6
10
  admin: ['notifications.*'],
7
- employee: ['notifications.view'],
11
+ employee: ['notifications.view', 'notifications.manage_preferences'],
12
+ },
13
+
14
+ async seedDefaults({ container, tenantId, organizationId }) {
15
+ // Best-effort: nudge the type catalogue into the DB at init time. The
16
+ // `GET /api/notifications/types` lazy reconcile is the reliable fallback, so
17
+ // a failure here must never break tenant initialization.
18
+ try {
19
+ const eventBus = container.resolve('eventBus') as {
20
+ emit: (event: string, payload: unknown, options?: unknown) => Promise<void>
21
+ }
22
+ await eventBus.emit(
23
+ TYPE_REGISTRY_SYNC_EVENT,
24
+ { tenantId, organizationId },
25
+ { tenantId, organizationId, persistent: true },
26
+ )
27
+ } catch (err) {
28
+ logger.warn('type_registry.sync emit skipped during seedDefaults', { err })
29
+ }
8
30
  },
9
31
  }
10
32
 
@@ -2,12 +2,12 @@ import type { EntityManager } from '@mikro-orm/postgresql'
2
2
  import { Notification } from '../data/entities'
3
3
  import { NOTIFICATION_EVENTS } from '../lib/events'
4
4
  import { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, resolveNotificationDeliveryConfig, resolveNotificationPanelUrl } from '../lib/deliveryConfig'
5
- import { getNotificationDeliveryStrategies } from '../lib/deliveryStrategies'
6
- import { sendEmail } from '@open-mercato/shared/lib/email/send'
7
- import NotificationEmail from '../emails/NotificationEmail'
8
- import { loadDictionary } from '@open-mercato/shared/lib/i18n/server'
9
- import { createFallbackTranslator } from '@open-mercato/shared/lib/i18n/translate'
10
- import { defaultLocale } from '@open-mercato/shared/lib/i18n/config'
5
+ import { getNotificationDeliveryStrategies, type NotificationDeliveryContext } from '../lib/deliveryStrategies'
6
+ import { resolveEffectiveChannels } from '../lib/shouldDeliver'
7
+ import { getNotificationType } from '../lib/notification-type-registry'
8
+ import { getNotificationTypeOverrides } from '../lib/typeOverrides'
9
+ import { resolveNotificationPreferenceService } from '../lib/notificationPreferenceService'
10
+ import { resolveNotificationCopy } from '../lib/notificationCopy'
11
11
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
12
12
  import { User } from '../../auth/data/entities'
13
13
  import type { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'
@@ -46,23 +46,6 @@ const buildPanelLink = (panelUrl: string, notificationId: string) => {
46
46
  return `${panelUrl}${separator}notificationId=${encodeURIComponent(notificationId)}`
47
47
  }
48
48
 
49
- const resolveNotificationCopy = async (
50
- notification: Notification
51
- ) => {
52
- const dict = await loadDictionary(defaultLocale)
53
- const t = createFallbackTranslator(dict)
54
-
55
- const title = notification.titleKey
56
- ? t(notification.titleKey, notification.title ?? notification.titleKey, notification.titleVariables ?? undefined)
57
- : notification.title
58
-
59
- const body = notification.bodyKey
60
- ? t(notification.bodyKey, notification.body ?? notification.bodyKey ?? '', notification.bodyVariables ?? undefined)
61
- : notification.body ?? null
62
-
63
- return { title, body, t }
64
- }
65
-
66
49
  const resolveRecipient = async (
67
50
  em: EntityManager,
68
51
  notification: Notification,
@@ -94,13 +77,18 @@ const resolveRecipient = async (
94
77
  }
95
78
  }
96
79
 
97
-
80
+ /**
81
+ * Dispatches a created notification across every registered delivery channel. This is a pure
82
+ * "resolve copy → loop strategies" loop with NO channel-specific branches: in-app, email, and push
83
+ * are all first-class strategies on the seam. Per-channel opt-out / `nonOptOut` / `silent` /
84
+ * eligibility / per-send targeting were already resolved once at create time into
85
+ * `notification.channels` (see `shouldDeliver`); here we only replay that target set and apply each
86
+ * strategy's technical short-circuits (`supports`, `isConfigured`). `notification.channels === null`
87
+ * means "all channels" (legacy rows + pre-Phase-7 behavior).
88
+ */
98
89
  export default async function handle(payload: NotificationCreatedPayload, ctx: ResolverContext) {
99
90
  debug('deliver notification event', payload)
100
91
  const deliveryConfig = await resolveNotificationDeliveryConfig(ctx, { defaultValue: DEFAULT_NOTIFICATION_DELIVERY_CONFIG })
101
- if (!deliveryConfig.strategies.email.enabled) {
102
- debug('email delivery disabled')
103
- }
104
92
 
105
93
  const em = ctx.resolve('em') as EntityManager
106
94
  const notification = await findOneWithDecryption(
@@ -159,61 +147,107 @@ export default async function handle(payload: NotificationCreatedPayload, ctx: R
159
147
  })
160
148
  .filter((action): action is NonNullable<typeof action> => action !== null)
161
149
 
162
- if (deliveryConfig.strategies.email.enabled && recipient?.email && panelLink) {
163
- const subjectPrefix = deliveryConfig.strategies.email.subjectPrefix?.trim()
164
- const subject = subjectPrefix ? `${subjectPrefix} ${title}` : title
165
- const copy = {
166
- preview: t('notifications.delivery.email.preview', 'New notification'),
167
- heading: t('notifications.delivery.email.heading', 'You have a new notification'),
168
- bodyIntro: t('notifications.delivery.email.bodyIntro', 'Review the notification details and take any required actions.'),
169
- actionNotice: t('notifications.delivery.email.actionNotice', 'Actions are available in Open Mercato and are read-only in this email.'),
170
- openCta: t('notifications.delivery.email.openCta', 'Open notification center'),
171
- footer: t('notifications.delivery.email.footer', 'Open Mercato notifications'),
172
- }
173
-
150
+ const strategyConfigs = deliveryConfig.strategies.custom ?? {}
151
+ const strategies = getNotificationDeliveryStrategies()
152
+ // Authoritative per-send target resolved at create time. A null snapshot (legacy pre-Phase-7 row,
153
+ // or a create that ran before the delivery strategies were registered) would otherwise deliver
154
+ // every channel with no gate — so recompute the effective set from current preferences here,
155
+ // keeping the single `shouldDeliver` gate instead of re-checking opt-out inside each strategy.
156
+ const persistedChannels = notification.channels
157
+ const registeredStrategyIds = strategies.map((strategy) => strategy.id)
158
+ let targetChannels: string[] | null = persistedChannels ?? null
159
+ // Only recompute for a null-snapshot row when strategies are actually registered — mirror the
160
+ // create path (`resolveChannelsFor`: `registeredChannels.length === 0 => null`). With zero
161
+ // strategies, `resolveEffectiveChannels` returns `[]` (never null); persisting that would HIDE
162
+ // the row (the visibility layer treats `[]` as "no channels"), so leave `channels` null and keep
163
+ // legacy all-channels back-compat instead.
164
+ if (persistedChannels == null && registeredStrategyIds.length > 0) {
174
165
  try {
175
- debug('sending email', { recipientUserId: notification.recipientUserId })
176
- await sendEmail({
177
- to: recipient.email,
178
- subject,
179
- from: deliveryConfig.strategies.email.from,
180
- replyTo: deliveryConfig.strategies.email.replyTo,
181
- react: NotificationEmail({
182
- title,
183
- body,
184
- actions: actionLinks,
185
- panelUrl: panelLink,
186
- copy,
187
- }),
166
+ const typeOverrides = (
167
+ await getNotificationTypeOverrides(em as EntityManager, notification.tenantId, [notification.type])
168
+ ).get(notification.type)
169
+ targetChannels = await resolveEffectiveChannels({
170
+ typeId: notification.type,
171
+ type: getNotificationType(notification.type),
172
+ scope: { tenantId: notification.tenantId, userId: notification.recipientUserId },
173
+ targetChannels: null,
174
+ registeredChannels: registeredStrategyIds,
175
+ preferences: resolveNotificationPreferenceService({ resolve: ctx.resolve }),
176
+ channelsOverride: typeOverrides?.channels ?? null,
177
+ nonOptOutOverride: typeOverrides?.nonOptOut ?? null,
188
178
  })
189
- } catch (error) {
190
- logger.error('Email delivery failed', { err: error })
179
+ } catch (err) {
180
+ // Fail CLOSED, not open. Falling back to `null` here means "deliver EVERY registered channel"
181
+ // — which now includes push and email — for a type the user may have opted out of, so a
182
+ // transient overrides/preference-service blip would leak a push/email on opt-out. Re-throw
183
+ // instead: this subscriber is `persistent` (retried with backoff) and the recompute runs
184
+ // BEFORE any strategy delivers, so the retry recomputes the correct target set with no
185
+ // double-send. In-app visibility is unaffected meanwhile — the row's `channels` stays null,
186
+ // which the visibility layer already treats as "visible everywhere".
187
+ logger.error('failed to recompute delivery channels; retrying via persistent subscriber', {
188
+ notificationId: notification.id,
189
+ type: notification.type,
190
+ err,
191
+ })
192
+ throw err
193
+ }
194
+ }
195
+ // Persist the recomputed set back onto a null-channels row so the in-app
196
+ // VISIBILITY path (bell/inbox/unread — see notificationVisibility.ts) reads
197
+ // the same authoritative target the DELIVERY gate just applied. Without this
198
+ // the row stays `null` ⇒ "visible everywhere", so a notification suppressed
199
+ // from in_app by the user's opt-out would still surface in the bell while
200
+ // delivery correctly skipped it. `targetChannels` is null (skip persist) only
201
+ // when no strategies are registered or the recompute fell back — both keep the
202
+ // row null for legacy all-channels back-compat. `channels` is a plaintext JSONB
203
+ // column, so a forked nativeUpdate avoids the shared UoW.
204
+ if (persistedChannels == null && targetChannels != null) {
205
+ try {
206
+ await (em as EntityManager)
207
+ .fork()
208
+ .nativeUpdate(
209
+ Notification,
210
+ { id: notification.id, tenantId: notification.tenantId },
211
+ { channels: targetChannels },
212
+ )
213
+ } catch (err) {
214
+ debug('failed to persist recomputed channels', err)
191
215
  }
192
216
  }
193
-
194
- const strategyConfigs = deliveryConfig.strategies.custom ?? {}
195
- const strategies = getNotificationDeliveryStrategies()
196
217
  for (const strategy of strategies) {
218
+ if (targetChannels && !targetChannels.includes(strategy.id)) {
219
+ debug('channel not targeted', strategy.id)
220
+ continue
221
+ }
197
222
  const strategyConfig = strategyConfigs[strategy.id]
198
223
  const enabled = strategyConfig?.enabled ?? strategy.defaultEnabled ?? false
199
224
  if (!enabled) {
200
- debug('custom delivery disabled', strategy.id)
225
+ debug('delivery disabled', strategy.id)
226
+ continue
227
+ }
228
+ if (strategy.supports && !strategy.supports(notification)) {
229
+ debug('strategy does not support notification', strategy.id)
201
230
  continue
202
231
  }
232
+ const context: NotificationDeliveryContext = {
233
+ notification,
234
+ recipient,
235
+ title,
236
+ body,
237
+ panelUrl,
238
+ panelLink,
239
+ actionLinks,
240
+ deliveryConfig,
241
+ config: strategyConfig ?? {},
242
+ resolve: ctx.resolve,
243
+ t,
244
+ }
203
245
  try {
204
- await strategy.deliver({
205
- notification,
206
- recipient,
207
- title,
208
- body,
209
- panelUrl,
210
- panelLink,
211
- actionLinks,
212
- deliveryConfig,
213
- config: strategyConfig ?? {},
214
- resolve: ctx.resolve,
215
- t,
216
- })
246
+ if (strategy.isConfigured && !(await strategy.isConfigured(context))) {
247
+ debug('strategy not configured', strategy.id)
248
+ continue
249
+ }
250
+ await strategy.deliver(context)
217
251
  } catch (error) {
218
252
  logger.error('Delivery strategy failed', { strategyId: strategy.id, err: error })
219
253
  }
@@ -0,0 +1,18 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { TYPE_REGISTRY_SYNC_EVENT } from '../events'
3
+ import { syncNotificationTypes } from '../lib/notification-type-registry'
4
+
5
+ export const metadata = {
6
+ event: TYPE_REGISTRY_SYNC_EVENT,
7
+ persistent: true,
8
+ id: 'notifications:sync-notification-types',
9
+ }
10
+
11
+ type ResolverContext = {
12
+ resolve: <T = unknown>(name: string) => T
13
+ }
14
+
15
+ export default async function handler(_payload: unknown, ctx: ResolverContext): Promise<void> {
16
+ const em = ctx.resolve<EntityManager>('em')
17
+ await syncNotificationTypes(em, { force: true })
18
+ }
@@ -0,0 +1,35 @@
1
+ import { InjectionPosition } from '@open-mercato/shared/modules/widgets/injection-position'
2
+ import type { InjectionMenuItemWidget } from '@open-mercato/shared/modules/widgets/injection'
3
+
4
+ /**
5
+ * Adds a "Notification Preferences" entry to the top-right profile dropdown,
6
+ * next to "My communication channels". The per-user preferences page
7
+ * (`/backend/profile/notification-preferences`) is `pageContext: 'profile'`, so
8
+ * it is excluded from the main sidebar and otherwise only reachable via the
9
+ * profile-mode sidebar or a direct URL — this gives it a discoverable entry.
10
+ * Mirrors `communication_channels.injection.profile-channels-menu`.
11
+ *
12
+ * Feature-gated on `notifications.manage_preferences` (the same guard as the
13
+ * page), so users who cannot edit preferences don't see a dead link. Wildcard
14
+ * grants (`notifications.*`, `*`) are honored by the menu filter's
15
+ * wildcard-aware matcher.
16
+ */
17
+ const widget: InjectionMenuItemWidget = {
18
+ metadata: {
19
+ id: 'notifications.injection.profile-preferences-menu',
20
+ title: 'Notification preferences profile menu item',
21
+ },
22
+ menuItems: [
23
+ {
24
+ id: 'notification-preferences-profile-link',
25
+ labelKey: 'notifications.preferences.pageTitle',
26
+ label: 'Notification Preferences',
27
+ icon: 'Bell',
28
+ href: '/backend/profile/notification-preferences',
29
+ features: ['notifications.manage_preferences'],
30
+ placement: { position: InjectionPosition.After, relativeTo: 'change-password' },
31
+ },
32
+ ],
33
+ }
34
+
35
+ export default widget
@@ -0,0 +1,18 @@
1
+ import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'
2
+
3
+ /**
4
+ * Profile dropdown (top-right avatar): a discoverable entry to the per-user
5
+ * notification-preferences page, placed directly under "Change Password" —
6
+ * the same pattern `communication_channels` uses for its per-user
7
+ * channel-connect page.
8
+ */
9
+ export const injectionTable: ModuleInjectionTable = {
10
+ 'menu:topbar:profile-dropdown': [
11
+ {
12
+ widgetId: 'notifications.injection.profile-preferences-menu',
13
+ priority: 90,
14
+ },
15
+ ],
16
+ }
17
+
18
+ export default injectionTable
@@ -0,0 +1,92 @@
1
+ # push_notifications Module — Agent Guidelines
2
+
3
+ Push delivery **rails**. Owns the delivery log, the `push` notification delivery strategy, and the
4
+ `send-push` worker. It does **not** own device tokens (that's `devices`), per-user opt-out (that's
5
+ `notifications`), or provider credentials/transport (that's the `communication_channels` hub +
6
+ FCM/APNs/Expo channel packages). Spec: `.ai/specs/2026-04-28-push-notifications-and-devices.md` (Module 3).
7
+
8
+ ## Architecture
9
+
10
+ - **Strategy** (`lib/push-delivery-strategy.ts`) registers via the notifications `delivery-strategies`
11
+ generator plugin (export `deliveryStrategies` from `notifications.delivery-strategies.ts`). It runs
12
+ inside the persistent `notifications:deliver` subscriber and only enqueues — the actual send happens
13
+ in the worker so a slow provider never blocks notification creation.
14
+ - **Worker** (`workers/send-push.worker.ts` → `lib/push-delivery.ts`) atomically claims the row
15
+ (`pending` → `sending`, so a redelivered at-least-once job is processed once), resolves the tenant push
16
+ `CommunicationChannel` + hub adapter (`channelAdapterRegistry`) + creds (`integrationCredentialsService`)
17
+ and calls `convertOutbound` → `sendMessage` — the `communication_channels` `test-send` flow. Retries
18
+ transient failures with exponential backoff + jitter (3 attempts, shared
19
+ `@open-mercato/shared/lib/delivery/retry`), records `next_retry_at`, and marks the row `expired` once
20
+ retries are exhausted (vs `failed` for terminal errors); on the `unregistered` sentinel soft-deletes the device.
21
+ - **Queue** (`lib/queue.ts`) mirrors the webhooks queue: `createModuleQueue` + `enqueuePushDelivery` +
22
+ a local-worker bootstrap for dev/test (`QUEUE_STRATEGY !== 'async'`).
23
+ - **Reaper** (`lib/push-reaper.ts` → `workers/reclaim-stuck.worker.ts`) recovers rows stranded in
24
+ `sending` by a crashed worker — the send-path claim only matches `pending`, so such a row has no
25
+ outstanding job and would never terminate. A per-tenant `@open-mercato/scheduler` interval entry
26
+ (registered best-effort in `setup.ts`, mirroring the `communication_channels` poll-tick) fires the
27
+ tick; rows still in `sending` past `OM_PUSH_STUCK_RECLAIM_MINUTES` (default 5) are re-opened +
28
+ re-enqueued when attempts remain, else finalized `expired`. Each transition is an atomic
29
+ `nativeUpdate` guarded on `status='sending'` + still-stale `updated_at`, so overlapping ticks or a
30
+ worker that re-claimed the row never re-open an active delivery. The per-tick scan is batch-bounded
31
+ by `OM_PUSH_STUCK_RECLAIM_BATCH_LIMIT` (default 500, oldest-stuck first) so a stranded backlog from a
32
+ provider/queue outage cannot load an unbounded row set into memory in one tick — the remainder drains
33
+ on subsequent ticks (mirrors the receipt reaper's `OM_PUSH_RECEIPT_BATCH_LIMIT`).
34
+ - **Fan-out** (`lib/push-fanout.ts`, `fanOutPushDeliveries`) is the shared device-resolution + provider
35
+ routing + delivery-row insert + enqueue. The strategy (visible notifications) and `sendCustomPush`
36
+ call it; it stays preference-agnostic. Its channel/device short-circuits (no push channel / no
37
+ devices / no provider match → `{ enqueued: 0 }`) are push's technical `isConfigured` equivalent and
38
+ remain the authoritative "is push set up for this tenant/recipient" check.
39
+ - **Opt-out is enforced upstream, once (Phase 7).** The `push` strategy no longer calls
40
+ `isChannelEnabled`/checks `nonOptOut`; the notifications create-time gate (`shouldDeliver`) already
41
+ resolved per-channel opt-out into `notification.channels`, and the dispatcher only invokes this
42
+ strategy when `push ∈ channels`. The strategy still reads the **type** for `silent` (delivery style).
43
+ - **Silent push** is **not** a separate API — it is just a notification whose **type** is declared
44
+ `silent: true` (`NotificationTypeDefinition.silent`), created through the **normal**
45
+ `notificationService.create()` flow (`create()` → `notifications:deliver` subscriber → the `push`
46
+ strategy). The strategy derives `silent` from the type, sends a content-available (data-only) push,
47
+ and skips user-facing copy; the in-app `Notification` row is still created and per-channel
48
+ preferences still apply (now enforced by the notifications create-time gate, not the strategy) — to
49
+ make a silent type always fire, declare it `nonOptOut: true`. There is no `sendSilentPush` helper.
50
+ - **Admin custom push** (`lib/send-custom-push.ts`, exposed in DI as `pushNotificationService`) is a
51
+ one-off **visible** push with literal title/body that fans out directly (no in-app row, no email, no
52
+ preference check); it backs `api/custom-send/route.ts`.
53
+ - **Flexible payload.** A notification's optional `data` (arbitrary app-readable map, also exposed to
54
+ in-app clients) and `pushOptions` (flat `sound`/`badge`/`image`/`priority`/`channelId`/`body` map, both
55
+ from the `notifications` module) ride the push envelope `raw`. The adapters map `pushOptions` onto each
56
+ provider's native message and branch on `silent`; see `communication_channels/lib/push-envelope.ts`
57
+ (`PushOptions`, `readPushEnvelope`, `resolvePushBody`).
58
+
59
+ ## Always
60
+
61
+ - Never export `OM_PUSH_FAKE_PROVIDERS` by hand and point `TC-PUSH-004+` / `TC-CHANNEL-PUSH-005..007` at a
62
+ live server that does not itself have it. Their `.meta.ts` gate skips them when the flag is absent from the
63
+ test process, which is the protection you want; exporting it defeats that gate. The fake swaps the provider
64
+ SDK client in `di.ts` `register()`, so whichever process claims the delivery job must have the flag — a dev
65
+ server without it runs its in-process worker against the **real** provider, and on real credentials that
66
+ means a real push to the recipient's real devices. Start the server with `OM_PUSH_FAKE_PROVIDERS=1`, or use
67
+ the ephemeral harness, which sets it for both the app and the drain child.
68
+ - Resolve cross-module entities (`UserDevice`, `CommunicationChannel`) via DI tokens (`ctx.resolve(...)`),
69
+ not import-time references, to stay decoupled.
70
+ - Keep `push_token` a secret: persist only `provider` + last-8 `token_snapshot`; never expose a full token
71
+ in any API/UI/log.
72
+ - Soft-delete an `unregistered` device through the `devices.user_devices.deactivate` command (system ctx:
73
+ `auth: null, systemActor: true`) — never mutate the `devices` table directly.
74
+ - Keep the `unregistered` sentinel identical across provider adapters (`result.metadata.unregistered ===
75
+ true` or `result.error === 'device_unregistered'`) so the worker's soft-delete fires uniformly.
76
+ - Keep the delivery log append-only (status transitions only); it is intentionally optimistic-lock-exempt.
77
+ - To send a silent push, declare the notification type `silent: true` (in its module's
78
+ `notifications.ts`) and create it via the normal `notificationService.create()` — the `push`
79
+ strategy turns it into a content-available wake-up. Do not add a bespoke silent-send path.
80
+
81
+ ## Never
82
+
83
+ - Never add token-management or self-serve notification CRUD here (device fields live in `devices`).
84
+ - Never introduce a `PushProvider` interface — the hub `ChannelAdapter` registry is the provider seam;
85
+ real providers are separate `channel-*` packages (Phase 4).
86
+
87
+ ## Validation
88
+
89
+ ```bash
90
+ yarn workspace @open-mercato/core test -- push_notifications
91
+ yarn workspace @open-mercato/core build
92
+ ```
@@ -0,0 +1,9 @@
1
+ export const features = [
2
+ // Admin observability over the push delivery log. There are no self-serve push features:
3
+ // device/token lifecycle lives in the `devices` module, per-user opt-out in `notifications`.
4
+ { id: 'push_notifications.view_deliveries', title: 'View push delivery log', module: 'push_notifications' },
5
+ // Admin one-off custom push send (bypasses opt-out via the nonOptOut admin.custom_message type).
6
+ { id: 'push_notifications.send_custom', title: 'Send custom push notifications', module: 'push_notifications' },
7
+ ]
8
+
9
+ export default features