@powerduck/openapi-codegen 0.5.0 → 0.5.2

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 (3) hide show
  1. package/README.md +253 -434
  2. package/dist/index.cjs +1 -1
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,90 +1,36 @@
1
1
  # @powerduck/openapi-codegen
2
2
 
3
- Generate runnable HTTP request examples from OpenAPI documents for **21 languages** and **41 language/client combinations**.
3
+ Generate runnable HTTP request examples from OpenAPI documents. Supports 21 languages and 41 language/client combinations. Browser-compatible, zero runtime dependencies.
4
4
 
5
- `@powerduck/openapi-codegen` is a high-performance, browser-compatible TypeScript library for API documentation systems, developer portals, API explorers, command-line tools, and build-time code generation.
5
+ [![npm version](https://img.shields.io/npm/v/@powerduck/openapi-codegen)](https://www.npmjs.com/package/@powerduck/openapi-codegen)
6
+ [![license](https://img.shields.io/npm/l/@powerduck/openapi-codegen)](https://github.com/PowerDuckie/openapi-codegen/blob/main/LICENSE)
6
7
 
7
- ## Features
8
+ ## Links
8
9
 
9
- - **Zero runtime dependencies** — works in browser, Node.js, Electron, and edge environments
10
- - **Dual module support** — native ESM and CommonJS (`require()`)
11
- - **OpenAPI 3.0, 3.1, and 3.2** support
12
- - **41 built-in generators** across 21 languages
13
- - **Parameter serialization** — path, query, header, cookie with full OpenAPI style support
14
- - **Security schemes** — API key, HTTP bearer, HTTP basic
15
- - **Body types** — JSON, plain-text, URL-encoded form, multipart form-data
16
- - **$ref resolution** — in-document JSON Pointer references with circular detection
17
- - **Example generation** — automatic example values from JSON Schema
18
- - **TypeScript declarations** — full type safety
19
- - **Highly optimized** — no `JSON.parse`/`JSON.stringify` deep cloning, structured reference handling
20
-
21
- ## Supported Languages and Clients
22
-
23
- Use the exact `language` and `client` identifiers shown below when calling `generate()`. Identifiers are case-sensitive.
24
-
25
- | Language | `language` | Supported `client` values |
26
- | ----------------- | ------------ | ----------------------------------------------------------------- |
27
- | C | `c` | `libcurl` |
28
- | C# | `csharp` | `httpclient`, `restsharp` |
29
- | Clojure | `clojure` | `clj-http` |
30
- | Dart | `dart` | `http` |
31
- | F# | `fsharp` | `httpclient` |
32
- | Go | `go` | `new-request` |
33
- | HTTP request file | `http` | `http1` |
34
- | Java | `java` | `asynchttp`, `java-net-http`, `okhttp`, `unirest` |
35
- | JavaScript | `javascript` | `fetch`, `axios`, `ofetch`, `jquery`, `xhr` |
36
- | Kotlin | `kotlin` | `okhttp` |
37
- | Node.js | `node` | `fetch`, `axios`, `ofetch`, `undici` |
38
- | Objective-C | `objc` | `nsurlsession` |
39
- | OCaml | `ocaml` | `cohttp` |
40
- | PHP | `php` | `curl`, `guzzle`, `laravel-http` |
41
- | PowerShell | `powershell` | `invoke-webrequest`, `invoke-restmethod` |
42
- | Python | `python` | `http-client`, `requests`, `aiohttp`, `httpx-sync`, `httpx-async` |
43
- | R | `r` | `httr2` |
44
- | Ruby | `ruby` | `net-http` |
45
- | Rust | `rust` | `reqwest` |
46
- | Shell | `shell` | `curl`, `wget`, `httpie` |
47
- | Swift | `swift` | `nsurlsession` |
48
-
49
- > Generator availability does not imply that every client can represent every OpenAPI operation. For example, GNU Wget cannot safely construct arbitrary `multipart/form-data` requests. Use `shell/curl` or `shell/httpie` for multipart uploads.
50
-
51
- ## Requirements
52
-
53
- - Node.js 18 or later (for Node.js usage)
54
- - A parsed OpenAPI document (JavaScript object)
55
- - Modern browser with Fetch API (for browser usage)
56
-
57
- ## Installation
58
-
59
- Using npm:
10
+ - [Official Website](https://www.powerduck.com/opensource/openapi-codegen.html)
11
+ - [Documentation](https://www.powerduck.com/docs/openapi-codegen/introduction)
12
+ - [Live Demo](https://www.powerduck.com/demo/openapi-codegen.html)
13
+ - [GitHub](https://github.com/PowerDuckie/openapi-codegen)
14
+ - [npm](https://www.npmjs.com/package/@powerduck/openapi-codegen)
60
15
 
61
- ```bash
62
- npm install @powerduck/openapi-codegen
63
- ```
16
+ ---
64
17
 
65
- Using pnpm:
66
-
67
- ```bash
68
- pnpm add @powerduck/openapi-codegen
69
- ```
18
+ ## Quick Start
70
19
 
71
- Using Yarn:
20
+ ### Install
72
21
 
73
22
  ```bash
74
- yarn add @powerduck/openapi-codegen
23
+ npm install @powerduck/openapi-codegen
75
24
  ```
76
25
 
77
- ## Quick Start
78
-
79
- ### ESM (import)
26
+ ### Generate from a document
80
27
 
81
28
  ```typescript
82
29
  import { generate } from "@powerduck/openapi-codegen";
83
- import document from "./openapi.json" with { type: "json" };
84
30
 
85
31
  const code = generate({
86
- document,
87
- path: "/users/{id}",
32
+ document: openApiDocument,
33
+ path: "/pets/{id}",
88
34
  method: "get",
89
35
  language: "javascript",
90
36
  client: "fetch",
@@ -93,7 +39,7 @@ const code = generate({
93
39
  console.log(code);
94
40
  ```
95
41
 
96
- ### CommonJS (require)
42
+ ### CommonJS equivalent
97
43
 
98
44
  ```javascript
99
45
  const { generate } = require("@powerduck/openapi-codegen");
@@ -103,8 +49,8 @@ const document = JSON.parse(fs.readFileSync("./openapi.json", "utf8"));
103
49
 
104
50
  const code = generate({
105
51
  document,
106
- path: "/users/{id}",
107
- method: "get",
52
+ path: "/pets",
53
+ method: "post",
108
54
  language: "python",
109
55
  client: "requests",
110
56
  });
@@ -112,68 +58,7 @@ const code = generate({
112
58
  console.log(code);
113
59
  ```
114
60
 
115
- `generate()` returns the generated source code as a string.
116
-
117
- ## Authentication
118
-
119
- Provide credentials through `securityValues`:
120
-
121
- ```typescript
122
- import { generate } from "@powerduck/openapi-codegen";
123
-
124
- const code = generate({
125
- document,
126
- path: "/users/{id}",
127
- method: "get",
128
- language: "javascript",
129
- client: "fetch",
130
- securityValues: {
131
- bearerAuth: "YOUR_ACCESS_TOKEN",
132
- apiKey: "YOUR_API_KEY",
133
- },
134
- });
135
-
136
- console.log(code);
137
- ```
138
-
139
- The keys in `securityValues` must match the security scheme names defined in the OpenAPI document.
140
-
141
- **Security note:** Do not commit real credentials to source control.
142
-
143
- ## Multipart Uploads
144
-
145
- ```typescript
146
- import { generate } from "@powerduck/openapi-codegen";
147
-
148
- const code = generate({
149
- document,
150
- path: "/upload/{id}",
151
- method: "post",
152
- language: "shell",
153
- client: "curl",
154
- securityValues: {
155
- bearerAuth: "YOUR_ACCESS_TOKEN",
156
- },
157
- });
158
-
159
- console.log(code);
160
- ```
161
-
162
- Generated multipart examples may include placeholder file paths:
163
-
164
- ```text
165
- /tmp/file.bin
166
- ```
167
-
168
- Replace all placeholder paths before running the generated code.
169
-
170
- Some clients cannot safely represent every OpenAPI request. Generation may throw a descriptive error when the selected client is incompatible with an operation.
171
-
172
- For example, `shell/wget` does not support arbitrary multipart generation. Use `shell/curl` or `shell/httpie` instead.
173
-
174
- ## Discover Available Generators
175
-
176
- Use `list()` to inspect every installed language/client combination:
61
+ ### List available generators
177
62
 
178
63
  ```typescript
179
64
  import { list } from "@powerduck/openapi-codegen";
@@ -183,38 +68,28 @@ for (const { language, client } of list()) {
183
68
  }
184
69
  ```
185
70
 
186
- You can also select a generator programmatically:
71
+ ### Provide credentials
187
72
 
188
73
  ```typescript
189
- import { generate, list } from "@powerduck/openapi-codegen";
190
-
191
- const generator = list().find(
192
- ({ language, client }) => language === "javascript" && client === "fetch",
193
- );
194
-
195
- if (!generator) {
196
- throw new Error("The requested generator is not available");
197
- }
198
-
199
74
  const code = generate({
200
75
  document,
201
- path: "/users",
76
+ path: "/pets/{id}",
202
77
  method: "get",
203
- language: generator.language,
204
- client: generator.client,
78
+ language: "javascript",
79
+ client: "fetch",
80
+ securityValues: {
81
+ bearerAuth: "YOUR_ACCESS_TOKEN",
82
+ apiKey: "YOUR_API_KEY",
83
+ },
205
84
  });
206
-
207
- console.log(code);
208
85
  ```
209
86
 
210
- ## Custom Server URL
211
-
212
- Override the server URL from the OpenAPI document:
87
+ ### Override the server URL
213
88
 
214
89
  ```typescript
215
90
  const code = generate({
216
91
  document,
217
- path: "/users",
92
+ path: "/pets",
218
93
  method: "get",
219
94
  language: "shell",
220
95
  client: "curl",
@@ -222,363 +97,307 @@ const code = generate({
222
97
  });
223
98
  ```
224
99
 
225
- ## API Reference
226
-
227
- ### `generate(options)`
228
-
229
- Generates an HTTP request example for an OpenAPI operation.
230
-
231
- ```typescript
232
- const code = generate({
233
- document,
234
- path,
235
- method,
236
- language,
237
- client,
238
- securityValues,
239
- serverUrl,
240
- softRefMode,
241
- });
242
- ```
243
-
244
- #### Options
100
+ ---
245
101
 
246
- | Option | Type | Required | Description |
247
- | ---------------- | ----------------------------- | -------: | -------------------------------------------- |
248
- | `document` | `unknown` (OpenAPI object) | Yes* | Parsed OpenAPI document |
249
- | `path` | `string` | Yes* | Exact OpenAPI path template |
250
- | `method` | `string` | Yes* | HTTP operation method |
251
- | `language` | `string` | Yes | Target language identifier |
252
- | `client` | `string` | Yes | Target HTTP client identifier |
253
- | `securityValues` | `Record<string, string>` | No | Credentials for named security schemes |
254
- | `serverUrl` | `string` | No | Override server URL from document |
255
- | `softRefMode` | `boolean` | No | Soft mode for $ref resolution (no throws) |
256
- | `request` | `RequestIR` | Yes* | Pre-normalized request (alternative to doc) |
257
-
258
- \* Either `document` + `path` + `method` OR `request` must be provided.
102
+ ## Features
259
103
 
260
- #### Return Value
104
+ - **21 languages, 41 clients** — JavaScript, Python, Go, Java, Ruby, PHP, C#, Rust, Swift, Kotlin, Dart, and more
105
+ - **Full OpenAPI parameter serialization** — path, query, header, cookie parameters with style/explode rules (form, spaceDelimited, pipeDelimited, label, matrix, simple, deepObject)
106
+ - **All body types** — JSON, form-urlencoded, multipart/form-data (text + file fields), XML, text, binary
107
+ - **Security resolution** — Bearer, Basic, API key (header/query/cookie), with `securityValues` for credentials
108
+ - **`$ref` resolution** — in-document JSON Pointer resolution with soft mode for broken/circular refs
109
+ - **Example generation** — generates realistic example values from JSON Schema (respects `example`, `enum`, `const`, `default`, `oneOf`/`anyOf`/`allOf`, types, formats, constraints)
110
+ - **Plugin system** — register custom generators through a simple plugin API
111
+ - **Direct RequestIR** — skip normalization entirely by building a `RequestIR` yourself
112
+ - **Browser-compatible** — zero Node.js dependencies, works in browsers, Edge Functions, and Node.js
113
+ - **Dual ESM/CJS builds** — works with `import` and `require`, with bundled TypeScript declarations
261
114
 
262
- Returns the generated source code as a `string`.
115
+ ---
263
116
 
264
- #### Errors
117
+ ## GenerateOptions
265
118
 
266
- Generation can throw when:
119
+ ```typescript
120
+ interface GenerateOptions {
121
+ language: string; // Target language identifier (case-sensitive)
122
+ client: string; // Target HTTP client identifier
123
+ request?: RequestIR; // Pre-normalized request; skips normalize
124
+ document?: unknown; // A parsed OpenAPI document object
125
+ path?: string; // Exact OpenAPI path template, e.g. "/pets/{id}"
126
+ method?: string; // HTTP method, e.g. "get" / "post"
127
+ serverUrl?: string; // Override the server URL from the document
128
+ securityValues?: Record<string, string>; // Credentials keyed by security scheme name
129
+ softRefMode?: boolean; // Do not throw on broken/circular $refs
130
+ }
131
+ ```
267
132
 
268
- - The requested path or operation does not exist
269
- - The language/client combination is unknown
270
- - The OpenAPI document is invalid or unsupported
271
- - Required generation data cannot be resolved
272
- - The selected client cannot safely represent the request
273
- - A circular $ref is detected (unless `softRefMode` is enabled)
133
+ > Either `request` alone, or `document` + `path` + `method`, must be provided. When `request` is supplied, `document`, `path`, `method`, `serverUrl`, `securityValues`, and `softRefMode` are ignored.
274
134
 
275
- Handle errors when processing untrusted documents or user-selected generators:
135
+ ### Input validation errors
276
136
 
277
- ```typescript
278
- try {
279
- const code = generate({
280
- document,
281
- path: "/users/{id}",
282
- method: "get",
283
- language: "javascript",
284
- client: "fetch",
285
- });
137
+ `generate()` throws when required input is missing:
286
138
 
287
- console.log(code);
288
- } catch (error) {
289
- const message = error instanceof Error ? error.message : String(error);
139
+ - `TypeError: generate() requires an options object`
140
+ - `Error: Unsupported generator: <language>/<client>`
141
+ - `TypeError: generate() requires either a 'request' or a 'document' option`
142
+ - `TypeError: generate() requires a 'path' option when using 'document'`
143
+ - `TypeError: generate() requires a 'method' option when using 'document'`
290
144
 
291
- console.error(`Generation failed: ${message}`);
292
- process.exitCode = 1;
293
- }
294
- ```
145
+ ---
295
146
 
296
- ### `list()`
147
+ ## Top-level API
297
148
 
298
- Returns the available generator descriptors:
149
+ | Export | Signature | Description |
150
+ |---|---|---|
151
+ | `generate` | `(options: GenerateOptions) => string` | Generate runnable source code for one operation |
152
+ | `register` | `(generator: Generator) => void` | Register a custom generator |
153
+ | `get` | `(language: string, client: string) => Generator \| undefined` | Look up a generator |
154
+ | `list` | `() => Array<{ language: string; client: string }>` | List all registered generators |
155
+ | `use` | `(plugin: Plugin) => void` | Apply a plugin that registers generators |
156
+ | `registerBuiltins` | `() => void` | Register all built-in generators (auto-called on import) |
157
+ | `normalize` | `(options) => NormalizedRequest` | Turn a document + path + method into a normalized request |
158
+ | `builtinGenerators` | `Generator[]` | The raw array of all built-in generator definitions |
299
159
 
300
- ```typescript
301
- const generators = list();
302
- ```
160
+ Built-ins are registered automatically when the module loads, so calling `registerBuiltins()` manually is optional.
303
161
 
304
- Each descriptor contains:
162
+ ---
305
163
 
306
- ```typescript
307
- interface GeneratorDescriptor {
308
- language: string;
309
- client: string;
310
- }
311
- ```
164
+ ## Built-in Generators (21 languages, 41 clients)
312
165
 
313
- ### `get(language, client)`
166
+ | Language | `language` | `client` values |
167
+ |---|---|---|
168
+ | C | `c` | `libcurl` |
169
+ | C# | `csharp` | `httpclient`, `restsharp` |
170
+ | Clojure | `clojure` | `clj-http` |
171
+ | Dart | `dart` | `http` |
172
+ | F# | `fsharp` | `httpclient` |
173
+ | Go | `go` | `new-request` |
174
+ | HTTP request file | `http` | `http1` |
175
+ | Java | `java` | `asynchttp`, `java-net-http`, `okhttp`, `unirest` |
176
+ | JavaScript | `javascript` | `axios`, `fetch`, `jquery`, `ofetch`, `xhr` |
177
+ | Kotlin | `kotlin` | `okhttp` |
178
+ | Node.js | `node` | `axios`, `fetch`, `ofetch`, `undici` |
179
+ | Objective-C | `objc` | `nsurlsession` |
180
+ | OCaml | `ocaml` | `cohttp` |
181
+ | PHP | `php` | `curl`, `guzzle`, `laravel-http` |
182
+ | PowerShell | `powershell` | `invoke-restmethod`, `invoke-webrequest` |
183
+ | Python | `python` | `aiohttp`, `http-client`, `httpx-async`, `httpx-sync`, `requests` |
184
+ | R | `r` | `httr2` |
185
+ | Ruby | `ruby` | `net-http` |
186
+ | Rust | `rust` | `reqwest` |
187
+ | Shell | `shell` | `curl`, `httpie`, `wget` |
188
+ | Swift | `swift` | `nsurlsession` |
314
189
 
315
- Returns a specific generator by language and client, or `undefined` if not found:
190
+ > Generator availability does not mean every client can represent every operation. For example, `shell/wget` cannot safely build arbitrary `multipart/form-data` requests; use `shell/curl` or `shell/httpie` instead.
316
191
 
317
- ```typescript
318
- const generator = get("javascript", "fetch");
319
- ```
192
+ ---
320
193
 
321
- ### `register(generator)`
194
+ ## Custom Generator
322
195
 
323
- Register a custom generator:
196
+ Implement the `Generator` interface and call `register`:
324
197
 
325
198
  ```typescript
326
- import { register } from "@powerduck/openapi-codegen";
199
+ import { register, generate } from "@powerduck/openapi-codegen";
200
+ import type { RequestIR } from "@powerduck/openapi-codegen";
327
201
 
328
202
  register({
329
- language: "custom",
203
+ language: "mylang",
330
204
  client: "my-client",
331
- generate: (request) => {
332
- // Custom generation logic
333
- return "// Custom generated code";
205
+ generate(request: RequestIR): string {
206
+ return [
207
+ `// ${request.method} ${request.baseUrl}${request.path}`,
208
+ `mylang.request(${JSON.stringify({
209
+ method: request.method,
210
+ url: request.baseUrl + request.path,
211
+ })})`,
212
+ ].join("\n");
334
213
  },
335
214
  });
336
- ```
337
-
338
- ## OpenAPI Document Input
339
-
340
- Pass a parsed JavaScript object rather than a JSON or YAML source string.
341
-
342
- ### JSON
343
-
344
- ```typescript
345
- import { readFile } from "node:fs/promises";
346
- import { generate } from "@powerduck/openapi-codegen";
347
-
348
- const source = await readFile("./openapi.json", "utf8");
349
- const document = JSON.parse(source);
350
215
 
351
216
  const code = generate({
352
217
  document,
353
- path: "/pets/{petId}",
218
+ path: "/pets",
354
219
  method: "get",
355
- language: "javascript",
356
- client: "fetch",
220
+ language: "mylang",
221
+ client: "my-client",
357
222
  });
358
-
359
- console.log(code);
360
223
  ```
361
224
 
362
- ### YAML
225
+ ---
363
226
 
364
- Install a YAML parser separately:
227
+ ## Plugin
365
228
 
366
- ```bash
367
- npm install yaml
368
- ```
369
-
370
- Parse the document before passing it to `generate()`:
229
+ A plugin bundles one or more generator registrations through `use()`:
371
230
 
372
231
  ```typescript
373
- import { readFile } from "node:fs/promises";
374
- import YAML from "yaml";
375
- import { generate } from "@powerduck/openapi-codegen";
376
-
377
- const source = await readFile("./openapi.yaml", "utf8");
378
- const document = YAML.parse(source);
232
+ import { use, generate } from "@powerduck/openapi-codegen";
233
+
234
+ use({
235
+ name: "internal-generators",
236
+ register(api) {
237
+ api.register({
238
+ language: "go",
239
+ client: "our-wrapper",
240
+ generate(request) {
241
+ return `// go wrapper for ${request.method} ${request.path}`;
242
+ },
243
+ });
244
+ api.register({
245
+ language: "ruby",
246
+ client: "our-wrapper",
247
+ generate(request) {
248
+ return `# ruby wrapper for ${request.method} ${request.path}`;
249
+ },
250
+ });
251
+ },
252
+ });
379
253
 
380
254
  const code = generate({
381
255
  document,
382
- path: "/pets/{petId}",
256
+ path: "/pets",
383
257
  method: "get",
384
- language: "javascript",
385
- client: "fetch",
258
+ language: "go",
259
+ client: "our-wrapper",
386
260
  });
387
-
388
- console.log(code);
389
- ```
390
-
391
- ## Browser Usage
392
-
393
- The library is fully browser-compatible with zero Node.js dependencies:
394
-
395
- ```html
396
- <!DOCTYPE html>
397
- <html>
398
- <head>
399
- <title>API Code Generator</title>
400
- </head>
401
- <body>
402
- <script type="module">
403
- import { generate, list } from "https://esm.sh/@powerduck/openapi-codegen";
404
-
405
- const document = {
406
- openapi: "3.1.0",
407
- info: { title: "Demo API", version: "1.0.0" },
408
- paths: {
409
- "/hello": {
410
- get: {
411
- responses: { "200": { description: "OK" } },
412
- },
413
- },
414
- },
415
- };
416
-
417
- const code = generate({
418
- document,
419
- path: "/hello",
420
- method: "get",
421
- language: "javascript",
422
- client: "fetch",
423
- });
424
-
425
- console.log(code);
426
- </script>
427
- </body>
428
- </html>
429
261
  ```
430
262
 
431
- ## Generated Code
432
-
433
- Generated examples are intended to be readable, runnable starting points. Depending on the selected language and client, they may include:
263
+ ---
434
264
 
435
- - A 30-second connection or request timeout
436
- - HTTP status validation
437
- - Response body output
438
- - Resource cleanup
439
- - Runtime and dependency notes
440
- - Placeholder multipart file paths
441
- - Environment-specific proxy or TLS behavior
265
+ ## Generate from a Direct RequestIR
442
266
 
443
- Review generated code before using it in production. You may need to:
267
+ Skip `normalize` entirely by building a `RequestIR` yourself:
444
268
 
445
- - Install dependencies for the selected client
446
- - Replace placeholder parameter values
447
- - Replace multipart file paths
448
- - Supply authentication credentials securely
449
- - Configure certificate trust
450
- - Configure proxies
451
- - Adjust timeout and redirect policies
452
- - Add application-specific response parsing
269
+ ```typescript
270
+ import { generate } from "@powerduck/openapi-codegen";
271
+ import type { RequestIR } from "@powerduck/openapi-codegen";
453
272
 
454
- ## Performance
273
+ const request: RequestIR = {
274
+ method: "GET",
275
+ baseUrl: "https://api.example.com",
276
+ path: "/users/{id}",
277
+ parameters: [
278
+ { name: "id", in: "path", value: 123 },
279
+ { name: "limit", in: "query", value: 10, style: "form", explode: true },
280
+ ],
281
+ headers: [
282
+ { name: "Accept", in: "header", value: "application/json" },
283
+ ],
284
+ body: undefined,
285
+ security: [],
286
+ };
455
287
 
456
- The library is optimized for high performance:
288
+ const code = generate({
289
+ request,
290
+ language: "python",
291
+ client: "requests",
292
+ });
293
+ ```
457
294
 
458
- - **No deep cloning via `JSON.parse(JSON.stringify())`** — uses structured reference handling
459
- - **Efficient $ref resolution** with caching and circular detection
460
- - **Lazy example generation** — only generates examples when needed
461
- - **Zero runtime dependencies** — minimal bundle size (~130KB minified)
462
- - **Tree-shakeable** — ESM build supports tree-shaking
295
+ ---
463
296
 
464
- ## Development
297
+ ## Core Types
465
298
 
466
- Clone the repository and install dependencies:
299
+ ```typescript
300
+ type ParameterLocation = "path" | "query" | "querystring" | "header" | "cookie";
301
+
302
+ interface Parameter {
303
+ name: string;
304
+ in: ParameterLocation;
305
+ value: unknown;
306
+ style?: string;
307
+ explode?: boolean;
308
+ allowReserved?: boolean;
309
+ }
467
310
 
468
- ```bash
469
- git clone https://github.com/PowerDuckie/openapi-codegen.git
470
- cd openapi-codegen
471
- npm install
472
- ```
311
+ interface Body {
312
+ mediaType: string;
313
+ value: unknown;
314
+ encoding?: Record<string, unknown>;
315
+ }
473
316
 
474
- Run the test suite:
317
+ interface Security {
318
+ name: string;
319
+ type: string;
320
+ scheme?: string;
321
+ in?: string;
322
+ paramName?: string;
323
+ value: string;
324
+ }
475
325
 
476
- ```bash
477
- npm test
478
- ```
326
+ interface RequestIR {
327
+ method: string;
328
+ baseUrl: string;
329
+ path: string;
330
+ parameters: Parameter[];
331
+ headers: Parameter[];
332
+ body?: Body;
333
+ security: Security[];
334
+ }
479
335
 
480
- Run tests with coverage:
336
+ interface Generator {
337
+ language: string;
338
+ client: string;
339
+ generate(request: RequestIR): string;
340
+ }
481
341
 
482
- ```bash
483
- npm run test:coverage
342
+ interface Plugin {
343
+ name: string;
344
+ register(api: { register(generator: Generator): void }): void;
345
+ }
484
346
  ```
485
347
 
486
- Build the package:
348
+ ---
487
349
 
488
- ```bash
489
- npm run build
490
- ```
350
+ ## Error Handling
491
351
 
492
- Run type checking:
352
+ `generate()` throws for missing input or unsupported generators. Wrap calls when processing untrusted documents:
493
353
 
494
- ```bash
495
- npm run typecheck
354
+ ```typescript
355
+ try {
356
+ const code = generate({
357
+ document,
358
+ path: "/pets/{id}",
359
+ method: "get",
360
+ language: "javascript",
361
+ client: "fetch",
362
+ });
363
+ console.log(code);
364
+ } catch (error) {
365
+ console.error("Generation failed:", error instanceof Error ? error.message : error);
366
+ }
496
367
  ```
497
368
 
498
- Run all release checks:
499
-
500
- ```bash
501
- npm run check
502
- ```
369
+ ---
503
370
 
504
- Run the demo:
371
+ ## Development
505
372
 
506
373
  ```bash
507
- npm run demo
508
- ```
509
-
510
- ## Testing
511
-
512
- The project uses:
374
+ # Install dependencies
375
+ npm install
513
376
 
514
- - [Vitest](https://vitest.dev/) for unit and integration tests
515
- - 270+ tests covering all core modules
516
- - Full branch coverage for core logic
377
+ # Type check
378
+ npm run typecheck
517
379
 
518
- Run all tests:
380
+ # Build (ESM + CJS + type declarations)
381
+ npm run build
519
382
 
520
- ```bash
383
+ # Run tests
521
384
  npm test
522
- ```
523
-
524
- ## Project Structure
525
385
 
386
+ # Watch mode
387
+ npx vitest watch
526
388
  ```
527
- src/
528
- ├── index.ts # Main entry point
529
- ├── types.ts # TypeScript type definitions
530
- ├── core/
531
- │ ├── generator.ts # Generator factory
532
- │ ├── helpers.ts # Utility functions (escaping, indentation, etc.)
533
- │ ├── normalize.ts # OpenAPI document → RequestIR normalization
534
- │ ├── refs.ts # JSON Pointer $ref resolver
535
- │ ├── registry.ts # Generator registry (Map-based)
536
- │ ├── request.ts # RequestIR → compiled request
537
- │ ├── serialize.ts # Parameter serialization (path/query/header/cookie)
538
- │ ├── example.ts # JSON Schema → example value generation
539
- │ └── plugin.ts # Plugin system
540
- └── emitters/
541
- ├── index.ts # Unified emitter registry (41 generators)
542
- ├── c/ # C (libcurl)
543
- ├── csharp/ # C# (httpclient, restsharp)
544
- ├── javascript/ # JavaScript (fetch, axios, ofetch, jquery, xhr)
545
- ├── python/ # Python (requests, aiohttp, httpx, http-client)
546
- └── ... # 17 more languages
547
- ```
548
-
549
- ## Security
550
-
551
- Generated source code can contain request URLs, headers, parameter values, and credentials supplied to the generator.
552
389
 
553
- Avoid:
390
+ ---
554
391
 
555
- - Committing generated examples that contain real credentials
556
- - Logging secrets in CI output
557
- - Running unreviewed generated commands in production
558
- - Passing untrusted file paths directly to generated upload code
392
+ ## Related Packages
559
393
 
560
- Report security issues privately to the repository owner instead of opening a public issue.
394
+ - [`@powerduck/openapi-parser`](https://www.npmjs.com/package/@powerduck/openapi-parser) OpenAPI 3.2 parser, validator, and upgrader
395
+ - [`@powerduck/openapi-request`](https://www.npmjs.com/package/@powerduck/openapi-request) — Execute OpenAPI operations with full parameter serialization
396
+ - [`@powerduck/openapi-cli`](https://www.npmjs.com/package/@powerduck/openapi-cli) — CI-ready batch testing for OpenAPI documents
397
+ - [`@powerduck/x-to-openapi`](https://www.npmjs.com/package/@powerduck/x-to-openapi) — Convert curl commands and Postman Collections to OpenAPI 3.2
561
398
 
562
- ## Contributing
563
-
564
- Contributions are welcome.
565
-
566
- 1. Fork the repository.
567
- 2. Create a feature branch.
568
- 3. Add or update tests.
569
- 4. Run the full validation suite.
570
- 5. Commit the changes.
571
- 6. Open a pull request.
572
-
573
- ```bash
574
- git checkout -b feature/my-change
575
- npm install
576
- npm run check
577
- git add .
578
- git commit -m "feat: describe the change"
579
- git push -u origin feature/my-change
580
- ```
399
+ ---
581
400
 
582
401
  ## License
583
402
 
584
- [MIT](LICENSE)
403
+ MIT