53 lines
2.1 KiB
TypeScript
53 lines
2.1 KiB
TypeScript
import type { DirectiveDescriptor } from "./directives.types.js";
|
|
|
|
/**
|
|
* `@auth(scope: String!)` directive descriptor. Engine adapters install it at
|
|
* schema-build time; the handler checks `ctx.principal` against the declared
|
|
* scope and raises the host's auth error when the scope is not present.
|
|
*
|
|
* The handler here is the **contract** body — concrete engines wire it into
|
|
* the resolver pipeline. The default raises a typed error noting the missing
|
|
* scope so callers see a stable failure shape during early integration.
|
|
*/
|
|
export const authDirective: DirectiveDescriptor<{ readonly scope: string }> = {
|
|
name: "auth",
|
|
sdl: "directive @auth(scope: String!) on FIELD_DEFINITION",
|
|
locations: ["FIELD_DEFINITION"],
|
|
args: [{ name: "scope", type: "String!" }],
|
|
handler: (args, ctx) => {
|
|
const scopes: readonly string[] = ctx.principal?.scopes ?? [];
|
|
if (!scopes.includes(args.scope)) {
|
|
throw new Error(
|
|
`@auth(scope: "${args.scope}") failed — principal does not carry the required scope`
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
/**
|
|
* `@rateLimit(rpm: Int!)` directive descriptor. Engine adapters install it at
|
|
* schema-build time; the handler delegates to the host's rate-limit slot.
|
|
* The contract body checks the argument bound and otherwise yields — concrete
|
|
* engines wire it into the request pipeline.
|
|
*/
|
|
export const rateLimitDirective: DirectiveDescriptor<{ readonly rpm: number }> =
|
|
{
|
|
name: "rateLimit",
|
|
sdl: "directive @rateLimit(rpm: Int!) on FIELD_DEFINITION",
|
|
locations: ["FIELD_DEFINITION"],
|
|
args: [{ name: "rpm", type: "Int!" }],
|
|
handler: (args) => {
|
|
if (!Number.isInteger(args.rpm) || args.rpm <= 0) {
|
|
throw new Error(
|
|
`@rateLimit(rpm: ${String(args.rpm)}) — rpm must be a positive integer`
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
/** Lookup of every host-supplied directive descriptor, keyed by name. */
|
|
export const HOST_DIRECTIVES = Object.freeze({
|
|
auth: authDirective,
|
|
rateLimit: rateLimitDirective,
|
|
}) as Readonly<Record<"auth" | "rateLimit", DirectiveDescriptor>>;
|