@shipfox/api-workflows 10.2.0 → 12.0.0
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 +8 -8
- package/CHANGELOG.md +117 -0
- package/README.md +75 -0
- package/dist/core/agent-defaults.d.ts +2 -1
- package/dist/core/agent-defaults.d.ts.map +1 -1
- package/dist/core/agent-defaults.js.map +1 -1
- package/dist/core/checkout.d.ts +7 -21
- package/dist/core/checkout.d.ts.map +1 -1
- package/dist/core/checkout.js +122 -41
- package/dist/core/checkout.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +8 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +13 -1
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job-listener-event.d.ts +2 -0
- package/dist/core/entities/job-listener-event.d.ts.map +1 -1
- package/dist/core/entities/job-listener-event.js.map +1 -1
- package/dist/core/entities/step.d.ts +13 -3
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js +6 -0
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/entities/workflow-run-attempt.d.ts +1 -0
- package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
- package/dist/core/entities/workflow-run-attempt.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +17 -0
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/errors.d.ts +9 -2
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +10 -5
- package/dist/core/errors.js.map +1 -1
- package/dist/core/job-execution.d.ts.map +1 -1
- package/dist/core/job-execution.js +14 -8
- package/dist/core/job-execution.js.map +1 -1
- package/dist/core/job-transition/decide-job-activation.d.ts +1 -0
- package/dist/core/job-transition/decide-job-activation.d.ts.map +1 -1
- package/dist/core/job-transition/decide-job-activation.js +1 -0
- package/dist/core/job-transition/decide-job-activation.js.map +1 -1
- package/dist/core/job-transition/derive-job-success.d.ts +1 -0
- package/dist/core/job-transition/derive-job-success.d.ts.map +1 -1
- package/dist/core/job-transition/derive-job-success.js +5 -8
- package/dist/core/job-transition/derive-job-success.js.map +1 -1
- package/dist/core/listener-execution-materialization.d.ts +6 -3
- package/dist/core/listener-execution-materialization.d.ts.map +1 -1
- package/dist/core/listener-execution-materialization.js +44 -6
- package/dist/core/listener-execution-materialization.js.map +1 -1
- package/dist/core/resolve-trigger-reference.d.ts +11 -0
- package/dist/core/resolve-trigger-reference.d.ts.map +1 -0
- package/dist/core/resolve-trigger-reference.js +39 -0
- package/dist/core/resolve-trigger-reference.js.map +1 -0
- package/dist/core/run-workflow.d.ts +1 -0
- package/dist/core/run-workflow.d.ts.map +1 -1
- package/dist/core/run-workflow.js +1 -0
- package/dist/core/run-workflow.js.map +1 -1
- package/dist/core/step-config/agent.d.ts +1 -2
- package/dist/core/step-config/agent.d.ts.map +1 -1
- package/dist/core/step-config/agent.js +22 -7
- package/dist/core/step-config/agent.js.map +1 -1
- package/dist/core/step-config/assemble-run-context.d.ts +19 -8
- package/dist/core/step-config/assemble-run-context.d.ts.map +1 -1
- package/dist/core/step-config/assemble-run-context.js +51 -19
- package/dist/core/step-config/assemble-run-context.js.map +1 -1
- package/dist/core/step-config/complete-step-dispatch-config.d.ts.map +1 -1
- package/dist/core/step-config/complete-step-dispatch-config.js +72 -4
- package/dist/core/step-config/complete-step-dispatch-config.js.map +1 -1
- package/dist/core/step-config/index.d.ts +1 -0
- package/dist/core/step-config/index.d.ts.map +1 -1
- package/dist/core/step-config/index.js +1 -0
- package/dist/core/step-config/index.js.map +1 -1
- package/dist/core/step-config/materialize-job-execution-steps.d.ts +1 -1
- package/dist/core/step-config/materialize-job-execution-steps.d.ts.map +1 -1
- package/dist/core/step-config/materialize-job-execution-steps.js +49 -8
- package/dist/core/step-config/materialize-job-execution-steps.js.map +1 -1
- package/dist/core/step-config/materialize-workflow-model.d.ts +3 -3
- package/dist/core/step-config/materialize-workflow-model.d.ts.map +1 -1
- package/dist/core/step-config/materialize-workflow-model.js +9 -5
- package/dist/core/step-config/materialize-workflow-model.js.map +1 -1
- package/dist/core/step-config/resolve-job-execution-name.d.ts +3 -3
- package/dist/core/step-config/resolve-job-execution-name.d.ts.map +1 -1
- package/dist/core/step-config/resolve-job-execution-name.js +24 -13
- package/dist/core/step-config/resolve-job-execution-name.js.map +1 -1
- package/dist/core/step-config/resolve-step-config.d.ts.map +1 -1
- package/dist/core/step-config/resolve-step-config.js +174 -9
- package/dist/core/step-config/resolve-step-config.js.map +1 -1
- package/dist/core/step-config/resolve-workflow-run-name.d.ts +19 -0
- package/dist/core/step-config/resolve-workflow-run-name.d.ts.map +1 -0
- package/dist/core/step-config/resolve-workflow-run-name.js +73 -0
- package/dist/core/step-config/resolve-workflow-run-name.js.map +1 -0
- package/dist/core/step-transition/decide-step-transition.js.map +1 -1
- package/dist/core/step-transition/evaluate-gate.d.ts +2 -1
- package/dist/core/step-transition/evaluate-gate.d.ts.map +1 -1
- package/dist/core/step-transition/evaluate-gate.js +5 -3
- package/dist/core/step-transition/evaluate-gate.js.map +1 -1
- package/dist/core/workflow-run-creation.d.ts +3 -0
- package/dist/core/workflow-run-creation.d.ts.map +1 -1
- package/dist/core/workflow-run-creation.js +22 -7
- package/dist/core/workflow-run-creation.js.map +1 -1
- package/dist/db/db.d.ts +278 -10
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +2 -0
- package/dist/db/db.js.map +1 -1
- package/dist/db/index.d.ts +3 -2
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +2 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/job-listener-events.d.ts +2 -0
- package/dist/db/job-listener-events.d.ts.map +1 -1
- package/dist/db/job-listener-events.js +1 -0
- package/dist/db/job-listener-events.js.map +1 -1
- package/dist/db/job-listeners.d.ts +2 -0
- package/dist/db/job-listeners.d.ts.map +1 -1
- package/dist/db/job-listeners.js +81 -21
- package/dist/db/job-listeners.js.map +1 -1
- package/dist/db/schema/job-executions.d.ts +3 -3
- package/dist/db/schema/job-executions.d.ts.map +1 -1
- package/dist/db/schema/job-executions.js +8 -4
- package/dist/db/schema/job-executions.js.map +1 -1
- package/dist/db/schema/job-listener-events.d.ts +20 -0
- package/dist/db/schema/job-listener-events.d.ts.map +1 -1
- package/dist/db/schema/job-listener-events.js +2 -0
- package/dist/db/schema/job-listener-events.js.map +1 -1
- package/dist/db/schema/step-attempts.js +2 -2
- package/dist/db/schema/step-attempts.js.map +1 -1
- package/dist/db/schema/steps.d.ts +4 -2
- package/dist/db/schema/steps.d.ts.map +1 -1
- package/dist/db/schema/steps.js +1 -1
- package/dist/db/schema/steps.js.map +1 -1
- package/dist/db/schema/workflow-run-attempts.d.ts +21 -2
- package/dist/db/schema/workflow-run-attempts.d.ts.map +1 -1
- package/dist/db/schema/workflow-run-attempts.js +2 -0
- package/dist/db/schema/workflow-run-attempts.js.map +1 -1
- package/dist/db/schema/workflow-run-counters.d.ts +43 -0
- package/dist/db/schema/workflow-run-counters.d.ts.map +1 -0
- package/dist/db/schema/workflow-run-counters.js +10 -0
- package/dist/db/schema/workflow-run-counters.js.map +1 -0
- package/dist/db/schema/workflow-runs.d.ts +54 -1
- package/dist/db/schema/workflow-runs.d.ts.map +1 -1
- package/dist/db/schema/workflow-runs.js +10 -2
- package/dist/db/schema/workflow-runs.js.map +1 -1
- package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
- package/dist/db/workflow-runs/job-executions.js +35 -14
- package/dist/db/workflow-runs/job-executions.js.map +1 -1
- package/dist/db/workflow-runs/jobs.d.ts +2 -0
- package/dist/db/workflow-runs/jobs.d.ts.map +1 -1
- package/dist/db/workflow-runs/jobs.js +9 -5
- package/dist/db/workflow-runs/jobs.js.map +1 -1
- package/dist/db/workflow-runs/queries.d.ts +51 -0
- package/dist/db/workflow-runs/queries.d.ts.map +1 -1
- package/dist/db/workflow-runs/queries.js +82 -2
- package/dist/db/workflow-runs/queries.js.map +1 -1
- package/dist/db/workflow-runs/run-create.d.ts +1 -0
- package/dist/db/workflow-runs/run-create.d.ts.map +1 -1
- package/dist/db/workflow-runs/run-create.js +159 -32
- package/dist/db/workflow-runs/run-create.js.map +1 -1
- package/dist/db/workflow-runs/run-rerun.d.ts.map +1 -1
- package/dist/db/workflow-runs/run-rerun.js +13 -5
- package/dist/db/workflow-runs/run-rerun.js.map +1 -1
- package/dist/db/workflow-runs/shared.d.ts +1 -0
- package/dist/db/workflow-runs/shared.d.ts.map +1 -1
- package/dist/db/workflow-runs/shared.js +2 -1
- package/dist/db/workflow-runs/shared.js.map +1 -1
- package/dist/db/workflow-runs/steps.js +3 -3
- package/dist/db/workflow-runs/steps.js.map +1 -1
- package/dist/db/workflow-runs.d.ts +4 -3
- package/dist/db/workflow-runs.d.ts.map +1 -1
- package/dist/db/workflow-runs.js +3 -2
- package/dist/db/workflow-runs.js.map +1 -1
- package/dist/metrics/instance.d.ts +1 -0
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js +9 -0
- package/dist/metrics/instance.js.map +1 -1
- package/dist/presentation/dto/checkout-token.d.ts +1 -0
- package/dist/presentation/dto/checkout-token.d.ts.map +1 -1
- package/dist/presentation/dto/checkout-token.js +1 -0
- package/dist/presentation/dto/checkout-token.js.map +1 -1
- package/dist/presentation/dto/index.d.ts +1 -1
- package/dist/presentation/dto/index.d.ts.map +1 -1
- package/dist/presentation/dto/index.js +1 -1
- package/dist/presentation/dto/index.js.map +1 -1
- package/dist/presentation/dto/step.d.ts.map +1 -1
- package/dist/presentation/dto/step.js +2 -2
- package/dist/presentation/dto/step.js.map +1 -1
- package/dist/presentation/dto/workflow-run.d.ts +3 -1
- package/dist/presentation/dto/workflow-run.d.ts.map +1 -1
- package/dist/presentation/dto/workflow-run.js +34 -0
- package/dist/presentation/dto/workflow-run.js.map +1 -1
- package/dist/presentation/inter-module.d.ts.map +1 -1
- package/dist/presentation/inter-module.js +31 -3
- package/dist/presentation/inter-module.js.map +1 -1
- package/dist/presentation/routes/checkout-token.d.ts +1 -1
- package/dist/presentation/routes/checkout-token.d.ts.map +1 -1
- package/dist/presentation/routes/checkout-token.js +32 -20
- package/dist/presentation/routes/checkout-token.js.map +1 -1
- package/dist/presentation/routes/leased-step.d.ts +2 -0
- package/dist/presentation/routes/leased-step.d.ts.map +1 -1
- package/dist/presentation/routes/leased-step.js +2 -1
- package/dist/presentation/routes/leased-step.js.map +1 -1
- package/dist/presentation/routes/list-runs.d.ts.map +1 -1
- package/dist/presentation/routes/list-runs.js +9 -3
- package/dist/presentation/routes/list-runs.js.map +1 -1
- package/dist/presentation/routes/project-access.d.ts +1 -0
- package/dist/presentation/routes/project-access.d.ts.map +1 -1
- package/dist/presentation/routes/report-step.d.ts.map +1 -1
- package/dist/presentation/routes/report-step.js +9 -2
- package/dist/presentation/routes/report-step.js.map +1 -1
- package/dist/presentation/routes/require-accessible-run.d.ts.map +1 -1
- package/dist/presentation/routes/require-accessible-run.js +1 -1
- package/dist/presentation/routes/require-accessible-run.js.map +1 -1
- package/dist/presentation/subscribers/on-job-steps-settled.js +1 -1
- package/dist/presentation/subscribers/on-job-steps-settled.js.map +1 -1
- package/dist/presentation/subscribers/on-runner-job-claimed.d.ts.map +1 -1
- package/dist/presentation/subscribers/on-runner-job-claimed.js +21 -0
- package/dist/presentation/subscribers/on-runner-job-claimed.js.map +1 -1
- package/dist/temporal/activities/index.d.ts.map +1 -1
- package/dist/temporal/activities/index.js +2 -1
- package/dist/temporal/activities/index.js.map +1 -1
- package/dist/temporal/activities/orchestration-activities.d.ts +1 -0
- package/dist/temporal/activities/orchestration-activities.d.ts.map +1 -1
- package/dist/temporal/activities/orchestration-activities.js.map +1 -1
- package/dist/temporal/constants.d.ts +1 -0
- package/dist/temporal/constants.d.ts.map +1 -1
- package/dist/temporal/constants.js +1 -0
- package/dist/temporal/constants.js.map +1 -1
- package/dist/temporal/index.d.ts +1 -1
- package/dist/temporal/index.d.ts.map +1 -1
- package/dist/temporal/index.js +1 -1
- package/dist/temporal/index.js.map +1 -1
- package/dist/temporal/workflows/index.bundle.js +82 -43
- package/dist/temporal/workflows/index.d.ts +1 -1
- package/dist/temporal/workflows/index.d.ts.map +1 -1
- package/dist/temporal/workflows/index.js +1 -1
- package/dist/temporal/workflows/index.js.map +1 -1
- package/dist/temporal/workflows/job-execution-orchestration.d.ts +5 -0
- package/dist/temporal/workflows/job-execution-orchestration.d.ts.map +1 -1
- package/dist/temporal/workflows/job-execution-orchestration.js +77 -43
- package/dist/temporal/workflows/job-execution-orchestration.js.map +1 -1
- package/dist/temporal/workflows/test-env.d.ts +5 -1
- package/dist/temporal/workflows/test-env.d.ts.map +1 -1
- package/dist/temporal/workflows/test-env.js +32 -11
- package/dist/temporal/workflows/test-env.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0000_initial.sql +14 -2
- package/drizzle/0001_windy_sunfire.sql +1 -0
- package/drizzle/meta/0000_snapshot.json +93 -1
- package/drizzle/meta/0001_snapshot.json +1711 -0
- package/drizzle/meta/_journal.json +2 -2
- package/package.json +21 -21
- package/src/core/agent-defaults.ts +2 -1
- package/src/core/checkout.test.ts +378 -67
- package/src/core/checkout.ts +142 -59
- package/src/core/entities/job-execution.test.ts +21 -0
- package/src/core/entities/job-execution.ts +24 -0
- package/src/core/entities/job-listener-event.ts +3 -0
- package/src/core/entities/step.ts +15 -3
- package/src/core/entities/workflow-run-attempt.ts +1 -0
- package/src/core/entities/workflow-run.ts +16 -0
- package/src/core/errors.ts +21 -7
- package/src/core/job-execution.ts +27 -4
- package/src/core/job-transition/decide-job-activation.test.ts +4 -0
- package/src/core/job-transition/decide-job-activation.ts +2 -0
- package/src/core/job-transition/derive-job-success.test.ts +12 -0
- package/src/core/job-transition/derive-job-success.ts +6 -9
- package/src/core/listener-execution-materialization.test.ts +2 -0
- package/src/core/listener-execution-materialization.ts +57 -11
- package/src/core/resolve-trigger-reference.test.ts +160 -0
- package/src/core/resolve-trigger-reference.ts +53 -0
- package/src/core/run-workflow.ts +2 -0
- package/src/core/step-config/agent.ts +29 -12
- package/src/core/step-config/assemble-run-context.test.ts +149 -6
- package/src/core/step-config/assemble-run-context.ts +87 -25
- package/src/core/step-config/complete-step-dispatch-config.test.ts +125 -2
- package/src/core/step-config/complete-step-dispatch-config.ts +83 -1
- package/src/core/step-config/index.ts +7 -0
- package/src/core/step-config/materialize-job-execution-steps.test.ts +250 -1
- package/src/core/step-config/materialize-job-execution-steps.ts +57 -10
- package/src/core/step-config/materialize-workflow-model.test.ts +212 -4
- package/src/core/step-config/materialize-workflow-model.ts +33 -28
- package/src/core/step-config/resolve-job-execution-name.test.ts +37 -15
- package/src/core/step-config/resolve-job-execution-name.ts +30 -15
- package/src/core/step-config/resolve-step-config.ts +175 -10
- package/src/core/step-config/resolve-workflow-run-name.test.ts +115 -0
- package/src/core/step-config/resolve-workflow-run-name.ts +108 -0
- package/src/core/step-transition/decide-step-transition.ts +2 -2
- package/src/core/step-transition/evaluate-gate.test.ts +8 -0
- package/src/core/step-transition/evaluate-gate.ts +8 -1
- package/src/core/workflow-run-creation.test.ts +7 -4
- package/src/core/workflow-run-creation.ts +13 -7
- package/src/db/db.ts +2 -0
- package/src/db/index.ts +6 -0
- package/src/db/job-listener-events.test.ts +19 -0
- package/src/db/job-listener-events.ts +3 -0
- package/src/db/job-listeners.test.ts +187 -5
- package/src/db/job-listeners.ts +122 -30
- package/src/db/schema/job-executions.ts +12 -5
- package/src/db/schema/job-listener-events.ts +3 -0
- package/src/db/schema/step-attempts.ts +2 -2
- package/src/db/schema/steps.ts +2 -1
- package/src/db/schema/workflow-run-attempts.ts +2 -0
- package/src/db/schema/workflow-run-counters.ts +13 -0
- package/src/db/schema/workflow-runs.ts +11 -2
- package/src/db/workflow-runs/job-executions.ts +37 -15
- package/src/db/workflow-runs/jobs.ts +17 -4
- package/src/db/workflow-runs/queries.test.ts +206 -0
- package/src/db/workflow-runs/queries.ts +146 -2
- package/src/db/workflow-runs/run-create.test.ts +606 -8
- package/src/db/workflow-runs/run-create.ts +185 -40
- package/src/db/workflow-runs/run-rerun.test.ts +61 -2
- package/src/db/workflow-runs/run-rerun.ts +15 -5
- package/src/db/workflow-runs/shared.ts +2 -0
- package/src/db/workflow-runs/steps.ts +3 -3
- package/src/db/workflow-runs.ts +7 -0
- package/src/metrics/instance.ts +14 -0
- package/src/presentation/dto/checkout-token.test.ts +19 -9
- package/src/presentation/dto/checkout-token.ts +2 -1
- package/src/presentation/dto/index.ts +1 -1
- package/src/presentation/dto/job.test.ts +9 -0
- package/src/presentation/dto/step.test.ts +12 -0
- package/src/presentation/dto/step.ts +5 -2
- package/src/presentation/dto/workflow-run.ts +44 -2
- package/src/presentation/inter-module.test.ts +165 -0
- package/src/presentation/inter-module.ts +38 -4
- package/src/presentation/routes/cancel-run.test.ts +1 -1
- package/src/presentation/routes/checkout-token.test.ts +357 -142
- package/src/presentation/routes/checkout-token.ts +56 -26
- package/src/presentation/routes/get-run-aggregates.test.ts +1 -1
- package/src/presentation/routes/get-run.test.ts +29 -1
- package/src/presentation/routes/get-step-secrets.test.ts +7 -2
- package/src/presentation/routes/leased-step.ts +9 -1
- package/src/presentation/routes/list-run-attempts.test.ts +1 -1
- package/src/presentation/routes/list-runs.test.ts +58 -1
- package/src/presentation/routes/list-runs.ts +8 -3
- package/src/presentation/routes/report-step.test.ts +25 -0
- package/src/presentation/routes/report-step.ts +10 -2
- package/src/presentation/routes/require-accessible-run.ts +4 -1
- package/src/presentation/routes/rerun-run.test.ts +1 -1
- package/src/presentation/subscribers/on-job-steps-settled.ts +1 -1
- package/src/presentation/subscribers/on-runner-job-claimed.test.ts +49 -0
- package/src/presentation/subscribers/on-runner-job-claimed.ts +22 -0
- package/src/temporal/activities/index.ts +1 -0
- package/src/temporal/activities/orchestration-activities.ts +1 -0
- package/src/temporal/constants.ts +1 -0
- package/src/temporal/index.ts +1 -0
- package/src/temporal/workflows/index.ts +1 -0
- package/src/temporal/workflows/job-execution-orchestration.test.ts +142 -6
- package/src/temporal/workflows/job-execution-orchestration.ts +99 -50
- package/src/temporal/workflows/run-orchestration.test.ts +1 -1
- package/src/temporal/workflows/test-env.ts +50 -13
- package/test/factories/project.ts +2 -0
- package/test/factories/workflow-model.ts +80 -26
- package/test/factories/workflow-run.ts +4 -1
- package/test/fixtures/agent-inter-module.ts +2 -1
- package/test/fixtures/job-with-steps.ts +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/drizzle/0001_agent_tool_materialization.sql +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { jobExecutionOrchestration, jobFinishedSignal, jobLeaseExpiredSignal, } from './job-execution-orchestration.js';
|
|
1
|
+
export { jobClaimedSignal, jobExecutionOrchestration, jobFinishedSignal, jobLeaseExpiredSignal, } from './job-execution-orchestration.js';
|
|
2
2
|
export { jobListenerOrchestration, listenerEventsAvailableSignal, listenerResolveSignal, } from './job-listener-orchestration.js';
|
|
3
3
|
export { runOrchestration } from './run-orchestration.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/temporal/workflows/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,wBAAwB,EACxB,6BAA6B,EAC7B,qBAAqB,GACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/temporal/workflows/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,wBAAwB,EACxB,6BAA6B,EAC7B,qBAAqB,GACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { jobExecutionOrchestration, jobFinishedSignal, jobLeaseExpiredSignal } from './job-execution-orchestration.js';
|
|
1
|
+
export { jobClaimedSignal, jobExecutionOrchestration, jobFinishedSignal, jobLeaseExpiredSignal } from './job-execution-orchestration.js';
|
|
2
2
|
export { jobListenerOrchestration, listenerEventsAvailableSignal, listenerResolveSignal } from './job-listener-orchestration.js';
|
|
3
3
|
export { runOrchestration } from './run-orchestration.js';
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/temporal/workflows/index.ts"],"sourcesContent":["export {\n jobExecutionOrchestration,\n jobFinishedSignal,\n jobLeaseExpiredSignal,\n} from './job-execution-orchestration.js';\nexport {\n jobListenerOrchestration,\n listenerEventsAvailableSignal,\n listenerResolveSignal,\n} from './job-listener-orchestration.js';\nexport {runOrchestration} from './run-orchestration.js';\n"],"names":["jobExecutionOrchestration","jobFinishedSignal","jobLeaseExpiredSignal","jobListenerOrchestration","listenerEventsAvailableSignal","listenerResolveSignal","runOrchestration"],"mappings":"AAAA,SACEA,yBAAyB,EACzBC,iBAAiB,EACjBC,qBAAqB,QAChB,mCAAmC;AAC1C,SACEC,wBAAwB,EACxBC,6BAA6B,EAC7BC,qBAAqB,QAChB,kCAAkC;AACzC,SAAQC,gBAAgB,QAAO,yBAAyB"}
|
|
1
|
+
{"version":3,"sources":["../../../src/temporal/workflows/index.ts"],"sourcesContent":["export {\n jobClaimedSignal,\n jobExecutionOrchestration,\n jobFinishedSignal,\n jobLeaseExpiredSignal,\n} from './job-execution-orchestration.js';\nexport {\n jobListenerOrchestration,\n listenerEventsAvailableSignal,\n listenerResolveSignal,\n} from './job-listener-orchestration.js';\nexport {runOrchestration} from './run-orchestration.js';\n"],"names":["jobClaimedSignal","jobExecutionOrchestration","jobFinishedSignal","jobLeaseExpiredSignal","jobListenerOrchestration","listenerEventsAvailableSignal","listenerResolveSignal","runOrchestration"],"mappings":"AAAA,SACEA,gBAAgB,EAChBC,yBAAyB,EACzBC,iBAAiB,EACjBC,qBAAqB,QAChB,mCAAmC;AAC1C,SACEC,wBAAwB,EACxBC,6BAA6B,EAC7BC,qBAAqB,QAChB,kCAAkC;AACzC,SAAQC,gBAAgB,QAAO,yBAAyB"}
|
|
@@ -6,6 +6,11 @@ export declare const jobFinishedSignal: import("@temporalio/common").SignalDefin
|
|
|
6
6
|
export declare const jobLeaseExpiredSignal: import("@temporalio/common").SignalDefinition<[{
|
|
7
7
|
jobExecutionId?: string | undefined;
|
|
8
8
|
}], string>;
|
|
9
|
+
export interface JobClaimedSignalPayload {
|
|
10
|
+
jobExecutionId: string;
|
|
11
|
+
claimedAt: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const jobClaimedSignal: import("@temporalio/common").SignalDefinition<[JobClaimedSignalPayload], string>;
|
|
9
14
|
export interface JobExecutionOrchestrationInput {
|
|
10
15
|
workspaceId: string;
|
|
11
16
|
workflowRunId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-execution-orchestration.d.ts","sourceRoot":"","sources":["../../../src/temporal/workflows/job-execution-orchestration.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,0CAA0C,CAAC;AAoDtF,eAAO,MAAM,iBAAiB;YACL,uBAAuB;qBAAmB,MAAM,GAAG,SAAS;WAElF,CAAC;AACJ,eAAO,MAAM,qBAAqB;qBACA,MAAM,GAAG,SAAS;WAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"job-execution-orchestration.d.ts","sourceRoot":"","sources":["../../../src/temporal/workflows/job-execution-orchestration.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,0CAA0C,CAAC;AAoDtF,eAAO,MAAM,iBAAiB;YACL,uBAAuB;qBAAmB,MAAM,GAAG,SAAS;WAElF,CAAC;AACJ,eAAO,MAAM,qBAAqB;qBACA,MAAM,GAAG,SAAS;WAA6B,CAAC;AAClF,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,eAAO,MAAM,gBAAgB,kFAA8D,CAAC;AAE5F,MAAM,WAAW,8BAA8B;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,uBAAuB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAwND,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,+BAA+B,CAAC,CAuD1C"}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { ApplicationFailure } from '@temporalio/common';
|
|
2
2
|
import { condition, defineSignal, log, proxyActivities, setHandler } from '@temporalio/workflow';
|
|
3
3
|
import { hasNoRequiredRunnerLabels, jobExecutionStartOutcome, resolveJobExecutionOutcomeSignal } from '#core/job-execution-outcome.js';
|
|
4
|
-
import { JOB_FINISHED_SIGNAL, JOB_LEASE_EXPIRED_SIGNAL } from '../constants.js';
|
|
4
|
+
import { JOB_CLAIMED_SIGNAL, JOB_FINISHED_SIGNAL, JOB_LEASE_EXPIRED_SIGNAL } from '../constants.js';
|
|
5
|
+
import { remainingMs } from './deadline.js';
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
+
* Enqueue, wait for the runner claim, then wait for one of the terminal facts:
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* └─ neither (6h) → failJobExecutionAsTimedOutActivity + sweep steps (NO release; the
|
|
15
|
-
* TIMED_OUT event drives cooperative cancel, the detector reaps)
|
|
9
|
+
* enqueue ──> PENDING ── job-claimed ──> RUNNING
|
|
10
|
+
* │ │
|
|
11
|
+
* │ timeout ├─ job-finished
|
|
12
|
+
* │ ├─ job-lease-expired
|
|
13
|
+
* ▼ └─ timeout
|
|
14
|
+
* TERMINAL
|
|
16
15
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* The claim signal is the runner-owned lifecycle boundary. The workflow keeps the
|
|
17
|
+
* execution pending while it is queued, then performs the versioned pending → running
|
|
18
|
+
* transition after the current claim. One deadline spans both waits; claim never resets
|
|
19
|
+
* it. Signals can arrive in any order, so the precedence is finished > lease expired >
|
|
20
|
+
* claimed > timeout. Lease cleanup remains best-effort: a runners DB outage must never
|
|
21
|
+
* block the child workflow from returning the job outcome to run-orchestration.
|
|
22
22
|
*/ const { setJobStatus, setJobExecutionStatus, enqueueJobExecutionForRunner, bulkSetStepStatuses, failJobExecutionAsTimedOutActivity, resolveLeaseExpiredJobExecutionActivity } = proxyActivities({
|
|
23
23
|
startToCloseTimeout: '30s'
|
|
24
24
|
});
|
|
@@ -40,6 +40,7 @@ const { releaseLeaseActivity } = proxyActivities({
|
|
|
40
40
|
const DEFAULT_EXECUTION_MAX_DURATION_MS = 6 * 60 * 60 * 1000;
|
|
41
41
|
export const jobFinishedSignal = defineSignal(JOB_FINISHED_SIGNAL);
|
|
42
42
|
export const jobLeaseExpiredSignal = defineSignal(JOB_LEASE_EXPIRED_SIGNAL);
|
|
43
|
+
export const jobClaimedSignal = defineSignal(JOB_CLAIMED_SIGNAL);
|
|
43
44
|
async function releaseLeaseBestEffort(jobExecutionId) {
|
|
44
45
|
try {
|
|
45
46
|
await releaseLeaseActivity({
|
|
@@ -75,10 +76,21 @@ async function resolveJobStatusOrFailClosed(input) {
|
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
|
-
async function
|
|
79
|
+
async function enqueueJobExecution(input) {
|
|
79
80
|
if (hasNoRequiredRunnerLabels(input.requiredLabels)) {
|
|
80
81
|
throw ApplicationFailure.nonRetryable(`Job ${input.jobId} has no required runner labels`, 'EmptyRequiredLabelsError');
|
|
81
82
|
}
|
|
83
|
+
await enqueueJobExecutionForRunner({
|
|
84
|
+
workspaceId: input.workspaceId,
|
|
85
|
+
workflowRunId: input.workflowRunId,
|
|
86
|
+
runAttemptId: input.runAttemptId,
|
|
87
|
+
jobId: input.jobId,
|
|
88
|
+
jobExecutionId: input.jobExecutionId,
|
|
89
|
+
projectId: input.projectId,
|
|
90
|
+
requiredLabels: input.requiredLabels
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async function markJobExecutionRunning(input) {
|
|
82
94
|
const { newVersion: runningVersion, status } = await setJobExecutionStatus({
|
|
83
95
|
jobExecutionId: input.jobExecutionId,
|
|
84
96
|
status: 'running',
|
|
@@ -89,38 +101,27 @@ async function markJobExecutionRunningAndEnqueue(input) {
|
|
|
89
101
|
status
|
|
90
102
|
});
|
|
91
103
|
if (start.kind === 'terminal') return start;
|
|
92
|
-
await enqueueJobExecutionForRunner({
|
|
93
|
-
workspaceId: input.workspaceId,
|
|
94
|
-
workflowRunId: input.workflowRunId,
|
|
95
|
-
runAttemptId: input.runAttemptId,
|
|
96
|
-
jobId: input.jobId,
|
|
97
|
-
jobExecutionId: input.jobExecutionId,
|
|
98
|
-
projectId: input.projectId,
|
|
99
|
-
requiredLabels: input.requiredLabels
|
|
100
|
-
});
|
|
101
104
|
return {
|
|
102
105
|
kind: 'running',
|
|
103
106
|
runningVersion
|
|
104
107
|
};
|
|
105
108
|
}
|
|
106
|
-
|
|
107
|
-
// ordering), so callers must evaluate `finished` before `leaseExpired`.
|
|
108
|
-
async function awaitJobOutcome(jobExecutionId, timeoutMs) {
|
|
109
|
-
let finished;
|
|
110
|
-
let leaseExpired = false;
|
|
109
|
+
function registerJobExecutionSignalHandlers(jobExecutionId, signals) {
|
|
111
110
|
setHandler(jobFinishedSignal, (payload)=>{
|
|
112
111
|
if (payload.jobExecutionId !== undefined && payload.jobExecutionId !== jobExecutionId) return;
|
|
113
|
-
finished ??= payload;
|
|
112
|
+
signals.finished ??= payload;
|
|
114
113
|
});
|
|
115
114
|
setHandler(jobLeaseExpiredSignal, (payload = {})=>{
|
|
116
115
|
if (payload.jobExecutionId !== undefined && payload.jobExecutionId !== jobExecutionId) return;
|
|
117
|
-
leaseExpired = true;
|
|
116
|
+
signals.leaseExpired = true;
|
|
118
117
|
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
setHandler(jobClaimedSignal, (payload)=>{
|
|
119
|
+
if (payload.jobExecutionId !== jobExecutionId) return;
|
|
120
|
+
signals.claimed ??= payload;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async function waitForJobExecutionSignal(signals, deadline, includeClaim) {
|
|
124
|
+
await condition(()=>signals.finished !== undefined || signals.leaseExpired || includeClaim && signals.claimed !== undefined, remainingMs(deadline) ?? 0);
|
|
124
125
|
}
|
|
125
126
|
async function resolveFinishedJobExecution({ input, runningVersion, status }) {
|
|
126
127
|
await setJobExecutionStatus({
|
|
@@ -225,7 +226,43 @@ async function resolveTimedOutJobExecution({ input, runningVersion }) {
|
|
|
225
226
|
};
|
|
226
227
|
}
|
|
227
228
|
export async function jobExecutionOrchestration(input) {
|
|
228
|
-
const
|
|
229
|
+
const signals = {
|
|
230
|
+
finished: undefined,
|
|
231
|
+
leaseExpired: false,
|
|
232
|
+
claimed: undefined
|
|
233
|
+
};
|
|
234
|
+
// Register every signal before enqueue can block or publish a claim/outcome event. The
|
|
235
|
+
// handlers retain signals that arrive while the enqueue activity is in flight.
|
|
236
|
+
registerJobExecutionSignalHandlers(input.jobExecutionId, signals);
|
|
237
|
+
await enqueueJobExecution(input);
|
|
238
|
+
const timeoutMs = input.executionTimeoutMs ?? DEFAULT_EXECUTION_MAX_DURATION_MS;
|
|
239
|
+
const deadline = Date.now() + timeoutMs;
|
|
240
|
+
await waitForJobExecutionSignal(signals, deadline, true);
|
|
241
|
+
// A terminal fact wins over claim even when both signals arrive before the condition
|
|
242
|
+
// resumes. This prevents a late claim from reopening an execution that already finished.
|
|
243
|
+
let resolution = resolveJobExecutionOutcomeSignal(signals);
|
|
244
|
+
if (resolution === 'finished') {
|
|
245
|
+
const { finished } = signals;
|
|
246
|
+
if (finished === undefined) throw new Error('Missing finished signal for finished resolution');
|
|
247
|
+
return resolveFinishedJobExecution({
|
|
248
|
+
input,
|
|
249
|
+
runningVersion: input.executionVersion,
|
|
250
|
+
status: finished.status
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
if (resolution === 'lease-expired') {
|
|
254
|
+
return resolveLeaseExpiredJobExecution({
|
|
255
|
+
input,
|
|
256
|
+
runningVersion: input.executionVersion
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (!signals.claimed) {
|
|
260
|
+
return resolveTimedOutJobExecution({
|
|
261
|
+
input,
|
|
262
|
+
runningVersion: input.executionVersion
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
const running = await markJobExecutionRunning(input);
|
|
229
266
|
if (running.kind === 'terminal') {
|
|
230
267
|
if (input.resolveJobStatus === false) {
|
|
231
268
|
return {
|
|
@@ -236,11 +273,8 @@ export async function jobExecutionOrchestration(input) {
|
|
|
236
273
|
return running.result;
|
|
237
274
|
}
|
|
238
275
|
const { runningVersion } = running;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
// Precedence ladder: a genuinely-finished job is never flipped to failed by a
|
|
242
|
-
// late lease-expiry, so `finished` wins over `leaseExpired`.
|
|
243
|
-
const resolution = resolveJobExecutionOutcomeSignal(signals);
|
|
276
|
+
await waitForJobExecutionSignal(signals, deadline, false);
|
|
277
|
+
resolution = resolveJobExecutionOutcomeSignal(signals);
|
|
244
278
|
if (resolution === 'finished') {
|
|
245
279
|
const { finished } = signals;
|
|
246
280
|
if (finished === undefined) throw new Error('Missing finished signal for finished resolution');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/temporal/workflows/job-execution-orchestration.ts"],"sourcesContent":["import {ApplicationFailure} from '@temporalio/common';\nimport {condition, defineSignal, log, proxyActivities, setHandler} from '@temporalio/workflow';\nimport {\n hasNoRequiredRunnerLabels,\n type JobExecutionOutcomeSignals,\n jobExecutionStartOutcome,\n resolveJobExecutionOutcomeSignal,\n} from '#core/job-execution-outcome.js';\nimport type {RuntimeCompletionStatus} from '#core/workflow-scheduling/runtime-dag.js';\n\nimport type {createOrchestrationActivities} from '../activities/index.js';\nimport {JOB_FINISHED_SIGNAL, JOB_LEASE_EXPIRED_SIGNAL} from '../constants.js';\n\n/**\n * Two signals, one precedence ladder, then best-effort lease release:\n *\n * job-finished (recordStepResult exhausted the steps) ─┐\n * job-lease-expired (runner heartbeat went stale) ─────┤\n * ▼\n * condition(finished ∥ leaseExpired, executionTimeout)\n * ├─ finished → setJobExecutionStatus(status) + resolve job status ─┐\n * ├─ leaseExpired → resolveLeaseExpiredJobExecutionActivity ─┤→ releaseLease\n * └─ neither (6h) → failJobExecutionAsTimedOutActivity + sweep steps (NO release; the\n * TIMED_OUT event drives cooperative cancel, the detector reaps)\n *\n * Both signals can be delivered before condition() resumes (independent outboxes,\n * no ordering), so finished is evaluated FIRST: a genuinely-finished job is never\n * flipped to failed by a late lease-expiry. releaseLease is best-effort — a runners\n * DB outage must never block the child workflow from returning the job outcome to\n * run-orchestration; a leftover lease row is reaped by the stuck detector.\n */\n\nconst {\n setJobStatus,\n setJobExecutionStatus,\n enqueueJobExecutionForRunner,\n bulkSetStepStatuses,\n failJobExecutionAsTimedOutActivity,\n resolveLeaseExpiredJobExecutionActivity,\n} = proxyActivities<ReturnType<typeof createOrchestrationActivities>>({\n startToCloseTimeout: '30s',\n});\n\nconst {resolveJobStatusFromJobExecutionsActivity} = proxyActivities<\n ReturnType<typeof createOrchestrationActivities>\n>({\n startToCloseTimeout: '30s',\n retry: {maximumAttempts: 5},\n});\n\n// Lease cleanup gets a bounded retry policy of its own: after a few attempts the\n// workflow stops waiting on it (see releaseLeaseBestEffort) rather than blocking\n// the DAG on runners-side availability.\nconst {releaseLeaseActivity} = proxyActivities<ReturnType<typeof createOrchestrationActivities>>({\n startToCloseTimeout: '30s',\n retry: {maximumAttempts: 5},\n});\n\nconst DEFAULT_EXECUTION_MAX_DURATION_MS = 6 * 60 * 60 * 1000;\n\nexport const jobFinishedSignal =\n defineSignal<[{status: RuntimeCompletionStatus; jobExecutionId?: string | undefined}]>(\n JOB_FINISHED_SIGNAL,\n );\nexport const jobLeaseExpiredSignal =\n defineSignal<[{jobExecutionId?: string | undefined}]>(JOB_LEASE_EXPIRED_SIGNAL);\n\nexport interface JobExecutionOrchestrationInput {\n workspaceId: string;\n workflowRunId: string;\n runAttemptId: string;\n jobId: string;\n jobExecutionId: string;\n projectId: string;\n jobVersion: number;\n executionVersion: number;\n executionTimeoutMs?: number | null | undefined;\n resolveJobStatus?: boolean | undefined;\n requiredLabels: string[];\n}\n\nexport interface JobExecutionOrchestrationResult {\n status: RuntimeCompletionStatus;\n jobVersion: number;\n}\n\nasync function releaseLeaseBestEffort(jobExecutionId: string): Promise<void> {\n try {\n await releaseLeaseActivity({jobExecutionId});\n } catch (err) {\n log.warn('lease release failed; stuck detector will reap the row', {\n jobExecutionId,\n error: String(err),\n });\n }\n}\n\nasync function resolveJobStatusOrFailClosed(\n input: JobExecutionOrchestrationInput,\n): Promise<{status: RuntimeCompletionStatus; jobVersion: number}> {\n try {\n return await resolveJobStatusFromJobExecutionsActivity({jobId: input.jobId});\n } catch (err) {\n log.error('job status resolution failed; failing job closed', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n error: String(err),\n });\n const {newVersion} = await setJobStatus({\n jobId: input.jobId,\n status: 'failed',\n version: input.jobVersion,\n statusReason: 'step_failed',\n });\n return {status: 'failed', jobVersion: newVersion};\n }\n}\n\nasync function markJobExecutionRunningAndEnqueue(\n input: JobExecutionOrchestrationInput,\n): Promise<\n | {kind: 'running'; runningVersion: number}\n | {kind: 'terminal'; result: JobExecutionOrchestrationResult}\n> {\n if (hasNoRequiredRunnerLabels(input.requiredLabels)) {\n throw ApplicationFailure.nonRetryable(\n `Job ${input.jobId} has no required runner labels`,\n 'EmptyRequiredLabelsError',\n );\n }\n\n const {newVersion: runningVersion, status} = await setJobExecutionStatus({\n jobExecutionId: input.jobExecutionId,\n status: 'running',\n version: input.executionVersion,\n });\n\n const start = jobExecutionStartOutcome({newVersion: runningVersion, status});\n if (start.kind === 'terminal') return start;\n\n await enqueueJobExecutionForRunner({\n workspaceId: input.workspaceId,\n workflowRunId: input.workflowRunId,\n runAttemptId: input.runAttemptId,\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n projectId: input.projectId,\n requiredLabels: input.requiredLabels,\n });\n\n return {kind: 'running', runningVersion};\n}\n\n// Both signals can arrive before condition() resumes (independent outboxes, no\n// ordering), so callers must evaluate `finished` before `leaseExpired`.\nasync function awaitJobOutcome(\n jobExecutionId: string,\n timeoutMs: number,\n): Promise<JobExecutionOutcomeSignals> {\n let finished: {status: RuntimeCompletionStatus; jobExecutionId?: string | undefined} | undefined;\n let leaseExpired = false;\n setHandler(jobFinishedSignal, (payload) => {\n if (payload.jobExecutionId !== undefined && payload.jobExecutionId !== jobExecutionId) return;\n finished ??= payload;\n });\n setHandler(jobLeaseExpiredSignal, (payload = {}) => {\n if (payload.jobExecutionId !== undefined && payload.jobExecutionId !== jobExecutionId) return;\n leaseExpired = true;\n });\n\n await condition(() => finished !== undefined || leaseExpired, timeoutMs);\n\n return {finished, leaseExpired};\n}\n\ninterface JobExecutionResolution {\n input: JobExecutionOrchestrationInput;\n runningVersion: number;\n}\n\nasync function resolveFinishedJobExecution({\n input,\n runningVersion,\n status,\n}: JobExecutionResolution & {\n status: RuntimeCompletionStatus;\n}): Promise<JobExecutionOrchestrationResult> {\n await setJobExecutionStatus({\n jobExecutionId: input.jobExecutionId,\n status: jobExecutionStatusForRuntimeStatus(status),\n version: runningVersion,\n statusReason: status === 'failed' ? 'step_failed' : null,\n });\n if (input.resolveJobStatus === false) {\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'finished',\n status,\n });\n await releaseLeaseBestEffort(input.jobExecutionId);\n return {status, jobVersion: input.jobVersion};\n }\n const resolved = await resolveJobStatusOrFailClosed(input);\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'finished',\n status: resolved.status,\n });\n await releaseLeaseBestEffort(input.jobExecutionId);\n return resolved;\n}\n\nfunction jobExecutionStatusForRuntimeStatus(\n status: RuntimeCompletionStatus,\n): 'succeeded' | 'failed' | 'cancelled' {\n if (status === 'succeeded' || status === 'failed' || status === 'cancelled') return status;\n throw ApplicationFailure.nonRetryable(\n `Job execution cannot be marked ${status}`,\n 'InvalidJobExecutionStatusError',\n );\n}\n\nasync function resolveLeaseExpiredJobExecution({\n input,\n runningVersion,\n}: JobExecutionResolution): Promise<JobExecutionOrchestrationResult> {\n const leaseExpired = await resolveLeaseExpiredJobExecutionActivity({\n jobExecutionId: input.jobExecutionId,\n expectedVersion: runningVersion,\n });\n if (input.resolveJobStatus === false) {\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'lease_expired',\n status: leaseExpired.status,\n });\n await releaseLeaseBestEffort(input.jobExecutionId);\n return {status: leaseExpired.status, jobVersion: input.jobVersion};\n }\n const {status, jobVersion} = await resolveJobStatusOrFailClosed(input);\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'lease_expired',\n status,\n });\n await releaseLeaseBestEffort(input.jobExecutionId);\n return {status, jobVersion};\n}\n\n// Timeout backstop. The activity atomically fails the execution, marks\n// `timed_out_at`, and enqueues WORKFLOWS_JOB_EXECUTION_TIMED_OUT; the runners subscriber\n// then asks the runner to cancel. The lease is intentionally NOT released here.\nasync function resolveTimedOutJobExecution({\n input,\n runningVersion,\n}: JobExecutionResolution): Promise<JobExecutionOrchestrationResult> {\n await failJobExecutionAsTimedOutActivity({\n jobExecutionId: input.jobExecutionId,\n runAttemptId: input.runAttemptId,\n expectedVersion: runningVersion,\n });\n await bulkSetStepStatuses({jobExecutionId: input.jobExecutionId, status: 'failed'});\n if (input.resolveJobStatus === false) {\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'max_duration',\n status: 'failed',\n });\n return {status: 'failed', jobVersion: input.jobVersion};\n }\n const {jobVersion} = await resolveJobStatusOrFailClosed(input);\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'max_duration',\n status: 'failed',\n });\n return {status: 'failed', jobVersion};\n}\n\nexport async function jobExecutionOrchestration(\n input: JobExecutionOrchestrationInput,\n): Promise<JobExecutionOrchestrationResult> {\n const running = await markJobExecutionRunningAndEnqueue(input);\n if (running.kind === 'terminal') {\n if (input.resolveJobStatus === false) {\n return {status: running.result.status, jobVersion: input.jobVersion};\n }\n return running.result;\n }\n const {runningVersion} = running;\n\n const timeoutMs = input.executionTimeoutMs ?? DEFAULT_EXECUTION_MAX_DURATION_MS;\n const signals = await awaitJobOutcome(input.jobExecutionId, timeoutMs);\n\n // Precedence ladder: a genuinely-finished job is never flipped to failed by a\n // late lease-expiry, so `finished` wins over `leaseExpired`.\n const resolution = resolveJobExecutionOutcomeSignal(signals);\n if (resolution === 'finished') {\n const {finished} = signals;\n if (finished === undefined) throw new Error('Missing finished signal for finished resolution');\n\n return resolveFinishedJobExecution({input, runningVersion, status: finished.status});\n }\n if (resolution === 'lease-expired') {\n return resolveLeaseExpiredJobExecution({input, runningVersion});\n }\n return resolveTimedOutJobExecution({input, runningVersion});\n}\n"],"names":["ApplicationFailure","condition","defineSignal","log","proxyActivities","setHandler","hasNoRequiredRunnerLabels","jobExecutionStartOutcome","resolveJobExecutionOutcomeSignal","JOB_FINISHED_SIGNAL","JOB_LEASE_EXPIRED_SIGNAL","setJobStatus","setJobExecutionStatus","enqueueJobExecutionForRunner","bulkSetStepStatuses","failJobExecutionAsTimedOutActivity","resolveLeaseExpiredJobExecutionActivity","startToCloseTimeout","resolveJobStatusFromJobExecutionsActivity","retry","maximumAttempts","releaseLeaseActivity","DEFAULT_EXECUTION_MAX_DURATION_MS","jobFinishedSignal","jobLeaseExpiredSignal","releaseLeaseBestEffort","jobExecutionId","err","warn","error","String","resolveJobStatusOrFailClosed","input","jobId","newVersion","status","version","jobVersion","statusReason","markJobExecutionRunningAndEnqueue","requiredLabels","nonRetryable","runningVersion","executionVersion","start","kind","workspaceId","workflowRunId","runAttemptId","projectId","awaitJobOutcome","timeoutMs","finished","leaseExpired","payload","undefined","resolveFinishedJobExecution","jobExecutionStatusForRuntimeStatus","resolveJobStatus","info","terminationReason","resolved","resolveLeaseExpiredJobExecution","expectedVersion","resolveTimedOutJobExecution","jobExecutionOrchestration","running","result","executionTimeoutMs","signals","resolution","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/temporal/workflows/job-execution-orchestration.ts"],"sourcesContent":["import {ApplicationFailure} from '@temporalio/common';\nimport {condition, defineSignal, log, proxyActivities, setHandler} from '@temporalio/workflow';\nimport {\n hasNoRequiredRunnerLabels,\n type JobExecutionOutcomeSignals,\n jobExecutionStartOutcome,\n resolveJobExecutionOutcomeSignal,\n} from '#core/job-execution-outcome.js';\nimport type {RuntimeCompletionStatus} from '#core/workflow-scheduling/runtime-dag.js';\n\nimport type {createOrchestrationActivities} from '../activities/index.js';\nimport {JOB_CLAIMED_SIGNAL, JOB_FINISHED_SIGNAL, JOB_LEASE_EXPIRED_SIGNAL} from '../constants.js';\nimport {remainingMs} from './deadline.js';\n\n/**\n * Enqueue, wait for the runner claim, then wait for one of the terminal facts:\n *\n * enqueue ──> PENDING ── job-claimed ──> RUNNING\n * │ │\n * │ timeout ├─ job-finished\n * │ ├─ job-lease-expired\n * ▼ └─ timeout\n * TERMINAL\n *\n * The claim signal is the runner-owned lifecycle boundary. The workflow keeps the\n * execution pending while it is queued, then performs the versioned pending → running\n * transition after the current claim. One deadline spans both waits; claim never resets\n * it. Signals can arrive in any order, so the precedence is finished > lease expired >\n * claimed > timeout. Lease cleanup remains best-effort: a runners DB outage must never\n * block the child workflow from returning the job outcome to run-orchestration.\n */\n\nconst {\n setJobStatus,\n setJobExecutionStatus,\n enqueueJobExecutionForRunner,\n bulkSetStepStatuses,\n failJobExecutionAsTimedOutActivity,\n resolveLeaseExpiredJobExecutionActivity,\n} = proxyActivities<ReturnType<typeof createOrchestrationActivities>>({\n startToCloseTimeout: '30s',\n});\n\nconst {resolveJobStatusFromJobExecutionsActivity} = proxyActivities<\n ReturnType<typeof createOrchestrationActivities>\n>({\n startToCloseTimeout: '30s',\n retry: {maximumAttempts: 5},\n});\n\n// Lease cleanup gets a bounded retry policy of its own: after a few attempts the\n// workflow stops waiting on it (see releaseLeaseBestEffort) rather than blocking\n// the DAG on runners-side availability.\nconst {releaseLeaseActivity} = proxyActivities<ReturnType<typeof createOrchestrationActivities>>({\n startToCloseTimeout: '30s',\n retry: {maximumAttempts: 5},\n});\n\nconst DEFAULT_EXECUTION_MAX_DURATION_MS = 6 * 60 * 60 * 1000;\n\nexport const jobFinishedSignal =\n defineSignal<[{status: RuntimeCompletionStatus; jobExecutionId?: string | undefined}]>(\n JOB_FINISHED_SIGNAL,\n );\nexport const jobLeaseExpiredSignal =\n defineSignal<[{jobExecutionId?: string | undefined}]>(JOB_LEASE_EXPIRED_SIGNAL);\nexport interface JobClaimedSignalPayload {\n jobExecutionId: string;\n claimedAt: string;\n}\nexport const jobClaimedSignal = defineSignal<[JobClaimedSignalPayload]>(JOB_CLAIMED_SIGNAL);\n\nexport interface JobExecutionOrchestrationInput {\n workspaceId: string;\n workflowRunId: string;\n runAttemptId: string;\n jobId: string;\n jobExecutionId: string;\n projectId: string;\n jobVersion: number;\n executionVersion: number;\n executionTimeoutMs?: number | null | undefined;\n resolveJobStatus?: boolean | undefined;\n requiredLabels: string[];\n}\n\nexport interface JobExecutionOrchestrationResult {\n status: RuntimeCompletionStatus;\n jobVersion: number;\n}\n\nasync function releaseLeaseBestEffort(jobExecutionId: string): Promise<void> {\n try {\n await releaseLeaseActivity({jobExecutionId});\n } catch (err) {\n log.warn('lease release failed; stuck detector will reap the row', {\n jobExecutionId,\n error: String(err),\n });\n }\n}\n\nasync function resolveJobStatusOrFailClosed(\n input: JobExecutionOrchestrationInput,\n): Promise<{status: RuntimeCompletionStatus; jobVersion: number}> {\n try {\n return await resolveJobStatusFromJobExecutionsActivity({jobId: input.jobId});\n } catch (err) {\n log.error('job status resolution failed; failing job closed', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n error: String(err),\n });\n const {newVersion} = await setJobStatus({\n jobId: input.jobId,\n status: 'failed',\n version: input.jobVersion,\n statusReason: 'step_failed',\n });\n return {status: 'failed', jobVersion: newVersion};\n }\n}\n\nasync function enqueueJobExecution(input: JobExecutionOrchestrationInput): Promise<void> {\n if (hasNoRequiredRunnerLabels(input.requiredLabels)) {\n throw ApplicationFailure.nonRetryable(\n `Job ${input.jobId} has no required runner labels`,\n 'EmptyRequiredLabelsError',\n );\n }\n\n await enqueueJobExecutionForRunner({\n workspaceId: input.workspaceId,\n workflowRunId: input.workflowRunId,\n runAttemptId: input.runAttemptId,\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n projectId: input.projectId,\n requiredLabels: input.requiredLabels,\n });\n}\n\nasync function markJobExecutionRunning(\n input: JobExecutionOrchestrationInput,\n): Promise<\n | {kind: 'running'; runningVersion: number}\n | {kind: 'terminal'; result: JobExecutionOrchestrationResult}\n> {\n const {newVersion: runningVersion, status} = await setJobExecutionStatus({\n jobExecutionId: input.jobExecutionId,\n status: 'running',\n version: input.executionVersion,\n });\n\n const start = jobExecutionStartOutcome({newVersion: runningVersion, status});\n if (start.kind === 'terminal') return start;\n return {kind: 'running', runningVersion};\n}\n\ninterface JobExecutionSignals extends JobExecutionOutcomeSignals {\n claimed: JobClaimedSignalPayload | undefined;\n}\n\nfunction registerJobExecutionSignalHandlers(\n jobExecutionId: string,\n signals: JobExecutionSignals,\n): void {\n setHandler(jobFinishedSignal, (payload) => {\n if (payload.jobExecutionId !== undefined && payload.jobExecutionId !== jobExecutionId) return;\n signals.finished ??= payload;\n });\n setHandler(jobLeaseExpiredSignal, (payload = {}) => {\n if (payload.jobExecutionId !== undefined && payload.jobExecutionId !== jobExecutionId) return;\n signals.leaseExpired = true;\n });\n setHandler(jobClaimedSignal, (payload) => {\n if (payload.jobExecutionId !== jobExecutionId) return;\n signals.claimed ??= payload;\n });\n}\n\nasync function waitForJobExecutionSignal(\n signals: JobExecutionSignals,\n deadline: number,\n includeClaim: boolean,\n): Promise<void> {\n await condition(\n () =>\n signals.finished !== undefined ||\n signals.leaseExpired ||\n (includeClaim && signals.claimed !== undefined),\n remainingMs(deadline) ?? 0,\n );\n}\n\ninterface JobExecutionResolution {\n input: JobExecutionOrchestrationInput;\n runningVersion: number;\n}\n\nasync function resolveFinishedJobExecution({\n input,\n runningVersion,\n status,\n}: JobExecutionResolution & {\n status: RuntimeCompletionStatus;\n}): Promise<JobExecutionOrchestrationResult> {\n await setJobExecutionStatus({\n jobExecutionId: input.jobExecutionId,\n status: jobExecutionStatusForRuntimeStatus(status),\n version: runningVersion,\n statusReason: status === 'failed' ? 'step_failed' : null,\n });\n if (input.resolveJobStatus === false) {\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'finished',\n status,\n });\n await releaseLeaseBestEffort(input.jobExecutionId);\n return {status, jobVersion: input.jobVersion};\n }\n const resolved = await resolveJobStatusOrFailClosed(input);\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'finished',\n status: resolved.status,\n });\n await releaseLeaseBestEffort(input.jobExecutionId);\n return resolved;\n}\n\nfunction jobExecutionStatusForRuntimeStatus(\n status: RuntimeCompletionStatus,\n): 'succeeded' | 'failed' | 'cancelled' {\n if (status === 'succeeded' || status === 'failed' || status === 'cancelled') return status;\n throw ApplicationFailure.nonRetryable(\n `Job execution cannot be marked ${status}`,\n 'InvalidJobExecutionStatusError',\n );\n}\n\nasync function resolveLeaseExpiredJobExecution({\n input,\n runningVersion,\n}: JobExecutionResolution): Promise<JobExecutionOrchestrationResult> {\n const leaseExpired = await resolveLeaseExpiredJobExecutionActivity({\n jobExecutionId: input.jobExecutionId,\n expectedVersion: runningVersion,\n });\n if (input.resolveJobStatus === false) {\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'lease_expired',\n status: leaseExpired.status,\n });\n await releaseLeaseBestEffort(input.jobExecutionId);\n return {status: leaseExpired.status, jobVersion: input.jobVersion};\n }\n const {status, jobVersion} = await resolveJobStatusOrFailClosed(input);\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'lease_expired',\n status,\n });\n await releaseLeaseBestEffort(input.jobExecutionId);\n return {status, jobVersion};\n}\n\n// Timeout backstop. The activity atomically fails the execution, marks\n// `timed_out_at`, and enqueues WORKFLOWS_JOB_EXECUTION_TIMED_OUT; the runners subscriber\n// then asks the runner to cancel. The lease is intentionally NOT released here.\nasync function resolveTimedOutJobExecution({\n input,\n runningVersion,\n}: JobExecutionResolution): Promise<JobExecutionOrchestrationResult> {\n await failJobExecutionAsTimedOutActivity({\n jobExecutionId: input.jobExecutionId,\n runAttemptId: input.runAttemptId,\n expectedVersion: runningVersion,\n });\n await bulkSetStepStatuses({jobExecutionId: input.jobExecutionId, status: 'failed'});\n if (input.resolveJobStatus === false) {\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'max_duration',\n status: 'failed',\n });\n return {status: 'failed', jobVersion: input.jobVersion};\n }\n const {jobVersion} = await resolveJobStatusOrFailClosed(input);\n log.info('job execution terminated', {\n jobId: input.jobId,\n jobExecutionId: input.jobExecutionId,\n terminationReason: 'max_duration',\n status: 'failed',\n });\n return {status: 'failed', jobVersion};\n}\n\nexport async function jobExecutionOrchestration(\n input: JobExecutionOrchestrationInput,\n): Promise<JobExecutionOrchestrationResult> {\n const signals: JobExecutionSignals = {\n finished: undefined,\n leaseExpired: false,\n claimed: undefined,\n };\n // Register every signal before enqueue can block or publish a claim/outcome event. The\n // handlers retain signals that arrive while the enqueue activity is in flight.\n registerJobExecutionSignalHandlers(input.jobExecutionId, signals);\n await enqueueJobExecution(input);\n\n const timeoutMs = input.executionTimeoutMs ?? DEFAULT_EXECUTION_MAX_DURATION_MS;\n const deadline = Date.now() + timeoutMs;\n await waitForJobExecutionSignal(signals, deadline, true);\n\n // A terminal fact wins over claim even when both signals arrive before the condition\n // resumes. This prevents a late claim from reopening an execution that already finished.\n let resolution = resolveJobExecutionOutcomeSignal(signals);\n if (resolution === 'finished') {\n const {finished} = signals;\n if (finished === undefined) throw new Error('Missing finished signal for finished resolution');\n\n return resolveFinishedJobExecution({\n input,\n runningVersion: input.executionVersion,\n status: finished.status,\n });\n }\n if (resolution === 'lease-expired') {\n return resolveLeaseExpiredJobExecution({input, runningVersion: input.executionVersion});\n }\n if (!signals.claimed) {\n return resolveTimedOutJobExecution({input, runningVersion: input.executionVersion});\n }\n\n const running = await markJobExecutionRunning(input);\n if (running.kind === 'terminal') {\n if (input.resolveJobStatus === false) {\n return {status: running.result.status, jobVersion: input.jobVersion};\n }\n return running.result;\n }\n const {runningVersion} = running;\n\n await waitForJobExecutionSignal(signals, deadline, false);\n resolution = resolveJobExecutionOutcomeSignal(signals);\n if (resolution === 'finished') {\n const {finished} = signals;\n if (finished === undefined) throw new Error('Missing finished signal for finished resolution');\n return resolveFinishedJobExecution({input, runningVersion, status: finished.status});\n }\n if (resolution === 'lease-expired') {\n return resolveLeaseExpiredJobExecution({input, runningVersion});\n }\n return resolveTimedOutJobExecution({input, runningVersion});\n}\n"],"names":["ApplicationFailure","condition","defineSignal","log","proxyActivities","setHandler","hasNoRequiredRunnerLabels","jobExecutionStartOutcome","resolveJobExecutionOutcomeSignal","JOB_CLAIMED_SIGNAL","JOB_FINISHED_SIGNAL","JOB_LEASE_EXPIRED_SIGNAL","remainingMs","setJobStatus","setJobExecutionStatus","enqueueJobExecutionForRunner","bulkSetStepStatuses","failJobExecutionAsTimedOutActivity","resolveLeaseExpiredJobExecutionActivity","startToCloseTimeout","resolveJobStatusFromJobExecutionsActivity","retry","maximumAttempts","releaseLeaseActivity","DEFAULT_EXECUTION_MAX_DURATION_MS","jobFinishedSignal","jobLeaseExpiredSignal","jobClaimedSignal","releaseLeaseBestEffort","jobExecutionId","err","warn","error","String","resolveJobStatusOrFailClosed","input","jobId","newVersion","status","version","jobVersion","statusReason","enqueueJobExecution","requiredLabels","nonRetryable","workspaceId","workflowRunId","runAttemptId","projectId","markJobExecutionRunning","runningVersion","executionVersion","start","kind","registerJobExecutionSignalHandlers","signals","payload","undefined","finished","leaseExpired","claimed","waitForJobExecutionSignal","deadline","includeClaim","resolveFinishedJobExecution","jobExecutionStatusForRuntimeStatus","resolveJobStatus","info","terminationReason","resolved","resolveLeaseExpiredJobExecution","expectedVersion","resolveTimedOutJobExecution","jobExecutionOrchestration","timeoutMs","executionTimeoutMs","Date","now","resolution","Error","running","result"],"mappings":"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"}
|
|
@@ -14,8 +14,12 @@ export interface TestConfig {
|
|
|
14
14
|
enqueueError?: string;
|
|
15
15
|
/** If true, enqueueJobExecutionForRunner sends two job-finished signals (for dedup testing) */
|
|
16
16
|
duplicateSignal?: boolean;
|
|
17
|
-
/** If true, enqueueJobExecutionForRunner does nothing
|
|
17
|
+
/** If true, enqueueJobExecutionForRunner does nothing without a signal for timeout testing */
|
|
18
18
|
skipSignal?: boolean;
|
|
19
|
+
/** If true, emit the claim but hold back the terminal outcome (for deadline testing) */
|
|
20
|
+
skipOutcomeSignal?: boolean;
|
|
21
|
+
/** Runner-owned claim timestamp used by the mock claim event */
|
|
22
|
+
claimedAt?: string;
|
|
19
23
|
/** If true, signal job-lease-expired instead of job-finished */
|
|
20
24
|
signalLeaseExpired?: boolean;
|
|
21
25
|
/** If true, signal BOTH job-finished and job-lease-expired (precedence testing) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-env.d.ts","sourceRoot":"","sources":["../../../src/temporal/workflows/test-env.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,uBAAuB,EAAC,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAC,MAAM,EAAC,MAAM,oBAAoB,CAAC;AAC1C,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,0CAA0C,CAAC;AACtF,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EACV,yBAAyB,EACzB,yBAAyB,EACzB,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,2CAA2C,CAAC;AAGtE,QAAA,MAAM,UAAU,uBAAuB,CAAC;AAGxC,OAAO,EAAC,UAAU,EAAC,CAAC;AAOpB,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IACjD,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+FAA+F;IAC/F,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,
|
|
1
|
+
{"version":3,"file":"test-env.d.ts","sourceRoot":"","sources":["../../../src/temporal/workflows/test-env.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { TestWorkflowEnvironment } from '@temporalio/testing';
|
|
3
3
|
import { Worker } from '@temporalio/worker';
|
|
4
|
-
import { JOB_FINISHED_SIGNAL, JOB_LEASE_EXPIRED_SIGNAL } from '../constants.js';
|
|
4
|
+
import { JOB_CLAIMED_SIGNAL, JOB_FINISHED_SIGNAL, JOB_LEASE_EXPIRED_SIGNAL } from '../constants.js';
|
|
5
5
|
const TASK_QUEUE = 'test-orchestration';
|
|
6
6
|
const WORKFLOWS_PATH = resolve(import.meta.dirname, '../../../dist/temporal/workflows/index.js');
|
|
7
7
|
export { TASK_QUEUE };
|
|
@@ -11,12 +11,14 @@ export function setCfg(value) {
|
|
|
11
11
|
}
|
|
12
12
|
export let calls;
|
|
13
13
|
let versionSeq;
|
|
14
|
+
const pendingJobOutcomes = new Map();
|
|
14
15
|
function nextVersion() {
|
|
15
16
|
return ++versionSeq;
|
|
16
17
|
}
|
|
17
18
|
export function resetCalls() {
|
|
18
19
|
calls = [];
|
|
19
20
|
versionSeq = 0;
|
|
21
|
+
pendingJobOutcomes.clear();
|
|
20
22
|
}
|
|
21
23
|
export function callsNamed(name) {
|
|
22
24
|
return calls.filter((c)=>c.name === name);
|
|
@@ -90,7 +92,7 @@ export async function setupEnv() {
|
|
|
90
92
|
workflowsPath: WORKFLOWS_PATH,
|
|
91
93
|
activities: createMockActivities()
|
|
92
94
|
});
|
|
93
|
-
// Run worker in background
|
|
95
|
+
// Run worker in background: it processes tasks until shutdown is called
|
|
94
96
|
workerRunPromise = worker.run();
|
|
95
97
|
workerRunPromise.catch(()=>{
|
|
96
98
|
// Suppress unhandled rejection on shutdown
|
|
@@ -145,12 +147,28 @@ function createMockActivities() {
|
|
|
145
147
|
};
|
|
146
148
|
});
|
|
147
149
|
},
|
|
148
|
-
setJobExecutionStatus: (params)=>{
|
|
150
|
+
setJobExecutionStatus: async (params)=>{
|
|
149
151
|
calls.push({
|
|
150
152
|
name: 'setJobExecutionStatus',
|
|
151
153
|
params
|
|
152
154
|
});
|
|
153
155
|
const status = params.status === 'running' && cfg.runningJobStatus ? cfg.runningJobStatus : params.status;
|
|
156
|
+
if (params.status === 'running') {
|
|
157
|
+
const outcome = pendingJobOutcomes.get(params.jobExecutionId);
|
|
158
|
+
pendingJobOutcomes.delete(params.jobExecutionId);
|
|
159
|
+
if (outcome && status === 'running') {
|
|
160
|
+
await outcome.handle.signal(JOB_FINISHED_SIGNAL, {
|
|
161
|
+
status: outcome.status,
|
|
162
|
+
jobExecutionId: params.jobExecutionId
|
|
163
|
+
});
|
|
164
|
+
if (outcome.duplicateSignal) {
|
|
165
|
+
await outcome.handle.signal(JOB_FINISHED_SIGNAL, {
|
|
166
|
+
status: 'failed',
|
|
167
|
+
jobExecutionId: params.jobExecutionId
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
154
172
|
return {
|
|
155
173
|
newVersion: nextVersion(),
|
|
156
174
|
status
|
|
@@ -183,6 +201,11 @@ function createMockActivities() {
|
|
|
183
201
|
if (cfg.skipSignal) return;
|
|
184
202
|
const status = cfg.jobResults.get(params.jobId) ?? 'succeeded';
|
|
185
203
|
const handle = testEnv.client.workflow.getHandle(`job:${params.jobId}`);
|
|
204
|
+
await handle.signal(JOB_CLAIMED_SIGNAL, {
|
|
205
|
+
jobExecutionId: params.jobExecutionId,
|
|
206
|
+
claimedAt: cfg.claimedAt ?? '2026-06-22T10:05:00.000Z'
|
|
207
|
+
});
|
|
208
|
+
if (cfg.skipOutcomeSignal) return;
|
|
186
209
|
if (cfg.signalLeaseExpired) {
|
|
187
210
|
await handle.signal(JOB_LEASE_EXPIRED_SIGNAL, {
|
|
188
211
|
jobExecutionId: params.jobExecutionId
|
|
@@ -199,16 +222,14 @@ function createMockActivities() {
|
|
|
199
222
|
});
|
|
200
223
|
return;
|
|
201
224
|
}
|
|
202
|
-
|
|
225
|
+
// The terminal outbox is independent from the claim, but the mock delivers it from the
|
|
226
|
+
// pending → running activity. This gives the workflow a deterministic ordering boundary
|
|
227
|
+
// without leaving a polling promise alive after the test finishes.
|
|
228
|
+
pendingJobOutcomes.set(params.jobExecutionId, {
|
|
229
|
+
handle,
|
|
203
230
|
status,
|
|
204
|
-
|
|
231
|
+
duplicateSignal: cfg.duplicateSignal ?? false
|
|
205
232
|
});
|
|
206
|
-
if (cfg.duplicateSignal) {
|
|
207
|
-
await handle.signal(JOB_FINISHED_SIGNAL, {
|
|
208
|
-
status: 'failed',
|
|
209
|
-
jobExecutionId: params.jobExecutionId
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
233
|
},
|
|
213
234
|
resolveLeaseExpiredJobExecutionActivity: (params)=>{
|
|
214
235
|
calls.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/temporal/workflows/test-env.ts"],"sourcesContent":["import {resolve} from 'node:path';\nimport {TestWorkflowEnvironment} from '@temporalio/testing';\nimport {Worker} from '@temporalio/worker';\nimport type {RuntimeCompletionStatus} from '#core/workflow-scheduling/runtime-dag.js';\nimport type {JobActivationDecision} from '#db/index.js';\nimport type {\n ActivateJobListenerResult,\n DrainListenerEventsResult,\n ListenerBufferPeek,\n} from '#db/job-listeners.js';\nimport type {RunDag} from '../activities/orchestration-activities.js';\nimport {JOB_FINISHED_SIGNAL, JOB_LEASE_EXPIRED_SIGNAL} from '../constants.js';\n\nconst TASK_QUEUE = 'test-orchestration';\nconst WORKFLOWS_PATH = resolve(import.meta.dirname, '../../../dist/temporal/workflows/index.js');\n\nexport {TASK_QUEUE};\n\n// ---------------------------------------------------------------------------\n// Shared mutable test config — each test sets this before starting a workflow.\n// Activities read from it via closure. Tests run serially within a describe.\n// ---------------------------------------------------------------------------\n\nexport interface TestConfig {\n dag: RunDag;\n /** Map of jobId → status the mock runner should signal back */\n jobResults: Map<string, RuntimeCompletionStatus>;\n /** If set, enqueueJobExecutionForRunner will throw with this message instead of signaling */\n enqueueError?: string;\n /** If true, enqueueJobExecutionForRunner sends two job-finished signals (for dedup testing) */\n duplicateSignal?: boolean;\n /** If true, enqueueJobExecutionForRunner does nothing (no signal — for timeout testing) */\n skipSignal?: boolean;\n /** If true, signal job-lease-expired instead of job-finished */\n signalLeaseExpired?: boolean;\n /** If true, signal BOTH job-finished and job-lease-expired (precedence testing) */\n signalBoth?: boolean;\n /** Status resolveLeaseExpiredJobExecutionActivity returns (defaults to 'failed') */\n leaseExpiredStatus?: RuntimeCompletionStatus;\n /** If set, resolveJobStatusFromJobExecutionsActivity throws with this message */\n resolveJobStatusError?: string;\n /** If set, releaseLeaseActivity throws (non-retryable) to prove the workflow still returns */\n releaseLeaseError?: string;\n /** Scripted job activation decisions keyed by job id; defaults to start */\n activationDecisions?: Map<string, JobActivationDecision>;\n /** If set, failJobExecutionAsTimedOutActivity throws (for timeout error-path testing) */\n failJobExecutionAsTimedOutError?: string;\n /** Effective status returned by the initial running setRunAttemptStatus call */\n initialRunStatus?: string;\n /** Effective status returned by a running setJobStatus call */\n runningJobStatus?: string;\n /** Result returned by activateJobListenerActivity (defaults to a running listener) */\n listenerActivated?: ActivateJobListenerResult;\n /** Scripted drain results consumed in order; once exhausted, drain returns {kind: 'empty'} */\n drainResults?: DrainListenerEventsResult[];\n /** Scripted listener buffer peeks consumed in order; once exhausted, returns an empty buffer */\n peekResults?: ListenerBufferPeek[];\n /** Result returned by resolveJobListenerActivity (defaults to succeeded) */\n listenerResolved?: {status: 'succeeded' | 'failed'; jobVersion: number};\n}\n\nexport let cfg: TestConfig;\n\nexport function setCfg(value: TestConfig): void {\n cfg = value;\n}\n\n// Track every activity call for assertions\nexport interface ActivityCall {\n name: string;\n params: unknown;\n}\n\nexport let calls: ActivityCall[];\nlet versionSeq: number;\n\nfunction nextVersion(): number {\n return ++versionSeq;\n}\n\nexport function resetCalls(): void {\n calls = [];\n versionSeq = 0;\n}\n\nexport function callsNamed(name: string): ActivityCall[] {\n return calls.filter((c) => c.name === name);\n}\n\nexport function setRunAttemptStatusCalls() {\n return callsNamed('setRunAttemptStatus') as Array<{\n name: string;\n params: {runAttemptId: string; status: string; version: number};\n }>;\n}\n\nexport function setJobStatusCalls() {\n return callsNamed('setJobStatus') as Array<{\n name: string;\n params: {jobId: string; status: string; version: number; statusReason?: string | null};\n }>;\n}\n\nexport function setExecutionStatusCalls() {\n return callsNamed('setJobExecutionStatus') as Array<{\n name: string;\n params: {jobExecutionId: string; status: string; version: number; statusReason?: string | null};\n }>;\n}\n\nexport function listenerFiringOutcomeCalls() {\n return callsNamed('recordListenerFiringOutcomeActivity') as Array<{\n name: string;\n params: {outcome: 'succeeded' | 'failed' | 'cancelled'};\n }>;\n}\n\nexport function resolveJobListenerCalls() {\n return callsNamed('resolveJobListenerActivity') as Array<{\n name: string;\n params: {jobId: string; reason: string};\n }>;\n}\n\nexport function settleListenerCalls() {\n return callsNamed('settleListenerJobExecutionActivity') as Array<{\n name: string;\n params: {jobExecutionId: string; status: 'failed' | 'cancelled'};\n }>;\n}\n\n// ---------------------------------------------------------------------------\n// DAG helpers\n// ---------------------------------------------------------------------------\n\nexport function dagJob(\n id: string,\n key: string,\n deps: string[] = [],\n options: {\n mode?: RunDag['jobs'][number]['mode'] | undefined;\n status?: RunDag['jobs'][number]['status'] | undefined;\n hasActivationCondition?: boolean | undefined;\n } = {},\n): RunDag['jobs'][number] {\n const mode = options.mode ?? 'one_shot';\n return {\n id,\n key,\n mode,\n status: options.status ?? 'pending',\n ...(mode === 'listening' ? {} : {jobExecutionId: id, executionVersion: 1}),\n executionTimeoutMs: null,\n listeningTimeoutMs: null,\n maxExecutions: null,\n onResolve: null,\n hasActivationCondition: options.hasActivationCondition ?? false,\n dependencies: deps,\n runner: ['ubuntu22'],\n version: 1,\n };\n}\n\nexport function makeDag(jobs: RunDag['jobs'], workflowRunId = 'run-1'): RunDag {\n return {\n workflowRunId,\n runAttemptId: `${workflowRunId}-attempt-1`,\n workspaceId: 'workspace-1',\n projectId: 'project-1',\n runVersion: 1,\n runTimeoutMs: 30 * 24 * 60 * 60 * 1000,\n jobs,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Environment lifecycle\n// ---------------------------------------------------------------------------\n\nexport let testEnv: TestWorkflowEnvironment;\nexport let worker: Worker;\n\nlet workerRunPromise: Promise<void> | undefined;\n\nexport async function setupEnv(): Promise<void> {\n testEnv = await TestWorkflowEnvironment.createLocal();\n\n worker = await Worker.create({\n connection: testEnv.nativeConnection,\n taskQueue: TASK_QUEUE,\n workflowsPath: WORKFLOWS_PATH,\n activities: createMockActivities(),\n });\n\n // Run worker in background — it processes tasks until shutdown is called\n workerRunPromise = worker.run();\n workerRunPromise.catch(() => {\n // Suppress unhandled rejection on shutdown\n });\n}\n\nexport async function teardownEnv(): Promise<void> {\n worker?.shutdown();\n await workerRunPromise;\n await testEnv?.teardown();\n}\n\nfunction createMockActivities() {\n return {\n loadRunAttemptDag: (runAttemptId: string): RunDag => {\n calls.push({name: 'loadRunAttemptDag', params: runAttemptId});\n return cfg.dag;\n },\n\n setRunAttemptStatus: (params: {runAttemptId: string; status: string; version: number}) => {\n calls.push({name: 'setRunAttemptStatus', params});\n const status =\n params.status === 'running' && cfg.initialRunStatus ? cfg.initialRunStatus : params.status;\n return {newVersion: nextVersion(), status};\n },\n\n setJobStatus: (params: {\n jobId: string;\n status: string;\n version: number;\n statusReason?: string | null;\n }) => {\n calls.push({name: 'setJobStatus', params});\n const status =\n params.status === 'running' && cfg.runningJobStatus ? cfg.runningJobStatus : params.status;\n return {newVersion: nextVersion(), status};\n },\n\n evaluateJobActivationsActivity: (params: {\n runAttemptId: string;\n jobs: readonly {jobId: string; expectedVersion: number}[];\n }): JobActivationDecision[] => {\n calls.push({name: 'evaluateJobActivationsActivity', params});\n return params.jobs.map((job) => {\n const decision = cfg.activationDecisions?.get(job.jobId);\n return decision ?? {kind: 'start-job', jobId: job.jobId};\n });\n },\n\n setJobExecutionStatus: (params: {\n jobExecutionId: string;\n status: string;\n version: number;\n statusReason?: string | null;\n }) => {\n calls.push({name: 'setJobExecutionStatus', params});\n const status =\n params.status === 'running' && cfg.runningJobStatus ? cfg.runningJobStatus : params.status;\n return {newVersion: nextVersion(), status};\n },\n\n bulkSetStepStatuses: (params: {jobExecutionId: string; status: string}) => {\n calls.push({name: 'bulkSetStepStatuses', params});\n },\n\n cancelRunnerJobsActivity: (params: {jobIds: string[]}) => {\n calls.push({name: 'cancelRunnerJobsActivity', params});\n },\n\n // Scheduling is step-less. The mock runner reports the job outcome by signalling\n // the per-step terminal-completion signal (job-finished) and/or the lease-expiry\n // signal, reproducing the outbox → subscriber → signal rail.\n enqueueJobExecutionForRunner: async (params: {\n workspaceId: string;\n workflowRunId: string;\n runAttemptId: string;\n jobId: string;\n jobExecutionId: string;\n projectId: string;\n requiredLabels: string[];\n }) => {\n calls.push({name: 'enqueueJobExecutionForRunner', params});\n\n if (cfg.enqueueError) {\n const {ApplicationFailure} = await import('@temporalio/common');\n throw ApplicationFailure.nonRetryable(cfg.enqueueError);\n }\n\n if (cfg.skipSignal) return;\n\n const status = cfg.jobResults.get(params.jobId) ?? 'succeeded';\n const handle = testEnv.client.workflow.getHandle(`job:${params.jobId}`);\n\n if (cfg.signalLeaseExpired) {\n await handle.signal(JOB_LEASE_EXPIRED_SIGNAL, {jobExecutionId: params.jobExecutionId});\n return;\n }\n\n if (cfg.signalBoth) {\n await handle.signal(JOB_FINISHED_SIGNAL, {status, jobExecutionId: params.jobExecutionId});\n await handle.signal(JOB_LEASE_EXPIRED_SIGNAL, {jobExecutionId: params.jobExecutionId});\n return;\n }\n\n await handle.signal(JOB_FINISHED_SIGNAL, {status, jobExecutionId: params.jobExecutionId});\n\n if (cfg.duplicateSignal) {\n await handle.signal(JOB_FINISHED_SIGNAL, {\n status: 'failed',\n jobExecutionId: params.jobExecutionId,\n });\n }\n },\n\n resolveLeaseExpiredJobExecutionActivity: (params: {\n jobExecutionId: string;\n expectedVersion: number;\n }) => {\n calls.push({name: 'resolveLeaseExpiredJobExecutionActivity', params});\n return {status: cfg.leaseExpiredStatus ?? 'failed', executionVersion: nextVersion()};\n },\n\n resolveJobStatusFromJobExecutionsActivity: async (params: {jobId: string}) => {\n calls.push({name: 'resolveJobStatusFromJobExecutionsActivity', params});\n if (cfg.resolveJobStatusError) {\n const {ApplicationFailure} = await import('@temporalio/common');\n throw ApplicationFailure.nonRetryable(cfg.resolveJobStatusError);\n }\n const terminalExecutionStatus = [...setExecutionStatusCalls()]\n .reverse()\n .find(\n (call) => call.params.jobExecutionId === params.jobId && call.params.status !== 'running',\n )?.params.status as RuntimeCompletionStatus | undefined;\n return {\n status: cfg.leaseExpiredStatus ?? terminalExecutionStatus ?? 'succeeded',\n jobVersion: nextVersion(),\n };\n },\n\n releaseLeaseActivity: async (params: {jobExecutionId: string}) => {\n calls.push({name: 'releaseLeaseActivity', params});\n if (cfg.releaseLeaseError) {\n const {ApplicationFailure} = await import('@temporalio/common');\n throw ApplicationFailure.nonRetryable(cfg.releaseLeaseError);\n }\n },\n\n failJobExecutionAsTimedOutActivity: async (params: {\n jobExecutionId: string;\n runAttemptId: string;\n expectedVersion: number;\n }) => {\n calls.push({name: 'failJobExecutionAsTimedOutActivity', params});\n if (cfg.failJobExecutionAsTimedOutError) {\n const {ApplicationFailure} = await import('@temporalio/common');\n throw ApplicationFailure.nonRetryable(cfg.failJobExecutionAsTimedOutError);\n }\n return {newVersion: nextVersion()};\n },\n\n activateJobListenerActivity: (params: {jobId: string; expectedVersion: number}) => {\n calls.push({name: 'activateJobListenerActivity', params});\n return (\n cfg.listenerActivated ?? {\n status: 'running',\n jobStatus: 'running',\n jobVersion: nextVersion(),\n executionCount: 0,\n }\n );\n },\n\n // Each call consumes the next scripted drain result. An exhausted script means\n // \"no events buffered\", which parks the listener until a signal or its deadline.\n drainListenerEventsActivity: (params: {\n jobId: string;\n expectedSequence: number;\n maxSize?: number;\n }) => {\n calls.push({name: 'drainListenerEventsActivity', params});\n return cfg.drainResults?.shift() ?? {kind: 'empty'};\n },\n\n peekListenerBufferActivity: (params: {jobId: string}) => {\n calls.push({name: 'peekListenerBufferActivity', params});\n return (\n cfg.peekResults?.shift() ?? {\n fireCount: 0,\n resolvePending: false,\n oldestAgeMs: 0,\n newestAgeMs: 0,\n }\n );\n },\n\n resolveJobListenerActivity: (params: {jobId: string; reason: string}) => {\n calls.push({name: 'resolveJobListenerActivity', params});\n return cfg.listenerResolved ?? {status: 'succeeded', jobVersion: nextVersion()};\n },\n\n settleListenerJobExecutionActivity: (params: {\n jobExecutionId: string;\n status: 'failed' | 'cancelled';\n }) => {\n calls.push({name: 'settleListenerJobExecutionActivity', params});\n },\n\n recordListenerFiringOutcomeActivity: (params: {\n outcome: 'succeeded' | 'failed' | 'cancelled';\n }) => {\n calls.push({name: 'recordListenerFiringOutcomeActivity', params});\n },\n };\n}\n"],"names":["resolve","TestWorkflowEnvironment","Worker","JOB_FINISHED_SIGNAL","JOB_LEASE_EXPIRED_SIGNAL","TASK_QUEUE","WORKFLOWS_PATH","dirname","cfg","setCfg","value","calls","versionSeq","nextVersion","resetCalls","callsNamed","name","filter","c","setRunAttemptStatusCalls","setJobStatusCalls","setExecutionStatusCalls","listenerFiringOutcomeCalls","resolveJobListenerCalls","settleListenerCalls","dagJob","id","key","deps","options","mode","status","jobExecutionId","executionVersion","executionTimeoutMs","listeningTimeoutMs","maxExecutions","onResolve","hasActivationCondition","dependencies","runner","version","makeDag","jobs","workflowRunId","runAttemptId","workspaceId","projectId","runVersion","runTimeoutMs","testEnv","worker","workerRunPromise","setupEnv","createLocal","create","connection","nativeConnection","taskQueue","workflowsPath","activities","createMockActivities","run","catch","teardownEnv","shutdown","teardown","loadRunAttemptDag","push","params","dag","setRunAttemptStatus","initialRunStatus","newVersion","setJobStatus","runningJobStatus","evaluateJobActivationsActivity","map","job","decision","activationDecisions","get","jobId","kind","setJobExecutionStatus","bulkSetStepStatuses","cancelRunnerJobsActivity","enqueueJobExecutionForRunner","enqueueError","ApplicationFailure","nonRetryable","skipSignal","jobResults","handle","client","workflow","getHandle","signalLeaseExpired","signal","signalBoth","duplicateSignal","resolveLeaseExpiredJobExecutionActivity","leaseExpiredStatus","resolveJobStatusFromJobExecutionsActivity","resolveJobStatusError","terminalExecutionStatus","reverse","find","call","jobVersion","releaseLeaseActivity","releaseLeaseError","failJobExecutionAsTimedOutActivity","failJobExecutionAsTimedOutError","activateJobListenerActivity","listenerActivated","jobStatus","executionCount","drainListenerEventsActivity","drainResults","shift","peekListenerBufferActivity","peekResults","fireCount","resolvePending","oldestAgeMs","newestAgeMs","resolveJobListenerActivity","listenerResolved","settleListenerJobExecutionActivity","recordListenerFiringOutcomeActivity"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/temporal/workflows/test-env.ts"],"sourcesContent":["import {resolve} from 'node:path';\nimport {TestWorkflowEnvironment} from '@temporalio/testing';\nimport {Worker} from '@temporalio/worker';\nimport type {RuntimeCompletionStatus} from '#core/workflow-scheduling/runtime-dag.js';\nimport type {JobActivationDecision} from '#db/index.js';\nimport type {\n ActivateJobListenerResult,\n DrainListenerEventsResult,\n ListenerBufferPeek,\n} from '#db/job-listeners.js';\nimport type {RunDag} from '../activities/orchestration-activities.js';\nimport {JOB_CLAIMED_SIGNAL, JOB_FINISHED_SIGNAL, JOB_LEASE_EXPIRED_SIGNAL} from '../constants.js';\n\nconst TASK_QUEUE = 'test-orchestration';\nconst WORKFLOWS_PATH = resolve(import.meta.dirname, '../../../dist/temporal/workflows/index.js');\n\nexport {TASK_QUEUE};\n\n// ---------------------------------------------------------------------------\n// Shared mutable test config: each test sets this before starting a workflow.\n// Activities read from it via closure. Tests run serially within a describe.\n// ---------------------------------------------------------------------------\n\nexport interface TestConfig {\n dag: RunDag;\n /** Map of jobId → status the mock runner should signal back */\n jobResults: Map<string, RuntimeCompletionStatus>;\n /** If set, enqueueJobExecutionForRunner will throw with this message instead of signaling */\n enqueueError?: string;\n /** If true, enqueueJobExecutionForRunner sends two job-finished signals (for dedup testing) */\n duplicateSignal?: boolean;\n /** If true, enqueueJobExecutionForRunner does nothing without a signal for timeout testing */\n skipSignal?: boolean;\n /** If true, emit the claim but hold back the terminal outcome (for deadline testing) */\n skipOutcomeSignal?: boolean;\n /** Runner-owned claim timestamp used by the mock claim event */\n claimedAt?: string;\n /** If true, signal job-lease-expired instead of job-finished */\n signalLeaseExpired?: boolean;\n /** If true, signal BOTH job-finished and job-lease-expired (precedence testing) */\n signalBoth?: boolean;\n /** Status resolveLeaseExpiredJobExecutionActivity returns (defaults to 'failed') */\n leaseExpiredStatus?: RuntimeCompletionStatus;\n /** If set, resolveJobStatusFromJobExecutionsActivity throws with this message */\n resolveJobStatusError?: string;\n /** If set, releaseLeaseActivity throws (non-retryable) to prove the workflow still returns */\n releaseLeaseError?: string;\n /** Scripted job activation decisions keyed by job id; defaults to start */\n activationDecisions?: Map<string, JobActivationDecision>;\n /** If set, failJobExecutionAsTimedOutActivity throws (for timeout error-path testing) */\n failJobExecutionAsTimedOutError?: string;\n /** Effective status returned by the initial running setRunAttemptStatus call */\n initialRunStatus?: string;\n /** Effective status returned by a running setJobStatus call */\n runningJobStatus?: string;\n /** Result returned by activateJobListenerActivity (defaults to a running listener) */\n listenerActivated?: ActivateJobListenerResult;\n /** Scripted drain results consumed in order; once exhausted, drain returns {kind: 'empty'} */\n drainResults?: DrainListenerEventsResult[];\n /** Scripted listener buffer peeks consumed in order; once exhausted, returns an empty buffer */\n peekResults?: ListenerBufferPeek[];\n /** Result returned by resolveJobListenerActivity (defaults to succeeded) */\n listenerResolved?: {status: 'succeeded' | 'failed'; jobVersion: number};\n}\n\nexport let cfg: TestConfig;\n\nexport function setCfg(value: TestConfig): void {\n cfg = value;\n}\n\n// Track every activity call for assertions\nexport interface ActivityCall {\n name: string;\n params: unknown;\n}\n\nexport let calls: ActivityCall[];\nlet versionSeq: number;\nconst pendingJobOutcomes = new Map<\n string,\n {\n handle: ReturnType<typeof testEnv.client.workflow.getHandle>;\n status: RuntimeCompletionStatus;\n duplicateSignal: boolean;\n }\n>();\n\nfunction nextVersion(): number {\n return ++versionSeq;\n}\n\nexport function resetCalls(): void {\n calls = [];\n versionSeq = 0;\n pendingJobOutcomes.clear();\n}\n\nexport function callsNamed(name: string): ActivityCall[] {\n return calls.filter((c) => c.name === name);\n}\n\nexport function setRunAttemptStatusCalls() {\n return callsNamed('setRunAttemptStatus') as Array<{\n name: string;\n params: {runAttemptId: string; status: string; version: number};\n }>;\n}\n\nexport function setJobStatusCalls() {\n return callsNamed('setJobStatus') as Array<{\n name: string;\n params: {jobId: string; status: string; version: number; statusReason?: string | null};\n }>;\n}\n\nexport function setExecutionStatusCalls() {\n return callsNamed('setJobExecutionStatus') as Array<{\n name: string;\n params: {jobExecutionId: string; status: string; version: number; statusReason?: string | null};\n }>;\n}\n\nexport function listenerFiringOutcomeCalls() {\n return callsNamed('recordListenerFiringOutcomeActivity') as Array<{\n name: string;\n params: {outcome: 'succeeded' | 'failed' | 'cancelled'};\n }>;\n}\n\nexport function resolveJobListenerCalls() {\n return callsNamed('resolveJobListenerActivity') as Array<{\n name: string;\n params: {jobId: string; reason: string};\n }>;\n}\n\nexport function settleListenerCalls() {\n return callsNamed('settleListenerJobExecutionActivity') as Array<{\n name: string;\n params: {jobExecutionId: string; status: 'failed' | 'cancelled'};\n }>;\n}\n\n// ---------------------------------------------------------------------------\n// DAG helpers\n// ---------------------------------------------------------------------------\n\nexport function dagJob(\n id: string,\n key: string,\n deps: string[] = [],\n options: {\n mode?: RunDag['jobs'][number]['mode'] | undefined;\n status?: RunDag['jobs'][number]['status'] | undefined;\n hasActivationCondition?: boolean | undefined;\n } = {},\n): RunDag['jobs'][number] {\n const mode = options.mode ?? 'one_shot';\n return {\n id,\n key,\n mode,\n status: options.status ?? 'pending',\n ...(mode === 'listening' ? {} : {jobExecutionId: id, executionVersion: 1}),\n executionTimeoutMs: null,\n listeningTimeoutMs: null,\n maxExecutions: null,\n onResolve: null,\n hasActivationCondition: options.hasActivationCondition ?? false,\n dependencies: deps,\n runner: ['ubuntu22'],\n version: 1,\n };\n}\n\nexport function makeDag(jobs: RunDag['jobs'], workflowRunId = 'run-1'): RunDag {\n return {\n workflowRunId,\n runAttemptId: `${workflowRunId}-attempt-1`,\n workspaceId: 'workspace-1',\n projectId: 'project-1',\n runVersion: 1,\n runTimeoutMs: 30 * 24 * 60 * 60 * 1000,\n jobs,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Environment lifecycle\n// ---------------------------------------------------------------------------\n\nexport let testEnv: TestWorkflowEnvironment;\nexport let worker: Worker;\n\nlet workerRunPromise: Promise<void> | undefined;\n\nexport async function setupEnv(): Promise<void> {\n testEnv = await TestWorkflowEnvironment.createLocal();\n\n worker = await Worker.create({\n connection: testEnv.nativeConnection,\n taskQueue: TASK_QUEUE,\n workflowsPath: WORKFLOWS_PATH,\n activities: createMockActivities(),\n });\n\n // Run worker in background: it processes tasks until shutdown is called\n workerRunPromise = worker.run();\n workerRunPromise.catch(() => {\n // Suppress unhandled rejection on shutdown\n });\n}\n\nexport async function teardownEnv(): Promise<void> {\n worker?.shutdown();\n await workerRunPromise;\n await testEnv?.teardown();\n}\n\nfunction createMockActivities() {\n return {\n loadRunAttemptDag: (runAttemptId: string): RunDag => {\n calls.push({name: 'loadRunAttemptDag', params: runAttemptId});\n return cfg.dag;\n },\n\n setRunAttemptStatus: (params: {runAttemptId: string; status: string; version: number}) => {\n calls.push({name: 'setRunAttemptStatus', params});\n const status =\n params.status === 'running' && cfg.initialRunStatus ? cfg.initialRunStatus : params.status;\n return {newVersion: nextVersion(), status};\n },\n\n setJobStatus: (params: {\n jobId: string;\n status: string;\n version: number;\n statusReason?: string | null;\n }) => {\n calls.push({name: 'setJobStatus', params});\n const status =\n params.status === 'running' && cfg.runningJobStatus ? cfg.runningJobStatus : params.status;\n return {newVersion: nextVersion(), status};\n },\n\n evaluateJobActivationsActivity: (params: {\n runAttemptId: string;\n jobs: readonly {jobId: string; expectedVersion: number}[];\n }): JobActivationDecision[] => {\n calls.push({name: 'evaluateJobActivationsActivity', params});\n return params.jobs.map((job) => {\n const decision = cfg.activationDecisions?.get(job.jobId);\n return decision ?? {kind: 'start-job', jobId: job.jobId};\n });\n },\n\n setJobExecutionStatus: async (params: {\n jobExecutionId: string;\n status: string;\n version: number;\n statusReason?: string | null;\n }) => {\n calls.push({name: 'setJobExecutionStatus', params});\n const status =\n params.status === 'running' && cfg.runningJobStatus ? cfg.runningJobStatus : params.status;\n\n if (params.status === 'running') {\n const outcome = pendingJobOutcomes.get(params.jobExecutionId);\n pendingJobOutcomes.delete(params.jobExecutionId);\n if (outcome && status === 'running') {\n await outcome.handle.signal(JOB_FINISHED_SIGNAL, {\n status: outcome.status,\n jobExecutionId: params.jobExecutionId,\n });\n if (outcome.duplicateSignal) {\n await outcome.handle.signal(JOB_FINISHED_SIGNAL, {\n status: 'failed',\n jobExecutionId: params.jobExecutionId,\n });\n }\n }\n }\n\n return {newVersion: nextVersion(), status};\n },\n\n bulkSetStepStatuses: (params: {jobExecutionId: string; status: string}) => {\n calls.push({name: 'bulkSetStepStatuses', params});\n },\n\n cancelRunnerJobsActivity: (params: {jobIds: string[]}) => {\n calls.push({name: 'cancelRunnerJobsActivity', params});\n },\n\n // Scheduling is step-less. The mock runner reports the job outcome by signalling\n // the per-step terminal-completion signal (job-finished) and/or the lease-expiry\n // signal, reproducing the outbox → subscriber → signal rail.\n enqueueJobExecutionForRunner: async (params: {\n workspaceId: string;\n workflowRunId: string;\n runAttemptId: string;\n jobId: string;\n jobExecutionId: string;\n projectId: string;\n requiredLabels: string[];\n }) => {\n calls.push({name: 'enqueueJobExecutionForRunner', params});\n\n if (cfg.enqueueError) {\n const {ApplicationFailure} = await import('@temporalio/common');\n throw ApplicationFailure.nonRetryable(cfg.enqueueError);\n }\n\n if (cfg.skipSignal) return;\n\n const status = cfg.jobResults.get(params.jobId) ?? 'succeeded';\n const handle = testEnv.client.workflow.getHandle(`job:${params.jobId}`);\n await handle.signal(JOB_CLAIMED_SIGNAL, {\n jobExecutionId: params.jobExecutionId,\n claimedAt: cfg.claimedAt ?? '2026-06-22T10:05:00.000Z',\n });\n\n if (cfg.skipOutcomeSignal) return;\n\n if (cfg.signalLeaseExpired) {\n await handle.signal(JOB_LEASE_EXPIRED_SIGNAL, {jobExecutionId: params.jobExecutionId});\n return;\n }\n\n if (cfg.signalBoth) {\n await handle.signal(JOB_FINISHED_SIGNAL, {status, jobExecutionId: params.jobExecutionId});\n await handle.signal(JOB_LEASE_EXPIRED_SIGNAL, {jobExecutionId: params.jobExecutionId});\n return;\n }\n\n // The terminal outbox is independent from the claim, but the mock delivers it from the\n // pending → running activity. This gives the workflow a deterministic ordering boundary\n // without leaving a polling promise alive after the test finishes.\n pendingJobOutcomes.set(params.jobExecutionId, {\n handle,\n status,\n duplicateSignal: cfg.duplicateSignal ?? false,\n });\n },\n\n resolveLeaseExpiredJobExecutionActivity: (params: {\n jobExecutionId: string;\n expectedVersion: number;\n }) => {\n calls.push({name: 'resolveLeaseExpiredJobExecutionActivity', params});\n return {status: cfg.leaseExpiredStatus ?? 'failed', executionVersion: nextVersion()};\n },\n\n resolveJobStatusFromJobExecutionsActivity: async (params: {jobId: string}) => {\n calls.push({name: 'resolveJobStatusFromJobExecutionsActivity', params});\n if (cfg.resolveJobStatusError) {\n const {ApplicationFailure} = await import('@temporalio/common');\n throw ApplicationFailure.nonRetryable(cfg.resolveJobStatusError);\n }\n const terminalExecutionStatus = [...setExecutionStatusCalls()]\n .reverse()\n .find(\n (call) => call.params.jobExecutionId === params.jobId && call.params.status !== 'running',\n )?.params.status as RuntimeCompletionStatus | undefined;\n return {\n status: cfg.leaseExpiredStatus ?? terminalExecutionStatus ?? 'succeeded',\n jobVersion: nextVersion(),\n };\n },\n\n releaseLeaseActivity: async (params: {jobExecutionId: string}) => {\n calls.push({name: 'releaseLeaseActivity', params});\n if (cfg.releaseLeaseError) {\n const {ApplicationFailure} = await import('@temporalio/common');\n throw ApplicationFailure.nonRetryable(cfg.releaseLeaseError);\n }\n },\n\n failJobExecutionAsTimedOutActivity: async (params: {\n jobExecutionId: string;\n runAttemptId: string;\n expectedVersion: number;\n }) => {\n calls.push({name: 'failJobExecutionAsTimedOutActivity', params});\n if (cfg.failJobExecutionAsTimedOutError) {\n const {ApplicationFailure} = await import('@temporalio/common');\n throw ApplicationFailure.nonRetryable(cfg.failJobExecutionAsTimedOutError);\n }\n return {newVersion: nextVersion()};\n },\n\n activateJobListenerActivity: (params: {jobId: string; expectedVersion: number}) => {\n calls.push({name: 'activateJobListenerActivity', params});\n return (\n cfg.listenerActivated ?? {\n status: 'running',\n jobStatus: 'running',\n jobVersion: nextVersion(),\n executionCount: 0,\n }\n );\n },\n\n // Each call consumes the next scripted drain result. An exhausted script means\n // \"no events buffered\", which parks the listener until a signal or its deadline.\n drainListenerEventsActivity: (params: {\n jobId: string;\n expectedSequence: number;\n maxSize?: number;\n }) => {\n calls.push({name: 'drainListenerEventsActivity', params});\n return cfg.drainResults?.shift() ?? {kind: 'empty'};\n },\n\n peekListenerBufferActivity: (params: {jobId: string}) => {\n calls.push({name: 'peekListenerBufferActivity', params});\n return (\n cfg.peekResults?.shift() ?? {\n fireCount: 0,\n resolvePending: false,\n oldestAgeMs: 0,\n newestAgeMs: 0,\n }\n );\n },\n\n resolveJobListenerActivity: (params: {jobId: string; reason: string}) => {\n calls.push({name: 'resolveJobListenerActivity', params});\n return cfg.listenerResolved ?? {status: 'succeeded', jobVersion: nextVersion()};\n },\n\n settleListenerJobExecutionActivity: (params: {\n jobExecutionId: string;\n status: 'failed' | 'cancelled';\n }) => {\n calls.push({name: 'settleListenerJobExecutionActivity', params});\n },\n\n recordListenerFiringOutcomeActivity: (params: {\n outcome: 'succeeded' | 'failed' | 'cancelled';\n }) => {\n calls.push({name: 'recordListenerFiringOutcomeActivity', params});\n },\n };\n}\n"],"names":["resolve","TestWorkflowEnvironment","Worker","JOB_CLAIMED_SIGNAL","JOB_FINISHED_SIGNAL","JOB_LEASE_EXPIRED_SIGNAL","TASK_QUEUE","WORKFLOWS_PATH","dirname","cfg","setCfg","value","calls","versionSeq","pendingJobOutcomes","Map","nextVersion","resetCalls","clear","callsNamed","name","filter","c","setRunAttemptStatusCalls","setJobStatusCalls","setExecutionStatusCalls","listenerFiringOutcomeCalls","resolveJobListenerCalls","settleListenerCalls","dagJob","id","key","deps","options","mode","status","jobExecutionId","executionVersion","executionTimeoutMs","listeningTimeoutMs","maxExecutions","onResolve","hasActivationCondition","dependencies","runner","version","makeDag","jobs","workflowRunId","runAttemptId","workspaceId","projectId","runVersion","runTimeoutMs","testEnv","worker","workerRunPromise","setupEnv","createLocal","create","connection","nativeConnection","taskQueue","workflowsPath","activities","createMockActivities","run","catch","teardownEnv","shutdown","teardown","loadRunAttemptDag","push","params","dag","setRunAttemptStatus","initialRunStatus","newVersion","setJobStatus","runningJobStatus","evaluateJobActivationsActivity","map","job","decision","activationDecisions","get","jobId","kind","setJobExecutionStatus","outcome","delete","handle","signal","duplicateSignal","bulkSetStepStatuses","cancelRunnerJobsActivity","enqueueJobExecutionForRunner","enqueueError","ApplicationFailure","nonRetryable","skipSignal","jobResults","client","workflow","getHandle","claimedAt","skipOutcomeSignal","signalLeaseExpired","signalBoth","set","resolveLeaseExpiredJobExecutionActivity","leaseExpiredStatus","resolveJobStatusFromJobExecutionsActivity","resolveJobStatusError","terminalExecutionStatus","reverse","find","call","jobVersion","releaseLeaseActivity","releaseLeaseError","failJobExecutionAsTimedOutActivity","failJobExecutionAsTimedOutError","activateJobListenerActivity","listenerActivated","jobStatus","executionCount","drainListenerEventsActivity","drainResults","shift","peekListenerBufferActivity","peekResults","fireCount","resolvePending","oldestAgeMs","newestAgeMs","resolveJobListenerActivity","listenerResolved","settleListenerJobExecutionActivity","recordListenerFiringOutcomeActivity"],"mappings":"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"}
|