@shipfox/client-workflows 18.0.0 → 20.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 (62) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +25 -0
  3. package/dist/components/workflow-run-duration-label.d.ts +8 -7
  4. package/dist/components/workflow-run-duration-label.d.ts.map +1 -1
  5. package/dist/components/workflow-run-duration-label.js +15 -31
  6. package/dist/components/workflow-run-duration-label.js.map +1 -1
  7. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
  8. package/dist/components/workflow-run-list/job-status-strip.js +20 -7
  9. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
  10. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  11. package/dist/components/workflow-run-list/workflow-run-row.js +9 -6
  12. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  13. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  14. package/dist/components/workflow-run-summary/workflow-run-summary.js +7 -34
  15. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  16. package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
  17. package/dist/components/workflow-status/status-visuals.js +0 -9
  18. package/dist/components/workflow-status/status-visuals.js.map +1 -1
  19. package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
  20. package/dist/components/workflow-status/workflow-status-icon.js +0 -11
  21. package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
  22. package/dist/core/entities/job.d.ts +5 -2
  23. package/dist/core/entities/job.d.ts.map +1 -1
  24. package/dist/core/entities/job.js +8 -1
  25. package/dist/core/entities/job.js.map +1 -1
  26. package/dist/core/entities/workflow-run.d.ts +15 -57
  27. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  28. package/dist/core/entities/workflow-run.js +1 -94
  29. package/dist/core/entities/workflow-run.js.map +1 -1
  30. package/dist/core/workflow-run.d.ts +2 -2
  31. package/dist/core/workflow-run.d.ts.map +1 -1
  32. package/dist/core/workflow-run.js +1 -1
  33. package/dist/core/workflow-run.js.map +1 -1
  34. package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
  35. package/dist/hooks/api/workflow-run-mapper.js +14 -2
  36. package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
  37. package/dist/hooks/api/workflow-runs.js +1 -0
  38. package/dist/hooks/api/workflow-runs.js.map +1 -1
  39. package/dist/tsconfig.test.tsbuildinfo +1 -1
  40. package/package.json +2 -2
  41. package/src/components/job-graph/job-node.test.tsx +2 -2
  42. package/src/components/workflow-run-duration-label.tsx +21 -44
  43. package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
  44. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +121 -0
  45. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +31 -0
  46. package/src/components/workflow-run-list/workflow-run-row.tsx +7 -6
  47. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +0 -38
  48. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +73 -21
  49. package/src/components/workflow-run-summary/workflow-run-summary.tsx +6 -27
  50. package/src/components/workflow-status/status-visuals.ts +0 -4
  51. package/src/components/workflow-status/workflow-status-icon.stories.tsx +0 -1
  52. package/src/components/workflow-status/workflow-status-icon.tsx +0 -18
  53. package/src/core/entities/job-status.test.ts +47 -1
  54. package/src/core/entities/job.ts +15 -3
  55. package/src/core/entities/workflow-run.ts +21 -134
  56. package/src/core/workflow-run.test.ts +15 -0
  57. package/src/core/workflow-run.ts +0 -8
  58. package/src/hooks/api/workflow-run-mapper.ts +20 -1
  59. package/src/hooks/api/workflow-runs.ts +1 -1
  60. package/test/fixtures/workflow-run.ts +60 -13
  61. package/tsconfig.build.tsbuildinfo +1 -1
  62. package/src/core/entities/workflow-run-display.test.ts +0 -159
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {MetadataSeparator} from '@shipfox/client-ui';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport {WorkflowRunNumberLabel} from '#components/workflow-run-number-label.js';\nimport {\n isWorkflowRunTerminal,\n type Job,\n WORKFLOW_RUN_STATUSES,\n type WorkflowRunDetail,\n type WorkflowRunRerunMode,\n workflowRunBlockingJob,\n workflowRunDetailDisplay,\n} from '#core/workflow-run.js';\nimport {WorkflowRunDurationLabel} from '../workflow-run-duration-label.js';\nimport {getWorkflowStatusVisual} from '../workflow-status/status-visuals.js';\nimport {WorkflowRunAttemptSwitcher} from './workflow-run-attempt-switcher.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...[...WORKFLOW_RUN_STATUSES, 'queued' as const].map(\n (status) => getWorkflowStatusVisual(status).label.length,\n ),\n);\nconst RERUN_BUTTON_SURFACE_CLASS_NAME =\n 'bg-background-neutral-base hover:bg-background-neutral-hover active:bg-background-neutral-pressed';\n\ntype WorkflowRunAction = 'cancel' | 'rerun-all' | 'rerun-menu' | 'none';\n\nexport interface WorkflowRunSummaryProps {\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n run: WorkflowRunDetail;\n cancelling?: boolean | undefined;\n onCancel?: (() => void) | undefined;\n rerunPending?: boolean | undefined;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n latestAttempt?: number | undefined;\n}\n\nexport function WorkflowRunSummary({\n workspaceSlug,\n projectSlug,\n run,\n cancelling = false,\n onCancel,\n rerunPending = false,\n onRerun,\n latestAttempt,\n}: WorkflowRunSummaryProps) {\n const headingId = useId();\n const display = workflowRunDetailDisplay(run);\n const status = getWorkflowStatusVisual(display.status);\n const action = workflowRunActionForRun(run);\n const hasAction = canRenderWorkflowRunAction(action, onCancel, onRerun);\n const attemptSwitcher =\n latestAttempt && latestAttempt > 1 && workspaceSlug && projectSlug\n ? {workspaceSlug, projectSlug, latestAttempt}\n : null;\n const displayDuration = display.duration;\n // Named only while the run is waiting and has more than one job: with a single job the rail\n // and the graph already say which one it is.\n const blockingJob =\n display.status === 'queued' && run.jobs.length > 1 ? workflowRunBlockingJob(run.jobs) : null;\n const {ref: headingTextRef, isTruncated: isHeadingTruncated} =\n useIsTextTruncated<HTMLSpanElement>(run.name);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={headingId}\n className=\"bg-background-neutral-background px-row py-row\"\n >\n <div className=\"grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-cluster gap-y-inline overflow-hidden max-[480px]:grid-cols-1\">\n <div className=\"col-start-1 row-start-1 min-w-0 max-[480px]:col-start-auto max-[480px]:row-start-auto\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Badge variant={status.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {status.label}\n </span>\n </Badge>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Header as=\"h1\" id={headingId} variant=\"h3\" className=\"min-w-0 truncate\">\n <span ref={headingTextRef} className=\"block min-w-0 truncate\">\n {run.name}\n </span>\n </Header>\n </TooltipTrigger>\n {isHeadingTruncated ? (\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"max-w-[360px] break-words\">\n {run.name}\n </Text>\n </TooltipContent>\n ) : null}\n </Tooltip>\n </div>\n </div>\n\n {hasAction ? (\n <div className=\"col-start-2 row-start-1 flex min-w-max items-center gap-inline justify-self-end max-[480px]:col-start-auto max-[480px]:row-start-auto max-[480px]:justify-self-start\">\n <WorkflowRunActionSlot\n action={action}\n cancelling={cancelling}\n onCancel={onCancel}\n rerunPending={rerunPending}\n onRerun={onRerun}\n />\n </div>\n ) : null}\n\n <div className=\"col-span-2 row-start-2 flex min-w-0 items-center gap-cluster overflow-hidden text-foreground-neutral-subtle max-[480px]:col-span-1 max-[480px]:row-start-auto\">\n {run.number !== null ? (\n <>\n <WorkflowRunNumberLabel run={run} />\n {attemptSwitcher || run.triggerDisplayLabel ? <MetadataSeparator /> : null}\n </>\n ) : null}\n\n {attemptSwitcher ? (\n <WorkflowRunAttemptSwitcher\n workspaceSlug={attemptSwitcher.workspaceSlug}\n projectSlug={attemptSwitcher.projectSlug}\n run={run}\n latestAttempt={attemptSwitcher.latestAttempt}\n />\n ) : null}\n\n {run.triggerDisplayLabel ? (\n <>\n {attemptSwitcher ? <MetadataSeparator /> : null}\n <span className=\"min-w-0\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={run.triggerLabel}\n className=\"inline-flex max-w-full min-w-0 cursor-help items-center gap-tight rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-subtle outline-none focus-visible:shadow-button-neutral-focus\"\n >\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n </span>\n </>\n ) : null}\n\n {run.number !== null || attemptSwitcher || run.triggerDisplayLabel ? (\n <MetadataSeparator />\n ) : null}\n <RelativeTime\n value={run.runAttempt.createdAt}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-subtle\"\n />\n\n {displayDuration ? (\n <>\n <MetadataSeparator />\n <WorkflowRunDurationLabel\n duration={displayDuration}\n className=\"text-foreground-neutral-subtle\"\n />\n </>\n ) : null}\n\n {blockingJob ? (\n <>\n <MetadataSeparator />\n <span className=\"flex min-w-0 items-center gap-tight\">\n <Text as=\"span\" size=\"xs\" className=\"shrink-0\">\n waiting on\n </Text>\n <Code as=\"span\" variant=\"label\" className=\"min-w-0 truncate\">\n {blockingJob.displayName}\n </Code>\n </span>\n </>\n ) : null}\n </div>\n </div>\n </section>\n </TimeTickerProvider>\n );\n}\n\nfunction WorkflowRunActionSlot({\n action,\n cancelling,\n onCancel,\n rerunPending,\n onRerun,\n}: {\n action: WorkflowRunAction;\n cancelling: boolean;\n onCancel?: (() => void) | undefined;\n rerunPending: boolean;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n}) {\n if (action === 'none') return null;\n\n if (action === 'cancel') {\n if (!onCancel) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"danger\"\n size=\"xs\"\n isLoading={cancelling}\n disabled={cancelling}\n onClick={onCancel}\n >\n Cancel workflow\n </Button>\n );\n }\n\n if (action === 'rerun-all') {\n if (!onRerun) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n className={RERUN_BUTTON_SURFACE_CLASS_NAME}\n isLoading={rerunPending}\n disabled={rerunPending}\n onClick={() => onRerun('all')}\n >\n Re-run workflow\n </Button>\n );\n }\n\n if (!onRerun) return null;\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n className={RERUN_BUTTON_SURFACE_CLASS_NAME}\n iconRight=\"arrowDownSLine\"\n isLoading={rerunPending}\n disabled={rerunPending}\n >\n Re-run jobs\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('all')}>\n Re-run all jobs\n </DropdownMenuItem>\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('failed')}>\n Re-run failed jobs\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction workflowRunActionForRun(run: WorkflowRunDetail): WorkflowRunAction {\n if (run.runAttempt.attempt !== run.currentAttempt) return 'none';\n if (!isWorkflowRunTerminal(run.runAttempt.status)) return 'cancel';\n if (run.runAttempt.status === 'succeeded' || !hasFailedOrCancelledJobs(run)) return 'rerun-all';\n return 'rerun-menu';\n}\n\nfunction canRenderWorkflowRunAction(\n action: WorkflowRunAction,\n onCancel: (() => void) | undefined,\n onRerun: ((mode: WorkflowRunRerunMode) => void) | undefined,\n): boolean {\n if (action === 'cancel') return onCancel !== undefined;\n if (action === 'rerun-all' || action === 'rerun-menu') return onRerun !== undefined;\n return false;\n}\n\nfunction hasFailedOrCancelledJobs(run: WorkflowRunDetail): boolean {\n if (!workflowRunHasJobs(run)) return false;\n\n return run.jobs.some((job) => job.status === 'failed' || job.status === 'cancelled');\n}\n\nfunction workflowRunHasJobs(run: WorkflowRunDetail): run is WorkflowRunDetail & {jobs: Job[]} {\n return 'jobs' in run && Array.isArray(run.jobs);\n}\n"],"names":["TriggerSourceIcon","MetadataSeparator","Badge","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","useIsTextTruncated","RelativeTime","TimeTickerProvider","Tooltip","TooltipContent","TooltipTrigger","Code","Header","Text","useId","WorkflowRunNumberLabel","isWorkflowRunTerminal","WORKFLOW_RUN_STATUSES","workflowRunBlockingJob","workflowRunDetailDisplay","WorkflowRunDurationLabel","getWorkflowStatusVisual","WorkflowRunAttemptSwitcher","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","RERUN_BUTTON_SURFACE_CLASS_NAME","WorkflowRunSummary","workspaceSlug","projectSlug","run","cancelling","onCancel","rerunPending","onRerun","latestAttempt","headingId","display","action","workflowRunActionForRun","hasAction","canRenderWorkflowRunAction","attemptSwitcher","displayDuration","duration","blockingJob","jobs","ref","headingTextRef","isTruncated","isHeadingTruncated","name","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","className","div","variant","badge","size","span","style","width","asChild","as","id","WorkflowRunActionSlot","number","triggerDisplayLabel","button","type","aria-label","triggerLabel","provider","triggerProvider","source","triggerSource","aria-hidden","value","runAttempt","createdAt","displayName","isLoading","disabled","onClick","iconRight","align","onSelect","attempt","currentAttempt","hasFailedOrCancelledJobs","undefined","workflowRunHasJobs","some","job","Array","isArray"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {MetadataSeparator} from '@shipfox/client-ui';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport {WorkflowRunNumberLabel} from '#components/workflow-run-number-label.js';\nimport {\n isWorkflowRunTerminal,\n type Job,\n WORKFLOW_RUN_STATUSES,\n type WorkflowRunDetail,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport {WorkflowRunDurationLabel} from '../workflow-run-duration-label.js';\nimport {getWorkflowStatusVisual} from '../workflow-status/status-visuals.js';\nimport {WorkflowRunAttemptSwitcher} from './workflow-run-attempt-switcher.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...WORKFLOW_RUN_STATUSES.map((status) => getWorkflowStatusVisual(status).label.length),\n);\nconst RERUN_BUTTON_SURFACE_CLASS_NAME =\n 'bg-background-neutral-base hover:bg-background-neutral-hover active:bg-background-neutral-pressed';\n\ntype WorkflowRunAction = 'cancel' | 'rerun-all' | 'rerun-menu' | 'none';\n\nexport interface WorkflowRunSummaryProps {\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n run: WorkflowRunDetail;\n cancelling?: boolean | undefined;\n onCancel?: (() => void) | undefined;\n rerunPending?: boolean | undefined;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n latestAttempt?: number | undefined;\n}\n\nexport function WorkflowRunSummary({\n workspaceSlug,\n projectSlug,\n run,\n cancelling = false,\n onCancel,\n rerunPending = false,\n onRerun,\n latestAttempt,\n}: WorkflowRunSummaryProps) {\n const headingId = useId();\n const status = getWorkflowStatusVisual(run.runAttempt.status);\n const action = workflowRunActionForRun(run);\n const hasAction = canRenderWorkflowRunAction(action, onCancel, onRerun);\n const attemptSwitcher =\n latestAttempt && latestAttempt > 1 && workspaceSlug && projectSlug\n ? {workspaceSlug, projectSlug, latestAttempt}\n : null;\n const displayDuration = run.runAttempt.displayDuration;\n const hasStarted = run.hasStartedJobExecution;\n const {ref: headingTextRef, isTruncated: isHeadingTruncated} =\n useIsTextTruncated<HTMLSpanElement>(run.name);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={headingId}\n className=\"bg-background-neutral-background px-row py-row\"\n >\n <div className=\"grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-cluster gap-y-inline overflow-hidden max-[480px]:grid-cols-1\">\n <div className=\"col-start-1 row-start-1 min-w-0 max-[480px]:col-start-auto max-[480px]:row-start-auto\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Badge variant={status.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {status.label}\n </span>\n </Badge>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Header as=\"h1\" id={headingId} variant=\"h3\" className=\"min-w-0 truncate\">\n <span ref={headingTextRef} className=\"block min-w-0 truncate\">\n {run.name}\n </span>\n </Header>\n </TooltipTrigger>\n {isHeadingTruncated ? (\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"max-w-[360px] break-words\">\n {run.name}\n </Text>\n </TooltipContent>\n ) : null}\n </Tooltip>\n </div>\n </div>\n\n {hasAction ? (\n <div className=\"col-start-2 row-start-1 flex min-w-max items-center gap-inline justify-self-end max-[480px]:col-start-auto max-[480px]:row-start-auto max-[480px]:justify-self-start\">\n <WorkflowRunActionSlot\n action={action}\n cancelling={cancelling}\n onCancel={onCancel}\n rerunPending={rerunPending}\n onRerun={onRerun}\n />\n </div>\n ) : null}\n\n <div className=\"col-span-2 row-start-2 flex min-w-0 items-center gap-cluster overflow-hidden text-foreground-neutral-subtle max-[480px]:col-span-1 max-[480px]:row-start-auto\">\n {run.number !== null ? (\n <>\n <WorkflowRunNumberLabel run={run} />\n {attemptSwitcher || run.triggerDisplayLabel ? <MetadataSeparator /> : null}\n </>\n ) : null}\n\n {attemptSwitcher ? (\n <WorkflowRunAttemptSwitcher\n workspaceSlug={attemptSwitcher.workspaceSlug}\n projectSlug={attemptSwitcher.projectSlug}\n run={run}\n latestAttempt={attemptSwitcher.latestAttempt}\n />\n ) : null}\n\n {run.triggerDisplayLabel ? (\n <>\n {attemptSwitcher ? <MetadataSeparator /> : null}\n <span className=\"min-w-0\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={run.triggerLabel}\n className=\"inline-flex max-w-full min-w-0 cursor-help items-center gap-tight rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-subtle outline-none focus-visible:shadow-button-neutral-focus\"\n >\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n </span>\n </>\n ) : null}\n\n {run.number !== null || attemptSwitcher || run.triggerDisplayLabel ? (\n <MetadataSeparator />\n ) : null}\n <RelativeTime\n value={run.runAttempt.createdAt}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-subtle\"\n />\n\n {displayDuration ? (\n <>\n <MetadataSeparator />\n <WorkflowRunDurationLabel\n duration={displayDuration}\n hasStarted={hasStarted}\n className=\"text-foreground-neutral-subtle\"\n />\n </>\n ) : null}\n </div>\n </div>\n </section>\n </TimeTickerProvider>\n );\n}\n\nfunction WorkflowRunActionSlot({\n action,\n cancelling,\n onCancel,\n rerunPending,\n onRerun,\n}: {\n action: WorkflowRunAction;\n cancelling: boolean;\n onCancel?: (() => void) | undefined;\n rerunPending: boolean;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n}) {\n if (action === 'none') return null;\n\n if (action === 'cancel') {\n if (!onCancel) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"danger\"\n size=\"xs\"\n isLoading={cancelling}\n disabled={cancelling}\n onClick={onCancel}\n >\n Cancel workflow\n </Button>\n );\n }\n\n if (action === 'rerun-all') {\n if (!onRerun) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n className={RERUN_BUTTON_SURFACE_CLASS_NAME}\n isLoading={rerunPending}\n disabled={rerunPending}\n onClick={() => onRerun('all')}\n >\n Re-run workflow\n </Button>\n );\n }\n\n if (!onRerun) return null;\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n className={RERUN_BUTTON_SURFACE_CLASS_NAME}\n iconRight=\"arrowDownSLine\"\n isLoading={rerunPending}\n disabled={rerunPending}\n >\n Re-run jobs\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('all')}>\n Re-run all jobs\n </DropdownMenuItem>\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('failed')}>\n Re-run failed jobs\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction workflowRunActionForRun(run: WorkflowRunDetail): WorkflowRunAction {\n if (run.runAttempt.attempt !== run.currentAttempt) return 'none';\n if (!isWorkflowRunTerminal(run.runAttempt.status)) return 'cancel';\n if (run.runAttempt.status === 'succeeded' || !hasFailedOrCancelledJobs(run)) return 'rerun-all';\n return 'rerun-menu';\n}\n\nfunction canRenderWorkflowRunAction(\n action: WorkflowRunAction,\n onCancel: (() => void) | undefined,\n onRerun: ((mode: WorkflowRunRerunMode) => void) | undefined,\n): boolean {\n if (action === 'cancel') return onCancel !== undefined;\n if (action === 'rerun-all' || action === 'rerun-menu') return onRerun !== undefined;\n return false;\n}\n\nfunction hasFailedOrCancelledJobs(run: WorkflowRunDetail): boolean {\n if (!workflowRunHasJobs(run)) return false;\n\n return run.jobs.some((job) => job.status === 'failed' || job.status === 'cancelled');\n}\n\nfunction workflowRunHasJobs(run: WorkflowRunDetail): run is WorkflowRunDetail & {jobs: Job[]} {\n return 'jobs' in run && Array.isArray(run.jobs);\n}\n"],"names":["TriggerSourceIcon","MetadataSeparator","Badge","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","useIsTextTruncated","RelativeTime","TimeTickerProvider","Tooltip","TooltipContent","TooltipTrigger","Header","Text","useId","WorkflowRunNumberLabel","isWorkflowRunTerminal","WORKFLOW_RUN_STATUSES","WorkflowRunDurationLabel","getWorkflowStatusVisual","WorkflowRunAttemptSwitcher","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","RERUN_BUTTON_SURFACE_CLASS_NAME","WorkflowRunSummary","workspaceSlug","projectSlug","run","cancelling","onCancel","rerunPending","onRerun","latestAttempt","headingId","runAttempt","action","workflowRunActionForRun","hasAction","canRenderWorkflowRunAction","attemptSwitcher","displayDuration","hasStarted","hasStartedJobExecution","ref","headingTextRef","isTruncated","isHeadingTruncated","name","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","className","div","variant","badge","size","span","style","width","asChild","as","id","WorkflowRunActionSlot","number","triggerDisplayLabel","button","type","aria-label","triggerLabel","provider","triggerProvider","source","triggerSource","aria-hidden","value","createdAt","duration","isLoading","disabled","onClick","iconRight","align","onSelect","attempt","currentAttempt","hasFailedOrCancelledJobs","undefined","workflowRunHasJobs","jobs","some","job","Array","isArray"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"status-visuals.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-status/status-visuals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,uBAAuB,CAAC;AAEjE,YAAY,EAAC,qBAAqB,EAAC,CAAC;AAEpC,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;CACrB;AAKD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,qBAAqB,GAAG,oBAAoB,CAwB3F"}
1
+ {"version":3,"file":"status-visuals.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-status/status-visuals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,uBAAuB,CAAC;AAEjE,YAAY,EAAC,qBAAqB,EAAC,CAAC;AAEpC,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;CACrB;AAKD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,qBAAqB,GAAG,oBAAoB,CAoB3F"}
@@ -10,15 +10,6 @@ export function getWorkflowStatusVisual(status) {
10
10
  dot: 'neutral',
11
11
  badge: 'neutral'
12
12
  };
13
- // Waiting on a runner is the awaiting-* state the warning tone was held for: the run is
14
- // live but nothing is executing, which is the operator's problem to see, not ours to hide.
15
- case 'queued':
16
- return {
17
- kind: 'queued',
18
- label: 'Queued',
19
- dot: 'warning',
20
- badge: 'warning'
21
- };
22
13
  case 'running':
23
14
  return {
24
15
  kind: 'running',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-status/status-visuals.ts"],"sourcesContent":["import type {BadgeVariant} from '@shipfox/react-ui/badge';\nimport type {DotVariant} from '@shipfox/react-ui/dot';\nimport type {WorkflowDisplayStatus} from '#core/workflow-run.js';\n\nexport type {WorkflowDisplayStatus};\n\nexport interface WorkflowStatusVisual {\n kind: WorkflowDisplayStatus;\n label: string;\n dot: DotVariant;\n badge: BadgeVariant;\n}\n\n// The status -> visual mapping shared by the run-header pill (color + label) and\n// WorkflowStatusIcon (which renders the glyph per kind). The exhaustive switch turns any new\n// status the API grows into (DESIGN.md section 9) into a compile error.\nexport function getWorkflowStatusVisual(status: WorkflowDisplayStatus): WorkflowStatusVisual {\n switch (status) {\n case 'pending':\n return {kind: 'pending', label: 'Pending', dot: 'neutral', badge: 'neutral'};\n // Waiting on a runner is the awaiting-* state the warning tone was held for: the run is\n // live but nothing is executing, which is the operator's problem to see, not ours to hide.\n case 'queued':\n return {kind: 'queued', label: 'Queued', dot: 'warning', badge: 'warning'};\n case 'running':\n return {kind: 'running', label: 'Running', dot: 'info', badge: 'info'};\n case 'listening':\n return {kind: 'listening', label: 'Listening', dot: 'info', badge: 'info'};\n case 'succeeded':\n return {kind: 'succeeded', label: 'Succeeded', dot: 'success', badge: 'success'};\n case 'failed':\n return {kind: 'failed', label: 'Failed', dot: 'error', badge: 'error'};\n case 'cancelled':\n return {kind: 'cancelled', label: 'Cancelled', dot: 'neutral', badge: 'neutral'};\n case 'skipped':\n return {kind: 'skipped', label: 'Skipped', dot: 'neutral', badge: 'neutral'};\n }\n\n const exhaustive: never = status;\n return exhaustive;\n}\n"],"names":["getWorkflowStatusVisual","status","kind","label","dot","badge","exhaustive"],"mappings":"AAaA,iFAAiF;AACjF,6FAA6F;AAC7F,wEAAwE;AACxE,OAAO,SAASA,wBAAwBC,MAA6B;IACnE,OAAQA;QACN,KAAK;YACH,OAAO;gBAACC,MAAM;gBAAWC,OAAO;gBAAWC,KAAK;gBAAWC,OAAO;YAAS;QAC7E,wFAAwF;QACxF,2FAA2F;QAC3F,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAUC,OAAO;gBAAUC,KAAK;gBAAWC,OAAO;YAAS;QAC3E,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAWC,OAAO;gBAAWC,KAAK;gBAAQC,OAAO;YAAM;QACvE,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAaC,OAAO;gBAAaC,KAAK;gBAAQC,OAAO;YAAM;QAC3E,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAaC,OAAO;gBAAaC,KAAK;gBAAWC,OAAO;YAAS;QACjF,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAUC,OAAO;gBAAUC,KAAK;gBAASC,OAAO;YAAO;QACvE,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAaC,OAAO;gBAAaC,KAAK;gBAAWC,OAAO;YAAS;QACjF,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAWC,OAAO;gBAAWC,KAAK;gBAAWC,OAAO;YAAS;IAC/E;IAEA,MAAMC,aAAoBL;IAC1B,OAAOK;AACT"}
1
+ {"version":3,"sources":["../../../src/components/workflow-status/status-visuals.ts"],"sourcesContent":["import type {BadgeVariant} from '@shipfox/react-ui/badge';\nimport type {DotVariant} from '@shipfox/react-ui/dot';\nimport type {WorkflowDisplayStatus} from '#core/workflow-run.js';\n\nexport type {WorkflowDisplayStatus};\n\nexport interface WorkflowStatusVisual {\n kind: WorkflowDisplayStatus;\n label: string;\n dot: DotVariant;\n badge: BadgeVariant;\n}\n\n// The status -> visual mapping shared by the run-header pill (color + label) and\n// WorkflowStatusIcon (which renders the glyph per kind). The exhaustive switch turns any new\n// status the API grows into (DESIGN.md section 9) into a compile error.\nexport function getWorkflowStatusVisual(status: WorkflowDisplayStatus): WorkflowStatusVisual {\n switch (status) {\n case 'pending':\n return {kind: 'pending', label: 'Pending', dot: 'neutral', badge: 'neutral'};\n case 'running':\n return {kind: 'running', label: 'Running', dot: 'info', badge: 'info'};\n case 'listening':\n return {kind: 'listening', label: 'Listening', dot: 'info', badge: 'info'};\n case 'succeeded':\n return {kind: 'succeeded', label: 'Succeeded', dot: 'success', badge: 'success'};\n case 'failed':\n return {kind: 'failed', label: 'Failed', dot: 'error', badge: 'error'};\n case 'cancelled':\n return {kind: 'cancelled', label: 'Cancelled', dot: 'neutral', badge: 'neutral'};\n case 'skipped':\n return {kind: 'skipped', label: 'Skipped', dot: 'neutral', badge: 'neutral'};\n }\n\n const exhaustive: never = status;\n return exhaustive;\n}\n"],"names":["getWorkflowStatusVisual","status","kind","label","dot","badge","exhaustive"],"mappings":"AAaA,iFAAiF;AACjF,6FAA6F;AAC7F,wEAAwE;AACxE,OAAO,SAASA,wBAAwBC,MAA6B;IACnE,OAAQA;QACN,KAAK;YACH,OAAO;gBAACC,MAAM;gBAAWC,OAAO;gBAAWC,KAAK;gBAAWC,OAAO;YAAS;QAC7E,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAWC,OAAO;gBAAWC,KAAK;gBAAQC,OAAO;YAAM;QACvE,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAaC,OAAO;gBAAaC,KAAK;gBAAQC,OAAO;YAAM;QAC3E,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAaC,OAAO;gBAAaC,KAAK;gBAAWC,OAAO;YAAS;QACjF,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAUC,OAAO;gBAAUC,KAAK;gBAASC,OAAO;YAAO;QACvE,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAaC,OAAO;gBAAaC,KAAK;gBAAWC,OAAO;YAAS;QACjF,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAWC,OAAO;gBAAWC,KAAK;gBAAWC,OAAO;YAAS;IAC/E;IAEA,MAAMC,aAAoBL;IAC1B,OAAOK;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-status-icon.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-status/workflow-status-icon.tsx"],"names":[],"mappings":"AAKA,OAAO,EAA0B,KAAK,qBAAqB,EAAC,MAAM,qBAAqB,CAAC;AAyCxF,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,SAAS,EACT,IAAS,EACT,MAAa,EACb,OAAc,EACd,SAAS,GACV,EAAE,uBAAuB,+BA8EzB"}
1
+ {"version":3,"file":"workflow-status-icon.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-status/workflow-status-icon.tsx"],"names":[],"mappings":"AAKA,OAAO,EAA0B,KAAK,qBAAqB,EAAC,MAAM,qBAAqB,CAAC;AAyCxF,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,SAAS,EACT,IAAS,EACT,MAAa,EACb,OAAc,EACd,SAAS,GACV,EAAE,uBAAuB,+BA4DzB"}
@@ -55,17 +55,6 @@ const PENDING_RING_MASK = 'radial-gradient(circle closest-side, transparent 0 52
55
55
  className: "text-white"
56
56
  })
57
57
  });
58
- } else if (visual.kind === 'queued') {
59
- // An hourglass in the same filled disc the listening state uses, so waiting reads as its
60
- // own shape rather than as a pending ring that happens to be a different colour.
61
- glyph = /*#__PURE__*/ _jsx("span", {
62
- className: cn('inline-flex shrink-0 items-center justify-center rounded-full bg-current', box, toneByVariant.warning),
63
- children: /*#__PURE__*/ _jsx(Icon, {
64
- name: "hourglassLine",
65
- size: Math.max(8, Math.round(size * 0.7)),
66
- className: "text-white"
67
- })
68
- });
69
58
  } else if (visual.kind === 'running') {
70
59
  glyph = /*#__PURE__*/ _jsx(Dot, {
71
60
  variant: "info",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-status/workflow-status-icon.tsx"],"sourcesContent":["import {Dot, type DotVariant} from '@shipfox/react-ui/dot';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {getWorkflowStatusVisual, type WorkflowDisplayStatus} from './status-visuals.js';\n\n// Status glyphs use the same saturated accent tokens as IconBadge/StatusBadge.\nconst toneByVariant: Record<DotVariant, string> = {\n neutral: 'text-tag-neutral-icon',\n info: 'text-tag-blue-icon',\n feature: 'text-tag-purple-icon',\n success: 'text-tag-success-icon',\n warning: 'text-tag-warning-icon',\n error: 'text-tag-error-icon',\n};\n\n// Terminal states are self-contained solid discs; the shape names the state. Running and\n// pending render their own shapes (the live Dot and a bold ring), so they aren't here.\nconst glyphByKind: Partial<Record<WorkflowDisplayStatus, IconName>> = {\n succeeded: 'checkCircleSolid',\n failed: 'xCircleSolid',\n cancelled: 'forbid2Fill',\n skipped: 'forbid2Fill',\n};\n\n// The solid discs fill ~90-95% of their viewBox and forbid2Fill ~83%, so one numeric size\n// renders different diameters. These multipliers land every disc at the running Dot's diameter.\nconst glyphScale: Partial<Record<IconName, number>> = {\n checkCircleSolid: 1.12,\n xCircleSolid: 1.06,\n forbid2Fill: 1.22,\n};\n\n// Only the two sizes the app uses are mapped; both are literal strings so Tailwind emits them.\nconst dotSizeClass: Record<number, string> = {\n 12: 'size-12',\n 14: 'size-14',\n 20: 'size-20',\n};\n\n// Pending is a neutral ring: a filled disc with a transparent center punched out by a radial\n// mask. Reads as \"not started\" and stays visually consistent with the solid discs - much\n// bolder than a dotted outline. Tune the mask stops to make the band thicker or thinner.\nconst PENDING_RING_MASK = 'radial-gradient(circle closest-side, transparent 0 52%, #000 56%)';\n\nexport interface WorkflowStatusIconProps {\n status: WorkflowDisplayStatus;\n /** Accessible status label when the surrounding surface needs to name its scope. */\n ariaLabel?: string | undefined;\n /** Optical diameter in px: 14 in the DAG node and run row. */\n size?: number;\n /** Pulsing halo for the running state. */\n ripple?: boolean;\n /**\n * Wrap the indicator in a hover tooltip naming the status. Defaults on. Pass false where\n * the status text is already visible next to it (the DAG node) to avoid a redundant tooltip.\n */\n tooltip?: boolean;\n className?: string;\n}\n\n/**\n * The job/run/step state indicator: an icon-in-circle glyph whose shape (not just color)\n * names the state. Carries the status as its accessible name (`role=\"img\"`) and, by default,\n * a hover tooltip, so the state is reachable by pointer and assistive tech everywhere.\n */\nexport function WorkflowStatusIcon({\n status,\n ariaLabel,\n size = 14,\n ripple = true,\n tooltip = true,\n className,\n}: WorkflowStatusIconProps) {\n const visual = getWorkflowStatusVisual(status);\n const box = dotSizeClass[size] ?? 'size-14';\n\n let glyph: ReactNode;\n if (visual.kind === 'listening') {\n glyph = (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center rounded-full bg-current',\n box,\n toneByVariant.info,\n )}\n >\n <Icon name=\"pulseLine\" size={Math.max(8, Math.round(size * 0.7))} className=\"text-white\" />\n </span>\n );\n } else if (visual.kind === 'queued') {\n // An hourglass in the same filled disc the listening state uses, so waiting reads as its\n // own shape rather than as a pending ring that happens to be a different colour.\n glyph = (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center rounded-full bg-current',\n box,\n toneByVariant.warning,\n )}\n >\n <Icon\n name=\"hourglassLine\"\n size={Math.max(8, Math.round(size * 0.7))}\n className=\"text-white\"\n />\n </span>\n );\n } else if (visual.kind === 'running') {\n glyph = <Dot variant=\"info\" ripple={ripple} className={box} />;\n } else if (visual.kind === 'pending') {\n glyph = (\n <span\n className={cn('rounded-full bg-current', box, toneByVariant.neutral)}\n style={{maskImage: PENDING_RING_MASK, WebkitMaskImage: PENDING_RING_MASK}}\n />\n );\n } else {\n const name = glyphByKind[visual.kind] ?? 'forbid2Fill';\n glyph = (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center',\n box,\n toneByVariant[visual.dot],\n (visual.kind === 'cancelled' || visual.kind === 'skipped') && 'opacity-70',\n )}\n >\n <Icon name={name} size={Math.round(size * (glyphScale[name] ?? 1))} />\n </span>\n );\n }\n\n const indicator = (\n <span\n role=\"img\"\n aria-label={ariaLabel ?? visual.label}\n className={cn('inline-flex shrink-0', className)}\n >\n {glyph}\n </span>\n );\n\n if (!tooltip) return indicator;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>{indicator}</TooltipTrigger>\n <TooltipContent>{visual.label}</TooltipContent>\n </Tooltip>\n );\n}\n"],"names":["Dot","Icon","Tooltip","TooltipContent","TooltipTrigger","cn","getWorkflowStatusVisual","toneByVariant","neutral","info","feature","success","warning","error","glyphByKind","succeeded","failed","cancelled","skipped","glyphScale","checkCircleSolid","xCircleSolid","forbid2Fill","dotSizeClass","PENDING_RING_MASK","WorkflowStatusIcon","status","ariaLabel","size","ripple","tooltip","className","visual","box","glyph","kind","span","name","Math","max","round","variant","style","maskImage","WebkitMaskImage","dot","indicator","role","aria-label","label","asChild"],"mappings":";AAAA,SAAQA,GAAG,QAAwB,wBAAwB;AAC3D,SAAQC,IAAI,QAAsB,yBAAyB;AAC3D,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,EAAE,QAAO,0BAA0B;AAE3C,SAAQC,uBAAuB,QAAmC,sBAAsB;AAExF,+EAA+E;AAC/E,MAAMC,gBAA4C;IAChDC,SAAS;IACTC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,SAAS;IACTC,OAAO;AACT;AAEA,yFAAyF;AACzF,uFAAuF;AACvF,MAAMC,cAAgE;IACpEC,WAAW;IACXC,QAAQ;IACRC,WAAW;IACXC,SAAS;AACX;AAEA,0FAA0F;AAC1F,gGAAgG;AAChG,MAAMC,aAAgD;IACpDC,kBAAkB;IAClBC,cAAc;IACdC,aAAa;AACf;AAEA,+FAA+F;AAC/F,MAAMC,eAAuC;IAC3C,IAAI;IACJ,IAAI;IACJ,IAAI;AACN;AAEA,6FAA6F;AAC7F,yFAAyF;AACzF,yFAAyF;AACzF,MAAMC,oBAAoB;AAkB1B;;;;CAIC,GACD,OAAO,SAASC,mBAAmB,EACjCC,MAAM,EACNC,SAAS,EACTC,OAAO,EAAE,EACTC,SAAS,IAAI,EACbC,UAAU,IAAI,EACdC,SAAS,EACe;IACxB,MAAMC,SAAS1B,wBAAwBoB;IACvC,MAAMO,MAAMV,YAAY,CAACK,KAAK,IAAI;IAElC,IAAIM;IACJ,IAAIF,OAAOG,IAAI,KAAK,aAAa;QAC/BD,sBACE,KAACE;YACCL,WAAW1B,GACT,4EACA4B,KACA1B,cAAcE,IAAI;sBAGpB,cAAA,KAACR;gBAAKoC,MAAK;gBAAYT,MAAMU,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAACZ,OAAO;gBAAOG,WAAU;;;IAGlF,OAAO,IAAIC,OAAOG,IAAI,KAAK,UAAU;QACnC,yFAAyF;QACzF,iFAAiF;QACjFD,sBACE,KAACE;YACCL,WAAW1B,GACT,4EACA4B,KACA1B,cAAcK,OAAO;sBAGvB,cAAA,KAACX;gBACCoC,MAAK;gBACLT,MAAMU,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAACZ,OAAO;gBACpCG,WAAU;;;IAIlB,OAAO,IAAIC,OAAOG,IAAI,KAAK,WAAW;QACpCD,sBAAQ,KAAClC;YAAIyC,SAAQ;YAAOZ,QAAQA;YAAQE,WAAWE;;IACzD,OAAO,IAAID,OAAOG,IAAI,KAAK,WAAW;QACpCD,sBACE,KAACE;YACCL,WAAW1B,GAAG,2BAA2B4B,KAAK1B,cAAcC,OAAO;YACnEkC,OAAO;gBAACC,WAAWnB;gBAAmBoB,iBAAiBpB;YAAiB;;IAG9E,OAAO;QACL,MAAMa,OAAOvB,WAAW,CAACkB,OAAOG,IAAI,CAAC,IAAI;QACzCD,sBACE,KAACE;YACCL,WAAW1B,GACT,oDACA4B,KACA1B,aAAa,CAACyB,OAAOa,GAAG,CAAC,EACzB,AAACb,CAAAA,OAAOG,IAAI,KAAK,eAAeH,OAAOG,IAAI,KAAK,SAAQ,KAAM;sBAGhE,cAAA,KAAClC;gBAAKoC,MAAMA;gBAAMT,MAAMU,KAAKE,KAAK,CAACZ,OAAQT,CAAAA,UAAU,CAACkB,KAAK,IAAI,CAAA;;;IAGrE;IAEA,MAAMS,0BACJ,KAACV;QACCW,MAAK;QACLC,cAAYrB,aAAaK,OAAOiB,KAAK;QACrClB,WAAW1B,GAAG,wBAAwB0B;kBAErCG;;IAIL,IAAI,CAACJ,SAAS,OAAOgB;IAErB,qBACE,MAAC5C;;0BACC,KAACE;gBAAe8C,OAAO;0BAAEJ;;0BACzB,KAAC3C;0BAAgB6B,OAAOiB,KAAK;;;;AAGnC"}
1
+ {"version":3,"sources":["../../../src/components/workflow-status/workflow-status-icon.tsx"],"sourcesContent":["import {Dot, type DotVariant} from '@shipfox/react-ui/dot';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {getWorkflowStatusVisual, type WorkflowDisplayStatus} from './status-visuals.js';\n\n// Status glyphs use the same saturated accent tokens as IconBadge/StatusBadge.\nconst toneByVariant: Record<DotVariant, string> = {\n neutral: 'text-tag-neutral-icon',\n info: 'text-tag-blue-icon',\n feature: 'text-tag-purple-icon',\n success: 'text-tag-success-icon',\n warning: 'text-tag-warning-icon',\n error: 'text-tag-error-icon',\n};\n\n// Terminal states are self-contained solid discs; the shape names the state. Running and\n// pending render their own shapes (the live Dot and a bold ring), so they aren't here.\nconst glyphByKind: Partial<Record<WorkflowDisplayStatus, IconName>> = {\n succeeded: 'checkCircleSolid',\n failed: 'xCircleSolid',\n cancelled: 'forbid2Fill',\n skipped: 'forbid2Fill',\n};\n\n// The solid discs fill ~90-95% of their viewBox and forbid2Fill ~83%, so one numeric size\n// renders different diameters. These multipliers land every disc at the running Dot's diameter.\nconst glyphScale: Partial<Record<IconName, number>> = {\n checkCircleSolid: 1.12,\n xCircleSolid: 1.06,\n forbid2Fill: 1.22,\n};\n\n// Only the two sizes the app uses are mapped; both are literal strings so Tailwind emits them.\nconst dotSizeClass: Record<number, string> = {\n 12: 'size-12',\n 14: 'size-14',\n 20: 'size-20',\n};\n\n// Pending is a neutral ring: a filled disc with a transparent center punched out by a radial\n// mask. Reads as \"not started\" and stays visually consistent with the solid discs - much\n// bolder than a dotted outline. Tune the mask stops to make the band thicker or thinner.\nconst PENDING_RING_MASK = 'radial-gradient(circle closest-side, transparent 0 52%, #000 56%)';\n\nexport interface WorkflowStatusIconProps {\n status: WorkflowDisplayStatus;\n /** Accessible status label when the surrounding surface needs to name its scope. */\n ariaLabel?: string | undefined;\n /** Optical diameter in px: 14 in the DAG node and run row. */\n size?: number;\n /** Pulsing halo for the running state. */\n ripple?: boolean;\n /**\n * Wrap the indicator in a hover tooltip naming the status. Defaults on. Pass false where\n * the status text is already visible next to it (the DAG node) to avoid a redundant tooltip.\n */\n tooltip?: boolean;\n className?: string;\n}\n\n/**\n * The job/run/step state indicator: an icon-in-circle glyph whose shape (not just color)\n * names the state. Carries the status as its accessible name (`role=\"img\"`) and, by default,\n * a hover tooltip, so the state is reachable by pointer and assistive tech everywhere.\n */\nexport function WorkflowStatusIcon({\n status,\n ariaLabel,\n size = 14,\n ripple = true,\n tooltip = true,\n className,\n}: WorkflowStatusIconProps) {\n const visual = getWorkflowStatusVisual(status);\n const box = dotSizeClass[size] ?? 'size-14';\n\n let glyph: ReactNode;\n if (visual.kind === 'listening') {\n glyph = (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center rounded-full bg-current',\n box,\n toneByVariant.info,\n )}\n >\n <Icon name=\"pulseLine\" size={Math.max(8, Math.round(size * 0.7))} className=\"text-white\" />\n </span>\n );\n } else if (visual.kind === 'running') {\n glyph = <Dot variant=\"info\" ripple={ripple} className={box} />;\n } else if (visual.kind === 'pending') {\n glyph = (\n <span\n className={cn('rounded-full bg-current', box, toneByVariant.neutral)}\n style={{maskImage: PENDING_RING_MASK, WebkitMaskImage: PENDING_RING_MASK}}\n />\n );\n } else {\n const name = glyphByKind[visual.kind] ?? 'forbid2Fill';\n glyph = (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center',\n box,\n toneByVariant[visual.dot],\n (visual.kind === 'cancelled' || visual.kind === 'skipped') && 'opacity-70',\n )}\n >\n <Icon name={name} size={Math.round(size * (glyphScale[name] ?? 1))} />\n </span>\n );\n }\n\n const indicator = (\n <span\n role=\"img\"\n aria-label={ariaLabel ?? visual.label}\n className={cn('inline-flex shrink-0', className)}\n >\n {glyph}\n </span>\n );\n\n if (!tooltip) return indicator;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>{indicator}</TooltipTrigger>\n <TooltipContent>{visual.label}</TooltipContent>\n </Tooltip>\n );\n}\n"],"names":["Dot","Icon","Tooltip","TooltipContent","TooltipTrigger","cn","getWorkflowStatusVisual","toneByVariant","neutral","info","feature","success","warning","error","glyphByKind","succeeded","failed","cancelled","skipped","glyphScale","checkCircleSolid","xCircleSolid","forbid2Fill","dotSizeClass","PENDING_RING_MASK","WorkflowStatusIcon","status","ariaLabel","size","ripple","tooltip","className","visual","box","glyph","kind","span","name","Math","max","round","variant","style","maskImage","WebkitMaskImage","dot","indicator","role","aria-label","label","asChild"],"mappings":";AAAA,SAAQA,GAAG,QAAwB,wBAAwB;AAC3D,SAAQC,IAAI,QAAsB,yBAAyB;AAC3D,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,EAAE,QAAO,0BAA0B;AAE3C,SAAQC,uBAAuB,QAAmC,sBAAsB;AAExF,+EAA+E;AAC/E,MAAMC,gBAA4C;IAChDC,SAAS;IACTC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,SAAS;IACTC,OAAO;AACT;AAEA,yFAAyF;AACzF,uFAAuF;AACvF,MAAMC,cAAgE;IACpEC,WAAW;IACXC,QAAQ;IACRC,WAAW;IACXC,SAAS;AACX;AAEA,0FAA0F;AAC1F,gGAAgG;AAChG,MAAMC,aAAgD;IACpDC,kBAAkB;IAClBC,cAAc;IACdC,aAAa;AACf;AAEA,+FAA+F;AAC/F,MAAMC,eAAuC;IAC3C,IAAI;IACJ,IAAI;IACJ,IAAI;AACN;AAEA,6FAA6F;AAC7F,yFAAyF;AACzF,yFAAyF;AACzF,MAAMC,oBAAoB;AAkB1B;;;;CAIC,GACD,OAAO,SAASC,mBAAmB,EACjCC,MAAM,EACNC,SAAS,EACTC,OAAO,EAAE,EACTC,SAAS,IAAI,EACbC,UAAU,IAAI,EACdC,SAAS,EACe;IACxB,MAAMC,SAAS1B,wBAAwBoB;IACvC,MAAMO,MAAMV,YAAY,CAACK,KAAK,IAAI;IAElC,IAAIM;IACJ,IAAIF,OAAOG,IAAI,KAAK,aAAa;QAC/BD,sBACE,KAACE;YACCL,WAAW1B,GACT,4EACA4B,KACA1B,cAAcE,IAAI;sBAGpB,cAAA,KAACR;gBAAKoC,MAAK;gBAAYT,MAAMU,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAACZ,OAAO;gBAAOG,WAAU;;;IAGlF,OAAO,IAAIC,OAAOG,IAAI,KAAK,WAAW;QACpCD,sBAAQ,KAAClC;YAAIyC,SAAQ;YAAOZ,QAAQA;YAAQE,WAAWE;;IACzD,OAAO,IAAID,OAAOG,IAAI,KAAK,WAAW;QACpCD,sBACE,KAACE;YACCL,WAAW1B,GAAG,2BAA2B4B,KAAK1B,cAAcC,OAAO;YACnEkC,OAAO;gBAACC,WAAWnB;gBAAmBoB,iBAAiBpB;YAAiB;;IAG9E,OAAO;QACL,MAAMa,OAAOvB,WAAW,CAACkB,OAAOG,IAAI,CAAC,IAAI;QACzCD,sBACE,KAACE;YACCL,WAAW1B,GACT,oDACA4B,KACA1B,aAAa,CAACyB,OAAOa,GAAG,CAAC,EACzB,AAACb,CAAAA,OAAOG,IAAI,KAAK,eAAeH,OAAOG,IAAI,KAAK,SAAQ,KAAM;sBAGhE,cAAA,KAAClC;gBAAKoC,MAAMA;gBAAMT,MAAMU,KAAKE,KAAK,CAACZ,OAAQT,CAAAA,UAAU,CAACkB,KAAK,IAAI,CAAA;;;IAGrE;IAEA,MAAMS,0BACJ,KAACV;QACCW,MAAK;QACLC,cAAYrB,aAAaK,OAAOiB,KAAK;QACrClB,WAAW1B,GAAG,wBAAwB0B;kBAErCG;;IAIL,IAAI,CAACJ,SAAS,OAAOgB;IAErB,qBACE,MAAC5C;;0BACC,KAACE;gBAAe8C,OAAO;0BAAEJ;;0BACzB,KAAC3C;0BAAgB6B,OAAOiB,KAAK;;;;AAGnC"}
@@ -1,4 +1,4 @@
1
- import { type JobExecution, type JobExecutionDisplayDuration } from './job-execution.js';
1
+ import { type JobExecution, type JobExecutionDisplayDuration, type JobExecutionStatus } from './job-execution.js';
2
2
  import type { EvaluationTraceEntry } from './step-attempt.js';
3
3
  export type JobStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'skipped';
4
4
  export type JobDisplayStatus = JobStatus | 'listening';
@@ -82,7 +82,10 @@ export declare class Job {
82
82
  get executionCountVisible(): boolean;
83
83
  }
84
84
  export declare function isTerminalJobStatus(status: JobStatus): boolean;
85
- export declare function deriveJobDisplayStatus(job: Pick<Job, 'mode' | 'status' | 'listenerStatus' | 'jobExecutions'>): JobDisplayStatus;
85
+ export declare function deriveJobDisplayStatus(job: Pick<Job, 'mode' | 'status' | 'listenerStatus' | 'jobExecutions'> & {
86
+ /** List previews carry the selected execution state without its step tree. */
87
+ executionStatus?: JobExecutionStatus | null | undefined;
88
+ }): JobDisplayStatus;
86
89
  export declare function resolveJobExecution(job: Job, jobExecutionId: string | undefined): JobExecution | undefined;
87
90
  export declare function defaultJobExecution(job: Pick<Job, 'jobExecutions'>): JobExecution | undefined;
88
91
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../../../src/core/entities/job.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../../../src/core/entities/job.ts"],"names":[],"mappings":"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"}
@@ -36,7 +36,14 @@ export function isTerminalJobStatus(status) {
36
36
  export function deriveJobDisplayStatus(job) {
37
37
  if (isTerminalJobStatus(job.status)) return job.status;
38
38
  if (job.mode === 'listening' && job.listenerStatus === 'listening') return 'listening';
39
- const execution = defaultJobExecution(job);
39
+ const execution = job.executionStatus === undefined ? defaultJobExecution(job) : job.executionStatus === null ? undefined : {
40
+ status: job.executionStatus,
41
+ steps: []
42
+ };
43
+ // A running execution is the shared display rule for both list previews and detail jobs.
44
+ // The list has no step tree, while the detail path can still use the execution's steps for
45
+ // other non-running states.
46
+ if (execution?.status === 'running') return 'running';
40
47
  return execution ? deriveJobExecutionDisplayStatus(execution) : 'pending';
41
48
  }
42
49
  export function resolveJobExecution(job, jobExecutionId) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/entities/job.ts"],"sourcesContent":["import {\n deriveJobExecutionDisplayStatus,\n type JobExecution,\n type JobExecutionDisplayDuration,\n} from './job-execution.js';\nimport type {EvaluationTraceEntry} from './step-attempt.js';\n\nexport type JobStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'skipped';\nexport type JobDisplayStatus = JobStatus | 'listening';\nexport type JobMode = 'one_shot' | 'listening';\nexport type ListenerStatus = 'inactive' | 'listening' | 'resolved';\nexport type ResolutionReason = 'until' | 'timeout' | 'max_executions' | 'cancelled';\nexport type JobStatusReason =\n | 'dependency_not_completed'\n | 'condition_false'\n | 'default_gate_rejected'\n | 'condition_rejected'\n | 'condition_errored'\n | 'user_cancelled'\n | 'run_cancelled'\n | 'timed_out'\n | 'runner_lost'\n | 'output_too_large'\n | 'step_failed'\n | 'unknown'\n | 'output_invalid';\nexport interface JobListening {\n on: Array<{\n source: string;\n event: string;\n inputs?: Record<string, unknown> | undefined;\n filter?: string | undefined;\n }>;\n until: Array<{\n source: string;\n event: string;\n inputs?: Record<string, unknown> | undefined;\n filter?: string | undefined;\n }> | null;\n timeoutMs: number | null;\n maxExecutions: number | null;\n batch: {\n debounceMs?: number | undefined;\n maxSize?: number | undefined;\n maxWaitMs?: number | undefined;\n } | null;\n onResolve: 'finish' | 'cancel';\n executionTimeoutMs: number | null;\n name: string | null;\n}\n\nexport type JobDisplayDuration = JobExecutionDisplayDuration;\n\nexport const WORKFLOW_JOB_STATUSES = [\n 'pending',\n 'running',\n 'succeeded',\n 'failed',\n 'cancelled',\n 'skipped',\n] as const satisfies readonly JobStatus[];\n\nexport const TERMINAL_WORKFLOW_JOB_STATUSES = [\n 'succeeded',\n 'failed',\n 'cancelled',\n 'skipped',\n] as const satisfies readonly JobStatus[];\n\nconst TERMINAL_JOB_STATUS_SET = new Set<JobStatus>(TERMINAL_WORKFLOW_JOB_STATUSES);\n\ninterface JobFields {\n id: string;\n runAttemptId: string;\n key: string;\n name: string | null;\n mode: JobMode;\n status: JobStatus;\n statusReason: JobStatusReason | null;\n carriedOver: boolean;\n outputs: Record<string, unknown> | null;\n success: string | null;\n runner: string[] | null;\n evaluationTrace: EvaluationTraceEntry[] | null;\n listening: JobListening | null;\n listenerStatus: ListenerStatus;\n resolutionReason: ResolutionReason | null;\n dependencies: string[];\n position: number;\n createdAt: string;\n updatedAt: string;\n jobExecutions: JobExecution[];\n}\n\nexport class Job {\n id!: string;\n runAttemptId!: string;\n key!: string;\n name!: string | null;\n mode!: JobMode;\n status!: JobStatus;\n statusReason!: JobStatusReason | null;\n carriedOver!: boolean;\n outputs!: Record<string, unknown> | null;\n success!: string | null;\n runner!: string[] | null;\n evaluationTrace!: EvaluationTraceEntry[] | null;\n listening!: JobListening | null;\n listenerStatus!: ListenerStatus;\n resolutionReason!: ResolutionReason | null;\n dependencies!: string[];\n position!: number;\n createdAt!: string;\n updatedAt!: string;\n jobExecutions!: JobExecution[];\n\n constructor(fields: JobFields) {\n Object.assign(this, fields);\n }\n\n get displayName(): string {\n return this.name ?? this.key;\n }\n\n get displayDuration(): JobDisplayDuration | null {\n if (this.mode === 'listening') return null;\n if (this.jobExecutions.length !== 1) return null;\n return this.jobExecutions[0]?.displayDuration ?? null;\n }\n\n get executionCountVisible(): boolean {\n return (\n this.jobExecutions.length > 0 && (this.mode === 'listening' || this.jobExecutions.length > 1)\n );\n }\n}\n\nexport function isTerminalJobStatus(status: JobStatus): boolean {\n return TERMINAL_JOB_STATUS_SET.has(status);\n}\n\nexport function deriveJobDisplayStatus(\n job: Pick<Job, 'mode' | 'status' | 'listenerStatus' | 'jobExecutions'>,\n): JobDisplayStatus {\n if (isTerminalJobStatus(job.status)) return job.status;\n if (job.mode === 'listening' && job.listenerStatus === 'listening') return 'listening';\n\n const execution = defaultJobExecution(job);\n return execution ? deriveJobExecutionDisplayStatus(execution) : 'pending';\n}\n\nexport function resolveJobExecution(\n job: Job,\n jobExecutionId: string | undefined,\n): JobExecution | undefined {\n const selectedExecution = jobExecutionId\n ? job.jobExecutions.find((jobExecution) => jobExecution.id === jobExecutionId)\n : undefined;\n if (selectedExecution) return selectedExecution;\n\n return defaultJobExecution(job);\n}\n\nexport function defaultJobExecution(job: Pick<Job, 'jobExecutions'>): JobExecution | undefined {\n const runningExecution = job.jobExecutions.find(\n (jobExecution) => jobExecution.status === 'running',\n );\n if (runningExecution) return runningExecution;\n\n return job.jobExecutions.reduce<JobExecution | undefined>((latest, jobExecution) => {\n if (!latest) return jobExecution;\n return jobExecution.sequence > latest.sequence ? jobExecution : latest;\n }, undefined);\n}\n"],"names":["deriveJobExecutionDisplayStatus","WORKFLOW_JOB_STATUSES","TERMINAL_WORKFLOW_JOB_STATUSES","TERMINAL_JOB_STATUS_SET","Set","Job","fields","Object","assign","displayName","name","key","displayDuration","mode","jobExecutions","length","executionCountVisible","isTerminalJobStatus","status","has","deriveJobDisplayStatus","job","listenerStatus","execution","defaultJobExecution","resolveJobExecution","jobExecutionId","selectedExecution","find","jobExecution","id","undefined","runningExecution","reduce","latest","sequence"],"mappings":"AAAA,SACEA,+BAA+B,QAG1B,qBAAqB;AAiD5B,OAAO,MAAMC,wBAAwB;IACnC;IACA;IACA;IACA;IACA;IACA;CACD,CAAyC;AAE1C,OAAO,MAAMC,iCAAiC;IAC5C;IACA;IACA;IACA;CACD,CAAyC;AAE1C,MAAMC,0BAA0B,IAAIC,IAAeF;AAyBnD,OAAO,MAAMG;IAsBX,YAAYC,MAAiB,CAAE;QAC7BC,OAAOC,MAAM,CAAC,IAAI,EAAEF;IACtB;IAEA,IAAIG,cAAsB;QACxB,OAAO,IAAI,CAACC,IAAI,IAAI,IAAI,CAACC,GAAG;IAC9B;IAEA,IAAIC,kBAA6C;QAC/C,IAAI,IAAI,CAACC,IAAI,KAAK,aAAa,OAAO;QACtC,IAAI,IAAI,CAACC,aAAa,CAACC,MAAM,KAAK,GAAG,OAAO;QAC5C,OAAO,IAAI,CAACD,aAAa,CAAC,EAAE,EAAEF,mBAAmB;IACnD;IAEA,IAAII,wBAAiC;QACnC,OACE,IAAI,CAACF,aAAa,CAACC,MAAM,GAAG,KAAM,CAAA,IAAI,CAACF,IAAI,KAAK,eAAe,IAAI,CAACC,aAAa,CAACC,MAAM,GAAG,CAAA;IAE/F;AACF;AAEA,OAAO,SAASE,oBAAoBC,MAAiB;IACnD,OAAOf,wBAAwBgB,GAAG,CAACD;AACrC;AAEA,OAAO,SAASE,uBACdC,GAAsE;IAEtE,IAAIJ,oBAAoBI,IAAIH,MAAM,GAAG,OAAOG,IAAIH,MAAM;IACtD,IAAIG,IAAIR,IAAI,KAAK,eAAeQ,IAAIC,cAAc,KAAK,aAAa,OAAO;IAE3E,MAAMC,YAAYC,oBAAoBH;IACtC,OAAOE,YAAYvB,gCAAgCuB,aAAa;AAClE;AAEA,OAAO,SAASE,oBACdJ,GAAQ,EACRK,cAAkC;IAElC,MAAMC,oBAAoBD,iBACtBL,IAAIP,aAAa,CAACc,IAAI,CAAC,CAACC,eAAiBA,aAAaC,EAAE,KAAKJ,kBAC7DK;IACJ,IAAIJ,mBAAmB,OAAOA;IAE9B,OAAOH,oBAAoBH;AAC7B;AAEA,OAAO,SAASG,oBAAoBH,GAA+B;IACjE,MAAMW,mBAAmBX,IAAIP,aAAa,CAACc,IAAI,CAC7C,CAACC,eAAiBA,aAAaX,MAAM,KAAK;IAE5C,IAAIc,kBAAkB,OAAOA;IAE7B,OAAOX,IAAIP,aAAa,CAACmB,MAAM,CAA2B,CAACC,QAAQL;QACjE,IAAI,CAACK,QAAQ,OAAOL;QACpB,OAAOA,aAAaM,QAAQ,GAAGD,OAAOC,QAAQ,GAAGN,eAAeK;IAClE,GAAGH;AACL"}
1
+ {"version":3,"sources":["../../../src/core/entities/job.ts"],"sourcesContent":["import {\n deriveJobExecutionDisplayStatus,\n type JobExecution,\n type JobExecutionDisplayDuration,\n type JobExecutionStatus,\n} from './job-execution.js';\nimport type {EvaluationTraceEntry} from './step-attempt.js';\n\nexport type JobStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'skipped';\nexport type JobDisplayStatus = JobStatus | 'listening';\nexport type JobMode = 'one_shot' | 'listening';\nexport type ListenerStatus = 'inactive' | 'listening' | 'resolved';\nexport type ResolutionReason = 'until' | 'timeout' | 'max_executions' | 'cancelled';\nexport type JobStatusReason =\n | 'dependency_not_completed'\n | 'condition_false'\n | 'default_gate_rejected'\n | 'condition_rejected'\n | 'condition_errored'\n | 'user_cancelled'\n | 'run_cancelled'\n | 'timed_out'\n | 'runner_lost'\n | 'output_too_large'\n | 'step_failed'\n | 'unknown'\n | 'output_invalid';\nexport interface JobListening {\n on: Array<{\n source: string;\n event: string;\n inputs?: Record<string, unknown> | undefined;\n filter?: string | undefined;\n }>;\n until: Array<{\n source: string;\n event: string;\n inputs?: Record<string, unknown> | undefined;\n filter?: string | undefined;\n }> | null;\n timeoutMs: number | null;\n maxExecutions: number | null;\n batch: {\n debounceMs?: number | undefined;\n maxSize?: number | undefined;\n maxWaitMs?: number | undefined;\n } | null;\n onResolve: 'finish' | 'cancel';\n executionTimeoutMs: number | null;\n name: string | null;\n}\n\nexport type JobDisplayDuration = JobExecutionDisplayDuration;\n\nexport const WORKFLOW_JOB_STATUSES = [\n 'pending',\n 'running',\n 'succeeded',\n 'failed',\n 'cancelled',\n 'skipped',\n] as const satisfies readonly JobStatus[];\n\nexport const TERMINAL_WORKFLOW_JOB_STATUSES = [\n 'succeeded',\n 'failed',\n 'cancelled',\n 'skipped',\n] as const satisfies readonly JobStatus[];\n\nconst TERMINAL_JOB_STATUS_SET = new Set<JobStatus>(TERMINAL_WORKFLOW_JOB_STATUSES);\n\ninterface JobFields {\n id: string;\n runAttemptId: string;\n key: string;\n name: string | null;\n mode: JobMode;\n status: JobStatus;\n statusReason: JobStatusReason | null;\n carriedOver: boolean;\n outputs: Record<string, unknown> | null;\n success: string | null;\n runner: string[] | null;\n evaluationTrace: EvaluationTraceEntry[] | null;\n listening: JobListening | null;\n listenerStatus: ListenerStatus;\n resolutionReason: ResolutionReason | null;\n dependencies: string[];\n position: number;\n createdAt: string;\n updatedAt: string;\n jobExecutions: JobExecution[];\n}\n\nexport class Job {\n id!: string;\n runAttemptId!: string;\n key!: string;\n name!: string | null;\n mode!: JobMode;\n status!: JobStatus;\n statusReason!: JobStatusReason | null;\n carriedOver!: boolean;\n outputs!: Record<string, unknown> | null;\n success!: string | null;\n runner!: string[] | null;\n evaluationTrace!: EvaluationTraceEntry[] | null;\n listening!: JobListening | null;\n listenerStatus!: ListenerStatus;\n resolutionReason!: ResolutionReason | null;\n dependencies!: string[];\n position!: number;\n createdAt!: string;\n updatedAt!: string;\n jobExecutions!: JobExecution[];\n\n constructor(fields: JobFields) {\n Object.assign(this, fields);\n }\n\n get displayName(): string {\n return this.name ?? this.key;\n }\n\n get displayDuration(): JobDisplayDuration | null {\n if (this.mode === 'listening') return null;\n if (this.jobExecutions.length !== 1) return null;\n return this.jobExecutions[0]?.displayDuration ?? null;\n }\n\n get executionCountVisible(): boolean {\n return (\n this.jobExecutions.length > 0 && (this.mode === 'listening' || this.jobExecutions.length > 1)\n );\n }\n}\n\nexport function isTerminalJobStatus(status: JobStatus): boolean {\n return TERMINAL_JOB_STATUS_SET.has(status);\n}\n\nexport function deriveJobDisplayStatus(\n job: Pick<Job, 'mode' | 'status' | 'listenerStatus' | 'jobExecutions'> & {\n /** List previews carry the selected execution state without its step tree. */\n executionStatus?: JobExecutionStatus | null | undefined;\n },\n): JobDisplayStatus {\n if (isTerminalJobStatus(job.status)) return job.status;\n if (job.mode === 'listening' && job.listenerStatus === 'listening') return 'listening';\n const execution =\n job.executionStatus === undefined\n ? defaultJobExecution(job)\n : job.executionStatus === null\n ? undefined\n : {status: job.executionStatus, steps: []};\n // A running execution is the shared display rule for both list previews and detail jobs.\n // The list has no step tree, while the detail path can still use the execution's steps for\n // other non-running states.\n if (execution?.status === 'running') return 'running';\n return execution ? deriveJobExecutionDisplayStatus(execution) : 'pending';\n}\n\nexport function resolveJobExecution(\n job: Job,\n jobExecutionId: string | undefined,\n): JobExecution | undefined {\n const selectedExecution = jobExecutionId\n ? job.jobExecutions.find((jobExecution) => jobExecution.id === jobExecutionId)\n : undefined;\n if (selectedExecution) return selectedExecution;\n\n return defaultJobExecution(job);\n}\n\nexport function defaultJobExecution(job: Pick<Job, 'jobExecutions'>): JobExecution | undefined {\n const runningExecution = job.jobExecutions.find(\n (jobExecution) => jobExecution.status === 'running',\n );\n if (runningExecution) return runningExecution;\n\n return job.jobExecutions.reduce<JobExecution | undefined>((latest, jobExecution) => {\n if (!latest) return jobExecution;\n return jobExecution.sequence > latest.sequence ? jobExecution : latest;\n }, undefined);\n}\n"],"names":["deriveJobExecutionDisplayStatus","WORKFLOW_JOB_STATUSES","TERMINAL_WORKFLOW_JOB_STATUSES","TERMINAL_JOB_STATUS_SET","Set","Job","fields","Object","assign","displayName","name","key","displayDuration","mode","jobExecutions","length","executionCountVisible","isTerminalJobStatus","status","has","deriveJobDisplayStatus","job","listenerStatus","execution","executionStatus","undefined","defaultJobExecution","steps","resolveJobExecution","jobExecutionId","selectedExecution","find","jobExecution","id","runningExecution","reduce","latest","sequence"],"mappings":"AAAA,SACEA,+BAA+B,QAI1B,qBAAqB;AAiD5B,OAAO,MAAMC,wBAAwB;IACnC;IACA;IACA;IACA;IACA;IACA;CACD,CAAyC;AAE1C,OAAO,MAAMC,iCAAiC;IAC5C;IACA;IACA;IACA;CACD,CAAyC;AAE1C,MAAMC,0BAA0B,IAAIC,IAAeF;AAyBnD,OAAO,MAAMG;IAsBX,YAAYC,MAAiB,CAAE;QAC7BC,OAAOC,MAAM,CAAC,IAAI,EAAEF;IACtB;IAEA,IAAIG,cAAsB;QACxB,OAAO,IAAI,CAACC,IAAI,IAAI,IAAI,CAACC,GAAG;IAC9B;IAEA,IAAIC,kBAA6C;QAC/C,IAAI,IAAI,CAACC,IAAI,KAAK,aAAa,OAAO;QACtC,IAAI,IAAI,CAACC,aAAa,CAACC,MAAM,KAAK,GAAG,OAAO;QAC5C,OAAO,IAAI,CAACD,aAAa,CAAC,EAAE,EAAEF,mBAAmB;IACnD;IAEA,IAAII,wBAAiC;QACnC,OACE,IAAI,CAACF,aAAa,CAACC,MAAM,GAAG,KAAM,CAAA,IAAI,CAACF,IAAI,KAAK,eAAe,IAAI,CAACC,aAAa,CAACC,MAAM,GAAG,CAAA;IAE/F;AACF;AAEA,OAAO,SAASE,oBAAoBC,MAAiB;IACnD,OAAOf,wBAAwBgB,GAAG,CAACD;AACrC;AAEA,OAAO,SAASE,uBACdC,GAGC;IAED,IAAIJ,oBAAoBI,IAAIH,MAAM,GAAG,OAAOG,IAAIH,MAAM;IACtD,IAAIG,IAAIR,IAAI,KAAK,eAAeQ,IAAIC,cAAc,KAAK,aAAa,OAAO;IAC3E,MAAMC,YACJF,IAAIG,eAAe,KAAKC,YACpBC,oBAAoBL,OACpBA,IAAIG,eAAe,KAAK,OACtBC,YACA;QAACP,QAAQG,IAAIG,eAAe;QAAEG,OAAO,EAAE;IAAA;IAC/C,yFAAyF;IACzF,2FAA2F;IAC3F,4BAA4B;IAC5B,IAAIJ,WAAWL,WAAW,WAAW,OAAO;IAC5C,OAAOK,YAAYvB,gCAAgCuB,aAAa;AAClE;AAEA,OAAO,SAASK,oBACdP,GAAQ,EACRQ,cAAkC;IAElC,MAAMC,oBAAoBD,iBACtBR,IAAIP,aAAa,CAACiB,IAAI,CAAC,CAACC,eAAiBA,aAAaC,EAAE,KAAKJ,kBAC7DJ;IACJ,IAAIK,mBAAmB,OAAOA;IAE9B,OAAOJ,oBAAoBL;AAC7B;AAEA,OAAO,SAASK,oBAAoBL,GAA+B;IACjE,MAAMa,mBAAmBb,IAAIP,aAAa,CAACiB,IAAI,CAC7C,CAACC,eAAiBA,aAAad,MAAM,KAAK;IAE5C,IAAIgB,kBAAkB,OAAOA;IAE7B,OAAOb,IAAIP,aAAa,CAACqB,MAAM,CAA2B,CAACC,QAAQJ;QACjE,IAAI,CAACI,QAAQ,OAAOJ;QACpB,OAAOA,aAAaK,QAAQ,GAAGD,OAAOC,QAAQ,GAAGL,eAAeI;IAClE,GAAGX;AACL"}
@@ -1,16 +1,16 @@
1
- import { type Job, type JobStatus, WORKFLOW_JOB_STATUSES } from './job.js';
2
- import { type JobExecutionDisplayDuration } from './job-execution.js';
1
+ import { type Job, type JobDisplayStatus, type JobMode, type JobStatus, type ListenerStatus, WORKFLOW_JOB_STATUSES } from './job.js';
2
+ import type { JobExecutionStatus } from './job-execution.js';
3
3
  import type { WorkflowRunAttempt, WorkflowRunAttemptSummary } from './workflow-run-attempt.js';
4
4
  export type WorkflowRunStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled';
5
5
  export type WorkflowRunRerunMode = 'all' | 'failed';
6
6
  export type WorkflowStatus = WorkflowRunStatus | (typeof WORKFLOW_JOB_STATUSES)[number];
7
7
  /**
8
- * `listening` and `queued` are display-only: the API never returns them, and both are derived
9
- * from the jobs a run already carries.
8
+ * `listening` is display-only: the API never returns it, and it is derived from the listener
9
+ * state a job already carries.
10
10
  */
11
- export type WorkflowDisplayStatus = WorkflowStatus | 'listening' | 'queued';
11
+ export type WorkflowDisplayStatus = WorkflowStatus | 'listening';
12
12
  export declare const WORKFLOW_RUN_STATUSES: readonly ["pending", "running", "succeeded", "failed", "cancelled"];
13
- export declare const WORKFLOW_DISPLAY_STATUSES: readonly ["pending", "running", "succeeded", "failed", "cancelled", "pending", "running", "succeeded", "failed", "cancelled", "skipped", "listening", "queued"];
13
+ export declare const WORKFLOW_DISPLAY_STATUSES: readonly ["pending", "running", "succeeded", "failed", "cancelled", "pending", "running", "succeeded", "failed", "cancelled", "skipped", "listening"];
14
14
  export declare const TERMINAL_WORKFLOW_RUN_STATUSES: readonly ["succeeded", "failed", "cancelled"];
15
15
  export interface WorkflowSourceSnapshot {
16
16
  content: string;
@@ -32,10 +32,13 @@ export interface WorkflowRunJobSummary {
32
32
  key: string;
33
33
  name: string | null;
34
34
  status: JobStatus;
35
+ mode: JobMode;
36
+ listenerStatus: ListenerStatus;
37
+ executionStatus: JobExecutionStatus | null;
35
38
  position: number;
36
39
  }
37
40
  export interface WorkflowRunJobStatusCount {
38
- status: JobStatus;
41
+ status: JobDisplayStatus;
39
42
  count: number;
40
43
  }
41
44
  /**
@@ -43,11 +46,14 @@ export interface WorkflowRunJobStatusCount {
43
46
  * of them.
44
47
  *
45
48
  * `preview` is capped by the API, so `preview.length` is not the job count and the strip
46
- * reads `total` and `statusCounts` for anything it says rather than anything it draws.
49
+ * reads `total` and `statusCounts` for anything it says rather than anything it draws. The API
50
+ * also reports whether any job execution has started because a terminal `cancelled` job can have
51
+ * started work even though its job status does not preserve that distinction.
47
52
  */
48
53
  export interface WorkflowRunJobs {
49
54
  preview: WorkflowRunJobSummary[];
50
55
  statusCounts: WorkflowRunJobStatusCount[];
56
+ hasStartedJobExecution: boolean;
51
57
  total: number;
52
58
  }
53
59
  export interface WorkflowRun {
@@ -84,6 +90,7 @@ export interface WorkflowRunDetail extends WorkflowRun {
84
90
  latestAttempt: number;
85
91
  runAttempt: WorkflowRunAttempt;
86
92
  jobs: Job[];
93
+ hasStartedJobExecution: boolean;
87
94
  }
88
95
  export interface WorkflowRunListPage {
89
96
  runs: WorkflowRunListItem[];
@@ -111,53 +118,4 @@ export declare function workflowRunCommitLabel(run: Pick<WorkflowRun, 'triggerRe
111
118
  export declare function workflowRunActor(run: Pick<WorkflowRun, 'triggerReference'>): string | null;
112
119
  export declare function isWorkflowRunTerminal(status: WorkflowRunStatus): boolean;
113
120
  export declare function isWorkflowStatus(status: string): status is WorkflowStatus;
114
- /**
115
- * A run's headline duration, split the way a job's already is. An attempt's `startedAt` marks
116
- * when the orchestrator picked the run up, not when work began, so a run whose jobs are all
117
- * still waiting for a runner reads as queue time rather than as run time.
118
- */
119
- export type WorkflowRunDisplayDuration = JobExecutionDisplayDuration;
120
- /** What every surface shows for a run, derived together so status and duration cannot disagree. */
121
- export interface WorkflowRunDisplay {
122
- status: WorkflowDisplayStatus;
123
- duration: WorkflowRunDisplayDuration | null;
124
- }
125
- /**
126
- * The evidence a surface can offer about a run's progress.
127
- *
128
- * `jobStatuses` is what every surface has: the list carries status counts, the detail carries
129
- * whole jobs. `firstStartedAt` is the detail's extra precision, and where it is absent the
130
- * attempt's own mark stands in.
131
- */
132
- export interface WorkflowRunProgress {
133
- runStatus: WorkflowRunStatus;
134
- startedAt: string | null;
135
- finishedAt: string | null;
136
- jobStatuses: JobStatus[];
137
- firstStartedAt?: string | null | undefined;
138
- }
139
- /** When the run's earliest execution began, or null while none of them has. */
140
- export declare function workflowRunFirstStartedAt(jobs: Job[]): string | null;
141
- /**
142
- * The single rule behind every run readout. An attempt's `startedAt` marks when the
143
- * orchestrator picked the run up, not when work began, so a run whose jobs are all still
144
- * waiting reads as `Queued` for queue time. Calling that "running for 2h" sends an operator to
145
- * debug a build that never began.
146
- */
147
- export declare function deriveWorkflowRunDisplay(progress: WorkflowRunProgress): WorkflowRunDisplay;
148
- /** The run detail's reading: whole jobs, so queue and run time split on the first execution. */
149
- export declare function workflowRunDetailDisplay(run: {
150
- runAttempt: Pick<WorkflowRunAttemptSummary, 'status' | 'startedAt' | 'finishedAt'>;
151
- jobs: Job[];
152
- }): WorkflowRunDisplay;
153
- /**
154
- * The run list's reading: status counts cover every job, not just the drawn preview, so a row
155
- * reaches the same verdict as the detail page without fetching a single timestamp more.
156
- */
157
- export declare function workflowRunListItemDisplay(run: WorkflowRunListItem): WorkflowRunDisplay;
158
- /**
159
- * The job the run is waiting on: the one queued longest without starting. Named only so a
160
- * queued run says what it waits for instead of showing a number with no subject.
161
- */
162
- export declare function workflowRunBlockingJob(jobs: Job[]): Job | null;
163
121
  //# sourceMappingURL=workflow-run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-run.d.ts","sourceRoot":"","sources":["../../../src/core/entities/workflow-run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workflow-run.d.ts","sourceRoot":"","sources":["../../../src/core/entities/workflow-run.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { WORKFLOW_JOB_STATUSES } from './job.js';
2
- import { elapsedTimeFromTimestamps } from './job-execution.js';
3
2
  export const WORKFLOW_RUN_STATUSES = [
4
3
  'pending',
5
4
  'running',
@@ -10,8 +9,7 @@ export const WORKFLOW_RUN_STATUSES = [
10
9
  export const WORKFLOW_DISPLAY_STATUSES = [
11
10
  ...WORKFLOW_RUN_STATUSES,
12
11
  ...WORKFLOW_JOB_STATUSES,
13
- 'listening',
14
- 'queued'
12
+ 'listening'
15
13
  ];
16
14
  export const TERMINAL_WORKFLOW_RUN_STATUSES = [
17
15
  'succeeded',
@@ -60,96 +58,5 @@ export function isWorkflowRunTerminal(status) {
60
58
  export function isWorkflowStatus(status) {
61
59
  return WORKFLOW_STATUSES.has(status);
62
60
  }
63
- /** When the run's earliest execution began, or null while none of them has. */ export function workflowRunFirstStartedAt(jobs) {
64
- let earliest = null;
65
- let earliestMs = Number.POSITIVE_INFINITY;
66
- for (const job of jobs){
67
- for (const { startedAt } of job.jobExecutions){
68
- if (startedAt === null) continue;
69
- const startedMs = new Date(startedAt).getTime();
70
- if (!Number.isFinite(startedMs) || startedMs >= earliestMs) continue;
71
- earliest = startedAt;
72
- earliestMs = startedMs;
73
- }
74
- }
75
- return earliest;
76
- }
77
- /**
78
- * Whether any work has begun. A run with no jobs on hand is a run whose jobs were not fetched,
79
- * which is no evidence that nothing started, so it counts as started rather than claiming a
80
- * queue this surface cannot see.
81
- */ function workflowRunHasStarted({ jobStatuses, firstStartedAt }) {
82
- if (firstStartedAt != null) return true;
83
- if (jobStatuses.length === 0) return true;
84
- return jobStatuses.some((status)=>status !== 'pending' && status !== 'skipped');
85
- }
86
- /**
87
- * The single rule behind every run readout. An attempt's `startedAt` marks when the
88
- * orchestrator picked the run up, not when work began, so a run whose jobs are all still
89
- * waiting reads as `Queued` for queue time. Calling that "running for 2h" sends an operator to
90
- * debug a build that never began.
91
- */ export function deriveWorkflowRunDisplay(progress) {
92
- const { runStatus, startedAt, finishedAt, firstStartedAt } = progress;
93
- const hasStarted = workflowRunHasStarted(progress);
94
- const status = runStatus === 'running' && !hasStarted ? 'queued' : runStatus;
95
- if (startedAt === null) return {
96
- status,
97
- duration: null
98
- };
99
- // A run cancelled before anything started keeps its queue reading rather than reporting a
100
- // run that never was.
101
- const time = elapsedTimeFromTimestamps({
102
- from: firstStartedAt ?? startedAt,
103
- to: finishedAt
104
- });
105
- if (time === null) return {
106
- status,
107
- duration: null
108
- };
109
- return {
110
- status,
111
- duration: {
112
- kind: hasStarted ? 'run' : 'queue',
113
- ...time
114
- }
115
- };
116
- }
117
- /** The run detail's reading: whole jobs, so queue and run time split on the first execution. */ export function workflowRunDetailDisplay(run) {
118
- return deriveWorkflowRunDisplay({
119
- runStatus: run.runAttempt.status,
120
- startedAt: run.runAttempt.startedAt,
121
- finishedAt: run.runAttempt.finishedAt,
122
- jobStatuses: run.jobs.map((job)=>job.status),
123
- firstStartedAt: workflowRunFirstStartedAt(run.jobs)
124
- });
125
- }
126
- /**
127
- * The run list's reading: status counts cover every job, not just the drawn preview, so a row
128
- * reaches the same verdict as the detail page without fetching a single timestamp more.
129
- */ export function workflowRunListItemDisplay(run) {
130
- return deriveWorkflowRunDisplay({
131
- runStatus: run.status,
132
- startedAt: run.runAttempt.startedAt,
133
- finishedAt: run.runAttempt.finishedAt,
134
- jobStatuses: run.jobs.statusCounts.filter(({ count })=>count > 0).map(({ status })=>status)
135
- });
136
- }
137
- /**
138
- * The job the run is waiting on: the one queued longest without starting. Named only so a
139
- * queued run says what it waits for instead of showing a number with no subject.
140
- */ export function workflowRunBlockingJob(jobs) {
141
- let blocking = null;
142
- let queuedMs = Number.POSITIVE_INFINITY;
143
- for (const job of jobs){
144
- for (const { queuedAt, startedAt, finishedAt } of job.jobExecutions){
145
- if (queuedAt === null || startedAt !== null || finishedAt !== null) continue;
146
- const candidateMs = new Date(queuedAt).getTime();
147
- if (!Number.isFinite(candidateMs) || candidateMs >= queuedMs) continue;
148
- blocking = job;
149
- queuedMs = candidateMs;
150
- }
151
- }
152
- return blocking;
153
- }
154
61
 
155
62
  //# sourceMappingURL=workflow-run.js.map