@workos/oagen 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ Three stages transform an OpenAPI spec into SDK files:
10
10
 
11
11
  1. **Parse** (`src/parser/parse.ts`) — Load and bundle the spec via `@redocly/openapi-core`, extract `Model[]`, `Enum[]`, and `Service[]` into the IR (`ApiSpec`). Sub-modules handle ref resolution (`refs.ts`), schema walking (`schemas.ts`), operation grouping (`operations.ts`), pagination detection (`pagination.ts`), and inline model extraction (`inline-models.ts`).
12
12
 
13
- 2. **Emit** (`src/engine/orchestrator.ts`) — Call each emitter method in order (`generateModels` → `generateEnums` → `generateResources` → `generateClient` → `generateErrors` → `generateTypeSignatures` → `generateTests` → `generateManifest`), collect `GeneratedFile[]`, prepend file headers.
13
+ 2. **Emit** (`src/engine/orchestrator.ts`) — Call each emitter method in order (`generateModels` → `generateEnums` → `generateResources` → `generateClient` → `generateErrors` → `generateTypeSignatures` → `generateTests`), then call `buildOperationsMap` to produce the operations map (stored in `.oagen-manifest.json`), collect `GeneratedFile[]`, prepend file headers.
14
14
 
15
15
  3. **Write** (`src/engine/writer.ts`) — Write files to disk. New files are created in full. Existing files are merged at the AST level via `merger.ts` (additive-only — new symbols appended, existing symbols untouched). Files marked `skipIfExists` are never overwritten.
16
16
 
@@ -19,7 +19,7 @@ interface Emitter {
19
19
  generateErrors(ctx: EmitterContext): GeneratedFile[];
20
20
  generateTypeSignatures?(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[];
21
21
  generateTests(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[];
22
- generateManifest?(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[];
22
+ buildOperationsMap?(spec: ApiSpec, ctx: EmitterContext): OperationsMap;
23
23
  fileHeader(): string;
24
24
  }
25
25
  ```
@@ -85,7 +85,7 @@ When a user passes `--api-surface` to `oagen generate`, the engine builds an `Ov
85
85
  | `modelNameByIR` | `Map<string, string>` | IR model name → SDK interface name (auto-inferred from field structure) |
86
86
  | `fileBySymbol` | `Map<string, string>` | IR symbol name → relative file path in the live SDK |
87
87
 
88
- The `httpKeyByMethod` reverse map is only populated when a manifest (`smoke-manifest.json`) is available. Without it, method-level violations cannot be auto-patched in the self-correcting loop. Emitters that support compat verification should implement `generateManifest`.
88
+ The `httpKeyByMethod` reverse map is only populated when an `operations` map is present in `.oagen-manifest.json`. Without it, method-level violations cannot be auto-patched in the self-correcting loop. Emitters that support compat verification should implement `buildOperationsMap`.
89
89
 
90
90
  ## Rules
91
91
 
@@ -97,7 +97,7 @@ The `httpKeyByMethod` reverse map is only populated when a manifest (`smoke-mani
97
97
 
98
98
  4. **Tests include fixtures** — `generateTests` should internally call fixture generation and return both test files and fixture files in the combined `GeneratedFile[]`.
99
99
 
100
- 5. **Inapplicable methods return `[]`** — If a language doesn't need type signature files, `generateTypeSignatures` returns `[]`. Optional methods like `generateManifest` can simply be omitted.
100
+ 5. **Inapplicable methods return `[]`** — If a language doesn't need type signature files, `generateTypeSignatures` returns `[]`. Optional methods like `buildOperationsMap` can simply be omitted.
101
101
 
102
102
  6. **Composable generators** — Interface methods can compose multiple internal generators when a language needs them.
103
103
 
@@ -63,7 +63,7 @@ generateClient(spec, ctx); // HTTP client
63
63
  generateErrors(ctx); // Error hierarchy
64
64
  generateTypeSignatures(spec, ctx); // Type annotations (optional)
65
65
  generateTests(spec, ctx); // Tests + fixtures
66
- generateManifest?.(spec, ctx); // Smoke-test manifest (optional)
66
+ buildOperationsMap?.(spec, ctx); // Operations map for .oagen-manifest.json (optional)
67
67
  ```
68
68
 
69
69
  ### OperationPlan
package/docs/cli.md CHANGED
@@ -10,17 +10,16 @@ Generate SDK code from an OpenAPI spec.
10
10
  oagen generate --spec openapi.yml --lang node --output ./sdk --namespace WorkOS
11
11
  ```
12
12
 
13
- | Argument | Required | Default | Description |
14
- | ---------------------- | -------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
- | `--spec <path>` | No | `OPENAPI_SPEC_PATH` env | Path to an OpenAPI 3.x spec file. |
16
- | `--lang <language>` | Yes | | Target language — must have a registered emitter (via `oagen.config.ts`) |
17
- | `--output <dir>` | Yes | | Directory to write generated files into |
18
- | `--namespace <name>` | No | Spec's `info.title` | SDK namespace in PascalCase — used directly for code identifiers (e.g., `WorkOS::Client`) and converted to `snake_case` for file paths (e.g., `lib/work_os/`). Use PascalCase with the exact casing you want (e.g., `WorkOS` not `work_os`). |
19
- | `--dry-run` | No | `false` | Print the list of file paths that would be generated, without writing anything to disk |
20
- | `--api-surface <path>` | No | | Path to baseline API surface JSON for compat overlay |
21
- | `--manifest <path>` | No | `<output>/smoke-manifest.json` | Path to smoke-manifest.json for method overlay (auto-discovered in output dir if present) |
22
- | `--target <dir>` | No | | Target directory for live SDK integration — generated files are merged into this directory instead of `--output` |
23
- | `--no-compat-check` | No | `false` | Skip compat overlay even if `--api-surface` is provided |
13
+ | Argument | Required | Default | Description |
14
+ | ---------------------- | -------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
+ | `--spec <path>` | No | `OPENAPI_SPEC_PATH` env | Path to an OpenAPI 3.x spec file. |
16
+ | `--lang <language>` | Yes | | Target language — must have a registered emitter (via `oagen.config.ts`) |
17
+ | `--output <dir>` | Yes | | Directory to write generated files into |
18
+ | `--namespace <name>` | No | Spec's `info.title` | SDK namespace in PascalCase — used directly for code identifiers (e.g., `WorkOS::Client`) and converted to `snake_case` for file paths (e.g., `lib/work_os/`). Use PascalCase with the exact casing you want (e.g., `WorkOS` not `work_os`). |
19
+ | `--dry-run` | No | `false` | Print the list of file paths that would be generated, without writing anything to disk |
20
+ | `--api-surface <path>` | No | | Path to baseline API surface JSON for compat overlay |
21
+ | `--target <dir>` | No | | Target directory for live SDK integration — generated files are merged into this directory instead of `--output` |
22
+ | `--no-compat-check` | No | `false` | Skip compat overlay even if `--api-surface` is provided |
24
23
 
25
24
  ## `oagen diff`
26
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workos/oagen",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Framework for building OpenAPI SDK emitters",
5
5
  "author": "WorkOS",
6
6
  "license": "MIT",
@@ -220,7 +220,7 @@ export const {language}Emitter: Emitter = {
220
220
  generateConfig(ctx) { return generateConfig(ctx); },
221
221
  generateTypeSignatures(spec, ctx) { return generateTypeSignatures(spec, ctx); },
222
222
  generateTests(spec, ctx) { return generateTests(spec, ctx); },
223
- generateManifest(spec, ctx) { return generateManifest(spec, ctx); },
223
+ buildOperationsMap(spec, ctx) { return buildOperationsMap(spec, ctx); },
224
224
  fileHeader() { return "{language-appropriate auto-generated file header}"; },
225
225
  };
226
226
  ```
@@ -323,9 +323,9 @@ Patterns replicated: (Scenario A only)
323
323
  Model / Enum / Resource / Client / Error / Serialization / Pagination / Testing / Barrel exports
324
324
  ```
325
325
 
326
- ## Smoke Manifest Generation (required)
326
+ ## Operations Map (required)
327
327
 
328
- Every emitter **must** implement `generateManifest`. The smoke test runner uses the manifest to resolve SDK methods from HTTP operations — without it, the smoke test cannot find methods and most operations will be skipped.
328
+ Every emitter **must** implement `buildOperationsMap`. The smoke test runner uses the operations map (stored in `.oagen-manifest.json`) to resolve SDK methods from HTTP operations — without it, the smoke test cannot find methods and most operations will be skipped.
329
329
 
330
330
  The manifest maps every `HTTP_METHOD /path` to `{ sdkMethod, service }`:
331
331
 
@@ -342,25 +342,22 @@ The manifest maps every `HTTP_METHOD /path` to `{ sdkMethod, service }`:
342
342
  }
343
343
  ```
344
344
 
345
- The emitter already knows every operation→method mapping (from `resolveMethodName` or the equivalent). `generateManifest` simply serializes that mapping to `smoke-manifest.json` as a `GeneratedFile`. The orchestrator calls `emitter.generateManifest?.(spec, ctx)` and writes the result alongside the SDK output.
345
+ The emitter already knows every operation→method mapping (from `resolveMethodName` or the equivalent). `buildOperationsMap` returns this mapping as an `OperationsMap` object. The orchestrator calls `emitter.buildOperationsMap?.(spec, ctx)` and writes the result into the `operations` field of `.oagen-manifest.json`.
346
346
 
347
347
  **Implementation pattern** (`manifest.ts`):
348
348
 
349
349
  ```typescript
350
- export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
351
- const manifest: Record<string, { sdkMethod: string; service: string }> = {};
350
+ export function buildOperationsMap(spec: ApiSpec, ctx: EmitterContext): OperationsMap {
351
+ const operations: OperationsMap = {};
352
352
  for (const service of spec.services) {
353
353
  const propName = /* service property name on the client */;
354
354
  for (const op of service.operations) {
355
355
  const httpKey = `${op.httpMethod.toUpperCase()} ${op.path}`;
356
356
  const methodName = /* resolved method name for this operation */;
357
- manifest[httpKey] = { sdkMethod: methodName, service: propName };
357
+ operations[httpKey] = { sdkMethod: methodName, service: propName };
358
358
  }
359
359
  }
360
- return [{
361
- path: "smoke-manifest.json",
362
- content: JSON.stringify(manifest, null, 2),
363
- }];
360
+ return operations;
364
361
  }
365
362
  ```
366
363
 
@@ -66,9 +66,9 @@ Map IR service names to SDK resource accessors. There are two sources of truth d
66
66
 
67
67
  ### Generated SDK (preferred)
68
68
 
69
- For emitter-generated SDKs, the `smoke-manifest.json` (produced by the emitter's `generateManifest` hook) contains the authoritative `service` field for every operation. The smoke test should load this manifest and use its service mappings. The `SERVICE_MAP` only needs fallback entries for services not covered by the manifest.
69
+ For emitter-generated SDKs, the `operations` field in `.oagen-manifest.json` (produced by the emitter's `buildOperationsMap` hook) contains the authoritative `service` field for every operation. The smoke test should load this manifest and use its service mappings. The `SERVICE_MAP` only needs fallback entries for services not covered by the manifest.
70
70
 
71
- If no manifest exists, warn the user: the emitter should implement `generateManifest`. Without it, most operations will be skipped because heuristic method resolution fails on disambiguated names.
71
+ If no operations map exists, warn the user: the emitter should implement `buildOperationsMap`. Without it, most operations will be skipped because heuristic method resolution fails on disambiguated names.
72
72
 
73
73
  ### Live/hand-written SDK
74
74
 
@@ -93,7 +93,7 @@ Each language's SDK will have different accessor names — discover them by read
93
93
 
94
94
  Adapt the 4-tier resolution to the target language's naming conventions:
95
95
 
96
- 0. **Manifest match** — Load `smoke-manifest.json` from the SDK output directory (emitter-generated, not hand-maintained). This is the **primary** resolution path for generated SDKs. The manifest maps every `HTTP_METHOD /path` to `{ sdkMethod, service }` and is produced by the emitter's `generateManifest` hook. If the manifest is missing, warn and fall through to heuristic tiers.
96
+ 0. **Manifest match** — Load the `operations` map from `.oagen-manifest.json` in the SDK output directory (emitter-generated, not hand-maintained). This is the **primary** resolution path for generated SDKs. The manifest maps every `HTTP_METHOD /path` to `{ sdkMethod, service }` and is produced by the emitter's `buildOperationsMap` hook. If the operations map is missing, warn and fall through to heuristic tiers.
97
97
  1. **Exact match** — IR operation name converted to target convention
98
98
  2. **CRUD prefix match** — standard verbs (create, list, retrieve/get, update, delete) with service name tiebreaker
99
99
  3. **Keyword fuzzy match** — stem words and score overlap
@@ -123,7 +123,7 @@ Create the script **in the emitter project**:
123
123
  3. `main()` function:
124
124
  - Parse CLI args, validate API key
125
125
  - Parse spec via `parseSpec()`
126
- - Load manifest from `{sdk-path}/smoke-manifest.json` (emitter-generated). If missing, log a warning — method resolution will rely on heuristic tiers and most operations will likely be skipped.
126
+ - Load operations map from `{sdk-path}/.oagen-manifest.json` (emitter-generated). If missing, log a warning — method resolution will rely on heuristic tiers and most operations will likely be skipped.
127
127
  - Load and configure the SDK
128
128
  - Iterate `planOperations()` groups
129
129
  - For each operation: resolve SDK method, resolve path params, build args, call SDK, capture exchange
@@ -167,11 +167,11 @@ During initial setup, run `oagen generate` then the smoke test until skips are m
167
167
 
168
168
  ```bash
169
169
  oagen generate --lang {lang} --output {sdk-path} --spec {spec} --namespace {ns}
170
- # The emitter produces smoke-manifest.json alongside the SDK — the smoke test loads it automatically.
170
+ # The emitter writes the operations map into .oagen-manifest.json — the smoke test loads it automatically.
171
171
  oagen verify --lang {lang} --output {sdk-path} --spec {spec}
172
172
  ```
173
173
 
174
- If many operations are skipped with "No matching SDK method", check that the emitter's `generateManifest` is implemented and that the manifest file is being written to the SDK output directory. The manifest is the primary mechanism the smoke test uses to find SDK methods.
174
+ If many operations are skipped with "No matching SDK method", check that the emitter's `buildOperationsMap` is implemented and that `.oagen-manifest.json` contains an `operations` field. The operations map is the primary mechanism the smoke test uses to find SDK methods.
175
175
 
176
176
  | Exit | Meaning | Output | Action |
177
177
  | ---- | ------------- | --------------------------- | --------------------------------------- |
@@ -32,7 +32,7 @@ Determine required paths:
32
32
  - **Emitter project** (`project`): from argument, or detect from current directory
33
33
  - **Output directory** (`output`): use a temp directory or the emitter project's `sdk/` directory
34
34
  - **API surface** (`api_surface`): `sdk-{language}-surface.json` in the emitter project
35
- - **Manifest** (`manifest`): `smoke-manifest.json` in the output directory (if it exists)
35
+ - **Manifest** (`manifest`): `.oagen-manifest.json` in the output directory (contains file list and operations map)
36
36
 
37
37
  ## Step 2: Dry-Run Preview
38
38