@open-mercato/core 0.8.1-develop.7274.1.21a0083828 → 0.8.1-develop.7294.1.0ef99fc518
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/components/AclEditor.js +21 -3
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +6 -4
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js +6 -4
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +6 -4
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js +5 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +6 -4
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +6 -4
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +9 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +4 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/lookups.js +7 -3
- package/dist/modules/customers/components/calendar/editor/lookups.js.map +2 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js +5 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js +4 -2
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +8 -5
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +10 -7
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/fieldConfig.js +9 -0
- package/dist/modules/customers/components/detail/schedule/fieldConfig.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/index.js +3 -1
- package/dist/modules/customers/components/detail/schedule/index.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +11 -4
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js +17 -9
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/data/validators.js +12 -7
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/lib/assignableStaff.js +103 -22
- package/dist/modules/customers/lib/assignableStaff.js.map +2 -2
- package/dist/modules/customers/setup.js +10 -2
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +5 -0
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +5 -0
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +29 -5
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +48 -11
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/data_sync/data/validators.js +13 -2
- package/dist/modules/data_sync/data/validators.js.map +2 -2
- package/dist/modules/data_sync/lib/schedule-value.js +30 -0
- package/dist/modules/data_sync/lib/schedule-value.js.map +7 -0
- package/dist/modules/eudr/data/validators.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +3 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js +3 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +2 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.js +3 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js +3 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js.map +2 -2
- package/dist/modules/portal/lib/navigation.js +3 -4
- package/dist/modules/portal/lib/navigation.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +79 -14
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/page.js +122 -86
- package/dist/modules/staff/backend/staff/time-tracking/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +38 -17
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js +45 -18
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js.map +2 -2
- package/dist/modules/staff/extension-points.js +16 -0
- package/dist/modules/staff/extension-points.js.map +2 -2
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js +3 -4
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js.map +2 -2
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js +3 -4
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js +14 -13
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js +60 -41
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/draft-reply/route.js +3 -4
- package/dist/modules/warranty_claims/api/ai/draft-reply/route.js.map +3 -3
- package/dist/modules/warranty_claims/api/ai/suggest/route.js +3 -3
- package/dist/modules/warranty_claims/api/ai/suggest/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/assign/route.js +3 -3
- package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/entitlement/route.js +2 -2
- package/dist/modules/warranty_claims/api/entitlement/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +4 -4
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/external/claims/route.js +3 -3
- package/dist/modules/warranty_claims/api/external/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/settings-general/route.js +4 -4
- package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/stats/route.js +2 -2
- package/dist/modules/warranty_claims/api/stats/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/submit/route.js +3 -3
- package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/transition/route.js +3 -3
- package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js +3 -3
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
- package/dist/modules/warranty_claims/backend/page.js +4 -2
- package/dist/modules/warranty_claims/backend/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js +9 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/settings/page.js +4 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/settings/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js +3 -4
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.js +3 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -1
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js +2 -2
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/lib/event-trigger-service.js +56 -1
- package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/api/users/acl/route.ts +4 -3
- package/src/modules/auth/components/AclEditor.tsx +38 -2
- package/src/modules/auth/i18n/de.json +5 -1
- package/src/modules/auth/i18n/en.json +5 -1
- package/src/modules/auth/i18n/es.json +5 -1
- package/src/modules/auth/i18n/ko.json +5 -1
- package/src/modules/auth/i18n/pl.json +5 -1
- package/src/modules/customers/backend/customers/companies/page.tsx +6 -4
- package/src/modules/customers/backend/customers/deals/map/components/DealsMapView.tsx +6 -4
- package/src/modules/customers/backend/customers/deals/page.tsx +6 -4
- package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.tsx +5 -3
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +6 -4
- package/src/modules/customers/backend/customers/people/page.tsx +6 -4
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +9 -2
- package/src/modules/customers/components/calendar/editor/hooks.ts +4 -2
- package/src/modules/customers/components/calendar/editor/lookups.ts +12 -4
- package/src/modules/customers/components/detail/AssignRoleDialog.tsx +4 -1
- package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +4 -2
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +15 -7
- package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +16 -7
- package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +4 -2
- package/src/modules/customers/components/detail/schedule/fieldConfig.ts +15 -0
- package/src/modules/customers/components/detail/schedule/index.ts +1 -1
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +16 -9
- package/src/modules/customers/components/formConfig.tsx +35 -12
- package/src/modules/customers/data/validators.ts +14 -8
- package/src/modules/customers/i18n/de.json +1 -0
- package/src/modules/customers/i18n/en.json +1 -0
- package/src/modules/customers/i18n/es.json +1 -0
- package/src/modules/customers/i18n/ko.json +1 -0
- package/src/modules/customers/i18n/pl.json +1 -0
- package/src/modules/customers/lib/assignableStaff.ts +172 -34
- package/src/modules/customers/setup.ts +8 -0
- package/src/modules/data_sync/api/schedules/[id]/route.ts +5 -0
- package/src/modules/data_sync/api/schedules/route.ts +5 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +48 -4
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +61 -2
- package/src/modules/data_sync/data/validators.ts +26 -2
- package/src/modules/data_sync/i18n/de.json +2 -0
- package/src/modules/data_sync/i18n/en.json +2 -0
- package/src/modules/data_sync/i18n/es.json +2 -0
- package/src/modules/data_sync/i18n/ko.json +2 -0
- package/src/modules/data_sync/i18n/pl.json +2 -0
- package/src/modules/data_sync/lib/schedule-value.ts +45 -0
- package/src/modules/eudr/data/validators.ts +3 -4
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +5 -4
- package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.tsx +3 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +2 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/page.tsx +5 -5
- package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.tsx +5 -4
- package/src/modules/portal/i18n/de.json +1 -0
- package/src/modules/portal/i18n/en.json +1 -0
- package/src/modules/portal/i18n/es.json +1 -0
- package/src/modules/portal/i18n/ko.json +1 -0
- package/src/modules/portal/i18n/pl.json +1 -0
- package/src/modules/portal/lib/navigation.ts +8 -3
- package/src/modules/query_index/lib/engine.ts +111 -16
- package/src/modules/staff/backend/staff/time-tracking/page.tsx +161 -130
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +55 -43
- package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx +61 -38
- package/src/modules/staff/extension-points.ts +16 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.tsx +3 -4
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.tsx +3 -4
- package/src/modules/staff/i18n/de.json +3 -0
- package/src/modules/staff/i18n/en.json +3 -0
- package/src/modules/staff/i18n/es.json +3 -0
- package/src/modules/staff/i18n/ko.json +3 -0
- package/src/modules/staff/i18n/pl.json +3 -0
- package/src/modules/staff/lib/time-tracking-ui/ReportSheet.tsx +32 -29
- package/src/modules/staff/lib/time-tracking-ui/TaskListView.tsx +89 -79
- package/src/modules/warranty_claims/api/ai/assess/route.ts +2 -2
- package/src/modules/warranty_claims/api/ai/draft-reply/route.ts +2 -3
- package/src/modules/warranty_claims/api/ai/suggest/route.ts +3 -3
- package/src/modules/warranty_claims/api/assign/route.ts +3 -3
- package/src/modules/warranty_claims/api/credit-memo/route.ts +2 -2
- package/src/modules/warranty_claims/api/entitlement/route.ts +2 -2
- package/src/modules/warranty_claims/api/events/route.ts +4 -4
- package/src/modules/warranty_claims/api/external/claims/route.ts +3 -3
- package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/events/route.ts +2 -2
- package/src/modules/warranty_claims/api/receiving/route.ts +2 -2
- package/src/modules/warranty_claims/api/replacement-order/route.ts +2 -2
- package/src/modules/warranty_claims/api/return-label/route.ts +2 -2
- package/src/modules/warranty_claims/api/risk/route.ts +2 -2
- package/src/modules/warranty_claims/api/sales-return/route.ts +2 -2
- package/src/modules/warranty_claims/api/settings-general/route.ts +4 -4
- package/src/modules/warranty_claims/api/stats/route.ts +2 -2
- package/src/modules/warranty_claims/api/submit/route.ts +3 -3
- package/src/modules/warranty_claims/api/transition/route.ts +3 -3
- package/src/modules/warranty_claims/api/vendor-recovery/route.ts +3 -3
- package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +2 -2
- package/src/modules/warranty_claims/backend/page.tsx +4 -2
- package/src/modules/warranty_claims/backend/warranty_claims/[id]/page.tsx +9 -2
- package/src/modules/warranty_claims/backend/warranty_claims/settings/page.tsx +4 -2
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.tsx +5 -4
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.tsx +5 -2
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +5 -2
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -1
- package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.tsx +2 -2
- package/src/modules/workflows/i18n/de.json +1 -1
- package/src/modules/workflows/i18n/en.json +1 -1
- package/src/modules/workflows/i18n/es.json +1 -1
- package/src/modules/workflows/i18n/ko.json +1 -1
- package/src/modules/workflows/i18n/pl.json +1 -1
- package/src/modules/workflows/lib/event-trigger-service.ts +139 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/workflows/components/DefinitionTriggersEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useState, useCallback } from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { SwitchField } from '@open-mercato/ui/primitives/switch-field'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport {\n Drawer,\n DrawerBody,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} from '@open-mercato/ui/primitives/drawer'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { ConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { EventPatternInput } from '@open-mercato/ui/backend/inputs/EventPatternInput'\nimport { ComboboxInput, type ComboboxOption } from '@open-mercato/ui/backend/inputs/ComboboxInput'\nimport { useAvailableEvents } from '@open-mercato/ui/backend/inputs/EventSelect'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { EventPayloadSchemaField } from '@open-mercato/shared/modules/events'\nimport { Plus, Trash2, Edit2, Zap, X } from 'lucide-react'\nimport type { WorkflowDefinitionTrigger } from '../data/entities'\n\ninterface DefinitionTriggersEditorProps {\n value: WorkflowDefinitionTrigger[]\n onChange: (triggers: WorkflowDefinitionTrigger[]) => void\n className?: string\n}\n\nconst FILTER_OPERATORS = [\n { value: 'eq', label: 'Equals' },\n { value: 'neq', label: 'Not Equals' },\n { value: 'gt', label: 'Greater Than' },\n { value: 'gte', label: 'Greater Than or Equal' },\n { value: 'lt', label: 'Less Than' },\n { value: 'lte', label: 'Less Than or Equal' },\n { value: 'contains', label: 'Contains' },\n { value: 'startsWith', label: 'Starts With' },\n { value: 'endsWith', label: 'Ends With' },\n { value: 'in', label: 'In (array)' },\n { value: 'notIn', label: 'Not In (array)' },\n { value: 'exists', label: 'Exists' },\n { value: 'notExists', label: 'Not Exists' },\n { value: 'regex', label: 'Regex Match' },\n] as const\n\ntype TriggerFormValues = {\n triggerId: string\n name: string\n description: string\n eventPattern: string\n enabled: boolean\n priority: number\n filterConditions: Array<{ field: string; operator: string; value: string }>\n contextMappings: Array<{ targetKey: string; sourceExpression: string; defaultValue: string }>\n debounceMs: string\n maxConcurrentInstances: string\n}\n\nconst defaultFormValues: TriggerFormValues = {\n triggerId: '',\n name: '',\n description: '',\n eventPattern: '',\n enabled: true,\n priority: 0,\n filterConditions: [],\n contextMappings: [],\n debounceMs: '',\n maxConcurrentInstances: '',\n}\n\n/**\n * DefinitionTriggersEditor\n *\n * Manages event triggers embedded in workflow definitions.\n * Works with local state - no API calls, changes are saved with the definition.\n */\nexport function DefinitionTriggersEditor({\n value,\n onChange,\n className,\n}: DefinitionTriggersEditorProps) {\n const t = useT()\n const [showDialog, setShowDialog] = useState(false)\n const [editingTrigger, setEditingTrigger] = useState<WorkflowDefinitionTrigger | null>(null)\n const [formValues, setFormValues] = useState<TriggerFormValues>(defaultFormValues)\n const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null)\n const { events: availableEvents } = useAvailableEvents()\n\n // Declared payload fields for the selected event. Only an EXACT event id\n // resolves a schema \u2014 wildcard patterns can match events with different\n // payloads, so they stay free-text with the untyped-payload chip.\n const payloadFields = React.useMemo<ReadonlyArray<EventPayloadSchemaField> | null>(() => {\n const pattern = formValues.eventPattern.trim()\n if (!pattern || pattern.includes('*')) return null\n const event = availableEvents.find(candidate => candidate.id === pattern)\n const fields = event?.payloadSchema?.fields\n return fields && fields.length > 0 ? fields : null\n }, [availableEvents, formValues.eventPattern])\n\n const payloadPathOptions = React.useMemo<ComboboxOption[]>(\n () =>\n (payloadFields ?? []).map(field => ({\n value: field.path,\n label: field.path,\n description: field.optional\n ? `${field.type} \u00B7 ${t('workflows.triggers.payloadField.optional', 'optional')}`\n : field.type,\n })),\n [payloadFields, t],\n )\n\n const payloadFieldType = useCallback(\n (path: string) => payloadFields?.find(field => field.path === path)?.type,\n [payloadFields],\n )\n\n const untypedPayloadChip = formValues.eventPattern.trim() && !payloadFields ? (\n <Badge variant=\"outline\" className=\"text-muted-foreground\">\n {t('workflows.triggers.untypedPayload', 'Untyped event payload')}\n </Badge>\n ) : null\n\n // Generate trigger ID from name\n const generateTriggerId = useCallback((name: string) => {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/(?:^_+|_+$)/g, '')\n .substring(0, 50) || `trigger_${Date.now()}`\n }, [])\n\n // Parse condition value (try JSON, fallback to string)\n const parseConditionValue = (valueStr: string): unknown => {\n try {\n return JSON.parse(valueStr)\n } catch {\n return valueStr\n }\n }\n\n // Build trigger from form values\n const buildTriggerFromForm = useCallback((values: TriggerFormValues): WorkflowDefinitionTrigger => {\n const config: WorkflowDefinitionTrigger['config'] = {}\n\n if (values.filterConditions.length > 0) {\n config.filterConditions = values.filterConditions.map(fc => ({\n field: fc.field,\n operator: fc.operator as any,\n value: parseConditionValue(fc.value),\n }))\n }\n\n if (values.contextMappings.length > 0) {\n config.contextMapping = values.contextMappings.map(cm => ({\n targetKey: cm.targetKey,\n sourceExpression: cm.sourceExpression,\n defaultValue: cm.defaultValue ? parseConditionValue(cm.defaultValue) : undefined,\n }))\n }\n\n if (values.debounceMs) {\n config.debounceMs = parseInt(values.debounceMs, 10)\n }\n\n if (values.maxConcurrentInstances) {\n config.maxConcurrentInstances = parseInt(values.maxConcurrentInstances, 10)\n }\n\n return {\n triggerId: values.triggerId || generateTriggerId(values.name),\n name: values.name,\n description: values.description || null,\n eventPattern: values.eventPattern,\n enabled: values.enabled,\n priority: values.priority,\n config: Object.keys(config).length > 0 ? config : null,\n }\n }, [generateTriggerId])\n\n // Open dialog for creating new trigger\n const handleCreateNew = useCallback(() => {\n setEditingTrigger(null)\n setFormValues(defaultFormValues)\n setShowDialog(true)\n }, [])\n\n // Open dialog for editing trigger\n const handleEdit = useCallback((trigger: WorkflowDefinitionTrigger) => {\n setEditingTrigger(trigger)\n setFormValues({\n triggerId: trigger.triggerId,\n name: trigger.name,\n description: trigger.description || '',\n eventPattern: trigger.eventPattern,\n enabled: trigger.enabled,\n priority: trigger.priority,\n filterConditions: trigger.config?.filterConditions?.map(fc => ({\n field: fc.field,\n operator: fc.operator,\n value: typeof fc.value === 'string' ? fc.value : JSON.stringify(fc.value),\n })) || [],\n contextMappings: trigger.config?.contextMapping?.map(cm => ({\n targetKey: cm.targetKey,\n sourceExpression: cm.sourceExpression,\n defaultValue: cm.defaultValue !== undefined\n ? (typeof cm.defaultValue === 'string' ? cm.defaultValue : JSON.stringify(cm.defaultValue))\n : '',\n })) || [],\n debounceMs: trigger.config?.debounceMs?.toString() || '',\n maxConcurrentInstances: trigger.config?.maxConcurrentInstances?.toString() || '',\n })\n setShowDialog(true)\n }, [])\n\n // Close dialog\n const handleCloseDialog = useCallback(() => {\n setShowDialog(false)\n setEditingTrigger(null)\n setFormValues(defaultFormValues)\n }, [])\n\n // Submit form\n const handleSubmit = useCallback(() => {\n if (!formValues.name.trim()) {\n return\n }\n if (!formValues.eventPattern.trim()) {\n return\n }\n\n const newTrigger = buildTriggerFromForm(formValues)\n\n if (editingTrigger) {\n // Update existing trigger\n onChange(value.map(t => t.triggerId === editingTrigger.triggerId ? newTrigger : t))\n } else {\n // Check for duplicate triggerId\n const existingIds = new Set(value.map(t => t.triggerId))\n if (existingIds.has(newTrigger.triggerId)) {\n // Append timestamp to make unique\n newTrigger.triggerId = `${newTrigger.triggerId}_${Date.now()}`\n }\n // Add new trigger\n onChange([...value, newTrigger])\n }\n\n handleCloseDialog()\n }, [formValues, editingTrigger, buildTriggerFromForm, value, onChange, handleCloseDialog])\n\n // Delete trigger\n const handleDelete = useCallback((triggerId: string) => {\n onChange(value.filter(t => t.triggerId !== triggerId))\n setDeleteConfirmId(null)\n }, [value, onChange])\n\n // Add filter condition\n const addFilterCondition = useCallback(() => {\n setFormValues(prev => ({\n ...prev,\n filterConditions: [...prev.filterConditions, { field: '', operator: 'eq', value: '' }],\n }))\n }, [])\n\n // Remove filter condition\n const removeFilterCondition = useCallback((index: number) => {\n setFormValues(prev => ({\n ...prev,\n filterConditions: prev.filterConditions.filter((_, i) => i !== index),\n }))\n }, [])\n\n // Update filter condition\n const updateFilterCondition = useCallback((index: number, field: string, fieldValue: string) => {\n setFormValues(prev => ({\n ...prev,\n filterConditions: prev.filterConditions.map((fc, i) =>\n i === index ? { ...fc, [field]: fieldValue } : fc\n ),\n }))\n }, [])\n\n // Add context mapping\n const addContextMapping = useCallback(() => {\n setFormValues(prev => ({\n ...prev,\n contextMappings: [...prev.contextMappings, { targetKey: '', sourceExpression: '', defaultValue: '' }],\n }))\n }, [])\n\n // Remove context mapping\n const removeContextMapping = useCallback((index: number) => {\n setFormValues(prev => ({\n ...prev,\n contextMappings: prev.contextMappings.filter((_, i) => i !== index),\n }))\n }, [])\n\n // Update context mapping\n const updateContextMapping = useCallback((index: number, field: string, fieldValue: string) => {\n setFormValues(prev => ({\n ...prev,\n contextMappings: prev.contextMappings.map((cm, i) =>\n i === index ? { ...cm, [field]: fieldValue } : cm\n ),\n }))\n }, [])\n\n return (\n <div className={className}>\n <div className=\"rounded-lg border bg-card p-3 md:p-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between mb-4\">\n <div className=\"flex items-center gap-2\">\n <Zap className=\"w-5 h-5 text-status-warning-icon\" />\n <h3 className=\"text-sm font-semibold uppercase text-muted-foreground\">\n {t('workflows.triggers.title', 'Event Triggers')}\n </h3>\n </div>\n <Button size=\"sm\" variant=\"outline\" onClick={handleCreateNew} className=\"w-full sm:w-auto\">\n <Plus className=\"w-4 h-4 mr-1\" />\n {t('workflows.triggers.add', 'Add Trigger')}\n </Button>\n </div>\n\n <p className=\"text-xs text-muted-foreground mb-4\">\n {t('workflows.triggers.description', 'Configure events that automatically start this workflow. When a matching event occurs in the system, a new workflow instance will be created with the mapped context.')}\n </p>\n\n {value.length === 0 ? (\n <Alert status=\"information\">\n <AlertTitle>{t('workflows.triggers.empty.title', 'No triggers configured')}</AlertTitle>\n <AlertDescription>\n {t('workflows.triggers.empty.description', 'Click \"Add Trigger\" to create an event trigger that automatically starts this workflow.')}\n </AlertDescription>\n </Alert>\n ) : (\n <div className=\"space-y-2\">\n {value.map(trigger => (\n <div\n key={trigger.triggerId}\n className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between p-3 rounded-lg border bg-background hover:bg-accent/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <Badge variant={trigger.enabled ? 'default' : 'secondary'} className=\"shrink-0\">\n {trigger.enabled ? t('common.active', 'Active') : t('common.disabled', 'Disabled')}\n </Badge>\n <div className=\"min-w-0\">\n <div className=\"font-medium text-sm truncate\">{trigger.name}</div>\n <code className=\"text-xs text-muted-foreground truncate block\">{trigger.eventPattern}</code>\n </div>\n </div>\n <div className=\"flex items-center gap-2 self-end sm:self-auto\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={t('workflows.triggers.actions.edit', 'Edit trigger')}\n onClick={() => handleEdit(trigger)}\n >\n <Edit2 className=\"w-4 h-4\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"destructive-ghost\"\n aria-label={t('workflows.triggers.actions.delete', 'Delete trigger')}\n onClick={() => setDeleteConfirmId(trigger.triggerId)}\n >\n <Trash2 className=\"w-4 h-4\" />\n </Button>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Create/Edit rail. It opens ON TOP of the definition metadata drawer\n that hosts this editor, so it slides in over it rather than centring a\n modal on a page whose left half is already a panel. */}\n <Drawer open={showDialog} onOpenChange={setShowDialog}>\n <DrawerContent\n data-testid=\"workflow-trigger-drawer\"\n className=\"w-full max-w-none sm:w-3/5\"\n closeAriaLabel={t('workflows.triggers.dialog.close', 'Close trigger editor')}\n // Cmd/Ctrl+Enter submits \u2014 the project-wide rule this surface never\n // implemented. It also stops the keystroke here so it cannot bubble\n // to the details drawer and save the whole workflow from a\n // half-filled trigger form.\n onKeyDown={(event) => {\n if (!(event.metaKey || event.ctrlKey) || event.key !== 'Enter') return\n event.preventDefault()\n event.stopPropagation()\n if (!formValues.name.trim() || !formValues.eventPattern.trim()) return\n handleSubmit()\n }}\n >\n <DrawerHeader>\n <DrawerTitle>\n {editingTrigger\n ? t('workflows.triggers.dialog.edit.title', 'Edit Event Trigger')\n : t('workflows.triggers.dialog.create.title', 'Create Event Trigger')\n }\n </DrawerTitle>\n <DrawerDescription>\n {t('workflows.triggers.dialog.description', 'Configure when this workflow should be automatically started based on system events.')}\n </DrawerDescription>\n </DrawerHeader>\n\n <DrawerBody className=\"space-y-4 py-4\">\n {/* Basic Info */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-name\">{t('workflows.triggers.fields.name', 'Name')} *</Label>\n <Input\n id=\"trigger-name\"\n value={formValues.name}\n onChange={e => setFormValues(prev => ({ ...prev, name: e.target.value }))}\n placeholder={t('workflows.triggers.placeholders.name', 'Order Created Trigger')}\n />\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-priority\">{t('workflows.triggers.fields.priority', 'Priority')}</Label>\n <Input\n id=\"trigger-priority\"\n type=\"number\"\n value={formValues.priority}\n onChange={e => setFormValues(prev => ({ ...prev, priority: parseInt(e.target.value) || 0 }))}\n placeholder=\"0\"\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.priority', 'Higher priority triggers execute first')}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-description\">{t('workflows.triggers.fields.description', 'Description')}</Label>\n <Textarea\n id=\"trigger-description\"\n value={formValues.description}\n onChange={e => setFormValues(prev => ({ ...prev, description: e.target.value }))}\n placeholder={t('workflows.triggers.placeholders.description', 'Describe when this trigger should fire...')}\n rows={2}\n />\n </div>\n\n {/* Event Pattern */}\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-pattern\">{t('workflows.triggers.fields.eventPattern', 'Event Pattern')} *</Label>\n <EventPatternInput\n value={formValues.eventPattern}\n onChange={eventPattern => setFormValues(prev => ({ ...prev, eventPattern }))}\n placeholder={t('workflows.triggers.placeholders.eventPattern')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.eventPattern', 'Use * as wildcard: \"sales.orders.*\" matches any order event')}\n </p>\n </div>\n\n {/* Enabled Switch */}\n <SwitchField\n id=\"trigger-enabled\"\n label={t('workflows.triggers.fields.enabled', 'Enabled')}\n flip\n checked={formValues.enabled}\n onCheckedChange={checked => setFormValues(prev => ({ ...prev, enabled: checked }))}\n />\n\n {/* Filter Conditions */}\n <div className=\"space-y-2\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2\">\n <Label>{t('workflows.triggers.fields.filterConditions', 'Filter Conditions')}</Label>\n {untypedPayloadChip}\n </div>\n <Button size=\"sm\" variant=\"ghost\" onClick={addFilterCondition} className=\"w-full sm:w-auto\">\n <Plus className=\"w-4 h-4 mr-1\" />\n {t('workflows.triggers.addCondition', 'Add Condition')}\n </Button>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.filterConditions', 'Only trigger when the event payload matches these conditions (all must match)')}\n </p>\n {formValues.filterConditions.map((fc, index) => {\n const conditionFieldType = payloadFieldType(fc.field)\n return (\n <div key={index} className=\"flex flex-wrap items-center gap-2\">\n {payloadFields ? (\n <div className=\"w-full sm:w-1/3\">\n <ComboboxInput\n value={fc.field}\n onChange={next => updateFilterCondition(index, 'field', next)}\n suggestions={payloadPathOptions}\n placeholder={t('workflows.triggers.placeholders.status', 'status')}\n allowCustomValues\n />\n </div>\n ) : (\n <Input\n value={fc.field}\n onChange={e => updateFilterCondition(index, 'field', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.status', 'status')}\n className=\"w-full sm:w-1/3\"\n />\n )}\n <Select\n value={fc.operator}\n onValueChange={(value) => updateFilterCondition(index, 'operator', value)}\n >\n <SelectTrigger size=\"lg\" className=\"w-full sm:w-[140px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {FILTER_OPERATORS.map(op => (\n <SelectItem key={op.value} value={op.value}>\n {op.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {conditionFieldType === 'boolean' ? (\n <Select\n value={fc.value || undefined}\n onValueChange={(value) => updateFilterCondition(index, 'value', value ?? '')}\n >\n <SelectTrigger size=\"lg\" className=\"flex-1 min-w-0\">\n <SelectValue placeholder={t('workflows.triggers.values.placeholder', 'Select value')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"true\">{t('workflows.triggers.values.true', 'True')}</SelectItem>\n <SelectItem value=\"false\">{t('workflows.triggers.values.false', 'False')}</SelectItem>\n </SelectContent>\n </Select>\n ) : (\n <Input\n type={conditionFieldType === 'number' ? 'number' : 'text'}\n value={fc.value}\n onChange={e => updateFilterCondition(index, 'value', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.submitted', 'submitted')}\n className=\"flex-1 min-w-0\"\n />\n )}\n <Button size=\"icon\" variant=\"ghost\" className=\"shrink-0\" onClick={() => removeFilterCondition(index)}>\n <X className=\"w-4 h-4\" />\n </Button>\n </div>\n )\n })}\n </div>\n\n {/* Context Mapping */}\n <div className=\"space-y-2\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2\">\n <Label>{t('workflows.triggers.fields.contextMapping', 'Context Mapping')}</Label>\n {untypedPayloadChip}\n </div>\n <Button size=\"sm\" variant=\"ghost\" onClick={addContextMapping} className=\"w-full sm:w-auto\">\n <Plus className=\"w-4 h-4 mr-1\" />\n {t('workflows.triggers.addMapping', 'Add Mapping')}\n </Button>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.contextMapping', \"Map values from the event payload to the workflow's initial context\")}\n </p>\n {formValues.contextMappings.map((cm, index) => (\n <div key={index} className=\"flex flex-wrap items-center gap-2\">\n <Input\n value={cm.targetKey}\n onChange={e => updateContextMapping(index, 'targetKey', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.orderId', 'orderId')}\n className=\"w-full sm:w-1/3\"\n />\n <span className=\"hidden sm:inline text-muted-foreground\">=</span>\n {payloadFields ? (\n <div className=\"flex-1 min-w-0\">\n <ComboboxInput\n value={cm.sourceExpression}\n onChange={next => updateContextMapping(index, 'sourceExpression', next)}\n suggestions={payloadPathOptions}\n placeholder={t('workflows.triggers.placeholders.sourceExpression', 'id')}\n allowCustomValues\n />\n </div>\n ) : (\n <Input\n value={cm.sourceExpression}\n onChange={e => updateContextMapping(index, 'sourceExpression', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.sourceExpression', 'id')}\n className=\"flex-1 min-w-0\"\n />\n )}\n <Input\n value={cm.defaultValue}\n onChange={e => updateContextMapping(index, 'defaultValue', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.defaultValue', 'default')}\n className=\"w-full sm:w-24\"\n />\n <Button size=\"icon\" variant=\"ghost\" className=\"shrink-0\" onClick={() => removeContextMapping(index)}>\n <X className=\"w-4 h-4\" />\n </Button>\n </div>\n ))}\n </div>\n\n {/* Advanced Options */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-debounce\">{t('workflows.triggers.fields.debounceMs', 'Debounce (ms)')}</Label>\n <Input\n id=\"trigger-debounce\"\n type=\"number\"\n value={formValues.debounceMs}\n onChange={e => setFormValues(prev => ({ ...prev, debounceMs: e.target.value }))}\n placeholder={t('workflows.triggers.placeholders.debounce', 'e.g. 1000')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.debounce', 'Wait this many milliseconds after an event before starting, so a burst of similar events starts only one workflow. Leave empty to start immediately.')}\n </p>\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-max-concurrent\">{t('workflows.triggers.fields.maxConcurrent', 'Max Concurrent Instances')}</Label>\n <Input\n id=\"trigger-max-concurrent\"\n type=\"number\"\n value={formValues.maxConcurrentInstances}\n onChange={e => setFormValues(prev => ({ ...prev, maxConcurrentInstances: e.target.value }))}\n placeholder={t('workflows.triggers.placeholders.unlimited', 'Unlimited')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.maxConcurrent', 'Cap how many instances this trigger can have running at the same time; events past the cap are skipped. Leave empty for no limit.')}\n </p>\n </div>\n </div>\n </DrawerBody>\n\n <DrawerFooter>\n <Button variant=\"outline\" onClick={handleCloseDialog}>\n {t('common.cancel', 'Cancel')}\n </Button>\n <Button\n onClick={handleSubmit}\n disabled={!formValues.name.trim() || !formValues.eventPattern.trim()}\n >\n {editingTrigger\n ? t('common.update', 'Update')\n : t('common.create', 'Create')\n }\n </Button>\n </DrawerFooter>\n </DrawerContent>\n </Drawer>\n\n {/* Removing a trigger is a yes/no interruption, so it stays a blocking\n confirmation rather than becoming a rail. */}\n <ConfirmDialog\n open={!!deleteConfirmId}\n onOpenChange={(next) => { if (!next) setDeleteConfirmId(null) }}\n title={t('workflows.triggers.delete.title', 'Delete Event Trigger?')}\n text={t('workflows.triggers.delete.description', 'This will remove the event trigger. The change will take effect when you save the workflow definition.')}\n confirmText={t('common.delete', 'Delete')}\n cancelText={t('common.cancel', 'Cancel')}\n variant=\"destructive\"\n onConfirm={() => { if (deleteConfirmId) handleDelete(deleteConfirmId) }}\n />\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useState, useCallback } from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { SwitchField } from '@open-mercato/ui/primitives/switch-field'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport {\n Drawer,\n DrawerBody,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} from '@open-mercato/ui/primitives/drawer'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { ConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { EventPatternInput } from '@open-mercato/ui/backend/inputs/EventPatternInput'\nimport { ComboboxInput, type ComboboxOption } from '@open-mercato/ui/backend/inputs/ComboboxInput'\nimport { useAvailableEvents } from '@open-mercato/ui/backend/inputs/EventSelect'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { EventPayloadSchemaField } from '@open-mercato/shared/modules/events'\nimport { Plus, Trash2, Edit2, Zap, X } from 'lucide-react'\nimport type { WorkflowDefinitionTrigger } from '../data/entities'\n\ninterface DefinitionTriggersEditorProps {\n value: WorkflowDefinitionTrigger[]\n onChange: (triggers: WorkflowDefinitionTrigger[]) => void\n className?: string\n}\n\nconst FILTER_OPERATORS = [\n { value: 'eq', label: 'Equals' },\n { value: 'neq', label: 'Not Equals' },\n { value: 'gt', label: 'Greater Than' },\n { value: 'gte', label: 'Greater Than or Equal' },\n { value: 'lt', label: 'Less Than' },\n { value: 'lte', label: 'Less Than or Equal' },\n { value: 'contains', label: 'Contains' },\n { value: 'startsWith', label: 'Starts With' },\n { value: 'endsWith', label: 'Ends With' },\n { value: 'in', label: 'In (array)' },\n { value: 'notIn', label: 'Not In (array)' },\n { value: 'exists', label: 'Exists' },\n { value: 'notExists', label: 'Not Exists' },\n { value: 'regex', label: 'Regex Match' },\n] as const\n\ntype TriggerFormValues = {\n triggerId: string\n name: string\n description: string\n eventPattern: string\n enabled: boolean\n priority: number\n filterConditions: Array<{ field: string; operator: string; value: string }>\n contextMappings: Array<{ targetKey: string; sourceExpression: string; defaultValue: string }>\n debounceMs: string\n maxConcurrentInstances: string\n}\n\nconst defaultFormValues: TriggerFormValues = {\n triggerId: '',\n name: '',\n description: '',\n eventPattern: '',\n enabled: true,\n priority: 0,\n filterConditions: [],\n contextMappings: [],\n debounceMs: '',\n maxConcurrentInstances: '',\n}\n\n/**\n * DefinitionTriggersEditor\n *\n * Manages event triggers embedded in workflow definitions.\n * Works with local state - no API calls, changes are saved with the definition.\n */\nexport function DefinitionTriggersEditor({\n value,\n onChange,\n className,\n}: DefinitionTriggersEditorProps) {\n const t = useT()\n const [showDialog, setShowDialog] = useState(false)\n const [editingTrigger, setEditingTrigger] = useState<WorkflowDefinitionTrigger | null>(null)\n const [formValues, setFormValues] = useState<TriggerFormValues>(defaultFormValues)\n const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null)\n const { events: availableEvents } = useAvailableEvents()\n\n // Declared payload fields for the selected event. Only an EXACT event id\n // resolves a schema \u2014 wildcard patterns can match events with different\n // payloads, so they stay free-text with the untyped-payload chip.\n const payloadFields = React.useMemo<ReadonlyArray<EventPayloadSchemaField> | null>(() => {\n const pattern = formValues.eventPattern.trim()\n if (!pattern || pattern.includes('*')) return null\n const event = availableEvents.find(candidate => candidate.id === pattern)\n const fields = event?.payloadSchema?.fields\n return fields && fields.length > 0 ? fields : null\n }, [availableEvents, formValues.eventPattern])\n\n const payloadPathOptions = React.useMemo<ComboboxOption[]>(\n () =>\n (payloadFields ?? []).map(field => ({\n value: field.path,\n label: field.path,\n description: field.optional\n ? `${field.type} \u00B7 ${t('workflows.triggers.payloadField.optional', 'optional')}`\n : field.type,\n })),\n [payloadFields, t],\n )\n\n const payloadFieldType = useCallback(\n (path: string) => payloadFields?.find(field => field.path === path)?.type,\n [payloadFields],\n )\n\n const untypedPayloadChip = formValues.eventPattern.trim() && !payloadFields ? (\n <Badge variant=\"outline\" className=\"text-muted-foreground\">\n {t('workflows.triggers.untypedPayload', 'Untyped event payload')}\n </Badge>\n ) : null\n\n // Generate trigger ID from name\n const generateTriggerId = useCallback((name: string) => {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/(?:^_+|_+$)/g, '')\n .substring(0, 50) || `trigger_${Date.now()}`\n }, [])\n\n // Parse condition value (try JSON, fallback to string)\n const parseConditionValue = (valueStr: string): unknown => {\n try {\n return JSON.parse(valueStr)\n } catch {\n return valueStr\n }\n }\n\n // Build trigger from form values\n const buildTriggerFromForm = useCallback((values: TriggerFormValues): WorkflowDefinitionTrigger => {\n const config: WorkflowDefinitionTrigger['config'] = {}\n\n if (values.filterConditions.length > 0) {\n config.filterConditions = values.filterConditions.map(fc => ({\n field: fc.field,\n operator: fc.operator as any,\n value: parseConditionValue(fc.value),\n }))\n }\n\n if (values.contextMappings.length > 0) {\n config.contextMapping = values.contextMappings.map(cm => ({\n targetKey: cm.targetKey,\n sourceExpression: cm.sourceExpression,\n defaultValue: cm.defaultValue ? parseConditionValue(cm.defaultValue) : undefined,\n }))\n }\n\n if (values.debounceMs) {\n config.debounceMs = parseInt(values.debounceMs, 10)\n }\n\n if (values.maxConcurrentInstances) {\n config.maxConcurrentInstances = parseInt(values.maxConcurrentInstances, 10)\n }\n\n return {\n triggerId: values.triggerId || generateTriggerId(values.name),\n name: values.name,\n description: values.description || null,\n eventPattern: values.eventPattern,\n enabled: values.enabled,\n priority: values.priority,\n config: Object.keys(config).length > 0 ? config : null,\n }\n }, [generateTriggerId])\n\n // Open dialog for creating new trigger\n const handleCreateNew = useCallback(() => {\n setEditingTrigger(null)\n setFormValues(defaultFormValues)\n setShowDialog(true)\n }, [])\n\n // Open dialog for editing trigger\n const handleEdit = useCallback((trigger: WorkflowDefinitionTrigger) => {\n setEditingTrigger(trigger)\n setFormValues({\n triggerId: trigger.triggerId,\n name: trigger.name,\n description: trigger.description || '',\n eventPattern: trigger.eventPattern,\n enabled: trigger.enabled,\n priority: trigger.priority,\n filterConditions: trigger.config?.filterConditions?.map(fc => ({\n field: fc.field,\n operator: fc.operator,\n value: typeof fc.value === 'string' ? fc.value : JSON.stringify(fc.value),\n })) || [],\n contextMappings: trigger.config?.contextMapping?.map(cm => ({\n targetKey: cm.targetKey,\n sourceExpression: cm.sourceExpression,\n defaultValue: cm.defaultValue !== undefined\n ? (typeof cm.defaultValue === 'string' ? cm.defaultValue : JSON.stringify(cm.defaultValue))\n : '',\n })) || [],\n debounceMs: trigger.config?.debounceMs?.toString() || '',\n maxConcurrentInstances: trigger.config?.maxConcurrentInstances?.toString() || '',\n })\n setShowDialog(true)\n }, [])\n\n // Close dialog\n const handleCloseDialog = useCallback(() => {\n setShowDialog(false)\n setEditingTrigger(null)\n setFormValues(defaultFormValues)\n }, [])\n\n // Submit form\n const handleSubmit = useCallback(() => {\n if (!formValues.name.trim()) {\n return\n }\n if (!formValues.eventPattern.trim()) {\n return\n }\n\n const newTrigger = buildTriggerFromForm(formValues)\n\n if (editingTrigger) {\n // Update existing trigger\n onChange(value.map(t => t.triggerId === editingTrigger.triggerId ? newTrigger : t))\n } else {\n // Check for duplicate triggerId\n const existingIds = new Set(value.map(t => t.triggerId))\n if (existingIds.has(newTrigger.triggerId)) {\n // Append timestamp to make unique\n newTrigger.triggerId = `${newTrigger.triggerId}_${Date.now()}`\n }\n // Add new trigger\n onChange([...value, newTrigger])\n }\n\n handleCloseDialog()\n }, [formValues, editingTrigger, buildTriggerFromForm, value, onChange, handleCloseDialog])\n\n // Delete trigger\n const handleDelete = useCallback((triggerId: string) => {\n onChange(value.filter(t => t.triggerId !== triggerId))\n setDeleteConfirmId(null)\n }, [value, onChange])\n\n // Add filter condition\n const addFilterCondition = useCallback(() => {\n setFormValues(prev => ({\n ...prev,\n filterConditions: [...prev.filterConditions, { field: '', operator: 'eq', value: '' }],\n }))\n }, [])\n\n // Remove filter condition\n const removeFilterCondition = useCallback((index: number) => {\n setFormValues(prev => ({\n ...prev,\n filterConditions: prev.filterConditions.filter((_, i) => i !== index),\n }))\n }, [])\n\n // Update filter condition\n const updateFilterCondition = useCallback((index: number, field: string, fieldValue: string) => {\n setFormValues(prev => ({\n ...prev,\n filterConditions: prev.filterConditions.map((fc, i) =>\n i === index ? { ...fc, [field]: fieldValue } : fc\n ),\n }))\n }, [])\n\n // Add context mapping\n const addContextMapping = useCallback(() => {\n setFormValues(prev => ({\n ...prev,\n contextMappings: [...prev.contextMappings, { targetKey: '', sourceExpression: '', defaultValue: '' }],\n }))\n }, [])\n\n // Remove context mapping\n const removeContextMapping = useCallback((index: number) => {\n setFormValues(prev => ({\n ...prev,\n contextMappings: prev.contextMappings.filter((_, i) => i !== index),\n }))\n }, [])\n\n // Update context mapping\n const updateContextMapping = useCallback((index: number, field: string, fieldValue: string) => {\n setFormValues(prev => ({\n ...prev,\n contextMappings: prev.contextMappings.map((cm, i) =>\n i === index ? { ...cm, [field]: fieldValue } : cm\n ),\n }))\n }, [])\n\n return (\n <div className={className}>\n <div className=\"rounded-lg border bg-card p-3 md:p-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between mb-4\">\n <div className=\"flex items-center gap-2\">\n <Zap className=\"w-5 h-5 text-status-warning-icon\" />\n <h3 className=\"text-sm font-semibold uppercase text-muted-foreground\">\n {t('workflows.triggers.title', 'Event Triggers')}\n </h3>\n </div>\n <Button size=\"sm\" variant=\"outline\" onClick={handleCreateNew} className=\"w-full sm:w-auto\">\n <Plus className=\"w-4 h-4 mr-1\" />\n {t('workflows.triggers.add', 'Add Trigger')}\n </Button>\n </div>\n\n <p className=\"text-xs text-muted-foreground mb-4\">\n {t('workflows.triggers.description', 'Configure events that automatically start this workflow. When a matching event occurs in the system, a new workflow instance will be created with the mapped context.')}\n </p>\n\n {value.length === 0 ? (\n <Alert status=\"information\">\n <AlertTitle>{t('workflows.triggers.empty.title', 'No triggers configured')}</AlertTitle>\n <AlertDescription>\n {t('workflows.triggers.empty.description', 'Click \"Add Trigger\" to create an event trigger that automatically starts this workflow.')}\n </AlertDescription>\n </Alert>\n ) : (\n <div className=\"space-y-2\">\n {value.map(trigger => (\n <div\n key={trigger.triggerId}\n className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between p-3 rounded-lg border bg-background hover:bg-accent/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <Badge variant={trigger.enabled ? 'default' : 'secondary'} className=\"shrink-0\">\n {trigger.enabled ? t('common.active', 'Active') : t('common.disabled', 'Disabled')}\n </Badge>\n <div className=\"min-w-0\">\n <div className=\"font-medium text-sm truncate\">{trigger.name}</div>\n <code className=\"text-xs text-muted-foreground truncate block\">{trigger.eventPattern}</code>\n </div>\n </div>\n <div className=\"flex items-center gap-2 self-end sm:self-auto\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={t('workflows.triggers.actions.edit', 'Edit trigger')}\n onClick={() => handleEdit(trigger)}\n >\n <Edit2 className=\"w-4 h-4\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"destructive-ghost\"\n aria-label={t('workflows.triggers.actions.delete', 'Delete trigger')}\n onClick={() => setDeleteConfirmId(trigger.triggerId)}\n >\n <Trash2 className=\"w-4 h-4\" />\n </Button>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Create/Edit rail. It opens ON TOP of the definition metadata drawer\n that hosts this editor, so it slides in over it rather than centring a\n modal on a page whose left half is already a panel. */}\n <Drawer open={showDialog} onOpenChange={setShowDialog}>\n <DrawerContent\n data-testid=\"workflow-trigger-drawer\"\n className=\"w-full max-w-none sm:w-3/5\"\n closeAriaLabel={t('workflows.triggers.dialog.close', 'Close trigger editor')}\n // Cmd/Ctrl+Enter submits \u2014 the project-wide rule this surface never\n // implemented. It also stops the keystroke here so it cannot bubble\n // to the details drawer and save the whole workflow from a\n // half-filled trigger form.\n onKeyDown={(event) => {\n if (!(event.metaKey || event.ctrlKey) || event.key !== 'Enter') return\n event.preventDefault()\n event.stopPropagation()\n if (!formValues.name.trim() || !formValues.eventPattern.trim()) return\n handleSubmit()\n }}\n >\n <DrawerHeader>\n <DrawerTitle>\n {editingTrigger\n ? t('workflows.triggers.dialog.edit.title', 'Edit Event Trigger')\n : t('workflows.triggers.dialog.create.title', 'Create Event Trigger')\n }\n </DrawerTitle>\n <DrawerDescription>\n {t('workflows.triggers.dialog.description', 'Configure when this workflow should be automatically started based on system events.')}\n </DrawerDescription>\n </DrawerHeader>\n\n <DrawerBody className=\"space-y-4 py-4\">\n {/* Basic Info */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-name\">{t('workflows.triggers.fields.name', 'Name')} *</Label>\n <Input\n id=\"trigger-name\"\n value={formValues.name}\n onChange={e => setFormValues(prev => ({ ...prev, name: e.target.value }))}\n placeholder={t('workflows.triggers.placeholders.name', 'Order Created Trigger')}\n />\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-priority\">{t('workflows.triggers.fields.priority', 'Priority')}</Label>\n <Input\n id=\"trigger-priority\"\n type=\"number\"\n value={formValues.priority}\n onChange={e => setFormValues(prev => ({ ...prev, priority: parseInt(e.target.value) || 0 }))}\n placeholder=\"0\"\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.priority', 'Higher priority triggers execute first')}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-description\">{t('workflows.triggers.fields.description', 'Description')}</Label>\n <Textarea\n id=\"trigger-description\"\n value={formValues.description}\n onChange={e => setFormValues(prev => ({ ...prev, description: e.target.value }))}\n placeholder={t('workflows.triggers.placeholders.description', 'Describe when this trigger should fire...')}\n rows={2}\n />\n </div>\n\n {/* Event Pattern */}\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-pattern\">{t('workflows.triggers.fields.eventPattern', 'Event Pattern')} *</Label>\n <EventPatternInput\n value={formValues.eventPattern}\n onChange={eventPattern => setFormValues(prev => ({ ...prev, eventPattern }))}\n placeholder={t('workflows.triggers.placeholders.eventPattern')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.eventPattern', 'Use * as wildcard: \"sales.orders.*\" matches any order event')}\n </p>\n </div>\n\n {/* Enabled Switch */}\n <SwitchField\n id=\"trigger-enabled\"\n label={t('workflows.triggers.fields.enabled', 'Enabled')}\n flip\n checked={formValues.enabled}\n onCheckedChange={checked => setFormValues(prev => ({ ...prev, enabled: checked }))}\n />\n\n {/* Filter Conditions */}\n <div className=\"space-y-2\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2\">\n <Label>{t('workflows.triggers.fields.filterConditions', 'Filter Conditions')}</Label>\n {untypedPayloadChip}\n </div>\n <Button size=\"sm\" variant=\"ghost\" onClick={addFilterCondition} className=\"w-full sm:w-auto\">\n <Plus className=\"w-4 h-4 mr-1\" />\n {t('workflows.triggers.addCondition', 'Add Condition')}\n </Button>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.filterConditions', 'Only trigger when the event payload matches these conditions (all must match)')}\n </p>\n {formValues.filterConditions.map((fc, index) => {\n const conditionFieldType = payloadFieldType(fc.field)\n return (\n <div key={index} className=\"flex flex-wrap items-center gap-2\">\n {payloadFields ? (\n <div className=\"w-full sm:w-1/3\">\n <ComboboxInput\n value={fc.field}\n onChange={next => updateFilterCondition(index, 'field', next)}\n suggestions={payloadPathOptions}\n placeholder={t('workflows.triggers.placeholders.status', 'status')}\n allowCustomValues\n />\n </div>\n ) : (\n <Input\n value={fc.field}\n onChange={e => updateFilterCondition(index, 'field', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.status', 'status')}\n className=\"w-full sm:w-1/3\"\n />\n )}\n <Select\n value={fc.operator}\n onValueChange={(value) => updateFilterCondition(index, 'operator', value)}\n >\n <SelectTrigger size=\"lg\" className=\"w-full sm:w-[140px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {FILTER_OPERATORS.map(op => (\n <SelectItem key={op.value} value={op.value}>\n {op.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {conditionFieldType === 'boolean' ? (\n <Select\n value={fc.value || undefined}\n onValueChange={(value) => updateFilterCondition(index, 'value', value ?? '')}\n >\n <SelectTrigger size=\"lg\" className=\"flex-1 min-w-0\">\n <SelectValue placeholder={t('workflows.triggers.values.placeholder', 'Select value')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"true\">{t('workflows.triggers.values.true', 'True')}</SelectItem>\n <SelectItem value=\"false\">{t('workflows.triggers.values.false', 'False')}</SelectItem>\n </SelectContent>\n </Select>\n ) : (\n <Input\n type={conditionFieldType === 'number' ? 'number' : 'text'}\n value={fc.value}\n onChange={e => updateFilterCondition(index, 'value', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.submitted', 'submitted')}\n className=\"flex-1 min-w-0\"\n />\n )}\n <Button size=\"icon\" variant=\"ghost\" className=\"shrink-0\" onClick={() => removeFilterCondition(index)}>\n <X className=\"w-4 h-4\" />\n </Button>\n </div>\n )\n })}\n </div>\n\n {/* Context Mapping */}\n <div className=\"space-y-2\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2\">\n <Label>{t('workflows.triggers.fields.contextMapping', 'Context Mapping')}</Label>\n {untypedPayloadChip}\n </div>\n <Button size=\"sm\" variant=\"ghost\" onClick={addContextMapping} className=\"w-full sm:w-auto\">\n <Plus className=\"w-4 h-4 mr-1\" />\n {t('workflows.triggers.addMapping', 'Add Mapping')}\n </Button>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.contextMapping', \"Map values from the event payload to the workflow's initial context\")}\n </p>\n {formValues.contextMappings.map((cm, index) => (\n <div key={index} className=\"flex flex-wrap items-center gap-2\">\n <Input\n value={cm.targetKey}\n onChange={e => updateContextMapping(index, 'targetKey', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.orderId', 'orderId')}\n className=\"w-full sm:w-1/3\"\n />\n <span className=\"hidden sm:inline text-muted-foreground\">=</span>\n {payloadFields ? (\n <div className=\"flex-1 min-w-0\">\n <ComboboxInput\n value={cm.sourceExpression}\n onChange={next => updateContextMapping(index, 'sourceExpression', next)}\n suggestions={payloadPathOptions}\n placeholder={t('workflows.triggers.placeholders.sourceExpression', 'id')}\n allowCustomValues\n />\n </div>\n ) : (\n <Input\n value={cm.sourceExpression}\n onChange={e => updateContextMapping(index, 'sourceExpression', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.sourceExpression', 'id')}\n className=\"flex-1 min-w-0\"\n />\n )}\n <Input\n value={cm.defaultValue}\n onChange={e => updateContextMapping(index, 'defaultValue', e.target.value)}\n placeholder={t('workflows.triggers.placeholders.defaultValue', 'default')}\n className=\"w-full sm:w-24\"\n />\n <Button size=\"icon\" variant=\"ghost\" className=\"shrink-0\" onClick={() => removeContextMapping(index)}>\n <X className=\"w-4 h-4\" />\n </Button>\n </div>\n ))}\n </div>\n\n {/* Advanced Options */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-debounce\">{t('workflows.triggers.fields.debounceMs', 'Debounce (ms)')}</Label>\n <Input\n id=\"trigger-debounce\"\n type=\"number\"\n value={formValues.debounceMs}\n onChange={e => setFormValues(prev => ({ ...prev, debounceMs: e.target.value }))}\n placeholder={t('workflows.triggers.placeholders.debounce', 'e.g. 1000')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.debounce', 'The first matching event starts an instance immediately; further events for the same record within this many milliseconds are skipped. Leave empty to start an instance for every event.')}\n </p>\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"trigger-max-concurrent\">{t('workflows.triggers.fields.maxConcurrent', 'Max Concurrent Instances')}</Label>\n <Input\n id=\"trigger-max-concurrent\"\n type=\"number\"\n value={formValues.maxConcurrentInstances}\n onChange={e => setFormValues(prev => ({ ...prev, maxConcurrentInstances: e.target.value }))}\n placeholder={t('workflows.triggers.placeholders.unlimited', 'Unlimited')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.triggers.hints.maxConcurrent', 'Cap how many instances this trigger can have running at the same time; events past the cap are skipped. Leave empty for no limit.')}\n </p>\n </div>\n </div>\n </DrawerBody>\n\n <DrawerFooter>\n <Button variant=\"outline\" onClick={handleCloseDialog}>\n {t('common.cancel', 'Cancel')}\n </Button>\n <Button\n onClick={handleSubmit}\n disabled={!formValues.name.trim() || !formValues.eventPattern.trim()}\n >\n {editingTrigger\n ? t('common.update', 'Update')\n : t('common.create', 'Create')\n }\n </Button>\n </DrawerFooter>\n </DrawerContent>\n </Drawer>\n\n {/* Removing a trigger is a yes/no interruption, so it stays a blocking\n confirmation rather than becoming a rail. */}\n <ConfirmDialog\n open={!!deleteConfirmId}\n onOpenChange={(next) => { if (!next) setDeleteConfirmId(null) }}\n title={t('workflows.triggers.delete.title', 'Delete Event Trigger?')}\n text={t('workflows.triggers.delete.description', 'This will remove the event trigger. The change will take effect when you save the workflow definition.')}\n confirmText={t('common.delete', 'Delete')}\n cancelText={t('common.cancel', 'Cancel')}\n variant=\"destructive\"\n onConfirm={() => { if (deleteConfirmId) handleDelete(deleteConfirmId) }}\n />\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["t", "value"]
|
|
7
7
|
}
|
|
@@ -73,9 +73,9 @@ function PortalTaskDetailPage({ params }) {
|
|
|
73
73
|
setFailure("submit");
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
|
-
router.push(
|
|
76
|
+
router.push(`/${params.orgSlug}/portal/tasks`);
|
|
77
77
|
},
|
|
78
|
-
[task, values, comments, router,
|
|
78
|
+
[task, values, comments, router, params.orgSlug]
|
|
79
79
|
);
|
|
80
80
|
if (notFound) {
|
|
81
81
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../../src/modules/workflows/frontend/%5BorgSlug%5D/portal/tasks/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\n/**\n * Portal task detail + completion.\n *\n * `canComplete` comes from the API, not from anything this page computes: a\n * portal admin reading a company member's task gets `false` and sees the work\n * read-only. The completion route enforces the same answer independently, so\n * hiding the form is a courtesy and never the control.\n */\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { PortalPageHeader } from '@open-mercato/ui/portal/components/PortalPageHeader'\nimport { PortalCard } from '@open-mercato/ui/portal/components/PortalCard'\nimport { usePortalAppEvent } from '@open-mercato/ui/portal/hooks/usePortalAppEvent'\nimport { PortalTaskForm, type PortalTaskFormValue } from '../../../../../components/portal/PortalTaskForm'\nimport { flattenTaskText } from '../../../../../lib/task-resolution'\n\ntype PortalTaskDetail = {\n id: string\n taskName: string\n description: string | null\n status: string\n dueDate: string | null\n formSchema: unknown\n formData: unknown\n entityBindings: unknown[] | null\n}\n\ntype PortalTaskDetailResponse = {\n ok: boolean\n task?: PortalTaskDetail\n decisions?: Array<{ id: string; label: unknown }>\n formKey?: string | null\n canComplete?: boolean\n error?: string\n}\n\nconst OPEN_STATUSES = ['PENDING', 'IN_PROGRESS']\n\ntype Props = { params: { orgSlug: string; id: string } }\n\nexport default function PortalTaskDetailPage({ params }: Props) {\n const t = useT()\n const router = useRouter()\n\n const [detail, setDetail] = React.useState<PortalTaskDetailResponse | null>(null)\n const [notFound, setNotFound] = React.useState(false)\n const [values, setValues] = React.useState<Record<string, PortalTaskFormValue>>({})\n const [comments, setComments] = React.useState('')\n const [submitting, setSubmitting] = React.useState(false)\n const [failure, setFailure] = React.useState<'load' | 'submit' | null>(null)\n\n const listHref = `/${params.orgSlug}/portal/tasks`\n\n // Depends on the id alone. The failure copy is translated at render rather\n // than stored, so a `t` that changes identity cannot re-run this effect.\n const load = React.useCallback(async () => {\n const { ok, status, result } = await apiCall<PortalTaskDetailResponse>(\n `/api/workflows/portal/tasks/${encodeURIComponent(params.id)}`,\n )\n if (status === 404) {\n setNotFound(true)\n return\n }\n if (!ok || !result?.ok) {\n setFailure('load')\n return\n }\n setDetail(result)\n }, [params.id])\n\n React.useEffect(() => {\n void load()\n }, [load])\n\n usePortalAppEvent('workflows.task.portal_assigned', () => {\n void load()\n }, [load])\n\n const task = detail?.task ?? null\n const decisions = React.useMemo(\n () =>\n (detail?.decisions ?? []).map((decision) => ({\n id: decision.id,\n label: flattenTaskText(decision.label as never) ?? decision.id,\n })),\n [detail?.decisions],\n )\n\n const handleSubmit = React.useCallback(\n async (decisionId?: string) => {\n if (!task) return\n setSubmitting(true)\n setFailure(null)\n const { ok, result } = await apiCall<{ ok: boolean; error?: string }>(\n `/api/workflows/portal/tasks/${encodeURIComponent(task.id)}/complete`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n formData: values,\n ...(comments ? { comments } : {}),\n ...(decisionId ? { decisionId } : {}),\n }),\n },\n )\n setSubmitting(false)\n if (!ok || !result?.ok) {\n setFailure('submit')\n return\n }\n router.push(
|
|
5
|
-
"mappings": ";AA8HQ,cACA,YADA;AAnHR,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,eAAe;AACxB,SAAS,aAAa;AACtB,SAAS,wBAAwB;AACjC,SAAS,kBAAkB;AAC3B,SAAS,yBAAyB;AAClC,SAAS,sBAAgD;AACzD,SAAS,uBAAuB;AAsBhC,MAAM,gBAAgB,CAAC,WAAW,aAAa;AAIhC,SAAR,qBAAsC,EAAE,OAAO,GAAU;AAC9D,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AAEzB,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA0C,IAAI;AAChF,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AACpD,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA8C,CAAC,CAAC;AAClF,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,EAAE;AACjD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAmC,IAAI;AAE3E,QAAM,WAAW,IAAI,OAAO,OAAO;AAInC,QAAM,OAAO,MAAM,YAAY,YAAY;AACzC,UAAM,EAAE,IAAI,QAAQ,OAAO,IAAI,MAAM;AAAA,MACnC,+BAA+B,mBAAmB,OAAO,EAAE,CAAC;AAAA,IAC9D;AACA,QAAI,WAAW,KAAK;AAClB,kBAAY,IAAI;AAChB;AAAA,IACF;AACA,QAAI,CAAC,MAAM,CAAC,QAAQ,IAAI;AACtB,iBAAW,MAAM;AACjB;AAAA,IACF;AACA,cAAU,MAAM;AAAA,EAClB,GAAG,CAAC,OAAO,EAAE,CAAC;AAEd,QAAM,UAAU,MAAM;AACpB,SAAK,KAAK;AAAA,EACZ,GAAG,CAAC,IAAI,CAAC;AAET,oBAAkB,kCAAkC,MAAM;AACxD,SAAK,KAAK;AAAA,EACZ,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,YAAY,MAAM;AAAA,IACtB,OACG,QAAQ,aAAa,CAAC,GAAG,IAAI,CAAC,cAAc;AAAA,MAC3C,IAAI,SAAS;AAAA,MACb,OAAO,gBAAgB,SAAS,KAAc,KAAK,SAAS;AAAA,IAC9D,EAAE;AAAA,IACJ,CAAC,QAAQ,SAAS;AAAA,EACpB;AAEA,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO,eAAwB;AAC7B,UAAI,CAAC,KAAM;AACX,oBAAc,IAAI;AAClB,iBAAW,IAAI;AACf,YAAM,EAAE,IAAI,OAAO,IAAI,MAAM;AAAA,QAC3B,+BAA+B,mBAAmB,KAAK,EAAE,CAAC;AAAA,QAC1D;AAAA,UACE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU;AAAA,YACnB,UAAU;AAAA,YACV,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,YAC/B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,UACrC,CAAC;AAAA,QACH;AAAA,MACF;AACA,oBAAc,KAAK;AACnB,UAAI,CAAC,MAAM,CAAC,QAAQ,IAAI;AACtB,mBAAW,QAAQ;AACnB;AAAA,MACF;AACA,aAAO,KAAK,
|
|
4
|
+
"sourcesContent": ["'use client'\n\n/**\n * Portal task detail + completion.\n *\n * `canComplete` comes from the API, not from anything this page computes: a\n * portal admin reading a company member's task gets `false` and sees the work\n * read-only. The completion route enforces the same answer independently, so\n * hiding the form is a courtesy and never the control.\n */\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { PortalPageHeader } from '@open-mercato/ui/portal/components/PortalPageHeader'\nimport { PortalCard } from '@open-mercato/ui/portal/components/PortalCard'\nimport { usePortalAppEvent } from '@open-mercato/ui/portal/hooks/usePortalAppEvent'\nimport { PortalTaskForm, type PortalTaskFormValue } from '../../../../../components/portal/PortalTaskForm'\nimport { flattenTaskText } from '../../../../../lib/task-resolution'\n\ntype PortalTaskDetail = {\n id: string\n taskName: string\n description: string | null\n status: string\n dueDate: string | null\n formSchema: unknown\n formData: unknown\n entityBindings: unknown[] | null\n}\n\ntype PortalTaskDetailResponse = {\n ok: boolean\n task?: PortalTaskDetail\n decisions?: Array<{ id: string; label: unknown }>\n formKey?: string | null\n canComplete?: boolean\n error?: string\n}\n\nconst OPEN_STATUSES = ['PENDING', 'IN_PROGRESS']\n\ntype Props = { params: { orgSlug: string; id: string } }\n\nexport default function PortalTaskDetailPage({ params }: Props) {\n const t = useT()\n const router = useRouter()\n\n const [detail, setDetail] = React.useState<PortalTaskDetailResponse | null>(null)\n const [notFound, setNotFound] = React.useState(false)\n const [values, setValues] = React.useState<Record<string, PortalTaskFormValue>>({})\n const [comments, setComments] = React.useState('')\n const [submitting, setSubmitting] = React.useState(false)\n const [failure, setFailure] = React.useState<'load' | 'submit' | null>(null)\n\n const listHref = `/${params.orgSlug}/portal/tasks`\n\n // Depends on the id alone. The failure copy is translated at render rather\n // than stored, so a `t` that changes identity cannot re-run this effect.\n const load = React.useCallback(async () => {\n const { ok, status, result } = await apiCall<PortalTaskDetailResponse>(\n `/api/workflows/portal/tasks/${encodeURIComponent(params.id)}`,\n )\n if (status === 404) {\n setNotFound(true)\n return\n }\n if (!ok || !result?.ok) {\n setFailure('load')\n return\n }\n setDetail(result)\n }, [params.id])\n\n React.useEffect(() => {\n void load()\n }, [load])\n\n usePortalAppEvent('workflows.task.portal_assigned', () => {\n void load()\n }, [load])\n\n const task = detail?.task ?? null\n const decisions = React.useMemo(\n () =>\n (detail?.decisions ?? []).map((decision) => ({\n id: decision.id,\n label: flattenTaskText(decision.label as never) ?? decision.id,\n })),\n [detail?.decisions],\n )\n\n const handleSubmit = React.useCallback(\n async (decisionId?: string) => {\n if (!task) return\n setSubmitting(true)\n setFailure(null)\n const { ok, result } = await apiCall<{ ok: boolean; error?: string }>(\n `/api/workflows/portal/tasks/${encodeURIComponent(task.id)}/complete`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n formData: values,\n ...(comments ? { comments } : {}),\n ...(decisionId ? { decisionId } : {}),\n }),\n },\n )\n setSubmitting(false)\n if (!ok || !result?.ok) {\n setFailure('submit')\n return\n }\n router.push(`/${params.orgSlug}/portal/tasks`)\n },\n [task, values, comments, router, params.orgSlug],\n )\n\n if (notFound) {\n return (\n <div className=\"flex flex-col gap-6\">\n <PortalPageHeader title={t('workflows.portal.tasks.detail.notFound', 'Task not available')} />\n <PortalCard>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'workflows.portal.tasks.detail.notFoundBody',\n 'This task is not available to you. It may have been completed or reassigned.',\n )}\n </p>\n <Link\n href={listHref}\n className=\"mt-4 inline-block text-sm font-medium underline underline-offset-4\"\n >\n {t('workflows.portal.tasks.detail.back', 'Back to tasks')}\n </Link>\n </PortalCard>\n </div>\n )\n }\n\n if (!detail || !task) {\n return (\n <div className=\"flex items-center justify-center py-20\">\n <Spinner />\n </div>\n )\n }\n\n const isOpen = OPEN_STATUSES.includes(task.status)\n const canComplete = detail.canComplete === true && isOpen\n\n return (\n <div className=\"flex flex-col gap-8\">\n <PortalPageHeader\n label={t('workflows.portal.tasks.label', 'Your work')}\n title={task.taskName}\n description={task.description ?? undefined}\n action={\n <Badge variant=\"outline\" className=\"text-overline\">\n {t(`workflows.portal.tasks.status.${task.status}`, task.status)}\n </Badge>\n }\n />\n\n <PortalCard>\n {canComplete ? (\n <PortalTaskForm\n taskId={task.id}\n taskName={task.taskName}\n formSchema={task.formSchema}\n formKey={detail.formKey ?? null}\n entityBindings={task.entityBindings}\n decisions={decisions}\n values={values}\n comments={comments}\n submitting={submitting}\n onFieldChange={(name, value) => setValues((current) => ({ ...current, [name]: value }))}\n onValuesChange={setValues}\n onCommentsChange={setComments}\n onSubmit={handleSubmit}\n />\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n {isOpen\n ? t(\n 'workflows.portal.tasks.detail.readOnly',\n 'You can see this task because it belongs to your company. Only the person it is assigned to can complete it.',\n )\n : t('workflows.portal.tasks.detail.closed', 'This task is already finished.')}\n </p>\n )}\n\n {failure ? (\n <p role=\"alert\" className=\"mt-4 text-sm text-status-error-text\">\n {failure === 'submit'\n ? t('workflows.portal.tasks.submitError', 'Could not submit this task.')\n : t('workflows.portal.tasks.loadError', 'Could not load your tasks.')}\n </p>\n ) : null}\n </PortalCard>\n\n <Link href={listHref} className=\"text-sm font-medium underline underline-offset-4\">\n {t('workflows.portal.tasks.detail.back', 'Back to tasks')}\n </Link>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AA8HQ,cACA,YADA;AAnHR,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,eAAe;AACxB,SAAS,aAAa;AACtB,SAAS,wBAAwB;AACjC,SAAS,kBAAkB;AAC3B,SAAS,yBAAyB;AAClC,SAAS,sBAAgD;AACzD,SAAS,uBAAuB;AAsBhC,MAAM,gBAAgB,CAAC,WAAW,aAAa;AAIhC,SAAR,qBAAsC,EAAE,OAAO,GAAU;AAC9D,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AAEzB,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA0C,IAAI;AAChF,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AACpD,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA8C,CAAC,CAAC;AAClF,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,EAAE;AACjD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAmC,IAAI;AAE3E,QAAM,WAAW,IAAI,OAAO,OAAO;AAInC,QAAM,OAAO,MAAM,YAAY,YAAY;AACzC,UAAM,EAAE,IAAI,QAAQ,OAAO,IAAI,MAAM;AAAA,MACnC,+BAA+B,mBAAmB,OAAO,EAAE,CAAC;AAAA,IAC9D;AACA,QAAI,WAAW,KAAK;AAClB,kBAAY,IAAI;AAChB;AAAA,IACF;AACA,QAAI,CAAC,MAAM,CAAC,QAAQ,IAAI;AACtB,iBAAW,MAAM;AACjB;AAAA,IACF;AACA,cAAU,MAAM;AAAA,EAClB,GAAG,CAAC,OAAO,EAAE,CAAC;AAEd,QAAM,UAAU,MAAM;AACpB,SAAK,KAAK;AAAA,EACZ,GAAG,CAAC,IAAI,CAAC;AAET,oBAAkB,kCAAkC,MAAM;AACxD,SAAK,KAAK;AAAA,EACZ,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,YAAY,MAAM;AAAA,IACtB,OACG,QAAQ,aAAa,CAAC,GAAG,IAAI,CAAC,cAAc;AAAA,MAC3C,IAAI,SAAS;AAAA,MACb,OAAO,gBAAgB,SAAS,KAAc,KAAK,SAAS;AAAA,IAC9D,EAAE;AAAA,IACJ,CAAC,QAAQ,SAAS;AAAA,EACpB;AAEA,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO,eAAwB;AAC7B,UAAI,CAAC,KAAM;AACX,oBAAc,IAAI;AAClB,iBAAW,IAAI;AACf,YAAM,EAAE,IAAI,OAAO,IAAI,MAAM;AAAA,QAC3B,+BAA+B,mBAAmB,KAAK,EAAE,CAAC;AAAA,QAC1D;AAAA,UACE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU;AAAA,YACnB,UAAU;AAAA,YACV,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,YAC/B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,UACrC,CAAC;AAAA,QACH;AAAA,MACF;AACA,oBAAc,KAAK;AACnB,UAAI,CAAC,MAAM,CAAC,QAAQ,IAAI;AACtB,mBAAW,QAAQ;AACnB;AAAA,MACF;AACA,aAAO,KAAK,IAAI,OAAO,OAAO,eAAe;AAAA,IAC/C;AAAA,IACA,CAAC,MAAM,QAAQ,UAAU,QAAQ,OAAO,OAAO;AAAA,EACjD;AAEA,MAAI,UAAU;AACZ,WACE,qBAAC,SAAI,WAAU,uBACb;AAAA,0BAAC,oBAAiB,OAAO,EAAE,0CAA0C,oBAAoB,GAAG;AAAA,MAC5F,qBAAC,cACC;AAAA,4BAAC,OAAE,WAAU,iCACV;AAAA,UACC;AAAA,UACA;AAAA,QACF,GACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,WAAU;AAAA,YAET,YAAE,sCAAsC,eAAe;AAAA;AAAA,QAC1D;AAAA,SACF;AAAA,OACF;AAAA,EAEJ;AAEA,MAAI,CAAC,UAAU,CAAC,MAAM;AACpB,WACE,oBAAC,SAAI,WAAU,0CACb,8BAAC,WAAQ,GACX;AAAA,EAEJ;AAEA,QAAM,SAAS,cAAc,SAAS,KAAK,MAAM;AACjD,QAAM,cAAc,OAAO,gBAAgB,QAAQ;AAEnD,SACE,qBAAC,SAAI,WAAU,uBACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,gCAAgC,WAAW;AAAA,QACpD,OAAO,KAAK;AAAA,QACZ,aAAa,KAAK,eAAe;AAAA,QACjC,QACE,oBAAC,SAAM,SAAQ,WAAU,WAAU,iBAChC,YAAE,iCAAiC,KAAK,MAAM,IAAI,KAAK,MAAM,GAChE;AAAA;AAAA,IAEJ;AAAA,IAEA,qBAAC,cACE;AAAA,oBACC;AAAA,QAAC;AAAA;AAAA,UACC,QAAQ,KAAK;AAAA,UACb,UAAU,KAAK;AAAA,UACf,YAAY,KAAK;AAAA,UACjB,SAAS,OAAO,WAAW;AAAA,UAC3B,gBAAgB,KAAK;AAAA,UACrB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,eAAe,CAAC,MAAM,UAAU,UAAU,CAAC,aAAa,EAAE,GAAG,SAAS,CAAC,IAAI,GAAG,MAAM,EAAE;AAAA,UACtF,gBAAgB;AAAA,UAChB,kBAAkB;AAAA,UAClB,UAAU;AAAA;AAAA,MACZ,IAEA,oBAAC,OAAE,WAAU,iCACV,mBACG;AAAA,QACE;AAAA,QACA;AAAA,MACF,IACA,EAAE,wCAAwC,gCAAgC,GAChF;AAAA,MAGD,UACC,oBAAC,OAAE,MAAK,SAAQ,WAAU,uCACvB,sBAAY,WACT,EAAE,sCAAsC,6BAA6B,IACrE,EAAE,oCAAoC,4BAA4B,GACxE,IACE;AAAA,OACN;AAAA,IAEA,oBAAC,QAAK,MAAM,UAAU,WAAU,oDAC7B,YAAE,sCAAsC,eAAe,GAC1D;AAAA,KACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -357,6 +357,51 @@ async function findMatchingTriggers(em, context) {
|
|
|
357
357
|
return true;
|
|
358
358
|
});
|
|
359
359
|
}
|
|
360
|
+
const GLOBAL_TRIGGER_DEBOUNCE_KEY = "__openMercatoWorkflowTriggerDebounce__";
|
|
361
|
+
const TRIGGER_DEBOUNCE_MAX_KEYS = 5e3;
|
|
362
|
+
function getTriggerDebounceState() {
|
|
363
|
+
const globalScope = globalThis;
|
|
364
|
+
if (!globalScope[GLOBAL_TRIGGER_DEBOUNCE_KEY]) {
|
|
365
|
+
globalScope[GLOBAL_TRIGGER_DEBOUNCE_KEY] = /* @__PURE__ */ new Map();
|
|
366
|
+
}
|
|
367
|
+
return globalScope[GLOBAL_TRIGGER_DEBOUNCE_KEY];
|
|
368
|
+
}
|
|
369
|
+
function pruneTriggerDebounceState(state, now) {
|
|
370
|
+
if (state.size < TRIGGER_DEBOUNCE_MAX_KEYS) return;
|
|
371
|
+
for (const [key, expiresAt] of state) {
|
|
372
|
+
if (expiresAt <= now) state.delete(key);
|
|
373
|
+
}
|
|
374
|
+
if (state.size < TRIGGER_DEBOUNCE_MAX_KEYS) return;
|
|
375
|
+
const oldestKey = state.keys().next().value;
|
|
376
|
+
if (oldestKey !== void 0) state.delete(oldestKey);
|
|
377
|
+
}
|
|
378
|
+
function resolveTriggerDebounceWindow(trigger, payload) {
|
|
379
|
+
const debounceMs = trigger.config?.debounceMs;
|
|
380
|
+
if (!debounceMs || debounceMs <= 0) return null;
|
|
381
|
+
const rawPayloadId = payload?.id;
|
|
382
|
+
const payloadId = typeof rawPayloadId === "string" ? rawPayloadId : typeof rawPayloadId === "number" ? String(rawPayloadId) : null;
|
|
383
|
+
return { key: `${trigger.tenantId}:${trigger.organizationId}:${trigger.id}:${payloadId ?? "*"}`, debounceMs };
|
|
384
|
+
}
|
|
385
|
+
function shouldDebounceTrigger(trigger, payload) {
|
|
386
|
+
const debounceWindow = resolveTriggerDebounceWindow(trigger, payload);
|
|
387
|
+
if (!debounceWindow) return false;
|
|
388
|
+
const state = getTriggerDebounceState();
|
|
389
|
+
const now = Date.now();
|
|
390
|
+
const expiresAt = state.get(debounceWindow.key);
|
|
391
|
+
if (expiresAt !== void 0 && now < expiresAt) return true;
|
|
392
|
+
pruneTriggerDebounceState(state, now);
|
|
393
|
+
state.delete(debounceWindow.key);
|
|
394
|
+
state.set(debounceWindow.key, now + debounceWindow.debounceMs);
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
function releaseTriggerDebounceWindow(trigger, payload) {
|
|
398
|
+
const debounceWindow = resolveTriggerDebounceWindow(trigger, payload);
|
|
399
|
+
if (!debounceWindow) return;
|
|
400
|
+
getTriggerDebounceState().delete(debounceWindow.key);
|
|
401
|
+
}
|
|
402
|
+
function resetTriggerDebounceState() {
|
|
403
|
+
getTriggerDebounceState().clear();
|
|
404
|
+
}
|
|
360
405
|
async function checkConcurrencyLimit(em, trigger) {
|
|
361
406
|
const maxInstances = trigger.config?.maxConcurrentInstances;
|
|
362
407
|
if (!maxInstances) return true;
|
|
@@ -381,10 +426,17 @@ async function processEventTriggers(em, container, context) {
|
|
|
381
426
|
return result;
|
|
382
427
|
}
|
|
383
428
|
for (const trigger of triggers) {
|
|
429
|
+
let startedWorkflow = false;
|
|
384
430
|
try {
|
|
431
|
+
if (shouldDebounceTrigger(trigger, context.payload)) {
|
|
432
|
+
logger.debug("Skipping trigger: inside debounce window", { triggerId: trigger.id, triggerName: trigger.name, debounceMs: trigger.config?.debounceMs });
|
|
433
|
+
result.skipped++;
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
385
436
|
const canStart = await checkConcurrencyLimit(em, trigger);
|
|
386
437
|
if (!canStart) {
|
|
387
438
|
logger.debug("Skipping trigger: max concurrent instances reached", { triggerId: trigger.id, triggerName: trigger.name });
|
|
439
|
+
releaseTriggerDebounceWindow(trigger, context.payload);
|
|
388
440
|
result.skipped++;
|
|
389
441
|
continue;
|
|
390
442
|
}
|
|
@@ -425,6 +477,7 @@ async function processEventTriggers(em, container, context) {
|
|
|
425
477
|
tenantId: context.tenantId,
|
|
426
478
|
organizationId: context.organizationId
|
|
427
479
|
});
|
|
480
|
+
startedWorkflow = true;
|
|
428
481
|
result.triggered++;
|
|
429
482
|
result.instances.push({
|
|
430
483
|
triggerId: trigger.id,
|
|
@@ -441,6 +494,7 @@ async function processEventTriggers(em, container, context) {
|
|
|
441
494
|
} catch (error) {
|
|
442
495
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
443
496
|
logger.error("Error processing trigger", { triggerId: trigger.id, triggerName: trigger.name, err: error });
|
|
497
|
+
if (!startedWorkflow) releaseTriggerDebounceWindow(trigger, context.payload);
|
|
444
498
|
result.errors.push({
|
|
445
499
|
triggerId: trigger.id,
|
|
446
500
|
error: errorMessage
|
|
@@ -456,6 +510,7 @@ export {
|
|
|
456
510
|
loadCodeTriggers,
|
|
457
511
|
loadTriggersForTenant,
|
|
458
512
|
mapEventToContext,
|
|
459
|
-
processEventTriggers
|
|
513
|
+
processEventTriggers,
|
|
514
|
+
resetTriggerDebounceState
|
|
460
515
|
};
|
|
461
516
|
//# sourceMappingURL=event-trigger-service.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/workflows/lib/event-trigger-service.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Workflows Module - Event Trigger Service\n *\n * Core service for evaluating and processing workflow event triggers.\n * Handles pattern matching, filter evaluation, context mapping, and workflow starting.\n */\n\nimport type { EntityManager } from '@mikro-orm/core'\nimport type { EntityManager as PostgreSqlEntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport type { CacheService } from '@open-mercato/cache'\nimport { matchEventPattern } from '@open-mercato/shared/lib/events/patterns'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { testLinearRegex } from '@open-mercato/shared/lib/regex/linear'\nimport {\n WorkflowEventTrigger,\n WorkflowDefinition,\n WorkflowInstance,\n type TriggerFilterCondition,\n type TriggerContextMapping,\n type WorkflowEventTriggerConfig,\n type WorkflowDefinitionTrigger,\n} from '../data/entities'\nimport { startWorkflow, executeWorkflow } from './workflow-executor'\nimport { getAllCodeWorkflows } from './code-registry'\nimport { codeWorkflowUuid } from './find-definition'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('workflows').child({ component: 'event-trigger' })\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface EventTriggerContext {\n eventName: string\n payload: Record<string, unknown>\n tenantId: string\n organizationId: string\n}\n\nexport interface ProcessTriggersResult {\n triggered: number\n skipped: number\n errors: Array<{ triggerId: string; error: string }>\n instances: Array<{ triggerId: string; instanceId: string }>\n}\n\n/**\n * Unified trigger interface for both legacy (entity) and embedded (definition) triggers\n */\nexport interface UnifiedTrigger {\n id: string // For legacy: entity ID, for embedded: `${definitionId}:${triggerId}`\n triggerId: string\n name: string\n description?: string | null\n eventPattern: string\n config: WorkflowEventTriggerConfig | null\n enabled: boolean\n priority: number\n workflowDefinitionId: string\n workflowId: string\n workflowVersion: number\n source: 'legacy' | 'embedded' | 'code'\n tenantId: string\n organizationId: string\n}\n\ninterface CachedTriggers {\n triggers: UnifiedTrigger[]\n cachedAt: number\n}\n\n// Cache TTL: 5 minutes\nconst TRIGGER_CACHE_TTL = 5 * 60 * 1000\n\nfunction readEventActorUserId(payload: Record<string, unknown>): string | null {\n const candidate = payload.userId ?? payload.actorUserId\n if (typeof candidate !== 'string') return null\n const trimmed = candidate.trim()\n return trimmed.length > 0 && !trimmed.startsWith('trigger:') ? trimmed : null\n}\n\n// ============================================================================\n// Pattern Matching\n// ============================================================================\n\n// ============================================================================\n// Filter Evaluation\n// ============================================================================\n\nconst MAX_WORKFLOW_REGEX_PATTERN_LENGTH = 200\nconst MAX_WORKFLOW_REGEX_INPUT_LENGTH = 10_000\n\n/**\n * Get a nested value from an object using dot notation.\n */\nfunction getNestedValue(obj: unknown, path: string): unknown {\n if (obj === null || obj === undefined) return undefined\n\n const parts = path.split('.')\n let current: unknown = obj\n\n for (const part of parts) {\n if (current === null || current === undefined) return undefined\n if (typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n\n return current\n}\n\nfunction getQuantifierEnd(pattern: string, index: number): number | null {\n const char = pattern[index]\n if (char === '*' || char === '+' || char === '?') return index\n if (char !== '{') return null\n\n const closeIndex = pattern.indexOf('}', index + 1)\n if (closeIndex === -1) return null\n\n const body = pattern.slice(index + 1, closeIndex)\n return /^[0-9]+(?:,[0-9]*)?$/.test(body) ? closeIndex : null\n}\n\ninterface RegexGroupFrame {\n hasAlternation: boolean\n hasQuantifier: boolean\n}\n\nfunction isSafeWorkflowRegexPattern(pattern: string): boolean {\n if (pattern.length > MAX_WORKFLOW_REGEX_PATTERN_LENGTH) return false\n\n const groupStack: RegexGroupFrame[] = [{ hasAlternation: false, hasQuantifier: false }]\n let inCharClass = false\n let lastClosedGroup: RegexGroupFrame | null = null\n let lastAtomWasQuantified = false\n\n // Hand-written single-pass lexer: `index` is intentionally advanced inside the\n // loop body (escape pairs, `(?:` prefixes, multi-char quantifiers, lazy `?`) on\n // top of the `index += 1` update clause. Do not \"simplify\" these in-body writes.\n for (let index = 0; index < pattern.length; index += 1) {\n const char = pattern[index]\n\n if (char === '\\\\') {\n const next = pattern[index + 1]\n if (!inCharClass && (/[1-9]/.test(next ?? '') || (next === 'k' && pattern[index + 2] === '<'))) {\n return false\n }\n index += 1\n lastClosedGroup = null\n lastAtomWasQuantified = false\n continue\n }\n\n if (inCharClass) {\n if (char === ']') {\n inCharClass = false\n lastAtomWasQuantified = false\n }\n lastClosedGroup = null\n continue\n }\n\n if (char === '[') {\n inCharClass = true\n lastClosedGroup = null\n lastAtomWasQuantified = false\n continue\n }\n\n if (char === '(') {\n if (pattern[index + 1] === '?') {\n if (pattern[index + 2] !== ':') return false\n index += 2\n }\n\n groupStack.push({ hasAlternation: false, hasQuantifier: false })\n lastClosedGroup = null\n lastAtomWasQuantified = false\n continue\n }\n\n if (char === ')') {\n if (groupStack.length === 1) return false\n lastClosedGroup = groupStack.pop()!\n lastAtomWasQuantified = false\n continue\n }\n\n if (char === '|') {\n groupStack[groupStack.length - 1].hasAlternation = true\n lastClosedGroup = null\n lastAtomWasQuantified = false\n continue\n }\n\n const quantifierEnd = getQuantifierEnd(pattern, index)\n if (quantifierEnd !== null) {\n if (lastAtomWasQuantified) return false\n if (lastClosedGroup?.hasAlternation || lastClosedGroup?.hasQuantifier) return false\n\n groupStack[groupStack.length - 1].hasQuantifier = true\n lastClosedGroup = null\n lastAtomWasQuantified = true\n index = quantifierEnd\n\n if (pattern[index + 1] === '?') {\n index += 1\n }\n\n continue\n }\n\n lastClosedGroup = null\n lastAtomWasQuantified = false\n }\n\n return groupStack.length === 1 && !inCharClass\n}\n\n/**\n * Evaluate a single filter condition against the event payload.\n */\nfunction evaluateCondition(condition: TriggerFilterCondition, payload: Record<string, unknown>): boolean {\n const value = getNestedValue(payload, condition.field)\n const expected = condition.value\n\n switch (condition.operator) {\n case 'eq':\n return value === expected\n\n case 'neq':\n return value !== expected\n\n case 'gt':\n return typeof value === 'number' && typeof expected === 'number' && value > expected\n\n case 'gte':\n return typeof value === 'number' && typeof expected === 'number' && value >= expected\n\n case 'lt':\n return typeof value === 'number' && typeof expected === 'number' && value < expected\n\n case 'lte':\n return typeof value === 'number' && typeof expected === 'number' && value <= expected\n\n case 'contains':\n if (typeof value === 'string' && typeof expected === 'string') {\n return value.includes(expected)\n }\n if (Array.isArray(value)) {\n return value.includes(expected)\n }\n return false\n\n case 'startsWith':\n return typeof value === 'string' && typeof expected === 'string' && value.startsWith(expected)\n\n case 'endsWith':\n return typeof value === 'string' && typeof expected === 'string' && value.endsWith(expected)\n\n case 'in':\n return Array.isArray(expected) && expected.includes(value)\n\n case 'notIn':\n return Array.isArray(expected) && !expected.includes(value)\n\n case 'exists':\n return value !== undefined && value !== null\n\n case 'notExists':\n return value === undefined || value === null\n\n case 'regex':\n if (typeof value !== 'string' || typeof expected !== 'string') return false\n if (value.length > MAX_WORKFLOW_REGEX_INPUT_LENGTH) return false\n if (!isSafeWorkflowRegexPattern(expected)) return false\n {\n const result = testLinearRegex(expected, value, {\n maxPatternLength: MAX_WORKFLOW_REGEX_PATTERN_LENGTH,\n maxInputLength: MAX_WORKFLOW_REGEX_INPUT_LENGTH,\n })\n return result.ok ? result.matched : false\n }\n\n default:\n return false\n }\n}\n\n/**\n * Evaluate all filter conditions against the event payload.\n * All conditions must pass (AND logic).\n */\nexport function evaluateFilterConditions(\n conditions: TriggerFilterCondition[] | undefined,\n payload: Record<string, unknown>\n): boolean {\n if (!conditions || conditions.length === 0) return true\n\n return conditions.every(condition => evaluateCondition(condition, payload))\n}\n\n// ============================================================================\n// Context Mapping\n// ============================================================================\n\n/**\n * Map event payload to workflow initial context.\n */\nexport function mapEventToContext(\n mapping: TriggerContextMapping[] | undefined,\n payload: Record<string, unknown>\n): Record<string, unknown> {\n const context: Record<string, unknown> = {}\n\n if (!mapping || mapping.length === 0) return context\n\n for (const item of mapping) {\n const value = getNestedValue(payload, item.sourceExpression)\n context[item.targetKey] = value !== undefined ? value : item.defaultValue\n }\n\n return context\n}\n\n// ============================================================================\n// Trigger Loading with Caching\n// ============================================================================\n\n// In-memory cache for triggers (per tenant/org).\n// Park the Map on globalThis so the same compiled module loaded under two\n// paths (a Next.js server chunk vs. a worker resolving the file through a\n// different import root) shares one cache. Without this,\n// `invalidateTriggerCache(...)` called from the PUT /api/workflows/definitions\n// route clears its own copy while the wildcard event-trigger subscriber keeps\n// reading a stale copy populated before the trigger was added \u2014 newly added\n// triggers stay invisible for up to TRIGGER_CACHE_TTL. Mirrors the same\n// workaround used by the modules registry and `getDiRegistrars()`.\nconst GLOBAL_TRIGGER_CACHE_KEY = '__openMercatoWorkflowTriggerCache__'\n\nfunction getTriggerCache(): Map<string, CachedTriggers> {\n const existing = (globalThis as any)[GLOBAL_TRIGGER_CACHE_KEY] as\n | Map<string, CachedTriggers>\n | undefined\n if (existing) return existing\n const created = new Map<string, CachedTriggers>()\n ;(globalThis as any)[GLOBAL_TRIGGER_CACHE_KEY] = created\n return created\n}\n\nfunction getCacheKey(tenantId: string, organizationId: string): string {\n return `${tenantId}:${organizationId}`\n}\n\n/**\n * Load legacy triggers from WorkflowEventTrigger entity table.\n * For backward compatibility with existing triggers.\n */\nasync function loadLegacyTriggers(\n em: EntityManager,\n tenantId: string,\n organizationId: string\n): Promise<UnifiedTrigger[]> {\n const postgresEm = em as unknown as PostgreSqlEntityManager\n const legacyTriggers = await findWithDecryption(\n postgresEm,\n WorkflowEventTrigger,\n {\n tenantId,\n organizationId,\n enabled: true,\n deletedAt: null,\n },\n {\n orderBy: { priority: 'DESC', createdAt: 'ASC' },\n },\n { tenantId, organizationId },\n )\n\n // Get definitions for these triggers to get workflowId\n const definitionIds = [...new Set(legacyTriggers.map(t => t.workflowDefinitionId))]\n const definitions = definitionIds.length > 0 ? await findWithDecryption(postgresEm, WorkflowDefinition, {\n id: { $in: definitionIds },\n tenantId,\n organizationId,\n enabled: true,\n deletedAt: null,\n }, {}, { tenantId, organizationId }) : []\n const definitionMap = new Map(definitions.map(d => [d.id, d]))\n\n return legacyTriggers\n .filter(t => definitionMap.has(t.workflowDefinitionId))\n .map(t => {\n const def = definitionMap.get(t.workflowDefinitionId)!\n return {\n id: t.id,\n triggerId: t.id,\n name: t.name,\n description: t.description,\n eventPattern: t.eventPattern,\n config: t.config ?? null,\n enabled: t.enabled,\n priority: t.priority,\n workflowDefinitionId: t.workflowDefinitionId,\n workflowId: def.workflowId,\n workflowVersion: def.version,\n source: 'legacy' as const,\n tenantId: t.tenantId,\n organizationId: t.organizationId,\n }\n })\n}\n\n/**\n * Load embedded triggers from workflow definitions.\n * New triggers are embedded directly in the definition JSONB.\n *\n * Also returns the set of every non-deleted DB definition's `workflowId` \u2014 even\n * those with no triggers \u2014 so the caller can let a materialized (customized)\n * definition suppress its code-registry counterpart regardless of whether the\n * customization kept any triggers (#4425).\n */\nasync function loadEmbeddedTriggers(\n em: EntityManager,\n tenantId: string,\n organizationId: string\n): Promise<{ triggers: UnifiedTrigger[]; workflowIds: Set<string> }> {\n const postgresEm = em as unknown as PostgreSqlEntityManager\n // Load all definitions so disabled customizations still shadow their code\n // counterpart. Only enabled definitions contribute embedded triggers below.\n const definitions = await findWithDecryption(\n postgresEm,\n WorkflowDefinition,\n {\n tenantId,\n organizationId,\n deletedAt: null,\n },\n {},\n { tenantId, organizationId },\n )\n\n const triggers: UnifiedTrigger[] = []\n const workflowIds = new Set<string>()\n\n for (const def of definitions) {\n workflowIds.add(def.workflowId)\n if (!def.enabled) continue\n\n const embeddedTriggers = def.definition?.triggers as WorkflowDefinitionTrigger[] | undefined\n if (!embeddedTriggers || embeddedTriggers.length === 0) continue\n\n for (const trigger of embeddedTriggers) {\n if (!trigger.enabled) continue\n\n triggers.push({\n id: `${def.id}:${trigger.triggerId}`,\n triggerId: trigger.triggerId,\n name: trigger.name,\n description: trigger.description ?? null,\n eventPattern: trigger.eventPattern,\n config: trigger.config ?? null,\n enabled: trigger.enabled,\n priority: trigger.priority,\n workflowDefinitionId: def.id,\n workflowId: def.workflowId,\n workflowVersion: def.version,\n source: 'embedded' as const,\n tenantId,\n organizationId,\n })\n }\n }\n\n return { triggers, workflowIds }\n}\n\n/**\n * Project triggers declared on code-defined workflows into UnifiedTriggers.\n *\n * Code workflows live only in the in-memory registry, so their `triggers`\n * arrays never reached the DB-backed loaders \u2014 a code-declared trigger was inert\n * until an operator ran `POST \u2026/code:<id>/customize` to materialize the\n * definition into a `workflow_definitions` row (#4425). This projects them\n * directly. It is synchronous (no DB) and scoped to the caller's tenant/org via\n * the deterministic virtual definition id, matching what `startWorkflow`\n * persists as the instance's `definitionId` so the concurrency limit counts\n * correctly.\n *\n * `dbBackedWorkflowIds` are workflowIds already contributed by the legacy or\n * embedded (DB) sources; a code workflow whose id appears there is skipped so a\n * customized DB override wins and its trigger is not double-registered.\n */\nexport function loadCodeTriggers(\n tenantId: string,\n organizationId: string,\n dbBackedWorkflowIds: Set<string>,\n): UnifiedTrigger[] {\n const triggers: UnifiedTrigger[] = []\n\n for (const codeDef of getAllCodeWorkflows()) {\n if (!codeDef.enabled) continue\n if (dbBackedWorkflowIds.has(codeDef.workflowId)) continue\n\n const embeddedTriggers = codeDef.definition?.triggers\n if (!embeddedTriggers || embeddedTriggers.length === 0) continue\n\n const definitionId = codeWorkflowUuid(codeDef.workflowId)\n for (const trigger of embeddedTriggers) {\n if (!trigger.enabled) continue\n\n triggers.push({\n id: `code:${codeDef.workflowId}:${trigger.triggerId}`,\n triggerId: trigger.triggerId,\n name: trigger.name,\n description: trigger.description ?? null,\n eventPattern: trigger.eventPattern,\n config: (trigger.config ?? null) as WorkflowEventTriggerConfig | null,\n enabled: trigger.enabled,\n priority: trigger.priority,\n workflowDefinitionId: definitionId,\n workflowId: codeDef.workflowId,\n workflowVersion: codeDef.version,\n source: 'code' as const,\n tenantId,\n organizationId,\n })\n }\n }\n\n return triggers\n}\n\n/**\n * Load all enabled triggers for a tenant/organization with caching.\n * Merges legacy (entity), embedded (definition), and code-registry triggers;\n * a DB-backed source for a given workflowId takes precedence over its code\n * trigger so `customize` semantics are preserved.\n */\nexport async function loadTriggersForTenant(\n em: EntityManager,\n tenantId: string,\n organizationId: string,\n cacheService?: CacheService\n): Promise<UnifiedTrigger[]> {\n const cacheKey = getCacheKey(tenantId, organizationId)\n const cache = getTriggerCache()\n\n // Check in-memory cache\n const cached = cache.get(cacheKey)\n if (cached && Date.now() - cached.cachedAt < TRIGGER_CACHE_TTL) {\n return cached.triggers\n }\n\n // Load from both DB sources\n const [legacyTriggers, embedded] = await Promise.all([\n loadLegacyTriggers(em, tenantId, organizationId),\n loadEmbeddedTriggers(em, tenantId, organizationId),\n ])\n const embeddedTriggers = embedded.triggers\n\n // Project code-registry triggers, letting any DB-backed definition for the\n // same workflowId win (preserves `customize` override semantics \u2014 including\n // a customization that removed its triggers).\n const dbBackedWorkflowIds = new Set<string>([\n ...embedded.workflowIds,\n ...legacyTriggers.map((t) => t.workflowId),\n ])\n const codeTriggers = loadCodeTriggers(tenantId, organizationId, dbBackedWorkflowIds)\n\n // Merge and sort by priority (higher first)\n const allTriggers = [...legacyTriggers, ...embeddedTriggers, ...codeTriggers]\n .sort((a, b) => b.priority - a.priority)\n\n // Update cache\n cache.set(cacheKey, {\n triggers: allTriggers,\n cachedAt: Date.now(),\n })\n\n return allTriggers\n}\n\n/**\n * Invalidate trigger cache for a tenant/organization.\n * Call this when:\n * - Legacy triggers are created/updated/deleted\n * - Workflow definitions with embedded triggers are created/updated/deleted\n */\nexport function invalidateTriggerCache(tenantId: string, organizationId?: string): void {\n const cache = getTriggerCache()\n if (organizationId) {\n // Invalidate specific org\n const cacheKey = getCacheKey(tenantId, organizationId)\n cache.delete(cacheKey)\n } else {\n // Invalidate all orgs for tenant\n for (const key of cache.keys()) {\n if (key.startsWith(`${tenantId}:`)) {\n cache.delete(key)\n }\n }\n }\n}\n\n// ============================================================================\n// Trigger Matching\n// ============================================================================\n\n/**\n * Find all triggers that match an event.\n */\nexport async function findMatchingTriggers(\n em: EntityManager,\n context: EventTriggerContext\n): Promise<UnifiedTrigger[]> {\n const triggers = await loadTriggersForTenant(\n em,\n context.tenantId,\n context.organizationId\n )\n\n return triggers.filter(trigger => {\n // Check event pattern\n if (!matchEventPattern(context.eventName, trigger.eventPattern)) {\n return false\n }\n\n // Check filter conditions\n if (!evaluateFilterConditions(trigger.config?.filterConditions, context.payload)) {\n return false\n }\n\n return true\n })\n}\n\n// ============================================================================\n// Trigger Processing\n// ============================================================================\n\n/**\n * Check if max concurrent instances limit is reached.\n */\nasync function checkConcurrencyLimit(\n em: EntityManager,\n trigger: UnifiedTrigger\n): Promise<boolean> {\n const maxInstances = trigger.config?.maxConcurrentInstances\n\n if (!maxInstances) return true // No limit\n\n // Count running instances for this trigger's workflow definition\n const runningCount = await em.count(WorkflowInstance, {\n definitionId: trigger.workflowDefinitionId,\n status: { $in: ['RUNNING', 'WAITING_FOR_ACTIVITIES'] },\n tenantId: trigger.tenantId,\n organizationId: trigger.organizationId,\n deletedAt: null,\n })\n\n return runningCount < maxInstances\n}\n\n/**\n * Process all matching triggers for an event and start workflows.\n */\nexport async function processEventTriggers(\n em: EntityManager,\n container: AwilixContainer,\n context: EventTriggerContext\n): Promise<ProcessTriggersResult> {\n const result: ProcessTriggersResult = {\n triggered: 0,\n skipped: 0,\n errors: [],\n instances: [],\n }\n\n // Find matching triggers\n const triggers = await findMatchingTriggers(em, context)\n\n if (triggers.length === 0) {\n return result\n }\n\n // Process each trigger (definitions already validated during loading)\n for (const trigger of triggers) {\n try {\n // Check concurrency limit\n const canStart = await checkConcurrencyLimit(em, trigger)\n if (!canStart) {\n logger.debug('Skipping trigger: max concurrent instances reached', { triggerId: trigger.id, triggerName: trigger.name })\n result.skipped++\n continue\n }\n\n // Map event payload to workflow context\n const mappedContext = mapEventToContext(trigger.config?.contextMapping, context.payload)\n\n // Extract entity info from payload for metadata\n const payloadId = context.payload?.id as string | undefined\n const payloadEntityType = context.payload?.entityType as string | undefined\n const actorUserId = readEventActorUserId(context.payload)\n\n // Include event metadata and payload in context\n const initialContext = {\n // Include raw event payload fields (e.g., id, organizationId, tenantId)\n ...context.payload,\n // Override with explicit mappings if provided\n ...mappedContext,\n __trigger: {\n triggerId: trigger.id,\n triggerName: trigger.name,\n eventName: context.eventName,\n eventPayload: context.payload,\n triggeredAt: new Date().toISOString(),\n source: trigger.source,\n },\n }\n\n // Start workflow\n const instance = await startWorkflow(em, {\n workflowId: trigger.workflowId,\n version: trigger.workflowVersion,\n initialContext,\n metadata: {\n initiatedBy: actorUserId ?? `trigger:${trigger.id}`,\n // Include entityId and entityType for widget discovery\n entityId: payloadId,\n entityType: payloadEntityType || trigger.config?.entityType,\n labels: {\n trigger_id: trigger.id,\n trigger_name: trigger.name,\n event_name: context.eventName,\n trigger_source: trigger.source,\n },\n },\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n\n result.triggered++\n result.instances.push({\n triggerId: trigger.id,\n instanceId: instance.id,\n })\n\n // Execute workflow asynchronously (don't wait)\n executeWorkflow(\n em.fork(),\n container,\n instance.id,\n actorUserId ? { userId: actorUserId } : undefined\n ).catch(err => {\n logger.error('Error executing workflow', { instanceId: instance.id, err })\n })\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n logger.error('Error processing trigger', { triggerId: trigger.id, triggerName: trigger.name, err: error })\n result.errors.push({\n triggerId: trigger.id,\n error: errorMessage,\n })\n }\n }\n\n return result\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Workflows Module - Event Trigger Service\n *\n * Core service for evaluating and processing workflow event triggers.\n * Handles pattern matching, filter evaluation, context mapping, and workflow starting.\n */\n\nimport type { EntityManager } from '@mikro-orm/core'\nimport type { EntityManager as PostgreSqlEntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport type { CacheService } from '@open-mercato/cache'\nimport { matchEventPattern } from '@open-mercato/shared/lib/events/patterns'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { testLinearRegex } from '@open-mercato/shared/lib/regex/linear'\nimport {\n WorkflowEventTrigger,\n WorkflowDefinition,\n WorkflowInstance,\n type TriggerFilterCondition,\n type TriggerContextMapping,\n type WorkflowEventTriggerConfig,\n type WorkflowDefinitionTrigger,\n} from '../data/entities'\nimport { startWorkflow, executeWorkflow } from './workflow-executor'\nimport { getAllCodeWorkflows } from './code-registry'\nimport { codeWorkflowUuid } from './find-definition'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('workflows').child({ component: 'event-trigger' })\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface EventTriggerContext {\n eventName: string\n payload: Record<string, unknown>\n tenantId: string\n organizationId: string\n}\n\nexport interface ProcessTriggersResult {\n triggered: number\n skipped: number\n errors: Array<{ triggerId: string; error: string }>\n instances: Array<{ triggerId: string; instanceId: string }>\n}\n\n/**\n * Unified trigger interface for both legacy (entity) and embedded (definition) triggers\n */\nexport interface UnifiedTrigger {\n id: string // For legacy: entity ID, for embedded: `${definitionId}:${triggerId}`\n triggerId: string\n name: string\n description?: string | null\n eventPattern: string\n config: WorkflowEventTriggerConfig | null\n enabled: boolean\n priority: number\n workflowDefinitionId: string\n workflowId: string\n workflowVersion: number\n source: 'legacy' | 'embedded' | 'code'\n tenantId: string\n organizationId: string\n}\n\ninterface CachedTriggers {\n triggers: UnifiedTrigger[]\n cachedAt: number\n}\n\n// Cache TTL: 5 minutes\nconst TRIGGER_CACHE_TTL = 5 * 60 * 1000\n\nfunction readEventActorUserId(payload: Record<string, unknown>): string | null {\n const candidate = payload.userId ?? payload.actorUserId\n if (typeof candidate !== 'string') return null\n const trimmed = candidate.trim()\n return trimmed.length > 0 && !trimmed.startsWith('trigger:') ? trimmed : null\n}\n\n// ============================================================================\n// Pattern Matching\n// ============================================================================\n\n// ============================================================================\n// Filter Evaluation\n// ============================================================================\n\nconst MAX_WORKFLOW_REGEX_PATTERN_LENGTH = 200\nconst MAX_WORKFLOW_REGEX_INPUT_LENGTH = 10_000\n\n/**\n * Get a nested value from an object using dot notation.\n */\nfunction getNestedValue(obj: unknown, path: string): unknown {\n if (obj === null || obj === undefined) return undefined\n\n const parts = path.split('.')\n let current: unknown = obj\n\n for (const part of parts) {\n if (current === null || current === undefined) return undefined\n if (typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n\n return current\n}\n\nfunction getQuantifierEnd(pattern: string, index: number): number | null {\n const char = pattern[index]\n if (char === '*' || char === '+' || char === '?') return index\n if (char !== '{') return null\n\n const closeIndex = pattern.indexOf('}', index + 1)\n if (closeIndex === -1) return null\n\n const body = pattern.slice(index + 1, closeIndex)\n return /^[0-9]+(?:,[0-9]*)?$/.test(body) ? closeIndex : null\n}\n\ninterface RegexGroupFrame {\n hasAlternation: boolean\n hasQuantifier: boolean\n}\n\nfunction isSafeWorkflowRegexPattern(pattern: string): boolean {\n if (pattern.length > MAX_WORKFLOW_REGEX_PATTERN_LENGTH) return false\n\n const groupStack: RegexGroupFrame[] = [{ hasAlternation: false, hasQuantifier: false }]\n let inCharClass = false\n let lastClosedGroup: RegexGroupFrame | null = null\n let lastAtomWasQuantified = false\n\n // Hand-written single-pass lexer: `index` is intentionally advanced inside the\n // loop body (escape pairs, `(?:` prefixes, multi-char quantifiers, lazy `?`) on\n // top of the `index += 1` update clause. Do not \"simplify\" these in-body writes.\n for (let index = 0; index < pattern.length; index += 1) {\n const char = pattern[index]\n\n if (char === '\\\\') {\n const next = pattern[index + 1]\n if (!inCharClass && (/[1-9]/.test(next ?? '') || (next === 'k' && pattern[index + 2] === '<'))) {\n return false\n }\n index += 1\n lastClosedGroup = null\n lastAtomWasQuantified = false\n continue\n }\n\n if (inCharClass) {\n if (char === ']') {\n inCharClass = false\n lastAtomWasQuantified = false\n }\n lastClosedGroup = null\n continue\n }\n\n if (char === '[') {\n inCharClass = true\n lastClosedGroup = null\n lastAtomWasQuantified = false\n continue\n }\n\n if (char === '(') {\n if (pattern[index + 1] === '?') {\n if (pattern[index + 2] !== ':') return false\n index += 2\n }\n\n groupStack.push({ hasAlternation: false, hasQuantifier: false })\n lastClosedGroup = null\n lastAtomWasQuantified = false\n continue\n }\n\n if (char === ')') {\n if (groupStack.length === 1) return false\n lastClosedGroup = groupStack.pop()!\n lastAtomWasQuantified = false\n continue\n }\n\n if (char === '|') {\n groupStack[groupStack.length - 1].hasAlternation = true\n lastClosedGroup = null\n lastAtomWasQuantified = false\n continue\n }\n\n const quantifierEnd = getQuantifierEnd(pattern, index)\n if (quantifierEnd !== null) {\n if (lastAtomWasQuantified) return false\n if (lastClosedGroup?.hasAlternation || lastClosedGroup?.hasQuantifier) return false\n\n groupStack[groupStack.length - 1].hasQuantifier = true\n lastClosedGroup = null\n lastAtomWasQuantified = true\n index = quantifierEnd\n\n if (pattern[index + 1] === '?') {\n index += 1\n }\n\n continue\n }\n\n lastClosedGroup = null\n lastAtomWasQuantified = false\n }\n\n return groupStack.length === 1 && !inCharClass\n}\n\n/**\n * Evaluate a single filter condition against the event payload.\n */\nfunction evaluateCondition(condition: TriggerFilterCondition, payload: Record<string, unknown>): boolean {\n const value = getNestedValue(payload, condition.field)\n const expected = condition.value\n\n switch (condition.operator) {\n case 'eq':\n return value === expected\n\n case 'neq':\n return value !== expected\n\n case 'gt':\n return typeof value === 'number' && typeof expected === 'number' && value > expected\n\n case 'gte':\n return typeof value === 'number' && typeof expected === 'number' && value >= expected\n\n case 'lt':\n return typeof value === 'number' && typeof expected === 'number' && value < expected\n\n case 'lte':\n return typeof value === 'number' && typeof expected === 'number' && value <= expected\n\n case 'contains':\n if (typeof value === 'string' && typeof expected === 'string') {\n return value.includes(expected)\n }\n if (Array.isArray(value)) {\n return value.includes(expected)\n }\n return false\n\n case 'startsWith':\n return typeof value === 'string' && typeof expected === 'string' && value.startsWith(expected)\n\n case 'endsWith':\n return typeof value === 'string' && typeof expected === 'string' && value.endsWith(expected)\n\n case 'in':\n return Array.isArray(expected) && expected.includes(value)\n\n case 'notIn':\n return Array.isArray(expected) && !expected.includes(value)\n\n case 'exists':\n return value !== undefined && value !== null\n\n case 'notExists':\n return value === undefined || value === null\n\n case 'regex':\n if (typeof value !== 'string' || typeof expected !== 'string') return false\n if (value.length > MAX_WORKFLOW_REGEX_INPUT_LENGTH) return false\n if (!isSafeWorkflowRegexPattern(expected)) return false\n {\n const result = testLinearRegex(expected, value, {\n maxPatternLength: MAX_WORKFLOW_REGEX_PATTERN_LENGTH,\n maxInputLength: MAX_WORKFLOW_REGEX_INPUT_LENGTH,\n })\n return result.ok ? result.matched : false\n }\n\n default:\n return false\n }\n}\n\n/**\n * Evaluate all filter conditions against the event payload.\n * All conditions must pass (AND logic).\n */\nexport function evaluateFilterConditions(\n conditions: TriggerFilterCondition[] | undefined,\n payload: Record<string, unknown>\n): boolean {\n if (!conditions || conditions.length === 0) return true\n\n return conditions.every(condition => evaluateCondition(condition, payload))\n}\n\n// ============================================================================\n// Context Mapping\n// ============================================================================\n\n/**\n * Map event payload to workflow initial context.\n */\nexport function mapEventToContext(\n mapping: TriggerContextMapping[] | undefined,\n payload: Record<string, unknown>\n): Record<string, unknown> {\n const context: Record<string, unknown> = {}\n\n if (!mapping || mapping.length === 0) return context\n\n for (const item of mapping) {\n const value = getNestedValue(payload, item.sourceExpression)\n context[item.targetKey] = value !== undefined ? value : item.defaultValue\n }\n\n return context\n}\n\n// ============================================================================\n// Trigger Loading with Caching\n// ============================================================================\n\n// In-memory cache for triggers (per tenant/org).\n// Park the Map on globalThis so the same compiled module loaded under two\n// paths (a Next.js server chunk vs. a worker resolving the file through a\n// different import root) shares one cache. Without this,\n// `invalidateTriggerCache(...)` called from the PUT /api/workflows/definitions\n// route clears its own copy while the wildcard event-trigger subscriber keeps\n// reading a stale copy populated before the trigger was added \u2014 newly added\n// triggers stay invisible for up to TRIGGER_CACHE_TTL. Mirrors the same\n// workaround used by the modules registry and `getDiRegistrars()`.\nconst GLOBAL_TRIGGER_CACHE_KEY = '__openMercatoWorkflowTriggerCache__'\n\nfunction getTriggerCache(): Map<string, CachedTriggers> {\n const existing = (globalThis as any)[GLOBAL_TRIGGER_CACHE_KEY] as\n | Map<string, CachedTriggers>\n | undefined\n if (existing) return existing\n const created = new Map<string, CachedTriggers>()\n ;(globalThis as any)[GLOBAL_TRIGGER_CACHE_KEY] = created\n return created\n}\n\nfunction getCacheKey(tenantId: string, organizationId: string): string {\n return `${tenantId}:${organizationId}`\n}\n\n/**\n * Load legacy triggers from WorkflowEventTrigger entity table.\n * For backward compatibility with existing triggers.\n */\nasync function loadLegacyTriggers(\n em: EntityManager,\n tenantId: string,\n organizationId: string\n): Promise<UnifiedTrigger[]> {\n const postgresEm = em as unknown as PostgreSqlEntityManager\n const legacyTriggers = await findWithDecryption(\n postgresEm,\n WorkflowEventTrigger,\n {\n tenantId,\n organizationId,\n enabled: true,\n deletedAt: null,\n },\n {\n orderBy: { priority: 'DESC', createdAt: 'ASC' },\n },\n { tenantId, organizationId },\n )\n\n // Get definitions for these triggers to get workflowId\n const definitionIds = [...new Set(legacyTriggers.map(t => t.workflowDefinitionId))]\n const definitions = definitionIds.length > 0 ? await findWithDecryption(postgresEm, WorkflowDefinition, {\n id: { $in: definitionIds },\n tenantId,\n organizationId,\n enabled: true,\n deletedAt: null,\n }, {}, { tenantId, organizationId }) : []\n const definitionMap = new Map(definitions.map(d => [d.id, d]))\n\n return legacyTriggers\n .filter(t => definitionMap.has(t.workflowDefinitionId))\n .map(t => {\n const def = definitionMap.get(t.workflowDefinitionId)!\n return {\n id: t.id,\n triggerId: t.id,\n name: t.name,\n description: t.description,\n eventPattern: t.eventPattern,\n config: t.config ?? null,\n enabled: t.enabled,\n priority: t.priority,\n workflowDefinitionId: t.workflowDefinitionId,\n workflowId: def.workflowId,\n workflowVersion: def.version,\n source: 'legacy' as const,\n tenantId: t.tenantId,\n organizationId: t.organizationId,\n }\n })\n}\n\n/**\n * Load embedded triggers from workflow definitions.\n * New triggers are embedded directly in the definition JSONB.\n *\n * Also returns the set of every non-deleted DB definition's `workflowId` \u2014 even\n * those with no triggers \u2014 so the caller can let a materialized (customized)\n * definition suppress its code-registry counterpart regardless of whether the\n * customization kept any triggers (#4425).\n */\nasync function loadEmbeddedTriggers(\n em: EntityManager,\n tenantId: string,\n organizationId: string\n): Promise<{ triggers: UnifiedTrigger[]; workflowIds: Set<string> }> {\n const postgresEm = em as unknown as PostgreSqlEntityManager\n // Load all definitions so disabled customizations still shadow their code\n // counterpart. Only enabled definitions contribute embedded triggers below.\n const definitions = await findWithDecryption(\n postgresEm,\n WorkflowDefinition,\n {\n tenantId,\n organizationId,\n deletedAt: null,\n },\n {},\n { tenantId, organizationId },\n )\n\n const triggers: UnifiedTrigger[] = []\n const workflowIds = new Set<string>()\n\n for (const def of definitions) {\n workflowIds.add(def.workflowId)\n if (!def.enabled) continue\n\n const embeddedTriggers = def.definition?.triggers as WorkflowDefinitionTrigger[] | undefined\n if (!embeddedTriggers || embeddedTriggers.length === 0) continue\n\n for (const trigger of embeddedTriggers) {\n if (!trigger.enabled) continue\n\n triggers.push({\n id: `${def.id}:${trigger.triggerId}`,\n triggerId: trigger.triggerId,\n name: trigger.name,\n description: trigger.description ?? null,\n eventPattern: trigger.eventPattern,\n config: trigger.config ?? null,\n enabled: trigger.enabled,\n priority: trigger.priority,\n workflowDefinitionId: def.id,\n workflowId: def.workflowId,\n workflowVersion: def.version,\n source: 'embedded' as const,\n tenantId,\n organizationId,\n })\n }\n }\n\n return { triggers, workflowIds }\n}\n\n/**\n * Project triggers declared on code-defined workflows into UnifiedTriggers.\n *\n * Code workflows live only in the in-memory registry, so their `triggers`\n * arrays never reached the DB-backed loaders \u2014 a code-declared trigger was inert\n * until an operator ran `POST \u2026/code:<id>/customize` to materialize the\n * definition into a `workflow_definitions` row (#4425). This projects them\n * directly. It is synchronous (no DB) and scoped to the caller's tenant/org via\n * the deterministic virtual definition id, matching what `startWorkflow`\n * persists as the instance's `definitionId` so the concurrency limit counts\n * correctly.\n *\n * `dbBackedWorkflowIds` are workflowIds already contributed by the legacy or\n * embedded (DB) sources; a code workflow whose id appears there is skipped so a\n * customized DB override wins and its trigger is not double-registered.\n */\nexport function loadCodeTriggers(\n tenantId: string,\n organizationId: string,\n dbBackedWorkflowIds: Set<string>,\n): UnifiedTrigger[] {\n const triggers: UnifiedTrigger[] = []\n\n for (const codeDef of getAllCodeWorkflows()) {\n if (!codeDef.enabled) continue\n if (dbBackedWorkflowIds.has(codeDef.workflowId)) continue\n\n const embeddedTriggers = codeDef.definition?.triggers\n if (!embeddedTriggers || embeddedTriggers.length === 0) continue\n\n const definitionId = codeWorkflowUuid(codeDef.workflowId)\n for (const trigger of embeddedTriggers) {\n if (!trigger.enabled) continue\n\n triggers.push({\n id: `code:${codeDef.workflowId}:${trigger.triggerId}`,\n triggerId: trigger.triggerId,\n name: trigger.name,\n description: trigger.description ?? null,\n eventPattern: trigger.eventPattern,\n config: (trigger.config ?? null) as WorkflowEventTriggerConfig | null,\n enabled: trigger.enabled,\n priority: trigger.priority,\n workflowDefinitionId: definitionId,\n workflowId: codeDef.workflowId,\n workflowVersion: codeDef.version,\n source: 'code' as const,\n tenantId,\n organizationId,\n })\n }\n }\n\n return triggers\n}\n\n/**\n * Load all enabled triggers for a tenant/organization with caching.\n * Merges legacy (entity), embedded (definition), and code-registry triggers;\n * a DB-backed source for a given workflowId takes precedence over its code\n * trigger so `customize` semantics are preserved.\n */\nexport async function loadTriggersForTenant(\n em: EntityManager,\n tenantId: string,\n organizationId: string,\n cacheService?: CacheService\n): Promise<UnifiedTrigger[]> {\n const cacheKey = getCacheKey(tenantId, organizationId)\n const cache = getTriggerCache()\n\n // Check in-memory cache\n const cached = cache.get(cacheKey)\n if (cached && Date.now() - cached.cachedAt < TRIGGER_CACHE_TTL) {\n return cached.triggers\n }\n\n // Load from both DB sources\n const [legacyTriggers, embedded] = await Promise.all([\n loadLegacyTriggers(em, tenantId, organizationId),\n loadEmbeddedTriggers(em, tenantId, organizationId),\n ])\n const embeddedTriggers = embedded.triggers\n\n // Project code-registry triggers, letting any DB-backed definition for the\n // same workflowId win (preserves `customize` override semantics \u2014 including\n // a customization that removed its triggers).\n const dbBackedWorkflowIds = new Set<string>([\n ...embedded.workflowIds,\n ...legacyTriggers.map((t) => t.workflowId),\n ])\n const codeTriggers = loadCodeTriggers(tenantId, organizationId, dbBackedWorkflowIds)\n\n // Merge and sort by priority (higher first)\n const allTriggers = [...legacyTriggers, ...embeddedTriggers, ...codeTriggers]\n .sort((a, b) => b.priority - a.priority)\n\n // Update cache\n cache.set(cacheKey, {\n triggers: allTriggers,\n cachedAt: Date.now(),\n })\n\n return allTriggers\n}\n\n/**\n * Invalidate trigger cache for a tenant/organization.\n * Call this when:\n * - Legacy triggers are created/updated/deleted\n * - Workflow definitions with embedded triggers are created/updated/deleted\n */\nexport function invalidateTriggerCache(tenantId: string, organizationId?: string): void {\n const cache = getTriggerCache()\n if (organizationId) {\n // Invalidate specific org\n const cacheKey = getCacheKey(tenantId, organizationId)\n cache.delete(cacheKey)\n } else {\n // Invalidate all orgs for tenant\n for (const key of cache.keys()) {\n if (key.startsWith(`${tenantId}:`)) {\n cache.delete(key)\n }\n }\n }\n}\n\n// ============================================================================\n// Trigger Matching\n// ============================================================================\n\n/**\n * Find all triggers that match an event.\n */\nexport async function findMatchingTriggers(\n em: EntityManager,\n context: EventTriggerContext\n): Promise<UnifiedTrigger[]> {\n const triggers = await loadTriggersForTenant(\n em,\n context.tenantId,\n context.organizationId\n )\n\n return triggers.filter(trigger => {\n // Check event pattern\n if (!matchEventPattern(context.eventName, trigger.eventPattern)) {\n return false\n }\n\n // Check filter conditions\n if (!evaluateFilterConditions(trigger.config?.filterConditions, context.payload)) {\n return false\n }\n\n return true\n })\n}\n\n// ============================================================================\n// Trigger Processing\n// ============================================================================\n\n// Open debounce windows for triggers configured with `debounceMs`, keyed by\n// tenant/org/trigger/entity and holding the timestamp each window closes at.\n// Parked on globalThis for the same reason as the trigger cache above: the\n// compiled module can be loaded under two import roots (a Next.js server chunk\n// vs. a worker), and a module-local Map would debounce each copy separately \u2014\n// halving the effective window whenever both copies see the same event stream.\n// The state is still per process: a deployment running several app or worker\n// processes debounces independently in each one, so the guard bounds trigger\n// storms per process rather than cluster-wide.\nconst GLOBAL_TRIGGER_DEBOUNCE_KEY = '__openMercatoWorkflowTriggerDebounce__'\n\n// Cap on tracked keys so a long-lived worker cannot grow the Map without bound;\n// mirrors the prune step in query_index's `markAutoReindexScheduled`.\nconst TRIGGER_DEBOUNCE_MAX_KEYS = 5000\n\ntype GlobalWithTriggerDebounce = typeof globalThis & {\n [GLOBAL_TRIGGER_DEBOUNCE_KEY]?: Map<string, number>\n}\n\nfunction getTriggerDebounceState(): Map<string, number> {\n const globalScope = globalThis as GlobalWithTriggerDebounce\n if (!globalScope[GLOBAL_TRIGGER_DEBOUNCE_KEY]) {\n globalScope[GLOBAL_TRIGGER_DEBOUNCE_KEY] = new Map<string, number>()\n }\n return globalScope[GLOBAL_TRIGGER_DEBOUNCE_KEY]\n}\n\n// Entries store their own expiry rather than their start, so pruning never has\n// to assume one shared window length: `debounceMs` is per trigger and ranges\n// from 0 to the validator's one-hour ceiling, and expiring a one-hour window\n// because a 100 ms trigger happened to run the prune would silently reopen it.\nfunction pruneTriggerDebounceState(state: Map<string, number>, now: number): void {\n if (state.size < TRIGGER_DEBOUNCE_MAX_KEYS) return\n\n for (const [key, expiresAt] of state) {\n if (expiresAt <= now) state.delete(key)\n }\n\n if (state.size < TRIGGER_DEBOUNCE_MAX_KEYS) return\n\n const oldestKey = state.keys().next().value\n if (oldestKey !== undefined) state.delete(oldestKey)\n}\n\n/**\n * Resolve the debounce window a trigger/event pair shares, or `null` when the\n * trigger has no debounce configured.\n *\n * The key includes the event payload's `id` so the window is per trigger *and*\n * per entity \u2014 rapid updates to two different records still start two\n * workflows. Tenant and organization are part of the key because code-defined\n * triggers reuse one id across every tenant.\n */\nfunction resolveTriggerDebounceWindow(\n trigger: UnifiedTrigger,\n payload: Record<string, unknown>,\n): { key: string; debounceMs: number } | null {\n const debounceMs = trigger.config?.debounceMs\n\n if (!debounceMs || debounceMs <= 0) return null\n\n // Accept numeric ids too: falling back to the shared `*` bucket would collapse\n // every record under one key and suppress workflows for unrelated entities.\n const rawPayloadId = payload?.id\n const payloadId =\n typeof rawPayloadId === 'string' ? rawPayloadId\n : typeof rawPayloadId === 'number' ? String(rawPayloadId)\n : null\n\n return { key: `${trigger.tenantId}:${trigger.organizationId}:${trigger.id}:${payloadId ?? '*'}`, debounceMs }\n}\n\n/**\n * Check whether this event falls inside the trigger's debounce window, opening\n * a fresh window when it does not.\n *\n * Leading-edge semantics: the first event fires the workflow and opens the\n * window; repeats inside it are dropped. This matches the two other\n * `debounceMs` implementations in the codebase (`markAutoReindexScheduled` in\n * query_index, `NotificationDispatcher.shouldDebounce` in the UI package) and\n * needs no timer that would have to survive a process restart.\n *\n * The window is opened here rather than after the workflow starts so that\n * nothing awaits between reading and writing it \u2014 two events racing through\n * `processEventTriggers` must not both observe an empty window. An attempt that\n * does not go on to start a workflow closes its window again via\n * `releaseTriggerDebounceWindow`, so only a real start keeps one open.\n */\nfunction shouldDebounceTrigger(trigger: UnifiedTrigger, payload: Record<string, unknown>): boolean {\n const debounceWindow = resolveTriggerDebounceWindow(trigger, payload)\n\n if (!debounceWindow) return false\n\n const state = getTriggerDebounceState()\n const now = Date.now()\n const expiresAt = state.get(debounceWindow.key)\n\n if (expiresAt !== undefined && now < expiresAt) return true\n\n pruneTriggerDebounceState(state, now)\n state.delete(debounceWindow.key)\n state.set(debounceWindow.key, now + debounceWindow.debounceMs)\n return false\n}\n\n/**\n * Close a window `shouldDebounceTrigger` opened for an event that never started\n * a workflow \u2014 the concurrency limit blocked it, or mapping/starting threw.\n * Leaving it open would let one blocked or failed attempt suppress every later\n * event for the same record until the window elapsed, which at the validator's\n * one-hour ceiling means an hour of silently dropped starts.\n */\nfunction releaseTriggerDebounceWindow(trigger: UnifiedTrigger, payload: Record<string, unknown>): void {\n const debounceWindow = resolveTriggerDebounceWindow(trigger, payload)\n\n if (!debounceWindow) return\n\n getTriggerDebounceState().delete(debounceWindow.key)\n}\n\n/**\n * Clear all debounce windows. Test-only seam \u2014 production code relies on the\n * windows expiring on their own.\n */\nexport function resetTriggerDebounceState(): void {\n getTriggerDebounceState().clear()\n}\n\n/**\n * Check if max concurrent instances limit is reached.\n */\nasync function checkConcurrencyLimit(\n em: EntityManager,\n trigger: UnifiedTrigger\n): Promise<boolean> {\n const maxInstances = trigger.config?.maxConcurrentInstances\n\n if (!maxInstances) return true // No limit\n\n // Count running instances for this trigger's workflow definition\n const runningCount = await em.count(WorkflowInstance, {\n definitionId: trigger.workflowDefinitionId,\n status: { $in: ['RUNNING', 'WAITING_FOR_ACTIVITIES'] },\n tenantId: trigger.tenantId,\n organizationId: trigger.organizationId,\n deletedAt: null,\n })\n\n return runningCount < maxInstances\n}\n\n/**\n * Process all matching triggers for an event and start workflows.\n */\nexport async function processEventTriggers(\n em: EntityManager,\n container: AwilixContainer,\n context: EventTriggerContext\n): Promise<ProcessTriggersResult> {\n const result: ProcessTriggersResult = {\n triggered: 0,\n skipped: 0,\n errors: [],\n instances: [],\n }\n\n // Find matching triggers\n const triggers = await findMatchingTriggers(em, context)\n\n if (triggers.length === 0) {\n return result\n }\n\n // Process each trigger (definitions already validated during loading)\n for (const trigger of triggers) {\n let startedWorkflow = false\n try {\n // Check debounce window before any DB work\n if (shouldDebounceTrigger(trigger, context.payload)) {\n logger.debug('Skipping trigger: inside debounce window', { triggerId: trigger.id, triggerName: trigger.name, debounceMs: trigger.config?.debounceMs })\n result.skipped++\n continue\n }\n\n // Check concurrency limit\n const canStart = await checkConcurrencyLimit(em, trigger)\n if (!canStart) {\n logger.debug('Skipping trigger: max concurrent instances reached', { triggerId: trigger.id, triggerName: trigger.name })\n releaseTriggerDebounceWindow(trigger, context.payload)\n result.skipped++\n continue\n }\n\n // Map event payload to workflow context\n const mappedContext = mapEventToContext(trigger.config?.contextMapping, context.payload)\n\n // Extract entity info from payload for metadata\n const payloadId = context.payload?.id as string | undefined\n const payloadEntityType = context.payload?.entityType as string | undefined\n const actorUserId = readEventActorUserId(context.payload)\n\n // Include event metadata and payload in context\n const initialContext = {\n // Include raw event payload fields (e.g., id, organizationId, tenantId)\n ...context.payload,\n // Override with explicit mappings if provided\n ...mappedContext,\n __trigger: {\n triggerId: trigger.id,\n triggerName: trigger.name,\n eventName: context.eventName,\n eventPayload: context.payload,\n triggeredAt: new Date().toISOString(),\n source: trigger.source,\n },\n }\n\n // Start workflow\n const instance = await startWorkflow(em, {\n workflowId: trigger.workflowId,\n version: trigger.workflowVersion,\n initialContext,\n metadata: {\n initiatedBy: actorUserId ?? `trigger:${trigger.id}`,\n // Include entityId and entityType for widget discovery\n entityId: payloadId,\n entityType: payloadEntityType || trigger.config?.entityType,\n labels: {\n trigger_id: trigger.id,\n trigger_name: trigger.name,\n event_name: context.eventName,\n trigger_source: trigger.source,\n },\n },\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n\n startedWorkflow = true\n result.triggered++\n result.instances.push({\n triggerId: trigger.id,\n instanceId: instance.id,\n })\n\n // Execute workflow asynchronously (don't wait)\n executeWorkflow(\n em.fork(),\n container,\n instance.id,\n actorUserId ? { userId: actorUserId } : undefined\n ).catch(err => {\n logger.error('Error executing workflow', { instanceId: instance.id, err })\n })\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n logger.error('Error processing trigger', { triggerId: trigger.id, triggerName: trigger.name, err: error })\n if (!startedWorkflow) releaseTriggerDebounceWindow(trigger, context.payload)\n result.errors.push({\n triggerId: trigger.id,\n error: errorMessage,\n })\n }\n }\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|