@shipfox/client-workflows 5.0.0 → 6.0.1

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 (121) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +53 -0
  3. package/dist/components/workflow-run-list/types.d.ts +12 -0
  4. package/dist/components/workflow-run-list/types.d.ts.map +1 -1
  5. package/dist/components/workflow-run-list/types.js.map +1 -1
  6. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
  7. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
  8. package/dist/components/workflow-run-list/workflow-run-list-view.js +32 -11
  9. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  10. package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
  11. package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
  12. package/dist/components/workflow-run-list/workflow-run-list.js +7 -2
  13. package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
  14. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +2 -3
  15. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  16. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  17. package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
  18. package/dist/components/workflow-run-view/job-card.js +9 -8
  19. package/dist/components/workflow-run-view/job-card.js.map +1 -1
  20. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  21. package/dist/core/entities/job-execution.d.ts +11 -6
  22. package/dist/core/entities/job-execution.d.ts.map +1 -1
  23. package/dist/core/entities/job-execution.js +0 -19
  24. package/dist/core/entities/job-execution.js.map +1 -1
  25. package/dist/core/entities/job.d.ts +32 -10
  26. package/dist/core/entities/job.d.ts.map +1 -1
  27. package/dist/core/entities/job.js +0 -22
  28. package/dist/core/entities/job.js.map +1 -1
  29. package/dist/core/entities/step-attempt.d.ts +28 -3
  30. package/dist/core/entities/step-attempt.d.ts.map +1 -1
  31. package/dist/core/entities/step-attempt.js +0 -17
  32. package/dist/core/entities/step-attempt.js.map +1 -1
  33. package/dist/core/entities/step.d.ts +6 -6
  34. package/dist/core/entities/step.d.ts.map +1 -1
  35. package/dist/core/entities/step.js +19 -49
  36. package/dist/core/entities/step.js.map +1 -1
  37. package/dist/core/entities/workflow-run-attempt.d.ts +0 -2
  38. package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
  39. package/dist/core/entities/workflow-run-attempt.js +0 -12
  40. package/dist/core/entities/workflow-run-attempt.js.map +1 -1
  41. package/dist/core/entities/workflow-run.d.ts +6 -7
  42. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  43. package/dist/core/entities/workflow-run.js +1 -67
  44. package/dist/core/entities/workflow-run.js.map +1 -1
  45. package/dist/core/workflow-run.d.ts +9 -9
  46. package/dist/core/workflow-run.d.ts.map +1 -1
  47. package/dist/core/workflow-run.js +6 -6
  48. package/dist/core/workflow-run.js.map +1 -1
  49. package/dist/feature.d.ts +13 -0
  50. package/dist/feature.d.ts.map +1 -1
  51. package/dist/feature.js +17 -16
  52. package/dist/feature.js.map +1 -1
  53. package/dist/hooks/api/workflow-run-mapper.d.ts +12 -0
  54. package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -0
  55. package/dist/hooks/api/workflow-run-mapper.js +207 -0
  56. package/dist/hooks/api/workflow-run-mapper.js.map +1 -0
  57. package/dist/hooks/api/workflow-runs.d.ts +25 -55
  58. package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
  59. package/dist/hooks/api/workflow-runs.js +95 -51
  60. package/dist/hooks/api/workflow-runs.js.map +1 -1
  61. package/dist/index.d.ts +2 -2
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/pages/project-workflows-page.d.ts.map +1 -1
  66. package/dist/pages/project-workflows-page.js +14 -14
  67. package/dist/pages/project-workflows-page.js.map +1 -1
  68. package/dist/pages/workflow-run-page.d.ts +3 -1
  69. package/dist/pages/workflow-run-page.d.ts.map +1 -1
  70. package/dist/pages/workflow-run-page.js +21 -14
  71. package/dist/pages/workflow-run-page.js.map +1 -1
  72. package/dist/routes/inputs.d.ts +22 -0
  73. package/dist/routes/inputs.d.ts.map +1 -0
  74. package/dist/routes/inputs.js +76 -0
  75. package/dist/routes/inputs.js.map +1 -0
  76. package/dist/routes/run-detail.d.ts +2 -0
  77. package/dist/routes/run-detail.d.ts.map +1 -1
  78. package/dist/routes/run-detail.js +6 -6
  79. package/dist/routes/run-detail.js.map +1 -1
  80. package/dist/routes/runs.d.ts +2 -0
  81. package/dist/routes/runs.d.ts.map +1 -1
  82. package/dist/routes/runs.js +6 -6
  83. package/dist/routes/runs.js.map +1 -1
  84. package/dist/routes/workflows.js +3 -5
  85. package/dist/routes/workflows.js.map +1 -1
  86. package/dist/tsconfig.test.tsbuildinfo +1 -1
  87. package/package.json +13 -14
  88. package/src/components/workflow-run-list/types.ts +6 -0
  89. package/src/components/workflow-run-list/workflow-run-list-view.tsx +27 -11
  90. package/src/components/workflow-run-list/workflow-run-list.tsx +6 -0
  91. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +5 -6
  92. package/src/components/workflow-run-summary/workflow-run-summary.tsx +3 -3
  93. package/src/components/workflow-run-view/job-card.tsx +20 -11
  94. package/src/components/workflow-run-view/workflow-run-view.test.tsx +3 -3
  95. package/src/components/workflow-run-view/workflow-run-view.tsx +2 -2
  96. package/src/core/entities/job-execution.ts +12 -28
  97. package/src/core/entities/job.ts +44 -45
  98. package/src/core/entities/step-attempt.ts +9 -20
  99. package/src/core/entities/step.ts +38 -66
  100. package/src/core/entities/workflow-run-attempt.ts +0 -14
  101. package/src/core/entities/workflow-run.ts +8 -86
  102. package/src/core/workflow-run.test.ts +13 -11
  103. package/src/core/workflow-run.ts +8 -13
  104. package/src/feature.ts +19 -17
  105. package/src/hooks/api/workflow-run-mapper.ts +246 -0
  106. package/src/hooks/api/workflow-runs.test.tsx +57 -59
  107. package/src/hooks/api/workflow-runs.ts +187 -88
  108. package/src/index.ts +4 -0
  109. package/src/pages/project-workflows-page.test.tsx +1 -1
  110. package/src/pages/project-workflows-page.tsx +31 -26
  111. package/src/pages/workflow-run-page.test.tsx +4 -2
  112. package/src/pages/workflow-run-page.tsx +22 -17
  113. package/src/routes/inputs.test.ts +22 -0
  114. package/src/routes/inputs.ts +58 -0
  115. package/src/routes/run-detail.tsx +12 -8
  116. package/src/routes/runs.tsx +11 -4
  117. package/src/routes/workflows.tsx +3 -3
  118. package/test/fixtures/workflow-run.ts +9 -7
  119. package/test/pages.tsx +20 -5
  120. package/tsconfig.build.tsbuildinfo +1 -1
  121. package/vercel.json +5 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/client-workflows",
3
3
  "license": "MIT",
4
- "version": "5.0.0",
4
+ "version": "6.0.1",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -10,10 +10,6 @@
10
10
  "type": "module",
11
11
  "main": "dist/index.js",
12
12
  "types": "dist/index.d.ts",
13
- "imports": {
14
- "#*": "./dist/*",
15
- "#test/*": "./test/*"
16
- },
17
13
  "exports": {
18
14
  ".": {
19
15
  "types": "./dist/index.d.ts",
@@ -31,15 +27,15 @@
31
27
  "dependencies": {
32
28
  "@swc/helpers": "^0.5.17",
33
29
  "date-fns": "^4.1.0",
34
- "@shipfox/api-definitions-dto": "6.0.0",
35
- "@shipfox/api-workflows-dto": "8.0.0",
36
- "@shipfox/client-api": "4.0.0",
37
- "@shipfox/client-projects": "5.0.0",
38
- "@shipfox/client-logs": "5.0.0",
39
- "@shipfox/client-shell": "5.0.0",
40
- "@shipfox/client-triggers": "5.0.0",
41
- "@shipfox/client-ui": "4.0.0",
42
- "@shipfox/react-ui": "0.3.5"
30
+ "@shipfox/api-definitions-dto": "9.0.1",
31
+ "@shipfox/api-workflows-dto": "9.0.1",
32
+ "@shipfox/client-api": "6.0.1",
33
+ "@shipfox/client-logs": "6.0.1",
34
+ "@shipfox/client-projects": "6.0.1",
35
+ "@shipfox/client-shell": "6.0.1",
36
+ "@shipfox/client-triggers": "6.0.1",
37
+ "@shipfox/client-ui": "6.0.1",
38
+ "@shipfox/react-ui": "0.3.6"
43
39
  },
44
40
  "peerDependencies": {
45
41
  "@tanstack/react-query": "^5.101.0",
@@ -47,6 +43,9 @@
47
43
  "react": "^19.0.0",
48
44
  "react-dom": "^19.0.0"
49
45
  },
46
+ "imports": {
47
+ "#*": "./dist/*"
48
+ },
50
49
  "scripts": {
51
50
  "build": "shipfox-swc",
52
51
  "check": "shipfox-biome-check",
@@ -8,6 +8,9 @@ export interface WorkflowRunListProps {
8
8
  projectId: string;
9
9
  selectedWorkflowRunId?: string | undefined;
10
10
  className?: string | undefined;
11
+ search?: string;
12
+ statusFilter?: WorkflowRunListStatusFilter;
13
+ onFiltersChange?: (filters: {search?: string; status?: WorkflowRunListStatusFilter}) => void;
11
14
  }
12
15
 
13
16
  export type WorkflowRunListQuery = QueryLoadErrorQuery & {isPending: boolean};
@@ -19,4 +22,7 @@ export interface WorkflowRunListViewProps {
19
22
  projectId: string;
20
23
  selectedWorkflowRunId?: string | undefined;
21
24
  className?: string | undefined;
25
+ search?: string;
26
+ statusFilter?: WorkflowRunListStatusFilter;
27
+ onFiltersChange?: (filters: {search?: string; status?: WorkflowRunListStatusFilter}) => void;
22
28
  }
@@ -2,7 +2,7 @@ import {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';
2
2
  import {cn} from '@shipfox/react-ui/utils';
3
3
  import {useState} from 'react';
4
4
  import {runMatchesSearch, runMatchesStatusFilter} from './run-display.js';
5
- import type {WorkflowRunListStatusFilter, WorkflowRunListViewProps} from './types.js';
5
+ import type {WorkflowRunListViewProps} from './types.js';
6
6
  import {WorkflowRunListContent} from './workflow-run-list-content.js';
7
7
  import {WorkflowRunListHeader} from './workflow-run-list-header.js';
8
8
 
@@ -13,18 +13,26 @@ export function WorkflowRunListView({
13
13
  projectId,
14
14
  selectedWorkflowRunId,
15
15
  className,
16
+ search = '',
17
+ statusFilter = 'all',
18
+ onFiltersChange,
16
19
  }: WorkflowRunListViewProps) {
17
- const [search, setSearch] = useState('');
18
- const [statusFilter, setStatusFilter] = useState<WorkflowRunListStatusFilter>('all');
20
+ const [localSearch, setLocalSearch] = useState(search);
21
+ const [localStatusFilter, setLocalStatusFilter] = useState(statusFilter);
22
+ const currentSearch = onFiltersChange ? search : localSearch;
23
+ const currentStatusFilter = onFiltersChange ? statusFilter : localStatusFilter;
19
24
 
20
25
  const filteredRuns = runs.filter((run) => {
21
- if (!runMatchesStatusFilter(run.status, statusFilter)) return false;
22
- return runMatchesSearch(run, search);
26
+ if (!runMatchesStatusFilter(run.status, currentStatusFilter)) return false;
27
+ return runMatchesSearch(run, currentSearch);
23
28
  });
24
29
 
25
30
  function handleClearFilters() {
26
- setSearch('');
27
- setStatusFilter('all');
31
+ if (onFiltersChange) onFiltersChange({});
32
+ else {
33
+ setLocalSearch('');
34
+ setLocalStatusFilter('all');
35
+ }
28
36
  }
29
37
 
30
38
  return (
@@ -37,10 +45,18 @@ export function WorkflowRunListView({
37
45
  aria-label="Workflow runs"
38
46
  >
39
47
  <WorkflowRunListHeader
40
- query={search}
41
- onQueryChange={setSearch}
42
- statusFilter={statusFilter}
43
- onStatusFilterChange={setStatusFilter}
48
+ query={currentSearch}
49
+ onQueryChange={(next) => {
50
+ if (onFiltersChange)
51
+ onFiltersChange({...(next ? {search: next} : {}), status: currentStatusFilter});
52
+ else setLocalSearch(next);
53
+ }}
54
+ statusFilter={currentStatusFilter}
55
+ onStatusFilterChange={(next) => {
56
+ if (onFiltersChange)
57
+ onFiltersChange({...(currentSearch ? {search: currentSearch} : {}), status: next});
58
+ else setLocalStatusFilter(next);
59
+ }}
44
60
  />
45
61
  <WorkflowRunListContent
46
62
  query={query}
@@ -9,6 +9,9 @@ export function WorkflowRunList({
9
9
  projectId,
10
10
  selectedWorkflowRunId,
11
11
  className,
12
+ search = '',
13
+ statusFilter = 'all',
14
+ onFiltersChange,
12
15
  }: WorkflowRunListProps) {
13
16
  const query = useWorkflowRunsInfiniteQuery(projectId, {});
14
17
  const runs = useMemo<WorkflowRunListItem[]>(
@@ -24,6 +27,9 @@ export function WorkflowRunList({
24
27
  projectId={projectId}
25
28
  selectedWorkflowRunId={selectedWorkflowRunId}
26
29
  className={className}
30
+ search={search}
31
+ statusFilter={statusFilter}
32
+ {...(onFiltersChange ? {onFiltersChange} : {})}
27
33
  />
28
34
  );
29
35
  }
@@ -1,6 +1,7 @@
1
1
  import {configureApiClient} from '@shipfox/client-api';
2
2
  import {screen, waitFor, within} from '@testing-library/react';
3
3
  import userEvent from '@testing-library/user-event';
4
+ import {toWorkflowRunAttempt} from '#hooks/api/workflow-run-mapper.js';
4
5
  import {workflowRunsQueryKeys} from '#hooks/api/workflow-runs.js';
5
6
  import {
6
7
  runAttemptsResponseDto,
@@ -145,12 +146,10 @@ describe('WorkflowRunAttemptSwitcher', () => {
145
146
  const {queryClient} = renderSwitcher({latestAttempt: 3});
146
147
  queryClient.setQueryData(
147
148
  workflowRunsQueryKeys.attempts(CURRENT_RUN_ID),
148
- runAttemptsResponseDto({
149
- attempts: [
150
- workflowRunAttemptDto({id: ROOT_RUN_ID, attempt: 1}),
151
- workflowRunAttemptDto({id: CURRENT_RUN_ID, attempt: 2}),
152
- ],
153
- }),
149
+ [
150
+ workflowRunAttemptDto({id: ROOT_RUN_ID, attempt: 1}),
151
+ workflowRunAttemptDto({id: CURRENT_RUN_ID, attempt: 2}),
152
+ ].map(toWorkflowRunAttempt),
154
153
  );
155
154
 
156
155
  await user.click(await screen.findByRole('button', {name: 'Switch attempt, currently 2 of 3'}));
@@ -1,4 +1,3 @@
1
- import type {WorkflowRunRerunModeDto} from '@shipfox/api-workflows-dto';
2
1
  import {TriggerSourceIcon} from '@shipfox/client-triggers';
3
2
  import {Badge} from '@shipfox/react-ui/badge';
4
3
  import {Button} from '@shipfox/react-ui/button';
@@ -20,6 +19,7 @@ import {
20
19
  type Job,
21
20
  WORKFLOW_RUN_STATUSES,
22
21
  type WorkflowRunDetail,
22
+ type WorkflowRunRerunMode,
23
23
  } from '#core/workflow-run.js';
24
24
  import {WorkflowRunDurationLabel} from '../workflow-run-duration-label.js';
25
25
  import {getWorkflowStatusVisual} from '../workflow-status/status-visuals.js';
@@ -43,7 +43,7 @@ export interface WorkflowRunSummaryProps {
43
43
  cancelling?: boolean | undefined;
44
44
  onCancel?: (() => void) | undefined;
45
45
  rerunPending?: boolean | undefined;
46
- onRerun?: ((mode: WorkflowRunRerunModeDto) => void) | undefined;
46
+ onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;
47
47
  latestAttempt?: number | undefined;
48
48
  }
49
49
 
@@ -207,7 +207,7 @@ function WorkflowRunActionSlot({
207
207
  cancelling: boolean;
208
208
  onCancel?: (() => void) | undefined;
209
209
  rerunPending: boolean;
210
- onRerun?: ((mode: WorkflowRunRerunModeDto) => void) | undefined;
210
+ onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;
211
211
  }) {
212
212
  if (action === 'none') return null;
213
213
 
@@ -1,4 +1,3 @@
1
- import {agentConfigIssueSchema, stepErrorReasonSchema} from '@shipfox/api-workflows-dto';
2
1
  import {TriggerSourceIcon} from '@shipfox/client-triggers';
3
2
  import {Badge} from '@shipfox/react-ui/badge';
4
3
  import {Button} from '@shipfox/react-ui/button';
@@ -12,9 +11,11 @@ import type {ReactNode} from 'react';
12
11
  import {Fragment, useId, useRef, useState} from 'react';
13
12
  import {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';
14
13
  import {
14
+ AGENT_CONFIG_ISSUES,
15
15
  type Job,
16
16
  type JobExecution,
17
17
  type JobExecutionTime,
18
+ STEP_ERROR_REASONS,
18
19
  type Step,
19
20
  type StepError,
20
21
  type StepSourceLocation,
@@ -404,25 +405,33 @@ function toSelectedAttemptError(
404
405
  ): StepError | null {
405
406
  if (error === null) return null;
406
407
 
407
- const reason = stepErrorReasonSchema.safeParse(error.reason);
408
- const agentConfigIssue = agentConfigIssueSchema.safeParse(
409
- error.agentConfigIssue ?? error.agent_config_issue,
410
- );
408
+ const rawReason = error.reason;
409
+ const parsedReason =
410
+ typeof rawReason === 'string' &&
411
+ STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)
412
+ ? (rawReason as NonNullable<StepError['reason']>)
413
+ : undefined;
414
+ const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;
415
+ const agentConfigIssue =
416
+ typeof rawAgentConfigIssue === 'string' &&
417
+ AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)
418
+ ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)
419
+ : undefined;
411
420
  const exitCode = error.exitCode ?? error.exit_code;
412
- const parsedReason = reason.success
413
- ? reason.data
414
- : agentConfigIssue.success
421
+ const resolvedReason = parsedReason
422
+ ? parsedReason
423
+ : agentConfigIssue
415
424
  ? 'agent_config_invalid'
416
425
  : undefined;
417
426
 
418
- if (parsedReason === undefined) return null;
427
+ if (resolvedReason === undefined) return null;
419
428
 
420
429
  return {
421
430
  message: typeof error.message === 'string' ? error.message : '',
422
431
  exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,
423
432
  signal: typeof error.signal === 'string' ? error.signal : undefined,
424
- reason: parsedReason,
425
- agentConfigIssue: agentConfigIssue.success ? agentConfigIssue.data : undefined,
433
+ reason: resolvedReason,
434
+ agentConfigIssue,
426
435
  category: step.type === 'setup' ? 'setup' : 'user',
427
436
  };
428
437
  }
@@ -122,7 +122,7 @@ describe('WorkflowRunView', () => {
122
122
  listener_status: 'listening',
123
123
  job_executions: [
124
124
  workflowJobExecutionDto({
125
- id: 'execution-1',
125
+ id: '77777777-7777-4777-8777-000000000001',
126
126
  job_id: BUILD_JOB_ID,
127
127
  sequence: 1,
128
128
  status: 'succeeded',
@@ -152,7 +152,7 @@ describe('WorkflowRunView', () => {
152
152
  ],
153
153
  }),
154
154
  workflowJobExecutionDto({
155
- id: 'execution-2',
155
+ id: '77777777-7777-4777-8777-000000000002',
156
156
  job_id: BUILD_JOB_ID,
157
157
  sequence: 2,
158
158
  status: 'failed',
@@ -427,7 +427,7 @@ describe('WorkflowRunView', () => {
427
427
  status: 'succeeded',
428
428
  job_executions: [
429
429
  workflowJobExecutionDto({
430
- id: 'retry-execution',
430
+ id: '77777777-7777-4777-8777-000000000003',
431
431
  job_id: DEPLOY_JOB_ID,
432
432
  sequence: 2,
433
433
  name: 'retry',
@@ -1,4 +1,3 @@
1
- import type {WorkflowRunRerunModeDto} from '@shipfox/api-workflows-dto';
2
1
  import {ApiError} from '@shipfox/client-api';
3
2
  import {QueryLoadError} from '@shipfox/client-ui';
4
3
  import {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
@@ -9,6 +8,7 @@ import {
9
8
  type JobExecution,
10
9
  resolveJobExecution,
11
10
  type StepSourceLocation,
11
+ type WorkflowRunRerunMode,
12
12
  } from '#core/workflow-run.js';
13
13
  import {
14
14
  type WorkflowRunSelectionInput,
@@ -160,7 +160,7 @@ function RunViewContent({
160
160
  const highlightedLineRange =
161
161
  sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;
162
162
  const sourceSnapshot = runData.sourceSnapshot;
163
- async function rerun(mode: WorkflowRunRerunModeDto) {
163
+ async function rerun(mode: WorkflowRunRerunMode) {
164
164
  try {
165
165
  const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});
166
166
  toast.success('Re-run started');
@@ -1,11 +1,14 @@
1
- import type {
2
- WorkflowExecutionEventDto,
3
- WorkflowRunJobExecutionDetailDto,
4
- } from '@shipfox/api-workflows-dto';
5
1
  import {type Duration, intervalToDuration} from 'date-fns';
6
- import {type Step, toStep} from './step.js';
7
-
8
- export type JobExecutionStatus = WorkflowRunJobExecutionDetailDto['status'];
2
+ import type {Step} from './step.js';
3
+
4
+ export type JobExecutionStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled';
5
+ export interface WorkflowExecutionEvent {
6
+ source: string;
7
+ event: string;
8
+ deliveryId: string;
9
+ receivedAt: string;
10
+ data: unknown;
11
+ }
9
12
  export type JobExecutionTime =
10
13
  | {state: 'fixed'; elapsed: Duration}
11
14
  | {state: 'live'; fromIso: string};
@@ -20,7 +23,7 @@ interface JobExecutionFields {
20
23
  name: string;
21
24
  status: JobExecutionStatus;
22
25
  statusReason: string | null;
23
- triggerEvents: WorkflowExecutionEventDto[];
26
+ triggerEvents: WorkflowExecutionEvent[];
24
27
  queuedAt: string | null;
25
28
  startedAt: string | null;
26
29
  finishedAt: string | null;
@@ -37,7 +40,7 @@ export class JobExecution {
37
40
  name!: string;
38
41
  status!: JobExecutionStatus;
39
42
  statusReason!: string | null;
40
- triggerEvents!: WorkflowExecutionEventDto[];
43
+ triggerEvents!: WorkflowExecutionEvent[];
41
44
  queuedAt!: string | null;
42
45
  startedAt!: string | null;
43
46
  finishedAt!: string | null;
@@ -63,25 +66,6 @@ export class JobExecution {
63
66
  }
64
67
  }
65
68
 
66
- export function toJobExecution(dto: WorkflowRunJobExecutionDetailDto): JobExecution {
67
- return new JobExecution({
68
- id: dto.id,
69
- jobId: dto.job_id,
70
- sequence: dto.sequence,
71
- name: dto.name,
72
- status: dto.status,
73
- statusReason: dto.status_reason,
74
- triggerEvents: dto.trigger_events ?? [],
75
- queuedAt: dto.queued_at ?? null,
76
- startedAt: dto.started_at ?? null,
77
- finishedAt: dto.finished_at ?? null,
78
- timedOutAt: dto.timed_out_at ?? null,
79
- createdAt: dto.created_at,
80
- updatedAt: dto.updated_at,
81
- steps: dto.steps.map(toStep),
82
- });
83
- }
84
-
85
69
  export function jobExecutionQueueTimeFromTimestamps({
86
70
  queuedAt,
87
71
  startedAt,
@@ -1,23 +1,45 @@
1
- import type {
2
- JobListeningDto,
3
- JobModeDto,
4
- JobStatusDto,
5
- JobStatusReasonDto,
6
- ListenerStatusDto,
7
- ResolutionReasonDto,
8
- WorkflowRunJobDetailDto,
9
- } from '@shipfox/api-workflows-dto';
10
- import {
11
- type JobExecution,
12
- type JobExecutionDisplayDuration,
13
- toJobExecution,
14
- } from './job-execution.js';
15
-
16
- export type JobStatus = JobStatusDto;
17
- export type JobMode = JobModeDto;
18
- export type ListenerStatus = ListenerStatusDto;
19
- export type ResolutionReason = ResolutionReasonDto;
20
- export type JobStatusReason = JobStatusReasonDto;
1
+ import type {JobExecution, JobExecutionDisplayDuration} from './job-execution.js';
2
+
3
+ export type JobStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'skipped';
4
+ export type JobMode = 'one_shot' | 'listening';
5
+ export type ListenerStatus = 'inactive' | 'listening' | 'resolved';
6
+ export type ResolutionReason = 'until' | 'timeout' | 'max_executions' | 'cancelled';
7
+ export type JobStatusReason =
8
+ | 'dependency_not_completed'
9
+ | 'condition_false'
10
+ | 'default_gate_rejected'
11
+ | 'condition_rejected'
12
+ | 'condition_errored'
13
+ | 'user_cancelled'
14
+ | 'run_cancelled'
15
+ | 'timed_out'
16
+ | 'runner_lost'
17
+ | 'step_failed'
18
+ | 'unknown';
19
+ export interface JobListening {
20
+ on: Array<{
21
+ source: string;
22
+ event: string;
23
+ inputs?: Record<string, unknown> | undefined;
24
+ filter?: string | undefined;
25
+ }>;
26
+ until: Array<{
27
+ source: string;
28
+ event: string;
29
+ inputs?: Record<string, unknown> | undefined;
30
+ filter?: string | undefined;
31
+ }> | null;
32
+ timeoutMs: number | null;
33
+ maxExecutions: number | null;
34
+ batch: {
35
+ debounceMs?: number | undefined;
36
+ maxSize?: number | undefined;
37
+ maxWaitMs?: number | undefined;
38
+ } | null;
39
+ onResolve: 'finish' | 'cancel';
40
+ executionTimeoutMs: number | null;
41
+ name: string | null;
42
+ }
21
43
 
22
44
  export type JobDisplayDuration = JobExecutionDisplayDuration;
23
45
 
@@ -48,7 +70,7 @@ interface JobFields {
48
70
  status: JobStatus;
49
71
  statusReason: JobStatusReason | null;
50
72
  carriedOver: boolean;
51
- listening: JobListeningDto | null;
73
+ listening: JobListening | null;
52
74
  listenerStatus: ListenerStatus;
53
75
  resolutionReason: ResolutionReason | null;
54
76
  dependencies: string[];
@@ -67,7 +89,7 @@ export class Job {
67
89
  status!: JobStatus;
68
90
  statusReason!: JobStatusReason | null;
69
91
  carriedOver!: boolean;
70
- listening!: JobListeningDto | null;
92
+ listening!: JobListening | null;
71
93
  listenerStatus!: ListenerStatus;
72
94
  resolutionReason!: ResolutionReason | null;
73
95
  dependencies!: string[];
@@ -124,26 +146,3 @@ export function defaultJobExecution(job: Job): JobExecution | undefined {
124
146
  return jobExecution.sequence > latest.sequence ? jobExecution : latest;
125
147
  }, undefined);
126
148
  }
127
-
128
- export function toJob(dto: WorkflowRunJobDetailDto): Job {
129
- const jobExecutions = dto.job_executions.map(toJobExecution);
130
-
131
- return new Job({
132
- id: dto.id,
133
- runAttemptId: dto.run_attempt_id,
134
- key: dto.key,
135
- name: dto.name,
136
- mode: dto.mode,
137
- status: dto.status,
138
- statusReason: dto.status_reason,
139
- carriedOver: dto.carried_over,
140
- listening: dto.listening,
141
- listenerStatus: dto.listener_status,
142
- resolutionReason: dto.resolution_reason,
143
- dependencies: dto.dependencies,
144
- position: dto.position,
145
- createdAt: dto.created_at,
146
- updatedAt: dto.updated_at,
147
- jobExecutions,
148
- });
149
- }
@@ -1,7 +1,14 @@
1
- import type {StepAttemptDto, StepGateResultDto} from '@shipfox/api-workflows-dto';
2
1
  import {type Duration, intervalToDuration} from 'date-fns';
3
2
 
4
- export type StepGateResult = StepGateResultDto;
3
+ export type StepGateResult =
4
+ | {kind: 'none'}
5
+ | {kind: 'not_evaluated'}
6
+ | {kind: 'passed'; passed: true; source: string; exitCode: number | null}
7
+ | {kind: 'failed'; passed: false; source: string; exitCode: number | null}
8
+ | {kind: 'uncheckable'; passed: false; uncheckable: true; reason: string; exitCode: number | null}
9
+ | {kind: 'evaluation_error'; reason: string; exitCode: number | null}
10
+ | {kind: 'unknown'; data: Record<string, unknown>}
11
+ | null;
5
12
  export type StepAttemptDisplayDuration =
6
13
  | {state: 'fixed'; elapsed: Duration}
7
14
  | {state: 'live'; fromIso: string};
@@ -46,24 +53,6 @@ export class StepAttempt {
46
53
  }
47
54
  }
48
55
 
49
- export function toStepAttempt(dto: StepAttemptDto, jobExecutionId: string): StepAttempt {
50
- return new StepAttempt({
51
- id: dto.id,
52
- stepId: dto.step_id,
53
- jobExecutionId,
54
- attempt: dto.attempt,
55
- executionOrder: dto.execution_order,
56
- status: dto.status,
57
- exitCode: dto.exit_code ?? null,
58
- output: dto.output ?? null,
59
- error: dto.error ?? null,
60
- gateResult: dto.gate_result ?? null,
61
- restartFeedback: dto.restart_feedback ?? null,
62
- startedAt: dto.started_at,
63
- finishedAt: dto.finished_at ?? null,
64
- });
65
- }
66
-
67
56
  export function stepAttemptDisplayDurationFromTimestamps({
68
57
  startedAt,
69
58
  finishedAt,
@@ -1,15 +1,42 @@
1
- import type {
2
- AgentConfigIssueDto,
3
- StepErrorCategoryDto,
4
- StepErrorReasonDto,
5
- StepSourceLocationDto,
6
- WorkflowRunStepDetailDto,
7
- } from '@shipfox/api-workflows-dto';
8
- import {type StepAttempt, toStepAttempt} from './step-attempt.js';
1
+ import type {StepAttempt} from './step-attempt.js';
9
2
 
10
- export type StepErrorReason = StepErrorReasonDto;
11
- export type AgentConfigIssue = AgentConfigIssueDto;
12
- export type StepErrorCategory = StepErrorCategoryDto;
3
+ export type StepErrorReason =
4
+ | 'checkout_failed'
5
+ | 'checkout_auth_failed'
6
+ | 'checkout_unavailable'
7
+ | 'git_unavailable'
8
+ | 'workspace_prep_failed'
9
+ | 'setup_aborted'
10
+ | 'config_unresolvable'
11
+ | 'output_invalid'
12
+ | 'agent_config_invalid'
13
+ | 'agent_invocation_failed';
14
+ export type AgentConfigIssue =
15
+ | 'step_config_invalid'
16
+ | 'provider_not_configured'
17
+ | 'provider_unsupported'
18
+ | 'model_unavailable'
19
+ | 'credentials_invalid';
20
+ export type StepErrorCategory = 'setup' | 'user';
21
+ export const STEP_ERROR_REASONS = new Set<StepErrorReason>([
22
+ 'checkout_failed',
23
+ 'checkout_auth_failed',
24
+ 'checkout_unavailable',
25
+ 'git_unavailable',
26
+ 'workspace_prep_failed',
27
+ 'setup_aborted',
28
+ 'config_unresolvable',
29
+ 'output_invalid',
30
+ 'agent_config_invalid',
31
+ 'agent_invocation_failed',
32
+ ]);
33
+ export const AGENT_CONFIG_ISSUES = new Set<AgentConfigIssue>([
34
+ 'step_config_invalid',
35
+ 'provider_not_configured',
36
+ 'provider_unsupported',
37
+ 'model_unavailable',
38
+ 'credentials_invalid',
39
+ ]);
13
40
 
14
41
  export interface StepSourceLocation {
15
42
  startLine: number;
@@ -48,58 +75,3 @@ export interface Step {
48
75
  updatedAt: string;
49
76
  attempts: StepAttempt[];
50
77
  }
51
-
52
- export function toStep(dto: WorkflowRunStepDetailDto): Step {
53
- return {
54
- id: dto.id,
55
- jobExecutionId: dto.job_execution_id,
56
- key: dto.key,
57
- name: dto.name,
58
- sourceLocation: dto.source_location ? toStepSourceLocation(dto.source_location) : null,
59
- status: dto.status,
60
- type: dto.type,
61
- config: dto.config,
62
- agentConfig: toAgentStepConfig(dto),
63
- error: dto.error ? toStepError(dto.error) : null,
64
- position: dto.position,
65
- currentAttempt: dto.current_attempt,
66
- createdAt: dto.created_at,
67
- updatedAt: dto.updated_at,
68
- attempts: dto.attempts.map((attempt) => toStepAttempt(attempt, dto.job_execution_id)),
69
- };
70
- }
71
-
72
- function toStepSourceLocation(dto: StepSourceLocationDto): StepSourceLocation {
73
- return {
74
- startLine: dto.start_line,
75
- endLine: dto.end_line,
76
- };
77
- }
78
-
79
- function toStepError(dto: NonNullable<WorkflowRunStepDetailDto['error']>): StepError {
80
- return {
81
- message: dto.message,
82
- exitCode: dto.exit_code ?? null,
83
- signal: dto.signal,
84
- reason: dto.reason,
85
- agentConfigIssue: dto.agent_config_issue,
86
- category: dto.category,
87
- };
88
- }
89
-
90
- function toAgentStepConfig(dto: WorkflowRunStepDetailDto): AgentStepConfig | null {
91
- if (dto.type !== 'agent') return null;
92
-
93
- return {
94
- provider: stringConfigValue(dto.config.provider),
95
- model: stringConfigValue(dto.config.model),
96
- thinking: stringConfigValue(dto.config.thinking),
97
- };
98
- }
99
-
100
- function stringConfigValue(value: unknown): string | null {
101
- if (typeof value !== 'string') return null;
102
-
103
- const trimmedValue = value.trim();
104
- return trimmedValue ? trimmedValue : null;
105
- }