Common Utilities
frg
export const frg = ( strings: readonly string[] | ArrayLike<string>, ...values: any[]) => { const templated = String.raw({ raw: strings }, ...values) .replace(/\n/g, "") .replace(/\s{1,}/g, " ");
return templated.startsWith("__typename ") ? templated : `__typename ${templated}`;};Utilises Tagged Templates to create a GraphQL fragment.
This will append __typename to the start of the fragment and remove all new lines and multiple spaces. If the fragment
starts with __typename it will not be appended. If the fragment contains __typename anywhere else, we will still
append it to the start. We will not remove duplicate __typename’s. This is a simple string append, we don’t analyse
the fragment.
Example
const registration = createRegistration({ fragment: frg` id name `})// registration.fragment === "__typename id name"dependenciesToMappedQuery
export function dependenciesToMappedQuery( registrations: (RegistrationStruct | DependencyStruct)[]): string { return registrations .map((value) => `... on ${value.__typename} { ...${value.fragmentName} }`) .join("\n");}Sometimes a field can have multiple options, for example, a Component field that can be a HeroBanner or Image. In that case you would use dependenciesToMappedQuery to map the dependencies to a query.
Before
const query = ` component { __typename ... on HeroBanner { ...${HeroBannerFragment.fragmentName} } ... on Asset { ...${ImageFragment.fragmentName} } }`After
const query = ` component { __typename ${dependenciesToMappedQuery([HeroBannerFragment, ImageFragment])} }`Struct
import type { RegistrationStruct, DependencyStruct } from '@adrocodes/pigeon';import { z } from 'zod';
type Struct<R extends RegistrationStruct | DependencyStruct> = z.infer<R["schema"]>This is a utility type that will infer the schema of a createRegistration or createDependency.
Example
type ImageStruct = Struct<typeof ImageFragment>;resolver
import { z, type ZodError, type ZodSchema } from "zod";// IMPORTANT: Replace with YOUR clientimport { getClient, type QueryInput } from "./client";
type Resolver<T extends ZodSchema, QE extends unknown, ZE extends unknown> = { schema: T; query: QueryInput; onQueryError: (error: Error) => QE; onZodError: (error: ZodError) => ZE; label?: string;};
export async function resolver< T extends ZodSchema, QE extends unknown, ZE extends unknown>( input: Resolver<T, QE, ZE>): Promise< | z.infer<T> | ReturnType<Resolver<T, QE, ZE>["onQueryError"]> | ReturnType<Resolver<T, QE, ZE>["onZodError"]>> { const { query, schema, onQueryError, onZodError, label = "Unknown" } = input; const { data, error } = await getClient().query(query);
if (error) { console.error(`[${label}] - Query Error`, { error, message: error.message, }); return onQueryError(error); }
const parsed = await schema.safeParseAsync(data);
if (!parsed.success) { console.error(`[${label}] - Zod Error`, { error: parsed.error, }); return onZodError(parsed.error); }
return parsed.data;}This is a utility function that will resolve a query using a Zod schema. It will return the data if successful, or call the onQueryError or onZodError functions if there is an error.
You’ll need to provide your own getClient function and QueryInput type.
Example
const data = await resolver({ query: {}, schema: z.object({...}), onQueryError: (error) => {...}, onZodError: (error) => {...}, label: "GetPageData"})