# Minimal Quickstart

This guide takes you from an OpenAPI spec to a tiny working emitter with the fewest moving parts possible.

## What You Will Build

- parse an OpenAPI spec into oagen's IR
- scaffold an emitter project
- replace the stub emitter with a minimal implementation
- generate a few files

This quickstart intentionally ignores advanced features like API-surface extraction, compatibility overlays, smoke verification, and live-SDK integration.

## 1. Install oagen

```bash
npm install @workos/oagen
```

## 2. Inspect Your Spec

```bash
oagen parse --spec openapi.yml
```

This prints the parsed IR as JSON. Before writing an emitter, confirm the parser is extracting the services, models, and enums you expect.

## 3. Scaffold an Emitter Project

```bash
oagen init --lang demo --project ./demo-emitter
cd ./demo-emitter
npm install
```

The scaffold gives you:

- `oagen.config.ts` -- a minimal config for local development that imports the plugin bundle
- `src/plugin.ts` -- plugin bundle export registering the stub emitter
- `src/demo/index.ts` -- stub emitter implementing the `Emitter` interface
- build/test tooling
- `sdk:generate` and related package scripts

> **Note:** The scaffolded project uses the plugin model. The consumer project
> (e.g. a spec repo) imports the plugin bundle from `src/plugin.ts` and layers
> its own spec interpretation policy on top. See the
> [CLI Reference](../cli.md#configuration-oagenconfigts) for plugin
> composition examples.

## 4. Replace the Stub Emitter

Edit `src/demo/index.ts` to return a few simple files:

```ts
import type {
  ApiSpec,
  Emitter,
  Enum,
  GeneratedFile,
  Model,
  Service,
} from "@workos/oagen";

function renderSummary(spec: ApiSpec): string {
  return [
    `name: ${spec.name}`,
    `services: ${spec.services.length}`,
    `models: ${spec.models.length}`,
    `enums: ${spec.enums.length}`,
  ].join("\n");
}

function renderModels(models: Model[]): GeneratedFile[] {
  if (models.length === 0) return [];
  return [
    {
      path: "models.txt",
      content: models.map((model) => model.name).join("\n"),
    },
  ];
}

function renderEnums(enums: Enum[]): GeneratedFile[] {
  if (enums.length === 0) return [];
  return [
    {
      path: "enums.txt",
      content: enums.map((entry) => entry.name).join("\n"),
    },
  ];
}

function renderServices(services: Service[]): GeneratedFile[] {
  if (services.length === 0) return [];
  return [
    {
      path: "services.txt",
      content: services.map((service) => service.name).join("\n"),
    },
  ];
}

export const demoEmitter: Emitter = {
  language: "demo",
  generateModels: (models) => renderModels(models),
  generateEnums: (enums) => renderEnums(enums),
  generateResources: (services) => renderServices(services),
  generateClient: (spec) => [
    {
      path: "summary.txt",
      content: renderSummary(spec),
    },
  ],
  generateErrors: () => [],
  generateTypeSignatures: () => [],
  generateTests: () => [],
  fileHeader: () => "# Generated by oagen",
};
```

## 5. Generate Files

```bash
npm run sdk:generate -- --spec ../openapi.yml --namespace DemoSdk
```

Expected result:

- `sdk/summary.txt`
- `sdk/models.txt` when the spec has models
- `sdk/enums.txt` when the spec has enums
- `sdk/services.txt` when the spec has services

## 6. Grow from Here

Once the minimal loop works:

1. Replace text files with real language output.
2. Use [`planOperation`](../../src/engine/operation-plan.ts) via `@workos/oagen` to share operation decision logic.
3. Add emitter tests with fixture specs and golden files.
4. Only then consider advanced workflows like `oagen verify` or compatibility overlays.

## Next Reads

- [Reference Emitter](../../examples/reference-emitter/) — a working TypeScript emitter you can study and copy
- [Emitter Contract](../architecture/emitter-contract.md)
- [IR Type System Reference](../architecture/ir-types.md)
- [CLI Reference](../cli.md)
