@open-mercato/core 0.4.2-canary-49d47ff90e → 0.4.2-canary-02d8ce2991

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 (51) hide show
  1. package/dist/modules/auth/backend/auth/profile/page.js.map +1 -1
  2. package/dist/modules/auth/backend/roles/[id]/edit/page.js +4 -1
  3. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  4. package/dist/modules/auth/backend/users/[id]/edit/page.js +4 -1
  5. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  6. package/dist/modules/auth/cli.js +13 -12
  7. package/dist/modules/auth/cli.js.map +2 -2
  8. package/dist/modules/business_rules/api/execute/route.js +7 -1
  9. package/dist/modules/business_rules/api/execute/route.js.map +2 -2
  10. package/dist/modules/business_rules/lib/rule-engine.js +33 -3
  11. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  12. package/dist/modules/dashboards/cli.js +12 -4
  13. package/dist/modules/dashboards/cli.js.map +2 -2
  14. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +16 -11
  15. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
  16. package/dist/modules/dashboards/services/widgetDataService.js +46 -2
  17. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  18. package/dist/modules/notifications/data/validators.js +5 -1
  19. package/dist/modules/notifications/data/validators.js.map +2 -2
  20. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +2 -1
  21. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
  22. package/dist/modules/notifications/lib/deliveryConfig.js +4 -2
  23. package/dist/modules/notifications/lib/deliveryConfig.js.map +2 -2
  24. package/dist/modules/notifications/lib/deliveryStrategies.js +14 -0
  25. package/dist/modules/notifications/lib/deliveryStrategies.js.map +7 -0
  26. package/dist/modules/notifications/subscribers/deliver-notification.js +33 -7
  27. package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
  28. package/dist/modules/workflows/lib/transition-handler.js +14 -6
  29. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  30. package/package.json +2 -2
  31. package/src/modules/auth/README.md +1 -1
  32. package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
  33. package/src/modules/auth/backend/auth/profile/page.tsx +2 -2
  34. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +4 -1
  35. package/src/modules/auth/backend/users/[id]/edit/page.tsx +4 -1
  36. package/src/modules/auth/cli.ts +25 -12
  37. package/src/modules/business_rules/api/execute/route.ts +8 -1
  38. package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +51 -0
  39. package/src/modules/business_rules/lib/rule-engine.ts +57 -3
  40. package/src/modules/dashboards/cli.ts +14 -4
  41. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +22 -11
  42. package/src/modules/dashboards/services/widgetDataService.ts +52 -2
  43. package/src/modules/notifications/__tests__/deliver-notification.test.ts +195 -0
  44. package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +19 -0
  45. package/src/modules/notifications/__tests__/notificationService.test.ts +208 -0
  46. package/src/modules/notifications/data/validators.ts +5 -0
  47. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +2 -0
  48. package/src/modules/notifications/lib/deliveryConfig.ts +8 -0
  49. package/src/modules/notifications/lib/deliveryStrategies.ts +50 -0
  50. package/src/modules/notifications/subscribers/deliver-notification.ts +39 -10
  51. package/src/modules/workflows/lib/transition-handler.ts +18 -6
@@ -2,6 +2,7 @@ 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'
5
6
  import { sendEmail } from '@open-mercato/shared/lib/email/send'
6
7
  import NotificationEmail from '../emails/NotificationEmail'
7
8
  import { loadDictionary } from '@open-mercato/shared/lib/i18n/server'
@@ -100,7 +101,6 @@ export default async function handle(payload: NotificationCreatedPayload, ctx: R
100
101
  const deliveryConfig = await resolveNotificationDeliveryConfig(ctx, { defaultValue: DEFAULT_NOTIFICATION_DELIVERY_CONFIG })
101
102
  if (!deliveryConfig.strategies.email.enabled) {
102
103
  debug('email delivery disabled')
103
- return
104
104
  }
105
105
 
106
106
  const em = ctx.resolve('em') as EntityManager
@@ -121,7 +121,7 @@ export default async function handle(payload: NotificationCreatedPayload, ctx: R
121
121
  encryptionService = null
122
122
  }
123
123
 
124
- const recipient = await resolveRecipient(em, notification, encryptionService)
124
+ const recipient = (await resolveRecipient(em, notification, encryptionService)) ?? { email: null, name: null }
125
125
  if (!recipient?.email) {
126
126
  debug('recipient has no email', notification.recipientUserId)
127
127
  }
@@ -129,17 +129,18 @@ export default async function handle(payload: NotificationCreatedPayload, ctx: R
129
129
  const panelUrl = resolveNotificationPanelUrl(deliveryConfig)
130
130
  if (!panelUrl) {
131
131
  debug('missing panelUrl; check appUrl/panelPath settings')
132
- return
133
132
  }
134
133
 
135
- const panelLink = buildPanelLink(panelUrl, notification.id)
136
- const actionLinks = (notification.actionData?.actions ?? []).map((action) => ({
137
- id: action.id,
138
- label: action.labelKey ? t(action.labelKey, action.label) : action.label,
139
- href: panelLink,
140
- }))
134
+ const panelLink = panelUrl ? buildPanelLink(panelUrl, notification.id) : null
135
+ const actionLinks = panelLink
136
+ ? (notification.actionData?.actions ?? []).map((action) => ({
137
+ id: action.id,
138
+ label: action.labelKey ? t(action.labelKey, action.label) : action.label,
139
+ href: panelLink,
140
+ }))
141
+ : []
141
142
 
142
- if (deliveryConfig.strategies.email.enabled && recipient?.email) {
143
+ if (deliveryConfig.strategies.email.enabled && recipient?.email && panelLink) {
143
144
  const subjectPrefix = deliveryConfig.strategies.email.subjectPrefix?.trim()
144
145
  const subject = subjectPrefix ? `${subjectPrefix} ${title}` : title
145
146
  const copy = {
@@ -171,5 +172,33 @@ export default async function handle(payload: NotificationCreatedPayload, ctx: R
171
172
  }
172
173
  }
173
174
 
175
+ const strategyConfigs = deliveryConfig.strategies.custom ?? {}
176
+ const strategies = getNotificationDeliveryStrategies()
177
+ for (const strategy of strategies) {
178
+ const strategyConfig = strategyConfigs[strategy.id]
179
+ const enabled = strategyConfig?.enabled ?? strategy.defaultEnabled ?? false
180
+ if (!enabled) {
181
+ debug('custom delivery disabled', strategy.id)
182
+ continue
183
+ }
184
+ try {
185
+ await strategy.deliver({
186
+ notification,
187
+ recipient,
188
+ title,
189
+ body,
190
+ panelUrl,
191
+ panelLink,
192
+ actionLinks,
193
+ deliveryConfig,
194
+ config: strategyConfig ?? {},
195
+ resolve: ctx.resolve,
196
+ t,
197
+ })
198
+ } catch (error) {
199
+ console.error(`[notifications] delivery strategy failed (${strategy.id})`, error)
200
+ }
201
+ }
202
+
174
203
  return
175
204
  }
@@ -12,6 +12,7 @@
12
12
 
13
13
  import { EntityManager } from '@mikro-orm/core'
14
14
  import type { AwilixContainer } from 'awilix'
15
+ import type { EventBus } from '@open-mercato/events'
15
16
  import {
16
17
  WorkflowInstance,
17
18
  WorkflowDefinition,
@@ -271,6 +272,13 @@ export async function executeTransition(
271
272
  context: TransitionExecutionContext
272
273
  ): Promise<TransitionExecutionResult> {
273
274
  try {
275
+ let eventBus: Pick<EventBus, 'emitEvent'> | null = null
276
+ try {
277
+ eventBus = container.resolve('eventBus') as EventBus
278
+ } catch {
279
+ eventBus = null
280
+ }
281
+
274
282
  // First, evaluate if transition is valid
275
283
  const evaluation = await evaluateTransition(
276
284
  em,
@@ -294,7 +302,8 @@ export async function executeTransition(
294
302
  em,
295
303
  instance,
296
304
  transition,
297
- context
305
+ context,
306
+ eventBus
298
307
  )
299
308
 
300
309
  if (!preConditionsResult.allowed) {
@@ -505,7 +514,8 @@ export async function executeTransition(
505
514
  em,
506
515
  instance,
507
516
  transition,
508
- context
517
+ context,
518
+ eventBus
509
519
  )
510
520
 
511
521
  if (!postConditionsResult.allowed) {
@@ -659,7 +669,8 @@ async function evaluatePreConditions(
659
669
  em: EntityManager,
660
670
  instance: WorkflowInstance,
661
671
  transition: any,
662
- context: TransitionExecutionContext
672
+ context: TransitionExecutionContext,
673
+ eventBus: Pick<EventBus, 'emitEvent'> | null
663
674
  ): Promise<ruleEngine.RuleEngineResult> {
664
675
  try {
665
676
  // Load workflow definition to get workflow ID
@@ -698,7 +709,7 @@ async function evaluatePreConditions(
698
709
  }
699
710
 
700
711
  // Execute rules - only GUARD rules will affect the 'allowed' status
701
- const result = await ruleEngine.executeRules(em, ruleContext)
712
+ const result = await ruleEngine.executeRules(em, ruleContext, { eventBus })
702
713
 
703
714
  return result
704
715
  } catch (error) {
@@ -728,7 +739,8 @@ async function evaluatePostConditions(
728
739
  em: EntityManager,
729
740
  instance: WorkflowInstance,
730
741
  transition: any,
731
- context: TransitionExecutionContext
742
+ context: TransitionExecutionContext,
743
+ eventBus: Pick<EventBus, 'emitEvent'> | null
732
744
  ): Promise<ruleEngine.RuleEngineResult> {
733
745
  try {
734
746
  // Load workflow definition to get workflow ID
@@ -767,7 +779,7 @@ async function evaluatePostConditions(
767
779
  }
768
780
 
769
781
  // Execute rules
770
- const result = await ruleEngine.executeRules(em, ruleContext)
782
+ const result = await ruleEngine.executeRules(em, ruleContext, { eventBus })
771
783
 
772
784
  return result
773
785
  } catch (error) {