TypeDrop
2026-10-04 Challenge
2026-10-04
Hard
Typed Plugin Middleware Pipeline
You're building the extensibility layer for a developer-facing HTTP gateway. Third-party plugins are registered into a typed middleware pipeline where each plugin declares the context shape it reads, the context shape it writes, and whether it can fail — the compiler must enforce that plugins are composed in a valid order (i.e. a plugin's required reads are always satisfied by prior writes), that error-producing plugins are handled, and that the final resolved context type is derived structurally from the full chain.
Goals
- Implement `MergeCtx`, `SatisfiedBy`, `PipelineFinalCtx`, and `PipelineErrors` as purely structural compile-time types with no `any`.
- Implement `definePlugin` as a zero-runtime-overhead generic inference anchor.
- Implement `buildPipeline` so plugins execute sequentially, context is accumulated, timings are recorded, and plugin errors are re-thrown as branded `PluginError` objects.
- Implement `composePlugins` to fuse two plugins into one whose name is a template literal, whose Writes are merged, and whose error brand is a union.
challenge.ts
/** A plain record of string keys to unknown values — the base context shape. */
type BaseCtx = Record<string, unknown>;
/** A branded error produced by a plugin. */
type PluginError<Brand extends string> = {
readonly brand: Brand;
readonly message: string;
readonly cause?: unknown;
};
/** Describes one plugin in the pipeline. */
type PluginDef<
Reads extends BaseCtx,
Writes extends BaseCtx,
EBrand extends string | never
> = {
readonly name: string;
readonly run: (ctx: Reads) => Promise<Writes>;
};
// --- Your key helpers to implement ---
type MergeCtx<Acc extends BaseCtx, Writes extends BaseCtx> = never; // TODO
type SatisfiedBy<Required extends BaseCtx, Acc extends BaseCtx> = never; // TODO
type PipelineFinalCtx<Plugins extends readonly PluginDef<BaseCtx,BaseCtx,string|never>[], InitCtx extends BaseCtx = Record<never,never>> = never; // TODO
declare function definePlugin<R extends BaseCtx, W extends BaseCtx, E extends string | never = never>(
def: PluginDef<R, W, E>
): PluginDef<R, W, E>;
declare function buildPipeline<const I extends BaseCtx, const P extends readonly PluginDef<BaseCtx,BaseCtx,string|never>[]>(
plugins: P
): Pipeline<P, I>;
Hints (click to reveal)
Hints
- For `MergeCtx`, think `Omit<Acc, keyof Writes> & Writes` — then wrap it with `{ [K in keyof ...]: ... }` to flatten the intersection into a single object type.
- For `PipelineFinalCtx`, write a recursive conditional type that peels the tuple one element at a time with `infer Head extends PluginDef<...>` and `infer Tail extends readonly PluginDef<...>[]`, folding `MergeCtx` on each step.
- For `PipelineErrors`, use a distributive conditional type: `Plugins[number] extends PluginDef<any, any, infer E>` — then filter out `never` from the resulting union before wrapping in `PluginError<E>`.
Useful resources
Or clone locally
git clone -b challenge/2026-10-04 https://github.com/niltonheck/typedrop.git