@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.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/notification/index.js +6 -0
- package/dist/generated/entities/notification/index.js.map +2 -2
- package/dist/generated/entities/notification_preference/index.js +19 -0
- package/dist/generated/entities/notification_preference/index.js.map +7 -0
- package/dist/generated/entities/notification_type/index.js +21 -0
- package/dist/generated/entities/notification_type/index.js.map +7 -0
- package/dist/generated/entities/notification_type_override/index.js +17 -0
- package/dist/generated/entities/notification_type_override/index.js.map +7 -0
- package/dist/generated/entities/push_notification_delivery/index.js +41 -0
- package/dist/generated/entities/push_notification_delivery/index.js.map +7 -0
- package/dist/generated/entities/user_device/index.js +35 -0
- package/dist/generated/entities/user_device/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +12 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +72 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/api.js +2 -1
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/helpers/integration/appRoot.js +9 -0
- package/dist/helpers/integration/appRoot.js.map +7 -0
- package/dist/helpers/integration/dbFixtures.js +1 -4
- package/dist/helpers/integration/dbFixtures.js.map +2 -2
- package/dist/helpers/integration/pushFake.js +126 -0
- package/dist/helpers/integration/pushFake.js.map +7 -0
- package/dist/modules/auth/notifications.js +16 -0
- package/dist/modules/auth/notifications.js.map +2 -2
- package/dist/modules/business_rules/notifications.js +2 -0
- package/dist/modules/business_rules/notifications.js.map +2 -2
- package/dist/modules/catalog/notifications.js +2 -0
- package/dist/modules/catalog/notifications.js.map +2 -2
- package/dist/modules/communication_channels/acl.js +9 -0
- package/dist/modules/communication_channels/acl.js.map +2 -2
- package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js +133 -0
- package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +6 -2
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +2 -2
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js +6 -2
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +2 -2
- package/dist/modules/communication_channels/api/get/channels/route.js +2 -1
- package/dist/modules/communication_channels/api/get/channels/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +10 -5
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +29 -1
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +157 -0
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +8 -1
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/admin-delete-channel.js +141 -0
- package/dist/modules/communication_channels/commands/admin-delete-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/connect-credential-channel.js +25 -8
- package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +2 -2
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +2 -2
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
- package/dist/modules/communication_channels/commands/push-register.js +1 -1
- package/dist/modules/communication_channels/commands/push-register.js.map +2 -2
- package/dist/modules/communication_channels/commands/push-unregister.js +4 -3
- package/dist/modules/communication_channels/commands/push-unregister.js.map +2 -2
- package/dist/modules/communication_channels/data/entities.js +4 -0
- package/dist/modules/communication_channels/data/entities.js.map +2 -2
- package/dist/modules/communication_channels/data/validators.js +2 -1
- package/dist/modules/communication_channels/data/validators.js.map +2 -2
- package/dist/modules/communication_channels/lib/access-control.js +11 -1
- package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
- package/dist/modules/communication_channels/lib/connect-channel.js +20 -12
- package/dist/modules/communication_channels/lib/connect-channel.js.map +2 -2
- package/dist/modules/communication_channels/lib/push-adapter.js +68 -0
- package/dist/modules/communication_channels/lib/push-adapter.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-capabilities.js +31 -0
- package/dist/modules/communication_channels/lib/push-capabilities.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-connect-error.js +20 -0
- package/dist/modules/communication_channels/lib/push-connect-error.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-credential-errors.js +41 -0
- package/dist/modules/communication_channels/lib/push-credential-errors.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-envelope.js +46 -0
- package/dist/modules/communication_channels/lib/push-envelope.js.map +7 -0
- package/dist/modules/communication_channels/lib/refcounted-client-cache.js +64 -0
- package/dist/modules/communication_channels/lib/refcounted-client-cache.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js +13 -0
- package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/notifications.js +4 -0
- package/dist/modules/communication_channels/notifications.js.map +2 -2
- package/dist/modules/communication_channels/setup.js +2 -0
- package/dist/modules/communication_channels/setup.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +21 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/customer_accounts/notifications.js +12 -0
- package/dist/modules/customer_accounts/notifications.js.map +2 -2
- package/dist/modules/customers/notifications.js +4 -0
- package/dist/modules/customers/notifications.js.map +2 -2
- package/dist/modules/devices/acl.js +11 -0
- package/dist/modules/devices/acl.js.map +7 -0
- package/dist/modules/devices/api/[id]/route.js +167 -0
- package/dist/modules/devices/api/[id]/route.js.map +7 -0
- package/dist/modules/devices/api/admin/devices/[id]/route.js +219 -0
- package/dist/modules/devices/api/admin/devices/[id]/route.js.map +7 -0
- package/dist/modules/devices/api/admin/devices/route.js +140 -0
- package/dist/modules/devices/api/admin/devices/route.js.map +7 -0
- package/dist/modules/devices/api/auth.js +9 -0
- package/dist/modules/devices/api/auth.js.map +7 -0
- package/dist/modules/devices/api/deviceList.js +53 -0
- package/dist/modules/devices/api/deviceList.js.map +7 -0
- package/dist/modules/devices/api/deviceOps.js +118 -0
- package/dist/modules/devices/api/deviceOps.js.map +7 -0
- package/dist/modules/devices/api/openapi.js +19 -0
- package/dist/modules/devices/api/openapi.js.map +7 -0
- package/dist/modules/devices/api/route.js +143 -0
- package/dist/modules/devices/api/route.js.map +7 -0
- package/dist/modules/devices/backend/devices/[id]/page.js +135 -0
- package/dist/modules/devices/backend/devices/[id]/page.js.map +7 -0
- package/dist/modules/devices/backend/devices/[id]/page.meta.js +16 -0
- package/dist/modules/devices/backend/devices/[id]/page.meta.js.map +7 -0
- package/dist/modules/devices/backend/devices/create/page.js +68 -0
- package/dist/modules/devices/backend/devices/create/page.js.map +7 -0
- package/dist/modules/devices/backend/devices/create/page.meta.js +16 -0
- package/dist/modules/devices/backend/devices/create/page.meta.js.map +7 -0
- package/dist/modules/devices/backend/devices/page.js +235 -0
- package/dist/modules/devices/backend/devices/page.js.map +7 -0
- package/dist/modules/devices/backend/devices/page.meta.js +23 -0
- package/dist/modules/devices/backend/devices/page.meta.js.map +7 -0
- package/dist/modules/devices/commands/deactivate.js +107 -0
- package/dist/modules/devices/commands/deactivate.js.map +7 -0
- package/dist/modules/devices/commands/index.js +4 -0
- package/dist/modules/devices/commands/index.js.map +7 -0
- package/dist/modules/devices/commands/register.js +183 -0
- package/dist/modules/devices/commands/register.js.map +7 -0
- package/dist/modules/devices/commands/shared.js +74 -0
- package/dist/modules/devices/commands/shared.js.map +7 -0
- package/dist/modules/devices/commands/update.js +126 -0
- package/dist/modules/devices/commands/update.js.map +7 -0
- package/dist/modules/devices/data/entities.js +83 -0
- package/dist/modules/devices/data/entities.js.map +7 -0
- package/dist/modules/devices/data/validators.js +49 -0
- package/dist/modules/devices/data/validators.js.map +7 -0
- package/dist/modules/devices/di.js +11 -0
- package/dist/modules/devices/di.js.map +7 -0
- package/dist/modules/devices/encryption.js +12 -0
- package/dist/modules/devices/encryption.js.map +7 -0
- package/dist/modules/devices/events.js +19 -0
- package/dist/modules/devices/events.js.map +7 -0
- package/dist/modules/devices/index.js +17 -0
- package/dist/modules/devices/index.js.map +7 -0
- package/dist/modules/devices/lib/operationMetadata.js +20 -0
- package/dist/modules/devices/lib/operationMetadata.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260602120000.js +15 -0
- package/dist/modules/devices/migrations/Migration20260602120000.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260630120000.js +15 -0
- package/dist/modules/devices/migrations/Migration20260630120000.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260701120000.js +13 -0
- package/dist/modules/devices/migrations/Migration20260701120000.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260721120000.js +15 -0
- package/dist/modules/devices/migrations/Migration20260721120000.js.map +7 -0
- package/dist/modules/devices/setup.js +13 -0
- package/dist/modules/devices/setup.js.map +7 -0
- package/dist/modules/entities/cli.js +8 -3
- package/dist/modules/entities/cli.js.map +2 -2
- package/dist/modules/entities/migrations/Migration20260722120000.js +28 -0
- package/dist/modules/entities/migrations/Migration20260722120000.js.map +7 -0
- package/dist/modules/inbox_ops/notifications.js +2 -0
- package/dist/modules/inbox_ops/notifications.js.map +2 -2
- package/dist/modules/messages/notifications.js +2 -0
- package/dist/modules/messages/notifications.js.map +2 -2
- package/dist/modules/notifications/acl.js +3 -1
- package/dist/modules/notifications/acl.js.map +2 -2
- package/dist/modules/notifications/api/admin/preferences/route.js +167 -0
- package/dist/modules/notifications/api/admin/preferences/route.js.map +7 -0
- package/dist/modules/notifications/api/channels/route.js +54 -0
- package/dist/modules/notifications/api/channels/route.js.map +7 -0
- package/dist/modules/notifications/api/preferences/route.js +126 -0
- package/dist/modules/notifications/api/preferences/route.js.map +7 -0
- package/dist/modules/notifications/api/route.js +8 -1
- package/dist/modules/notifications/api/route.js.map +2 -2
- package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js +187 -0
- package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js.map +7 -0
- package/dist/modules/notifications/api/types/route.js +225 -0
- package/dist/modules/notifications/api/types/route.js.map +7 -0
- package/dist/modules/notifications/api/unread-count/route.js +5 -1
- package/dist/modules/notifications/api/unread-count/route.js.map +2 -2
- package/dist/modules/notifications/backend/notifications/user-preferences/page.js +10 -0
- package/dist/modules/notifications/backend/notifications/user-preferences/page.js.map +7 -0
- package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js +28 -0
- package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js.map +7 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.js +10 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.js.map +7 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js +33 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js.map +7 -0
- package/dist/modules/notifications/data/entities.js +128 -1
- package/dist/modules/notifications/data/entities.js.map +2 -2
- package/dist/modules/notifications/data/validators.js +95 -2
- package/dist/modules/notifications/data/validators.js.map +2 -2
- package/dist/modules/notifications/di.js +7 -0
- package/dist/modules/notifications/di.js.map +2 -2
- package/dist/modules/notifications/events.js +17 -0
- package/dist/modules/notifications/events.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js +190 -0
- package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js +124 -0
- package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +166 -1
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js +172 -0
- package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js.map +7 -0
- package/dist/modules/notifications/generators.js +63 -0
- package/dist/modules/notifications/generators.js.map +7 -0
- package/dist/modules/notifications/lib/delivery-strategies-registry.js +19 -0
- package/dist/modules/notifications/lib/delivery-strategies-registry.js.map +7 -0
- package/dist/modules/notifications/lib/deliveryStrategies.js.map +2 -2
- package/dist/modules/notifications/lib/derive-category.js +8 -0
- package/dist/modules/notifications/lib/derive-category.js.map +7 -0
- package/dist/modules/notifications/lib/notification-channel-registry.js +35 -0
- package/dist/modules/notifications/lib/notification-channel-registry.js.map +7 -0
- package/dist/modules/notifications/lib/notification-type-registry.js +87 -0
- package/dist/modules/notifications/lib/notification-type-registry.js.map +7 -0
- package/dist/modules/notifications/lib/notificationCopy.js +14 -0
- package/dist/modules/notifications/lib/notificationCopy.js.map +7 -0
- package/dist/modules/notifications/lib/notificationFactory.js +4 -1
- package/dist/modules/notifications/lib/notificationFactory.js.map +2 -2
- package/dist/modules/notifications/lib/notificationMapper.js +2 -0
- package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
- package/dist/modules/notifications/lib/notificationPreferenceService.js +83 -0
- package/dist/modules/notifications/lib/notificationPreferenceService.js.map +7 -0
- package/dist/modules/notifications/lib/notificationService.js +83 -28
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/dist/modules/notifications/lib/notificationVisibility.js +24 -0
- package/dist/modules/notifications/lib/notificationVisibility.js.map +7 -0
- package/dist/modules/notifications/lib/routeHelpers.js +11 -1
- package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
- package/dist/modules/notifications/lib/shouldDeliver.js +46 -0
- package/dist/modules/notifications/lib/shouldDeliver.js.map +7 -0
- package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js +54 -0
- package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js.map +7 -0
- package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js +12 -0
- package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js.map +7 -0
- package/dist/modules/notifications/lib/typeCatalogue.js +71 -0
- package/dist/modules/notifications/lib/typeCatalogue.js.map +7 -0
- package/dist/modules/notifications/lib/typeChannelSettings.js +12 -0
- package/dist/modules/notifications/lib/typeChannelSettings.js.map +7 -0
- package/dist/modules/notifications/lib/typeOverrides.js +19 -0
- package/dist/modules/notifications/lib/typeOverrides.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js +14 -0
- package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js +15 -0
- package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js +13 -0
- package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js +15 -0
- package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js +13 -0
- package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js +14 -0
- package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js.map +7 -0
- package/dist/modules/notifications/notification-channels.js +26 -0
- package/dist/modules/notifications/notification-channels.js.map +7 -0
- package/dist/modules/notifications/notifications.delivery-strategies.js +12 -0
- package/dist/modules/notifications/notifications.delivery-strategies.js.map +7 -0
- package/dist/modules/notifications/setup.js +16 -1
- package/dist/modules/notifications/setup.js.map +2 -2
- package/dist/modules/notifications/subscribers/deliver-notification.js +66 -57
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
- package/dist/modules/notifications/subscribers/sync-notification-types.js +16 -0
- package/dist/modules/notifications/subscribers/sync-notification-types.js.map +7 -0
- package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js +23 -0
- package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js.map +7 -0
- package/dist/modules/notifications/widgets/injection-table.js +14 -0
- package/dist/modules/notifications/widgets/injection-table.js.map +7 -0
- package/dist/modules/push_notifications/acl.js +13 -0
- package/dist/modules/push_notifications/acl.js.map +7 -0
- package/dist/modules/push_notifications/api/custom-send/route.js +141 -0
- package/dist/modules/push_notifications/api/custom-send/route.js.map +7 -0
- package/dist/modules/push_notifications/api/deliveries/[id]/route.js +110 -0
- package/dist/modules/push_notifications/api/deliveries/[id]/route.js.map +7 -0
- package/dist/modules/push_notifications/api/deliveries/route.js +56 -0
- package/dist/modules/push_notifications/api/deliveries/route.js.map +7 -0
- package/dist/modules/push_notifications/api/openapi.js +19 -0
- package/dist/modules/push_notifications/api/openapi.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js +103 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js +13 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.js +213 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.meta.js +23 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.meta.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js +245 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js +26 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js.map +7 -0
- package/dist/modules/push_notifications/data/entities.js +92 -0
- package/dist/modules/push_notifications/data/entities.js.map +7 -0
- package/dist/modules/push_notifications/data/extensions.js +23 -0
- package/dist/modules/push_notifications/data/extensions.js.map +7 -0
- package/dist/modules/push_notifications/data/validators.js +89 -0
- package/dist/modules/push_notifications/data/validators.js.map +7 -0
- package/dist/modules/push_notifications/di.js +17 -0
- package/dist/modules/push_notifications/di.js.map +7 -0
- package/dist/modules/push_notifications/events.js +24 -0
- package/dist/modules/push_notifications/events.js.map +7 -0
- package/dist/modules/push_notifications/index.js +16 -0
- package/dist/modules/push_notifications/index.js.map +7 -0
- package/dist/modules/push_notifications/lib/fake-provider-recorder.js +80 -0
- package/dist/modules/push_notifications/lib/fake-provider-recorder.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-delivery-strategy.js +54 -0
- package/dist/modules/push_notifications/lib/push-delivery-strategy.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-delivery.js +297 -0
- package/dist/modules/push_notifications/lib/push-delivery.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-fanout.js +138 -0
- package/dist/modules/push_notifications/lib/push-fanout.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-health.js +32 -0
- package/dist/modules/push_notifications/lib/push-health.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-reaper.js +87 -0
- package/dist/modules/push_notifications/lib/push-reaper.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-receipt-reaper.js +136 -0
- package/dist/modules/push_notifications/lib/push-receipt-reaper.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-stub-adapter.js +63 -0
- package/dist/modules/push_notifications/lib/push-stub-adapter.js.map +7 -0
- package/dist/modules/push_notifications/lib/queue.js +53 -0
- package/dist/modules/push_notifications/lib/queue.js.map +7 -0
- package/dist/modules/push_notifications/lib/reclaim-window.js +13 -0
- package/dist/modules/push_notifications/lib/reclaim-window.js.map +7 -0
- package/dist/modules/push_notifications/lib/send-custom-push.js +48 -0
- package/dist/modules/push_notifications/lib/send-custom-push.js.map +7 -0
- package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js +16 -0
- package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js.map +7 -0
- package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js +13 -0
- package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js.map +7 -0
- package/dist/modules/push_notifications/notifications.delivery-strategies.js +8 -0
- package/dist/modules/push_notifications/notifications.delivery-strategies.js.map +7 -0
- package/dist/modules/push_notifications/notifications.js +40 -0
- package/dist/modules/push_notifications/notifications.js.map +7 -0
- package/dist/modules/push_notifications/setup.js +52 -0
- package/dist/modules/push_notifications/setup.js.map +7 -0
- package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js +57 -0
- package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js.map +7 -0
- package/dist/modules/push_notifications/workers/send-push.worker.js +27 -0
- package/dist/modules/push_notifications/workers/send-push.worker.js.map +7 -0
- package/dist/modules/sales/notifications.js +8 -0
- package/dist/modules/sales/notifications.js.map +2 -2
- package/dist/modules/staff/notifications.js +6 -0
- package/dist/modules/staff/notifications.js.map +2 -2
- package/dist/modules/workflows/notifications.js +2 -0
- package/dist/modules/workflows/notifications.js.map +2 -2
- package/generated/entities/notification/index.ts +3 -0
- package/generated/entities/notification_preference/index.ts +8 -0
- package/generated/entities/notification_type/index.ts +9 -0
- package/generated/entities/notification_type_override/index.ts +7 -0
- package/generated/entities/push_notification_delivery/index.ts +19 -0
- package/generated/entities/user_device/index.ts +16 -0
- package/generated/entities.ids.generated.ts +12 -1
- package/generated/entity-fields-registry.ts +72 -0
- package/package.json +7 -7
- package/src/helpers/integration/api.ts +2 -1
- package/src/helpers/integration/appRoot.ts +13 -0
- package/src/helpers/integration/dbFixtures.ts +1 -5
- package/src/helpers/integration/pushFake.ts +219 -0
- package/src/modules/auth/i18n/de.json +1 -0
- package/src/modules/auth/i18n/en.json +1 -0
- package/src/modules/auth/i18n/es.json +1 -0
- package/src/modules/auth/i18n/ko.json +1 -0
- package/src/modules/auth/i18n/pl.json +1 -0
- package/src/modules/auth/notifications.ts +16 -0
- package/src/modules/business_rules/i18n/de.json +2 -1
- package/src/modules/business_rules/i18n/en.json +2 -1
- package/src/modules/business_rules/i18n/es.json +2 -1
- package/src/modules/business_rules/i18n/ko.json +2 -1
- package/src/modules/business_rules/i18n/pl.json +2 -1
- package/src/modules/business_rules/notifications.ts +2 -0
- package/src/modules/catalog/i18n/de.json +2 -1
- package/src/modules/catalog/i18n/en.json +2 -1
- package/src/modules/catalog/i18n/es.json +2 -1
- package/src/modules/catalog/i18n/ko.json +2 -1
- package/src/modules/catalog/i18n/pl.json +2 -1
- package/src/modules/catalog/notifications.ts +2 -0
- package/src/modules/communication_channels/acl.ts +9 -0
- package/src/modules/communication_channels/api/delete/admin/channels/[id]/route.ts +168 -0
- package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +6 -2
- package/src/modules/communication_channels/api/get/channels/[id]/route.ts +6 -2
- package/src/modules/communication_channels/api/get/channels/route.ts +2 -1
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +16 -5
- package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +37 -1
- package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +175 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +11 -1
- package/src/modules/communication_channels/commands/admin-delete-channel.ts +203 -0
- package/src/modules/communication_channels/commands/connect-credential-channel.ts +63 -15
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +10 -2
- package/src/modules/communication_channels/commands/push-register.ts +7 -1
- package/src/modules/communication_channels/commands/push-unregister.ts +14 -3
- package/src/modules/communication_channels/data/entities.ts +10 -0
- package/src/modules/communication_channels/data/validators.ts +1 -0
- package/src/modules/communication_channels/i18n/de.json +35 -1
- package/src/modules/communication_channels/i18n/en.json +35 -1
- package/src/modules/communication_channels/i18n/es.json +35 -1
- package/src/modules/communication_channels/i18n/ko.json +35 -1
- package/src/modules/communication_channels/i18n/pl.json +35 -1
- package/src/modules/communication_channels/lib/access-control.ts +42 -0
- package/src/modules/communication_channels/lib/adapter.ts +25 -0
- package/src/modules/communication_channels/lib/connect-channel.ts +61 -26
- package/src/modules/communication_channels/lib/push-adapter.ts +139 -0
- package/src/modules/communication_channels/lib/push-capabilities.ts +42 -0
- package/src/modules/communication_channels/lib/push-connect-error.ts +52 -0
- package/src/modules/communication_channels/lib/push-credential-errors.ts +58 -0
- package/src/modules/communication_channels/lib/push-envelope.ts +104 -0
- package/src/modules/communication_channels/lib/refcounted-client-cache.ts +123 -0
- package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +9 -0
- package/src/modules/communication_channels/migrations/Migration20260703123630_communication_channels.ts +20 -0
- package/src/modules/communication_channels/notifications.ts +4 -0
- package/src/modules/communication_channels/setup.ts +2 -0
- package/src/modules/configs/i18n/de.json +5 -1
- package/src/modules/configs/i18n/en.json +5 -1
- package/src/modules/configs/i18n/es.json +5 -1
- package/src/modules/configs/i18n/ko.json +5 -1
- package/src/modules/configs/i18n/pl.json +5 -1
- package/src/modules/configs/lib/upgrade-actions.ts +21 -0
- package/src/modules/customer_accounts/i18n/de.json +2 -1
- package/src/modules/customer_accounts/i18n/en.json +2 -1
- package/src/modules/customer_accounts/i18n/es.json +2 -1
- package/src/modules/customer_accounts/i18n/ko.json +2 -1
- package/src/modules/customer_accounts/i18n/pl.json +2 -1
- package/src/modules/customer_accounts/notifications.ts +12 -0
- package/src/modules/customers/i18n/de.json +2 -1
- package/src/modules/customers/i18n/en.json +2 -1
- package/src/modules/customers/i18n/es.json +2 -1
- package/src/modules/customers/i18n/ko.json +2 -1
- package/src/modules/customers/i18n/pl.json +2 -1
- package/src/modules/customers/notifications.ts +4 -0
- package/src/modules/devices/AGENTS.md +101 -0
- package/src/modules/devices/acl.ts +7 -0
- package/src/modules/devices/api/[id]/route.ts +180 -0
- package/src/modules/devices/api/admin/devices/[id]/route.ts +235 -0
- package/src/modules/devices/api/admin/devices/route.ts +160 -0
- package/src/modules/devices/api/auth.ts +13 -0
- package/src/modules/devices/api/deviceList.ts +54 -0
- package/src/modules/devices/api/deviceOps.ts +186 -0
- package/src/modules/devices/api/openapi.ts +23 -0
- package/src/modules/devices/api/route.ts +165 -0
- package/src/modules/devices/backend/devices/[id]/page.meta.ts +12 -0
- package/src/modules/devices/backend/devices/[id]/page.tsx +164 -0
- package/src/modules/devices/backend/devices/create/page.meta.ts +12 -0
- package/src/modules/devices/backend/devices/create/page.tsx +81 -0
- package/src/modules/devices/backend/devices/page.meta.ts +21 -0
- package/src/modules/devices/backend/devices/page.tsx +259 -0
- package/src/modules/devices/commands/deactivate.ts +126 -0
- package/src/modules/devices/commands/index.ts +3 -0
- package/src/modules/devices/commands/register.ts +199 -0
- package/src/modules/devices/commands/shared.ts +119 -0
- package/src/modules/devices/commands/update.ts +142 -0
- package/src/modules/devices/data/entities.ts +79 -0
- package/src/modules/devices/data/validators.ts +65 -0
- package/src/modules/devices/di.ts +9 -0
- package/src/modules/devices/encryption.ts +15 -0
- package/src/modules/devices/events.ts +16 -0
- package/src/modules/devices/i18n/de.json +45 -0
- package/src/modules/devices/i18n/en.json +45 -0
- package/src/modules/devices/i18n/es.json +45 -0
- package/src/modules/devices/i18n/ko.json +45 -0
- package/src/modules/devices/i18n/pl.json +45 -0
- package/src/modules/devices/index.ts +15 -0
- package/src/modules/devices/lib/operationMetadata.ts +35 -0
- package/src/modules/devices/migrations/.snapshot-open-mercato.json +200 -0
- package/src/modules/devices/migrations/Migration20260602120000.ts +15 -0
- package/src/modules/devices/migrations/Migration20260630120000.ts +20 -0
- package/src/modules/devices/migrations/Migration20260701120000.ts +13 -0
- package/src/modules/devices/migrations/Migration20260721120000.ts +18 -0
- package/src/modules/devices/setup.ts +11 -0
- package/src/modules/entities/cli.ts +16 -2
- package/src/modules/entities/migrations/Migration20260722120000.ts +38 -0
- package/src/modules/inbox_ops/i18n/de.json +2 -1
- package/src/modules/inbox_ops/i18n/en.json +2 -1
- package/src/modules/inbox_ops/i18n/es.json +2 -1
- package/src/modules/inbox_ops/i18n/ko.json +2 -1
- package/src/modules/inbox_ops/i18n/pl.json +2 -1
- package/src/modules/inbox_ops/notifications.ts +2 -0
- package/src/modules/messages/i18n/de.json +2 -1
- package/src/modules/messages/i18n/en.json +2 -1
- package/src/modules/messages/i18n/es.json +2 -1
- package/src/modules/messages/i18n/ko.json +2 -1
- package/src/modules/messages/i18n/pl.json +2 -1
- package/src/modules/messages/notifications.ts +2 -0
- package/src/modules/notifications/AGENTS.md +54 -0
- package/src/modules/notifications/acl.ts +2 -0
- package/src/modules/notifications/api/admin/preferences/route.ts +193 -0
- package/src/modules/notifications/api/channels/route.ts +54 -0
- package/src/modules/notifications/api/preferences/route.ts +131 -0
- package/src/modules/notifications/api/route.ts +8 -1
- package/src/modules/notifications/api/types/[id]/channels/[channel]/route.ts +226 -0
- package/src/modules/notifications/api/types/route.ts +239 -0
- package/src/modules/notifications/api/unread-count/route.ts +5 -1
- package/src/modules/notifications/backend/notifications/user-preferences/page.meta.ts +26 -0
- package/src/modules/notifications/backend/notifications/user-preferences/page.tsx +12 -0
- package/src/modules/notifications/backend/profile/notification-preferences/page.meta.ts +31 -0
- package/src/modules/notifications/backend/profile/notification-preferences/page.tsx +12 -0
- package/src/modules/notifications/data/entities.ts +144 -0
- package/src/modules/notifications/data/validators.ts +116 -0
- package/src/modules/notifications/di.ts +7 -0
- package/src/modules/notifications/events.ts +23 -0
- package/src/modules/notifications/frontend/NotificationPreferenceMatrix.tsx +287 -0
- package/src/modules/notifications/frontend/NotificationPreferencesPageClient.tsx +155 -0
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +273 -2
- package/src/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.tsx +214 -0
- package/src/modules/notifications/generators.ts +82 -0
- package/src/modules/notifications/i18n/de.json +43 -0
- package/src/modules/notifications/i18n/en.json +43 -0
- package/src/modules/notifications/i18n/es.json +43 -0
- package/src/modules/notifications/i18n/ko.json +43 -0
- package/src/modules/notifications/i18n/pl.json +44 -1
- package/src/modules/notifications/lib/delivery-strategies-registry.ts +30 -0
- package/src/modules/notifications/lib/deliveryStrategies.ts +40 -4
- package/src/modules/notifications/lib/derive-category.ts +15 -0
- package/src/modules/notifications/lib/notification-channel-registry.ts +59 -0
- package/src/modules/notifications/lib/notification-type-registry.ts +182 -0
- package/src/modules/notifications/lib/notificationCopy.ts +34 -0
- package/src/modules/notifications/lib/notificationFactory.ts +5 -1
- package/src/modules/notifications/lib/notificationMapper.ts +2 -0
- package/src/modules/notifications/lib/notificationPreferenceService.ts +138 -0
- package/src/modules/notifications/lib/notificationService.ts +129 -25
- package/src/modules/notifications/lib/notificationVisibility.ts +48 -0
- package/src/modules/notifications/lib/routeHelpers.ts +14 -1
- package/src/modules/notifications/lib/shouldDeliver.ts +112 -0
- package/src/modules/notifications/lib/strategies/email-delivery-strategy.ts +63 -0
- package/src/modules/notifications/lib/strategies/in-app-delivery-strategy.ts +21 -0
- package/src/modules/notifications/lib/typeCatalogue.ts +141 -0
- package/src/modules/notifications/lib/typeChannelSettings.ts +29 -0
- package/src/modules/notifications/lib/typeOverrides.ts +46 -0
- package/src/modules/notifications/migrations/.snapshot-open-mercato.json +874 -100
- package/src/modules/notifications/migrations/Migration20260625122947_notifications.ts +14 -0
- package/src/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.ts +15 -0
- package/src/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.ts +13 -0
- package/src/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.ts +15 -0
- package/src/modules/notifications/migrations/Migration20260706120000_notifications_channels.ts +13 -0
- package/src/modules/notifications/migrations/Migration20260716123121_notifications.ts +14 -0
- package/src/modules/notifications/notification-channels.ts +30 -0
- package/src/modules/notifications/notifications.delivery-strategies.ts +15 -0
- package/src/modules/notifications/setup.ts +23 -1
- package/src/modules/notifications/subscribers/deliver-notification.ts +105 -71
- package/src/modules/notifications/subscribers/sync-notification-types.ts +18 -0
- package/src/modules/notifications/widgets/injection/profile-preferences-menu/widget.ts +35 -0
- package/src/modules/notifications/widgets/injection-table.ts +18 -0
- package/src/modules/push_notifications/AGENTS.md +92 -0
- package/src/modules/push_notifications/acl.ts +9 -0
- package/src/modules/push_notifications/api/custom-send/route.ts +175 -0
- package/src/modules/push_notifications/api/deliveries/[id]/route.ts +122 -0
- package/src/modules/push_notifications/api/deliveries/route.ts +58 -0
- package/src/modules/push_notifications/api/openapi.ts +22 -0
- package/src/modules/push_notifications/backend/push_notifications/[id]/page.meta.ts +9 -0
- package/src/modules/push_notifications/backend/push_notifications/[id]/page.tsx +149 -0
- package/src/modules/push_notifications/backend/push_notifications/page.meta.ts +21 -0
- package/src/modules/push_notifications/backend/push_notifications/page.tsx +239 -0
- package/src/modules/push_notifications/backend/push_notifications/send/page.meta.ts +24 -0
- package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +327 -0
- package/src/modules/push_notifications/data/entities.ts +111 -0
- package/src/modules/push_notifications/data/extensions.ts +29 -0
- package/src/modules/push_notifications/data/validators.ts +102 -0
- package/src/modules/push_notifications/di.ts +20 -0
- package/src/modules/push_notifications/events.ts +21 -0
- package/src/modules/push_notifications/i18n/de.json +79 -0
- package/src/modules/push_notifications/i18n/en.json +79 -0
- package/src/modules/push_notifications/i18n/es.json +79 -0
- package/src/modules/push_notifications/i18n/ko.json +79 -0
- package/src/modules/push_notifications/i18n/pl.json +79 -0
- package/src/modules/push_notifications/index.ts +15 -0
- package/src/modules/push_notifications/lib/fake-provider-recorder.ts +154 -0
- package/src/modules/push_notifications/lib/push-delivery-strategy.ts +90 -0
- package/src/modules/push_notifications/lib/push-delivery.ts +455 -0
- package/src/modules/push_notifications/lib/push-fanout.ts +237 -0
- package/src/modules/push_notifications/lib/push-health.ts +47 -0
- package/src/modules/push_notifications/lib/push-reaper.ts +140 -0
- package/src/modules/push_notifications/lib/push-receipt-reaper.ts +210 -0
- package/src/modules/push_notifications/lib/push-stub-adapter.ts +80 -0
- package/src/modules/push_notifications/lib/queue.ts +74 -0
- package/src/modules/push_notifications/lib/reclaim-window.ts +16 -0
- package/src/modules/push_notifications/lib/send-custom-push.ts +100 -0
- package/src/modules/push_notifications/migrations/.snapshot-open-mercato.json +386 -0
- package/src/modules/push_notifications/migrations/Migration20260625150049_push_notifications.ts +16 -0
- package/src/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.ts +13 -0
- package/src/modules/push_notifications/notifications.delivery-strategies.ts +8 -0
- package/src/modules/push_notifications/notifications.ts +50 -0
- package/src/modules/push_notifications/setup.ts +92 -0
- package/src/modules/push_notifications/workers/reclaim-stuck.worker.ts +78 -0
- package/src/modules/push_notifications/workers/send-push.worker.ts +34 -0
- package/src/modules/sales/i18n/de.json +1 -0
- package/src/modules/sales/i18n/en.json +1 -0
- package/src/modules/sales/i18n/es.json +1 -0
- package/src/modules/sales/i18n/ko.json +1 -0
- package/src/modules/sales/i18n/pl.json +1 -0
- package/src/modules/sales/notifications.ts +8 -0
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/ko.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/notifications.ts +6 -0
- package/src/modules/workflows/i18n/de.json +1 -0
- package/src/modules/workflows/i18n/en.json +1 -0
- package/src/modules/workflows/i18n/es.json +1 -0
- package/src/modules/workflows/i18n/ko.json +1 -0
- package/src/modules/workflows/i18n/pl.json +1 -0
- 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
|
+
}
|
package/src/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.ts
ADDED
|
@@ -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
|
+
}
|
package/src/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.ts
ADDED
|
@@ -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
|
+
}
|
package/src/modules/notifications/migrations/Migration20260706120000_notifications_channels.ts
ADDED
|
@@ -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 {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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 (
|
|
190
|
-
|
|
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('
|
|
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.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|