> ## 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.

# Polyglot Monorepos

> One schema, many SDKs: generate clients for several languages and services from a single source of truth, with atomic writes and CI drift checks.

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 />

A schema can carry any number of `generator` blocks, one per SDK you want to emit. A monorepo with a TypeScript API, a Go payments service, and a Python analytics worker generates all three clients from the same file, so every service publishes against the same event definitions.

```repost repost/schema.repost theme={"languages":{"custom":["/languages/repost.json"]}}
generator api {
  language = "typescript"
}

generator payments {
  language = "go"
  output   = "../services/payments/internal/repostclient"
}

generator analytics {
  language = "python"
  output   = "../services/analytics/repost_sdk"
}
```

## One run, every generator

`repost schema generate` runs every generator in the schema, in source order. There is no per-generator flag on the CLI; the schema is generated as a unit. `repost schema migrate dev` does the same as part of recording a migration.

The write is transactional across all outputs. Every generator's tree is staged first, then committed together; if any output fails to write, the already-written ones are rolled back. A generate either updates every client or none of them, so two services can never end up on different versions of the schema after one run. Concurrent runs against the same tree are refused rather than raced.

Every output also receives an identical `schemas.json`, the JSON Schema catalog for your event types. The migration history is language-neutral: one lineage, one set of versions, shared by every generator.

## Output rules

* Every output path is resolved relative to the schema file, and each generator needs its own distinct directory. Two generators writing to the same path is a schema error.
* TypeScript is the only language with a default output (`node_modules/.repost/client`). One TypeScript generator may use the default; any others need explicit paths.
* Two generators can share a language. A schema with two TypeScript generators writes byte-identical packages to both outputs.
* Go and Python require an `output`. Java and Kotlin additionally require `resourceOutput` and `packageName`, as covered in their [generation](/docs/send/java/generation) pages.

Each output directory is engine-owned. The generator marks its trees with a marker file, refuses to write into a non-empty directory it doesn't recognize as its own, and replaces the whole tree on regeneration. Don't hand-edit or add files inside an output.

## What gets committed

The default TypeScript output lives in `node_modules` and stays out of your repo; a `postinstall` script regenerates it after installs. Every other output, including a TypeScript generator with an explicit path, is committed like any other generated code, and its diff shows up in review alongside the migration that caused it.

## CI

Two checks keep a polyglot repo honest:

```bash theme={"languages":{"custom":["/languages/repost.json"]}}
repost schema migrate status   # the migration lineage is clean
repost schema generate --check # every committed client matches the schema
```

`--check` recomputes every generator's output and exits non-zero on drift, listing the changed and stale files per output. Run both on every pull request.

## JVM builds generate their own subset

The Maven and Gradle plugins select generators by name — `<generators><generator>javaSdk</generator></generators>` in Maven, `repostSdk.generators` in Gradle — and process only JVM generators. In a schema that mixes `javaSdk` with `typescript` and `go`, the JVM build regenerates only its own client, and `repost schema generate` handles the rest. The two paths are complementary and never overlap.

`repost schema init` scaffolds a single generator; a polyglot schema grows by adding further `generator` blocks to it by hand. `repost schema languages` lists what's available.

## Continue

<Columns cols={3} className="gap-y-4">
  <Card title="Defining events" icon="braces" href="/docs/send/schema" cta="Schema" arrow="true">
    The generator block and everything else in the schema language.
  </Card>

  <Card title="Migrations & deploys" icon="git-branch" href="/docs/send/deployments" cta="Version" arrow="true">
    The one migration history all these generators share.
  </Card>

  <Card title="SDKs" icon="package" href="/docs/send/sdks" cta="Languages" arrow="true">
    The five language guides these generators feed.
  </Card>
</Columns>
