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

# Generate types

> Turn a real webhook payload into typed models from the forward TUI — pick a language, name the output, and tune inference and renderer options.

export const CliTuiTypes = ({view = "code", caption}) => {
  const W = 760;
  const u = n => `${(n / W * 100).toFixed(3)}cqw`;
  const MONO = "ui-monospace, SFMono-Regular, Menlo, monospace";
  const C = {
    bg: "var(--fd-surface)",
    chip: "var(--fd-node-bg)",
    chipBorder: "var(--fd-node-border)",
    border: "var(--fd-surface-border)",
    accent: "var(--fd-accent)",
    text: "var(--fd-text)",
    muted: "var(--fd-muted)",
    green: "var(--fd-ok)",
    blue: "var(--fd-accent-cool)",
    yellow: "var(--fd-warn)"
  };
  const tint = (c, pct) => `color-mix(in srgb, ${c} ${pct}%, transparent)`;
  const faint = "color-mix(in srgb, var(--fd-muted) 62%, transparent)";
  const role = {
    key: C.accent,
    str: C.green,
    kw: C.accent,
    fn: C.blue,
    num: C.yellow,
    ident: C.text,
    punct: C.muted
  };
  const languages = ["TypeScript", "TypeScript Zod", "TypeScript Effect Schema", "Python", "Go", "Java", "C#", "Rust", "Swift", "Kotlin", "JSON Schema", "JavaScript"];
  const selectedLang = 1;
  const json = [[["{", "punct"]], [['    "id"', "key"], [": ", "punct"], ['"evt_1QscWJ2eZvKYlo2C"', "str"], [",", "punct"]], [['    "type"', "key"], [": ", "punct"], ['"checkout.session.completed"', "str"], [",", "punct"]], [['    "customer_email"', "key"], [": ", "punct"], ['"ada@example.com"', "str"], [",", "punct"]], [['    "amount_total"', "key"], [": ", "punct"], ["4200", "num"], [",", "punct"]], [['    "currency"', "key"], [": ", "punct"], ['"usd"', "str"], [",", "punct"]], [['    "created_at"', "key"], [": ", "punct"], ['"2026-06-16T14:04:47Z"', "str"]], [["}", "punct"]]];
  const zod = [[["import", "kw"], [" * ", "punct"], ["as", "kw"], [" z ", "ident"], ["from", "kw"], [" ", "punct"], ['"zod"', "str"], [";", "punct"]], [], [["export", "kw"], [" ", "punct"], ["const", "kw"], [" WebhookPayloadSchema", "ident"], [" = ", "punct"], ["z", "ident"], [".", "punct"], ["object", "fn"], ["({", "punct"]], [['    "id"', "key"], [": ", "punct"], ["z", "ident"], [".", "punct"], ["string", "fn"], ["().", "punct"], ["optional", "fn"], ["(),", "punct"]], [['    "type"', "key"], [": ", "punct"], ["z", "ident"], [".", "punct"], ["string", "fn"], ["().", "punct"], ["optional", "fn"], ["(),", "punct"]], [['    "customer_email"', "key"], [": ", "punct"], ["z", "ident"], [".", "punct"], ["string", "fn"], ["().", "punct"], ["optional", "fn"], ["(),", "punct"]], [['    "amount_total"', "key"], [": ", "punct"], ["z", "ident"], [".", "punct"], ["number", "fn"], ["().", "punct"], ["optional", "fn"], ["(),", "punct"]], [['    "currency"', "key"], [": ", "punct"], ["z", "ident"], [".", "punct"], ["string", "fn"], ["().", "punct"], ["optional", "fn"], ["(),", "punct"]], [['    "created_at"', "key"], [": ", "punct"], ["z", "ident"], [".", "punct"], ["coerce", "fn"], [".", "punct"], ["date", "fn"], ["().", "punct"], ["optional", "fn"], ["(),", "punct"]], [["});", "punct"]], [["export", "kw"], [" ", "punct"], ["type", "kw"], [" WebhookPayload", "ident"], [" = ", "punct"], ["z", "ident"], [".", "punct"], ["infer", "fn"], ["<", "punct"], ["typeof", "kw"], [" WebhookPayloadSchema", "ident"], [">;", "punct"]]];
  const options = [{
    label: "Make all properties optional",
    on: true
  }, {
    label: "Detect date & times",
    on: true
  }, {
    label: "Detect enums",
    on: true
  }, {
    label: "Detect maps (merge similar classes)",
    on: true
  }, {
    label: "Don't treat $ref as reference",
    on: false
  }, {
    label: "Combine similar classes",
    on: true
  }];
  const codeLine = (tokens, n, blank) => <div key={n} style={{
    display: "flex",
    gap: u(10),
    whiteSpace: "nowrap",
    lineHeight: 1.5
  }}>
      <span style={{
    color: faint,
    width: u(16),
    textAlign: "right",
    flex: "0 0 auto",
    userSelect: "none"
  }}>{n}</span>
      <span style={{
    minWidth: 0,
    overflow: "hidden",
    textOverflow: "ellipsis",
    whiteSpace: "pre"
  }}>
        {blank ? " " : tokens.map((t, i) => <span key={i} style={{
    color: role[t[1]]
  }}>{t[0]}</span>)}
      </span>
    </div>;
  const pane = (title, body) => <div style={{
    display: "flex",
    flexDirection: "column",
    gap: u(10),
    minWidth: 0,
    overflow: "hidden"
  }}>
      <span style={{
    color: C.text,
    fontWeight: 700
  }}>{title}</span>
      <div style={{
    display: "flex",
    flexDirection: "column"
  }}>{body}</div>
    </div>;
  const leftPane = view === "options" ? pane("Options", <div style={{
    display: "flex",
    flexDirection: "column",
    gap: u(7)
  }}>
          {options.map((o, i) => <div key={i} style={{
    display: "flex",
    alignItems: "center",
    gap: u(8),
    color: i === 0 ? C.text : C.muted,
    whiteSpace: "nowrap"
  }}>
              <span style={{
    width: u(10),
    color: C.accent
  }}>{i === 0 ? "▸" : ""}</span>
              <span style={{
    color: o.on ? C.green : C.muted,
    fontWeight: 700
  }}>{o.on ? "[x]" : "[ ]"}</span>
              <span>{o.label}</span>
            </div>)}
        </div>) : pane("Request Body", json.map((t, i) => codeLine(t, i + 1, t.length === 0)));
  const rightPane = pane("TypeScript Zod", zod.map((t, i) => codeLine(t, i + 1, t.length === 0)));
  const footer = view === "options" ? [["←/→", "language"], ["↑/↓", "navigate"], ["space", "toggle"], ["enter", "edit"], ["o", "close options"], ["esc", "close"]] : [["←/→", "language"], ["↑/↓", "scroll"], ["tab", "name"], ["o", "options"], ["c", "copy"], ["esc", "close"]];
  return <div className="not-prose" style={{
    width: "100%"
  }}>
      {caption && <div style={{
    display: "flex",
    alignItems: "center",
    gap: "8px",
    marginBottom: "12px",
    fontSize: "11px",
    fontWeight: 600,
    letterSpacing: "0.04em",
    textTransform: "uppercase",
    color: "var(--fd-muted)"
  }}>
          <span className="fd-pulse" style={{
    width: "8px",
    height: "8px",
    background: "var(--fd-accent)"
  }} />
          {caption}
        </div>}

      <div style={{
    containerType: "inline-size",
    width: "100%",
    aspectRatio: "760 / 500",
    boxSizing: "border-box",
    display: "flex",
    flexDirection: "column",
    padding: u(18),
    fontFamily: MONO,
    fontSize: u(11),
    color: C.text,
    background: C.bg,
    border: `1px solid ${C.border}`,
    overflow: "hidden"
  }}>
        {}
        <div style={{
    display: "flex",
    flexDirection: "column",
    gap: u(6),
    padding: `${u(9)} ${u(13)}`,
    border: `1px solid ${C.chipBorder}`,
    marginBottom: u(14)
  }}>
          <div style={{
    display: "flex",
    alignItems: "center",
    gap: u(9)
  }}>
            <span className="fd-pulse" style={{
    width: u(7),
    height: u(7),
    borderRadius: "9999px",
    background: C.green,
    flex: "0 0 auto"
  }} />
            <span style={{
    color: C.muted
  }}>Listening on</span>
            <span style={{
    padding: `${u(1)} ${u(6)}`,
    color: C.text,
    background: C.chip,
    border: `1px solid ${C.chipBorder}`
  }}>https://nuvera-dev.bucket.repost.sh</span>
          </div>
        </div>

        {}
        <div style={{
    flex: 1,
    display: "flex",
    flexDirection: "column",
    gap: u(12),
    padding: u(16),
    border: `1px solid ${tint(C.accent, 55)}`,
    minHeight: 0
  }}>
          <span style={{
    color: C.accent,
    fontWeight: 700
  }}>Generate Types</span>

          <div style={{
    display: "flex",
    alignItems: "center",
    gap: u(8),
    whiteSpace: "nowrap"
  }}>
            <span style={{
    color: C.text,
    fontWeight: 700
  }}>Type name:</span>
            <span style={{
    color: C.muted
  }}>&gt;</span>
            <span style={{
    color: C.text
  }}>WebhookPayload</span>
            <span style={{
    color: faint
  }}>(tab to edit)</span>
          </div>

          {}
          <div style={{
    display: "flex",
    alignItems: "center",
    gap: u(12),
    whiteSpace: "nowrap",
    overflow: "hidden"
  }}>
            {languages.map((l, i) => i === selectedLang ? <span key={i} style={{
    display: "inline-flex",
    alignItems: "center",
    gap: u(6),
    padding: `${u(1)} ${u(7)}`,
    color: C.accent,
    fontWeight: 700,
    background: tint(C.accent, 18)
  }}>
                  <span>{"◀"}</span><span>{l}</span><span>{"▶"}</span>
                </span> : <span key={i} style={{
    color: C.muted
  }}>{l}</span>)}
            <span style={{
    color: C.muted
  }}>{"▶"}</span>
          </div>

          <div style={{
    height: "1px",
    background: C.border
  }} />

          {}
          <div style={{
    flex: 1,
    display: "grid",
    gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)",
    gap: u(22),
    minHeight: 0,
    overflow: "hidden"
  }}>
            {leftPane}
            {rightPane}
          </div>

          <div style={{
    height: "1px",
    background: C.border
  }} />

          {}
          <div style={{
    display: "flex",
    alignItems: "center",
    gap: u(16),
    whiteSpace: "nowrap",
    overflow: "hidden"
  }}>
            {footer.map(([k, d]) => <span key={k} style={{
    display: "inline-flex",
    gap: u(5)
  }}>
                <span style={{
    color: C.text,
    fontWeight: 700
  }}>{k}</span>
                <span style={{
    color: faint
  }}>{d}</span>
              </span>)}
          </div>
        </div>
      </div>
    </div>;
};

In a `repost forward` session, select an event with a JSON body and press `t` to open the **Generate Types** view. It turns the captured request body into typed models in the language of your choice — generated from real traffic, not a hand-written schema.

<CliTuiTypes />

The view is a live split: the captured request body on the left, the generated code on the right. Switch languages with `←` / `→` and the code regenerates from the same payload.

## Supported languages

Cycle through targets with the windowed selector. Each language renders the same payload in its idiomatic shape:

<Columns cols={3} className="gap-y-4">
  <Card title="TypeScript" icon="braces">
    Plain `interface` / `type`, `Zod` schemas, or `Effect Schema`.
  </Card>

  <Card title="JavaScript & JSON" icon="file-json">
    JavaScript with JSDoc, or a JSON Schema document.
  </Card>

  <Card title="Backend languages" icon="server">
    Python, Go, Java, C#, Rust, Swift, and Kotlin.
  </Card>
</Columns>

## Name the output

The generated root type is named `WebhookPayload` by default. Press `tab` to edit the **Type name**, then `enter` to commit — the code regenerates with the new name applied to every reference.

```ts theme={"languages":{"custom":["/languages/repost.json"]}}
// Type name: OrderCreated
export const OrderCreatedSchema = z.object({ /* ... */ });
export type OrderCreated = z.infer<typeof OrderCreatedSchema>;
```

## Inference options

Press `o` to open the options panel. The first group controls how the payload is **interpreted**, and these choices persist as you switch languages.

<CliTuiTypes view="options" />

| Option                              | Default | Effect                                                           |
| ----------------------------------- | ------- | ---------------------------------------------------------------- |
| Make all properties optional        | Off     | Marks every field optional instead of inferring required fields. |
| Detect date & times                 | On      | Recognises ISO date/time strings as date types.                  |
| Detect enums                        | On      | Infers enums from repeated string values.                        |
| Detect maps (merge similar classes) | On      | Treats uniform objects as maps rather than fixed shapes.         |
| Don't treat \$ref as reference      | Off     | Inlines `$ref` instead of resolving it as a reference.           |
| Combine similar classes             | On      | Merges objects with the same shape into one type.                |

Toggle a row with `space`; the code regenerates immediately.

## Language options

Below the inference group, each language exposes its own **renderer** options — booleans, value pickers (`◀ ▶`), and editable text fields. These are specific to the selected language and reset when you switch, so a TypeScript-only option does not carry over to Go.

## Keyboard

| Key                    | Action                                          |
| ---------------------- | ----------------------------------------------- |
| `t`                    | Open Generate Types for the selected event.     |
| `←` / `→` or `h` / `l` | Switch language.                                |
| `tab`                  | Edit the type name.                             |
| `o`                    | Open or close the options panel.                |
| `↑` / `↓`              | Scroll the code, or navigate options when open. |
| `space`                | Toggle or cycle the focused option.             |
| `enter`                | Edit a text option, or commit an edited value.  |
| `c`                    | Copy the generated code.                        |
| `esc`                  | Close the view.                                 |

<Note>
  Your preferred language and option choices are saved to the CLI config directory and restored on the next session. See [Authentication & configuration](/docs/cli/authentication#configuration-location) for where that lives.
</Note>
