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.
Useful resources
Or clone locally
git clone -b challenge/2026-06-22 https://github.com/niltonheck/typedrop.git