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>`.

Or clone locally

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