@shipfox/client-workflows 22.0.3 → 23.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.
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 86 files with swc (508.33ms)
2
+ Successfully compiled: 86 files with swc (603.81ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @shipfox/client-workflows
2
2
 
3
+ ## 23.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4f30864: Trigger `event` is now optional end to end. An omitted event subscribes to every event from its source. Explicit events continue to work unchanged. Built-in manual and scheduled triggers use `fire` and `tick`, respectively.
8
+
9
+ ### Patch Changes
10
+
11
+ - f7b3db8: Replaces published definition warning exports and the sync `warnings` field with severity-aware diagnostics, including workflow file paths for the workflows UI.
12
+
13
+ Consumers must migrate from `warnings` to `diagnostics`; this release does not provide a legacy-field fallback.
14
+
15
+ - Updated dependencies [693e656]
16
+ - Updated dependencies [c0fc35b]
17
+ - Updated dependencies [05c7c4d]
18
+ - Updated dependencies [f7b3db8]
19
+ - Updated dependencies [4f30864]
20
+ - Updated dependencies [aeaa0de]
21
+ - Updated dependencies [b766ee3]
22
+ - @shipfox/client-shell@23.0.0
23
+ - @shipfox/client-projects@23.0.0
24
+ - @shipfox/api-definitions-dto@14.0.0
25
+ - @shipfox/client-triggers@23.0.0
26
+ - @shipfox/api-workflows-dto@14.0.0
27
+
3
28
  ## 22.0.3
4
29
 
5
30
  ### Patch Changes
@@ -9,13 +9,13 @@ export type JobStatusReason = 'dependency_not_completed' | 'condition_false' | '
9
9
  export interface JobListening {
10
10
  on: Array<{
11
11
  source: string;
12
- event: string;
12
+ event?: string | undefined;
13
13
  inputs?: Record<string, unknown> | undefined;
14
14
  filter?: string | undefined;
15
15
  }>;
16
16
  until: Array<{
17
17
  source: string;
18
- event: string;
18
+ event?: string | undefined;
19
19
  inputs?: Record<string, unknown> | undefined;
20
20
  filter?: string | undefined;
21
21
  }> | null;
@@ -1 +1 @@
1
- {"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../../../src/core/entities/job.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../../../src/core/entities/job.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/entities/job.ts"],"sourcesContent":["import {\n deriveJobExecutionDisplayStatus,\n type JobExecution,\n type JobExecutionDisplayDuration,\n type JobExecutionStatus,\n} from './job-execution.js';\nimport type {EvaluationTraceEntry} from './step-attempt.js';\n\nexport type JobStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'skipped';\nexport type JobDisplayStatus = JobStatus | 'listening';\nexport type JobMode = 'one_shot' | 'listening';\nexport type ListenerStatus = 'inactive' | 'listening' | 'resolved';\nexport type ResolutionReason = 'until' | 'timeout' | 'max_executions' | 'cancelled';\nexport type JobStatusReason =\n | 'dependency_not_completed'\n | 'condition_false'\n | 'default_gate_rejected'\n | 'condition_rejected'\n | 'condition_errored'\n | 'user_cancelled'\n | 'run_cancelled'\n | 'timed_out'\n | 'runner_lost'\n | 'output_too_large'\n | 'step_failed'\n | 'unknown'\n | 'output_invalid';\nexport interface JobListening {\n on: Array<{\n source: string;\n event: string;\n inputs?: Record<string, unknown> | undefined;\n filter?: string | undefined;\n }>;\n until: Array<{\n source: string;\n event: string;\n inputs?: Record<string, unknown> | undefined;\n filter?: string | undefined;\n }> | null;\n timeoutMs: number | null;\n maxExecutions: number | null;\n batch: {\n debounceMs?: number | undefined;\n maxSize?: number | undefined;\n maxWaitMs?: number | undefined;\n } | null;\n onResolve: 'finish' | 'cancel';\n executionTimeoutMs: number | null;\n name: string | null;\n}\n\nexport type JobDisplayDuration = JobExecutionDisplayDuration;\n\nexport const WORKFLOW_JOB_STATUSES = [\n 'pending',\n 'running',\n 'succeeded',\n 'failed',\n 'cancelled',\n 'skipped',\n] as const satisfies readonly JobStatus[];\n\nexport const TERMINAL_WORKFLOW_JOB_STATUSES = [\n 'succeeded',\n 'failed',\n 'cancelled',\n 'skipped',\n] as const satisfies readonly JobStatus[];\n\nconst TERMINAL_JOB_STATUS_SET = new Set<JobStatus>(TERMINAL_WORKFLOW_JOB_STATUSES);\n\ninterface JobFields {\n id: string;\n runAttemptId: string;\n key: string;\n name: string | null;\n mode: JobMode;\n status: JobStatus;\n statusReason: JobStatusReason | null;\n carriedOver: boolean;\n outputs: Record<string, unknown> | null;\n success: string | null;\n runner: string[] | null;\n evaluationTrace: EvaluationTraceEntry[] | null;\n listening: JobListening | null;\n listenerStatus: ListenerStatus;\n resolutionReason: ResolutionReason | null;\n dependencies: string[];\n position: number;\n createdAt: string;\n updatedAt: string;\n jobExecutions: JobExecution[];\n}\n\nexport class Job {\n id!: string;\n runAttemptId!: string;\n key!: string;\n name!: string | null;\n mode!: JobMode;\n status!: JobStatus;\n statusReason!: JobStatusReason | null;\n carriedOver!: boolean;\n outputs!: Record<string, unknown> | null;\n success!: string | null;\n runner!: string[] | null;\n evaluationTrace!: EvaluationTraceEntry[] | null;\n listening!: JobListening | null;\n listenerStatus!: ListenerStatus;\n resolutionReason!: ResolutionReason | null;\n dependencies!: string[];\n position!: number;\n createdAt!: string;\n updatedAt!: string;\n jobExecutions!: JobExecution[];\n\n constructor(fields: JobFields) {\n Object.assign(this, fields);\n }\n\n get displayName(): string {\n return this.name ?? this.key;\n }\n\n get displayDuration(): JobDisplayDuration | null {\n if (this.mode === 'listening') return null;\n if (this.jobExecutions.length !== 1) return null;\n return this.jobExecutions[0]?.displayDuration ?? null;\n }\n\n get executionCountVisible(): boolean {\n return (\n this.jobExecutions.length > 0 && (this.mode === 'listening' || this.jobExecutions.length > 1)\n );\n }\n}\n\nexport function isTerminalJobStatus(status: JobStatus): boolean {\n return TERMINAL_JOB_STATUS_SET.has(status);\n}\n\nexport function deriveJobDisplayStatus(\n job: Pick<Job, 'mode' | 'status' | 'listenerStatus' | 'jobExecutions'> & {\n /** List previews carry the selected execution state without its step tree. */\n executionStatus?: JobExecutionStatus | null | undefined;\n },\n): JobDisplayStatus {\n if (isTerminalJobStatus(job.status)) return job.status;\n if (job.mode === 'listening' && job.listenerStatus === 'listening') return 'listening';\n const execution =\n job.executionStatus === undefined\n ? defaultJobExecution(job)\n : job.executionStatus === null\n ? undefined\n : {status: job.executionStatus, steps: []};\n // A running execution is the shared display rule for both list previews and detail jobs.\n // The list has no step tree, while the detail path can still use the execution's steps for\n // other non-running states.\n if (execution?.status === 'running') return 'running';\n return execution ? deriveJobExecutionDisplayStatus(execution) : 'pending';\n}\n\nexport function resolveJobExecution(\n job: Job,\n jobExecutionId: string | undefined,\n): JobExecution | undefined {\n const selectedExecution = jobExecutionId\n ? job.jobExecutions.find((jobExecution) => jobExecution.id === jobExecutionId)\n : undefined;\n if (selectedExecution) return selectedExecution;\n\n return defaultJobExecution(job);\n}\n\nexport function defaultJobExecution(job: Pick<Job, 'jobExecutions'>): JobExecution | undefined {\n const runningExecution = job.jobExecutions.find(\n (jobExecution) => jobExecution.status === 'running',\n );\n if (runningExecution) return runningExecution;\n\n return job.jobExecutions.reduce<JobExecution | undefined>((latest, jobExecution) => {\n if (!latest) return jobExecution;\n return jobExecution.sequence > latest.sequence ? jobExecution : latest;\n }, undefined);\n}\n"],"names":["deriveJobExecutionDisplayStatus","WORKFLOW_JOB_STATUSES","TERMINAL_WORKFLOW_JOB_STATUSES","TERMINAL_JOB_STATUS_SET","Set","Job","fields","Object","assign","displayName","name","key","displayDuration","mode","jobExecutions","length","executionCountVisible","isTerminalJobStatus","status","has","deriveJobDisplayStatus","job","listenerStatus","execution","executionStatus","undefined","defaultJobExecution","steps","resolveJobExecution","jobExecutionId","selectedExecution","find","jobExecution","id","runningExecution","reduce","latest","sequence"],"mappings":"AAAA,SACEA,+BAA+B,QAI1B,qBAAqB;AAiD5B,OAAO,MAAMC,wBAAwB;IACnC;IACA;IACA;IACA;IACA;IACA;CACD,CAAyC;AAE1C,OAAO,MAAMC,iCAAiC;IAC5C;IACA;IACA;IACA;CACD,CAAyC;AAE1C,MAAMC,0BAA0B,IAAIC,IAAeF;AAyBnD,OAAO,MAAMG;IAsBX,YAAYC,MAAiB,CAAE;QAC7BC,OAAOC,MAAM,CAAC,IAAI,EAAEF;IACtB;IAEA,IAAIG,cAAsB;QACxB,OAAO,IAAI,CAACC,IAAI,IAAI,IAAI,CAACC,GAAG;IAC9B;IAEA,IAAIC,kBAA6C;QAC/C,IAAI,IAAI,CAACC,IAAI,KAAK,aAAa,OAAO;QACtC,IAAI,IAAI,CAACC,aAAa,CAACC,MAAM,KAAK,GAAG,OAAO;QAC5C,OAAO,IAAI,CAACD,aAAa,CAAC,EAAE,EAAEF,mBAAmB;IACnD;IAEA,IAAII,wBAAiC;QACnC,OACE,IAAI,CAACF,aAAa,CAACC,MAAM,GAAG,KAAM,CAAA,IAAI,CAACF,IAAI,KAAK,eAAe,IAAI,CAACC,aAAa,CAACC,MAAM,GAAG,CAAA;IAE/F;AACF;AAEA,OAAO,SAASE,oBAAoBC,MAAiB;IACnD,OAAOf,wBAAwBgB,GAAG,CAACD;AACrC;AAEA,OAAO,SAASE,uBACdC,GAGC;IAED,IAAIJ,oBAAoBI,IAAIH,MAAM,GAAG,OAAOG,IAAIH,MAAM;IACtD,IAAIG,IAAIR,IAAI,KAAK,eAAeQ,IAAIC,cAAc,KAAK,aAAa,OAAO;IAC3E,MAAMC,YACJF,IAAIG,eAAe,KAAKC,YACpBC,oBAAoBL,OACpBA,IAAIG,eAAe,KAAK,OACtBC,YACA;QAACP,QAAQG,IAAIG,eAAe;QAAEG,OAAO,EAAE;IAAA;IAC/C,yFAAyF;IACzF,2FAA2F;IAC3F,4BAA4B;IAC5B,IAAIJ,WAAWL,WAAW,WAAW,OAAO;IAC5C,OAAOK,YAAYvB,gCAAgCuB,aAAa;AAClE;AAEA,OAAO,SAASK,oBACdP,GAAQ,EACRQ,cAAkC;IAElC,MAAMC,oBAAoBD,iBACtBR,IAAIP,aAAa,CAACiB,IAAI,CAAC,CAACC,eAAiBA,aAAaC,EAAE,KAAKJ,kBAC7DJ;IACJ,IAAIK,mBAAmB,OAAOA;IAE9B,OAAOJ,oBAAoBL;AAC7B;AAEA,OAAO,SAASK,oBAAoBL,GAA+B;IACjE,MAAMa,mBAAmBb,IAAIP,aAAa,CAACiB,IAAI,CAC7C,CAACC,eAAiBA,aAAad,MAAM,KAAK;IAE5C,IAAIgB,kBAAkB,OAAOA;IAE7B,OAAOb,IAAIP,aAAa,CAACqB,MAAM,CAA2B,CAACC,QAAQJ;QACjE,IAAI,CAACI,QAAQ,OAAOJ;QACpB,OAAOA,aAAaK,QAAQ,GAAGD,OAAOC,QAAQ,GAAGL,eAAeI;IAClE,GAAGX;AACL"}
1
+ {"version":3,"sources":["../../../src/core/entities/job.ts"],"sourcesContent":["import {\n deriveJobExecutionDisplayStatus,\n type JobExecution,\n type JobExecutionDisplayDuration,\n type JobExecutionStatus,\n} from './job-execution.js';\nimport type {EvaluationTraceEntry} from './step-attempt.js';\n\nexport type JobStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'skipped';\nexport type JobDisplayStatus = JobStatus | 'listening';\nexport type JobMode = 'one_shot' | 'listening';\nexport type ListenerStatus = 'inactive' | 'listening' | 'resolved';\nexport type ResolutionReason = 'until' | 'timeout' | 'max_executions' | 'cancelled';\nexport type JobStatusReason =\n | 'dependency_not_completed'\n | 'condition_false'\n | 'default_gate_rejected'\n | 'condition_rejected'\n | 'condition_errored'\n | 'user_cancelled'\n | 'run_cancelled'\n | 'timed_out'\n | 'runner_lost'\n | 'output_too_large'\n | 'step_failed'\n | 'unknown'\n | 'output_invalid';\nexport interface JobListening {\n on: Array<{\n source: string;\n event?: string | undefined;\n inputs?: Record<string, unknown> | undefined;\n filter?: string | undefined;\n }>;\n until: Array<{\n source: string;\n event?: string | undefined;\n inputs?: Record<string, unknown> | undefined;\n filter?: string | undefined;\n }> | null;\n timeoutMs: number | null;\n maxExecutions: number | null;\n batch: {\n debounceMs?: number | undefined;\n maxSize?: number | undefined;\n maxWaitMs?: number | undefined;\n } | null;\n onResolve: 'finish' | 'cancel';\n executionTimeoutMs: number | null;\n name: string | null;\n}\n\nexport type JobDisplayDuration = JobExecutionDisplayDuration;\n\nexport const WORKFLOW_JOB_STATUSES = [\n 'pending',\n 'running',\n 'succeeded',\n 'failed',\n 'cancelled',\n 'skipped',\n] as const satisfies readonly JobStatus[];\n\nexport const TERMINAL_WORKFLOW_JOB_STATUSES = [\n 'succeeded',\n 'failed',\n 'cancelled',\n 'skipped',\n] as const satisfies readonly JobStatus[];\n\nconst TERMINAL_JOB_STATUS_SET = new Set<JobStatus>(TERMINAL_WORKFLOW_JOB_STATUSES);\n\ninterface JobFields {\n id: string;\n runAttemptId: string;\n key: string;\n name: string | null;\n mode: JobMode;\n status: JobStatus;\n statusReason: JobStatusReason | null;\n carriedOver: boolean;\n outputs: Record<string, unknown> | null;\n success: string | null;\n runner: string[] | null;\n evaluationTrace: EvaluationTraceEntry[] | null;\n listening: JobListening | null;\n listenerStatus: ListenerStatus;\n resolutionReason: ResolutionReason | null;\n dependencies: string[];\n position: number;\n createdAt: string;\n updatedAt: string;\n jobExecutions: JobExecution[];\n}\n\nexport class Job {\n id!: string;\n runAttemptId!: string;\n key!: string;\n name!: string | null;\n mode!: JobMode;\n status!: JobStatus;\n statusReason!: JobStatusReason | null;\n carriedOver!: boolean;\n outputs!: Record<string, unknown> | null;\n success!: string | null;\n runner!: string[] | null;\n evaluationTrace!: EvaluationTraceEntry[] | null;\n listening!: JobListening | null;\n listenerStatus!: ListenerStatus;\n resolutionReason!: ResolutionReason | null;\n dependencies!: string[];\n position!: number;\n createdAt!: string;\n updatedAt!: string;\n jobExecutions!: JobExecution[];\n\n constructor(fields: JobFields) {\n Object.assign(this, fields);\n }\n\n get displayName(): string {\n return this.name ?? this.key;\n }\n\n get displayDuration(): JobDisplayDuration | null {\n if (this.mode === 'listening') return null;\n if (this.jobExecutions.length !== 1) return null;\n return this.jobExecutions[0]?.displayDuration ?? null;\n }\n\n get executionCountVisible(): boolean {\n return (\n this.jobExecutions.length > 0 && (this.mode === 'listening' || this.jobExecutions.length > 1)\n );\n }\n}\n\nexport function isTerminalJobStatus(status: JobStatus): boolean {\n return TERMINAL_JOB_STATUS_SET.has(status);\n}\n\nexport function deriveJobDisplayStatus(\n job: Pick<Job, 'mode' | 'status' | 'listenerStatus' | 'jobExecutions'> & {\n /** List previews carry the selected execution state without its step tree. */\n executionStatus?: JobExecutionStatus | null | undefined;\n },\n): JobDisplayStatus {\n if (isTerminalJobStatus(job.status)) return job.status;\n if (job.mode === 'listening' && job.listenerStatus === 'listening') return 'listening';\n const execution =\n job.executionStatus === undefined\n ? defaultJobExecution(job)\n : job.executionStatus === null\n ? undefined\n : {status: job.executionStatus, steps: []};\n // A running execution is the shared display rule for both list previews and detail jobs.\n // The list has no step tree, while the detail path can still use the execution's steps for\n // other non-running states.\n if (execution?.status === 'running') return 'running';\n return execution ? deriveJobExecutionDisplayStatus(execution) : 'pending';\n}\n\nexport function resolveJobExecution(\n job: Job,\n jobExecutionId: string | undefined,\n): JobExecution | undefined {\n const selectedExecution = jobExecutionId\n ? job.jobExecutions.find((jobExecution) => jobExecution.id === jobExecutionId)\n : undefined;\n if (selectedExecution) return selectedExecution;\n\n return defaultJobExecution(job);\n}\n\nexport function defaultJobExecution(job: Pick<Job, 'jobExecutions'>): JobExecution | undefined {\n const runningExecution = job.jobExecutions.find(\n (jobExecution) => jobExecution.status === 'running',\n );\n if (runningExecution) return runningExecution;\n\n return job.jobExecutions.reduce<JobExecution | undefined>((latest, jobExecution) => {\n if (!latest) return jobExecution;\n return jobExecution.sequence > latest.sequence ? jobExecution : latest;\n }, undefined);\n}\n"],"names":["deriveJobExecutionDisplayStatus","WORKFLOW_JOB_STATUSES","TERMINAL_WORKFLOW_JOB_STATUSES","TERMINAL_JOB_STATUS_SET","Set","Job","fields","Object","assign","displayName","name","key","displayDuration","mode","jobExecutions","length","executionCountVisible","isTerminalJobStatus","status","has","deriveJobDisplayStatus","job","listenerStatus","execution","executionStatus","undefined","defaultJobExecution","steps","resolveJobExecution","jobExecutionId","selectedExecution","find","jobExecution","id","runningExecution","reduce","latest","sequence"],"mappings":"AAAA,SACEA,+BAA+B,QAI1B,qBAAqB;AAiD5B,OAAO,MAAMC,wBAAwB;IACnC;IACA;IACA;IACA;IACA;IACA;CACD,CAAyC;AAE1C,OAAO,MAAMC,iCAAiC;IAC5C;IACA;IACA;IACA;CACD,CAAyC;AAE1C,MAAMC,0BAA0B,IAAIC,IAAeF;AAyBnD,OAAO,MAAMG;IAsBX,YAAYC,MAAiB,CAAE;QAC7BC,OAAOC,MAAM,CAAC,IAAI,EAAEF;IACtB;IAEA,IAAIG,cAAsB;QACxB,OAAO,IAAI,CAACC,IAAI,IAAI,IAAI,CAACC,GAAG;IAC9B;IAEA,IAAIC,kBAA6C;QAC/C,IAAI,IAAI,CAACC,IAAI,KAAK,aAAa,OAAO;QACtC,IAAI,IAAI,CAACC,aAAa,CAACC,MAAM,KAAK,GAAG,OAAO;QAC5C,OAAO,IAAI,CAACD,aAAa,CAAC,EAAE,EAAEF,mBAAmB;IACnD;IAEA,IAAII,wBAAiC;QACnC,OACE,IAAI,CAACF,aAAa,CAACC,MAAM,GAAG,KAAM,CAAA,IAAI,CAACF,IAAI,KAAK,eAAe,IAAI,CAACC,aAAa,CAACC,MAAM,GAAG,CAAA;IAE/F;AACF;AAEA,OAAO,SAASE,oBAAoBC,MAAiB;IACnD,OAAOf,wBAAwBgB,GAAG,CAACD;AACrC;AAEA,OAAO,SAASE,uBACdC,GAGC;IAED,IAAIJ,oBAAoBI,IAAIH,MAAM,GAAG,OAAOG,IAAIH,MAAM;IACtD,IAAIG,IAAIR,IAAI,KAAK,eAAeQ,IAAIC,cAAc,KAAK,aAAa,OAAO;IAC3E,MAAMC,YACJF,IAAIG,eAAe,KAAKC,YACpBC,oBAAoBL,OACpBA,IAAIG,eAAe,KAAK,OACtBC,YACA;QAACP,QAAQG,IAAIG,eAAe;QAAEG,OAAO,EAAE;IAAA;IAC/C,yFAAyF;IACzF,2FAA2F;IAC3F,4BAA4B;IAC5B,IAAIJ,WAAWL,WAAW,WAAW,OAAO;IAC5C,OAAOK,YAAYvB,gCAAgCuB,aAAa;AAClE;AAEA,OAAO,SAASK,oBACdP,GAAQ,EACRQ,cAAkC;IAElC,MAAMC,oBAAoBD,iBACtBR,IAAIP,aAAa,CAACiB,IAAI,CAAC,CAACC,eAAiBA,aAAaC,EAAE,KAAKJ,kBAC7DJ;IACJ,IAAIK,mBAAmB,OAAOA;IAE9B,OAAOJ,oBAAoBL;AAC7B;AAEA,OAAO,SAASK,oBAAoBL,GAA+B;IACjE,MAAMa,mBAAmBb,IAAIP,aAAa,CAACiB,IAAI,CAC7C,CAACC,eAAiBA,aAAad,MAAM,KAAK;IAE5C,IAAIgB,kBAAkB,OAAOA;IAE7B,OAAOb,IAAIP,aAAa,CAACqB,MAAM,CAA2B,CAACC,QAAQJ;QACjE,IAAI,CAACI,QAAQ,OAAOJ;QACpB,OAAOA,aAAaK,QAAQ,GAAGD,OAAOC,QAAQ,GAAGL,eAAeI;IAClE,GAAGX;AACL"}
@@ -1 +1 @@
1
- {"version":3,"file":"project-workflows-page.d.ts","sourceRoot":"","sources":["../../src/pages/project-workflows-page.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,oBAAoB,CAAC,EAAC,SAAS,EAAC,EAAE;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,+BAMpE"}
1
+ {"version":3,"file":"project-workflows-page.d.ts","sourceRoot":"","sources":["../../src/pages/project-workflows-page.tsx"],"names":[],"mappings":"AAuCA,wBAAgB,oBAAoB,CAAC,EAAC,SAAS,EAAC,EAAE;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,+BAMpE"}
@@ -87,7 +87,7 @@ function ProjectWorkflowsPageInner({ projectId }) {
87
87
  /*#__PURE__*/ _jsx(WorkflowSyncAlert, {
88
88
  sync: sync
89
89
  }),
90
- /*#__PURE__*/ _jsx(WorkflowSyncWarnings, {
90
+ /*#__PURE__*/ _jsx(WorkflowSyncDiagnostics, {
91
91
  sync: sync
92
92
  }),
93
93
  /*#__PURE__*/ _jsx(WorkflowDefinitionsList, {
@@ -395,48 +395,98 @@ function WorkflowSyncAlert({ sync }) {
395
395
  })
396
396
  });
397
397
  }
398
- function WorkflowSyncWarnings({ sync }) {
399
- if (sync?.status !== 'succeeded' || sync.warnings.length === 0) return null;
400
- const seenKeys = new Map();
401
- const warningItems = sync.warnings.map((warning)=>{
402
- const baseKey = `${warning.code}-${warning.path ?? 'workflow'}-${warning.message}`;
403
- const occurrence = seenKeys.get(baseKey) ?? 0;
404
- seenKeys.set(baseKey, occurrence + 1);
405
- return {
406
- key: `${baseKey}-${occurrence}`,
407
- warning
408
- };
409
- });
398
+ function WorkflowSyncDiagnostics({ sync }) {
399
+ if (sync?.status !== 'succeeded' || sync.diagnostics.length === 0) return null;
400
+ const hasErrors = sync.diagnostics.some((diagnostic)=>diagnostic.severity === 'error');
401
+ const hasWarnings = sync.diagnostics.some((diagnostic)=>diagnostic.severity === 'warning');
402
+ const groups = groupDiagnosticsByFilePath(sync.diagnostics);
403
+ const title = hasErrors && hasWarnings ? 'Workflow definition diagnostics' : hasErrors ? 'Workflow definition errors' : 'Workflow definition warnings';
410
404
  return /*#__PURE__*/ _jsx(Callout, {
411
405
  role: "status",
412
- type: "warning",
406
+ type: hasErrors ? 'error' : 'warning',
413
407
  children: /*#__PURE__*/ _jsxs("div", {
414
408
  className: "flex flex-col gap-inline",
415
409
  children: [
416
410
  /*#__PURE__*/ _jsx(Text, {
417
411
  size: "sm",
418
412
  bold: true,
419
- children: "Workflow definition warnings"
413
+ children: title
420
414
  }),
421
415
  /*#__PURE__*/ _jsx("ul", {
422
416
  className: "flex flex-col gap-tight",
423
- children: warningItems.map(({ key, warning })=>/*#__PURE__*/ _jsxs("li", {
417
+ children: groups.map((group)=>/*#__PURE__*/ _jsxs("li", {
418
+ className: "flex flex-col gap-tight",
424
419
  children: [
425
- /*#__PURE__*/ _jsx(Text, {
426
- size: "sm",
427
- children: warning.message
428
- }),
429
- warning.path ? /*#__PURE__*/ _jsx(Code, {
420
+ group.filePath ? /*#__PURE__*/ _jsx(Code, {
430
421
  className: "text-foreground-neutral-muted",
431
- children: warning.path
432
- }) : null
422
+ children: group.filePath
423
+ }) : null,
424
+ /*#__PURE__*/ _jsx("ul", {
425
+ className: "flex flex-col gap-tight",
426
+ children: group.items.map(({ key, diagnostic })=>{
427
+ const severityLabel = diagnostic.severity === 'error' ? 'Error' : 'Warning';
428
+ return /*#__PURE__*/ _jsxs("li", {
429
+ children: [
430
+ diagnostic.path ? /*#__PURE__*/ _jsx(Code, {
431
+ className: "text-foreground-neutral-muted",
432
+ children: diagnostic.path
433
+ }) : null,
434
+ /*#__PURE__*/ _jsxs(Text, {
435
+ size: "sm",
436
+ children: [
437
+ /*#__PURE__*/ _jsxs("span", {
438
+ className: "font-medium",
439
+ children: [
440
+ severityLabel,
441
+ ":"
442
+ ]
443
+ }),
444
+ ' ',
445
+ /*#__PURE__*/ _jsx("span", {
446
+ className: diagnostic.severity === 'error' ? 'text-tag-error-text' : undefined,
447
+ children: diagnostic.message
448
+ })
449
+ ]
450
+ })
451
+ ]
452
+ }, key);
453
+ })
454
+ })
433
455
  ]
434
- }, key))
456
+ }, group.key))
435
457
  })
436
458
  ]
437
459
  })
438
460
  });
439
461
  }
462
+ function groupDiagnosticsByFilePath(diagnostics) {
463
+ const groups = [];
464
+ const indexByFilePath = new Map();
465
+ for (const diagnostic of diagnostics){
466
+ const filePathKey = diagnostic.filePath ?? '';
467
+ const groupIndex = indexByFilePath.get(filePathKey);
468
+ if (groupIndex === undefined) {
469
+ indexByFilePath.set(filePathKey, groups.length);
470
+ groups.push({
471
+ key: `${filePathKey}-${groups.length}`,
472
+ filePath: diagnostic.filePath,
473
+ items: [
474
+ {
475
+ key: `${filePathKey}-0`,
476
+ diagnostic
477
+ }
478
+ ]
479
+ });
480
+ } else {
481
+ const group = groups[groupIndex];
482
+ if (group) group.items.push({
483
+ key: `${filePathKey}-${group.items.length}`,
484
+ diagnostic
485
+ });
486
+ }
487
+ }
488
+ return groups;
489
+ }
440
490
  function DefinitionSheet({ definition, onOpenChange }) {
441
491
  const normalizedJson = definition ? JSON.stringify({
442
492
  workflow_document: definition.workflowDocument,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/project-workflows-page.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {\n type Definition,\n type DefinitionSyncSummary,\n SourceStrip,\n useDefinitionsInfiniteQuery,\n useProjectQuery,\n} from '@shipfox/client-projects';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {LoadErrorState} from '@shipfox/react-ui/load-error-state';\nimport {Panel} from '@shipfox/react-ui/panel';\nimport {RelativeTime, RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Sheet,\n SheetBody,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from '@shipfox/react-ui/sheet';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@shipfox/react-ui/table';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\nimport {useFireManualWorkflowMutation} from '#hooks/api/workflow-runs.js';\n\nexport function ProjectWorkflowsPage({projectId}: {projectId: string}) {\n return (\n <RelativeTimeProvider>\n <ProjectWorkflowsPageInner projectId={projectId} />\n </RelativeTimeProvider>\n );\n}\n\nfunction ProjectWorkflowsPageInner({projectId}: {projectId: string}) {\n const projectQuery = useProjectQuery(projectId);\n const definitionsQuery = useDefinitionsInfiniteQuery(projectId);\n const fireManual = useFireManualWorkflowMutation();\n const [selectedDefinition, setSelectedDefinition] = useState<Definition | null>(null);\n const [runError, setRunError] = useState<{definitionId: string; message: string} | null>(null);\n const definitions = definitionsQuery.data?.pages.flatMap((page) => page.definitions) ?? [];\n const sync = definitionsQuery.data?.pages[0]?.sync;\n\n async function handleRun(definition: Definition) {\n setRunError(null);\n if (!definition.manualTrigger) return;\n try {\n await fireManual.mutateAsync({projectId, definitionId: definition.id});\n toast.success('Run queued');\n } catch (error) {\n const message = errorMessage(error, 'Could not queue run.');\n setRunError({definitionId: definition.id, message});\n toast.error(message);\n }\n }\n\n return (\n <div className=\"flex w-full flex-col gap-section\">\n <Header variant=\"h1\" className=\"sr-only\">\n Workflows\n </Header>\n\n {projectQuery.isPending ? (\n <div className=\"flex flex-col gap-cluster\">\n <Skeleton className=\"h-28 w-1/3\" />\n <Skeleton className=\"h-18 w-1/2\" />\n </div>\n ) : null}\n\n {projectQuery.isError && projectQuery.data === undefined ? (\n projectQuery.error instanceof ApiError && projectQuery.error.status === 404 ? (\n <EmptyState\n icon=\"errorWarningLine\"\n title=\"Project not found\"\n description=\"This project doesn't exist, or you don't have access to it.\"\n />\n ) : (\n <QueryLoadError query={projectQuery} subject=\"project\" />\n )\n ) : null}\n\n {projectQuery.data ? (\n <>\n <SourceStrip\n connectionId={projectQuery.data.source.connectionId}\n externalRepositoryId={projectQuery.data.source.externalRepositoryId}\n sync={sync}\n isPending={definitionsQuery.isPending}\n />\n\n <WorkflowSyncAlert sync={sync} />\n <WorkflowSyncWarnings sync={sync} />\n\n <WorkflowDefinitionsList\n definitions={definitions}\n isPending={definitionsQuery.isPending}\n isError={definitionsQuery.isError}\n sync={sync ?? null}\n runError={runError}\n runningDefinitionId={\n fireManual.isPending && fireManual.variables\n ? fireManual.variables.definitionId\n : null\n }\n hasNextPage={definitionsQuery.hasNextPage}\n isFetchingNextPage={definitionsQuery.isFetchingNextPage}\n isFetchNextPageError={definitionsQuery.isFetchNextPageError}\n onRetry={() => definitionsQuery.refetch()}\n onLoadMore={() => definitionsQuery.fetchNextPage()}\n onOpenDefinition={setSelectedDefinition}\n onRun={(definition) => {\n void handleRun(definition);\n }}\n />\n </>\n ) : null}\n\n <DefinitionSheet\n definition={selectedDefinition}\n onOpenChange={(open) => {\n if (!open) setSelectedDefinition(null);\n }}\n />\n </div>\n );\n}\n\nfunction WorkflowDefinitionsList({\n definitions,\n isPending,\n isError,\n sync,\n runError,\n runningDefinitionId,\n hasNextPage,\n isFetchingNextPage,\n isFetchNextPageError,\n onRetry,\n onLoadMore,\n onOpenDefinition,\n onRun,\n}: {\n definitions: Definition[];\n isPending: boolean;\n isError: boolean;\n sync: DefinitionSyncSummary | null;\n runError: {definitionId: string; message: string} | null;\n runningDefinitionId: string | null;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n isFetchNextPageError: boolean;\n onRetry: () => void;\n onLoadMore: () => void;\n onOpenDefinition: (definition: Definition) => void;\n onRun: (definition: Definition) => void;\n}) {\n if (isPending) {\n return (\n <Panel role=\"status\" aria-label=\"Loading workflows\" className=\"divide-y\">\n <Skeleton className=\"h-44 w-full rounded-none\" />\n <Skeleton className=\"h-44 w-full rounded-none\" />\n <Skeleton className=\"h-44 w-full rounded-none\" />\n </Panel>\n );\n }\n\n if (isError && definitions.length === 0) {\n return (\n <Panel role=\"region\" aria-label=\"Workflow definitions\">\n <LoadErrorState\n title=\"Couldn't load workflows\"\n description=\"Definitions could not be loaded. Source metadata remains visible.\"\n onRetry={onRetry}\n retryLabel=\"Retry loading workflows\"\n variant=\"panel\"\n />\n </Panel>\n );\n }\n\n if (definitions.length === 0) {\n return (\n <Panel role=\"region\" aria-label=\"Workflow definitions\">\n <WorkflowEmptyState sync={sync} />\n </Panel>\n );\n }\n\n return (\n <>\n <Panel role=\"region\" aria-label=\"Workflow definitions\">\n <div className=\"hidden md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-40\"></TableHead>\n <TableHead>Workflow</TableHead>\n <TableHead className=\"w-180\">Updated</TableHead>\n <TableHead className=\"w-80 text-right\"></TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {definitions.map((definition) => {\n const runErrorMessage =\n runError?.definitionId === definition.id ? runError.message : null;\n const isRunning = runningDefinitionId === definition.id;\n\n return (\n // The workflow-name cell holds a `<button>` so the row is\n // keyboard-reachable (Tab focuses, Enter/Space activates\n // via native button semantics). The TableRow itself is no\n // longer clickable: a row-level onClick would be invisible\n // to keyboard users and require custom keydown handling.\n // The `group` class on the row still drives the Run button\n // reveal on hover or focus-within.\n <TableRow key={definition.id} className=\"group\">\n <TableCell>\n <Icon\n name={sourceIcon(definition.source)}\n className=\"size-16 text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n </TableCell>\n <TableCell className=\"max-w-260\">\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <button\n type=\"button\"\n onClick={() => onOpenDefinition(definition)}\n className=\"flex min-w-0 flex-col gap-tight rounded-4 text-left outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Text size=\"sm\" bold className=\"truncate\">\n {definition.name}\n </Text>\n <Code className=\"truncate text-foreground-neutral-muted\">\n {definition.configPath ?? 'Manual definition'}\n </Code>\n </button>\n {runErrorMessage ? (\n <Text size=\"xs\" className=\"text-tag-error-text\">\n {runErrorMessage}\n </Text>\n ) : null}\n </div>\n </TableCell>\n <TableCell className=\"text-foreground-neutral-muted\">\n <RelativeTime value={definition.updatedAt} />\n </TableCell>\n <TableCell>\n {definition.manualTrigger ? (\n <div className=\"flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100\">\n <Button size=\"xs\" isLoading={isRunning} onClick={() => onRun(definition)}>\n Run\n </Button>\n </div>\n ) : null}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n\n <div className=\"flex flex-col md:hidden\">\n {definitions.map((definition) => {\n const runErrorMessage =\n runError?.definitionId === definition.id ? runError.message : null;\n const isRunning = runningDefinitionId === definition.id;\n\n return (\n <div\n key={definition.id}\n className=\"flex flex-col gap-inline border-b border-border-neutral-base p-panel-compact last:border-b-0\"\n >\n <button\n type=\"button\"\n className=\"flex min-w-0 items-start gap-inline text-left\"\n onClick={() => onOpenDefinition(definition)}\n >\n <Icon\n name={sourceIcon(definition.source)}\n className=\"size-16 shrink-0 text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"sm\" bold className=\"break-words\">\n {definition.name}\n </Text>\n <Code className=\"break-words text-foreground-neutral-muted\">\n {definition.configPath ?? 'Manual definition'}\n </Code>\n </div>\n </button>\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n Updated <RelativeTime value={definition.updatedAt} />\n </Text>\n {definition.manualTrigger ? (\n <Button size=\"sm\" isLoading={isRunning} onClick={() => onRun(definition)}>\n Run\n </Button>\n ) : null}\n </div>\n {runErrorMessage ? (\n <Text size=\"xs\" className=\"text-tag-error-text\">\n {runErrorMessage}\n </Text>\n ) : null}\n </div>\n );\n })}\n </div>\n </Panel>\n\n {isFetchNextPageError ? (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex items-center justify-between gap-cluster\">\n <Text size=\"sm\">Could not load more workflows.</Text>\n <Button size=\"sm\" variant=\"secondary\" onClick={onLoadMore}>\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n\n {hasNextPage ? (\n <div className=\"flex justify-center\">\n <Button size=\"sm\" variant=\"secondary\" isLoading={isFetchingNextPage} onClick={onLoadMore}>\n Load more\n </Button>\n </div>\n ) : null}\n </>\n );\n}\n\nfunction sourceIcon(source: 'manual' | 'vcs'): IconName {\n return source === 'vcs' ? ('gitBranchLine' as IconName) : ('terminalLine' as IconName);\n}\n\nfunction WorkflowEmptyState({sync}: {sync: DefinitionSyncSummary | null}) {\n const message =\n sync?.status === 'failed' && sync.lastErrorCode === 'no-workflow-files'\n ? 'No workflow files found under .shipfox/workflows/.'\n : sync?.status === 'failed'\n ? (sync.lastErrorMessage ?? 'Workflow definitions could not be synced.')\n : sync?.status === 'syncing'\n ? 'Workflow definitions are being discovered.'\n : sync?.status === 'succeeded'\n ? 'No workflow definitions found.'\n : 'Workflow sync has not reported yet.';\n\n return <EmptyState icon=\"flowChart\" title=\"No workflows\" description={message} variant=\"panel\" />;\n}\n\nfunction WorkflowSyncAlert({sync}: {sync: DefinitionSyncSummary | null | undefined}) {\n if (sync?.status !== 'failed') return null;\n\n return (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex flex-col gap-tight\">\n <Text size=\"sm\" bold>\n Workflow sync failed\n </Text>\n <Text size=\"sm\">\n {sync.lastErrorMessage ?? 'The latest workflow sync failed before definitions updated.'}\n </Text>\n </div>\n </Callout>\n );\n}\n\nfunction WorkflowSyncWarnings({sync}: {sync: DefinitionSyncSummary | null | undefined}) {\n if (sync?.status !== 'succeeded' || sync.warnings.length === 0) return null;\n\n const seenKeys = new Map<string, number>();\n const warningItems = sync.warnings.map((warning) => {\n const baseKey = `${warning.code}-${warning.path ?? 'workflow'}-${warning.message}`;\n const occurrence = seenKeys.get(baseKey) ?? 0;\n seenKeys.set(baseKey, occurrence + 1);\n return {key: `${baseKey}-${occurrence}`, warning};\n });\n\n return (\n <Callout role=\"status\" type=\"warning\">\n <div className=\"flex flex-col gap-inline\">\n <Text size=\"sm\" bold>\n Workflow definition warnings\n </Text>\n <ul className=\"flex flex-col gap-tight\">\n {warningItems.map(({key, warning}) => (\n <li key={key}>\n <Text size=\"sm\">{warning.message}</Text>\n {warning.path ? (\n <Code className=\"text-foreground-neutral-muted\">{warning.path}</Code>\n ) : null}\n </li>\n ))}\n </ul>\n </div>\n </Callout>\n );\n}\n\nfunction DefinitionSheet({\n definition,\n onOpenChange,\n}: {\n definition: Definition | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const normalizedJson = definition\n ? JSON.stringify(\n {\n workflow_document: definition.workflowDocument,\n workflow_model: definition.workflowModel,\n },\n null,\n 2,\n )\n : '';\n\n return (\n <Sheet open={Boolean(definition)} onOpenChange={onOpenChange}>\n <SheetContent className=\"w-full sm:max-w-[560px]\">\n {definition ? (\n <>\n <SheetHeader>\n <SheetTitle>{definition.name}</SheetTitle>\n <SheetDescription>\n {definition.configPath ?? 'Manual workflow definition'}\n </SheetDescription>\n </SheetHeader>\n <SheetBody className=\"gap-group\">\n <div className=\"grid w-full gap-inline\">\n <Metadata label=\"Definition id\" value={definition.id} />\n <Metadata label=\"Source\" value={definition.source} />\n <Metadata label=\"Ref\" value={definition.ref ?? 'Not set'} />\n <Metadata label=\"SHA\" value={definition.sha ?? 'Not set'} />\n </div>\n <div className=\"flex w-full flex-col gap-inline\">\n <Text size=\"sm\" bold>\n Normalized definition\n </Text>\n <pre className=\"max-h-[52vh] w-full overflow-auto rounded-8 border border-border-neutral-base bg-background-neutral-subtle p-panel-compact scrollbar\">\n <Code as=\"code\" className=\"whitespace-pre text-foreground-neutral-base\">\n {normalizedJson}\n </Code>\n </pre>\n </div>\n </SheetBody>\n </>\n ) : null}\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction Metadata({label, value}: {label: string; value: string}) {\n return (\n <div className=\"min-w-0 py-row first:pt-0 last:pb-0\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {label}\n </Text>\n <Text size=\"sm\" className=\"break-words\">\n {value}\n </Text>\n </div>\n );\n}\n\nfunction errorMessage(error: unknown, fallback: string) {\n if (error instanceof ApiError && error.message) return error.message;\n if (error instanceof Error && error.message) return error.message;\n return fallback;\n}\n"],"names":["ApiError","SourceStrip","useDefinitionsInfiniteQuery","useProjectQuery","QueryLoadError","Button","Callout","EmptyState","Icon","LoadErrorState","Panel","RelativeTime","RelativeTimeProvider","Sheet","SheetBody","SheetContent","SheetDescription","SheetHeader","SheetTitle","Skeleton","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useState","useFireManualWorkflowMutation","ProjectWorkflowsPage","projectId","ProjectWorkflowsPageInner","projectQuery","definitionsQuery","fireManual","selectedDefinition","setSelectedDefinition","runError","setRunError","definitions","data","pages","flatMap","page","sync","handleRun","definition","manualTrigger","mutateAsync","definitionId","id","success","error","message","errorMessage","div","className","variant","isPending","isError","undefined","status","icon","title","description","query","subject","connectionId","source","externalRepositoryId","WorkflowSyncAlert","WorkflowSyncWarnings","WorkflowDefinitionsList","runningDefinitionId","variables","hasNextPage","isFetchingNextPage","isFetchNextPageError","onRetry","refetch","onLoadMore","fetchNextPage","onOpenDefinition","onRun","DefinitionSheet","onOpenChange","open","role","aria-label","length","retryLabel","WorkflowEmptyState","map","runErrorMessage","isRunning","name","sourceIcon","aria-hidden","button","type","onClick","size","bold","configPath","value","updatedAt","isLoading","lastErrorCode","lastErrorMessage","warnings","seenKeys","Map","warningItems","warning","baseKey","code","path","occurrence","get","set","key","ul","li","normalizedJson","JSON","stringify","workflow_document","workflowDocument","workflow_model","workflowModel","Boolean","Metadata","label","ref","sha","pre","as","fallback","Error"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/pages/project-workflows-page.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {\n type Definition,\n type DefinitionSyncDiagnostic,\n type DefinitionSyncSummary,\n SourceStrip,\n useDefinitionsInfiniteQuery,\n useProjectQuery,\n} from '@shipfox/client-projects';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {LoadErrorState} from '@shipfox/react-ui/load-error-state';\nimport {Panel} from '@shipfox/react-ui/panel';\nimport {RelativeTime, RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Sheet,\n SheetBody,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from '@shipfox/react-ui/sheet';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@shipfox/react-ui/table';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\nimport {useFireManualWorkflowMutation} from '#hooks/api/workflow-runs.js';\n\nexport function ProjectWorkflowsPage({projectId}: {projectId: string}) {\n return (\n <RelativeTimeProvider>\n <ProjectWorkflowsPageInner projectId={projectId} />\n </RelativeTimeProvider>\n );\n}\n\nfunction ProjectWorkflowsPageInner({projectId}: {projectId: string}) {\n const projectQuery = useProjectQuery(projectId);\n const definitionsQuery = useDefinitionsInfiniteQuery(projectId);\n const fireManual = useFireManualWorkflowMutation();\n const [selectedDefinition, setSelectedDefinition] = useState<Definition | null>(null);\n const [runError, setRunError] = useState<{definitionId: string; message: string} | null>(null);\n const definitions = definitionsQuery.data?.pages.flatMap((page) => page.definitions) ?? [];\n const sync = definitionsQuery.data?.pages[0]?.sync;\n\n async function handleRun(definition: Definition) {\n setRunError(null);\n if (!definition.manualTrigger) return;\n try {\n await fireManual.mutateAsync({projectId, definitionId: definition.id});\n toast.success('Run queued');\n } catch (error) {\n const message = errorMessage(error, 'Could not queue run.');\n setRunError({definitionId: definition.id, message});\n toast.error(message);\n }\n }\n\n return (\n <div className=\"flex w-full flex-col gap-section\">\n <Header variant=\"h1\" className=\"sr-only\">\n Workflows\n </Header>\n\n {projectQuery.isPending ? (\n <div className=\"flex flex-col gap-cluster\">\n <Skeleton className=\"h-28 w-1/3\" />\n <Skeleton className=\"h-18 w-1/2\" />\n </div>\n ) : null}\n\n {projectQuery.isError && projectQuery.data === undefined ? (\n projectQuery.error instanceof ApiError && projectQuery.error.status === 404 ? (\n <EmptyState\n icon=\"errorWarningLine\"\n title=\"Project not found\"\n description=\"This project doesn't exist, or you don't have access to it.\"\n />\n ) : (\n <QueryLoadError query={projectQuery} subject=\"project\" />\n )\n ) : null}\n\n {projectQuery.data ? (\n <>\n <SourceStrip\n connectionId={projectQuery.data.source.connectionId}\n externalRepositoryId={projectQuery.data.source.externalRepositoryId}\n sync={sync}\n isPending={definitionsQuery.isPending}\n />\n\n <WorkflowSyncAlert sync={sync} />\n <WorkflowSyncDiagnostics sync={sync} />\n\n <WorkflowDefinitionsList\n definitions={definitions}\n isPending={definitionsQuery.isPending}\n isError={definitionsQuery.isError}\n sync={sync ?? null}\n runError={runError}\n runningDefinitionId={\n fireManual.isPending && fireManual.variables\n ? fireManual.variables.definitionId\n : null\n }\n hasNextPage={definitionsQuery.hasNextPage}\n isFetchingNextPage={definitionsQuery.isFetchingNextPage}\n isFetchNextPageError={definitionsQuery.isFetchNextPageError}\n onRetry={() => definitionsQuery.refetch()}\n onLoadMore={() => definitionsQuery.fetchNextPage()}\n onOpenDefinition={setSelectedDefinition}\n onRun={(definition) => {\n void handleRun(definition);\n }}\n />\n </>\n ) : null}\n\n <DefinitionSheet\n definition={selectedDefinition}\n onOpenChange={(open) => {\n if (!open) setSelectedDefinition(null);\n }}\n />\n </div>\n );\n}\n\nfunction WorkflowDefinitionsList({\n definitions,\n isPending,\n isError,\n sync,\n runError,\n runningDefinitionId,\n hasNextPage,\n isFetchingNextPage,\n isFetchNextPageError,\n onRetry,\n onLoadMore,\n onOpenDefinition,\n onRun,\n}: {\n definitions: Definition[];\n isPending: boolean;\n isError: boolean;\n sync: DefinitionSyncSummary | null;\n runError: {definitionId: string; message: string} | null;\n runningDefinitionId: string | null;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n isFetchNextPageError: boolean;\n onRetry: () => void;\n onLoadMore: () => void;\n onOpenDefinition: (definition: Definition) => void;\n onRun: (definition: Definition) => void;\n}) {\n if (isPending) {\n return (\n <Panel role=\"status\" aria-label=\"Loading workflows\" className=\"divide-y\">\n <Skeleton className=\"h-44 w-full rounded-none\" />\n <Skeleton className=\"h-44 w-full rounded-none\" />\n <Skeleton className=\"h-44 w-full rounded-none\" />\n </Panel>\n );\n }\n\n if (isError && definitions.length === 0) {\n return (\n <Panel role=\"region\" aria-label=\"Workflow definitions\">\n <LoadErrorState\n title=\"Couldn't load workflows\"\n description=\"Definitions could not be loaded. Source metadata remains visible.\"\n onRetry={onRetry}\n retryLabel=\"Retry loading workflows\"\n variant=\"panel\"\n />\n </Panel>\n );\n }\n\n if (definitions.length === 0) {\n return (\n <Panel role=\"region\" aria-label=\"Workflow definitions\">\n <WorkflowEmptyState sync={sync} />\n </Panel>\n );\n }\n\n return (\n <>\n <Panel role=\"region\" aria-label=\"Workflow definitions\">\n <div className=\"hidden md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-40\"></TableHead>\n <TableHead>Workflow</TableHead>\n <TableHead className=\"w-180\">Updated</TableHead>\n <TableHead className=\"w-80 text-right\"></TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {definitions.map((definition) => {\n const runErrorMessage =\n runError?.definitionId === definition.id ? runError.message : null;\n const isRunning = runningDefinitionId === definition.id;\n\n return (\n // The workflow-name cell holds a `<button>` so the row is\n // keyboard-reachable (Tab focuses, Enter/Space activates\n // via native button semantics). The TableRow itself is no\n // longer clickable: a row-level onClick would be invisible\n // to keyboard users and require custom keydown handling.\n // The `group` class on the row still drives the Run button\n // reveal on hover or focus-within.\n <TableRow key={definition.id} className=\"group\">\n <TableCell>\n <Icon\n name={sourceIcon(definition.source)}\n className=\"size-16 text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n </TableCell>\n <TableCell className=\"max-w-260\">\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <button\n type=\"button\"\n onClick={() => onOpenDefinition(definition)}\n className=\"flex min-w-0 flex-col gap-tight rounded-4 text-left outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Text size=\"sm\" bold className=\"truncate\">\n {definition.name}\n </Text>\n <Code className=\"truncate text-foreground-neutral-muted\">\n {definition.configPath ?? 'Manual definition'}\n </Code>\n </button>\n {runErrorMessage ? (\n <Text size=\"xs\" className=\"text-tag-error-text\">\n {runErrorMessage}\n </Text>\n ) : null}\n </div>\n </TableCell>\n <TableCell className=\"text-foreground-neutral-muted\">\n <RelativeTime value={definition.updatedAt} />\n </TableCell>\n <TableCell>\n {definition.manualTrigger ? (\n <div className=\"flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100\">\n <Button size=\"xs\" isLoading={isRunning} onClick={() => onRun(definition)}>\n Run\n </Button>\n </div>\n ) : null}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n\n <div className=\"flex flex-col md:hidden\">\n {definitions.map((definition) => {\n const runErrorMessage =\n runError?.definitionId === definition.id ? runError.message : null;\n const isRunning = runningDefinitionId === definition.id;\n\n return (\n <div\n key={definition.id}\n className=\"flex flex-col gap-inline border-b border-border-neutral-base p-panel-compact last:border-b-0\"\n >\n <button\n type=\"button\"\n className=\"flex min-w-0 items-start gap-inline text-left\"\n onClick={() => onOpenDefinition(definition)}\n >\n <Icon\n name={sourceIcon(definition.source)}\n className=\"size-16 shrink-0 text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"sm\" bold className=\"break-words\">\n {definition.name}\n </Text>\n <Code className=\"break-words text-foreground-neutral-muted\">\n {definition.configPath ?? 'Manual definition'}\n </Code>\n </div>\n </button>\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n Updated <RelativeTime value={definition.updatedAt} />\n </Text>\n {definition.manualTrigger ? (\n <Button size=\"sm\" isLoading={isRunning} onClick={() => onRun(definition)}>\n Run\n </Button>\n ) : null}\n </div>\n {runErrorMessage ? (\n <Text size=\"xs\" className=\"text-tag-error-text\">\n {runErrorMessage}\n </Text>\n ) : null}\n </div>\n );\n })}\n </div>\n </Panel>\n\n {isFetchNextPageError ? (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex items-center justify-between gap-cluster\">\n <Text size=\"sm\">Could not load more workflows.</Text>\n <Button size=\"sm\" variant=\"secondary\" onClick={onLoadMore}>\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n\n {hasNextPage ? (\n <div className=\"flex justify-center\">\n <Button size=\"sm\" variant=\"secondary\" isLoading={isFetchingNextPage} onClick={onLoadMore}>\n Load more\n </Button>\n </div>\n ) : null}\n </>\n );\n}\n\nfunction sourceIcon(source: 'manual' | 'vcs'): IconName {\n return source === 'vcs' ? ('gitBranchLine' as IconName) : ('terminalLine' as IconName);\n}\n\nfunction WorkflowEmptyState({sync}: {sync: DefinitionSyncSummary | null}) {\n const message =\n sync?.status === 'failed' && sync.lastErrorCode === 'no-workflow-files'\n ? 'No workflow files found under .shipfox/workflows/.'\n : sync?.status === 'failed'\n ? (sync.lastErrorMessage ?? 'Workflow definitions could not be synced.')\n : sync?.status === 'syncing'\n ? 'Workflow definitions are being discovered.'\n : sync?.status === 'succeeded'\n ? 'No workflow definitions found.'\n : 'Workflow sync has not reported yet.';\n\n return <EmptyState icon=\"flowChart\" title=\"No workflows\" description={message} variant=\"panel\" />;\n}\n\nfunction WorkflowSyncAlert({sync}: {sync: DefinitionSyncSummary | null | undefined}) {\n if (sync?.status !== 'failed') return null;\n\n return (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex flex-col gap-tight\">\n <Text size=\"sm\" bold>\n Workflow sync failed\n </Text>\n <Text size=\"sm\">\n {sync.lastErrorMessage ?? 'The latest workflow sync failed before definitions updated.'}\n </Text>\n </div>\n </Callout>\n );\n}\n\nfunction WorkflowSyncDiagnostics({sync}: {sync: DefinitionSyncSummary | null | undefined}) {\n if (sync?.status !== 'succeeded' || sync.diagnostics.length === 0) return null;\n\n const hasErrors = sync.diagnostics.some((diagnostic) => diagnostic.severity === 'error');\n const hasWarnings = sync.diagnostics.some((diagnostic) => diagnostic.severity === 'warning');\n const groups = groupDiagnosticsByFilePath(sync.diagnostics);\n const title =\n hasErrors && hasWarnings\n ? 'Workflow definition diagnostics'\n : hasErrors\n ? 'Workflow definition errors'\n : 'Workflow definition warnings';\n\n return (\n <Callout role=\"status\" type={hasErrors ? 'error' : 'warning'}>\n <div className=\"flex flex-col gap-inline\">\n <Text size=\"sm\" bold>\n {title}\n </Text>\n <ul className=\"flex flex-col gap-tight\">\n {groups.map((group) => (\n <li key={group.key} className=\"flex flex-col gap-tight\">\n {group.filePath ? (\n <Code className=\"text-foreground-neutral-muted\">{group.filePath}</Code>\n ) : null}\n <ul className=\"flex flex-col gap-tight\">\n {group.items.map(({key, diagnostic}) => {\n const severityLabel = diagnostic.severity === 'error' ? 'Error' : 'Warning';\n\n return (\n <li key={key}>\n {diagnostic.path ? (\n <Code className=\"text-foreground-neutral-muted\">{diagnostic.path}</Code>\n ) : null}\n <Text size=\"sm\">\n <span className=\"font-medium\">{severityLabel}:</span>{' '}\n <span\n className={\n diagnostic.severity === 'error' ? 'text-tag-error-text' : undefined\n }\n >\n {diagnostic.message}\n </span>\n </Text>\n </li>\n );\n })}\n </ul>\n </li>\n ))}\n </ul>\n </div>\n </Callout>\n );\n}\n\ninterface DiagnosticGroup {\n key: string;\n filePath: string | undefined;\n items: {key: string; diagnostic: DefinitionSyncDiagnostic}[];\n}\n\nfunction groupDiagnosticsByFilePath(\n diagnostics: readonly DefinitionSyncDiagnostic[],\n): DiagnosticGroup[] {\n const groups: DiagnosticGroup[] = [];\n const indexByFilePath = new Map<string, number>();\n for (const diagnostic of diagnostics) {\n const filePathKey = diagnostic.filePath ?? '';\n const groupIndex = indexByFilePath.get(filePathKey);\n if (groupIndex === undefined) {\n indexByFilePath.set(filePathKey, groups.length);\n groups.push({\n key: `${filePathKey}-${groups.length}`,\n filePath: diagnostic.filePath,\n items: [{key: `${filePathKey}-0`, diagnostic}],\n });\n } else {\n const group = groups[groupIndex];\n if (group) group.items.push({key: `${filePathKey}-${group.items.length}`, diagnostic});\n }\n }\n return groups;\n}\n\nfunction DefinitionSheet({\n definition,\n onOpenChange,\n}: {\n definition: Definition | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const normalizedJson = definition\n ? JSON.stringify(\n {\n workflow_document: definition.workflowDocument,\n workflow_model: definition.workflowModel,\n },\n null,\n 2,\n )\n : '';\n\n return (\n <Sheet open={Boolean(definition)} onOpenChange={onOpenChange}>\n <SheetContent className=\"w-full sm:max-w-[560px]\">\n {definition ? (\n <>\n <SheetHeader>\n <SheetTitle>{definition.name}</SheetTitle>\n <SheetDescription>\n {definition.configPath ?? 'Manual workflow definition'}\n </SheetDescription>\n </SheetHeader>\n <SheetBody className=\"gap-group\">\n <div className=\"grid w-full gap-inline\">\n <Metadata label=\"Definition id\" value={definition.id} />\n <Metadata label=\"Source\" value={definition.source} />\n <Metadata label=\"Ref\" value={definition.ref ?? 'Not set'} />\n <Metadata label=\"SHA\" value={definition.sha ?? 'Not set'} />\n </div>\n <div className=\"flex w-full flex-col gap-inline\">\n <Text size=\"sm\" bold>\n Normalized definition\n </Text>\n <pre className=\"max-h-[52vh] w-full overflow-auto rounded-8 border border-border-neutral-base bg-background-neutral-subtle p-panel-compact scrollbar\">\n <Code as=\"code\" className=\"whitespace-pre text-foreground-neutral-base\">\n {normalizedJson}\n </Code>\n </pre>\n </div>\n </SheetBody>\n </>\n ) : null}\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction Metadata({label, value}: {label: string; value: string}) {\n return (\n <div className=\"min-w-0 py-row first:pt-0 last:pb-0\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {label}\n </Text>\n <Text size=\"sm\" className=\"break-words\">\n {value}\n </Text>\n </div>\n );\n}\n\nfunction errorMessage(error: unknown, fallback: string) {\n if (error instanceof ApiError && error.message) return error.message;\n if (error instanceof Error && error.message) return error.message;\n return fallback;\n}\n"],"names":["ApiError","SourceStrip","useDefinitionsInfiniteQuery","useProjectQuery","QueryLoadError","Button","Callout","EmptyState","Icon","LoadErrorState","Panel","RelativeTime","RelativeTimeProvider","Sheet","SheetBody","SheetContent","SheetDescription","SheetHeader","SheetTitle","Skeleton","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useState","useFireManualWorkflowMutation","ProjectWorkflowsPage","projectId","ProjectWorkflowsPageInner","projectQuery","definitionsQuery","fireManual","selectedDefinition","setSelectedDefinition","runError","setRunError","definitions","data","pages","flatMap","page","sync","handleRun","definition","manualTrigger","mutateAsync","definitionId","id","success","error","message","errorMessage","div","className","variant","isPending","isError","undefined","status","icon","title","description","query","subject","connectionId","source","externalRepositoryId","WorkflowSyncAlert","WorkflowSyncDiagnostics","WorkflowDefinitionsList","runningDefinitionId","variables","hasNextPage","isFetchingNextPage","isFetchNextPageError","onRetry","refetch","onLoadMore","fetchNextPage","onOpenDefinition","onRun","DefinitionSheet","onOpenChange","open","role","aria-label","length","retryLabel","WorkflowEmptyState","map","runErrorMessage","isRunning","name","sourceIcon","aria-hidden","button","type","onClick","size","bold","configPath","value","updatedAt","isLoading","lastErrorCode","lastErrorMessage","diagnostics","hasErrors","some","diagnostic","severity","hasWarnings","groups","groupDiagnosticsByFilePath","ul","group","li","filePath","items","key","severityLabel","path","span","indexByFilePath","Map","filePathKey","groupIndex","get","set","push","normalizedJson","JSON","stringify","workflow_document","workflowDocument","workflow_model","workflowModel","Boolean","Metadata","label","ref","sha","pre","as","fallback","Error"],"mappings":"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"}