Skip to content

Commit e992bb3

Browse files
constantantclaude
andcommitted
docs: document new generator features and stale-cleanup fix
- Generator options tables (root README, generator README, CLAUDE.md): add verbose flag - Key properties list (root README): add cookie params, binary body, response unions, @deprecated JSDoc - Generator README anatomy sections: add GET with cookie params, deprecated operations, binary/octet-stream body, response type unions - Stale cleanup section (generator README): expand to cover index.ts barrel removal and empty tag folder deletion - Key decisions table (CLAUDE.md): update stale cleanup row; add rows for cookie params, @deprecated, response unions, binary body Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7d3b2c3 commit e992bb3

3 files changed

Lines changed: 90 additions & 6 deletions

File tree

CLAUDE.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ provideHttpClient(withInterceptors([myapiDigestAuthInterceptor])),
208208
5. `@apidevtools/swagger-parser` — dereference all `$ref` chains for endpoint extraction.
209209
6. `parseSecuritySchemes(api)` — extract security schemes into `SecuritySchemeModel[]`.
210210
7. `buildEndpoints(api, tags, convention)` — map each operation to `EndpointModel`.
211-
8. `renderTokenFile()` / `renderSecurityTokenFile()` — emit token files as strings.
212-
9. Stale file cleanup — delete any `.token.ts` / `.security-token.ts` files present before the run that weren't produced this run.
211+
8. `renderTokenFile()` / `renderSecurityTokenFile()` / `renderMockFile()` — emit token and mock files as strings.
212+
9. Stale file cleanup — snapshot `.token.ts`, `.security-token.ts`, `.mock.ts`, `mocks.manifest.json`, `index.ts`, and `index.mock.ts` files before the run; delete any that weren't produced this run.
213213
10. `@nx/devkit` `formatFiles()` — run Prettier over all written files.
214214

215215
### Schema inputs (`schema.json`)
@@ -223,7 +223,8 @@ provideHttpClient(withInterceptors([myapiDigestAuthInterceptor])),
223223
"namingConvention":"camel | kebab (default: kebab for filenames, SCREAMING_SNAKE for token names)",
224224
"providedIn": "none | root (default: none)",
225225
"includeMocks": "true | false (default: false) — emit *.mock.ts alongside each *.token.ts",
226-
"specId": "string — embedded in MockResourceMeta._meta; must match the specId used when importing the spec into the DevTools panel"
226+
"specId": "string — embedded in MockResourceMeta._meta; must match the specId used when importing the spec into the DevTools panel",
227+
"verbose": "true | false (default: false) — print +/~/- summary of created/updated/deleted files after generation"
227228
}
228229
```
229230

@@ -484,7 +485,11 @@ Conventional Commits PR title (it becomes the squash commit). See
484485
| Security tokens — digest | `InjectionToken<HttpInterceptorFn>` + named host-scoped interceptor | Challenge-response at HTTP layer; base URL token prevents cross-API interceptor conflicts |
485486
| Remote spec URL | `specPath` accepts `http://`/`https://` URLs — downloads to a temp file, then processes identically to local files | Eliminates the `curl` pre-step; temp file is cleaned up in the `finally` block regardless of success/failure |
486487
| Header params | `in: header` params become named string args (required or optional), rendered into a `headers` block alongside auth scheme headers | Consistent with how path params are surfaced; keeps the public API surface explicit and typed |
487-
| Stale file cleanup | Before generation, snapshot existing `.token.ts`/`.security-token.ts` files; after generation, delete any that weren't produced this run | Prevents phantom exports when endpoints are removed or `tagFilter` narrows the output |
488+
| Cookie params | `in: cookie` params become named string args (after header params) and are combined into a single `Cookie` header using `[...].join('; ')`; optional cookies use a conditional spread | Cookie params work in SSR (Node HttpClient); browser Cookie header is a forbidden header — document that constraint at usage time rather than in the type |
489+
| `@deprecated` JSDoc | Generator emits `/** @deprecated */` above the `InjectionToken` constant when `operation.deprecated === true` in the spec | TypeScript deprecation warning at the inject() call site; no runtime cost |
490+
| Response type unions | Generator collects all 2xx codes with `application/json` content (not just the first); emits a `\|`-union type alias when multiple codes exist | Some endpoints legitimately return 200 (update) or 201 (create) with different shapes; a union preserves that information instead of silently picking one |
491+
| Binary body | When `requestBody` has no json/form/multipart content type (e.g. `application/octet-stream`, `image/*`), the generated `Body` type alias is `Blob \| ArrayBuffer` instead of the `paths` chain | `paths[...]['requestBody']['content']['application/octet-stream']` would be `string \| Blob` from openapi-typescript — not useful for Angular's `HttpClient` which needs the actual binary object |
492+
| Stale file cleanup | Before generation, snapshot `.token.ts`, `.security-token.ts`, `.mock.ts`, `mocks.manifest.json`, `index.ts`, and `index.mock.ts` files; after generation, delete any that weren't produced this run | Prevents phantom exports when endpoints are removed or `tagFilter` narrows the output; including barrel files ensures orphaned tag folders (with their `index.ts`) are also removed |
488493
| Nx executor | `@constantant/openapi-resource-gen:generate` executor wraps the generator so users can declare a `generate` target in `project.json` | `nx run mylib:generate` is easier to remember and can be wired into CI; uses `FsTree`+`flushChanges` from `nx/src/generators/tree` (not in `@nx/devkit` public API) |
489494
| Lint cache invalidation | `@nx/eslint:lint` has an `externalDependencies` input listing the ESLint plugin packages (`eslint`, `angular-eslint`, `typescript-eslint`, `@eslint/js`, …) in `nx.json` | A rule-strengthening dependency bump (e.g. an `angular-eslint` major) must re-lint against current source, not return a stale cached "pass". Without this, the angular-eslint 22 upgrade merged green while leaving `master` failing `prefer-on-push-component-change-detection` |
490495
| `includeMocks` + MockResourceMeta | Generator emits `*.mock.ts` alongside each `*.token.ts`; each mock file exports `_meta: MockResourceMeta` with `specId`, `operationId`, `path`, `method`, `tag` | DevTools panel needs this metadata to display operation info and match mock keys to spec entries |

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ Re-run the generator command whenever your spec changes — it overwrites genera
9494
| `providedIn` | no | `none` | `none` (use `provideX()` helpers) or `root` (self-registering) |
9595
| `includeMocks` | no | `false` | Co-generate `.mock.ts` providers, `index.mock.ts` barrels, and `mocks.manifest.json` — requires `@constantant/openapi-resource-mocks` |
9696
| `specId` | no | derived | Identifier embedded in `MockResourceMeta` and `mocks.manifest.json`. Defaults to `baseUrlToken` with `_BASE_URL` stripped (e.g. `PETSTORE_BASE_URL``petstore`). Must match when importing into the DevTools panel. |
97+
| `verbose` | no | `false` | Print a `+`/`~`/`-` summary of created, updated, and deleted files after generation. |
9798

9899
See [`tools/openapi-resource-gen/README.md`](tools/openapi-resource-gen/README.md) for full documentation.
99100

@@ -148,6 +149,10 @@ Key properties of every generated file:
148149
- **Request suppression** — returning `undefined` from the lambda keeps the resource idle (no request)
149150
- **Scoped base URL** — each lib has its own `InjectionToken<string>` so different parts of an app can point at different environments
150151
- **Header params**`in: header` parameters become named string args on the factory function and are merged into the `headers` object alongside any auth scheme headers
152+
- **Cookie params**`in: cookie` parameters become named string args (after header params) and are combined into a single `Cookie` header value; optional cookies are conditionally included
153+
- **Binary body** — non-json/form/multipart request bodies (e.g. `application/octet-stream`, `image/*`) emit `Blob | ArrayBuffer` as the body type
154+
- **Response type unions** — when an endpoint returns multiple 2xx JSON response codes (e.g. 200 and 201), the generated `Response` type alias is a union of all of them
155+
- **`@deprecated` JSDoc** — operations marked `deprecated: true` in the spec emit `/** @deprecated */` above the token constant, surfacing the warning at every `inject()` call site
151156
- **Security tokens** — signal-based schemes (`bearer`, `basic`, `apiKey`) emit `InjectionToken<Signal<string | null>>`; `digest` schemes emit `InjectionToken<HttpInterceptorFn>` + a named, host-scoped interceptor that delegates only to requests matching the lib's base URL, preventing cross-API conflicts
152157

153158
---

tools/openapi-resource-gen/README.md

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Re-run the same command whenever your spec changes — the generator overwrites
8888
| `providedIn` | no | `none` | `none` or `root` — see table above |
8989
| `includeMocks` | no | `false` | Emit a `.mock.ts` per endpoint plus `index.mock.ts` barrels and a `mocks.manifest.json` — requires [`@constantant/openapi-resource-mocks`](https://www.npmjs.com/package/@constantant/openapi-resource-mocks) |
9090
| `specId` | no | derived from `baseUrlToken` | Identifier embedded in every generated `MockResourceMeta` and in `mocks.manifest.json`. Defaults to `baseUrlToken` with `_BASE_URL` stripped and lowercased (e.g. `PETSTORE_BASE_URL``petstore`). Must match the value used when importing the spec into the DevTools panel. |
91+
| `verbose` | no | `false` | Print a `+`/`~`/`-` summary of created, updated, and deleted files after generation. |
9192

9293
---
9394

@@ -218,8 +219,13 @@ are never accidentally bundled into a production build.
218219

219220
### Stale cleanup
220221

221-
Re-running the generator without `--includeMocks` deletes any `.mock.ts` files from
222-
the previous run. Removing an endpoint from the spec also removes its `.mock.ts`.
222+
Re-running the generator cleans up any file it no longer produces. This includes:
223+
- `.token.ts` / `.mock.ts` for removed endpoints
224+
- `.security-token.ts` for removed security schemes
225+
- `{tag}/index.ts` / `{tag}/index.mock.ts` barrel files when all endpoints for a tag are removed (e.g. after narrowing `--tagFilter`)
226+
- `mocks.manifest.json` when `--includeMocks` is dropped
227+
228+
Stale tag folders (left empty after their barrels are removed) are also deleted.
223229

224230
---
225231

@@ -303,6 +309,42 @@ headers: {
303309
},
304310
```
305311

312+
### GET with cookie params
313+
314+
`in: cookie` parameters become named string args placed after header params. Required
315+
cookies are required args; optional ones get `?`. All cookies for the endpoint are
316+
combined into a single `Cookie` header value at request time:
317+
318+
```typescript
319+
export const GET_CURRENT_USER = new InjectionToken<
320+
(session: string, theme?: string) => ReturnType<typeof httpResource<GetCurrentUserResponse>>
321+
>('GET_CURRENT_USER');
322+
```
323+
324+
Inside the resource the cookie header is built as an array join, so absent optional
325+
cookies are not included:
326+
327+
```typescript
328+
headers: {
329+
Cookie: [`session=${session}`, ...(theme != null ? [`theme=${theme}`] : [])].join('; '),
330+
},
331+
```
332+
333+
> **Browser note:** The `Cookie` header is a [forbidden header name](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name) — browsers silently block it. Cookie params work correctly in Angular Universal / SSR (Node's `HttpClient` has no such restriction). For browser-only apps, set cookies via `document.cookie` before making the request and pass `withCredentials: true`.
334+
335+
### Deprecated operations
336+
337+
When the spec marks an operation with `deprecated: true`, the generator emits a
338+
`/** @deprecated */` JSDoc comment immediately above the token constant:
339+
340+
```typescript
341+
/** @deprecated */
342+
export const OLD_ENDPOINT = new InjectionToken<...>('OLD_ENDPOINT');
343+
```
344+
345+
TypeScript then surfaces the deprecation warning at every `inject(OLD_ENDPOINT)` call
346+
site with no runtime cost.
347+
306348
### Mutation (POST/PUT/PATCH/DELETE)
307349

308350
The factory returns `(body: BodyType | Signal<BodyType>) => httpResource(...)`.
@@ -382,6 +424,38 @@ const submitForm = inject(SUBMIT_FORM);
382424
readonly result = submitForm({ username: 'alice', password: 's3cr3t' });
383425
```
384426

427+
#### Binary / octet-stream body
428+
429+
When the spec declares `application/octet-stream`, `image/*`, `application/pdf`, or
430+
any other non-JSON/form/multipart content type, the generated body type is
431+
`Blob | ArrayBuffer` rather than a `paths[...]` derivation:
432+
433+
```typescript
434+
export type UploadBinaryBody = Blob | ArrayBuffer;
435+
436+
export const UPLOAD_BINARY = new InjectionToken<
437+
(body: UploadBinaryBody | Signal<UploadBinaryBody>)
438+
=> ReturnType<typeof httpResource<UploadBinaryResponse>>
439+
>('UPLOAD_BINARY');
440+
```
441+
442+
Pass a `Blob`, `File`, or `ArrayBuffer` directly — `HttpClient` sends binary bodies
443+
as-is without any encoding step.
444+
445+
### Response type unions
446+
447+
When an endpoint can return JSON on multiple 2xx status codes (e.g. `200` for an
448+
update and `201` for a create), the generated `Response` type alias is a union:
449+
450+
```typescript
451+
export type UpsertResourceResponse =
452+
| paths['/resources']['put']['responses']['200']['content']['application/json']
453+
| paths['/resources']['put']['responses']['201']['content']['application/json'];
454+
```
455+
456+
The `httpResource<UpsertResourceResponse>` call site receives a value that is the
457+
union of all possible success shapes.
458+
385459
### Security schemes
386460

387461
The generator emits one file per security scheme. Two patterns are used depending on the

0 commit comments

Comments
 (0)