Files
jobqueue/src/types.ts

217 lines
5.6 KiB
TypeScript

export type JsonPrimitive = string | number | boolean | null;
export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };
export type Awaitable<T> = T | Promise<T>;
export type JobData = Record<string, JsonValue>;
export type JobStatus =
| 'pending'
| 'active'
| 'completed'
| 'failed'
| 'cancelled'
| 'stale';
export type JobPhaseStatus = 'pending' | 'active' | 'completed' | 'failed' | 'cancelled';
export type RetryBackoffStrategy = 'fixed' | 'linear' | 'exponential';
export type ErrorDisposition = 'recoverable' | 'fatal';
export interface JobPhaseState {
name: string;
status: JobPhaseStatus;
progress: number;
message: string | null;
startedAt: string | null;
completedAt: string | null;
error: string | null;
}
export interface JobFailure {
message: string;
phase: string | null;
recoverable: boolean;
timestamp: string;
attempt: number;
}
export interface JobRetryEvent {
jobId: string;
phase: string | null;
attempt: number;
delayMs: number;
nextRunAt: string;
timestamp: string;
}
export interface JobProgressEvent {
jobId: string;
phase: string;
phaseProgress: number;
overallProgress: number;
message: string | null;
timestamp: string;
details?: JsonValue;
}
export interface JobRecord<TData extends JobData = JobData> {
id: string;
status: JobStatus;
data: TData;
currentPhase: string | null;
phases: JobPhaseState[];
phaseResults: Record<string, JsonValue | undefined>;
progress: number;
progressMessage: string | null;
error: JobFailure | null;
retryCount: number;
maxAttempts: number;
webhookUrl: string | null;
webhookSent: boolean;
createdAt: string;
startedAt: string | null;
completedAt: string | null;
updatedAt: string;
scheduledAt: string | null;
cancelledAt: string | null;
}
export interface EnqueueOptions {
id?: string;
scheduledAt?: string | Date;
maxAttempts?: number;
webhookUrl?: string;
}
export interface RetryOptions {
fromStart?: boolean;
scheduledAt?: string | Date;
}
export interface ListJobsOptions {
statuses?: JobStatus[];
limit?: number;
offset?: number;
}
export interface StreamOptions {
jobId?: string;
includeSnapshot?: boolean;
keepAliveMs?: number;
}
export interface RetryConfig<TData extends JobData = JobData> {
maxAttempts?: number;
strategy?: RetryBackoffStrategy;
baseDelayMs?: number;
maxDelayMs?: number;
classifyError?: (
error: unknown,
job: JobRecord<TData>,
) => Awaitable<ErrorDisposition>;
}
export interface RetentionConfig<TData extends JobData = JobData> {
staleAfterMs: number;
deleteAfterMs: number;
intervalMs?: number;
onStale?: (job: JobRecord<TData>) => Awaitable<void>;
onDelete?: (job: JobRecord<TData>) => Awaitable<void>;
}
export type WebhookEventName =
| 'job:completed'
| 'job:failed'
| 'job:retrying'
| 'job:cancelled'
| 'job:stale';
export interface WebhookConfig {
url?: string;
events?: WebhookEventName[];
secret?: string;
timeoutMs?: number;
headers?: Record<string, string>;
maxAttempts?: number;
baseDelayMs?: number;
maxDelayMs?: number;
}
export interface QueueConfig<TData extends JobData = JobData> {
dbPath: string;
phases: readonly string[];
concurrency?: number;
retry?: RetryConfig<TData>;
retention?: RetentionConfig<TData>;
webhook?: WebhookConfig;
shutdownTimeoutMs?: number;
}
export interface PhaseContext<TData extends JobData = JobData> {
readonly job: JobRecord<TData>;
readonly phase: string;
readonly signal: AbortSignal;
progress: (percent: number, message?: string, details?: JsonValue) => Promise<JobRecord<TData>>;
phaseResult: <TResult extends JsonValue = JsonValue>(phaseName: string) => TResult | undefined;
phaseResults: () => Record<string, JsonValue | undefined>;
isCancelled: () => boolean;
throwIfCancelled: () => Promise<void>;
}
export type PhaseHandler<TData extends JobData = JobData> = (
job: JobRecord<TData>,
context: PhaseContext<TData>,
) => Awaitable<JsonValue | undefined>;
export interface WebhookDispatchResult {
event: WebhookEventName;
jobId: string;
status: number;
deliveredAt: string;
}
export interface WebhookDispatchError {
event: WebhookEventName;
jobId: string;
message: string;
finalAttempt: number;
}
export interface QueueStreamEvent<TData extends JobData = JobData> {
type:
| 'snapshot'
| 'job:enqueued'
| 'job:started'
| 'job:progress'
| 'job:phase:completed'
| 'job:completed'
| 'job:failed'
| 'job:retrying'
| 'job:cancelled'
| 'job:stale'
| 'job:deleted'
| 'job:webhook:delivered'
| 'job:webhook:failed'
| 'ping';
jobId?: string;
job?: JobRecord<TData>;
progress?: JobProgressEvent;
phase?: JobPhaseState;
failure?: JobFailure;
retry?: JobRetryEvent;
webhook?: WebhookDispatchResult | WebhookDispatchError;
deletedJobId?: string;
timestamp: string;
}
export interface JobQueueEvents<TData extends JobData = JobData> {
'job:enqueued': [job: JobRecord<TData>];
'job:started': [job: JobRecord<TData>];
'job:progress': [job: JobRecord<TData>, progress: JobProgressEvent];
'job:phase:completed': [job: JobRecord<TData>, phase: JobPhaseState];
'job:completed': [job: JobRecord<TData>];
'job:failed': [job: JobRecord<TData>, failure: JobFailure];
'job:retrying': [job: JobRecord<TData>, retry: JobRetryEvent];
'job:cancelled': [job: JobRecord<TData>];
'job:stale': [job: JobRecord<TData>];
'job:deleted': [jobId: string];
'job:webhook:delivered': [job: JobRecord<TData>, result: WebhookDispatchResult];
'job:webhook:failed': [job: JobRecord<TData>, error: WebhookDispatchError];
}