---
url: /reference/typescript/wow-generator/generated-output.md
description: Generated output and regeneration — @ahoo-wang/wow-generator
---

# Generated output and regeneration

Generation produces TypeScript source, not a standalone HTTP implementation. Compile decorator classes with `experimentalDecorators: true`; install the packages imported by the actual output.

## Output families

| Output                    | Generation rules                                                                                                                         |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `types.ts`                | Component schemas grouped by schema-name namespace; model types/enums and imported Wow types                                             |
| `*ApiClient.ts`           | Ordinary tagged operations with operationId; operation tags must all be eligible API tags; wow/Actuator/Wow aggregate tags excluded      |
| `commandClient.ts`        | Resolved aggregate command paths, body aliases, regular and stream command clients                                                       |
| `queryClient.ts`          | Aggregate QueryClientFactory, state/field types, domain-event union (`never` when empty) and event title enum                            |
| `boundedContext.ts`       | Context-alias constant for resolved contexts                                                                                             |
| `index.ts`                | Recursive exports for .ts files and nonempty subdirectories                                                                              |
| `.wow-generator.json`     | Version 1 ownership manifest with SHA-256 hashes of generated .ts files; an older `.fetcher-generator.json` is read once and replaced    |

## Source conventions

Every generated file starts with `// Code generated by wow-generator. DO NOT EDIT.` and uses single quotes, two-space indentation and semicolons. Relative imports end in `.js` and each barrel re-exports `./dir/index.js`, so the output compiles under `"moduleResolution": "NodeNext"` as well as `"bundler"`.

Imports are written explicitly while generating, so the output is byte-for-byte the same whether or not the output directory resolves `@ahoo-wang/*`. Before saving, the generator checks that every file declares or imports each name it uses and declares none twice; if not, nothing is written. That check does not replace the consumer compiler.

A name that two sibling modules both export, such as a model of the same name in two packages, is left out of their common `index.ts` with a warning; import it from its own module.

Model doc comments carry a summary: title, description, schema key, format, default, example and constraints. `--schema-docs full` (option `schemaDocs: 'full'`) also embeds the complete JSON schema.

## Methods

The method name is, in order: `apiClients[tag].methodNames[operationId]` from the [configuration](./configuration), the operation's `x-fetcher-method` extension, or the last dot-separated segment of the operationId, camel-cased:

| operationId                  | Method           |
| ---------------------------- | ---------------- |
| `getUserById`                | `getUserById`    |
| `delete_user_by_id`          | `deleteUserById` |
| `getUser_1`                  | `getUser1`       |
| `users.list`                 | `list`           |
| `example.cart.add_cart_item` | `addCartItem`    |

The name depends on the operation alone, so adding an operation never renames an existing method. Two operations of one client that arrive at the same name fail the run with exit code 4, naming both operations; give one of them a name with `methodNames` or `x-fetcher-method`.

Ordinary methods take every path, query and header parameter as a typed positional parameter named after it (`item-id` → `itemId`), and the request body as its own `@body()` parameter. Required ones come first in document order - path, query, header, then the body - and optional ones follow, so a caller never passes `undefined` to reach a required one. Then come `httpRequest?: ParameterRequest`, for anything else a request may set (extra headers, a timeout, a signal), and `attributes?: Record<string, unknown>`:

```ts
search(@path('item-id') itemId: string, @query('q') q: string,
       @header('X-Tenant') xTenant: string, @query('page') page?: number,
       @query('status') status?: 'open' | 'closed',
       @request() httpRequest?: ParameterRequest,
       @attribute() attributes?: Record<string, unknown>): Promise<Item[]>
```

| Parameter or body                          | Generated type                                                                                          |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| Path parameter                             | Always required; `ignorePathParameters` leaves some out                                                 |
| Query or header parameter                  | Required when the document says `required: true`; a primitive enum is the union of its literals        |
| Cookie parameter                           | Left out with a warning: the browser sends cookies, and fetch cannot set them                           |
| JSON body (`application/json`, `+json`)    | The schema's type; properties the schema does not require stay optional through `PartialBy<Item, 'id'>` |
| `multipart/form-data`                      | `FormData`                                                                                              |
| `application/x-www-form-urlencoded`        | `URLSearchParams`                                                                                       |
| `text/*`                                   | `string`                                                                                                |
| Any other media type                       | `BodyInit`                                                                                              |

The body is required when `requestBody.required` is true. A parameter's description becomes a `@param` tag of the method's doc comment. Path-level parameters are inherited; operation parameters override matching in/name. An operation without an operationId or a tag is skipped with a warning.

The return type comes from the success response: `200`, else the lowest other 2xx, else `2XX`. Any JSON media type (`application/json`, `+json`, with or without a charset) is typed from its schema, then a wildcard schema, then SSE. `text/*`, and a string schema under a wildcard, return `Promise<string>`; recognized SSE returns JsonServerSentEventStream, with any fallback when the event model cannot be inferred. Without an inferred body, the fallback is `Promise<Response>` with native Response extraction. Generated methods rely on decorators replacing their `throw autoGeneratedError(...)` placeholder at runtime.

## Names and schemas

* Names become identifiers: parameter `item-id` → `itemId`, schema `Page«User»` → `PageUser`, `1stThing` → `_1stThing`, command `pay-order` → `PAY_ORDER` and `payOrder`. `*/` inside descriptions is escaped.
* Two schemas that normalise to the same model fail with exit code 4. Enum values that normalise alike get distinct members. Tags that name the same client generate a numbered class (`User2ApiClient`) with a warning.
* A `$ref` that points at nothing fails with exit code 4, listing the references.
* `{ nullable: true, allOf: [{ $ref }] }` admits `null`. A `oneOf` with a discriminator narrows each branch by the discriminator property. A map of its own type generates an interface with an index signature. Models named `Record` or `Response` are imported under an alias so they do not shadow the globals.
* An empty command or event body is `Record<string, never>`.

Wow query schemas map to `@ahoo-wang/wow-client` types. A `wow.api.query.ListQuery` or `PagedQuery` schema with a `filter` property (Wow 8.11 and later) maps to `FilterListQuery` or `FilterPagedQuery` from the root entry. `wow.api.query.Condition`, `ConditionOptions`, and `Operator`, and a `ListQuery` or `PagedQuery` schema without `filter` (Wow 8.10), map to the deprecated types of `@ahoo-wang/wow-client/legacy`, which is removed in v10.

## Runtime setup

```bash
pnpm add @ahoo-wang/fetcher @ahoo-wang/fetcher-decorator @ahoo-wang/fetcher-eventstream @ahoo-wang/wow-client
pnpm exec wow-generator generate -i ./openapi.json -o ./src/generated -t ./tsconfig.json
pnpm exec tsc --noEmit -p ./tsconfig.json
```

Create the generated client with its ApiMetadata constructor, typically `{ fetcher }`. Configure the Fetcher baseURL for the target server; generation does not call the generated API.

Command clients, and API clients of a document with `x-wow-context-alias`, merge the `apiMetadata` passed to the constructor over their defaults, so `new CartCommandClient({ fetcher })` keeps the bounded context's base path and sends to `/example/...`. The stream command client inherits that constructor. To reach a service directly, without a gateway that routes by context alias, pass `basePath: ''`; a query client factory takes `contextAlias: ''` instead:

```ts
import { Fetcher } from '@ahoo-wang/fetcher';
import { CartCommandClient, cartQueryClientFactory } from './generated/index.js';

const fetcher = new Fetcher({ baseURL: 'http://localhost:8080' });
const commands = new CartCommandClient({ fetcher, basePath: '' });
const snapshots = cartQueryClientFactory.createSnapshotQueryClient({
  fetcher,
  contextAlias: '',
});
```

The query client factory's `aggregateName` is the aggregate's route segment read from its snapshot routes, for example `sales-order` for the aggregate `order` declared with `@AggregateRoute(resourceName = "sales-order")`. Its fields type argument is `` `${OrderAggregatedFields}` ``, the string values of the field enum: an enum member and the matching string both compile, a misspelt field does not. Code that annotates a query as `ListQuery` or `FilterListQuery`, whose fields default to `string`, names the fields type: ``ListQuery<`${CartAggregatedFields}`>``. See [declarative endpoints](https://fetcher.ahoo.me/reference/decorator/services-and-endpoints).

## Ownership and failures

Files emitted again at the same path are replaced: keep hand-written customizations outside generated files. Stale files are deleted only if they were recorded in the prior manifest and their content hash is unchanged. Modified stale files and unrelated files are preserved; preservation does not make them part of the current generated API. Index rebuilding can still include source files present in the project.

An invalid manifest or a generated path escaping the output root throws. Saves are awaited before stale deletion and the new manifest, but the operation is not an atomic directory transaction: partial writes can remain after failure. Do not delete the manifest to force cleanup; use a dedicated output directory and review its diff after regeneration. The generator's own name check does not prove the output type-checks against your dependencies: run the consumer compiler.

## Implementation sources

[typescript/wow-generator/src/utils/sourceFiles.ts](https://github.com/Ahoo-Wang/Wow/blob/main/typescript/wow-generator/src/utils/sourceFiles.ts)

[typescript/wow-generator/src/client/apiClientGenerator.ts](https://github.com/Ahoo-Wang/Wow/blob/main/typescript/wow-generator/src/client/apiClientGenerator.ts)

[typescript/wow-generator/src/client/queryClientGenerator.ts](https://github.com/Ahoo-Wang/Wow/blob/main/typescript/wow-generator/src/client/queryClientGenerator.ts)

[typescript/wow-generator/src/model/modelGenerator.ts](https://github.com/Ahoo-Wang/Wow/blob/main/typescript/wow-generator/src/model/modelGenerator.ts)
