@powerduck/openapi-codegen 0.4.2 → 0.5.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Powerduck limited
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,23 +1,22 @@
1
1
  # @powerduck/openapi-codegen
2
2
 
3
- Generate runnable HTTP request examples from parsed OpenAPI documents for **21 language targets** and **41 language/client combinations**.
3
+ Generate runnable HTTP request examples from OpenAPI documents for **21 languages** and **41 language/client combinations**.
4
4
 
5
- `@powerduck/openapi-codegen` is an ESM-first TypeScript library for API documentation systems, developer portals, API explorers, command-line tools, and build-time code generation.
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.
6
6
 
7
7
  ## Features
8
8
 
9
- - No need for Node environment support
10
- - Supports parsed OpenAPI documents, including OpenAPI 3.2
11
- - Generates readable HTTP request examples across 41 generators
12
- - Resolves path, query, header, and cookie parameters
13
- - Supports API key, HTTP bearer, and other security schemes
14
- - Supports JSON, plain-text, URL-encoded form, and multipart bodies
15
- - Generates multipart file-upload examples with placeholder paths
16
- - Preserves request bodies that have already been serialized
17
- - Adds timeouts, HTTP status checks, and resource cleanup where supported
18
- - Exposes generator discovery through `list()`
19
- - Includes TypeScript declarations
20
- - Ships as an ECMAScript module
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
21
20
 
22
21
  ## Supported Languages and Clients
23
22
 
@@ -51,9 +50,9 @@ Use the exact `language` and `client` identifiers shown below when calling `gene
51
50
 
52
51
  ## Requirements
53
52
 
54
- - Node.js 20 or later
55
- - A parsed OpenAPI document
56
- - The runtime and dependencies required by the selected generated client
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)
57
56
 
58
57
  ## Installation
59
58
 
@@ -77,7 +76,9 @@ yarn add @powerduck/openapi-codegen
77
76
 
78
77
  ## Quick Start
79
78
 
80
- ```ts
79
+ ### ESM (import)
80
+
81
+ ```typescript
81
82
  import { generate } from "@powerduck/openapi-codegen";
82
83
  import document from "./openapi.json" with { type: "json" };
83
84
 
@@ -92,15 +93,33 @@ const code = generate({
92
93
  console.log(code);
93
94
  ```
94
95
 
96
+ ### CommonJS (require)
97
+
98
+ ```javascript
99
+ const { generate } = require("@powerduck/openapi-codegen");
100
+ const fs = require("node:fs");
101
+
102
+ const document = JSON.parse(fs.readFileSync("./openapi.json", "utf8"));
103
+
104
+ const code = generate({
105
+ document,
106
+ path: "/users/{id}",
107
+ method: "get",
108
+ language: "python",
109
+ client: "requests",
110
+ });
111
+
112
+ console.log(code);
113
+ ```
114
+
95
115
  `generate()` returns the generated source code as a string.
96
116
 
97
117
  ## Authentication
98
118
 
99
119
  Provide credentials through `securityValues`:
100
120
 
101
- ```ts
121
+ ```typescript
102
122
  import { generate } from "@powerduck/openapi-codegen";
103
- import document from "./openapi.json" with { type: "json" };
104
123
 
105
124
  const code = generate({
106
125
  document,
@@ -109,7 +128,7 @@ const code = generate({
109
128
  language: "javascript",
110
129
  client: "fetch",
111
130
  securityValues: {
112
- bearer: "YOUR_ACCESS_TOKEN",
131
+ bearerAuth: "YOUR_ACCESS_TOKEN",
113
132
  apiKey: "YOUR_API_KEY",
114
133
  },
115
134
  });
@@ -119,13 +138,12 @@ console.log(code);
119
138
 
120
139
  The keys in `securityValues` must match the security scheme names defined in the OpenAPI document.
121
140
 
122
- Do not commit real credentials to source control.
141
+ **Security note:** Do not commit real credentials to source control.
123
142
 
124
143
  ## Multipart Uploads
125
144
 
126
- ```ts
145
+ ```typescript
127
146
  import { generate } from "@powerduck/openapi-codegen";
128
- import document from "./openapi.json" with { type: "json" };
129
147
 
130
148
  const code = generate({
131
149
  document,
@@ -134,7 +152,7 @@ const code = generate({
134
152
  language: "shell",
135
153
  client: "curl",
136
154
  securityValues: {
137
- bearer: "YOUR_ACCESS_TOKEN",
155
+ bearerAuth: "YOUR_ACCESS_TOKEN",
138
156
  },
139
157
  });
140
158
 
@@ -157,7 +175,7 @@ For example, `shell/wget` does not support arbitrary multipart generation. Use `
157
175
 
158
176
  Use `list()` to inspect every installed language/client combination:
159
177
 
160
- ```ts
178
+ ```typescript
161
179
  import { list } from "@powerduck/openapi-codegen";
162
180
 
163
181
  for (const { language, client } of list()) {
@@ -167,9 +185,8 @@ for (const { language, client } of list()) {
167
185
 
168
186
  You can also select a generator programmatically:
169
187
 
170
- ```ts
188
+ ```typescript
171
189
  import { generate, list } from "@powerduck/openapi-codegen";
172
- import document from "./openapi.json" with { type: "json" };
173
190
 
174
191
  const generator = list().find(
175
192
  ({ language, client }) => language === "javascript" && client === "fetch",
@@ -190,13 +207,28 @@ const code = generate({
190
207
  console.log(code);
191
208
  ```
192
209
 
210
+ ## Custom Server URL
211
+
212
+ Override the server URL from the OpenAPI document:
213
+
214
+ ```typescript
215
+ const code = generate({
216
+ document,
217
+ path: "/users",
218
+ method: "get",
219
+ language: "shell",
220
+ client: "curl",
221
+ serverUrl: "https://staging.example.com/v1",
222
+ });
223
+ ```
224
+
193
225
  ## API Reference
194
226
 
195
227
  ### `generate(options)`
196
228
 
197
229
  Generates an HTTP request example for an OpenAPI operation.
198
230
 
199
- ```ts
231
+ ```typescript
200
232
  const code = generate({
201
233
  document,
202
234
  path,
@@ -204,19 +236,26 @@ const code = generate({
204
236
  language,
205
237
  client,
206
238
  securityValues,
239
+ serverUrl,
240
+ softRefMode,
207
241
  });
208
242
  ```
209
243
 
210
244
  #### Options
211
245
 
212
- | Option | Type | Required | Description |
213
- | ---------------- | ------------------------ | -------: | -------------------------------------- |
214
- | `document` | `unknown` | Yes | Parsed OpenAPI document |
215
- | `path` | `string` | Yes | Exact OpenAPI path template |
216
- | `method` | `string` | Yes | HTTP operation method |
217
- | `language` | `string` | Yes | Target language identifier |
218
- | `client` | `string` | Yes | Target HTTP client identifier |
219
- | `securityValues` | `Record<string, string>` | No | Credentials for named security schemes |
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.
220
259
 
221
260
  #### Return Value
222
261
 
@@ -231,10 +270,11 @@ Generation can throw when:
231
270
  - The OpenAPI document is invalid or unsupported
232
271
  - Required generation data cannot be resolved
233
272
  - The selected client cannot safely represent the request
273
+ - A circular $ref is detected (unless `softRefMode` is enabled)
234
274
 
235
275
  Handle errors when processing untrusted documents or user-selected generators:
236
276
 
237
- ```ts
277
+ ```typescript
238
278
  try {
239
279
  const code = generate({
240
280
  document,
@@ -257,25 +297,51 @@ try {
257
297
 
258
298
  Returns the available generator descriptors:
259
299
 
260
- ```ts
300
+ ```typescript
261
301
  const generators = list();
262
302
  ```
263
303
 
264
- Each descriptor contains the identifiers required by `generate()`:
304
+ Each descriptor contains:
265
305
 
266
- ```ts
267
- for (const { language, client } of generators) {
268
- console.log(language, client);
306
+ ```typescript
307
+ interface GeneratorDescriptor {
308
+ language: string;
309
+ client: string;
269
310
  }
270
311
  ```
271
312
 
313
+ ### `get(language, client)`
314
+
315
+ Returns a specific generator by language and client, or `undefined` if not found:
316
+
317
+ ```typescript
318
+ const generator = get("javascript", "fetch");
319
+ ```
320
+
321
+ ### `register(generator)`
322
+
323
+ Register a custom generator:
324
+
325
+ ```typescript
326
+ import { register } from "@powerduck/openapi-codegen";
327
+
328
+ register({
329
+ language: "custom",
330
+ client: "my-client",
331
+ generate: (request) => {
332
+ // Custom generation logic
333
+ return "// Custom generated code";
334
+ },
335
+ });
336
+ ```
337
+
272
338
  ## OpenAPI Document Input
273
339
 
274
340
  Pass a parsed JavaScript object rather than a JSON or YAML source string.
275
341
 
276
342
  ### JSON
277
343
 
278
- ```ts
344
+ ```typescript
279
345
  import { readFile } from "node:fs/promises";
280
346
  import { generate } from "@powerduck/openapi-codegen";
281
347
 
@@ -303,7 +369,7 @@ npm install yaml
303
369
 
304
370
  Parse the document before passing it to `generate()`:
305
371
 
306
- ```ts
372
+ ```typescript
307
373
  import { readFile } from "node:fs/promises";
308
374
  import YAML from "yaml";
309
375
  import { generate } from "@powerduck/openapi-codegen";
@@ -322,6 +388,46 @@ const code = generate({
322
388
  console.log(code);
323
389
  ```
324
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
+ ```
430
+
325
431
  ## Generated Code
326
432
 
327
433
  Generated examples are intended to be readable, runnable starting points. Depending on the selected language and client, they may include:
@@ -345,13 +451,23 @@ Review generated code before using it in production. You may need to:
345
451
  - Adjust timeout and redirect policies
346
452
  - Add application-specific response parsing
347
453
 
454
+ ## Performance
455
+
456
+ The library is optimized for high performance:
457
+
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
463
+
348
464
  ## Development
349
465
 
350
466
  Clone the repository and install dependencies:
351
467
 
352
468
  ```bash
353
- git clone https://github.com/PowerDuckie/-powerduck-openapi-codegen.git
354
- cd ./-powerduck-openapi-codegen
469
+ git clone https://github.com/PowerDuckie/openapi-codegen.git
470
+ cd openapi-codegen
355
471
  npm install
356
472
  ```
357
473
 
@@ -361,10 +477,10 @@ Run the test suite:
361
477
  npm test
362
478
  ```
363
479
 
364
- Run property-based fuzz tests:
480
+ Run tests with coverage:
365
481
 
366
482
  ```bash
367
- npm run fuzz
483
+ npm run test:coverage
368
484
  ```
369
485
 
370
486
  Build the package:
@@ -373,6 +489,12 @@ Build the package:
373
489
  npm run build
374
490
  ```
375
491
 
492
+ Run type checking:
493
+
494
+ ```bash
495
+ npm run typecheck
496
+ ```
497
+
376
498
  Run all release checks:
377
499
 
378
500
  ```bash
@@ -389,8 +511,9 @@ npm run demo
389
511
 
390
512
  The project uses:
391
513
 
392
- - [Vitest](https://vitest.dev/) for unit and snapshot tests
393
- - [fast-check](https://fast-check.dev/) for property-based fuzz testing
514
+ - [Vitest](https://vitest.dev/) for unit and integration tests
515
+ - 270+ tests covering all core modules
516
+ - Full branch coverage for core logic
394
517
 
395
518
  Run all tests:
396
519
 
@@ -398,22 +521,29 @@ Run all tests:
398
521
  npm test
399
522
  ```
400
523
 
401
- Update snapshots after intentionally changing generated output:
524
+ ## Project Structure
402
525
 
403
- ```bash
404
- npx vitest run -u
405
526
  ```
406
-
407
- Run only the fuzz test suite:
408
-
409
- ```bash
410
- npm run fuzz
411
- ```
412
-
413
- Before committing snapshot changes, inspect the diff:
414
-
415
- ```bash
416
- git diff
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
417
547
  ```
418
548
 
419
549
  ## Security
@@ -449,13 +579,6 @@ git commit -m "feat: describe the change"
449
579
  git push -u origin feature/my-change
450
580
  ```
451
581
 
452
- When changing generated output, update and review the affected snapshots:
453
-
454
- ```bash
455
- npx vitest run -u
456
- git diff
457
- ```
458
-
459
582
  ## License
460
583
 
461
584
  [MIT](LICENSE)