genoc 0.3.0 → 0.4.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.
Files changed (71) hide show
  1. package/README.md +31 -187
  2. package/dist/analyzer/analyze.d.ts +18 -0
  3. package/dist/analyzer/analyze.js +493 -0
  4. package/dist/analyzer/discriminator-registry.d.ts +108 -0
  5. package/dist/analyzer/discriminator-registry.js +209 -0
  6. package/dist/analyzer/path-analyzer.d.ts +43 -6
  7. package/dist/analyzer/path-analyzer.js +57 -84
  8. package/dist/analyzer/schema-mapper.d.ts +68 -6
  9. package/dist/analyzer/schema-mapper.js +299 -40
  10. package/dist/analyzer/types.d.ts +126 -0
  11. package/dist/cli/app.d.ts +4 -2
  12. package/dist/cli/app.js +23 -4
  13. package/dist/cli/config-loader.d.ts +20 -0
  14. package/dist/cli/config-loader.js +186 -0
  15. package/dist/cli/config-schema.d.ts +105 -0
  16. package/dist/cli/config-schema.js +183 -0
  17. package/dist/cli/impl.d.ts +9 -1
  18. package/dist/cli/impl.js +189 -36
  19. package/dist/generator/client-generator.d.ts +13 -14
  20. package/dist/generator/client-generator.js +45 -77
  21. package/dist/generator/contracts-generator.d.ts +11 -9
  22. package/dist/generator/contracts-generator.js +45 -491
  23. package/dist/generator/method-generator.d.ts +1 -1
  24. package/dist/generator/method-generator.js +10 -150
  25. package/dist/parser/ref-resolver.d.ts +1 -1
  26. package/dist/parser/ref-resolver.js +1 -2
  27. package/dist/parser/version/index.d.ts +4 -16
  28. package/dist/parser/version/index.js +3 -14
  29. package/dist/parser/version/v3.0/validator.d.ts +1 -1
  30. package/dist/parser/version/v3.1/validator.d.ts +17 -0
  31. package/dist/parser/version/v3.1/validator.js +126 -0
  32. package/dist/parser/version/validate.d.ts +9 -0
  33. package/dist/parser/version/validate.js +12 -0
  34. package/dist/parser/version/version-detector.d.ts +21 -0
  35. package/dist/parser/version/version-detector.js +19 -0
  36. package/dist/pipeline.d.ts +15 -0
  37. package/dist/pipeline.js +25 -0
  38. package/dist/types/client.d.ts +0 -12
  39. package/dist/utils/generator-helpers.d.ts +1 -24
  40. package/dist/utils/generator-helpers.js +11 -78
  41. package/dist/utils/operation-naming.d.ts +84 -0
  42. package/dist/utils/operation-naming.js +178 -0
  43. package/dist/utils/proxy.d.ts +2 -0
  44. package/dist/utils/proxy.js +16 -0
  45. package/dist/utils/string.d.ts +7 -0
  46. package/dist/utils/string.js +9 -0
  47. package/package.json +19 -15
  48. package/dist/index.d.ts +0 -9
  49. package/dist/index.js +0 -11
  50. package/dist/parser/validators.d.ts +0 -7
  51. package/dist/parser/validators.js +0 -79
  52. package/dist/parser/version/normalized-spec.d.ts +0 -199
  53. package/dist/parser/version/registry.d.ts +0 -28
  54. package/dist/parser/version/registry.js +0 -44
  55. package/dist/parser/version/v3.0/index.d.ts +0 -3
  56. package/dist/parser/version/v3.0/index.js +0 -3
  57. package/dist/parser/version/v3.0/normalizer.d.ts +0 -15
  58. package/dist/parser/version/v3.0/normalizer.js +0 -389
  59. package/dist/parser/version/v3.0/strategy.d.ts +0 -27
  60. package/dist/parser/version/v3.0/strategy.js +0 -96
  61. package/dist/parser/version/v3.1/index.d.ts +0 -1
  62. package/dist/parser/version/v3.1/index.js +0 -1
  63. package/dist/parser/version/v3.1/strategy.d.ts +0 -42
  64. package/dist/parser/version/v3.1/strategy.js +0 -513
  65. package/dist/parser/version/v3.2/index.d.ts +0 -4
  66. package/dist/parser/version/v3.2/index.js +0 -4
  67. package/dist/parser/version/v3.2/strategy.d.ts +0 -39
  68. package/dist/parser/version/v3.2/strategy.js +0 -57
  69. package/dist/parser/version/version-strategy.d.ts +0 -31
  70. package/dist/parser/version/version-strategy.js +0 -1
  71. /package/dist/{parser/version/normalized-spec.js → analyzer/types.js} +0 -0
package/README.md CHANGED
@@ -1,23 +1,22 @@
1
1
  # genoc
2
2
 
3
- Generate TypeScript HTTP clients from OpenAPI 3.0 / 3.1 specifications.
3
+ Generate TypeScript HTTP clients from OpenAPI 3.0 or 3.1 specifications.
4
4
  Generated code depends only on the tiny `genoc/runtime` module. Full type safety. Bring your own HTTP client.
5
5
 
6
6
  [![npm version](https://img.shields.io/npm/v/genoc)](https://www.npmjs.com/package/genoc)
7
- [![Node.js](https://img.shields.io/badge/node-%3E%3D18-green)](https://nodejs.org/)
7
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D20.18.1-green)](https://nodejs.org/)
8
8
  [![TypeScript](https://img.shields.io/badge/typescript-blue)](https://www.typescriptlang.org/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
10
10
 
11
11
  ## Features
12
12
 
13
13
  - Full OpenAPI 3.0 and 3.1 specification support with automatic version detection
14
- - End-to-end type safety — requests, responses, and errors are fully typed
15
- - HTTP-client agnostic — adapter pattern lets you plug in fetch, axios, or anything else
16
- - Shared runtime contract — `genoc/runtime` exports the `Requester` type and response/error classes, so one requester implementation works with every generated client
14
+ - End-to-end type safety: requests, responses, and errors are fully typed
15
+ - Works with any HTTP client: plug in fetch, axios, or anything else
16
+ - Shared runtime contract: `genoc/runtime` exports the `Requester` type and response and error classes, so one requester implementation works with every generated client
17
17
  - Error types with per-status-code narrowing and type guards
18
- - File and binary upload/download with stream handling
18
+ - File and binary uploads and downloads with stream handling
19
19
  - Flexible method naming strategies (path-based, operationId, operationId-with-fallback)
20
- - CLI and programmatic API
21
20
 
22
21
  ## Quick Start
23
22
 
@@ -36,15 +35,18 @@ Generate:
36
35
  genoc ./path/to/spec.yaml --output-dir ./src/api
37
36
  ```
38
37
 
39
- This creates two files in `./src/api`:
38
+ This creates three files in `./src/api`:
40
39
 
41
- - `contracts.ts` — Type definitions, error classes, and helper types
42
- - `client.ts` — Typed client with `createClient(requester)` factory
40
+ - `contracts.ts`: type definitions, error classes, and helper types
41
+ - `client.ts`: typed client with the `createClient(requester)` factory
42
+ - `index.ts`: barrel that re-exports both files, so you can import directly from the output directory
43
+
44
+ The barrel means `import { createClient } from './index.js'` works too.
43
45
 
44
46
  ## Usage
45
47
 
46
- The generated client requires a `Requester` implementation — a function that
47
- performs the actual HTTP call and returns the result. The type lives in
48
+ The generated client requires a `Requester` implementation: a function that
49
+ performs the HTTP call and returns the result. The type lives in
48
50
  `genoc/runtime`, so you can write and compile a requester before generating
49
51
  anything:
50
52
 
@@ -52,20 +54,7 @@ anything:
52
54
  import type { Requester } from 'genoc/runtime';
53
55
  ```
54
56
 
55
- ```typescript
56
- type Requester = <TResponse>(
57
- method: string,
58
- path: string,
59
- options: {
60
- query?: Record<string, unknown>;
61
- body?: unknown;
62
- headers?: Record<string, string>;
63
- expectStream?: true;
64
- }
65
- ) => Promise<TResponse | StreamResponse | ErrorResponse>;
66
- ```
67
-
68
- ### Basic Example with `fetch`
57
+ For the full `Requester` type signature and binary and stream handling, see [Usage](./docs/usage.md).
69
58
 
70
59
  ```typescript
71
60
  import { createClient } from './client.js';
@@ -105,182 +94,37 @@ const requester: Requester = async (method, path, options) => {
105
94
 
106
95
  const client = createClient(requester);
107
96
 
108
- // Typed call — response type is inferred from the spec
97
+ // Typed call: response type is inferred from the spec
109
98
  const pets = await client.getPets({ limit: 10 });
110
99
  ```
111
100
 
112
- See [Binary / File Responses](#binary--file-responses) for handling `expectStream: true`.
113
-
114
- ## Shared Runtime (`genoc/runtime`)
115
-
116
- Generated clients import their response and error classes from `genoc/runtime`
117
- instead of declaring inline copies (the generated `contracts.ts` re-exports
118
- them, so existing imports keep working). This gives every generated client the
119
- same class identity — `instanceof` checks work across clients, and you can
120
- implement **one shared requester** typed against the package:
121
-
122
- ```typescript
123
- // common-requester.ts — reusable across all generated clients,
124
- // no generated imports needed
125
- import type { Requester } from 'genoc/runtime';
126
-
127
- export const requester: Requester = async (method, path, options) => {
128
- // ...your fetch/axios/etc. implementation
129
- };
130
- ```
131
-
132
- To pin a specific version or point at a mirror, override the import specifier
133
- via `--runtime-import-path` (CLI) or `runtimeImportPath` (programmatic config).
134
-
135
- ## Binary / File Responses
136
-
137
- When your spec defines binary responses (e.g. `format: binary`,
138
- `application/octet-stream`, `image/*`), the generated client sends
139
- `expectStream: true` in options. Your `Requester` should return a
140
- `StreamResponse` in that case:
141
-
142
- ```typescript
143
- import { StreamResponse } from 'genoc/runtime';
144
-
145
- // Inside your Requester implementation:
146
- if (options.expectStream === true) {
147
- return new StreamResponse(
148
- response.body as ReadableStream<Uint8Array>,
149
- getFilename(response.headers), // extract from Content-Disposition
150
- Object.fromEntries(response.headers.entries())
151
- );
152
- }
153
- ```
154
-
155
- `StreamResponse` is a simple container:
156
-
157
- ```typescript
158
- class StreamResponse {
159
- data: ReadableStream<Uint8Array>;
160
- filename?: string;
161
- headers: Record<string, string>;
162
- }
163
- ```
164
-
165
- ## CLI Reference
166
-
167
- ```bash
168
- genoc <spec> [flags]
169
- ```
170
-
171
- `<spec>` — Path or URL to an OpenAPI 3.0 / 3.1 spec (JSON or YAML).
172
-
173
- | Flag | Default | Description |
174
- | ------------------------ | --------------- | -------------------------------------------------------------------------------------------------- |
175
- | `--output-dir` | (required) | Output directory for generated files |
176
- | `--method-name-strategy` | `path-based` | Method naming strategy |
177
- | `--spec-version` | auto-detect | Override version detection (`"3.0"` or `"3.1"`) |
178
- | `--strict-version` | `true` | Warn if `--spec-version` mismatches detected version |
179
- | `--runtime-import-path` | `genoc/runtime` | Module specifier generated code imports runtime classes from |
180
- | `--proxy` | (none) | HTTP(S) proxy URL for fetching specs from URLs; overrides HTTP_PROXY/HTTPS_PROXY/NO_PROXY env vars |
181
-
182
- ## Method Naming Strategies
183
-
184
- - **`path-based`** (default) — HTTP method + path segments in PascalCase.
185
- `GET /pets` → `getPets`, `GET /api/v1/products` → `getApiV1Products`
186
-
187
- - **`operationId`** — Use the `operationId` field from the spec.
188
- `GET /pets` → `findPets` (if `operationId` is `"findPets"`)
189
-
190
- - **`operationId-with-fallback`** — Use `operationId` if present, otherwise
191
- fall back to path-based naming.
192
-
193
- ## Proxy Support
194
-
195
- When fetching a spec from a URL, `genoc` can route the request through an
196
- HTTP(S) proxy:
197
-
198
- ```bash
199
- genoc https://api.example.com/openapi.yaml --output-dir ./src/api \
200
- --proxy http://user:pass@proxy.example.com:8080
201
- ```
202
-
203
- - Credentials are supported in the proxy URL (`user:pass@host:port`).
204
- - Without `--proxy`, the `HTTP_PROXY` / `HTTPS_PROXY` environment variables
205
- (either casing) are respected automatically, minus `NO_PROXY` exclusions.
206
- - An explicit `--proxy` flag overrides environment detection entirely.
207
- - Programmatic equivalent: `proxy: 'http://proxy.example.com:8080'` in the
208
- `generateClient` config, or an optional second argument `loadSpec(url, { proxy })`.
209
- - SOCKS proxies and `ALL_PROXY` are not supported.
210
-
211
- Note: if you already have `HTTP_PROXY` set in your environment, spec fetches
212
- now go through it (previously ignored). Use `NO_PROXY` to opt out.
213
-
214
- ## JSDoc Documentation Output
101
+ For handling `expectStream: true`, see [Binary and file responses](./docs/usage.md#binary-and-file-responses).
215
102
 
216
- `description`, `deprecated`, `default`, `example`/`examples`, and `title`
217
- fields from your spec become JSDoc comments in the generated code — on object
218
- type properties, named schemas, and client methods. Object types are always
219
- emitted multi-line, so per-property JSDoc stays aligned at each nesting depth.
220
- `@deprecated` tags give you editor strikethrough on deprecated fields and
221
- methods for free.
222
-
223
- ## Programmatic API
224
-
225
- ```typescript
226
- import { generateClient } from 'genoc';
227
-
228
- await generateClient({
229
- input: './openapi.yaml',
230
- outputDir: './src/api',
231
- methodNameStrategy: 'path-based',
232
- specVersion: '3.1',
233
- strictVersion: true,
234
- proxy: 'http://proxy.example.com:8080',
235
- });
236
- ```
237
-
238
- ## Error Handling
103
+ ## Error handling
239
104
 
240
105
  The generated client throws typed errors. Each method carries its own error
241
106
  union, and `isDefinedError` narrows a caught error to that union:
242
107
 
243
- - **`ApiError<TStatus, TData>`** — Error for a specific status code defined in the spec
244
- - **`UnspecifiedApiError`** — Error for a status code not defined in the spec
245
- - **`RequesterFailError`** — Wraps unexpected failures in your `Requester`
246
- - **`isDefinedError(err, client.method)`** — Type guard that narrows to the method's defined error union
247
-
248
- ```typescript
249
- import { UnspecifiedApiError, RequesterFailError } from './contracts.js';
250
- import { isDefinedError } from 'genoc/runtime';
251
-
252
- try {
253
- const result = await client.getPets();
254
- } catch (error) {
255
- if (isDefinedError(error, client.getPets)) {
256
- // error is narrowed to GetPetsErrors (ApiError<400, ...> | ApiError<500, ...>)
257
- if (error.status === 400) {
258
- console.error('Bad request:', error.data);
259
- }
260
- }
261
-
262
- if (error instanceof UnspecifiedApiError) {
263
- console.error('Unexpected status:', error.status, error.data);
264
- }
265
-
266
- if (error instanceof RequesterFailError) {
267
- console.error('Requester failed:', error.cause);
268
- }
269
- }
270
- ```
108
+ - **`ApiError<TStatus, TData>`**: error for a specific status code defined in the spec
109
+ - **`UnspecifiedApiError`**: error for a status code not defined in the spec
110
+ - **`RequesterFailError`**: wraps unexpected failures in your `Requester`
111
+ - **`isDefinedError(err, client.method)`**: type guard that narrows to the method's defined error union
271
112
 
272
- ## Feature Support
113
+ For the full example of catching and narrowing errors, see [Error handling](./docs/error-handling.md).
273
114
 
274
- Check the detailed feature support tables to see if your OpenAPI spec features are covered:
115
+ ## Documentation
275
116
 
276
- - **[OpenAPI 3.0 Support](./docs/openapi-3.0-support.md)** — Data types, schema keywords, parameters, request bodies, file uploads, responses, error handling, `$ref` resolution, components, security schemes, servers, and path operations.
277
- - **[OpenAPI 3.1 Support](./docs/openapi-3.1-support.md)** — All 3.0 features plus type arrays, `$ref` siblings, webhooks, JSON Schema 2020-12 alignment, and a [3.0 → 3.1 diff](./docs/openapi-3.1-support.md#differences-from-openapi-30).
117
+ - [Configuration](./docs/configuration.md): CLI flags, `.genocrc` config files, method naming strategies, and proxy support
118
+ - [Usage](./docs/usage.md): the `Requester` contract, shared runtime, binary and file responses, and JSDoc output
119
+ - [Error handling](./docs/error-handling.md): typed errors, `isDefinedError`, and the catching example
120
+ - [OpenAPI 3.0 support](./docs/openapi-3.0-support.md): data types, schema keywords, parameters, bodies, uploads, responses, errors, `$ref`, components, security, servers, and operations
121
+ - [OpenAPI 3.1 support](./docs/openapi-3.1-support.md): all 3.0 features plus webhooks and JSON Schema 2020-12 alignment, with a diff against 3.0
278
122
 
279
123
  ## Requirements
280
124
 
281
- - Node.js >= 18
125
+ - Node.js 20.18.1 or later
282
126
  - OpenAPI 3.0.x or 3.1.x specification (JSON or YAML, file path or URL)
283
127
 
284
128
  ## License
285
129
 
286
- [MIT](./LICENSE) — Copyright © Andrey Kiselev
130
+ [MIT](./LICENSE). Copyright © Andrey Kiselev
@@ -0,0 +1,18 @@
1
+ import type { OpenAPIDocument } from '../types/openapi.js';
2
+ import type { AnalyzedSpec, AnalyzeOptions } from './types.js';
3
+ /**
4
+ * Build a description-only type-level JSDoc comment for operation-derived
5
+ * types (body / response). Returns '' when the description is absent or
6
+ * whitespace-only so callers can skip emission entirely.
7
+ */
8
+ export declare function buildDescriptionJsDoc(description: string | undefined): string;
9
+ /**
10
+ * Analyze an OpenAPI document into the finished model every renderer consumes:
11
+ * schema→TS-type translation (rename machinery, discriminator discovery, the
12
+ * schema mapper) runs here exactly once, producing render-ready type text.
13
+ *
14
+ * @param doc - The parsed and validated OpenAPI document
15
+ * @param opts - Optional resolver override and method naming strategy
16
+ * @returns The AnalyzedSpec model consumed by all generators
17
+ */
18
+ export declare function analyze(doc: OpenAPIDocument, opts?: AnalyzeOptions): AnalyzedSpec;