@shipfox/client-workflows 15.0.0 → 16.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 (91) hide show
  1. package/.storybook/preview.css +4 -0
  2. package/.turbo/turbo-build.log +1 -1
  3. package/CHANGELOG.md +21 -0
  4. package/dist/components/job-detail/job-detail-header.d.ts +8 -1
  5. package/dist/components/job-detail/job-detail-header.d.ts.map +1 -1
  6. package/dist/components/job-detail/job-detail-header.js +25 -12
  7. package/dist/components/job-detail/job-detail-header.js.map +1 -1
  8. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
  9. package/dist/components/job-detail/job-detail-view.js +17 -2
  10. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  11. package/dist/components/workflow-run-summary/workflow-run-summary.js +1 -0
  12. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  13. package/dist/components/workflow-run-tabs/index.d.ts +3 -0
  14. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
  15. package/dist/components/workflow-run-tabs/index.js +3 -0
  16. package/dist/components/workflow-run-tabs/index.js.map +1 -1
  17. package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts +16 -0
  18. package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts.map +1 -0
  19. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +50 -0
  20. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -0
  21. package/dist/components/workflow-run-tabs/run-annotation-item.d.ts +20 -0
  22. package/dist/components/workflow-run-tabs/run-annotation-item.d.ts.map +1 -0
  23. package/dist/components/workflow-run-tabs/run-annotation-item.js +102 -0
  24. package/dist/components/workflow-run-tabs/run-annotation-item.js.map +1 -0
  25. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +32 -0
  26. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -0
  27. package/dist/components/workflow-run-tabs/run-annotation-list.js +202 -0
  28. package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -0
  29. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +1 -1
  30. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
  31. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +87 -22
  32. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
  33. package/dist/components/workflow-run-tabs/severity-visuals.d.ts +20 -0
  34. package/dist/components/workflow-run-tabs/severity-visuals.d.ts.map +1 -0
  35. package/dist/components/workflow-run-tabs/severity-visuals.js +31 -0
  36. package/dist/components/workflow-run-tabs/severity-visuals.js.map +1 -0
  37. package/dist/components/workflow-run-view/run-workspace-nav.d.ts +1 -1
  38. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
  39. package/dist/components/workflow-run-view/run-workspace-nav.js +9 -3
  40. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  41. package/dist/components/workflow-run-view/workflow-run-view.d.ts +1 -3
  42. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  43. package/dist/components/workflow-run-view/workflow-run-view.js +150 -52
  44. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  45. package/dist/core/run-annotation.d.ts +73 -0
  46. package/dist/core/run-annotation.d.ts.map +1 -0
  47. package/dist/core/run-annotation.js +125 -0
  48. package/dist/core/run-annotation.js.map +1 -0
  49. package/dist/hooks/api/run-annotation-mapper.d.ts +10 -0
  50. package/dist/hooks/api/run-annotation-mapper.d.ts.map +1 -0
  51. package/dist/hooks/api/run-annotation-mapper.js +22 -0
  52. package/dist/hooks/api/run-annotation-mapper.js.map +1 -0
  53. package/dist/hooks/api/run-annotations.d.ts +25 -0
  54. package/dist/hooks/api/run-annotations.d.ts.map +1 -0
  55. package/dist/hooks/api/run-annotations.js +78 -0
  56. package/dist/hooks/api/run-annotations.js.map +1 -0
  57. package/dist/routes/inputs.d.ts +7 -2
  58. package/dist/routes/inputs.d.ts.map +1 -1
  59. package/dist/routes/inputs.js +5 -6
  60. package/dist/routes/inputs.js.map +1 -1
  61. package/dist/tsconfig.test.tsbuildinfo +1 -1
  62. package/package.json +10 -9
  63. package/src/components/job-detail/job-detail-header.test.tsx +3 -0
  64. package/src/components/job-detail/job-detail-header.tsx +30 -5
  65. package/src/components/job-detail/job-detail-view.stories.tsx +5 -0
  66. package/src/components/job-detail/job-detail-view.tsx +18 -1
  67. package/src/components/workflow-run-summary/workflow-run-summary.tsx +1 -1
  68. package/src/components/workflow-run-tabs/index.ts +3 -0
  69. package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +63 -0
  70. package/src/components/workflow-run-tabs/run-annotation-item.tsx +123 -0
  71. package/src/components/workflow-run-tabs/run-annotation-list.tsx +258 -0
  72. package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +77 -3
  73. package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +92 -17
  74. package/src/components/workflow-run-tabs/run-annotations.stories.tsx +293 -0
  75. package/src/components/workflow-run-tabs/severity-visuals.ts +37 -0
  76. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +44 -0
  77. package/src/components/workflow-run-view/run-workspace-nav.tsx +15 -2
  78. package/src/components/workflow-run-view/workflow-run-view.test.tsx +246 -20
  79. package/src/components/workflow-run-view/workflow-run-view.tsx +148 -30
  80. package/src/core/run-annotation.test.ts +315 -0
  81. package/src/core/run-annotation.ts +238 -0
  82. package/src/hooks/api/run-annotation-mapper.ts +30 -0
  83. package/src/hooks/api/run-annotations.ts +137 -0
  84. package/src/pages/workflow-job-detail-page.test.tsx +66 -0
  85. package/src/routes/inputs.ts +7 -2
  86. package/tsconfig.build.tsbuildinfo +1 -1
  87. package/dist/core/workflow-run-tabs.d.ts +0 -9
  88. package/dist/core/workflow-run-tabs.d.ts.map +0 -1
  89. package/dist/core/workflow-run-tabs.js +0 -3
  90. package/dist/core/workflow-run-tabs.js.map +0 -1
  91. package/src/core/workflow-run-tabs.ts +0 -8
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@shipfox/react-ui/select';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {type ReactNode, useEffect} from 'react';\nimport type {Job, WorkflowRunRerunMode} from '#core/workflow-run.js';\nimport type {RunAnnotationSummary} from '#core/workflow-run-tabs.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n type WorkflowRunsSearch,\n type WorkflowRunTab,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport type {JobGraphSelectionSource} from '../job-graph/types.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {RunAnnotationSummaryLine, RunAnnotationsEmpty} from '../workflow-run-tabs/index.js';\nimport {WorkflowSourceContent} from '../workflow-source-panel/index.js';\nimport {RunWorkspaceNav} from './run-workspace-nav.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunContentSkeleton,\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ntype RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;\n\nexport interface WorkflowRunViewProps {\n projectId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n runAttempt?: number | undefined;\n selection?: WorkflowRunsSearch | undefined;\n tab?: WorkflowRunTab | undefined;\n annotationSummary?: RunAnnotationSummary | undefined;\n activeJobId?: string | undefined;\n jobSearch?: WorkflowJobSearch | undefined;\n jobContent?: ReactNode | undefined;\n}\n\n/**\n * The persistent run workspace. Run-level sections and dedicated job routes share the same\n * header and job index, while the all-jobs Summary always opens on the dependency graph.\n */\nexport function WorkflowRunView({\n projectId,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n selection,\n tab,\n annotationSummary,\n activeJobId,\n jobSearch,\n jobContent,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({\n workflowRunId,\n runAttempt: selection?.runAttempt ?? runAttempt,\n });\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n query={runQuery}\n rerunMutation={rerunMutation}\n runAttempt={runAttempt}\n selection={selection}\n tab={tab}\n annotationSummary={annotationSummary}\n activeJobId={activeJobId}\n jobSearch={jobSearch}\n jobContent={jobContent}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceSlug,\n projectSlug,\n query,\n rerunMutation,\n runAttempt,\n selection,\n tab,\n annotationSummary,\n activeJobId,\n jobSearch,\n jobContent,\n}: {\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n runAttempt: number | undefined;\n selection: WorkflowRunsSearch | undefined;\n tab: WorkflowRunTab | undefined;\n annotationSummary: RunAnnotationSummary | undefined;\n activeJobId: string | undefined;\n jobSearch: WorkflowJobSearch | undefined;\n jobContent: ReactNode | undefined;\n}) {\n const navigate = useNavigate();\n const runData = query.data;\n const activeSection = runWorkspaceSection(tab);\n const cancelMutation = useCancelWorkflowRunMutation(runData);\n const sourceSnapshot = runData?.sourceSnapshot ?? null;\n const resolvedSelection =\n runData && selection ? resolveWorkflowRunSelection({run: runData, selection}) : undefined;\n const highlightedLineRange = resolvedSelection?.step?.sourceLocation ?? null;\n\n useEffect(() => {\n const hasLegacyJobSelection = Boolean(\n selection?.jobId ||\n selection?.jobExecutionId ||\n selection?.stepId ||\n selection?.stepAttemptId,\n );\n if (\n activeJobId ||\n activeSection !== 'summary' ||\n !hasLegacyJobSelection ||\n !resolvedSelection?.job ||\n !runData ||\n !workspaceSlug ||\n !projectSlug\n ) {\n return;\n }\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',\n params: {\n workspaceSlug,\n projectSlug,\n workflowRunId: runData.id,\n jobId: resolvedSelection.job.id,\n },\n search: workflowJobSearchParams({\n jobExecutionId: selection?.jobExecutionId,\n stepId: selection?.stepId,\n stepAttemptId: selection?.stepAttemptId,\n runAttempt: selection?.runAttempt,\n }) as never,\n replace: true,\n });\n }, [\n activeJobId,\n activeSection,\n navigate,\n projectSlug,\n resolvedSelection?.job,\n runData,\n selection,\n workspaceSlug,\n ]);\n\n async function rerun(mode: WorkflowRunRerunMode) {\n if (!runData || !workspaceSlug || !projectSlug) {\n toast.error('Could not start re-run from this route.');\n return;\n }\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: run.id},\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectGraphJob(jobId: string | undefined, source: JobGraphSelectionSource = 'pointer') {\n if (source !== 'pointer' || !jobId || !runData || !workspaceSlug || !projectSlug) return;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id, jobId},\n search: workflowJobSearchParams({\n runAttempt: selection?.runAttempt ?? runAttempt ?? runData.runAttempt.attempt,\n }) as never,\n });\n }\n\n function selectAnnotationJob(jobId: string | undefined) {\n if (!runData || !workspaceSlug || !projectSlug) return;\n const nextSearch: WorkflowRunsSearch = {...selection, tab: 'annotations'};\n if (jobId) nextSearch.jobId = jobId;\n else delete nextSearch.jobId;\n delete nextSearch.jobExecutionId;\n delete nextSearch.stepId;\n delete nextSearch.stepAttemptId;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id},\n search: workflowRunSearchParams(nextSearch, nextSearch) as never,\n });\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => toast.error(cancelErrorMessage(error)),\n });\n }\n\n const fatalError = query.isError && runData === undefined;\n const loadingOrError = fatalError ? (\n query.error instanceof ApiError && query.error.status === 404 ? (\n <WorkflowRunNotFound />\n ) : (\n <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />\n )\n ) : query.isPending || runData === undefined ? (\n <WorkflowRunContentSkeleton />\n ) : null;\n\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {runData ? (\n <WorkflowRunSummary\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n ) : (\n <WorkflowRunSkeleton />\n )}\n {runData && query.isError ? <WorkflowRunStaleError query={query} /> : null}\n\n <div\n data-run-workspace-layout\n className=\"flex min-h-0 min-w-0 flex-1 flex-col border-t border-border-neutral-base min-[768px]:flex-row\"\n >\n {runData && workspaceSlug && projectSlug ? (\n <RunWorkspaceNav\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n activeSection={activeSection}\n currentJobId={activeJobId}\n jobSearch={jobSearch}\n annotationSummary={annotationSummary}\n />\n ) : (\n <RunWorkspaceNavSkeleton />\n )}\n\n <div\n data-run-workspace-content\n className=\"flex min-h-0 min-w-0 flex-1 flex-col bg-background-neutral-base\"\n >\n {runData && jobContent ? (\n jobContent\n ) : runData ? (\n <RunSectionContent\n section={activeSection}\n run={runData}\n annotationSummary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n selection={selection}\n selectedJobId={selection?.jobId}\n onSelectGraphJob={selectGraphJob}\n onSelectAnnotationJob={selectAnnotationJob}\n sourceSnapshot={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n />\n ) : (\n <div className=\"min-h-0 flex-1 overflow-auto p-24\">{loadingOrError}</div>\n )}\n </div>\n </div>\n </div>\n );\n}\n\nfunction RunSectionContent({\n section,\n run,\n annotationSummary,\n workspaceSlug,\n projectSlug,\n selection,\n selectedJobId,\n onSelectGraphJob,\n onSelectAnnotationJob,\n sourceSnapshot,\n highlightedLineRange,\n}: {\n section: RunWorkspaceSection;\n run: NonNullable<ReturnType<typeof useWorkflowRunAttemptQuery>['data']>;\n annotationSummary: RunAnnotationSummary | undefined;\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n selection: WorkflowRunsSearch | undefined;\n selectedJobId: string | undefined;\n onSelectGraphJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;\n onSelectAnnotationJob: (jobId: string | undefined) => void;\n sourceSnapshot: NonNullable<\n ReturnType<typeof useWorkflowRunAttemptQuery>['data']\n >['sourceSnapshot'];\n highlightedLineRange: NonNullable<\n ReturnType<typeof useWorkflowRunAttemptQuery>['data']\n >['jobs'][number]['jobExecutions'][number]['steps'][number]['sourceLocation'];\n}) {\n if (section === 'summary') {\n return (\n <section aria-label=\"All jobs summary\" className=\"min-h-0 flex-1 overflow-auto pb-24 pt-16\">\n <div className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24\">\n <JobGraph\n run={run}\n selectedJobId={selectedJobId}\n onSelectedJobChange={onSelectGraphJob}\n className=\"min-h-160 overflow-hidden\"\n />\n </div>\n </section>\n );\n }\n\n if (section === 'annotations') {\n const selectedJob = run.jobs.find((job) => job.id === selectedJobId);\n return (\n <section aria-label=\"Run annotations\" className=\"min-h-0 flex-1 overflow-auto pb-24 pt-16\">\n <div className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24\">\n <div className=\"flex flex-wrap items-center justify-between gap-8\">\n <RunAnnotationSummaryLine\n summary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n search={selection}\n />\n <AnnotationJobFilter\n jobs={run.jobs}\n selectedJobId={selectedJob?.id}\n onSelect={onSelectAnnotationJob}\n />\n </div>\n <RunAnnotationsEmpty jobName={selectedJob?.displayName} />\n </div>\n </section>\n );\n }\n\n return (\n <section aria-label=\"Workflow source\" className=\"min-h-0 flex-1 overflow-auto pb-24 pt-16\">\n <div className=\"mx-auto flex min-h-full w-full max-w-[1120px] px-24\">\n {sourceSnapshot ? (\n <WorkflowSourceContent\n source={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n ) : (\n <EmptyState\n className=\"min-h-160 w-full\"\n icon=\"fileDamageLine\"\n title=\"Source snapshot unavailable\"\n description={\n run.isTemporary\n ? 'Temporary runs do not capture workflow source.'\n : 'This run was created before workflow source snapshots were captured.'\n }\n />\n )}\n </div>\n </section>\n );\n}\n\nconst ALL_ANNOTATION_JOBS = 'all-jobs';\n\nfunction AnnotationJobFilter({\n jobs,\n selectedJobId,\n onSelect,\n}: {\n jobs: Job[];\n selectedJobId: string | undefined;\n onSelect: (jobId: string | undefined) => void;\n}) {\n const sortedJobs = [...jobs].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n\n return (\n <Select\n value={selectedJobId ?? ALL_ANNOTATION_JOBS}\n onValueChange={(value) => onSelect(value === ALL_ANNOTATION_JOBS ? undefined : value)}\n >\n <SelectTrigger\n size=\"small\"\n aria-label=\"Filter annotations by job\"\n className=\"w-full min-[480px]:w-240\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent align=\"end\">\n <SelectItem value={ALL_ANNOTATION_JOBS}>All jobs</SelectItem>\n {sortedJobs.map((job) => (\n <SelectItem key={job.id} value={job.id}>\n {job.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction RunWorkspaceNavSkeleton() {\n return (\n <aside\n aria-label=\"Loading run navigation\"\n className=\"hidden w-240 shrink-0 border-r border-border-neutral-base bg-background-neutral-background p-12 min-[768px]:block\"\n >\n <div className=\"flex flex-col gap-16\">\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n <div className=\"flex flex-col gap-6\">\n <div className=\"h-16 w-64 rounded-4 bg-background-components-subtle\" />\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n </div>\n </div>\n </aside>\n );\n}\n\nfunction runWorkspaceSection(tab: WorkflowRunTab | undefined): RunWorkspaceSection {\n return tab === 'annotations' || tab === 'source' ? tab : 'summary';\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","RelativeTimeProvider","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","toast","useNavigate","useEffect","withoutWorkflowRunSelectionSearch","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","workflowJobSearchParams","workflowRunSearchParams","JobGraph","WorkflowRunSummary","RunAnnotationSummaryLine","RunAnnotationsEmpty","WorkflowSourceContent","RunWorkspaceNav","resolveWorkflowRunSelection","WorkflowRunContentSkeleton","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","WorkflowRunView","projectId","workspaceSlug","projectSlug","workflowRunId","runAttempt","selection","tab","annotationSummary","activeJobId","jobSearch","jobContent","runQuery","rerunMutation","div","className","RunViewContent","query","navigate","runData","data","activeSection","runWorkspaceSection","cancelMutation","sourceSnapshot","resolvedSelection","run","undefined","highlightedLineRange","step","sourceLocation","hasLegacyJobSelection","Boolean","jobId","jobExecutionId","stepId","stepAttemptId","job","to","params","id","search","replace","rerun","mode","error","mutateAsync","success","previous","message","selectGraphJob","source","attempt","selectAnnotationJob","nextSearch","cancelRun","mutate","onError","cancelErrorMessage","fatalError","isError","loadingOrError","status","subject","icon","isPending","cancelling","onCancel","rerunPending","onRerun","latestAttempt","data-run-workspace-layout","currentJobId","RunWorkspaceNavSkeleton","data-run-workspace-content","RunSectionContent","section","selectedJobId","onSelectGraphJob","onSelectAnnotationJob","aria-label","onSelectedJobChange","selectedJob","jobs","find","summary","AnnotationJobFilter","onSelect","jobName","displayName","scrollHighlightedIntoView","title","description","isTemporary","ALL_ANNOTATION_JOBS","sortedJobs","sort","left","right","position","localeCompare","value","onValueChange","size","align","map","aside","code"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@shipfox/react-ui/select';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useNavigate} from '@tanstack/react-router';\nimport {type ReactNode, useEffect, useMemo} from 'react';\nimport {buildRunAnnotationList, type RunAnnotationSummary} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type WorkflowRunRerunMode} from '#core/workflow-run.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n type WorkflowRunsSearch,\n type WorkflowRunTab,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport type {JobGraphSelectionSource} from '../job-graph/types.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {RunAnnotationList, RunAnnotationSummaryLine} from '../workflow-run-tabs/index.js';\nimport {WorkflowSourceContent} from '../workflow-source-panel/index.js';\nimport {RunWorkspaceNav} from './run-workspace-nav.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunContentSkeleton,\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ntype RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;\n\nexport interface WorkflowRunViewProps {\n projectId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n runAttempt?: number | undefined;\n selection?: WorkflowRunsSearch | undefined;\n tab?: WorkflowRunTab | undefined;\n activeJobId?: string | undefined;\n jobSearch?: WorkflowJobSearch | undefined;\n jobContent?: ReactNode | undefined;\n}\n\n/**\n * The persistent run workspace. Run-level sections and dedicated job routes share the same\n * header and job index, while the all-jobs Summary always opens on the dependency graph.\n */\nexport function WorkflowRunView({\n projectId,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n selection,\n tab,\n activeJobId,\n jobSearch,\n jobContent,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({\n workflowRunId,\n runAttempt: selection?.runAttempt ?? runAttempt,\n });\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n // Annotations are read on their own cadence rather than hydrated into the run, so the two\n // modules stay decoupled. The key is shared, so the job page's count chip costs no extra fetch.\n //\n // Polling is scoped to the surface that renders bodies. The read has no counts-only mode, so\n // every poll transfers whole Markdown bodies; doing that every four seconds behind Summary,\n // Source, and the job log spends megabytes to keep one rail number warm. Off that surface the\n // counts still refresh on navigation and on window focus.\n const annotationsQuery = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: runQuery.data?.runAttempt.attempt,\n live:\n runWorkspaceSection(tab) === 'annotations' &&\n !activeJobId &&\n Boolean(runQuery.data) &&\n !isWorkflowRunTerminal(runQuery.data?.runAttempt.status ?? 'pending'),\n });\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n query={runQuery}\n annotations={annotationsQuery}\n rerunMutation={rerunMutation}\n runAttempt={runAttempt}\n selection={selection}\n tab={tab}\n activeJobId={activeJobId}\n jobSearch={jobSearch}\n jobContent={jobContent}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceSlug,\n projectSlug,\n query,\n annotations,\n rerunMutation,\n runAttempt,\n selection,\n tab,\n activeJobId,\n jobSearch,\n jobContent,\n}: {\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n annotations: ReturnType<typeof useRunAnnotationsQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n runAttempt: number | undefined;\n selection: WorkflowRunsSearch | undefined;\n tab: WorkflowRunTab | undefined;\n activeJobId: string | undefined;\n jobSearch: WorkflowJobSearch | undefined;\n jobContent: ReactNode | undefined;\n}) {\n const navigate = useNavigate();\n const runData = query.data;\n const annotationSummary = annotations.summary;\n const activeSection = runWorkspaceSection(tab);\n const cancelMutation = useCancelWorkflowRunMutation(runData);\n const sourceSnapshot = runData?.sourceSnapshot ?? null;\n const resolvedSelection =\n runData && selection ? resolveWorkflowRunSelection({run: runData, selection}) : undefined;\n const highlightedLineRange = resolvedSelection?.step?.sourceLocation ?? null;\n\n useEffect(() => {\n const hasLegacyJobSelection = Boolean(\n selection?.jobId ||\n selection?.jobExecutionId ||\n selection?.stepId ||\n selection?.stepAttemptId,\n );\n if (\n activeJobId ||\n activeSection !== 'summary' ||\n !hasLegacyJobSelection ||\n !resolvedSelection?.job ||\n !runData ||\n !workspaceSlug ||\n !projectSlug\n ) {\n return;\n }\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',\n params: {\n workspaceSlug,\n projectSlug,\n workflowRunId: runData.id,\n jobId: resolvedSelection.job.id,\n },\n search: workflowJobSearchParams({\n jobExecutionId: selection?.jobExecutionId,\n stepId: selection?.stepId,\n stepAttemptId: selection?.stepAttemptId,\n runAttempt: selection?.runAttempt,\n }) as never,\n replace: true,\n });\n }, [\n activeJobId,\n activeSection,\n navigate,\n projectSlug,\n resolvedSelection?.job,\n runData,\n selection,\n workspaceSlug,\n ]);\n\n async function rerun(mode: WorkflowRunRerunMode) {\n if (!runData || !workspaceSlug || !projectSlug) {\n toast.error('Could not start re-run from this route.');\n return;\n }\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: run.id},\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectGraphJob(jobId: string | undefined, source: JobGraphSelectionSource = 'pointer') {\n if (source !== 'pointer' || !jobId || !runData || !workspaceSlug || !projectSlug) return;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id, jobId},\n search: workflowJobSearchParams({\n runAttempt: selection?.runAttempt ?? runAttempt ?? runData.runAttempt.attempt,\n }) as never,\n });\n }\n\n function selectAnnotationJob(jobId: string | undefined) {\n if (!runData || !workspaceSlug || !projectSlug) return;\n const nextSearch: WorkflowRunsSearch = {...selection, tab: 'annotations'};\n if (jobId) nextSearch.jobId = jobId;\n else delete nextSearch.jobId;\n delete nextSearch.jobExecutionId;\n delete nextSearch.stepId;\n delete nextSearch.stepAttemptId;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id},\n search: workflowRunSearchParams(nextSearch, nextSearch) as never,\n });\n }\n\n function clearAnnotationFilters() {\n if (!runData || !workspaceSlug || !projectSlug) return;\n const nextSearch: WorkflowRunsSearch = {...selection, tab: 'annotations'};\n delete nextSearch.jobId;\n delete nextSearch.jobExecutionId;\n delete nextSearch.stepId;\n delete nextSearch.stepAttemptId;\n delete nextSearch.severity;\n delete nextSearch.annotation;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id},\n search: workflowRunSearchParams(nextSearch, nextSearch) as never,\n replace: true,\n });\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => toast.error(cancelErrorMessage(error)),\n });\n }\n\n const fatalError = query.isError && runData === undefined;\n const loadingOrError = fatalError ? (\n query.error instanceof ApiError && query.error.status === 404 ? (\n <WorkflowRunNotFound />\n ) : (\n <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />\n )\n ) : query.isPending || runData === undefined ? (\n <WorkflowRunContentSkeleton />\n ) : null;\n\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {runData ? (\n <WorkflowRunSummary\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n ) : (\n <WorkflowRunSkeleton />\n )}\n {runData && query.isError ? <WorkflowRunStaleError query={query} /> : null}\n\n <div\n data-run-workspace-layout\n className=\"flex min-h-0 min-w-0 flex-1 flex-col border-t border-border-neutral-base min-[768px]:flex-row\"\n >\n {runData && workspaceSlug && projectSlug ? (\n <RunWorkspaceNav\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n activeSection={activeSection}\n currentJobId={activeJobId}\n jobSearch={jobSearch}\n annotationSummary={annotationSummary}\n />\n ) : (\n <RunWorkspaceNavSkeleton />\n )}\n\n <div\n data-run-workspace-content\n className=\"flex min-h-0 min-w-0 flex-1 flex-col bg-background-neutral-base\"\n >\n {runData && jobContent ? (\n jobContent\n ) : runData ? (\n <RunSectionContent\n section={activeSection}\n run={runData}\n annotations={annotations}\n annotationSummary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n selection={selection}\n selectedJobId={selection?.jobId}\n onSelectGraphJob={selectGraphJob}\n onSelectAnnotationJob={selectAnnotationJob}\n onClearAnnotationFilters={clearAnnotationFilters}\n sourceSnapshot={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n />\n ) : (\n <div className=\"min-h-0 flex-1 overflow-auto p-24\">{loadingOrError}</div>\n )}\n </div>\n </div>\n </div>\n );\n}\n\nfunction RunSectionContent({\n section,\n run,\n annotations,\n annotationSummary,\n workspaceSlug,\n projectSlug,\n selection,\n selectedJobId,\n onSelectGraphJob,\n onSelectAnnotationJob,\n onClearAnnotationFilters,\n sourceSnapshot,\n highlightedLineRange,\n}: {\n section: RunWorkspaceSection;\n run: NonNullable<ReturnType<typeof useWorkflowRunAttemptQuery>['data']>;\n annotations: ReturnType<typeof useRunAnnotationsQuery>;\n annotationSummary: RunAnnotationSummary | undefined;\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n selection: WorkflowRunsSearch | undefined;\n selectedJobId: string | undefined;\n onSelectGraphJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;\n onSelectAnnotationJob: (jobId: string | undefined) => void;\n onClearAnnotationFilters: () => void;\n sourceSnapshot: NonNullable<\n ReturnType<typeof useWorkflowRunAttemptQuery>['data']\n >['sourceSnapshot'];\n highlightedLineRange: NonNullable<\n ReturnType<typeof useWorkflowRunAttemptQuery>['data']\n >['jobs'][number]['jobExecutions'][number]['steps'][number]['sourceLocation'];\n}) {\n if (section === 'summary') {\n return (\n <section aria-label=\"All jobs summary\" className=\"min-h-0 flex-1 overflow-auto pb-24 pt-16\">\n <div className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24\">\n <Text as=\"h2\" className=\"sr-only\">\n All jobs summary\n </Text>\n <JobGraph\n run={run}\n selectedJobId={selectedJobId}\n onSelectedJobChange={onSelectGraphJob}\n className=\"min-h-160 overflow-hidden\"\n />\n </div>\n </section>\n );\n }\n\n if (section === 'annotations') {\n return (\n <RunAnnotationsSection\n run={run}\n annotations={annotations}\n annotationSummary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n selection={selection}\n selectedJobId={selectedJobId}\n onSelectAnnotationJob={onSelectAnnotationJob}\n onClearAnnotationFilters={onClearAnnotationFilters}\n />\n );\n }\n\n return (\n <section aria-label=\"Workflow source\" className=\"min-h-0 flex-1 overflow-auto pb-24 pt-16\">\n <div className=\"mx-auto flex min-h-full w-full max-w-[1120px] flex-col px-24\">\n <Text as=\"h2\" className=\"sr-only\">\n Workflow source\n </Text>\n {sourceSnapshot ? (\n <WorkflowSourceContent\n source={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n ) : (\n <EmptyState\n className=\"min-h-160 w-full\"\n icon=\"fileDamageLine\"\n title=\"Source snapshot unavailable\"\n description={\n run.isTemporary\n ? 'Temporary runs do not capture workflow source.'\n : 'This run was created before workflow source snapshots were captured.'\n }\n />\n )}\n </div>\n </section>\n );\n}\n\nfunction RunAnnotationsSection({\n run,\n annotations,\n annotationSummary,\n workspaceSlug,\n projectSlug,\n selection,\n selectedJobId,\n onSelectAnnotationJob,\n onClearAnnotationFilters,\n}: {\n run: NonNullable<ReturnType<typeof useWorkflowRunAttemptQuery>['data']>;\n annotations: ReturnType<typeof useRunAnnotationsQuery>;\n annotationSummary: RunAnnotationSummary | undefined;\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n selection: WorkflowRunsSearch | undefined;\n selectedJobId: string | undefined;\n onSelectAnnotationJob: (jobId: string | undefined) => void;\n onClearAnnotationFilters: () => void;\n}) {\n const selectedJob = run.jobs.find((job) => job.id === selectedJobId);\n const severity = selection?.severity;\n const records = annotations.annotations;\n\n const entries = useMemo(\n () =>\n records\n ? buildRunAnnotationList({\n annotations: records,\n jobs: run.jobs,\n severity,\n jobId: selectedJob?.id,\n })\n : undefined,\n [records, run.jobs, selectedJob?.id, severity],\n );\n\n return (\n <section aria-label=\"Run annotations\" className=\"min-h-0 flex-1 overflow-auto pb-24 pt-16\">\n <div className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24\">\n <Text as=\"h2\" className=\"sr-only\">\n Annotations\n </Text>\n <div className=\"flex flex-wrap items-center justify-between gap-8\">\n <RunAnnotationSummaryLine\n summary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n search={selection}\n />\n <AnnotationJobFilter\n jobs={run.jobs}\n selectedJobId={selectedJob?.id}\n onSelect={onSelectAnnotationJob}\n />\n </div>\n <RunAnnotationList\n // Remounting on a filter or run-attempt change resets the render window, so the next\n // list never inherits a \"show more\" position from different data.\n key={`${run.id}:${run.runAttempt.attempt}:${severity ?? 'all'}:${selectedJob?.id ?? 'all'}`}\n query={annotations.query}\n entries={entries}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n // A run with no annotations at all offers no filter to clear, whatever the URL says.\n filtered={Boolean((severity || selectedJob) && (annotationSummary?.total ?? 0) > 0)}\n filteredJobName={selectedJob?.displayName}\n filteredSeverity={severity}\n onClearFilters={onClearAnnotationFilters}\n selectedAnnotationId={selection?.annotation}\n />\n </div>\n </section>\n );\n}\n\nconst ALL_ANNOTATION_JOBS = 'all-jobs';\n\nfunction AnnotationJobFilter({\n jobs,\n selectedJobId,\n onSelect,\n}: {\n jobs: Job[];\n selectedJobId: string | undefined;\n onSelect: (jobId: string | undefined) => void;\n}) {\n const sortedJobs = [...jobs].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n\n return (\n <Select\n value={selectedJobId ?? ALL_ANNOTATION_JOBS}\n onValueChange={(value) => onSelect(value === ALL_ANNOTATION_JOBS ? undefined : value)}\n >\n <SelectTrigger\n size=\"small\"\n aria-label=\"Filter annotations by job\"\n className=\"w-full min-[480px]:w-240\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent align=\"end\">\n <SelectItem value={ALL_ANNOTATION_JOBS}>All jobs</SelectItem>\n {sortedJobs.map((job) => (\n <SelectItem key={job.id} value={job.id}>\n {job.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction RunWorkspaceNavSkeleton() {\n return (\n <aside\n aria-label=\"Loading run navigation\"\n className=\"hidden w-240 shrink-0 border-r border-border-neutral-base bg-background-neutral-background p-12 min-[768px]:block\"\n >\n <div className=\"flex flex-col gap-16\">\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n <div className=\"flex flex-col gap-6\">\n <div className=\"h-16 w-64 rounded-4 bg-background-components-subtle\" />\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n </div>\n </div>\n </aside>\n );\n}\n\nfunction runWorkspaceSection(tab: WorkflowRunTab | undefined): RunWorkspaceSection {\n return tab === 'annotations' || tab === 'source' ? tab : 'summary';\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","RelativeTimeProvider","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","toast","Text","useNavigate","useEffect","useMemo","buildRunAnnotationList","isWorkflowRunTerminal","withoutWorkflowRunSelectionSearch","useRunAnnotationsQuery","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","workflowJobSearchParams","workflowRunSearchParams","JobGraph","WorkflowRunSummary","RunAnnotationList","RunAnnotationSummaryLine","WorkflowSourceContent","RunWorkspaceNav","resolveWorkflowRunSelection","WorkflowRunContentSkeleton","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","WorkflowRunView","projectId","workspaceSlug","projectSlug","workflowRunId","runAttempt","selection","tab","activeJobId","jobSearch","jobContent","runQuery","rerunMutation","annotationsQuery","data","attempt","live","runWorkspaceSection","Boolean","status","div","className","RunViewContent","query","annotations","navigate","runData","annotationSummary","summary","activeSection","cancelMutation","sourceSnapshot","resolvedSelection","run","undefined","highlightedLineRange","step","sourceLocation","hasLegacyJobSelection","jobId","jobExecutionId","stepId","stepAttemptId","job","to","params","id","search","replace","rerun","mode","error","mutateAsync","success","previous","message","selectGraphJob","source","selectAnnotationJob","nextSearch","clearAnnotationFilters","severity","annotation","cancelRun","mutate","onError","cancelErrorMessage","fatalError","isError","loadingOrError","subject","icon","isPending","cancelling","onCancel","rerunPending","onRerun","latestAttempt","data-run-workspace-layout","currentJobId","RunWorkspaceNavSkeleton","data-run-workspace-content","RunSectionContent","section","selectedJobId","onSelectGraphJob","onSelectAnnotationJob","onClearAnnotationFilters","aria-label","as","onSelectedJobChange","RunAnnotationsSection","scrollHighlightedIntoView","title","description","isTemporary","selectedJob","jobs","find","records","entries","AnnotationJobFilter","onSelect","filtered","total","filteredJobName","displayName","filteredSeverity","onClearFilters","selectedAnnotationId","ALL_ANNOTATION_JOBS","sortedJobs","sort","left","right","position","localeCompare","value","onValueChange","size","align","map","aside","code"],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import type { Job } from './workflow-run.js';
2
+ export type RunAnnotationStyle = 'default' | 'info' | 'success' | 'warning' | 'error';
3
+ /**
4
+ * The severities the run surfaces rank and filter by. The `default` style carries no severity:
5
+ * it counts toward the total and sorts last, but no severity filter selects it.
6
+ */
7
+ export declare const RUN_ANNOTATION_SEVERITIES: readonly ['error', 'warning', 'info', 'success'];
8
+ export type RunAnnotationSeverity = (typeof RUN_ANNOTATION_SEVERITIES)[number];
9
+ export interface RunAnnotation {
10
+ id: string;
11
+ jobId: string;
12
+ jobExecutionId: string;
13
+ originStepId: string;
14
+ originStepAttempt: number;
15
+ context: string;
16
+ style: RunAnnotationStyle;
17
+ sequence: number;
18
+ }
19
+ export interface RunAnnotationRecord extends RunAnnotation {
20
+ body: string;
21
+ }
22
+ /** Counts shown by the run rail and the severity summary line. */
23
+ export interface RunAnnotationSummary {
24
+ total: number;
25
+ error: number;
26
+ warning: number;
27
+ info: number;
28
+ success: number;
29
+ /** True when the read hit its page budget, so every count is a lower bound. */
30
+ truncated: boolean;
31
+ }
32
+ /** Everything the annotations list needs to route back to the step that emitted an annotation. */
33
+ export interface RunAnnotationOrigin {
34
+ jobId: string;
35
+ jobExecutionId: string;
36
+ stepId: string;
37
+ stepAttemptId: string;
38
+ }
39
+ export interface RunAnnotationEntry {
40
+ annotation: RunAnnotationRecord;
41
+ jobName: string | null;
42
+ /** Only set when the job ran more than once, so a single-execution job stays uncluttered. */
43
+ executionLabel: string | null;
44
+ stepLabel: string | null;
45
+ attemptLabel: string;
46
+ /** `null` when the run attempt no longer contains the emitting step, so no link is offered. */
47
+ origin: RunAnnotationOrigin | null;
48
+ }
49
+ export declare function annotationSeverity(style: RunAnnotationStyle): RunAnnotationSeverity | null;
50
+ export declare function summarizeRunAnnotations(annotations: readonly RunAnnotation[], { truncated }?: {
51
+ truncated?: boolean | undefined;
52
+ }): RunAnnotationSummary;
53
+ export interface BuildRunAnnotationListInput {
54
+ annotations: readonly RunAnnotationRecord[];
55
+ jobs: readonly Job[];
56
+ severity?: RunAnnotationSeverity | undefined;
57
+ jobId?: string | undefined;
58
+ }
59
+ /**
60
+ * Ranks annotations for display.
61
+ *
62
+ * Severity leads: emission order is a log affordance, not a summary affordance. `sequence` is
63
+ * only stable within one job execution, so the tie-break walks down job position and execution
64
+ * sequence before using it, and ends on the id so the order never depends on input order.
65
+ */
66
+ export declare function buildRunAnnotationList({ annotations, jobs, severity, jobId, }: BuildRunAnnotationListInput): RunAnnotationEntry[];
67
+ /** Counts annotations owned by one job, for the job page's bounded reference chip. */
68
+ export declare function summarizeJobAnnotations(annotations: readonly RunAnnotation[], jobId: string, options?: {
69
+ truncated?: boolean | undefined;
70
+ }): RunAnnotationSummary;
71
+ /** The loudest severity present, which tints a count chip. */
72
+ export declare function highestRunAnnotationSeverity(summary: RunAnnotationSummary): RunAnnotationSeverity | null;
73
+ //# sourceMappingURL=run-annotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-annotation.d.ts","sourceRoot":"","sources":["../../src/core/run-annotation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mBAAmB,CAAC;AAE3C,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,YAAI,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,CAAU,CAAC;AAE1F,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAY/E,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kEAAkE;AAClE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,kGAAkG;AAClG,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,mBAAmB,CAAC;IAChC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,6FAA6F;IAC7F,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACpC;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,qBAAqB,GAAG,IAAI,CAE1F;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,SAAS,aAAa,EAAE,EACrC,EAAC,SAAiB,EAAC,GAAE;IAAC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAM,GAC1D,oBAAoB,CAgBtB;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC5C,IAAI,EAAE,SAAS,GAAG,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC7C,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,KAAK,GACN,EAAE,2BAA2B,GAAG,kBAAkB,EAAE,CAYpD;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,SAAS,aAAa,EAAE,EACrC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAM,GAC9C,oBAAoB,CAKtB;AAED,8DAA8D;AAC9D,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,qBAAqB,GAAG,IAAI,CAE9B"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The severities the run surfaces rank and filter by. The `default` style carries no severity:
3
+ * it counts toward the total and sorts last, but no severity filter selects it.
4
+ */ export const RUN_ANNOTATION_SEVERITIES = [
5
+ 'error',
6
+ 'warning',
7
+ 'info',
8
+ 'success'
9
+ ];
10
+ const SEVERITY_RANK = {
11
+ error: 0,
12
+ warning: 1,
13
+ info: 2,
14
+ success: 3,
15
+ default: 4
16
+ };
17
+ const SEVERITY_SET = new Set(RUN_ANNOTATION_SEVERITIES);
18
+ export function annotationSeverity(style) {
19
+ return SEVERITY_SET.has(style) ? style : null;
20
+ }
21
+ export function summarizeRunAnnotations(annotations, { truncated = false } = {}) {
22
+ const summary = {
23
+ total: annotations.length,
24
+ error: 0,
25
+ warning: 0,
26
+ info: 0,
27
+ success: 0,
28
+ truncated
29
+ };
30
+ for (const annotation of annotations){
31
+ const severity = annotationSeverity(annotation.style);
32
+ if (severity) summary[severity] += 1;
33
+ }
34
+ return summary;
35
+ }
36
+ /**
37
+ * Ranks annotations for display.
38
+ *
39
+ * Severity leads: emission order is a log affordance, not a summary affordance. `sequence` is
40
+ * only stable within one job execution, so the tie-break walks down job position and execution
41
+ * sequence before using it, and ends on the id so the order never depends on input order.
42
+ */ export function buildRunAnnotationList({ annotations, jobs, severity, jobId }) {
43
+ const index = indexJobs(jobs);
44
+ const entries = [];
45
+ for (const annotation of annotations){
46
+ if (jobId && annotation.jobId !== jobId) continue;
47
+ const annotationSeverityValue = annotationSeverity(annotation.style);
48
+ if (severity && annotationSeverityValue !== severity) continue;
49
+ entries.push(toEntry(annotation, index));
50
+ }
51
+ return entries.sort((left, right)=>compareEntries(left, right, index));
52
+ }
53
+ /** Counts annotations owned by one job, for the job page's bounded reference chip. */ export function summarizeJobAnnotations(annotations, jobId, options = {}) {
54
+ return summarizeRunAnnotations(annotations.filter((annotation)=>annotation.jobId === jobId), options);
55
+ }
56
+ /** The loudest severity present, which tints a count chip. */ export function highestRunAnnotationSeverity(summary) {
57
+ return RUN_ANNOTATION_SEVERITIES.find((severity)=>summary[severity] > 0) ?? null;
58
+ }
59
+ function indexJobs(jobs) {
60
+ const index = new Map();
61
+ for (const job of jobs){
62
+ const executions = new Map();
63
+ for (const execution of job.jobExecutions){
64
+ const steps = new Map();
65
+ for (const step of execution.steps){
66
+ const attemptIds = new Map();
67
+ for (const attempt of step.attempts)attemptIds.set(attempt.attempt, attempt.id);
68
+ steps.set(step.id, {
69
+ label: step.name || step.key || step.id,
70
+ attemptIds
71
+ });
72
+ }
73
+ executions.set(execution.id, {
74
+ sequence: execution.sequence,
75
+ steps
76
+ });
77
+ }
78
+ index.set(job.id, {
79
+ position: job.position,
80
+ name: job.displayName,
81
+ executionCount: job.jobExecutions.length,
82
+ executions
83
+ });
84
+ }
85
+ return index;
86
+ }
87
+ function toEntry(annotation, index) {
88
+ const job = index.get(annotation.jobId);
89
+ const execution = job?.executions.get(annotation.jobExecutionId);
90
+ const step = execution?.steps.get(annotation.originStepId);
91
+ const stepAttemptId = step?.attemptIds.get(annotation.originStepAttempt);
92
+ return {
93
+ annotation,
94
+ jobName: job?.name ?? null,
95
+ executionLabel: execution && job && job.executionCount > 1 ? `execution #${execution.sequence}` : null,
96
+ stepLabel: step?.label ?? null,
97
+ attemptLabel: `attempt ${annotation.originStepAttempt}`,
98
+ origin: stepAttemptId ? {
99
+ jobId: annotation.jobId,
100
+ jobExecutionId: annotation.jobExecutionId,
101
+ stepId: annotation.originStepId,
102
+ stepAttemptId
103
+ } : null
104
+ };
105
+ }
106
+ function compareEntries(left, right, index) {
107
+ const bySeverity = SEVERITY_RANK[left.annotation.style] - SEVERITY_RANK[right.annotation.style];
108
+ if (bySeverity !== 0) return bySeverity;
109
+ const byJob = jobPosition(left, index) - jobPosition(right, index);
110
+ if (byJob !== 0) return byJob;
111
+ const byExecution = executionSequence(left, index) - executionSequence(right, index);
112
+ if (byExecution !== 0) return byExecution;
113
+ const bySequence = left.annotation.sequence - right.annotation.sequence;
114
+ if (bySequence !== 0) return bySequence;
115
+ return left.annotation.id.localeCompare(right.annotation.id);
116
+ }
117
+ /** An unmatched job sorts last rather than first, so known provenance leads the list. */ function jobPosition(entry, index) {
118
+ return index.get(entry.annotation.jobId)?.position ?? Number.MAX_SAFE_INTEGER;
119
+ }
120
+ function executionSequence(entry, index) {
121
+ const job = index.get(entry.annotation.jobId);
122
+ return job?.executions.get(entry.annotation.jobExecutionId)?.sequence ?? Number.MAX_SAFE_INTEGER;
123
+ }
124
+
125
+ //# sourceMappingURL=run-annotation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/run-annotation.ts"],"sourcesContent":["import type {Job} from './workflow-run.js';\n\nexport type RunAnnotationStyle = 'default' | 'info' | 'success' | 'warning' | 'error';\n\n/**\n * The severities the run surfaces rank and filter by. The `default` style carries no severity:\n * it counts toward the total and sorts last, but no severity filter selects it.\n */\nexport const RUN_ANNOTATION_SEVERITIES = ['error', 'warning', 'info', 'success'] as const;\n\nexport type RunAnnotationSeverity = (typeof RUN_ANNOTATION_SEVERITIES)[number];\n\nconst SEVERITY_RANK: Record<RunAnnotationStyle, number> = {\n error: 0,\n warning: 1,\n info: 2,\n success: 3,\n default: 4,\n};\n\nconst SEVERITY_SET = new Set<string>(RUN_ANNOTATION_SEVERITIES);\n\nexport interface RunAnnotation {\n id: string;\n jobId: string;\n jobExecutionId: string;\n originStepId: string;\n originStepAttempt: number;\n context: string;\n style: RunAnnotationStyle;\n sequence: number;\n}\n\nexport interface RunAnnotationRecord extends RunAnnotation {\n body: string;\n}\n\n/** Counts shown by the run rail and the severity summary line. */\nexport interface RunAnnotationSummary {\n total: number;\n error: number;\n warning: number;\n info: number;\n success: number;\n /** True when the read hit its page budget, so every count is a lower bound. */\n truncated: boolean;\n}\n\n/** Everything the annotations list needs to route back to the step that emitted an annotation. */\nexport interface RunAnnotationOrigin {\n jobId: string;\n jobExecutionId: string;\n stepId: string;\n stepAttemptId: string;\n}\n\nexport interface RunAnnotationEntry {\n annotation: RunAnnotationRecord;\n jobName: string | null;\n /** Only set when the job ran more than once, so a single-execution job stays uncluttered. */\n executionLabel: string | null;\n stepLabel: string | null;\n attemptLabel: string;\n /** `null` when the run attempt no longer contains the emitting step, so no link is offered. */\n origin: RunAnnotationOrigin | null;\n}\n\nexport function annotationSeverity(style: RunAnnotationStyle): RunAnnotationSeverity | null {\n return SEVERITY_SET.has(style) ? (style as RunAnnotationSeverity) : null;\n}\n\nexport function summarizeRunAnnotations(\n annotations: readonly RunAnnotation[],\n {truncated = false}: {truncated?: boolean | undefined} = {},\n): RunAnnotationSummary {\n const summary: RunAnnotationSummary = {\n total: annotations.length,\n error: 0,\n warning: 0,\n info: 0,\n success: 0,\n truncated,\n };\n\n for (const annotation of annotations) {\n const severity = annotationSeverity(annotation.style);\n if (severity) summary[severity] += 1;\n }\n\n return summary;\n}\n\nexport interface BuildRunAnnotationListInput {\n annotations: readonly RunAnnotationRecord[];\n jobs: readonly Job[];\n severity?: RunAnnotationSeverity | undefined;\n jobId?: string | undefined;\n}\n\n/**\n * Ranks annotations for display.\n *\n * Severity leads: emission order is a log affordance, not a summary affordance. `sequence` is\n * only stable within one job execution, so the tie-break walks down job position and execution\n * sequence before using it, and ends on the id so the order never depends on input order.\n */\nexport function buildRunAnnotationList({\n annotations,\n jobs,\n severity,\n jobId,\n}: BuildRunAnnotationListInput): RunAnnotationEntry[] {\n const index = indexJobs(jobs);\n const entries: RunAnnotationEntry[] = [];\n\n for (const annotation of annotations) {\n if (jobId && annotation.jobId !== jobId) continue;\n const annotationSeverityValue = annotationSeverity(annotation.style);\n if (severity && annotationSeverityValue !== severity) continue;\n entries.push(toEntry(annotation, index));\n }\n\n return entries.sort((left, right) => compareEntries(left, right, index));\n}\n\n/** Counts annotations owned by one job, for the job page's bounded reference chip. */\nexport function summarizeJobAnnotations(\n annotations: readonly RunAnnotation[],\n jobId: string,\n options: {truncated?: boolean | undefined} = {},\n): RunAnnotationSummary {\n return summarizeRunAnnotations(\n annotations.filter((annotation) => annotation.jobId === jobId),\n options,\n );\n}\n\n/** The loudest severity present, which tints a count chip. */\nexport function highestRunAnnotationSeverity(\n summary: RunAnnotationSummary,\n): RunAnnotationSeverity | null {\n return RUN_ANNOTATION_SEVERITIES.find((severity) => summary[severity] > 0) ?? null;\n}\n\ninterface JobIndexEntry {\n position: number;\n name: string;\n executionCount: number;\n executions: Map<string, JobExecutionIndexEntry>;\n}\n\ninterface JobExecutionIndexEntry {\n sequence: number;\n steps: Map<string, {label: string; attemptIds: Map<number, string>}>;\n}\n\nfunction indexJobs(jobs: readonly Job[]): Map<string, JobIndexEntry> {\n const index = new Map<string, JobIndexEntry>();\n\n for (const job of jobs) {\n const executions = new Map<string, JobExecutionIndexEntry>();\n for (const execution of job.jobExecutions) {\n const steps = new Map<string, {label: string; attemptIds: Map<number, string>}>();\n for (const step of execution.steps) {\n const attemptIds = new Map<number, string>();\n for (const attempt of step.attempts) attemptIds.set(attempt.attempt, attempt.id);\n steps.set(step.id, {label: step.name || step.key || step.id, attemptIds});\n }\n executions.set(execution.id, {sequence: execution.sequence, steps});\n }\n\n index.set(job.id, {\n position: job.position,\n name: job.displayName,\n executionCount: job.jobExecutions.length,\n executions,\n });\n }\n\n return index;\n}\n\nfunction toEntry(\n annotation: RunAnnotationRecord,\n index: Map<string, JobIndexEntry>,\n): RunAnnotationEntry {\n const job = index.get(annotation.jobId);\n const execution = job?.executions.get(annotation.jobExecutionId);\n const step = execution?.steps.get(annotation.originStepId);\n const stepAttemptId = step?.attemptIds.get(annotation.originStepAttempt);\n\n return {\n annotation,\n jobName: job?.name ?? null,\n executionLabel:\n execution && job && job.executionCount > 1 ? `execution #${execution.sequence}` : null,\n stepLabel: step?.label ?? null,\n attemptLabel: `attempt ${annotation.originStepAttempt}`,\n origin: stepAttemptId\n ? {\n jobId: annotation.jobId,\n jobExecutionId: annotation.jobExecutionId,\n stepId: annotation.originStepId,\n stepAttemptId,\n }\n : null,\n };\n}\n\nfunction compareEntries(\n left: RunAnnotationEntry,\n right: RunAnnotationEntry,\n index: Map<string, JobIndexEntry>,\n): number {\n const bySeverity = SEVERITY_RANK[left.annotation.style] - SEVERITY_RANK[right.annotation.style];\n if (bySeverity !== 0) return bySeverity;\n\n const byJob = jobPosition(left, index) - jobPosition(right, index);\n if (byJob !== 0) return byJob;\n\n const byExecution = executionSequence(left, index) - executionSequence(right, index);\n if (byExecution !== 0) return byExecution;\n\n const bySequence = left.annotation.sequence - right.annotation.sequence;\n if (bySequence !== 0) return bySequence;\n\n return left.annotation.id.localeCompare(right.annotation.id);\n}\n\n/** An unmatched job sorts last rather than first, so known provenance leads the list. */\nfunction jobPosition(entry: RunAnnotationEntry, index: Map<string, JobIndexEntry>): number {\n return index.get(entry.annotation.jobId)?.position ?? Number.MAX_SAFE_INTEGER;\n}\n\nfunction executionSequence(entry: RunAnnotationEntry, index: Map<string, JobIndexEntry>): number {\n const job = index.get(entry.annotation.jobId);\n return job?.executions.get(entry.annotation.jobExecutionId)?.sequence ?? Number.MAX_SAFE_INTEGER;\n}\n"],"names":["RUN_ANNOTATION_SEVERITIES","SEVERITY_RANK","error","warning","info","success","default","SEVERITY_SET","Set","annotationSeverity","style","has","summarizeRunAnnotations","annotations","truncated","summary","total","length","annotation","severity","buildRunAnnotationList","jobs","jobId","index","indexJobs","entries","annotationSeverityValue","push","toEntry","sort","left","right","compareEntries","summarizeJobAnnotations","options","filter","highestRunAnnotationSeverity","find","Map","job","executions","execution","jobExecutions","steps","step","attemptIds","attempt","attempts","set","id","label","name","key","sequence","position","displayName","executionCount","get","jobExecutionId","originStepId","stepAttemptId","originStepAttempt","jobName","executionLabel","stepLabel","attemptLabel","origin","stepId","bySeverity","byJob","jobPosition","byExecution","executionSequence","bySequence","localeCompare","entry","Number","MAX_SAFE_INTEGER"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { AnnotationDto, ReadAnnotationsResponseDto } from '@shipfox/annotations-dto';
2
+ import type { RunAnnotationRecord } from '#core/run-annotation.js';
3
+ export interface RunAnnotationPage {
4
+ annotations: RunAnnotationRecord[];
5
+ hasMore: boolean;
6
+ nextCursor: string | null;
7
+ }
8
+ export declare function toRunAnnotation(dto: AnnotationDto): RunAnnotationRecord;
9
+ export declare function toRunAnnotationPage(response: ReadAnnotationsResponseDto): RunAnnotationPage;
10
+ //# sourceMappingURL=run-annotation-mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-annotation-mapper.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/run-annotation-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAE,0BAA0B,EAAC,MAAM,0BAA0B,CAAC;AACxF,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,yBAAyB,CAAC;AAEjE,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,mBAAmB,CAYvE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,0BAA0B,GAAG,iBAAiB,CAM3F"}
@@ -0,0 +1,22 @@
1
+ export function toRunAnnotation(dto) {
2
+ return {
3
+ id: dto.id,
4
+ jobId: dto.job_id,
5
+ jobExecutionId: dto.job_execution_id,
6
+ originStepId: dto.origin_step_id,
7
+ originStepAttempt: dto.origin_step_attempt,
8
+ context: dto.context,
9
+ style: dto.style,
10
+ sequence: dto.sequence,
11
+ body: dto.body
12
+ };
13
+ }
14
+ export function toRunAnnotationPage(response) {
15
+ return {
16
+ annotations: response.annotations.map(toRunAnnotation),
17
+ hasMore: response.has_more,
18
+ nextCursor: response.next_cursor
19
+ };
20
+ }
21
+
22
+ //# sourceMappingURL=run-annotation-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/api/run-annotation-mapper.ts"],"sourcesContent":["import type {AnnotationDto, ReadAnnotationsResponseDto} from '@shipfox/annotations-dto';\nimport type {RunAnnotationRecord} from '#core/run-annotation.js';\n\nexport interface RunAnnotationPage {\n annotations: RunAnnotationRecord[];\n hasMore: boolean;\n nextCursor: string | null;\n}\n\nexport function toRunAnnotation(dto: AnnotationDto): RunAnnotationRecord {\n return {\n id: dto.id,\n jobId: dto.job_id,\n jobExecutionId: dto.job_execution_id,\n originStepId: dto.origin_step_id,\n originStepAttempt: dto.origin_step_attempt,\n context: dto.context,\n style: dto.style,\n sequence: dto.sequence,\n body: dto.body,\n };\n}\n\nexport function toRunAnnotationPage(response: ReadAnnotationsResponseDto): RunAnnotationPage {\n return {\n annotations: response.annotations.map(toRunAnnotation),\n hasMore: response.has_more,\n nextCursor: response.next_cursor,\n };\n}\n"],"names":["toRunAnnotation","dto","id","jobId","job_id","jobExecutionId","job_execution_id","originStepId","origin_step_id","originStepAttempt","origin_step_attempt","context","style","sequence","body","toRunAnnotationPage","response","annotations","map","hasMore","has_more","nextCursor","next_cursor"],"mappings":"AASA,OAAO,SAASA,gBAAgBC,GAAkB;IAChD,OAAO;QACLC,IAAID,IAAIC,EAAE;QACVC,OAAOF,IAAIG,MAAM;QACjBC,gBAAgBJ,IAAIK,gBAAgB;QACpCC,cAAcN,IAAIO,cAAc;QAChCC,mBAAmBR,IAAIS,mBAAmB;QAC1CC,SAASV,IAAIU,OAAO;QACpBC,OAAOX,IAAIW,KAAK;QAChBC,UAAUZ,IAAIY,QAAQ;QACtBC,MAAMb,IAAIa,IAAI;IAChB;AACF;AAEA,OAAO,SAASC,oBAAoBC,QAAoC;IACtE,OAAO;QACLC,aAAaD,SAASC,WAAW,CAACC,GAAG,CAAClB;QACtCmB,SAASH,SAASI,QAAQ;QAC1BC,YAAYL,SAASM,WAAW;IAClC;AACF"}
@@ -0,0 +1,25 @@
1
+ import { type InfiniteData, type UseInfiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query';
2
+ import { type RunAnnotationRecord, type RunAnnotationSummary } from '#core/run-annotation.js';
3
+ import { type RunAnnotationPage } from './run-annotation-mapper.js';
4
+ export declare const runAnnotationsQueryKeys: {
5
+ all: readonly ['run-annotations'];
6
+ list: (workflowRunId: string, runAttempt?: number | undefined) => readonly ["run-annotations", "list", string, number | null];
7
+ };
8
+ type RunAnnotationsQueryKey = ReturnType<typeof runAnnotationsQueryKeys.list> | readonly ['run-annotations', 'list'];
9
+ type RunAnnotationsQueryOptions = UseInfiniteQueryOptions<RunAnnotationPage, Error, InfiniteData<RunAnnotationPage, string | undefined>, RunAnnotationsQueryKey, string | undefined>;
10
+ export interface RunAnnotationsQueryInput {
11
+ workflowRunId: string | undefined;
12
+ runAttempt: number | undefined;
13
+ /** Poll while the run attempt is non-terminal, then settle. */
14
+ live?: boolean | undefined;
15
+ }
16
+ export declare function runAnnotationsQueryOptions({ workflowRunId, runAttempt, live, }: RunAnnotationsQueryInput): RunAnnotationsQueryOptions;
17
+ export interface RunAnnotationsQueryResult {
18
+ query: ReturnType<typeof useInfiniteQuery<RunAnnotationPage, Error, InfiniteData<RunAnnotationPage, string | undefined>, RunAnnotationsQueryKey, string | undefined>>;
19
+ /** `undefined` until the first page resolves, so counts never render a speculative zero. */
20
+ annotations: RunAnnotationRecord[] | undefined;
21
+ summary: RunAnnotationSummary | undefined;
22
+ }
23
+ export declare function useRunAnnotationsQuery(input: RunAnnotationsQueryInput): RunAnnotationsQueryResult;
24
+ export {};
25
+ //# sourceMappingURL=run-annotations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-annotations.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/run-annotations.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,uBAAuB,EAC5B,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EAE1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAC,KAAK,iBAAiB,EAAsB,MAAM,4BAA4B,CAAC;AAKvF,eAAO,MAAM,uBAAuB;IAClC,GAAG,YAAG,iBAAiB;IACvB,IAAI,kBAAkB,MAAM,eAAe,MAAM,GAAG,SAAS;CAE9D,CAAC;AAEF,KAAK,sBAAsB,GACvB,UAAU,CAAC,OAAO,uBAAuB,CAAC,IAAI,CAAC,GAC/C,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;AAEzC,KAAK,0BAA0B,GAAG,uBAAuB,CACvD,iBAAiB,EACjB,KAAK,EACL,YAAY,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC,EACnD,sBAAsB,EACtB,MAAM,GAAG,SAAS,CACnB,CAAC;AA2BF,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC5B;AAED,wBAAgB,0BAA0B,CAAC,EACzC,aAAa,EACb,UAAU,EACV,IAAY,GACb,EAAE,wBAAwB,GAAG,0BAA0B,CA6BvD;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,UAAU,CACf,OAAO,gBAAgB,CACrB,iBAAiB,EACjB,KAAK,EACL,YAAY,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC,EACnD,sBAAsB,EACtB,MAAM,GAAG,SAAS,CACnB,CACF,CAAC;IACF,4FAA4F;IAC5F,WAAW,EAAE,mBAAmB,EAAE,GAAG,SAAS,CAAC;IAC/C,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;CAC3C;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,wBAAwB,GAAG,yBAAyB,CAiBjG"}
@@ -0,0 +1,78 @@
1
+ import { READ_ANNOTATIONS_MAX_LIMIT, readAnnotationsResponseSchema } from '@shipfox/annotations-dto';
2
+ import { checkedApiRequest } from '@shipfox/client-api';
3
+ import { infiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query';
4
+ import { useMemo } from 'react';
5
+ import { summarizeRunAnnotations } from '#core/run-annotation.js';
6
+ import { toRunAnnotationPage } from './run-annotation-mapper.js';
7
+ /** Matches the run detail poll, so annotations and run state never disagree by more than a tick. */ const ACTIVE_POLL_MS = 4_000;
8
+ export const runAnnotationsQueryKeys = {
9
+ all: [
10
+ 'run-annotations'
11
+ ],
12
+ list: (workflowRunId, runAttempt)=>[
13
+ ...runAnnotationsQueryKeys.all,
14
+ 'list',
15
+ workflowRunId,
16
+ runAttempt ?? null
17
+ ]
18
+ };
19
+ async function listRunAnnotations({ workflowRunId, runAttempt, cursor, signal }) {
20
+ const params = new URLSearchParams({
21
+ workflow_run_id: workflowRunId,
22
+ attempt: String(runAttempt),
23
+ limit: String(READ_ANNOTATIONS_MAX_LIMIT)
24
+ });
25
+ if (cursor) params.set('cursor', cursor);
26
+ return toRunAnnotationPage(await checkedApiRequest(readAnnotationsResponseSchema, `/annotations?${params.toString()}`, {
27
+ signal
28
+ }));
29
+ }
30
+ export function runAnnotationsQueryOptions({ workflowRunId, runAttempt, live = false }) {
31
+ const enabled = Boolean(workflowRunId) && runAttempt !== undefined;
32
+ // Polling stops once the reader has paged past the first page: the cursor bounding page 2 was
33
+ // computed from page 1's last row, so a refetch that shifts that boundary can drop a range of
34
+ // annotations into a between-pages gap.
35
+ return infiniteQueryOptions({
36
+ queryKey: workflowRunId ? runAnnotationsQueryKeys.list(workflowRunId, runAttempt) : [
37
+ ...runAnnotationsQueryKeys.all,
38
+ 'list'
39
+ ],
40
+ enabled,
41
+ initialPageParam: undefined,
42
+ queryFn: ({ pageParam, signal })=>listRunAnnotations({
43
+ workflowRunId: workflowRunId ?? '',
44
+ runAttempt: runAttempt ?? 1,
45
+ cursor: pageParam,
46
+ signal
47
+ }),
48
+ getNextPageParam: (lastPage)=>lastPage.nextCursor ?? undefined,
49
+ staleTime: 2_000,
50
+ refetchOnWindowFocus: true,
51
+ refetchInterval: (query)=>{
52
+ if (!live) return false;
53
+ const pages = query.state.data?.pages;
54
+ return pages && pages.length > 1 ? false : ACTIVE_POLL_MS;
55
+ },
56
+ refetchIntervalInBackground: false
57
+ });
58
+ }
59
+ export function useRunAnnotationsQuery(input) {
60
+ const query = useInfiniteQuery(runAnnotationsQueryOptions(input));
61
+ const pages = query.data?.pages;
62
+ const annotations = useMemo(()=>pages ? pages.flatMap((page)=>page.annotations) : undefined, [
63
+ pages
64
+ ]);
65
+ const summary = useMemo(()=>annotations ? summarizeRunAnnotations(annotations, {
66
+ truncated: pages?.at(-1)?.hasMore ?? false
67
+ }) : undefined, [
68
+ annotations,
69
+ pages
70
+ ]);
71
+ return {
72
+ query,
73
+ annotations,
74
+ summary
75
+ };
76
+ }
77
+
78
+ //# sourceMappingURL=run-annotations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/api/run-annotations.ts"],"sourcesContent":["import {READ_ANNOTATIONS_MAX_LIMIT, readAnnotationsResponseSchema} from '@shipfox/annotations-dto';\nimport {checkedApiRequest} from '@shipfox/client-api';\nimport {\n type InfiniteData,\n infiniteQueryOptions,\n type UseInfiniteQueryOptions,\n useInfiniteQuery,\n} from '@tanstack/react-query';\nimport {useMemo} from 'react';\nimport {\n type RunAnnotationRecord,\n type RunAnnotationSummary,\n summarizeRunAnnotations,\n} from '#core/run-annotation.js';\nimport {type RunAnnotationPage, toRunAnnotationPage} from './run-annotation-mapper.js';\n\n/** Matches the run detail poll, so annotations and run state never disagree by more than a tick. */\nconst ACTIVE_POLL_MS = 4_000;\n\nexport const runAnnotationsQueryKeys = {\n all: ['run-annotations'] as const,\n list: (workflowRunId: string, runAttempt?: number | undefined) =>\n [...runAnnotationsQueryKeys.all, 'list', workflowRunId, runAttempt ?? null] as const,\n};\n\ntype RunAnnotationsQueryKey =\n | ReturnType<typeof runAnnotationsQueryKeys.list>\n | readonly ['run-annotations', 'list'];\n\ntype RunAnnotationsQueryOptions = UseInfiniteQueryOptions<\n RunAnnotationPage,\n Error,\n InfiniteData<RunAnnotationPage, string | undefined>,\n RunAnnotationsQueryKey,\n string | undefined\n>;\n\nasync function listRunAnnotations({\n workflowRunId,\n runAttempt,\n cursor,\n signal,\n}: {\n workflowRunId: string;\n runAttempt: number;\n cursor?: string | undefined;\n signal?: AbortSignal;\n}): Promise<RunAnnotationPage> {\n const params = new URLSearchParams({\n workflow_run_id: workflowRunId,\n attempt: String(runAttempt),\n limit: String(READ_ANNOTATIONS_MAX_LIMIT),\n });\n if (cursor) params.set('cursor', cursor);\n\n return toRunAnnotationPage(\n await checkedApiRequest(readAnnotationsResponseSchema, `/annotations?${params.toString()}`, {\n signal,\n }),\n );\n}\n\nexport interface RunAnnotationsQueryInput {\n workflowRunId: string | undefined;\n runAttempt: number | undefined;\n /** Poll while the run attempt is non-terminal, then settle. */\n live?: boolean | undefined;\n}\n\nexport function runAnnotationsQueryOptions({\n workflowRunId,\n runAttempt,\n live = false,\n}: RunAnnotationsQueryInput): RunAnnotationsQueryOptions {\n const enabled = Boolean(workflowRunId) && runAttempt !== undefined;\n\n // Polling stops once the reader has paged past the first page: the cursor bounding page 2 was\n // computed from page 1's last row, so a refetch that shifts that boundary can drop a range of\n // annotations into a between-pages gap.\n return infiniteQueryOptions({\n queryKey: workflowRunId\n ? runAnnotationsQueryKeys.list(workflowRunId, runAttempt)\n : ([...runAnnotationsQueryKeys.all, 'list'] as const),\n enabled,\n initialPageParam: undefined as string | undefined,\n queryFn: ({pageParam, signal}) =>\n listRunAnnotations({\n workflowRunId: workflowRunId ?? '',\n runAttempt: runAttempt ?? 1,\n cursor: pageParam,\n signal,\n }),\n getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,\n staleTime: 2_000,\n refetchOnWindowFocus: true,\n refetchInterval: (query) => {\n if (!live) return false;\n const pages = query.state.data?.pages;\n return pages && pages.length > 1 ? false : ACTIVE_POLL_MS;\n },\n refetchIntervalInBackground: false,\n });\n}\n\nexport interface RunAnnotationsQueryResult {\n query: ReturnType<\n typeof useInfiniteQuery<\n RunAnnotationPage,\n Error,\n InfiniteData<RunAnnotationPage, string | undefined>,\n RunAnnotationsQueryKey,\n string | undefined\n >\n >;\n /** `undefined` until the first page resolves, so counts never render a speculative zero. */\n annotations: RunAnnotationRecord[] | undefined;\n summary: RunAnnotationSummary | undefined;\n}\n\nexport function useRunAnnotationsQuery(input: RunAnnotationsQueryInput): RunAnnotationsQueryResult {\n const query = useInfiniteQuery(runAnnotationsQueryOptions(input));\n const pages = query.data?.pages;\n\n const annotations = useMemo(\n () => (pages ? pages.flatMap((page) => page.annotations) : undefined),\n [pages],\n );\n const summary = useMemo(\n () =>\n annotations\n ? summarizeRunAnnotations(annotations, {truncated: pages?.at(-1)?.hasMore ?? false})\n : undefined,\n [annotations, pages],\n );\n\n return {query, annotations, summary};\n}\n"],"names":["READ_ANNOTATIONS_MAX_LIMIT","readAnnotationsResponseSchema","checkedApiRequest","infiniteQueryOptions","useInfiniteQuery","useMemo","summarizeRunAnnotations","toRunAnnotationPage","ACTIVE_POLL_MS","runAnnotationsQueryKeys","all","list","workflowRunId","runAttempt","listRunAnnotations","cursor","signal","params","URLSearchParams","workflow_run_id","attempt","String","limit","set","toString","runAnnotationsQueryOptions","live","enabled","Boolean","undefined","queryKey","initialPageParam","queryFn","pageParam","getNextPageParam","lastPage","nextCursor","staleTime","refetchOnWindowFocus","refetchInterval","query","pages","state","data","length","refetchIntervalInBackground","useRunAnnotationsQuery","input","annotations","flatMap","page","summary","truncated","at","hasMore"],"mappings":"AAAA,SAAQA,0BAA0B,EAAEC,6BAA6B,QAAO,2BAA2B;AACnG,SAAQC,iBAAiB,QAAO,sBAAsB;AACtD,SAEEC,oBAAoB,EAEpBC,gBAAgB,QACX,wBAAwB;AAC/B,SAAQC,OAAO,QAAO,QAAQ;AAC9B,SAGEC,uBAAuB,QAClB,0BAA0B;AACjC,SAAgCC,mBAAmB,QAAO,6BAA6B;AAEvF,kGAAkG,GAClG,MAAMC,iBAAiB;AAEvB,OAAO,MAAMC,0BAA0B;IACrCC,KAAK;QAAC;KAAkB;IACxBC,MAAM,CAACC,eAAuBC,aAC5B;eAAIJ,wBAAwBC,GAAG;YAAE;YAAQE;YAAeC,cAAc;SAAK;AAC/E,EAAE;AAcF,eAAeC,mBAAmB,EAChCF,aAAa,EACbC,UAAU,EACVE,MAAM,EACNC,MAAM,EAMP;IACC,MAAMC,SAAS,IAAIC,gBAAgB;QACjCC,iBAAiBP;QACjBQ,SAASC,OAAOR;QAChBS,OAAOD,OAAOrB;IAChB;IACA,IAAIe,QAAQE,OAAOM,GAAG,CAAC,UAAUR;IAEjC,OAAOR,oBACL,MAAML,kBAAkBD,+BAA+B,CAAC,aAAa,EAAEgB,OAAOO,QAAQ,IAAI,EAAE;QAC1FR;IACF;AAEJ;AASA,OAAO,SAASS,2BAA2B,EACzCb,aAAa,EACbC,UAAU,EACVa,OAAO,KAAK,EACa;IACzB,MAAMC,UAAUC,QAAQhB,kBAAkBC,eAAegB;IAEzD,8FAA8F;IAC9F,8FAA8F;IAC9F,wCAAwC;IACxC,OAAO1B,qBAAqB;QAC1B2B,UAAUlB,gBACNH,wBAAwBE,IAAI,CAACC,eAAeC,cAC3C;eAAIJ,wBAAwBC,GAAG;YAAE;SAAO;QAC7CiB;QACAI,kBAAkBF;QAClBG,SAAS,CAAC,EAACC,SAAS,EAAEjB,MAAM,EAAC,GAC3BF,mBAAmB;gBACjBF,eAAeA,iBAAiB;gBAChCC,YAAYA,cAAc;gBAC1BE,QAAQkB;gBACRjB;YACF;QACFkB,kBAAkB,CAACC,WAAaA,SAASC,UAAU,IAAIP;QACvDQ,WAAW;QACXC,sBAAsB;QACtBC,iBAAiB,CAACC;YAChB,IAAI,CAACd,MAAM,OAAO;YAClB,MAAMe,QAAQD,MAAME,KAAK,CAACC,IAAI,EAAEF;YAChC,OAAOA,SAASA,MAAMG,MAAM,GAAG,IAAI,QAAQpC;QAC7C;QACAqC,6BAA6B;IAC/B;AACF;AAiBA,OAAO,SAASC,uBAAuBC,KAA+B;IACpE,MAAMP,QAAQpC,iBAAiBqB,2BAA2BsB;IAC1D,MAAMN,QAAQD,MAAMG,IAAI,EAAEF;IAE1B,MAAMO,cAAc3C,QAClB,IAAOoC,QAAQA,MAAMQ,OAAO,CAAC,CAACC,OAASA,KAAKF,WAAW,IAAInB,WAC3D;QAACY;KAAM;IAET,MAAMU,UAAU9C,QACd,IACE2C,cACI1C,wBAAwB0C,aAAa;YAACI,WAAWX,OAAOY,GAAG,CAAC,IAAIC,WAAW;QAAK,KAChFzB,WACN;QAACmB;QAAaP;KAAM;IAGtB,OAAO;QAACD;QAAOQ;QAAaG;IAAO;AACrC"}
@@ -1,3 +1,4 @@
1
+ import { type RunAnnotationSeverity } from '#core/run-annotation.js';
1
2
  import type { WorkflowRunSelectionInput } from '#core/workflow-run-url-state.js';
2
3
  /**
3
4
  * Statuses the run list can filter by. Absent means all; there is deliberately no `all`
@@ -7,8 +8,12 @@ export declare const WORKFLOW_RUN_LIST_STATUSES: readonly ['succeeded', 'failed'
7
8
  export type WorkflowRunListStatus = (typeof WORKFLOW_RUN_LIST_STATUSES)[number];
8
9
  export declare const WORKFLOW_RUN_TABS: readonly ['summary', 'jobs', 'annotations', 'source'];
9
10
  export type WorkflowRunTab = (typeof WORKFLOW_RUN_TABS)[number];
10
- export declare const WORKFLOW_RUN_ANNOTATION_SEVERITIES: readonly ['error', 'warning', 'info', 'success'];
11
- export type WorkflowRunAnnotationSeverity = (typeof WORKFLOW_RUN_ANNOTATION_SEVERITIES)[number];
11
+ /**
12
+ * The `severity` parameter's vocabulary is the display severity set, not a second list: a URL
13
+ * that could name a severity the list cannot rank would be a filter with no matching rows.
14
+ */
15
+ export declare const WORKFLOW_RUN_ANNOTATION_SEVERITIES: readonly ["error", "warning", "info", "success"];
16
+ export type WorkflowRunAnnotationSeverity = RunAnnotationSeverity;
12
17
  export interface WorkflowRunsSearch extends WorkflowRunSelectionInput {
13
18
  search?: string;
14
19
  status?: WorkflowRunListStatus[];
@@ -1 +1 @@
1
- {"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/routes/inputs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/routes/inputs.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { RUN_ANNOTATION_SEVERITIES } from '#core/run-annotation.js';
1
2
  /**
2
3
  * Statuses the run list can filter by. Absent means all; there is deliberately no `all`
3
4
  * value, so "no filter" and "every filter" are the same URL rather than two.
@@ -13,12 +14,10 @@ export const WORKFLOW_RUN_TABS = [
13
14
  'annotations',
14
15
  'source'
15
16
  ];
16
- export const WORKFLOW_RUN_ANNOTATION_SEVERITIES = [
17
- 'error',
18
- 'warning',
19
- 'info',
20
- 'success'
21
- ];
17
+ /**
18
+ * The `severity` parameter's vocabulary is the display severity set, not a second list: a URL
19
+ * that could name a severity the list cannot rank would be a filter with no matching rows.
20
+ */ export const WORKFLOW_RUN_ANNOTATION_SEVERITIES = RUN_ANNOTATION_SEVERITIES;
22
21
  const STATUS_VALUES = new Set(WORKFLOW_RUN_LIST_STATUSES);
23
22
  const TAB_VALUES = new Set(WORKFLOW_RUN_TABS);
24
23
  const ANNOTATION_SEVERITY_VALUES = new Set(WORKFLOW_RUN_ANNOTATION_SEVERITIES);