@open-mercato/core 0.6.6-develop.6551.1.1511bfa890 → 0.6.7-develop.6552.1.5208a19369
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/workflows/frontend/checkout-demo/page.js +8 -1
- package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +1 -1
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/find-definition.js +18 -2
- package/dist/modules/workflows/lib/find-definition.js.map +2 -2
- package/dist/modules/workflows/lib/signal-handler.js +3 -2
- package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
- package/dist/modules/workflows/lib/step-handler.js +4 -8
- package/dist/modules/workflows/lib/step-handler.js.map +2 -2
- package/dist/modules/workflows/lib/task-handler.js +3 -5
- package/dist/modules/workflows/lib/task-handler.js.map +2 -2
- package/dist/modules/workflows/lib/timer-handler.js +2 -1
- package/dist/modules/workflows/lib/timer-handler.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +5 -13
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-executor.js +11 -9
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/dist/modules/workflows/migrations/Migration20260715120000.js +68 -0
- package/dist/modules/workflows/migrations/Migration20260715120000.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260716120000.js +24 -0
- package/dist/modules/workflows/migrations/Migration20260716120000.js.map +7 -0
- package/dist/modules/workflows/workflows.js +2 -0
- package/dist/modules/workflows/workflows.js.map +2 -2
- package/package.json +8 -8
- package/src/modules/workflows/frontend/checkout-demo/page.tsx +12 -1
- package/src/modules/workflows/lib/activity-executor.ts +22 -2
- package/src/modules/workflows/lib/find-definition.ts +53 -1
- package/src/modules/workflows/lib/signal-handler.ts +5 -4
- package/src/modules/workflows/lib/step-handler.ts +4 -8
- package/src/modules/workflows/lib/task-handler.ts +2 -4
- package/src/modules/workflows/lib/timer-handler.ts +3 -2
- package/src/modules/workflows/lib/transition-handler.ts +5 -13
- package/src/modules/workflows/lib/workflow-executor.ts +11 -8
- package/src/modules/workflows/migrations/Migration20260715120000.ts +114 -0
- package/src/modules/workflows/migrations/Migration20260716120000.ts +57 -0
- package/src/modules/workflows/workflows.ts +2 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/workflows/migrations/Migration20260716120000.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations'\n\nconst CODE_WORKFLOW_ID = 'workflows.checkout-demo'\n\n/**\n * Retires the persisted checkout-demo seed rows so runtime resolves the\n * maintained CODE definition instead (issue #4211).\n *\n * Background: `workflows.checkout-demo` is now code-defined\n * (`packages/core/src/modules/workflows/workflows.ts`), and only the code\n * definition forwards the cart's line items + shipping/tax adjustments to\n * `POST /api/sales/orders`. But existing tenants still carry a persisted\n * `workflow_definitions` row from the old seed \u2014 originally seeded under ids\n * like `checkout_simple_v1` / names like `Simple Checkout Flow` and later\n * `Checkout with Payment Webhook`, then renamed to `workflows.checkout-demo`\n * by Migration20260428102318. Its `create_order` body never sent `lines`, and\n * because `findWorkflowDefinition` resolves the DB row first, that stale row\n * SHADOWS the code definition \u2014 so orders come out with zero line items and\n * $0.00 totals. Patching the persisted body is unreliable: the seed's name and\n * body shape vary across historical versions, so a fingerprint on either misses\n * whole cohorts of tenants (the defect QA hit).\n *\n * Instead, soft-delete the seeded rows. This is body/name-agnostic, so it fixes\n * every historical seed variant:\n * - New instances: `findWorkflowDefinition` filters `deleted_at IS NULL`, so\n * the retired row is skipped and the code definition is used (correct body).\n * - In-flight instances: `findDefinitionForInstance` loads the definition by\n * `id` with no soft-delete filter (WorkflowDefinition has no global filter),\n * so instances that reference the row keep resolving and advancing.\n *\n * Scope is limited to `code_workflow_id IS NULL` rows \u2014 user customizations of\n * the code definition set `code_workflow_id` and MUST NOT be retired. The\n * `deleted_at IS NULL` guard makes the migration idempotent.\n */\nexport function buildRetireSeededCheckoutDemoSql(): string {\n return `\n update \"workflow_definitions\" as \"wf\"\n set \"deleted_at\" = current_timestamp,\n \"updated_at\" = current_timestamp\n where \"wf\".\"workflow_id\" = '${CODE_WORKFLOW_ID}'\n and \"wf\".\"code_workflow_id\" is null\n and \"wf\".\"deleted_at\" is null;\n `.trim()\n}\n\nexport class Migration20260716120000 extends Migration {\n override async up(): Promise<void> {\n this.addSql(buildRetireSeededCheckoutDemoSql())\n }\n\n override async down(): Promise<void> {\n // Forward-only. Restoring `deleted_at = null` would re-shadow the maintained\n // code definition with the stale seed body and re-introduce zero-line orders\n // (#4211). The code definition supersedes the seed, so there is nothing to\n // restore.\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAE1B,MAAM,mBAAmB;AAgClB,SAAS,mCAA2C;AACzD,SAAO;AAAA;AAAA;AAAA;AAAA,kCAIyB,gBAAgB;AAAA;AAAA;AAAA,IAG9C,KAAK;AACT;AAEO,MAAM,gCAAgC,UAAU;AAAA,EACrD,MAAe,KAAoB;AACjC,SAAK,OAAO,iCAAiC,CAAC;AAAA,EAChD;AAAA,EAEA,MAAe,OAAsB;AAAA,EAKrC;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -222,6 +222,8 @@ const checkoutDemo = defineWorkflow({
|
|
|
222
222
|
customerEntityId: "{{context.customer.id}}",
|
|
223
223
|
currencyCode: "{{context.cart.currency}}",
|
|
224
224
|
placedAt: "{{now}}",
|
|
225
|
+
lines: "{{context.cart.orderLines}}",
|
|
226
|
+
adjustments: "{{context.cart.orderAdjustments}}",
|
|
225
227
|
grandTotalGrossAmount: "{{context.cart.total}}"
|
|
226
228
|
},
|
|
227
229
|
validateTenantMatch: true
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/workflows/workflows.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Workflows Module \u2014 Code-Based Workflow Definitions\n *\n * Demo workflows shipped with the workflows module.\n * These are auto-discovered by the generator and loaded into the in-memory registry.\n */\n\nimport { defineWorkflow, createWorkflowsModuleConfig } from '@open-mercato/shared/modules/workflows'\n\n// ============================================================================\n// Simple Approval Workflow\n// ============================================================================\n\nconst simpleApproval = defineWorkflow({\n workflowId: 'workflows.simple-approval',\n workflowName: 'Simple Approval Workflow',\n description: 'Basic three-step approval workflow: Start \u2192 Approval \u2192 End',\n metadata: { category: 'Testing', tags: ['test', 'approval', 'simple'], icon: 'check-circle' },\n steps: [\n { stepId: 'start', stepName: 'Start', stepType: 'START', description: 'Initialize workflow' },\n {\n stepId: 'approval',\n stepName: 'Pending Approval',\n stepType: 'USER_TASK',\n description: 'Approve or reject request',\n userTaskConfig: { assignedTo: 'approver', slaDuration: 'PT1H' },\n },\n { stepId: 'end', stepName: 'Complete', stepType: 'END', description: 'Workflow complete' },\n ] as const,\n transitions: [\n {\n transitionId: 'start_to_approval',\n transitionName: 'Submit for Approval',\n fromStepId: 'start',\n toStepId: 'approval',\n trigger: 'auto',\n priority: 100,\n activities: [\n {\n activityId: 'notify_approver',\n activityName: 'Notify Approver',\n activityType: 'SEND_EMAIL',\n config: {\n to: '{{context.approver.email}}',\n subject: 'Approval Required: {{context.request.title}}',\n template: 'approval_request',\n data: {\n requestId: '{{context.request.id}}',\n title: '{{context.request.title}}',\n requester: '{{context.requester.name}}',\n },\n },\n timeout: 'PT10S',\n async: true,\n retryPolicy: { maxAttempts: 3, initialIntervalMs: 2000, backoffCoefficient: 2, maxIntervalMs: 20000 },\n },\n {\n activityId: 'emit_approval_requested',\n activityName: 'Emit Approval Requested Event',\n activityType: 'EMIT_EVENT',\n config: {\n eventType: 'approval.requested',\n payload: { requestId: '{{context.request.id}}', workflowInstanceId: '{{workflow.instanceId}}' },\n },\n async: true,\n },\n ],\n },\n {\n transitionId: 'approval_to_end',\n transitionName: 'Approval Decision',\n fromStepId: 'approval',\n toStepId: 'end',\n trigger: 'manual',\n priority: 100,\n activities: [\n {\n activityId: 'notify_requester',\n activityName: 'Notify Requester of Decision',\n activityType: 'SEND_EMAIL',\n config: {\n to: '{{context.requester.email}}',\n subject: 'Approval Decision: {{context.request.title}}',\n template: 'approval_decision',\n data: {\n requestId: '{{context.request.id}}',\n title: '{{context.request.title}}',\n decision: '{{task.result.approved}}',\n comments: '{{task.result.comments}}',\n approver: '{{task.completedBy}}',\n },\n },\n timeout: 'PT10S',\n async: true,\n },\n {\n activityId: 'emit_approval_completed',\n activityName: 'Emit Approval Completed Event',\n activityType: 'EMIT_EVENT',\n config: {\n eventType: 'approval.completed',\n payload: {\n requestId: '{{context.request.id}}',\n approved: '{{task.result.approved}}',\n approvedBy: '{{task.completedBy}}',\n timestamp: '{{now}}',\n },\n },\n async: true,\n },\n ],\n },\n ],\n})\n\n// ============================================================================\n// Checkout Demo Workflow\n// ============================================================================\n\nconst checkoutDemo = defineWorkflow({\n workflowId: 'workflows.checkout-demo',\n workflowName: 'Checkout with Payment Webhook',\n description: 'Realistic checkout workflow with signal-based payment confirmation',\n metadata: { category: 'E-commerce', tags: ['checkout', 'demo', 'payments', 'signals'], icon: 'ShoppingCart' },\n steps: [\n {\n stepId: 'start',\n stepName: 'Start',\n stepType: 'START',\n description: 'Checkout process initiated',\n preConditions: [{\n ruleId: 'workflow_checkout_cart_not_empty',\n required: true,\n validationMessage: {\n en: 'Your shopping cart is empty. Please add items before starting checkout.',\n pl: 'Tw\u00F3j koszyk jest pusty. Dodaj produkty przed rozpocz\u0119ciem zakupu.',\n },\n }],\n },\n { stepId: 'cart_validation', stepName: 'Cart Validation', stepType: 'AUTOMATED', description: 'Validate cart has items and inventory is available', timeout: 'PT10S' },\n {\n stepId: 'customer_info',\n stepName: 'Customer Information',\n stepType: 'USER_TASK',\n description: 'Collect customer shipping and contact information',\n userTaskConfig: {\n formSchema: {\n type: 'object',\n properties: {\n fullName: { type: 'string', title: 'Full Name', description: \"Customer's full legal name\" },\n email: { type: 'string', format: 'email', title: 'Email Address', description: 'Email for order confirmation' },\n phone: { type: 'string', title: 'Phone Number', description: 'Contact number for delivery' },\n comment: { type: 'string', title: 'Order Comment', maxLength: 500, description: 'Special instructions or notes' },\n },\n required: ['fullName', 'email'],\n },\n slaDuration: 'PT24H',\n },\n },\n { stepId: 'payment_initiation', stepName: 'Initiate Payment', stepType: 'AUTOMATED', description: 'Send payment request to payment provider', timeout: 'PT10S' },\n {\n stepId: 'wait_payment_confirmation',\n stepName: 'Wait for Payment Confirmation',\n stepType: 'WAIT_FOR_SIGNAL',\n description: 'Waiting for payment provider webhook confirmation',\n signalConfig: { signalName: 'payment_confirmed', timeout: 'PT5M' },\n },\n { stepId: 'order_confirmation', stepName: 'Order Confirmation', stepType: 'AUTOMATED', description: 'Create order record and send confirmation', timeout: 'PT15S' },\n { stepId: 'end', stepName: 'Complete', stepType: 'END', description: 'Checkout completed successfully' },\n ] as const,\n transitions: [\n {\n transitionId: 'start_to_cart',\n transitionName: 'Initialize Checkout',\n fromStepId: 'start',\n toStepId: 'cart_validation',\n trigger: 'auto',\n priority: 100,\n activities: [{\n activityId: 'log_checkout_start',\n activityName: 'Log Checkout Start',\n activityType: 'EMIT_EVENT',\n config: {\n eventName: 'checkout.started',\n payload: { customerId: '{{context.customerId}}', cartId: '{{context.cartId}}', currency: '{{context.currency}}', timestamp: '{{now}}' },\n },\n async: false,\n }],\n },\n {\n transitionId: 'cart_to_customer_info',\n transitionName: 'Collect Customer Information',\n fromStepId: 'cart_validation',\n toStepId: 'customer_info',\n trigger: 'auto',\n priority: 100,\n preConditions: [{ ruleId: 'workflow_checkout_cart_not_empty', required: true }],\n },\n {\n transitionId: 'customer_info_to_payment',\n transitionName: 'Initiate Payment',\n fromStepId: 'customer_info',\n toStepId: 'payment_initiation',\n trigger: 'auto',\n priority: 100,\n },\n {\n transitionId: 'payment_to_wait_confirmation',\n transitionName: 'Wait for Confirmation',\n fromStepId: 'payment_initiation',\n toStepId: 'wait_payment_confirmation',\n trigger: 'auto',\n priority: 100,\n },\n {\n transitionId: 'confirmation_to_order',\n transitionName: 'Payment Confirmed',\n fromStepId: 'wait_payment_confirmation',\n toStepId: 'order_confirmation',\n trigger: 'auto',\n priority: 100,\n },\n {\n transitionId: 'confirmation_to_end',\n transitionName: 'Finalize Order',\n fromStepId: 'order_confirmation',\n toStepId: 'end',\n trigger: 'auto',\n priority: 100,\n activities: [\n {\n activityId: 'create_order',\n activityName: 'Create Order Record',\n activityType: 'CALL_API',\n config: {\n endpoint: '/api/sales/orders',\n method: 'POST',\n body: {\n customerEntityId: '{{context.customer.id}}',\n currencyCode: '{{context.cart.currency}}',\n placedAt: '{{now}}',\n grandTotalGrossAmount: '{{context.cart.total}}',\n },\n validateTenantMatch: true,\n },\n timeout: 'PT10S',\n async: false,\n retryPolicy: { maxAttempts: 3, initialIntervalMs: 2000, backoffCoefficient: 2, maxIntervalMs: 10000 },\n },\n {\n activityId: 'send_confirmation_email',\n activityName: 'Send Order Confirmation Email',\n activityType: 'SEND_EMAIL',\n config: {\n to: '{{context.customer.email}}',\n subject: 'Order Confirmation',\n template: 'order_confirmation',\n data: { customerName: '{{context.customer.name}}', total: '{{context.cart.total}}' },\n },\n timeout: 'PT15S',\n async: true,\n retryPolicy: { maxAttempts: 3, initialIntervalMs: 2000, backoffCoefficient: 2, maxIntervalMs: 20000 },\n },\n ],\n },\n ],\n})\n\n// ============================================================================\n// Module Export\n// ============================================================================\n\nexport const workflowsConfig = createWorkflowsModuleConfig({\n moduleId: 'workflows',\n workflows: [simpleApproval, checkoutDemo],\n})\n\nexport default workflowsConfig\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Workflows Module \u2014 Code-Based Workflow Definitions\n *\n * Demo workflows shipped with the workflows module.\n * These are auto-discovered by the generator and loaded into the in-memory registry.\n */\n\nimport { defineWorkflow, createWorkflowsModuleConfig } from '@open-mercato/shared/modules/workflows'\n\n// ============================================================================\n// Simple Approval Workflow\n// ============================================================================\n\nconst simpleApproval = defineWorkflow({\n workflowId: 'workflows.simple-approval',\n workflowName: 'Simple Approval Workflow',\n description: 'Basic three-step approval workflow: Start \u2192 Approval \u2192 End',\n metadata: { category: 'Testing', tags: ['test', 'approval', 'simple'], icon: 'check-circle' },\n steps: [\n { stepId: 'start', stepName: 'Start', stepType: 'START', description: 'Initialize workflow' },\n {\n stepId: 'approval',\n stepName: 'Pending Approval',\n stepType: 'USER_TASK',\n description: 'Approve or reject request',\n userTaskConfig: { assignedTo: 'approver', slaDuration: 'PT1H' },\n },\n { stepId: 'end', stepName: 'Complete', stepType: 'END', description: 'Workflow complete' },\n ] as const,\n transitions: [\n {\n transitionId: 'start_to_approval',\n transitionName: 'Submit for Approval',\n fromStepId: 'start',\n toStepId: 'approval',\n trigger: 'auto',\n priority: 100,\n activities: [\n {\n activityId: 'notify_approver',\n activityName: 'Notify Approver',\n activityType: 'SEND_EMAIL',\n config: {\n to: '{{context.approver.email}}',\n subject: 'Approval Required: {{context.request.title}}',\n template: 'approval_request',\n data: {\n requestId: '{{context.request.id}}',\n title: '{{context.request.title}}',\n requester: '{{context.requester.name}}',\n },\n },\n timeout: 'PT10S',\n async: true,\n retryPolicy: { maxAttempts: 3, initialIntervalMs: 2000, backoffCoefficient: 2, maxIntervalMs: 20000 },\n },\n {\n activityId: 'emit_approval_requested',\n activityName: 'Emit Approval Requested Event',\n activityType: 'EMIT_EVENT',\n config: {\n eventType: 'approval.requested',\n payload: { requestId: '{{context.request.id}}', workflowInstanceId: '{{workflow.instanceId}}' },\n },\n async: true,\n },\n ],\n },\n {\n transitionId: 'approval_to_end',\n transitionName: 'Approval Decision',\n fromStepId: 'approval',\n toStepId: 'end',\n trigger: 'manual',\n priority: 100,\n activities: [\n {\n activityId: 'notify_requester',\n activityName: 'Notify Requester of Decision',\n activityType: 'SEND_EMAIL',\n config: {\n to: '{{context.requester.email}}',\n subject: 'Approval Decision: {{context.request.title}}',\n template: 'approval_decision',\n data: {\n requestId: '{{context.request.id}}',\n title: '{{context.request.title}}',\n decision: '{{task.result.approved}}',\n comments: '{{task.result.comments}}',\n approver: '{{task.completedBy}}',\n },\n },\n timeout: 'PT10S',\n async: true,\n },\n {\n activityId: 'emit_approval_completed',\n activityName: 'Emit Approval Completed Event',\n activityType: 'EMIT_EVENT',\n config: {\n eventType: 'approval.completed',\n payload: {\n requestId: '{{context.request.id}}',\n approved: '{{task.result.approved}}',\n approvedBy: '{{task.completedBy}}',\n timestamp: '{{now}}',\n },\n },\n async: true,\n },\n ],\n },\n ],\n})\n\n// ============================================================================\n// Checkout Demo Workflow\n// ============================================================================\n\nconst checkoutDemo = defineWorkflow({\n workflowId: 'workflows.checkout-demo',\n workflowName: 'Checkout with Payment Webhook',\n description: 'Realistic checkout workflow with signal-based payment confirmation',\n metadata: { category: 'E-commerce', tags: ['checkout', 'demo', 'payments', 'signals'], icon: 'ShoppingCart' },\n steps: [\n {\n stepId: 'start',\n stepName: 'Start',\n stepType: 'START',\n description: 'Checkout process initiated',\n preConditions: [{\n ruleId: 'workflow_checkout_cart_not_empty',\n required: true,\n validationMessage: {\n en: 'Your shopping cart is empty. Please add items before starting checkout.',\n pl: 'Tw\u00F3j koszyk jest pusty. Dodaj produkty przed rozpocz\u0119ciem zakupu.',\n },\n }],\n },\n { stepId: 'cart_validation', stepName: 'Cart Validation', stepType: 'AUTOMATED', description: 'Validate cart has items and inventory is available', timeout: 'PT10S' },\n {\n stepId: 'customer_info',\n stepName: 'Customer Information',\n stepType: 'USER_TASK',\n description: 'Collect customer shipping and contact information',\n userTaskConfig: {\n formSchema: {\n type: 'object',\n properties: {\n fullName: { type: 'string', title: 'Full Name', description: \"Customer's full legal name\" },\n email: { type: 'string', format: 'email', title: 'Email Address', description: 'Email for order confirmation' },\n phone: { type: 'string', title: 'Phone Number', description: 'Contact number for delivery' },\n comment: { type: 'string', title: 'Order Comment', maxLength: 500, description: 'Special instructions or notes' },\n },\n required: ['fullName', 'email'],\n },\n slaDuration: 'PT24H',\n },\n },\n { stepId: 'payment_initiation', stepName: 'Initiate Payment', stepType: 'AUTOMATED', description: 'Send payment request to payment provider', timeout: 'PT10S' },\n {\n stepId: 'wait_payment_confirmation',\n stepName: 'Wait for Payment Confirmation',\n stepType: 'WAIT_FOR_SIGNAL',\n description: 'Waiting for payment provider webhook confirmation',\n signalConfig: { signalName: 'payment_confirmed', timeout: 'PT5M' },\n },\n { stepId: 'order_confirmation', stepName: 'Order Confirmation', stepType: 'AUTOMATED', description: 'Create order record and send confirmation', timeout: 'PT15S' },\n { stepId: 'end', stepName: 'Complete', stepType: 'END', description: 'Checkout completed successfully' },\n ] as const,\n transitions: [\n {\n transitionId: 'start_to_cart',\n transitionName: 'Initialize Checkout',\n fromStepId: 'start',\n toStepId: 'cart_validation',\n trigger: 'auto',\n priority: 100,\n activities: [{\n activityId: 'log_checkout_start',\n activityName: 'Log Checkout Start',\n activityType: 'EMIT_EVENT',\n config: {\n eventName: 'checkout.started',\n payload: { customerId: '{{context.customerId}}', cartId: '{{context.cartId}}', currency: '{{context.currency}}', timestamp: '{{now}}' },\n },\n async: false,\n }],\n },\n {\n transitionId: 'cart_to_customer_info',\n transitionName: 'Collect Customer Information',\n fromStepId: 'cart_validation',\n toStepId: 'customer_info',\n trigger: 'auto',\n priority: 100,\n preConditions: [{ ruleId: 'workflow_checkout_cart_not_empty', required: true }],\n },\n {\n transitionId: 'customer_info_to_payment',\n transitionName: 'Initiate Payment',\n fromStepId: 'customer_info',\n toStepId: 'payment_initiation',\n trigger: 'auto',\n priority: 100,\n },\n {\n transitionId: 'payment_to_wait_confirmation',\n transitionName: 'Wait for Confirmation',\n fromStepId: 'payment_initiation',\n toStepId: 'wait_payment_confirmation',\n trigger: 'auto',\n priority: 100,\n },\n {\n transitionId: 'confirmation_to_order',\n transitionName: 'Payment Confirmed',\n fromStepId: 'wait_payment_confirmation',\n toStepId: 'order_confirmation',\n trigger: 'auto',\n priority: 100,\n },\n {\n transitionId: 'confirmation_to_end',\n transitionName: 'Finalize Order',\n fromStepId: 'order_confirmation',\n toStepId: 'end',\n trigger: 'auto',\n priority: 100,\n activities: [\n {\n activityId: 'create_order',\n activityName: 'Create Order Record',\n activityType: 'CALL_API',\n config: {\n endpoint: '/api/sales/orders',\n method: 'POST',\n body: {\n customerEntityId: '{{context.customer.id}}',\n currencyCode: '{{context.cart.currency}}',\n placedAt: '{{now}}',\n lines: '{{context.cart.orderLines}}',\n adjustments: '{{context.cart.orderAdjustments}}',\n grandTotalGrossAmount: '{{context.cart.total}}',\n },\n validateTenantMatch: true,\n },\n timeout: 'PT10S',\n async: false,\n retryPolicy: { maxAttempts: 3, initialIntervalMs: 2000, backoffCoefficient: 2, maxIntervalMs: 10000 },\n },\n {\n activityId: 'send_confirmation_email',\n activityName: 'Send Order Confirmation Email',\n activityType: 'SEND_EMAIL',\n config: {\n to: '{{context.customer.email}}',\n subject: 'Order Confirmation',\n template: 'order_confirmation',\n data: { customerName: '{{context.customer.name}}', total: '{{context.cart.total}}' },\n },\n timeout: 'PT15S',\n async: true,\n retryPolicy: { maxAttempts: 3, initialIntervalMs: 2000, backoffCoefficient: 2, maxIntervalMs: 20000 },\n },\n ],\n },\n ],\n})\n\n// ============================================================================\n// Module Export\n// ============================================================================\n\nexport const workflowsConfig = createWorkflowsModuleConfig({\n moduleId: 'workflows',\n workflows: [simpleApproval, checkoutDemo],\n})\n\nexport default workflowsConfig\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7-develop.6552.1.5208a19369",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -253,16 +253,16 @@
|
|
|
253
253
|
"zod": "^4.4.3"
|
|
254
254
|
},
|
|
255
255
|
"peerDependencies": {
|
|
256
|
-
"@open-mercato/ai-assistant": "0.6.
|
|
257
|
-
"@open-mercato/shared": "0.6.
|
|
258
|
-
"@open-mercato/ui": "0.6.
|
|
256
|
+
"@open-mercato/ai-assistant": "0.6.7-develop.6552.1.5208a19369",
|
|
257
|
+
"@open-mercato/shared": "0.6.7-develop.6552.1.5208a19369",
|
|
258
|
+
"@open-mercato/ui": "0.6.7-develop.6552.1.5208a19369",
|
|
259
259
|
"react": "^19.0.0",
|
|
260
260
|
"react-dom": "^19.0.0"
|
|
261
261
|
},
|
|
262
262
|
"devDependencies": {
|
|
263
|
-
"@open-mercato/ai-assistant": "0.6.
|
|
264
|
-
"@open-mercato/shared": "0.6.
|
|
265
|
-
"@open-mercato/ui": "0.6.
|
|
263
|
+
"@open-mercato/ai-assistant": "0.6.7-develop.6552.1.5208a19369",
|
|
264
|
+
"@open-mercato/shared": "0.6.7-develop.6552.1.5208a19369",
|
|
265
|
+
"@open-mercato/ui": "0.6.7-develop.6552.1.5208a19369",
|
|
266
266
|
"@testing-library/dom": "^10.4.1",
|
|
267
267
|
"@testing-library/jest-dom": "^6.9.1",
|
|
268
268
|
"@testing-library/react": "^16.3.1",
|
|
@@ -286,5 +286,5 @@
|
|
|
286
286
|
"url": "https://github.com/open-mercato/open-mercato",
|
|
287
287
|
"directory": "packages/core"
|
|
288
288
|
},
|
|
289
|
-
"stableVersion": "0.6.
|
|
289
|
+
"stableVersion": "0.6.6"
|
|
290
290
|
}
|
|
@@ -389,9 +389,20 @@ export default function CheckoutDemoPage() {
|
|
|
389
389
|
currencyCode: selectedCurrency || 'USD', // Ensure currency is always set
|
|
390
390
|
kind: 'product' as const,
|
|
391
391
|
productId: item.id,
|
|
392
|
-
|
|
392
|
+
name: item.name,
|
|
393
393
|
unitPriceGross: item.price,
|
|
394
394
|
})),
|
|
395
|
+
// Fold the demo's shipping and tax into order-level adjustments so the
|
|
396
|
+
// created order's grand total matches the cart total shown to the user
|
|
397
|
+
// (line items alone only cover the product subtotal).
|
|
398
|
+
orderAdjustments: [
|
|
399
|
+
...(shipping > 0
|
|
400
|
+
? [{ kind: 'shipping' as const, label: 'Shipping', currencyCode: selectedCurrency || 'USD', amountGross: shipping }]
|
|
401
|
+
: []),
|
|
402
|
+
...(tax > 0
|
|
403
|
+
? [{ kind: 'tax' as const, label: 'Tax', currencyCode: selectedCurrency || 'USD', amountGross: tax }]
|
|
404
|
+
: []),
|
|
405
|
+
],
|
|
395
406
|
itemCount: demoCart.reduce((sum, item) => sum + item.quantity, 0),
|
|
396
407
|
subtotal: subtotal,
|
|
397
408
|
tax: tax,
|
|
@@ -929,8 +929,28 @@ export async function executeCallApi(
|
|
|
929
929
|
// 3. Import the one-time API key helper
|
|
930
930
|
const { withOnetimeApiKey } = await import('../../api_keys/services/apiKeyService')
|
|
931
931
|
|
|
932
|
-
// 4.
|
|
933
|
-
|
|
932
|
+
// 4. Create the one-time API key on an EntityManager that is fully detached
|
|
933
|
+
// from the surrounding request/transaction context.
|
|
934
|
+
//
|
|
935
|
+
// CALL_API runs inside `workflowExecutor.executeWorkflow()`, which wraps
|
|
936
|
+
// the whole execution in `em.transactional(...)`. The request EM is forked
|
|
937
|
+
// with `useContext: true`, so while that transaction is open, EVERY
|
|
938
|
+
// operation on the container's `em` — including this API key's
|
|
939
|
+
// persist/flush — is transparently redirected to the uncommitted
|
|
940
|
+
// transaction fork (MikroORM `getContext()` → `TransactionContext`). The
|
|
941
|
+
// key would therefore stay invisible until the transaction commits, but
|
|
942
|
+
// the commit cannot happen until this activity returns. The outbound
|
|
943
|
+
// self-authenticated `fetch` below opens a SEPARATE DB connection that
|
|
944
|
+
// cannot see the uncommitted row, so the internal API responds `401` and
|
|
945
|
+
// the activity fails (issue #4202).
|
|
946
|
+
//
|
|
947
|
+
// Forking with `useContext: false` (matching the query_index/webhooks
|
|
948
|
+
// isolated-EM convention) gives the key its own pooled connection with
|
|
949
|
+
// autocommit, so it is committed and visible to the internal request
|
|
950
|
+
// immediately.
|
|
951
|
+
const apiKeyEm = container
|
|
952
|
+
.resolve<PostgreSqlEntityManager>('em')
|
|
953
|
+
.fork({ clear: true, freshEventManager: true, useContext: false }) as PostgreSqlEntityManager
|
|
934
954
|
|
|
935
955
|
// 5. Resolve the roles that the one-time API key will inherit.
|
|
936
956
|
//
|
|
@@ -80,9 +80,17 @@ export async function findWorkflowDefinition(
|
|
|
80
80
|
// so disabled code workflows aren't silently executable.
|
|
81
81
|
if (version === undefined && !codeDef.enabled) return null
|
|
82
82
|
|
|
83
|
+
return buildVirtualCodeDefinition(codeDef, tenantId, organizationId)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function buildVirtualCodeDefinition(
|
|
87
|
+
codeDef: NonNullable<ReturnType<typeof getCodeWorkflow>>,
|
|
88
|
+
tenantId: string,
|
|
89
|
+
organizationId: string,
|
|
90
|
+
): WorkflowDefinition {
|
|
83
91
|
// Construct a virtual WorkflowDefinition object (not persisted)
|
|
84
92
|
const virtual = new WorkflowDefinition()
|
|
85
|
-
virtual.id = codeWorkflowUuid(workflowId)
|
|
93
|
+
virtual.id = codeWorkflowUuid(codeDef.workflowId)
|
|
86
94
|
virtual.workflowId = codeDef.workflowId
|
|
87
95
|
virtual.workflowName = codeDef.workflowName
|
|
88
96
|
virtual.description = codeDef.description ?? null
|
|
@@ -98,3 +106,47 @@ export async function findWorkflowDefinition(
|
|
|
98
106
|
|
|
99
107
|
return virtual
|
|
100
108
|
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Resolve a virtual code definition for an already-started instance whose
|
|
112
|
+
* `definitionId` does not exist in the database.
|
|
113
|
+
*
|
|
114
|
+
* Instances started from a virtual code definition persist the deterministic
|
|
115
|
+
* `codeWorkflowUuid(workflowId)` as their `definitionId`. Runtime handlers
|
|
116
|
+
* (step/transition/signal/timer/task) load definitions by that id, so without
|
|
117
|
+
* this fallback an unpersisted code workflow starts but can never advance.
|
|
118
|
+
*
|
|
119
|
+
* The UUID equality gate ensures the fallback never substitutes the code
|
|
120
|
+
* definition for a hard-deleted persisted row: those rows have their own
|
|
121
|
+
* random UUIDs that can never match the deterministic code UUID.
|
|
122
|
+
*/
|
|
123
|
+
export function resolveCodeDefinitionForInstance(instance: {
|
|
124
|
+
definitionId: string
|
|
125
|
+
workflowId: string
|
|
126
|
+
tenantId: string
|
|
127
|
+
organizationId: string
|
|
128
|
+
}): WorkflowDefinition | null {
|
|
129
|
+
const codeDef = getCodeWorkflow(instance.workflowId)
|
|
130
|
+
if (!codeDef) return null
|
|
131
|
+
if (codeWorkflowUuid(instance.workflowId) !== instance.definitionId) return null
|
|
132
|
+
return buildVirtualCodeDefinition(codeDef, instance.tenantId, instance.organizationId)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Find the definition backing a workflow instance: database row by id first,
|
|
137
|
+
* falling back to the in-memory code registry for instances started from an
|
|
138
|
+
* unpersisted code definition.
|
|
139
|
+
*/
|
|
140
|
+
export async function findDefinitionForInstance(
|
|
141
|
+
em: EntityManager,
|
|
142
|
+
instance: {
|
|
143
|
+
definitionId: string
|
|
144
|
+
workflowId: string
|
|
145
|
+
tenantId: string
|
|
146
|
+
organizationId: string
|
|
147
|
+
},
|
|
148
|
+
): Promise<WorkflowDefinition | null> {
|
|
149
|
+
const dbDef = await em.findOne(WorkflowDefinition, { id: instance.definitionId })
|
|
150
|
+
if (dbDef) return dbDef
|
|
151
|
+
return resolveCodeDefinitionForInstance(instance)
|
|
152
|
+
}
|
|
@@ -13,6 +13,7 @@ import type * as eventLoggerModule from './event-logger'
|
|
|
13
13
|
import type * as stepHandlerModule from './step-handler'
|
|
14
14
|
import type * as transitionHandlerModule from './transition-handler'
|
|
15
15
|
import type * as workflowExecutorModule from './workflow-executor'
|
|
16
|
+
import { resolveCodeDefinitionForInstance } from './find-definition'
|
|
16
17
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
17
18
|
|
|
18
19
|
const logger = createLogger('workflows')
|
|
@@ -95,13 +96,13 @@ export async function sendSignal(
|
|
|
95
96
|
// Branch-scoped signal: a FORKED instance routes the signal to the branch
|
|
96
97
|
// paused at a matching WAIT_FOR_SIGNAL step.
|
|
97
98
|
if (instance.status === 'FORKED') {
|
|
98
|
-
const branchDefinition = await findOneWithDecryption(
|
|
99
|
+
const branchDefinition = (await findOneWithDecryption(
|
|
99
100
|
em as PostgreSqlEntityManager,
|
|
100
101
|
WorkflowDefinition,
|
|
101
102
|
{ id: instance.definitionId, tenantId: instance.tenantId, organizationId: instance.organizationId, deletedAt: null },
|
|
102
103
|
undefined,
|
|
103
104
|
{ tenantId: instance.tenantId, organizationId: instance.organizationId },
|
|
104
|
-
)
|
|
105
|
+
)) ?? resolveCodeDefinitionForInstance(instance)
|
|
105
106
|
if (!branchDefinition) {
|
|
106
107
|
throw new SignalError('Workflow definition not found', 'DEFINITION_NOT_FOUND', { definitionId: instance.definitionId })
|
|
107
108
|
}
|
|
@@ -173,7 +174,7 @@ export async function sendSignal(
|
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
// Load workflow definition with tenant/org scope to check current step
|
|
176
|
-
const definition = await findOneWithDecryption(
|
|
177
|
+
const definition = (await findOneWithDecryption(
|
|
177
178
|
em as PostgreSqlEntityManager,
|
|
178
179
|
WorkflowDefinition,
|
|
179
180
|
{
|
|
@@ -184,7 +185,7 @@ export async function sendSignal(
|
|
|
184
185
|
},
|
|
185
186
|
undefined,
|
|
186
187
|
{ tenantId: instance.tenantId, organizationId: instance.organizationId },
|
|
187
|
-
)
|
|
188
|
+
)) ?? resolveCodeDefinitionForInstance(instance)
|
|
188
189
|
if (!definition) {
|
|
189
190
|
throw new SignalError(
|
|
190
191
|
'Workflow definition not found',
|
|
@@ -13,7 +13,6 @@ import { EntityManager } from '@mikro-orm/core'
|
|
|
13
13
|
import {
|
|
14
14
|
WorkflowInstance,
|
|
15
15
|
WorkflowBranchInstance,
|
|
16
|
-
WorkflowDefinition,
|
|
17
16
|
StepInstance,
|
|
18
17
|
UserTask,
|
|
19
18
|
WorkflowEvent,
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
} from '../data/entities'
|
|
23
22
|
import { parseDuration } from './duration'
|
|
24
23
|
import { logWorkflowEvent } from './event-logger'
|
|
24
|
+
import { findDefinitionForInstance } from './find-definition'
|
|
25
25
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
26
26
|
|
|
27
27
|
const logger = createLogger('workflows')
|
|
@@ -76,9 +76,7 @@ export async function enterStep(
|
|
|
76
76
|
branch?: WorkflowBranchInstance | null
|
|
77
77
|
): Promise<StepInstance> {
|
|
78
78
|
// Load workflow definition to get step details
|
|
79
|
-
const definition = await em
|
|
80
|
-
id: instance.definitionId,
|
|
81
|
-
})
|
|
79
|
+
const definition = await findDefinitionForInstance(em, instance)
|
|
82
80
|
|
|
83
81
|
if (!definition) {
|
|
84
82
|
throw new StepExecutionError(
|
|
@@ -213,9 +211,7 @@ export async function executeStep(
|
|
|
213
211
|
const stepInstance = await enterStep(em, instance, stepId, context, branch)
|
|
214
212
|
|
|
215
213
|
// Load workflow definition to get step configuration
|
|
216
|
-
const definition = await em
|
|
217
|
-
id: instance.definitionId,
|
|
218
|
-
})
|
|
214
|
+
const definition = await findDefinitionForInstance(em, instance)
|
|
219
215
|
|
|
220
216
|
if (!definition) {
|
|
221
217
|
throw new StepExecutionError(
|
|
@@ -359,7 +355,7 @@ async function executeStepByType(
|
|
|
359
355
|
{ stepType, stepId: stepDef.stepId }
|
|
360
356
|
)
|
|
361
357
|
}
|
|
362
|
-
const definition = await em
|
|
358
|
+
const definition = await findDefinitionForInstance(em, instance)
|
|
363
359
|
if (!definition) {
|
|
364
360
|
throw new StepExecutionError(
|
|
365
361
|
`Workflow definition not found: ${instance.definitionId}`,
|
|
@@ -17,9 +17,9 @@ import {
|
|
|
17
17
|
WorkflowInstance,
|
|
18
18
|
WorkflowEvent,
|
|
19
19
|
StepInstance,
|
|
20
|
-
WorkflowDefinition,
|
|
21
20
|
} from '../data/entities'
|
|
22
21
|
import { executeWorkflow } from './workflow-executor'
|
|
22
|
+
import { findDefinitionForInstance } from './find-definition'
|
|
23
23
|
import * as stepHandler from './step-handler'
|
|
24
24
|
import * as transitionHandler from './transition-handler'
|
|
25
25
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
@@ -191,9 +191,7 @@ export async function completeUserTask(
|
|
|
191
191
|
const currentStepId = instance.currentStepId
|
|
192
192
|
|
|
193
193
|
// Load workflow definition to find transitions
|
|
194
|
-
const definition = await em
|
|
195
|
-
id: instance.definitionId,
|
|
196
|
-
})
|
|
194
|
+
const definition = await findDefinitionForInstance(em, instance)
|
|
197
195
|
|
|
198
196
|
if (!definition) {
|
|
199
197
|
throw new UserTaskError(
|
|
@@ -14,6 +14,7 @@ import type * as eventLoggerModule from './event-logger'
|
|
|
14
14
|
import type * as stepHandlerModule from './step-handler'
|
|
15
15
|
import type * as transitionHandlerModule from './transition-handler'
|
|
16
16
|
import type * as workflowExecutorModule from './workflow-executor'
|
|
17
|
+
import { resolveCodeDefinitionForInstance } from './find-definition'
|
|
17
18
|
|
|
18
19
|
export interface FireTimerOptions {
|
|
19
20
|
instanceId: string
|
|
@@ -110,7 +111,7 @@ export async function fireTimer(
|
|
|
110
111
|
)
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
const definition = await findOneWithDecryption(
|
|
114
|
+
const definition = (await findOneWithDecryption(
|
|
114
115
|
em as PostgreSqlEntityManager,
|
|
115
116
|
WorkflowDefinition,
|
|
116
117
|
{
|
|
@@ -121,7 +122,7 @@ export async function fireTimer(
|
|
|
121
122
|
},
|
|
122
123
|
undefined,
|
|
123
124
|
{ tenantId: instance.tenantId, organizationId: instance.organizationId },
|
|
124
|
-
)
|
|
125
|
+
)) ?? resolveCodeDefinitionForInstance(instance)
|
|
125
126
|
if (!definition) {
|
|
126
127
|
throw new TimerError(
|
|
127
128
|
'Workflow definition not found',
|
|
@@ -15,7 +15,6 @@ import type { AwilixContainer } from 'awilix'
|
|
|
15
15
|
import type { EventBus } from '@open-mercato/events'
|
|
16
16
|
import {
|
|
17
17
|
WorkflowInstance,
|
|
18
|
-
WorkflowDefinition,
|
|
19
18
|
WorkflowEvent,
|
|
20
19
|
} from '../data/entities'
|
|
21
20
|
import * as ruleEvaluator from '../../business_rules/lib/rule-evaluator'
|
|
@@ -23,6 +22,7 @@ import * as ruleEngine from '../../business_rules/lib/rule-engine'
|
|
|
23
22
|
import * as activityExecutor from './activity-executor'
|
|
24
23
|
import type { ActivityDefinition } from './activity-executor'
|
|
25
24
|
import * as stepHandler from './step-handler'
|
|
25
|
+
import { findDefinitionForInstance } from './find-definition'
|
|
26
26
|
import {
|
|
27
27
|
type ExecutionToken,
|
|
28
28
|
rootToken,
|
|
@@ -116,9 +116,7 @@ export async function evaluateTransition(
|
|
|
116
116
|
|
|
117
117
|
try {
|
|
118
118
|
// Load workflow definition
|
|
119
|
-
const definition = await em
|
|
120
|
-
id: instance.definitionId,
|
|
121
|
-
})
|
|
119
|
+
const definition = await findDefinitionForInstance(em, instance)
|
|
122
120
|
|
|
123
121
|
if (!definition) {
|
|
124
122
|
return {
|
|
@@ -227,9 +225,7 @@ export async function findValidTransitions(
|
|
|
227
225
|
): Promise<TransitionEvaluationResult[]> {
|
|
228
226
|
try {
|
|
229
227
|
// Load workflow definition
|
|
230
|
-
const definition = await em
|
|
231
|
-
id: instance.definitionId,
|
|
232
|
-
})
|
|
228
|
+
const definition = await findDefinitionForInstance(em, instance)
|
|
233
229
|
|
|
234
230
|
if (!definition) {
|
|
235
231
|
return []
|
|
@@ -752,9 +748,7 @@ async function evaluatePreConditions(
|
|
|
752
748
|
): Promise<ruleEngine.RuleEngineResult> {
|
|
753
749
|
try {
|
|
754
750
|
// Load workflow definition to get workflow ID
|
|
755
|
-
const definition = await em
|
|
756
|
-
id: instance.definitionId,
|
|
757
|
-
})
|
|
751
|
+
const definition = await findDefinitionForInstance(em, instance)
|
|
758
752
|
|
|
759
753
|
if (!definition) {
|
|
760
754
|
return {
|
|
@@ -881,9 +875,7 @@ async function evaluatePostConditions(
|
|
|
881
875
|
): Promise<ruleEngine.RuleEngineResult> {
|
|
882
876
|
try {
|
|
883
877
|
// Load workflow definition to get workflow ID
|
|
884
|
-
const definition = await em
|
|
885
|
-
id: instance.definitionId,
|
|
886
|
-
})
|
|
878
|
+
const definition = await findDefinitionForInstance(em, instance)
|
|
887
879
|
|
|
888
880
|
if (!definition) {
|
|
889
881
|
return {
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
type WorkflowInstanceStatus,
|
|
20
20
|
} from '../data/entities'
|
|
21
21
|
import { compensateWorkflow } from './compensation-handler'
|
|
22
|
-
import { findWorkflowDefinition } from './find-definition'
|
|
22
|
+
import { findWorkflowDefinition, findDefinitionForInstance } from './find-definition'
|
|
23
23
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
24
24
|
|
|
25
25
|
const logger = createLogger('workflows')
|
|
@@ -278,9 +278,7 @@ export async function executeWorkflow(
|
|
|
278
278
|
)
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
const definition = await trx
|
|
282
|
-
id: instance.definitionId,
|
|
283
|
-
})
|
|
281
|
+
const definition = await findDefinitionForInstance(trx, instance)
|
|
284
282
|
|
|
285
283
|
if (!definition) {
|
|
286
284
|
throw new WorkflowExecutionError(
|
|
@@ -614,7 +612,7 @@ export async function completeWorkflow(
|
|
|
614
612
|
|
|
615
613
|
// Trigger compensation if workflow failed and has compensatable activities (Phase 8.2)
|
|
616
614
|
if (status === 'FAILED') {
|
|
617
|
-
const definition = await em
|
|
615
|
+
const definition = await findDefinitionForInstance(em, instance)
|
|
618
616
|
|
|
619
617
|
if (definition && checkIfCompensationNeeded(definition)) {
|
|
620
618
|
try {
|
|
@@ -836,9 +834,14 @@ export async function resumeWorkflowAfterActivities(
|
|
|
836
834
|
fromStepId: instance.currentStepId,
|
|
837
835
|
})
|
|
838
836
|
|
|
839
|
-
const definition = await trx
|
|
840
|
-
|
|
841
|
-
|
|
837
|
+
const definition = await findDefinitionForInstance(trx, instance)
|
|
838
|
+
if (!definition) {
|
|
839
|
+
throw new WorkflowExecutionError(
|
|
840
|
+
`Workflow definition not found: ${instance.definitionId}`,
|
|
841
|
+
'DEFINITION_NOT_FOUND',
|
|
842
|
+
{ definitionId: instance.definitionId }
|
|
843
|
+
)
|
|
844
|
+
}
|
|
842
845
|
|
|
843
846
|
const step = definition.definition.steps.find(s => s.stepId === pendingTransition.toStepId)
|
|
844
847
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations'
|
|
2
|
+
|
|
3
|
+
const LEGACY_WORKFLOW_ID = 'workflows.checkout-demo'
|
|
4
|
+
const LEGACY_WORKFLOW_NAME = 'Checkout with Payment Webhook'
|
|
5
|
+
const LEGACY_ACTIVITY_ID = 'reserve_inventory'
|
|
6
|
+
const LEGACY_ACTIVITY_URL = '{{env.INVENTORY_SERVICE_URL}}/api/inventory/reserve'
|
|
7
|
+
const TRANSITIONS_REPLACED_BY_CODE_DEFINITION = [
|
|
8
|
+
'cart_to_customer_info',
|
|
9
|
+
'customer_info_to_payment',
|
|
10
|
+
'confirmation_to_order',
|
|
11
|
+
] as const
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* SQL fragment (for the UPDATE `where` clause) that matches ONLY the exact
|
|
15
|
+
* legacy checkout seed: workflow id, name, version, and the presence of the
|
|
16
|
+
* obsolete `reserve_inventory` `CALL_WEBHOOK` activity with its exact env-based
|
|
17
|
+
* URL. The match is intentionally strict so the repair never touches a row that
|
|
18
|
+
* is not the known-broken seed. A tenant whose persisted row diverges from this
|
|
19
|
+
* fingerprint (renamed, re-versioned, or a different webhook URL) is left
|
|
20
|
+
* untouched — the trade-off is safety over coverage for an unknown variant.
|
|
21
|
+
*/
|
|
22
|
+
export function legacyCheckoutPredicate(): string {
|
|
23
|
+
return `
|
|
24
|
+
"wf"."workflow_id" = '${LEGACY_WORKFLOW_ID}'
|
|
25
|
+
and "wf"."workflow_name" = '${LEGACY_WORKFLOW_NAME}'
|
|
26
|
+
and "wf"."version" = 1
|
|
27
|
+
and exists (
|
|
28
|
+
select 1
|
|
29
|
+
from jsonb_array_elements(coalesce("wf"."definition"->'transitions', '[]'::jsonb)) as "transition"("value")
|
|
30
|
+
cross join lateral jsonb_array_elements(coalesce("transition"."value"->'activities', '[]'::jsonb)) as "activity"("value")
|
|
31
|
+
where "activity"."value"->>'activityId' = '${LEGACY_ACTIVITY_ID}'
|
|
32
|
+
and "activity"."value"->>'activityType' = 'CALL_WEBHOOK'
|
|
33
|
+
and "activity"."value"#>>'{config,url}' = '${LEGACY_ACTIVITY_URL}'
|
|
34
|
+
)
|
|
35
|
+
`.trim()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The exact UPDATE the migration runs. Exported so the DB-level regression test
|
|
40
|
+
* (`__integration__/TC-WF-031`) executes the identical SQL against Postgres,
|
|
41
|
+
* making the jsonb transformation itself — not just its rendered string —
|
|
42
|
+
* the thing under test.
|
|
43
|
+
*
|
|
44
|
+
* It rebuilds the `transitions` array in original order, dropping the
|
|
45
|
+
* `activities` key from exactly the three transitions the maintained code
|
|
46
|
+
* definition made side-effect-free. All other keys (e.g. `preConditions`) and
|
|
47
|
+
* all other transitions — including `confirmation_to_end`, whose remaining
|
|
48
|
+
* activities are internal (`CALL_API`/`SEND_EMAIL`/`EMIT_EVENT`) and never trip
|
|
49
|
+
* the outbound URL guard — are preserved verbatim.
|
|
50
|
+
*/
|
|
51
|
+
export function buildLegacyCheckoutRepairSql(): string {
|
|
52
|
+
const strippedTransitionList = TRANSITIONS_REPLACED_BY_CODE_DEFINITION
|
|
53
|
+
.map((id) => `'${id}'`)
|
|
54
|
+
.join(', ')
|
|
55
|
+
return `
|
|
56
|
+
update "workflow_definitions" as "wf"
|
|
57
|
+
set "definition" = jsonb_set(
|
|
58
|
+
"wf"."definition",
|
|
59
|
+
'{transitions}',
|
|
60
|
+
coalesce(
|
|
61
|
+
(
|
|
62
|
+
select jsonb_agg(
|
|
63
|
+
case
|
|
64
|
+
when "transition"."value"->>'transitionId' in (${strippedTransitionList})
|
|
65
|
+
then "transition"."value" - 'activities'
|
|
66
|
+
else "transition"."value"
|
|
67
|
+
end
|
|
68
|
+
order by "transition"."ordinality"
|
|
69
|
+
)
|
|
70
|
+
from jsonb_array_elements(
|
|
71
|
+
coalesce("wf"."definition"->'transitions', '[]'::jsonb)
|
|
72
|
+
) with ordinality as "transition"("value", "ordinality")
|
|
73
|
+
),
|
|
74
|
+
'[]'::jsonb
|
|
75
|
+
)
|
|
76
|
+
),
|
|
77
|
+
"updated_at" = current_timestamp
|
|
78
|
+
where "wf"."code_workflow_id" is null
|
|
79
|
+
and "wf"."deleted_at" is null
|
|
80
|
+
and ${legacyCheckoutPredicate()};
|
|
81
|
+
`.trim()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Repairs the former seeded checkout demo after it was renamed to the same ID
|
|
86
|
+
* as the code-defined workflow. The DB-first lookup shadows the current code
|
|
87
|
+
* definition, including its obsolete external inventory webhook.
|
|
88
|
+
*
|
|
89
|
+
* The persisted row must remain active because historical and in-flight
|
|
90
|
+
* workflow instances reference it by definition_id — a random UUID the
|
|
91
|
+
* code-registry fallback in find-definition.ts deliberately never substitutes.
|
|
92
|
+
* The migration removes the activity arrays from the three transitions that
|
|
93
|
+
* the maintained code definition intentionally made side-effect-free,
|
|
94
|
+
* preserving the row and all instance references while keeping the demo
|
|
95
|
+
* self-contained. (Fresh installs never had this row; they execute the virtual
|
|
96
|
+
* code definition directly via findDefinitionForInstance.)
|
|
97
|
+
*
|
|
98
|
+
* `confirmation_to_end` is deliberately NOT rebuilt: unlike the three stripped
|
|
99
|
+
* transitions it still holds activities in the maintained code definition, and
|
|
100
|
+
* its legacy-only extra (`emit_order_completed`, an internal `EMIT_EVENT`) is
|
|
101
|
+
* harmless — it emits no external request, so leaving it costs nothing and
|
|
102
|
+
* avoids fragile per-activity surgery that could re-introduce a failure.
|
|
103
|
+
*/
|
|
104
|
+
export class Migration20260715120000 extends Migration {
|
|
105
|
+
override async up(): Promise<void> {
|
|
106
|
+
this.addSql(buildLegacyCheckoutRepairSql())
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
override async down(): Promise<void> {
|
|
110
|
+
// Forward-only data repair. The removed demo-only activity payloads cannot
|
|
111
|
+
// be reconstructed safely, and restoring obsolete external webhooks would
|
|
112
|
+
// re-open issue #4179.
|
|
113
|
+
}
|
|
114
|
+
}
|