TypeDrop

2026-04-13 Challenge

2026-04-13 Hard

Typed Workflow Orchestrator

You're building the execution engine for a low-code automation platform. Raw workflow definitions arrive as unknown JSON blobs from a database; your orchestrator must validate them, compile them into a strongly-typed execution graph, run steps through a typed middleware pipeline with retry logic, and surface a discriminated-union result per step — with zero `any`.

Goals

  • Define `HandlerRegistry` as a mapped type over `WorkflowStep["kind"]` using `Extract` to bind each key to the correct `StepHandler` generic instantiation.
  • Implement `parseWorkflowDef` to validate unknown input into a fully-typed `WorkflowDef`, checking all structural invariants and returning the first `ValidationError` encountered.
  • Implement `composeMiddleware` to chain an array of `MiddlewareFn` wrappers around a terminal handler, preserving outermost-first execution order.
  • Implement `runWorkflow` to parse, dispatch, retry, branch on conditions, collect `StepOutcome` records, and return a typed `WorkflowReport` — handling every discriminated step kind exhaustively.
challenge.ts
// Core types and main orchestrator signature

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

type WorkflowStep = HttpStep | TransformStep | ConditionStep | NotifyStep;

// TODO 2: Define this as a mapped type
type HandlerRegistry = /* mapped over WorkflowStep["kind"] */ unknown;

type MiddlewareFn = (
  step: WorkflowStep,
  next: () => Promise<Result<unknown, ExecutionError>>
) => Promise<Result<unknown, ExecutionError>>;

// Main entry point — parse, compose, execute, report
declare function runWorkflow(
  raw: unknown,
  registry: HandlerRegistry,
  middlewares?: readonly MiddlewareFn[]
): Promise<Result<WorkflowReport, OrchestratorError>>;
Hints (click to reveal)

Hints

  • For `HandlerRegistry`, try: `{ [K in WorkflowStep["kind"]]: StepHandler<Extract<WorkflowStep, { kind: K }>> }` — the mapped type does the narrowing for you.
  • In `composeMiddleware`, build the chain from the inside out using `reduceRight` so that the first middleware in the array becomes the outermost wrapper.
  • In `withRetry`, thread the attempt index through a recursive async helper rather than a `for` loop — it makes the exponential back-off calculation cleaner and avoids mutable state.

Or clone locally

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