TypeDrop

2026-06-22 Challenge

2026-06-22 Hard

Typed Workflow Orchestrator with Dependency Resolution & Typed Step Registry

You're building the execution engine for a low-code automation platform. Raw workflow definitions arrive as `unknown` from a user-uploaded JSON file; your orchestrator must validate them, topologically sort steps by their declared dependencies, execute each step through a registry of strongly-typed handlers, propagate outputs between steps, and return a fully-typed execution report — with zero `any`.

Goals

  • Implement `toStepId` and `toHandlerKind` branded-type constructors that reject empty/non-string input.
  • Implement `validateWorkflow` to narrow `unknown` → `ValidatedWorkflow`, enforcing uniqueness, dangling-reference, and registry-membership checks without using `any`.
  • Implement `topologicalSort` using Kahn's algorithm to produce a wave-ordered flat array and throw a descriptive error on circular dependencies.
  • Implement `runWorkflow` to execute steps wave-by-wave with `Promise.allSettled`, propagate outputs as `StepInputMap`, skip steps whose dependencies failed, and compute the correct `overallStatus`.
challenge.ts
// Core types & main entry-point signature

type StepId      = string & { readonly __brand: "StepId" };
type HandlerKind = string & { readonly __brand: "HandlerKind" };

type HandlerDescriptor<C, O> = {
  readonly kind: HandlerKind;
  validate(rawConfig: unknown): C;
  execute(config: C, inputs: StepInputMap): Promise<O>;
};

type StepResult<O = unknown> =
  | { readonly status: "success"; readonly stepId: StepId; readonly output: O }
  | { readonly status: "failure"; readonly stepId: StepId; readonly error: string };

type WorkflowReport = {
  readonly workflowId: string;
  readonly results: StepResult[];
  readonly overallStatus: "completed" | "partial" | "failed";
};

// Full pipeline: raw JSON string → strongly-typed execution report
async function executeWorkflowJson(
  rawJson: string,
  registry: HandlerRegistry
): Promise<WorkflowReport> { /* TODO */ }
Hints (click to reveal)

Hints

  • For `HandlerRegistry`, an index signature `[K in HandlerKind]: HandlerDescriptor<unknown, unknown>` is the correct escape hatch at the registry level — individual handlers are still fully typed via their own `HandlerDescriptor<C, O>` generics.
  • Kahn's algorithm: build an in-degree map, push all zero-in-degree nodes into a queue as the first wave, process wave-by-wave, decrementing in-degrees as you go — if nodes remain after the queue empties, you have a cycle.
  • To build waves for concurrent execution in `runWorkflow`, group steps by their topological 'level': a step's level is `1 + max(level of dependencies)`, then `Promise.allSettled` each level's group together.

Or clone locally

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