@open-mercato/core 0.4.2-canary-ab36fbd8f2 → 0.4.2-canary-da2b080494
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/dist/generated/entities.ids.generated.js +1 -5
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +0 -2
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/api_docs/frontend/docs/api/page.js +2 -3
- package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/page.js +1 -1
- package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentLibrary.js +0 -4
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js +0 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
- package/dist/modules/auth/api/admin/nav.js +3 -4
- package/dist/modules/auth/api/admin/nav.js.map +2 -2
- package/dist/modules/auth/api/reset/confirm.js +2 -25
- package/dist/modules/auth/api/reset/confirm.js.map +2 -2
- package/dist/modules/auth/api/reset.js +0 -23
- package/dist/modules/auth/api/reset.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +9 -14
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +2 -4
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +1 -4
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/roles/page.js +3 -3
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +3 -18
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +2 -15
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +3 -3
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/auth/cli.js +11 -25
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/commands/users.js +2 -59
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/validators.js +2 -4
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/frontend/reset/[token]/page.js +10 -20
- package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +0 -1
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/services/authService.js +3 -3
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/business_rules/api/execute/route.js +1 -7
- package/dist/modules/business_rules/api/execute/route.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/page.js +0 -4
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +0 -3
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +3 -33
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/catalog/components/PriceKindSettings.js +0 -2
- package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +0 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/configs/cli.js +0 -6
- package/dist/modules/configs/cli.js.map +2 -2
- package/dist/modules/configs/components/CachePanel.js +4 -4
- package/dist/modules/configs/components/CachePanel.js.map +2 -2
- package/dist/modules/configs/lib/system-status.js +1 -48
- package/dist/modules/configs/lib/system-status.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +0 -18
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +0 -3
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +0 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +0 -3
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +0 -3
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +0 -3
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +0 -31
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/components/CustomerTodosTable.js +0 -1
- package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
- package/dist/modules/dashboards/cli.js +5 -44
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +11 -16
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
- package/dist/modules/dashboards/services/widgetDataService.js +3 -132
- package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryTable.js +0 -2
- package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/components/SystemEntitiesTable.js +1 -1
- package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
- package/dist/modules/entities/components/UserEntitiesTable.js +2 -2
- package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +3 -3
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/OverridesTable.js +1 -1
- package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +1 -7
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +0 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +0 -2
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +0 -53
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +0 -26
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/ShippingMethodsSettings.js +2 -2
- package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/TaxRatesSettings.js +2 -2
- package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +0 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +0 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +1 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +0 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/page.js +1 -1
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/commands/leave-requests.js +0 -79
- package/dist/modules/staff/commands/leave-requests.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/page.js +0 -5
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.js +0 -3
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +0 -3
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +6 -14
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/generated/entities.ids.generated.ts +1 -5
- package/generated/entity-fields-registry.ts +0 -2
- package/package.json +2 -2
- package/src/modules/api_docs/frontend/docs/api/page.tsx +2 -3
- package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
- package/src/modules/attachments/components/AttachmentLibrary.tsx +0 -4
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +0 -2
- package/src/modules/auth/README.md +1 -1
- package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
- package/src/modules/auth/api/admin/nav.ts +6 -10
- package/src/modules/auth/api/reset/confirm.ts +2 -25
- package/src/modules/auth/api/reset.ts +0 -23
- package/src/modules/auth/api/sidebar/preferences/route.ts +12 -21
- package/src/modules/auth/api/users/route.ts +2 -5
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +1 -4
- package/src/modules/auth/backend/roles/page.tsx +3 -3
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +3 -22
- package/src/modules/auth/backend/users/create/page.tsx +2 -19
- package/src/modules/auth/backend/users/page.tsx +3 -3
- package/src/modules/auth/cli.ts +11 -38
- package/src/modules/auth/commands/users.ts +2 -73
- package/src/modules/auth/data/validators.ts +2 -5
- package/src/modules/auth/frontend/reset/[token]/page.tsx +11 -24
- package/src/modules/auth/i18n/de.json +1 -43
- package/src/modules/auth/i18n/en.json +1 -43
- package/src/modules/auth/i18n/es.json +1 -43
- package/src/modules/auth/i18n/pl.json +1 -43
- package/src/modules/auth/lib/setup-app.ts +0 -1
- package/src/modules/auth/services/authService.ts +4 -4
- package/src/modules/business_rules/api/execute/route.ts +1 -8
- package/src/modules/business_rules/backend/rules/page.tsx +0 -4
- package/src/modules/business_rules/backend/sets/page.tsx +0 -3
- package/src/modules/business_rules/i18n/en.json +1 -3
- package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +0 -51
- package/src/modules/business_rules/lib/rule-engine.ts +3 -57
- package/src/modules/catalog/components/PriceKindSettings.tsx +0 -2
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +0 -2
- package/src/modules/catalog/i18n/en.json +1 -3
- package/src/modules/configs/cli.ts +0 -6
- package/src/modules/configs/components/CachePanel.tsx +4 -4
- package/src/modules/configs/i18n/en.json +2 -12
- package/src/modules/configs/i18n/pl.json +2 -12
- package/src/modules/configs/lib/system-status.ts +1 -48
- package/src/modules/configs/lib/system-status.types.ts +0 -1
- package/src/modules/configs/lib/upgrade-actions.ts +0 -18
- package/src/modules/currencies/backend/currencies/page.tsx +0 -3
- package/src/modules/currencies/backend/exchange-rates/page.tsx +0 -2
- package/src/modules/customers/backend/customers/companies/page.tsx +0 -3
- package/src/modules/customers/backend/customers/deals/page.tsx +0 -3
- package/src/modules/customers/backend/customers/people/page.tsx +0 -3
- package/src/modules/customers/commands/deals.ts +0 -39
- package/src/modules/customers/components/CustomerTodosTable.tsx +0 -1
- package/src/modules/customers/i18n/en.json +1 -5
- package/src/modules/dashboards/cli.ts +5 -55
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +11 -22
- package/src/modules/dashboards/services/widgetDataService.ts +4 -157
- package/src/modules/dictionaries/components/DictionaryTable.tsx +0 -2
- package/src/modules/directory/backend/directory/organizations/page.tsx +2 -2
- package/src/modules/directory/backend/directory/tenants/page.tsx +2 -2
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -2
- package/src/modules/entities/components/SystemEntitiesTable.tsx +1 -1
- package/src/modules/entities/components/UserEntitiesTable.tsx +2 -2
- package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +4 -3
- package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
- package/src/modules/query_index/components/QueryIndexesTable.tsx +2 -8
- package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -2
- package/src/modules/resources/backend/resources/resources/page.tsx +2 -2
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +0 -2
- package/src/modules/sales/backend/sales/channels/page.tsx +0 -2
- package/src/modules/sales/commands/documents.ts +0 -65
- package/src/modules/sales/commands/payments.ts +0 -33
- package/src/modules/sales/components/AdjustmentKindSettings.tsx +2 -2
- package/src/modules/sales/components/PaymentMethodsSettings.tsx +2 -2
- package/src/modules/sales/components/ShippingMethodsSettings.tsx +2 -2
- package/src/modules/sales/components/TaxRatesSettings.tsx +2 -2
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +0 -2
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +0 -2
- package/src/modules/sales/components/documents/PaymentsSection.tsx +1 -2
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +0 -2
- package/src/modules/sales/i18n/de.json +0 -20
- package/src/modules/sales/i18n/en.json +1 -25
- package/src/modules/sales/i18n/es.json +0 -20
- package/src/modules/sales/i18n/pl.json +0 -20
- package/src/modules/staff/backend/staff/team-members/page.tsx +1 -1
- package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -2
- package/src/modules/staff/backend/staff/teams/page.tsx +2 -2
- package/src/modules/staff/commands/leave-requests.ts +0 -94
- package/src/modules/staff/i18n/de.json +0 -4
- package/src/modules/staff/i18n/en.json +1 -9
- package/src/modules/staff/i18n/es.json +0 -4
- package/src/modules/staff/i18n/pl.json +0 -4
- package/src/modules/workflows/backend/definitions/page.tsx +0 -5
- package/src/modules/workflows/backend/instances/page.tsx +1 -4
- package/src/modules/workflows/backend/tasks/page.tsx +1 -4
- package/src/modules/workflows/i18n/en.json +1 -3
- package/src/modules/workflows/lib/transition-handler.ts +6 -18
- package/dist/generated/entities/notification/index.js +0 -57
- package/dist/generated/entities/notification/index.js.map +0 -7
- package/dist/modules/auth/api/profile/route.js +0 -157
- package/dist/modules/auth/api/profile/route.js.map +0 -7
- package/dist/modules/auth/backend/auth/profile/page.js +0 -141
- package/dist/modules/auth/backend/auth/profile/page.js.map +0 -7
- package/dist/modules/auth/backend/auth/profile/page.meta.js +0 -13
- package/dist/modules/auth/backend/auth/profile/page.meta.js.map +0 -7
- package/dist/modules/auth/notifications.js +0 -112
- package/dist/modules/auth/notifications.js.map +0 -7
- package/dist/modules/business_rules/notifications.js +0 -28
- package/dist/modules/business_rules/notifications.js.map +0 -7
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +0 -37
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +0 -7
- package/dist/modules/catalog/notifications.js +0 -28
- package/dist/modules/catalog/notifications.js.map +0 -7
- package/dist/modules/catalog/subscribers/low-stock-notification.js +0 -38
- package/dist/modules/catalog/subscribers/low-stock-notification.js.map +0 -7
- package/dist/modules/customers/notifications.js +0 -48
- package/dist/modules/customers/notifications.js.map +0 -7
- package/dist/modules/dashboards/lib/role-widgets.js +0 -58
- package/dist/modules/dashboards/lib/role-widgets.js.map +0 -7
- package/dist/modules/notifications/acl.js +0 -11
- package/dist/modules/notifications/acl.js.map +0 -7
- package/dist/modules/notifications/api/[id]/action/route.js +0 -74
- package/dist/modules/notifications/api/[id]/action/route.js.map +0 -7
- package/dist/modules/notifications/api/[id]/dismiss/route.js +0 -15
- package/dist/modules/notifications/api/[id]/dismiss/route.js.map +0 -7
- package/dist/modules/notifications/api/[id]/read/route.js +0 -15
- package/dist/modules/notifications/api/[id]/read/route.js.map +0 -7
- package/dist/modules/notifications/api/[id]/restore/route.js +0 -53
- package/dist/modules/notifications/api/[id]/restore/route.js.map +0 -7
- package/dist/modules/notifications/api/batch/route.js +0 -17
- package/dist/modules/notifications/api/batch/route.js.map +0 -7
- package/dist/modules/notifications/api/feature/route.js +0 -17
- package/dist/modules/notifications/api/feature/route.js.map +0 -7
- package/dist/modules/notifications/api/mark-all-read/route.js +0 -35
- package/dist/modules/notifications/api/mark-all-read/route.js.map +0 -7
- package/dist/modules/notifications/api/openapi.js +0 -76
- package/dist/modules/notifications/api/openapi.js.map +0 -7
- package/dist/modules/notifications/api/role/route.js +0 -17
- package/dist/modules/notifications/api/role/route.js.map +0 -7
- package/dist/modules/notifications/api/route.js +0 -85
- package/dist/modules/notifications/api/route.js.map +0 -7
- package/dist/modules/notifications/api/settings/route.js +0 -155
- package/dist/modules/notifications/api/settings/route.js.map +0 -7
- package/dist/modules/notifications/api/unread-count/route.js +0 -38
- package/dist/modules/notifications/api/unread-count/route.js.map +0 -7
- package/dist/modules/notifications/backend/config/notifications/page.js +0 -10
- package/dist/modules/notifications/backend/config/notifications/page.js.map +0 -7
- package/dist/modules/notifications/backend/config/notifications/page.meta.js +0 -24
- package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +0 -7
- package/dist/modules/notifications/cli.js +0 -16
- package/dist/modules/notifications/cli.js.map +0 -7
- package/dist/modules/notifications/data/entities.js +0 -112
- package/dist/modules/notifications/data/entities.js.map +0 -7
- package/dist/modules/notifications/data/validators.js +0 -98
- package/dist/modules/notifications/data/validators.js.map +0 -7
- package/dist/modules/notifications/di.js +0 -13
- package/dist/modules/notifications/di.js.map +0 -7
- package/dist/modules/notifications/emails/NotificationEmail.js +0 -58
- package/dist/modules/notifications/emails/NotificationEmail.js.map +0 -7
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +0 -44
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +0 -7
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +0 -220
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +0 -7
- package/dist/modules/notifications/index.js +0 -14
- package/dist/modules/notifications/index.js.map +0 -7
- package/dist/modules/notifications/lib/deliveryConfig.js +0 -107
- package/dist/modules/notifications/lib/deliveryConfig.js.map +0 -7
- package/dist/modules/notifications/lib/deliveryStrategies.js +0 -14
- package/dist/modules/notifications/lib/deliveryStrategies.js.map +0 -7
- package/dist/modules/notifications/lib/events.js +0 -12
- package/dist/modules/notifications/lib/events.js.map +0 -7
- package/dist/modules/notifications/lib/notificationBuilder.js +0 -66
- package/dist/modules/notifications/lib/notificationBuilder.js.map +0 -7
- package/dist/modules/notifications/lib/notificationFactory.js +0 -54
- package/dist/modules/notifications/lib/notificationFactory.js.map +0 -7
- package/dist/modules/notifications/lib/notificationMapper.js +0 -34
- package/dist/modules/notifications/lib/notificationMapper.js.map +0 -7
- package/dist/modules/notifications/lib/notificationRecipients.js +0 -35
- package/dist/modules/notifications/lib/notificationRecipients.js.map +0 -7
- package/dist/modules/notifications/lib/notificationService.js +0 -279
- package/dist/modules/notifications/lib/notificationService.js.map +0 -7
- package/dist/modules/notifications/lib/routeHelpers.js +0 -101
- package/dist/modules/notifications/lib/routeHelpers.js.map +0 -7
- package/dist/modules/notifications/lib/safeHref.js +0 -24
- package/dist/modules/notifications/lib/safeHref.js.map +0 -7
- package/dist/modules/notifications/migrations/Migration20260123000001.js +0 -70
- package/dist/modules/notifications/migrations/Migration20260123000001.js.map +0 -7
- package/dist/modules/notifications/migrations/Migration20260126150000.js +0 -37
- package/dist/modules/notifications/migrations/Migration20260126150000.js.map +0 -7
- package/dist/modules/notifications/subscribers/deliver-notification.js +0 -165
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +0 -7
- package/dist/modules/notifications/workers/create-notification.worker.js +0 -70
- package/dist/modules/notifications/workers/create-notification.worker.js.map +0 -7
- package/dist/modules/sales/notifications.client.js +0 -51
- package/dist/modules/sales/notifications.client.js.map +0 -7
- package/dist/modules/sales/notifications.js +0 -88
- package/dist/modules/sales/notifications.js.map +0 -7
- package/dist/modules/sales/subscribers/quote-expiring-notification.js +0 -38
- package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +0 -137
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +0 -137
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/index.js +0 -7
- package/dist/modules/sales/widgets/notifications/index.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +0 -60
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +0 -7
- package/dist/modules/staff/notifications.js +0 -75
- package/dist/modules/staff/notifications.js.map +0 -7
- package/dist/modules/workflows/notifications.js +0 -28
- package/dist/modules/workflows/notifications.js.map +0 -7
- package/dist/modules/workflows/subscribers/task-assigned-notification.js +0 -38
- package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +0 -7
- package/generated/entities/notification/index.ts +0 -27
- package/src/modules/auth/api/profile/route.ts +0 -163
- package/src/modules/auth/backend/auth/profile/page.meta.ts +0 -9
- package/src/modules/auth/backend/auth/profile/page.tsx +0 -174
- package/src/modules/auth/notifications.ts +0 -109
- package/src/modules/business_rules/notifications.ts +0 -25
- package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +0 -50
- package/src/modules/catalog/notifications.ts +0 -25
- package/src/modules/catalog/subscribers/low-stock-notification.ts +0 -52
- package/src/modules/customers/notifications.ts +0 -44
- package/src/modules/dashboards/lib/role-widgets.ts +0 -80
- package/src/modules/notifications/__tests__/deliver-notification.test.ts +0 -195
- package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +0 -19
- package/src/modules/notifications/__tests__/notificationService.test.ts +0 -208
- package/src/modules/notifications/acl.ts +0 -7
- package/src/modules/notifications/api/[id]/action/route.ts +0 -75
- package/src/modules/notifications/api/[id]/dismiss/route.ts +0 -12
- package/src/modules/notifications/api/[id]/read/route.ts +0 -12
- package/src/modules/notifications/api/[id]/restore/route.ts +0 -53
- package/src/modules/notifications/api/batch/route.ts +0 -14
- package/src/modules/notifications/api/feature/route.ts +0 -14
- package/src/modules/notifications/api/mark-all-read/route.ts +0 -34
- package/src/modules/notifications/api/openapi.ts +0 -76
- package/src/modules/notifications/api/role/route.ts +0 -14
- package/src/modules/notifications/api/route.ts +0 -92
- package/src/modules/notifications/api/settings/route.ts +0 -157
- package/src/modules/notifications/api/unread-count/route.ts +0 -38
- package/src/modules/notifications/backend/config/notifications/page.meta.ts +0 -22
- package/src/modules/notifications/backend/config/notifications/page.tsx +0 -12
- package/src/modules/notifications/cli.ts +0 -18
- package/src/modules/notifications/data/entities.ts +0 -99
- package/src/modules/notifications/data/validators.ts +0 -115
- package/src/modules/notifications/di.ts +0 -11
- package/src/modules/notifications/emails/NotificationEmail.tsx +0 -98
- package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +0 -42
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +0 -233
- package/src/modules/notifications/i18n/de.json +0 -50
- package/src/modules/notifications/i18n/en.json +0 -50
- package/src/modules/notifications/i18n/es.json +0 -50
- package/src/modules/notifications/i18n/pl.json +0 -50
- package/src/modules/notifications/index.ts +0 -12
- package/src/modules/notifications/lib/deliveryConfig.ts +0 -153
- package/src/modules/notifications/lib/deliveryStrategies.ts +0 -50
- package/src/modules/notifications/lib/events.ts +0 -48
- package/src/modules/notifications/lib/notificationBuilder.ts +0 -121
- package/src/modules/notifications/lib/notificationFactory.ts +0 -76
- package/src/modules/notifications/lib/notificationMapper.ts +0 -33
- package/src/modules/notifications/lib/notificationRecipients.ts +0 -83
- package/src/modules/notifications/lib/notificationService.ts +0 -414
- package/src/modules/notifications/lib/routeHelpers.ts +0 -151
- package/src/modules/notifications/lib/safeHref.ts +0 -29
- package/src/modules/notifications/migrations/.snapshot-open-mercato.json +0 -300
- package/src/modules/notifications/migrations/Migration20260123000001.ts +0 -73
- package/src/modules/notifications/migrations/Migration20260126150000.ts +0 -39
- package/src/modules/notifications/subscribers/deliver-notification.ts +0 -204
- package/src/modules/notifications/workers/create-notification.worker.ts +0 -122
- package/src/modules/sales/notifications.client.ts +0 -65
- package/src/modules/sales/notifications.ts +0 -82
- package/src/modules/sales/subscribers/quote-expiring-notification.ts +0 -53
- package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +0 -156
- package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +0 -156
- package/src/modules/sales/widgets/notifications/index.ts +0 -2
- package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +0 -81
- package/src/modules/staff/notifications.ts +0 -71
- package/src/modules/workflows/notifications.ts +0 -25
- package/src/modules/workflows/subscribers/task-assigned-notification.ts +0 -53
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/auth/services/authService.ts"],
|
|
4
|
-
"sourcesContent": ["import { EntityManager } from '@mikro-orm/postgresql'\nimport { compare, hash } from 'bcryptjs'\nimport { User, Role, UserRole, Session, PasswordReset } from '@open-mercato/core/modules/auth/data/entities'\nimport crypto from 'node:crypto'\nimport { computeEmailHash } from '@open-mercato/core/modules/auth/lib/emailHash'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nexport class AuthService {\n constructor(private em: EntityManager) {}\n\n async findUserByEmail(email: string) {\n const emailHash = computeEmailHash(email)\n return this.em.findOne(User, {\n $or: [\n { email },\n { emailHash },\n ],\n } as any)\n }\n\n async verifyPassword(user: User, password: string) {\n if (!user.passwordHash) return false\n return compare(password, user.passwordHash)\n }\n\n async updateLastLoginAt(user: User) {\n const now = new Date()\n // Use native update to avoid flushing unrelated entities that might be pending in this EM\n await this.em.nativeUpdate(User, { id: user.id }, { lastLoginAt: now })\n user.lastLoginAt = now\n }\n\n async getUserRoles(user: User, tenantId?: string | null): Promise<string[]> {\n const resolvedTenantId = tenantId ?? user.tenantId ?? null\n if (!resolvedTenantId) return []\n const links = await findWithDecryption(\n this.em,\n UserRole,\n { user, role: { tenantId: resolvedTenantId } as any },\n { populate: ['role'] },\n { tenantId: resolvedTenantId, organizationId: user.organizationId ?? null },\n )\n return links.map((l) => l.role.name)\n }\n\n\n async createSession(user: User, expiresAt: Date): Promise<Session> {\n const token = crypto.randomBytes(32).toString('hex')\n const sess = this.em.create(Session as any, { user, token, expiresAt, createdAt: new Date() } as any)\n await this.em.persistAndFlush(sess)\n return sess as Session\n }\n\n async deleteSessionByToken(token: string) {\n await this.em.nativeDelete(Session, { token })\n }\n\n async refreshFromSessionToken(token: string) {\n const now = new Date()\n const sess = await this.em.findOne(Session, { token })\n if (!sess || sess.expiresAt <= now) return null\n const user = await this.em.findOne(User, { id: sess.user.id })\n if (!user) return null\n const roles = await this.getUserRoles(user, user.tenantId ?? null)\n return { user, roles }\n }\n\n async requestPasswordReset(email: string) {\n const user = await this.findUserByEmail(email)\n if (!user) return null\n const token = crypto.randomBytes(32).toString('hex')\n const expiresAt = new Date(Date.now() + 60 * 60 * 1000)\n const row = this.em.create(PasswordReset as any, { user, token, expiresAt, createdAt: new Date() } as any)\n await this.em.persistAndFlush(row)\n return { user, token }\n }\n\n async confirmPasswordReset(token: string, newPassword: string)
|
|
5
|
-
"mappings": "AACA,SAAS,SAAS,YAAY;AAC9B,SAAS,MAAY,UAAU,SAAS,qBAAqB;AAC7D,OAAO,YAAY;AACnB,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAE5B,MAAM,YAAY;AAAA,EACvB,YAAoB,IAAmB;AAAnB;AAAA,EAAoB;AAAA,EAExC,MAAM,gBAAgB,OAAe;AACnC,UAAM,YAAY,iBAAiB,KAAK;AACxC,WAAO,KAAK,GAAG,QAAQ,MAAM;AAAA,MAC3B,KAAK;AAAA,QACH,EAAE,MAAM;AAAA,QACR,EAAE,UAAU;AAAA,MACd;AAAA,IACF,CAAQ;AAAA,EACV;AAAA,EAEA,MAAM,eAAe,MAAY,UAAkB;AACjD,QAAI,CAAC,KAAK,aAAc,QAAO;AAC/B,WAAO,QAAQ,UAAU,KAAK,YAAY;AAAA,EAC5C;AAAA,EAEA,MAAM,kBAAkB,MAAY;AAClC,UAAM,MAAM,oBAAI,KAAK;AAErB,UAAM,KAAK,GAAG,aAAa,MAAM,EAAE,IAAI,KAAK,GAAG,GAAG,EAAE,aAAa,IAAI,CAAC;AACtE,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,MAAM,aAAa,MAAY,UAA6C;AAC1E,UAAM,mBAAmB,YAAY,KAAK,YAAY;AACtD,QAAI,CAAC,iBAAkB,QAAO,CAAC;AAC/B,UAAM,QAAQ,MAAM;AAAA,MAClB,KAAK;AAAA,MACL;AAAA,MACA,EAAE,MAAM,MAAM,EAAE,UAAU,iBAAiB,EAAS;AAAA,MACpD,EAAE,UAAU,CAAC,MAAM,EAAE;AAAA,MACrB,EAAE,UAAU,kBAAkB,gBAAgB,KAAK,kBAAkB,KAAK;AAAA,IAC5E;AACA,WAAO,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI;AAAA,EACrC;AAAA,EAGA,MAAM,cAAc,MAAY,WAAmC;AACjE,UAAM,QAAQ,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AACnD,UAAM,OAAO,KAAK,GAAG,OAAO,SAAgB,EAAE,MAAM,OAAO,WAAW,WAAW,oBAAI,KAAK,EAAE,CAAQ;AACpG,UAAM,KAAK,GAAG,gBAAgB,IAAI;AAClC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,qBAAqB,OAAe;AACxC,UAAM,KAAK,GAAG,aAAa,SAAS,EAAE,MAAM,CAAC;AAAA,EAC/C;AAAA,EAEA,MAAM,wBAAwB,OAAe;AAC3C,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,OAAO,MAAM,KAAK,GAAG,QAAQ,SAAS,EAAE,MAAM,CAAC;AACrD,QAAI,CAAC,QAAQ,KAAK,aAAa,IAAK,QAAO;AAC3C,UAAM,OAAO,MAAM,KAAK,GAAG,QAAQ,MAAM,EAAE,IAAI,KAAK,KAAK,GAAG,CAAC;AAC7D,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,QAAQ,MAAM,KAAK,aAAa,MAAM,KAAK,YAAY,IAAI;AACjE,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB;AAAA,EAEA,MAAM,qBAAqB,OAAe;AACxC,UAAM,OAAO,MAAM,KAAK,gBAAgB,KAAK;AAC7C,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,QAAQ,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AACnD,UAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,GAAI;AACtD,UAAM,MAAM,KAAK,GAAG,OAAO,eAAsB,EAAE,MAAM,OAAO,WAAW,WAAW,oBAAI,KAAK,EAAE,CAAQ;AACzG,UAAM,KAAK,GAAG,gBAAgB,GAAG;AACjC,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB;AAAA,EAEA,MAAM,qBAAqB,OAAe,
|
|
4
|
+
"sourcesContent": ["import { EntityManager } from '@mikro-orm/postgresql'\nimport { compare, hash } from 'bcryptjs'\nimport { User, Role, UserRole, Session, PasswordReset } from '@open-mercato/core/modules/auth/data/entities'\nimport crypto from 'node:crypto'\nimport { computeEmailHash } from '@open-mercato/core/modules/auth/lib/emailHash'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nexport class AuthService {\n constructor(private em: EntityManager) {}\n\n async findUserByEmail(email: string) {\n const emailHash = computeEmailHash(email)\n return this.em.findOne(User, {\n $or: [\n { email },\n { emailHash },\n ],\n } as any)\n }\n\n async verifyPassword(user: User, password: string) {\n if (!user.passwordHash) return false\n return compare(password, user.passwordHash)\n }\n\n async updateLastLoginAt(user: User) {\n const now = new Date()\n // Use native update to avoid flushing unrelated entities that might be pending in this EM\n await this.em.nativeUpdate(User, { id: user.id }, { lastLoginAt: now })\n user.lastLoginAt = now\n }\n\n async getUserRoles(user: User, tenantId?: string | null): Promise<string[]> {\n const resolvedTenantId = tenantId ?? user.tenantId ?? null\n if (!resolvedTenantId) return []\n const links = await findWithDecryption(\n this.em,\n UserRole,\n { user, role: { tenantId: resolvedTenantId } as any },\n { populate: ['role'] },\n { tenantId: resolvedTenantId, organizationId: user.organizationId ?? null },\n )\n return links.map((l) => l.role.name)\n }\n\n\n async createSession(user: User, expiresAt: Date): Promise<Session> {\n const token = crypto.randomBytes(32).toString('hex')\n const sess = this.em.create(Session as any, { user, token, expiresAt, createdAt: new Date() } as any)\n await this.em.persistAndFlush(sess)\n return sess as Session\n }\n\n async deleteSessionByToken(token: string) {\n await this.em.nativeDelete(Session, { token })\n }\n\n async refreshFromSessionToken(token: string) {\n const now = new Date()\n const sess = await this.em.findOne(Session, { token })\n if (!sess || sess.expiresAt <= now) return null\n const user = await this.em.findOne(User, { id: sess.user.id })\n if (!user) return null\n const roles = await this.getUserRoles(user, user.tenantId ?? null)\n return { user, roles }\n }\n\n async requestPasswordReset(email: string) {\n const user = await this.findUserByEmail(email)\n if (!user) return null\n const token = crypto.randomBytes(32).toString('hex')\n const expiresAt = new Date(Date.now() + 60 * 60 * 1000)\n const row = this.em.create(PasswordReset as any, { user, token, expiresAt, createdAt: new Date() } as any)\n await this.em.persistAndFlush(row)\n return { user, token }\n }\n\n async confirmPasswordReset(token: string, newPassword: string) {\n const now = new Date()\n const row = await this.em.findOne(PasswordReset, { token })\n if (!row || (row.usedAt && row.usedAt <= now) || row.expiresAt <= now) return false\n const user = await this.em.findOne(User, { id: row.user.id })\n if (!user) return false\n user.passwordHash = await hash(newPassword, 10)\n row.usedAt = new Date()\n await this.em.flush()\n return true\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -37,12 +37,6 @@ async function POST(req) {
|
|
|
37
37
|
}
|
|
38
38
|
const container = await createRequestContainer();
|
|
39
39
|
const em = container.resolve("em");
|
|
40
|
-
let eventBus = null;
|
|
41
|
-
try {
|
|
42
|
-
eventBus = container.resolve("eventBus");
|
|
43
|
-
} catch {
|
|
44
|
-
eventBus = null;
|
|
45
|
-
}
|
|
46
40
|
let body;
|
|
47
41
|
try {
|
|
48
42
|
body = await req.json();
|
|
@@ -76,7 +70,7 @@ async function POST(req) {
|
|
|
76
70
|
return NextResponse.json({ error: `Invalid execution context: ${errors.join(", ")}` }, { status: 400 });
|
|
77
71
|
}
|
|
78
72
|
try {
|
|
79
|
-
const result = await ruleEngine.executeRules(em, context
|
|
73
|
+
const result = await ruleEngine.executeRules(em, context);
|
|
80
74
|
const response = {
|
|
81
75
|
allowed: result.allowed,
|
|
82
76
|
executedRules: result.executedRules.map((r) => ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/business_rules/api/execute/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { ruleEngineContextSchema } from '../../data/validators'\nimport * as ruleEngine from '../../lib/rule-engine'\n\nconst executeRequestSchema = z.object({\n entityType: z.string().min(1, 'entityType is required'),\n entityId: z.string().optional(),\n eventType: z.string().optional(),\n data: z.any(),\n dryRun: z.boolean().optional().default(false),\n})\n\nconst executeResponseSchema = z.object({\n allowed: z.boolean(),\n executedRules: z.array(z.object({\n ruleId: z.string(),\n ruleName: z.string(),\n conditionResult: z.boolean(),\n executionTime: z.number(),\n error: z.string().optional(),\n })),\n totalExecutionTime: z.number(),\n errors: z.array(z.string()).optional(),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['business_rules.execute'] },\n}\n\nexport const metadata = routeMetadata\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const parsed = executeRequestSchema.safeParse(body)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const { entityType, entityId, eventType, data, dryRun } = parsed.data\n\n const context: ruleEngine.RuleEngineContext = {\n entityType,\n entityId,\n eventType,\n data,\n user: {\n id: auth.sub,\n email: auth.email,\n role: (auth.role as string) ?? undefined,\n },\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? '',\n executedBy: auth.sub ?? auth.email ?? null,\n dryRun,\n }\n\n const validation = ruleEngineContextSchema.safeParse(context)\n if (!validation.success) {\n const errors = validation.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Invalid execution context: ${errors.join(', ')}` }, { status: 400 })\n }\n\n try {\n const result = await ruleEngine.executeRules(em, context)\n\n const response = {\n allowed: result.allowed,\n executedRules: result.executedRules.map(r => ({\n ruleId: r.rule.ruleId,\n ruleName: r.rule.ruleName,\n ruleType: r.rule.ruleType,\n conditionResult: r.conditionResult,\n actionsExecuted: r.actionsExecuted ? {\n success: r.actionsExecuted.success,\n results: r.actionsExecuted.results.map(ar => ({\n type: ar.action.type,\n success: ar.success,\n error: ar.error,\n })),\n } : null,\n executionTime: r.executionTime,\n error: r.error,\n logId: r.logId,\n })),\n totalExecutionTime: result.totalExecutionTime,\n errors: result.errors,\n logIds: result.logIds,\n }\n\n return NextResponse.json(response, { status: 200 })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return NextResponse.json(\n { error: `Rule execution failed: ${errorMessage}` },\n { status: 500 }\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Business Rules',\n summary: 'Execute business rules',\n methods: {\n POST: {\n summary: 'Execute rules for given context',\n description: 'Manually executes applicable business rules for the specified entity type, event, and data. Supports dry-run mode to test rules without executing actions.',\n requestBody: {\n contentType: 'application/json',\n schema: executeRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Rules executed successfully',\n schema: executeResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid request payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 500, description: 'Execution error', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -203,24 +203,20 @@ function RulesListPage() {
|
|
|
203
203
|
{
|
|
204
204
|
items: [
|
|
205
205
|
{
|
|
206
|
-
id: "edit",
|
|
207
206
|
label: t("common.edit"),
|
|
208
207
|
href: `/backend/rules/${row.original.id}`
|
|
209
208
|
},
|
|
210
209
|
{
|
|
211
|
-
id: row.original.enabled ? "disable" : "enable",
|
|
212
210
|
label: row.original.enabled ? t("common.disable") : t("common.enable"),
|
|
213
211
|
onSelect: () => handleToggleEnabled(row.original.id, row.original.enabled)
|
|
214
212
|
},
|
|
215
213
|
{
|
|
216
|
-
id: "duplicate",
|
|
217
214
|
label: t("common.duplicate"),
|
|
218
215
|
onSelect: () => {
|
|
219
216
|
flash(t("business_rules.messages.duplicateNotYetImplemented"), "info");
|
|
220
217
|
}
|
|
221
218
|
},
|
|
222
219
|
{
|
|
223
|
-
id: "delete",
|
|
224
220
|
label: t("common.delete"),
|
|
225
221
|
onSelect: () => handleDelete(row.original.id, row.original.ruleName),
|
|
226
222
|
destructive: true
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/business_rules/backend/rules/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\n\ntype Rule = {\n id: string\n ruleId: string\n ruleName: string\n description: string | null\n ruleType: 'GUARD' | 'VALIDATION' | 'CALCULATION' | 'ACTION' | 'ASSIGNMENT'\n ruleCategory: string | null\n entityType: string\n eventType: string | null\n enabled: boolean\n priority: number\n version: number\n effectiveFrom: string | null\n effectiveTo: string | null\n tenantId: string\n organizationId: string\n createdAt: string\n updatedAt: string\n}\n\ntype RulesResponse = {\n items: Rule[]\n total: number\n totalPages: number\n}\n\nexport default function RulesListPage() {\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(20)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const t = useT()\n const router = useRouter()\n const queryClient = useQueryClient()\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n\n const { data, isLoading, error } = useQuery({\n queryKey: ['business-rules', 'list', filterValues, page],\n queryFn: async () => {\n const params = new URLSearchParams()\n params.set('page', page.toString())\n params.set('pageSize', pageSize.toString())\n params.set('sortField', 'priority')\n params.set('sortDir', 'desc')\n\n if (filterValues.enabled) params.set('enabled', filterValues.enabled as string)\n if (filterValues.ruleType) params.set('ruleType', filterValues.ruleType as string)\n if (filterValues.entityType) params.set('entityType', filterValues.entityType as string)\n if (filterValues.eventType) params.set('eventType', filterValues.eventType as string)\n if (filterValues.search) params.set('search', filterValues.search as string)\n\n const result = await apiCall<RulesResponse>(\n `/api/business_rules/rules?${params.toString()}`\n )\n\n if (!result.ok) {\n throw new Error('Failed to fetch rules')\n }\n\n const response = result.result\n if (response) {\n setTotal(response.total || 0)\n setTotalPages(response.totalPages || 1)\n }\n\n return response?.items || []\n },\n })\n\n const handleDelete = async (id: string, ruleName: string) => {\n if (!confirm(t('business_rules.confirm.delete', { name: ruleName }))) {\n return\n }\n\n const result = await apiCall(`/api/business_rules/rules?id=${id}`, {\n method: 'DELETE',\n })\n\n if (result.ok) {\n flash(t('business_rules.messages.deleted'), 'success')\n queryClient.invalidateQueries({ queryKey: ['business-rules'] })\n } else {\n flash(t('business_rules.messages.deleteFailed'), 'error')\n }\n }\n\n const handleToggleEnabled = async (id: string, currentEnabled: boolean) => {\n const result = await apiCall('/api/business_rules/rules', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id,\n enabled: !currentEnabled,\n }),\n })\n\n if (result.ok) {\n flash(t('business_rules.messages.updated'), 'success')\n queryClient.invalidateQueries({ queryKey: ['business-rules'] })\n } else {\n flash(t('business_rules.messages.updateFailed'), 'error')\n }\n }\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n Object.entries(values).forEach(([key, value]) => {\n if (value !== undefined) next[key] = value\n })\n setFilterValues(next)\n setPage(1)\n }, [setFilterValues, setPage])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [setFilterValues, setPage])\n\n const filters: FilterDef[] = [\n {\n id: 'search',\n type: 'text',\n label: t('business_rules.filters.search'),\n placeholder: t('business_rules.filters.searchPlaceholder'),\n },\n {\n id: 'enabled',\n type: 'select',\n label: t('business_rules.filters.status'),\n options: [\n { label: t('common.all'), value: '' },\n { label: t('common.enabled'), value: 'true' },\n { label: t('common.disabled'), value: 'false' },\n ],\n },\n {\n id: 'ruleType',\n type: 'select',\n label: t('business_rules.filters.type'),\n options: [\n { label: t('common.all'), value: '' },\n { label: t('business_rules.types.guard'), value: 'GUARD' },\n { label: t('business_rules.types.validation'), value: 'VALIDATION' },\n { label: t('business_rules.types.calculation'), value: 'CALCULATION' },\n { label: t('business_rules.types.action'), value: 'ACTION' },\n { label: t('business_rules.types.assignment'), value: 'ASSIGNMENT' },\n ],\n },\n {\n id: 'entityType',\n type: 'text',\n label: t('business_rules.filters.entityType'),\n placeholder: t('business_rules.filters.entityTypePlaceholder'),\n },\n {\n id: 'eventType',\n type: 'text',\n label: t('business_rules.filters.eventType'),\n placeholder: t('business_rules.filters.eventTypePlaceholder'),\n },\n ]\n\n const columns: ColumnDef<Rule>[] = [\n {\n id: 'ruleId',\n header: t('business_rules.fields.ruleId'),\n accessorKey: 'ruleId',\n cell: ({ row }) => (\n <span className=\"font-mono text-sm\">{row.original.ruleId}</span>\n ),\n },\n {\n id: 'ruleName',\n header: t('business_rules.fields.ruleName'),\n accessorKey: 'ruleName',\n cell: ({ row }) => (\n <div>\n <div className=\"font-medium\">{row.original.ruleName}</div>\n {row.original.description && (\n <div className=\"text-xs text-muted-foreground line-clamp-1\">\n {row.original.description}\n </div>\n )}\n </div>\n ),\n },\n {\n id: 'ruleType',\n header: t('business_rules.fields.ruleType'),\n accessorKey: 'ruleType',\n cell: ({ row }) => {\n const typeColors = {\n GUARD: 'bg-red-100 text-red-800',\n VALIDATION: 'bg-yellow-100 text-yellow-800',\n CALCULATION: 'bg-blue-100 text-blue-800',\n ACTION: 'bg-green-100 text-green-800',\n ASSIGNMENT: 'bg-purple-100 text-purple-800',\n }\n const color = typeColors[row.original.ruleType] || 'bg-muted text-foreground'\n return (\n <span className={`inline-flex items-center px-2 py-1 rounded text-xs font-medium ${color}`}>\n {row.original.ruleType}\n </span>\n )\n },\n },\n {\n id: 'entityType',\n header: t('business_rules.fields.entityType'),\n accessorKey: 'entityType',\n },\n {\n id: 'eventType',\n header: t('business_rules.fields.eventType'),\n accessorKey: 'eventType',\n cell: ({ row }) => row.original.eventType || '-',\n },\n {\n id: 'enabled',\n header: t('business_rules.fields.enabled'),\n accessorKey: 'enabled',\n cell: ({ row }) => (\n <button\n onClick={() => handleToggleEnabled(row.original.id, row.original.enabled)}\n className={`inline-flex items-center px-2 py-1 rounded text-xs font-medium cursor-pointer ${\n row.original.enabled\n ? 'bg-green-100 text-green-800 hover:bg-green-200 dark:bg-green-900 dark:text-green-300 dark:hover:bg-green-800'\n : 'bg-muted text-muted-foreground hover:bg-muted/80'\n }`}\n title={t('business_rules.actions.toggleEnabled')}\n >\n {row.original.enabled ? t('common.yes') : t('common.no')}\n </button>\n ),\n },\n {\n id: 'priority',\n header: t('business_rules.fields.priority'),\n accessorKey: 'priority',\n cell: ({ row }) => (\n <span className=\"font-mono text-sm\">{row.original.priority}</span>\n ),\n },\n {\n id: 'actions',\n header: '',\n cell: ({ row }) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('common.edit'),\n href: `/backend/rules/${row.original.id}`,\n },\n {\n id: row.original.enabled ? 'disable' : 'enable',\n label: row.original.enabled ? t('common.disable') : t('common.enable'),\n onSelect: () => handleToggleEnabled(row.original.id, row.original.enabled),\n },\n {\n id: 'duplicate',\n label: t('common.duplicate'),\n onSelect: () => {\n // TODO: Implement duplicate functionality in Step 5.2\n flash(t('business_rules.messages.duplicateNotYetImplemented'), 'info')\n },\n },\n {\n id: 'delete',\n label: t('common.delete'),\n onSelect: () => handleDelete(row.original.id, row.original.ruleName),\n destructive: true,\n },\n ]}\n />\n ),\n },\n ]\n\n if (error) {\n return (\n <Page>\n <PageBody>\n <div className=\"p-8 text-center\">\n <p className=\"text-red-600\">{t('business_rules.messages.loadFailed')}</p>\n <Button onClick={() => queryClient.invalidateQueries({ queryKey: ['business-rules'] })} className=\"mt-4\">\n {t('common.retry')}\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('business_rules.list.title')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/rules/create\">\n {t('business_rules.actions.create')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={data || []}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n perspective={{\n tableId: 'business-rules.rules.list',\n }}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\n\ntype Rule = {\n id: string\n ruleId: string\n ruleName: string\n description: string | null\n ruleType: 'GUARD' | 'VALIDATION' | 'CALCULATION' | 'ACTION' | 'ASSIGNMENT'\n ruleCategory: string | null\n entityType: string\n eventType: string | null\n enabled: boolean\n priority: number\n version: number\n effectiveFrom: string | null\n effectiveTo: string | null\n tenantId: string\n organizationId: string\n createdAt: string\n updatedAt: string\n}\n\ntype RulesResponse = {\n items: Rule[]\n total: number\n totalPages: number\n}\n\nexport default function RulesListPage() {\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(20)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const t = useT()\n const router = useRouter()\n const queryClient = useQueryClient()\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n\n const { data, isLoading, error } = useQuery({\n queryKey: ['business-rules', 'list', filterValues, page],\n queryFn: async () => {\n const params = new URLSearchParams()\n params.set('page', page.toString())\n params.set('pageSize', pageSize.toString())\n params.set('sortField', 'priority')\n params.set('sortDir', 'desc')\n\n if (filterValues.enabled) params.set('enabled', filterValues.enabled as string)\n if (filterValues.ruleType) params.set('ruleType', filterValues.ruleType as string)\n if (filterValues.entityType) params.set('entityType', filterValues.entityType as string)\n if (filterValues.eventType) params.set('eventType', filterValues.eventType as string)\n if (filterValues.search) params.set('search', filterValues.search as string)\n\n const result = await apiCall<RulesResponse>(\n `/api/business_rules/rules?${params.toString()}`\n )\n\n if (!result.ok) {\n throw new Error('Failed to fetch rules')\n }\n\n const response = result.result\n if (response) {\n setTotal(response.total || 0)\n setTotalPages(response.totalPages || 1)\n }\n\n return response?.items || []\n },\n })\n\n const handleDelete = async (id: string, ruleName: string) => {\n if (!confirm(t('business_rules.confirm.delete', { name: ruleName }))) {\n return\n }\n\n const result = await apiCall(`/api/business_rules/rules?id=${id}`, {\n method: 'DELETE',\n })\n\n if (result.ok) {\n flash(t('business_rules.messages.deleted'), 'success')\n queryClient.invalidateQueries({ queryKey: ['business-rules'] })\n } else {\n flash(t('business_rules.messages.deleteFailed'), 'error')\n }\n }\n\n const handleToggleEnabled = async (id: string, currentEnabled: boolean) => {\n const result = await apiCall('/api/business_rules/rules', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id,\n enabled: !currentEnabled,\n }),\n })\n\n if (result.ok) {\n flash(t('business_rules.messages.updated'), 'success')\n queryClient.invalidateQueries({ queryKey: ['business-rules'] })\n } else {\n flash(t('business_rules.messages.updateFailed'), 'error')\n }\n }\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n Object.entries(values).forEach(([key, value]) => {\n if (value !== undefined) next[key] = value\n })\n setFilterValues(next)\n setPage(1)\n }, [setFilterValues, setPage])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [setFilterValues, setPage])\n\n const filters: FilterDef[] = [\n {\n id: 'search',\n type: 'text',\n label: t('business_rules.filters.search'),\n placeholder: t('business_rules.filters.searchPlaceholder'),\n },\n {\n id: 'enabled',\n type: 'select',\n label: t('business_rules.filters.status'),\n options: [\n { label: t('common.all'), value: '' },\n { label: t('common.enabled'), value: 'true' },\n { label: t('common.disabled'), value: 'false' },\n ],\n },\n {\n id: 'ruleType',\n type: 'select',\n label: t('business_rules.filters.type'),\n options: [\n { label: t('common.all'), value: '' },\n { label: t('business_rules.types.guard'), value: 'GUARD' },\n { label: t('business_rules.types.validation'), value: 'VALIDATION' },\n { label: t('business_rules.types.calculation'), value: 'CALCULATION' },\n { label: t('business_rules.types.action'), value: 'ACTION' },\n { label: t('business_rules.types.assignment'), value: 'ASSIGNMENT' },\n ],\n },\n {\n id: 'entityType',\n type: 'text',\n label: t('business_rules.filters.entityType'),\n placeholder: t('business_rules.filters.entityTypePlaceholder'),\n },\n {\n id: 'eventType',\n type: 'text',\n label: t('business_rules.filters.eventType'),\n placeholder: t('business_rules.filters.eventTypePlaceholder'),\n },\n ]\n\n const columns: ColumnDef<Rule>[] = [\n {\n id: 'ruleId',\n header: t('business_rules.fields.ruleId'),\n accessorKey: 'ruleId',\n cell: ({ row }) => (\n <span className=\"font-mono text-sm\">{row.original.ruleId}</span>\n ),\n },\n {\n id: 'ruleName',\n header: t('business_rules.fields.ruleName'),\n accessorKey: 'ruleName',\n cell: ({ row }) => (\n <div>\n <div className=\"font-medium\">{row.original.ruleName}</div>\n {row.original.description && (\n <div className=\"text-xs text-muted-foreground line-clamp-1\">\n {row.original.description}\n </div>\n )}\n </div>\n ),\n },\n {\n id: 'ruleType',\n header: t('business_rules.fields.ruleType'),\n accessorKey: 'ruleType',\n cell: ({ row }) => {\n const typeColors = {\n GUARD: 'bg-red-100 text-red-800',\n VALIDATION: 'bg-yellow-100 text-yellow-800',\n CALCULATION: 'bg-blue-100 text-blue-800',\n ACTION: 'bg-green-100 text-green-800',\n ASSIGNMENT: 'bg-purple-100 text-purple-800',\n }\n const color = typeColors[row.original.ruleType] || 'bg-muted text-foreground'\n return (\n <span className={`inline-flex items-center px-2 py-1 rounded text-xs font-medium ${color}`}>\n {row.original.ruleType}\n </span>\n )\n },\n },\n {\n id: 'entityType',\n header: t('business_rules.fields.entityType'),\n accessorKey: 'entityType',\n },\n {\n id: 'eventType',\n header: t('business_rules.fields.eventType'),\n accessorKey: 'eventType',\n cell: ({ row }) => row.original.eventType || '-',\n },\n {\n id: 'enabled',\n header: t('business_rules.fields.enabled'),\n accessorKey: 'enabled',\n cell: ({ row }) => (\n <button\n onClick={() => handleToggleEnabled(row.original.id, row.original.enabled)}\n className={`inline-flex items-center px-2 py-1 rounded text-xs font-medium cursor-pointer ${\n row.original.enabled\n ? 'bg-green-100 text-green-800 hover:bg-green-200 dark:bg-green-900 dark:text-green-300 dark:hover:bg-green-800'\n : 'bg-muted text-muted-foreground hover:bg-muted/80'\n }`}\n title={t('business_rules.actions.toggleEnabled')}\n >\n {row.original.enabled ? t('common.yes') : t('common.no')}\n </button>\n ),\n },\n {\n id: 'priority',\n header: t('business_rules.fields.priority'),\n accessorKey: 'priority',\n cell: ({ row }) => (\n <span className=\"font-mono text-sm\">{row.original.priority}</span>\n ),\n },\n {\n id: 'actions',\n header: '',\n cell: ({ row }) => (\n <RowActions\n items={[\n {\n label: t('common.edit'),\n href: `/backend/rules/${row.original.id}`,\n },\n {\n label: row.original.enabled ? t('common.disable') : t('common.enable'),\n onSelect: () => handleToggleEnabled(row.original.id, row.original.enabled),\n },\n {\n label: t('common.duplicate'),\n onSelect: () => {\n // TODO: Implement duplicate functionality in Step 5.2\n flash(t('business_rules.messages.duplicateNotYetImplemented'), 'info')\n },\n },\n {\n label: t('common.delete'),\n onSelect: () => handleDelete(row.original.id, row.original.ruleName),\n destructive: true,\n },\n ]}\n />\n ),\n },\n ]\n\n if (error) {\n return (\n <Page>\n <PageBody>\n <div className=\"p-8 text-center\">\n <p className=\"text-red-600\">{t('business_rules.messages.loadFailed')}</p>\n <Button onClick={() => queryClient.invalidateQueries({ queryKey: ['business-rules'] })} className=\"mt-4\">\n {t('common.retry')}\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('business_rules.list.title')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/rules/create\">\n {t('business_rules.actions.create')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={data || []}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n perspective={{\n tableId: 'business-rules.rules.list',\n }}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -149,17 +149,14 @@ function RuleSetsListPage() {
|
|
|
149
149
|
{
|
|
150
150
|
items: [
|
|
151
151
|
{
|
|
152
|
-
id: "edit",
|
|
153
152
|
label: t("common.edit"),
|
|
154
153
|
href: `/backend/sets/${row.original.id}`
|
|
155
154
|
},
|
|
156
155
|
{
|
|
157
|
-
id: row.original.enabled ? "disable" : "enable",
|
|
158
156
|
label: row.original.enabled ? t("common.disable") : t("common.enable"),
|
|
159
157
|
onSelect: () => handleToggleEnabled(row.original.id, row.original.enabled)
|
|
160
158
|
},
|
|
161
159
|
{
|
|
162
|
-
id: "delete",
|
|
163
160
|
label: t("common.delete"),
|
|
164
161
|
onSelect: () => handleDelete(row.original.id, row.original.setName),
|
|
165
162
|
destructive: true
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/business_rules/backend/sets/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\n\ntype RuleSet = {\n id: string\n setId: string\n setName: string\n description: string | null\n enabled: boolean\n tenantId: string\n organizationId: string\n createdBy: string | null\n updatedBy: string | null\n createdAt: string\n updatedAt: string\n}\n\ntype RuleSetsResponse = {\n items: RuleSet[]\n total: number\n totalPages: number\n}\n\nexport default function RuleSetsListPage() {\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(20)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const t = useT()\n const router = useRouter()\n const queryClient = useQueryClient()\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n\n const { data, isLoading, error } = useQuery({\n queryKey: ['business-rules', 'sets', filterValues, page],\n queryFn: async () => {\n const params = new URLSearchParams()\n params.set('page', page.toString())\n params.set('pageSize', pageSize.toString())\n params.set('sortField', 'setName')\n params.set('sortDir', 'asc')\n\n if (filterValues.enabled) params.set('enabled', filterValues.enabled as string)\n if (filterValues.search) params.set('search', filterValues.search as string)\n\n const result = await apiCall<RuleSetsResponse>(\n `/api/business_rules/sets?${params.toString()}`\n )\n\n if (!result.ok) {\n throw new Error('Failed to fetch rule sets')\n }\n\n const response = result.result\n if (response) {\n setTotal(response.total || 0)\n setTotalPages(response.totalPages || 1)\n }\n\n return response?.items || []\n },\n })\n\n const handleDelete = async (id: string, setName: string) => {\n if (!confirm(t('business_rules.sets.confirm.delete', { name: setName }))) {\n return\n }\n\n const result = await apiCall(`/api/business_rules/sets?id=${id}`, {\n method: 'DELETE',\n })\n\n if (result.ok) {\n flash(t('business_rules.sets.messages.deleted'), 'success')\n queryClient.invalidateQueries({ queryKey: ['business-rules', 'sets'] })\n } else {\n flash(t('business_rules.sets.messages.deleteFailed'), 'error')\n }\n }\n\n const handleToggleEnabled = async (id: string, currentEnabled: boolean) => {\n const result = await apiCall('/api/business_rules/sets', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id,\n enabled: !currentEnabled,\n }),\n })\n\n if (result.ok) {\n flash(t('business_rules.sets.messages.updated'), 'success')\n queryClient.invalidateQueries({ queryKey: ['business-rules', 'sets'] })\n } else {\n flash(t('business_rules.sets.messages.updateFailed'), 'error')\n }\n }\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n Object.entries(values).forEach(([key, value]) => {\n if (value !== undefined) next[key] = value\n })\n setFilterValues(next)\n setPage(1)\n }, [setFilterValues, setPage])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [setFilterValues, setPage])\n\n const filters: FilterDef[] = [\n {\n id: 'search',\n type: 'text',\n label: t('business_rules.filters.search'),\n placeholder: t('business_rules.sets.filters.searchPlaceholder'),\n },\n {\n id: 'enabled',\n type: 'select',\n label: t('business_rules.filters.status'),\n options: [\n { value: '', label: t('common.all') },\n { value: 'true', label: t('common.enabled') },\n { value: 'false', label: t('common.disabled') },\n ],\n },\n ]\n\n const columns: ColumnDef<RuleSet>[] = [\n {\n id: 'setId',\n header: t('business_rules.sets.fields.setId'),\n accessorKey: 'setId',\n cell: ({ row }) => (\n <Link\n href={`/backend/sets/${row.original.id}`}\n className=\"font-mono text-sm text-blue-600 hover:text-blue-800 hover:underline\"\n >\n {row.original.setId}\n </Link>\n ),\n },\n {\n id: 'setName',\n header: t('business_rules.sets.fields.setName'),\n accessorKey: 'setName',\n cell: ({ row }) => (\n <div>\n <div className=\"font-medium\">{row.original.setName}</div>\n {row.original.description && (\n <div className=\"text-xs text-muted-foreground mt-0.5 line-clamp-1\">\n {row.original.description}\n </div>\n )}\n </div>\n ),\n },\n {\n id: 'enabled',\n header: t('business_rules.sets.fields.enabled'),\n accessorKey: 'enabled',\n cell: ({ row }) => (\n <button\n onClick={() => handleToggleEnabled(row.original.id, row.original.enabled)}\n className={`inline-flex items-center px-2 py-1 rounded text-xs font-medium cursor-pointer ${\n row.original.enabled\n ? 'bg-green-100 text-green-800 hover:bg-green-200 dark:bg-green-900 dark:text-green-300 dark:hover:bg-green-800'\n : 'bg-muted text-muted-foreground hover:bg-muted/80'\n }`}\n title={t('business_rules.sets.actions.toggleEnabled')}\n >\n {row.original.enabled ? t('common.yes') : t('common.no')}\n </button>\n ),\n },\n {\n id: 'actions',\n header: '',\n cell: ({ row }) => (\n <RowActions\n items={[\n {\n
|
|
5
|
-
"mappings": ";AAsJQ,cAaA,YAbA;AApJR,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,MAAM,gBAAgB;AAC/B,SAAS,iBAAiB;AAE1B,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,aAAa;AACtB,SAAS,UAAU,sBAAsB;AACzC,SAAS,YAAY;AAuBN,SAAR,mBAAoC;AACzC,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,CAAC;AACxC,QAAM,CAAC,QAAQ,IAAI,MAAM,SAAS,EAAE;AACpC,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,CAAC;AAC1C,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,CAAC;AACpD,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,cAAc,eAAe;AACnC,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAuB,CAAC,CAAC;AAEvE,QAAM,EAAE,MAAM,WAAW,MAAM,IAAI,SAAS;AAAA,IAC1C,UAAU,CAAC,kBAAkB,QAAQ,cAAc,IAAI;AAAA,IACvD,SAAS,YAAY;AACnB,YAAM,SAAS,IAAI,gBAAgB;AACnC,aAAO,IAAI,QAAQ,KAAK,SAAS,CAAC;AAClC,aAAO,IAAI,YAAY,SAAS,SAAS,CAAC;AAC1C,aAAO,IAAI,aAAa,SAAS;AACjC,aAAO,IAAI,WAAW,KAAK;AAE3B,UAAI,aAAa,QAAS,QAAO,IAAI,WAAW,aAAa,OAAiB;AAC9E,UAAI,aAAa,OAAQ,QAAO,IAAI,UAAU,aAAa,MAAgB;AAE3E,YAAM,SAAS,MAAM;AAAA,QACnB,4BAA4B,OAAO,SAAS,CAAC;AAAA,MAC/C;AAEA,UAAI,CAAC,OAAO,IAAI;AACd,cAAM,IAAI,MAAM,2BAA2B;AAAA,MAC7C;AAEA,YAAM,WAAW,OAAO;AACxB,UAAI,UAAU;AACZ,iBAAS,SAAS,SAAS,CAAC;AAC5B,sBAAc,SAAS,cAAc,CAAC;AAAA,MACxC;AAEA,aAAO,UAAU,SAAS,CAAC;AAAA,IAC7B;AAAA,EACF,CAAC;AAED,QAAM,eAAe,OAAO,IAAY,YAAoB;AAC1D,QAAI,CAAC,QAAQ,EAAE,sCAAsC,EAAE,MAAM,QAAQ,CAAC,CAAC,GAAG;AACxE;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,QAAQ,+BAA+B,EAAE,IAAI;AAAA,MAChE,QAAQ;AAAA,IACV,CAAC;AAED,QAAI,OAAO,IAAI;AACb,YAAM,EAAE,sCAAsC,GAAG,SAAS;AAC1D,kBAAY,kBAAkB,EAAE,UAAU,CAAC,kBAAkB,MAAM,EAAE,CAAC;AAAA,IACxE,OAAO;AACL,YAAM,EAAE,2CAA2C,GAAG,OAAO;AAAA,IAC/D;AAAA,EACF;AAEA,QAAM,sBAAsB,OAAO,IAAY,mBAA4B;AACzE,UAAM,SAAS,MAAM,QAAQ,4BAA4B;AAAA,MACvD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA,SAAS,CAAC;AAAA,MACZ,CAAC;AAAA,IACH,CAAC;AAED,QAAI,OAAO,IAAI;AACb,YAAM,EAAE,sCAAsC,GAAG,SAAS;AAC1D,kBAAY,kBAAkB,EAAE,UAAU,CAAC,kBAAkB,MAAM,EAAE,CAAC;AAAA,IACxE,OAAO;AACL,YAAM,EAAE,2CAA2C,GAAG,OAAO;AAAA,IAC/D;AAAA,EACF;AAEA,QAAM,qBAAqB,MAAM,YAAY,CAAC,WAAyB;AACrE,UAAM,OAAqB,CAAC;AAC5B,WAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAC/C,UAAI,UAAU,OAAW,MAAK,GAAG,IAAI;AAAA,IACvC,CAAC;AACD,oBAAgB,IAAI;AACpB,YAAQ,CAAC;AAAA,EACX,GAAG,CAAC,iBAAiB,OAAO,CAAC;AAE7B,QAAM,qBAAqB,MAAM,YAAY,MAAM;AACjD,oBAAgB,CAAC,CAAC;AAClB,YAAQ,CAAC;AAAA,EACX,GAAG,CAAC,iBAAiB,OAAO,CAAC;AAE7B,QAAM,UAAuB;AAAA,IAC3B;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO,EAAE,+BAA+B;AAAA,MACxC,aAAa,EAAE,+CAA+C;AAAA,IAChE;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO,EAAE,+BAA+B;AAAA,MACxC,SAAS;AAAA,QACP,EAAE,OAAO,IAAI,OAAO,EAAE,YAAY,EAAE;AAAA,QACpC,EAAE,OAAO,QAAQ,OAAO,EAAE,gBAAgB,EAAE;AAAA,QAC5C,EAAE,OAAO,SAAS,OAAO,EAAE,iBAAiB,EAAE;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAgC;AAAA,IACpC;AAAA,MACE,IAAI;AAAA,MACJ,QAAQ,EAAE,kCAAkC;AAAA,MAC5C,aAAa;AAAA,MACb,MAAM,CAAC,EAAE,IAAI,MACX;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,iBAAiB,IAAI,SAAS,EAAE;AAAA,UACtC,WAAU;AAAA,UAET,cAAI,SAAS;AAAA;AAAA,MAChB;AAAA,IAEJ;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,QAAQ,EAAE,oCAAoC;AAAA,MAC9C,aAAa;AAAA,MACb,MAAM,CAAC,EAAE,IAAI,MACX,qBAAC,SACC;AAAA,4BAAC,SAAI,WAAU,eAAe,cAAI,SAAS,SAAQ;AAAA,QAClD,IAAI,SAAS,eACZ,oBAAC,SAAI,WAAU,qDACZ,cAAI,SAAS,aAChB;AAAA,SAEJ;AAAA,IAEJ;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,QAAQ,EAAE,oCAAoC;AAAA,MAC9C,aAAa;AAAA,MACb,MAAM,CAAC,EAAE,IAAI,MACX;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,oBAAoB,IAAI,SAAS,IAAI,IAAI,SAAS,OAAO;AAAA,UACxE,WAAW,iFACT,IAAI,SAAS,UACT,iHACA,kDACN;AAAA,UACA,OAAO,EAAE,2CAA2C;AAAA,UAEnD,cAAI,SAAS,UAAU,EAAE,YAAY,IAAI,EAAE,WAAW;AAAA;AAAA,MACzD;AAAA,IAEJ;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,MAAM,CAAC,EAAE,IAAI,MACX;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL;AAAA,cACE,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\n\ntype RuleSet = {\n id: string\n setId: string\n setName: string\n description: string | null\n enabled: boolean\n tenantId: string\n organizationId: string\n createdBy: string | null\n updatedBy: string | null\n createdAt: string\n updatedAt: string\n}\n\ntype RuleSetsResponse = {\n items: RuleSet[]\n total: number\n totalPages: number\n}\n\nexport default function RuleSetsListPage() {\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(20)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const t = useT()\n const router = useRouter()\n const queryClient = useQueryClient()\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n\n const { data, isLoading, error } = useQuery({\n queryKey: ['business-rules', 'sets', filterValues, page],\n queryFn: async () => {\n const params = new URLSearchParams()\n params.set('page', page.toString())\n params.set('pageSize', pageSize.toString())\n params.set('sortField', 'setName')\n params.set('sortDir', 'asc')\n\n if (filterValues.enabled) params.set('enabled', filterValues.enabled as string)\n if (filterValues.search) params.set('search', filterValues.search as string)\n\n const result = await apiCall<RuleSetsResponse>(\n `/api/business_rules/sets?${params.toString()}`\n )\n\n if (!result.ok) {\n throw new Error('Failed to fetch rule sets')\n }\n\n const response = result.result\n if (response) {\n setTotal(response.total || 0)\n setTotalPages(response.totalPages || 1)\n }\n\n return response?.items || []\n },\n })\n\n const handleDelete = async (id: string, setName: string) => {\n if (!confirm(t('business_rules.sets.confirm.delete', { name: setName }))) {\n return\n }\n\n const result = await apiCall(`/api/business_rules/sets?id=${id}`, {\n method: 'DELETE',\n })\n\n if (result.ok) {\n flash(t('business_rules.sets.messages.deleted'), 'success')\n queryClient.invalidateQueries({ queryKey: ['business-rules', 'sets'] })\n } else {\n flash(t('business_rules.sets.messages.deleteFailed'), 'error')\n }\n }\n\n const handleToggleEnabled = async (id: string, currentEnabled: boolean) => {\n const result = await apiCall('/api/business_rules/sets', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id,\n enabled: !currentEnabled,\n }),\n })\n\n if (result.ok) {\n flash(t('business_rules.sets.messages.updated'), 'success')\n queryClient.invalidateQueries({ queryKey: ['business-rules', 'sets'] })\n } else {\n flash(t('business_rules.sets.messages.updateFailed'), 'error')\n }\n }\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n Object.entries(values).forEach(([key, value]) => {\n if (value !== undefined) next[key] = value\n })\n setFilterValues(next)\n setPage(1)\n }, [setFilterValues, setPage])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [setFilterValues, setPage])\n\n const filters: FilterDef[] = [\n {\n id: 'search',\n type: 'text',\n label: t('business_rules.filters.search'),\n placeholder: t('business_rules.sets.filters.searchPlaceholder'),\n },\n {\n id: 'enabled',\n type: 'select',\n label: t('business_rules.filters.status'),\n options: [\n { value: '', label: t('common.all') },\n { value: 'true', label: t('common.enabled') },\n { value: 'false', label: t('common.disabled') },\n ],\n },\n ]\n\n const columns: ColumnDef<RuleSet>[] = [\n {\n id: 'setId',\n header: t('business_rules.sets.fields.setId'),\n accessorKey: 'setId',\n cell: ({ row }) => (\n <Link\n href={`/backend/sets/${row.original.id}`}\n className=\"font-mono text-sm text-blue-600 hover:text-blue-800 hover:underline\"\n >\n {row.original.setId}\n </Link>\n ),\n },\n {\n id: 'setName',\n header: t('business_rules.sets.fields.setName'),\n accessorKey: 'setName',\n cell: ({ row }) => (\n <div>\n <div className=\"font-medium\">{row.original.setName}</div>\n {row.original.description && (\n <div className=\"text-xs text-muted-foreground mt-0.5 line-clamp-1\">\n {row.original.description}\n </div>\n )}\n </div>\n ),\n },\n {\n id: 'enabled',\n header: t('business_rules.sets.fields.enabled'),\n accessorKey: 'enabled',\n cell: ({ row }) => (\n <button\n onClick={() => handleToggleEnabled(row.original.id, row.original.enabled)}\n className={`inline-flex items-center px-2 py-1 rounded text-xs font-medium cursor-pointer ${\n row.original.enabled\n ? 'bg-green-100 text-green-800 hover:bg-green-200 dark:bg-green-900 dark:text-green-300 dark:hover:bg-green-800'\n : 'bg-muted text-muted-foreground hover:bg-muted/80'\n }`}\n title={t('business_rules.sets.actions.toggleEnabled')}\n >\n {row.original.enabled ? t('common.yes') : t('common.no')}\n </button>\n ),\n },\n {\n id: 'actions',\n header: '',\n cell: ({ row }) => (\n <RowActions\n items={[\n {\n label: t('common.edit'),\n href: `/backend/sets/${row.original.id}`,\n },\n {\n label: row.original.enabled ? t('common.disable') : t('common.enable'),\n onSelect: () => handleToggleEnabled(row.original.id, row.original.enabled),\n },\n {\n label: t('common.delete'),\n onSelect: () => handleDelete(row.original.id, row.original.setName),\n destructive: true,\n },\n ]}\n />\n ),\n },\n ]\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('business_rules.sets.list.title')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/sets/create\">\n {t('business_rules.sets.actions.create')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={data || []}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n isLoading={isLoading}\n error={error ? t('business_rules.sets.messages.loadFailed') : undefined}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -23,7 +23,7 @@ async function withTimeout(promise, timeoutMs, errorMessage) {
|
|
|
23
23
|
clearTimeout(timeoutId);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
async function executeRules(em, context
|
|
26
|
+
async function executeRules(em, context) {
|
|
27
27
|
const validation = ruleEngineContextSchema.safeParse(context);
|
|
28
28
|
if (!validation.success) {
|
|
29
29
|
const validationErrors = validation.error.issues.map((e) => `${e.path.join(".")}: ${e.message}`);
|
|
@@ -59,7 +59,7 @@ async function executeRules(em, context, options = {}) {
|
|
|
59
59
|
const executionPromise = (async () => {
|
|
60
60
|
for (const rule of rules) {
|
|
61
61
|
try {
|
|
62
|
-
const ruleResult = await executeSingleRule(em, rule, context
|
|
62
|
+
const ruleResult = await executeSingleRule(em, rule, context);
|
|
63
63
|
executedRules.push(ruleResult);
|
|
64
64
|
if (ruleResult.logId) {
|
|
65
65
|
logIds.push(ruleResult.logId);
|
|
@@ -74,16 +74,6 @@ async function executeRules(em, context, options = {}) {
|
|
|
74
74
|
errors.push(
|
|
75
75
|
`Unexpected error in rule execution [ruleId=${rule.ruleId}, type=${rule.ruleType}]: ${errorMessage}`
|
|
76
76
|
);
|
|
77
|
-
if (!context.dryRun) {
|
|
78
|
-
await emitRuleExecutionFailed(options.eventBus, {
|
|
79
|
-
ruleId: rule.ruleId,
|
|
80
|
-
ruleName: rule.ruleName,
|
|
81
|
-
entityType: context.entityType ?? null,
|
|
82
|
-
errorMessage,
|
|
83
|
-
tenantId: context.tenantId,
|
|
84
|
-
organizationId: context.organizationId ?? null
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
77
|
executedRules.push({
|
|
88
78
|
rule,
|
|
89
79
|
conditionResult: false,
|
|
@@ -125,7 +115,7 @@ Stack: ${stack}` : ""}`
|
|
|
125
115
|
};
|
|
126
116
|
}
|
|
127
117
|
}
|
|
128
|
-
async function executeSingleRule(em, rule, context
|
|
118
|
+
async function executeSingleRule(em, rule, context) {
|
|
129
119
|
const startTime = Date.now();
|
|
130
120
|
try {
|
|
131
121
|
const executeWithTimeout = async () => {
|
|
@@ -150,14 +140,6 @@ async function executeSingleRule(em, rule, context, options = {}) {
|
|
|
150
140
|
executionTime: executionTime2,
|
|
151
141
|
error: result.error
|
|
152
142
|
});
|
|
153
|
-
await emitRuleExecutionFailed(options.eventBus, {
|
|
154
|
-
ruleId: rule.ruleId,
|
|
155
|
-
ruleName: rule.ruleName,
|
|
156
|
-
entityType: context.entityType ?? null,
|
|
157
|
-
errorMessage: result.error ?? null,
|
|
158
|
-
tenantId: context.tenantId,
|
|
159
|
-
organizationId: context.organizationId ?? null
|
|
160
|
-
});
|
|
161
143
|
}
|
|
162
144
|
return {
|
|
163
145
|
rule,
|
|
@@ -217,14 +199,6 @@ async function executeSingleRule(em, rule, context, options = {}) {
|
|
|
217
199
|
executionTime,
|
|
218
200
|
error: enhancedError
|
|
219
201
|
});
|
|
220
|
-
await emitRuleExecutionFailed(options.eventBus, {
|
|
221
|
-
ruleId: rule.ruleId,
|
|
222
|
-
ruleName: rule.ruleName,
|
|
223
|
-
entityType: context.entityType ?? null,
|
|
224
|
-
errorMessage: enhancedError,
|
|
225
|
-
tenantId: context.tenantId,
|
|
226
|
-
organizationId: context.organizationId ?? null
|
|
227
|
-
});
|
|
228
202
|
}
|
|
229
203
|
return {
|
|
230
204
|
rule,
|
|
@@ -350,10 +324,6 @@ async function logRuleExecution(em, options) {
|
|
|
350
324
|
await em.persistAndFlush(log);
|
|
351
325
|
return log.id;
|
|
352
326
|
}
|
|
353
|
-
async function emitRuleExecutionFailed(eventBus, payload) {
|
|
354
|
-
if (!eventBus?.emitEvent) return;
|
|
355
|
-
await eventBus.emitEvent("business_rules.rule.execution_failed", payload).catch(() => void 0);
|
|
356
|
-
}
|
|
357
327
|
export {
|
|
358
328
|
executeRules,
|
|
359
329
|
executeSingleRule,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/business_rules/lib/rule-engine.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport type { EventBus } from '@open-mercato/events'\nimport { BusinessRule, RuleExecutionLog, type RuleType } from '../data/entities'\nimport * as ruleEvaluator from './rule-evaluator'\nimport * as actionExecutor from './action-executor'\nimport type { RuleEvaluationContext } from './rule-evaluator'\nimport type { ActionContext, ActionExecutionOutcome } from './action-executor'\nimport { ruleEngineContextSchema, ruleDiscoveryOptionsSchema } from '../data/validators'\n\n/**\n * Constants\n */\nconst DEFAULT_ENTITY_ID = 'unknown'\nconst RULE_TYPE_GUARD = 'GUARD'\nconst EXECUTION_RESULT_ERROR = 'ERROR'\nconst EXECUTION_RESULT_SUCCESS = 'SUCCESS'\nconst EXECUTION_RESULT_FAILURE = 'FAILURE'\n\n/**\n * Execution limits\n */\nconst MAX_RULES_PER_EXECUTION = 100\nconst MAX_SINGLE_RULE_TIMEOUT_MS = 30000 // 30 seconds\nconst MAX_TOTAL_EXECUTION_TIMEOUT_MS = 60000 // 60 seconds\n\n/**\n * Rule execution context\n */\nexport interface RuleEngineContext {\n entityType: string\n entityId?: string\n eventType?: string\n data: any\n user?: {\n id?: string\n email?: string\n role?: string\n [key: string]: any\n }\n tenant?: {\n id?: string\n [key: string]: any\n }\n organization?: {\n id?: string\n [key: string]: any\n }\n tenantId: string\n organizationId: string\n executedBy?: string\n dryRun?: boolean\n [key: string]: any\n}\n\n/**\n * Single rule execution result\n */\nexport interface RuleExecutionResult {\n rule: BusinessRule\n conditionResult: boolean\n actionsExecuted: ActionExecutionOutcome | null\n executionTime: number\n error?: string\n logId?: string // Database log ID (if logged)\n}\n\n/**\n * Overall rule engine result\n */\nexport interface RuleEngineResult {\n allowed: boolean\n executedRules: RuleExecutionResult[]\n totalExecutionTime: number\n errors?: string[]\n logIds?: string[]\n}\n\n/**\n * Rule discovery options\n */\nexport interface RuleDiscoveryOptions {\n entityType: string\n eventType?: string\n tenantId: string\n organizationId: string\n ruleType?: RuleType\n}\n\nexport type RuleEngineExecutionOptions = {\n eventBus?: Pick<EventBus, 'emitEvent'> | null\n}\n\ntype RuleExecutionFailedPayload = {\n ruleId: string\n ruleName: string\n entityType?: string | null\n errorMessage?: string | null\n tenantId: string\n organizationId?: string | null\n}\n\n/**\n * Execute a function with a timeout\n */\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n errorMessage: string\n): Promise<T> {\n let timeoutId: NodeJS.Timeout\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(`${errorMessage} (timeout: ${timeoutMs}ms)`))\n }, timeoutMs)\n })\n\n try {\n return await Promise.race([promise, timeoutPromise])\n } finally {\n clearTimeout(timeoutId!)\n }\n}\n\n/**\n * Execute all applicable rules for the given context\n */\nexport async function executeRules(\n em: EntityManager,\n context: RuleEngineContext,\n options: RuleEngineExecutionOptions = {}\n): Promise<RuleEngineResult> {\n // Validate input\n const validation = ruleEngineContextSchema.safeParse(context)\n if (!validation.success) {\n const validationErrors = validation.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return {\n allowed: false,\n executedRules: [],\n totalExecutionTime: 0,\n errors: validationErrors,\n }\n }\n\n const startTime = Date.now()\n const executedRules: RuleExecutionResult[] = []\n const errors: string[] = []\n const logIds: string[] = []\n\n try {\n // Discover applicable rules\n const rules = await findApplicableRules(em, {\n entityType: context.entityType,\n eventType: context.eventType,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n\n // Check rule count limit\n if (rules.length > MAX_RULES_PER_EXECUTION) {\n errors.push(\n `Rule count limit exceeded: ${rules.length} rules found, maximum is ${MAX_RULES_PER_EXECUTION}`\n )\n return {\n allowed: false,\n executedRules: [],\n totalExecutionTime: Date.now() - startTime,\n errors,\n }\n }\n\n // Rules already sorted by database query (priority DESC, ruleId ASC)\n // Execute each rule with total timeout\n const executionPromise = (async () => {\n for (const rule of rules) {\n try {\n const ruleResult = await executeSingleRule(em, rule, context, options)\n executedRules.push(ruleResult)\n\n if (ruleResult.logId) {\n logIds.push(ruleResult.logId)\n }\n\n if (ruleResult.error) {\n errors.push(\n `Rule execution failed [ruleId=${rule.ruleId}, type=${rule.ruleType}, entityType=${context.entityType}]: ${ruleResult.error}`\n )\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n errors.push(\n `Unexpected error in rule execution [ruleId=${rule.ruleId}, type=${rule.ruleType}]: ${errorMessage}`\n )\n\n if (!context.dryRun) {\n await emitRuleExecutionFailed(options.eventBus, {\n ruleId: rule.ruleId,\n ruleName: rule.ruleName,\n entityType: context.entityType ?? null,\n errorMessage,\n tenantId: context.tenantId,\n organizationId: context.organizationId ?? null,\n })\n }\n\n executedRules.push({\n rule,\n conditionResult: false,\n actionsExecuted: null,\n executionTime: 0,\n error: errorMessage,\n })\n }\n }\n })()\n\n // Execute with timeout\n await withTimeout(\n executionPromise,\n MAX_TOTAL_EXECUTION_TIMEOUT_MS,\n `Total rule execution timeout [entityType=${context.entityType}]`\n )\n\n // Determine overall allowed status\n // For GUARD rules: all must pass for operation to be allowed\n const guardRules = executedRules.filter((r) => r.rule.ruleType === RULE_TYPE_GUARD)\n const allowed = guardRules.length === 0 || guardRules.every((r) => r.conditionResult)\n\n const totalExecutionTime = Date.now() - startTime\n\n return {\n allowed,\n executedRules,\n totalExecutionTime,\n errors: errors.length > 0 ? errors : undefined,\n logIds: logIds.length > 0 ? logIds : undefined,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n const stack = error instanceof Error ? error.stack : undefined\n errors.push(\n `Critical rule engine error [entityType=${context.entityType}, entityId=${context.entityId || 'unknown'}]: ${errorMessage}${stack ? `\\nStack: ${stack}` : ''}`\n )\n\n const totalExecutionTime = Date.now() - startTime\n\n return {\n allowed: false,\n executedRules,\n totalExecutionTime,\n errors,\n }\n }\n}\n\n/**\n * Execute a single rule\n */\nexport async function executeSingleRule(\n em: EntityManager,\n rule: BusinessRule,\n context: RuleEngineContext,\n options: RuleEngineExecutionOptions = {}\n): Promise<RuleExecutionResult> {\n const startTime = Date.now()\n\n try {\n // Wrap execution in timeout\n const executeWithTimeout = async () => {\n // Build evaluation context\n const evalContext: RuleEvaluationContext = {\n entityType: context.entityType,\n entityId: context.entityId,\n eventType: context.eventType,\n user: context.user,\n tenant: context.tenant,\n organization: context.organization,\n }\n\n // Evaluate rule conditions\n const result = await ruleEvaluator.evaluateSingleRule(rule, context.data, evalContext)\n\n // Check if evaluation completed (not just if conditions passed)\n if (!result.evaluationCompleted) {\n const executionTime = Date.now() - startTime\n\n let logId: string | undefined\n\n // Log failure if not dry run\n if (!context.dryRun) {\n logId = await logRuleExecution(em, {\n rule,\n context,\n conditionResult: false,\n actionsExecuted: null,\n executionTime,\n error: result.error,\n })\n\n await emitRuleExecutionFailed(options.eventBus, {\n ruleId: rule.ruleId,\n ruleName: rule.ruleName,\n entityType: context.entityType ?? null,\n errorMessage: result.error ?? null,\n tenantId: context.tenantId,\n organizationId: context.organizationId ?? null,\n })\n }\n\n return {\n rule,\n conditionResult: false,\n actionsExecuted: null,\n executionTime,\n error: result.error,\n logId,\n }\n }\n\n // Evaluation completed successfully - determine which actions to execute\n const actions = result.conditionsPassed ? rule.successActions : rule.failureActions\n\n let actionsExecuted: ActionExecutionOutcome | null = null\n\n if (actions && Array.isArray(actions) && actions.length > 0) {\n // Build action context\n const actionContext: ActionContext = {\n ...evalContext,\n data: context.data,\n ruleId: rule.ruleId,\n ruleName: rule.ruleName,\n }\n\n // Execute actions\n actionsExecuted = await actionExecutor.executeActions(actions, actionContext)\n }\n\n const executionTime = Date.now() - startTime\n\n let logId: string | undefined\n\n // Log execution if not dry run\n if (!context.dryRun) {\n logId = await logRuleExecution(em, {\n rule,\n context,\n conditionResult: result.conditionsPassed,\n actionsExecuted,\n executionTime,\n })\n }\n\n return {\n rule,\n conditionResult: result.conditionsPassed,\n actionsExecuted,\n executionTime,\n logId,\n }\n }\n\n // Execute with single rule timeout\n return await withTimeout(\n executeWithTimeout(),\n MAX_SINGLE_RULE_TIMEOUT_MS,\n `Single rule execution timeout [ruleId=${rule.ruleId}]`\n )\n } catch (error) {\n const executionTime = Date.now() - startTime\n const errorMessage = error instanceof Error ? error.message : String(error)\n const enhancedError = `Failed to execute rule [ruleId=${rule.ruleId}, entityType=${context.entityType}]: ${errorMessage}`\n\n let logId: string | undefined\n\n // Log error if not dry run\n if (!context.dryRun) {\n logId = await logRuleExecution(em, {\n rule,\n context,\n conditionResult: false,\n actionsExecuted: null,\n executionTime,\n error: enhancedError,\n })\n\n await emitRuleExecutionFailed(options.eventBus, {\n ruleId: rule.ruleId,\n ruleName: rule.ruleName,\n entityType: context.entityType ?? null,\n errorMessage: enhancedError,\n tenantId: context.tenantId,\n organizationId: context.organizationId ?? null,\n })\n }\n\n return {\n rule,\n conditionResult: false,\n actionsExecuted: null,\n executionTime,\n error: enhancedError,\n logId,\n }\n }\n}\n\n/**\n * Find all applicable rules for the given criteria\n */\nexport async function findApplicableRules(\n em: EntityManager,\n options: RuleDiscoveryOptions\n): Promise<BusinessRule[]> {\n // Validate input\n ruleDiscoveryOptionsSchema.parse(options)\n\n const { entityType, eventType, tenantId, organizationId, ruleType } = options\n\n const where: Partial<BusinessRule> = {\n entityType,\n tenantId,\n organizationId,\n enabled: true,\n deletedAt: null,\n }\n\n if (eventType) {\n where.eventType = eventType\n }\n\n if (ruleType) {\n where.ruleType = ruleType\n }\n\n const rules = await em.find(BusinessRule, where, {\n orderBy: { priority: 'DESC', ruleId: 'ASC' },\n })\n\n // Filter by effective date range\n const now = new Date()\n return rules.filter((rule) => {\n if (rule.effectiveFrom && rule.effectiveFrom > now) {\n return false\n }\n if (rule.effectiveTo && rule.effectiveTo < now) {\n return false\n }\n return true\n })\n}\n\n/**\n * Sensitive field patterns to exclude from logs\n */\nconst SENSITIVE_FIELD_PATTERNS = [\n /password/i,\n /passwd/i,\n /pwd/i,\n /secret/i,\n /token/i,\n /api[_-]?key/i,\n /auth/i,\n /credit[_-]?card/i,\n /card[_-]?number/i,\n /cvv/i,\n /ssn/i,\n /social[_-]?security/i,\n /tax[_-]?id/i,\n /driver[_-]?license/i,\n /passport/i,\n]\n\n/**\n * Maximum depth for nested object sanitization\n */\nconst MAX_SANITIZATION_DEPTH = 5\n\n/**\n * Sanitize data for logging by removing sensitive fields\n */\nfunction sanitizeForLogging(data: any, depth: number = 0): any {\n // Prevent infinite recursion\n if (depth > MAX_SANITIZATION_DEPTH) {\n return '[Max depth exceeded]'\n }\n\n // Handle null/undefined\n if (data === null || data === undefined) {\n return data\n }\n\n // Handle primitives\n if (typeof data !== 'object') {\n return data\n }\n\n // Handle arrays\n if (Array.isArray(data)) {\n return data.map(item => sanitizeForLogging(item, depth + 1))\n }\n\n // Handle objects\n const sanitized: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(data)) {\n // Check if field name matches sensitive patterns\n const isSensitive = SENSITIVE_FIELD_PATTERNS.some(pattern => pattern.test(key))\n\n if (isSensitive) {\n sanitized[key] = '[REDACTED]'\n } else if (typeof value === 'object' && value !== null) {\n sanitized[key] = sanitizeForLogging(value, depth + 1)\n } else {\n sanitized[key] = value\n }\n }\n\n return sanitized\n}\n\n/**\n * Sanitize user object for logging (keep only safe fields)\n */\nfunction sanitizeUser(user: any): any {\n if (!user) {\n return undefined\n }\n\n // Only log safe user fields\n return {\n id: user.id,\n role: user.role,\n // Don't log: email, name, phone, address, etc.\n }\n}\n\n/**\n * Log rule execution to database\n */\ninterface LogExecutionOptions {\n rule: BusinessRule\n context: RuleEngineContext\n conditionResult: boolean\n actionsExecuted: ActionExecutionOutcome | null\n executionTime: number\n error?: string\n}\n\nexport async function logRuleExecution(\n em: EntityManager,\n options: LogExecutionOptions\n): Promise<string> {\n const { rule, context, conditionResult, actionsExecuted, executionTime, error } = options\n\n const executionResult: 'SUCCESS' | 'FAILURE' | 'ERROR' = error\n ? EXECUTION_RESULT_ERROR\n : conditionResult\n ? EXECUTION_RESULT_SUCCESS\n : EXECUTION_RESULT_FAILURE\n\n const log = em.create(RuleExecutionLog, {\n rule,\n entityId: context.entityId || DEFAULT_ENTITY_ID,\n entityType: context.entityType,\n executionResult,\n inputContext: {\n data: sanitizeForLogging(context.data),\n eventType: context.eventType,\n user: sanitizeUser(context.user),\n },\n outputContext: actionsExecuted\n ? {\n conditionResult,\n actionsExecuted: actionsExecuted.results.map((r) => ({\n type: r.action.type,\n success: r.success,\n error: r.error,\n })),\n }\n : null,\n errorMessage: error || null,\n executionTimeMs: executionTime,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n executedBy: context.executedBy || null,\n })\n\n await em.persistAndFlush(log)\n\n return log.id\n}\n\nasync function emitRuleExecutionFailed(\n eventBus: Pick<EventBus, 'emitEvent'> | null | undefined,\n payload: RuleExecutionFailedPayload\n): Promise<void> {\n if (!eventBus?.emitEvent) return\n\n await eventBus.emitEvent('business_rules.rule.execution_failed', payload).catch(() => undefined)\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport { BusinessRule, RuleExecutionLog, type RuleType } from '../data/entities'\nimport * as ruleEvaluator from './rule-evaluator'\nimport * as actionExecutor from './action-executor'\nimport type { RuleEvaluationContext } from './rule-evaluator'\nimport type { ActionContext, ActionExecutionOutcome } from './action-executor'\nimport { ruleEngineContextSchema, ruleDiscoveryOptionsSchema } from '../data/validators'\n\n/**\n * Constants\n */\nconst DEFAULT_ENTITY_ID = 'unknown'\nconst RULE_TYPE_GUARD = 'GUARD'\nconst EXECUTION_RESULT_ERROR = 'ERROR'\nconst EXECUTION_RESULT_SUCCESS = 'SUCCESS'\nconst EXECUTION_RESULT_FAILURE = 'FAILURE'\n\n/**\n * Execution limits\n */\nconst MAX_RULES_PER_EXECUTION = 100\nconst MAX_SINGLE_RULE_TIMEOUT_MS = 30000 // 30 seconds\nconst MAX_TOTAL_EXECUTION_TIMEOUT_MS = 60000 // 60 seconds\n\n/**\n * Rule execution context\n */\nexport interface RuleEngineContext {\n entityType: string\n entityId?: string\n eventType?: string\n data: any\n user?: {\n id?: string\n email?: string\n role?: string\n [key: string]: any\n }\n tenant?: {\n id?: string\n [key: string]: any\n }\n organization?: {\n id?: string\n [key: string]: any\n }\n tenantId: string\n organizationId: string\n executedBy?: string\n dryRun?: boolean\n [key: string]: any\n}\n\n/**\n * Single rule execution result\n */\nexport interface RuleExecutionResult {\n rule: BusinessRule\n conditionResult: boolean\n actionsExecuted: ActionExecutionOutcome | null\n executionTime: number\n error?: string\n logId?: string // Database log ID (if logged)\n}\n\n/**\n * Overall rule engine result\n */\nexport interface RuleEngineResult {\n allowed: boolean\n executedRules: RuleExecutionResult[]\n totalExecutionTime: number\n errors?: string[]\n logIds?: string[]\n}\n\n/**\n * Rule discovery options\n */\nexport interface RuleDiscoveryOptions {\n entityType: string\n eventType?: string\n tenantId: string\n organizationId: string\n ruleType?: RuleType\n}\n\n/**\n * Execute a function with a timeout\n */\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n errorMessage: string\n): Promise<T> {\n let timeoutId: NodeJS.Timeout\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(`${errorMessage} (timeout: ${timeoutMs}ms)`))\n }, timeoutMs)\n })\n\n try {\n return await Promise.race([promise, timeoutPromise])\n } finally {\n clearTimeout(timeoutId!)\n }\n}\n\n/**\n * Execute all applicable rules for the given context\n */\nexport async function executeRules(\n em: EntityManager,\n context: RuleEngineContext\n): Promise<RuleEngineResult> {\n // Validate input\n const validation = ruleEngineContextSchema.safeParse(context)\n if (!validation.success) {\n const validationErrors = validation.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return {\n allowed: false,\n executedRules: [],\n totalExecutionTime: 0,\n errors: validationErrors,\n }\n }\n\n const startTime = Date.now()\n const executedRules: RuleExecutionResult[] = []\n const errors: string[] = []\n const logIds: string[] = []\n\n try {\n // Discover applicable rules\n const rules = await findApplicableRules(em, {\n entityType: context.entityType,\n eventType: context.eventType,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n\n // Check rule count limit\n if (rules.length > MAX_RULES_PER_EXECUTION) {\n errors.push(\n `Rule count limit exceeded: ${rules.length} rules found, maximum is ${MAX_RULES_PER_EXECUTION}`\n )\n return {\n allowed: false,\n executedRules: [],\n totalExecutionTime: Date.now() - startTime,\n errors,\n }\n }\n\n // Rules already sorted by database query (priority DESC, ruleId ASC)\n // Execute each rule with total timeout\n const executionPromise = (async () => {\n for (const rule of rules) {\n try {\n const ruleResult = await executeSingleRule(em, rule, context)\n executedRules.push(ruleResult)\n\n if (ruleResult.logId) {\n logIds.push(ruleResult.logId)\n }\n\n if (ruleResult.error) {\n errors.push(\n `Rule execution failed [ruleId=${rule.ruleId}, type=${rule.ruleType}, entityType=${context.entityType}]: ${ruleResult.error}`\n )\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n errors.push(\n `Unexpected error in rule execution [ruleId=${rule.ruleId}, type=${rule.ruleType}]: ${errorMessage}`\n )\n\n executedRules.push({\n rule,\n conditionResult: false,\n actionsExecuted: null,\n executionTime: 0,\n error: errorMessage,\n })\n }\n }\n })()\n\n // Execute with timeout\n await withTimeout(\n executionPromise,\n MAX_TOTAL_EXECUTION_TIMEOUT_MS,\n `Total rule execution timeout [entityType=${context.entityType}]`\n )\n\n // Determine overall allowed status\n // For GUARD rules: all must pass for operation to be allowed\n const guardRules = executedRules.filter((r) => r.rule.ruleType === RULE_TYPE_GUARD)\n const allowed = guardRules.length === 0 || guardRules.every((r) => r.conditionResult)\n\n const totalExecutionTime = Date.now() - startTime\n\n return {\n allowed,\n executedRules,\n totalExecutionTime,\n errors: errors.length > 0 ? errors : undefined,\n logIds: logIds.length > 0 ? logIds : undefined,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n const stack = error instanceof Error ? error.stack : undefined\n errors.push(\n `Critical rule engine error [entityType=${context.entityType}, entityId=${context.entityId || 'unknown'}]: ${errorMessage}${stack ? `\\nStack: ${stack}` : ''}`\n )\n\n const totalExecutionTime = Date.now() - startTime\n\n return {\n allowed: false,\n executedRules,\n totalExecutionTime,\n errors,\n }\n }\n}\n\n/**\n * Execute a single rule\n */\nexport async function executeSingleRule(\n em: EntityManager,\n rule: BusinessRule,\n context: RuleEngineContext\n): Promise<RuleExecutionResult> {\n const startTime = Date.now()\n\n try {\n // Wrap execution in timeout\n const executeWithTimeout = async () => {\n // Build evaluation context\n const evalContext: RuleEvaluationContext = {\n entityType: context.entityType,\n entityId: context.entityId,\n eventType: context.eventType,\n user: context.user,\n tenant: context.tenant,\n organization: context.organization,\n }\n\n // Evaluate rule conditions\n const result = await ruleEvaluator.evaluateSingleRule(rule, context.data, evalContext)\n\n // Check if evaluation completed (not just if conditions passed)\n if (!result.evaluationCompleted) {\n const executionTime = Date.now() - startTime\n\n let logId: string | undefined\n\n // Log failure if not dry run\n if (!context.dryRun) {\n logId = await logRuleExecution(em, {\n rule,\n context,\n conditionResult: false,\n actionsExecuted: null,\n executionTime,\n error: result.error,\n })\n }\n\n return {\n rule,\n conditionResult: false,\n actionsExecuted: null,\n executionTime,\n error: result.error,\n logId,\n }\n }\n\n // Evaluation completed successfully - determine which actions to execute\n const actions = result.conditionsPassed ? rule.successActions : rule.failureActions\n\n let actionsExecuted: ActionExecutionOutcome | null = null\n\n if (actions && Array.isArray(actions) && actions.length > 0) {\n // Build action context\n const actionContext: ActionContext = {\n ...evalContext,\n data: context.data,\n ruleId: rule.ruleId,\n ruleName: rule.ruleName,\n }\n\n // Execute actions\n actionsExecuted = await actionExecutor.executeActions(actions, actionContext)\n }\n\n const executionTime = Date.now() - startTime\n\n let logId: string | undefined\n\n // Log execution if not dry run\n if (!context.dryRun) {\n logId = await logRuleExecution(em, {\n rule,\n context,\n conditionResult: result.conditionsPassed,\n actionsExecuted,\n executionTime,\n })\n }\n\n return {\n rule,\n conditionResult: result.conditionsPassed,\n actionsExecuted,\n executionTime,\n logId,\n }\n }\n\n // Execute with single rule timeout\n return await withTimeout(\n executeWithTimeout(),\n MAX_SINGLE_RULE_TIMEOUT_MS,\n `Single rule execution timeout [ruleId=${rule.ruleId}]`\n )\n } catch (error) {\n const executionTime = Date.now() - startTime\n const errorMessage = error instanceof Error ? error.message : String(error)\n const enhancedError = `Failed to execute rule [ruleId=${rule.ruleId}, entityType=${context.entityType}]: ${errorMessage}`\n\n let logId: string | undefined\n\n // Log error if not dry run\n if (!context.dryRun) {\n logId = await logRuleExecution(em, {\n rule,\n context,\n conditionResult: false,\n actionsExecuted: null,\n executionTime,\n error: enhancedError,\n })\n }\n\n return {\n rule,\n conditionResult: false,\n actionsExecuted: null,\n executionTime,\n error: enhancedError,\n logId,\n }\n }\n}\n\n/**\n * Find all applicable rules for the given criteria\n */\nexport async function findApplicableRules(\n em: EntityManager,\n options: RuleDiscoveryOptions\n): Promise<BusinessRule[]> {\n // Validate input\n ruleDiscoveryOptionsSchema.parse(options)\n\n const { entityType, eventType, tenantId, organizationId, ruleType } = options\n\n const where: Partial<BusinessRule> = {\n entityType,\n tenantId,\n organizationId,\n enabled: true,\n deletedAt: null,\n }\n\n if (eventType) {\n where.eventType = eventType\n }\n\n if (ruleType) {\n where.ruleType = ruleType\n }\n\n const rules = await em.find(BusinessRule, where, {\n orderBy: { priority: 'DESC', ruleId: 'ASC' },\n })\n\n // Filter by effective date range\n const now = new Date()\n return rules.filter((rule) => {\n if (rule.effectiveFrom && rule.effectiveFrom > now) {\n return false\n }\n if (rule.effectiveTo && rule.effectiveTo < now) {\n return false\n }\n return true\n })\n}\n\n/**\n * Sensitive field patterns to exclude from logs\n */\nconst SENSITIVE_FIELD_PATTERNS = [\n /password/i,\n /passwd/i,\n /pwd/i,\n /secret/i,\n /token/i,\n /api[_-]?key/i,\n /auth/i,\n /credit[_-]?card/i,\n /card[_-]?number/i,\n /cvv/i,\n /ssn/i,\n /social[_-]?security/i,\n /tax[_-]?id/i,\n /driver[_-]?license/i,\n /passport/i,\n]\n\n/**\n * Maximum depth for nested object sanitization\n */\nconst MAX_SANITIZATION_DEPTH = 5\n\n/**\n * Sanitize data for logging by removing sensitive fields\n */\nfunction sanitizeForLogging(data: any, depth: number = 0): any {\n // Prevent infinite recursion\n if (depth > MAX_SANITIZATION_DEPTH) {\n return '[Max depth exceeded]'\n }\n\n // Handle null/undefined\n if (data === null || data === undefined) {\n return data\n }\n\n // Handle primitives\n if (typeof data !== 'object') {\n return data\n }\n\n // Handle arrays\n if (Array.isArray(data)) {\n return data.map(item => sanitizeForLogging(item, depth + 1))\n }\n\n // Handle objects\n const sanitized: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(data)) {\n // Check if field name matches sensitive patterns\n const isSensitive = SENSITIVE_FIELD_PATTERNS.some(pattern => pattern.test(key))\n\n if (isSensitive) {\n sanitized[key] = '[REDACTED]'\n } else if (typeof value === 'object' && value !== null) {\n sanitized[key] = sanitizeForLogging(value, depth + 1)\n } else {\n sanitized[key] = value\n }\n }\n\n return sanitized\n}\n\n/**\n * Sanitize user object for logging (keep only safe fields)\n */\nfunction sanitizeUser(user: any): any {\n if (!user) {\n return undefined\n }\n\n // Only log safe user fields\n return {\n id: user.id,\n role: user.role,\n // Don't log: email, name, phone, address, etc.\n }\n}\n\n/**\n * Log rule execution to database\n */\ninterface LogExecutionOptions {\n rule: BusinessRule\n context: RuleEngineContext\n conditionResult: boolean\n actionsExecuted: ActionExecutionOutcome | null\n executionTime: number\n error?: string\n}\n\nexport async function logRuleExecution(\n em: EntityManager,\n options: LogExecutionOptions\n): Promise<string> {\n const { rule, context, conditionResult, actionsExecuted, executionTime, error } = options\n\n const executionResult: 'SUCCESS' | 'FAILURE' | 'ERROR' = error\n ? EXECUTION_RESULT_ERROR\n : conditionResult\n ? EXECUTION_RESULT_SUCCESS\n : EXECUTION_RESULT_FAILURE\n\n const log = em.create(RuleExecutionLog, {\n rule,\n entityId: context.entityId || DEFAULT_ENTITY_ID,\n entityType: context.entityType,\n executionResult,\n inputContext: {\n data: sanitizeForLogging(context.data),\n eventType: context.eventType,\n user: sanitizeUser(context.user),\n },\n outputContext: actionsExecuted\n ? {\n conditionResult,\n actionsExecuted: actionsExecuted.results.map((r) => ({\n type: r.action.type,\n success: r.success,\n error: r.error,\n })),\n }\n : null,\n errorMessage: error || null,\n executionTimeMs: executionTime,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n executedBy: context.executedBy || null,\n })\n\n await em.persistAndFlush(log)\n\n return log.id\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["executionTime", "logId"]
|
|
7
7
|
}
|
|
@@ -326,12 +326,10 @@ function PriceKindSettings() {
|
|
|
326
326
|
{
|
|
327
327
|
items: [
|
|
328
328
|
{
|
|
329
|
-
id: "edit",
|
|
330
329
|
label: t("catalog.priceKinds.actions.edit", "Edit"),
|
|
331
330
|
onSelect: () => openDialog({ mode: "edit", entry })
|
|
332
331
|
},
|
|
333
332
|
{
|
|
334
|
-
id: "delete",
|
|
335
333
|
label: t("catalog.priceKinds.actions.delete", "Delete"),
|
|
336
334
|
destructive: true,
|
|
337
335
|
onSelect: () => {
|