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

# TypeScript Code Generation

> How the generated package lands in node_modules, what it contains, custom output paths for monorepos, and the version handshake.

export const RepostHighlight = () => {
  useEffect(() => {
    const colors = {
      comment: ['#116329', '#6A9955'],
      constant: ['#0550AE', '#9CDCFE'],
      keyword: ['#CF222E', '#569CD6'],
      name: ['#953800', '#4EC9B0'],
      string: ['#0A3069', '#CE9178'],
      type: ['#0550AE', '#4EC9B0'],
      variable: ['#1F2328', '#9CDCFE']
    };
    const tokenPattern = /"[^"\n]*"|@[A-Za-z_]\w*|\b(?:String|Int|Float|Boolean|DateTime|Json)\b|\b(?:true|false)\b|\b\d+(?:\.\d+)?\b|\b(?:now|uuid|cuid)(?=\s*\()|\b[A-Z]\w*\b|\b[A-Za-z_]\w*\b/g;
    const append = (line, text, color) => {
      if (!text) return;
      if (!color) {
        line.append(text);
        return;
      }
      const span = document.createElement('span');
      span.textContent = text;
      span.style.color = color[0];
      span.style.setProperty('--shiki-dark', color[1]);
      line.append(span);
    };
    const commentStart = text => {
      let quoted = false;
      for (let index = 0; index < text.length - 1; index += 1) {
        if (text[index] === '"' && text[index - 1] !== '\\') quoted = !quoted;
        if (!quoted && text[index] === '/' && text[index + 1] === '/') return index;
      }
      return -1;
    };
    const highlightCode = code => {
      if (code.dataset.repostHighlighted !== undefined || code.querySelector('span[style*="--shiki-dark"]')) {
        return;
      }
      code.dataset.repostHighlighted = '';
      const source = code.textContent ?? '';
      const fragment = document.createDocumentFragment();
      for (const text of source.split('\n')) {
        const line = document.createElement('span');
        line.className = 'line';
        const declaration = text.match(/^(\s*)(generator|model|enum|type|event)(\s+)([A-Za-z_]\w*)(.*)$/);
        if (declaration) {
          append(line, declaration[1]);
          append(line, declaration[2], colors.keyword);
          append(line, declaration[3]);
          append(line, declaration[4], colors.name);
          append(line, declaration[5]);
        } else {
          const start = commentStart(text);
          const codeText = start === -1 ? text : text.slice(0, start);
          const leading = codeText.match(/^(\s*)([A-Za-z_]\w*)(?=\s+(?:@|[A-Z]))/);
          const bare = codeText.match(/^(\s*)([A-Za-z_]\w*)(\s*)$/);
          let offset = 0;
          for (const match of codeText.matchAll(tokenPattern)) {
            append(line, codeText.slice(offset, match.index));
            const token = match[0];
            let color = colors.constant;
            if (token.startsWith('"')) color = colors.string; else if (token.startsWith('@') || (/^(now|uuid|cuid)$/).test(token)) color = colors.name; else if ((/^(String|Int|Float|Boolean|DateTime|Json)$/).test(token)) color = colors.type; else if (leading && match.index === leading[1].length) color = colors.variable; else if (bare && match.index === bare[1].length) color = colors.constant; else if ((/^[A-Z]/).test(token)) color = colors.name; else if ((/^[A-Za-z_]/).test(token)) color = colors.variable;
            append(line, token, color);
            offset = match.index + token.length;
          }
          append(line, codeText.slice(offset));
          if (start !== -1) append(line, text.slice(start), colors.comment);
        }
        fragment.append(line, '\n');
      }
      code.replaceChildren(fragment);
    };
    const highlightRepostBlocks = () => {
      for (const block of document.querySelectorAll('.code-block')) {
        const code = block.querySelector('pre code');
        const filename = block.querySelector('[data-component-part="code-block-header-filename"] [title$=".repost"]');
        const looksLikeRepost = (/^\s*(generator|model|enum|type|event)\s+[A-Za-z_]\w*\s*\{/m).test(code?.textContent ?? '');
        if (code && (filename || looksLikeRepost)) highlightCode(code);
      }
    };
    let frame;
    const schedule = () => {
      if (frame !== undefined) return;
      frame = requestAnimationFrame(() => {
        frame = undefined;
        highlightRepostBlocks();
      });
    };
    const observer = new MutationObserver(schedule);
    observer.observe(document.documentElement, {
      childList: true,
      subtree: true
    });
    schedule();
    return () => {
      observer.disconnect();
      if (frame !== undefined) cancelAnimationFrame(frame);
    };
  }, []);
  return null;
};

<RepostHighlight />

Generation emits a complete, precompiled npm package. By default it is written to `node_modules/.repost/client`, and `@repost/client` re-exports it, so application imports stay stable while the generated code stays out of your repo. This is the same pattern Prisma uses for `@prisma/client`.

## What gets emitted

| File                         | Purpose                                                                                                                      |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `package.json`               | The generated package manifest. Its name is `repost-client-` followed by a content hash, and it depends on `@repost/client`. |
| `index.js`, `index.mjs`      | Precompiled CommonJS and ESM entries: enum objects, descriptors, and `createRepostClient`.                                   |
| `index.d.ts`                 | The types: model interfaces, enums, the `webhooks` method tree, and the factory signature.                                   |
| `default.js`, `default.d.ts` | The shim `@repost/client` re-exports through.                                                                                |
| `schema.repost`              | A verbatim copy of the schema this package was generated from.                                                               |
| `schemas.json`               | The JSON Schema catalog for your event types, identical across every generator in the schema.                                |

The generated code is data and types only. Serialization, retries, and transport live in the `@repost/client` runtime, which the generated package imports from `@repost/client/runtime`.

Models become interfaces, optional fields become `field?: T | null`, and enums become a const object plus a type:

```ts theme={"languages":{"custom":["/languages/repost.json"]}}
export const Currency: {
  readonly USD: "USD";
  readonly EUR: "EUR";
};
export type Currency = (typeof Currency)[keyof typeof Currency];

export interface User {
  id: string;
  email: string;
}
```

## Regenerating

`node_modules` is not committed, so a fresh clone or a CI job with a restored dependency cache has no generated client yet. `@repost/client`'s install hook leaves a placeholder whose only job is to fail with a clear message instead of a raw module-resolution error:

```text theme={"languages":{"custom":["/languages/repost.json"]}}
@repost/client did not initialize yet. Please run "repost schema generate" and try to import it again.
```

Run generation in your build, typically as a `postinstall` script:

```json package.json theme={"languages":{"custom":["/languages/repost.json"]}}
{
  "scripts": {
    "postinstall": "repost schema generate"
  }
}
```

`repost schema migrate dev` also regenerates as part of recording a migration, and `repost schema generate --check` verifies in CI that nothing has drifted without writing.

Generation only ever overwrites directories it recognizes as its own output (by the `repost-client-` package-name prefix or the generation marker), so a mistyped path cannot destroy your code.

## Custom output for monorepos

Set `output` to write the identical package to a path instead, and import it directly:

```repost repost/schema.repost theme={"languages":{"custom":["/languages/repost.json"]}}
generator sdk {
  language = "typescript"
  output   = "../../packages/repost-events/src/generated"
}
```

The package still depends on `@repost/client` for its runtime. This is the pattern for workspaces where several apps consume one generated client, and for Yarn PnP layouts that have no `node_modules` to generate into. A schema can carry several TypeScript generators with distinct outputs; see [polyglot monorepos](/docs/send/monorepos).

## The version handshake

The generated package declares descriptor format 2, checked when `createRepostClient` builds the method tree:

* Generated code older than the runtime: the error tells you to re-run `repost schema generate` with the current CLI.
* Generated code newer than the runtime: the error tells you to upgrade `@repost/client`.
* Generated code that predates versioning: accepted with a one-time console warning asking you to regenerate.

A real mismatch fails at construction, never silently at send time.

## Continue

<Columns cols={3} className="gap-y-4">
  <Card title="Configuration" icon="sliders-horizontal" href="/docs/send/typescript/configuration" cta="Configure" arrow="true">
    Client options, transport tuning, and injectable generators.
  </Card>

  <Card title="Polyglot monorepos" icon="git-merge" href="/docs/send/monorepos" cta="Monorepos" arrow="true">
    Generate this client alongside other languages from one schema.
  </Card>

  <Card title="Schema workflow" icon="terminal" href="/docs/cli/schema" cta="Commands" arrow="true">
    The commands behind init, generate, and migrate.
  </Card>
</Columns>
