@open-mercato/core 0.6.5-develop.4670.1.afe50dfd5c → 0.6.5-develop.4691.1.bb409545b3
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/AGENTS.md +31 -0
- package/dist/helpers/integration/standaloneEnv.js +58 -0
- package/dist/helpers/integration/standaloneEnv.js.map +7 -0
- package/dist/helpers/integration/undoHarness.js +97 -2
- package/dist/helpers/integration/undoHarness.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +80 -83
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/entities/lib/helpers.js +79 -82
- package/dist/modules/entities/lib/helpers.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +50 -24
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/query_index/subscribers/delete_one.js +28 -15
- package/dist/modules/query_index/subscribers/delete_one.js.map +2 -2
- package/dist/modules/query_index/subscribers/upsert_one.js +31 -13
- package/dist/modules/query_index/subscribers/upsert_one.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/[id]/page.js +3 -0
- package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-executor.js +15 -0
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/package.json +7 -7
- package/src/helpers/integration/standaloneEnv.ts +62 -0
- package/src/helpers/integration/undoHarness.ts +132 -1
- package/src/modules/customers/AGENTS.md +1 -0
- package/src/modules/customers/commands/deals.ts +106 -111
- package/src/modules/entities/lib/helpers.ts +43 -21
- package/src/modules/query_index/lib/indexer.ts +71 -24
- package/src/modules/query_index/subscribers/delete_one.ts +36 -16
- package/src/modules/query_index/subscribers/upsert_one.ts +44 -15
- package/src/modules/resources/backend/resources/resources/[id]/page.tsx +11 -0
- package/src/modules/workflows/lib/workflow-executor.ts +17 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/resources/backend/resources/resources/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ActivitiesSection, NotesSection, RecordNotFoundState, type SectionAction, type TagOption } from '@open-mercato/ui/backend/detail'\nimport { VersionHistoryAction } from '@open-mercato/ui/backend/version-history'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { buildResourceScheduleItems } from '@open-mercato/core/modules/resources/lib/resourceSchedule'\nimport { RESOURCES_RESOURCE_FIELDSET_DEFAULT } from '@open-mercato/core/modules/resources/lib/resourceCustomFields'\nimport type { AvailabilityScheduleItemBuilder } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { ResourcesResourceForm, useResourcesResourceFormConfig } from '@open-mercato/core/modules/resources/components/ResourceCrudForm'\nimport { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { createResourceNotesAdapter } from '@open-mercato/core/modules/resources/components/detail/notesAdapter'\nimport { createResourceActivitiesAdapter } from '@open-mercato/core/modules/resources/components/detail/activitiesAdapter'\nimport {\n createResourceDictionaryEntry,\n loadResourceDictionary,\n type DictionaryEntryOption,\n} from '@open-mercato/core/modules/resources/components/detail/dictionaries'\nimport type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\n\ntype ResourceRecord = {\n id: string\n name: string\n description?: string | null\n resourceTypeId: string | null\n capacity: number | null\n capacityUnitValue: string | null\n capacityUnitName: string | null\n capacityUnitColor: string | null\n capacityUnitIcon: string | null\n tags?: TagOption[] | null\n isActive: boolean\n appearanceIcon?: string | null\n appearanceColor?: string | null\n resource_type_id?: string | null\n capacity_unit_value?: string | null\n capacity_unit_name?: string | null\n capacity_unit_color?: string | null\n capacity_unit_icon?: string | null\n appearance_icon?: string | null\n appearance_color?: string | null\n is_active?: boolean\n availabilityRuleSetId?: string | null\n availability_rule_set_id?: string | null\n} & Record<string, unknown>\n\ntype ResourceResponse = {\n items: ResourceRecord[]\n}\n\nfunction normalizeResourceRecord(record: ResourceRecord): ResourceRecord {\n return {\n ...record,\n resourceTypeId: record.resourceTypeId ?? record.resource_type_id ?? null,\n description: record.description ?? null,\n capacityUnitValue: record.capacityUnitValue ?? record.capacity_unit_value ?? null,\n capacityUnitName: record.capacityUnitName ?? record.capacity_unit_name ?? null,\n capacityUnitColor: record.capacityUnitColor ?? record.capacity_unit_color ?? null,\n capacityUnitIcon: record.capacityUnitIcon ?? record.capacity_unit_icon ?? null,\n appearanceIcon: record.appearanceIcon ?? record.appearance_icon ?? null,\n appearanceColor: record.appearanceColor ?? record.appearance_color ?? null,\n isActive: record.isActive ?? record.is_active ?? true,\n }\n}\n\nexport default function ResourcesResourceDetailPage({ params }: { params?: { id?: string } }) {\n const resourceId = params?.id\n const t = useT()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n const router = useRouter()\n const searchParams = useSearchParams()\n const [initialValues, setInitialValues] = React.useState<Record<string, unknown> | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n const [tags, setTags] = React.useState<TagOption[]>([])\n const [activeTab, setActiveTab] = React.useState<'details' | 'availability'>('details')\n const [activeDetailTab, setActiveDetailTab] = React.useState<'notes' | 'activities'>('notes')\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n const [availabilityRuleSetId, setAvailabilityRuleSetId] = React.useState<string | null>(null)\n const [activityDictionaryId, setActivityDictionaryId] = React.useState<string | null>(null)\n const [activityTypeEntries, setActivityTypeEntries] = React.useState<DictionaryEntryOption[]>([])\n const flashShownRef = React.useRef(false)\n\n const availabilityMode = 'availability'\n const notesAdapter = React.useMemo(() => createResourceNotesAdapter(detailTranslator), [detailTranslator])\n const activitiesAdapter = React.useMemo(() => createResourceActivitiesAdapter(detailTranslator), [detailTranslator])\n\n const activityTypeLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('resources.resources.detail.activities.dictionary.placeholder', 'Select an activity type'),\n addLabel: t('resources.resources.detail.activities.dictionary.add', 'Add type'),\n addPrompt: t('resources.resources.detail.activities.dictionary.prompt', 'Name the type'),\n dialogTitle: t('resources.resources.detail.activities.dictionary.dialogTitle', 'Add activity type'),\n valueLabel: t('resources.resources.detail.activities.dictionary.valueLabel', 'Name'),\n valuePlaceholder: t('resources.resources.detail.activities.dictionary.valuePlaceholder', 'Name'),\n labelLabel: t('resources.resources.detail.activities.dictionary.labelLabel', 'Label'),\n labelPlaceholder: t('resources.resources.detail.activities.dictionary.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('resources.resources.detail.activities.dictionary.emptyError', 'Please enter a name'),\n cancelLabel: t('resources.resources.detail.activities.dictionary.cancel', 'Cancel'),\n saveLabel: t('resources.resources.detail.activities.dictionary.save', 'Save'),\n saveShortcutHint: t('resources.resources.detail.activities.dictionary.saveShortcut', '\u2318/Ctrl + Enter'),\n errorLoad: t('resources.resources.detail.activities.dictionary.errorLoad', 'Failed to load options'),\n errorSave: t('resources.resources.detail.activities.dictionary.errorSave', 'Failed to save option'),\n loadingLabel: t('resources.resources.detail.activities.dictionary.loading', 'Loading\u2026'),\n manageTitle: t('resources.resources.detail.activities.dictionary.manage', 'Manage dictionary'),\n }), [t])\n\n const loadActivityOptions = React.useCallback(async () => {\n const { dictionary, entries } = await loadResourceDictionary('activityTypes')\n setActivityDictionaryId(dictionary?.id ?? null)\n setActivityTypeEntries(entries)\n return entries\n }, [])\n\n const createActivityOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createResourceDictionaryEntry('activityTypes', input)\n if (!entry) {\n throw new Error(t('resources.resources.detail.activities.dictionary.errorSave', 'Failed to save option'))\n }\n return entry\n },\n [t],\n )\n\n React.useEffect(() => {\n loadActivityOptions().catch(() => {})\n }, [loadActivityOptions])\n\n const activityTypeMap = React.useMemo(\n () => new Map(activityTypeEntries.map((entry) => [entry.value, entry])),\n [activityTypeEntries],\n )\n\n const resolveActivityPresentation = React.useCallback(\n (activity: { activityType: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const entry = activityTypeMap.get(activity.activityType)\n return {\n label: entry?.label ?? activity.activityType,\n icon: entry?.icon ?? activity.appearanceIcon ?? null,\n color: entry?.color ?? activity.appearanceColor ?? null,\n }\n },\n [activityTypeMap],\n )\n\n const manageActivityHref = React.useMemo(() => {\n if (!activityDictionaryId) return '/backend/config/dictionaries'\n return `/backend/config/dictionaries?dictionaryId=${encodeURIComponent(activityDictionaryId)}`\n }, [activityDictionaryId])\n\n const appearanceLabels = React.useMemo(() => ({\n colorLabel: t('resources.resources.detail.activities.appearance.colorLabel', 'Color'),\n colorHelp: t('resources.resources.detail.activities.appearance.colorHelp', 'Pick a highlight color for this entry.'),\n colorClearLabel: t('resources.resources.detail.activities.appearance.colorClear', 'Remove color'),\n iconLabel: t('resources.resources.detail.activities.appearance.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('resources.resources.detail.activities.appearance.iconPlaceholder', 'Type an emoji or pick one of the suggestions.'),\n iconPickerTriggerLabel: t('resources.resources.detail.activities.appearance.iconBrowse', 'Browse icons and emojis'),\n iconSearchPlaceholder: t('resources.resources.detail.activities.appearance.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('resources.resources.detail.activities.appearance.iconSearchEmpty', 'No icons match your search.'),\n iconSuggestionsLabel: t('resources.resources.detail.activities.appearance.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('resources.resources.detail.activities.appearance.iconClear', 'Remove icon'),\n previewEmptyLabel: t('resources.resources.detail.activities.appearance.previewEmpty', 'No appearance selected'),\n }), [t])\n\n const renderCustomFields = React.useCallback((activity: { id?: string; customFields?: Array<{ key: string; label?: string | null; value: unknown }> }) => {\n const entries = Array.isArray(activity.customFields) ? activity.customFields : []\n if (!entries.length) return null\n const emptyLabel = t('resources.resources.detail.activities.customFields.empty', 'Not provided')\n return (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {entries.map((entry, index) => {\n const label = entry.label ?? entry.key\n const value = entry.value\n const hasValue = !(value == null || value === '' || (Array.isArray(value) && value.length === 0))\n const content = hasValue\n ? Array.isArray(value)\n ? value.map((item) => String(item)).join(', ')\n : String(value)\n : emptyLabel\n return (\n <div\n key={`activity-${activity.id ?? 'row'}-custom-${index}`}\n className=\"rounded-md border border-border/70 bg-muted/30 px-3 py-2\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n <div className=\"mt-1 text-sm text-foreground\">{content}</div>\n </div>\n )\n })}\n </div>\n )\n }, [t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const tabParam = searchParams.get('tab')\n if (tabParam === 'availability') {\n setActiveTab('availability')\n }\n const created = searchParams.get('created') === '1'\n if (created && !flashShownRef.current) {\n flashShownRef.current = true\n flash(t('resources.resources.flash.createdAvailability', 'Saved. You can now set availability.'), 'success')\n const nextParams = new URLSearchParams(searchParams.toString())\n nextParams.delete('created')\n const nextQuery = nextParams.toString()\n const nextPath = resourceId\n ? `/backend/resources/resources/${encodeURIComponent(resourceId)}${nextQuery ? `?${nextQuery}` : ''}`\n : `/backend/resources/resources${nextQuery ? `?${nextQuery}` : ''}`\n router.replace(nextPath)\n }\n }, [resourceId, router, searchParams, t])\n\n const buildScheduleItems = React.useCallback<AvailabilityScheduleItemBuilder>(\n ({ availabilityRules, translate }) => buildResourceScheduleItems({\n availabilityRules,\n isAvailableByDefault: false,\n translate,\n }),\n [],\n )\n\n const tagLabels = React.useMemo(\n () => ({\n loading: t('resources.resources.tags.loading', 'Loading tags...'),\n placeholder: t('resources.resources.tags.placeholder', 'Type to add tags'),\n empty: t('resources.resources.tags.placeholder', 'No tags yet. Add labels to keep resources organized.'),\n loadError: t('resources.resources.tags.loadError', 'Failed to load tags.'),\n createError: t('resources.resources.tags.createError', 'Failed to create tag.'),\n updateError: t('resources.resources.tags.updateError', 'Failed to update tags.'),\n labelRequired: t('resources.resources.tags.labelRequired', 'Tag name is required.'),\n saveShortcut: t('resources.resources.tags.saveShortcut', 'Save Cmd+Enter / Ctrl+Enter'),\n cancelShortcut: t('resources.resources.tags.cancelShortcut', 'Cancel (Esc)'),\n edit: t('ui.forms.actions.edit', 'Edit'),\n cancel: t('ui.forms.actions.cancel', 'Cancel'),\n success: t('resources.resources.tags.success', 'Tags updated.'),\n }),\n [t],\n )\n\n const handleSubmit = React.useCallback(async (values: Record<string, unknown>) => {\n if (!resourceId) return\n const appearance = values.appearance && typeof values.appearance === 'object'\n ? values.appearance as { icon?: string | null; color?: string | null }\n : {}\n const { appearance: _appearance, ...rest } = values\n const customFieldsetCode = typeof values.customFieldsetCode === 'string' && values.customFieldsetCode.trim().length\n ? values.customFieldsetCode.trim()\n : RESOURCES_RESOURCE_FIELDSET_DEFAULT\n const payload: Record<string, unknown> = {\n ...rest,\n id: resourceId,\n resourceTypeId: values.resourceTypeId || null,\n capacity: values.capacity ? Number(values.capacity) : null,\n capacityUnitValue: values.capacityUnitValue ? String(values.capacityUnitValue) : null,\n appearanceIcon: appearance.icon ?? null,\n appearanceColor: appearance.color ?? null,\n isActive: values.isActive ?? true,\n customFieldsetCode,\n ...collectCustomFieldValues(values),\n }\n if (!payload.name || String(payload.name).trim().length === 0) {\n throw createCrudFormError(t('resources.resources.form.errors.nameRequired', 'Name is required.'))\n }\n await updateCrud('resources/resources', payload, {\n errorMessage: t('resources.resources.form.errors.update', 'Failed to update resource.'),\n })\n flash(t('resources.resources.form.flash.updated', 'Resource updated.'), 'success')\n }, [resourceId, t])\n\n const tabs = React.useMemo(() => ([\n { id: 'details', label: t('resources.resources.tabs.details', 'Details') },\n { id: 'availability', label: t('resources.resources.tabs.availability', 'Availability') },\n ]), [t])\n const detailTabs = React.useMemo(() => ([\n { id: 'notes' as const, label: t('resources.resources.detail.tabs.notes', 'Notes') },\n { id: 'activities' as const, label: t('resources.resources.detail.tabs.activities', 'Activities') },\n ]), [t])\n\n const loadTagOptions = React.useCallback(\n async (query?: string): Promise<TagOption[]> => {\n const params = new URLSearchParams({ pageSize: '100' })\n if (query) params.set('search', query)\n const payload = await readApiResultOrThrow<Record<string, unknown>>(\n `/api/resources/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('resources.resources.tags.loadError', 'Failed to load tags.') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n return items\n .map((item: unknown): TagOption | null => {\n if (!item || typeof item !== 'object') return null\n const raw = item as { id?: unknown; tagId?: unknown; label?: unknown; slug?: unknown; color?: unknown }\n const rawId =\n typeof raw.id === 'string'\n ? raw.id\n : typeof raw.tagId === 'string'\n ? raw.tagId\n : null\n if (!rawId) return null\n const labelValue =\n (typeof raw.label === 'string' && raw.label.trim().length && raw.label.trim()) ||\n (typeof raw.slug === 'string' && raw.slug.trim().length && raw.slug.trim()) ||\n rawId\n const color = typeof raw.color === 'string' && raw.color.trim().length ? raw.color.trim() : null\n return { id: rawId, label: labelValue, color }\n })\n .filter((entry): entry is TagOption => entry !== null)\n },\n [t],\n )\n\n const createTag = React.useCallback(\n async (label: string): Promise<TagOption> => {\n const trimmed = label.trim()\n if (!trimmed.length) {\n throw new Error(t('resources.resources.tags.labelRequired', 'Tag name is required.'))\n }\n const response = await apiCallOrThrow<Record<string, unknown>>(\n '/api/resources/tags',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ label: trimmed }),\n },\n { errorMessage: t('resources.resources.tags.createError', 'Failed to create tag.') },\n )\n const payload = response.result ?? {}\n const id =\n typeof payload?.id === 'string'\n ? payload.id\n : typeof (payload as any)?.tagId === 'string'\n ? (payload as any).tagId\n : ''\n if (!id) throw new Error(t('resources.resources.tags.createError', 'Failed to create tag.'))\n const color = typeof (payload as any)?.color === 'string' && (payload as any).color.trim().length\n ? (payload as any).color.trim()\n : null\n return { id, label: trimmed, color }\n },\n [t],\n )\n\n const assignTag = React.useCallback(async (tagId: string) => {\n if (!resourceId) return\n await apiCallOrThrow(\n '/api/resources/resources/tags/assign',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tagId, resourceId }),\n },\n { errorMessage: t('resources.resources.tags.updateError', 'Failed to update tags.') },\n )\n }, [resourceId, t])\n\n const unassignTag = React.useCallback(async (tagId: string) => {\n if (!resourceId) return\n await apiCallOrThrow(\n '/api/resources/resources/tags/unassign',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tagId, resourceId }),\n },\n { errorMessage: t('resources.resources.tags.updateError', 'Failed to update tags.') },\n )\n }, [resourceId, t])\n\n const handleTagsSave = React.useCallback(\n async ({ next, added, removed }: { next: TagOption[]; added: TagOption[]; removed: TagOption[] }) => {\n if (!resourceId) return\n for (const tag of added) {\n await assignTag(tag.id)\n }\n for (const tag of removed) {\n await unassignTag(tag.id)\n }\n setTags(next)\n flash(t('resources.resources.tags.success', 'Tags updated.'), 'success')\n },\n [assignTag, resourceId, t, unassignTag],\n )\n\n const tagsSection = React.useMemo(\n () => ({\n title: t('resources.resources.tags.title', 'Tags'),\n tags,\n onChange: setTags,\n loadOptions: loadTagOptions,\n createTag,\n onSave: handleTagsSave,\n labels: tagLabels,\n }),\n [createTag, handleTagsSave, loadTagOptions, t, tagLabels, tags],\n )\n\n const formConfig = useResourcesResourceFormConfig({ tagsSection })\n const { resourceTypesLoaded, resolveFieldsetCode } = formConfig\n\n React.useEffect(() => {\n if (!resourceId || !resourceTypesLoaded) return\n setIsNotFound(false)\n let cancelled = false\n async function loadResource() {\n try {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '1')\n if (resourceId) params.set('ids', resourceId)\n const record = await readApiResultOrThrow<ResourceResponse>(`/api/resources/resources?${params.toString()}`)\n const resourceRaw = Array.isArray(record?.items) ? record.items[0] : null\n const resource = resourceRaw ? normalizeResourceRecord(resourceRaw) : null\n if (!resource) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n if (!cancelled) {\n const customValues = extractCustomFieldEntries(resource)\n setTags(Array.isArray(resource.tags) ? resource.tags : [])\n setAvailabilityRuleSetId(\n typeof resource.availabilityRuleSetId === 'string'\n ? resource.availabilityRuleSetId\n : typeof resource.availability_rule_set_id === 'string'\n ? resource.availability_rule_set_id\n : null,\n )\n setInitialValues({\n id: resource.id,\n name: resource.name,\n description: resource.description ?? '',\n resourceTypeId: resource.resourceTypeId || '',\n capacity: resource.capacity ?? '',\n capacityUnitValue: resource.capacityUnitValue ?? '',\n appearance: { icon: resource.appearanceIcon ?? null, color: resource.appearanceColor ?? null },\n isActive: resource.isActive ?? true,\n updatedAt:\n typeof resource.updatedAt === 'string'\n ? resource.updatedAt\n : typeof resource.updated_at === 'string'\n ? resource.updated_at\n : null,\n customFieldsetCode: resource.resourceTypeId\n ? resolveFieldsetCode(resource.resourceTypeId)\n : RESOURCES_RESOURCE_FIELDSET_DEFAULT,\n ...customValues,\n })\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('resources.resources.form.errors.load', 'Failed to load resource.')\n flash(message, 'error')\n }\n }\n loadResource()\n return () => { cancelled = true }\n }, [resourceId, resourceTypesLoaded, resolveFieldsetCode, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!resourceId) return\n await deleteCrud('resources/resources', resourceId, {\n errorMessage: t('resources.resources.form.errors.delete', 'Failed to delete resource.'),\n })\n flash(t('resources.resources.form.flash.deleted', 'Resource deleted.'), 'success')\n router.push('/backend/resources/resources')\n }, [resourceId, router, t])\n\n const handleRulesetChange = React.useCallback(async (nextId: string | null) => {\n if (!resourceId) return\n const updateSchedule = () => updateCrud('resources/resources', { id: resourceId, availabilityRuleSetId: nextId }, {\n errorMessage: t('resources.resources.availability.ruleset.updateError', 'Failed to update schedule.'),\n })\n const resourceOptimisticLockHeader = buildOptimisticLockHeader(\n typeof initialValues?.updatedAt === 'string' ? initialValues.updatedAt : null,\n )\n if (Object.keys(resourceOptimisticLockHeader).length > 0) {\n await withScopedApiRequestHeaders(resourceOptimisticLockHeader, updateSchedule)\n } else {\n await updateSchedule()\n }\n setAvailabilityRuleSetId(nextId)\n flash(t('resources.resources.availability.ruleset.updateSuccess', 'Schedule updated.'), 'success')\n }, [initialValues?.updatedAt, resourceId, t])\n\n const resourceTitle =\n typeof initialValues?.name === 'string' && initialValues.name.trim().length > 0\n ? initialValues.name.trim()\n : t('resources.resources.detail.untitled', 'Unnamed resource')\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('resources.resources.form.errors.notFound', 'Resource not found.')}\n backHref=\"/backend/resources/resources\"\n backLabel={t('resources.resources.detail.back', 'Back to resources')}\n />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/resources/resources\"\n backLabel={t('resources.resources.detail.back', 'Back to resources')}\n utilityActions={(\n <>\n {resourceId ? (\n <SendObjectMessageDialog\n object={{\n entityModule: 'resources',\n entityType: 'resource',\n entityId: resourceId,\n previewData: {\n title: resourceTitle,\n },\n }}\n viewHref={`/backend/resources/resources/${resourceId}`}\n />\n ) : null}\n <VersionHistoryAction\n config={resourceId ? { resourceKind: 'resources.resource', resourceId, includeRelated: true } : null}\n t={t}\n />\n </>\n )}\n title={resourceTitle}\n subtitle={t('resources.resources.detail.subtitle', 'Resource profile and activity')}\n />\n\n <div className=\"border-b\">\n <nav className=\"flex flex-wrap items-center gap-5 text-sm\" aria-label={t('resources.resources.tabs.label', 'Resource sections')}>\n {tabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === tab.id}\n variant=\"ghost\"\n size=\"sm\"\n className={`relative -mb-px h-auto rounded-none border-b-2 px-0 py-2 font-medium ${\n activeTab === tab.id\n ? 'border-accent-indigo text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveTab(tab.id as 'details' | 'availability')}\n >\n {tab.label}\n </Button>\n ))}\n </nav>\n </div>\n\n {activeTab === 'details' ? (\n <>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex gap-2\">\n {detailTabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={`relative -mb-px h-auto rounded-none border-b-2 px-0 py-1 font-medium ${\n activeDetailTab === tab.id\n ? 'border-accent-indigo text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveDetailTab(tab.id)}\n >\n {tab.label}\n </Button>\n ))}\n </div>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={sectionAction.disabled}\n onClick={() => sectionAction.onClick()}\n >\n {sectionAction.icon ?? null}\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n {activeDetailTab === 'notes' ? (\n <NotesSection\n entityId={resourceId ?? null}\n emptyLabel={t('resources.resources.detail.notes.empty', 'No notes yet.')}\n viewerUserId={null}\n viewerName={null}\n viewerEmail={null}\n addActionLabel={t('resources.resources.detail.notes.add', 'Add note')}\n emptyState={{\n title: t('resources.resources.detail.notes.emptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('resources.resources.detail.notes.emptyAction', 'Add a note'),\n }}\n onActionChange={setSectionAction}\n translator={detailTranslator}\n labelPrefix=\"resources.resources.detail.notes\"\n inlineLabelPrefix=\"resources.resources.detail.inline\"\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n />\n ) : null}\n {activeDetailTab === 'activities' ? (\n <ActivitiesSection\n entityId={resourceId ?? null}\n addActionLabel={t('resources.resources.detail.activities.add', 'Log activity')}\n emptyState={{\n title: t('resources.resources.detail.activities.emptyTitle', 'No activities yet'),\n actionLabel: t('resources.resources.detail.activities.emptyAction', 'Add an activity'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={activitiesAdapter}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n resolveActivityPresentation={resolveActivityPresentation}\n renderCustomFields={renderCustomFields}\n labelPrefix=\"resources.resources.detail.activities\"\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n appearanceLabels={appearanceLabels}\n manageHref={manageActivityHref}\n customFieldEntityIds={['resources:resources_resource_activity']}\n />\n ) : null}\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('resources.resources.detail.formTitle', 'Resource settings')}\n </h2>\n <ResourcesResourceForm\n embedded\n title={t('resources.resources.form.editTitle', 'Edit resource')}\n backHref=\"/backend/resources/resources\"\n cancelHref=\"/backend/resources/resources\"\n successRedirect=\"/backend/resources/resources\"\n formConfig={formConfig}\n initialValues={initialValues ?? undefined}\n optimisticLockUpdatedAt={\n typeof initialValues?.updatedAt === 'string'\n ? initialValues.updatedAt\n : null\n }\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('resources.resources.form.loading', 'Loading resource...')}\n />\n </div>\n </>\n ) : (\n <AvailabilityRulesEditor\n subjectType=\"resource\"\n subjectId={resourceId ?? ''}\n labelPrefix=\"resources.resources\"\n mode={availabilityMode}\n rulesetId={availabilityRuleSetId}\n onRulesetChange={handleRulesetChange}\n buildScheduleItems={buildScheduleItems}\n />\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ActivitiesSection, NotesSection, RecordNotFoundState, type SectionAction, type TagOption } from '@open-mercato/ui/backend/detail'\nimport { VersionHistoryAction } from '@open-mercato/ui/backend/version-history'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { buildResourceScheduleItems } from '@open-mercato/core/modules/resources/lib/resourceSchedule'\nimport { RESOURCES_RESOURCE_FIELDSET_DEFAULT } from '@open-mercato/core/modules/resources/lib/resourceCustomFields'\nimport type { AvailabilityScheduleItemBuilder } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { ResourcesResourceForm, useResourcesResourceFormConfig } from '@open-mercato/core/modules/resources/components/ResourceCrudForm'\nimport { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { createResourceNotesAdapter } from '@open-mercato/core/modules/resources/components/detail/notesAdapter'\nimport { createResourceActivitiesAdapter } from '@open-mercato/core/modules/resources/components/detail/activitiesAdapter'\nimport {\n createResourceDictionaryEntry,\n loadResourceDictionary,\n type DictionaryEntryOption,\n} from '@open-mercato/core/modules/resources/components/detail/dictionaries'\nimport type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\n\ntype ResourceRecord = {\n id: string\n name: string\n description?: string | null\n resourceTypeId: string | null\n capacity: number | null\n capacityUnitValue: string | null\n capacityUnitName: string | null\n capacityUnitColor: string | null\n capacityUnitIcon: string | null\n tags?: TagOption[] | null\n isActive: boolean\n appearanceIcon?: string | null\n appearanceColor?: string | null\n resource_type_id?: string | null\n capacity_unit_value?: string | null\n capacity_unit_name?: string | null\n capacity_unit_color?: string | null\n capacity_unit_icon?: string | null\n appearance_icon?: string | null\n appearance_color?: string | null\n is_active?: boolean\n availabilityRuleSetId?: string | null\n availability_rule_set_id?: string | null\n} & Record<string, unknown>\n\ntype ResourceResponse = {\n items: ResourceRecord[]\n}\n\nfunction normalizeResourceRecord(record: ResourceRecord): ResourceRecord {\n return {\n ...record,\n resourceTypeId: record.resourceTypeId ?? record.resource_type_id ?? null,\n description: record.description ?? null,\n capacityUnitValue: record.capacityUnitValue ?? record.capacity_unit_value ?? null,\n capacityUnitName: record.capacityUnitName ?? record.capacity_unit_name ?? null,\n capacityUnitColor: record.capacityUnitColor ?? record.capacity_unit_color ?? null,\n capacityUnitIcon: record.capacityUnitIcon ?? record.capacity_unit_icon ?? null,\n appearanceIcon: record.appearanceIcon ?? record.appearance_icon ?? null,\n appearanceColor: record.appearanceColor ?? record.appearance_color ?? null,\n isActive: record.isActive ?? record.is_active ?? true,\n }\n}\n\nexport default function ResourcesResourceDetailPage({ params }: { params?: { id?: string } }) {\n const resourceId = params?.id\n const t = useT()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n const router = useRouter()\n const searchParams = useSearchParams()\n const [initialValues, setInitialValues] = React.useState<Record<string, unknown> | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n // Capture the record (incl. its optimistic-lock `updatedAt`) exactly ONCE per\n // resource. The load effect's deps include identity-unstable values (`t`,\n // `resolveFieldsetCode`), so without this guard a re-render would re-fetch and\n // overwrite `initialValues.updatedAt` with a newer server value mid-edit \u2014\n // silently defeating optimistic locking (a concurrent change would no longer be\n // detected) and making the conflict flaky.\n const loadedResourceIdRef = React.useRef<string | null>(null)\n const [tags, setTags] = React.useState<TagOption[]>([])\n const [activeTab, setActiveTab] = React.useState<'details' | 'availability'>('details')\n const [activeDetailTab, setActiveDetailTab] = React.useState<'notes' | 'activities'>('notes')\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n const [availabilityRuleSetId, setAvailabilityRuleSetId] = React.useState<string | null>(null)\n const [activityDictionaryId, setActivityDictionaryId] = React.useState<string | null>(null)\n const [activityTypeEntries, setActivityTypeEntries] = React.useState<DictionaryEntryOption[]>([])\n const flashShownRef = React.useRef(false)\n\n const availabilityMode = 'availability'\n const notesAdapter = React.useMemo(() => createResourceNotesAdapter(detailTranslator), [detailTranslator])\n const activitiesAdapter = React.useMemo(() => createResourceActivitiesAdapter(detailTranslator), [detailTranslator])\n\n const activityTypeLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('resources.resources.detail.activities.dictionary.placeholder', 'Select an activity type'),\n addLabel: t('resources.resources.detail.activities.dictionary.add', 'Add type'),\n addPrompt: t('resources.resources.detail.activities.dictionary.prompt', 'Name the type'),\n dialogTitle: t('resources.resources.detail.activities.dictionary.dialogTitle', 'Add activity type'),\n valueLabel: t('resources.resources.detail.activities.dictionary.valueLabel', 'Name'),\n valuePlaceholder: t('resources.resources.detail.activities.dictionary.valuePlaceholder', 'Name'),\n labelLabel: t('resources.resources.detail.activities.dictionary.labelLabel', 'Label'),\n labelPlaceholder: t('resources.resources.detail.activities.dictionary.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('resources.resources.detail.activities.dictionary.emptyError', 'Please enter a name'),\n cancelLabel: t('resources.resources.detail.activities.dictionary.cancel', 'Cancel'),\n saveLabel: t('resources.resources.detail.activities.dictionary.save', 'Save'),\n saveShortcutHint: t('resources.resources.detail.activities.dictionary.saveShortcut', '\u2318/Ctrl + Enter'),\n errorLoad: t('resources.resources.detail.activities.dictionary.errorLoad', 'Failed to load options'),\n errorSave: t('resources.resources.detail.activities.dictionary.errorSave', 'Failed to save option'),\n loadingLabel: t('resources.resources.detail.activities.dictionary.loading', 'Loading\u2026'),\n manageTitle: t('resources.resources.detail.activities.dictionary.manage', 'Manage dictionary'),\n }), [t])\n\n const loadActivityOptions = React.useCallback(async () => {\n const { dictionary, entries } = await loadResourceDictionary('activityTypes')\n setActivityDictionaryId(dictionary?.id ?? null)\n setActivityTypeEntries(entries)\n return entries\n }, [])\n\n const createActivityOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createResourceDictionaryEntry('activityTypes', input)\n if (!entry) {\n throw new Error(t('resources.resources.detail.activities.dictionary.errorSave', 'Failed to save option'))\n }\n return entry\n },\n [t],\n )\n\n React.useEffect(() => {\n loadActivityOptions().catch(() => {})\n }, [loadActivityOptions])\n\n const activityTypeMap = React.useMemo(\n () => new Map(activityTypeEntries.map((entry) => [entry.value, entry])),\n [activityTypeEntries],\n )\n\n const resolveActivityPresentation = React.useCallback(\n (activity: { activityType: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const entry = activityTypeMap.get(activity.activityType)\n return {\n label: entry?.label ?? activity.activityType,\n icon: entry?.icon ?? activity.appearanceIcon ?? null,\n color: entry?.color ?? activity.appearanceColor ?? null,\n }\n },\n [activityTypeMap],\n )\n\n const manageActivityHref = React.useMemo(() => {\n if (!activityDictionaryId) return '/backend/config/dictionaries'\n return `/backend/config/dictionaries?dictionaryId=${encodeURIComponent(activityDictionaryId)}`\n }, [activityDictionaryId])\n\n const appearanceLabels = React.useMemo(() => ({\n colorLabel: t('resources.resources.detail.activities.appearance.colorLabel', 'Color'),\n colorHelp: t('resources.resources.detail.activities.appearance.colorHelp', 'Pick a highlight color for this entry.'),\n colorClearLabel: t('resources.resources.detail.activities.appearance.colorClear', 'Remove color'),\n iconLabel: t('resources.resources.detail.activities.appearance.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('resources.resources.detail.activities.appearance.iconPlaceholder', 'Type an emoji or pick one of the suggestions.'),\n iconPickerTriggerLabel: t('resources.resources.detail.activities.appearance.iconBrowse', 'Browse icons and emojis'),\n iconSearchPlaceholder: t('resources.resources.detail.activities.appearance.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('resources.resources.detail.activities.appearance.iconSearchEmpty', 'No icons match your search.'),\n iconSuggestionsLabel: t('resources.resources.detail.activities.appearance.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('resources.resources.detail.activities.appearance.iconClear', 'Remove icon'),\n previewEmptyLabel: t('resources.resources.detail.activities.appearance.previewEmpty', 'No appearance selected'),\n }), [t])\n\n const renderCustomFields = React.useCallback((activity: { id?: string; customFields?: Array<{ key: string; label?: string | null; value: unknown }> }) => {\n const entries = Array.isArray(activity.customFields) ? activity.customFields : []\n if (!entries.length) return null\n const emptyLabel = t('resources.resources.detail.activities.customFields.empty', 'Not provided')\n return (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {entries.map((entry, index) => {\n const label = entry.label ?? entry.key\n const value = entry.value\n const hasValue = !(value == null || value === '' || (Array.isArray(value) && value.length === 0))\n const content = hasValue\n ? Array.isArray(value)\n ? value.map((item) => String(item)).join(', ')\n : String(value)\n : emptyLabel\n return (\n <div\n key={`activity-${activity.id ?? 'row'}-custom-${index}`}\n className=\"rounded-md border border-border/70 bg-muted/30 px-3 py-2\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n <div className=\"mt-1 text-sm text-foreground\">{content}</div>\n </div>\n )\n })}\n </div>\n )\n }, [t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const tabParam = searchParams.get('tab')\n if (tabParam === 'availability') {\n setActiveTab('availability')\n }\n const created = searchParams.get('created') === '1'\n if (created && !flashShownRef.current) {\n flashShownRef.current = true\n flash(t('resources.resources.flash.createdAvailability', 'Saved. You can now set availability.'), 'success')\n const nextParams = new URLSearchParams(searchParams.toString())\n nextParams.delete('created')\n const nextQuery = nextParams.toString()\n const nextPath = resourceId\n ? `/backend/resources/resources/${encodeURIComponent(resourceId)}${nextQuery ? `?${nextQuery}` : ''}`\n : `/backend/resources/resources${nextQuery ? `?${nextQuery}` : ''}`\n router.replace(nextPath)\n }\n }, [resourceId, router, searchParams, t])\n\n const buildScheduleItems = React.useCallback<AvailabilityScheduleItemBuilder>(\n ({ availabilityRules, translate }) => buildResourceScheduleItems({\n availabilityRules,\n isAvailableByDefault: false,\n translate,\n }),\n [],\n )\n\n const tagLabels = React.useMemo(\n () => ({\n loading: t('resources.resources.tags.loading', 'Loading tags...'),\n placeholder: t('resources.resources.tags.placeholder', 'Type to add tags'),\n empty: t('resources.resources.tags.placeholder', 'No tags yet. Add labels to keep resources organized.'),\n loadError: t('resources.resources.tags.loadError', 'Failed to load tags.'),\n createError: t('resources.resources.tags.createError', 'Failed to create tag.'),\n updateError: t('resources.resources.tags.updateError', 'Failed to update tags.'),\n labelRequired: t('resources.resources.tags.labelRequired', 'Tag name is required.'),\n saveShortcut: t('resources.resources.tags.saveShortcut', 'Save Cmd+Enter / Ctrl+Enter'),\n cancelShortcut: t('resources.resources.tags.cancelShortcut', 'Cancel (Esc)'),\n edit: t('ui.forms.actions.edit', 'Edit'),\n cancel: t('ui.forms.actions.cancel', 'Cancel'),\n success: t('resources.resources.tags.success', 'Tags updated.'),\n }),\n [t],\n )\n\n const handleSubmit = React.useCallback(async (values: Record<string, unknown>) => {\n if (!resourceId) return\n const appearance = values.appearance && typeof values.appearance === 'object'\n ? values.appearance as { icon?: string | null; color?: string | null }\n : {}\n const { appearance: _appearance, ...rest } = values\n const customFieldsetCode = typeof values.customFieldsetCode === 'string' && values.customFieldsetCode.trim().length\n ? values.customFieldsetCode.trim()\n : RESOURCES_RESOURCE_FIELDSET_DEFAULT\n const payload: Record<string, unknown> = {\n ...rest,\n id: resourceId,\n resourceTypeId: values.resourceTypeId || null,\n capacity: values.capacity ? Number(values.capacity) : null,\n capacityUnitValue: values.capacityUnitValue ? String(values.capacityUnitValue) : null,\n appearanceIcon: appearance.icon ?? null,\n appearanceColor: appearance.color ?? null,\n isActive: values.isActive ?? true,\n customFieldsetCode,\n ...collectCustomFieldValues(values),\n }\n if (!payload.name || String(payload.name).trim().length === 0) {\n throw createCrudFormError(t('resources.resources.form.errors.nameRequired', 'Name is required.'))\n }\n await updateCrud('resources/resources', payload, {\n errorMessage: t('resources.resources.form.errors.update', 'Failed to update resource.'),\n })\n flash(t('resources.resources.form.flash.updated', 'Resource updated.'), 'success')\n }, [resourceId, t])\n\n const tabs = React.useMemo(() => ([\n { id: 'details', label: t('resources.resources.tabs.details', 'Details') },\n { id: 'availability', label: t('resources.resources.tabs.availability', 'Availability') },\n ]), [t])\n const detailTabs = React.useMemo(() => ([\n { id: 'notes' as const, label: t('resources.resources.detail.tabs.notes', 'Notes') },\n { id: 'activities' as const, label: t('resources.resources.detail.tabs.activities', 'Activities') },\n ]), [t])\n\n const loadTagOptions = React.useCallback(\n async (query?: string): Promise<TagOption[]> => {\n const params = new URLSearchParams({ pageSize: '100' })\n if (query) params.set('search', query)\n const payload = await readApiResultOrThrow<Record<string, unknown>>(\n `/api/resources/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('resources.resources.tags.loadError', 'Failed to load tags.') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n return items\n .map((item: unknown): TagOption | null => {\n if (!item || typeof item !== 'object') return null\n const raw = item as { id?: unknown; tagId?: unknown; label?: unknown; slug?: unknown; color?: unknown }\n const rawId =\n typeof raw.id === 'string'\n ? raw.id\n : typeof raw.tagId === 'string'\n ? raw.tagId\n : null\n if (!rawId) return null\n const labelValue =\n (typeof raw.label === 'string' && raw.label.trim().length && raw.label.trim()) ||\n (typeof raw.slug === 'string' && raw.slug.trim().length && raw.slug.trim()) ||\n rawId\n const color = typeof raw.color === 'string' && raw.color.trim().length ? raw.color.trim() : null\n return { id: rawId, label: labelValue, color }\n })\n .filter((entry): entry is TagOption => entry !== null)\n },\n [t],\n )\n\n const createTag = React.useCallback(\n async (label: string): Promise<TagOption> => {\n const trimmed = label.trim()\n if (!trimmed.length) {\n throw new Error(t('resources.resources.tags.labelRequired', 'Tag name is required.'))\n }\n const response = await apiCallOrThrow<Record<string, unknown>>(\n '/api/resources/tags',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ label: trimmed }),\n },\n { errorMessage: t('resources.resources.tags.createError', 'Failed to create tag.') },\n )\n const payload = response.result ?? {}\n const id =\n typeof payload?.id === 'string'\n ? payload.id\n : typeof (payload as any)?.tagId === 'string'\n ? (payload as any).tagId\n : ''\n if (!id) throw new Error(t('resources.resources.tags.createError', 'Failed to create tag.'))\n const color = typeof (payload as any)?.color === 'string' && (payload as any).color.trim().length\n ? (payload as any).color.trim()\n : null\n return { id, label: trimmed, color }\n },\n [t],\n )\n\n const assignTag = React.useCallback(async (tagId: string) => {\n if (!resourceId) return\n await apiCallOrThrow(\n '/api/resources/resources/tags/assign',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tagId, resourceId }),\n },\n { errorMessage: t('resources.resources.tags.updateError', 'Failed to update tags.') },\n )\n }, [resourceId, t])\n\n const unassignTag = React.useCallback(async (tagId: string) => {\n if (!resourceId) return\n await apiCallOrThrow(\n '/api/resources/resources/tags/unassign',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tagId, resourceId }),\n },\n { errorMessage: t('resources.resources.tags.updateError', 'Failed to update tags.') },\n )\n }, [resourceId, t])\n\n const handleTagsSave = React.useCallback(\n async ({ next, added, removed }: { next: TagOption[]; added: TagOption[]; removed: TagOption[] }) => {\n if (!resourceId) return\n for (const tag of added) {\n await assignTag(tag.id)\n }\n for (const tag of removed) {\n await unassignTag(tag.id)\n }\n setTags(next)\n flash(t('resources.resources.tags.success', 'Tags updated.'), 'success')\n },\n [assignTag, resourceId, t, unassignTag],\n )\n\n const tagsSection = React.useMemo(\n () => ({\n title: t('resources.resources.tags.title', 'Tags'),\n tags,\n onChange: setTags,\n loadOptions: loadTagOptions,\n createTag,\n onSave: handleTagsSave,\n labels: tagLabels,\n }),\n [createTag, handleTagsSave, loadTagOptions, t, tagLabels, tags],\n )\n\n const formConfig = useResourcesResourceFormConfig({ tagsSection })\n const { resourceTypesLoaded, resolveFieldsetCode } = formConfig\n\n React.useEffect(() => {\n if (!resourceId || !resourceTypesLoaded) return\n // Load once per resource \u2014 never re-fetch (and thereby refresh the captured\n // optimistic-lock token) on subsequent re-renders. See loadedResourceIdRef.\n if (loadedResourceIdRef.current === resourceId) return\n setIsNotFound(false)\n let cancelled = false\n async function loadResource() {\n try {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '1')\n if (resourceId) params.set('ids', resourceId)\n const record = await readApiResultOrThrow<ResourceResponse>(`/api/resources/resources?${params.toString()}`)\n const resourceRaw = Array.isArray(record?.items) ? record.items[0] : null\n const resource = resourceRaw ? normalizeResourceRecord(resourceRaw) : null\n if (!resource) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n if (!cancelled) {\n loadedResourceIdRef.current = resourceId ?? null\n const customValues = extractCustomFieldEntries(resource)\n setTags(Array.isArray(resource.tags) ? resource.tags : [])\n setAvailabilityRuleSetId(\n typeof resource.availabilityRuleSetId === 'string'\n ? resource.availabilityRuleSetId\n : typeof resource.availability_rule_set_id === 'string'\n ? resource.availability_rule_set_id\n : null,\n )\n setInitialValues({\n id: resource.id,\n name: resource.name,\n description: resource.description ?? '',\n resourceTypeId: resource.resourceTypeId || '',\n capacity: resource.capacity ?? '',\n capacityUnitValue: resource.capacityUnitValue ?? '',\n appearance: { icon: resource.appearanceIcon ?? null, color: resource.appearanceColor ?? null },\n isActive: resource.isActive ?? true,\n updatedAt:\n typeof resource.updatedAt === 'string'\n ? resource.updatedAt\n : typeof resource.updated_at === 'string'\n ? resource.updated_at\n : null,\n customFieldsetCode: resource.resourceTypeId\n ? resolveFieldsetCode(resource.resourceTypeId)\n : RESOURCES_RESOURCE_FIELDSET_DEFAULT,\n ...customValues,\n })\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('resources.resources.form.errors.load', 'Failed to load resource.')\n flash(message, 'error')\n }\n }\n loadResource()\n return () => { cancelled = true }\n }, [resourceId, resourceTypesLoaded, resolveFieldsetCode, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!resourceId) return\n await deleteCrud('resources/resources', resourceId, {\n errorMessage: t('resources.resources.form.errors.delete', 'Failed to delete resource.'),\n })\n flash(t('resources.resources.form.flash.deleted', 'Resource deleted.'), 'success')\n router.push('/backend/resources/resources')\n }, [resourceId, router, t])\n\n const handleRulesetChange = React.useCallback(async (nextId: string | null) => {\n if (!resourceId) return\n const updateSchedule = () => updateCrud('resources/resources', { id: resourceId, availabilityRuleSetId: nextId }, {\n errorMessage: t('resources.resources.availability.ruleset.updateError', 'Failed to update schedule.'),\n })\n const resourceOptimisticLockHeader = buildOptimisticLockHeader(\n typeof initialValues?.updatedAt === 'string' ? initialValues.updatedAt : null,\n )\n if (Object.keys(resourceOptimisticLockHeader).length > 0) {\n await withScopedApiRequestHeaders(resourceOptimisticLockHeader, updateSchedule)\n } else {\n await updateSchedule()\n }\n setAvailabilityRuleSetId(nextId)\n flash(t('resources.resources.availability.ruleset.updateSuccess', 'Schedule updated.'), 'success')\n }, [initialValues?.updatedAt, resourceId, t])\n\n const resourceTitle =\n typeof initialValues?.name === 'string' && initialValues.name.trim().length > 0\n ? initialValues.name.trim()\n : t('resources.resources.detail.untitled', 'Unnamed resource')\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('resources.resources.form.errors.notFound', 'Resource not found.')}\n backHref=\"/backend/resources/resources\"\n backLabel={t('resources.resources.detail.back', 'Back to resources')}\n />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/resources/resources\"\n backLabel={t('resources.resources.detail.back', 'Back to resources')}\n utilityActions={(\n <>\n {resourceId ? (\n <SendObjectMessageDialog\n object={{\n entityModule: 'resources',\n entityType: 'resource',\n entityId: resourceId,\n previewData: {\n title: resourceTitle,\n },\n }}\n viewHref={`/backend/resources/resources/${resourceId}`}\n />\n ) : null}\n <VersionHistoryAction\n config={resourceId ? { resourceKind: 'resources.resource', resourceId, includeRelated: true } : null}\n t={t}\n />\n </>\n )}\n title={resourceTitle}\n subtitle={t('resources.resources.detail.subtitle', 'Resource profile and activity')}\n />\n\n <div className=\"border-b\">\n <nav className=\"flex flex-wrap items-center gap-5 text-sm\" aria-label={t('resources.resources.tabs.label', 'Resource sections')}>\n {tabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === tab.id}\n variant=\"ghost\"\n size=\"sm\"\n className={`relative -mb-px h-auto rounded-none border-b-2 px-0 py-2 font-medium ${\n activeTab === tab.id\n ? 'border-accent-indigo text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveTab(tab.id as 'details' | 'availability')}\n >\n {tab.label}\n </Button>\n ))}\n </nav>\n </div>\n\n {activeTab === 'details' ? (\n <>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex gap-2\">\n {detailTabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={`relative -mb-px h-auto rounded-none border-b-2 px-0 py-1 font-medium ${\n activeDetailTab === tab.id\n ? 'border-accent-indigo text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveDetailTab(tab.id)}\n >\n {tab.label}\n </Button>\n ))}\n </div>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={sectionAction.disabled}\n onClick={() => sectionAction.onClick()}\n >\n {sectionAction.icon ?? null}\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n {activeDetailTab === 'notes' ? (\n <NotesSection\n entityId={resourceId ?? null}\n emptyLabel={t('resources.resources.detail.notes.empty', 'No notes yet.')}\n viewerUserId={null}\n viewerName={null}\n viewerEmail={null}\n addActionLabel={t('resources.resources.detail.notes.add', 'Add note')}\n emptyState={{\n title: t('resources.resources.detail.notes.emptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('resources.resources.detail.notes.emptyAction', 'Add a note'),\n }}\n onActionChange={setSectionAction}\n translator={detailTranslator}\n labelPrefix=\"resources.resources.detail.notes\"\n inlineLabelPrefix=\"resources.resources.detail.inline\"\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n />\n ) : null}\n {activeDetailTab === 'activities' ? (\n <ActivitiesSection\n entityId={resourceId ?? null}\n addActionLabel={t('resources.resources.detail.activities.add', 'Log activity')}\n emptyState={{\n title: t('resources.resources.detail.activities.emptyTitle', 'No activities yet'),\n actionLabel: t('resources.resources.detail.activities.emptyAction', 'Add an activity'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={activitiesAdapter}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n resolveActivityPresentation={resolveActivityPresentation}\n renderCustomFields={renderCustomFields}\n labelPrefix=\"resources.resources.detail.activities\"\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n appearanceLabels={appearanceLabels}\n manageHref={manageActivityHref}\n customFieldEntityIds={['resources:resources_resource_activity']}\n />\n ) : null}\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('resources.resources.detail.formTitle', 'Resource settings')}\n </h2>\n <ResourcesResourceForm\n embedded\n title={t('resources.resources.form.editTitle', 'Edit resource')}\n backHref=\"/backend/resources/resources\"\n cancelHref=\"/backend/resources/resources\"\n successRedirect=\"/backend/resources/resources\"\n formConfig={formConfig}\n initialValues={initialValues ?? undefined}\n optimisticLockUpdatedAt={\n typeof initialValues?.updatedAt === 'string'\n ? initialValues.updatedAt\n : null\n }\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('resources.resources.form.loading', 'Loading resource...')}\n />\n </div>\n </>\n ) : (\n <AvailabilityRulesEditor\n subjectType=\"resource\"\n subjectId={resourceId ?? ''}\n labelPrefix=\"resources.resources\"\n mode={availabilityMode}\n rulesetId={availabilityRuleSetId}\n onRulesetChange={handleRulesetChange}\n buildScheduleItems={buildScheduleItems}\n />\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["params"]
|
|
7
7
|
}
|
|
@@ -280,6 +280,13 @@ async function executeWorkflow(em, container, instanceId, context) {
|
|
|
280
280
|
const rejectionMessage = transitionResult.error || "Transition failed";
|
|
281
281
|
console.error(`[WORKFLOW] Transition rejected (instance: ${currentInstance.id}, workflow: ${currentInstance.workflowId}, step: ${currentInstance.currentStepId} \u2192 ${selectedTransition.toStepId}): ${rejectionMessage}`);
|
|
282
282
|
errors.push(rejectionMessage);
|
|
283
|
+
await completeWorkflow(trx, container, instanceId, "FAILED", {
|
|
284
|
+
error: rejectionMessage
|
|
285
|
+
});
|
|
286
|
+
events.push({
|
|
287
|
+
eventType: "WORKFLOW_FAILED",
|
|
288
|
+
occurredAt: /* @__PURE__ */ new Date()
|
|
289
|
+
});
|
|
283
290
|
return {
|
|
284
291
|
status: "FAILED",
|
|
285
292
|
currentStep: currentInstance.currentStepId,
|
|
@@ -341,6 +348,14 @@ async function executeWorkflow(em, container, instanceId, context) {
|
|
|
341
348
|
error: errorMessage
|
|
342
349
|
}
|
|
343
350
|
});
|
|
351
|
+
await completeWorkflow(trx, container, instanceId, "FAILED", {
|
|
352
|
+
error: errorMessage,
|
|
353
|
+
details: error instanceof WorkflowExecutionError ? error.details : void 0
|
|
354
|
+
});
|
|
355
|
+
events.push({
|
|
356
|
+
eventType: "WORKFLOW_FAILED",
|
|
357
|
+
occurredAt: /* @__PURE__ */ new Date()
|
|
358
|
+
});
|
|
344
359
|
return {
|
|
345
360
|
status: "FAILED",
|
|
346
361
|
currentStep: currentInstance.currentStepId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/workflows/lib/workflow-executor.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Workflows Module - Workflow Executor Service\n *\n * Main orchestrator for workflow execution. Handles workflow lifecycle:\n * - Starting workflow instances from definitions\n * - Executing workflow steps and transitions\n * - Completing workflows with final status\n * - Triggering compensation on failure\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager, LockMode } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\nimport {\n WorkflowDefinition,\n WorkflowInstance,\n WorkflowEvent,\n type WorkflowInstanceStatus,\n} from '../data/entities'\nimport { compensateWorkflow } from './compensation-handler'\nimport { findWorkflowDefinition } from './find-definition'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface StartWorkflowOptions {\n workflowId: string\n version?: number // Default to latest enabled version\n initialContext?: Record<string, any>\n correlationKey?: string\n metadata?: {\n entityType?: string\n entityId?: string\n initiatedBy?: string\n labels?: Record<string, string>\n }\n tenantId: string\n organizationId: string\n}\n\nexport interface ExecutionContext {\n userId?: string\n dryRun?: boolean\n timeout?: number\n}\n\nexport interface ExecutionResult {\n status: WorkflowInstanceStatus\n currentStep: string\n context: Record<string, any>\n events: WorkflowEventSummary[]\n errors?: string[]\n executionTime: number\n}\n\nexport interface WorkflowEventSummary {\n eventType: string\n occurredAt: Date\n data?: any\n}\n\nexport class WorkflowExecutionError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'WorkflowExecutionError'\n }\n}\n\n// ============================================================================\n// Main Orchestration Functions\n// ============================================================================\n\n/**\n * Start a new workflow instance from a definition\n *\n * @param em - Entity manager for database operations\n * @param options - Workflow start options\n * @returns Created workflow instance\n * @throws WorkflowExecutionError if definition not found or validation fails\n */\nexport async function startWorkflow(\n em: EntityManager,\n options: StartWorkflowOptions\n): Promise<WorkflowInstance> {\n const {\n workflowId,\n version,\n initialContext = {},\n correlationKey,\n metadata,\n tenantId,\n organizationId,\n } = options\n\n // Find workflow definition\n const definition = await findWorkflowDefinition(em, {\n workflowId,\n version,\n tenantId,\n organizationId,\n })\n\n if (!definition) {\n throw new WorkflowExecutionError(\n `Workflow definition not found: ${workflowId}${version ? ` v${version}` : ''}`,\n 'DEFINITION_NOT_FOUND',\n { workflowId, version }\n )\n }\n\n if (!definition.enabled) {\n throw new WorkflowExecutionError(\n `Workflow definition is disabled: ${workflowId}`,\n 'DEFINITION_DISABLED',\n { workflowId, version: definition.version }\n )\n }\n\n // Validate definition has required steps\n const { steps, transitions } = definition.definition\n if (!steps || steps.length < 2) {\n throw new WorkflowExecutionError(\n 'Workflow definition must have at least START and END steps',\n 'INVALID_DEFINITION',\n { workflowId, stepsCount: steps?.length || 0 }\n )\n }\n\n if (!transitions || transitions.length < 1) {\n throw new WorkflowExecutionError(\n 'Workflow definition must have at least one transition',\n 'INVALID_DEFINITION',\n { workflowId, transitionsCount: transitions?.length || 0 }\n )\n }\n\n // Find START step\n const startStep = steps.find((s: any) => s.stepType === 'START')\n if (!startStep) {\n throw new WorkflowExecutionError(\n 'Workflow definition must have a START step',\n 'INVALID_DEFINITION',\n { workflowId }\n )\n }\n\n // Validate START step pre-conditions if defined\n if (startStep.preConditions && startStep.preConditions.length > 0) {\n const { validateWorkflowStart } = await import('./start-validator')\n\n const validationResult = await validateWorkflowStart(em, {\n workflowId,\n version: definition.version,\n context: initialContext,\n tenantId,\n organizationId,\n })\n\n if (!validationResult.canStart) {\n throw new WorkflowExecutionError(\n `Workflow start pre-conditions failed: ${validationResult.errors.map(e => e.message).join('; ')}`,\n 'START_PRE_CONDITIONS_FAILED',\n {\n workflowId,\n errors: validationResult.errors,\n validatedRules: validationResult.validatedRules,\n }\n )\n }\n }\n\n // Create workflow instance\n const now = new Date()\n const instance = em.create(WorkflowInstance, {\n definitionId: definition.id,\n workflowId: definition.workflowId,\n version: definition.version,\n status: 'RUNNING',\n currentStepId: startStep.stepId,\n context: initialContext,\n correlationKey,\n metadata,\n startedAt: now,\n retryCount: 0,\n tenantId,\n organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(instance).flush()\n\n // Log WORKFLOW_STARTED event\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'WORKFLOW_STARTED',\n eventData: {\n workflowId: instance.workflowId,\n version: instance.version,\n startStepId: startStep.stepId,\n initialContext,\n metadata,\n },\n userId: metadata?.initiatedBy,\n tenantId,\n organizationId,\n })\n\n return instance\n}\n\n/**\n * Execute a workflow instance\n *\n * Main execution loop that processes steps and transitions until:\n * - Workflow completes (reaches END step)\n * - Workflow waits (USER_TASK, SIGNAL, TIMER)\n * - Workflow fails (error occurs)\n * - Timeout is reached\n *\n * @param em - Entity manager\n * @param container - DI container (for activity execution and other services)\n * @param instanceId - Workflow instance ID\n * @param context - Execution context (userId, dryRun, timeout)\n * @returns Execution result with status and events\n */\nexport async function executeWorkflow(\n em: EntityManager,\n container: AwilixContainer,\n instanceId: string,\n context?: ExecutionContext\n): Promise<ExecutionResult> {\n const startTime = Date.now()\n const transactionalEm = em as EntityManager & {\n transactional?: <TResult>(\n callback: (trx: EntityManager) => Promise<TResult>,\n ) => Promise<TResult>\n }\n\n const runExecution = async (trx: EntityManager): Promise<ExecutionResult> => {\n const events: WorkflowEventSummary[] = []\n const errors: string[] = []\n\n try {\n const instance = await getWorkflowInstanceForExecution(trx, instanceId)\n if (!instance) {\n throw new WorkflowExecutionError(\n `Workflow instance not found: ${instanceId}`,\n 'INSTANCE_NOT_FOUND',\n { instanceId }\n )\n }\n\n if (instance.status === 'COMPLETED') {\n return {\n status: 'COMPLETED',\n currentStep: instance.currentStepId,\n context: instance.context,\n events: [],\n executionTime: 0,\n }\n }\n\n if (instance.status === 'CANCELLED') {\n throw new WorkflowExecutionError(\n 'Cannot execute cancelled workflow',\n 'WORKFLOW_CANCELLED',\n { instanceId, status: instance.status }\n )\n }\n\n const definition = await trx.findOne(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n if (!definition) {\n throw new WorkflowExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n const maxIterations = 100\n let iterations = 0\n\n while (iterations < maxIterations) {\n iterations++\n\n const currentInstance = await getWorkflowInstanceForExecution(trx, instanceId, { refresh: iterations > 1 })\n if (!currentInstance) {\n throw new WorkflowExecutionError(\n 'Instance not found during execution',\n 'INSTANCE_NOT_FOUND',\n { instanceId }\n )\n }\n\n // Parallel execution: while the instance is FORKED, drive the branches.\n if (currentInstance.status === 'FORKED') {\n const { advanceBranches } = await import('./parallel-handler')\n const branchResult = await advanceBranches(trx, container, currentInstance, definition, {\n userId: context?.userId,\n })\n\n if (branchResult.outcome === 'joined') {\n // Instance resumed at the post-join step; continue single-token.\n continue\n }\n\n if (branchResult.outcome === 'failed') {\n errors.push(branchResult.error || 'Parallel branch failed')\n await completeWorkflow(trx, container, instanceId, 'FAILED', {\n error: branchResult.error || 'Parallel branch failed',\n })\n return {\n status: 'FAILED',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n errors,\n executionTime: Date.now() - startTime,\n }\n }\n\n // 'waiting' \u2014 all branches paused for external resume (task/signal/timer/async).\n return {\n status: 'RUNNING',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n const currentStep = definition.definition.steps.find(\n (s: any) => s.stepId === currentInstance.currentStepId\n )\n\n if (currentStep?.stepType === 'END') {\n await completeWorkflow(trx, container, instanceId, 'COMPLETED')\n events.push({\n eventType: 'WORKFLOW_COMPLETED',\n occurredAt: new Date(),\n })\n\n return {\n status: 'COMPLETED',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n if (\n currentStep?.stepType === 'USER_TASK' ||\n currentStep?.stepType === 'WAIT_FOR_SIGNAL' ||\n currentStep?.stepType === 'WAIT_FOR_TIMER'\n ) {\n return {\n status: 'RUNNING',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n const transitions = definition.definition.transitions.filter(\n (t: any) =>\n t.fromStepId === currentInstance.currentStepId &&\n t.trigger === 'auto'\n )\n\n if (transitions.length === 0) {\n return {\n status: 'RUNNING',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n const transitionHandler = await import('./transition-handler')\n const evalContext: any = {\n workflowContext: currentInstance.context,\n userId: context?.userId,\n }\n\n const validTransitions = await transitionHandler.findValidTransitions(\n trx,\n currentInstance,\n currentInstance.currentStepId!,\n evalContext\n )\n\n const validAutoTransitions = validTransitions.filter(\n (vt) => vt.isValid && vt.transition?.trigger === 'auto'\n )\n\n if (validAutoTransitions.length === 0) {\n return {\n status: 'RUNNING',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n const selectedTransition = validAutoTransitions[0].transition\n\n try {\n const transitionResult = await transitionHandler.executeTransition(\n trx,\n container,\n currentInstance,\n selectedTransition.fromStepId,\n selectedTransition.toStepId,\n evalContext\n )\n\n if (!transitionResult.success) {\n const rejectionMessage = transitionResult.error || 'Transition failed'\n console.error(`[WORKFLOW] Transition rejected (instance: ${currentInstance.id}, workflow: ${currentInstance.workflowId}, step: ${currentInstance.currentStepId} \u2192 ${selectedTransition.toStepId}): ${rejectionMessage}`)\n errors.push(rejectionMessage)\n\n return {\n status: 'FAILED',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n errors,\n executionTime: Date.now() - startTime,\n }\n }\n\n events.push({\n eventType: 'TRANSITION_EXECUTED',\n occurredAt: new Date(),\n data: {\n fromStepId: selectedTransition.fromStepId,\n toStepId: selectedTransition.toStepId,\n transitionId: selectedTransition.transitionId,\n },\n })\n\n if (transitionResult.pausedForActivities) {\n await logWorkflowEvent(trx, {\n workflowInstanceId: currentInstance.id,\n eventType: 'WORKFLOW_WAITING_FOR_ACTIVITIES',\n eventData: {\n pendingActivities: transitionResult.activitiesExecuted?.filter(a => a.async),\n pausedAtTransition: {\n fromStepId: selectedTransition.fromStepId,\n toStepId: selectedTransition.toStepId,\n },\n },\n tenantId: currentInstance.tenantId,\n organizationId: currentInstance.organizationId,\n })\n\n events.push({\n eventType: 'WORKFLOW_WAITING_FOR_ACTIVITIES',\n occurredAt: new Date(),\n data: {\n pendingActivities: transitionResult.activitiesExecuted?.filter(a => a.async),\n },\n })\n\n return {\n status: 'WAITING_FOR_ACTIVITIES',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n await trx.flush()\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(`[WORKFLOW] Transition execution failed (instance: ${currentInstance.id}, workflow: ${currentInstance.workflowId}, step: ${currentInstance.currentStepId} \u2192 ${selectedTransition.toStepId}):`, error)\n console.error('[WORKFLOW] Error stack:', error instanceof Error ? error.stack : 'No stack trace')\n errors.push(errorMessage)\n\n events.push({\n eventType: 'TRANSITION_FAILED',\n occurredAt: new Date(),\n data: {\n transitionId: selectedTransition.transitionId,\n error: errorMessage,\n },\n })\n\n return {\n status: 'FAILED',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n errors,\n executionTime: Date.now() - startTime,\n }\n }\n }\n\n errors.push('Maximum execution iterations reached - possible infinite loop')\n return {\n status: 'RUNNING',\n currentStep: instance.currentStepId,\n context: instance.context,\n events,\n errors,\n executionTime: Date.now() - startTime,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(`[WORKFLOW] Execution failed (instance: ${instanceId}):`, error)\n if (error instanceof Error && error.stack) {\n console.error('[WORKFLOW] Error stack:', error.stack)\n }\n errors.push(errorMessage)\n\n try {\n const instance = await getWorkflowInstanceForExecution(trx, instanceId, { refresh: true })\n if (instance && instance.status === 'RUNNING') {\n instance.status = 'FAILED'\n instance.errorMessage = errorMessage\n instance.errorDetails = error instanceof WorkflowExecutionError ? error.details : undefined\n instance.updatedAt = new Date()\n await trx.flush()\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instanceId,\n eventType: 'WORKFLOW_FAILED',\n eventData: { error: errorMessage },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n }\n } catch (updateError) {\n console.error(`[WORKFLOW] Failed to update instance ${instanceId} with error state:`, updateError)\n }\n\n throw error\n }\n }\n\n return typeof transactionalEm.transactional === 'function'\n ? transactionalEm.transactional((trx) => runExecution(trx))\n : runExecution(em)\n}\n\n/**\n * Complete a workflow instance with final status\n *\n * @param em - Entity manager\n * @param container\n * @param instanceId - Workflow instance ID\n * @param status - Final status (COMPLETED, FAILED, CANCELLED)\n * @param result - Optional result data\n */\nexport async function completeWorkflow(\n em: EntityManager,\n container: AwilixContainer,\n instanceId: string,\n status: 'COMPLETED' | 'FAILED' | 'CANCELLED',\n result?: any\n): Promise<void> {\n const instance = await getWorkflowInstance(em, instanceId)\n if (!instance) {\n throw new WorkflowExecutionError(\n `Workflow instance not found: ${instanceId}`,\n 'INSTANCE_NOT_FOUND',\n { instanceId }\n )\n }\n\n // Trigger compensation if workflow failed and has compensatable activities (Phase 8.2)\n if (status === 'FAILED') {\n const definition = await em.findOne(WorkflowDefinition, { id: instance.definitionId })\n\n if (definition && checkIfCompensationNeeded(definition)) {\n try {\n\n // Set error message before compensation\n if (result?.error) {\n instance.errorMessage = result.error\n instance.errorDetails = result.details\n await em.flush()\n }\n\n const compensationResult = await compensateWorkflow(\n em,\n container,\n instance,\n definition,\n {\n continueOnError: true // Best-effort compensation\n }\n )\n\n console.log(\n `[Workflow] Compensation ${compensationResult.status}: ${compensationResult.compensatedActivities}/${compensationResult.totalActivities} activities`\n )\n\n // Note: instance status already updated by compensateWorkflow\n // It will be COMPENSATED or remain FAILED\n return\n } catch (error: any) {\n console.error(`[Workflow] Compensation failed with exception:`, error)\n // Continue to mark workflow as failed\n }\n }\n }\n\n // Original completion logic (no compensation needed or status is COMPLETED/CANCELLED)\n const now = new Date()\n instance.status = status\n instance.updatedAt = now\n\n switch (status) {\n case 'COMPLETED':\n instance.completedAt = now\n if (result) {\n instance.context = { ...instance.context, __result: result }\n }\n break\n\n case 'FAILED':\n instance.completedAt = now\n if (result?.error) {\n instance.errorMessage = result.error\n instance.errorDetails = result.details\n }\n break\n\n case 'CANCELLED':\n instance.cancelledAt = now\n break\n }\n\n await em.flush()\n\n // Log completion event\n const eventType =\n status === 'COMPLETED'\n ? 'WORKFLOW_COMPLETED'\n : status === 'FAILED'\n ? 'WORKFLOW_FAILED'\n : 'WORKFLOW_CANCELLED'\n\n await logWorkflowEvent(em, {\n workflowInstanceId: instanceId,\n eventType,\n eventData: result || {},\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n}\n\n/**\n * Resume workflow after async activities complete\n *\n * Called by the activity worker after all async activities finish execution.\n * Checks if all activities are done, merges outputs into context, and resumes execution.\n *\n * @param em - Entity manager\n * @param container - DI container\n * @param instanceId - Workflow instance ID\n */\nexport async function resumeWorkflowAfterActivities(\n em: EntityManager,\n container: AwilixContainer,\n instanceId: string,\n branchInstanceId?: string | null\n): Promise<void> {\n const transactionalEm = em as EntityManager & {\n transactional?: <TResult>(callback: (trx: EntityManager) => Promise<TResult>) => Promise<TResult>\n }\n\n // Branch-scoped async resume: the instance is FORKED and the branch (not the\n // instance) is WAITING_FOR_ACTIVITIES. Resume just that branch, then let the\n // interleaved loop continue. Missing branchInstanceId \u2192 legacy instance path.\n if (branchInstanceId) {\n const { resumeBranchAfterActivities } = await import('./parallel-handler')\n const branchResume = typeof transactionalEm.transactional === 'function'\n ? await transactionalEm.transactional((trx) => resumeBranchAfterActivities(trx, container, instanceId, branchInstanceId))\n : await resumeBranchAfterActivities(em, container, instanceId, branchInstanceId)\n if (branchResume.continueExecution) {\n await executeWorkflow(em, container, instanceId)\n }\n return\n }\n\n const runResume = async (trx: EntityManager): Promise<{ continueExecution: boolean }> => {\n const instance = await trx.findOne(WorkflowInstance, {\n id: instanceId,\n status: 'WAITING_FOR_ACTIVITIES',\n }, { lockMode: LockMode.PESSIMISTIC_WRITE })\n\n if (!instance) {\n throw new Error('Workflow instance not waiting for activities')\n }\n\n const pendingJobIds = (instance.context._pendingAsyncActivities as any[]) || []\n\n const completedActivities = await trx.count(WorkflowEvent, {\n workflowInstanceId: instanceId,\n eventType: 'ACTIVITY_COMPLETED',\n eventData: { async: true },\n })\n\n const failedActivities = await trx.count(WorkflowEvent, {\n workflowInstanceId: instanceId,\n eventType: 'ACTIVITY_FAILED',\n eventData: { async: true },\n })\n\n const totalProcessed = completedActivities + failedActivities\n\n if (totalProcessed < pendingJobIds.length) {\n throw new Error('Activities still pending')\n }\n\n if (failedActivities > 0) {\n const failedEvents = await trx.find(WorkflowEvent, {\n workflowInstanceId: instanceId,\n eventType: 'ACTIVITY_FAILED',\n eventData: { async: true },\n })\n\n instance.status = 'FAILED'\n instance.errorMessage = `${failedActivities} async activities failed`\n instance.errorDetails = {\n failedActivities: failedEvents.map(e => ({\n activityId: e.eventData.activityId,\n error: e.eventData.error,\n jobId: e.eventData.jobId,\n })),\n }\n await trx.flush()\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instanceId,\n eventType: 'WORKFLOW_FAILED',\n eventData: {\n reason: 'Async activities failed',\n failedActivities: instance.errorDetails.failedActivities,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return { continueExecution: false }\n }\n\n const completedEvents = await trx.find(WorkflowEvent, {\n workflowInstanceId: instanceId,\n eventType: 'ACTIVITY_COMPLETED',\n eventData: { async: true },\n })\n\n for (const event of completedEvents) {\n if (event.eventData.output) {\n instance.context = {\n ...instance.context,\n [`${event.eventData.activityId}_result`]: event.eventData.output,\n }\n }\n }\n\n delete instance.context._pendingAsyncActivities\n\n const pendingTransition = instance.pendingTransition\n\n if (!pendingTransition) {\n console.warn('[WORKFLOW] No pending transition found during resume')\n instance.status = 'RUNNING'\n await trx.flush()\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instanceId,\n eventType: 'WORKFLOW_RESUMED',\n eventData: {\n reason: 'All async activities completed',\n completedActivities: completedActivities,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return { continueExecution: true }\n }\n\n console.log('[WORKFLOW] Completing pending transition:', {\n toStepId: pendingTransition.toStepId,\n from: instance.currentStepId,\n })\n\n const definition = await trx.findOneOrFail(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n const step = definition.definition.steps.find(s => s.stepId === pendingTransition.toStepId)\n\n instance.currentStepId = pendingTransition.toStepId\n instance.status = 'RUNNING'\n instance.pendingTransition = null\n instance.updatedAt = new Date()\n await trx.flush()\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instance.id,\n eventType: 'STEP_ENTERED',\n eventData: {\n stepId: pendingTransition.toStepId,\n stepName: step?.stepName,\n stepType: step?.stepType,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instanceId,\n eventType: 'WORKFLOW_RESUMED',\n eventData: {\n reason: 'Async activities completed, resuming pending transition',\n completedActivities: completedActivities,\n completedTransitionTo: pendingTransition.toStepId,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n const { executeStep } = await import('./step-handler')\n await executeStep(\n trx,\n instance,\n pendingTransition.toStepId,\n {\n workflowContext: instance.context || {},\n userId: undefined,\n },\n container\n )\n\n return { continueExecution: true }\n }\n\n const resumeResult = typeof transactionalEm.transactional === 'function'\n ? await transactionalEm.transactional((trx) => runResume(trx))\n : await runResume(em)\n\n if (resumeResult.continueExecution) {\n await executeWorkflow(em, container, instanceId)\n }\n}\n\n/**\n * Check if workflow definition has any compensatable activities\n */\nfunction checkIfCompensationNeeded(definition: WorkflowDefinition): boolean {\n // Check if any activities have compensation defined\n for (const transition of definition.definition.transitions) {\n if (transition.activities) {\n for (const activity of transition.activities) {\n if (activity.compensation?.activityId) {\n return true\n }\n }\n }\n }\n\n // Check root-level activities (legacy)\n if (definition.definition.activities) {\n for (const activity of definition.definition.activities) {\n if (activity.compensation?.activityId) {\n return true\n }\n }\n }\n\n return false\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Get workflow instance by ID\n *\n * @param em - Entity manager\n * @param instanceId - Workflow instance ID\n * @returns Workflow instance or null if not found\n */\nexport async function getWorkflowInstance(\n em: EntityManager,\n instanceId: string\n): Promise<WorkflowInstance | null> {\n return em.findOne(WorkflowInstance, { id: instanceId })\n}\n\nasync function getWorkflowInstanceForExecution(\n em: EntityManager,\n instanceId: string,\n options?: { refresh?: boolean }\n): Promise<WorkflowInstance | null> {\n return em.findOne(\n WorkflowInstance,\n { id: instanceId },\n {\n lockMode: LockMode.PESSIMISTIC_WRITE,\n ...(options?.refresh ? { refresh: true } : {}),\n }\n )\n}\n\n/**\n * Update workflow context with new data\n *\n * @param em - Entity manager\n * @param instanceId - Workflow instance ID\n * @param updates - Context updates (merged with existing context)\n */\nexport async function updateWorkflowContext(\n em: EntityManager,\n instanceId: string,\n updates: Record<string, any>\n): Promise<void> {\n const instance = await getWorkflowInstance(em, instanceId)\n if (!instance) {\n throw new WorkflowExecutionError(\n `Workflow instance not found: ${instanceId}`,\n 'INSTANCE_NOT_FOUND',\n { instanceId }\n )\n }\n\n instance.context = {\n ...instance.context,\n ...updates,\n }\n instance.updatedAt = new Date()\n\n await em.flush()\n}\n\n// findWorkflowDefinition is imported from ./find-definition\n\n/**\n * Log workflow event to event sourcing table\n *\n * @param em - Entity manager\n * @param event - Event data\n */\nasync function logWorkflowEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n stepInstanceId?: string\n eventType: string\n eventData: any\n userId?: string\n tenantId: string\n organizationId: string\n }\n): Promise<WorkflowEvent> {\n const workflowEvent = em.create(WorkflowEvent, {\n ...event,\n occurredAt: new Date(),\n })\n\n await em.persist(workflowEvent).flush()\n return workflowEvent\n}\n"],
|
|
5
|
-
"mappings": "AAYA,SAAwB,gBAAgB;AAExC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AA0ChC,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACE,SACO,MACA,SACP;AACA,UAAM,OAAO;AAHN;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAcA,eAAsB,cACpB,IACA,SAC2B;AAC3B,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,iBAAiB,CAAC;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,QAAM,aAAa,MAAM,uBAAuB,IAAI;AAAA,IAClD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,CAAC,YAAY;AACf,UAAM,IAAI;AAAA,MACR,kCAAkC,UAAU,GAAG,UAAU,KAAK,OAAO,KAAK,EAAE;AAAA,MAC5E;AAAA,MACA,EAAE,YAAY,QAAQ;AAAA,IACxB;AAAA,EACF;AAEA,MAAI,CAAC,WAAW,SAAS;AACvB,UAAM,IAAI;AAAA,MACR,oCAAoC,UAAU;AAAA,MAC9C;AAAA,MACA,EAAE,YAAY,SAAS,WAAW,QAAQ;AAAA,IAC5C;AAAA,EACF;AAGA,QAAM,EAAE,OAAO,YAAY,IAAI,WAAW;AAC1C,MAAI,CAAC,SAAS,MAAM,SAAS,GAAG;AAC9B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,YAAY,YAAY,OAAO,UAAU,EAAE;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,CAAC,eAAe,YAAY,SAAS,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,YAAY,kBAAkB,aAAa,UAAU,EAAE;AAAA,IAC3D;AAAA,EACF;AAGA,QAAM,YAAY,MAAM,KAAK,CAAC,MAAW,EAAE,aAAa,OAAO;AAC/D,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,WAAW;AAAA,IACf;AAAA,EACF;AAGA,MAAI,UAAU,iBAAiB,UAAU,cAAc,SAAS,GAAG;AACjE,UAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,mBAAmB;AAElE,UAAM,mBAAmB,MAAM,sBAAsB,IAAI;AAAA,MACvD;AAAA,MACA,SAAS,WAAW;AAAA,MACpB,SAAS;AAAA,MACT;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,CAAC,iBAAiB,UAAU;AAC9B,YAAM,IAAI;AAAA,QACR,yCAAyC,iBAAiB,OAAO,IAAI,OAAK,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QAC/F;AAAA,QACA;AAAA,UACE;AAAA,UACA,QAAQ,iBAAiB;AAAA,UACzB,gBAAgB,iBAAiB;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,WAAW,GAAG,OAAO,kBAAkB;AAAA,IAC3C,cAAc,WAAW;AAAA,IACzB,YAAY,WAAW;AAAA,IACvB,SAAS,WAAW;AAAA,IACpB,QAAQ;AAAA,IACR,eAAe,UAAU;AAAA,IACzB,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,WAAW;AAAA,EACb,CAAC;AAED,QAAM,GAAG,QAAQ,QAAQ,EAAE,MAAM;AAGjC,QAAM,iBAAiB,IAAI;AAAA,IACzB,oBAAoB,SAAS;AAAA,IAC7B,WAAW;AAAA,IACX,WAAW;AAAA,MACT,YAAY,SAAS;AAAA,MACrB,SAAS,SAAS;AAAA,MAClB,aAAa,UAAU;AAAA,MACvB;AAAA,MACA;AAAA,IACF;AAAA,IACA,QAAQ,UAAU;AAAA,IAClB;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAiBA,eAAsB,gBACpB,IACA,WACA,YACA,SAC0B;AAC1B,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,kBAAkB;AAMxB,QAAM,eAAe,OAAO,QAAiD;AAC3E,UAAM,SAAiC,CAAC;AACxC,UAAM,SAAmB,CAAC;AAE1B,QAAI;AACF,YAAM,WAAW,MAAM,gCAAgC,KAAK,UAAU;AACtE,UAAI,CAAC,UAAU;AACb,cAAM,IAAI;AAAA,UACR,gCAAgC,UAAU;AAAA,UAC1C;AAAA,UACA,EAAE,WAAW;AAAA,QACf;AAAA,MACF;AAEA,UAAI,SAAS,WAAW,aAAa;AACnC,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,aAAa,SAAS;AAAA,UACtB,SAAS,SAAS;AAAA,UAClB,QAAQ,CAAC;AAAA,UACT,eAAe;AAAA,QACjB;AAAA,MACF;AAEA,UAAI,SAAS,WAAW,aAAa;AACnC,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA,EAAE,YAAY,QAAQ,SAAS,OAAO;AAAA,QACxC;AAAA,MACF;AAEA,YAAM,aAAa,MAAM,IAAI,QAAQ,oBAAoB;AAAA,QACvD,IAAI,SAAS;AAAA,MACf,CAAC;AAED,UAAI,CAAC,YAAY;AACf,cAAM,IAAI;AAAA,UACR,kCAAkC,SAAS,YAAY;AAAA,UACvD;AAAA,UACA,EAAE,cAAc,SAAS,aAAa;AAAA,QACxC;AAAA,MACF;AAEA,YAAM,gBAAgB;AACtB,UAAI,aAAa;AAEjB,aAAO,aAAa,eAAe;AACjC;AAEA,cAAM,kBAAkB,MAAM,gCAAgC,KAAK,YAAY,EAAE,SAAS,aAAa,EAAE,CAAC;AAC1G,YAAI,CAAC,iBAAiB;AACpB,gBAAM,IAAI;AAAA,YACR;AAAA,YACA;AAAA,YACA,EAAE,WAAW;AAAA,UACf;AAAA,QACF;AAGA,YAAI,gBAAgB,WAAW,UAAU;AACvC,gBAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,oBAAoB;AAC7D,gBAAM,eAAe,MAAM,gBAAgB,KAAK,WAAW,iBAAiB,YAAY;AAAA,YACtF,QAAQ,SAAS;AAAA,UACnB,CAAC;AAED,cAAI,aAAa,YAAY,UAAU;AAErC;AAAA,UACF;AAEA,cAAI,aAAa,YAAY,UAAU;AACrC,mBAAO,KAAK,aAAa,SAAS,wBAAwB;AAC1D,kBAAM,iBAAiB,KAAK,WAAW,YAAY,UAAU;AAAA,cAC3D,OAAO,aAAa,SAAS;AAAA,YAC/B,CAAC;AACD,mBAAO;AAAA,cACL,QAAQ;AAAA,cACR,aAAa,gBAAgB;AAAA,cAC7B,SAAS,gBAAgB;AAAA,cACzB;AAAA,cACA;AAAA,cACA,eAAe,KAAK,IAAI,IAAI;AAAA,YAC9B;AAAA,UACF;AAGA,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,aAAa,gBAAgB;AAAA,YAC7B,SAAS,gBAAgB;AAAA,YACzB;AAAA,YACA,eAAe,KAAK,IAAI,IAAI;AAAA,UAC9B;AAAA,QACF;AAEA,cAAM,cAAc,WAAW,WAAW,MAAM;AAAA,UAC9C,CAAC,MAAW,EAAE,WAAW,gBAAgB;AAAA,QAC3C;AAEA,YAAI,aAAa,aAAa,OAAO;AACnC,gBAAM,iBAAiB,KAAK,WAAW,YAAY,WAAW;AAC9D,iBAAO,KAAK;AAAA,YACV,WAAW;AAAA,YACX,YAAY,oBAAI,KAAK;AAAA,UACvB,CAAC;AAED,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,aAAa,gBAAgB;AAAA,YAC7B,SAAS,gBAAgB;AAAA,YACzB;AAAA,YACA,eAAe,KAAK,IAAI,IAAI;AAAA,UAC9B;AAAA,QACF;AAEA,YACE,aAAa,aAAa,eAC1B,aAAa,aAAa,qBAC1B,aAAa,aAAa,kBAC1B;AACA,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,aAAa,gBAAgB;AAAA,YAC7B,SAAS,gBAAgB;AAAA,YACzB;AAAA,YACA,eAAe,KAAK,IAAI,IAAI;AAAA,UAC9B;AAAA,QACF;AAEA,cAAM,cAAc,WAAW,WAAW,YAAY;AAAA,UACpD,CAAC,MACC,EAAE,eAAe,gBAAgB,iBACjC,EAAE,YAAY;AAAA,QAClB;AAEA,YAAI,YAAY,WAAW,GAAG;AAC5B,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,aAAa,gBAAgB;AAAA,YAC7B,SAAS,gBAAgB;AAAA,YACzB;AAAA,YACA,eAAe,KAAK,IAAI,IAAI;AAAA,UAC9B;AAAA,QACF;AAEA,cAAM,oBAAoB,MAAM,OAAO,sBAAsB;AAC7D,cAAM,cAAmB;AAAA,UACvB,iBAAiB,gBAAgB;AAAA,UACjC,QAAQ,SAAS;AAAA,QACnB;AAEA,cAAM,mBAAmB,MAAM,kBAAkB;AAAA,UAC/C;AAAA,UACA;AAAA,UACA,gBAAgB;AAAA,UAChB;AAAA,QACF;AAEA,cAAM,uBAAuB,iBAAiB;AAAA,UAC5C,CAAC,OAAO,GAAG,WAAW,GAAG,YAAY,YAAY;AAAA,QACnD;AAEA,YAAI,qBAAqB,WAAW,GAAG;AACrC,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,aAAa,gBAAgB;AAAA,YAC7B,SAAS,gBAAgB;AAAA,YACzB;AAAA,YACA,eAAe,KAAK,IAAI,IAAI;AAAA,UAC9B;AAAA,QACF;AAEA,cAAM,qBAAqB,qBAAqB,CAAC,EAAE;AAEnD,YAAI;AACF,gBAAM,mBAAmB,MAAM,kBAAkB;AAAA,YAC/C;AAAA,YACA;AAAA,YACA;AAAA,YACA,mBAAmB;AAAA,YACnB,mBAAmB;AAAA,YACnB;AAAA,UACF;AAEA,cAAI,CAAC,iBAAiB,SAAS;AAC7B,kBAAM,mBAAmB,iBAAiB,SAAS;AACnD,oBAAQ,MAAM,6CAA6C,gBAAgB,EAAE,eAAe,gBAAgB,UAAU,WAAW,gBAAgB,aAAa,WAAM,mBAAmB,QAAQ,MAAM,gBAAgB,EAAE;AACvN,mBAAO,KAAK,gBAAgB;AAE5B,mBAAO;AAAA,cACL,QAAQ;AAAA,cACR,aAAa,gBAAgB;AAAA,cAC7B,SAAS,gBAAgB;AAAA,cACzB;AAAA,cACA;AAAA,cACA,eAAe,KAAK,IAAI,IAAI;AAAA,YAC9B;AAAA,UACF;AAEA,iBAAO,KAAK;AAAA,YACV,WAAW;AAAA,YACX,YAAY,oBAAI,KAAK;AAAA,YACrB,MAAM;AAAA,cACJ,YAAY,mBAAmB;AAAA,cAC/B,UAAU,mBAAmB;AAAA,cAC7B,cAAc,mBAAmB;AAAA,YACnC;AAAA,UACF,CAAC;AAED,cAAI,iBAAiB,qBAAqB;AACxC,kBAAM,iBAAiB,KAAK;AAAA,cAC1B,oBAAoB,gBAAgB;AAAA,cACpC,WAAW;AAAA,cACX,WAAW;AAAA,gBACT,mBAAmB,iBAAiB,oBAAoB,OAAO,OAAK,EAAE,KAAK;AAAA,gBAC3E,oBAAoB;AAAA,kBAClB,YAAY,mBAAmB;AAAA,kBAC/B,UAAU,mBAAmB;AAAA,gBAC/B;AAAA,cACF;AAAA,cACA,UAAU,gBAAgB;AAAA,cAC1B,gBAAgB,gBAAgB;AAAA,YAClC,CAAC;AAED,mBAAO,KAAK;AAAA,cACV,WAAW;AAAA,cACX,YAAY,oBAAI,KAAK;AAAA,cACrB,MAAM;AAAA,gBACJ,mBAAmB,iBAAiB,oBAAoB,OAAO,OAAK,EAAE,KAAK;AAAA,cAC7E;AAAA,YACF,CAAC;AAED,mBAAO;AAAA,cACL,QAAQ;AAAA,cACR,aAAa,gBAAgB;AAAA,cAC7B,SAAS,gBAAgB;AAAA,cACzB;AAAA,cACA,eAAe,KAAK,IAAI,IAAI;AAAA,YAC9B;AAAA,UACF;AAEA,gBAAM,IAAI,MAAM;AAAA,QAClB,SAAS,OAAO;AACd,gBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC1E,kBAAQ,MAAM,qDAAqD,gBAAgB,EAAE,eAAe,gBAAgB,UAAU,WAAW,gBAAgB,aAAa,WAAM,mBAAmB,QAAQ,MAAM,KAAK;AAClN,kBAAQ,MAAM,2BAA2B,iBAAiB,QAAQ,MAAM,QAAQ,gBAAgB;AAChG,iBAAO,KAAK,YAAY;AAExB,iBAAO,KAAK;AAAA,YACV,WAAW;AAAA,YACX,YAAY,oBAAI,KAAK;AAAA,YACrB,MAAM;AAAA,cACJ,cAAc,mBAAmB;AAAA,cACjC,OAAO;AAAA,YACT;AAAA,UACF,CAAC;AAED,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,aAAa,gBAAgB;AAAA,YAC7B,SAAS,gBAAgB;AAAA,YACzB;AAAA,YACA;AAAA,YACA,eAAe,KAAK,IAAI,IAAI;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AAEA,aAAO,KAAK,+DAA+D;AAC3E,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,aAAa,SAAS;AAAA,QACtB,SAAS,SAAS;AAAA,QAClB;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,MAC9B;AAAA,IACF,SAAS,OAAO;AACd,YAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC1E,cAAQ,MAAM,0CAA0C,UAAU,MAAM,KAAK;AAC7E,UAAI,iBAAiB,SAAS,MAAM,OAAO;AACzC,gBAAQ,MAAM,2BAA2B,MAAM,KAAK;AAAA,MACtD;AACA,aAAO,KAAK,YAAY;AAExB,UAAI;AACF,cAAM,WAAW,MAAM,gCAAgC,KAAK,YAAY,EAAE,SAAS,KAAK,CAAC;AACzF,YAAI,YAAY,SAAS,WAAW,WAAW;AAC7C,mBAAS,SAAS;AAClB,mBAAS,eAAe;AACxB,mBAAS,eAAe,iBAAiB,yBAAyB,MAAM,UAAU;AAClF,mBAAS,YAAY,oBAAI,KAAK;AAC9B,gBAAM,IAAI,MAAM;AAEhB,gBAAM,iBAAiB,KAAK;AAAA,YAC1B,oBAAoB;AAAA,YACpB,WAAW;AAAA,YACX,WAAW,EAAE,OAAO,aAAa;AAAA,YACjC,UAAU,SAAS;AAAA,YACnB,gBAAgB,SAAS;AAAA,UAC3B,CAAC;AAAA,QACH;AAAA,MACF,SAAS,aAAa;AACpB,gBAAQ,MAAM,wCAAwC,UAAU,sBAAsB,WAAW;AAAA,MACnG;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AAEA,SAAO,OAAO,gBAAgB,kBAAkB,aAC5C,gBAAgB,cAAc,CAAC,QAAQ,aAAa,GAAG,CAAC,IACxD,aAAa,EAAE;AACrB;AAWA,eAAsB,iBACpB,IACA,WACA,YACA,QACA,QACe;AACf,QAAM,WAAW,MAAM,oBAAoB,IAAI,UAAU;AACzD,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR,gCAAgC,UAAU;AAAA,MAC1C;AAAA,MACA,EAAE,WAAW;AAAA,IACf;AAAA,EACF;AAGA,MAAI,WAAW,UAAU;AACvB,UAAM,aAAa,MAAM,GAAG,QAAQ,oBAAoB,EAAE,IAAI,SAAS,aAAa,CAAC;AAErF,QAAI,cAAc,0BAA0B,UAAU,GAAG;AACvD,UAAI;AAGF,YAAI,QAAQ,OAAO;AACjB,mBAAS,eAAe,OAAO;AAC/B,mBAAS,eAAe,OAAO;AAC/B,gBAAM,GAAG,MAAM;AAAA,QACjB;AAEA,cAAM,qBAAqB,MAAM;AAAA,UAC/B;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,YACE,iBAAiB;AAAA;AAAA,UACnB;AAAA,QACF;AAEA,gBAAQ;AAAA,UACN,2BAA2B,mBAAmB,MAAM,KAAK,mBAAmB,qBAAqB,IAAI,mBAAmB,eAAe;AAAA,QACzI;AAIA;AAAA,MACF,SAAS,OAAY;AACnB,gBAAQ,MAAM,kDAAkD,KAAK;AAAA,MAEvE;AAAA,IACF;AAAA,EACF;AAGA,QAAM,MAAM,oBAAI,KAAK;AACrB,WAAS,SAAS;AAClB,WAAS,YAAY;AAErB,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,eAAS,cAAc;AACvB,UAAI,QAAQ;AACV,iBAAS,UAAU,EAAE,GAAG,SAAS,SAAS,UAAU,OAAO;AAAA,MAC7D;AACA;AAAA,IAEF,KAAK;AACH,eAAS,cAAc;AACvB,UAAI,QAAQ,OAAO;AACjB,iBAAS,eAAe,OAAO;AAC/B,iBAAS,eAAe,OAAO;AAAA,MACjC;AACA;AAAA,IAEF,KAAK;AACH,eAAS,cAAc;AACvB;AAAA,EACJ;AAEA,QAAM,GAAG,MAAM;AAGf,QAAM,YACJ,WAAW,cACP,uBACA,WAAW,WACT,oBACA;AAER,QAAM,iBAAiB,IAAI;AAAA,IACzB,oBAAoB;AAAA,IACpB;AAAA,IACA,WAAW,UAAU,CAAC;AAAA,IACtB,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,EAC3B,CAAC;AACH;AAYA,eAAsB,8BACpB,IACA,WACA,YACA,kBACe;AACf,QAAM,kBAAkB;AAOxB,MAAI,kBAAkB;AACpB,UAAM,EAAE,4BAA4B,IAAI,MAAM,OAAO,oBAAoB;AACzE,UAAM,eAAe,OAAO,gBAAgB,kBAAkB,aAC1D,MAAM,gBAAgB,cAAc,CAAC,QAAQ,4BAA4B,KAAK,WAAW,YAAY,gBAAgB,CAAC,IACtH,MAAM,4BAA4B,IAAI,WAAW,YAAY,gBAAgB;AACjF,QAAI,aAAa,mBAAmB;AAClC,YAAM,gBAAgB,IAAI,WAAW,UAAU;AAAA,IACjD;AACA;AAAA,EACF;AAEA,QAAM,YAAY,OAAO,QAAgE;AACvF,UAAM,WAAW,MAAM,IAAI,QAAQ,kBAAkB;AAAA,MACnD,IAAI;AAAA,MACJ,QAAQ;AAAA,IACV,GAAG,EAAE,UAAU,SAAS,kBAAkB,CAAC;AAE3C,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAEA,UAAM,gBAAiB,SAAS,QAAQ,2BAAqC,CAAC;AAE9E,UAAM,sBAAsB,MAAM,IAAI,MAAM,eAAe;AAAA,MACzD,oBAAoB;AAAA,MACpB,WAAW;AAAA,MACX,WAAW,EAAE,OAAO,KAAK;AAAA,IAC3B,CAAC;AAED,UAAM,mBAAmB,MAAM,IAAI,MAAM,eAAe;AAAA,MACtD,oBAAoB;AAAA,MACpB,WAAW;AAAA,MACX,WAAW,EAAE,OAAO,KAAK;AAAA,IAC3B,CAAC;AAED,UAAM,iBAAiB,sBAAsB;AAE7C,QAAI,iBAAiB,cAAc,QAAQ;AACzC,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,QAAI,mBAAmB,GAAG;AACxB,YAAM,eAAe,MAAM,IAAI,KAAK,eAAe;AAAA,QACjD,oBAAoB;AAAA,QACpB,WAAW;AAAA,QACX,WAAW,EAAE,OAAO,KAAK;AAAA,MAC3B,CAAC;AAED,eAAS,SAAS;AAClB,eAAS,eAAe,GAAG,gBAAgB;AAC3C,eAAS,eAAe;AAAA,QACtB,kBAAkB,aAAa,IAAI,QAAM;AAAA,UACvC,YAAY,EAAE,UAAU;AAAA,UACxB,OAAO,EAAE,UAAU;AAAA,UACnB,OAAO,EAAE,UAAU;AAAA,QACrB,EAAE;AAAA,MACJ;AACA,YAAM,IAAI,MAAM;AAEhB,YAAM,iBAAiB,KAAK;AAAA,QAC1B,oBAAoB;AAAA,QACpB,WAAW;AAAA,QACX,WAAW;AAAA,UACT,QAAQ;AAAA,UACR,kBAAkB,SAAS,aAAa;AAAA,QAC1C;AAAA,QACA,UAAU,SAAS;AAAA,QACnB,gBAAgB,SAAS;AAAA,MAC3B,CAAC;AAED,aAAO,EAAE,mBAAmB,MAAM;AAAA,IACpC;AAEA,UAAM,kBAAkB,MAAM,IAAI,KAAK,eAAe;AAAA,MACpD,oBAAoB;AAAA,MACpB,WAAW;AAAA,MACX,WAAW,EAAE,OAAO,KAAK;AAAA,IAC3B,CAAC;AAED,eAAW,SAAS,iBAAiB;AACnC,UAAI,MAAM,UAAU,QAAQ;AAC1B,iBAAS,UAAU;AAAA,UACjB,GAAG,SAAS;AAAA,UACZ,CAAC,GAAG,MAAM,UAAU,UAAU,SAAS,GAAG,MAAM,UAAU;AAAA,QAC5D;AAAA,MACF;AAAA,IACF;AAEA,WAAO,SAAS,QAAQ;AAExB,UAAM,oBAAoB,SAAS;AAEnC,QAAI,CAAC,mBAAmB;AACtB,cAAQ,KAAK,sDAAsD;AACnE,eAAS,SAAS;AAClB,YAAM,IAAI,MAAM;AAEhB,YAAM,iBAAiB,KAAK;AAAA,QAC1B,oBAAoB;AAAA,QACpB,WAAW;AAAA,QACX,WAAW;AAAA,UACT,QAAQ;AAAA,UACR;AAAA,QACF;AAAA,QACA,UAAU,SAAS;AAAA,QACnB,gBAAgB,SAAS;AAAA,MAC3B,CAAC;AAED,aAAO,EAAE,mBAAmB,KAAK;AAAA,IACnC;AAEA,YAAQ,IAAI,6CAA6C;AAAA,MACvD,UAAU,kBAAkB;AAAA,MAC5B,MAAM,SAAS;AAAA,IACjB,CAAC;AAED,UAAM,aAAa,MAAM,IAAI,cAAc,oBAAoB;AAAA,MAC7D,IAAI,SAAS;AAAA,IACf,CAAC;AAED,UAAM,OAAO,WAAW,WAAW,MAAM,KAAK,OAAK,EAAE,WAAW,kBAAkB,QAAQ;AAE1F,aAAS,gBAAgB,kBAAkB;AAC3C,aAAS,SAAS;AAClB,aAAS,oBAAoB;AAC7B,aAAS,YAAY,oBAAI,KAAK;AAC9B,UAAM,IAAI,MAAM;AAEhB,UAAM,iBAAiB,KAAK;AAAA,MAC1B,oBAAoB,SAAS;AAAA,MAC7B,WAAW;AAAA,MACX,WAAW;AAAA,QACT,QAAQ,kBAAkB;AAAA,QAC1B,UAAU,MAAM;AAAA,QAChB,UAAU,MAAM;AAAA,MAClB;AAAA,MACA,UAAU,SAAS;AAAA,MACnB,gBAAgB,SAAS;AAAA,IAC3B,CAAC;AAED,UAAM,iBAAiB,KAAK;AAAA,MAC1B,oBAAoB;AAAA,MACpB,WAAW;AAAA,MACX,WAAW;AAAA,QACT,QAAQ;AAAA,QACR;AAAA,QACA,uBAAuB,kBAAkB;AAAA,MAC3C;AAAA,MACA,UAAU,SAAS;AAAA,MACnB,gBAAgB,SAAS;AAAA,IAC3B,CAAC;AAED,UAAM,EAAE,YAAY,IAAI,MAAM,OAAO,gBAAgB;AACrD,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,kBAAkB;AAAA,MAClB;AAAA,QACE,iBAAiB,SAAS,WAAW,CAAC;AAAA,QACtC,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,IACF;AAEA,WAAO,EAAE,mBAAmB,KAAK;AAAA,EACnC;AAEA,QAAM,eAAe,OAAO,gBAAgB,kBAAkB,aAC1D,MAAM,gBAAgB,cAAc,CAAC,QAAQ,UAAU,GAAG,CAAC,IAC3D,MAAM,UAAU,EAAE;AAEtB,MAAI,aAAa,mBAAmB;AAClC,UAAM,gBAAgB,IAAI,WAAW,UAAU;AAAA,EACjD;AACF;AAKA,SAAS,0BAA0B,YAAyC;AAE1E,aAAW,cAAc,WAAW,WAAW,aAAa;AAC1D,QAAI,WAAW,YAAY;AACzB,iBAAW,YAAY,WAAW,YAAY;AAC5C,YAAI,SAAS,cAAc,YAAY;AACrC,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,WAAW,WAAW,YAAY;AACpC,eAAW,YAAY,WAAW,WAAW,YAAY;AACvD,UAAI,SAAS,cAAc,YAAY;AACrC,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAaA,eAAsB,oBACpB,IACA,YACkC;AAClC,SAAO,GAAG,QAAQ,kBAAkB,EAAE,IAAI,WAAW,CAAC;AACxD;AAEA,eAAe,gCACb,IACA,YACA,SACkC;AAClC,SAAO,GAAG;AAAA,IACR;AAAA,IACA,EAAE,IAAI,WAAW;AAAA,IACjB;AAAA,MACE,UAAU,SAAS;AAAA,MACnB,GAAI,SAAS,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,IAC9C;AAAA,EACF;AACF;AASA,eAAsB,sBACpB,IACA,YACA,SACe;AACf,QAAM,WAAW,MAAM,oBAAoB,IAAI,UAAU;AACzD,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR,gCAAgC,UAAU;AAAA,MAC1C;AAAA,MACA,EAAE,WAAW;AAAA,IACf;AAAA,EACF;AAEA,WAAS,UAAU;AAAA,IACjB,GAAG,SAAS;AAAA,IACZ,GAAG;AAAA,EACL;AACA,WAAS,YAAY,oBAAI,KAAK;AAE9B,QAAM,GAAG,MAAM;AACjB;AAUA,eAAe,iBACb,IACA,OASwB;AACxB,QAAM,gBAAgB,GAAG,OAAO,eAAe;AAAA,IAC7C,GAAG;AAAA,IACH,YAAY,oBAAI,KAAK;AAAA,EACvB,CAAC;AAED,QAAM,GAAG,QAAQ,aAAa,EAAE,MAAM;AACtC,SAAO;AACT;",
|
|
4
|
+
"sourcesContent": ["/**\n * Workflows Module - Workflow Executor Service\n *\n * Main orchestrator for workflow execution. Handles workflow lifecycle:\n * - Starting workflow instances from definitions\n * - Executing workflow steps and transitions\n * - Completing workflows with final status\n * - Triggering compensation on failure\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager, LockMode } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\nimport {\n WorkflowDefinition,\n WorkflowInstance,\n WorkflowEvent,\n type WorkflowInstanceStatus,\n} from '../data/entities'\nimport { compensateWorkflow } from './compensation-handler'\nimport { findWorkflowDefinition } from './find-definition'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface StartWorkflowOptions {\n workflowId: string\n version?: number // Default to latest enabled version\n initialContext?: Record<string, any>\n correlationKey?: string\n metadata?: {\n entityType?: string\n entityId?: string\n initiatedBy?: string\n labels?: Record<string, string>\n }\n tenantId: string\n organizationId: string\n}\n\nexport interface ExecutionContext {\n userId?: string\n dryRun?: boolean\n timeout?: number\n}\n\nexport interface ExecutionResult {\n status: WorkflowInstanceStatus\n currentStep: string\n context: Record<string, any>\n events: WorkflowEventSummary[]\n errors?: string[]\n executionTime: number\n}\n\nexport interface WorkflowEventSummary {\n eventType: string\n occurredAt: Date\n data?: any\n}\n\nexport class WorkflowExecutionError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'WorkflowExecutionError'\n }\n}\n\n// ============================================================================\n// Main Orchestration Functions\n// ============================================================================\n\n/**\n * Start a new workflow instance from a definition\n *\n * @param em - Entity manager for database operations\n * @param options - Workflow start options\n * @returns Created workflow instance\n * @throws WorkflowExecutionError if definition not found or validation fails\n */\nexport async function startWorkflow(\n em: EntityManager,\n options: StartWorkflowOptions\n): Promise<WorkflowInstance> {\n const {\n workflowId,\n version,\n initialContext = {},\n correlationKey,\n metadata,\n tenantId,\n organizationId,\n } = options\n\n // Find workflow definition\n const definition = await findWorkflowDefinition(em, {\n workflowId,\n version,\n tenantId,\n organizationId,\n })\n\n if (!definition) {\n throw new WorkflowExecutionError(\n `Workflow definition not found: ${workflowId}${version ? ` v${version}` : ''}`,\n 'DEFINITION_NOT_FOUND',\n { workflowId, version }\n )\n }\n\n if (!definition.enabled) {\n throw new WorkflowExecutionError(\n `Workflow definition is disabled: ${workflowId}`,\n 'DEFINITION_DISABLED',\n { workflowId, version: definition.version }\n )\n }\n\n // Validate definition has required steps\n const { steps, transitions } = definition.definition\n if (!steps || steps.length < 2) {\n throw new WorkflowExecutionError(\n 'Workflow definition must have at least START and END steps',\n 'INVALID_DEFINITION',\n { workflowId, stepsCount: steps?.length || 0 }\n )\n }\n\n if (!transitions || transitions.length < 1) {\n throw new WorkflowExecutionError(\n 'Workflow definition must have at least one transition',\n 'INVALID_DEFINITION',\n { workflowId, transitionsCount: transitions?.length || 0 }\n )\n }\n\n // Find START step\n const startStep = steps.find((s: any) => s.stepType === 'START')\n if (!startStep) {\n throw new WorkflowExecutionError(\n 'Workflow definition must have a START step',\n 'INVALID_DEFINITION',\n { workflowId }\n )\n }\n\n // Validate START step pre-conditions if defined\n if (startStep.preConditions && startStep.preConditions.length > 0) {\n const { validateWorkflowStart } = await import('./start-validator')\n\n const validationResult = await validateWorkflowStart(em, {\n workflowId,\n version: definition.version,\n context: initialContext,\n tenantId,\n organizationId,\n })\n\n if (!validationResult.canStart) {\n throw new WorkflowExecutionError(\n `Workflow start pre-conditions failed: ${validationResult.errors.map(e => e.message).join('; ')}`,\n 'START_PRE_CONDITIONS_FAILED',\n {\n workflowId,\n errors: validationResult.errors,\n validatedRules: validationResult.validatedRules,\n }\n )\n }\n }\n\n // Create workflow instance\n const now = new Date()\n const instance = em.create(WorkflowInstance, {\n definitionId: definition.id,\n workflowId: definition.workflowId,\n version: definition.version,\n status: 'RUNNING',\n currentStepId: startStep.stepId,\n context: initialContext,\n correlationKey,\n metadata,\n startedAt: now,\n retryCount: 0,\n tenantId,\n organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(instance).flush()\n\n // Log WORKFLOW_STARTED event\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'WORKFLOW_STARTED',\n eventData: {\n workflowId: instance.workflowId,\n version: instance.version,\n startStepId: startStep.stepId,\n initialContext,\n metadata,\n },\n userId: metadata?.initiatedBy,\n tenantId,\n organizationId,\n })\n\n return instance\n}\n\n/**\n * Execute a workflow instance\n *\n * Main execution loop that processes steps and transitions until:\n * - Workflow completes (reaches END step)\n * - Workflow waits (USER_TASK, SIGNAL, TIMER)\n * - Workflow fails (error occurs)\n * - Timeout is reached\n *\n * @param em - Entity manager\n * @param container - DI container (for activity execution and other services)\n * @param instanceId - Workflow instance ID\n * @param context - Execution context (userId, dryRun, timeout)\n * @returns Execution result with status and events\n */\nexport async function executeWorkflow(\n em: EntityManager,\n container: AwilixContainer,\n instanceId: string,\n context?: ExecutionContext\n): Promise<ExecutionResult> {\n const startTime = Date.now()\n const transactionalEm = em as EntityManager & {\n transactional?: <TResult>(\n callback: (trx: EntityManager) => Promise<TResult>,\n ) => Promise<TResult>\n }\n\n const runExecution = async (trx: EntityManager): Promise<ExecutionResult> => {\n const events: WorkflowEventSummary[] = []\n const errors: string[] = []\n\n try {\n const instance = await getWorkflowInstanceForExecution(trx, instanceId)\n if (!instance) {\n throw new WorkflowExecutionError(\n `Workflow instance not found: ${instanceId}`,\n 'INSTANCE_NOT_FOUND',\n { instanceId }\n )\n }\n\n if (instance.status === 'COMPLETED') {\n return {\n status: 'COMPLETED',\n currentStep: instance.currentStepId,\n context: instance.context,\n events: [],\n executionTime: 0,\n }\n }\n\n if (instance.status === 'CANCELLED') {\n throw new WorkflowExecutionError(\n 'Cannot execute cancelled workflow',\n 'WORKFLOW_CANCELLED',\n { instanceId, status: instance.status }\n )\n }\n\n const definition = await trx.findOne(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n if (!definition) {\n throw new WorkflowExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n const maxIterations = 100\n let iterations = 0\n\n while (iterations < maxIterations) {\n iterations++\n\n const currentInstance = await getWorkflowInstanceForExecution(trx, instanceId, { refresh: iterations > 1 })\n if (!currentInstance) {\n throw new WorkflowExecutionError(\n 'Instance not found during execution',\n 'INSTANCE_NOT_FOUND',\n { instanceId }\n )\n }\n\n // Parallel execution: while the instance is FORKED, drive the branches.\n if (currentInstance.status === 'FORKED') {\n const { advanceBranches } = await import('./parallel-handler')\n const branchResult = await advanceBranches(trx, container, currentInstance, definition, {\n userId: context?.userId,\n })\n\n if (branchResult.outcome === 'joined') {\n // Instance resumed at the post-join step; continue single-token.\n continue\n }\n\n if (branchResult.outcome === 'failed') {\n errors.push(branchResult.error || 'Parallel branch failed')\n await completeWorkflow(trx, container, instanceId, 'FAILED', {\n error: branchResult.error || 'Parallel branch failed',\n })\n return {\n status: 'FAILED',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n errors,\n executionTime: Date.now() - startTime,\n }\n }\n\n // 'waiting' \u2014 all branches paused for external resume (task/signal/timer/async).\n return {\n status: 'RUNNING',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n const currentStep = definition.definition.steps.find(\n (s: any) => s.stepId === currentInstance.currentStepId\n )\n\n if (currentStep?.stepType === 'END') {\n await completeWorkflow(trx, container, instanceId, 'COMPLETED')\n events.push({\n eventType: 'WORKFLOW_COMPLETED',\n occurredAt: new Date(),\n })\n\n return {\n status: 'COMPLETED',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n if (\n currentStep?.stepType === 'USER_TASK' ||\n currentStep?.stepType === 'WAIT_FOR_SIGNAL' ||\n currentStep?.stepType === 'WAIT_FOR_TIMER'\n ) {\n return {\n status: 'RUNNING',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n const transitions = definition.definition.transitions.filter(\n (t: any) =>\n t.fromStepId === currentInstance.currentStepId &&\n t.trigger === 'auto'\n )\n\n if (transitions.length === 0) {\n return {\n status: 'RUNNING',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n const transitionHandler = await import('./transition-handler')\n const evalContext: any = {\n workflowContext: currentInstance.context,\n userId: context?.userId,\n }\n\n const validTransitions = await transitionHandler.findValidTransitions(\n trx,\n currentInstance,\n currentInstance.currentStepId!,\n evalContext\n )\n\n const validAutoTransitions = validTransitions.filter(\n (vt) => vt.isValid && vt.transition?.trigger === 'auto'\n )\n\n if (validAutoTransitions.length === 0) {\n return {\n status: 'RUNNING',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n const selectedTransition = validAutoTransitions[0].transition\n\n try {\n const transitionResult = await transitionHandler.executeTransition(\n trx,\n container,\n currentInstance,\n selectedTransition.fromStepId,\n selectedTransition.toStepId,\n evalContext\n )\n\n if (!transitionResult.success) {\n const rejectionMessage = transitionResult.error || 'Transition failed'\n console.error(`[WORKFLOW] Transition rejected (instance: ${currentInstance.id}, workflow: ${currentInstance.workflowId}, step: ${currentInstance.currentStepId} \u2192 ${selectedTransition.toStepId}): ${rejectionMessage}`)\n errors.push(rejectionMessage)\n\n await completeWorkflow(trx, container, instanceId, 'FAILED', {\n error: rejectionMessage,\n })\n events.push({\n eventType: 'WORKFLOW_FAILED',\n occurredAt: new Date(),\n })\n\n return {\n status: 'FAILED',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n errors,\n executionTime: Date.now() - startTime,\n }\n }\n\n events.push({\n eventType: 'TRANSITION_EXECUTED',\n occurredAt: new Date(),\n data: {\n fromStepId: selectedTransition.fromStepId,\n toStepId: selectedTransition.toStepId,\n transitionId: selectedTransition.transitionId,\n },\n })\n\n if (transitionResult.pausedForActivities) {\n await logWorkflowEvent(trx, {\n workflowInstanceId: currentInstance.id,\n eventType: 'WORKFLOW_WAITING_FOR_ACTIVITIES',\n eventData: {\n pendingActivities: transitionResult.activitiesExecuted?.filter(a => a.async),\n pausedAtTransition: {\n fromStepId: selectedTransition.fromStepId,\n toStepId: selectedTransition.toStepId,\n },\n },\n tenantId: currentInstance.tenantId,\n organizationId: currentInstance.organizationId,\n })\n\n events.push({\n eventType: 'WORKFLOW_WAITING_FOR_ACTIVITIES',\n occurredAt: new Date(),\n data: {\n pendingActivities: transitionResult.activitiesExecuted?.filter(a => a.async),\n },\n })\n\n return {\n status: 'WAITING_FOR_ACTIVITIES',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n executionTime: Date.now() - startTime,\n }\n }\n\n await trx.flush()\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(`[WORKFLOW] Transition execution failed (instance: ${currentInstance.id}, workflow: ${currentInstance.workflowId}, step: ${currentInstance.currentStepId} \u2192 ${selectedTransition.toStepId}):`, error)\n console.error('[WORKFLOW] Error stack:', error instanceof Error ? error.stack : 'No stack trace')\n errors.push(errorMessage)\n\n events.push({\n eventType: 'TRANSITION_FAILED',\n occurredAt: new Date(),\n data: {\n transitionId: selectedTransition.transitionId,\n error: errorMessage,\n },\n })\n\n await completeWorkflow(trx, container, instanceId, 'FAILED', {\n error: errorMessage,\n details: error instanceof WorkflowExecutionError ? error.details : undefined,\n })\n events.push({\n eventType: 'WORKFLOW_FAILED',\n occurredAt: new Date(),\n })\n\n return {\n status: 'FAILED',\n currentStep: currentInstance.currentStepId,\n context: currentInstance.context,\n events,\n errors,\n executionTime: Date.now() - startTime,\n }\n }\n }\n\n errors.push('Maximum execution iterations reached - possible infinite loop')\n return {\n status: 'RUNNING',\n currentStep: instance.currentStepId,\n context: instance.context,\n events,\n errors,\n executionTime: Date.now() - startTime,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(`[WORKFLOW] Execution failed (instance: ${instanceId}):`, error)\n if (error instanceof Error && error.stack) {\n console.error('[WORKFLOW] Error stack:', error.stack)\n }\n errors.push(errorMessage)\n\n try {\n const instance = await getWorkflowInstanceForExecution(trx, instanceId, { refresh: true })\n if (instance && instance.status === 'RUNNING') {\n instance.status = 'FAILED'\n instance.errorMessage = errorMessage\n instance.errorDetails = error instanceof WorkflowExecutionError ? error.details : undefined\n instance.updatedAt = new Date()\n await trx.flush()\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instanceId,\n eventType: 'WORKFLOW_FAILED',\n eventData: { error: errorMessage },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n }\n } catch (updateError) {\n console.error(`[WORKFLOW] Failed to update instance ${instanceId} with error state:`, updateError)\n }\n\n throw error\n }\n }\n\n return typeof transactionalEm.transactional === 'function'\n ? transactionalEm.transactional((trx) => runExecution(trx))\n : runExecution(em)\n}\n\n/**\n * Complete a workflow instance with final status\n *\n * @param em - Entity manager\n * @param container\n * @param instanceId - Workflow instance ID\n * @param status - Final status (COMPLETED, FAILED, CANCELLED)\n * @param result - Optional result data\n */\nexport async function completeWorkflow(\n em: EntityManager,\n container: AwilixContainer,\n instanceId: string,\n status: 'COMPLETED' | 'FAILED' | 'CANCELLED',\n result?: any\n): Promise<void> {\n const instance = await getWorkflowInstance(em, instanceId)\n if (!instance) {\n throw new WorkflowExecutionError(\n `Workflow instance not found: ${instanceId}`,\n 'INSTANCE_NOT_FOUND',\n { instanceId }\n )\n }\n\n // Trigger compensation if workflow failed and has compensatable activities (Phase 8.2)\n if (status === 'FAILED') {\n const definition = await em.findOne(WorkflowDefinition, { id: instance.definitionId })\n\n if (definition && checkIfCompensationNeeded(definition)) {\n try {\n\n // Set error message before compensation\n if (result?.error) {\n instance.errorMessage = result.error\n instance.errorDetails = result.details\n await em.flush()\n }\n\n const compensationResult = await compensateWorkflow(\n em,\n container,\n instance,\n definition,\n {\n continueOnError: true // Best-effort compensation\n }\n )\n\n console.log(\n `[Workflow] Compensation ${compensationResult.status}: ${compensationResult.compensatedActivities}/${compensationResult.totalActivities} activities`\n )\n\n // Note: instance status already updated by compensateWorkflow\n // It will be COMPENSATED or remain FAILED\n return\n } catch (error: any) {\n console.error(`[Workflow] Compensation failed with exception:`, error)\n // Continue to mark workflow as failed\n }\n }\n }\n\n // Original completion logic (no compensation needed or status is COMPLETED/CANCELLED)\n const now = new Date()\n instance.status = status\n instance.updatedAt = now\n\n switch (status) {\n case 'COMPLETED':\n instance.completedAt = now\n if (result) {\n instance.context = { ...instance.context, __result: result }\n }\n break\n\n case 'FAILED':\n instance.completedAt = now\n if (result?.error) {\n instance.errorMessage = result.error\n instance.errorDetails = result.details\n }\n break\n\n case 'CANCELLED':\n instance.cancelledAt = now\n break\n }\n\n await em.flush()\n\n // Log completion event\n const eventType =\n status === 'COMPLETED'\n ? 'WORKFLOW_COMPLETED'\n : status === 'FAILED'\n ? 'WORKFLOW_FAILED'\n : 'WORKFLOW_CANCELLED'\n\n await logWorkflowEvent(em, {\n workflowInstanceId: instanceId,\n eventType,\n eventData: result || {},\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n}\n\n/**\n * Resume workflow after async activities complete\n *\n * Called by the activity worker after all async activities finish execution.\n * Checks if all activities are done, merges outputs into context, and resumes execution.\n *\n * @param em - Entity manager\n * @param container - DI container\n * @param instanceId - Workflow instance ID\n */\nexport async function resumeWorkflowAfterActivities(\n em: EntityManager,\n container: AwilixContainer,\n instanceId: string,\n branchInstanceId?: string | null\n): Promise<void> {\n const transactionalEm = em as EntityManager & {\n transactional?: <TResult>(callback: (trx: EntityManager) => Promise<TResult>) => Promise<TResult>\n }\n\n // Branch-scoped async resume: the instance is FORKED and the branch (not the\n // instance) is WAITING_FOR_ACTIVITIES. Resume just that branch, then let the\n // interleaved loop continue. Missing branchInstanceId \u2192 legacy instance path.\n if (branchInstanceId) {\n const { resumeBranchAfterActivities } = await import('./parallel-handler')\n const branchResume = typeof transactionalEm.transactional === 'function'\n ? await transactionalEm.transactional((trx) => resumeBranchAfterActivities(trx, container, instanceId, branchInstanceId))\n : await resumeBranchAfterActivities(em, container, instanceId, branchInstanceId)\n if (branchResume.continueExecution) {\n await executeWorkflow(em, container, instanceId)\n }\n return\n }\n\n const runResume = async (trx: EntityManager): Promise<{ continueExecution: boolean }> => {\n const instance = await trx.findOne(WorkflowInstance, {\n id: instanceId,\n status: 'WAITING_FOR_ACTIVITIES',\n }, { lockMode: LockMode.PESSIMISTIC_WRITE })\n\n if (!instance) {\n throw new Error('Workflow instance not waiting for activities')\n }\n\n const pendingJobIds = (instance.context._pendingAsyncActivities as any[]) || []\n\n const completedActivities = await trx.count(WorkflowEvent, {\n workflowInstanceId: instanceId,\n eventType: 'ACTIVITY_COMPLETED',\n eventData: { async: true },\n })\n\n const failedActivities = await trx.count(WorkflowEvent, {\n workflowInstanceId: instanceId,\n eventType: 'ACTIVITY_FAILED',\n eventData: { async: true },\n })\n\n const totalProcessed = completedActivities + failedActivities\n\n if (totalProcessed < pendingJobIds.length) {\n throw new Error('Activities still pending')\n }\n\n if (failedActivities > 0) {\n const failedEvents = await trx.find(WorkflowEvent, {\n workflowInstanceId: instanceId,\n eventType: 'ACTIVITY_FAILED',\n eventData: { async: true },\n })\n\n instance.status = 'FAILED'\n instance.errorMessage = `${failedActivities} async activities failed`\n instance.errorDetails = {\n failedActivities: failedEvents.map(e => ({\n activityId: e.eventData.activityId,\n error: e.eventData.error,\n jobId: e.eventData.jobId,\n })),\n }\n await trx.flush()\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instanceId,\n eventType: 'WORKFLOW_FAILED',\n eventData: {\n reason: 'Async activities failed',\n failedActivities: instance.errorDetails.failedActivities,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return { continueExecution: false }\n }\n\n const completedEvents = await trx.find(WorkflowEvent, {\n workflowInstanceId: instanceId,\n eventType: 'ACTIVITY_COMPLETED',\n eventData: { async: true },\n })\n\n for (const event of completedEvents) {\n if (event.eventData.output) {\n instance.context = {\n ...instance.context,\n [`${event.eventData.activityId}_result`]: event.eventData.output,\n }\n }\n }\n\n delete instance.context._pendingAsyncActivities\n\n const pendingTransition = instance.pendingTransition\n\n if (!pendingTransition) {\n console.warn('[WORKFLOW] No pending transition found during resume')\n instance.status = 'RUNNING'\n await trx.flush()\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instanceId,\n eventType: 'WORKFLOW_RESUMED',\n eventData: {\n reason: 'All async activities completed',\n completedActivities: completedActivities,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return { continueExecution: true }\n }\n\n console.log('[WORKFLOW] Completing pending transition:', {\n toStepId: pendingTransition.toStepId,\n from: instance.currentStepId,\n })\n\n const definition = await trx.findOneOrFail(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n const step = definition.definition.steps.find(s => s.stepId === pendingTransition.toStepId)\n\n instance.currentStepId = pendingTransition.toStepId\n instance.status = 'RUNNING'\n instance.pendingTransition = null\n instance.updatedAt = new Date()\n await trx.flush()\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instance.id,\n eventType: 'STEP_ENTERED',\n eventData: {\n stepId: pendingTransition.toStepId,\n stepName: step?.stepName,\n stepType: step?.stepType,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n await logWorkflowEvent(trx, {\n workflowInstanceId: instanceId,\n eventType: 'WORKFLOW_RESUMED',\n eventData: {\n reason: 'Async activities completed, resuming pending transition',\n completedActivities: completedActivities,\n completedTransitionTo: pendingTransition.toStepId,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n const { executeStep } = await import('./step-handler')\n await executeStep(\n trx,\n instance,\n pendingTransition.toStepId,\n {\n workflowContext: instance.context || {},\n userId: undefined,\n },\n container\n )\n\n return { continueExecution: true }\n }\n\n const resumeResult = typeof transactionalEm.transactional === 'function'\n ? await transactionalEm.transactional((trx) => runResume(trx))\n : await runResume(em)\n\n if (resumeResult.continueExecution) {\n await executeWorkflow(em, container, instanceId)\n }\n}\n\n/**\n * Check if workflow definition has any compensatable activities\n */\nfunction checkIfCompensationNeeded(definition: WorkflowDefinition): boolean {\n // Check if any activities have compensation defined\n for (const transition of definition.definition.transitions) {\n if (transition.activities) {\n for (const activity of transition.activities) {\n if (activity.compensation?.activityId) {\n return true\n }\n }\n }\n }\n\n // Check root-level activities (legacy)\n if (definition.definition.activities) {\n for (const activity of definition.definition.activities) {\n if (activity.compensation?.activityId) {\n return true\n }\n }\n }\n\n return false\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Get workflow instance by ID\n *\n * @param em - Entity manager\n * @param instanceId - Workflow instance ID\n * @returns Workflow instance or null if not found\n */\nexport async function getWorkflowInstance(\n em: EntityManager,\n instanceId: string\n): Promise<WorkflowInstance | null> {\n return em.findOne(WorkflowInstance, { id: instanceId })\n}\n\nasync function getWorkflowInstanceForExecution(\n em: EntityManager,\n instanceId: string,\n options?: { refresh?: boolean }\n): Promise<WorkflowInstance | null> {\n return em.findOne(\n WorkflowInstance,\n { id: instanceId },\n {\n lockMode: LockMode.PESSIMISTIC_WRITE,\n ...(options?.refresh ? { refresh: true } : {}),\n }\n )\n}\n\n/**\n * Update workflow context with new data\n *\n * @param em - Entity manager\n * @param instanceId - Workflow instance ID\n * @param updates - Context updates (merged with existing context)\n */\nexport async function updateWorkflowContext(\n em: EntityManager,\n instanceId: string,\n updates: Record<string, any>\n): Promise<void> {\n const instance = await getWorkflowInstance(em, instanceId)\n if (!instance) {\n throw new WorkflowExecutionError(\n `Workflow instance not found: ${instanceId}`,\n 'INSTANCE_NOT_FOUND',\n { instanceId }\n )\n }\n\n instance.context = {\n ...instance.context,\n ...updates,\n }\n instance.updatedAt = new Date()\n\n await em.flush()\n}\n\n// findWorkflowDefinition is imported from ./find-definition\n\n/**\n * Log workflow event to event sourcing table\n *\n * @param em - Entity manager\n * @param event - Event data\n */\nasync function logWorkflowEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n stepInstanceId?: string\n eventType: string\n eventData: any\n userId?: string\n tenantId: string\n organizationId: string\n }\n): Promise<WorkflowEvent> {\n const workflowEvent = em.create(WorkflowEvent, {\n ...event,\n occurredAt: new Date(),\n })\n\n await em.persist(workflowEvent).flush()\n return workflowEvent\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.5-develop.
|
|
3
|
+
"version": "0.6.5-develop.4691.1.bb409545b3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -245,16 +245,16 @@
|
|
|
245
245
|
"zod": "^4.4.3"
|
|
246
246
|
},
|
|
247
247
|
"peerDependencies": {
|
|
248
|
-
"@open-mercato/ai-assistant": "0.6.5-develop.
|
|
249
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
250
|
-
"@open-mercato/ui": "0.6.5-develop.
|
|
248
|
+
"@open-mercato/ai-assistant": "0.6.5-develop.4691.1.bb409545b3",
|
|
249
|
+
"@open-mercato/shared": "0.6.5-develop.4691.1.bb409545b3",
|
|
250
|
+
"@open-mercato/ui": "0.6.5-develop.4691.1.bb409545b3",
|
|
251
251
|
"react": "^19.0.0",
|
|
252
252
|
"react-dom": "^19.0.0"
|
|
253
253
|
},
|
|
254
254
|
"devDependencies": {
|
|
255
|
-
"@open-mercato/ai-assistant": "0.6.5-develop.
|
|
256
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
257
|
-
"@open-mercato/ui": "0.6.5-develop.
|
|
255
|
+
"@open-mercato/ai-assistant": "0.6.5-develop.4691.1.bb409545b3",
|
|
256
|
+
"@open-mercato/shared": "0.6.5-develop.4691.1.bb409545b3",
|
|
257
|
+
"@open-mercato/ui": "0.6.5-develop.4691.1.bb409545b3",
|
|
258
258
|
"@testing-library/dom": "^10.4.1",
|
|
259
259
|
"@testing-library/jest-dom": "^6.9.1",
|
|
260
260
|
"@testing-library/react": "^16.3.1",
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const truthyValues = new Set(['1', 'true', 'yes', 'on']);
|
|
5
|
+
const falsyValues = new Set(['0', 'false', 'no', 'off']);
|
|
6
|
+
|
|
7
|
+
let cachedStandaloneEnv: Record<string, string> | null = null;
|
|
8
|
+
|
|
9
|
+
function parseDotEnv(contents: string): Record<string, string> {
|
|
10
|
+
const values: Record<string, string> = {};
|
|
11
|
+
for (const line of contents.split(/\r?\n/)) {
|
|
12
|
+
const trimmed = line.trim();
|
|
13
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
14
|
+
const index = trimmed.indexOf('=');
|
|
15
|
+
if (index <= 0) continue;
|
|
16
|
+
const key = trimmed.slice(0, index).trim();
|
|
17
|
+
let value = trimmed.slice(index + 1).trim();
|
|
18
|
+
if (
|
|
19
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
20
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
21
|
+
) {
|
|
22
|
+
value = value.slice(1, -1);
|
|
23
|
+
}
|
|
24
|
+
values[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return values;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function readStandaloneEnv(): Record<string, string> {
|
|
30
|
+
if (cachedStandaloneEnv) return cachedStandaloneEnv;
|
|
31
|
+
const appRoot = process.env.OM_TEST_APP_ROOT?.trim();
|
|
32
|
+
if (!appRoot) {
|
|
33
|
+
cachedStandaloneEnv = {};
|
|
34
|
+
return cachedStandaloneEnv;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const envPath = path.join(appRoot, '.env');
|
|
38
|
+
try {
|
|
39
|
+
cachedStandaloneEnv = parseDotEnv(fs.readFileSync(envPath, 'utf8'));
|
|
40
|
+
} catch {
|
|
41
|
+
cachedStandaloneEnv = {};
|
|
42
|
+
}
|
|
43
|
+
return cachedStandaloneEnv;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function readIntegrationEnv(name: string): string | undefined {
|
|
47
|
+
const fromProcess = process.env[name];
|
|
48
|
+
if (typeof fromProcess === 'string') return fromProcess;
|
|
49
|
+
return readStandaloneEnv()[name];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function isStandaloneIntegration(): boolean {
|
|
53
|
+
return Boolean(process.env.OM_TEST_APP_ROOT?.trim());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function readIntegrationEnvFlag(name: string, defaultValue = false): boolean {
|
|
57
|
+
const raw = readIntegrationEnv(name)?.trim().toLowerCase();
|
|
58
|
+
if (!raw) return defaultValue;
|
|
59
|
+
if (truthyValues.has(raw)) return true;
|
|
60
|
+
if (falsyValues.has(raw)) return false;
|
|
61
|
+
return defaultValue;
|
|
62
|
+
}
|