@open-mercato/core 0.4.2-canary-36ab8921da → 0.4.2-canary-07dbc98202

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 (235) hide show
  1. package/dist/generated/entities/notification/index.js +57 -0
  2. package/dist/generated/entities/notification/index.js.map +7 -0
  3. package/dist/generated/entities.ids.generated.js +63 -59
  4. package/dist/generated/entities.ids.generated.js.map +2 -2
  5. package/dist/generated/entity-fields-registry.js +2 -0
  6. package/dist/generated/entity-fields-registry.js.map +2 -2
  7. package/dist/modules/api_docs/frontend/docs/api/page.js +3 -2
  8. package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
  9. package/dist/modules/auth/api/admin/nav.js +4 -3
  10. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  11. package/dist/modules/auth/api/profile/route.js +155 -0
  12. package/dist/modules/auth/api/profile/route.js.map +7 -0
  13. package/dist/modules/auth/api/reset/confirm.js +25 -2
  14. package/dist/modules/auth/api/reset/confirm.js.map +2 -2
  15. package/dist/modules/auth/api/reset.js +23 -0
  16. package/dist/modules/auth/api/reset.js.map +2 -2
  17. package/dist/modules/auth/api/sidebar/preferences/route.js +14 -9
  18. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  19. package/dist/modules/auth/backend/auth/profile/page.js +99 -0
  20. package/dist/modules/auth/backend/auth/profile/page.js.map +7 -0
  21. package/dist/modules/auth/backend/auth/profile/page.meta.js +12 -0
  22. package/dist/modules/auth/backend/auth/profile/page.meta.js.map +7 -0
  23. package/dist/modules/auth/commands/users.js +55 -0
  24. package/dist/modules/auth/commands/users.js.map +2 -2
  25. package/dist/modules/auth/lib/setup-app.js +1 -0
  26. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  27. package/dist/modules/auth/notifications.js +112 -0
  28. package/dist/modules/auth/notifications.js.map +7 -0
  29. package/dist/modules/auth/services/authService.js +3 -3
  30. package/dist/modules/auth/services/authService.js.map +2 -2
  31. package/dist/modules/business_rules/notifications.js +28 -0
  32. package/dist/modules/business_rules/notifications.js.map +7 -0
  33. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +37 -0
  34. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +7 -0
  35. package/dist/modules/catalog/notifications.js +28 -0
  36. package/dist/modules/catalog/notifications.js.map +7 -0
  37. package/dist/modules/catalog/subscribers/low-stock-notification.js +38 -0
  38. package/dist/modules/catalog/subscribers/low-stock-notification.js.map +7 -0
  39. package/dist/modules/configs/cli.js +6 -0
  40. package/dist/modules/configs/cli.js.map +2 -2
  41. package/dist/modules/customers/commands/deals.js +31 -0
  42. package/dist/modules/customers/commands/deals.js.map +2 -2
  43. package/dist/modules/customers/notifications.js +48 -0
  44. package/dist/modules/customers/notifications.js.map +7 -0
  45. package/dist/modules/notifications/acl.js +11 -0
  46. package/dist/modules/notifications/acl.js.map +7 -0
  47. package/dist/modules/notifications/api/[id]/action/route.js +74 -0
  48. package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
  49. package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
  50. package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
  51. package/dist/modules/notifications/api/[id]/read/route.js +15 -0
  52. package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
  53. package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
  54. package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
  55. package/dist/modules/notifications/api/batch/route.js +17 -0
  56. package/dist/modules/notifications/api/batch/route.js.map +7 -0
  57. package/dist/modules/notifications/api/feature/route.js +17 -0
  58. package/dist/modules/notifications/api/feature/route.js.map +7 -0
  59. package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
  60. package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
  61. package/dist/modules/notifications/api/openapi.js +76 -0
  62. package/dist/modules/notifications/api/openapi.js.map +7 -0
  63. package/dist/modules/notifications/api/role/route.js +17 -0
  64. package/dist/modules/notifications/api/role/route.js.map +7 -0
  65. package/dist/modules/notifications/api/route.js +85 -0
  66. package/dist/modules/notifications/api/route.js.map +7 -0
  67. package/dist/modules/notifications/api/settings/route.js +155 -0
  68. package/dist/modules/notifications/api/settings/route.js.map +7 -0
  69. package/dist/modules/notifications/api/unread-count/route.js +38 -0
  70. package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
  71. package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
  72. package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
  73. package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
  74. package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
  75. package/dist/modules/notifications/cli.js +16 -0
  76. package/dist/modules/notifications/cli.js.map +7 -0
  77. package/dist/modules/notifications/data/entities.js +112 -0
  78. package/dist/modules/notifications/data/entities.js.map +7 -0
  79. package/dist/modules/notifications/data/validators.js +94 -0
  80. package/dist/modules/notifications/data/validators.js.map +7 -0
  81. package/dist/modules/notifications/di.js +13 -0
  82. package/dist/modules/notifications/di.js.map +7 -0
  83. package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
  84. package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
  85. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
  86. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
  87. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +219 -0
  88. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
  89. package/dist/modules/notifications/index.js +14 -0
  90. package/dist/modules/notifications/index.js.map +7 -0
  91. package/dist/modules/notifications/lib/deliveryConfig.js +105 -0
  92. package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
  93. package/dist/modules/notifications/lib/events.js +12 -0
  94. package/dist/modules/notifications/lib/events.js.map +7 -0
  95. package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
  96. package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
  97. package/dist/modules/notifications/lib/notificationFactory.js +54 -0
  98. package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
  99. package/dist/modules/notifications/lib/notificationMapper.js +34 -0
  100. package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
  101. package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
  102. package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
  103. package/dist/modules/notifications/lib/notificationService.js +279 -0
  104. package/dist/modules/notifications/lib/notificationService.js.map +7 -0
  105. package/dist/modules/notifications/lib/routeHelpers.js +101 -0
  106. package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
  107. package/dist/modules/notifications/lib/safeHref.js +24 -0
  108. package/dist/modules/notifications/lib/safeHref.js.map +7 -0
  109. package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
  110. package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
  111. package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
  112. package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
  113. package/dist/modules/notifications/subscribers/deliver-notification.js +139 -0
  114. package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
  115. package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
  116. package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
  117. package/dist/modules/sales/commands/documents.js +53 -0
  118. package/dist/modules/sales/commands/documents.js.map +2 -2
  119. package/dist/modules/sales/commands/payments.js +26 -0
  120. package/dist/modules/sales/commands/payments.js.map +2 -2
  121. package/dist/modules/sales/notifications.client.js +51 -0
  122. package/dist/modules/sales/notifications.client.js.map +7 -0
  123. package/dist/modules/sales/notifications.js +88 -0
  124. package/dist/modules/sales/notifications.js.map +7 -0
  125. package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
  126. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
  127. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
  128. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
  129. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
  130. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
  131. package/dist/modules/sales/widgets/notifications/index.js +7 -0
  132. package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
  133. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
  134. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
  135. package/dist/modules/staff/commands/leave-requests.js +79 -0
  136. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  137. package/dist/modules/staff/notifications.js +75 -0
  138. package/dist/modules/staff/notifications.js.map +7 -0
  139. package/dist/modules/workflows/notifications.js +28 -0
  140. package/dist/modules/workflows/notifications.js.map +7 -0
  141. package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
  142. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
  143. package/generated/entities/notification/index.ts +27 -0
  144. package/generated/entities.ids.generated.ts +63 -59
  145. package/generated/entity-fields-registry.ts +2 -0
  146. package/package.json +2 -2
  147. package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
  148. package/src/modules/auth/api/admin/nav.ts +10 -6
  149. package/src/modules/auth/api/profile/route.ts +160 -0
  150. package/src/modules/auth/api/reset/confirm.ts +25 -2
  151. package/src/modules/auth/api/reset.ts +23 -0
  152. package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
  153. package/src/modules/auth/backend/auth/profile/page.meta.ts +8 -0
  154. package/src/modules/auth/backend/auth/profile/page.tsx +127 -0
  155. package/src/modules/auth/commands/users.ts +68 -0
  156. package/src/modules/auth/i18n/de.json +29 -1
  157. package/src/modules/auth/i18n/en.json +29 -1
  158. package/src/modules/auth/i18n/es.json +29 -1
  159. package/src/modules/auth/i18n/pl.json +29 -1
  160. package/src/modules/auth/lib/setup-app.ts +1 -0
  161. package/src/modules/auth/notifications.ts +109 -0
  162. package/src/modules/auth/services/authService.ts +4 -4
  163. package/src/modules/business_rules/i18n/en.json +3 -1
  164. package/src/modules/business_rules/notifications.ts +25 -0
  165. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
  166. package/src/modules/catalog/i18n/en.json +3 -1
  167. package/src/modules/catalog/notifications.ts +25 -0
  168. package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
  169. package/src/modules/configs/cli.ts +6 -0
  170. package/src/modules/customers/commands/deals.ts +39 -0
  171. package/src/modules/customers/i18n/en.json +5 -1
  172. package/src/modules/customers/notifications.ts +44 -0
  173. package/src/modules/notifications/acl.ts +7 -0
  174. package/src/modules/notifications/api/[id]/action/route.ts +75 -0
  175. package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
  176. package/src/modules/notifications/api/[id]/read/route.ts +12 -0
  177. package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
  178. package/src/modules/notifications/api/batch/route.ts +14 -0
  179. package/src/modules/notifications/api/feature/route.ts +14 -0
  180. package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
  181. package/src/modules/notifications/api/openapi.ts +76 -0
  182. package/src/modules/notifications/api/role/route.ts +14 -0
  183. package/src/modules/notifications/api/route.ts +92 -0
  184. package/src/modules/notifications/api/settings/route.ts +157 -0
  185. package/src/modules/notifications/api/unread-count/route.ts +38 -0
  186. package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
  187. package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
  188. package/src/modules/notifications/cli.ts +18 -0
  189. package/src/modules/notifications/data/entities.ts +99 -0
  190. package/src/modules/notifications/data/validators.ts +110 -0
  191. package/src/modules/notifications/di.ts +11 -0
  192. package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
  193. package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
  194. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +231 -0
  195. package/src/modules/notifications/i18n/de.json +50 -0
  196. package/src/modules/notifications/i18n/en.json +50 -0
  197. package/src/modules/notifications/i18n/es.json +50 -0
  198. package/src/modules/notifications/i18n/pl.json +50 -0
  199. package/src/modules/notifications/index.ts +12 -0
  200. package/src/modules/notifications/lib/deliveryConfig.ts +145 -0
  201. package/src/modules/notifications/lib/events.ts +48 -0
  202. package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
  203. package/src/modules/notifications/lib/notificationFactory.ts +76 -0
  204. package/src/modules/notifications/lib/notificationMapper.ts +33 -0
  205. package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
  206. package/src/modules/notifications/lib/notificationService.ts +414 -0
  207. package/src/modules/notifications/lib/routeHelpers.ts +151 -0
  208. package/src/modules/notifications/lib/safeHref.ts +29 -0
  209. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +300 -0
  210. package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
  211. package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
  212. package/src/modules/notifications/subscribers/deliver-notification.ts +175 -0
  213. package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
  214. package/src/modules/sales/commands/documents.ts +65 -0
  215. package/src/modules/sales/commands/payments.ts +33 -0
  216. package/src/modules/sales/i18n/de.json +20 -0
  217. package/src/modules/sales/i18n/en.json +25 -1
  218. package/src/modules/sales/i18n/es.json +20 -0
  219. package/src/modules/sales/i18n/pl.json +20 -0
  220. package/src/modules/sales/notifications.client.ts +65 -0
  221. package/src/modules/sales/notifications.ts +82 -0
  222. package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
  223. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
  224. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
  225. package/src/modules/sales/widgets/notifications/index.ts +2 -0
  226. package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
  227. package/src/modules/staff/commands/leave-requests.ts +94 -0
  228. package/src/modules/staff/i18n/de.json +4 -0
  229. package/src/modules/staff/i18n/en.json +9 -1
  230. package/src/modules/staff/i18n/es.json +4 -0
  231. package/src/modules/staff/i18n/pl.json +4 -0
  232. package/src/modules/staff/notifications.ts +71 -0
  233. package/src/modules/workflows/i18n/en.json +3 -1
  234. package/src/modules/workflows/notifications.ts +25 -0
  235. package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
@@ -0,0 +1,105 @@
1
+ import { parseBooleanWithDefault } from "@open-mercato/shared/lib/boolean";
2
+ import { notificationDeliveryConfigSchema } from "../data/validators.js";
3
+ const NOTIFICATIONS_DELIVERY_CONFIG_KEY = "delivery_strategies";
4
+ const envString = (value) => {
5
+ if (!value) return void 0;
6
+ const trimmed = value.trim();
7
+ return trimmed.length ? trimmed : void 0;
8
+ };
9
+ const resolveEnvDefaults = () => {
10
+ const appUrl = envString(
11
+ process.env.NOTIFICATIONS_APP_URL || process.env.APPLICATION_URL || process.env.NEXT_PUBLIC_APP_URL || process.env.APP_URL
12
+ );
13
+ const panelPath = envString(process.env.NOTIFICATIONS_PANEL_PATH);
14
+ const emailEnabled = parseBooleanWithDefault(process.env.NOTIFICATIONS_EMAIL_ENABLED, true);
15
+ const emailFrom = envString(process.env.NOTIFICATIONS_EMAIL_FROM || process.env.EMAIL_FROM);
16
+ const emailReplyTo = envString(process.env.NOTIFICATIONS_EMAIL_REPLY_TO || process.env.ADMIN_EMAIL);
17
+ const emailSubjectPrefix = envString(process.env.NOTIFICATIONS_EMAIL_SUBJECT_PREFIX);
18
+ return {
19
+ appUrl,
20
+ panelPath,
21
+ emailEnabled,
22
+ emailFrom,
23
+ emailReplyTo,
24
+ emailSubjectPrefix
25
+ };
26
+ };
27
+ const DEFAULT_NOTIFICATION_DELIVERY_CONFIG = (() => {
28
+ const env = resolveEnvDefaults();
29
+ return {
30
+ appUrl: env.appUrl,
31
+ panelPath: env.panelPath ?? "/backend/notifications",
32
+ strategies: {
33
+ database: { enabled: true },
34
+ email: {
35
+ enabled: env.emailEnabled,
36
+ from: env.emailFrom,
37
+ replyTo: env.emailReplyTo,
38
+ subjectPrefix: env.emailSubjectPrefix
39
+ }
40
+ }
41
+ };
42
+ })();
43
+ const normalizeDeliveryConfig = (input) => {
44
+ const parsed = notificationDeliveryConfigSchema.safeParse(input ?? {});
45
+ if (!parsed.success) {
46
+ return { ...DEFAULT_NOTIFICATION_DELIVERY_CONFIG };
47
+ }
48
+ const value = parsed.data ?? {};
49
+ const strategies = value.strategies ?? {};
50
+ return {
51
+ appUrl: value.appUrl,
52
+ panelPath: value.panelPath ?? DEFAULT_NOTIFICATION_DELIVERY_CONFIG.panelPath,
53
+ strategies: {
54
+ database: {
55
+ enabled: DEFAULT_NOTIFICATION_DELIVERY_CONFIG.strategies.database.enabled
56
+ },
57
+ email: {
58
+ enabled: strategies.email?.enabled ?? DEFAULT_NOTIFICATION_DELIVERY_CONFIG.strategies.email.enabled,
59
+ from: strategies.email?.from,
60
+ replyTo: strategies.email?.replyTo,
61
+ subjectPrefix: strategies.email?.subjectPrefix
62
+ }
63
+ }
64
+ };
65
+ };
66
+ async function resolveNotificationDeliveryConfig(resolver, options) {
67
+ const fallback = options?.defaultValue ?? DEFAULT_NOTIFICATION_DELIVERY_CONFIG;
68
+ let service;
69
+ try {
70
+ service = resolver.resolve("moduleConfigService");
71
+ } catch {
72
+ return { ...fallback };
73
+ }
74
+ try {
75
+ const value = await service.getValue("notifications", NOTIFICATIONS_DELIVERY_CONFIG_KEY, { defaultValue: fallback });
76
+ return normalizeDeliveryConfig(value);
77
+ } catch {
78
+ return { ...fallback };
79
+ }
80
+ }
81
+ async function saveNotificationDeliveryConfig(resolver, config) {
82
+ let service;
83
+ try {
84
+ service = resolver.resolve("moduleConfigService");
85
+ } catch {
86
+ throw new Error("Configuration service unavailable");
87
+ }
88
+ const normalized = normalizeDeliveryConfig(config);
89
+ await service.setValue("notifications", NOTIFICATIONS_DELIVERY_CONFIG_KEY, normalized);
90
+ }
91
+ function resolveNotificationPanelUrl(config) {
92
+ const base = config.appUrl || process.env.APPLICATION_URL || process.env.NEXT_PUBLIC_APP_URL || process.env.APP_URL;
93
+ if (!base || !base.trim()) {
94
+ return config.panelPath;
95
+ }
96
+ return `${base.replace(/\/$/, "")}${config.panelPath}`;
97
+ }
98
+ export {
99
+ DEFAULT_NOTIFICATION_DELIVERY_CONFIG,
100
+ NOTIFICATIONS_DELIVERY_CONFIG_KEY,
101
+ resolveNotificationDeliveryConfig,
102
+ resolveNotificationPanelUrl,
103
+ saveNotificationDeliveryConfig
104
+ };
105
+ //# sourceMappingURL=deliveryConfig.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/lib/deliveryConfig.ts"],
4
+ "sourcesContent": ["import type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\nimport { notificationDeliveryConfigSchema, type NotificationDeliveryConfigInput } from '../data/validators'\n\nexport const NOTIFICATIONS_DELIVERY_CONFIG_KEY = 'delivery_strategies'\n\nexport type NotificationDeliveryStrategyState = {\n enabled: boolean\n}\n\nexport type NotificationEmailDeliveryConfig = NotificationDeliveryStrategyState & {\n from?: string\n replyTo?: string\n subjectPrefix?: string\n}\n\nexport type NotificationDeliveryConfig = {\n appUrl?: string\n panelPath: string\n strategies: {\n database: NotificationDeliveryStrategyState\n email: NotificationEmailDeliveryConfig\n }\n}\n\nconst envString = (value: string | undefined | null) => {\n if (!value) return undefined\n const trimmed = value.trim()\n return trimmed.length ? trimmed : undefined\n}\n\nconst resolveEnvDefaults = () => {\n const appUrl = envString(\n process.env.NOTIFICATIONS_APP_URL ||\n process.env.APPLICATION_URL ||\n process.env.NEXT_PUBLIC_APP_URL ||\n process.env.APP_URL\n )\n const panelPath = envString(process.env.NOTIFICATIONS_PANEL_PATH)\n const emailEnabled = parseBooleanWithDefault(process.env.NOTIFICATIONS_EMAIL_ENABLED, true)\n const emailFrom = envString(process.env.NOTIFICATIONS_EMAIL_FROM || process.env.EMAIL_FROM)\n const emailReplyTo = envString(process.env.NOTIFICATIONS_EMAIL_REPLY_TO || process.env.ADMIN_EMAIL)\n const emailSubjectPrefix = envString(process.env.NOTIFICATIONS_EMAIL_SUBJECT_PREFIX)\n\n return {\n appUrl,\n panelPath,\n emailEnabled,\n emailFrom,\n emailReplyTo,\n emailSubjectPrefix,\n }\n}\n\nexport const DEFAULT_NOTIFICATION_DELIVERY_CONFIG: NotificationDeliveryConfig = (() => {\n const env = resolveEnvDefaults()\n return {\n appUrl: env.appUrl,\n panelPath: env.panelPath ?? '/backend/notifications',\n strategies: {\n database: { enabled: true },\n email: {\n enabled: env.emailEnabled,\n from: env.emailFrom,\n replyTo: env.emailReplyTo,\n subjectPrefix: env.emailSubjectPrefix,\n },\n },\n }\n})()\n\nconst normalizeDeliveryConfig = (input?: unknown | null): NotificationDeliveryConfig => {\n const parsed = notificationDeliveryConfigSchema.safeParse(input ?? {})\n if (!parsed.success) {\n return { ...DEFAULT_NOTIFICATION_DELIVERY_CONFIG }\n }\n\n const value = parsed.data ?? {}\n const strategies = value.strategies ?? {}\n\n return {\n appUrl: value.appUrl,\n panelPath: value.panelPath ?? DEFAULT_NOTIFICATION_DELIVERY_CONFIG.panelPath,\n strategies: {\n database: {\n enabled: DEFAULT_NOTIFICATION_DELIVERY_CONFIG.strategies.database.enabled,\n },\n email: {\n enabled: strategies.email?.enabled ?? DEFAULT_NOTIFICATION_DELIVERY_CONFIG.strategies.email.enabled,\n from: strategies.email?.from,\n replyTo: strategies.email?.replyTo,\n subjectPrefix: strategies.email?.subjectPrefix,\n },\n },\n }\n}\n\ntype Resolver = {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport async function resolveNotificationDeliveryConfig(\n resolver: Resolver,\n options?: { defaultValue?: NotificationDeliveryConfig }\n): Promise<NotificationDeliveryConfig> {\n const fallback = options?.defaultValue ?? DEFAULT_NOTIFICATION_DELIVERY_CONFIG\n let service: ModuleConfigService\n try {\n service = resolver.resolve<ModuleConfigService>('moduleConfigService')\n } catch {\n return { ...fallback }\n }\n try {\n const value = await service.getValue('notifications', NOTIFICATIONS_DELIVERY_CONFIG_KEY, { defaultValue: fallback })\n return normalizeDeliveryConfig(value)\n } catch {\n return { ...fallback }\n }\n}\n\nexport async function saveNotificationDeliveryConfig(\n resolver: Resolver,\n config: NotificationDeliveryConfigInput\n): Promise<void> {\n let service: ModuleConfigService\n try {\n service = resolver.resolve<ModuleConfigService>('moduleConfigService')\n } catch {\n throw new Error('Configuration service unavailable')\n }\n\n const normalized = normalizeDeliveryConfig(config)\n await service.setValue('notifications', NOTIFICATIONS_DELIVERY_CONFIG_KEY, normalized)\n}\n\nexport function resolveNotificationPanelUrl(config: NotificationDeliveryConfig): string | null {\n const base = config.appUrl\n || process.env.APPLICATION_URL\n || process.env.NEXT_PUBLIC_APP_URL\n || process.env.APP_URL\n if (!base || !base.trim()) {\n return config.panelPath\n }\n return `${base.replace(/\\/$/, '')}${config.panelPath}`\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ const NOTIFICATION_EVENTS = {
2
+ CREATED: "notifications.created",
3
+ READ: "notifications.read",
4
+ ACTIONED: "notifications.actioned",
5
+ DISMISSED: "notifications.dismissed",
6
+ RESTORED: "notifications.restored",
7
+ EXPIRED: "notifications.expired"
8
+ };
9
+ export {
10
+ NOTIFICATION_EVENTS
11
+ };
12
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/lib/events.ts"],
4
+ "sourcesContent": ["export const NOTIFICATION_EVENTS = {\n CREATED: 'notifications.created',\n READ: 'notifications.read',\n ACTIONED: 'notifications.actioned',\n DISMISSED: 'notifications.dismissed',\n RESTORED: 'notifications.restored',\n EXPIRED: 'notifications.expired',\n} as const\n\nexport type NotificationCreatedPayload = {\n notificationId: string\n recipientUserId: string\n type: string\n title: string\n tenantId: string\n organizationId?: string | null\n}\n\nexport type NotificationReadPayload = {\n notificationId: string\n userId: string\n tenantId: string\n}\n\nexport type NotificationActionedPayload = {\n notificationId: string\n actionId: string\n userId: string\n tenantId: string\n}\n\nexport type NotificationDismissedPayload = {\n notificationId: string\n userId: string\n tenantId: string\n}\n\nexport type NotificationRestoredPayload = {\n notificationId: string\n userId: string\n tenantId: string\n status: 'read' | 'unread'\n}\n\nexport type NotificationExpiredPayload = {\n notificationIds: string[]\n tenantId: string\n}\n"],
5
+ "mappings": "AAAO,MAAM,sBAAsB;AAAA,EACjC,SAAS;AAAA,EACT,MAAM;AAAA,EACN,UAAU;AAAA,EACV,WAAW;AAAA,EACX,UAAU;AAAA,EACV,SAAS;AACX;",
6
+ "names": []
7
+ }
@@ -0,0 +1,66 @@
1
+ function mapActions(actions) {
2
+ if (!actions || actions.length === 0) return void 0;
3
+ return actions.map((action) => ({
4
+ id: action.id,
5
+ label: action.labelKey,
6
+ labelKey: action.labelKey,
7
+ variant: action.variant,
8
+ icon: action.icon,
9
+ commandId: action.commandId,
10
+ href: action.href,
11
+ confirmRequired: action.confirmRequired,
12
+ confirmMessage: action.confirmMessageKey
13
+ }));
14
+ }
15
+ function buildBaseNotificationFields(typeDef, options) {
16
+ return {
17
+ type: typeDef.type,
18
+ titleKey: typeDef.titleKey,
19
+ bodyKey: typeDef.bodyKey,
20
+ titleVariables: options.titleVariables,
21
+ bodyVariables: options.bodyVariables,
22
+ title: typeDef.titleKey,
23
+ body: typeDef.bodyKey,
24
+ icon: typeDef.icon,
25
+ severity: typeDef.severity,
26
+ actions: mapActions(typeDef.actions),
27
+ primaryActionId: typeDef.primaryActionId,
28
+ sourceModule: typeDef.module,
29
+ sourceEntityType: options.sourceEntityType,
30
+ sourceEntityId: options.sourceEntityId,
31
+ linkHref: options.linkHref ?? typeDef.linkHref,
32
+ groupKey: options.groupKey,
33
+ expiresAt: options.expiresAt ?? (typeDef.expiresAfterHours ? new Date(Date.now() + typeDef.expiresAfterHours * 60 * 60 * 1e3).toISOString() : void 0)
34
+ };
35
+ }
36
+ function buildNotificationFromType(typeDef, options) {
37
+ return {
38
+ recipientUserId: options.recipientUserId,
39
+ ...buildBaseNotificationFields(typeDef, options)
40
+ };
41
+ }
42
+ function buildBatchNotificationFromType(typeDef, options) {
43
+ return {
44
+ recipientUserIds: options.recipientUserIds,
45
+ ...buildBaseNotificationFields(typeDef, options)
46
+ };
47
+ }
48
+ function buildRoleNotificationFromType(typeDef, options) {
49
+ return {
50
+ roleId: options.roleId,
51
+ ...buildBaseNotificationFields(typeDef, options)
52
+ };
53
+ }
54
+ function buildFeatureNotificationFromType(typeDef, options) {
55
+ return {
56
+ requiredFeature: options.requiredFeature,
57
+ ...buildBaseNotificationFields(typeDef, options)
58
+ };
59
+ }
60
+ export {
61
+ buildBatchNotificationFromType,
62
+ buildFeatureNotificationFromType,
63
+ buildNotificationFromType,
64
+ buildRoleNotificationFromType
65
+ };
66
+ //# sourceMappingURL=notificationBuilder.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/lib/notificationBuilder.ts"],
4
+ "sourcesContent": ["import type { NotificationTypeDefinition, NotificationTypeAction } from '@open-mercato/shared/modules/notifications/types'\nimport type { CreateNotificationInput, CreateBatchNotificationInput, CreateRoleNotificationInput, CreateFeatureNotificationInput } from '../data/validators'\n\n/**\n * Transform type definition actions to API input actions.\n * Type definitions use labelKey (i18n-first), while API input uses label with optional labelKey.\n */\nfunction mapActions(actions: NotificationTypeAction[] | undefined) {\n if (!actions || actions.length === 0) return undefined\n return actions.map((action) => ({\n id: action.id,\n label: action.labelKey,\n labelKey: action.labelKey,\n variant: action.variant,\n icon: action.icon,\n commandId: action.commandId,\n href: action.href,\n confirmRequired: action.confirmRequired,\n confirmMessage: action.confirmMessageKey,\n }))\n}\n\n/**\n * Common options for building notifications from type definitions\n */\ninterface CommonBuildOptions {\n titleVariables?: Record<string, string>\n bodyVariables?: Record<string, string>\n sourceEntityType?: string\n sourceEntityId?: string\n linkHref?: string\n groupKey?: string\n expiresAt?: string\n}\n\n/**\n * Build base notification fields from a type definition.\n * Shared logic used by all notification builder functions.\n */\nfunction buildBaseNotificationFields(\n typeDef: NotificationTypeDefinition,\n options: CommonBuildOptions\n) {\n return {\n type: typeDef.type,\n titleKey: typeDef.titleKey,\n bodyKey: typeDef.bodyKey,\n titleVariables: options.titleVariables,\n bodyVariables: options.bodyVariables,\n title: typeDef.titleKey,\n body: typeDef.bodyKey,\n icon: typeDef.icon,\n severity: typeDef.severity,\n actions: mapActions(typeDef.actions),\n primaryActionId: typeDef.primaryActionId,\n sourceModule: typeDef.module,\n sourceEntityType: options.sourceEntityType,\n sourceEntityId: options.sourceEntityId,\n linkHref: options.linkHref ?? typeDef.linkHref,\n groupKey: options.groupKey,\n expiresAt: options.expiresAt ?? (\n typeDef.expiresAfterHours\n ? new Date(Date.now() + typeDef.expiresAfterHours * 60 * 60 * 1000).toISOString()\n : undefined\n ),\n }\n}\n\n/**\n * Build a notification input from a type definition with i18n support.\n * This is the recommended way to create notifications - use type definitions from notifications.ts\n * to ensure i18n-first approach.\n */\nexport function buildNotificationFromType(\n typeDef: NotificationTypeDefinition,\n options: CommonBuildOptions & { recipientUserId: string }\n): CreateNotificationInput {\n return {\n recipientUserId: options.recipientUserId,\n ...buildBaseNotificationFields(typeDef, options),\n }\n}\n\n/**\n * Build a batch notification input from a type definition\n */\nexport function buildBatchNotificationFromType(\n typeDef: NotificationTypeDefinition,\n options: CommonBuildOptions & { recipientUserIds: string[] }\n): CreateBatchNotificationInput {\n return {\n recipientUserIds: options.recipientUserIds,\n ...buildBaseNotificationFields(typeDef, options),\n }\n}\n\n/**\n * Build a role notification input from a type definition\n */\nexport function buildRoleNotificationFromType(\n typeDef: NotificationTypeDefinition,\n options: CommonBuildOptions & { roleId: string }\n): CreateRoleNotificationInput {\n return {\n roleId: options.roleId,\n ...buildBaseNotificationFields(typeDef, options),\n }\n}\n\n/**\n * Build a feature-based notification input from a type definition\n */\nexport function buildFeatureNotificationFromType(\n typeDef: NotificationTypeDefinition,\n options: CommonBuildOptions & { requiredFeature: string }\n): CreateFeatureNotificationInput {\n return {\n requiredFeature: options.requiredFeature,\n ...buildBaseNotificationFields(typeDef, options),\n }\n}\n"],
5
+ "mappings": "AAOA,SAAS,WAAW,SAA+C;AACjE,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO;AAC7C,SAAO,QAAQ,IAAI,CAAC,YAAY;AAAA,IAC9B,IAAI,OAAO;AAAA,IACX,OAAO,OAAO;AAAA,IACd,UAAU,OAAO;AAAA,IACjB,SAAS,OAAO;AAAA,IAChB,MAAM,OAAO;AAAA,IACb,WAAW,OAAO;AAAA,IAClB,MAAM,OAAO;AAAA,IACb,iBAAiB,OAAO;AAAA,IACxB,gBAAgB,OAAO;AAAA,EACzB,EAAE;AACJ;AAmBA,SAAS,4BACP,SACA,SACA;AACA,SAAO;AAAA,IACL,MAAM,QAAQ;AAAA,IACd,UAAU,QAAQ;AAAA,IAClB,SAAS,QAAQ;AAAA,IACjB,gBAAgB,QAAQ;AAAA,IACxB,eAAe,QAAQ;AAAA,IACvB,OAAO,QAAQ;AAAA,IACf,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,IACd,UAAU,QAAQ;AAAA,IAClB,SAAS,WAAW,QAAQ,OAAO;AAAA,IACnC,iBAAiB,QAAQ;AAAA,IACzB,cAAc,QAAQ;AAAA,IACtB,kBAAkB,QAAQ;AAAA,IAC1B,gBAAgB,QAAQ;AAAA,IACxB,UAAU,QAAQ,YAAY,QAAQ;AAAA,IACtC,UAAU,QAAQ;AAAA,IAClB,WAAW,QAAQ,cACjB,QAAQ,oBACJ,IAAI,KAAK,KAAK,IAAI,IAAI,QAAQ,oBAAoB,KAAK,KAAK,GAAI,EAAE,YAAY,IAC9E;AAAA,EAER;AACF;AAOO,SAAS,0BACd,SACA,SACyB;AACzB,SAAO;AAAA,IACL,iBAAiB,QAAQ;AAAA,IACzB,GAAG,4BAA4B,SAAS,OAAO;AAAA,EACjD;AACF;AAKO,SAAS,+BACd,SACA,SAC8B;AAC9B,SAAO;AAAA,IACL,kBAAkB,QAAQ;AAAA,IAC1B,GAAG,4BAA4B,SAAS,OAAO;AAAA,EACjD;AACF;AAKO,SAAS,8BACd,SACA,SAC6B;AAC7B,SAAO;AAAA,IACL,QAAQ,QAAQ;AAAA,IAChB,GAAG,4BAA4B,SAAS,OAAO;AAAA,EACjD;AACF;AAKO,SAAS,iCACd,SACA,SACgC;AAChC,SAAO;AAAA,IACL,iBAAiB,QAAQ;AAAA,IACzB,GAAG,4BAA4B,SAAS,OAAO;AAAA,EACjD;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,54 @@
1
+ import { Notification } from "../data/entities.js";
2
+ import { NOTIFICATION_EVENTS } from "./events.js";
3
+ import { assertSafeNotificationHref, sanitizeNotificationActions } from "./safeHref.js";
4
+ function buildNotificationEntity(em, input, recipientUserId, ctx) {
5
+ const actions = sanitizeNotificationActions(input.actions);
6
+ const linkHref = assertSafeNotificationHref(input.linkHref);
7
+ return em.create(Notification, {
8
+ recipientUserId,
9
+ type: input.type,
10
+ // i18n-first: store keys and variables for translation at display time
11
+ titleKey: input.titleKey,
12
+ bodyKey: input.bodyKey,
13
+ titleVariables: input.titleVariables,
14
+ bodyVariables: input.bodyVariables,
15
+ // Fallback text (required for backward compatibility)
16
+ title: input.title || input.titleKey || "",
17
+ body: input.body,
18
+ icon: input.icon,
19
+ severity: input.severity ?? "info",
20
+ actionData: actions ? {
21
+ actions,
22
+ primaryActionId: input.primaryActionId
23
+ } : null,
24
+ sourceModule: input.sourceModule,
25
+ sourceEntityType: input.sourceEntityType,
26
+ sourceEntityId: input.sourceEntityId,
27
+ linkHref,
28
+ groupKey: input.groupKey,
29
+ expiresAt: input.expiresAt ? new Date(input.expiresAt) : null,
30
+ tenantId: ctx.tenantId,
31
+ organizationId: ctx.organizationId
32
+ });
33
+ }
34
+ async function emitNotificationCreated(eventBus, notification, ctx) {
35
+ await eventBus.emit(NOTIFICATION_EVENTS.CREATED, {
36
+ notificationId: notification.id,
37
+ recipientUserId: notification.recipientUserId,
38
+ type: notification.type,
39
+ title: notification.title,
40
+ tenantId: ctx.tenantId,
41
+ organizationId: ctx.organizationId
42
+ });
43
+ }
44
+ async function emitNotificationCreatedBatch(eventBus, notifications, ctx) {
45
+ for (const notification of notifications) {
46
+ await emitNotificationCreated(eventBus, notification, ctx);
47
+ }
48
+ }
49
+ export {
50
+ buildNotificationEntity,
51
+ emitNotificationCreated,
52
+ emitNotificationCreatedBatch
53
+ };
54
+ //# sourceMappingURL=notificationFactory.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/lib/notificationFactory.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport { Notification } from '../data/entities'\nimport type { CreateNotificationInput } from '../data/validators'\nimport { NOTIFICATION_EVENTS } from './events'\nimport { assertSafeNotificationHref, sanitizeNotificationActions } from './safeHref'\n\nexport type NotificationContentInput = Omit<CreateNotificationInput, 'recipientUserId'>\n\nexport type NotificationTenantContext = {\n tenantId: string\n organizationId?: string | null\n}\n\nexport function buildNotificationEntity(\n em: EntityManager,\n input: NotificationContentInput,\n recipientUserId: string,\n ctx: NotificationTenantContext\n): Notification {\n const actions = sanitizeNotificationActions(input.actions)\n const linkHref = assertSafeNotificationHref(input.linkHref)\n\n return em.create(Notification, {\n recipientUserId,\n type: input.type,\n // i18n-first: store keys and variables for translation at display time\n titleKey: input.titleKey,\n bodyKey: input.bodyKey,\n titleVariables: input.titleVariables,\n bodyVariables: input.bodyVariables,\n // Fallback text (required for backward compatibility)\n title: input.title || input.titleKey || '',\n body: input.body,\n icon: input.icon,\n severity: input.severity ?? 'info',\n actionData: actions\n ? {\n actions,\n primaryActionId: input.primaryActionId,\n }\n : null,\n sourceModule: input.sourceModule,\n sourceEntityType: input.sourceEntityType,\n sourceEntityId: input.sourceEntityId,\n linkHref,\n groupKey: input.groupKey,\n expiresAt: input.expiresAt ? new Date(input.expiresAt) : null,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n })\n}\n\nexport async function emitNotificationCreated(\n eventBus: { emit: (event: string, payload: unknown) => Promise<void> },\n notification: Notification,\n ctx: NotificationTenantContext\n): Promise<void> {\n await eventBus.emit(NOTIFICATION_EVENTS.CREATED, {\n notificationId: notification.id,\n recipientUserId: notification.recipientUserId,\n type: notification.type,\n title: notification.title,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n })\n}\n\nexport async function emitNotificationCreatedBatch(\n eventBus: { emit: (event: string, payload: unknown) => Promise<void> },\n notifications: Notification[],\n ctx: NotificationTenantContext\n): Promise<void> {\n for (const notification of notifications) {\n await emitNotificationCreated(eventBus, notification, ctx)\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAE7B,SAAS,2BAA2B;AACpC,SAAS,4BAA4B,mCAAmC;AASjE,SAAS,wBACd,IACA,OACA,iBACA,KACc;AACd,QAAM,UAAU,4BAA4B,MAAM,OAAO;AACzD,QAAM,WAAW,2BAA2B,MAAM,QAAQ;AAE1D,SAAO,GAAG,OAAO,cAAc;AAAA,IAC7B;AAAA,IACA,MAAM,MAAM;AAAA;AAAA,IAEZ,UAAU,MAAM;AAAA,IAChB,SAAS,MAAM;AAAA,IACf,gBAAgB,MAAM;AAAA,IACtB,eAAe,MAAM;AAAA;AAAA,IAErB,OAAO,MAAM,SAAS,MAAM,YAAY;AAAA,IACxC,MAAM,MAAM;AAAA,IACZ,MAAM,MAAM;AAAA,IACZ,UAAU,MAAM,YAAY;AAAA,IAC5B,YAAY,UACR;AAAA,MACE;AAAA,MACA,iBAAiB,MAAM;AAAA,IACzB,IACA;AAAA,IACJ,cAAc,MAAM;AAAA,IACpB,kBAAkB,MAAM;AAAA,IACxB,gBAAgB,MAAM;AAAA,IACtB;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,WAAW,MAAM,YAAY,IAAI,KAAK,MAAM,SAAS,IAAI;AAAA,IACzD,UAAU,IAAI;AAAA,IACd,gBAAgB,IAAI;AAAA,EACtB,CAAC;AACH;AAEA,eAAsB,wBACpB,UACA,cACA,KACe;AACf,QAAM,SAAS,KAAK,oBAAoB,SAAS;AAAA,IAC/C,gBAAgB,aAAa;AAAA,IAC7B,iBAAiB,aAAa;AAAA,IAC9B,MAAM,aAAa;AAAA,IACnB,OAAO,aAAa;AAAA,IACpB,UAAU,IAAI;AAAA,IACd,gBAAgB,IAAI;AAAA,EACtB,CAAC;AACH;AAEA,eAAsB,6BACpB,UACA,eACA,KACe;AACf,aAAW,gBAAgB,eAAe;AACxC,UAAM,wBAAwB,UAAU,cAAc,GAAG;AAAA,EAC3D;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,34 @@
1
+ function toNotificationDto(notification) {
2
+ return {
3
+ id: notification.id,
4
+ type: notification.type,
5
+ title: notification.title,
6
+ body: notification.body,
7
+ titleKey: notification.titleKey,
8
+ bodyKey: notification.bodyKey,
9
+ titleVariables: notification.titleVariables,
10
+ bodyVariables: notification.bodyVariables,
11
+ icon: notification.icon,
12
+ severity: notification.severity,
13
+ status: notification.status,
14
+ actions: notification.actionData?.actions?.map((action) => ({
15
+ id: action.id,
16
+ label: action.label,
17
+ labelKey: action.labelKey,
18
+ variant: action.variant,
19
+ icon: action.icon
20
+ })) ?? [],
21
+ primaryActionId: notification.actionData?.primaryActionId,
22
+ sourceModule: notification.sourceModule,
23
+ sourceEntityType: notification.sourceEntityType,
24
+ sourceEntityId: notification.sourceEntityId,
25
+ linkHref: notification.linkHref,
26
+ createdAt: notification.createdAt.toISOString(),
27
+ readAt: notification.readAt?.toISOString() ?? null,
28
+ actionTaken: notification.actionTaken
29
+ };
30
+ }
31
+ export {
32
+ toNotificationDto
33
+ };
34
+ //# sourceMappingURL=notificationMapper.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/lib/notificationMapper.ts"],
4
+ "sourcesContent": ["import type { NotificationDto } from '@open-mercato/shared/modules/notifications/types'\nimport { Notification } from '../data/entities'\n\nexport function toNotificationDto(notification: Notification): NotificationDto {\n return {\n id: notification.id,\n type: notification.type,\n title: notification.title,\n body: notification.body,\n titleKey: notification.titleKey,\n bodyKey: notification.bodyKey,\n titleVariables: notification.titleVariables,\n bodyVariables: notification.bodyVariables,\n icon: notification.icon,\n severity: notification.severity,\n status: notification.status,\n actions: notification.actionData?.actions?.map((action) => ({\n id: action.id,\n label: action.label,\n labelKey: action.labelKey,\n variant: action.variant,\n icon: action.icon,\n })) ?? [],\n primaryActionId: notification.actionData?.primaryActionId,\n sourceModule: notification.sourceModule,\n sourceEntityType: notification.sourceEntityType,\n sourceEntityId: notification.sourceEntityId,\n linkHref: notification.linkHref,\n createdAt: notification.createdAt.toISOString(),\n readAt: notification.readAt?.toISOString() ?? null,\n actionTaken: notification.actionTaken,\n }\n}\n"],
5
+ "mappings": "AAGO,SAAS,kBAAkB,cAA6C;AAC7E,SAAO;AAAA,IACL,IAAI,aAAa;AAAA,IACjB,MAAM,aAAa;AAAA,IACnB,OAAO,aAAa;AAAA,IACpB,MAAM,aAAa;AAAA,IACnB,UAAU,aAAa;AAAA,IACvB,SAAS,aAAa;AAAA,IACtB,gBAAgB,aAAa;AAAA,IAC7B,eAAe,aAAa;AAAA,IAC5B,MAAM,aAAa;AAAA,IACnB,UAAU,aAAa;AAAA,IACvB,QAAQ,aAAa;AAAA,IACrB,SAAS,aAAa,YAAY,SAAS,IAAI,CAAC,YAAY;AAAA,MAC1D,IAAI,OAAO;AAAA,MACX,OAAO,OAAO;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,SAAS,OAAO;AAAA,MAChB,MAAM,OAAO;AAAA,IACf,EAAE,KAAK,CAAC;AAAA,IACR,iBAAiB,aAAa,YAAY;AAAA,IAC1C,cAAc,aAAa;AAAA,IAC3B,kBAAkB,aAAa;AAAA,IAC/B,gBAAgB,aAAa;AAAA,IAC7B,UAAU,aAAa;AAAA,IACvB,WAAW,aAAa,UAAU,YAAY;AAAA,IAC9C,QAAQ,aAAa,QAAQ,YAAY,KAAK;AAAA,IAC9C,aAAa,aAAa;AAAA,EAC5B;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,35 @@
1
+ import { hasFeature } from "@open-mercato/shared/security/features";
2
+ function normalizeFeatures(features) {
3
+ if (!Array.isArray(features)) return void 0;
4
+ const normalized = features.filter((feature) => typeof feature === "string");
5
+ return normalized.length ? normalized : void 0;
6
+ }
7
+ function collectUsersWithFeature(userIdsSet, rows, requiredFeature) {
8
+ for (const row of rows) {
9
+ if (row.is_super_admin) {
10
+ userIdsSet.add(row.user_id);
11
+ continue;
12
+ }
13
+ const features = normalizeFeatures(row.features_json);
14
+ if (features && hasFeature(features, requiredFeature)) {
15
+ userIdsSet.add(row.user_id);
16
+ }
17
+ }
18
+ }
19
+ async function getRecipientUserIdsForRole(knex, tenantId, roleId) {
20
+ const userRoles = await knex("user_roles").join("users", "user_roles.user_id", "users.id").where("user_roles.role_id", roleId).whereNull("user_roles.deleted_at").whereNull("users.deleted_at").where("users.tenant_id", tenantId).select("users.id as user_id");
21
+ return userRoles.map((row) => row.user_id);
22
+ }
23
+ async function getRecipientUserIdsForFeature(knex, tenantId, requiredFeature) {
24
+ const userIdsSet = /* @__PURE__ */ new Set();
25
+ const userAcls = await knex("user_acls").join("users", "user_acls.user_id", "users.id").where("user_acls.tenant_id", tenantId).whereNull("user_acls.deleted_at").whereNull("users.deleted_at").where("users.tenant_id", tenantId).select("users.id as user_id", "user_acls.features_json", "user_acls.is_super_admin");
26
+ collectUsersWithFeature(userIdsSet, userAcls, requiredFeature);
27
+ const roleAcls = await knex("role_acls").join("user_roles", "role_acls.role_id", "user_roles.role_id").join("users", "user_roles.user_id", "users.id").where("role_acls.tenant_id", tenantId).whereNull("role_acls.deleted_at").whereNull("user_roles.deleted_at").whereNull("users.deleted_at").where("users.tenant_id", tenantId).select("users.id as user_id", "role_acls.features_json", "role_acls.is_super_admin");
28
+ collectUsersWithFeature(userIdsSet, roleAcls, requiredFeature);
29
+ return Array.from(userIdsSet);
30
+ }
31
+ export {
32
+ getRecipientUserIdsForFeature,
33
+ getRecipientUserIdsForRole
34
+ };
35
+ //# sourceMappingURL=notificationRecipients.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/lib/notificationRecipients.ts"],
4
+ "sourcesContent": ["import type { Knex } from 'knex'\nimport { hasFeature } from '@open-mercato/shared/security/features'\n\ninterface AclRow {\n user_id: string\n features_json: unknown\n is_super_admin: boolean\n}\n\nfunction normalizeFeatures(features: unknown): string[] | undefined {\n if (!Array.isArray(features)) return undefined\n const normalized = features.filter((feature): feature is string => typeof feature === 'string')\n return normalized.length ? normalized : undefined\n}\n\n/**\n * Extract user IDs from ACL rows that have the required feature or are super admins.\n */\nfunction collectUsersWithFeature(\n userIdsSet: Set<string>,\n rows: AclRow[],\n requiredFeature: string\n): void {\n for (const row of rows) {\n if (row.is_super_admin) {\n userIdsSet.add(row.user_id)\n continue\n }\n\n const features = normalizeFeatures(row.features_json)\n if (features && hasFeature(features, requiredFeature)) {\n userIdsSet.add(row.user_id)\n }\n }\n}\n\nexport async function getRecipientUserIdsForRole(\n knex: Knex,\n tenantId: string,\n roleId: string\n): Promise<string[]> {\n const userRoles = await knex('user_roles')\n .join('users', 'user_roles.user_id', 'users.id')\n .where('user_roles.role_id', roleId)\n .whereNull('user_roles.deleted_at')\n .whereNull('users.deleted_at')\n .where('users.tenant_id', tenantId)\n .select('users.id as user_id')\n\n return userRoles.map((row: { user_id: string }) => row.user_id)\n}\n\nexport async function getRecipientUserIdsForFeature(\n knex: Knex,\n tenantId: string,\n requiredFeature: string\n): Promise<string[]> {\n const userIdsSet = new Set<string>()\n\n const userAcls = await knex('user_acls')\n .join('users', 'user_acls.user_id', 'users.id')\n .where('user_acls.tenant_id', tenantId)\n .whereNull('user_acls.deleted_at')\n .whereNull('users.deleted_at')\n .where('users.tenant_id', tenantId)\n .select('users.id as user_id', 'user_acls.features_json', 'user_acls.is_super_admin')\n\n collectUsersWithFeature(userIdsSet, userAcls, requiredFeature)\n\n const roleAcls = await knex('role_acls')\n .join('user_roles', 'role_acls.role_id', 'user_roles.role_id')\n .join('users', 'user_roles.user_id', 'users.id')\n .where('role_acls.tenant_id', tenantId)\n .whereNull('role_acls.deleted_at')\n .whereNull('user_roles.deleted_at')\n .whereNull('users.deleted_at')\n .where('users.tenant_id', tenantId)\n .select('users.id as user_id', 'role_acls.features_json', 'role_acls.is_super_admin')\n\n collectUsersWithFeature(userIdsSet, roleAcls, requiredFeature)\n\n return Array.from(userIdsSet)\n}\n"],
5
+ "mappings": "AACA,SAAS,kBAAkB;AAQ3B,SAAS,kBAAkB,UAAyC;AAClE,MAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG,QAAO;AACrC,QAAM,aAAa,SAAS,OAAO,CAAC,YAA+B,OAAO,YAAY,QAAQ;AAC9F,SAAO,WAAW,SAAS,aAAa;AAC1C;AAKA,SAAS,wBACP,YACA,MACA,iBACM;AACN,aAAW,OAAO,MAAM;AACtB,QAAI,IAAI,gBAAgB;AACtB,iBAAW,IAAI,IAAI,OAAO;AAC1B;AAAA,IACF;AAEA,UAAM,WAAW,kBAAkB,IAAI,aAAa;AACpD,QAAI,YAAY,WAAW,UAAU,eAAe,GAAG;AACrD,iBAAW,IAAI,IAAI,OAAO;AAAA,IAC5B;AAAA,EACF;AACF;AAEA,eAAsB,2BACpB,MACA,UACA,QACmB;AACnB,QAAM,YAAY,MAAM,KAAK,YAAY,EACtC,KAAK,SAAS,sBAAsB,UAAU,EAC9C,MAAM,sBAAsB,MAAM,EAClC,UAAU,uBAAuB,EACjC,UAAU,kBAAkB,EAC5B,MAAM,mBAAmB,QAAQ,EACjC,OAAO,qBAAqB;AAE/B,SAAO,UAAU,IAAI,CAAC,QAA6B,IAAI,OAAO;AAChE;AAEA,eAAsB,8BACpB,MACA,UACA,iBACmB;AACnB,QAAM,aAAa,oBAAI,IAAY;AAEnC,QAAM,WAAW,MAAM,KAAK,WAAW,EACpC,KAAK,SAAS,qBAAqB,UAAU,EAC7C,MAAM,uBAAuB,QAAQ,EACrC,UAAU,sBAAsB,EAChC,UAAU,kBAAkB,EAC5B,MAAM,mBAAmB,QAAQ,EACjC,OAAO,uBAAuB,2BAA2B,0BAA0B;AAEtF,0BAAwB,YAAY,UAAU,eAAe;AAE7D,QAAM,WAAW,MAAM,KAAK,WAAW,EACpC,KAAK,cAAc,qBAAqB,oBAAoB,EAC5D,KAAK,SAAS,sBAAsB,UAAU,EAC9C,MAAM,uBAAuB,QAAQ,EACrC,UAAU,sBAAsB,EAChC,UAAU,uBAAuB,EACjC,UAAU,kBAAkB,EAC5B,MAAM,mBAAmB,QAAQ,EACjC,OAAO,uBAAuB,2BAA2B,0BAA0B;AAEtF,0BAAwB,YAAY,UAAU,eAAe;AAE7D,SAAO,MAAM,KAAK,UAAU;AAC9B;",
6
+ "names": []
7
+ }