331 lines
11 KiB
TypeScript
331 lines
11 KiB
TypeScript
import {
|
|
ArrowPathIcon,
|
|
BoltSlashIcon,
|
|
BugAntIcon,
|
|
CheckCircleIcon,
|
|
ClockIcon,
|
|
FireIcon,
|
|
NoSymbolIcon,
|
|
PauseCircleIcon,
|
|
RectangleStackIcon,
|
|
TrashIcon,
|
|
XCircleIcon,
|
|
} from "@heroicons/react/20/solid";
|
|
import type { TaskRunStatus } from "@trigger.dev/database";
|
|
import { runFriendlyStatus, type RunFriendlyStatus } from "@trigger.dev/core/v3";
|
|
import assertNever from "assert-never";
|
|
import { HourglassIcon } from "lucide-react";
|
|
import { TimedOutIcon } from "~/assets/icons/TimedOutIcon";
|
|
import { Callout } from "~/components/primitives/Callout";
|
|
import { Spinner } from "~/components/primitives/Spinner";
|
|
import { cn } from "~/utils/cn";
|
|
|
|
export const allTaskRunStatuses = [
|
|
"DELAYED",
|
|
"WAITING_FOR_DEPLOY",
|
|
"PENDING_VERSION",
|
|
"PENDING",
|
|
"DEQUEUED",
|
|
"EXECUTING",
|
|
"RETRYING_AFTER_FAILURE",
|
|
"WAITING_TO_RESUME",
|
|
"COMPLETED_SUCCESSFULLY",
|
|
"COMPLETED_WITH_ERRORS",
|
|
"CANCELED",
|
|
"TIMED_OUT",
|
|
"CRASHED",
|
|
"PAUSED",
|
|
"INTERRUPTED",
|
|
"SYSTEM_FAILURE",
|
|
"EXPIRED",
|
|
] as const satisfies Readonly<Array<TaskRunStatus>>;
|
|
|
|
export const filterableTaskRunStatuses = [
|
|
"PENDING_VERSION",
|
|
"DELAYED",
|
|
"PENDING",
|
|
"DEQUEUED",
|
|
"EXECUTING",
|
|
"WAITING_TO_RESUME",
|
|
"COMPLETED_SUCCESSFULLY",
|
|
"COMPLETED_WITH_ERRORS",
|
|
"TIMED_OUT",
|
|
"CRASHED",
|
|
"SYSTEM_FAILURE",
|
|
"CANCELED",
|
|
"EXPIRED",
|
|
] as const satisfies Readonly<Array<TaskRunStatus>>;
|
|
|
|
const taskRunStatusDescriptions: Record<TaskRunStatus, string> = {
|
|
DELAYED: "Task has been delayed and is waiting to be executed.",
|
|
PENDING: "Task is waiting to be executed.",
|
|
PENDING_VERSION: "Run cannot execute until a version includes the task and queue.",
|
|
WAITING_FOR_DEPLOY: "Run cannot execute until a version includes the task and queue.",
|
|
DEQUEUED: "Task has been dequeued from the queue but is not yet executing.",
|
|
EXECUTING: "Task is currently being executed.",
|
|
RETRYING_AFTER_FAILURE: "Task is being reattempted after a failure.",
|
|
WAITING_TO_RESUME: `You have used a "wait" function. When the wait is complete, the task will resume execution.`,
|
|
COMPLETED_SUCCESSFULLY: "Task has been successfully completed.",
|
|
CANCELED: "Task has been canceled.",
|
|
COMPLETED_WITH_ERRORS: "Task has failed with errors.",
|
|
INTERRUPTED: "Task has failed because it was interrupted.",
|
|
SYSTEM_FAILURE: "Task has failed due to a system failure.",
|
|
PAUSED: "Task has been paused by the user.",
|
|
CRASHED: "Task has crashed and won't be retried.",
|
|
EXPIRED: "Task has surpassed its ttl and won't be executed.",
|
|
TIMED_OUT: "Task has failed because it exceeded its maxDuration.",
|
|
};
|
|
|
|
export const QUEUED_STATUSES = [
|
|
"PENDING",
|
|
"PENDING_VERSION",
|
|
"WAITING_FOR_DEPLOY",
|
|
"DELAYED",
|
|
] satisfies TaskRunStatus[];
|
|
|
|
export const RUNNING_STATUSES = [
|
|
"DEQUEUED",
|
|
"EXECUTING",
|
|
"RETRYING_AFTER_FAILURE",
|
|
"WAITING_TO_RESUME",
|
|
] satisfies TaskRunStatus[];
|
|
|
|
export function descriptionForTaskRunStatus(status: TaskRunStatus): string {
|
|
return taskRunStatusDescriptions[status];
|
|
}
|
|
|
|
export function TaskRunStatusCombo({
|
|
status,
|
|
className,
|
|
iconClassName,
|
|
}: {
|
|
status: TaskRunStatus;
|
|
className?: string;
|
|
iconClassName?: string;
|
|
}) {
|
|
return (
|
|
<span className={cn("flex items-center gap-1", className)}>
|
|
<TaskRunStatusIcon status={status} className={cn("h-4 w-4", iconClassName)} />
|
|
<TaskRunStatusLabel status={status} />
|
|
</span>
|
|
);
|
|
}
|
|
|
|
const statusReasonsToDescription: Record<string, string> = {
|
|
NO_DEPLOYMENT: "No deployment or deployment image reference found for deployed run",
|
|
NO_WORKER: "No worker found for run",
|
|
TASK_NEVER_REGISTERED: "Task never registered",
|
|
QUEUE_NOT_FOUND: "Queue not found",
|
|
TASK_NOT_IN_LATEST: "Task not in latest version",
|
|
BACKGROUND_WORKER_MISMATCH: "Background worker mismatch",
|
|
};
|
|
|
|
export function TaskRunStatusReason({
|
|
status,
|
|
statusReason,
|
|
}: {
|
|
status: TaskRunStatus;
|
|
statusReason?: string;
|
|
}) {
|
|
if (status !== "PENDING_VERSION") {
|
|
return null;
|
|
}
|
|
|
|
if (!statusReason) {
|
|
return null;
|
|
}
|
|
|
|
const description = statusReasonsToDescription[statusReason];
|
|
|
|
if (!description) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<Callout to="https://trigger.dev/docs" variant="warning" className="text-sm">
|
|
{description}
|
|
</Callout>
|
|
);
|
|
}
|
|
|
|
export function TaskRunStatusLabel({ status }: { status: TaskRunStatus }) {
|
|
return <span className={runStatusClassNameColor(status)}>{runStatusTitle(status)}</span>;
|
|
}
|
|
|
|
export function TaskRunStatusIcon({
|
|
status,
|
|
className,
|
|
}: {
|
|
status: TaskRunStatus;
|
|
className: string;
|
|
}) {
|
|
switch (status) {
|
|
case "DELAYED":
|
|
return <ClockIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "PENDING":
|
|
return <RectangleStackIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "PENDING_VERSION":
|
|
case "WAITING_FOR_DEPLOY":
|
|
return <RectangleStackIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "DEQUEUED":
|
|
return <RectangleStackIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "EXECUTING":
|
|
return <Spinner className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "WAITING_TO_RESUME":
|
|
return <HourglassIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "RETRYING_AFTER_FAILURE":
|
|
return <ArrowPathIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "PAUSED":
|
|
return <PauseCircleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "CANCELED":
|
|
return <NoSymbolIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "INTERRUPTED":
|
|
return <BoltSlashIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "COMPLETED_SUCCESSFULLY":
|
|
return <CheckCircleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "COMPLETED_WITH_ERRORS":
|
|
return <XCircleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "SYSTEM_FAILURE":
|
|
return <BugAntIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "CRASHED":
|
|
return <FireIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "EXPIRED":
|
|
return <TrashIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
case "TIMED_OUT":
|
|
return <TimedOutIcon className={cn(runStatusClassNameColor(status), className)} />;
|
|
|
|
default: {
|
|
assertNever(status);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function runStatusClassNameColor(status: TaskRunStatus): string {
|
|
switch (status) {
|
|
case "PENDING":
|
|
case "DELAYED":
|
|
return "text-text-faint";
|
|
case "PENDING_VERSION":
|
|
case "WAITING_FOR_DEPLOY":
|
|
return "text-amber-500";
|
|
case "EXECUTING":
|
|
case "RETRYING_AFTER_FAILURE":
|
|
case "DEQUEUED":
|
|
return "text-pending";
|
|
case "WAITING_TO_RESUME":
|
|
return "text-text-faint";
|
|
case "PAUSED":
|
|
return "text-amber-300";
|
|
case "CANCELED":
|
|
case "EXPIRED":
|
|
return "text-text-faint";
|
|
case "INTERRUPTED":
|
|
return "text-error";
|
|
case "COMPLETED_SUCCESSFULLY":
|
|
return "text-success";
|
|
case "COMPLETED_WITH_ERRORS":
|
|
return "text-error";
|
|
case "SYSTEM_FAILURE":
|
|
return "text-error";
|
|
case "CRASHED":
|
|
return "text-error";
|
|
case "TIMED_OUT":
|
|
return "text-error";
|
|
default: {
|
|
assertNever(status);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function runStatusTitle(status: TaskRunStatus): RunFriendlyStatus {
|
|
return runStatusTitleFromStatus[status];
|
|
}
|
|
|
|
export function runStatusFromFriendlyTitle(friendly: RunFriendlyStatus): TaskRunStatus {
|
|
const result = titlesStatusesArray.find(([_, f]) => f === friendly);
|
|
if (!result) {
|
|
throw new Error(`Unknown friendly status: ${friendly}`);
|
|
}
|
|
return result[0] as TaskRunStatus;
|
|
}
|
|
|
|
// runFriendlyStatus and RunFriendlyStatus are imported from @trigger.dev/core/v3
|
|
// and re-exported here for backward compatibility.
|
|
export { runFriendlyStatus, type RunFriendlyStatus } from "@trigger.dev/core/v3";
|
|
|
|
/**
|
|
* Check if a value is a valid TaskRunStatus
|
|
*/
|
|
export function isTaskRunStatus(value: unknown): value is TaskRunStatus {
|
|
return typeof value === "string" && allTaskRunStatuses.includes(value as TaskRunStatus);
|
|
}
|
|
|
|
/**
|
|
* Check if a value is a valid RunFriendlyStatus
|
|
*/
|
|
export function isRunFriendlyStatus(value: unknown): value is RunFriendlyStatus {
|
|
return typeof value === "string" && runFriendlyStatus.includes(value as RunFriendlyStatus);
|
|
}
|
|
|
|
export const runStatusTitleFromStatus: Record<TaskRunStatus, RunFriendlyStatus> = {
|
|
DELAYED: "Delayed",
|
|
PENDING: "Queued",
|
|
PENDING_VERSION: "Pending version",
|
|
WAITING_FOR_DEPLOY: "Pending version",
|
|
DEQUEUED: "Dequeued",
|
|
EXECUTING: "Executing",
|
|
WAITING_TO_RESUME: "Waiting",
|
|
RETRYING_AFTER_FAILURE: "Reattempting",
|
|
PAUSED: "Paused",
|
|
CANCELED: "Canceled",
|
|
INTERRUPTED: "Interrupted",
|
|
COMPLETED_SUCCESSFULLY: "Completed",
|
|
COMPLETED_WITH_ERRORS: "Failed",
|
|
SYSTEM_FAILURE: "System failure",
|
|
CRASHED: "Crashed",
|
|
EXPIRED: "Expired",
|
|
TIMED_OUT: "Timed out",
|
|
};
|
|
|
|
const titlesStatusesArray = Object.entries(runStatusTitleFromStatus);
|
|
|
|
/**
|
|
* Chart series color for each TaskRunStatus, mirroring `runStatusClassNameColor`.
|
|
* Values are CSS variables (defined in tailwind.css) so they follow the theme;
|
|
* only usable in CSS contexts (SVG attributes, chart config styles).
|
|
*/
|
|
const RUN_STATUS_CHART_COLORS: Record<TaskRunStatus, string> = {
|
|
PENDING: "var(--color-run-pending)",
|
|
DELAYED: "var(--color-run-delayed)",
|
|
PENDING_VERSION: "var(--color-run-pending-version)",
|
|
WAITING_FOR_DEPLOY: "var(--color-run-waiting-for-deploy)",
|
|
EXECUTING: "var(--color-run-executing)",
|
|
RETRYING_AFTER_FAILURE: "var(--color-run-retrying-after-failure)",
|
|
DEQUEUED: "var(--color-run-dequeued)",
|
|
WAITING_TO_RESUME: "var(--color-run-waiting-to-resume)",
|
|
PAUSED: "var(--color-run-paused)",
|
|
CANCELED: "var(--color-run-canceled)",
|
|
EXPIRED: "var(--color-run-expired)",
|
|
INTERRUPTED: "var(--color-run-interrupted)",
|
|
COMPLETED_SUCCESSFULLY: "var(--color-run-completed-successfully)",
|
|
COMPLETED_WITH_ERRORS: "var(--color-run-completed-with-errors)",
|
|
SYSTEM_FAILURE: "var(--color-run-system-failure)",
|
|
CRASHED: "var(--color-run-crashed)",
|
|
TIMED_OUT: "var(--color-run-timed-out)",
|
|
};
|
|
|
|
/**
|
|
* Get the chart color for a run status value. Accepts either a raw TaskRunStatus
|
|
* (e.g. "COMPLETED_SUCCESSFULLY") or a friendly name (e.g. "Completed").
|
|
* Returns `undefined` when the value is not a recognised status.
|
|
*/
|
|
export function getRunStatusChartColor(value: string): string | undefined {
|
|
if (isTaskRunStatus(value)) {
|
|
return RUN_STATUS_CHART_COLORS[value];
|
|
}
|
|
if (isRunFriendlyStatus(value)) {
|
|
return RUN_STATUS_CHART_COLORS[runStatusFromFriendlyTitle(value)];
|
|
}
|
|
return undefined;
|
|
}
|