@shipfox/api-workflows 13.1.0 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +31 -0
  3. package/README.md +12 -5
  4. package/dist/core/checkout.d.ts +4 -2
  5. package/dist/core/checkout.d.ts.map +1 -1
  6. package/dist/core/checkout.js +7 -2
  7. package/dist/core/checkout.js.map +1 -1
  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/job.js.map +1 -1
  11. package/dist/core/entities/workflow-run.d.ts +23 -4
  12. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  13. package/dist/core/entities/workflow-run.js.map +1 -1
  14. package/dist/core/run-workflow.js +1 -1
  15. package/dist/core/run-workflow.js.map +1 -1
  16. package/dist/db/db.d.ts +72 -0
  17. package/dist/db/db.d.ts.map +1 -1
  18. package/dist/db/schema/workflow-runs.d.ts +45 -1
  19. package/dist/db/schema/workflow-runs.d.ts.map +1 -1
  20. package/dist/db/schema/workflow-runs.js +42 -1
  21. package/dist/db/schema/workflow-runs.js.map +1 -1
  22. package/dist/db/workflow-runs/jobs.d.ts +3 -1
  23. package/dist/db/workflow-runs/jobs.d.ts.map +1 -1
  24. package/dist/db/workflow-runs/jobs.js +12 -3
  25. package/dist/db/workflow-runs/jobs.js.map +1 -1
  26. package/dist/db/workflow-runs/queries.d.ts +3 -2
  27. package/dist/db/workflow-runs/queries.d.ts.map +1 -1
  28. package/dist/db/workflow-runs/queries.js +3 -0
  29. package/dist/db/workflow-runs/queries.js.map +1 -1
  30. package/dist/presentation/dto/workflow-run.d.ts.map +1 -1
  31. package/dist/presentation/dto/workflow-run.js +14 -0
  32. package/dist/presentation/dto/workflow-run.js.map +1 -1
  33. package/dist/presentation/routes/checkout-token.d.ts.map +1 -1
  34. package/dist/presentation/routes/checkout-token.js +2 -1
  35. package/dist/presentation/routes/checkout-token.js.map +1 -1
  36. package/dist/presentation/routes/get-run-aggregates.d.ts.map +1 -1
  37. package/dist/presentation/routes/get-run-aggregates.js +2 -1
  38. package/dist/presentation/routes/get-run-aggregates.js.map +1 -1
  39. package/dist/presentation/routes/leased-step.d.ts +3 -1
  40. package/dist/presentation/routes/leased-step.d.ts.map +1 -1
  41. package/dist/presentation/routes/leased-step.js +2 -1
  42. package/dist/presentation/routes/leased-step.js.map +1 -1
  43. package/dist/presentation/routes/list-runs.d.ts.map +1 -1
  44. package/dist/presentation/routes/list-runs.js +2 -1
  45. package/dist/presentation/routes/list-runs.js.map +1 -1
  46. package/dist/presentation/routes/project-access.d.ts +1 -0
  47. package/dist/presentation/routes/project-access.d.ts.map +1 -1
  48. package/dist/tsconfig.test.tsbuildinfo +1 -1
  49. package/drizzle/0004_dev_run_origin.sql +12 -0
  50. package/drizzle/meta/0004_snapshot.json +1773 -0
  51. package/drizzle/meta/_journal.json +7 -0
  52. package/package.json +10 -10
  53. package/src/core/checkout.test.ts +198 -1
  54. package/src/core/checkout.ts +18 -5
  55. package/src/core/entities/job.ts +1 -1
  56. package/src/core/entities/workflow-run.ts +22 -4
  57. package/src/core/job-transition/decide-job-activation.test.ts +2 -0
  58. package/src/core/run-workflow.test.ts +50 -1
  59. package/src/core/run-workflow.ts +1 -1
  60. package/src/core/workflow-run-creation.test.ts +2 -0
  61. package/src/db/job-listeners.test.ts +17 -0
  62. package/src/db/schema/workflow-runs.ts +58 -0
  63. package/src/db/workflow-runs/jobs.ts +17 -3
  64. package/src/db/workflow-runs/queries.ts +6 -1
  65. package/src/db/workflow-runs/run-create.test.ts +39 -0
  66. package/src/presentation/dto/workflow-run.ts +23 -1
  67. package/src/presentation/routes/checkout-token.test.ts +45 -0
  68. package/src/presentation/routes/checkout-token.ts +2 -1
  69. package/src/presentation/routes/get-run-aggregates.test.ts +56 -0
  70. package/src/presentation/routes/get-run-aggregates.ts +2 -0
  71. package/src/presentation/routes/get-step-secrets.test.ts +1 -0
  72. package/src/presentation/routes/leased-step.ts +7 -1
  73. package/src/presentation/routes/list-runs.test.ts +152 -0
  74. package/src/presentation/routes/list-runs.ts +2 -0
  75. package/test/factories/workflow-run.ts +2 -0
  76. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/db/workflow-runs/queries.ts"],"sourcesContent":["import {WORKFLOW_RUN_JOB_PREVIEW_LIMIT} from '@shipfox/api-workflows-dto';\nimport {\n paginateTimestampIdRows,\n type TimestampIdCursor,\n timestampIdCursorWhere,\n} from '@shipfox/node-drizzle';\nimport {and, asc, count, desc, eq, gte, lte, type SQL, sql} from 'drizzle-orm';\nimport type {JobMode, JobStatus, ListenerStatus} from '#core/entities/job.js';\nimport type {JobExecutionStatus} from '#core/entities/job-execution.js';\nimport type {\n JobExecutionDetail,\n StepDetail,\n WorkflowJobDetail,\n WorkflowRun,\n WorkflowRunDetail,\n WorkflowRunStatus,\n} from '#core/entities/workflow-run.js';\nimport {db} from '../db.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 {toWorkflowRunAttempt, workflowRunAttempts} from '../schema/workflow-run-attempts.js';\nimport {toWorkflowRun, workflowRuns} from '../schema/workflow-runs.js';\n\nexport type WorkflowRunCursor = TimestampIdCursor;\n\nexport interface WorkflowRunFilters {\n status?: WorkflowRunStatus | undefined;\n definitionId?: string | undefined;\n triggerSource?: string | undefined;\n createdFrom?: Date | undefined;\n createdTo?: Date | undefined;\n}\n\nexport interface ListWorkflowRunsParams {\n projectId: string;\n limit: number;\n cursor?: WorkflowRunCursor | undefined;\n filters?: WorkflowRunFilters | undefined;\n includeTotal?: boolean | undefined;\n}\n\nexport interface ListWorkflowRunsResult {\n runs: WorkflowRun[];\n nextCursor: WorkflowRunCursor | null;\n filteredTotalCount: number | null;\n}\n\n/** A run-list job glyph: enough to draw and label it, none of its steps. */\nexport interface WorkflowRunJobSummary {\n id: string;\n key: string;\n name: string | null;\n status: JobStatus;\n mode: JobMode;\n listenerStatus: ListenerStatus;\n executionStatus: JobExecutionStatus | null;\n position: number;\n}\n\nexport interface WorkflowRunAggregates {\n status: Array<{value: WorkflowRunStatus; count: number}>;\n triggerSource: Array<{value: string; count: number}>;\n workflow: Array<{value: string; count: number}>;\n}\n\nexport interface WorkflowJobExecutionDepth {\n runningRuns: number;\n runningJobExecutions: number;\n}\n\nexport interface WorkflowJobExecutionDepthParams {\n workspaceId?: string;\n}\n\nexport async function getWorkflowRunById(id: string): Promise<WorkflowRun | undefined> {\n const rows = await db().select().from(workflowRuns).where(eq(workflowRuns.id, id)).limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toWorkflowRun(row);\n}\n\nexport async function getWorkflowRunByAttemptId(\n workflowRunAttemptId: string,\n): Promise<WorkflowRun | undefined> {\n const rows = await db()\n .select({run: workflowRuns})\n .from(workflowRunAttempts)\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(eq(workflowRunAttempts.id, workflowRunAttemptId))\n .limit(1);\n const row = rows[0];\n return row ? toWorkflowRun(row.run) : undefined;\n}\n\nexport async function getWorkflowRunAttemptById(workflowRunAttemptId: string) {\n const rows = await db()\n .select()\n .from(workflowRunAttempts)\n .where(eq(workflowRunAttempts.id, workflowRunAttemptId))\n .limit(1);\n const row = rows[0];\n return row ? toWorkflowRunAttempt(row) : undefined;\n}\n\nexport async function listRunAttempts(params: {workflowRunId: string; projectId: string}) {\n return (\n await db()\n .select({\n attempt: workflowRunAttempts,\n })\n .from(workflowRunAttempts)\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(\n and(\n eq(workflowRunAttempts.workflowRunId, params.workflowRunId),\n eq(workflowRuns.projectId, params.projectId),\n ),\n )\n .orderBy(asc(workflowRunAttempts.attempt))\n ).map((row) => toWorkflowRunAttempt(row.attempt));\n}\n\nexport async function getLatestAttempt(params: {\n workflowRunId: string;\n projectId: string;\n}): Promise<number> {\n const [row] = await db()\n .select({value: sql<number>`coalesce(max(${workflowRunAttempts.attempt}), 1)`})\n .from(workflowRunAttempts)\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(\n and(\n eq(workflowRunAttempts.workflowRunId, params.workflowRunId),\n eq(workflowRuns.projectId, params.projectId),\n ),\n )\n .limit(1);\n\n return Number(row?.value ?? 1);\n}\n\nexport function buildWorkflowRunListConditions(params: {\n projectId: string;\n filters?: WorkflowRunFilters | undefined;\n cursor?: WorkflowRunCursor | undefined;\n omit?: 'status' | 'definitionId' | 'triggerSource' | undefined;\n}): SQL[] {\n const filters = params.filters;\n const conditions: SQL[] = [eq(workflowRuns.projectId, params.projectId)];\n const cursorCondition = timestampIdCursorWhere({\n timestampColumn: workflowRuns.createdAt,\n idColumn: workflowRuns.id,\n cursor: params.cursor,\n });\n if (cursorCondition) conditions.push(cursorCondition);\n if (filters?.status && params.omit !== 'status') {\n conditions.push(eq(workflowRuns.status, filters.status));\n }\n if (filters?.definitionId && params.omit !== 'definitionId') {\n conditions.push(eq(workflowRuns.definitionId, filters.definitionId));\n }\n if (filters?.triggerSource && params.omit !== 'triggerSource') {\n conditions.push(eq(workflowRuns.triggerSource, filters.triggerSource));\n }\n if (filters?.createdFrom) {\n conditions.push(gte(workflowRuns.createdAt, filters.createdFrom));\n }\n if (filters?.createdTo) {\n conditions.push(lte(workflowRuns.createdAt, filters.createdTo));\n }\n return conditions;\n}\n\nexport async function listWorkflowRuns(\n params: ListWorkflowRunsParams,\n): Promise<ListWorkflowRunsResult> {\n const conditions = buildWorkflowRunListConditions(params);\n const rows = await db()\n .select()\n .from(workflowRuns)\n .where(and(...conditions))\n .orderBy(desc(workflowRuns.createdAt), desc(workflowRuns.id))\n .limit(params.limit + 1);\n\n let totalCount: number | null = null;\n if (params.includeTotal) {\n const [{value} = {value: 0}] = await db()\n .select({value: count()})\n .from(workflowRuns)\n .where(\n and(\n ...buildWorkflowRunListConditions({projectId: params.projectId, filters: params.filters}),\n ),\n );\n totalCount = value;\n }\n\n const page = paginateTimestampIdRows({rows, limit: params.limit, timestampKey: 'createdAt'});\n\n return {\n runs: page.pageRows.map(toWorkflowRun),\n nextCursor: page.nextCursor,\n filteredTotalCount: totalCount,\n };\n}\n\n/** The run whose jobs to fetch, pinned to the attempt the caller already read. */\nexport interface WorkflowRunJobSummaryTarget {\n id: string;\n currentAttempt: number;\n}\n\n/**\n * A page row's jobs: a bounded slice to draw, totals describing all of them, and whether any\n * execution has reached a runner.\n *\n * The preview is what a row can show; `statusCounts` is what it can say. Keeping the counts\n * server-side is what lets a row report a failure that sits past the preview. Counts use the\n * display status derived from the job verdict, listener state, and selected execution state;\n * the row keeps the verdict and evidence separate so the client can apply the same display\n * rule as run detail.\n */\nexport interface WorkflowRunJobsSummary {\n preview: WorkflowRunJobSummary[];\n statusCounts: WorkflowRunJobStatusCount[];\n rawStatusCounts: WorkflowRunJobRawStatusCount[];\n hasStartedJobExecution: boolean;\n}\n\nexport interface WorkflowRunJobStatusCount {\n status: JobStatus | 'listening';\n count: number;\n}\n\nexport interface WorkflowRunJobRawStatusCount {\n status: JobStatus;\n count: number;\n}\n\n/**\n * Jobs for a page of runs, keyed by run id.\n *\n * Issued once per page rather than once per run: the run list is the one surface that needs\n * every run's jobs at once, and a per-row query would put 50 round trips behind it.\n *\n * Both reads are bounded by the page, not by workflow size. A workflow has no job limit and\n * this list polls while runs are active, so returning every job of every row would let one\n * large workflow decide how much the endpoint moves every four seconds. The preview is cut\n * per run in the database, and everything past it is described by a grouped count instead.\n *\n * The attempt comes from the caller's own read rather than from a second look at\n * `current_attempt`. Re-reading it here would let a re-run landing between the queries pair\n * attempt 1's run metadata with attempt 2's jobs, and the row would report a status its strip\n * contradicts.\n *\n * The reads share one repeatable-read snapshot. They describe the same jobs and executions at\n * the same instant, and the strip combines them into a single glyph row, so under the default\n * read-committed isolation a job settling between the statements would draw a pending glyph\n * beside a summary counting it as failed. Sequential inside one transaction is the cost of\n * that; at a four-second poll the extra round trip does not register.\n */\nexport async function listWorkflowRunJobSummaries(\n runs: readonly WorkflowRunJobSummaryTarget[],\n): Promise<Map<string, WorkflowRunJobsSummary>> {\n const summaries = new Map<string, WorkflowRunJobsSummary>();\n if (runs.length === 0) return summaries;\n\n // A row-constructor IN over (run, attempt) so each pair is one lookup on the unique index\n // that already covers those two columns.\n const attemptPairs = sql.join(\n runs.map((run) => sql`(${run.id}::uuid, ${run.currentAttempt})`),\n sql`, `,\n );\n const attemptFilter = sql`(${workflowRunAttempts.workflowRunId}, ${workflowRunAttempts.attempt}) in (${attemptPairs})`;\n\n const {previewRows, countRows} = await db().transaction(\n async (tx) => {\n // Pick the execution the detail view would display once per job before either list\n // statement touches it. The existing (job_id, sequence) index supports the latest\n // execution ordering, while the status-first expression preserves the rule that an\n // active execution wins over a newer completed retry.\n const selectedExecution = tx.$with('selected_execution').as(\n tx\n .selectDistinctOn([jobExecutions.jobId], {\n jobId: jobExecutions.jobId,\n executionStatus: sql<JobExecutionStatus>`${jobExecutions.status}`.as(\n 'execution_status',\n ),\n })\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .where(attemptFilter)\n .orderBy(\n asc(jobExecutions.jobId),\n sql`case when ${jobExecutions.status} = 'running' then 0 else 1 end`,\n desc(jobExecutions.sequence),\n desc(jobExecutions.id),\n ),\n );\n\n const ranked = tx\n .with(selectedExecution)\n .select({\n workflowRunId: workflowRunAttempts.workflowRunId,\n id: jobs.id,\n key: jobs.key,\n name: jobs.name,\n status: jobs.status,\n mode: jobs.mode,\n listenerStatus: jobs.listenerStatus,\n executionStatus: selectedExecution.executionStatus,\n position: jobs.position,\n jobRank:\n sql<number>`row_number() over (partition by ${workflowRunAttempts.workflowRunId} order by ${jobs.position} asc, ${jobs.id} asc)`.as(\n 'job_rank',\n ),\n })\n .from(jobs)\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .leftJoin(selectedExecution, eq(selectedExecution.jobId, jobs.id))\n .where(attemptFilter)\n .as('ranked');\n // Correlated per job rather than a grouped scan of `job_executions`: an aggregate over the\n // whole table cannot have the page's filter pushed into it, so it would read every execution\n // ever recorded on each poll and grow with history rather than with the page.\n const hasStartedJobExecution = sql<boolean>`bool_or(exists (\n select 1\n from ${jobExecutions}\n where ${jobExecutions.jobId} = ${jobs.id} and ${jobExecutions.startedAt} is not null\n ))`;\n\n const executionDisplayStatus = sql<JobExecutionStatus | null>`\n ${selectedExecution.executionStatus}\n `;\n const displayStatus = sql<WorkflowRunJobStatusCount['status']>`\n case\n when ${jobs.status} in ('succeeded', 'failed', 'cancelled', 'skipped') then ${jobs.status}::text\n when ${jobs.mode} = 'listening' and ${jobs.listenerStatus} = 'listening' then 'listening'\n when ${executionDisplayStatus} is not null then ${executionDisplayStatus}::text\n else 'pending'\n end\n `;\n return {\n previewRows: await tx\n .select({\n workflowRunId: ranked.workflowRunId,\n id: ranked.id,\n key: ranked.key,\n name: ranked.name,\n status: ranked.status,\n mode: ranked.mode,\n listenerStatus: ranked.listenerStatus,\n executionStatus: ranked.executionStatus,\n position: ranked.position,\n })\n .from(ranked)\n .where(lte(ranked.jobRank, WORKFLOW_RUN_JOB_PREVIEW_LIMIT))\n .orderBy(asc(ranked.workflowRunId), asc(ranked.position), asc(ranked.id)),\n countRows: await tx\n .with(selectedExecution)\n .select({\n workflowRunId: workflowRunAttempts.workflowRunId,\n rawStatus: jobs.status,\n status: displayStatus,\n count: count(),\n hasStartedJobExecution,\n })\n .from(jobs)\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .leftJoin(selectedExecution, eq(selectedExecution.jobId, jobs.id))\n .where(attemptFilter)\n .groupBy(workflowRunAttempts.workflowRunId, jobs.status, displayStatus),\n };\n },\n {isolationLevel: 'repeatable read', accessMode: 'read only'},\n );\n\n for (const {workflowRunId, ...summary} of previewRows) {\n summaryFor(summaries, workflowRunId).preview.push(summary);\n }\n for (const {\n workflowRunId,\n rawStatus,\n status,\n count: statusCount,\n hasStartedJobExecution,\n } of countRows) {\n const summary = summaryFor(summaries, workflowRunId);\n appendStatusCount(summary.rawStatusCounts, rawStatus, statusCount);\n appendStatusCount(summary.statusCounts, status, statusCount);\n // One row per (run, verdict, display status), so the run's answer is the OR of its groups.\n summary.hasStartedJobExecution ||= hasStartedJobExecution;\n }\n\n return summaries;\n}\n\nfunction summaryFor(\n summaries: Map<string, WorkflowRunJobsSummary>,\n workflowRunId: string,\n): WorkflowRunJobsSummary {\n const existing = summaries.get(workflowRunId);\n if (existing) return existing;\n const created: WorkflowRunJobsSummary = {\n preview: [],\n statusCounts: [],\n rawStatusCounts: [],\n hasStartedJobExecution: false,\n };\n summaries.set(workflowRunId, created);\n return created;\n}\n\nfunction appendStatusCount<T extends string>(\n counts: Array<{status: T; count: number}>,\n status: T,\n count: number,\n): void {\n const existing = counts.find((entry) => entry.status === status);\n if (existing) {\n existing.count += count;\n } else {\n counts.push({status, count});\n }\n}\n\nexport async function listWorkflowRunsByProject(projectId: string): Promise<WorkflowRun[]> {\n const result = await listWorkflowRuns({projectId, limit: 100});\n return result.runs;\n}\n\nexport async function getWorkflowRunAggregates(params: {\n projectId: string;\n filters?: WorkflowRunFilters | undefined;\n}): Promise<WorkflowRunAggregates> {\n const [statusRows, triggerRows, workflowRows] = await Promise.all([\n db()\n .select({value: workflowRuns.status, count: count()})\n .from(workflowRuns)\n .where(\n and(\n ...buildWorkflowRunListConditions({\n projectId: params.projectId,\n filters: params.filters,\n omit: 'status',\n }),\n ),\n )\n .groupBy(workflowRuns.status),\n db()\n .select({value: workflowRuns.triggerSource, count: count()})\n .from(workflowRuns)\n .where(\n and(\n ...buildWorkflowRunListConditions({\n projectId: params.projectId,\n filters: params.filters,\n omit: 'triggerSource',\n }),\n ),\n )\n .groupBy(workflowRuns.triggerSource),\n db()\n .select({value: workflowRuns.definitionId, count: count()})\n .from(workflowRuns)\n .where(\n and(\n ...buildWorkflowRunListConditions({\n projectId: params.projectId,\n filters: params.filters,\n omit: 'definitionId',\n }),\n ),\n )\n .groupBy(workflowRuns.definitionId),\n ]);\n\n return {\n status: statusRows,\n triggerSource: triggerRows,\n workflow: workflowRows,\n };\n}\n\nexport async function getWorkflowJobExecutionDepth(\n params: WorkflowJobExecutionDepthParams = {},\n): Promise<WorkflowJobExecutionDepth> {\n const runConditions = [eq(workflowRuns.status, 'running')];\n const jobConditions = [eq(jobExecutions.status, 'running')];\n if (params.workspaceId) {\n runConditions.push(eq(workflowRuns.workspaceId, params.workspaceId));\n jobConditions.push(eq(workflowRuns.workspaceId, params.workspaceId));\n }\n\n const [runRows, jobRows] = await Promise.all([\n db()\n .select({value: count()})\n .from(workflowRuns)\n .where(and(...runConditions)),\n db()\n .select({value: count()})\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(and(...jobConditions)),\n ]);\n\n return {\n runningRuns: runRows[0]?.value ?? 0,\n runningJobExecutions: jobRows[0]?.value ?? 0,\n };\n}\n\nexport async function getWorkflowRunDetail(\n workflowRunId: string,\n attempt?: number | undefined,\n): Promise<WorkflowRunDetail | undefined> {\n const [target] = await db()\n .select({run: workflowRuns, attempt: workflowRunAttempts})\n .from(workflowRuns)\n .innerJoin(\n workflowRunAttempts,\n and(\n eq(workflowRunAttempts.workflowRunId, workflowRuns.id),\n eq(workflowRunAttempts.attempt, attempt ?? workflowRuns.currentAttempt),\n ),\n )\n .where(eq(workflowRuns.id, workflowRunId))\n .limit(1);\n if (!target) return undefined;\n\n const latestAttempt = await getLatestAttempt({\n workflowRunId: target.run.id,\n projectId: target.run.projectId,\n });\n\n const rows = await db()\n .select({\n run: workflowRuns,\n job: jobs,\n jobExecution: jobExecutions,\n step: steps,\n stepAttempt: stepAttempts,\n })\n .from(workflowRuns)\n .innerJoin(workflowRunAttempts, eq(workflowRunAttempts.id, target.attempt.id))\n .leftJoin(jobs, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .leftJoin(jobExecutions, eq(jobExecutions.jobId, jobs.id))\n .leftJoin(steps, eq(steps.jobExecutionId, jobExecutions.id))\n .leftJoin(stepAttempts, eq(stepAttempts.stepId, steps.id))\n .where(eq(workflowRuns.id, workflowRunId))\n .orderBy(\n asc(jobs.position),\n asc(jobs.id),\n asc(jobExecutions.sequence),\n asc(jobExecutions.id),\n asc(steps.position),\n asc(steps.id),\n asc(stepAttempts.executionOrder),\n asc(stepAttempts.id),\n );\n\n return hydrateWorkflowRunDetail(rows, target.attempt, latestAttempt);\n}\n\nexport async function getJobExecutionDetail(\n jobExecutionId: string,\n): Promise<JobExecutionDetail | undefined> {\n const rows = await db()\n .select({\n job: jobs,\n jobExecution: jobExecutions,\n step: steps,\n stepAttempt: stepAttempts,\n })\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 .leftJoin(steps, eq(steps.jobExecutionId, jobExecutions.id))\n .leftJoin(stepAttempts, eq(stepAttempts.stepId, steps.id))\n .where(eq(jobExecutions.id, jobExecutionId))\n .orderBy(\n asc(steps.position),\n asc(steps.id),\n asc(stepAttempts.executionOrder),\n asc(stepAttempts.id),\n );\n\n const first = rows[0];\n if (!first) return undefined;\n\n const detail: JobExecutionDetail = {\n ...toJobExecution(first.jobExecution, first.job.name ?? first.job.key),\n steps: [],\n };\n const stepById = new Map<string, StepDetail>();\n for (const row of rows) {\n if (row.step) {\n const step = getOrCreateStepDetail(stepById, detail.steps, row.step);\n if (row.stepAttempt) {\n step.attempts.push(toStepAttempt(row.stepAttempt));\n }\n }\n }\n\n return detail;\n}\n\nfunction hydrateWorkflowRunDetail(\n rows: {\n run: typeof workflowRuns.$inferSelect;\n job: typeof jobs.$inferSelect | null;\n jobExecution: typeof jobExecutions.$inferSelect | null;\n step: typeof steps.$inferSelect | null;\n stepAttempt: typeof stepAttempts.$inferSelect | null;\n }[],\n attempt: typeof workflowRunAttempts.$inferSelect,\n latestAttempt: number,\n): WorkflowRunDetail | undefined {\n const first = rows[0];\n if (!first) return undefined;\n\n const detail: WorkflowRunDetail = {\n ...toWorkflowRun(first.run),\n runAttempt: toWorkflowRunAttempt(attempt),\n latestAttempt,\n jobs: [],\n // Read off the same rows the executions come from, so the flag cannot contradict them.\n hasStartedJobExecution: rows.some((row) => row.jobExecution?.startedAt != null),\n };\n const jobById = new Map<string, WorkflowJobDetail>();\n const jobExecutionById = new Map<string, JobExecutionDetail>();\n const stepById = new Map<string, StepDetail>();\n\n for (const row of rows) {\n if (!row.job) continue;\n let job = jobById.get(row.job.id);\n if (!job) {\n job = {...toJob(row.job), jobExecutions: []};\n jobById.set(row.job.id, job);\n detail.jobs.push(job);\n }\n\n if (!row.jobExecution) continue;\n let jobExecution = jobExecutionById.get(row.jobExecution.id);\n if (!jobExecution) {\n jobExecution = {\n ...toJobExecution(row.jobExecution, row.job.name ?? row.job.key),\n steps: [],\n };\n jobExecutionById.set(row.jobExecution.id, jobExecution);\n job.jobExecutions.push(jobExecution);\n }\n\n if (!row.step) continue;\n const step = getOrCreateStepDetail(stepById, jobExecution.steps, row.step);\n if (row.stepAttempt) {\n step.attempts.push(toStepAttempt(row.stepAttempt));\n }\n }\n\n return detail;\n}\n\nfunction getOrCreateStepDetail(\n stepById: Map<string, StepDetail>,\n target: StepDetail[],\n row: typeof steps.$inferSelect,\n): StepDetail {\n let step = stepById.get(row.id);\n if (!step) {\n step = {...toStep(row), attempts: []};\n stepById.set(row.id, step);\n target.push(step);\n }\n return step;\n}\n"],"names":["WORKFLOW_RUN_JOB_PREVIEW_LIMIT","paginateTimestampIdRows","timestampIdCursorWhere","and","asc","count","desc","eq","gte","lte","sql","db","jobExecutions","toJobExecution","jobs","toJob","stepAttempts","toStepAttempt","steps","toStep","toWorkflowRunAttempt","workflowRunAttempts","toWorkflowRun","workflowRuns","getWorkflowRunById","id","rows","select","from","where","limit","row","undefined","getWorkflowRunByAttemptId","workflowRunAttemptId","run","innerJoin","workflowRunId","getWorkflowRunAttemptById","listRunAttempts","params","attempt","projectId","orderBy","map","getLatestAttempt","value","Number","buildWorkflowRunListConditions","filters","conditions","cursorCondition","timestampColumn","createdAt","idColumn","cursor","push","status","omit","definitionId","triggerSource","createdFrom","createdTo","listWorkflowRuns","totalCount","includeTotal","page","timestampKey","runs","pageRows","nextCursor","filteredTotalCount","listWorkflowRunJobSummaries","summaries","Map","length","attemptPairs","join","currentAttempt","attemptFilter","previewRows","countRows","transaction","tx","selectedExecution","$with","as","selectDistinctOn","jobId","executionStatus","sequence","ranked","with","key","name","mode","listenerStatus","position","jobRank","leftJoin","hasStartedJobExecution","startedAt","executionDisplayStatus","displayStatus","rawStatus","groupBy","isolationLevel","accessMode","summary","summaryFor","preview","statusCount","appendStatusCount","rawStatusCounts","statusCounts","existing","get","created","set","counts","find","entry","listWorkflowRunsByProject","result","getWorkflowRunAggregates","statusRows","triggerRows","workflowRows","Promise","all","workflow","getWorkflowJobExecutionDepth","runConditions","jobConditions","workspaceId","runRows","jobRows","runningRuns","runningJobExecutions","getWorkflowRunDetail","target","latestAttempt","job","jobExecution","step","stepAttempt","jobExecutionId","stepId","executionOrder","hydrateWorkflowRunDetail","getJobExecutionDetail","first","detail","stepById","getOrCreateStepDetail","attempts","runAttempt","some","jobById","jobExecutionById"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/db/workflow-runs/queries.ts"],"sourcesContent":["import {WORKFLOW_RUN_JOB_PREVIEW_LIMIT} from '@shipfox/api-workflows-dto';\nimport {\n paginateTimestampIdRows,\n type TimestampIdCursor,\n timestampIdCursorWhere,\n} from '@shipfox/node-drizzle';\nimport {and, asc, count, desc, eq, gte, lte, type SQL, sql} from 'drizzle-orm';\nimport type {JobMode, JobStatus, ListenerStatus} from '#core/entities/job.js';\nimport type {JobExecutionStatus} from '#core/entities/job-execution.js';\nimport type {\n JobExecutionDetail,\n StepDetail,\n WorkflowJobDetail,\n WorkflowRun,\n WorkflowRunDetail,\n WorkflowRunOrigin,\n WorkflowRunStatus,\n} from '#core/entities/workflow-run.js';\nimport {db} from '../db.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 {toWorkflowRunAttempt, workflowRunAttempts} from '../schema/workflow-run-attempts.js';\nimport {toWorkflowRun, workflowRuns} from '../schema/workflow-runs.js';\n\nexport type WorkflowRunCursor = TimestampIdCursor;\n\nexport interface WorkflowRunFilters {\n status?: WorkflowRunStatus | undefined;\n definitionId?: string | undefined;\n triggerSource?: string | undefined;\n origin?: WorkflowRunOrigin | undefined;\n createdFrom?: Date | undefined;\n createdTo?: Date | undefined;\n}\n\nexport interface ListWorkflowRunsParams {\n projectId: string;\n limit: number;\n cursor?: WorkflowRunCursor | undefined;\n filters?: WorkflowRunFilters | undefined;\n includeTotal?: boolean | undefined;\n}\n\nexport interface ListWorkflowRunsResult {\n runs: WorkflowRun[];\n nextCursor: WorkflowRunCursor | null;\n filteredTotalCount: number | null;\n}\n\n/** A run-list job glyph: enough to draw and label it, none of its steps. */\nexport interface WorkflowRunJobSummary {\n id: string;\n key: string;\n name: string | null;\n status: JobStatus;\n mode: JobMode;\n listenerStatus: ListenerStatus;\n executionStatus: JobExecutionStatus | null;\n position: number;\n}\n\nexport interface WorkflowRunAggregates {\n status: Array<{value: WorkflowRunStatus; count: number}>;\n triggerSource: Array<{value: string; count: number}>;\n workflow: Array<{value: string; count: number}>;\n}\n\nexport interface WorkflowJobExecutionDepth {\n runningRuns: number;\n runningJobExecutions: number;\n}\n\nexport interface WorkflowJobExecutionDepthParams {\n workspaceId?: string;\n}\n\nexport async function getWorkflowRunById(id: string): Promise<WorkflowRun | undefined> {\n const rows = await db().select().from(workflowRuns).where(eq(workflowRuns.id, id)).limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toWorkflowRun(row);\n}\n\nexport async function getWorkflowRunByAttemptId(\n workflowRunAttemptId: string,\n): Promise<WorkflowRun | undefined> {\n const rows = await db()\n .select({run: workflowRuns})\n .from(workflowRunAttempts)\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(eq(workflowRunAttempts.id, workflowRunAttemptId))\n .limit(1);\n const row = rows[0];\n return row ? toWorkflowRun(row.run) : undefined;\n}\n\nexport async function getWorkflowRunAttemptById(workflowRunAttemptId: string) {\n const rows = await db()\n .select()\n .from(workflowRunAttempts)\n .where(eq(workflowRunAttempts.id, workflowRunAttemptId))\n .limit(1);\n const row = rows[0];\n return row ? toWorkflowRunAttempt(row) : undefined;\n}\n\nexport async function listRunAttempts(params: {workflowRunId: string; projectId: string}) {\n return (\n await db()\n .select({\n attempt: workflowRunAttempts,\n })\n .from(workflowRunAttempts)\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(\n and(\n eq(workflowRunAttempts.workflowRunId, params.workflowRunId),\n eq(workflowRuns.projectId, params.projectId),\n ),\n )\n .orderBy(asc(workflowRunAttempts.attempt))\n ).map((row) => toWorkflowRunAttempt(row.attempt));\n}\n\nexport async function getLatestAttempt(params: {\n workflowRunId: string;\n projectId: string;\n}): Promise<number> {\n const [row] = await db()\n .select({value: sql<number>`coalesce(max(${workflowRunAttempts.attempt}), 1)`})\n .from(workflowRunAttempts)\n .innerJoin(workflowRuns, eq(workflowRunAttempts.workflowRunId, workflowRuns.id))\n .where(\n and(\n eq(workflowRunAttempts.workflowRunId, params.workflowRunId),\n eq(workflowRuns.projectId, params.projectId),\n ),\n )\n .limit(1);\n\n return Number(row?.value ?? 1);\n}\n\nexport function buildWorkflowRunListConditions(params: {\n projectId: string;\n filters?: WorkflowRunFilters | undefined;\n cursor?: WorkflowRunCursor | undefined;\n omit?: 'status' | 'definitionId' | 'triggerSource' | 'origin' | undefined;\n}): SQL[] {\n const filters = params.filters;\n const conditions: SQL[] = [eq(workflowRuns.projectId, params.projectId)];\n const cursorCondition = timestampIdCursorWhere({\n timestampColumn: workflowRuns.createdAt,\n idColumn: workflowRuns.id,\n cursor: params.cursor,\n });\n if (cursorCondition) conditions.push(cursorCondition);\n if (filters?.status && params.omit !== 'status') {\n conditions.push(eq(workflowRuns.status, filters.status));\n }\n if (filters?.definitionId && params.omit !== 'definitionId') {\n conditions.push(eq(workflowRuns.definitionId, filters.definitionId));\n }\n if (filters?.triggerSource && params.omit !== 'triggerSource') {\n conditions.push(eq(workflowRuns.triggerSource, filters.triggerSource));\n }\n if (filters?.origin && params.omit !== 'origin') {\n conditions.push(eq(workflowRuns.origin, filters.origin));\n }\n if (filters?.createdFrom) {\n conditions.push(gte(workflowRuns.createdAt, filters.createdFrom));\n }\n if (filters?.createdTo) {\n conditions.push(lte(workflowRuns.createdAt, filters.createdTo));\n }\n return conditions;\n}\n\nexport async function listWorkflowRuns(\n params: ListWorkflowRunsParams,\n): Promise<ListWorkflowRunsResult> {\n const conditions = buildWorkflowRunListConditions(params);\n const rows = await db()\n .select()\n .from(workflowRuns)\n .where(and(...conditions))\n .orderBy(desc(workflowRuns.createdAt), desc(workflowRuns.id))\n .limit(params.limit + 1);\n\n let totalCount: number | null = null;\n if (params.includeTotal) {\n const [{value} = {value: 0}] = await db()\n .select({value: count()})\n .from(workflowRuns)\n .where(\n and(\n ...buildWorkflowRunListConditions({projectId: params.projectId, filters: params.filters}),\n ),\n );\n totalCount = value;\n }\n\n const page = paginateTimestampIdRows({rows, limit: params.limit, timestampKey: 'createdAt'});\n\n return {\n runs: page.pageRows.map(toWorkflowRun),\n nextCursor: page.nextCursor,\n filteredTotalCount: totalCount,\n };\n}\n\n/** The run whose jobs to fetch, pinned to the attempt the caller already read. */\nexport interface WorkflowRunJobSummaryTarget {\n id: string;\n currentAttempt: number;\n}\n\n/**\n * A page row's jobs: a bounded slice to draw, totals describing all of them, and whether any\n * execution has reached a runner.\n *\n * The preview is what a row can show; `statusCounts` is what it can say. Keeping the counts\n * server-side is what lets a row report a failure that sits past the preview. Counts use the\n * display status derived from the job verdict, listener state, and selected execution state;\n * the row keeps the verdict and evidence separate so the client can apply the same display\n * rule as run detail.\n */\nexport interface WorkflowRunJobsSummary {\n preview: WorkflowRunJobSummary[];\n statusCounts: WorkflowRunJobStatusCount[];\n rawStatusCounts: WorkflowRunJobRawStatusCount[];\n hasStartedJobExecution: boolean;\n}\n\nexport interface WorkflowRunJobStatusCount {\n status: JobStatus | 'listening';\n count: number;\n}\n\nexport interface WorkflowRunJobRawStatusCount {\n status: JobStatus;\n count: number;\n}\n\n/**\n * Jobs for a page of runs, keyed by run id.\n *\n * Issued once per page rather than once per run: the run list is the one surface that needs\n * every run's jobs at once, and a per-row query would put 50 round trips behind it.\n *\n * Both reads are bounded by the page, not by workflow size. A workflow has no job limit and\n * this list polls while runs are active, so returning every job of every row would let one\n * large workflow decide how much the endpoint moves every four seconds. The preview is cut\n * per run in the database, and everything past it is described by a grouped count instead.\n *\n * The attempt comes from the caller's own read rather than from a second look at\n * `current_attempt`. Re-reading it here would let a re-run landing between the queries pair\n * attempt 1's run metadata with attempt 2's jobs, and the row would report a status its strip\n * contradicts.\n *\n * The reads share one repeatable-read snapshot. They describe the same jobs and executions at\n * the same instant, and the strip combines them into a single glyph row, so under the default\n * read-committed isolation a job settling between the statements would draw a pending glyph\n * beside a summary counting it as failed. Sequential inside one transaction is the cost of\n * that; at a four-second poll the extra round trip does not register.\n */\nexport async function listWorkflowRunJobSummaries(\n runs: readonly WorkflowRunJobSummaryTarget[],\n): Promise<Map<string, WorkflowRunJobsSummary>> {\n const summaries = new Map<string, WorkflowRunJobsSummary>();\n if (runs.length === 0) return summaries;\n\n // A row-constructor IN over (run, attempt) so each pair is one lookup on the unique index\n // that already covers those two columns.\n const attemptPairs = sql.join(\n runs.map((run) => sql`(${run.id}::uuid, ${run.currentAttempt})`),\n sql`, `,\n );\n const attemptFilter = sql`(${workflowRunAttempts.workflowRunId}, ${workflowRunAttempts.attempt}) in (${attemptPairs})`;\n\n const {previewRows, countRows} = await db().transaction(\n async (tx) => {\n // Pick the execution the detail view would display once per job before either list\n // statement touches it. The existing (job_id, sequence) index supports the latest\n // execution ordering, while the status-first expression preserves the rule that an\n // active execution wins over a newer completed retry.\n const selectedExecution = tx.$with('selected_execution').as(\n tx\n .selectDistinctOn([jobExecutions.jobId], {\n jobId: jobExecutions.jobId,\n executionStatus: sql<JobExecutionStatus>`${jobExecutions.status}`.as(\n 'execution_status',\n ),\n })\n .from(jobExecutions)\n .innerJoin(jobs, eq(jobExecutions.jobId, jobs.id))\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .where(attemptFilter)\n .orderBy(\n asc(jobExecutions.jobId),\n sql`case when ${jobExecutions.status} = 'running' then 0 else 1 end`,\n desc(jobExecutions.sequence),\n desc(jobExecutions.id),\n ),\n );\n\n const ranked = tx\n .with(selectedExecution)\n .select({\n workflowRunId: workflowRunAttempts.workflowRunId,\n id: jobs.id,\n key: jobs.key,\n name: jobs.name,\n status: jobs.status,\n mode: jobs.mode,\n listenerStatus: jobs.listenerStatus,\n executionStatus: selectedExecution.executionStatus,\n position: jobs.position,\n jobRank:\n sql<number>`row_number() over (partition by ${workflowRunAttempts.workflowRunId} order by ${jobs.position} asc, ${jobs.id} asc)`.as(\n 'job_rank',\n ),\n })\n .from(jobs)\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .leftJoin(selectedExecution, eq(selectedExecution.jobId, jobs.id))\n .where(attemptFilter)\n .as('ranked');\n // Correlated per job rather than a grouped scan of `job_executions`: an aggregate over the\n // whole table cannot have the page's filter pushed into it, so it would read every execution\n // ever recorded on each poll and grow with history rather than with the page.\n const hasStartedJobExecution = sql<boolean>`bool_or(exists (\n select 1\n from ${jobExecutions}\n where ${jobExecutions.jobId} = ${jobs.id} and ${jobExecutions.startedAt} is not null\n ))`;\n\n const executionDisplayStatus = sql<JobExecutionStatus | null>`\n ${selectedExecution.executionStatus}\n `;\n const displayStatus = sql<WorkflowRunJobStatusCount['status']>`\n case\n when ${jobs.status} in ('succeeded', 'failed', 'cancelled', 'skipped') then ${jobs.status}::text\n when ${jobs.mode} = 'listening' and ${jobs.listenerStatus} = 'listening' then 'listening'\n when ${executionDisplayStatus} is not null then ${executionDisplayStatus}::text\n else 'pending'\n end\n `;\n return {\n previewRows: await tx\n .select({\n workflowRunId: ranked.workflowRunId,\n id: ranked.id,\n key: ranked.key,\n name: ranked.name,\n status: ranked.status,\n mode: ranked.mode,\n listenerStatus: ranked.listenerStatus,\n executionStatus: ranked.executionStatus,\n position: ranked.position,\n })\n .from(ranked)\n .where(lte(ranked.jobRank, WORKFLOW_RUN_JOB_PREVIEW_LIMIT))\n .orderBy(asc(ranked.workflowRunId), asc(ranked.position), asc(ranked.id)),\n countRows: await tx\n .with(selectedExecution)\n .select({\n workflowRunId: workflowRunAttempts.workflowRunId,\n rawStatus: jobs.status,\n status: displayStatus,\n count: count(),\n hasStartedJobExecution,\n })\n .from(jobs)\n .innerJoin(workflowRunAttempts, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .leftJoin(selectedExecution, eq(selectedExecution.jobId, jobs.id))\n .where(attemptFilter)\n .groupBy(workflowRunAttempts.workflowRunId, jobs.status, displayStatus),\n };\n },\n {isolationLevel: 'repeatable read', accessMode: 'read only'},\n );\n\n for (const {workflowRunId, ...summary} of previewRows) {\n summaryFor(summaries, workflowRunId).preview.push(summary);\n }\n for (const {\n workflowRunId,\n rawStatus,\n status,\n count: statusCount,\n hasStartedJobExecution,\n } of countRows) {\n const summary = summaryFor(summaries, workflowRunId);\n appendStatusCount(summary.rawStatusCounts, rawStatus, statusCount);\n appendStatusCount(summary.statusCounts, status, statusCount);\n // One row per (run, verdict, display status), so the run's answer is the OR of its groups.\n summary.hasStartedJobExecution ||= hasStartedJobExecution;\n }\n\n return summaries;\n}\n\nfunction summaryFor(\n summaries: Map<string, WorkflowRunJobsSummary>,\n workflowRunId: string,\n): WorkflowRunJobsSummary {\n const existing = summaries.get(workflowRunId);\n if (existing) return existing;\n const created: WorkflowRunJobsSummary = {\n preview: [],\n statusCounts: [],\n rawStatusCounts: [],\n hasStartedJobExecution: false,\n };\n summaries.set(workflowRunId, created);\n return created;\n}\n\nfunction appendStatusCount<T extends string>(\n counts: Array<{status: T; count: number}>,\n status: T,\n count: number,\n): void {\n const existing = counts.find((entry) => entry.status === status);\n if (existing) {\n existing.count += count;\n } else {\n counts.push({status, count});\n }\n}\n\nexport async function listWorkflowRunsByProject(projectId: string): Promise<WorkflowRun[]> {\n const result = await listWorkflowRuns({projectId, limit: 100});\n return result.runs;\n}\n\nexport async function getWorkflowRunAggregates(params: {\n projectId: string;\n filters?: WorkflowRunFilters | undefined;\n}): Promise<WorkflowRunAggregates> {\n const [statusRows, triggerRows, workflowRows] = await Promise.all([\n db()\n .select({value: workflowRuns.status, count: count()})\n .from(workflowRuns)\n .where(\n and(\n ...buildWorkflowRunListConditions({\n projectId: params.projectId,\n filters: params.filters,\n omit: 'status',\n }),\n ),\n )\n .groupBy(workflowRuns.status),\n db()\n .select({value: workflowRuns.triggerSource, count: count()})\n .from(workflowRuns)\n .where(\n and(\n ...buildWorkflowRunListConditions({\n projectId: params.projectId,\n filters: params.filters,\n omit: 'triggerSource',\n }),\n ),\n )\n .groupBy(workflowRuns.triggerSource),\n db()\n .select({value: workflowRuns.definitionId, count: count()})\n .from(workflowRuns)\n .where(\n and(\n ...buildWorkflowRunListConditions({\n projectId: params.projectId,\n filters: params.filters,\n omit: 'definitionId',\n }),\n ),\n )\n .groupBy(workflowRuns.definitionId),\n ]);\n\n return {\n status: statusRows,\n triggerSource: triggerRows,\n workflow: workflowRows,\n };\n}\n\nexport async function getWorkflowJobExecutionDepth(\n params: WorkflowJobExecutionDepthParams = {},\n): Promise<WorkflowJobExecutionDepth> {\n const runConditions = [eq(workflowRuns.status, 'running')];\n const jobConditions = [eq(jobExecutions.status, 'running')];\n if (params.workspaceId) {\n runConditions.push(eq(workflowRuns.workspaceId, params.workspaceId));\n jobConditions.push(eq(workflowRuns.workspaceId, params.workspaceId));\n }\n\n const [runRows, jobRows] = await Promise.all([\n db()\n .select({value: count()})\n .from(workflowRuns)\n .where(and(...runConditions)),\n db()\n .select({value: count()})\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(and(...jobConditions)),\n ]);\n\n return {\n runningRuns: runRows[0]?.value ?? 0,\n runningJobExecutions: jobRows[0]?.value ?? 0,\n };\n}\n\nexport async function getWorkflowRunDetail(\n workflowRunId: string,\n attempt?: number | undefined,\n): Promise<WorkflowRunDetail | undefined> {\n const [target] = await db()\n .select({run: workflowRuns, attempt: workflowRunAttempts})\n .from(workflowRuns)\n .innerJoin(\n workflowRunAttempts,\n and(\n eq(workflowRunAttempts.workflowRunId, workflowRuns.id),\n eq(workflowRunAttempts.attempt, attempt ?? workflowRuns.currentAttempt),\n ),\n )\n .where(eq(workflowRuns.id, workflowRunId))\n .limit(1);\n if (!target) return undefined;\n\n const latestAttempt = await getLatestAttempt({\n workflowRunId: target.run.id,\n projectId: target.run.projectId,\n });\n\n const rows = await db()\n .select({\n run: workflowRuns,\n job: jobs,\n jobExecution: jobExecutions,\n step: steps,\n stepAttempt: stepAttempts,\n })\n .from(workflowRuns)\n .innerJoin(workflowRunAttempts, eq(workflowRunAttempts.id, target.attempt.id))\n .leftJoin(jobs, eq(jobs.workflowRunAttemptId, workflowRunAttempts.id))\n .leftJoin(jobExecutions, eq(jobExecutions.jobId, jobs.id))\n .leftJoin(steps, eq(steps.jobExecutionId, jobExecutions.id))\n .leftJoin(stepAttempts, eq(stepAttempts.stepId, steps.id))\n .where(eq(workflowRuns.id, workflowRunId))\n .orderBy(\n asc(jobs.position),\n asc(jobs.id),\n asc(jobExecutions.sequence),\n asc(jobExecutions.id),\n asc(steps.position),\n asc(steps.id),\n asc(stepAttempts.executionOrder),\n asc(stepAttempts.id),\n );\n\n return hydrateWorkflowRunDetail(rows, target.attempt, latestAttempt);\n}\n\nexport async function getJobExecutionDetail(\n jobExecutionId: string,\n): Promise<JobExecutionDetail | undefined> {\n const rows = await db()\n .select({\n job: jobs,\n jobExecution: jobExecutions,\n step: steps,\n stepAttempt: stepAttempts,\n })\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 .leftJoin(steps, eq(steps.jobExecutionId, jobExecutions.id))\n .leftJoin(stepAttempts, eq(stepAttempts.stepId, steps.id))\n .where(eq(jobExecutions.id, jobExecutionId))\n .orderBy(\n asc(steps.position),\n asc(steps.id),\n asc(stepAttempts.executionOrder),\n asc(stepAttempts.id),\n );\n\n const first = rows[0];\n if (!first) return undefined;\n\n const detail: JobExecutionDetail = {\n ...toJobExecution(first.jobExecution, first.job.name ?? first.job.key),\n steps: [],\n };\n const stepById = new Map<string, StepDetail>();\n for (const row of rows) {\n if (row.step) {\n const step = getOrCreateStepDetail(stepById, detail.steps, row.step);\n if (row.stepAttempt) {\n step.attempts.push(toStepAttempt(row.stepAttempt));\n }\n }\n }\n\n return detail;\n}\n\nfunction hydrateWorkflowRunDetail(\n rows: {\n run: typeof workflowRuns.$inferSelect;\n job: typeof jobs.$inferSelect | null;\n jobExecution: typeof jobExecutions.$inferSelect | null;\n step: typeof steps.$inferSelect | null;\n stepAttempt: typeof stepAttempts.$inferSelect | null;\n }[],\n attempt: typeof workflowRunAttempts.$inferSelect,\n latestAttempt: number,\n): WorkflowRunDetail | undefined {\n const first = rows[0];\n if (!first) return undefined;\n\n const detail: WorkflowRunDetail = {\n ...toWorkflowRun(first.run),\n runAttempt: toWorkflowRunAttempt(attempt),\n latestAttempt,\n jobs: [],\n // Read off the same rows the executions come from, so the flag cannot contradict them.\n hasStartedJobExecution: rows.some((row) => row.jobExecution?.startedAt != null),\n };\n const jobById = new Map<string, WorkflowJobDetail>();\n const jobExecutionById = new Map<string, JobExecutionDetail>();\n const stepById = new Map<string, StepDetail>();\n\n for (const row of rows) {\n if (!row.job) continue;\n let job = jobById.get(row.job.id);\n if (!job) {\n job = {...toJob(row.job), jobExecutions: []};\n jobById.set(row.job.id, job);\n detail.jobs.push(job);\n }\n\n if (!row.jobExecution) continue;\n let jobExecution = jobExecutionById.get(row.jobExecution.id);\n if (!jobExecution) {\n jobExecution = {\n ...toJobExecution(row.jobExecution, row.job.name ?? row.job.key),\n steps: [],\n };\n jobExecutionById.set(row.jobExecution.id, jobExecution);\n job.jobExecutions.push(jobExecution);\n }\n\n if (!row.step) continue;\n const step = getOrCreateStepDetail(stepById, jobExecution.steps, row.step);\n if (row.stepAttempt) {\n step.attempts.push(toStepAttempt(row.stepAttempt));\n }\n }\n\n return detail;\n}\n\nfunction getOrCreateStepDetail(\n stepById: Map<string, StepDetail>,\n target: StepDetail[],\n row: typeof steps.$inferSelect,\n): StepDetail {\n let step = stepById.get(row.id);\n if (!step) {\n step = {...toStep(row), attempts: []};\n stepById.set(row.id, step);\n target.push(step);\n }\n return step;\n}\n"],"names":["WORKFLOW_RUN_JOB_PREVIEW_LIMIT","paginateTimestampIdRows","timestampIdCursorWhere","and","asc","count","desc","eq","gte","lte","sql","db","jobExecutions","toJobExecution","jobs","toJob","stepAttempts","toStepAttempt","steps","toStep","toWorkflowRunAttempt","workflowRunAttempts","toWorkflowRun","workflowRuns","getWorkflowRunById","id","rows","select","from","where","limit","row","undefined","getWorkflowRunByAttemptId","workflowRunAttemptId","run","innerJoin","workflowRunId","getWorkflowRunAttemptById","listRunAttempts","params","attempt","projectId","orderBy","map","getLatestAttempt","value","Number","buildWorkflowRunListConditions","filters","conditions","cursorCondition","timestampColumn","createdAt","idColumn","cursor","push","status","omit","definitionId","triggerSource","origin","createdFrom","createdTo","listWorkflowRuns","totalCount","includeTotal","page","timestampKey","runs","pageRows","nextCursor","filteredTotalCount","listWorkflowRunJobSummaries","summaries","Map","length","attemptPairs","join","currentAttempt","attemptFilter","previewRows","countRows","transaction","tx","selectedExecution","$with","as","selectDistinctOn","jobId","executionStatus","sequence","ranked","with","key","name","mode","listenerStatus","position","jobRank","leftJoin","hasStartedJobExecution","startedAt","executionDisplayStatus","displayStatus","rawStatus","groupBy","isolationLevel","accessMode","summary","summaryFor","preview","statusCount","appendStatusCount","rawStatusCounts","statusCounts","existing","get","created","set","counts","find","entry","listWorkflowRunsByProject","result","getWorkflowRunAggregates","statusRows","triggerRows","workflowRows","Promise","all","workflow","getWorkflowJobExecutionDepth","runConditions","jobConditions","workspaceId","runRows","jobRows","runningRuns","runningJobExecutions","getWorkflowRunDetail","target","latestAttempt","job","jobExecution","step","stepAttempt","jobExecutionId","stepId","executionOrder","hydrateWorkflowRunDetail","getJobExecutionDetail","first","detail","stepById","getOrCreateStepDetail","attempts","runAttempt","some","jobById","jobExecutionById"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-run.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/workflow-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EAEvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAC,WAAW,EAA8B,MAAM,gCAAgC,CAAC;AAC7F,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,wCAAwC,CAAC;AAC/E,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,cAAc,CAAC;AAEzD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,aAAa,SAAqB,GAAG,cAAc,CAuB7F;AASD,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,WAAW,EAChB,IAAI,GAAE,sBAAmC,GACxC,sBAAsB,CAiBxB;AAgBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,kBAAkB,GAAG,qBAAqB,CAWlF"}
1
+ {"version":3,"file":"workflow-run.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/workflow-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EAErB,cAAc,EACd,sBAAsB,EAEvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EACV,WAAW,EAGZ,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,wCAAwC,CAAC;AAC/E,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,cAAc,CAAC;AAEzD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,aAAa,SAAqB,GAAG,cAAc,CAyB7F;AASD,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,WAAW,EAChB,IAAI,GAAE,sBAAmC,GACxC,sBAAsB,CAiBxB;AA+BD,wBAAgB,eAAe,CAAC,OAAO,EAAE,kBAAkB,GAAG,qBAAqB,CAWlF"}
@@ -7,6 +7,8 @@ export function toRunDto(run, latestAttempt = run.currentAttempt) {
7
7
  name: run.name,
8
8
  workflow_name: run.workflowName,
9
9
  status: run.status,
10
+ origin: run.origin,
11
+ dev_source: toDevSourceDto(run.devSource),
10
12
  current_attempt: run.currentAttempt,
11
13
  latest_attempt: latestAttempt,
12
14
  trigger_provider: run.triggerProvider,
@@ -63,6 +65,18 @@ function toTriggerReferenceDto(reference) {
63
65
  actor: reference.actor ?? null
64
66
  };
65
67
  }
68
+ // The persisted dev source uses camelCase field names while the API contract is snake_case,
69
+ // so each field is mapped explicitly rather than spread onto the response.
70
+ function toDevSourceDto(devSource) {
71
+ if (!devSource) return null;
72
+ return {
73
+ ref: devSource.ref,
74
+ commit: devSource.commit,
75
+ config_path: devSource.configPath,
76
+ initiated_by_user_id: devSource.initiatedByUserId,
77
+ replay_of_event_id: devSource.replayOfEventId
78
+ };
79
+ }
66
80
  export function toRunAttemptDto(attempt) {
67
81
  return {
68
82
  id: attempt.id,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/dto/workflow-run.ts"],"sourcesContent":["import type {\n WorkflowRunAttemptDto,\n WorkflowRunDto,\n WorkflowRunListItemDto,\n WorkflowRunTriggerReferenceDto,\n} from '@shipfox/api-workflows-dto';\nimport type {WorkflowRun, WorkflowRunTriggerReference} from '#core/entities/workflow-run.js';\nimport type {WorkflowRunAttempt} from '#core/entities/workflow-run-attempt.js';\nimport type {WorkflowRunJobsSummary} from '#db/index.js';\n\nexport function toRunDto(run: WorkflowRun, latestAttempt = run.currentAttempt): WorkflowRunDto {\n return {\n id: run.id,\n project_id: run.projectId,\n definition_id: run.definitionId,\n number: run.number,\n name: run.name,\n workflow_name: run.workflowName,\n status: run.status,\n current_attempt: run.currentAttempt,\n latest_attempt: latestAttempt,\n trigger_provider: run.triggerProvider,\n trigger_source: run.triggerSource,\n trigger_event: run.triggerEvent,\n trigger_payload: run.triggerPayload,\n trigger_reference: toTriggerReferenceDto(run.triggerReference),\n inputs: run.inputs,\n source_snapshot: run.sourceSnapshot,\n created_at: run.createdAt.toISOString(),\n updated_at: run.updatedAt.toISOString(),\n started_at: run.startedAt?.toISOString() ?? null,\n finished_at: run.finishedAt?.toISOString() ?? null,\n };\n}\n\nconst EMPTY_JOBS: WorkflowRunJobsSummary = {\n preview: [],\n statusCounts: [],\n rawStatusCounts: [],\n hasStartedJobExecution: false,\n};\n\nexport function toRunListItemDto(\n run: WorkflowRun,\n jobs: WorkflowRunJobsSummary = EMPTY_JOBS,\n): WorkflowRunListItemDto {\n return {\n ...toRunDto(run),\n jobs: jobs.preview.map((job) => ({\n id: job.id,\n key: job.key,\n name: job.name,\n status: job.status,\n mode: job.mode,\n listener_status: job.listenerStatus,\n execution_status: job.executionStatus,\n position: job.position,\n })),\n job_status_counts: jobs.rawStatusCounts.map(({status, count}) => ({status, count})),\n job_display_status_counts: jobs.statusCounts.map(({status, count}) => ({status, count})),\n has_started_job_execution: jobs.hasStartedJobExecution,\n };\n}\n\n// The persisted reference predates `actor` and carries an internal project id the client has\n// no use for, so each field is read defensively rather than spread onto the response.\nfunction toTriggerReferenceDto(\n reference: WorkflowRunTriggerReference | null | undefined,\n): WorkflowRunTriggerReferenceDto | null {\n if (!reference) return null;\n return {\n repository: reference.repository ?? null,\n ref: reference.ref ?? null,\n commit: reference.commit ?? null,\n actor: reference.actor ?? null,\n };\n}\n\nexport function toRunAttemptDto(attempt: WorkflowRunAttempt): WorkflowRunAttemptDto {\n return {\n id: attempt.id,\n workflow_run_id: attempt.workflowRunId,\n attempt: attempt.attempt,\n status: attempt.status,\n created_at: attempt.createdAt.toISOString(),\n started_at: attempt.startedAt?.toISOString() ?? null,\n finished_at: attempt.finishedAt?.toISOString() ?? null,\n rerun_mode: attempt.rerunMode,\n };\n}\n"],"names":["toRunDto","run","latestAttempt","currentAttempt","id","project_id","projectId","definition_id","definitionId","number","name","workflow_name","workflowName","status","current_attempt","latest_attempt","trigger_provider","triggerProvider","trigger_source","triggerSource","trigger_event","triggerEvent","trigger_payload","triggerPayload","trigger_reference","toTriggerReferenceDto","triggerReference","inputs","source_snapshot","sourceSnapshot","created_at","createdAt","toISOString","updated_at","updatedAt","started_at","startedAt","finished_at","finishedAt","EMPTY_JOBS","preview","statusCounts","rawStatusCounts","hasStartedJobExecution","toRunListItemDto","jobs","map","job","key","mode","listener_status","listenerStatus","execution_status","executionStatus","position","job_status_counts","count","job_display_status_counts","has_started_job_execution","reference","repository","ref","commit","actor","toRunAttemptDto","attempt","workflow_run_id","workflowRunId","rerun_mode","rerunMode"],"mappings":"AAUA,OAAO,SAASA,SAASC,GAAgB,EAAEC,gBAAgBD,IAAIE,cAAc;IAC3E,OAAO;QACLC,IAAIH,IAAIG,EAAE;QACVC,YAAYJ,IAAIK,SAAS;QACzBC,eAAeN,IAAIO,YAAY;QAC/BC,QAAQR,IAAIQ,MAAM;QAClBC,MAAMT,IAAIS,IAAI;QACdC,eAAeV,IAAIW,YAAY;QAC/BC,QAAQZ,IAAIY,MAAM;QAClBC,iBAAiBb,IAAIE,cAAc;QACnCY,gBAAgBb;QAChBc,kBAAkBf,IAAIgB,eAAe;QACrCC,gBAAgBjB,IAAIkB,aAAa;QACjCC,eAAenB,IAAIoB,YAAY;QAC/BC,iBAAiBrB,IAAIsB,cAAc;QACnCC,mBAAmBC,sBAAsBxB,IAAIyB,gBAAgB;QAC7DC,QAAQ1B,IAAI0B,MAAM;QAClBC,iBAAiB3B,IAAI4B,cAAc;QACnCC,YAAY7B,IAAI8B,SAAS,CAACC,WAAW;QACrCC,YAAYhC,IAAIiC,SAAS,CAACF,WAAW;QACrCG,YAAYlC,IAAImC,SAAS,EAAEJ,iBAAiB;QAC5CK,aAAapC,IAAIqC,UAAU,EAAEN,iBAAiB;IAChD;AACF;AAEA,MAAMO,aAAqC;IACzCC,SAAS,EAAE;IACXC,cAAc,EAAE;IAChBC,iBAAiB,EAAE;IACnBC,wBAAwB;AAC1B;AAEA,OAAO,SAASC,iBACd3C,GAAgB,EAChB4C,OAA+BN,UAAU;IAEzC,OAAO;QACL,GAAGvC,SAASC,IAAI;QAChB4C,MAAMA,KAAKL,OAAO,CAACM,GAAG,CAAC,CAACC,MAAS,CAAA;gBAC/B3C,IAAI2C,IAAI3C,EAAE;gBACV4C,KAAKD,IAAIC,GAAG;gBACZtC,MAAMqC,IAAIrC,IAAI;gBACdG,QAAQkC,IAAIlC,MAAM;gBAClBoC,MAAMF,IAAIE,IAAI;gBACdC,iBAAiBH,IAAII,cAAc;gBACnCC,kBAAkBL,IAAIM,eAAe;gBACrCC,UAAUP,IAAIO,QAAQ;YACxB,CAAA;QACAC,mBAAmBV,KAAKH,eAAe,CAACI,GAAG,CAAC,CAAC,EAACjC,MAAM,EAAE2C,KAAK,EAAC,GAAM,CAAA;gBAAC3C;gBAAQ2C;YAAK,CAAA;QAChFC,2BAA2BZ,KAAKJ,YAAY,CAACK,GAAG,CAAC,CAAC,EAACjC,MAAM,EAAE2C,KAAK,EAAC,GAAM,CAAA;gBAAC3C;gBAAQ2C;YAAK,CAAA;QACrFE,2BAA2Bb,KAAKF,sBAAsB;IACxD;AACF;AAEA,6FAA6F;AAC7F,sFAAsF;AACtF,SAASlB,sBACPkC,SAAyD;IAEzD,IAAI,CAACA,WAAW,OAAO;IACvB,OAAO;QACLC,YAAYD,UAAUC,UAAU,IAAI;QACpCC,KAAKF,UAAUE,GAAG,IAAI;QACtBC,QAAQH,UAAUG,MAAM,IAAI;QAC5BC,OAAOJ,UAAUI,KAAK,IAAI;IAC5B;AACF;AAEA,OAAO,SAASC,gBAAgBC,OAA2B;IACzD,OAAO;QACL7D,IAAI6D,QAAQ7D,EAAE;QACd8D,iBAAiBD,QAAQE,aAAa;QACtCF,SAASA,QAAQA,OAAO;QACxBpD,QAAQoD,QAAQpD,MAAM;QACtBiB,YAAYmC,QAAQlC,SAAS,CAACC,WAAW;QACzCG,YAAY8B,QAAQ7B,SAAS,EAAEJ,iBAAiB;QAChDK,aAAa4B,QAAQ3B,UAAU,EAAEN,iBAAiB;QAClDoC,YAAYH,QAAQI,SAAS;IAC/B;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/dto/workflow-run.ts"],"sourcesContent":["import type {\n WorkflowRunAttemptDto,\n WorkflowRunDevSourceDto,\n WorkflowRunDto,\n WorkflowRunListItemDto,\n WorkflowRunTriggerReferenceDto,\n} from '@shipfox/api-workflows-dto';\nimport type {\n WorkflowRun,\n WorkflowRunDevSource,\n WorkflowRunTriggerReference,\n} from '#core/entities/workflow-run.js';\nimport type {WorkflowRunAttempt} from '#core/entities/workflow-run-attempt.js';\nimport type {WorkflowRunJobsSummary} from '#db/index.js';\n\nexport function toRunDto(run: WorkflowRun, latestAttempt = run.currentAttempt): WorkflowRunDto {\n return {\n id: run.id,\n project_id: run.projectId,\n definition_id: run.definitionId,\n number: run.number,\n name: run.name,\n workflow_name: run.workflowName,\n status: run.status,\n origin: run.origin,\n dev_source: toDevSourceDto(run.devSource),\n current_attempt: run.currentAttempt,\n latest_attempt: latestAttempt,\n trigger_provider: run.triggerProvider,\n trigger_source: run.triggerSource,\n trigger_event: run.triggerEvent,\n trigger_payload: run.triggerPayload,\n trigger_reference: toTriggerReferenceDto(run.triggerReference),\n inputs: run.inputs,\n source_snapshot: run.sourceSnapshot,\n created_at: run.createdAt.toISOString(),\n updated_at: run.updatedAt.toISOString(),\n started_at: run.startedAt?.toISOString() ?? null,\n finished_at: run.finishedAt?.toISOString() ?? null,\n };\n}\n\nconst EMPTY_JOBS: WorkflowRunJobsSummary = {\n preview: [],\n statusCounts: [],\n rawStatusCounts: [],\n hasStartedJobExecution: false,\n};\n\nexport function toRunListItemDto(\n run: WorkflowRun,\n jobs: WorkflowRunJobsSummary = EMPTY_JOBS,\n): WorkflowRunListItemDto {\n return {\n ...toRunDto(run),\n jobs: jobs.preview.map((job) => ({\n id: job.id,\n key: job.key,\n name: job.name,\n status: job.status,\n mode: job.mode,\n listener_status: job.listenerStatus,\n execution_status: job.executionStatus,\n position: job.position,\n })),\n job_status_counts: jobs.rawStatusCounts.map(({status, count}) => ({status, count})),\n job_display_status_counts: jobs.statusCounts.map(({status, count}) => ({status, count})),\n has_started_job_execution: jobs.hasStartedJobExecution,\n };\n}\n\n// The persisted reference predates `actor` and carries an internal project id the client has\n// no use for, so each field is read defensively rather than spread onto the response.\nfunction toTriggerReferenceDto(\n reference: WorkflowRunTriggerReference | null | undefined,\n): WorkflowRunTriggerReferenceDto | null {\n if (!reference) return null;\n return {\n repository: reference.repository ?? null,\n ref: reference.ref ?? null,\n commit: reference.commit ?? null,\n actor: reference.actor ?? null,\n };\n}\n\n// The persisted dev source uses camelCase field names while the API contract is snake_case,\n// so each field is mapped explicitly rather than spread onto the response.\nfunction toDevSourceDto(\n devSource: WorkflowRunDevSource | null | undefined,\n): WorkflowRunDevSourceDto | null {\n if (!devSource) return null;\n return {\n ref: devSource.ref,\n commit: devSource.commit,\n config_path: devSource.configPath,\n initiated_by_user_id: devSource.initiatedByUserId,\n replay_of_event_id: devSource.replayOfEventId,\n };\n}\n\nexport function toRunAttemptDto(attempt: WorkflowRunAttempt): WorkflowRunAttemptDto {\n return {\n id: attempt.id,\n workflow_run_id: attempt.workflowRunId,\n attempt: attempt.attempt,\n status: attempt.status,\n created_at: attempt.createdAt.toISOString(),\n started_at: attempt.startedAt?.toISOString() ?? null,\n finished_at: attempt.finishedAt?.toISOString() ?? null,\n rerun_mode: attempt.rerunMode,\n };\n}\n"],"names":["toRunDto","run","latestAttempt","currentAttempt","id","project_id","projectId","definition_id","definitionId","number","name","workflow_name","workflowName","status","origin","dev_source","toDevSourceDto","devSource","current_attempt","latest_attempt","trigger_provider","triggerProvider","trigger_source","triggerSource","trigger_event","triggerEvent","trigger_payload","triggerPayload","trigger_reference","toTriggerReferenceDto","triggerReference","inputs","source_snapshot","sourceSnapshot","created_at","createdAt","toISOString","updated_at","updatedAt","started_at","startedAt","finished_at","finishedAt","EMPTY_JOBS","preview","statusCounts","rawStatusCounts","hasStartedJobExecution","toRunListItemDto","jobs","map","job","key","mode","listener_status","listenerStatus","execution_status","executionStatus","position","job_status_counts","count","job_display_status_counts","has_started_job_execution","reference","repository","ref","commit","actor","config_path","configPath","initiated_by_user_id","initiatedByUserId","replay_of_event_id","replayOfEventId","toRunAttemptDto","attempt","workflow_run_id","workflowRunId","rerun_mode","rerunMode"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"checkout-token.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/checkout-token.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAE9B,MAAM,gDAAgD,CAAC;AACxD,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AAapF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,wBAAwB,CAAC;IAClC,YAAY,EAAE,wBAAwB,CAAC;IACvC,QAAQ,EAAE,oBAAoB,CAAC;CAChC,mDA6HA"}
1
+ {"version":3,"file":"checkout-token.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/checkout-token.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAE9B,MAAM,gDAAgD,CAAC;AACxD,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AAapF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,wBAAwB,CAAC;IAClC,YAAY,EAAE,wBAAwB,CAAC;IACvC,QAAQ,EAAE,oBAAoB,CAAC;CAChC,mDA8HA"}
@@ -63,7 +63,7 @@ export function createCheckoutTokenRoute(clients) {
63
63
  handler: async (request, reply)=>{
64
64
  const { stepId } = request.params;
65
65
  const { attempt } = request.query;
66
- const { step, workspaceId, projectId, triggerReference } = await loadRunningLeasedStep({
66
+ const { step, workspaceId, projectId, triggerReference, run } = await loadRunningLeasedStep({
67
67
  runners: clients.runners,
68
68
  request,
69
69
  stepId,
@@ -79,6 +79,7 @@ export function createCheckoutTokenRoute(clients) {
79
79
  workspaceId,
80
80
  projectId,
81
81
  triggerReference,
82
+ run,
82
83
  integrations: clients.integrations,
83
84
  projects: clients.projects
84
85
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/checkout-token.ts"],"sourcesContent":["import {\n type IntegrationsModuleClient,\n integrationsInterModuleContract,\n} from '@shipfox/api-integration-core-dto/inter-module';\nimport {\n type ProjectsModuleClient,\n projectsInterModuleContract,\n} from '@shipfox/api-projects-dto/inter-module';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport {\n checkoutTokenParamsSchema,\n checkoutTokenQuerySchema,\n checkoutTokenResponseSchema,\n} from '@shipfox/api-workflows-dto';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {createStepCheckoutSpec} from '#core/checkout.js';\nimport {CheckoutConfigInvalidError, CheckoutIntentUnresolvedError} from '#core/errors.js';\nimport {toCheckoutTokenDto} from '#presentation/dto/checkout-token.js';\nimport {loadRunningLeasedStep} from './leased-step.js';\n\nexport function createCheckoutTokenRoute(clients: {\n runners: RunnersInterModuleClient;\n integrations: IntegrationsModuleClient;\n projects: ProjectsModuleClient;\n}) {\n return defineRoute({\n method: 'POST',\n path: '/steps/:stepId/checkout-token',\n description:\n \"Exchanges the runner's lease for short-lived checkout credentials for the currently running checkout step. The step id and attempt are checked against the lease and the server-frozen step config supplies the target, ref, permissions, and fetch depth.\",\n schema: {\n params: checkoutTokenParamsSchema,\n querystring: checkoutTokenQuerySchema,\n response: {200: checkoutTokenResponseSchema},\n },\n errorHandler: (error) => {\n const known = isInterModuleKnownError(\n integrationsInterModuleContract.methods.createCheckoutSpec,\n error,\n )\n ? error\n : undefined;\n const targetError = isInterModuleKnownError(\n projectsInterModuleContract.methods.resolveCheckoutTarget,\n error,\n )\n ? error\n : undefined;\n if (error instanceof CheckoutIntentUnresolvedError)\n throw new ClientError(error.message, 'checkout-unavailable', {status: 404});\n if (error instanceof CheckoutConfigInvalidError)\n throw new ClientError('Checkout configuration is invalid', 'checkout-config-invalid', {\n status: 409,\n });\n if (targetError?.code === 'checkout-repository-not-authorized')\n throw new ClientError(\n 'Checkout repository is not authorized for this workspace',\n 'checkout-repository-not-authorized',\n {status: 404},\n );\n if (known?.code === 'connection-not-found')\n throw new ClientError(\n 'Integration connection not found',\n 'integration-connection-not-found',\n {\n status: 404,\n },\n );\n if (known?.code === 'connection-inactive')\n throw new ClientError(\n 'Integration connection is not active',\n 'integration-connection-inactive',\n {\n status: 422,\n },\n );\n if (known?.code === 'connection-workspace-mismatch')\n throw new ClientError(\n 'Integration connection does not belong to this workspace',\n 'forbidden',\n {status: 403},\n );\n if (known?.code === 'provider-unavailable')\n throw new ClientError(\n 'Integration provider is unavailable',\n 'integration-provider-unavailable',\n {\n status: 422,\n },\n );\n if (known?.code === 'capability-unavailable')\n throw new ClientError(\n 'Integration capability is unavailable',\n 'integration-capability-unavailable',\n {\n status: 422,\n },\n );\n if (known?.code === 'checkout-unsupported')\n throw new ClientError(\n 'Integration checkout is unsupported',\n 'integration-checkout-unsupported',\n {\n status: 422,\n },\n );\n if (known?.code === 'provider-failure') {\n const status =\n known.details.reason === 'rate-limited'\n ? 429\n : known.details.reason === 'timeout' || known.details.reason === 'provider-unavailable'\n ? 503\n : 422;\n throw new ClientError('Integration provider request failed', known.details.reason, {\n details: {retry_after_seconds: known.details.retryAfterSeconds},\n status,\n });\n }\n throw error;\n },\n handler: async (request, reply) => {\n const {stepId} = request.params;\n const {attempt} = request.query;\n const {step, workspaceId, projectId, triggerReference} = await loadRunningLeasedStep({\n runners: clients.runners,\n request,\n stepId,\n attempt,\n });\n\n if (step.type !== 'setup' && step.type !== 'checkout') {\n throw new ClientError('Step is not a checkout step', 'step-not-checkout', {status: 409});\n }\n\n const checkout = await createStepCheckoutSpec({\n step,\n workspaceId,\n projectId,\n triggerReference,\n integrations: clients.integrations,\n projects: clients.projects,\n });\n reply.header('cache-control', 'no-store');\n return toCheckoutTokenDto(checkout.spec, {\n fetchDepth: checkout.fetchDepth,\n persist: checkout.persistCredentials,\n });\n },\n });\n}\n"],"names":["integrationsInterModuleContract","projectsInterModuleContract","checkoutTokenParamsSchema","checkoutTokenQuerySchema","checkoutTokenResponseSchema","isInterModuleKnownError","ClientError","defineRoute","createStepCheckoutSpec","CheckoutConfigInvalidError","CheckoutIntentUnresolvedError","toCheckoutTokenDto","loadRunningLeasedStep","createCheckoutTokenRoute","clients","method","path","description","schema","params","querystring","response","errorHandler","error","known","methods","createCheckoutSpec","undefined","targetError","resolveCheckoutTarget","message","status","code","details","reason","retry_after_seconds","retryAfterSeconds","handler","request","reply","stepId","attempt","query","step","workspaceId","projectId","triggerReference","runners","type","checkout","integrations","projects","header","spec","fetchDepth","persist","persistCredentials"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/checkout-token.ts"],"sourcesContent":["import {\n type IntegrationsModuleClient,\n integrationsInterModuleContract,\n} from '@shipfox/api-integration-core-dto/inter-module';\nimport {\n type ProjectsModuleClient,\n projectsInterModuleContract,\n} from '@shipfox/api-projects-dto/inter-module';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport {\n checkoutTokenParamsSchema,\n checkoutTokenQuerySchema,\n checkoutTokenResponseSchema,\n} from '@shipfox/api-workflows-dto';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {createStepCheckoutSpec} from '#core/checkout.js';\nimport {CheckoutConfigInvalidError, CheckoutIntentUnresolvedError} from '#core/errors.js';\nimport {toCheckoutTokenDto} from '#presentation/dto/checkout-token.js';\nimport {loadRunningLeasedStep} from './leased-step.js';\n\nexport function createCheckoutTokenRoute(clients: {\n runners: RunnersInterModuleClient;\n integrations: IntegrationsModuleClient;\n projects: ProjectsModuleClient;\n}) {\n return defineRoute({\n method: 'POST',\n path: '/steps/:stepId/checkout-token',\n description:\n \"Exchanges the runner's lease for short-lived checkout credentials for the currently running checkout step. The step id and attempt are checked against the lease and the server-frozen step config supplies the target, ref, permissions, and fetch depth.\",\n schema: {\n params: checkoutTokenParamsSchema,\n querystring: checkoutTokenQuerySchema,\n response: {200: checkoutTokenResponseSchema},\n },\n errorHandler: (error) => {\n const known = isInterModuleKnownError(\n integrationsInterModuleContract.methods.createCheckoutSpec,\n error,\n )\n ? error\n : undefined;\n const targetError = isInterModuleKnownError(\n projectsInterModuleContract.methods.resolveCheckoutTarget,\n error,\n )\n ? error\n : undefined;\n if (error instanceof CheckoutIntentUnresolvedError)\n throw new ClientError(error.message, 'checkout-unavailable', {status: 404});\n if (error instanceof CheckoutConfigInvalidError)\n throw new ClientError('Checkout configuration is invalid', 'checkout-config-invalid', {\n status: 409,\n });\n if (targetError?.code === 'checkout-repository-not-authorized')\n throw new ClientError(\n 'Checkout repository is not authorized for this workspace',\n 'checkout-repository-not-authorized',\n {status: 404},\n );\n if (known?.code === 'connection-not-found')\n throw new ClientError(\n 'Integration connection not found',\n 'integration-connection-not-found',\n {\n status: 404,\n },\n );\n if (known?.code === 'connection-inactive')\n throw new ClientError(\n 'Integration connection is not active',\n 'integration-connection-inactive',\n {\n status: 422,\n },\n );\n if (known?.code === 'connection-workspace-mismatch')\n throw new ClientError(\n 'Integration connection does not belong to this workspace',\n 'forbidden',\n {status: 403},\n );\n if (known?.code === 'provider-unavailable')\n throw new ClientError(\n 'Integration provider is unavailable',\n 'integration-provider-unavailable',\n {\n status: 422,\n },\n );\n if (known?.code === 'capability-unavailable')\n throw new ClientError(\n 'Integration capability is unavailable',\n 'integration-capability-unavailable',\n {\n status: 422,\n },\n );\n if (known?.code === 'checkout-unsupported')\n throw new ClientError(\n 'Integration checkout is unsupported',\n 'integration-checkout-unsupported',\n {\n status: 422,\n },\n );\n if (known?.code === 'provider-failure') {\n const status =\n known.details.reason === 'rate-limited'\n ? 429\n : known.details.reason === 'timeout' || known.details.reason === 'provider-unavailable'\n ? 503\n : 422;\n throw new ClientError('Integration provider request failed', known.details.reason, {\n details: {retry_after_seconds: known.details.retryAfterSeconds},\n status,\n });\n }\n throw error;\n },\n handler: async (request, reply) => {\n const {stepId} = request.params;\n const {attempt} = request.query;\n const {step, workspaceId, projectId, triggerReference, run} = await loadRunningLeasedStep({\n runners: clients.runners,\n request,\n stepId,\n attempt,\n });\n\n if (step.type !== 'setup' && step.type !== 'checkout') {\n throw new ClientError('Step is not a checkout step', 'step-not-checkout', {status: 409});\n }\n\n const checkout = await createStepCheckoutSpec({\n step,\n workspaceId,\n projectId,\n triggerReference,\n run,\n integrations: clients.integrations,\n projects: clients.projects,\n });\n reply.header('cache-control', 'no-store');\n return toCheckoutTokenDto(checkout.spec, {\n fetchDepth: checkout.fetchDepth,\n persist: checkout.persistCredentials,\n });\n },\n });\n}\n"],"names":["integrationsInterModuleContract","projectsInterModuleContract","checkoutTokenParamsSchema","checkoutTokenQuerySchema","checkoutTokenResponseSchema","isInterModuleKnownError","ClientError","defineRoute","createStepCheckoutSpec","CheckoutConfigInvalidError","CheckoutIntentUnresolvedError","toCheckoutTokenDto","loadRunningLeasedStep","createCheckoutTokenRoute","clients","method","path","description","schema","params","querystring","response","errorHandler","error","known","methods","createCheckoutSpec","undefined","targetError","resolveCheckoutTarget","message","status","code","details","reason","retry_after_seconds","retryAfterSeconds","handler","request","reply","stepId","attempt","query","step","workspaceId","projectId","triggerReference","run","runners","type","checkout","integrations","projects","header","spec","fetchDepth","persist","persistCredentials"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-run-aggregates.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/get-run-aggregates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wCAAwC,CAAC;AAUjF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,oBAAoB,mDAuDnE"}
1
+ {"version":3,"file":"get-run-aggregates.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/get-run-aggregates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wCAAwC,CAAC;AAUjF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,oBAAoB,mDAyDnE"}
@@ -16,12 +16,13 @@ export function getRunAggregatesRoute(projects) {
16
16
  },
17
17
  handler: async (request)=>{
18
18
  const startedAt = performance.now();
19
- const { project_id: projectId, status, definition_id: definitionId, trigger_source: triggerSource, created_from: createdFrom, created_to: createdTo } = request.query;
19
+ const { project_id: projectId, status, definition_id: definitionId, trigger_source: triggerSource, origin, created_from: createdFrom, created_to: createdTo } = request.query;
20
20
  const project = await requireProjectAccess(request, projectId, projects);
21
21
  const filters = {
22
22
  status,
23
23
  definitionId,
24
24
  triggerSource,
25
+ origin,
25
26
  createdFrom: createdFrom ? new Date(createdFrom) : undefined,
26
27
  createdTo: createdTo ? new Date(createdTo) : undefined
27
28
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/get-run-aggregates.ts"],"sourcesContent":["import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport {\n workflowRunAggregatesQuerySchema,\n workflowRunAggregatesResponseSchema,\n} from '@shipfox/api-workflows-dto';\nimport {defineRoute} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {getWorkflowRunAggregates} from '#db/index.js';\nimport {requireProjectAccess} from './project-access.js';\n\nexport function getRunAggregatesRoute(projects: ProjectsModuleClient) {\n return defineRoute({\n method: 'GET',\n path: '/aggregates',\n description: 'Get faceted workflow run counts for a project',\n schema: {\n querystring: workflowRunAggregatesQuerySchema,\n response: {\n 200: workflowRunAggregatesResponseSchema,\n },\n },\n handler: async (request) => {\n const startedAt = performance.now();\n const {\n project_id: projectId,\n status,\n definition_id: definitionId,\n trigger_source: triggerSource,\n created_from: createdFrom,\n created_to: createdTo,\n } = request.query;\n\n const project = await requireProjectAccess(request, projectId, projects);\n const filters = {\n status,\n definitionId,\n triggerSource,\n createdFrom: createdFrom ? new Date(createdFrom) : undefined,\n createdTo: createdTo ? new Date(createdTo) : undefined,\n };\n const aggregates = await getWorkflowRunAggregates({projectId: project.id, filters});\n\n logger().info(\n {\n projectId: project.id,\n filterKeys: Object.entries(filters)\n .filter(([, value]) => value !== undefined)\n .map(([key]) => key),\n aggregateBucketSizes: {\n status: aggregates.status.length,\n triggerSource: aggregates.triggerSource.length,\n workflow: aggregates.workflow.length,\n },\n durationMs: Math.round(performance.now() - startedAt),\n },\n 'Aggregated workflow runs',\n );\n\n return {\n status: aggregates.status,\n trigger_source: aggregates.triggerSource,\n workflow: aggregates.workflow,\n };\n },\n });\n}\n"],"names":["workflowRunAggregatesQuerySchema","workflowRunAggregatesResponseSchema","defineRoute","logger","getWorkflowRunAggregates","requireProjectAccess","getRunAggregatesRoute","projects","method","path","description","schema","querystring","response","handler","request","startedAt","performance","now","project_id","projectId","status","definition_id","definitionId","trigger_source","triggerSource","created_from","createdFrom","created_to","createdTo","query","project","filters","Date","undefined","aggregates","id","info","filterKeys","Object","entries","filter","value","map","key","aggregateBucketSizes","length","workflow","durationMs","Math","round"],"mappings":"AACA,SACEA,gCAAgC,EAChCC,mCAAmC,QAC9B,6BAA6B;AACpC,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,MAAM,QAAO,8BAA8B;AACnD,SAAQC,wBAAwB,QAAO,eAAe;AACtD,SAAQC,oBAAoB,QAAO,sBAAsB;AAEzD,OAAO,SAASC,sBAAsBC,QAA8B;IAClE,OAAOL,YAAY;QACjBM,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,aAAaZ;YACba,UAAU;gBACR,KAAKZ;YACP;QACF;QACAa,SAAS,OAAOC;YACd,MAAMC,YAAYC,YAAYC,GAAG;YACjC,MAAM,EACJC,YAAYC,SAAS,EACrBC,MAAM,EACNC,eAAeC,YAAY,EAC3BC,gBAAgBC,aAAa,EAC7BC,cAAcC,WAAW,EACzBC,YAAYC,SAAS,EACtB,GAAGd,QAAQe,KAAK;YAEjB,MAAMC,UAAU,MAAM1B,qBAAqBU,SAASK,WAAWb;YAC/D,MAAMyB,UAAU;gBACdX;gBACAE;gBACAE;gBACAE,aAAaA,cAAc,IAAIM,KAAKN,eAAeO;gBACnDL,WAAWA,YAAY,IAAII,KAAKJ,aAAaK;YAC/C;YACA,MAAMC,aAAa,MAAM/B,yBAAyB;gBAACgB,WAAWW,QAAQK,EAAE;gBAAEJ;YAAO;YAEjF7B,SAASkC,IAAI,CACX;gBACEjB,WAAWW,QAAQK,EAAE;gBACrBE,YAAYC,OAAOC,OAAO,CAACR,SACxBS,MAAM,CAAC,CAAC,GAAGC,MAAM,GAAKA,UAAUR,WAChCS,GAAG,CAAC,CAAC,CAACC,IAAI,GAAKA;gBAClBC,sBAAsB;oBACpBxB,QAAQc,WAAWd,MAAM,CAACyB,MAAM;oBAChCrB,eAAeU,WAAWV,aAAa,CAACqB,MAAM;oBAC9CC,UAAUZ,WAAWY,QAAQ,CAACD,MAAM;gBACtC;gBACAE,YAAYC,KAAKC,KAAK,CAACjC,YAAYC,GAAG,KAAKF;YAC7C,GACA;YAGF,OAAO;gBACLK,QAAQc,WAAWd,MAAM;gBACzBG,gBAAgBW,WAAWV,aAAa;gBACxCsB,UAAUZ,WAAWY,QAAQ;YAC/B;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/get-run-aggregates.ts"],"sourcesContent":["import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport {\n workflowRunAggregatesQuerySchema,\n workflowRunAggregatesResponseSchema,\n} from '@shipfox/api-workflows-dto';\nimport {defineRoute} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {getWorkflowRunAggregates} from '#db/index.js';\nimport {requireProjectAccess} from './project-access.js';\n\nexport function getRunAggregatesRoute(projects: ProjectsModuleClient) {\n return defineRoute({\n method: 'GET',\n path: '/aggregates',\n description: 'Get faceted workflow run counts for a project',\n schema: {\n querystring: workflowRunAggregatesQuerySchema,\n response: {\n 200: workflowRunAggregatesResponseSchema,\n },\n },\n handler: async (request) => {\n const startedAt = performance.now();\n const {\n project_id: projectId,\n status,\n definition_id: definitionId,\n trigger_source: triggerSource,\n origin,\n created_from: createdFrom,\n created_to: createdTo,\n } = request.query;\n\n const project = await requireProjectAccess(request, projectId, projects);\n const filters = {\n status,\n definitionId,\n triggerSource,\n origin,\n createdFrom: createdFrom ? new Date(createdFrom) : undefined,\n createdTo: createdTo ? new Date(createdTo) : undefined,\n };\n const aggregates = await getWorkflowRunAggregates({projectId: project.id, filters});\n\n logger().info(\n {\n projectId: project.id,\n filterKeys: Object.entries(filters)\n .filter(([, value]) => value !== undefined)\n .map(([key]) => key),\n aggregateBucketSizes: {\n status: aggregates.status.length,\n triggerSource: aggregates.triggerSource.length,\n workflow: aggregates.workflow.length,\n },\n durationMs: Math.round(performance.now() - startedAt),\n },\n 'Aggregated workflow runs',\n );\n\n return {\n status: aggregates.status,\n trigger_source: aggregates.triggerSource,\n workflow: aggregates.workflow,\n };\n },\n });\n}\n"],"names":["workflowRunAggregatesQuerySchema","workflowRunAggregatesResponseSchema","defineRoute","logger","getWorkflowRunAggregates","requireProjectAccess","getRunAggregatesRoute","projects","method","path","description","schema","querystring","response","handler","request","startedAt","performance","now","project_id","projectId","status","definition_id","definitionId","trigger_source","triggerSource","origin","created_from","createdFrom","created_to","createdTo","query","project","filters","Date","undefined","aggregates","id","info","filterKeys","Object","entries","filter","value","map","key","aggregateBucketSizes","length","workflow","durationMs","Math","round"],"mappings":"AACA,SACEA,gCAAgC,EAChCC,mCAAmC,QAC9B,6BAA6B;AACpC,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,MAAM,QAAO,8BAA8B;AACnD,SAAQC,wBAAwB,QAAO,eAAe;AACtD,SAAQC,oBAAoB,QAAO,sBAAsB;AAEzD,OAAO,SAASC,sBAAsBC,QAA8B;IAClE,OAAOL,YAAY;QACjBM,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,aAAaZ;YACba,UAAU;gBACR,KAAKZ;YACP;QACF;QACAa,SAAS,OAAOC;YACd,MAAMC,YAAYC,YAAYC,GAAG;YACjC,MAAM,EACJC,YAAYC,SAAS,EACrBC,MAAM,EACNC,eAAeC,YAAY,EAC3BC,gBAAgBC,aAAa,EAC7BC,MAAM,EACNC,cAAcC,WAAW,EACzBC,YAAYC,SAAS,EACtB,GAAGf,QAAQgB,KAAK;YAEjB,MAAMC,UAAU,MAAM3B,qBAAqBU,SAASK,WAAWb;YAC/D,MAAM0B,UAAU;gBACdZ;gBACAE;gBACAE;gBACAC;gBACAE,aAAaA,cAAc,IAAIM,KAAKN,eAAeO;gBACnDL,WAAWA,YAAY,IAAII,KAAKJ,aAAaK;YAC/C;YACA,MAAMC,aAAa,MAAMhC,yBAAyB;gBAACgB,WAAWY,QAAQK,EAAE;gBAAEJ;YAAO;YAEjF9B,SAASmC,IAAI,CACX;gBACElB,WAAWY,QAAQK,EAAE;gBACrBE,YAAYC,OAAOC,OAAO,CAACR,SACxBS,MAAM,CAAC,CAAC,GAAGC,MAAM,GAAKA,UAAUR,WAChCS,GAAG,CAAC,CAAC,CAACC,IAAI,GAAKA;gBAClBC,sBAAsB;oBACpBzB,QAAQe,WAAWf,MAAM,CAAC0B,MAAM;oBAChCtB,eAAeW,WAAWX,aAAa,CAACsB,MAAM;oBAC9CC,UAAUZ,WAAWY,QAAQ,CAACD,MAAM;gBACtC;gBACAE,YAAYC,KAAKC,KAAK,CAAClC,YAAYC,GAAG,KAAKF;YAC7C,GACA;YAGF,OAAO;gBACLK,QAAQe,WAAWf,MAAM;gBACzBG,gBAAgBY,WAAWX,aAAa;gBACxCuB,UAAUZ,WAAWY,QAAQ;YAC/B;QACF;IACF;AACF"}
@@ -1,13 +1,15 @@
1
1
  import { type LeasedJobContext } from '@shipfox/api-auth-context';
2
2
  import type { RunnersInterModuleClient } from '@shipfox/api-runners-dto/inter-module';
3
3
  import type { Step } from '#core/entities/step.js';
4
- import type { WorkflowRunTriggerReference } from '#core/entities/workflow-run.js';
4
+ import type { WorkflowRunOriginState, WorkflowRunTriggerReference } from '#core/entities/workflow-run.js';
5
5
  export interface LoadedRunningLeasedStep {
6
6
  leasedJob: LeasedJobContext;
7
7
  step: Step;
8
8
  workspaceId: string;
9
9
  projectId: string;
10
10
  triggerReference: WorkflowRunTriggerReference | null;
11
+ /** The run's origin state, forwarded for checkout fallbacks. */
12
+ run: WorkflowRunOriginState;
11
13
  }
12
14
  export declare function loadRunningLeasedStep(params: {
13
15
  runners: RunnersInterModuleClient;
@@ -1 +1 @@
1
- {"version":3,"file":"leased-step.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/leased-step.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,gBAAgB,EAA0B,MAAM,2BAA2B,CAAC;AACzF,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AAEpF,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,gCAAgC,CAAC;AAGhF,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,2BAA2B,GAAG,IAAI,CAAC;CACtD;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,wBAAwB,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA0CnC"}
1
+ {"version":3,"file":"leased-step.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/leased-step.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,gBAAgB,EAA0B,MAAM,2BAA2B,CAAC;AACzF,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AAEpF,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,EACV,sBAAsB,EACtB,2BAA2B,EAC5B,MAAM,gCAAgC,CAAC;AAGxC,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,2BAA2B,GAAG,IAAI,CAAC;IACrD,gEAAgE;IAChE,GAAG,EAAE,sBAAsB,CAAC;CAC7B;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,wBAAwB,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA2CnC"}
@@ -43,7 +43,8 @@ export async function loadRunningLeasedStep(params) {
43
43
  step,
44
44
  workspaceId: scope.workspaceId,
45
45
  projectId: scope.projectId,
46
- triggerReference: scope.triggerReference
46
+ triggerReference: scope.triggerReference,
47
+ run: scope.run
47
48
  };
48
49
  }
49
50
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/leased-step.ts"],"sourcesContent":["import {type LeasedJobContext, requireLeasedJobContext} from '@shipfox/api-auth-context';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport {ClientError} from '@shipfox/node-fastify';\nimport type {Step} from '#core/entities/step.js';\nimport type {WorkflowRunTriggerReference} from '#core/entities/workflow-run.js';\nimport {getJobScope, getStepByIdForJobExecution} from '#db/index.js';\n\nexport interface LoadedRunningLeasedStep {\n leasedJob: LeasedJobContext;\n step: Step;\n workspaceId: string;\n projectId: string;\n triggerReference: WorkflowRunTriggerReference | null;\n}\n\nexport async function loadRunningLeasedStep(params: {\n runners: RunnersInterModuleClient;\n request: object;\n stepId: string;\n attempt: number;\n}): Promise<LoadedRunningLeasedStep> {\n const leasedJob = requireLeasedJobContext(params.request);\n\n const {active: leaseIsActive} = await params.runners.getLeaseState({\n jobId: leasedJob.jobId,\n jobExecutionId: leasedJob.jobExecutionId,\n runnerSessionId: leasedJob.runnerSessionId,\n });\n if (!leaseIsActive) {\n throw new ClientError('Job lease is no longer active', 'lease-not-active', {status: 404});\n }\n\n const step = await getStepByIdForJobExecution({\n stepId: params.stepId,\n jobExecutionId: leasedJob.jobExecutionId,\n });\n if (!step) {\n throw new ClientError('Step not found for leased job', 'step-not-found', {status: 404});\n }\n\n const scope = await getJobScope(leasedJob.jobId);\n if (!scope) {\n throw new ClientError('Leased job not found', 'job-not-found', {status: 404});\n }\n\n if (step.currentAttempt !== params.attempt) {\n throw new ClientError('Step attempt does not match current attempt', 'step-attempt-mismatch', {\n status: 409,\n });\n }\n\n if (step.status !== 'running') {\n throw new ClientError('Step is not running', 'step-not-running', {status: 409});\n }\n\n return {\n leasedJob,\n step,\n workspaceId: scope.workspaceId,\n projectId: scope.projectId,\n triggerReference: scope.triggerReference,\n };\n}\n"],"names":["requireLeasedJobContext","ClientError","getJobScope","getStepByIdForJobExecution","loadRunningLeasedStep","params","leasedJob","request","active","leaseIsActive","runners","getLeaseState","jobId","jobExecutionId","runnerSessionId","status","step","stepId","scope","currentAttempt","attempt","workspaceId","projectId","triggerReference"],"mappings":"AAAA,SAA+BA,uBAAuB,QAAO,4BAA4B;AAEzF,SAAQC,WAAW,QAAO,wBAAwB;AAGlD,SAAQC,WAAW,EAAEC,0BAA0B,QAAO,eAAe;AAUrE,OAAO,eAAeC,sBAAsBC,MAK3C;IACC,MAAMC,YAAYN,wBAAwBK,OAAOE,OAAO;IAExD,MAAM,EAACC,QAAQC,aAAa,EAAC,GAAG,MAAMJ,OAAOK,OAAO,CAACC,aAAa,CAAC;QACjEC,OAAON,UAAUM,KAAK;QACtBC,gBAAgBP,UAAUO,cAAc;QACxCC,iBAAiBR,UAAUQ,eAAe;IAC5C;IACA,IAAI,CAACL,eAAe;QAClB,MAAM,IAAIR,YAAY,iCAAiC,oBAAoB;YAACc,QAAQ;QAAG;IACzF;IAEA,MAAMC,OAAO,MAAMb,2BAA2B;QAC5Cc,QAAQZ,OAAOY,MAAM;QACrBJ,gBAAgBP,UAAUO,cAAc;IAC1C;IACA,IAAI,CAACG,MAAM;QACT,MAAM,IAAIf,YAAY,iCAAiC,kBAAkB;YAACc,QAAQ;QAAG;IACvF;IAEA,MAAMG,QAAQ,MAAMhB,YAAYI,UAAUM,KAAK;IAC/C,IAAI,CAACM,OAAO;QACV,MAAM,IAAIjB,YAAY,wBAAwB,iBAAiB;YAACc,QAAQ;QAAG;IAC7E;IAEA,IAAIC,KAAKG,cAAc,KAAKd,OAAOe,OAAO,EAAE;QAC1C,MAAM,IAAInB,YAAY,+CAA+C,yBAAyB;YAC5Fc,QAAQ;QACV;IACF;IAEA,IAAIC,KAAKD,MAAM,KAAK,WAAW;QAC7B,MAAM,IAAId,YAAY,uBAAuB,oBAAoB;YAACc,QAAQ;QAAG;IAC/E;IAEA,OAAO;QACLT;QACAU;QACAK,aAAaH,MAAMG,WAAW;QAC9BC,WAAWJ,MAAMI,SAAS;QAC1BC,kBAAkBL,MAAMK,gBAAgB;IAC1C;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/leased-step.ts"],"sourcesContent":["import {type LeasedJobContext, requireLeasedJobContext} from '@shipfox/api-auth-context';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport {ClientError} from '@shipfox/node-fastify';\nimport type {Step} from '#core/entities/step.js';\nimport type {\n WorkflowRunOriginState,\n WorkflowRunTriggerReference,\n} from '#core/entities/workflow-run.js';\nimport {getJobScope, getStepByIdForJobExecution} from '#db/index.js';\n\nexport interface LoadedRunningLeasedStep {\n leasedJob: LeasedJobContext;\n step: Step;\n workspaceId: string;\n projectId: string;\n triggerReference: WorkflowRunTriggerReference | null;\n /** The run's origin state, forwarded for checkout fallbacks. */\n run: WorkflowRunOriginState;\n}\n\nexport async function loadRunningLeasedStep(params: {\n runners: RunnersInterModuleClient;\n request: object;\n stepId: string;\n attempt: number;\n}): Promise<LoadedRunningLeasedStep> {\n const leasedJob = requireLeasedJobContext(params.request);\n\n const {active: leaseIsActive} = await params.runners.getLeaseState({\n jobId: leasedJob.jobId,\n jobExecutionId: leasedJob.jobExecutionId,\n runnerSessionId: leasedJob.runnerSessionId,\n });\n if (!leaseIsActive) {\n throw new ClientError('Job lease is no longer active', 'lease-not-active', {status: 404});\n }\n\n const step = await getStepByIdForJobExecution({\n stepId: params.stepId,\n jobExecutionId: leasedJob.jobExecutionId,\n });\n if (!step) {\n throw new ClientError('Step not found for leased job', 'step-not-found', {status: 404});\n }\n\n const scope = await getJobScope(leasedJob.jobId);\n if (!scope) {\n throw new ClientError('Leased job not found', 'job-not-found', {status: 404});\n }\n\n if (step.currentAttempt !== params.attempt) {\n throw new ClientError('Step attempt does not match current attempt', 'step-attempt-mismatch', {\n status: 409,\n });\n }\n\n if (step.status !== 'running') {\n throw new ClientError('Step is not running', 'step-not-running', {status: 409});\n }\n\n return {\n leasedJob,\n step,\n workspaceId: scope.workspaceId,\n projectId: scope.projectId,\n triggerReference: scope.triggerReference,\n run: scope.run,\n };\n}\n"],"names":["requireLeasedJobContext","ClientError","getJobScope","getStepByIdForJobExecution","loadRunningLeasedStep","params","leasedJob","request","active","leaseIsActive","runners","getLeaseState","jobId","jobExecutionId","runnerSessionId","status","step","stepId","scope","currentAttempt","attempt","workspaceId","projectId","triggerReference","run"],"mappings":"AAAA,SAA+BA,uBAAuB,QAAO,4BAA4B;AAEzF,SAAQC,WAAW,QAAO,wBAAwB;AAMlD,SAAQC,WAAW,EAAEC,0BAA0B,QAAO,eAAe;AAYrE,OAAO,eAAeC,sBAAsBC,MAK3C;IACC,MAAMC,YAAYN,wBAAwBK,OAAOE,OAAO;IAExD,MAAM,EAACC,QAAQC,aAAa,EAAC,GAAG,MAAMJ,OAAOK,OAAO,CAACC,aAAa,CAAC;QACjEC,OAAON,UAAUM,KAAK;QACtBC,gBAAgBP,UAAUO,cAAc;QACxCC,iBAAiBR,UAAUQ,eAAe;IAC5C;IACA,IAAI,CAACL,eAAe;QAClB,MAAM,IAAIR,YAAY,iCAAiC,oBAAoB;YAACc,QAAQ;QAAG;IACzF;IAEA,MAAMC,OAAO,MAAMb,2BAA2B;QAC5Cc,QAAQZ,OAAOY,MAAM;QACrBJ,gBAAgBP,UAAUO,cAAc;IAC1C;IACA,IAAI,CAACG,MAAM;QACT,MAAM,IAAIf,YAAY,iCAAiC,kBAAkB;YAACc,QAAQ;QAAG;IACvF;IAEA,MAAMG,QAAQ,MAAMhB,YAAYI,UAAUM,KAAK;IAC/C,IAAI,CAACM,OAAO;QACV,MAAM,IAAIjB,YAAY,wBAAwB,iBAAiB;YAACc,QAAQ;QAAG;IAC7E;IAEA,IAAIC,KAAKG,cAAc,KAAKd,OAAOe,OAAO,EAAE;QAC1C,MAAM,IAAInB,YAAY,+CAA+C,yBAAyB;YAC5Fc,QAAQ;QACV;IACF;IAEA,IAAIC,KAAKD,MAAM,KAAK,WAAW;QAC7B,MAAM,IAAId,YAAY,uBAAuB,oBAAoB;YAACc,QAAQ;QAAG;IAC/E;IAEA,OAAO;QACLT;QACAU;QACAK,aAAaH,MAAMG,WAAW;QAC9BC,WAAWJ,MAAMI,SAAS;QAC1BC,kBAAkBL,MAAMK,gBAAgB;QACxCC,KAAKN,MAAMM,GAAG;IAChB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"list-runs.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/list-runs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wCAAwC,CAAC;AAYjF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,oBAAoB,mDAwE3D"}
1
+ {"version":3,"file":"list-runs.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/list-runs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wCAAwC,CAAC;AAYjF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,oBAAoB,mDA0E3D"}
@@ -18,7 +18,7 @@ export function listRunsRoute(projects) {
18
18
  },
19
19
  handler: async (request)=>{
20
20
  const startedAt = performance.now();
21
- const { project_id: projectId, limit, cursor, status, definition_id: definitionId, trigger_source: triggerSource, created_from: createdFrom, created_to: createdTo } = request.query;
21
+ const { project_id: projectId, limit, cursor, status, definition_id: definitionId, trigger_source: triggerSource, origin, created_from: createdFrom, created_to: createdTo } = request.query;
22
22
  const decodedCursor = decodeTimestampIdCursor(cursor);
23
23
  if (cursor && !decodedCursor) {
24
24
  throw new ClientError('Invalid cursor', 'invalid-cursor', {
@@ -30,6 +30,7 @@ export function listRunsRoute(projects) {
30
30
  status,
31
31
  definitionId,
32
32
  triggerSource,
33
+ origin,
33
34
  createdFrom: createdFrom ? new Date(createdFrom) : undefined,
34
35
  createdTo: createdTo ? new Date(createdTo) : undefined
35
36
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/list-runs.ts"],"sourcesContent":["import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport {\n workflowRunListQuerySchema,\n workflowRunListResponseSchema,\n} from '@shipfox/api-workflows-dto';\nimport {decodeTimestampIdCursor, encodeTimestampIdCursor} from '@shipfox/node-drizzle';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {listWorkflowRunJobSummaries, listWorkflowRuns} from '#db/index.js';\nimport {toRunListItemDto} from '#presentation/dto/index.js';\nimport {requireProjectAccess} from './project-access.js';\n\nexport function listRunsRoute(projects: ProjectsModuleClient) {\n return defineRoute({\n method: 'GET',\n path: '/',\n description: 'List workflow runs for a project',\n schema: {\n querystring: workflowRunListQuerySchema,\n response: {\n 200: workflowRunListResponseSchema,\n },\n },\n handler: async (request) => {\n const startedAt = performance.now();\n const {\n project_id: projectId,\n limit,\n cursor,\n status,\n definition_id: definitionId,\n trigger_source: triggerSource,\n created_from: createdFrom,\n created_to: createdTo,\n } = request.query;\n const decodedCursor = decodeTimestampIdCursor(cursor);\n if (cursor && !decodedCursor) {\n throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});\n }\n\n const project = await requireProjectAccess(request, projectId, projects);\n\n const filters = {\n status,\n definitionId,\n triggerSource,\n createdFrom: createdFrom ? new Date(createdFrom) : undefined,\n createdTo: createdTo ? new Date(createdTo) : undefined,\n };\n const result = await listWorkflowRuns({\n projectId: project.id,\n limit,\n cursor: decodedCursor,\n filters,\n includeTotal: !decodedCursor,\n });\n // Paired with the attempt this read returned, so a re-run landing mid-request cannot\n // pair one attempt's metadata with another's jobs.\n const jobsByRun = await listWorkflowRunJobSummaries(\n result.runs.map((run) => ({id: run.id, currentAttempt: run.currentAttempt})),\n );\n\n logger().info(\n {\n projectId: project.id,\n filterKeys: Object.entries(filters)\n .filter(([, value]) => value !== undefined)\n .map(([key]) => key),\n limit,\n cursorPresent: Boolean(cursor),\n resultCount: result.runs.length,\n nextCursorPresent: Boolean(result.nextCursor),\n durationMs: Math.round(performance.now() - startedAt),\n },\n 'Listed workflow runs',\n );\n\n return {\n runs: result.runs.map((run) => toRunListItemDto(run, jobsByRun.get(run.id))),\n next_cursor: result.nextCursor ? encodeTimestampIdCursor(result.nextCursor) : null,\n filtered_total_count: result.filteredTotalCount,\n };\n },\n });\n}\n"],"names":["workflowRunListQuerySchema","workflowRunListResponseSchema","decodeTimestampIdCursor","encodeTimestampIdCursor","ClientError","defineRoute","logger","listWorkflowRunJobSummaries","listWorkflowRuns","toRunListItemDto","requireProjectAccess","listRunsRoute","projects","method","path","description","schema","querystring","response","handler","request","startedAt","performance","now","project_id","projectId","limit","cursor","status","definition_id","definitionId","trigger_source","triggerSource","created_from","createdFrom","created_to","createdTo","query","decodedCursor","project","filters","Date","undefined","result","id","includeTotal","jobsByRun","runs","map","run","currentAttempt","info","filterKeys","Object","entries","filter","value","key","cursorPresent","Boolean","resultCount","length","nextCursorPresent","nextCursor","durationMs","Math","round","get","next_cursor","filtered_total_count","filteredTotalCount"],"mappings":"AACA,SACEA,0BAA0B,EAC1BC,6BAA6B,QACxB,6BAA6B;AACpC,SAAQC,uBAAuB,EAAEC,uBAAuB,QAAO,wBAAwB;AACvF,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,MAAM,QAAO,8BAA8B;AACnD,SAAQC,2BAA2B,EAAEC,gBAAgB,QAAO,eAAe;AAC3E,SAAQC,gBAAgB,QAAO,6BAA6B;AAC5D,SAAQC,oBAAoB,QAAO,sBAAsB;AAEzD,OAAO,SAASC,cAAcC,QAA8B;IAC1D,OAAOP,YAAY;QACjBQ,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,aAAajB;YACbkB,UAAU;gBACR,KAAKjB;YACP;QACF;QACAkB,SAAS,OAAOC;YACd,MAAMC,YAAYC,YAAYC,GAAG;YACjC,MAAM,EACJC,YAAYC,SAAS,EACrBC,KAAK,EACLC,MAAM,EACNC,MAAM,EACNC,eAAeC,YAAY,EAC3BC,gBAAgBC,aAAa,EAC7BC,cAAcC,WAAW,EACzBC,YAAYC,SAAS,EACtB,GAAGhB,QAAQiB,KAAK;YACjB,MAAMC,gBAAgBpC,wBAAwByB;YAC9C,IAAIA,UAAU,CAACW,eAAe;gBAC5B,MAAM,IAAIlC,YAAY,kBAAkB,kBAAkB;oBAACwB,QAAQ;gBAAG;YACxE;YAEA,MAAMW,UAAU,MAAM7B,qBAAqBU,SAASK,WAAWb;YAE/D,MAAM4B,UAAU;gBACdZ;gBACAE;gBACAE;gBACAE,aAAaA,cAAc,IAAIO,KAAKP,eAAeQ;gBACnDN,WAAWA,YAAY,IAAIK,KAAKL,aAAaM;YAC/C;YACA,MAAMC,SAAS,MAAMnC,iBAAiB;gBACpCiB,WAAWc,QAAQK,EAAE;gBACrBlB;gBACAC,QAAQW;gBACRE;gBACAK,cAAc,CAACP;YACjB;YACA,qFAAqF;YACrF,mDAAmD;YACnD,MAAMQ,YAAY,MAAMvC,4BACtBoC,OAAOI,IAAI,CAACC,GAAG,CAAC,CAACC,MAAS,CAAA;oBAACL,IAAIK,IAAIL,EAAE;oBAAEM,gBAAgBD,IAAIC,cAAc;gBAAA,CAAA;YAG3E5C,SAAS6C,IAAI,CACX;gBACE1B,WAAWc,QAAQK,EAAE;gBACrBQ,YAAYC,OAAOC,OAAO,CAACd,SACxBe,MAAM,CAAC,CAAC,GAAGC,MAAM,GAAKA,UAAUd,WAChCM,GAAG,CAAC,CAAC,CAACS,IAAI,GAAKA;gBAClB/B;gBACAgC,eAAeC,QAAQhC;gBACvBiC,aAAajB,OAAOI,IAAI,CAACc,MAAM;gBAC/BC,mBAAmBH,QAAQhB,OAAOoB,UAAU;gBAC5CC,YAAYC,KAAKC,KAAK,CAAC5C,YAAYC,GAAG,KAAKF;YAC7C,GACA;YAGF,OAAO;gBACL0B,MAAMJ,OAAOI,IAAI,CAACC,GAAG,CAAC,CAACC,MAAQxC,iBAAiBwC,KAAKH,UAAUqB,GAAG,CAAClB,IAAIL,EAAE;gBACzEwB,aAAazB,OAAOoB,UAAU,GAAG5D,wBAAwBwC,OAAOoB,UAAU,IAAI;gBAC9EM,sBAAsB1B,OAAO2B,kBAAkB;YACjD;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/list-runs.ts"],"sourcesContent":["import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport {\n workflowRunListQuerySchema,\n workflowRunListResponseSchema,\n} from '@shipfox/api-workflows-dto';\nimport {decodeTimestampIdCursor, encodeTimestampIdCursor} from '@shipfox/node-drizzle';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {listWorkflowRunJobSummaries, listWorkflowRuns} from '#db/index.js';\nimport {toRunListItemDto} from '#presentation/dto/index.js';\nimport {requireProjectAccess} from './project-access.js';\n\nexport function listRunsRoute(projects: ProjectsModuleClient) {\n return defineRoute({\n method: 'GET',\n path: '/',\n description: 'List workflow runs for a project',\n schema: {\n querystring: workflowRunListQuerySchema,\n response: {\n 200: workflowRunListResponseSchema,\n },\n },\n handler: async (request) => {\n const startedAt = performance.now();\n const {\n project_id: projectId,\n limit,\n cursor,\n status,\n definition_id: definitionId,\n trigger_source: triggerSource,\n origin,\n created_from: createdFrom,\n created_to: createdTo,\n } = request.query;\n const decodedCursor = decodeTimestampIdCursor(cursor);\n if (cursor && !decodedCursor) {\n throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});\n }\n\n const project = await requireProjectAccess(request, projectId, projects);\n\n const filters = {\n status,\n definitionId,\n triggerSource,\n origin,\n createdFrom: createdFrom ? new Date(createdFrom) : undefined,\n createdTo: createdTo ? new Date(createdTo) : undefined,\n };\n const result = await listWorkflowRuns({\n projectId: project.id,\n limit,\n cursor: decodedCursor,\n filters,\n includeTotal: !decodedCursor,\n });\n // Paired with the attempt this read returned, so a re-run landing mid-request cannot\n // pair one attempt's metadata with another's jobs.\n const jobsByRun = await listWorkflowRunJobSummaries(\n result.runs.map((run) => ({id: run.id, currentAttempt: run.currentAttempt})),\n );\n\n logger().info(\n {\n projectId: project.id,\n filterKeys: Object.entries(filters)\n .filter(([, value]) => value !== undefined)\n .map(([key]) => key),\n limit,\n cursorPresent: Boolean(cursor),\n resultCount: result.runs.length,\n nextCursorPresent: Boolean(result.nextCursor),\n durationMs: Math.round(performance.now() - startedAt),\n },\n 'Listed workflow runs',\n );\n\n return {\n runs: result.runs.map((run) => toRunListItemDto(run, jobsByRun.get(run.id))),\n next_cursor: result.nextCursor ? encodeTimestampIdCursor(result.nextCursor) : null,\n filtered_total_count: result.filteredTotalCount,\n };\n },\n });\n}\n"],"names":["workflowRunListQuerySchema","workflowRunListResponseSchema","decodeTimestampIdCursor","encodeTimestampIdCursor","ClientError","defineRoute","logger","listWorkflowRunJobSummaries","listWorkflowRuns","toRunListItemDto","requireProjectAccess","listRunsRoute","projects","method","path","description","schema","querystring","response","handler","request","startedAt","performance","now","project_id","projectId","limit","cursor","status","definition_id","definitionId","trigger_source","triggerSource","origin","created_from","createdFrom","created_to","createdTo","query","decodedCursor","project","filters","Date","undefined","result","id","includeTotal","jobsByRun","runs","map","run","currentAttempt","info","filterKeys","Object","entries","filter","value","key","cursorPresent","Boolean","resultCount","length","nextCursorPresent","nextCursor","durationMs","Math","round","get","next_cursor","filtered_total_count","filteredTotalCount"],"mappings":"AACA,SACEA,0BAA0B,EAC1BC,6BAA6B,QACxB,6BAA6B;AACpC,SAAQC,uBAAuB,EAAEC,uBAAuB,QAAO,wBAAwB;AACvF,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,MAAM,QAAO,8BAA8B;AACnD,SAAQC,2BAA2B,EAAEC,gBAAgB,QAAO,eAAe;AAC3E,SAAQC,gBAAgB,QAAO,6BAA6B;AAC5D,SAAQC,oBAAoB,QAAO,sBAAsB;AAEzD,OAAO,SAASC,cAAcC,QAA8B;IAC1D,OAAOP,YAAY;QACjBQ,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,aAAajB;YACbkB,UAAU;gBACR,KAAKjB;YACP;QACF;QACAkB,SAAS,OAAOC;YACd,MAAMC,YAAYC,YAAYC,GAAG;YACjC,MAAM,EACJC,YAAYC,SAAS,EACrBC,KAAK,EACLC,MAAM,EACNC,MAAM,EACNC,eAAeC,YAAY,EAC3BC,gBAAgBC,aAAa,EAC7BC,MAAM,EACNC,cAAcC,WAAW,EACzBC,YAAYC,SAAS,EACtB,GAAGjB,QAAQkB,KAAK;YACjB,MAAMC,gBAAgBrC,wBAAwByB;YAC9C,IAAIA,UAAU,CAACY,eAAe;gBAC5B,MAAM,IAAInC,YAAY,kBAAkB,kBAAkB;oBAACwB,QAAQ;gBAAG;YACxE;YAEA,MAAMY,UAAU,MAAM9B,qBAAqBU,SAASK,WAAWb;YAE/D,MAAM6B,UAAU;gBACdb;gBACAE;gBACAE;gBACAC;gBACAE,aAAaA,cAAc,IAAIO,KAAKP,eAAeQ;gBACnDN,WAAWA,YAAY,IAAIK,KAAKL,aAAaM;YAC/C;YACA,MAAMC,SAAS,MAAMpC,iBAAiB;gBACpCiB,WAAWe,QAAQK,EAAE;gBACrBnB;gBACAC,QAAQY;gBACRE;gBACAK,cAAc,CAACP;YACjB;YACA,qFAAqF;YACrF,mDAAmD;YACnD,MAAMQ,YAAY,MAAMxC,4BACtBqC,OAAOI,IAAI,CAACC,GAAG,CAAC,CAACC,MAAS,CAAA;oBAACL,IAAIK,IAAIL,EAAE;oBAAEM,gBAAgBD,IAAIC,cAAc;gBAAA,CAAA;YAG3E7C,SAAS8C,IAAI,CACX;gBACE3B,WAAWe,QAAQK,EAAE;gBACrBQ,YAAYC,OAAOC,OAAO,CAACd,SACxBe,MAAM,CAAC,CAAC,GAAGC,MAAM,GAAKA,UAAUd,WAChCM,GAAG,CAAC,CAAC,CAACS,IAAI,GAAKA;gBAClBhC;gBACAiC,eAAeC,QAAQjC;gBACvBkC,aAAajB,OAAOI,IAAI,CAACc,MAAM;gBAC/BC,mBAAmBH,QAAQhB,OAAOoB,UAAU;gBAC5CC,YAAYC,KAAKC,KAAK,CAAC7C,YAAYC,GAAG,KAAKF;YAC7C,GACA;YAGF,OAAO;gBACL2B,MAAMJ,OAAOI,IAAI,CAACC,GAAG,CAAC,CAACC,MAAQzC,iBAAiByC,KAAKH,UAAUqB,GAAG,CAAClB,IAAIL,EAAE;gBACzEwB,aAAazB,OAAOoB,UAAU,GAAG7D,wBAAwByC,OAAOoB,UAAU,IAAI;gBAC9EM,sBAAsB1B,OAAO2B,kBAAkB;YACjD;QACF;IACF;AACF"}
@@ -6,6 +6,7 @@ export declare function requireProjectAccess(request: FastifyRequest, projectId:
6
6
  sourceConnectionId: string;
7
7
  sourceExternalRepositoryId: string;
8
8
  sourceRepositoryOwner?: string | null | undefined;
9
+ sourceDefaultBranch?: string | null | undefined;
9
10
  name: string;
10
11
  }>;
11
12
  //# sourceMappingURL=project-access.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"project-access.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/project-access.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wCAAwC,CAAC;AAEjF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAE5C,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,oBAAoB;;;;;;;GAO/B"}
1
+ {"version":3,"file":"project-access.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/project-access.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wCAAwC,CAAC;AAEjF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAE5C,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,oBAAoB;;;;;;;;GAO/B"}