Initial Commit
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
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<string>;
|
||||
readonly resolvers: ReadonlyArray<ResolverMap>;
|
||||
};
|
||||
};
|
||||
|
||||
export interface FederationResult {
|
||||
readonly merged: MergedSchema;
|
||||
readonly resolvers: Record<string, Record<string, unknown>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<PluginRegistryEntry>
|
||||
): FederationResult {
|
||||
const contributions: SdlContribution[] = [];
|
||||
const frozens: ReadonlyArray<ResolverMap>[] = [];
|
||||
|
||||
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<string, Record<string, unknown>> = {};
|
||||
for (const maps of frozens) {
|
||||
for (const map of maps) {
|
||||
for (const typeName of Object.keys(map)) {
|
||||
const incoming = (map as Record<string, unknown>)[
|
||||
typeName
|
||||
] as Record<string, unknown>;
|
||||
const existing = combined[typeName] ?? {};
|
||||
combined[typeName] = { ...existing, ...incoming };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { merged, resolvers: combined };
|
||||
}
|
||||
Reference in New Issue
Block a user