@open-mercato/core 0.10.1-develop.7741.1.61a2652671 → 0.10.1-develop.7749.1.ea3edf75e3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/attachment_partition/index.js +2 -0
  3. package/dist/generated/entities/attachment_partition/index.js.map +2 -2
  4. package/dist/generated/entity-fields-registry.js +1 -0
  5. package/dist/generated/entity-fields-registry.js.map +2 -2
  6. package/dist/helpers/integration/attachmentAccessFixtures.js +144 -0
  7. package/dist/helpers/integration/attachmentAccessFixtures.js.map +7 -0
  8. package/dist/modules/attachments/api/file/[id]/route.js +16 -10
  9. package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
  10. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +15 -9
  11. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
  12. package/dist/modules/attachments/api/library/[id]/route.js +100 -56
  13. package/dist/modules/attachments/api/library/[id]/route.js.map +3 -3
  14. package/dist/modules/attachments/api/library/route.js +56 -17
  15. package/dist/modules/attachments/api/library/route.js.map +2 -2
  16. package/dist/modules/attachments/api/partitions/route.js +2 -0
  17. package/dist/modules/attachments/api/partitions/route.js.map +2 -2
  18. package/dist/modules/attachments/api/route.js +74 -34
  19. package/dist/modules/attachments/api/route.js.map +3 -3
  20. package/dist/modules/attachments/api/transfer/route.js +63 -37
  21. package/dist/modules/attachments/api/transfer/route.js.map +2 -2
  22. package/dist/modules/attachments/data/entities.js +3 -0
  23. package/dist/modules/attachments/data/entities.js.map +2 -2
  24. package/dist/modules/attachments/data/validators.js +41 -0
  25. package/dist/modules/attachments/data/validators.js.map +7 -0
  26. package/dist/modules/attachments/di.js +5 -2
  27. package/dist/modules/attachments/di.js.map +2 -2
  28. package/dist/modules/attachments/generators.js +29 -0
  29. package/dist/modules/attachments/generators.js.map +7 -0
  30. package/dist/modules/attachments/index.js +8 -1
  31. package/dist/modules/attachments/index.js.map +2 -2
  32. package/dist/modules/attachments/lib/access-errors.js +35 -0
  33. package/dist/modules/attachments/lib/access-errors.js.map +7 -0
  34. package/dist/modules/attachments/lib/access-mutation.js +23 -0
  35. package/dist/modules/attachments/lib/access-mutation.js.map +7 -0
  36. package/dist/modules/attachments/lib/access-protection.js +73 -0
  37. package/dist/modules/attachments/lib/access-protection.js.map +7 -0
  38. package/dist/modules/attachments/lib/access-query.js +74 -0
  39. package/dist/modules/attachments/lib/access-query.js.map +7 -0
  40. package/dist/modules/attachments/lib/access-registry.js +87 -0
  41. package/dist/modules/attachments/lib/access-registry.js.map +7 -0
  42. package/dist/modules/attachments/lib/access-runner.js +161 -0
  43. package/dist/modules/attachments/lib/access-runner.js.map +7 -0
  44. package/dist/modules/attachments/lib/access-types.js +1 -0
  45. package/dist/modules/attachments/lib/access-types.js.map +7 -0
  46. package/dist/modules/attachments/lib/attachment-service.js +14 -6
  47. package/dist/modules/attachments/lib/attachment-service.js.map +2 -2
  48. package/dist/modules/attachments/lib/createFromBuffer.js +2 -0
  49. package/dist/modules/attachments/lib/createFromBuffer.js.map +2 -2
  50. package/dist/modules/attachments/lib/partitions.js +3 -2
  51. package/dist/modules/attachments/lib/partitions.js.map +2 -2
  52. package/dist/modules/attachments/lib/scoped-upload-service.js +2 -0
  53. package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
  54. package/dist/modules/attachments/lib/target-access-service.js +12 -3
  55. package/dist/modules/attachments/lib/target-access-service.js.map +2 -2
  56. package/dist/modules/attachments/migrations/Migration20261001120000_attachments.js +38 -0
  57. package/dist/modules/attachments/migrations/Migration20261001120000_attachments.js.map +7 -0
  58. package/dist/modules/attachments/setup.js +10 -0
  59. package/dist/modules/attachments/setup.js.map +2 -2
  60. package/dist/modules/notifications/api/[id]/action/route.js +8 -0
  61. package/dist/modules/notifications/api/[id]/action/route.js.map +2 -2
  62. package/dist/modules/notifications/api/route.js +17 -0
  63. package/dist/modules/notifications/api/route.js.map +2 -2
  64. package/dist/modules/notifications/data/validators.js +1 -1
  65. package/dist/modules/notifications/data/validators.js.map +2 -2
  66. package/dist/modules/notifications/lib/notificationService.js +45 -3
  67. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  68. package/dist/modules/notifications/lib/routeHelpers.js +27 -0
  69. package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
  70. package/dist/modules/payment_gateways/lib/gateway-service.js +50 -34
  71. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  72. package/dist/modules/payment_gateways/lib/status-machine.js +10 -1
  73. package/dist/modules/payment_gateways/lib/status-machine.js.map +2 -2
  74. package/dist/modules/payment_gateways/lib/webhook-processor.js +1 -1
  75. package/dist/modules/payment_gateways/lib/webhook-processor.js.map +2 -2
  76. package/dist/modules/staff/notifications.js +4 -2
  77. package/dist/modules/staff/notifications.js.map +2 -2
  78. package/dist/modules/workflows/data/entities.js.map +2 -2
  79. package/dist/modules/workflows/lib/activity-executor.js +18 -4
  80. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  81. package/dist/modules/workflows/lib/activity-types.js +2 -2
  82. package/dist/modules/workflows/lib/activity-types.js.map +2 -2
  83. package/dist/modules/workflows/lib/condition-handler.js +7 -1
  84. package/dist/modules/workflows/lib/condition-handler.js.map +2 -2
  85. package/dist/modules/workflows/lib/duration.js +13 -0
  86. package/dist/modules/workflows/lib/duration.js.map +2 -2
  87. package/dist/modules/workflows/lib/step-handler.js +9 -2
  88. package/dist/modules/workflows/lib/step-handler.js.map +2 -2
  89. package/dist/modules/workflows/lib/task-quick-action.js +13 -10
  90. package/dist/modules/workflows/lib/task-quick-action.js.map +2 -2
  91. package/dist/modules/workflows/lib/transition-handler.js +2 -1
  92. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  93. package/dist/modules/workflows/notifications.js +4 -0
  94. package/dist/modules/workflows/notifications.js.map +2 -2
  95. package/generated/entities/attachment_partition/index.ts +1 -0
  96. package/generated/entity-fields-registry.ts +1 -0
  97. package/package.json +7 -7
  98. package/src/helpers/integration/attachmentAccessFixtures.ts +141 -0
  99. package/src/modules/attachments/api/file/[id]/route.ts +12 -9
  100. package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +10 -8
  101. package/src/modules/attachments/api/library/[id]/route.ts +87 -58
  102. package/src/modules/attachments/api/library/route.ts +40 -11
  103. package/src/modules/attachments/api/partitions/route.ts +2 -0
  104. package/src/modules/attachments/api/route.ts +68 -35
  105. package/src/modules/attachments/api/transfer/route.ts +65 -42
  106. package/src/modules/attachments/data/entities.ts +4 -0
  107. package/src/modules/attachments/data/validators.ts +36 -0
  108. package/src/modules/attachments/di.ts +4 -1
  109. package/src/modules/attachments/generators.ts +27 -0
  110. package/src/modules/attachments/i18n/de.json +4 -0
  111. package/src/modules/attachments/i18n/en.json +4 -0
  112. package/src/modules/attachments/i18n/es.json +4 -0
  113. package/src/modules/attachments/i18n/ko.json +4 -0
  114. package/src/modules/attachments/i18n/pl.json +4 -0
  115. package/src/modules/attachments/index.ts +18 -0
  116. package/src/modules/attachments/lib/access-errors.ts +32 -0
  117. package/src/modules/attachments/lib/access-mutation.ts +26 -0
  118. package/src/modules/attachments/lib/access-protection.ts +83 -0
  119. package/src/modules/attachments/lib/access-query.ts +86 -0
  120. package/src/modules/attachments/lib/access-registry.ts +117 -0
  121. package/src/modules/attachments/lib/access-runner.ts +183 -0
  122. package/src/modules/attachments/lib/access-types.ts +78 -0
  123. package/src/modules/attachments/lib/attachment-service.ts +8 -4
  124. package/src/modules/attachments/lib/createFromBuffer.ts +3 -0
  125. package/src/modules/attachments/lib/partitions.ts +3 -2
  126. package/src/modules/attachments/lib/scoped-upload-service.ts +2 -0
  127. package/src/modules/attachments/lib/target-access-service.ts +10 -3
  128. package/src/modules/attachments/migrations/.snapshot-open-mercato.json +17 -0
  129. package/src/modules/attachments/migrations/Migration20261001120000_attachments.ts +35 -0
  130. package/src/modules/attachments/setup.ts +10 -0
  131. package/src/modules/auth/i18n/de.json +3 -0
  132. package/src/modules/auth/i18n/en.json +3 -0
  133. package/src/modules/auth/i18n/es.json +3 -0
  134. package/src/modules/auth/i18n/ko.json +3 -0
  135. package/src/modules/auth/i18n/pl.json +3 -0
  136. package/src/modules/notifications/api/[id]/action/route.ts +8 -0
  137. package/src/modules/notifications/api/route.ts +17 -0
  138. package/src/modules/notifications/data/validators.ts +1 -1
  139. package/src/modules/notifications/i18n/de.json +2 -0
  140. package/src/modules/notifications/i18n/en.json +2 -0
  141. package/src/modules/notifications/i18n/es.json +2 -0
  142. package/src/modules/notifications/i18n/ko.json +2 -0
  143. package/src/modules/notifications/i18n/pl.json +2 -0
  144. package/src/modules/notifications/lib/notificationService.ts +67 -1
  145. package/src/modules/notifications/lib/routeHelpers.ts +38 -0
  146. package/src/modules/payment_gateways/lib/gateway-service.ts +50 -33
  147. package/src/modules/payment_gateways/lib/status-machine.ts +19 -0
  148. package/src/modules/payment_gateways/lib/webhook-processor.ts +1 -1
  149. package/src/modules/staff/notifications.ts +2 -0
  150. package/src/modules/workflows/AGENTS.md +2 -2
  151. package/src/modules/workflows/data/entities.ts +6 -0
  152. package/src/modules/workflows/lib/activity-executor.ts +36 -4
  153. package/src/modules/workflows/lib/activity-types.ts +2 -2
  154. package/src/modules/workflows/lib/condition-handler.ts +16 -1
  155. package/src/modules/workflows/lib/duration.ts +25 -0
  156. package/src/modules/workflows/lib/step-handler.ts +16 -1
  157. package/src/modules/workflows/lib/task-quick-action.ts +19 -10
  158. package/src/modules/workflows/lib/transition-handler.ts +2 -1
  159. package/src/modules/workflows/notifications.ts +4 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/workflows/lib/transition-handler.ts"],
4
- "sourcesContent": ["/**\n * Workflows Module - Transition Handler Service\n *\n * Handles workflow transitions between steps:\n * - Evaluating if a transition is valid (checking conditions)\n * - Executing transitions (moving from one step to another)\n * - Integrating with business rules engine for pre/post conditions\n * - Executing activities on transition\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\nimport type { EventBus } from '@open-mercato/events'\nimport {\n WorkflowInstance,\n WorkflowEvent,\n} from '../data/entities'\nimport * as ruleEvaluator from '../../business_rules/lib/rule-evaluator'\nimport * as ruleEngine from '../../business_rules/lib/rule-engine'\nimport * as activityExecutor from './activity-executor'\nimport type { ActivityDefinition } from './activity-executor'\nimport * as stepHandler from './step-handler'\nimport { findDefinitionForInstance } from './find-definition'\nimport { resolveDefinitionInterpolationMode } from './interpolation-pipeline'\nimport { buildSetVariableContextPatch, isSetVariableOutput } from './set-variable'\nimport {\n resolveStepFailureHandling,\n type StepFailureHandling,\n} from './error-routing'\nimport { DRY_RUN_EVENT_TYPES, isDryRunInstance } from './dry-run'\nimport { excludeNonNormalTransitions } from './route-kinds'\nimport {\n type ExecutionToken,\n rootToken,\n tokenBranchInstanceId,\n tokenReadContext,\n setTokenCurrentStepId,\n applyTokenContextWrites,\n mergeTokenContext,\n setTokenWaitingForActivities,\n setTokenPendingTransition,\n touchToken,\n} from './execution-token'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('workflows')\n\n/**\n * Translate a resolved failure handling into the fields the executor acts on.\n * `fail` (the absent-config default) adds nothing, so untouched definitions\n * return the exact failure shape they returned before error routing existed.\n */\nfunction buildFailureRouting(\n handling: StepFailureHandling,\n failedStepId: string\n): Pick<\n TransitionExecutionResult,\n 'failedStepId' | 'errorRoute' | 'parkForAttention' | 'errorHandlerStepId'\n> {\n if (handling.kind === 'route' && typeof handling.transition.toStepId === 'string') {\n return {\n failedStepId,\n errorRoute: {\n transitionId: handling.transition.transitionId,\n toStepId: handling.transition.toStepId,\n },\n }\n }\n if (handling.kind === 'park') {\n return { failedStepId, parkForAttention: true }\n }\n if (handling.kind === 'handlerStep') {\n return { failedStepId, errorHandlerStepId: handling.stepId }\n }\n return {}\n}\n\n/**\n * Record that a dry run evaluated a transition's business rule but withheld its\n * ACTION arm, so the \"Would do\" report can say which side effect was skipped\n * rather than leaving the author to assume the rule did nothing.\n */\nasync function logSuppressedRuleActions(\n em: EntityManager,\n instance: WorkflowInstance,\n ruleId: string,\n phase: 'pre_transition' | 'post_transition'\n): Promise<void> {\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: DRY_RUN_EVENT_TYPES.businessRuleActionsSuppressed,\n eventData: { ruleId, phase },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n}\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface TransitionEvaluationContext {\n workflowContext: Record<string, any>\n userId?: string\n triggerData?: any\n transitionId?: string\n}\n\nexport interface TransitionEvaluationResult {\n isValid: boolean\n transition?: any\n reason?: string\n failedConditions?: string[]\n evaluationTime?: number\n}\n\nexport interface TransitionExecutionContext {\n workflowContext: Record<string, any>\n userId?: string\n triggerData?: any\n transitionId?: string\n}\n\nexport interface TransitionExecutionResult {\n success: boolean\n nextStepId?: string\n pausedForActivities?: boolean\n // The destination step entered a wait state (PAUSED / WAITING_FOR_ACTIVITIES)\n // after the transition into it executed \u2014 e.g. an INVOKE_AGENT AUTOMATED step\n // parked the instance on its agent signal. Distinct from `pausedForActivities`\n // (async activities ON THE TRANSITION, resumed via resumeWorkflowAfterActivities).\n paused?: boolean\n conditionsEvaluated?: {\n preConditions: boolean\n postConditions: boolean\n }\n activitiesExecuted?: activityExecutor.ActivityExecutionResult[]\n error?: string\n // Error routing (spec 5.9). Set only when the failure carries error handling\n // the executor must apply: `errorRoute` names the wired error transition to\n // follow, `parkForAttention` asks for a failure-queue park instead of a fail.\n // Absent on every legacy failure, which therefore keeps failing identically.\n failedStepId?: string\n errorRoute?: { transitionId?: string; toStepId: string }\n parkForAttention?: boolean\n // Definition-level handler step to jump to before failing (spec 5.9).\n errorHandlerStepId?: string\n}\n\nexport class TransitionError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'TransitionError'\n }\n}\n\n// ============================================================================\n// Main Transition Functions\n// ============================================================================\n\n/**\n * Evaluate if a transition from current step to target step is valid\n *\n * Checks:\n * - Transition exists in workflow definition\n * - Pre-conditions pass (if any business rules defined)\n * - Transition condition evaluates to true (if specified)\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param fromStepId - Current step ID\n * @param toStepId - Target step ID (optional - will auto-select if not provided)\n * @param context - Evaluation context\n * @returns Evaluation result with validity and reason\n */\nexport async function evaluateTransition(\n em: EntityManager,\n instance: WorkflowInstance,\n fromStepId: string,\n toStepId: string | undefined,\n context: TransitionEvaluationContext\n): Promise<TransitionEvaluationResult> {\n const startTime = Date.now()\n\n try {\n // Load workflow definition\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n return {\n isValid: false,\n reason: `Workflow definition not found: ${instance.definitionId}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n\n // Find transition\n const transitions = definition.definition.transitions || []\n let transition: any\n\n if (toStepId) {\n // Find specific transition\n transition = transitions.find(\n (candidate: { fromStepId?: unknown; toStepId?: unknown; transitionId?: unknown }) =>\n candidate.fromStepId === fromStepId\n && candidate.toStepId === toStepId\n && (context.transitionId === undefined || candidate.transitionId === context.transitionId)\n )\n\n if (!transition) {\n return {\n isValid: false,\n reason: `No transition found from ${fromStepId} to ${toStepId}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n } else {\n // Auto-select first valid transition (never a kinded route: error,\n // SLA-breach or agent-outcome)\n const availableTransitions = excludeNonNormalTransitions(transitions).filter(\n (t: any) => t.fromStepId === fromStepId\n )\n\n if (availableTransitions.length === 0) {\n return {\n isValid: false,\n reason: `No transitions available from step ${fromStepId}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n\n // Evaluate each transition to find first valid one\n for (const t of availableTransitions) {\n const result = await evaluateTransitionConditions(\n em,\n instance,\n t,\n context\n )\n\n if (result.isValid) {\n transition = t\n break\n }\n }\n\n if (!transition) {\n return {\n isValid: false,\n reason: `No valid transitions found from step ${fromStepId}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n }\n\n // Evaluate transition conditions (inline condition + business rules pre-conditions)\n const conditionResult = await evaluateTransitionConditions(\n em,\n instance,\n transition,\n context\n )\n\n return {\n ...conditionResult,\n transition,\n evaluationTime: Date.now() - startTime,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return {\n isValid: false,\n reason: `Transition evaluation error: ${errorMessage}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n}\n\n/**\n * Find all valid transitions from current step\n *\n * This function evaluates both inline conditions AND preConditions (business rules)\n * to determine which transitions are truly valid. This is important for decision\n * branching where multiple transitions exist with different preConditions.\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param fromStepId - Current step ID\n * @param context - Evaluation context\n * @returns Array of evaluation results for all transitions, sorted by priority (desc)\n */\nexport async function findValidTransitions(\n em: EntityManager,\n instance: WorkflowInstance,\n fromStepId: string,\n context: TransitionEvaluationContext\n): Promise<TransitionEvaluationResult[]> {\n try {\n // Load workflow definition\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n return []\n }\n\n // Find all transitions from current step, sorted by priority (highest first).\n // Every kinded route is excluded \u2014 each is reachable only from its own\n // trigger (a step failure, a task's deadline passing, a resolved agent\n // disposition), never from normal routing.\n const transitions = excludeNonNormalTransitions(definition.definition.transitions || [])\n .filter((t: any) => t.fromStepId === fromStepId)\n .sort((a: any, b: any) => (b.priority || 0) - (a.priority || 0))\n\n // Evaluate each transition including preConditions\n const results: TransitionEvaluationResult[] = []\n\n for (const transition of transitions) {\n // First check inline condition\n const conditionResult = await evaluateTransition(\n em,\n instance,\n fromStepId,\n transition.toStepId,\n { ...context, transitionId: transition.transitionId },\n )\n\n if (!conditionResult.isValid) {\n results.push(conditionResult)\n continue\n }\n\n // Also evaluate preConditions if they exist\n const preConditions = transition.preConditions || []\n if (preConditions.length > 0) {\n const preConditionsResult = await evaluatePreConditions(\n em,\n instance,\n transition,\n context as TransitionExecutionContext,\n null\n )\n\n if (!preConditionsResult.allowed) {\n // Transition is invalid due to preConditions\n const failedRules = preConditionsResult.executedRules\n .filter((r) => !r.conditionResult)\n .map((r) => r.rule.ruleId || r.rule.ruleName)\n\n results.push({\n isValid: false,\n transition,\n reason: `Pre-conditions failed: ${failedRules.join(', ')}`,\n failedConditions: failedRules,\n })\n continue\n }\n }\n\n // Transition is valid (both condition and preConditions passed)\n results.push({\n ...conditionResult,\n transition,\n })\n }\n\n return results\n } catch (error) {\n logger.error('Error finding valid transitions', { err: error })\n return []\n }\n}\n\n/**\n * Execute a transition from one step to another\n *\n * This is the main entry point for transition execution. It:\n * 1. Validates the transition\n * 2. Evaluates pre-conditions\n * 3. Executes activities (if any)\n * 4. Updates workflow instance state (atomically with activity outputs)\n * 5. Evaluates post-conditions\n * 6. Logs transition event\n *\n * @param em - Entity manager\n * @param container - DI container (for activity execution)\n * @param instance - Workflow instance\n * @param fromStepId - Current step ID\n * @param toStepId - Target step ID\n * @param context - Execution context\n * @returns Execution result\n */\nexport async function executeTransition(\n em: EntityManager,\n container: AwilixContainer,\n instance: WorkflowInstance,\n fromStepId: string,\n toStepId: string,\n context: TransitionExecutionContext\n): Promise<TransitionExecutionResult> {\n // Public DI signature preserved: the root token adapts the instance, so the\n // single-token path is behaviourally unchanged.\n return executeTransitionForToken(em, container, rootToken(instance), fromStepId, toStepId, context)\n}\n\n/**\n * Token-aware transition execution. The root token wraps a WorkflowInstance\n * (legacy single-token path); a branch token wraps a WorkflowBranchInstance so\n * a parallel branch advances independently with its own context namespace,\n * status and pending transition.\n */\nexport async function executeTransitionForToken(\n em: EntityManager,\n container: AwilixContainer,\n token: ExecutionToken,\n fromStepId: string,\n toStepId: string,\n context: TransitionExecutionContext\n): Promise<TransitionExecutionResult> {\n const instance = token.instance\n const branch = token.kind === 'branch' ? token.branch : null\n const branchInstanceId = tokenBranchInstanceId(token)\n try {\n let eventBus: Pick<EventBus, 'emitEvent'> | null = null\n try {\n eventBus = container.resolve('eventBus') as EventBus\n } catch {\n eventBus = null\n }\n\n // First, evaluate if transition is valid\n const evaluation = await evaluateTransition(\n em,\n instance,\n fromStepId,\n toStepId,\n context\n )\n\n if (!evaluation.isValid) {\n return {\n success: false,\n error: evaluation.reason || 'Transition validation failed',\n }\n }\n\n const transition = evaluation.transition!\n\n // Evaluate pre-conditions (business rules)\n const preConditionsResult = await evaluatePreConditions(\n em,\n instance,\n transition,\n context,\n eventBus\n )\n\n if (!preConditionsResult.allowed) {\n // Build detailed failure information\n const failedRules = preConditionsResult.executedRules\n .filter((r) => !r.conditionResult)\n .map((r) => ({\n ruleId: r.rule.ruleId,\n ruleName: r.rule.ruleName,\n error: r.error,\n }))\n\n const failedRulesDetails = failedRules.length > 0\n ? failedRules.map(r => `${r.ruleId}: ${r.error || 'condition failed'}`).join('; ')\n : preConditionsResult.errors?.join(', ') || 'Unknown pre-condition failure'\n\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'TRANSITION_REJECTED',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n reason: 'Pre-conditions failed',\n failedRules: failedRulesDetails,\n failedRulesDetail: failedRules,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n success: false,\n error: `Pre-conditions failed: ${failedRulesDetails}`,\n conditionsEvaluated: {\n preConditions: false,\n postConditions: false,\n },\n }\n }\n\n // Execute activities (if any)\n let activityOutputs: Record<string, any> = {}\n const activityResults: activityExecutor.ActivityExecutionResult[] = []\n\n if (transition.activities && transition.activities.length > 0) {\n const definitionForInterpolation = await findDefinitionForInstance(em, instance)\n const activityContext: activityExecutor.ActivityContext = {\n workflowInstance: instance,\n workflowContext: {\n ...tokenReadContext(token),\n ...context.workflowContext,\n },\n branchInstanceId,\n userId: context.userId,\n interpolationMode: resolveDefinitionInterpolationMode(definitionForInterpolation?.definition),\n }\n\n // Execute all activities\n const results = await activityExecutor.executeActivities(\n em,\n container,\n transition.activities as ActivityDefinition[],\n activityContext\n )\n\n activityResults.push(...results)\n\n // Check for failures\n const failedActivities = results.filter(r => !r.success)\n\n if (failedActivities.length > 0) {\n // A dry-run refusal stops the run AT that node (spec section 8.2). It is\n // not an activity failure \u2014 nothing was attempted \u2014 so neither\n // `continueOnActivityFailure` nor the step's error route/directive may\n // absorb it. Answering it before either is consulted is what keeps the\n // marker explicit instead of silently routed.\n const refusal = failedActivities.find((result) => result.dryRunRefused)\n if (refusal) {\n return {\n success: false,\n error: refusal.error ?? 'Dry run stopped',\n conditionsEvaluated: { preConditions: true, postConditions: false },\n }\n }\n\n const continueOnFailure = transition.continueOnActivityFailure ?? false\n\n // Log activity failures\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'ACTIVITY_FAILED',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n failedActivities: failedActivities.map(f => ({\n activityType: f.activityType,\n activityName: f.activityName,\n error: f.error,\n retryCount: f.retryCount,\n })),\n continueOnFailure,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n if (!continueOnFailure) {\n // Error routing (spec 5.9). The failing node here is the step the\n // route leaves, because the token cursor has not advanced yet.\n const failureHandling = resolveStepFailureHandling(\n definitionForInterpolation?.definition,\n fromStepId\n )\n\n if (failureHandling.kind === 'continue') {\n // `continueWithFallback` is the directive form of the legacy\n // `continueOnActivityFailure` flag: identical continue semantics,\n // plus the authored fallback value landing under the step id.\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n branchInstanceId,\n eventType: 'ERROR_DIRECTIVE_APPLIED',\n eventData: {\n stepId: fromStepId,\n directive: 'continueWithFallback',\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n hasFallbackValue: failureHandling.fallbackValue !== undefined,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n if (failureHandling.fallbackValue !== undefined) {\n activityOutputs[fromStepId] = failureHandling.fallbackValue\n }\n } else {\n return {\n success: false,\n error: `Activities failed: ${failedActivities.map(f => f.error).join(', ')}`,\n conditionsEvaluated: {\n preConditions: true,\n postConditions: false,\n },\n ...buildFailureRouting(failureHandling, fromStepId),\n }\n }\n }\n }\n\n // Collect activity outputs for context update. SET_VARIABLE outputs are\n // applied at their assignment paths in top-level context (preserving\n // sibling keys of any nested target); other outputs merge under the\n // activity name/type key.\n results.forEach(result => {\n if (result.success && result.output) {\n if (result.activityType === 'SET_VARIABLE' && isSetVariableOutput(result.output)) {\n const assignmentBase = {\n ...tokenReadContext(token),\n ...context.workflowContext,\n ...activityOutputs,\n }\n Object.assign(\n activityOutputs,\n buildSetVariableContextPatch(assignmentBase, result.output.assignments)\n )\n } else {\n const key = result.activityName || result.activityType\n activityOutputs[key] = result.output\n }\n }\n })\n }\n\n // Check if any activities are async - if so, pause before executing step\n const hasAsyncActivities = activityResults.some(r => r.async)\n\n if (hasAsyncActivities) {\n const pendingJobIds = activityResults\n .filter(a => a.async && a.jobId)\n .map(a => ({ activityId: a.activityId, jobId: a.jobId }))\n\n // Store pending transition state (per-token: branch or instance)\n setTokenPendingTransition(token, {\n toStepId,\n activityResults,\n timestamp: new Date(),\n })\n\n // Store activity outputs + pending-activity tracking in the token's own\n // context scope (instance.context for root, branch namespace for a branch)\n applyTokenContextWrites(token, context.workflowContext, activityOutputs)\n mergeTokenContext(token, { _pendingAsyncActivities: pendingJobIds })\n\n // Set status to waiting (branch-scoped when running inside a branch)\n setTokenWaitingForActivities(token)\n touchToken(token, new Date())\n await em.flush()\n\n // Log event\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n branchInstanceId,\n eventType: 'TRANSITION_PAUSED_FOR_ACTIVITIES',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId,\n pendingActivities: pendingJobIds,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Return WITHOUT executing step\n return {\n success: true,\n pausedForActivities: true,\n nextStepId: toStepId,\n conditionsEvaluated: {\n preConditions: true,\n postConditions: false, // Not evaluated yet\n },\n activitiesExecuted: activityResults,\n }\n }\n\n // Advance the token cursor and update context atomically (instance for the\n // root token; branch cursor + namespace for a branch token).\n setTokenCurrentStepId(token, toStepId)\n applyTokenContextWrites(token, context.workflowContext, activityOutputs)\n touchToken(token, new Date())\n\n await em.flush()\n\n // Execute the new step (this will create USER_TASK, handle END steps, etc.)\n const stepExecutionResult = await stepHandler.executeStep(\n em,\n instance,\n toStepId,\n {\n workflowContext: tokenReadContext(token),\n userId: context.userId,\n triggerData: context.triggerData,\n },\n container,\n branch\n )\n\n // Flush to database after step execution completes to make state visible to UI\n await em.flush()\n\n // Handle step execution failure. Error routing (spec 5.9) keys on the step\n // that failed \u2014 the cursor already advanced, so that is `toStepId`.\n if (stepExecutionResult.status === 'FAILED') {\n const definitionForFailure = await findDefinitionForInstance(em, instance)\n const failureHandling = resolveStepFailureHandling(definitionForFailure?.definition, toStepId)\n\n if (failureHandling.kind === 'continue') {\n // The step instance stays FAILED \u2014 its state machine is untouched. The\n // instance advances with the authored fallback value in context, and\n // the executor re-evaluates the failed step's outgoing routes.\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n branchInstanceId,\n eventType: 'ERROR_DIRECTIVE_APPLIED',\n eventData: {\n stepId: toStepId,\n directive: 'continueWithFallback',\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n error: stepExecutionResult.error,\n hasFallbackValue: failureHandling.fallbackValue !== undefined,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n if (failureHandling.fallbackValue !== undefined) {\n mergeTokenContext(token, { [toStepId]: failureHandling.fallbackValue })\n await em.flush()\n }\n } else {\n return {\n success: false,\n error: stepExecutionResult.error || 'Step execution failed',\n ...buildFailureRouting(failureHandling, toStepId),\n }\n }\n }\n\n // The transition INTO the step genuinely happened, but the step then parked\n // the instance (e.g. an INVOKE_AGENT AUTOMATED step enqueued an async agent\n // job and set status PAUSED). Surface that via `paused` so the executor loop\n // stops advancing instead of taking the next auto-transition.\n //\n // `FORK` is the ONE wait reason that is not an external park: `openFork`\n // leaves the instance FORKED with its branch rows already written, and the\n // branches are driven by `advanceBranches` on the executor loop's NEXT\n // iteration. Reporting it as paused makes the executor return before it\n // re-reads the instance, so the branches never advance and the instance\n // stays FORKED at the fork step forever.\n const stepPaused =\n stepExecutionResult.status === 'WAITING' && stepExecutionResult.waitReason !== 'FORK'\n\n // Evaluate post-conditions (business rules)\n const postConditionsResult = await evaluatePostConditions(\n em,\n instance,\n transition,\n context,\n eventBus\n )\n\n if (!postConditionsResult.allowed) {\n const failedRules = postConditionsResult.errors?.join(', ') || 'Unknown post-condition failure'\n\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'TRANSITION_POST_CONDITION_FAILED',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n reason: 'Post-conditions failed',\n failedRules,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Note: We don't roll back the transition on post-condition failure\n // Post-conditions are warnings, not blockers\n }\n\n // Log successful transition\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'TRANSITION_EXECUTED',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n transitionName: transition.transitionName,\n preConditionsPassed: true,\n postConditionsPassed: postConditionsResult.allowed,\n activitiesExecuted: activityResults.length,\n activitiesSucceeded: activityResults.filter(r => r.success).length,\n activitiesFailed: activityResults.filter(r => !r.success).length,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n success: true,\n nextStepId: toStepId,\n paused: stepPaused,\n conditionsEvaluated: {\n preConditions: true,\n postConditions: postConditionsResult.allowed,\n },\n activitiesExecuted: activityResults,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'TRANSITION_FAILED',\n eventData: {\n fromStepId,\n toStepId,\n error: errorMessage,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n success: false,\n error: `Transition execution failed: ${errorMessage}`,\n }\n }\n}\n\n// ============================================================================\n// Condition Evaluation\n// ============================================================================\n\n/**\n * Evaluate transition conditions (inline condition expression)\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param transition - Transition definition\n * @param context - Evaluation context\n * @returns Evaluation result\n */\nasync function evaluateTransitionConditions(\n em: EntityManager,\n instance: WorkflowInstance,\n transition: any,\n context: TransitionEvaluationContext\n): Promise<TransitionEvaluationResult> {\n try {\n // If no condition specified, transition is always valid\n if (!transition.condition) {\n return {\n isValid: true,\n }\n }\n\n // Build data context for rule evaluation\n const data = {\n ...instance.context,\n ...context.workflowContext,\n triggerData: context.triggerData,\n }\n\n // Build evaluation context\n const evalContext: ruleEvaluator.RuleEvaluationContext = {\n entityType: 'workflow:transition',\n entityId: instance.id,\n user: context.userId ? { id: context.userId } : undefined,\n }\n\n // Evaluate condition using expression evaluator\n const result = await ruleEvaluator.evaluateConditions(\n transition.condition,\n data,\n evalContext\n )\n\n return {\n isValid: result,\n reason: result ? undefined : 'Transition condition evaluated to false',\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return {\n isValid: false,\n reason: `Condition evaluation error: ${errorMessage}`,\n }\n }\n}\n\n/**\n * Evaluate pre-conditions using business rules engine\n *\n * Pre-conditions are GUARD rules that must pass before transition can execute.\n * If any GUARD rule fails, the transition is blocked.\n *\n * If the transition defines specific preConditions with ruleIds, those are\n * executed directly via executeRuleByRuleId. Otherwise, falls back to\n * discovery-based execution via executeRules.\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param transition - Transition definition\n * @param context - Execution context\n * @returns Rule engine result\n */\nasync function evaluatePreConditions(\n em: EntityManager,\n instance: WorkflowInstance,\n transition: any,\n context: TransitionExecutionContext,\n eventBus: Pick<EventBus, 'emitEvent'> | null\n): Promise<ruleEngine.RuleEngineResult> {\n try {\n // Load workflow definition to get workflow ID\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n return {\n allowed: true,\n executedRules: [],\n totalExecutionTime: 0,\n }\n }\n\n // Check if transition has specific preConditions defined\n const preConditions = transition.preConditions || []\n\n // If no pre-conditions defined, allow transition\n if (preConditions.length === 0) {\n return {\n allowed: true,\n executedRules: [],\n totalExecutionTime: 0,\n }\n }\n\n // Execute each pre-condition rule directly by ruleId\n const dryRun = isDryRunInstance(instance)\n const startTime = Date.now()\n const executedRules: ruleEngine.RuleExecutionResult[] = []\n const errors: string[] = []\n let allowed = true\n\n for (const condition of preConditions) {\n const result = await ruleEngine.executeRuleByRuleId(em, {\n ruleId: condition.ruleId, // String identifier\n data: {\n workflowInstanceId: instance.id,\n workflowId: definition.workflowId,\n fromStepId: transition.fromStepId,\n toStepId: transition.toStepId,\n workflowContext: {\n ...instance.context,\n ...context.workflowContext,\n },\n triggerData: context.triggerData,\n },\n user: context.userId ? { id: context.userId } : undefined,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n executedBy: context.userId,\n entityType: `workflow:${definition.workflowId}:transition`,\n entityId: transition.transitionId || `${transition.fromStepId}->${transition.toStepId}`,\n eventType: 'pre_transition',\n // Dry run (spec section 8.2): the rule engine's own `dryRun` only\n // suppresses its execution LOG \u2014 a rule's success/failure ACTIONS run\n // regardless \u2014 so a side-effect-free run has to ask for `skipActions`.\n // The conditions still evaluate, so the run takes the routes it really\n // would; only the ACTION arm is withheld, and it is reported.\n skipActions: dryRun,\n })\n if (dryRun && result.actionsExecuted === null) {\n await logSuppressedRuleActions(em, instance, condition.ruleId, 'pre_transition')\n }\n\n // Create a compatible RuleExecutionResult for tracking\n // We don't have the full BusinessRule entity, but we can create a partial result\n const ruleResult: ruleEngine.RuleExecutionResult = {\n rule: {\n ruleId: result.ruleId,\n ruleName: result.ruleName,\n ruleType: 'GUARD',\n } as any,\n conditionResult: result.conditionResult,\n actionsExecuted: result.actionsExecuted,\n executionTime: result.executionTime,\n error: result.error,\n logId: result.logId,\n }\n executedRules.push(ruleResult)\n\n // Handle rule errors\n if (result.error) {\n // Rule not found, disabled, or other errors\n const isRequired = condition.required !== false // Default to required\n if (isRequired) {\n allowed = false\n errors.push(`Rule '${result.ruleId}': ${result.error}`)\n }\n continue\n }\n\n // If required and condition failed, block transition\n const isRequired = condition.required !== false // Default to required\n if (isRequired && !result.conditionResult) {\n allowed = false\n errors.push(`Pre-condition '${result.ruleName || result.ruleId}' failed`)\n }\n }\n\n return {\n allowed,\n executedRules,\n totalExecutionTime: Date.now() - startTime,\n errors: errors.length > 0 ? errors : undefined,\n }\n } catch (error) {\n logger.error('Error evaluating pre-conditions', { err: error })\n return {\n allowed: false,\n executedRules: [],\n totalExecutionTime: 0,\n errors: [error instanceof Error ? error.message : String(error)],\n }\n }\n}\n\n/**\n * Evaluate post-conditions using business rules engine\n *\n * Post-conditions are GUARD rules that should pass after transition executes.\n * Unlike pre-conditions, post-condition failures are logged but don't block the transition.\n *\n * If the transition defines specific postConditions with ruleIds, those are\n * executed directly via executeRuleByRuleId. Otherwise, returns allowed: true.\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param transition - Transition definition\n * @param context - Execution context\n * @returns Rule engine result\n */\nasync function evaluatePostConditions(\n em: EntityManager,\n instance: WorkflowInstance,\n transition: any,\n context: TransitionExecutionContext,\n eventBus: Pick<EventBus, 'emitEvent'> | null\n): Promise<ruleEngine.RuleEngineResult> {\n try {\n // Load workflow definition to get workflow ID\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n return {\n allowed: true,\n executedRules: [],\n totalExecutionTime: 0,\n }\n }\n\n // Check if transition has specific postConditions defined\n const postConditions = transition.postConditions || []\n\n // If no post-conditions defined, allow\n if (postConditions.length === 0) {\n return {\n allowed: true,\n executedRules: [],\n totalExecutionTime: 0,\n }\n }\n\n // Execute each post-condition rule directly by ruleId\n const dryRun = isDryRunInstance(instance)\n const startTime = Date.now()\n const executedRules: ruleEngine.RuleExecutionResult[] = []\n const errors: string[] = []\n let allowed = true\n\n for (const condition of postConditions) {\n const result = await ruleEngine.executeRuleByRuleId(em, {\n ruleId: condition.ruleId, // String identifier\n data: {\n workflowInstanceId: instance.id,\n workflowId: definition.workflowId,\n fromStepId: transition.fromStepId,\n toStepId: transition.toStepId,\n workflowContext: {\n ...instance.context,\n ...context.workflowContext,\n },\n triggerData: context.triggerData,\n },\n user: context.userId ? { id: context.userId } : undefined,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n executedBy: context.userId,\n entityType: `workflow:${definition.workflowId}:transition`,\n entityId: transition.transitionId || `${transition.fromStepId}->${transition.toStepId}`,\n eventType: 'post_transition',\n // Dry run (spec section 8.2): the rule engine's own `dryRun` only\n // suppresses its execution LOG \u2014 a rule's success/failure ACTIONS run\n // regardless \u2014 so a side-effect-free run has to ask for `skipActions`.\n // The conditions still evaluate, so the run takes the routes it really\n // would; only the ACTION arm is withheld, and it is reported.\n skipActions: dryRun,\n })\n if (dryRun && result.actionsExecuted === null) {\n await logSuppressedRuleActions(em, instance, condition.ruleId, 'post_transition')\n }\n\n // Create a compatible RuleExecutionResult for tracking\n const ruleResult: ruleEngine.RuleExecutionResult = {\n rule: {\n ruleId: result.ruleId,\n ruleName: result.ruleName,\n ruleType: 'GUARD',\n } as any,\n conditionResult: result.conditionResult,\n actionsExecuted: result.actionsExecuted,\n executionTime: result.executionTime,\n error: result.error,\n logId: result.logId,\n }\n executedRules.push(ruleResult)\n\n // Handle rule errors\n if (result.error) {\n errors.push(`Rule '${result.ruleId}': ${result.error}`)\n // Post-conditions don't block, but track the failure\n allowed = false\n continue\n }\n\n // Track condition failures (post-conditions are warnings, not blockers)\n if (!result.conditionResult) {\n allowed = false\n errors.push(`Post-condition '${result.ruleName || result.ruleId}' failed`)\n }\n }\n\n return {\n allowed,\n executedRules,\n totalExecutionTime: Date.now() - startTime,\n errors: errors.length > 0 ? errors : undefined,\n }\n } catch (error) {\n logger.error('Error evaluating post-conditions', { err: error })\n return {\n allowed: false,\n executedRules: [],\n totalExecutionTime: 0,\n errors: [error instanceof Error ? error.message : String(error)],\n }\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Log transition-related event to event sourcing table\n */\nasync function logTransitionEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n branchInstanceId?: string | null\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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",
4
+ "sourcesContent": ["/**\n * Workflows Module - Transition Handler Service\n *\n * Handles workflow transitions between steps:\n * - Evaluating if a transition is valid (checking conditions)\n * - Executing transitions (moving from one step to another)\n * - Integrating with business rules engine for pre/post conditions\n * - Executing activities on transition\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\nimport type { EventBus } from '@open-mercato/events'\nimport {\n WorkflowInstance,\n WorkflowEvent,\n} from '../data/entities'\nimport * as ruleEvaluator from '../../business_rules/lib/rule-evaluator'\nimport * as ruleEngine from '../../business_rules/lib/rule-engine'\nimport * as activityExecutor from './activity-executor'\nimport type { ActivityDefinition } from './activity-executor'\nimport * as stepHandler from './step-handler'\nimport { findDefinitionForInstance } from './find-definition'\nimport { resolveDefinitionInterpolationMode } from './interpolation-pipeline'\nimport { buildSetVariableContextPatch, isSetVariableOutput } from './set-variable'\nimport {\n resolveStepFailureHandling,\n type StepFailureHandling,\n} from './error-routing'\nimport { DRY_RUN_EVENT_TYPES, isDryRunInstance } from './dry-run'\nimport { excludeNonNormalTransitions } from './route-kinds'\nimport {\n type ExecutionToken,\n rootToken,\n tokenBranchInstanceId,\n tokenReadContext,\n setTokenCurrentStepId,\n applyTokenContextWrites,\n mergeTokenContext,\n setTokenWaitingForActivities,\n setTokenPendingTransition,\n touchToken,\n} from './execution-token'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('workflows')\n\n/**\n * Translate a resolved failure handling into the fields the executor acts on.\n * `fail` (the absent-config default) adds nothing, so untouched definitions\n * return the exact failure shape they returned before error routing existed.\n */\nfunction buildFailureRouting(\n handling: StepFailureHandling,\n failedStepId: string\n): Pick<\n TransitionExecutionResult,\n 'failedStepId' | 'errorRoute' | 'parkForAttention' | 'errorHandlerStepId'\n> {\n if (handling.kind === 'route' && typeof handling.transition.toStepId === 'string') {\n return {\n failedStepId,\n errorRoute: {\n transitionId: handling.transition.transitionId,\n toStepId: handling.transition.toStepId,\n },\n }\n }\n if (handling.kind === 'park') {\n return { failedStepId, parkForAttention: true }\n }\n if (handling.kind === 'handlerStep') {\n return { failedStepId, errorHandlerStepId: handling.stepId }\n }\n return {}\n}\n\n/**\n * Record that a dry run evaluated a transition's business rule but withheld its\n * ACTION arm, so the \"Would do\" report can say which side effect was skipped\n * rather than leaving the author to assume the rule did nothing.\n */\nasync function logSuppressedRuleActions(\n em: EntityManager,\n instance: WorkflowInstance,\n ruleId: string,\n phase: 'pre_transition' | 'post_transition'\n): Promise<void> {\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: DRY_RUN_EVENT_TYPES.businessRuleActionsSuppressed,\n eventData: { ruleId, phase },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n}\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface TransitionEvaluationContext {\n workflowContext: Record<string, any>\n userId?: string\n triggerData?: any\n transitionId?: string\n}\n\nexport interface TransitionEvaluationResult {\n isValid: boolean\n transition?: any\n reason?: string\n failedConditions?: string[]\n evaluationTime?: number\n}\n\nexport interface TransitionExecutionContext {\n workflowContext: Record<string, any>\n userId?: string\n triggerData?: any\n transitionId?: string\n}\n\nexport interface TransitionExecutionResult {\n success: boolean\n nextStepId?: string\n pausedForActivities?: boolean\n // The destination step entered a wait state (PAUSED / WAITING_FOR_ACTIVITIES)\n // after the transition into it executed \u2014 e.g. an INVOKE_AGENT AUTOMATED step\n // parked the instance on its agent signal. Distinct from `pausedForActivities`\n // (async activities ON THE TRANSITION, resumed via resumeWorkflowAfterActivities).\n paused?: boolean\n conditionsEvaluated?: {\n preConditions: boolean\n postConditions: boolean\n }\n activitiesExecuted?: activityExecutor.ActivityExecutionResult[]\n error?: string\n // Error routing (spec 5.9). Set only when the failure carries error handling\n // the executor must apply: `errorRoute` names the wired error transition to\n // follow, `parkForAttention` asks for a failure-queue park instead of a fail.\n // Absent on every legacy failure, which therefore keeps failing identically.\n failedStepId?: string\n errorRoute?: { transitionId?: string; toStepId: string }\n parkForAttention?: boolean\n // Definition-level handler step to jump to before failing (spec 5.9).\n errorHandlerStepId?: string\n}\n\nexport class TransitionError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'TransitionError'\n }\n}\n\n// ============================================================================\n// Main Transition Functions\n// ============================================================================\n\n/**\n * Evaluate if a transition from current step to target step is valid\n *\n * Checks:\n * - Transition exists in workflow definition\n * - Pre-conditions pass (if any business rules defined)\n * - Transition condition evaluates to true (if specified)\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param fromStepId - Current step ID\n * @param toStepId - Target step ID (optional - will auto-select if not provided)\n * @param context - Evaluation context\n * @returns Evaluation result with validity and reason\n */\nexport async function evaluateTransition(\n em: EntityManager,\n instance: WorkflowInstance,\n fromStepId: string,\n toStepId: string | undefined,\n context: TransitionEvaluationContext\n): Promise<TransitionEvaluationResult> {\n const startTime = Date.now()\n\n try {\n // Load workflow definition\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n return {\n isValid: false,\n reason: `Workflow definition not found: ${instance.definitionId}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n\n // Find transition\n const transitions = definition.definition.transitions || []\n let transition: any\n\n if (toStepId) {\n // Find specific transition\n transition = transitions.find(\n (candidate: { fromStepId?: unknown; toStepId?: unknown; transitionId?: unknown }) =>\n candidate.fromStepId === fromStepId\n && candidate.toStepId === toStepId\n && (context.transitionId === undefined || candidate.transitionId === context.transitionId)\n )\n\n if (!transition) {\n return {\n isValid: false,\n reason: `No transition found from ${fromStepId} to ${toStepId}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n } else {\n // Auto-select first valid transition (never a kinded route: error,\n // SLA-breach or agent-outcome)\n const availableTransitions = excludeNonNormalTransitions(transitions).filter(\n (t: any) => t.fromStepId === fromStepId\n )\n\n if (availableTransitions.length === 0) {\n return {\n isValid: false,\n reason: `No transitions available from step ${fromStepId}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n\n // Evaluate each transition to find first valid one\n for (const t of availableTransitions) {\n const result = await evaluateTransitionConditions(\n em,\n instance,\n t,\n context\n )\n\n if (result.isValid) {\n transition = t\n break\n }\n }\n\n if (!transition) {\n return {\n isValid: false,\n reason: `No valid transitions found from step ${fromStepId}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n }\n\n // Evaluate transition conditions (inline condition + business rules pre-conditions)\n const conditionResult = await evaluateTransitionConditions(\n em,\n instance,\n transition,\n context\n )\n\n return {\n ...conditionResult,\n transition,\n evaluationTime: Date.now() - startTime,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return {\n isValid: false,\n reason: `Transition evaluation error: ${errorMessage}`,\n evaluationTime: Date.now() - startTime,\n }\n }\n}\n\n/**\n * Find all valid transitions from current step\n *\n * This function evaluates both inline conditions AND preConditions (business rules)\n * to determine which transitions are truly valid. This is important for decision\n * branching where multiple transitions exist with different preConditions.\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param fromStepId - Current step ID\n * @param context - Evaluation context\n * @returns Array of evaluation results for all transitions, sorted by priority (desc)\n */\nexport async function findValidTransitions(\n em: EntityManager,\n instance: WorkflowInstance,\n fromStepId: string,\n context: TransitionEvaluationContext\n): Promise<TransitionEvaluationResult[]> {\n try {\n // Load workflow definition\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n return []\n }\n\n // Find all transitions from current step, sorted by priority (highest first).\n // Every kinded route is excluded \u2014 each is reachable only from its own\n // trigger (a step failure, a task's deadline passing, a resolved agent\n // disposition), never from normal routing.\n const transitions = excludeNonNormalTransitions(definition.definition.transitions || [])\n .filter((t: any) => t.fromStepId === fromStepId)\n .sort((a: any, b: any) => (b.priority || 0) - (a.priority || 0))\n\n // Evaluate each transition including preConditions\n const results: TransitionEvaluationResult[] = []\n\n for (const transition of transitions) {\n // First check inline condition\n const conditionResult = await evaluateTransition(\n em,\n instance,\n fromStepId,\n transition.toStepId,\n { ...context, transitionId: transition.transitionId },\n )\n\n if (!conditionResult.isValid) {\n results.push(conditionResult)\n continue\n }\n\n // Also evaluate preConditions if they exist\n const preConditions = transition.preConditions || []\n if (preConditions.length > 0) {\n const preConditionsResult = await evaluatePreConditions(\n em,\n instance,\n transition,\n context as TransitionExecutionContext,\n null\n )\n\n if (!preConditionsResult.allowed) {\n // Transition is invalid due to preConditions\n const failedRules = preConditionsResult.executedRules\n .filter((r) => !r.conditionResult)\n .map((r) => r.rule.ruleId || r.rule.ruleName)\n\n results.push({\n isValid: false,\n transition,\n reason: `Pre-conditions failed: ${failedRules.join(', ')}`,\n failedConditions: failedRules,\n })\n continue\n }\n }\n\n // Transition is valid (both condition and preConditions passed)\n results.push({\n ...conditionResult,\n transition,\n })\n }\n\n return results\n } catch (error) {\n logger.error('Error finding valid transitions', { err: error })\n return []\n }\n}\n\n/**\n * Execute a transition from one step to another\n *\n * This is the main entry point for transition execution. It:\n * 1. Validates the transition\n * 2. Evaluates pre-conditions\n * 3. Executes activities (if any)\n * 4. Updates workflow instance state (atomically with activity outputs)\n * 5. Evaluates post-conditions\n * 6. Logs transition event\n *\n * @param em - Entity manager\n * @param container - DI container (for activity execution)\n * @param instance - Workflow instance\n * @param fromStepId - Current step ID\n * @param toStepId - Target step ID\n * @param context - Execution context\n * @returns Execution result\n */\nexport async function executeTransition(\n em: EntityManager,\n container: AwilixContainer,\n instance: WorkflowInstance,\n fromStepId: string,\n toStepId: string,\n context: TransitionExecutionContext\n): Promise<TransitionExecutionResult> {\n // Public DI signature preserved: the root token adapts the instance, so the\n // single-token path is behaviourally unchanged.\n return executeTransitionForToken(em, container, rootToken(instance), fromStepId, toStepId, context)\n}\n\n/**\n * Token-aware transition execution. The root token wraps a WorkflowInstance\n * (legacy single-token path); a branch token wraps a WorkflowBranchInstance so\n * a parallel branch advances independently with its own context namespace,\n * status and pending transition.\n */\nexport async function executeTransitionForToken(\n em: EntityManager,\n container: AwilixContainer,\n token: ExecutionToken,\n fromStepId: string,\n toStepId: string,\n context: TransitionExecutionContext\n): Promise<TransitionExecutionResult> {\n const instance = token.instance\n const branch = token.kind === 'branch' ? token.branch : null\n const branchInstanceId = tokenBranchInstanceId(token)\n try {\n let eventBus: Pick<EventBus, 'emitEvent'> | null = null\n try {\n eventBus = container.resolve('eventBus') as EventBus\n } catch {\n eventBus = null\n }\n\n // First, evaluate if transition is valid\n const evaluation = await evaluateTransition(\n em,\n instance,\n fromStepId,\n toStepId,\n context\n )\n\n if (!evaluation.isValid) {\n return {\n success: false,\n error: evaluation.reason || 'Transition validation failed',\n }\n }\n\n const transition = evaluation.transition!\n\n // Evaluate pre-conditions (business rules)\n const preConditionsResult = await evaluatePreConditions(\n em,\n instance,\n transition,\n context,\n eventBus\n )\n\n if (!preConditionsResult.allowed) {\n // Build detailed failure information\n const failedRules = preConditionsResult.executedRules\n .filter((r) => !r.conditionResult)\n .map((r) => ({\n ruleId: r.rule.ruleId,\n ruleName: r.rule.ruleName,\n error: r.error,\n }))\n\n const failedRulesDetails = failedRules.length > 0\n ? failedRules.map(r => `${r.ruleId}: ${r.error || 'condition failed'}`).join('; ')\n : preConditionsResult.errors?.join(', ') || 'Unknown pre-condition failure'\n\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'TRANSITION_REJECTED',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n reason: 'Pre-conditions failed',\n failedRules: failedRulesDetails,\n failedRulesDetail: failedRules,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n success: false,\n error: `Pre-conditions failed: ${failedRulesDetails}`,\n conditionsEvaluated: {\n preConditions: false,\n postConditions: false,\n },\n }\n }\n\n // Execute activities (if any)\n let activityOutputs: Record<string, any> = {}\n const activityResults: activityExecutor.ActivityExecutionResult[] = []\n\n if (transition.activities && transition.activities.length > 0) {\n const definitionForInterpolation = await findDefinitionForInstance(em, instance)\n const activityContext: activityExecutor.ActivityContext = {\n workflowInstance: instance,\n workflowContext: {\n ...tokenReadContext(token),\n ...context.workflowContext,\n },\n branchInstanceId,\n userId: context.userId,\n interpolationMode: resolveDefinitionInterpolationMode(definitionForInterpolation?.definition),\n }\n\n // Execute all activities\n const results = await activityExecutor.executeActivities(\n em,\n container,\n transition.activities as ActivityDefinition[],\n activityContext,\n { queueLongInlineWaits: true }\n )\n\n activityResults.push(...results)\n\n // Check for failures\n const failedActivities = results.filter(r => !r.success)\n\n if (failedActivities.length > 0) {\n // A dry-run refusal stops the run AT that node (spec section 8.2). It is\n // not an activity failure \u2014 nothing was attempted \u2014 so neither\n // `continueOnActivityFailure` nor the step's error route/directive may\n // absorb it. Answering it before either is consulted is what keeps the\n // marker explicit instead of silently routed.\n const refusal = failedActivities.find((result) => result.dryRunRefused)\n if (refusal) {\n return {\n success: false,\n error: refusal.error ?? 'Dry run stopped',\n conditionsEvaluated: { preConditions: true, postConditions: false },\n }\n }\n\n const continueOnFailure = transition.continueOnActivityFailure ?? false\n\n // Log activity failures\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'ACTIVITY_FAILED',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n failedActivities: failedActivities.map(f => ({\n activityType: f.activityType,\n activityName: f.activityName,\n error: f.error,\n retryCount: f.retryCount,\n })),\n continueOnFailure,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n if (!continueOnFailure) {\n // Error routing (spec 5.9). The failing node here is the step the\n // route leaves, because the token cursor has not advanced yet.\n const failureHandling = resolveStepFailureHandling(\n definitionForInterpolation?.definition,\n fromStepId\n )\n\n if (failureHandling.kind === 'continue') {\n // `continueWithFallback` is the directive form of the legacy\n // `continueOnActivityFailure` flag: identical continue semantics,\n // plus the authored fallback value landing under the step id.\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n branchInstanceId,\n eventType: 'ERROR_DIRECTIVE_APPLIED',\n eventData: {\n stepId: fromStepId,\n directive: 'continueWithFallback',\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n hasFallbackValue: failureHandling.fallbackValue !== undefined,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n if (failureHandling.fallbackValue !== undefined) {\n activityOutputs[fromStepId] = failureHandling.fallbackValue\n }\n } else {\n return {\n success: false,\n error: `Activities failed: ${failedActivities.map(f => f.error).join(', ')}`,\n conditionsEvaluated: {\n preConditions: true,\n postConditions: false,\n },\n ...buildFailureRouting(failureHandling, fromStepId),\n }\n }\n }\n }\n\n // Collect activity outputs for context update. SET_VARIABLE outputs are\n // applied at their assignment paths in top-level context (preserving\n // sibling keys of any nested target); other outputs merge under the\n // activity name/type key.\n results.forEach(result => {\n if (result.success && result.output) {\n if (result.activityType === 'SET_VARIABLE' && isSetVariableOutput(result.output)) {\n const assignmentBase = {\n ...tokenReadContext(token),\n ...context.workflowContext,\n ...activityOutputs,\n }\n Object.assign(\n activityOutputs,\n buildSetVariableContextPatch(assignmentBase, result.output.assignments)\n )\n } else {\n const key = result.activityName || result.activityType\n activityOutputs[key] = result.output\n }\n }\n })\n }\n\n // Check if any activities are async - if so, pause before executing step\n const hasAsyncActivities = activityResults.some(r => r.async)\n\n if (hasAsyncActivities) {\n const pendingJobIds = activityResults\n .filter(a => a.async && a.jobId)\n .map(a => ({ activityId: a.activityId, jobId: a.jobId }))\n\n // Store pending transition state (per-token: branch or instance)\n setTokenPendingTransition(token, {\n toStepId,\n activityResults,\n timestamp: new Date(),\n })\n\n // Store activity outputs + pending-activity tracking in the token's own\n // context scope (instance.context for root, branch namespace for a branch)\n applyTokenContextWrites(token, context.workflowContext, activityOutputs)\n mergeTokenContext(token, { _pendingAsyncActivities: pendingJobIds })\n\n // Set status to waiting (branch-scoped when running inside a branch)\n setTokenWaitingForActivities(token)\n touchToken(token, new Date())\n await em.flush()\n\n // Log event\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n branchInstanceId,\n eventType: 'TRANSITION_PAUSED_FOR_ACTIVITIES',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId,\n pendingActivities: pendingJobIds,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Return WITHOUT executing step\n return {\n success: true,\n pausedForActivities: true,\n nextStepId: toStepId,\n conditionsEvaluated: {\n preConditions: true,\n postConditions: false, // Not evaluated yet\n },\n activitiesExecuted: activityResults,\n }\n }\n\n // Advance the token cursor and update context atomically (instance for the\n // root token; branch cursor + namespace for a branch token).\n setTokenCurrentStepId(token, toStepId)\n applyTokenContextWrites(token, context.workflowContext, activityOutputs)\n touchToken(token, new Date())\n\n await em.flush()\n\n // Execute the new step (this will create USER_TASK, handle END steps, etc.)\n const stepExecutionResult = await stepHandler.executeStep(\n em,\n instance,\n toStepId,\n {\n workflowContext: tokenReadContext(token),\n userId: context.userId,\n triggerData: context.triggerData,\n },\n container,\n branch\n )\n\n // Flush to database after step execution completes to make state visible to UI\n await em.flush()\n\n // Handle step execution failure. Error routing (spec 5.9) keys on the step\n // that failed \u2014 the cursor already advanced, so that is `toStepId`.\n if (stepExecutionResult.status === 'FAILED') {\n const definitionForFailure = await findDefinitionForInstance(em, instance)\n const failureHandling = resolveStepFailureHandling(definitionForFailure?.definition, toStepId)\n\n if (failureHandling.kind === 'continue') {\n // The step instance stays FAILED \u2014 its state machine is untouched. The\n // instance advances with the authored fallback value in context, and\n // the executor re-evaluates the failed step's outgoing routes.\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n branchInstanceId,\n eventType: 'ERROR_DIRECTIVE_APPLIED',\n eventData: {\n stepId: toStepId,\n directive: 'continueWithFallback',\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n error: stepExecutionResult.error,\n hasFallbackValue: failureHandling.fallbackValue !== undefined,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n if (failureHandling.fallbackValue !== undefined) {\n mergeTokenContext(token, { [toStepId]: failureHandling.fallbackValue })\n await em.flush()\n }\n } else {\n return {\n success: false,\n error: stepExecutionResult.error || 'Step execution failed',\n ...buildFailureRouting(failureHandling, toStepId),\n }\n }\n }\n\n // The transition INTO the step genuinely happened, but the step then parked\n // the instance (e.g. an INVOKE_AGENT AUTOMATED step enqueued an async agent\n // job and set status PAUSED). Surface that via `paused` so the executor loop\n // stops advancing instead of taking the next auto-transition.\n //\n // `FORK` is the ONE wait reason that is not an external park: `openFork`\n // leaves the instance FORKED with its branch rows already written, and the\n // branches are driven by `advanceBranches` on the executor loop's NEXT\n // iteration. Reporting it as paused makes the executor return before it\n // re-reads the instance, so the branches never advance and the instance\n // stays FORKED at the fork step forever.\n const stepPaused =\n stepExecutionResult.status === 'WAITING' && stepExecutionResult.waitReason !== 'FORK'\n\n // Evaluate post-conditions (business rules)\n const postConditionsResult = await evaluatePostConditions(\n em,\n instance,\n transition,\n context,\n eventBus\n )\n\n if (!postConditionsResult.allowed) {\n const failedRules = postConditionsResult.errors?.join(', ') || 'Unknown post-condition failure'\n\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'TRANSITION_POST_CONDITION_FAILED',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n reason: 'Post-conditions failed',\n failedRules,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Note: We don't roll back the transition on post-condition failure\n // Post-conditions are warnings, not blockers\n }\n\n // Log successful transition\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'TRANSITION_EXECUTED',\n eventData: {\n fromStepId,\n toStepId,\n transitionId: transition.transitionId || `${fromStepId}->${toStepId}`,\n transitionName: transition.transitionName,\n preConditionsPassed: true,\n postConditionsPassed: postConditionsResult.allowed,\n activitiesExecuted: activityResults.length,\n activitiesSucceeded: activityResults.filter(r => r.success).length,\n activitiesFailed: activityResults.filter(r => !r.success).length,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n success: true,\n nextStepId: toStepId,\n paused: stepPaused,\n conditionsEvaluated: {\n preConditions: true,\n postConditions: postConditionsResult.allowed,\n },\n activitiesExecuted: activityResults,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n await logTransitionEvent(em, {\n workflowInstanceId: instance.id,\n eventType: 'TRANSITION_FAILED',\n eventData: {\n fromStepId,\n toStepId,\n error: errorMessage,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n success: false,\n error: `Transition execution failed: ${errorMessage}`,\n }\n }\n}\n\n// ============================================================================\n// Condition Evaluation\n// ============================================================================\n\n/**\n * Evaluate transition conditions (inline condition expression)\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param transition - Transition definition\n * @param context - Evaluation context\n * @returns Evaluation result\n */\nasync function evaluateTransitionConditions(\n em: EntityManager,\n instance: WorkflowInstance,\n transition: any,\n context: TransitionEvaluationContext\n): Promise<TransitionEvaluationResult> {\n try {\n // If no condition specified, transition is always valid\n if (!transition.condition) {\n return {\n isValid: true,\n }\n }\n\n // Build data context for rule evaluation\n const data = {\n ...instance.context,\n ...context.workflowContext,\n triggerData: context.triggerData,\n }\n\n // Build evaluation context\n const evalContext: ruleEvaluator.RuleEvaluationContext = {\n entityType: 'workflow:transition',\n entityId: instance.id,\n user: context.userId ? { id: context.userId } : undefined,\n }\n\n // Evaluate condition using expression evaluator\n const result = await ruleEvaluator.evaluateConditions(\n transition.condition,\n data,\n evalContext\n )\n\n return {\n isValid: result,\n reason: result ? undefined : 'Transition condition evaluated to false',\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return {\n isValid: false,\n reason: `Condition evaluation error: ${errorMessage}`,\n }\n }\n}\n\n/**\n * Evaluate pre-conditions using business rules engine\n *\n * Pre-conditions are GUARD rules that must pass before transition can execute.\n * If any GUARD rule fails, the transition is blocked.\n *\n * If the transition defines specific preConditions with ruleIds, those are\n * executed directly via executeRuleByRuleId. Otherwise, falls back to\n * discovery-based execution via executeRules.\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param transition - Transition definition\n * @param context - Execution context\n * @returns Rule engine result\n */\nasync function evaluatePreConditions(\n em: EntityManager,\n instance: WorkflowInstance,\n transition: any,\n context: TransitionExecutionContext,\n eventBus: Pick<EventBus, 'emitEvent'> | null\n): Promise<ruleEngine.RuleEngineResult> {\n try {\n // Load workflow definition to get workflow ID\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n return {\n allowed: true,\n executedRules: [],\n totalExecutionTime: 0,\n }\n }\n\n // Check if transition has specific preConditions defined\n const preConditions = transition.preConditions || []\n\n // If no pre-conditions defined, allow transition\n if (preConditions.length === 0) {\n return {\n allowed: true,\n executedRules: [],\n totalExecutionTime: 0,\n }\n }\n\n // Execute each pre-condition rule directly by ruleId\n const dryRun = isDryRunInstance(instance)\n const startTime = Date.now()\n const executedRules: ruleEngine.RuleExecutionResult[] = []\n const errors: string[] = []\n let allowed = true\n\n for (const condition of preConditions) {\n const result = await ruleEngine.executeRuleByRuleId(em, {\n ruleId: condition.ruleId, // String identifier\n data: {\n workflowInstanceId: instance.id,\n workflowId: definition.workflowId,\n fromStepId: transition.fromStepId,\n toStepId: transition.toStepId,\n workflowContext: {\n ...instance.context,\n ...context.workflowContext,\n },\n triggerData: context.triggerData,\n },\n user: context.userId ? { id: context.userId } : undefined,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n executedBy: context.userId,\n entityType: `workflow:${definition.workflowId}:transition`,\n entityId: transition.transitionId || `${transition.fromStepId}->${transition.toStepId}`,\n eventType: 'pre_transition',\n // Dry run (spec section 8.2): the rule engine's own `dryRun` only\n // suppresses its execution LOG \u2014 a rule's success/failure ACTIONS run\n // regardless \u2014 so a side-effect-free run has to ask for `skipActions`.\n // The conditions still evaluate, so the run takes the routes it really\n // would; only the ACTION arm is withheld, and it is reported.\n skipActions: dryRun,\n })\n if (dryRun && result.actionsExecuted === null) {\n await logSuppressedRuleActions(em, instance, condition.ruleId, 'pre_transition')\n }\n\n // Create a compatible RuleExecutionResult for tracking\n // We don't have the full BusinessRule entity, but we can create a partial result\n const ruleResult: ruleEngine.RuleExecutionResult = {\n rule: {\n ruleId: result.ruleId,\n ruleName: result.ruleName,\n ruleType: 'GUARD',\n } as any,\n conditionResult: result.conditionResult,\n actionsExecuted: result.actionsExecuted,\n executionTime: result.executionTime,\n error: result.error,\n logId: result.logId,\n }\n executedRules.push(ruleResult)\n\n // Handle rule errors\n if (result.error) {\n // Rule not found, disabled, or other errors\n const isRequired = condition.required !== false // Default to required\n if (isRequired) {\n allowed = false\n errors.push(`Rule '${result.ruleId}': ${result.error}`)\n }\n continue\n }\n\n // If required and condition failed, block transition\n const isRequired = condition.required !== false // Default to required\n if (isRequired && !result.conditionResult) {\n allowed = false\n errors.push(`Pre-condition '${result.ruleName || result.ruleId}' failed`)\n }\n }\n\n return {\n allowed,\n executedRules,\n totalExecutionTime: Date.now() - startTime,\n errors: errors.length > 0 ? errors : undefined,\n }\n } catch (error) {\n logger.error('Error evaluating pre-conditions', { err: error })\n return {\n allowed: false,\n executedRules: [],\n totalExecutionTime: 0,\n errors: [error instanceof Error ? error.message : String(error)],\n }\n }\n}\n\n/**\n * Evaluate post-conditions using business rules engine\n *\n * Post-conditions are GUARD rules that should pass after transition executes.\n * Unlike pre-conditions, post-condition failures are logged but don't block the transition.\n *\n * If the transition defines specific postConditions with ruleIds, those are\n * executed directly via executeRuleByRuleId. Otherwise, returns allowed: true.\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param transition - Transition definition\n * @param context - Execution context\n * @returns Rule engine result\n */\nasync function evaluatePostConditions(\n em: EntityManager,\n instance: WorkflowInstance,\n transition: any,\n context: TransitionExecutionContext,\n eventBus: Pick<EventBus, 'emitEvent'> | null\n): Promise<ruleEngine.RuleEngineResult> {\n try {\n // Load workflow definition to get workflow ID\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n return {\n allowed: true,\n executedRules: [],\n totalExecutionTime: 0,\n }\n }\n\n // Check if transition has specific postConditions defined\n const postConditions = transition.postConditions || []\n\n // If no post-conditions defined, allow\n if (postConditions.length === 0) {\n return {\n allowed: true,\n executedRules: [],\n totalExecutionTime: 0,\n }\n }\n\n // Execute each post-condition rule directly by ruleId\n const dryRun = isDryRunInstance(instance)\n const startTime = Date.now()\n const executedRules: ruleEngine.RuleExecutionResult[] = []\n const errors: string[] = []\n let allowed = true\n\n for (const condition of postConditions) {\n const result = await ruleEngine.executeRuleByRuleId(em, {\n ruleId: condition.ruleId, // String identifier\n data: {\n workflowInstanceId: instance.id,\n workflowId: definition.workflowId,\n fromStepId: transition.fromStepId,\n toStepId: transition.toStepId,\n workflowContext: {\n ...instance.context,\n ...context.workflowContext,\n },\n triggerData: context.triggerData,\n },\n user: context.userId ? { id: context.userId } : undefined,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n executedBy: context.userId,\n entityType: `workflow:${definition.workflowId}:transition`,\n entityId: transition.transitionId || `${transition.fromStepId}->${transition.toStepId}`,\n eventType: 'post_transition',\n // Dry run (spec section 8.2): the rule engine's own `dryRun` only\n // suppresses its execution LOG \u2014 a rule's success/failure ACTIONS run\n // regardless \u2014 so a side-effect-free run has to ask for `skipActions`.\n // The conditions still evaluate, so the run takes the routes it really\n // would; only the ACTION arm is withheld, and it is reported.\n skipActions: dryRun,\n })\n if (dryRun && result.actionsExecuted === null) {\n await logSuppressedRuleActions(em, instance, condition.ruleId, 'post_transition')\n }\n\n // Create a compatible RuleExecutionResult for tracking\n const ruleResult: ruleEngine.RuleExecutionResult = {\n rule: {\n ruleId: result.ruleId,\n ruleName: result.ruleName,\n ruleType: 'GUARD',\n } as any,\n conditionResult: result.conditionResult,\n actionsExecuted: result.actionsExecuted,\n executionTime: result.executionTime,\n error: result.error,\n logId: result.logId,\n }\n executedRules.push(ruleResult)\n\n // Handle rule errors\n if (result.error) {\n errors.push(`Rule '${result.ruleId}': ${result.error}`)\n // Post-conditions don't block, but track the failure\n allowed = false\n continue\n }\n\n // Track condition failures (post-conditions are warnings, not blockers)\n if (!result.conditionResult) {\n allowed = false\n errors.push(`Post-condition '${result.ruleName || result.ruleId}' failed`)\n }\n }\n\n return {\n allowed,\n executedRules,\n totalExecutionTime: Date.now() - startTime,\n errors: errors.length > 0 ? errors : undefined,\n }\n } catch (error) {\n logger.error('Error evaluating post-conditions', { err: error })\n return {\n allowed: false,\n executedRules: [],\n totalExecutionTime: 0,\n errors: [error instanceof Error ? error.message : String(error)],\n }\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Log transition-related event to event sourcing table\n */\nasync function logTransitionEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n branchInstanceId?: string | null\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": ["isRequired"]
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { buildTaskQuickCompleteAction, TASK_COMPLETE_REQUIRED_FEATURE } from "./lib/task-quick-action.js";
1
2
  const taskDeepLink = "/backend/tasks/{sourceEntityId}";
2
3
  const viewTaskAction = {
3
4
  id: "view",
@@ -17,6 +18,9 @@ const notificationTypes = [
17
18
  icon: "clipboard-list",
18
19
  severity: "info",
19
20
  actions: [viewTaskAction],
21
+ optionalActions: [
22
+ { ...buildTaskQuickCompleteAction(), requiredFeatures: [TASK_COMPLETE_REQUIRED_FEATURE] }
23
+ ],
20
24
  linkHref: taskDeepLink,
21
25
  expiresAfterHours: 168
22
26
  // 7 days
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/workflows/notifications.ts"],
4
- "sourcesContent": ["import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'\n\nconst taskDeepLink = '/backend/tasks/{sourceEntityId}'\n\nconst viewTaskAction = {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline' as const,\n href: taskDeepLink,\n icon: 'external-link',\n}\n\nexport const notificationTypes: NotificationTypeDefinition[] = [\n {\n type: 'workflows.task.assigned',\n // Ships without push \u2014 operators re-enable it per type from the Notification Delivery settings.\n channels: ['in_app', 'email'],\n module: 'workflows',\n titleKey: 'workflows.notifications.task.assigned.title',\n bodyKey: 'workflows.notifications.task.assigned.body',\n icon: 'clipboard-list',\n severity: 'info',\n actions: [viewTaskAction],\n linkHref: taskDeepLink,\n expiresAfterHours: 168, // 7 days\n },\n {\n type: 'workflows.task.reminder_due',\n // Same delivery as `workflows.task.assigned`: an SLA reminder or breach is\n // about the task the operator already gets assignment mail for.\n channels: ['in_app', 'email'],\n module: 'workflows',\n titleKey: 'workflows.notifications.task.reminderDue.title',\n bodyKey: 'workflows.notifications.task.reminderDue.body',\n icon: 'alarm-clock',\n severity: 'warning',\n actions: [viewTaskAction],\n linkHref: taskDeepLink,\n expiresAfterHours: 168,\n },\n {\n type: 'workflows.task.deadline_breached',\n // Same delivery as `workflows.task.assigned`: an SLA reminder or breach is\n // about the task the operator already gets assignment mail for.\n channels: ['in_app', 'email'],\n module: 'workflows',\n titleKey: 'workflows.notifications.task.deadlineBreached.title',\n bodyKey: 'workflows.notifications.task.deadlineBreached.body',\n icon: 'alert-triangle',\n severity: 'error',\n actions: [viewTaskAction],\n linkHref: taskDeepLink,\n expiresAfterHours: 168,\n },\n]\n\nexport default notificationTypes\n"],
5
- "mappings": "AAEA,MAAM,eAAe;AAErB,MAAM,iBAAiB;AAAA,EACrB,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AACR;AAEO,MAAM,oBAAkD;AAAA,EAC7D;AAAA,IACE,MAAM;AAAA;AAAA,IAEN,UAAU,CAAC,UAAU,OAAO;AAAA,IAC5B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS,CAAC,cAAc;AAAA,IACxB,UAAU;AAAA,IACV,mBAAmB;AAAA;AAAA,EACrB;AAAA,EACA;AAAA,IACE,MAAM;AAAA;AAAA;AAAA,IAGN,UAAU,CAAC,UAAU,OAAO;AAAA,IAC5B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS,CAAC,cAAc;AAAA,IACxB,UAAU;AAAA,IACV,mBAAmB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,MAAM;AAAA;AAAA;AAAA,IAGN,UAAU,CAAC,UAAU,OAAO;AAAA,IAC5B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS,CAAC,cAAc;AAAA,IACxB,UAAU;AAAA,IACV,mBAAmB;AAAA,EACrB;AACF;AAEA,IAAO,wBAAQ;",
4
+ "sourcesContent": ["import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'\nimport { buildTaskQuickCompleteAction, TASK_COMPLETE_REQUIRED_FEATURE } from './lib/task-quick-action'\n\nconst taskDeepLink = '/backend/tasks/{sourceEntityId}'\n\nconst viewTaskAction = {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline' as const,\n href: taskDeepLink,\n icon: 'external-link',\n}\n\nexport const notificationTypes: NotificationTypeDefinition[] = [\n {\n type: 'workflows.task.assigned',\n // Ships without push \u2014 operators re-enable it per type from the Notification Delivery settings.\n channels: ['in_app', 'email'],\n module: 'workflows',\n titleKey: 'workflows.notifications.task.assigned.title',\n bodyKey: 'workflows.notifications.task.assigned.body',\n icon: 'clipboard-list',\n severity: 'info',\n actions: [viewTaskAction],\n optionalActions: [\n { ...buildTaskQuickCompleteAction(), requiredFeatures: [TASK_COMPLETE_REQUIRED_FEATURE] },\n ],\n linkHref: taskDeepLink,\n expiresAfterHours: 168, // 7 days\n },\n {\n type: 'workflows.task.reminder_due',\n // Same delivery as `workflows.task.assigned`: an SLA reminder or breach is\n // about the task the operator already gets assignment mail for.\n channels: ['in_app', 'email'],\n module: 'workflows',\n titleKey: 'workflows.notifications.task.reminderDue.title',\n bodyKey: 'workflows.notifications.task.reminderDue.body',\n icon: 'alarm-clock',\n severity: 'warning',\n actions: [viewTaskAction],\n linkHref: taskDeepLink,\n expiresAfterHours: 168,\n },\n {\n type: 'workflows.task.deadline_breached',\n // Same delivery as `workflows.task.assigned`: an SLA reminder or breach is\n // about the task the operator already gets assignment mail for.\n channels: ['in_app', 'email'],\n module: 'workflows',\n titleKey: 'workflows.notifications.task.deadlineBreached.title',\n bodyKey: 'workflows.notifications.task.deadlineBreached.body',\n icon: 'alert-triangle',\n severity: 'error',\n actions: [viewTaskAction],\n linkHref: taskDeepLink,\n expiresAfterHours: 168,\n },\n]\n\nexport default notificationTypes\n"],
5
+ "mappings": "AACA,SAAS,8BAA8B,sCAAsC;AAE7E,MAAM,eAAe;AAErB,MAAM,iBAAiB;AAAA,EACrB,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AACR;AAEO,MAAM,oBAAkD;AAAA,EAC7D;AAAA,IACE,MAAM;AAAA;AAAA,IAEN,UAAU,CAAC,UAAU,OAAO;AAAA,IAC5B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS,CAAC,cAAc;AAAA,IACxB,iBAAiB;AAAA,MACf,EAAE,GAAG,6BAA6B,GAAG,kBAAkB,CAAC,8BAA8B,EAAE;AAAA,IAC1F;AAAA,IACA,UAAU;AAAA,IACV,mBAAmB;AAAA;AAAA,EACrB;AAAA,EACA;AAAA,IACE,MAAM;AAAA;AAAA;AAAA,IAGN,UAAU,CAAC,UAAU,OAAO;AAAA,IAC5B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS,CAAC,cAAc;AAAA,IACxB,UAAU;AAAA,IACV,mBAAmB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,MAAM;AAAA;AAAA;AAAA,IAGN,UAAU,CAAC,UAAU,OAAO;AAAA,IAC5B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS,CAAC,cAAc;AAAA,IACxB,UAAU;AAAA,IACV,mBAAmB;AAAA,EACrB;AACF;AAEA,IAAO,wBAAQ;",
6
6
  "names": []
7
7
  }
@@ -4,6 +4,7 @@ export const title = "title";
4
4
  export const description = "description";
5
5
  export const storage_driver = "storage_driver";
6
6
  export const config_json = "config_json";
7
+ export const access_resolver_requirements = "access_resolver_requirements";
7
8
  export const is_public = "is_public";
8
9
  export const requires_ocr = "requires_ocr";
9
10
  export const ocr_model = "ocr_model";
@@ -98,6 +98,7 @@ export const entityFieldsRegistry: Record<string, Record<string, string>> = {
98
98
  "description": "description",
99
99
  "storage_driver": "storage_driver",
100
100
  "config_json": "config_json",
101
+ "access_resolver_requirements": "access_resolver_requirements",
101
102
  "is_public": "is_public",
102
103
  "requires_ocr": "requires_ocr",
103
104
  "ocr_model": "ocr_model",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.10.1-develop.7741.1.61a2652671",
3
+ "version": "0.10.1-develop.7749.1.ea3edf75e3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -266,16 +266,16 @@
266
266
  "zod": "^4.4.3"
267
267
  },
268
268
  "peerDependencies": {
269
- "@open-mercato/ai-assistant": "0.10.1-develop.7741.1.61a2652671",
270
- "@open-mercato/shared": "0.10.1-develop.7741.1.61a2652671",
271
- "@open-mercato/ui": "0.10.1-develop.7741.1.61a2652671",
269
+ "@open-mercato/ai-assistant": "0.10.1-develop.7749.1.ea3edf75e3",
270
+ "@open-mercato/shared": "0.10.1-develop.7749.1.ea3edf75e3",
271
+ "@open-mercato/ui": "0.10.1-develop.7749.1.ea3edf75e3",
272
272
  "react": "^19.0.0",
273
273
  "react-dom": "^19.0.0"
274
274
  },
275
275
  "devDependencies": {
276
- "@open-mercato/ai-assistant": "0.10.1-develop.7741.1.61a2652671",
277
- "@open-mercato/shared": "0.10.1-develop.7741.1.61a2652671",
278
- "@open-mercato/ui": "0.10.1-develop.7741.1.61a2652671",
276
+ "@open-mercato/ai-assistant": "0.10.1-develop.7749.1.ea3edf75e3",
277
+ "@open-mercato/shared": "0.10.1-develop.7749.1.ea3edf75e3",
278
+ "@open-mercato/ui": "0.10.1-develop.7749.1.ea3edf75e3",
279
279
  "@testing-library/dom": "^10.4.1",
280
280
  "@testing-library/jest-dom": "^7.0.1",
281
281
  "@testing-library/react": "^16.3.3",
@@ -0,0 +1,141 @@
1
+ import { randomUUID } from 'node:crypto'
2
+ import { expect, type APIRequestContext } from '@playwright/test'
3
+ import { apiRequest, clearAuthTokenCache, getAuthToken, withCredentialIsolatedRequest } from './api'
4
+ import { createRoleFixture, createUserFixture, deleteRoleIfExists, deleteUserIfExists, setRoleAclFeatures } from './authFixtures'
5
+ import { deleteAttachmentIfExists, uploadAttachmentFixture } from './attachmentsFixtures'
6
+ import { expectId, getTokenContext, readJsonSafe } from './generalFixtures'
7
+
8
+ export const OWNER_ACCESS_FEATURES = [
9
+ 'documents.view', 'documents.edit', 'documents.create', 'documents.share', 'documents.delete',
10
+ 'attachments.view', 'attachments.manage',
11
+ ]
12
+ export const OWNER_ACCESS_PNG = Buffer.from(
13
+ 'iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAEUlEQVQImWMwSpn2H4QZYAwASOwIrfM45AsAAAAASUVORK5CYII=',
14
+ 'base64',
15
+ )
16
+
17
+ export type OwnerAccessItem = { id: string; tags?: string[]; assignments?: Array<{ type: string; id: string; label?: string | null }> }
18
+ export type OwnerAccessList = { items: OwnerAccessItem[]; total?: number; availableTags?: string[] }
19
+ export type OwnerAccessActor = { id: string; token: string; refreshToken: () => Promise<string> }
20
+ type Assignment = { type: string; id: string; label?: string }
21
+ type Share = { id: string; updatedAt: string }
22
+
23
+ export async function ownerPolicyRequest<Body = { error?: string }>(
24
+ method: string, path: string, token: string, data?: unknown, scheme: 'Bearer' | 'ApiKey' = 'Bearer',
25
+ ) {
26
+ return withCredentialIsolatedRequest(async (isolated) => {
27
+ const response = await isolated.fetch(path, {
28
+ method, headers: { Authorization: `${scheme} ${token}`, 'Content-Type': 'application/json' },
29
+ ...(data === undefined ? {} : { data }),
30
+ })
31
+ return {
32
+ status: response.status(), headers: response.headers(),
33
+ body: await readJsonSafe<Body>(response), bytes: await response.body(),
34
+ }
35
+ })
36
+ }
37
+
38
+ export type AttachmentOwnerFixture = {
39
+ admin: string
40
+ roleId: string
41
+ prefix: string
42
+ recipient: OwnerAccessActor
43
+ unshared: OwnerAccessActor
44
+ visible: string
45
+ hidden: string
46
+ destination: string
47
+ document: (label: string, token?: string) => Promise<string>
48
+ share: (documentId: string, permission: 'viewer' | 'editor', principalType?: 'user' | 'role', principalId?: string, token?: string) => Promise<Share>
49
+ upload: (documentId: string, label: string, options?: { entityId?: string; assignments?: Assignment[]; tags?: string[] }) => Promise<string>
50
+ uploadProxy: (documentId: string) => Promise<{ attachmentId: string; url: string }>
51
+ uploadPublicImage: (documentId: string) => Promise<string>
52
+ key: () => Promise<{ id: string; secret: string }>
53
+ }
54
+
55
+ export async function withAttachmentOwnerFixture(
56
+ request: APIRequestContext, run: (fixture: AttachmentOwnerFixture) => Promise<void>,
57
+ ): Promise<void> {
58
+ const admin = await getAuthToken(request, 'admin')
59
+ const scope = getTokenContext(admin)
60
+ const prefix = `attachment-owner-${randomUUID()}`
61
+ const users: string[] = [], documents: string[] = [], attachments: string[] = [], keys: string[] = []
62
+ let roleId: string | null = null
63
+ try {
64
+ roleId = await createRoleFixture(request, admin, { name: prefix, tenantId: scope.tenantId })
65
+ await setRoleAclFeatures(request, admin, { roleId, features: OWNER_ACCESS_FEATURES })
66
+ const actor = async (label: string): Promise<OwnerAccessActor> => {
67
+ const email = `${prefix}-${label}@example.com`, password = `Owner1!${randomUUID()}`
68
+ const id = await createUserFixture(request, admin, {
69
+ email, password, organizationId: scope.organizationId, roles: [roleId!], name: label,
70
+ })
71
+ users.push(id)
72
+ return { id, token: await getAuthToken(request, email, password), refreshToken: async () => {
73
+ clearAuthTokenCache()
74
+ return getAuthToken(request, email, password)
75
+ } }
76
+ }
77
+ const recipient = await actor('recipient'), unshared = await actor('unshared')
78
+ const document = async (label: string, token = admin): Promise<string> => {
79
+ const response = await apiRequest(request, 'POST', '/api/documents', { token, data: { title: `${prefix}-${label}`, folderId: null } })
80
+ expect(response.status()).toBe(201)
81
+ const body = await readJsonSafe<{ id: string }>(response)
82
+ const id = expectId(body?.id, 'Fixture response must include id'); documents.push(id); return id
83
+ }
84
+ const share: AttachmentOwnerFixture['share'] = async (documentId, permission, principalType = 'user', principalId = recipient.id, token = admin) => {
85
+ const response = await apiRequest(request, 'POST', `/api/documents/${documentId}/shares`, {
86
+ token, data: { principalType, principalId, permission },
87
+ })
88
+ expect(response.status()).toBe(201)
89
+ const body = await readJsonSafe<Share>(response)
90
+ expect(body?.updatedAt).toBeTruthy()
91
+ return { id: expectId(body?.id, 'Fixture response must include id'), updatedAt: body!.updatedAt }
92
+ }
93
+ const upload: AttachmentOwnerFixture['upload'] = async (documentId, label, options = {}) => {
94
+ const file = await uploadAttachmentFixture(request, admin, {
95
+ entityId: options.entityId ?? 'documents:document', recordId: documentId,
96
+ fileName: `${prefix}-${label}.txt`, mimeType: 'text/plain', buffer: Buffer.from(`${prefix}-${label}-content`),
97
+ assignments: options.assignments, tags: options.tags,
98
+ })
99
+ attachments.push(file.id); return file.id
100
+ }
101
+ const uploadProxy = async (documentId: string) => {
102
+ const route = `/api/documents/${documentId}/attachments`
103
+ const response = await request.fetch(process.env.BASE_URL?.trim() ? `${process.env.BASE_URL.trim()}${route}` : route, {
104
+ method: 'POST', headers: { Authorization: `Bearer ${admin}` },
105
+ multipart: { file: { name: `${prefix}-proxy.png`, mimeType: 'image/png', buffer: OWNER_ACCESS_PNG } },
106
+ })
107
+ expect(response.status()).toBe(201)
108
+ const body = await readJsonSafe<{ attachmentId: string; url: string }>(response)
109
+ return { attachmentId: expectId(body?.attachmentId, 'Document upload must include attachmentId'), url: String(body?.url) }
110
+ }
111
+ const key = async () => {
112
+ const response = await apiRequest(request, 'POST', '/api/api_keys/keys', {
113
+ token: admin, data: { name: prefix, tenantId: scope.tenantId, organizationId: scope.organizationId, roles: [roleId] },
114
+ })
115
+ expect(response.status()).toBe(201)
116
+ const body = await readJsonSafe<{ id: string; secret: string }>(response)
117
+ const id = expectId(body?.id, 'Fixture response must include id'); keys.push(id)
118
+ expect(body?.secret).toMatch(/^omk_/)
119
+ return { id, secret: body!.secret }
120
+ }
121
+ const uploadPublicImage = async (documentId: string) => {
122
+ const file = await uploadAttachmentFixture(request, admin, {
123
+ entityId: 'catalog:catalog_product', recordId: randomUUID(),
124
+ fileName: `${prefix}-public.png`, mimeType: 'image/png', buffer: OWNER_ACCESS_PNG,
125
+ assignments: [{ type: 'documents:document', id: documentId }],
126
+ })
127
+ attachments.push(file.id)
128
+ expect(file.partitionCode).toBe('productsMedia')
129
+ return file.id
130
+ }
131
+ const visible = await document('visible'), hidden = await document('hidden'), destination = await document('destination')
132
+ await share(visible, 'viewer')
133
+ await run({ admin, roleId, prefix, recipient, unshared, visible, hidden, destination, document, share, upload, uploadProxy, uploadPublicImage, key })
134
+ } finally {
135
+ for (const id of keys) await apiRequest(request, 'DELETE', `/api/api_keys/keys?id=${id}`, { token: admin }).catch(() => undefined)
136
+ for (const id of attachments) await deleteAttachmentIfExists(request, admin, id)
137
+ for (const id of documents.reverse()) await apiRequest(request, 'DELETE', `/api/documents/${id}`, { token: admin }).catch(() => undefined)
138
+ for (const id of users) await deleteUserIfExists(request, admin, id)
139
+ await deleteRoleIfExists(request, admin, roleId)
140
+ }
141
+ }
@@ -1,3 +1,5 @@
1
+ import { createAttachmentAccessContext, evaluateAttachmentAccess } from '../../../lib/access-runner'
2
+ import { attachmentAccessErrorResponse } from '../../../lib/access-errors'
1
3
  import { NextRequest, NextResponse } from "next/server";
2
4
  import type { OpenApiRouteDoc } from "@open-mercato/shared/lib/openapi";
3
5
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
@@ -69,11 +71,14 @@ export async function GET(
69
71
  );
70
72
  }
71
73
 
72
- const access = checkAttachmentAccess(scopedAuth, attachment, partition);
73
- if (!access.ok) {
74
- const message = access.status === 401 ? "Unauthorized" : "Forbidden";
75
- return NextResponse.json({ error: message }, { status: access.status });
76
- }
74
+ const url = new URL(req.url);
75
+ const forceDownload = url.searchParams.get("download") === "1";
76
+ const renderInline = !forceDownload && canRenderInlineAttachment(attachment.mimeType);
77
+ const access = await evaluateAttachmentAccess({
78
+ auth: scopedAuth, attachment, partition, action: renderInline ? 'render' : 'read',
79
+ context: createAttachmentAccessContext(container),
80
+ });
81
+ if (!access.ok) return attachmentAccessErrorResponse(access);
77
82
 
78
83
  const driver = await storageDriverFactory.resolveForPartition(attachment.partitionCode, {
79
84
  tenantId: attachment.tenantId ?? '',
@@ -87,11 +92,8 @@ export async function GET(
87
92
  return NextResponse.json({ error: "File not available" }, { status: 404 });
88
93
  }
89
94
 
90
- const url = new URL(req.url);
91
- const forceDownload = url.searchParams.get("download") === "1";
92
- const renderInline = !forceDownload && canRenderInlineAttachment(attachment.mimeType);
93
95
  const headers: Record<string, string> = {
94
- "Cache-Control": checkAttachmentAccess(null, attachment, partition).ok
96
+ "Cache-Control": access.protected ? "private, no-store" : checkAttachmentAccess(null, attachment, partition).ok
95
97
  ? "public, max-age=86400"
96
98
  : "private, max-age=60",
97
99
  "Content-Security-Policy": "default-src 'none'; sandbox",
@@ -155,6 +157,7 @@ export const openApi: OpenApiRouteDoc = {
155
157
  description: "Partition misconfigured",
156
158
  schema: attachmentErrorSchema,
157
159
  },
160
+ { status: 504, description: 'Owner authorization timed out', schema: attachmentErrorSchema },
158
161
  ],
159
162
  },
160
163
  },
@@ -1,3 +1,5 @@
1
+ import { createAttachmentAccessContext, evaluateAttachmentAccess } from '../../../../lib/access-runner'
2
+ import { attachmentAccessErrorResponse } from '../../../../lib/access-errors'
1
3
  import { NextRequest, NextResponse } from 'next/server'
2
4
  import sharp, { type ResizeOptions } from 'sharp'
3
5
  import { z } from 'zod'
@@ -71,17 +73,16 @@ export async function GET(
71
73
  if (!attachment) {
72
74
  return NextResponse.json({ error: 'Not found' }, { status: 404 })
73
75
  }
74
- if (!canRenderInlineAttachment(attachment.mimeType)) {
75
- return NextResponse.json({ error: 'Unsupported media type' }, { status: 400 })
76
- }
77
76
  const partition = await em.findOne(AttachmentPartition, { code: attachment.partitionCode })
78
77
  if (!partition) {
79
78
  return NextResponse.json({ error: 'Partition misconfigured' }, { status: 500 })
80
79
  }
81
- const access = checkAttachmentAccess(scopedAuth, attachment, partition)
82
- if (!access.ok) {
83
- const message = access.status === 401 ? 'Unauthorized' : 'Forbidden'
84
- return NextResponse.json({ error: message }, { status: access.status })
80
+ const access = await evaluateAttachmentAccess({
81
+ auth: scopedAuth, attachment, partition, action: 'render', context: createAttachmentAccessContext(container),
82
+ })
83
+ if (!access.ok) return attachmentAccessErrorResponse(access)
84
+ if (!canRenderInlineAttachment(attachment.mimeType)) {
85
+ return NextResponse.json({ error: 'Unsupported media type' }, { status: 400 })
85
86
  }
86
87
 
87
88
  const driver = await storageDriverFactory.resolveForPartition(attachment.partitionCode, {
@@ -136,7 +137,7 @@ export async function GET(
136
137
  return new NextResponse(responseBody, {
137
138
  headers: {
138
139
  'Content-Type': attachment.mimeType || 'image/jpeg',
139
- 'Cache-Control': checkAttachmentAccess(null, attachment, partition).ok ? 'public, max-age=3600' : 'private, max-age=60',
140
+ 'Cache-Control': access.protected ? 'private, no-store' : checkAttachmentAccess(null, attachment, partition).ok ? 'public, max-age=3600' : 'private, max-age=60',
140
141
  'X-Content-Type-Options': 'nosniff',
141
142
  },
142
143
  })
@@ -166,6 +167,7 @@ export const openApi: OpenApiRouteDoc = {
166
167
  { status: 403, description: 'Forbidden - insufficient permissions', schema: attachmentErrorSchema },
167
168
  { status: 404, description: 'Image not found', schema: attachmentErrorSchema },
168
169
  { status: 500, description: 'Partition misconfigured or image rendering failed', schema: attachmentErrorSchema },
170
+ { status: 504, description: 'Owner authorization timed out', schema: attachmentErrorSchema },
169
171
  ],
170
172
  },
171
173
  },