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.
- package/README.md +31 -187
- package/dist/analyzer/analyze.d.ts +18 -0
- package/dist/analyzer/analyze.js +493 -0
- package/dist/analyzer/discriminator-registry.d.ts +108 -0
- package/dist/analyzer/discriminator-registry.js +209 -0
- package/dist/analyzer/path-analyzer.d.ts +43 -6
- package/dist/analyzer/path-analyzer.js +57 -84
- package/dist/analyzer/schema-mapper.d.ts +68 -6
- package/dist/analyzer/schema-mapper.js +299 -40
- package/dist/analyzer/types.d.ts +126 -0
- package/dist/cli/app.d.ts +4 -2
- package/dist/cli/app.js +23 -4
- package/dist/cli/config-loader.d.ts +20 -0
- package/dist/cli/config-loader.js +186 -0
- package/dist/cli/config-schema.d.ts +105 -0
- package/dist/cli/config-schema.js +183 -0
- package/dist/cli/impl.d.ts +9 -1
- package/dist/cli/impl.js +189 -36
- package/dist/generator/client-generator.d.ts +13 -14
- package/dist/generator/client-generator.js +45 -77
- package/dist/generator/contracts-generator.d.ts +11 -9
- package/dist/generator/contracts-generator.js +45 -491
- package/dist/generator/method-generator.d.ts +1 -1
- package/dist/generator/method-generator.js +10 -150
- package/dist/parser/ref-resolver.d.ts +1 -1
- package/dist/parser/ref-resolver.js +1 -2
- package/dist/parser/version/index.d.ts +4 -16
- package/dist/parser/version/index.js +3 -14
- package/dist/parser/version/v3.0/validator.d.ts +1 -1
- package/dist/parser/version/v3.1/validator.d.ts +17 -0
- package/dist/parser/version/v3.1/validator.js +126 -0
- package/dist/parser/version/validate.d.ts +9 -0
- package/dist/parser/version/validate.js +12 -0
- package/dist/parser/version/version-detector.d.ts +21 -0
- package/dist/parser/version/version-detector.js +19 -0
- package/dist/pipeline.d.ts +15 -0
- package/dist/pipeline.js +25 -0
- package/dist/types/client.d.ts +0 -12
- package/dist/utils/generator-helpers.d.ts +1 -24
- package/dist/utils/generator-helpers.js +11 -78
- package/dist/utils/operation-naming.d.ts +84 -0
- package/dist/utils/operation-naming.js +178 -0
- package/dist/utils/proxy.d.ts +2 -0
- package/dist/utils/proxy.js +16 -0
- package/dist/utils/string.d.ts +7 -0
- package/dist/utils/string.js +9 -0
- package/package.json +19 -15
- package/dist/index.d.ts +0 -9
- package/dist/index.js +0 -11
- package/dist/parser/validators.d.ts +0 -7
- package/dist/parser/validators.js +0 -79
- package/dist/parser/version/normalized-spec.d.ts +0 -199
- package/dist/parser/version/registry.d.ts +0 -28
- package/dist/parser/version/registry.js +0 -44
- package/dist/parser/version/v3.0/index.d.ts +0 -3
- package/dist/parser/version/v3.0/index.js +0 -3
- package/dist/parser/version/v3.0/normalizer.d.ts +0 -15
- package/dist/parser/version/v3.0/normalizer.js +0 -389
- package/dist/parser/version/v3.0/strategy.d.ts +0 -27
- package/dist/parser/version/v3.0/strategy.js +0 -96
- package/dist/parser/version/v3.1/index.d.ts +0 -1
- package/dist/parser/version/v3.1/index.js +0 -1
- package/dist/parser/version/v3.1/strategy.d.ts +0 -42
- package/dist/parser/version/v3.1/strategy.js +0 -513
- package/dist/parser/version/v3.2/index.d.ts +0 -4
- package/dist/parser/version/v3.2/index.js +0 -4
- package/dist/parser/version/v3.2/strategy.d.ts +0 -39
- package/dist/parser/version/v3.2/strategy.js +0 -57
- package/dist/parser/version/version-strategy.d.ts +0 -31
- package/dist/parser/version/version-strategy.js +0 -1
- /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
|
+
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
|
[](https://www.npmjs.com/package/genoc)
|
|
7
|
-
[](https://nodejs.org/)
|
|
8
8
|
[](https://www.typescriptlang.org/)
|
|
9
9
|
[](./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
|
|
15
|
-
-
|
|
16
|
-
- Shared runtime contract
|
|
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
|
|
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
|
|
38
|
+
This creates three files in `./src/api`:
|
|
40
39
|
|
|
41
|
-
- `contracts.ts
|
|
42
|
-
- `client.ts
|
|
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
|
|
47
|
-
performs the
|
|
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
|
-
|
|
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
|
|
97
|
+
// Typed call: response type is inferred from the spec
|
|
109
98
|
const pets = await client.getPets({ limit: 10 });
|
|
110
99
|
```
|
|
111
100
|
|
|
112
|
-
|
|
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
|
-
|
|
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
|
|
244
|
-
- **`UnspecifiedApiError
|
|
245
|
-
- **`RequesterFailError
|
|
246
|
-
- **`isDefinedError(err, client.method)
|
|
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
|
-
|
|
113
|
+
For the full example of catching and narrowing errors, see [Error handling](./docs/error-handling.md).
|
|
273
114
|
|
|
274
|
-
|
|
115
|
+
## Documentation
|
|
275
116
|
|
|
276
|
-
-
|
|
277
|
-
-
|
|
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
|
|
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)
|
|
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;
|