@shipfox/api-workflows-dto 12.5.0 → 12.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/api-workflows-dto",
3
3
  "license": "MIT",
4
- "version": "12.5.0",
4
+ "version": "12.7.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
package/src/index.ts CHANGED
@@ -26,6 +26,7 @@ export {
26
26
  type JobStatusDto,
27
27
  type JobStatusReasonDto,
28
28
  jobDtoSchema,
29
+ jobExecutionStatusSchema,
29
30
  jobListeningBatchSchema,
30
31
  jobListeningSchema,
31
32
  jobModeSchema,
@@ -95,6 +96,7 @@ export {
95
96
  workflowRunDetailResponseSchema,
96
97
  workflowRunDtoSchema,
97
98
  workflowRunJobDetailDtoSchema,
99
+ workflowRunJobDisplayStatusCountDtoSchema,
98
100
  workflowRunJobExecutionDetailDtoSchema,
99
101
  workflowRunJobStatusCountDtoSchema,
100
102
  workflowRunJobSummaryDtoSchema,
@@ -109,6 +111,7 @@ export {
109
111
  export {type StepSourceLocationDto, stepSourceLocationSchema} from '#schemas/step.js';
110
112
  export {
111
113
  WORKFLOW_RUN_JOB_PREVIEW_LIMIT,
114
+ type WorkflowRunJobDisplayStatusCountDto,
112
115
  type WorkflowRunJobStatusCountDto,
113
116
  type WorkflowRunJobSummaryDto,
114
117
  type WorkflowRunListItemDto,
@@ -81,6 +81,7 @@ export {
81
81
  stepStatusReasonSchema,
82
82
  } from './step.js';
83
83
  export {
84
+ jobExecutionStatusSchema,
84
85
  type RerunWorkflowRunBodyDto,
85
86
  rerunWorkflowRunBodySchema,
86
87
  WORKFLOW_RUN_JOB_PREVIEW_LIMIT,
@@ -89,6 +90,7 @@ export {
89
90
  type WorkflowRunAttemptDto,
90
91
  type WorkflowRunAttemptsResponseDto,
91
92
  type WorkflowRunDto,
93
+ type WorkflowRunJobDisplayStatusCountDto,
92
94
  type WorkflowRunJobStatusCountDto,
93
95
  type WorkflowRunJobSummaryDto,
94
96
  type WorkflowRunListItemDto,
@@ -104,6 +106,7 @@ export {
104
106
  workflowRunAttemptDtoSchema,
105
107
  workflowRunAttemptsResponseSchema,
106
108
  workflowRunDtoSchema,
109
+ workflowRunJobDisplayStatusCountDtoSchema,
107
110
  workflowRunJobStatusCountDtoSchema,
108
111
  workflowRunJobSummaryDtoSchema,
109
112
  workflowRunListItemSchema,
@@ -118,7 +121,6 @@ export {
118
121
  export {
119
122
  type JobExecutionDto,
120
123
  jobExecutionDtoSchema,
121
- jobExecutionStatusSchema,
122
124
  type StepAttemptDetailResponseDto,
123
125
  stepAttemptDetailResponseSchema,
124
126
  type WorkflowRunDetailResponseDto,
@@ -3,15 +3,11 @@ import {evaluationTraceSchema} from './evaluation-trace.js';
3
3
  import {jobDtoSchema} from './job.js';
4
4
  import {workflowExecutionEventSchema} from './job-listening.js';
5
5
  import {stepAttemptDetailDtoSchema, stepAttemptDtoSchema, stepDtoSchema} from './step.js';
6
- import {workflowRunAttemptDtoSchema, workflowRunResponseSchema} from './workflow-run.js';
7
-
8
- export const jobExecutionStatusSchema = z.enum([
9
- 'pending',
10
- 'running',
11
- 'succeeded',
12
- 'failed',
13
- 'cancelled',
14
- ]);
6
+ import {
7
+ jobExecutionStatusSchema,
8
+ workflowRunAttemptDtoSchema,
9
+ workflowRunResponseSchema,
10
+ } from './workflow-run.js';
15
11
 
16
12
  export const jobExecutionDtoSchema = z.object({
17
13
  id: z.string().uuid(),
@@ -76,6 +72,14 @@ export type WorkflowRunJobDetailDto = z.infer<typeof workflowRunJobDetailDtoSche
76
72
  export const workflowRunDetailResponseSchema = workflowRunResponseSchema.extend({
77
73
  run_attempt: workflowRunAttemptDtoSchema,
78
74
  jobs: z.array(workflowRunJobDetailDtoSchema),
75
+ /**
76
+ * Whether any job execution of this attempt reached its runner. Redundant with the executions
77
+ * below, and deliberately so: the server decides it once for both this response and the run
78
+ * list, which is what keeps the two surfaces from reaching different answers.
79
+ *
80
+ * Defaults to started for the same rollout reason as the list item's copy.
81
+ */
82
+ has_started_job_execution: z.boolean().optional().default(true),
79
83
  });
80
84
 
81
85
  export type WorkflowRunDetailResponseDto = z.infer<typeof workflowRunDetailResponseSchema>;
@@ -107,6 +107,9 @@ describe('workflow run list item schema', () => {
107
107
  key: `job-${position}`,
108
108
  name: null,
109
109
  status: 'succeeded' as const,
110
+ mode: 'one_shot' as const,
111
+ listener_status: 'inactive' as const,
112
+ execution_status: null,
110
113
  position,
111
114
  };
112
115
  }
@@ -117,10 +120,60 @@ describe('workflow run list item schema', () => {
117
120
  source_snapshot: null,
118
121
  jobs: [jobDto(0)],
119
122
  job_status_counts: [{status: 'succeeded', count: 1}],
123
+ has_started_job_execution: true,
120
124
  });
121
125
 
122
126
  expect(result.jobs).toHaveLength(1);
123
127
  expect(result.jobs[0]?.status).toBe('succeeded');
128
+ expect(result.jobs[0]?.execution_status).toBeNull();
129
+ });
130
+
131
+ test('carries execution evidence and listening state for display derivation', () => {
132
+ const result = workflowRunListItemSchema.parse({
133
+ ...baseRun,
134
+ source_snapshot: null,
135
+ jobs: [
136
+ {
137
+ ...jobDto(0),
138
+ mode: 'listening',
139
+ listener_status: 'listening',
140
+ execution_status: 'running',
141
+ },
142
+ ],
143
+ job_status_counts: [{status: 'running', count: 1}],
144
+ job_display_status_counts: [{status: 'listening', count: 1}],
145
+ });
146
+
147
+ expect(result.jobs[0]).toMatchObject({
148
+ mode: 'listening',
149
+ listener_status: 'listening',
150
+ execution_status: 'running',
151
+ });
152
+ expect(result.job_status_counts).toEqual([{status: 'running', count: 1}]);
153
+ expect(result.job_display_status_counts).toEqual([{status: 'listening', count: 1}]);
154
+ });
155
+
156
+ test('accepts a pre-display-state API response during a mixed-version rollout', () => {
157
+ const {
158
+ mode: _mode,
159
+ listener_status: _listenerStatus,
160
+ execution_status: _executionStatus,
161
+ ...legacyJob
162
+ } = jobDto(0);
163
+
164
+ const result = workflowRunListItemSchema.parse({
165
+ ...baseRun,
166
+ source_snapshot: null,
167
+ jobs: [legacyJob],
168
+ job_status_counts: [{status: 'running', count: 1}],
169
+ });
170
+
171
+ expect(result.jobs[0]).toMatchObject({
172
+ mode: 'one_shot',
173
+ listener_status: 'inactive',
174
+ execution_status: null,
175
+ });
176
+ expect(result.job_display_status_counts).toBeUndefined();
124
177
  });
125
178
 
126
179
  // The preview is a bounded slice, so counts describe jobs the payload never carried.
@@ -133,6 +186,7 @@ describe('workflow run list item schema', () => {
133
186
  {status: 'succeeded', count: 40},
134
187
  {status: 'failed', count: 2},
135
188
  ],
189
+ has_started_job_execution: true,
136
190
  });
137
191
 
138
192
  expect(result.job_status_counts).toHaveLength(2);
@@ -144,6 +198,7 @@ describe('workflow run list item schema', () => {
144
198
  source_snapshot: null,
145
199
  jobs: Array.from({length: WORKFLOW_RUN_JOB_PREVIEW_LIMIT + 1}, (_, index) => jobDto(index)),
146
200
  job_status_counts: [],
201
+ has_started_job_execution: false,
147
202
  });
148
203
 
149
204
  expect(result.success).toBe(false);
@@ -1,5 +1,6 @@
1
1
  import {z} from 'zod';
2
2
  import {jobStatusSchema} from './job.js';
3
+ import {jobModeSchema, listenerStatusSchema} from './job-listening.js';
3
4
 
4
5
  export const workflowRunStatusSchema = z.enum([
5
6
  'pending',
@@ -11,6 +12,14 @@ export const workflowRunStatusSchema = z.enum([
11
12
 
12
13
  export type WorkflowRunStatusDto = z.infer<typeof workflowRunStatusSchema>;
13
14
 
15
+ export const jobExecutionStatusSchema = z.enum([
16
+ 'pending',
17
+ 'running',
18
+ 'succeeded',
19
+ 'failed',
20
+ 'cancelled',
21
+ ]);
22
+
14
23
  export const workflowRunRerunModeSchema = z.enum(['all', 'failed']);
15
24
 
16
25
  export type WorkflowRunRerunModeDto = z.infer<typeof workflowRunRerunModeSchema>;
@@ -137,16 +146,22 @@ export const workflowRunAttemptsResponseSchema = z.object({
137
146
  export type WorkflowRunAttemptsResponseDto = z.infer<typeof workflowRunAttemptsResponseSchema>;
138
147
 
139
148
  // The run list renders a status glyph per job so a failing run can be read without being
140
- // opened, which needs the current attempt's jobs in graph order but none of their steps.
149
+ // opened. Runtime state comes from the selected execution rather than the job verdict, while
150
+ // mode and listener status let the client apply the same display rule as run detail. These
151
+ // fields default to the pre-display-state contract so a web client can roll out before or
152
+ // alongside an API deployment without rejecting an older response.
141
153
  export const workflowRunJobSummaryDtoSchema = z.object({
142
154
  id: z.string().uuid(),
143
155
  key: z.string(),
144
156
  name: z.string().nullable(),
145
157
  status: jobStatusSchema,
158
+ mode: jobModeSchema.optional().default('one_shot'),
159
+ listener_status: listenerStatusSchema.optional().default('inactive'),
160
+ execution_status: jobExecutionStatusSchema.nullable().optional().default(null),
146
161
  position: z.number().int().nonnegative(),
147
162
  });
148
163
 
149
- export type WorkflowRunJobSummaryDto = z.infer<typeof workflowRunJobSummaryDtoSchema>;
164
+ export type WorkflowRunJobSummaryDto = z.input<typeof workflowRunJobSummaryDtoSchema>;
150
165
 
151
166
  /**
152
167
  * How many jobs a run list row carries in graph order.
@@ -158,7 +173,7 @@ export type WorkflowRunJobSummaryDto = z.infer<typeof workflowRunJobSummaryDtoSc
158
173
  */
159
174
  export const WORKFLOW_RUN_JOB_PREVIEW_LIMIT = 16;
160
175
 
161
- /** One status and how many of the run's jobs hold it, counted over all of them. */
176
+ /** The persisted job verdict and how many of the run's jobs carry it, counted over all of them. */
162
177
  export const workflowRunJobStatusCountDtoSchema = z.object({
163
178
  status: jobStatusSchema,
164
179
  count: z.number().int().positive(),
@@ -166,14 +181,37 @@ export const workflowRunJobStatusCountDtoSchema = z.object({
166
181
 
167
182
  export type WorkflowRunJobStatusCountDto = z.infer<typeof workflowRunJobStatusCountDtoSchema>;
168
183
 
184
+ /** One display status and how many of the run's jobs render it, counted over all of them. */
185
+ export const workflowRunJobDisplayStatusCountDtoSchema = z.object({
186
+ status: jobStatusSchema.or(z.literal('listening')),
187
+ count: z.number().int().positive(),
188
+ });
189
+
190
+ export type WorkflowRunJobDisplayStatusCountDto = z.infer<
191
+ typeof workflowRunJobDisplayStatusCountDtoSchema
192
+ >;
193
+
169
194
  export const workflowRunListItemSchema = workflowRunResponseSchema.extend({
170
195
  /** Up to `WORKFLOW_RUN_JOB_PREVIEW_LIMIT` jobs in graph order, not the whole set. */
171
196
  jobs: z.array(workflowRunJobSummaryDtoSchema).max(WORKFLOW_RUN_JOB_PREVIEW_LIMIT),
172
- /** Counted over every job of the attempt, including those past the preview. */
197
+ /** Persisted verdict counts, kept stable so older web clients can consume new responses. */
173
198
  job_status_counts: z.array(workflowRunJobStatusCountDtoSchema),
199
+ /**
200
+ * Display-state counts over every job of the attempt, including those past the preview.
201
+ * Optional so a new web client can consume an older API response during rollout.
202
+ */
203
+ job_display_status_counts: z.array(workflowRunJobDisplayStatusCountDtoSchema).optional(),
204
+ /**
205
+ * Whether any job execution in the attempt reached its runner. A `cancelled` job does not say
206
+ * this on its own, so the counts above cannot answer it.
207
+ *
208
+ * Defaults to started, so an older API response during a rollout keeps the reading a run had
209
+ * before this field existed rather than claiming work that ran never began.
210
+ */
211
+ has_started_job_execution: z.boolean().optional().default(true),
174
212
  });
175
213
 
176
- export type WorkflowRunListItemDto = z.infer<typeof workflowRunListItemSchema>;
214
+ export type WorkflowRunListItemDto = z.input<typeof workflowRunListItemSchema>;
177
215
 
178
216
  export const workflowRunListResponseSchema = z.object({
179
217
  runs: z.array(workflowRunListItemSchema),
@@ -181,7 +219,7 @@ export const workflowRunListResponseSchema = z.object({
181
219
  filtered_total_count: z.number().int().nonnegative().nullable(),
182
220
  });
183
221
 
184
- export type WorkflowRunListResponseDto = z.infer<typeof workflowRunListResponseSchema>;
222
+ export type WorkflowRunListResponseDto = z.input<typeof workflowRunListResponseSchema>;
185
223
 
186
224
  const aggregateBucketSchema = z.object({
187
225
  value: z.string(),