@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +25 -0
- package/dist/components/workflow-run-duration-label.d.ts +8 -7
- package/dist/components/workflow-run-duration-label.d.ts.map +1 -1
- package/dist/components/workflow-run-duration-label.js +15 -31
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +20 -7
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +9 -6
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +7 -34
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +0 -9
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +0 -11
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/job.d.ts +5 -2
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js +8 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +15 -57
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +1 -94
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run.d.ts +2 -2
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +1 -1
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +14 -2
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/hooks/api/workflow-runs.js +1 -0
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/components/job-graph/job-node.test.tsx +2 -2
- package/src/components/workflow-run-duration-label.tsx +21 -44
- package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +121 -0
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +31 -0
- package/src/components/workflow-run-list/workflow-run-row.tsx +7 -6
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +0 -38
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +73 -21
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +6 -27
- package/src/components/workflow-status/status-visuals.ts +0 -4
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +0 -1
- package/src/components/workflow-status/workflow-status-icon.tsx +0 -18
- package/src/core/entities/job-status.test.ts +47 -1
- package/src/core/entities/job.ts +15 -3
- package/src/core/entities/workflow-run.ts +21 -134
- package/src/core/workflow-run.test.ts +15 -0
- package/src/core/workflow-run.ts +0 -8
- package/src/hooks/api/workflow-run-mapper.ts +20 -1
- package/src/hooks/api/workflow-runs.ts +1 -1
- package/test/fixtures/workflow-run.ts +60 -13
- package/tsconfig.build.tsbuildinfo +1 -1
- 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":";AAAA,SAAQA,iBAAiB,QAAO,2BAA2B;AAC3D,SAAQC,iBAAiB,QAAO,qBAAqB;AACrD,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,MAAM,QAAO,2BAA2B;AAChD,SACEC,YAAY,EACZC,mBAAmB,EACnBC,gBAAgB,EAChBC,mBAAmB,QACd,kCAAkC;AACzC,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,YAAY,QAAO,kCAAkC;AAC7D,SAAQC,kBAAkB,QAAO,gCAAgC;AACjE,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,EAAEC,MAAM,EAAEC,IAAI,QAAO,+BAA+B;AAChE,SAAQC,KAAK,QAAO,QAAQ;AAC5B,SAAQC,sBAAsB,QAAO,2CAA2C;AAChF,SACEC,qBAAqB,EAErBC,qBAAqB,EAGrBC,sBAAsB,EACtBC,wBAAwB,QACnB,wBAAwB;AAC/B,SAAQC,wBAAwB,QAAO,oCAAoC;AAC3E,SAAQC,uBAAuB,QAAO,uCAAuC;AAC7E,SAAQC,0BAA0B,QAAO,qCAAqC;AAE9E,MAAMC,8BAA8BC,KAAKC,GAAG,IACvC;OAAIR;IAAuB;CAAkB,CAACS,GAAG,CAClD,CAACC,SAAWN,wBAAwBM,QAAQC,KAAK,CAACC,MAAM;AAG5D,MAAMC,kCACJ;AAeF,OAAO,SAASC,mBAAmB,EACjCC,aAAa,EACbC,WAAW,EACXC,GAAG,EACHC,aAAa,KAAK,EAClBC,QAAQ,EACRC,eAAe,KAAK,EACpBC,OAAO,EACPC,aAAa,EACW;IACxB,MAAMC,YAAY1B;IAClB,MAAM2B,UAAUtB,yBAAyBe;IACzC,MAAMP,SAASN,wBAAwBoB,QAAQd,MAAM;IACrD,MAAMe,SAASC,wBAAwBT;IACvC,MAAMU,YAAYC,2BAA2BH,QAAQN,UAAUE;IAC/D,MAAMQ,kBACJP,iBAAiBA,gBAAgB,KAAKP,iBAAiBC,cACnD;QAACD;QAAeC;QAAaM;IAAa,IAC1C;IACN,MAAMQ,kBAAkBN,QAAQO,QAAQ;IACxC,4FAA4F;IAC5F,6CAA6C;IAC7C,MAAMC,cACJR,QAAQd,MAAM,KAAK,YAAYO,IAAIgB,IAAI,CAACrB,MAAM,GAAG,IAAIX,uBAAuBgB,IAAIgB,IAAI,IAAI;IAC1F,MAAM,EAACC,KAAKC,cAAc,EAAEC,aAAaC,kBAAkB,EAAC,GAC1DjD,mBAAoC6B,IAAIqB,IAAI;IAE9C,qBACE,KAAChD;QAAmBiD,YAAY;QAAMC,yBAAyB;kBAC7D,cAAA,KAACC;YACCC,mBAAiBnB;YACjBoB,WAAU;sBAEV,cAAA,MAACC;gBAAID,WAAU;;kCACb,KAACC;wBAAID,WAAU;kCACb,cAAA,MAACC;4BAAID,WAAU;;8CACb,KAAC7D;oCAAM+D,SAASnC,OAAOoC,KAAK;oCAAEC,MAAK;8CACjC,cAAA,KAACC;wCAAKL,WAAU;wCAAcM,OAAO;4CAACC,OAAO,GAAG5C,4BAA4B,EAAE,CAAC;wCAAA;kDAC5EI,OAAOC,KAAK;;;8CAIjB,MAACpB;;sDACC,KAACE;4CAAe0D,OAAO;sDACrB,cAAA,KAACxD;gDAAOyD,IAAG;gDAAKC,IAAI9B;gDAAWsB,SAAQ;gDAAKF,WAAU;0DACpD,cAAA,KAACK;oDAAKd,KAAKC;oDAAgBQ,WAAU;8DAClC1B,IAAIqB,IAAI;;;;wCAIdD,mCACC,KAAC7C;sDACC,cAAA,KAACI;gDAAKwD,IAAG;gDAAOL,MAAK;gDAAKJ,WAAU;0DACjC1B,IAAIqB,IAAI;;6CAGX;;;;;;oBAKTX,0BACC,KAACiB;wBAAID,WAAU;kCACb,cAAA,KAACW;4BACC7B,QAAQA;4BACRP,YAAYA;4BACZC,UAAUA;4BACVC,cAAcA;4BACdC,SAASA;;yBAGX;kCAEJ,MAACuB;wBAAID,WAAU;;4BACZ1B,IAAIsC,MAAM,KAAK,qBACd;;kDACE,KAACzD;wCAAuBmB,KAAKA;;oCAC5BY,mBAAmBZ,IAAIuC,mBAAmB,iBAAG,KAAC3E,yBAAuB;;iCAEtE;4BAEHgD,gCACC,KAACxB;gCACCU,eAAec,gBAAgBd,aAAa;gCAC5CC,aAAaa,gBAAgBb,WAAW;gCACxCC,KAAKA;gCACLK,eAAeO,gBAAgBP,aAAa;iCAE5C;4BAEHL,IAAIuC,mBAAmB,iBACtB;;oCACG3B,gCAAkB,KAAChD,yBAAuB;kDAC3C,KAACmE;wCAAKL,WAAU;kDACd,cAAA,MAACpD;;8DACC,KAACE;oDAAe0D,OAAO;8DACrB,cAAA,MAACM;wDACCC,MAAK;wDACLC,cAAY1C,IAAI2C,YAAY;wDAC5BjB,WAAU;;0EAEV,KAAC/D;gEACCiF,UAAU5C,IAAI6C,eAAe;gEAC7BC,QAAQ9C,IAAI+C,aAAa;gEACzBC,eAAY;gEACZtB,WAAU;;0EAEZ,KAAC/C;gEAAKwD,IAAG;gEAAOL,MAAK;gEAAKJ,WAAU;0EACjC1B,IAAIuC,mBAAmB;;;;;8DAI9B,KAAChE;8DACC,cAAA,KAACI;wDAAKwD,IAAG;wDAAOL,MAAK;wDAAKJ,WAAU;kEACjC1B,IAAI2C,YAAY;;;;;;;iCAMzB;4BAEH3C,IAAIsC,MAAM,KAAK,QAAQ1B,mBAAmBZ,IAAIuC,mBAAmB,iBAChE,KAAC3E,yBACC;0CACJ,KAACQ;gCACC6E,OAAOjD,IAAIkD,UAAU,CAACC,SAAS;gCAC/BzB,WAAU;;4BAGXb,gCACC;;kDACE,KAACjD;kDACD,KAACsB;wCACC4B,UAAUD;wCACVa,WAAU;;;iCAGZ;4BAEHX,4BACC;;kDACE,KAACnD;kDACD,MAACmE;wCAAKL,WAAU;;0DACd,KAAC/C;gDAAKwD,IAAG;gDAAOL,MAAK;gDAAKJ,WAAU;0DAAW;;0DAG/C,KAACjD;gDAAK0D,IAAG;gDAAOP,SAAQ;gDAAQF,WAAU;0DACvCX,YAAYqC,WAAW;;;;;iCAI5B;;;;;;;AAMhB;AAEA,SAASf,sBAAsB,EAC7B7B,MAAM,EACNP,UAAU,EACVC,QAAQ,EACRC,YAAY,EACZC,OAAO,EAOR;IACC,IAAII,WAAW,QAAQ,OAAO;IAE9B,IAAIA,WAAW,UAAU;QACvB,IAAI,CAACN,UAAU,OAAO;QAEtB,qBACE,KAACpC;YACC2E,MAAK;YACLb,SAAQ;YACRE,MAAK;YACLuB,WAAWpD;YACXqD,UAAUrD;YACVsD,SAASrD;sBACV;;IAIL;IAEA,IAAIM,WAAW,aAAa;QAC1B,IAAI,CAACJ,SAAS,OAAO;QAErB,qBACE,KAACtC;YACC2E,MAAK;YACLb,SAAQ;YACRE,MAAK;YACLJ,WAAW9B;YACXyD,WAAWlD;YACXmD,UAAUnD;YACVoD,SAAS,IAAMnD,QAAQ;sBACxB;;IAIL;IAEA,IAAI,CAACA,SAAS,OAAO;IAErB,qBACE,MAACrC;;0BACC,KAACG;gBAAoBgE,OAAO;0BAC1B,cAAA,KAACpE;oBACC2E,MAAK;oBACLb,SAAQ;oBACRE,MAAK;oBACLJ,WAAW9B;oBACX4D,WAAU;oBACVH,WAAWlD;oBACXmD,UAAUnD;8BACX;;;0BAIH,MAACnC;gBAAoByF,OAAM;;kCACzB,KAACxF;wBAAiBqF,UAAUnD;wBAAcuD,UAAU,IAAMtD,QAAQ;kCAAQ;;kCAG1E,KAACnC;wBAAiBqF,UAAUnD;wBAAcuD,UAAU,IAAMtD,QAAQ;kCAAW;;;;;;AAMrF;AAEA,SAASK,wBAAwBT,GAAsB;IACrD,IAAIA,IAAIkD,UAAU,CAACS,OAAO,KAAK3D,IAAI4D,cAAc,EAAE,OAAO;IAC1D,IAAI,CAAC9E,sBAAsBkB,IAAIkD,UAAU,CAACzD,MAAM,GAAG,OAAO;IAC1D,IAAIO,IAAIkD,UAAU,CAACzD,MAAM,KAAK,eAAe,CAACoE,yBAAyB7D,MAAM,OAAO;IACpF,OAAO;AACT;AAEA,SAASW,2BACPH,MAAyB,EACzBN,QAAkC,EAClCE,OAA2D;IAE3D,IAAII,WAAW,UAAU,OAAON,aAAa4D;IAC7C,IAAItD,WAAW,eAAeA,WAAW,cAAc,OAAOJ,YAAY0D;IAC1E,OAAO;AACT;AAEA,SAASD,yBAAyB7D,GAAsB;IACtD,IAAI,CAAC+D,mBAAmB/D,MAAM,OAAO;IAErC,OAAOA,IAAIgB,IAAI,CAACgD,IAAI,CAAC,CAACC,MAAQA,IAAIxE,MAAM,KAAK,YAAYwE,IAAIxE,MAAM,KAAK;AAC1E;AAEA,SAASsE,mBAAmB/D,GAAsB;IAChD,OAAO,UAAUA,OAAOkE,MAAMC,OAAO,CAACnE,IAAIgB,IAAI;AAChD"}
|
|
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":";AAAA,SAAQA,iBAAiB,QAAO,2BAA2B;AAC3D,SAAQC,iBAAiB,QAAO,qBAAqB;AACrD,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,MAAM,QAAO,2BAA2B;AAChD,SACEC,YAAY,EACZC,mBAAmB,EACnBC,gBAAgB,EAChBC,mBAAmB,QACd,kCAAkC;AACzC,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,YAAY,QAAO,kCAAkC;AAC7D,SAAQC,kBAAkB,QAAO,gCAAgC;AACjE,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,MAAM,EAAEC,IAAI,QAAO,+BAA+B;AAC1D,SAAQC,KAAK,QAAO,QAAQ;AAC5B,SAAQC,sBAAsB,QAAO,2CAA2C;AAChF,SACEC,qBAAqB,EAErBC,qBAAqB,QAGhB,wBAAwB;AAC/B,SAAQC,wBAAwB,QAAO,oCAAoC;AAC3E,SAAQC,uBAAuB,QAAO,uCAAuC;AAC7E,SAAQC,0BAA0B,QAAO,qCAAqC;AAE9E,MAAMC,8BAA8BC,KAAKC,GAAG,IACvCN,sBAAsBO,GAAG,CAAC,CAACC,SAAWN,wBAAwBM,QAAQC,KAAK,CAACC,MAAM;AAEvF,MAAMC,kCACJ;AAeF,OAAO,SAASC,mBAAmB,EACjCC,aAAa,EACbC,WAAW,EACXC,GAAG,EACHC,aAAa,KAAK,EAClBC,QAAQ,EACRC,eAAe,KAAK,EACpBC,OAAO,EACPC,aAAa,EACW;IACxB,MAAMC,YAAYxB;IAClB,MAAMW,SAASN,wBAAwBa,IAAIO,UAAU,CAACd,MAAM;IAC5D,MAAMe,SAASC,wBAAwBT;IACvC,MAAMU,YAAYC,2BAA2BH,QAAQN,UAAUE;IAC/D,MAAMQ,kBACJP,iBAAiBA,gBAAgB,KAAKP,iBAAiBC,cACnD;QAACD;QAAeC;QAAaM;IAAa,IAC1C;IACN,MAAMQ,kBAAkBb,IAAIO,UAAU,CAACM,eAAe;IACtD,MAAMC,aAAad,IAAIe,sBAAsB;IAC7C,MAAM,EAACC,KAAKC,cAAc,EAAEC,aAAaC,kBAAkB,EAAC,GAC1D7C,mBAAoC0B,IAAIoB,IAAI;IAE9C,qBACE,KAAC5C;QAAmB6C,YAAY;QAAMC,yBAAyB;kBAC7D,cAAA,KAACC;YACCC,mBAAiBlB;YACjBmB,WAAU;sBAEV,cAAA,MAACC;gBAAID,WAAU;;kCACb,KAACC;wBAAID,WAAU;kCACb,cAAA,MAACC;4BAAID,WAAU;;8CACb,KAACzD;oCAAM2D,SAASlC,OAAOmC,KAAK;oCAAEC,MAAK;8CACjC,cAAA,KAACC;wCAAKL,WAAU;wCAAcM,OAAO;4CAACC,OAAO,GAAG3C,4BAA4B,EAAE,CAAC;wCAAA;kDAC5EI,OAAOC,KAAK;;;8CAIjB,MAACjB;;sDACC,KAACE;4CAAesD,OAAO;sDACrB,cAAA,KAACrD;gDAAOsD,IAAG;gDAAKC,IAAI7B;gDAAWqB,SAAQ;gDAAKF,WAAU;0DACpD,cAAA,KAACK;oDAAKd,KAAKC;oDAAgBQ,WAAU;8DAClCzB,IAAIoB,IAAI;;;;wCAIdD,mCACC,KAACzC;sDACC,cAAA,KAACG;gDAAKqD,IAAG;gDAAOL,MAAK;gDAAKJ,WAAU;0DACjCzB,IAAIoB,IAAI;;6CAGX;;;;;;oBAKTV,0BACC,KAACgB;wBAAID,WAAU;kCACb,cAAA,KAACW;4BACC5B,QAAQA;4BACRP,YAAYA;4BACZC,UAAUA;4BACVC,cAAcA;4BACdC,SAASA;;yBAGX;kCAEJ,MAACsB;wBAAID,WAAU;;4BACZzB,IAAIqC,MAAM,KAAK,qBACd;;kDACE,KAACtD;wCAAuBiB,KAAKA;;oCAC5BY,mBAAmBZ,IAAIsC,mBAAmB,iBAAG,KAACvE,yBAAuB;;iCAEtE;4BAEH6C,gCACC,KAACxB;gCACCU,eAAec,gBAAgBd,aAAa;gCAC5CC,aAAaa,gBAAgBb,WAAW;gCACxCC,KAAKA;gCACLK,eAAeO,gBAAgBP,aAAa;iCAE5C;4BAEHL,IAAIsC,mBAAmB,iBACtB;;oCACG1B,gCAAkB,KAAC7C,yBAAuB;kDAC3C,KAAC+D;wCAAKL,WAAU;kDACd,cAAA,MAAChD;;8DACC,KAACE;oDAAesD,OAAO;8DACrB,cAAA,MAACM;wDACCC,MAAK;wDACLC,cAAYzC,IAAI0C,YAAY;wDAC5BjB,WAAU;;0EAEV,KAAC3D;gEACC6E,UAAU3C,IAAI4C,eAAe;gEAC7BC,QAAQ7C,IAAI8C,aAAa;gEACzBC,eAAY;gEACZtB,WAAU;;0EAEZ,KAAC5C;gEAAKqD,IAAG;gEAAOL,MAAK;gEAAKJ,WAAU;0EACjCzB,IAAIsC,mBAAmB;;;;;8DAI9B,KAAC5D;8DACC,cAAA,KAACG;wDAAKqD,IAAG;wDAAOL,MAAK;wDAAKJ,WAAU;kEACjCzB,IAAI0C,YAAY;;;;;;;iCAMzB;4BAEH1C,IAAIqC,MAAM,KAAK,QAAQzB,mBAAmBZ,IAAIsC,mBAAmB,iBAChE,KAACvE,yBACC;0CACJ,KAACQ;gCACCyE,OAAOhD,IAAIO,UAAU,CAAC0C,SAAS;gCAC/BxB,WAAU;;4BAGXZ,gCACC;;kDACE,KAAC9C;kDACD,KAACmB;wCACCgE,UAAUrC;wCACVC,YAAYA;wCACZW,WAAU;;;iCAGZ;;;;;;;AAMhB;AAEA,SAASW,sBAAsB,EAC7B5B,MAAM,EACNP,UAAU,EACVC,QAAQ,EACRC,YAAY,EACZC,OAAO,EAOR;IACC,IAAII,WAAW,QAAQ,OAAO;IAE9B,IAAIA,WAAW,UAAU;QACvB,IAAI,CAACN,UAAU,OAAO;QAEtB,qBACE,KAACjC;YACCuE,MAAK;YACLb,SAAQ;YACRE,MAAK;YACLsB,WAAWlD;YACXmD,UAAUnD;YACVoD,SAASnD;sBACV;;IAIL;IAEA,IAAIM,WAAW,aAAa;QAC1B,IAAI,CAACJ,SAAS,OAAO;QAErB,qBACE,KAACnC;YACCuE,MAAK;YACLb,SAAQ;YACRE,MAAK;YACLJ,WAAW7B;YACXuD,WAAWhD;YACXiD,UAAUjD;YACVkD,SAAS,IAAMjD,QAAQ;sBACxB;;IAIL;IAEA,IAAI,CAACA,SAAS,OAAO;IAErB,qBACE,MAAClC;;0BACC,KAACG;gBAAoB4D,OAAO;0BAC1B,cAAA,KAAChE;oBACCuE,MAAK;oBACLb,SAAQ;oBACRE,MAAK;oBACLJ,WAAW7B;oBACX0D,WAAU;oBACVH,WAAWhD;oBACXiD,UAAUjD;8BACX;;;0BAIH,MAAChC;gBAAoBoF,OAAM;;kCACzB,KAACnF;wBAAiBgF,UAAUjD;wBAAcqD,UAAU,IAAMpD,QAAQ;kCAAQ;;kCAG1E,KAAChC;wBAAiBgF,UAAUjD;wBAAcqD,UAAU,IAAMpD,QAAQ;kCAAW;;;;;;AAMrF;AAEA,SAASK,wBAAwBT,GAAsB;IACrD,IAAIA,IAAIO,UAAU,CAACkD,OAAO,KAAKzD,IAAI0D,cAAc,EAAE,OAAO;IAC1D,IAAI,CAAC1E,sBAAsBgB,IAAIO,UAAU,CAACd,MAAM,GAAG,OAAO;IAC1D,IAAIO,IAAIO,UAAU,CAACd,MAAM,KAAK,eAAe,CAACkE,yBAAyB3D,MAAM,OAAO;IACpF,OAAO;AACT;AAEA,SAASW,2BACPH,MAAyB,EACzBN,QAAkC,EAClCE,OAA2D;IAE3D,IAAII,WAAW,UAAU,OAAON,aAAa0D;IAC7C,IAAIpD,WAAW,eAAeA,WAAW,cAAc,OAAOJ,YAAYwD;IAC1E,OAAO;AACT;AAEA,SAASD,yBAAyB3D,GAAsB;IACtD,IAAI,CAAC6D,mBAAmB7D,MAAM,OAAO;IAErC,OAAOA,IAAI8D,IAAI,CAACC,IAAI,CAAC,CAACC,MAAQA,IAAIvE,MAAM,KAAK,YAAYuE,IAAIvE,MAAM,KAAK;AAC1E;AAEA,SAASoE,mBAAmB7D,GAAsB;IAChD,OAAO,UAAUA,OAAOiE,MAAMC,OAAO,CAAClE,IAAI8D,IAAI;AAChD"}
|
|
@@ -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,
|
|
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
|
|
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,+
|
|
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 === '
|
|
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'>
|
|
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":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,2BAA2B,
|
|
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'
|
|
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 {
|
|
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`
|
|
9
|
-
*
|
|
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'
|
|
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"
|
|
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:
|
|
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":"AAAA,OAAO,
|
|
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
|