@getcronit/pylon
Version:

105 lines (104 loc) • 4.63 kB
TypeScript
import { Context } from '@getcronit/pylon';
type Primitive = string | number | boolean | symbol | bigint | undefined | null | Date;
/**
* Recursively maps a GraphQL return type to a selection map for the `needs` object.
* Allows boolean flags for selection and a special `__args` property for nested arguments.
*/
export type NeedsMap<T> = T extends Primitive ? boolean : T extends (...args: any[]) => infer Ret ? NeedsMap<Awaited<Ret>> : T extends Array<infer U> ? NeedsMap<U> : {
[K in keyof T]?: NeedsMap<T[K]> | boolean;
} & {
__args?: Record<string, any>;
};
/**
* Maps over the properties of the resolved type within the registry.
* Intercepting the lookup at the property level mitigates TS2589
* (Type instantiation is excessively deep and possibly infinite) during recursive type inference.
*/
type MapValue<V, P, R> = V extends Primitive ? V : V extends Array<infer U> ? Array<PatchSchema<U, P, R>> : V extends {
__typename: infer Name;
} ? Name extends keyof P ? P[Name & keyof P] extends (...args: any) => infer Res ? {
[Prop in keyof Res]: PatchSchema<Res[Prop], P, R>;
} : never : {
[Prop in keyof V]: PatchSchema<V[Prop], P, R>;
} : {
[Prop in keyof V]: PatchSchema<V[Prop], P, R>;
};
type MappedRegistry<R, P> = {
[K in keyof R]: MapValue<R[K], P, R>;
};
type PatchSchema<T, P, R> = T extends Primitive ? T : T extends (...args: infer Args) => infer Ret ? (...args: Args) => Ret extends Promise<any> ? Promise<PatchSchema<Awaited<Ret>, P, R>> : PatchSchema<Awaited<Ret>, P, R> : T extends Array<infer U> ? Array<PatchSchema<U, P, R>> : T extends {
__typename: infer Name;
} ? Name extends keyof R ? MappedRegistry<R, P>[Name & keyof R] : {
[K in keyof T]: PatchSchema<T[K], P, R>;
} : T extends object ? {
[K in keyof T]: PatchSchema<T[K], P, R>;
} : T;
export interface GatewayContext<TRegistry extends {
delegate: any;
types: any;
}> {
delegate: <K extends keyof TRegistry['delegate'], TNeeds extends NeedsMap<TRegistry['delegate'][K]['return']> = NeedsMap<TRegistry['delegate'][K]['return']>>(key: K, ...opts: {} extends TRegistry['delegate'][K]['args'] ? [options?: {
args?: TRegistry['delegate'][K]['args'];
needs?: TNeeds;
}] : [options: {
args: TRegistry['delegate'][K]['args'];
needs?: TNeeds;
}]) => Promise<TRegistry['delegate'][K]['return']>;
}
declare class PylonGateway<TRegistry extends {
delegate: any;
types: any;
}, TPatch extends Record<string, (data: any, api: any) => any>> {
private config;
private apiContext;
constructor(config: {
url: string;
headers?: (ctx: any) => Record<string, string>;
patches: TPatch;
});
delegate<K extends keyof TRegistry['delegate'], TNeeds extends NeedsMap<TRegistry['delegate'][K]['return']> = NeedsMap<TRegistry['delegate'][K]['return']>>(key: K, ...opts: {} extends TRegistry['delegate'][K]['args'] ? [options?: {
args?: TRegistry['delegate'][K]['args'];
needs?: TNeeds;
}] : [options: {
args: TRegistry['delegate'][K]['args'];
needs?: TNeeds;
}]): Promise<PatchSchema<TRegistry['delegate'][K]['return'], TPatch, TRegistry['types']>>;
}
/**
* Instantiates a factory for configuring a strongly-typed PylonGateway.
*
* Utilizes a generated type registry (`TRegistry`) mapping remote GraphQL typenames
* to TypeScript interfaces, guaranteeing end-to-end type safety for configurations,
* schema patches, and delegated execution payloads.
*
* @template TRegistry - Type map correlating GraphQL typenames to local TypeScript definitions.
* @returns Gateway configuration interface.
*
* @example
*
* // Provision remote schema via CLI: pylon pull <url> -n <name>
*
* ```typescript
* import { createGateway } from '@getcronit/pylon';
* import { RemoteRegistry } from './generated/remote';
*
* const gateway = createGateway<RemoteRegistry>().configure({
* url: '[https://api.example.com/graphql](https://api.example.com/graphql)',
* headers: (ctx) => ({ Authorization: ctx.token }),
* patches: {
* User: (data, api) => ({ ...data, fullName: `${data.firstName} ${data.lastName}` })
* }
* });
* ```
*/
export declare function createGateway<TRegistry extends {
delegate: any;
types: any;
}>(): {
configure: <TPatch extends { [K in keyof TPatch]: K extends keyof TRegistry["types"] ? (data: TRegistry["types"][K], api: GatewayContext<TRegistry>) => any : never; }>(config: {
url: string;
headers?: (ctx: Context) => Record<string, string>;
patches: TPatch;
}) => PylonGateway<TRegistry, TPatch>;
};
export {};