> ## Documentation Index
> Fetch the complete documentation index at: https://adro.codes/llms.txt
> Use this file to discover all available pages before exploring further.

# Common Utilities

> Here are some common utilities that you might find useful when working with Pigeon.

## `frg`

```ts frg.ts theme={null}
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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#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

```ts theme={null}
const registration = createRegistration({
  fragment: frg`
    id
    name
  `
})
// registration.fragment === "__typename id name"
```

## `dependenciesToMappedQuery`

```ts dependenciesToMappedQuery.ts theme={null}
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

```ts theme={null}
const query = `
  component {
    __typename
    ... on HeroBanner { ...${HeroBannerFragment.fragmentName} }
    ... on Asset { ...${ImageFragment.fragmentName} }
  }
`
```

### After

```ts theme={null}
const query = `
  component {
    __typename
    ${dependenciesToMappedQuery([HeroBannerFragment, ImageFragment])}
  }
`
```

## `Struct`

```ts struct.ts theme={null}
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

```ts theme={null}
type ImageStruct = Struct<typeof ImageFragment>;
```

## `resolver`

```ts resolver.ts theme={null}
import { z, type ZodError, type ZodSchema } from "zod";
// IMPORTANT: Replace with YOUR client
import { 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

```ts theme={null}
const data = await resolver({
  query: {},
  schema: z.object({...}),
  onQueryError: (error) => {...},
  onZodError: (error) => {...},
  label: "GetPageData"
})
```
