TypeDrop

2026-06-18 Challenge

2026-06-18 Hard

Typed GraphQL-Style Query Planner & Resolver

You're building the query execution layer for an in-house data-graph API. Raw query documents arrive as `unknown` from an HTTP body; your planner must validate them, resolve each field through a registry of strongly-typed resolvers, fan out leaf fetches concurrently with a concurrency cap, and return a deeply-typed response tree — with zero `any`.

Goals

  • Implement `validateQueryDocument` to safely narrow an `unknown` HTTP body into a typed `QueryDocument` using structural type guards — no `any`, no assertions.
  • Implement `buildExecutionPlan` to convert a `QueryDocument` into a recursive `ExecutionPlan` tree where every node carries a correctly-constructed branded `ResolverPath`.
  • Implement `createConcurrencyLimiter` as a generic queue-based scheduler that caps simultaneous async tasks while preserving each task's return type.
  • Implement `executeQuery` to fan out root resolvers concurrently (or sequentially for mutations), resolve children with parent values, isolate per-field errors, and assemble a `QueryResponse`.
challenge.ts

// Key types at a glance:

type ScalarValue = string | number | boolean | null;

type ResponseNode =
  | ScalarValue
  | ResponseNode[]
  | { [field: string]: ResponseNode };

declare const __resolverPath: unique symbol;
type ResolverPath = string & { readonly [__resolverPath]: true };

type ResolverRegistry = Map<ResolverPath, Resolver>;

interface ExecutionPlan {
  operation: "query" | "mutation";
  roots: PlanNode[];   // PlanNode.children is recursive
}

// --- Main entry point you must implement ---
async function runQuery(
  raw: unknown,                // straight from HTTP body
  registry: ResolverRegistry,
  context: ResolverContext
): Promise<QueryResponse>;
// pipeline: validate → buildExecutionPlan → executeQuery
Hints (click to reveal)

Hints

  • For `createConcurrencyLimiter`, maintain a counter of running tasks and a queue of `() => void` drain callbacks — resolve each queued promise only when a slot opens.
  • In `validateQueryDocument`, write a recursive `isFieldSelection(v: unknown): v is FieldSelection` guard; TypeScript will narrow the array element type for you if you use `Array.prototype.every`.
  • In `executeQuery`, remember that `mutation` root nodes must run one-at-a-time: use a `for...of` loop with `await` instead of `Promise.all`, even when the concurrency limiter is active.

Or clone locally

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