@shipfox/api-workflows 12.1.0 → 12.3.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.
Files changed (159) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +45 -0
  3. package/README.md +20 -0
  4. package/dist/config.d.ts +11 -0
  5. package/dist/config.d.ts.map +1 -0
  6. package/dist/config.js +56 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/core/entities/job.d.ts +1 -1
  9. package/dist/core/entities/job.d.ts.map +1 -1
  10. package/dist/core/entities/step.d.ts +2 -2
  11. package/dist/core/entities/step.d.ts.map +1 -1
  12. package/dist/core/errors.d.ts +18 -1
  13. package/dist/core/errors.d.ts.map +1 -1
  14. package/dist/core/errors.js +21 -2
  15. package/dist/core/errors.js.map +1 -1
  16. package/dist/core/step-config/assemble-run-context.d.ts +15 -5
  17. package/dist/core/step-config/assemble-run-context.d.ts.map +1 -1
  18. package/dist/core/step-config/assemble-run-context.js +77 -27
  19. package/dist/core/step-config/assemble-run-context.js.map +1 -1
  20. package/dist/core/step-config/fields.d.ts +5 -0
  21. package/dist/core/step-config/fields.d.ts.map +1 -1
  22. package/dist/core/step-config/fields.js +21 -3
  23. package/dist/core/step-config/fields.js.map +1 -1
  24. package/dist/core/step-config/index.d.ts +1 -1
  25. package/dist/core/step-config/index.d.ts.map +1 -1
  26. package/dist/core/step-config/index.js +1 -1
  27. package/dist/core/step-config/index.js.map +1 -1
  28. package/dist/core/step-config/job-output-limits.d.ts +11 -0
  29. package/dist/core/step-config/job-output-limits.d.ts.map +1 -0
  30. package/dist/core/step-config/job-output-limits.js +88 -0
  31. package/dist/core/step-config/job-output-limits.js.map +1 -0
  32. package/dist/core/step-config/materialize-workflow-model.d.ts +1 -1
  33. package/dist/core/step-config/materialize-workflow-model.d.ts.map +1 -1
  34. package/dist/core/step-config/materialize-workflow-model.js +47 -18
  35. package/dist/core/step-config/materialize-workflow-model.js.map +1 -1
  36. package/dist/db/db.d.ts +50 -50
  37. package/dist/db/index.d.ts +2 -2
  38. package/dist/db/index.d.ts.map +1 -1
  39. package/dist/db/index.js +1 -1
  40. package/dist/db/index.js.map +1 -1
  41. package/dist/db/job-listeners.d.ts.map +1 -1
  42. package/dist/db/job-listeners.js +6 -5
  43. package/dist/db/job-listeners.js.map +1 -1
  44. package/dist/db/schema/job-executions.d.ts +3 -3
  45. package/dist/db/schema/job-listener-events.d.ts +1 -1
  46. package/dist/db/schema/jobs.d.ts +7 -7
  47. package/dist/db/schema/outbox.d.ts +1 -1
  48. package/dist/db/schema/step-attempts.d.ts +2 -2
  49. package/dist/db/schema/steps.d.ts +5 -5
  50. package/dist/db/schema/workflow-run-attempts.d.ts +3 -3
  51. package/dist/db/schema/workflow-run-counters.d.ts +1 -1
  52. package/dist/db/schema/workflow-runs.d.ts +2 -2
  53. package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
  54. package/dist/db/workflow-runs/job-executions.js +4 -2
  55. package/dist/db/workflow-runs/job-executions.js.map +1 -1
  56. package/dist/db/workflow-runs/jobs.d.ts.map +1 -1
  57. package/dist/db/workflow-runs/jobs.js +33 -3
  58. package/dist/db/workflow-runs/jobs.js.map +1 -1
  59. package/dist/db/workflow-runs/outbox.d.ts +1 -0
  60. package/dist/db/workflow-runs/outbox.d.ts.map +1 -1
  61. package/dist/db/workflow-runs/outbox.js +1 -0
  62. package/dist/db/workflow-runs/outbox.js.map +1 -1
  63. package/dist/db/workflow-runs/steps.d.ts +28 -0
  64. package/dist/db/workflow-runs/steps.d.ts.map +1 -1
  65. package/dist/db/workflow-runs/steps.js +49 -1
  66. package/dist/db/workflow-runs/steps.js.map +1 -1
  67. package/dist/db/workflow-runs.d.ts +2 -2
  68. package/dist/db/workflow-runs.d.ts.map +1 -1
  69. package/dist/db/workflow-runs.js +1 -1
  70. package/dist/db/workflow-runs.js.map +1 -1
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +4 -2
  73. package/dist/index.js.map +1 -1
  74. package/dist/metrics/instance.d.ts +1 -0
  75. package/dist/metrics/instance.d.ts.map +1 -1
  76. package/dist/metrics/instance.js +8 -0
  77. package/dist/metrics/instance.js.map +1 -1
  78. package/dist/presentation/dto/evaluation-trace.d.ts +4 -0
  79. package/dist/presentation/dto/evaluation-trace.d.ts.map +1 -0
  80. package/dist/presentation/dto/evaluation-trace.js +39 -0
  81. package/dist/presentation/dto/evaluation-trace.js.map +1 -0
  82. package/dist/presentation/dto/index.d.ts +2 -1
  83. package/dist/presentation/dto/index.d.ts.map +1 -1
  84. package/dist/presentation/dto/index.js +2 -1
  85. package/dist/presentation/dto/index.js.map +1 -1
  86. package/dist/presentation/dto/job.d.ts.map +1 -1
  87. package/dist/presentation/dto/job.js +6 -0
  88. package/dist/presentation/dto/job.js.map +1 -1
  89. package/dist/presentation/dto/step.d.ts +2 -1
  90. package/dist/presentation/dto/step.d.ts.map +1 -1
  91. package/dist/presentation/dto/step.js +12 -0
  92. package/dist/presentation/dto/step.js.map +1 -1
  93. package/dist/presentation/index.d.ts +1 -1
  94. package/dist/presentation/index.d.ts.map +1 -1
  95. package/dist/presentation/index.js +1 -1
  96. package/dist/presentation/index.js.map +1 -1
  97. package/dist/presentation/routes/get-step-attempt-detail.d.ts +3 -0
  98. package/dist/presentation/routes/get-step-attempt-detail.d.ts.map +1 -0
  99. package/dist/presentation/routes/get-step-attempt-detail.js +38 -0
  100. package/dist/presentation/routes/get-step-attempt-detail.js.map +1 -0
  101. package/dist/presentation/routes/index.d.ts.map +1 -1
  102. package/dist/presentation/routes/index.js +2 -0
  103. package/dist/presentation/routes/index.js.map +1 -1
  104. package/dist/presentation/routes/project-access.d.ts +1 -1
  105. package/dist/presentation/subscribers/index.d.ts +1 -0
  106. package/dist/presentation/subscribers/index.d.ts.map +1 -1
  107. package/dist/presentation/subscribers/index.js +1 -0
  108. package/dist/presentation/subscribers/index.js.map +1 -1
  109. package/dist/presentation/subscribers/on-failure-annotations.d.ts +5 -0
  110. package/dist/presentation/subscribers/on-failure-annotations.d.ts.map +1 -0
  111. package/dist/presentation/subscribers/on-failure-annotations.js +160 -0
  112. package/dist/presentation/subscribers/on-failure-annotations.js.map +1 -0
  113. package/dist/temporal/activities/index.d.ts +1 -1
  114. package/dist/temporal/activities/index.d.ts.map +1 -1
  115. package/dist/temporal/activities/orchestration-activities.d.ts +1 -1
  116. package/dist/temporal/activities/orchestration-activities.d.ts.map +1 -1
  117. package/dist/temporal/workflows/test-env.d.ts +2 -2
  118. package/dist/tsconfig.test.tsbuildinfo +1 -1
  119. package/package.json +18 -16
  120. package/src/config.test.ts +85 -0
  121. package/src/config.ts +71 -0
  122. package/src/core/errors.ts +44 -2
  123. package/src/core/job-execution.test.ts +1 -0
  124. package/src/core/step-config/assemble-run-context.test.ts +232 -20
  125. package/src/core/step-config/assemble-run-context.ts +126 -28
  126. package/src/core/step-config/fields.ts +42 -3
  127. package/src/core/step-config/index.ts +2 -0
  128. package/src/core/step-config/job-output-limits.ts +130 -0
  129. package/src/core/step-config/materialize-workflow-model.catalog.test.ts +117 -0
  130. package/src/core/step-config/materialize-workflow-model.test.ts +306 -1
  131. package/src/core/step-config/materialize-workflow-model.ts +65 -18
  132. package/src/db/index.ts +4 -0
  133. package/src/db/job-listeners.ts +14 -3
  134. package/src/db/workflow-runs/job-executions.test.ts +207 -1
  135. package/src/db/workflow-runs/job-executions.ts +15 -2
  136. package/src/db/workflow-runs/job-status.test.ts +10 -3
  137. package/src/db/workflow-runs/jobs.test.ts +49 -0
  138. package/src/db/workflow-runs/jobs.ts +53 -6
  139. package/src/db/workflow-runs/outbox.ts +7 -1
  140. package/src/db/workflow-runs/run-create.test.ts +56 -0
  141. package/src/db/workflow-runs/steps.test.ts +148 -0
  142. package/src/db/workflow-runs/steps.ts +105 -1
  143. package/src/db/workflow-runs.ts +4 -0
  144. package/src/index.ts +9 -0
  145. package/src/metrics/instance.ts +10 -0
  146. package/src/presentation/dto/evaluation-trace.ts +27 -0
  147. package/src/presentation/dto/index.ts +2 -1
  148. package/src/presentation/dto/job.ts +6 -0
  149. package/src/presentation/dto/step.test.ts +96 -1
  150. package/src/presentation/dto/step.ts +17 -0
  151. package/src/presentation/index.ts +2 -0
  152. package/src/presentation/routes/get-step-attempt-detail.test.ts +159 -0
  153. package/src/presentation/routes/get-step-attempt-detail.ts +33 -0
  154. package/src/presentation/routes/index.ts +2 -0
  155. package/src/presentation/subscribers/index.ts +4 -0
  156. package/src/presentation/subscribers/on-failure-annotations.test.ts +510 -0
  157. package/src/presentation/subscribers/on-failure-annotations.ts +237 -0
  158. package/test/factories/workflow-model.ts +2 -0
  159. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/db/workflow-runs/job-executions.ts"],"sourcesContent":["import {readPersistedWorkflowModel} from '@shipfox/api-definitions-dto';\nimport type {SecretsInterModuleClient} from '@shipfox/api-secrets-dto/inter-module';\nimport {WORKFLOWS_JOB_EXECUTION_TIMED_OUT} from '@shipfox/api-workflows-dto';\nimport {and, asc, desc, eq, isNull, notInArray, sql} from 'drizzle-orm';\nimport type {JobStatusReason} from '#core/entities/job.js';\nimport type {JobExecution, JobExecutionStatus} from '#core/entities/job-execution.js';\nimport {InterpolationUnresolvableError, JobNotFoundError} from '#core/errors.js';\nimport {deriveJobExecutionOutputs} from '#core/job-transition/index.js';\nimport {deriveCompletion, isTerminal} from '#core/step-transition/decide-step-transition.js';\nimport type {RuntimeCompletionStatus} from '#core/workflow-scheduling/runtime-dag.js';\nimport {\n recordWorkflowJobExecutionLeaseExpiryResolved,\n recordWorkflowJobExecutionQueued,\n recordWorkflowJobExecutionStarted,\n recordWorkflowJobExecutionStatusChanged,\n recordWorkflowJobExecutionTimedOut,\n} from '#metrics/instance.js';\nimport {db, type Tx} from '../db.js';\nimport {writeWorkflowsOutboxEvent} from '../outbox-writes.js';\nimport {jobExecutions, toJobExecution} from '../schema/job-executions.js';\nimport {jobs, toJob} from '../schema/jobs.js';\nimport {stepAttempts, toStepAttempt} from '../schema/step-attempts.js';\nimport {steps, toStep} from '../schema/steps.js';\nimport {workflowRunAttempts} from '../schema/workflow-run-attempts.js';\nimport {toWorkflowRun, workflowRuns} from '../schema/workflow-runs.js';\nimport {getDirectDependencyJobContexts} from './jobs.js';\nimport {loadReferencedVariables} from './runs.js';\nimport {\n getWorkflowContextForJob,\n optimisticLockRetry,\n TERMINAL_EXECUTION_STATUSES,\n} from './shared.js';\nimport {bulkUpdateStepStatuses, getStepsByJobExecutionIdForUpdate} from './steps.js';\n\nasync function getJobExecutionFallbackName(tx: Tx, jobExecutionId: string): Promise<string> {\n const [row] = await tx\n .select({job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.id, jobExecutionId))\n .limit(1);\n if (!row) throw new JobNotFoundError(jobExecutionId);\n return row.job.name ?? row.job.key;\n}\n\nexport async function getJobExecutionById(id: string, tx?: Tx): Promise<JobExecution | undefined> {\n const rows = await (tx ?? db())\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.id, id))\n .limit(1);\n const row = rows[0];\n return row ? toJobExecution(row.jobExecution, row.job.name ?? row.job.key) : undefined;\n}\n\nexport async function getJobExecutionsByWorkflowRunAttemptId(\n workflowRunAttemptId: string,\n): Promise<JobExecution[]> {\n const rows = await db()\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobs.workflowRunAttemptId, workflowRunAttemptId))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id));\n return rows.map((row) => toJobExecution(row.jobExecution, row.job.name ?? row.job.key));\n}\n\nexport async function getJobExecutionsByJobId(jobId: string): Promise<JobExecution[]> {\n const rows = await db()\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.jobId, jobId))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id));\n return rows.map((row) => toJobExecution(row.jobExecution, row.job.name ?? row.job.key));\n}\n\nexport async function getFirstJobExecutionByJobId(\n jobId: string,\n tx?: Tx,\n): Promise<JobExecution | undefined> {\n const rows = await (tx ?? db())\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.jobId, jobId))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id))\n .limit(1);\n const row = rows[0];\n return row ? toJobExecution(row.jobExecution, row.job.name ?? row.job.key) : undefined;\n}\n\nexport async function getLatestJobExecutionByJobId(\n jobId: string,\n tx?: Tx,\n): Promise<JobExecution | undefined> {\n const rows = await (tx ?? db())\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.jobId, jobId))\n .orderBy(desc(jobExecutions.sequence), desc(jobExecutions.id))\n .limit(1);\n const row = rows[0];\n return row ? toJobExecution(row.jobExecution, row.job.name ?? row.job.key) : undefined;\n}\n\nexport interface UpdateJobExecutionStatusAtVersionParams {\n jobExecutionId: string;\n status: JobExecutionStatus;\n expectedVersion: number;\n statusReason?: JobStatusReason | null | undefined;\n markTimedOut?: boolean;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n}\n\nasync function resolveJobExecutionOutputs(\n tx: Tx,\n params: {\n jobExecutionId: string;\n status: JobExecutionStatus;\n statusReason: JobStatusReason | null;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n },\n): Promise<Record<string, unknown> | null> {\n const [target] = await tx\n .select({execution: jobExecutions, job: jobs, attempt: workflowRunAttempts, run: workflowRuns})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(eq(jobExecutions.id, params.jobExecutionId))\n .limit(1);\n if (!target) throw new JobNotFoundError(params.jobExecutionId);\n const model =\n target.attempt.model === null ? null : readPersistedWorkflowModel(target.attempt.model);\n if (!model) return null;\n const modelJob = model.jobs.find((job) => job.key === target.job.key);\n if (!modelJob || modelJob.outputs === undefined) return null;\n\n const executionRows = await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.jobId, target.job.id))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id));\n const fallbackName = target.job.name ?? target.job.key;\n const executions = executionRows.map((row) =>\n row.id === target.execution.id\n ? toJobExecution(\n {...row, status: params.status, statusReason: params.statusReason},\n fallbackName,\n )\n : toJobExecution(row, fallbackName),\n );\n const jobExecution = executions.find((execution) => execution.id === target.execution.id);\n if (!jobExecution) throw new JobNotFoundError(params.jobExecutionId);\n\n const stepRows = await tx\n .select()\n .from(steps)\n .where(eq(steps.jobExecutionId, params.jobExecutionId))\n .orderBy(asc(steps.position), asc(steps.id));\n const attemptRows = await tx\n .select()\n .from(stepAttempts)\n .where(eq(stepAttempts.jobExecutionId, params.jobExecutionId))\n .orderBy(asc(stepAttempts.executionOrder), asc(stepAttempts.id));\n const dependencyJobs = await getDirectDependencyJobContexts(target.job.id, tx);\n\n return deriveJobExecutionOutputs({\n run: toWorkflowRun(target.run),\n modelJob,\n job: toJob(target.job),\n jobExecution,\n executions,\n steps: stepRows.map(toStep),\n attempts: attemptRows.map(toStepAttempt),\n jobs: dependencyJobs,\n vars: await loadReferencedVariables({\n model,\n jobs: [modelJob],\n workspaceId: target.run.workspaceId,\n projectId: target.run.projectId,\n definitionId: target.run.definitionId,\n secrets: params.secrets,\n }),\n });\n}\n\nasync function updateJobExecutionStatusAtVersion(\n tx: Tx,\n params: UpdateJobExecutionStatusAtVersionParams,\n): Promise<{execution: JobExecution; changed: boolean} | null> {\n let status = params.status;\n let statusReason = params.statusReason ?? null;\n let outputs: Record<string, unknown> | null | undefined;\n if (TERMINAL_EXECUTION_STATUSES.includes(status)) {\n outputs = null;\n }\n if (status === 'succeeded') {\n try {\n outputs = await resolveJobExecutionOutputs(tx, {\n jobExecutionId: params.jobExecutionId,\n status,\n statusReason,\n secrets: params.secrets,\n });\n } catch (error) {\n if (!(error instanceof InterpolationUnresolvableError)) throw error;\n status = 'failed';\n statusReason = 'unknown';\n outputs = null;\n }\n }\n\n const rows = await tx\n .update(jobExecutions)\n .set({\n status,\n statusReason,\n ...(outputs === undefined ? {} : {outputs}),\n version: sql`${jobExecutions.version} + 1`,\n updatedAt: new Date(),\n ...(params.markTimedOut ? {timedOutAt: new Date()} : {}),\n ...(TERMINAL_EXECUTION_STATUSES.includes(status) ? {finishedAt: sql`now()`} : {}),\n })\n .where(\n and(\n eq(jobExecutions.id, params.jobExecutionId),\n eq(jobExecutions.version, params.expectedVersion),\n notInArray(jobExecutions.status, TERMINAL_EXECUTION_STATUSES),\n ),\n )\n .returning();\n\n const row = rows[0];\n if (!row) return null;\n return {\n execution: toJobExecution(row, await getJobExecutionFallbackName(tx, row.id)),\n changed: true,\n };\n}\n\nexport interface UpdateJobExecutionStatusParams {\n jobExecutionId: string;\n status: JobExecutionStatus;\n expectedVersion: number;\n statusReason?: JobStatusReason | null | undefined;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n}\n\nexport async function updateJobExecutionStatus(\n params: UpdateJobExecutionStatusParams,\n): Promise<JobExecution> {\n const statusReason = params.statusReason ?? null;\n const result = await db().transaction(async (tx) => {\n return await optimisticLockRetry({\n updateFn: () =>\n updateJobExecutionStatusAtVersion(tx, {\n jobExecutionId: params.jobExecutionId,\n status: params.status,\n expectedVersion: params.expectedVersion,\n statusReason,\n secrets: params.secrets,\n }),\n fetchFn: async () => {\n const row = (\n await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.id, params.jobExecutionId))\n .limit(1)\n )[0];\n return row ? toJobExecution(row, await getJobExecutionFallbackName(tx, row.id)) : undefined;\n },\n matchFn: (execution) =>\n (execution.status === params.status && execution.statusReason === statusReason) ||\n TERMINAL_EXECUTION_STATUSES.includes(execution.status)\n ? {execution, changed: false}\n : null,\n failureMessage: `Optimistic lock failure: job execution ${params.jobExecutionId} version ${params.expectedVersion}`,\n });\n });\n\n if (result.changed) recordWorkflowJobExecutionStatusChanged(result.execution.status);\n\n return result.execution;\n}\n\nexport async function recordJobExecutionQueuedAt(params: {\n jobExecutionId: string;\n queuedAt: Date;\n}): Promise<void> {\n const updated = await db()\n .update(jobExecutions)\n .set({queuedAt: params.queuedAt})\n .where(and(eq(jobExecutions.id, params.jobExecutionId), isNull(jobExecutions.queuedAt)))\n .returning({id: jobExecutions.id});\n\n if (updated.length > 0) recordWorkflowJobExecutionQueued();\n}\n\nexport async function recordJobExecutionStartedAt(params: {\n jobExecutionId: string;\n startedAt: Date;\n}): Promise<void> {\n const updated = await db()\n .update(jobExecutions)\n .set({startedAt: params.startedAt})\n .where(and(eq(jobExecutions.id, params.jobExecutionId), isNull(jobExecutions.startedAt)))\n .returning({id: jobExecutions.id});\n\n if (updated.length > 0) recordWorkflowJobExecutionStarted();\n}\n\nexport async function failJobExecutionAsTimedOut(params: {\n jobExecutionId: string;\n workflowRunAttemptId: string;\n expectedVersion: number;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n}): Promise<JobExecution> {\n const result = await db().transaction(async (tx) => {\n const updated = await optimisticLockRetry({\n updateFn: () =>\n updateJobExecutionStatusAtVersion(tx, {\n jobExecutionId: params.jobExecutionId,\n status: 'failed',\n expectedVersion: params.expectedVersion,\n statusReason: 'timed_out',\n markTimedOut: true,\n secrets: params.secrets,\n }),\n fetchFn: async () => {\n const row = (\n await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.id, params.jobExecutionId))\n .limit(1)\n )[0];\n return row ? toJobExecution(row, await getJobExecutionFallbackName(tx, row.id)) : undefined;\n },\n matchFn: (execution) => (execution.timedOutAt !== null ? {execution, changed: false} : null),\n failureMessage: `Optimistic lock failure: job execution ${params.jobExecutionId} version ${params.expectedVersion}`,\n });\n\n if (updated.changed) {\n const identity = await getWorkflowContextForJob(updated.execution.jobId, tx);\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_JOB_EXECUTION_TIMED_OUT,\n payload: {\n jobId: identity.jobId,\n jobExecutionId: params.jobExecutionId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n },\n });\n }\n\n return updated;\n });\n\n if (result.changed) {\n recordWorkflowJobExecutionStatusChanged(result.execution.status);\n recordWorkflowJobExecutionTimedOut();\n }\n\n return result.execution;\n}\n\nexport async function resolveJobExecutionAfterLeaseExpiry(params: {\n jobExecutionId: string;\n expectedVersion: number;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n}): Promise<{status: RuntimeCompletionStatus; executionVersion: number}> {\n const result = await db().transaction(async (tx) => {\n const jobExecutionSteps = await getStepsByJobExecutionIdForUpdate(params.jobExecutionId, tx);\n let changedJobExecution: JobExecution | null = null;\n\n if (jobExecutionSteps.length === 0) {\n throw new JobNotFoundError(params.jobExecutionId);\n }\n\n if (jobExecutionSteps.every((step) => isTerminal(step.status))) {\n const status = deriveCompletion(jobExecutionSteps);\n const updated = await updateJobExecutionStatusAtVersion(tx, {\n jobExecutionId: params.jobExecutionId,\n status,\n expectedVersion: params.expectedVersion,\n statusReason: statusReasonForStepCompletion(status),\n secrets: params.secrets,\n });\n changedJobExecution = updated?.changed ? updated.execution : null;\n } else {\n const updated = await updateJobExecutionStatusAtVersion(tx, {\n jobExecutionId: params.jobExecutionId,\n status: 'failed',\n expectedVersion: params.expectedVersion,\n statusReason: 'runner_lost',\n secrets: params.secrets,\n });\n if (updated?.changed) {\n changedJobExecution = updated.execution;\n await bulkUpdateStepStatuses(\n {jobExecutionId: params.jobExecutionId, status: 'cancelled'},\n tx,\n );\n }\n }\n\n const jobExecutionRow = (\n await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.id, params.jobExecutionId))\n .limit(1)\n )[0];\n if (!jobExecutionRow) {\n throw new Error(`Job execution not found resolving lease expiry: ${params.jobExecutionId}`);\n }\n const status: RuntimeCompletionStatus =\n jobExecutionRow.status === 'succeeded' ? 'succeeded' : 'failed';\n return {status, executionVersion: jobExecutionRow.version, changedJobExecution};\n });\n\n recordWorkflowJobExecutionLeaseExpiryResolved(result.status);\n if (result.changedJobExecution) {\n recordWorkflowJobExecutionStatusChanged(result.changedJobExecution.status);\n }\n\n return {status: result.status, executionVersion: result.executionVersion};\n}\n\nfunction statusReasonForStepCompletion(status: RuntimeCompletionStatus): JobStatusReason | null {\n return status === 'failed' ? 'step_failed' : null;\n}\n"],"names":["readPersistedWorkflowModel","WORKFLOWS_JOB_EXECUTION_TIMED_OUT","and","asc","desc","eq","isNull","notInArray","sql","InterpolationUnresolvableError","JobNotFoundError","deriveJobExecutionOutputs","deriveCompletion","isTerminal","recordWorkflowJobExecutionLeaseExpiryResolved","recordWorkflowJobExecutionQueued","recordWorkflowJobExecutionStarted","recordWorkflowJobExecutionStatusChanged","recordWorkflowJobExecutionTimedOut","db","writeWorkflowsOutboxEvent","jobExecutions","toJobExecution","jobs","toJob","stepAttempts","toStepAttempt","steps","toStep","workflowRunAttempts","toWorkflowRun","workflowRuns","getDirectDependencyJobContexts","loadReferencedVariables","getWorkflowContextForJob","optimisticLockRetry","TERMINAL_EXECUTION_STATUSES","bulkUpdateStepStatuses","getStepsByJobExecutionIdForUpdate","getJobExecutionFallbackName","tx","jobExecutionId","row","select","job","from","innerJoin","jobId","id","where","limit","name","key","getJobExecutionById","rows","jobExecution","undefined","getJobExecutionsByWorkflowRunAttemptId","workflowRunAttemptId","orderBy","sequence","map","getJobExecutionsByJobId","getFirstJobExecutionByJobId","getLatestJobExecutionByJobId","resolveJobExecutionOutputs","params","target","execution","attempt","run","workflowRunId","model","modelJob","find","outputs","executionRows","fallbackName","executions","status","statusReason","stepRows","position","attemptRows","executionOrder","dependencyJobs","attempts","vars","workspaceId","projectId","definitionId","secrets","updateJobExecutionStatusAtVersion","includes","error","update","set","version","updatedAt","Date","markTimedOut","timedOutAt","finishedAt","expectedVersion","returning","changed","updateJobExecutionStatus","result","transaction","updateFn","fetchFn","matchFn","failureMessage","recordJobExecutionQueuedAt","updated","queuedAt","length","recordJobExecutionStartedAt","startedAt","failJobExecutionAsTimedOut","identity","type","payload","resolveJobExecutionAfterLeaseExpiry","jobExecutionSteps","changedJobExecution","every","step","statusReasonForStepCompletion","jobExecutionRow","Error","executionVersion"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/db/workflow-runs/job-executions.ts"],"sourcesContent":["import {readPersistedWorkflowModel} from '@shipfox/api-definitions-dto';\nimport type {SecretsInterModuleClient} from '@shipfox/api-secrets-dto/inter-module';\nimport {WORKFLOWS_JOB_EXECUTION_TIMED_OUT} from '@shipfox/api-workflows-dto';\nimport {and, asc, desc, eq, isNull, notInArray, sql} from 'drizzle-orm';\nimport type {JobStatusReason} from '#core/entities/job.js';\nimport type {JobExecution, JobExecutionStatus} from '#core/entities/job-execution.js';\nimport {\n InterpolationUnresolvableError,\n JobNotFoundError,\n JobOutputNotJsonSafeError,\n JobOutputTooLargeError,\n JobOutputTooManyEntriesError,\n} from '#core/errors.js';\nimport {deriveJobExecutionOutputs} from '#core/job-transition/index.js';\nimport {deriveCompletion, isTerminal} from '#core/step-transition/decide-step-transition.js';\nimport type {RuntimeCompletionStatus} from '#core/workflow-scheduling/runtime-dag.js';\nimport {\n recordWorkflowJobExecutionLeaseExpiryResolved,\n recordWorkflowJobExecutionQueued,\n recordWorkflowJobExecutionStarted,\n recordWorkflowJobExecutionStatusChanged,\n recordWorkflowJobExecutionTimedOut,\n} from '#metrics/instance.js';\nimport {db, type Tx} from '../db.js';\nimport {writeWorkflowsOutboxEvent} from '../outbox-writes.js';\nimport {jobExecutions, toJobExecution} from '../schema/job-executions.js';\nimport {jobs, toJob} from '../schema/jobs.js';\nimport {stepAttempts, toStepAttempt} from '../schema/step-attempts.js';\nimport {steps, toStep} from '../schema/steps.js';\nimport {workflowRunAttempts} from '../schema/workflow-run-attempts.js';\nimport {toWorkflowRun, workflowRuns} from '../schema/workflow-runs.js';\nimport {getDirectDependencyJobContexts} from './jobs.js';\nimport {loadReferencedVariables} from './runs.js';\nimport {\n getWorkflowContextForJob,\n optimisticLockRetry,\n TERMINAL_EXECUTION_STATUSES,\n} from './shared.js';\nimport {bulkUpdateStepStatuses, getStepsByJobExecutionIdForUpdate} from './steps.js';\n\nasync function getJobExecutionFallbackName(tx: Tx, jobExecutionId: string): Promise<string> {\n const [row] = await tx\n .select({job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.id, jobExecutionId))\n .limit(1);\n if (!row) throw new JobNotFoundError(jobExecutionId);\n return row.job.name ?? row.job.key;\n}\n\nexport async function getJobExecutionById(id: string, tx?: Tx): Promise<JobExecution | undefined> {\n const rows = await (tx ?? db())\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.id, id))\n .limit(1);\n const row = rows[0];\n return row ? toJobExecution(row.jobExecution, row.job.name ?? row.job.key) : undefined;\n}\n\nexport async function getJobExecutionsByWorkflowRunAttemptId(\n workflowRunAttemptId: string,\n): Promise<JobExecution[]> {\n const rows = await db()\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobs.workflowRunAttemptId, workflowRunAttemptId))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id));\n return rows.map((row) => toJobExecution(row.jobExecution, row.job.name ?? row.job.key));\n}\n\nexport async function getJobExecutionsByJobId(jobId: string): Promise<JobExecution[]> {\n const rows = await db()\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.jobId, jobId))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id));\n return rows.map((row) => toJobExecution(row.jobExecution, row.job.name ?? row.job.key));\n}\n\nexport async function getFirstJobExecutionByJobId(\n jobId: string,\n tx?: Tx,\n): Promise<JobExecution | undefined> {\n const rows = await (tx ?? db())\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.jobId, jobId))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id))\n .limit(1);\n const row = rows[0];\n return row ? toJobExecution(row.jobExecution, row.job.name ?? row.job.key) : undefined;\n}\n\nexport async function getLatestJobExecutionByJobId(\n jobId: string,\n tx?: Tx,\n): Promise<JobExecution | undefined> {\n const rows = await (tx ?? db())\n .select({jobExecution: jobExecutions, job: jobs})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .where(eq(jobExecutions.jobId, jobId))\n .orderBy(desc(jobExecutions.sequence), desc(jobExecutions.id))\n .limit(1);\n const row = rows[0];\n return row ? toJobExecution(row.jobExecution, row.job.name ?? row.job.key) : undefined;\n}\n\nexport interface UpdateJobExecutionStatusAtVersionParams {\n jobExecutionId: string;\n status: JobExecutionStatus;\n expectedVersion: number;\n statusReason?: JobStatusReason | null | undefined;\n markTimedOut?: boolean;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n}\n\nasync function resolveJobExecutionOutputs(\n tx: Tx,\n params: {\n jobExecutionId: string;\n status: JobExecutionStatus;\n statusReason: JobStatusReason | null;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n },\n): Promise<Record<string, unknown> | null> {\n const [target] = await tx\n .select({execution: jobExecutions, job: jobs, attempt: workflowRunAttempts, run: workflowRuns})\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(eq(jobExecutions.id, params.jobExecutionId))\n .limit(1);\n if (!target) throw new JobNotFoundError(params.jobExecutionId);\n const model =\n target.attempt.model === null ? null : readPersistedWorkflowModel(target.attempt.model);\n if (!model) return null;\n const modelJob = model.jobs.find((job) => job.key === target.job.key);\n if (!modelJob || modelJob.outputs === undefined) return null;\n\n const executionRows = await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.jobId, target.job.id))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id));\n const fallbackName = target.job.name ?? target.job.key;\n const executions = executionRows.map((row) =>\n row.id === target.execution.id\n ? toJobExecution(\n {...row, status: params.status, statusReason: params.statusReason},\n fallbackName,\n )\n : toJobExecution(row, fallbackName),\n );\n const jobExecution = executions.find((execution) => execution.id === target.execution.id);\n if (!jobExecution) throw new JobNotFoundError(params.jobExecutionId);\n\n const stepRows = await tx\n .select()\n .from(steps)\n .where(eq(steps.jobExecutionId, params.jobExecutionId))\n .orderBy(asc(steps.position), asc(steps.id));\n const attemptRows = await tx\n .select()\n .from(stepAttempts)\n .where(eq(stepAttempts.jobExecutionId, params.jobExecutionId))\n .orderBy(asc(stepAttempts.executionOrder), asc(stepAttempts.id));\n const dependencyJobs = await getDirectDependencyJobContexts(target.job.id, tx);\n\n return deriveJobExecutionOutputs({\n run: toWorkflowRun(target.run),\n modelJob,\n job: toJob(target.job),\n jobExecution,\n executions,\n steps: stepRows.map(toStep),\n attempts: attemptRows.map(toStepAttempt),\n jobs: dependencyJobs,\n vars: await loadReferencedVariables({\n model,\n jobs: [modelJob],\n workspaceId: target.run.workspaceId,\n projectId: target.run.projectId,\n definitionId: target.run.definitionId,\n secrets: params.secrets,\n }),\n });\n}\n\nasync function updateJobExecutionStatusAtVersion(\n tx: Tx,\n params: UpdateJobExecutionStatusAtVersionParams,\n): Promise<{execution: JobExecution; changed: boolean} | null> {\n let status = params.status;\n let statusReason = params.statusReason ?? null;\n let outputs: Record<string, unknown> | null | undefined;\n if (TERMINAL_EXECUTION_STATUSES.includes(status)) {\n outputs = null;\n }\n if (status === 'succeeded') {\n try {\n outputs = await resolveJobExecutionOutputs(tx, {\n jobExecutionId: params.jobExecutionId,\n status,\n statusReason,\n secrets: params.secrets,\n });\n } catch (error) {\n if (\n !(error instanceof InterpolationUnresolvableError) &&\n !(error instanceof JobOutputNotJsonSafeError) &&\n !(error instanceof JobOutputTooLargeError) &&\n !(error instanceof JobOutputTooManyEntriesError)\n ) {\n throw error;\n }\n status = 'failed';\n statusReason = 'unknown';\n outputs = null;\n }\n }\n\n const rows = await tx\n .update(jobExecutions)\n .set({\n status,\n statusReason,\n ...(outputs === undefined ? {} : {outputs}),\n version: sql`${jobExecutions.version} + 1`,\n updatedAt: new Date(),\n ...(params.markTimedOut ? {timedOutAt: new Date()} : {}),\n ...(TERMINAL_EXECUTION_STATUSES.includes(status) ? {finishedAt: sql`now()`} : {}),\n })\n .where(\n and(\n eq(jobExecutions.id, params.jobExecutionId),\n eq(jobExecutions.version, params.expectedVersion),\n notInArray(jobExecutions.status, TERMINAL_EXECUTION_STATUSES),\n ),\n )\n .returning();\n\n const row = rows[0];\n if (!row) return null;\n return {\n execution: toJobExecution(row, await getJobExecutionFallbackName(tx, row.id)),\n changed: true,\n };\n}\n\nexport interface UpdateJobExecutionStatusParams {\n jobExecutionId: string;\n status: JobExecutionStatus;\n expectedVersion: number;\n statusReason?: JobStatusReason | null | undefined;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n}\n\nexport async function updateJobExecutionStatus(\n params: UpdateJobExecutionStatusParams,\n): Promise<JobExecution> {\n const statusReason = params.statusReason ?? null;\n const result = await db().transaction(async (tx) => {\n return await optimisticLockRetry({\n updateFn: () =>\n updateJobExecutionStatusAtVersion(tx, {\n jobExecutionId: params.jobExecutionId,\n status: params.status,\n expectedVersion: params.expectedVersion,\n statusReason,\n secrets: params.secrets,\n }),\n fetchFn: async () => {\n const row = (\n await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.id, params.jobExecutionId))\n .limit(1)\n )[0];\n return row ? toJobExecution(row, await getJobExecutionFallbackName(tx, row.id)) : undefined;\n },\n matchFn: (execution) =>\n (execution.status === params.status && execution.statusReason === statusReason) ||\n TERMINAL_EXECUTION_STATUSES.includes(execution.status)\n ? {execution, changed: false}\n : null,\n failureMessage: `Optimistic lock failure: job execution ${params.jobExecutionId} version ${params.expectedVersion}`,\n });\n });\n\n if (result.changed) recordWorkflowJobExecutionStatusChanged(result.execution.status);\n\n return result.execution;\n}\n\nexport async function recordJobExecutionQueuedAt(params: {\n jobExecutionId: string;\n queuedAt: Date;\n}): Promise<void> {\n const updated = await db()\n .update(jobExecutions)\n .set({queuedAt: params.queuedAt})\n .where(and(eq(jobExecutions.id, params.jobExecutionId), isNull(jobExecutions.queuedAt)))\n .returning({id: jobExecutions.id});\n\n if (updated.length > 0) recordWorkflowJobExecutionQueued();\n}\n\nexport async function recordJobExecutionStartedAt(params: {\n jobExecutionId: string;\n startedAt: Date;\n}): Promise<void> {\n const updated = await db()\n .update(jobExecutions)\n .set({startedAt: params.startedAt})\n .where(and(eq(jobExecutions.id, params.jobExecutionId), isNull(jobExecutions.startedAt)))\n .returning({id: jobExecutions.id});\n\n if (updated.length > 0) recordWorkflowJobExecutionStarted();\n}\n\nexport async function failJobExecutionAsTimedOut(params: {\n jobExecutionId: string;\n workflowRunAttemptId: string;\n expectedVersion: number;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n}): Promise<JobExecution> {\n const result = await db().transaction(async (tx) => {\n const updated = await optimisticLockRetry({\n updateFn: () =>\n updateJobExecutionStatusAtVersion(tx, {\n jobExecutionId: params.jobExecutionId,\n status: 'failed',\n expectedVersion: params.expectedVersion,\n statusReason: 'timed_out',\n markTimedOut: true,\n secrets: params.secrets,\n }),\n fetchFn: async () => {\n const row = (\n await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.id, params.jobExecutionId))\n .limit(1)\n )[0];\n return row ? toJobExecution(row, await getJobExecutionFallbackName(tx, row.id)) : undefined;\n },\n matchFn: (execution) => (execution.timedOutAt !== null ? {execution, changed: false} : null),\n failureMessage: `Optimistic lock failure: job execution ${params.jobExecutionId} version ${params.expectedVersion}`,\n });\n\n if (updated.changed) {\n const identity = await getWorkflowContextForJob(updated.execution.jobId, tx);\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_JOB_EXECUTION_TIMED_OUT,\n payload: {\n jobId: identity.jobId,\n jobExecutionId: params.jobExecutionId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n },\n });\n }\n\n return updated;\n });\n\n if (result.changed) {\n recordWorkflowJobExecutionStatusChanged(result.execution.status);\n recordWorkflowJobExecutionTimedOut();\n }\n\n return result.execution;\n}\n\nexport async function resolveJobExecutionAfterLeaseExpiry(params: {\n jobExecutionId: string;\n expectedVersion: number;\n secrets?: Pick<SecretsInterModuleClient, 'getVariablesByNamespace'> | undefined;\n}): Promise<{status: RuntimeCompletionStatus; executionVersion: number}> {\n const result = await db().transaction(async (tx) => {\n const jobExecutionSteps = await getStepsByJobExecutionIdForUpdate(params.jobExecutionId, tx);\n let changedJobExecution: JobExecution | null = null;\n\n if (jobExecutionSteps.length === 0) {\n throw new JobNotFoundError(params.jobExecutionId);\n }\n\n if (jobExecutionSteps.every((step) => isTerminal(step.status))) {\n const status = deriveCompletion(jobExecutionSteps);\n const updated = await updateJobExecutionStatusAtVersion(tx, {\n jobExecutionId: params.jobExecutionId,\n status,\n expectedVersion: params.expectedVersion,\n statusReason: statusReasonForStepCompletion(status),\n secrets: params.secrets,\n });\n changedJobExecution = updated?.changed ? updated.execution : null;\n } else {\n const updated = await updateJobExecutionStatusAtVersion(tx, {\n jobExecutionId: params.jobExecutionId,\n status: 'failed',\n expectedVersion: params.expectedVersion,\n statusReason: 'runner_lost',\n secrets: params.secrets,\n });\n if (updated?.changed) {\n changedJobExecution = updated.execution;\n await bulkUpdateStepStatuses(\n {jobExecutionId: params.jobExecutionId, status: 'cancelled'},\n tx,\n );\n }\n }\n\n const jobExecutionRow = (\n await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.id, params.jobExecutionId))\n .limit(1)\n )[0];\n if (!jobExecutionRow) {\n throw new Error(`Job execution not found resolving lease expiry: ${params.jobExecutionId}`);\n }\n const status: RuntimeCompletionStatus =\n jobExecutionRow.status === 'succeeded' ? 'succeeded' : 'failed';\n return {status, executionVersion: jobExecutionRow.version, changedJobExecution};\n });\n\n recordWorkflowJobExecutionLeaseExpiryResolved(result.status);\n if (result.changedJobExecution) {\n recordWorkflowJobExecutionStatusChanged(result.changedJobExecution.status);\n }\n\n return {status: result.status, executionVersion: result.executionVersion};\n}\n\nfunction statusReasonForStepCompletion(status: RuntimeCompletionStatus): JobStatusReason | null {\n return status === 'failed' ? 'step_failed' : null;\n}\n"],"names":["readPersistedWorkflowModel","WORKFLOWS_JOB_EXECUTION_TIMED_OUT","and","asc","desc","eq","isNull","notInArray","sql","InterpolationUnresolvableError","JobNotFoundError","JobOutputNotJsonSafeError","JobOutputTooLargeError","JobOutputTooManyEntriesError","deriveJobExecutionOutputs","deriveCompletion","isTerminal","recordWorkflowJobExecutionLeaseExpiryResolved","recordWorkflowJobExecutionQueued","recordWorkflowJobExecutionStarted","recordWorkflowJobExecutionStatusChanged","recordWorkflowJobExecutionTimedOut","db","writeWorkflowsOutboxEvent","jobExecutions","toJobExecution","jobs","toJob","stepAttempts","toStepAttempt","steps","toStep","workflowRunAttempts","toWorkflowRun","workflowRuns","getDirectDependencyJobContexts","loadReferencedVariables","getWorkflowContextForJob","optimisticLockRetry","TERMINAL_EXECUTION_STATUSES","bulkUpdateStepStatuses","getStepsByJobExecutionIdForUpdate","getJobExecutionFallbackName","tx","jobExecutionId","row","select","job","from","innerJoin","jobId","id","where","limit","name","key","getJobExecutionById","rows","jobExecution","undefined","getJobExecutionsByWorkflowRunAttemptId","workflowRunAttemptId","orderBy","sequence","map","getJobExecutionsByJobId","getFirstJobExecutionByJobId","getLatestJobExecutionByJobId","resolveJobExecutionOutputs","params","target","execution","attempt","run","workflowRunId","model","modelJob","find","outputs","executionRows","fallbackName","executions","status","statusReason","stepRows","position","attemptRows","executionOrder","dependencyJobs","attempts","vars","workspaceId","projectId","definitionId","secrets","updateJobExecutionStatusAtVersion","includes","error","update","set","version","updatedAt","Date","markTimedOut","timedOutAt","finishedAt","expectedVersion","returning","changed","updateJobExecutionStatus","result","transaction","updateFn","fetchFn","matchFn","failureMessage","recordJobExecutionQueuedAt","updated","queuedAt","length","recordJobExecutionStartedAt","startedAt","failJobExecutionAsTimedOut","identity","type","payload","resolveJobExecutionAfterLeaseExpiry","jobExecutionSteps","changedJobExecution","every","step","statusReasonForStepCompletion","jobExecutionRow","Error","executionVersion"],"mappings":"AAAA,SAAQA,0BAA0B,QAAO,+BAA+B;AAExE,SAAQC,iCAAiC,QAAO,6BAA6B;AAC7E,SAAQC,GAAG,EAAEC,GAAG,EAAEC,IAAI,EAAEC,EAAE,EAAEC,MAAM,EAAEC,UAAU,EAAEC,GAAG,QAAO,cAAc;AAGxE,SACEC,8BAA8B,EAC9BC,gBAAgB,EAChBC,yBAAyB,EACzBC,sBAAsB,EACtBC,4BAA4B,QACvB,kBAAkB;AACzB,SAAQC,yBAAyB,QAAO,gCAAgC;AACxE,SAAQC,gBAAgB,EAAEC,UAAU,QAAO,kDAAkD;AAE7F,SACEC,6CAA6C,EAC7CC,gCAAgC,EAChCC,iCAAiC,EACjCC,uCAAuC,EACvCC,kCAAkC,QAC7B,uBAAuB;AAC9B,SAAQC,EAAE,QAAgB,WAAW;AACrC,SAAQC,yBAAyB,QAAO,sBAAsB;AAC9D,SAAQC,aAAa,EAAEC,cAAc,QAAO,8BAA8B;AAC1E,SAAQC,IAAI,EAAEC,KAAK,QAAO,oBAAoB;AAC9C,SAAQC,YAAY,EAAEC,aAAa,QAAO,6BAA6B;AACvE,SAAQC,KAAK,EAAEC,MAAM,QAAO,qBAAqB;AACjD,SAAQC,mBAAmB,QAAO,qCAAqC;AACvE,SAAQC,aAAa,EAAEC,YAAY,QAAO,6BAA6B;AACvE,SAAQC,8BAA8B,QAAO,YAAY;AACzD,SAAQC,uBAAuB,QAAO,YAAY;AAClD,SACEC,wBAAwB,EACxBC,mBAAmB,EACnBC,2BAA2B,QACtB,cAAc;AACrB,SAAQC,sBAAsB,EAAEC,iCAAiC,QAAO,aAAa;AAErF,eAAeC,4BAA4BC,EAAM,EAAEC,cAAsB;IACvE,MAAM,CAACC,IAAI,GAAG,MAAMF,GACjBG,MAAM,CAAC;QAACC,KAAKrB;IAAI,GACjBsB,IAAI,CAACxB,eACLyB,SAAS,CAACvB,MAAMrB,GAAGmB,cAAc0B,KAAK,EAAExB,KAAKyB,EAAE,GAC/CC,KAAK,CAAC/C,GAAGmB,cAAc2B,EAAE,EAAEP,iBAC3BS,KAAK,CAAC;IACT,IAAI,CAACR,KAAK,MAAM,IAAInC,iBAAiBkC;IACrC,OAAOC,IAAIE,GAAG,CAACO,IAAI,IAAIT,IAAIE,GAAG,CAACQ,GAAG;AACpC;AAEA,OAAO,eAAeC,oBAAoBL,EAAU,EAAER,EAAO;IAC3D,MAAMc,OAAO,MAAM,AAACd,CAAAA,MAAMrB,IAAG,EAC1BwB,MAAM,CAAC;QAACY,cAAclC;QAAeuB,KAAKrB;IAAI,GAC9CsB,IAAI,CAACxB,eACLyB,SAAS,CAACvB,MAAMrB,GAAGmB,cAAc0B,KAAK,EAAExB,KAAKyB,EAAE,GAC/CC,KAAK,CAAC/C,GAAGmB,cAAc2B,EAAE,EAAEA,KAC3BE,KAAK,CAAC;IACT,MAAMR,MAAMY,IAAI,CAAC,EAAE;IACnB,OAAOZ,MAAMpB,eAAeoB,IAAIa,YAAY,EAAEb,IAAIE,GAAG,CAACO,IAAI,IAAIT,IAAIE,GAAG,CAACQ,GAAG,IAAII;AAC/E;AAEA,OAAO,eAAeC,uCACpBC,oBAA4B;IAE5B,MAAMJ,OAAO,MAAMnC,KAChBwB,MAAM,CAAC;QAACY,cAAclC;QAAeuB,KAAKrB;IAAI,GAC9CsB,IAAI,CAACxB,eACLyB,SAAS,CAACvB,MAAMrB,GAAGmB,cAAc0B,KAAK,EAAExB,KAAKyB,EAAE,GAC/CC,KAAK,CAAC/C,GAAGqB,KAAKmC,oBAAoB,EAAEA,uBACpCC,OAAO,CAAC3D,IAAIqB,cAAcuC,QAAQ,GAAG5D,IAAIqB,cAAc2B,EAAE;IAC5D,OAAOM,KAAKO,GAAG,CAAC,CAACnB,MAAQpB,eAAeoB,IAAIa,YAAY,EAAEb,IAAIE,GAAG,CAACO,IAAI,IAAIT,IAAIE,GAAG,CAACQ,GAAG;AACvF;AAEA,OAAO,eAAeU,wBAAwBf,KAAa;IACzD,MAAMO,OAAO,MAAMnC,KAChBwB,MAAM,CAAC;QAACY,cAAclC;QAAeuB,KAAKrB;IAAI,GAC9CsB,IAAI,CAACxB,eACLyB,SAAS,CAACvB,MAAMrB,GAAGmB,cAAc0B,KAAK,EAAExB,KAAKyB,EAAE,GAC/CC,KAAK,CAAC/C,GAAGmB,cAAc0B,KAAK,EAAEA,QAC9BY,OAAO,CAAC3D,IAAIqB,cAAcuC,QAAQ,GAAG5D,IAAIqB,cAAc2B,EAAE;IAC5D,OAAOM,KAAKO,GAAG,CAAC,CAACnB,MAAQpB,eAAeoB,IAAIa,YAAY,EAAEb,IAAIE,GAAG,CAACO,IAAI,IAAIT,IAAIE,GAAG,CAACQ,GAAG;AACvF;AAEA,OAAO,eAAeW,4BACpBhB,KAAa,EACbP,EAAO;IAEP,MAAMc,OAAO,MAAM,AAACd,CAAAA,MAAMrB,IAAG,EAC1BwB,MAAM,CAAC;QAACY,cAAclC;QAAeuB,KAAKrB;IAAI,GAC9CsB,IAAI,CAACxB,eACLyB,SAAS,CAACvB,MAAMrB,GAAGmB,cAAc0B,KAAK,EAAExB,KAAKyB,EAAE,GAC/CC,KAAK,CAAC/C,GAAGmB,cAAc0B,KAAK,EAAEA,QAC9BY,OAAO,CAAC3D,IAAIqB,cAAcuC,QAAQ,GAAG5D,IAAIqB,cAAc2B,EAAE,GACzDE,KAAK,CAAC;IACT,MAAMR,MAAMY,IAAI,CAAC,EAAE;IACnB,OAAOZ,MAAMpB,eAAeoB,IAAIa,YAAY,EAAEb,IAAIE,GAAG,CAACO,IAAI,IAAIT,IAAIE,GAAG,CAACQ,GAAG,IAAII;AAC/E;AAEA,OAAO,eAAeQ,6BACpBjB,KAAa,EACbP,EAAO;IAEP,MAAMc,OAAO,MAAM,AAACd,CAAAA,MAAMrB,IAAG,EAC1BwB,MAAM,CAAC;QAACY,cAAclC;QAAeuB,KAAKrB;IAAI,GAC9CsB,IAAI,CAACxB,eACLyB,SAAS,CAACvB,MAAMrB,GAAGmB,cAAc0B,KAAK,EAAExB,KAAKyB,EAAE,GAC/CC,KAAK,CAAC/C,GAAGmB,cAAc0B,KAAK,EAAEA,QAC9BY,OAAO,CAAC1D,KAAKoB,cAAcuC,QAAQ,GAAG3D,KAAKoB,cAAc2B,EAAE,GAC3DE,KAAK,CAAC;IACT,MAAMR,MAAMY,IAAI,CAAC,EAAE;IACnB,OAAOZ,MAAMpB,eAAeoB,IAAIa,YAAY,EAAEb,IAAIE,GAAG,CAACO,IAAI,IAAIT,IAAIE,GAAG,CAACQ,GAAG,IAAII;AAC/E;AAWA,eAAeS,2BACbzB,EAAM,EACN0B,MAKC;IAED,MAAM,CAACC,OAAO,GAAG,MAAM3B,GACpBG,MAAM,CAAC;QAACyB,WAAW/C;QAAeuB,KAAKrB;QAAM8C,SAASxC;QAAqByC,KAAKvC;IAAY,GAC5Fc,IAAI,CAACxB,eACLyB,SAAS,CAACvB,MAAMrB,GAAGmB,cAAc0B,KAAK,EAAExB,KAAKyB,EAAE,GAC/CF,SAAS,CAACjB,qBAAqB3B,GAAGqB,KAAKmC,oBAAoB,EAAE7B,oBAAoBmB,EAAE,GACnFF,SAAS,CAACf,cAAc7B,GAAG2B,oBAAoB0C,aAAa,EAAExC,aAAaiB,EAAE,GAC7EC,KAAK,CAAC/C,GAAGmB,cAAc2B,EAAE,EAAEkB,OAAOzB,cAAc,GAChDS,KAAK,CAAC;IACT,IAAI,CAACiB,QAAQ,MAAM,IAAI5D,iBAAiB2D,OAAOzB,cAAc;IAC7D,MAAM+B,QACJL,OAAOE,OAAO,CAACG,KAAK,KAAK,OAAO,OAAO3E,2BAA2BsE,OAAOE,OAAO,CAACG,KAAK;IACxF,IAAI,CAACA,OAAO,OAAO;IACnB,MAAMC,WAAWD,MAAMjD,IAAI,CAACmD,IAAI,CAAC,CAAC9B,MAAQA,IAAIQ,GAAG,KAAKe,OAAOvB,GAAG,CAACQ,GAAG;IACpE,IAAI,CAACqB,YAAYA,SAASE,OAAO,KAAKnB,WAAW,OAAO;IAExD,MAAMoB,gBAAgB,MAAMpC,GACzBG,MAAM,GACNE,IAAI,CAACxB,eACL4B,KAAK,CAAC/C,GAAGmB,cAAc0B,KAAK,EAAEoB,OAAOvB,GAAG,CAACI,EAAE,GAC3CW,OAAO,CAAC3D,IAAIqB,cAAcuC,QAAQ,GAAG5D,IAAIqB,cAAc2B,EAAE;IAC5D,MAAM6B,eAAeV,OAAOvB,GAAG,CAACO,IAAI,IAAIgB,OAAOvB,GAAG,CAACQ,GAAG;IACtD,MAAM0B,aAAaF,cAAcf,GAAG,CAAC,CAACnB,MACpCA,IAAIM,EAAE,KAAKmB,OAAOC,SAAS,CAACpB,EAAE,GAC1B1B,eACE;YAAC,GAAGoB,GAAG;YAAEqC,QAAQb,OAAOa,MAAM;YAAEC,cAAcd,OAAOc,YAAY;QAAA,GACjEH,gBAEFvD,eAAeoB,KAAKmC;IAE1B,MAAMtB,eAAeuB,WAAWJ,IAAI,CAAC,CAACN,YAAcA,UAAUpB,EAAE,KAAKmB,OAAOC,SAAS,CAACpB,EAAE;IACxF,IAAI,CAACO,cAAc,MAAM,IAAIhD,iBAAiB2D,OAAOzB,cAAc;IAEnE,MAAMwC,WAAW,MAAMzC,GACpBG,MAAM,GACNE,IAAI,CAAClB,OACLsB,KAAK,CAAC/C,GAAGyB,MAAMc,cAAc,EAAEyB,OAAOzB,cAAc,GACpDkB,OAAO,CAAC3D,IAAI2B,MAAMuD,QAAQ,GAAGlF,IAAI2B,MAAMqB,EAAE;IAC5C,MAAMmC,cAAc,MAAM3C,GACvBG,MAAM,GACNE,IAAI,CAACpB,cACLwB,KAAK,CAAC/C,GAAGuB,aAAagB,cAAc,EAAEyB,OAAOzB,cAAc,GAC3DkB,OAAO,CAAC3D,IAAIyB,aAAa2D,cAAc,GAAGpF,IAAIyB,aAAauB,EAAE;IAChE,MAAMqC,iBAAiB,MAAMrD,+BAA+BmC,OAAOvB,GAAG,CAACI,EAAE,EAAER;IAE3E,OAAO7B,0BAA0B;QAC/B2D,KAAKxC,cAAcqC,OAAOG,GAAG;QAC7BG;QACA7B,KAAKpB,MAAM2C,OAAOvB,GAAG;QACrBW;QACAuB;QACAnD,OAAOsD,SAASpB,GAAG,CAACjC;QACpB0D,UAAUH,YAAYtB,GAAG,CAACnC;QAC1BH,MAAM8D;QACNE,MAAM,MAAMtD,wBAAwB;YAClCuC;YACAjD,MAAM;gBAACkD;aAAS;YAChBe,aAAarB,OAAOG,GAAG,CAACkB,WAAW;YACnCC,WAAWtB,OAAOG,GAAG,CAACmB,SAAS;YAC/BC,cAAcvB,OAAOG,GAAG,CAACoB,YAAY;YACrCC,SAASzB,OAAOyB,OAAO;QACzB;IACF;AACF;AAEA,eAAeC,kCACbpD,EAAM,EACN0B,MAA+C;IAE/C,IAAIa,SAASb,OAAOa,MAAM;IAC1B,IAAIC,eAAed,OAAOc,YAAY,IAAI;IAC1C,IAAIL;IACJ,IAAIvC,4BAA4ByD,QAAQ,CAACd,SAAS;QAChDJ,UAAU;IACZ;IACA,IAAII,WAAW,aAAa;QAC1B,IAAI;YACFJ,UAAU,MAAMV,2BAA2BzB,IAAI;gBAC7CC,gBAAgByB,OAAOzB,cAAc;gBACrCsC;gBACAC;gBACAW,SAASzB,OAAOyB,OAAO;YACzB;QACF,EAAE,OAAOG,OAAO;YACd,IACE,CAAEA,CAAAA,iBAAiBxF,8BAA6B,KAChD,CAAEwF,CAAAA,iBAAiBtF,yBAAwB,KAC3C,CAAEsF,CAAAA,iBAAiBrF,sBAAqB,KACxC,CAAEqF,CAAAA,iBAAiBpF,4BAA2B,GAC9C;gBACA,MAAMoF;YACR;YACAf,SAAS;YACTC,eAAe;YACfL,UAAU;QACZ;IACF;IAEA,MAAMrB,OAAO,MAAMd,GAChBuD,MAAM,CAAC1E,eACP2E,GAAG,CAAC;QACHjB;QACAC;QACA,GAAIL,YAAYnB,YAAY,CAAC,IAAI;YAACmB;QAAO,CAAC;QAC1CsB,SAAS5F,GAAG,CAAC,EAAEgB,cAAc4E,OAAO,CAAC,IAAI,CAAC;QAC1CC,WAAW,IAAIC;QACf,GAAIjC,OAAOkC,YAAY,GAAG;YAACC,YAAY,IAAIF;QAAM,IAAI,CAAC,CAAC;QACvD,GAAI/D,4BAA4ByD,QAAQ,CAACd,UAAU;YAACuB,YAAYjG,GAAG,CAAC,KAAK,CAAC;QAAA,IAAI,CAAC,CAAC;IAClF,GACC4C,KAAK,CACJlD,IACEG,GAAGmB,cAAc2B,EAAE,EAAEkB,OAAOzB,cAAc,GAC1CvC,GAAGmB,cAAc4E,OAAO,EAAE/B,OAAOqC,eAAe,GAChDnG,WAAWiB,cAAc0D,MAAM,EAAE3C,+BAGpCoE,SAAS;IAEZ,MAAM9D,MAAMY,IAAI,CAAC,EAAE;IACnB,IAAI,CAACZ,KAAK,OAAO;IACjB,OAAO;QACL0B,WAAW9C,eAAeoB,KAAK,MAAMH,4BAA4BC,IAAIE,IAAIM,EAAE;QAC3EyD,SAAS;IACX;AACF;AAUA,OAAO,eAAeC,yBACpBxC,MAAsC;IAEtC,MAAMc,eAAed,OAAOc,YAAY,IAAI;IAC5C,MAAM2B,SAAS,MAAMxF,KAAKyF,WAAW,CAAC,OAAOpE;QAC3C,OAAO,MAAML,oBAAoB;YAC/B0E,UAAU,IACRjB,kCAAkCpD,IAAI;oBACpCC,gBAAgByB,OAAOzB,cAAc;oBACrCsC,QAAQb,OAAOa,MAAM;oBACrBwB,iBAAiBrC,OAAOqC,eAAe;oBACvCvB;oBACAW,SAASzB,OAAOyB,OAAO;gBACzB;YACFmB,SAAS;gBACP,MAAMpE,MAAM,AACV,CAAA,MAAMF,GACHG,MAAM,GACNE,IAAI,CAACxB,eACL4B,KAAK,CAAC/C,GAAGmB,cAAc2B,EAAE,EAAEkB,OAAOzB,cAAc,GAChDS,KAAK,CAAC,EAAC,CACX,CAAC,EAAE;gBACJ,OAAOR,MAAMpB,eAAeoB,KAAK,MAAMH,4BAA4BC,IAAIE,IAAIM,EAAE,KAAKQ;YACpF;YACAuD,SAAS,CAAC3C,YACR,AAACA,UAAUW,MAAM,KAAKb,OAAOa,MAAM,IAAIX,UAAUY,YAAY,KAAKA,gBAClE5C,4BAA4ByD,QAAQ,CAACzB,UAAUW,MAAM,IACjD;oBAACX;oBAAWqC,SAAS;gBAAK,IAC1B;YACNO,gBAAgB,CAAC,uCAAuC,EAAE9C,OAAOzB,cAAc,CAAC,SAAS,EAAEyB,OAAOqC,eAAe,EAAE;QACrH;IACF;IAEA,IAAII,OAAOF,OAAO,EAAExF,wCAAwC0F,OAAOvC,SAAS,CAACW,MAAM;IAEnF,OAAO4B,OAAOvC,SAAS;AACzB;AAEA,OAAO,eAAe6C,2BAA2B/C,MAGhD;IACC,MAAMgD,UAAU,MAAM/F,KACnB4E,MAAM,CAAC1E,eACP2E,GAAG,CAAC;QAACmB,UAAUjD,OAAOiD,QAAQ;IAAA,GAC9BlE,KAAK,CAAClD,IAAIG,GAAGmB,cAAc2B,EAAE,EAAEkB,OAAOzB,cAAc,GAAGtC,OAAOkB,cAAc8F,QAAQ,IACpFX,SAAS,CAAC;QAACxD,IAAI3B,cAAc2B,EAAE;IAAA;IAElC,IAAIkE,QAAQE,MAAM,GAAG,GAAGrG;AAC1B;AAEA,OAAO,eAAesG,4BAA4BnD,MAGjD;IACC,MAAMgD,UAAU,MAAM/F,KACnB4E,MAAM,CAAC1E,eACP2E,GAAG,CAAC;QAACsB,WAAWpD,OAAOoD,SAAS;IAAA,GAChCrE,KAAK,CAAClD,IAAIG,GAAGmB,cAAc2B,EAAE,EAAEkB,OAAOzB,cAAc,GAAGtC,OAAOkB,cAAciG,SAAS,IACrFd,SAAS,CAAC;QAACxD,IAAI3B,cAAc2B,EAAE;IAAA;IAElC,IAAIkE,QAAQE,MAAM,GAAG,GAAGpG;AAC1B;AAEA,OAAO,eAAeuG,2BAA2BrD,MAKhD;IACC,MAAMyC,SAAS,MAAMxF,KAAKyF,WAAW,CAAC,OAAOpE;QAC3C,MAAM0E,UAAU,MAAM/E,oBAAoB;YACxC0E,UAAU,IACRjB,kCAAkCpD,IAAI;oBACpCC,gBAAgByB,OAAOzB,cAAc;oBACrCsC,QAAQ;oBACRwB,iBAAiBrC,OAAOqC,eAAe;oBACvCvB,cAAc;oBACdoB,cAAc;oBACdT,SAASzB,OAAOyB,OAAO;gBACzB;YACFmB,SAAS;gBACP,MAAMpE,MAAM,AACV,CAAA,MAAMF,GACHG,MAAM,GACNE,IAAI,CAACxB,eACL4B,KAAK,CAAC/C,GAAGmB,cAAc2B,EAAE,EAAEkB,OAAOzB,cAAc,GAChDS,KAAK,CAAC,EAAC,CACX,CAAC,EAAE;gBACJ,OAAOR,MAAMpB,eAAeoB,KAAK,MAAMH,4BAA4BC,IAAIE,IAAIM,EAAE,KAAKQ;YACpF;YACAuD,SAAS,CAAC3C,YAAeA,UAAUiC,UAAU,KAAK,OAAO;oBAACjC;oBAAWqC,SAAS;gBAAK,IAAI;YACvFO,gBAAgB,CAAC,uCAAuC,EAAE9C,OAAOzB,cAAc,CAAC,SAAS,EAAEyB,OAAOqC,eAAe,EAAE;QACrH;QAEA,IAAIW,QAAQT,OAAO,EAAE;YACnB,MAAMe,WAAW,MAAMtF,yBAAyBgF,QAAQ9C,SAAS,CAACrB,KAAK,EAAEP;YACzE,MAAMpB,0BAA0BoB,IAAI;gBAClCiF,MAAM3H;gBACN4H,SAAS;oBACP3E,OAAOyE,SAASzE,KAAK;oBACrBN,gBAAgByB,OAAOzB,cAAc;oBACrCiB,sBAAsB8D,SAAS9D,oBAAoB;gBACrD;YACF;QACF;QAEA,OAAOwD;IACT;IAEA,IAAIP,OAAOF,OAAO,EAAE;QAClBxF,wCAAwC0F,OAAOvC,SAAS,CAACW,MAAM;QAC/D7D;IACF;IAEA,OAAOyF,OAAOvC,SAAS;AACzB;AAEA,OAAO,eAAeuD,oCAAoCzD,MAIzD;IACC,MAAMyC,SAAS,MAAMxF,KAAKyF,WAAW,CAAC,OAAOpE;QAC3C,MAAMoF,oBAAoB,MAAMtF,kCAAkC4B,OAAOzB,cAAc,EAAED;QACzF,IAAIqF,sBAA2C;QAE/C,IAAID,kBAAkBR,MAAM,KAAK,GAAG;YAClC,MAAM,IAAI7G,iBAAiB2D,OAAOzB,cAAc;QAClD;QAEA,IAAImF,kBAAkBE,KAAK,CAAC,CAACC,OAASlH,WAAWkH,KAAKhD,MAAM,IAAI;YAC9D,MAAMA,SAASnE,iBAAiBgH;YAChC,MAAMV,UAAU,MAAMtB,kCAAkCpD,IAAI;gBAC1DC,gBAAgByB,OAAOzB,cAAc;gBACrCsC;gBACAwB,iBAAiBrC,OAAOqC,eAAe;gBACvCvB,cAAcgD,8BAA8BjD;gBAC5CY,SAASzB,OAAOyB,OAAO;YACzB;YACAkC,sBAAsBX,SAAST,UAAUS,QAAQ9C,SAAS,GAAG;QAC/D,OAAO;YACL,MAAM8C,UAAU,MAAMtB,kCAAkCpD,IAAI;gBAC1DC,gBAAgByB,OAAOzB,cAAc;gBACrCsC,QAAQ;gBACRwB,iBAAiBrC,OAAOqC,eAAe;gBACvCvB,cAAc;gBACdW,SAASzB,OAAOyB,OAAO;YACzB;YACA,IAAIuB,SAAST,SAAS;gBACpBoB,sBAAsBX,QAAQ9C,SAAS;gBACvC,MAAM/B,uBACJ;oBAACI,gBAAgByB,OAAOzB,cAAc;oBAAEsC,QAAQ;gBAAW,GAC3DvC;YAEJ;QACF;QAEA,MAAMyF,kBAAkB,AACtB,CAAA,MAAMzF,GACHG,MAAM,GACNE,IAAI,CAACxB,eACL4B,KAAK,CAAC/C,GAAGmB,cAAc2B,EAAE,EAAEkB,OAAOzB,cAAc,GAChDS,KAAK,CAAC,EAAC,CACX,CAAC,EAAE;QACJ,IAAI,CAAC+E,iBAAiB;YACpB,MAAM,IAAIC,MAAM,CAAC,gDAAgD,EAAEhE,OAAOzB,cAAc,EAAE;QAC5F;QACA,MAAMsC,SACJkD,gBAAgBlD,MAAM,KAAK,cAAc,cAAc;QACzD,OAAO;YAACA;YAAQoD,kBAAkBF,gBAAgBhC,OAAO;YAAE4B;QAAmB;IAChF;IAEA/G,8CAA8C6F,OAAO5B,MAAM;IAC3D,IAAI4B,OAAOkB,mBAAmB,EAAE;QAC9B5G,wCAAwC0F,OAAOkB,mBAAmB,CAAC9C,MAAM;IAC3E;IAEA,OAAO;QAACA,QAAQ4B,OAAO5B,MAAM;QAAEoD,kBAAkBxB,OAAOwB,gBAAgB;IAAA;AAC1E;AAEA,SAASH,8BAA8BjD,MAA+B;IACpE,OAAOA,WAAW,WAAW,gBAAgB;AAC/C"}
@@ -1 +1 @@
1
- {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../../src/db/workflow-runs/jobs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../../src/db/workflow-runs/jobs.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,14 @@ import { workflowRunAttempts } from '../schema/workflow-run-attempts.js';
13
13
  import { toWorkflowRun, workflowRuns } from '../schema/workflow-runs.js';
14
14
  import { getWorkflowRunById } from './queries.js';
15
15
  import { getWorkflowContextForJob, optimisticLockRetry, TERMINAL_JOB_STATUSES } from './shared.js';
16
+ function outputTypesByJobKey(model) {
17
+ return new Map((model?.jobs ?? []).flatMap((job)=>job.outputTypes === undefined ? [] : [
18
+ [
19
+ job.key,
20
+ job.outputTypes
21
+ ]
22
+ ]));
23
+ }
16
24
  export async function getJobsByWorkflowRunAttemptId(workflowRunAttemptId) {
17
25
  const rows = await db().select().from(jobs).where(eq(jobs.workflowRunAttemptId, workflowRunAttemptId)).orderBy(asc(jobs.position));
18
26
  return rows.map(toJob);
@@ -38,19 +46,28 @@ export async function getJobScope(jobId) {
38
46
  return rows[0];
39
47
  }
40
48
  export async function getDirectDependencyJobContexts(jobId, tx) {
41
- const targetRows = await (tx ?? db()).select().from(jobs).where(eq(jobs.id, jobId)).limit(1);
49
+ const targetRows = await (tx ?? db()).select({
50
+ job: jobs,
51
+ attempt: workflowRunAttempts
52
+ }).from(jobs).innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id)).where(eq(jobs.id, jobId)).limit(1);
42
53
  const target = targetRows[0];
43
- if (!target || target.dependencies.length === 0) return [];
54
+ if (!target || target.job.dependencies.length === 0) return [];
55
+ const model = target.attempt.model === null ? null : readPersistedWorkflowModel(target.attempt.model);
56
+ const outputTypes = outputTypesByJobKey(model);
44
57
  const rows = await (tx ?? db()).select({
45
58
  job: jobs,
46
59
  execution: jobExecutions
47
- }).from(jobs).leftJoin(jobExecutions, eq(jobExecutions.jobId, jobs.id)).where(and(eq(jobs.workflowRunAttemptId, target.workflowRunAttemptId), inArray(jobs.key, target.dependencies))).orderBy(asc(jobs.position), asc(jobs.id), asc(jobExecutions.sequence), asc(jobExecutions.id));
60
+ }).from(jobs).leftJoin(jobExecutions, eq(jobExecutions.jobId, jobs.id)).where(and(eq(jobs.workflowRunAttemptId, target.job.workflowRunAttemptId), inArray(jobs.key, target.job.dependencies))).orderBy(asc(jobs.position), asc(jobs.id), asc(jobExecutions.sequence), asc(jobExecutions.id));
48
61
  const contextsByJobId = new Map();
49
62
  for (const row of rows){
50
63
  let context = contextsByJobId.get(row.job.id);
51
64
  if (!context) {
65
+ const jobOutputTypes = outputTypes.get(row.job.key);
52
66
  context = {
53
67
  job: toJob(row.job),
68
+ ...jobOutputTypes === undefined ? {} : {
69
+ outputTypes: jobOutputTypes
70
+ },
54
71
  executions: []
55
72
  };
56
73
  contextsByJobId.set(row.job.id, context);
@@ -152,6 +169,11 @@ export async function evaluateJobActivations(params) {
152
169
  async function directDependencyContextsByJobKey(tx, runAttemptId, targetJobs) {
153
170
  const dependencyKeys = new Set(targetJobs.flatMap((job)=>job.dependencies));
154
171
  if (dependencyKeys.size === 0) return new Map();
172
+ const [attempt] = await tx.select({
173
+ model: workflowRunAttempts.model
174
+ }).from(workflowRunAttempts).where(eq(workflowRunAttempts.id, runAttemptId)).limit(1);
175
+ const model = attempt === undefined || attempt.model === null ? null : readPersistedWorkflowModel(attempt.model);
176
+ const outputTypes = outputTypesByJobKey(model);
155
177
  const rows = await tx.select({
156
178
  job: jobs,
157
179
  execution: jobExecutions
@@ -162,8 +184,12 @@ async function directDependencyContextsByJobKey(tx, runAttemptId, targetJobs) {
162
184
  for (const row of rows){
163
185
  let context = contextsByJobKey.get(row.job.key);
164
186
  if (!context) {
187
+ const jobOutputTypes = outputTypes.get(row.job.key);
165
188
  context = {
166
189
  job: toJob(row.job),
190
+ ...jobOutputTypes === undefined ? {} : {
191
+ outputTypes: jobOutputTypes
192
+ },
167
193
  executions: []
168
194
  };
169
195
  contextsByJobKey.set(row.job.key, context);
@@ -197,11 +223,15 @@ export async function updateJobStatusAtVersion(tx, params) {
197
223
  // version match lets a single caller win. Emitting here, in the same transaction,
198
224
  // makes the terminal fact fire exactly once across all paths.
199
225
  if (isJobTerminal(job.status)) {
226
+ const [currentExecution] = await tx.select({
227
+ id: jobExecutions.id
228
+ }).from(jobExecutions).where(eq(jobExecutions.jobId, job.id)).orderBy(desc(jobExecutions.sequence), desc(jobExecutions.id)).limit(1);
200
229
  const identity = await getWorkflowContextForJob(job.id, tx);
201
230
  await writeWorkflowsOutboxEvent(tx, {
202
231
  type: WORKFLOWS_JOB_TERMINATED,
203
232
  payload: {
204
233
  jobId: job.id,
234
+ jobExecutionId: currentExecution?.id ?? null,
205
235
  workflowRunId: identity.workflowRunId,
206
236
  workflowRunAttemptId: identity.workflowRunAttemptId,
207
237
  status: job.status,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/db/workflow-runs/jobs.ts"],"sourcesContent":["import {readPersistedWorkflowModel} from '@shipfox/api-definitions-dto';\nimport {WORKFLOWS_JOB_TERMINATED} from '@shipfox/api-workflows-dto';\nimport {and, asc, desc, eq, inArray, notInArray, sql} from 'drizzle-orm';\nimport {isJobTerminal, type Job, type JobStatus, type JobStatusReason} from '#core/entities/job.js';\nimport type {JobExecution} from '#core/entities/job-execution.js';\nimport type {PersistedEvaluationTraceEntry} from '#core/entities/step.js';\nimport type {WorkflowRunTriggerReference} from '#core/entities/workflow-run.js';\nimport {JobNotFoundError} from '#core/errors.js';\nimport {\n type DeriveJobSuccessResult,\n decideJobActivation,\n deriveJobSuccess,\n runtimeCompletionStatusForJob,\n} from '#core/job-transition/index.js';\nimport type {JobContextInput} from '#core/step-config/assemble-run-context.js';\nimport type {RuntimeCompletionStatus} from '#core/workflow-scheduling/runtime-dag.js';\nimport {recordWorkflowJobStatusChanged} from '#metrics/instance.js';\nimport {db, type Tx} from '../db.js';\nimport {writeWorkflowsOutboxEvent} from '../outbox-writes.js';\nimport {jobExecutions, toJobExecution} from '../schema/job-executions.js';\nimport {jobs, toJob} from '../schema/jobs.js';\nimport {workflowRunAttempts} from '../schema/workflow-run-attempts.js';\nimport {toWorkflowRun, workflowRuns} from '../schema/workflow-runs.js';\nimport {getWorkflowRunById} from './queries.js';\nimport {getWorkflowContextForJob, optimisticLockRetry, TERMINAL_JOB_STATUSES} from './shared.js';\n\nexport async function getJobsByWorkflowRunAttemptId(workflowRunAttemptId: string): Promise<Job[]> {\n const rows = await db()\n .select()\n .from(jobs)\n .where(eq(jobs.workflowRunAttemptId, workflowRunAttemptId))\n .orderBy(asc(jobs.position));\n return rows.map(toJob);\n}\n\nexport async function getJobsByWorkflowRunId(workflowRunId: string): Promise<Job[]> {\n const run = await getWorkflowRunById(workflowRunId);\n if (!run) return [];\n const [attempt] = await db()\n .select()\n .from(workflowRunAttempts)\n .where(\n and(\n eq(workflowRunAttempts.workflowRunId, run.id),\n eq(workflowRunAttempts.attempt, run.currentAttempt),\n ),\n )\n .limit(1);\n return attempt ? getJobsByWorkflowRunAttemptId(attempt.id) : [];\n}\n\nexport async function getJobById(id: string): Promise<Job | undefined> {\n const rows = await db().select().from(jobs).where(eq(jobs.id, id)).limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toJob(row);\n}\n\nexport interface JobScope {\n workspaceId: string;\n projectId: string;\n triggerReference: WorkflowRunTriggerReference | null;\n}\n\nexport async function getJobScope(jobId: string): Promise<JobScope | undefined> {\n const rows = await db()\n .select({\n workspaceId: workflowRuns.workspaceId,\n projectId: workflowRuns.projectId,\n triggerReference: workflowRuns.triggerReference,\n })\n .from(jobs)\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(eq(jobs.id, jobId))\n .limit(1);\n return rows[0];\n}\n\nexport async function getDirectDependencyJobContexts(\n jobId: string,\n tx?: Tx,\n): Promise<JobContextInput[]> {\n const targetRows = await (tx ?? db()).select().from(jobs).where(eq(jobs.id, jobId)).limit(1);\n const target = targetRows[0];\n if (!target || target.dependencies.length === 0) return [];\n\n const rows = await (tx ?? db())\n .select({job: jobs, execution: jobExecutions})\n .from(jobs)\n .leftJoin(jobExecutions, eq(jobExecutions.jobId, jobs.id))\n .where(\n and(\n eq(jobs.workflowRunAttemptId, target.workflowRunAttemptId),\n inArray(jobs.key, target.dependencies),\n ),\n )\n .orderBy(asc(jobs.position), asc(jobs.id), asc(jobExecutions.sequence), asc(jobExecutions.id));\n\n const contextsByJobId = new Map<string, JobContextInput & {executions: JobExecution[]}>();\n for (const row of rows) {\n let context = contextsByJobId.get(row.job.id);\n if (!context) {\n context = {job: toJob(row.job), executions: []};\n contextsByJobId.set(row.job.id, context);\n }\n if (row.execution)\n context.executions.push(toJobExecution(row.execution, row.job.name ?? row.job.key));\n }\n\n return [...contextsByJobId.values()];\n}\n\nexport interface EvaluateJobActivationsParams {\n runAttemptId: string;\n jobs: readonly {\n jobId: string;\n expectedVersion: number;\n }[];\n}\n\nexport type JobActivationDecision =\n | {\n kind: 'start-job';\n jobId: string;\n }\n | {\n kind: 'terminal-job';\n jobId: string;\n status: RuntimeCompletionStatus;\n jobVersion: number;\n };\n\ntype InternalJobActivationDecision = JobActivationDecision & {changed?: boolean};\n\nexport async function evaluateJobActivations(\n params: EvaluateJobActivationsParams,\n): Promise<JobActivationDecision[]> {\n if (params.jobs.length === 0) return [];\n\n const result = await db().transaction(async (tx) => {\n const expectedVersions = new Map(\n params.jobs.map((job) => [job.jobId, job.expectedVersion] as const),\n );\n const jobIds = params.jobs.map((job) => job.jobId);\n const targets = await tx\n .select({job: jobs, attempt: workflowRunAttempts, run: workflowRuns})\n .from(jobs)\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(and(eq(jobs.workflowRunAttemptId, params.runAttemptId), inArray(jobs.id, jobIds)))\n .for('update');\n\n if (targets.length !== jobIds.length) {\n const found = new Set(targets.map((target) => target.job.id));\n const missing = jobIds.filter((jobId) => !found.has(jobId));\n throw new Error(`Cannot evaluate missing activation jobs: ${missing.join(', ')}`);\n }\n\n const contextsByJobKey = await directDependencyContextsByJobKey(\n tx,\n params.runAttemptId,\n targets.map((target) => toJob(target.job)),\n );\n const targetsByJobId = new Map(targets.map((target) => [target.job.id, target]));\n const decisions: InternalJobActivationDecision[] = [];\n\n for (const input of params.jobs) {\n const target = targetsByJobId.get(input.jobId);\n if (!target) throw new Error(`Cannot evaluate missing activation job: ${input.jobId}`);\n const job = toJob(target.job);\n const model =\n target.attempt.model === null ? null : readPersistedWorkflowModel(target.attempt.model);\n const modelJob = model?.jobs.find((item) => item.key === target.job.key);\n const decision = decideJobActivation({\n run: toWorkflowRun(target.run),\n job,\n condition: modelJob?.if,\n vars: target.attempt.vars ?? undefined,\n dependencies: job.dependencies.flatMap((key) => {\n const dependency = contextsByJobKey.get(key);\n return dependency === undefined ? [] : [dependency];\n }),\n });\n\n if (decision.kind === 'terminal-job' || decision.kind === 'start-job') {\n decisions.push(decision);\n continue;\n }\n\n const expectedVersion = expectedVersions.get(job.id);\n if (expectedVersion === undefined) {\n throw new Error(`Missing expected version for activation job ${job.id}`);\n }\n const updated = await updateJobStatusAtVersion(tx, {\n jobId: job.id,\n status: decision.status,\n expectedVersion,\n statusReason: decision.statusReason,\n evaluationTrace: decision.evaluationTrace,\n });\n if (updated) {\n decisions.push({\n kind: 'terminal-job',\n jobId: job.id,\n status: 'skipped',\n jobVersion: updated.job.version,\n changed: updated.changed,\n });\n continue;\n }\n\n const [existing] = await tx.select().from(jobs).where(eq(jobs.id, job.id)).limit(1);\n if (existing && isJobTerminal(existing.status)) {\n decisions.push({\n kind: 'terminal-job',\n jobId: job.id,\n status: runtimeCompletionStatusForJob(existing.status),\n jobVersion: existing.version,\n });\n continue;\n }\n throw new Error(`Optimistic lock failure evaluating activation for job ${job.id}`);\n }\n\n return decisions;\n });\n\n for (const decision of result) {\n if (decision.kind === 'terminal-job' && decision.changed) {\n recordWorkflowJobStatusChanged(decision.status);\n }\n }\n\n return result.map(({changed: _changed, ...decision}) => decision);\n}\n\nasync function directDependencyContextsByJobKey(\n tx: Tx,\n runAttemptId: string,\n targetJobs: readonly Job[],\n): Promise<ReadonlyMap<string, JobContextInput>> {\n const dependencyKeys = new Set(targetJobs.flatMap((job) => job.dependencies));\n if (dependencyKeys.size === 0) return new Map();\n\n const rows = await tx\n .select({job: jobs, execution: jobExecutions})\n .from(jobs)\n .leftJoin(jobExecutions, eq(jobExecutions.jobId, jobs.id))\n .where(and(eq(jobs.workflowRunAttemptId, runAttemptId), inArray(jobs.key, [...dependencyKeys])))\n .orderBy(asc(jobs.position), asc(jobs.id), asc(jobExecutions.sequence), asc(jobExecutions.id));\n\n const contextsByJobKey = new Map<string, JobContextInput & {executions: JobExecution[]}>();\n for (const row of rows) {\n let context = contextsByJobKey.get(row.job.key);\n if (!context) {\n context = {job: toJob(row.job), executions: []};\n contextsByJobKey.set(row.job.key, context);\n }\n if (row.execution)\n context.executions.push(toJobExecution(row.execution, row.job.name ?? row.job.key));\n }\n\n return contextsByJobKey;\n}\n\nexport interface UpdateJobStatusAtVersionParams {\n jobId: string;\n status: JobStatus;\n expectedVersion: number;\n statusReason?: JobStatusReason | null | undefined;\n evaluationTrace?: readonly PersistedEvaluationTraceEntry[] | null | undefined;\n}\n\n// Returns null on version mismatch so callers can choose throw vs treat-as-success.\nexport async function updateJobStatusAtVersion(\n tx: Tx,\n params: UpdateJobStatusAtVersionParams,\n): Promise<{job: Job; changed: boolean} | null> {\n const outputs = isJobTerminal(params.status)\n ? await reduceJobOutputs(tx, {jobId: params.jobId, status: params.status})\n : undefined;\n const rows = await tx\n .update(jobs)\n .set({\n status: params.status,\n statusReason: params.statusReason ?? null,\n ...(outputs === undefined ? {} : {outputs}),\n ...(params.evaluationTrace === undefined ? {} : {evaluationTrace: params.evaluationTrace}),\n version: sql`${jobs.version} + 1`,\n updatedAt: new Date(),\n })\n .where(\n and(\n eq(jobs.id, params.jobId),\n eq(jobs.version, params.expectedVersion),\n notInArray(jobs.status, TERMINAL_JOB_STATUSES),\n ),\n )\n .returning();\n\n const row = rows[0];\n if (!row) return null;\n const job = toJob(row);\n\n // Every terminal job-status write funnels through this one guarded UPDATE, where the\n // version match lets a single caller win. Emitting here, in the same transaction,\n // makes the terminal fact fire exactly once across all paths.\n if (isJobTerminal(job.status)) {\n const identity = await getWorkflowContextForJob(job.id, tx);\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_JOB_TERMINATED,\n payload: {\n jobId: job.id,\n workflowRunId: identity.workflowRunId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n status: job.status,\n statusReason: job.statusReason,\n },\n });\n }\n\n return {job, changed: true};\n}\n\nasync function reduceJobOutputs(\n tx: Tx,\n params: {jobId: string; status: JobStatus},\n): Promise<Record<string, unknown> | null> {\n if (params.status !== 'succeeded') return null;\n\n const [row] = await tx\n .select({outputs: jobExecutions.outputs})\n .from(jobExecutions)\n .where(and(eq(jobExecutions.jobId, params.jobId), eq(jobExecutions.status, 'succeeded')))\n .orderBy(desc(jobExecutions.sequence), desc(jobExecutions.id))\n .limit(1);\n\n return row?.outputs ? {...row.outputs} : null;\n}\n\nexport interface UpdateJobStatusParams {\n jobId: string;\n status: JobStatus;\n expectedVersion: number;\n statusReason?: JobStatusReason | null | undefined;\n evaluationTrace?: readonly PersistedEvaluationTraceEntry[] | null | undefined;\n}\n\nexport async function updateJobStatus(params: UpdateJobStatusParams): Promise<Job> {\n const statusReason = params.statusReason ?? null;\n const result = await db().transaction(async (tx) => {\n return await optimisticLockRetry({\n updateFn: () =>\n updateJobStatusAtVersion(tx, {\n jobId: params.jobId,\n status: params.status,\n expectedVersion: params.expectedVersion,\n statusReason,\n evaluationTrace: params.evaluationTrace,\n }),\n fetchFn: async () => {\n const row = (await tx.select().from(jobs).where(eq(jobs.id, params.jobId)).limit(1))[0];\n return row ? toJob(row) : undefined;\n },\n matchFn: (job) =>\n (job.status === params.status && job.statusReason === statusReason) ||\n isJobTerminal(job.status)\n ? {job, changed: false}\n : null,\n failureMessage: `Optimistic lock failure: job ${params.jobId} version ${params.expectedVersion}`,\n });\n });\n\n if (result.changed) recordWorkflowJobStatusChanged(result.job.status);\n\n return result.job;\n}\n\nexport type EvaluateJobSuccessResult = DeriveJobSuccessResult;\nexport const evaluateJobSuccess = deriveJobSuccess;\n\nexport async function resolveJobStatusFromJobExecutions(params: {\n jobId: string;\n}): Promise<{status: RuntimeCompletionStatus; jobVersion: number}> {\n const result = await db().transaction(async (tx) => {\n const jobRow = (await tx.select().from(jobs).where(eq(jobs.id, params.jobId)).limit(1))[0];\n if (!jobRow) throw new JobNotFoundError(params.jobId);\n\n const jobExecutionRows = await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.jobId, params.jobId))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id));\n const workflowContext = await getWorkflowContextForJob(params.jobId, tx);\n\n if (jobExecutionRows.length === 0) {\n throw new Error(`Cannot resolve job ${params.jobId}: no job executions found`);\n }\n\n const {status, statusReason, trace} = evaluateJobSuccess({\n success: jobRow.success,\n executions: jobExecutionRows.map((execution) =>\n toJobExecution(execution, jobRow.name ?? jobRow.key),\n ),\n jobs: await getDirectDependencyJobContexts(params.jobId, tx),\n vars: workflowContext.vars ?? undefined,\n });\n\n return optimisticLockRetry({\n updateFn: () =>\n updateJobStatusAtVersion(tx, {\n jobId: params.jobId,\n status,\n expectedVersion: jobRow.version,\n statusReason,\n evaluationTrace: trace,\n }),\n fetchFn: async () => {\n const row = (await tx.select().from(jobs).where(eq(jobs.id, params.jobId)).limit(1))[0];\n if (!row) throw new JobNotFoundError(params.jobId);\n return toJob(row);\n },\n matchFn: (job) => ({job, changed: false}),\n failureMessage: `Optimistic lock failure: job ${params.jobId} version ${jobRow.version}`,\n });\n });\n\n if (result.changed) recordWorkflowJobStatusChanged(result.job.status);\n return {\n status: result.job.status === 'succeeded' ? 'succeeded' : 'failed',\n jobVersion: result.job.version,\n };\n}\n"],"names":["readPersistedWorkflowModel","WORKFLOWS_JOB_TERMINATED","and","asc","desc","eq","inArray","notInArray","sql","isJobTerminal","JobNotFoundError","decideJobActivation","deriveJobSuccess","runtimeCompletionStatusForJob","recordWorkflowJobStatusChanged","db","writeWorkflowsOutboxEvent","jobExecutions","toJobExecution","jobs","toJob","workflowRunAttempts","toWorkflowRun","workflowRuns","getWorkflowRunById","getWorkflowContextForJob","optimisticLockRetry","TERMINAL_JOB_STATUSES","getJobsByWorkflowRunAttemptId","workflowRunAttemptId","rows","select","from","where","orderBy","position","map","getJobsByWorkflowRunId","workflowRunId","run","attempt","id","currentAttempt","limit","getJobById","row","undefined","getJobScope","jobId","workspaceId","projectId","triggerReference","innerJoin","getDirectDependencyJobContexts","tx","targetRows","target","dependencies","length","job","execution","leftJoin","key","sequence","contextsByJobId","Map","context","get","executions","set","push","name","values","evaluateJobActivations","params","result","transaction","expectedVersions","expectedVersion","jobIds","targets","runAttemptId","for","found","Set","missing","filter","has","Error","join","contextsByJobKey","directDependencyContextsByJobKey","targetsByJobId","decisions","input","model","modelJob","find","item","decision","condition","if","vars","flatMap","dependency","kind","updated","updateJobStatusAtVersion","status","statusReason","evaluationTrace","jobVersion","version","changed","existing","_changed","targetJobs","dependencyKeys","size","outputs","reduceJobOutputs","update","updatedAt","Date","returning","identity","type","payload","updateJobStatus","updateFn","fetchFn","matchFn","failureMessage","evaluateJobSuccess","resolveJobStatusFromJobExecutions","jobRow","jobExecutionRows","workflowContext","trace","success"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/db/workflow-runs/jobs.ts"],"sourcesContent":["import {readPersistedWorkflowModel} from '@shipfox/api-definitions-dto';\nimport {WORKFLOWS_JOB_TERMINATED} from '@shipfox/api-workflows-dto';\nimport type {ExpressionType} from '@shipfox/expression';\nimport {and, asc, desc, eq, inArray, notInArray, sql} from 'drizzle-orm';\nimport {isJobTerminal, type Job, type JobStatus, type JobStatusReason} from '#core/entities/job.js';\nimport type {JobExecution} from '#core/entities/job-execution.js';\nimport type {PersistedEvaluationTraceEntry} from '#core/entities/step.js';\nimport type {WorkflowRunTriggerReference} from '#core/entities/workflow-run.js';\nimport {JobNotFoundError} from '#core/errors.js';\nimport {\n type DeriveJobSuccessResult,\n decideJobActivation,\n deriveJobSuccess,\n runtimeCompletionStatusForJob,\n} from '#core/job-transition/index.js';\nimport type {JobContextInput} from '#core/step-config/assemble-run-context.js';\nimport type {RuntimeCompletionStatus} from '#core/workflow-scheduling/runtime-dag.js';\nimport {recordWorkflowJobStatusChanged} from '#metrics/instance.js';\nimport {db, type Tx} from '../db.js';\nimport {writeWorkflowsOutboxEvent} from '../outbox-writes.js';\nimport {jobExecutions, toJobExecution} from '../schema/job-executions.js';\nimport {jobs, toJob} from '../schema/jobs.js';\nimport {workflowRunAttempts} from '../schema/workflow-run-attempts.js';\nimport {toWorkflowRun, workflowRuns} from '../schema/workflow-runs.js';\nimport {getWorkflowRunById} from './queries.js';\nimport {getWorkflowContextForJob, optimisticLockRetry, TERMINAL_JOB_STATUSES} from './shared.js';\n\nfunction outputTypesByJobKey(\n model: ReturnType<typeof readPersistedWorkflowModel> | null,\n): ReadonlyMap<string, Readonly<Record<string, ExpressionType>>> {\n return new Map(\n (model?.jobs ?? []).flatMap((job) =>\n job.outputTypes === undefined ? [] : [[job.key, job.outputTypes] as const],\n ),\n );\n}\n\nexport async function getJobsByWorkflowRunAttemptId(workflowRunAttemptId: string): Promise<Job[]> {\n const rows = await db()\n .select()\n .from(jobs)\n .where(eq(jobs.workflowRunAttemptId, workflowRunAttemptId))\n .orderBy(asc(jobs.position));\n return rows.map(toJob);\n}\n\nexport async function getJobsByWorkflowRunId(workflowRunId: string): Promise<Job[]> {\n const run = await getWorkflowRunById(workflowRunId);\n if (!run) return [];\n const [attempt] = await db()\n .select()\n .from(workflowRunAttempts)\n .where(\n and(\n eq(workflowRunAttempts.workflowRunId, run.id),\n eq(workflowRunAttempts.attempt, run.currentAttempt),\n ),\n )\n .limit(1);\n return attempt ? getJobsByWorkflowRunAttemptId(attempt.id) : [];\n}\n\nexport async function getJobById(id: string): Promise<Job | undefined> {\n const rows = await db().select().from(jobs).where(eq(jobs.id, id)).limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toJob(row);\n}\n\nexport interface JobScope {\n workspaceId: string;\n projectId: string;\n triggerReference: WorkflowRunTriggerReference | null;\n}\n\nexport async function getJobScope(jobId: string): Promise<JobScope | undefined> {\n const rows = await db()\n .select({\n workspaceId: workflowRuns.workspaceId,\n projectId: workflowRuns.projectId,\n triggerReference: workflowRuns.triggerReference,\n })\n .from(jobs)\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(eq(jobs.id, jobId))\n .limit(1);\n return rows[0];\n}\n\nexport async function getDirectDependencyJobContexts(\n jobId: string,\n tx?: Tx,\n): Promise<JobContextInput[]> {\n const targetRows = await (tx ?? db())\n .select({job: jobs, attempt: workflowRunAttempts})\n .from(jobs)\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .where(eq(jobs.id, jobId))\n .limit(1);\n const target = targetRows[0];\n if (!target || target.job.dependencies.length === 0) return [];\n const model =\n target.attempt.model === null ? null : readPersistedWorkflowModel(target.attempt.model);\n const outputTypes = outputTypesByJobKey(model);\n\n const rows = await (tx ?? db())\n .select({job: jobs, execution: jobExecutions})\n .from(jobs)\n .leftJoin(jobExecutions, eq(jobExecutions.jobId, jobs.id))\n .where(\n and(\n eq(jobs.workflowRunAttemptId, target.job.workflowRunAttemptId),\n inArray(jobs.key, target.job.dependencies),\n ),\n )\n .orderBy(asc(jobs.position), asc(jobs.id), asc(jobExecutions.sequence), asc(jobExecutions.id));\n\n const contextsByJobId = new Map<string, JobContextInput & {executions: JobExecution[]}>();\n for (const row of rows) {\n let context = contextsByJobId.get(row.job.id);\n if (!context) {\n const jobOutputTypes = outputTypes.get(row.job.key);\n context = {\n job: toJob(row.job),\n ...(jobOutputTypes === undefined ? {} : {outputTypes: jobOutputTypes}),\n executions: [],\n };\n contextsByJobId.set(row.job.id, context);\n }\n if (row.execution)\n context.executions.push(toJobExecution(row.execution, row.job.name ?? row.job.key));\n }\n\n return [...contextsByJobId.values()];\n}\n\nexport interface EvaluateJobActivationsParams {\n runAttemptId: string;\n jobs: readonly {\n jobId: string;\n expectedVersion: number;\n }[];\n}\n\nexport type JobActivationDecision =\n | {\n kind: 'start-job';\n jobId: string;\n }\n | {\n kind: 'terminal-job';\n jobId: string;\n status: RuntimeCompletionStatus;\n jobVersion: number;\n };\n\ntype InternalJobActivationDecision = JobActivationDecision & {changed?: boolean};\n\nexport async function evaluateJobActivations(\n params: EvaluateJobActivationsParams,\n): Promise<JobActivationDecision[]> {\n if (params.jobs.length === 0) return [];\n\n const result = await db().transaction(async (tx) => {\n const expectedVersions = new Map(\n params.jobs.map((job) => [job.jobId, job.expectedVersion] as const),\n );\n const jobIds = params.jobs.map((job) => job.jobId);\n const targets = await tx\n .select({job: jobs, attempt: workflowRunAttempts, run: workflowRuns})\n .from(jobs)\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(and(eq(jobs.workflowRunAttemptId, params.runAttemptId), inArray(jobs.id, jobIds)))\n .for('update');\n\n if (targets.length !== jobIds.length) {\n const found = new Set(targets.map((target) => target.job.id));\n const missing = jobIds.filter((jobId) => !found.has(jobId));\n throw new Error(`Cannot evaluate missing activation jobs: ${missing.join(', ')}`);\n }\n\n const contextsByJobKey = await directDependencyContextsByJobKey(\n tx,\n params.runAttemptId,\n targets.map((target) => toJob(target.job)),\n );\n const targetsByJobId = new Map(targets.map((target) => [target.job.id, target]));\n const decisions: InternalJobActivationDecision[] = [];\n\n for (const input of params.jobs) {\n const target = targetsByJobId.get(input.jobId);\n if (!target) throw new Error(`Cannot evaluate missing activation job: ${input.jobId}`);\n const job = toJob(target.job);\n const model =\n target.attempt.model === null ? null : readPersistedWorkflowModel(target.attempt.model);\n const modelJob = model?.jobs.find((item) => item.key === target.job.key);\n const decision = decideJobActivation({\n run: toWorkflowRun(target.run),\n job,\n condition: modelJob?.if,\n vars: target.attempt.vars ?? undefined,\n dependencies: job.dependencies.flatMap((key) => {\n const dependency = contextsByJobKey.get(key);\n return dependency === undefined ? [] : [dependency];\n }),\n });\n\n if (decision.kind === 'terminal-job' || decision.kind === 'start-job') {\n decisions.push(decision);\n continue;\n }\n\n const expectedVersion = expectedVersions.get(job.id);\n if (expectedVersion === undefined) {\n throw new Error(`Missing expected version for activation job ${job.id}`);\n }\n const updated = await updateJobStatusAtVersion(tx, {\n jobId: job.id,\n status: decision.status,\n expectedVersion,\n statusReason: decision.statusReason,\n evaluationTrace: decision.evaluationTrace,\n });\n if (updated) {\n decisions.push({\n kind: 'terminal-job',\n jobId: job.id,\n status: 'skipped',\n jobVersion: updated.job.version,\n changed: updated.changed,\n });\n continue;\n }\n\n const [existing] = await tx.select().from(jobs).where(eq(jobs.id, job.id)).limit(1);\n if (existing && isJobTerminal(existing.status)) {\n decisions.push({\n kind: 'terminal-job',\n jobId: job.id,\n status: runtimeCompletionStatusForJob(existing.status),\n jobVersion: existing.version,\n });\n continue;\n }\n throw new Error(`Optimistic lock failure evaluating activation for job ${job.id}`);\n }\n\n return decisions;\n });\n\n for (const decision of result) {\n if (decision.kind === 'terminal-job' && decision.changed) {\n recordWorkflowJobStatusChanged(decision.status);\n }\n }\n\n return result.map(({changed: _changed, ...decision}) => decision);\n}\n\nasync function directDependencyContextsByJobKey(\n tx: Tx,\n runAttemptId: string,\n targetJobs: readonly Job[],\n): Promise<ReadonlyMap<string, JobContextInput>> {\n const dependencyKeys = new Set(targetJobs.flatMap((job) => job.dependencies));\n if (dependencyKeys.size === 0) return new Map();\n\n const [attempt] = await tx\n .select({model: workflowRunAttempts.model})\n .from(workflowRunAttempts)\n .where(eq(workflowRunAttempts.id, runAttemptId))\n .limit(1);\n const model =\n attempt === undefined || attempt.model === null\n ? null\n : readPersistedWorkflowModel(attempt.model);\n const outputTypes = outputTypesByJobKey(model);\n\n const rows = await tx\n .select({job: jobs, execution: jobExecutions})\n .from(jobs)\n .leftJoin(jobExecutions, eq(jobExecutions.jobId, jobs.id))\n .where(and(eq(jobs.workflowRunAttemptId, runAttemptId), inArray(jobs.key, [...dependencyKeys])))\n .orderBy(asc(jobs.position), asc(jobs.id), asc(jobExecutions.sequence), asc(jobExecutions.id));\n\n const contextsByJobKey = new Map<string, JobContextInput & {executions: JobExecution[]}>();\n for (const row of rows) {\n let context = contextsByJobKey.get(row.job.key);\n if (!context) {\n const jobOutputTypes = outputTypes.get(row.job.key);\n context = {\n job: toJob(row.job),\n ...(jobOutputTypes === undefined ? {} : {outputTypes: jobOutputTypes}),\n executions: [],\n };\n contextsByJobKey.set(row.job.key, context);\n }\n if (row.execution)\n context.executions.push(toJobExecution(row.execution, row.job.name ?? row.job.key));\n }\n\n return contextsByJobKey;\n}\n\nexport interface UpdateJobStatusAtVersionParams {\n jobId: string;\n status: JobStatus;\n expectedVersion: number;\n statusReason?: JobStatusReason | null | undefined;\n evaluationTrace?: readonly PersistedEvaluationTraceEntry[] | null | undefined;\n}\n\n// Returns null on version mismatch so callers can choose throw vs treat-as-success.\nexport async function updateJobStatusAtVersion(\n tx: Tx,\n params: UpdateJobStatusAtVersionParams,\n): Promise<{job: Job; changed: boolean} | null> {\n const outputs = isJobTerminal(params.status)\n ? await reduceJobOutputs(tx, {jobId: params.jobId, status: params.status})\n : undefined;\n const rows = await tx\n .update(jobs)\n .set({\n status: params.status,\n statusReason: params.statusReason ?? null,\n ...(outputs === undefined ? {} : {outputs}),\n ...(params.evaluationTrace === undefined ? {} : {evaluationTrace: params.evaluationTrace}),\n version: sql`${jobs.version} + 1`,\n updatedAt: new Date(),\n })\n .where(\n and(\n eq(jobs.id, params.jobId),\n eq(jobs.version, params.expectedVersion),\n notInArray(jobs.status, TERMINAL_JOB_STATUSES),\n ),\n )\n .returning();\n\n const row = rows[0];\n if (!row) return null;\n const job = toJob(row);\n\n // Every terminal job-status write funnels through this one guarded UPDATE, where the\n // version match lets a single caller win. Emitting here, in the same transaction,\n // makes the terminal fact fire exactly once across all paths.\n if (isJobTerminal(job.status)) {\n const [currentExecution] = await tx\n .select({id: jobExecutions.id})\n .from(jobExecutions)\n .where(eq(jobExecutions.jobId, job.id))\n .orderBy(desc(jobExecutions.sequence), desc(jobExecutions.id))\n .limit(1);\n const identity = await getWorkflowContextForJob(job.id, tx);\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_JOB_TERMINATED,\n payload: {\n jobId: job.id,\n jobExecutionId: currentExecution?.id ?? null,\n workflowRunId: identity.workflowRunId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n status: job.status,\n statusReason: job.statusReason,\n },\n });\n }\n\n return {job, changed: true};\n}\n\nasync function reduceJobOutputs(\n tx: Tx,\n params: {jobId: string; status: JobStatus},\n): Promise<Record<string, unknown> | null> {\n if (params.status !== 'succeeded') return null;\n\n const [row] = await tx\n .select({outputs: jobExecutions.outputs})\n .from(jobExecutions)\n .where(and(eq(jobExecutions.jobId, params.jobId), eq(jobExecutions.status, 'succeeded')))\n .orderBy(desc(jobExecutions.sequence), desc(jobExecutions.id))\n .limit(1);\n\n return row?.outputs ? {...row.outputs} : null;\n}\n\nexport interface UpdateJobStatusParams {\n jobId: string;\n status: JobStatus;\n expectedVersion: number;\n statusReason?: JobStatusReason | null | undefined;\n evaluationTrace?: readonly PersistedEvaluationTraceEntry[] | null | undefined;\n}\n\nexport async function updateJobStatus(params: UpdateJobStatusParams): Promise<Job> {\n const statusReason = params.statusReason ?? null;\n const result = await db().transaction(async (tx) => {\n return await optimisticLockRetry({\n updateFn: () =>\n updateJobStatusAtVersion(tx, {\n jobId: params.jobId,\n status: params.status,\n expectedVersion: params.expectedVersion,\n statusReason,\n evaluationTrace: params.evaluationTrace,\n }),\n fetchFn: async () => {\n const row = (await tx.select().from(jobs).where(eq(jobs.id, params.jobId)).limit(1))[0];\n return row ? toJob(row) : undefined;\n },\n matchFn: (job) =>\n (job.status === params.status && job.statusReason === statusReason) ||\n isJobTerminal(job.status)\n ? {job, changed: false}\n : null,\n failureMessage: `Optimistic lock failure: job ${params.jobId} version ${params.expectedVersion}`,\n });\n });\n\n if (result.changed) recordWorkflowJobStatusChanged(result.job.status);\n\n return result.job;\n}\n\nexport type EvaluateJobSuccessResult = DeriveJobSuccessResult;\nexport const evaluateJobSuccess = deriveJobSuccess;\n\nexport async function resolveJobStatusFromJobExecutions(params: {\n jobId: string;\n}): Promise<{status: RuntimeCompletionStatus; jobVersion: number}> {\n const result = await db().transaction(async (tx) => {\n const jobRow = (await tx.select().from(jobs).where(eq(jobs.id, params.jobId)).limit(1))[0];\n if (!jobRow) throw new JobNotFoundError(params.jobId);\n\n const jobExecutionRows = await tx\n .select()\n .from(jobExecutions)\n .where(eq(jobExecutions.jobId, params.jobId))\n .orderBy(asc(jobExecutions.sequence), asc(jobExecutions.id));\n const workflowContext = await getWorkflowContextForJob(params.jobId, tx);\n\n if (jobExecutionRows.length === 0) {\n throw new Error(`Cannot resolve job ${params.jobId}: no job executions found`);\n }\n\n const {status, statusReason, trace} = evaluateJobSuccess({\n success: jobRow.success,\n executions: jobExecutionRows.map((execution) =>\n toJobExecution(execution, jobRow.name ?? jobRow.key),\n ),\n jobs: await getDirectDependencyJobContexts(params.jobId, tx),\n vars: workflowContext.vars ?? undefined,\n });\n\n return optimisticLockRetry({\n updateFn: () =>\n updateJobStatusAtVersion(tx, {\n jobId: params.jobId,\n status,\n expectedVersion: jobRow.version,\n statusReason,\n evaluationTrace: trace,\n }),\n fetchFn: async () => {\n const row = (await tx.select().from(jobs).where(eq(jobs.id, params.jobId)).limit(1))[0];\n if (!row) throw new JobNotFoundError(params.jobId);\n return toJob(row);\n },\n matchFn: (job) => ({job, changed: false}),\n failureMessage: `Optimistic lock failure: job ${params.jobId} version ${jobRow.version}`,\n });\n });\n\n if (result.changed) recordWorkflowJobStatusChanged(result.job.status);\n return {\n status: result.job.status === 'succeeded' ? 'succeeded' : 'failed',\n jobVersion: result.job.version,\n };\n}\n"],"names":["readPersistedWorkflowModel","WORKFLOWS_JOB_TERMINATED","and","asc","desc","eq","inArray","notInArray","sql","isJobTerminal","JobNotFoundError","decideJobActivation","deriveJobSuccess","runtimeCompletionStatusForJob","recordWorkflowJobStatusChanged","db","writeWorkflowsOutboxEvent","jobExecutions","toJobExecution","jobs","toJob","workflowRunAttempts","toWorkflowRun","workflowRuns","getWorkflowRunById","getWorkflowContextForJob","optimisticLockRetry","TERMINAL_JOB_STATUSES","outputTypesByJobKey","model","Map","flatMap","job","outputTypes","undefined","key","getJobsByWorkflowRunAttemptId","workflowRunAttemptId","rows","select","from","where","orderBy","position","map","getJobsByWorkflowRunId","workflowRunId","run","attempt","id","currentAttempt","limit","getJobById","row","getJobScope","jobId","workspaceId","projectId","triggerReference","innerJoin","getDirectDependencyJobContexts","tx","targetRows","target","dependencies","length","execution","leftJoin","sequence","contextsByJobId","context","get","jobOutputTypes","executions","set","push","name","values","evaluateJobActivations","params","result","transaction","expectedVersions","expectedVersion","jobIds","targets","runAttemptId","for","found","Set","missing","filter","has","Error","join","contextsByJobKey","directDependencyContextsByJobKey","targetsByJobId","decisions","input","modelJob","find","item","decision","condition","if","vars","dependency","kind","updated","updateJobStatusAtVersion","status","statusReason","evaluationTrace","jobVersion","version","changed","existing","_changed","targetJobs","dependencyKeys","size","outputs","reduceJobOutputs","update","updatedAt","Date","returning","currentExecution","identity","type","payload","jobExecutionId","updateJobStatus","updateFn","fetchFn","matchFn","failureMessage","evaluateJobSuccess","resolveJobStatusFromJobExecutions","jobRow","jobExecutionRows","workflowContext","trace","success"],"mappings":"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"}
@@ -8,6 +8,7 @@ export declare function writeJobStepsSettledOutbox(tx: Tx, params: {
8
8
  export declare function writeStepAttemptTerminatedOutbox(tx: Tx, params: {
9
9
  stepId: string;
10
10
  attempt: number;
11
+ status: 'succeeded' | 'failed' | 'cancelled';
11
12
  logOutcome: LogOutcomeDto;
12
13
  }): Promise<void>;
13
14
  export declare function writeStepRestartEnqueuedOutbox(tx: Tx, params: {
@@ -1 +1 @@
1
- {"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../../src/db/workflow-runs/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,UAAU,CAAC;AAUjC,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,EAAE,EACN,MAAM,EAAE;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAA;CAAC,GAC9E,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,gCAAgC,CACpD,EAAE,EAAE,EAAE,EACN,MAAM,EAAE;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,aAAa,CAAA;CAAC,GACnE,OAAO,CAAC,IAAI,CAAC,CAgBf;AA8BD,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,EAAE,EACN,MAAM,EAAE;IACN,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB,GACA,OAAO,CAAC,IAAI,CAAC,CAef"}
1
+ {"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../../src/db/workflow-runs/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,UAAU,CAAC;AAUjC,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,EAAE,EACN,MAAM,EAAE;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAA;CAAC,GAC9E,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,gCAAgC,CACpD,EAAE,EAAE,EAAE,EACN,MAAM,EAAE;IACN,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC7C,UAAU,EAAE,aAAa,CAAC;CAC3B,GACA,OAAO,CAAC,IAAI,CAAC,CAiBf;AA8BD,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,EAAE,EACN,MAAM,EAAE;IACN,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB,GACA,OAAO,CAAC,IAAI,CAAC,CAef"}
@@ -33,6 +33,7 @@ export async function writeStepAttemptTerminatedOutbox(tx, params) {
33
33
  projectId: identity.projectId,
34
34
  stepId: params.stepId,
35
35
  attempt: params.attempt,
36
+ status: params.status,
36
37
  logOutcome: params.logOutcome
37
38
  }
38
39
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/db/workflow-runs/outbox.ts"],"sourcesContent":["import {\n type LogOutcomeDto,\n WORKFLOWS_JOB_STEPS_SETTLED,\n WORKFLOWS_STEP_ATTEMPT_TERMINATED,\n WORKFLOWS_STEP_RESTART_ENQUEUED,\n} from '@shipfox/api-workflows-dto';\nimport {eq} from 'drizzle-orm';\nimport type {Tx} from '../db.js';\nimport {writeWorkflowsOutboxEvent} from '../outbox-writes.js';\nimport {jobExecutions} from '../schema/job-executions.js';\nimport {steps} from '../schema/steps.js';\nimport {getWorkflowContextForJob} from './shared.js';\n\n// Enqueue the steps-settled signal in the same transaction as the final per-step\n// result, so per-step execution observes it exactly once (the outbox is at-least-once;\n// the job workflow dedupes the signal). Drives the Temporal JOB_FINISHED_SIGNAL; the\n// job's terminal fact is emitted separately by updateJobStatusAtVersion.\nexport async function writeJobStepsSettledOutbox(\n tx: Tx,\n params: {jobId: string; jobExecutionId: string; status: 'succeeded' | 'failed'},\n): Promise<void> {\n const identity = await getWorkflowContextForJob(params.jobId, tx);\n\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_JOB_STEPS_SETTLED,\n payload: {\n jobId: params.jobId,\n jobExecutionId: params.jobExecutionId,\n workflowRunId: identity.workflowRunId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n status: params.status,\n },\n });\n}\n\nexport async function writeStepAttemptTerminatedOutbox(\n tx: Tx,\n params: {stepId: string; attempt: number; logOutcome: LogOutcomeDto},\n): Promise<void> {\n const identity = await getStepAttemptTerminatedOutboxIdentity(tx, params.stepId);\n\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_STEP_ATTEMPT_TERMINATED,\n payload: {\n jobId: identity.jobId,\n workflowRunId: identity.workflowRunId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n workspaceId: identity.workspaceId,\n projectId: identity.projectId,\n stepId: params.stepId,\n attempt: params.attempt,\n logOutcome: params.logOutcome,\n },\n });\n}\n\nasync function getStepAttemptTerminatedOutboxIdentity(\n tx: Tx,\n stepId: string,\n): Promise<{\n jobId: string;\n workflowRunId: string;\n workflowRunAttemptId: string;\n workspaceId: string;\n projectId: string;\n}> {\n const rows = await tx\n .select({\n jobId: jobExecutions.jobId,\n })\n .from(steps)\n .innerJoin(jobExecutions, eq(steps.jobExecutionId, jobExecutions.id))\n .where(eq(steps.id, stepId))\n .limit(1);\n const step = rows[0];\n if (!step) {\n throw new Error(`Cannot enqueue step-attempt-terminated event: step ${stepId} not found`);\n }\n\n return getWorkflowContextForJob(step.jobId, tx);\n}\n\n// Enqueue the durable audit record of a restart, in the same transaction as the\n// rewind. Looks up the workflow run id like writeJobStepsSettledOutbox.\nexport async function writeStepRestartEnqueuedOutbox(\n tx: Tx,\n params: {\n jobId: string;\n failedStepId: string;\n failedStepAttempt: number;\n restartFromStepId: string;\n feedback: string;\n },\n): Promise<void> {\n const identity = await getWorkflowContextForJob(params.jobId, tx);\n\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_STEP_RESTART_ENQUEUED,\n payload: {\n jobId: params.jobId,\n workflowRunId: identity.workflowRunId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n failedStepId: params.failedStepId,\n failedStepAttempt: params.failedStepAttempt,\n restartFromStepId: params.restartFromStepId,\n feedback: params.feedback,\n },\n });\n}\n"],"names":["WORKFLOWS_JOB_STEPS_SETTLED","WORKFLOWS_STEP_ATTEMPT_TERMINATED","WORKFLOWS_STEP_RESTART_ENQUEUED","eq","writeWorkflowsOutboxEvent","jobExecutions","steps","getWorkflowContextForJob","writeJobStepsSettledOutbox","tx","params","identity","jobId","type","payload","jobExecutionId","workflowRunId","workflowRunAttemptId","status","writeStepAttemptTerminatedOutbox","getStepAttemptTerminatedOutboxIdentity","stepId","workspaceId","projectId","attempt","logOutcome","rows","select","from","innerJoin","id","where","limit","step","Error","writeStepRestartEnqueuedOutbox","failedStepId","failedStepAttempt","restartFromStepId","feedback"],"mappings":"AAAA,SAEEA,2BAA2B,EAC3BC,iCAAiC,EACjCC,+BAA+B,QAC1B,6BAA6B;AACpC,SAAQC,EAAE,QAAO,cAAc;AAE/B,SAAQC,yBAAyB,QAAO,sBAAsB;AAC9D,SAAQC,aAAa,QAAO,8BAA8B;AAC1D,SAAQC,KAAK,QAAO,qBAAqB;AACzC,SAAQC,wBAAwB,QAAO,cAAc;AAErD,iFAAiF;AACjF,uFAAuF;AACvF,qFAAqF;AACrF,yEAAyE;AACzE,OAAO,eAAeC,2BACpBC,EAAM,EACNC,MAA+E;IAE/E,MAAMC,WAAW,MAAMJ,yBAAyBG,OAAOE,KAAK,EAAEH;IAE9D,MAAML,0BAA0BK,IAAI;QAClCI,MAAMb;QACNc,SAAS;YACPF,OAAOF,OAAOE,KAAK;YACnBG,gBAAgBL,OAAOK,cAAc;YACrCC,eAAeL,SAASK,aAAa;YACrCC,sBAAsBN,SAASM,oBAAoB;YACnDC,QAAQR,OAAOQ,MAAM;QACvB;IACF;AACF;AAEA,OAAO,eAAeC,iCACpBV,EAAM,EACNC,MAAoE;IAEpE,MAAMC,WAAW,MAAMS,uCAAuCX,IAAIC,OAAOW,MAAM;IAE/E,MAAMjB,0BAA0BK,IAAI;QAClCI,MAAMZ;QACNa,SAAS;YACPF,OAAOD,SAASC,KAAK;YACrBI,eAAeL,SAASK,aAAa;YACrCC,sBAAsBN,SAASM,oBAAoB;YACnDK,aAAaX,SAASW,WAAW;YACjCC,WAAWZ,SAASY,SAAS;YAC7BF,QAAQX,OAAOW,MAAM;YACrBG,SAASd,OAAOc,OAAO;YACvBC,YAAYf,OAAOe,UAAU;QAC/B;IACF;AACF;AAEA,eAAeL,uCACbX,EAAM,EACNY,MAAc;IAQd,MAAMK,OAAO,MAAMjB,GAChBkB,MAAM,CAAC;QACNf,OAAOP,cAAcO,KAAK;IAC5B,GACCgB,IAAI,CAACtB,OACLuB,SAAS,CAACxB,eAAeF,GAAGG,MAAMS,cAAc,EAAEV,cAAcyB,EAAE,GAClEC,KAAK,CAAC5B,GAAGG,MAAMwB,EAAE,EAAET,SACnBW,KAAK,CAAC;IACT,MAAMC,OAAOP,IAAI,CAAC,EAAE;IACpB,IAAI,CAACO,MAAM;QACT,MAAM,IAAIC,MAAM,CAAC,mDAAmD,EAAEb,OAAO,UAAU,CAAC;IAC1F;IAEA,OAAOd,yBAAyB0B,KAAKrB,KAAK,EAAEH;AAC9C;AAEA,gFAAgF;AAChF,wEAAwE;AACxE,OAAO,eAAe0B,+BACpB1B,EAAM,EACNC,MAMC;IAED,MAAMC,WAAW,MAAMJ,yBAAyBG,OAAOE,KAAK,EAAEH;IAE9D,MAAML,0BAA0BK,IAAI;QAClCI,MAAMX;QACNY,SAAS;YACPF,OAAOF,OAAOE,KAAK;YACnBI,eAAeL,SAASK,aAAa;YACrCC,sBAAsBN,SAASM,oBAAoB;YACnDmB,cAAc1B,OAAO0B,YAAY;YACjCC,mBAAmB3B,OAAO2B,iBAAiB;YAC3CC,mBAAmB5B,OAAO4B,iBAAiB;YAC3CC,UAAU7B,OAAO6B,QAAQ;QAC3B;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/db/workflow-runs/outbox.ts"],"sourcesContent":["import {\n type LogOutcomeDto,\n WORKFLOWS_JOB_STEPS_SETTLED,\n WORKFLOWS_STEP_ATTEMPT_TERMINATED,\n WORKFLOWS_STEP_RESTART_ENQUEUED,\n} from '@shipfox/api-workflows-dto';\nimport {eq} from 'drizzle-orm';\nimport type {Tx} from '../db.js';\nimport {writeWorkflowsOutboxEvent} from '../outbox-writes.js';\nimport {jobExecutions} from '../schema/job-executions.js';\nimport {steps} from '../schema/steps.js';\nimport {getWorkflowContextForJob} from './shared.js';\n\n// Enqueue the steps-settled signal in the same transaction as the final per-step\n// result, so per-step execution observes it exactly once (the outbox is at-least-once;\n// the job workflow dedupes the signal). Drives the Temporal JOB_FINISHED_SIGNAL; the\n// job's terminal fact is emitted separately by updateJobStatusAtVersion.\nexport async function writeJobStepsSettledOutbox(\n tx: Tx,\n params: {jobId: string; jobExecutionId: string; status: 'succeeded' | 'failed'},\n): Promise<void> {\n const identity = await getWorkflowContextForJob(params.jobId, tx);\n\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_JOB_STEPS_SETTLED,\n payload: {\n jobId: params.jobId,\n jobExecutionId: params.jobExecutionId,\n workflowRunId: identity.workflowRunId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n status: params.status,\n },\n });\n}\n\nexport async function writeStepAttemptTerminatedOutbox(\n tx: Tx,\n params: {\n stepId: string;\n attempt: number;\n status: 'succeeded' | 'failed' | 'cancelled';\n logOutcome: LogOutcomeDto;\n },\n): Promise<void> {\n const identity = await getStepAttemptTerminatedOutboxIdentity(tx, params.stepId);\n\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_STEP_ATTEMPT_TERMINATED,\n payload: {\n jobId: identity.jobId,\n workflowRunId: identity.workflowRunId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n workspaceId: identity.workspaceId,\n projectId: identity.projectId,\n stepId: params.stepId,\n attempt: params.attempt,\n status: params.status,\n logOutcome: params.logOutcome,\n },\n });\n}\n\nasync function getStepAttemptTerminatedOutboxIdentity(\n tx: Tx,\n stepId: string,\n): Promise<{\n jobId: string;\n workflowRunId: string;\n workflowRunAttemptId: string;\n workspaceId: string;\n projectId: string;\n}> {\n const rows = await tx\n .select({\n jobId: jobExecutions.jobId,\n })\n .from(steps)\n .innerJoin(jobExecutions, eq(steps.jobExecutionId, jobExecutions.id))\n .where(eq(steps.id, stepId))\n .limit(1);\n const step = rows[0];\n if (!step) {\n throw new Error(`Cannot enqueue step-attempt-terminated event: step ${stepId} not found`);\n }\n\n return getWorkflowContextForJob(step.jobId, tx);\n}\n\n// Enqueue the durable audit record of a restart, in the same transaction as the\n// rewind. Looks up the workflow run id like writeJobStepsSettledOutbox.\nexport async function writeStepRestartEnqueuedOutbox(\n tx: Tx,\n params: {\n jobId: string;\n failedStepId: string;\n failedStepAttempt: number;\n restartFromStepId: string;\n feedback: string;\n },\n): Promise<void> {\n const identity = await getWorkflowContextForJob(params.jobId, tx);\n\n await writeWorkflowsOutboxEvent(tx, {\n type: WORKFLOWS_STEP_RESTART_ENQUEUED,\n payload: {\n jobId: params.jobId,\n workflowRunId: identity.workflowRunId,\n workflowRunAttemptId: identity.workflowRunAttemptId,\n failedStepId: params.failedStepId,\n failedStepAttempt: params.failedStepAttempt,\n restartFromStepId: params.restartFromStepId,\n feedback: params.feedback,\n },\n });\n}\n"],"names":["WORKFLOWS_JOB_STEPS_SETTLED","WORKFLOWS_STEP_ATTEMPT_TERMINATED","WORKFLOWS_STEP_RESTART_ENQUEUED","eq","writeWorkflowsOutboxEvent","jobExecutions","steps","getWorkflowContextForJob","writeJobStepsSettledOutbox","tx","params","identity","jobId","type","payload","jobExecutionId","workflowRunId","workflowRunAttemptId","status","writeStepAttemptTerminatedOutbox","getStepAttemptTerminatedOutboxIdentity","stepId","workspaceId","projectId","attempt","logOutcome","rows","select","from","innerJoin","id","where","limit","step","Error","writeStepRestartEnqueuedOutbox","failedStepId","failedStepAttempt","restartFromStepId","feedback"],"mappings":"AAAA,SAEEA,2BAA2B,EAC3BC,iCAAiC,EACjCC,+BAA+B,QAC1B,6BAA6B;AACpC,SAAQC,EAAE,QAAO,cAAc;AAE/B,SAAQC,yBAAyB,QAAO,sBAAsB;AAC9D,SAAQC,aAAa,QAAO,8BAA8B;AAC1D,SAAQC,KAAK,QAAO,qBAAqB;AACzC,SAAQC,wBAAwB,QAAO,cAAc;AAErD,iFAAiF;AACjF,uFAAuF;AACvF,qFAAqF;AACrF,yEAAyE;AACzE,OAAO,eAAeC,2BACpBC,EAAM,EACNC,MAA+E;IAE/E,MAAMC,WAAW,MAAMJ,yBAAyBG,OAAOE,KAAK,EAAEH;IAE9D,MAAML,0BAA0BK,IAAI;QAClCI,MAAMb;QACNc,SAAS;YACPF,OAAOF,OAAOE,KAAK;YACnBG,gBAAgBL,OAAOK,cAAc;YACrCC,eAAeL,SAASK,aAAa;YACrCC,sBAAsBN,SAASM,oBAAoB;YACnDC,QAAQR,OAAOQ,MAAM;QACvB;IACF;AACF;AAEA,OAAO,eAAeC,iCACpBV,EAAM,EACNC,MAKC;IAED,MAAMC,WAAW,MAAMS,uCAAuCX,IAAIC,OAAOW,MAAM;IAE/E,MAAMjB,0BAA0BK,IAAI;QAClCI,MAAMZ;QACNa,SAAS;YACPF,OAAOD,SAASC,KAAK;YACrBI,eAAeL,SAASK,aAAa;YACrCC,sBAAsBN,SAASM,oBAAoB;YACnDK,aAAaX,SAASW,WAAW;YACjCC,WAAWZ,SAASY,SAAS;YAC7BF,QAAQX,OAAOW,MAAM;YACrBG,SAASd,OAAOc,OAAO;YACvBN,QAAQR,OAAOQ,MAAM;YACrBO,YAAYf,OAAOe,UAAU;QAC/B;IACF;AACF;AAEA,eAAeL,uCACbX,EAAM,EACNY,MAAc;IAQd,MAAMK,OAAO,MAAMjB,GAChBkB,MAAM,CAAC;QACNf,OAAOP,cAAcO,KAAK;IAC5B,GACCgB,IAAI,CAACtB,OACLuB,SAAS,CAACxB,eAAeF,GAAGG,MAAMS,cAAc,EAAEV,cAAcyB,EAAE,GAClEC,KAAK,CAAC5B,GAAGG,MAAMwB,EAAE,EAAET,SACnBW,KAAK,CAAC;IACT,MAAMC,OAAOP,IAAI,CAAC,EAAE;IACpB,IAAI,CAACO,MAAM;QACT,MAAM,IAAIC,MAAM,CAAC,mDAAmD,EAAEb,OAAO,UAAU,CAAC;IAC1F;IAEA,OAAOd,yBAAyB0B,KAAKrB,KAAK,EAAEH;AAC9C;AAEA,gFAAgF;AAChF,wEAAwE;AACxE,OAAO,eAAe0B,+BACpB1B,EAAM,EACNC,MAMC;IAED,MAAMC,WAAW,MAAMJ,yBAAyBG,OAAOE,KAAK,EAAEH;IAE9D,MAAML,0BAA0BK,IAAI;QAClCI,MAAMX;QACNY,SAAS;YACPF,OAAOF,OAAOE,KAAK;YACnBI,eAAeL,SAASK,aAAa;YACrCC,sBAAsBN,SAASM,oBAAoB;YACnDmB,cAAc1B,OAAO0B,YAAY;YACjCC,mBAAmB3B,OAAO2B,iBAAiB;YAC3CC,mBAAmB5B,OAAO4B,iBAAiB;YAC3CC,UAAU7B,OAAO6B,QAAQ;QAC3B;IACF;AACF"}
@@ -6,6 +6,34 @@ export declare function getStepByIdForJobExecution(params: {
6
6
  jobExecutionId: string;
7
7
  }): Promise<Step | undefined>;
8
8
  export declare function getStepById(stepId: string): Promise<Step | undefined>;
9
+ export interface StepAttemptDetail {
10
+ workflowRunId: string;
11
+ workflowRunAttemptId: string;
12
+ step: Step;
13
+ attempt: StepAttempt;
14
+ }
15
+ export interface JobExecutionFailureOrigin {
16
+ jobExecutionId: string;
17
+ stepId: string;
18
+ stepName: string;
19
+ stepStatus: StepStatus;
20
+ stepAttempt: number;
21
+ stepError: Record<string, unknown> | null;
22
+ attemptStatus: StepAttemptStatus | null;
23
+ attemptError: Record<string, unknown> | null;
24
+ attemptExitCode: number | null;
25
+ }
26
+ /**
27
+ * Read only the current attempt for the most relevant step. The terminal job event identifies
28
+ * the execution, so this intentionally avoids hydrating the execution's complete attempt
29
+ * history. If no attempt was dispatched, the first step still gives the projection a stable
30
+ * troubleshooting origin for failures that happened before step work started.
31
+ */
32
+ export declare function getJobExecutionFailureOrigin(jobExecutionId: string): Promise<JobExecutionFailureOrigin | undefined>;
33
+ export declare function getStepAttemptDetail(params: {
34
+ stepId: string;
35
+ attempt: number;
36
+ }): Promise<StepAttemptDetail | undefined>;
9
37
  export declare function getStepsByJobId(jobId: string): Promise<Step[]>;
10
38
  export declare function getStepsByJobExecutionId(jobExecutionId: string): Promise<Step[]>;
11
39
  export interface BulkUpdateStepStatusesParams {
@@ -1 +1 @@
1
- {"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../../src/db/workflow-runs/steps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../../src/db/workflow-runs/steps.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,12 @@
1
- import { and, asc, count, eq, gte, inArray, sql } from 'drizzle-orm';
1
+ import { and, asc, count, desc, eq, gte, inArray, sql } from 'drizzle-orm';
2
2
  import { deriveCompletion, isTerminal } from '#core/step-transition/decide-step-transition.js';
3
3
  import { db } from '../db.js';
4
4
  import { jobExecutions } from '../schema/job-executions.js';
5
+ import { jobs } from '../schema/jobs.js';
5
6
  import { stepAttempts, toStepAttempt } from '../schema/step-attempts.js';
6
7
  import { steps, toStep } from '../schema/steps.js';
8
+ import { workflowRunAttempts } from '../schema/workflow-run-attempts.js';
9
+ import { workflowRuns } from '../schema/workflow-runs.js';
7
10
  import { writeJobStepsSettledOutbox, writeStepAttemptTerminatedOutbox } from './outbox.js';
8
11
  import { NON_TERMINAL_STEP_STATUS_FILTER } from './shared.js';
9
12
  export async function getStepByIdForJobExecution(params) {
@@ -18,6 +21,49 @@ export async function getStepById(stepId) {
18
21
  if (!row) return undefined;
19
22
  return toStep(row);
20
23
  }
24
+ /**
25
+ * Read only the current attempt for the most relevant step. The terminal job event identifies
26
+ * the execution, so this intentionally avoids hydrating the execution's complete attempt
27
+ * history. If no attempt was dispatched, the first step still gives the projection a stable
28
+ * troubleshooting origin for failures that happened before step work started.
29
+ */ export async function getJobExecutionFailureOrigin(jobExecutionId) {
30
+ const rows = await db().select({
31
+ jobExecutionId: jobExecutions.id,
32
+ stepId: steps.id,
33
+ stepName: steps.name,
34
+ stepStatus: steps.status,
35
+ stepAttempt: steps.currentAttempt,
36
+ stepError: steps.error,
37
+ attemptStatus: stepAttempts.status,
38
+ attemptError: stepAttempts.error,
39
+ attemptExitCode: stepAttempts.exitCode
40
+ }).from(jobExecutions).innerJoin(steps, eq(steps.jobExecutionId, jobExecutions.id)).leftJoin(stepAttempts, and(eq(stepAttempts.stepId, steps.id), eq(stepAttempts.attempt, steps.currentAttempt))).where(eq(jobExecutions.id, jobExecutionId)).orderBy(desc(sql`case when ${steps.status} = 'failed' or ${stepAttempts.status} = 'failed' or ${steps.statusReason} = 'condition_errored' then 1 else 0 end`), asc(steps.position), asc(steps.id)).limit(1);
41
+ const row = rows[0];
42
+ if (!row) return undefined;
43
+ return {
44
+ ...row,
45
+ stepError: row.stepError ?? null,
46
+ attemptError: row.attemptError ?? null,
47
+ attemptStatus: row.attemptStatus === null ? null : row.attemptStatus,
48
+ attemptExitCode: row.attemptExitCode ?? null
49
+ };
50
+ }
51
+ export async function getStepAttemptDetail(params) {
52
+ const rows = await db().select({
53
+ workflowRunId: workflowRuns.id,
54
+ workflowRunAttemptId: workflowRunAttempts.id,
55
+ step: steps,
56
+ stepAttempt: stepAttempts
57
+ }).from(stepAttempts).innerJoin(steps, eq(stepAttempts.stepId, steps.id)).innerJoin(jobExecutions, eq(steps.jobExecutionId, jobExecutions.id)).innerJoin(jobs, eq(jobExecutions.jobId, jobs.id)).innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id)).innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id)).where(and(eq(stepAttempts.stepId, params.stepId), eq(stepAttempts.attempt, params.attempt))).limit(1);
58
+ const row = rows[0];
59
+ if (!row) return undefined;
60
+ return {
61
+ workflowRunId: row.workflowRunId,
62
+ workflowRunAttemptId: row.workflowRunAttemptId,
63
+ step: toStep(row.step),
64
+ attempt: toStepAttempt(row.stepAttempt)
65
+ };
66
+ }
21
67
  export async function getStepsByJobId(jobId) {
22
68
  const rows = await db().select({
23
69
  step: steps
@@ -54,6 +100,7 @@ export async function bulkUpdateStepStatuses(params, tx) {
54
100
  await writeStepAttemptTerminatedOutbox(tx, {
55
101
  stepId: attempt.stepId,
56
102
  attempt: attempt.attempt,
103
+ status: params.status,
57
104
  logOutcome: attempt.logOutcome ?? 'abandoned'
58
105
  });
59
106
  }
@@ -185,6 +232,7 @@ export async function finishStepAttempt(params, tx) {
185
232
  await writeStepAttemptTerminatedOutbox(tx, {
186
233
  stepId: row.stepId,
187
234
  attempt: row.attempt,
235
+ status: params.status,
188
236
  logOutcome: row.logOutcome ?? params.logOutcome
189
237
  });
190
238
  }