TypeDrop

2026-06-15 Challenge

2026-06-15 Medium

Typed Job Queue Scheduler with Priority & Retry

You're building the background job processing layer for a SaaS platform. Raw job definitions arrive as `unknown` from a REST API; your scheduler must validate them, enqueue them by priority, execute them with concurrency limits, and produce a strongly-typed execution report — with zero `any`.

Goals

  • Implement `validateJob` to parse `unknown` API payloads into a discriminated-union `Job`, returning a typed `Result<Job, ValidationError>` for every failure case.
  • Implement `enqueueByPriority` to stably sort jobs by priority tier (high → normal → low) without mutating the input array.
  • Implement `runScheduler` to execute jobs with a sliding-window concurrency pool, per-job retry budgets, and correct `succeeded` / `exhausted` / `failed` status tracking.
  • Implement `buildReport` as a pure single-pass `reduce` that tallies outcomes and returns a `SchedulerReport` whose `outcomes` field is the exact same array reference passed in.
challenge.ts
// Key types & main function signatures

export type Priority = "high" | "normal" | "low";
export type JobKind  = "email" | "report" | "export";

export type Job =
  | { id: string; kind: "email";  priority: Priority; retries: number; payload: EmailPayload  }
  | { id: string; kind: "report"; priority: Priority; retries: number; payload: ReportPayload }
  | { id: string; kind: "export"; priority: Priority; retries: number; payload: ExportPayload };

export type Result<T, E> =
  | { ok: true;  value: T }
  | { ok: false; error: E };

// Maps every JobKind to a strongly-typed handler — must be complete
export type HandlerRegistry = {
  [K in JobKind]: (job: Extract<Job, { kind: K }>) => Promise<boolean>;
};

export function validateJob(raw: unknown): Result<Job, ValidationError>;

export function enqueueByPriority(jobs: Job[]): Job[];

export async function runScheduler(
  jobs: Job[],
  registry: HandlerRegistry,
  config: SchedulerConfig        // { concurrency: number; maxAttempts: number }
): Promise<SchedulerReport>;

export function buildReport(outcomes: JobOutcome[]): SchedulerReport;
Hints (click to reveal)

Hints

  • For `HandlerRegistry`, the mapped type `[K in JobKind]: JobHandler<K>` forces you to handle every kind — lean on `Extract<Job, { kind: K }>` to get the right payload type per handler.
  • A sliding-window concurrency pool can be built by maintaining a `Set` of in-flight Promises and `await`-ing the fastest one (via `Promise.race`) whenever the pool is full.
  • In `validateJob`, narrow `raw` with `typeof` / `in` guards first, then use a `switch` on `kind` to validate the per-kind payload fields — the compiler will track the narrowed type in each branch.

Or clone locally

git clone -b challenge/2026-06-15 https://github.com/niltonheck/typedrop.git