import { type GraphqlRegistry, type MergedSchema, mergeSdl, type ResolverMap, type SdlContribution, } from "@suaveplan/api-graphql"; /** * Aggregate one or more plugin registries into a single `MergedSchema` + * combined resolver map. The Yoga engine accepts a *flat* list of * `(registry, plugin, namespace?)` triples — one per contributing plugin — * and produces: * * - `merged` — output of `mergeSdl(...)` (raises * `GRAPHQL.SCHEMA_COLLISION` on cross-plugin field collisions). * - `resolvers` — combined `ResolverMap` (Type → field → resolver) where * each `Type` block is a shallow object-spread of every contributing * plugin's block. Within a single Type, fields contributed by the same * plugin via multiple `addResolvers` calls layer in order. */ export interface PluginRegistryEntry { readonly plugin: string; readonly registry: FreezableRegistry; readonly namespace?: string; } type FreezableRegistry = GraphqlRegistry & { readonly freeze: () => { readonly typeDefs: ReadonlyArray; readonly resolvers: ReadonlyArray; }; }; export interface FederationResult { readonly merged: MergedSchema; readonly resolvers: Record>; } /** * Merge plugin contributions into a single (sdl, resolvers) pair ready for * `makeExecutableSchema`. Collisions in resolvers across plugins are * surfaced by the SDL collision detection step that runs first — by the * time control reaches resolver merging, every `(Type, field)` pair is * single-plugin-owned. */ export function federatePlugins( entries: ReadonlyArray ): FederationResult { const contributions: SdlContribution[] = []; const frozens: ReadonlyArray[] = []; for (const entry of entries) { const snap = entry.registry.freeze(); const contribution: SdlContribution = entry.namespace !== undefined ? { plugin: entry.plugin, typeDefs: snap.typeDefs, namespace: entry.namespace, } : { plugin: entry.plugin, typeDefs: snap.typeDefs }; contributions.push(contribution); frozens.push(snap.resolvers); } const merged = mergeSdl(contributions); const combined: Record> = {}; for (const maps of frozens) { for (const map of maps) { for (const typeName of Object.keys(map)) { const incoming = (map as Record)[ typeName ] as Record; const existing = combined[typeName] ?? {}; combined[typeName] = { ...existing, ...incoming }; } } } return { merged, resolvers: combined }; }