TypeDrop

2026-05-13 Challenge

2026-05-13 Hard

Typed Async Job Scheduler with Priority Queues

You're building the task-execution layer for a distributed background-job platform. Raw job definitions arrive as `unknown` from multiple producer services; your scheduler must validate them, enqueue them into typed priority queues, execute batches with a concurrency limit and per-job timeout, and return a strongly-typed execution report — with zero `any`.

Goals

  • Implement `validateJob` to parse `unknown` payloads into a branded `Job`, collecting ALL validation errors before returning.
  • Implement `buildPriorityQueue` to perform a stable sort of jobs by their priority weight.
  • Implement `executeJob` to run a job with `Promise.race`-based timeout and per-failure retry logic, returning a discriminated `JobOutcome`.
  • Implement `runScheduler` to validate raw inputs, group and sort jobs into per-queue priority queues, and execute them under a global concurrency limit, returning a fully-typed `SchedulerReport`.
challenge.ts

type JobPriority = "critical" | "high" | "normal" | "low";

type JobId    = string & { readonly __brand: "JobId" };
type QueueId  = string & { readonly __brand: "QueueId" };

interface Job {
  readonly id:         JobId;
  readonly queueId:    QueueId;
  readonly priority:   JobPriority;
  readonly timeoutMs:  number;        // 100 – 30_000
  readonly payload:    Record<string, unknown>;
  readonly maxRetries: number;        // 0 – 5
}

type JobOutcome =
  | { readonly status: "succeeded"; readonly jobId: JobId; readonly durationMs: number }
  | { readonly status: "failed";    readonly jobId: JobId; readonly error: string; readonly attempts: number }
  | { readonly status: "timed_out"; readonly jobId: JobId; readonly timeoutMs: number };

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

// --- Four functions to implement ---
function validateJob(raw: unknown): Result<Job, ValidationError[]>;
function buildPriorityQueue(jobs: readonly Job[]): readonly Job[];
async function executeJob(job: Job, handler: JobHandler): Promise<JobOutcome>;
async function runScheduler(
  rawJobs: readonly unknown[],
  handler: JobHandler,
  config: SchedulerConfig
): Promise<SchedulerReport>;
Hints (click to reveal)

Hints

  • For the concurrency limiter in `runScheduler`, maintain a counter of active slots and use a queue of pending resolvers — or track in-flight Promises and use `Promise.race` to free a slot when any job finishes.
  • In `executeJob`, create a timeout Promise with `new Promise<never>((_, reject) => setTimeout(...))` and race it against the handler; a `timed_out` result must exit immediately without retrying.
  • When collecting `ValidationError[]` in `validateJob`, push errors into a mutable local array and only decide `ok: true` vs `ok: false` at the very end — this satisfies the 'collect ALL errors' requirement.

Or clone locally

git clone -b challenge/2026-05-13 https://github.com/niltonheck/typedrop.git