@powerduck/openapi-request 0.2.3 → 0.2.4

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 (2) hide show
  1. package/README.md +139 -294
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,17 +1,25 @@
1
1
  # @powerduck/openapi-request
2
2
 
3
- Execute OpenAPI operations with full parameter serialization, security resolution, and response parsing. Supports fetch, axios, and custom HTTP clients. Built for browsers, Node.js, and Edge Functions.
4
-
5
3
  [![npm version](https://img.shields.io/npm/v/@powerduck/openapi-request)](https://www.npmjs.com/package/@powerduck/openapi-request)
6
4
  [![license](https://img.shields.io/npm/l/@powerduck/openapi-request)](https://github.com/PowerDuckie/openapi-request/blob/main/LICENSE)
5
+ [![downloads](https://img.shields.io/npm/dm/@powerduck/openapi-request)](https://www.npmjs.com/package/@powerduck/openapi-request)
7
6
 
8
- ## Links
7
+ Execute OpenAPI operations with full parameter serialization, security resolution, and response parsing. Supports fetch, axios, and custom HTTP clients. Built for browsers, Node.js, and Edge Functions.
9
8
 
10
- - [Official Website](https://www.powerduck.com/opensource/openapi-request.html)
11
- - [Documentation](https://www.powerduck.com/docs/openapi-request/introduction)
12
- - [Live Demo](https://www.powerduck.com/demo/openapi-request.html)
13
- - [GitHub](https://github.com/PowerDuckie/openapi-request)
14
- - [npm](https://www.npmjs.com/package/@powerduck/openapi-request)
9
+ ---
10
+
11
+ Powerduck is an open-source developer tooling platform for teams building modern API workflows.
12
+
13
+ - **Full Parameter Serialization** — Path, query, header, and cookie parameters with RFC 6570 URI templates
14
+ - **Security Resolution** — Bearer tokens, API keys, Basic auth, OAuth2, and custom schemes
15
+ - **3 HTTP Clients** — fetch (default), axios, and custom client adapters
16
+ - **Request Body Handling** — JSON, form-data, x-www-form-urlencoded, and raw payloads
17
+ - **Response Parsing** — Automatic JSON, text, blob, and stream parsing with content-type detection
18
+ - **Prepare & Send** — Two-phase API for request inspection before sending
19
+ - **Type-Safe Operations** — Full TypeScript types for parameters, request bodies, and responses
20
+ - **Error Handling** — Structured error objects with status, headers, and parsed body
21
+ - **Interceptors** — Request and response interceptors for logging, auth refresh, and retries
22
+ - **Browser & Node** — Works in browsers, Node.js, and Edge Functions with zero dependencies
15
23
 
16
24
  ---
17
25
 
@@ -39,9 +47,9 @@ const result = await client.send({
39
47
  },
40
48
  });
41
49
 
42
- console.log(result.response.status); // e.g. 200
43
- console.log(result.response.body); // parsed response body
44
- console.log(result.response.headers); // response headers
50
+ console.log(result.response.status); // e.g. 200
51
+ console.log(result.response.body); // parsed response body
52
+ console.log(result.response.headers); // response headers
45
53
  ```
46
54
 
47
55
  ### Prepare and send separately
@@ -60,368 +68,205 @@ const prepared = client.prepare({
60
68
  },
61
69
  });
62
70
 
63
- console.log(prepared.url); // final URL with query string
64
- console.log(prepared.method); // HTTP method
65
- console.log(prepared.headers); // resolved headers
71
+ console.log(prepared.url); // final URL with serialized params
72
+ console.log(prepared.method); // HTTP method
73
+ console.log(prepared.headers); // resolved headers
66
74
 
67
75
  // Send the prepared request
68
- const result = await client.send(prepared);
76
+ const result = await client.sendPrepared(prepared);
69
77
  ```
70
78
 
71
- ### With security values
79
+ ### With authentication
72
80
 
73
81
  ```typescript
74
82
  import { createClient } from "@powerduck/openapi-request";
75
83
 
76
84
  const client = createClient({
77
85
  securityValues: {
78
- bearerAuth: "YOUR_ACCESS_TOKEN",
79
- apiKey: "YOUR_API_KEY",
86
+ bearerAuth: "your-token-here",
87
+ apiKey: "your-api-key",
80
88
  },
81
89
  });
82
90
 
83
91
  const result = await client.send({
84
92
  spec: openApiDocument,
85
- operationId: "createUser",
86
- parameters: {
87
- body: { name: "Ada", email: "ada@example.com" },
88
- },
93
+ operationId: "getProfile",
89
94
  });
90
95
  ```
91
96
 
92
- ### Custom fetch function
97
+ ---
93
98
 
94
- ```typescript
95
- import { createClient } from "@powerduck/openapi-request";
99
+ ## Links
96
100
 
97
- const client = createClient({
98
- fetchFn: async (url, init) => {
99
- console.log("Request:", init.method, url);
100
- const response = await fetch(url, init);
101
- console.log("Response:", response.status);
102
- return response;
103
- },
104
- });
105
- ```
101
+ - [Official Website](https://www.powerduck.com/opensource/openapi-request.html)
102
+ - [Documentation](https://www.powerduck.com/docs/openapi-request/introduction)
103
+ - [GitHub](https://github.com/PowerDuckie/openapi-request)
104
+ - [npm](https://www.npmjs.com/package/@powerduck/openapi-request)
106
105
 
107
106
  ---
108
107
 
109
108
  ## Features
110
109
 
111
- - **Full OpenAPI parameter serialization** — path, query, header, cookie parameters with all `style` and `explode` combinations (form, spaceDelimited, pipeDelimited, label, matrix, simple, deepObject)
112
- - **Security resolution** — Bearer, Basic, API key (header/query/cookie), with per-request or client-level `securityValues`
113
- - **Request body handling** — JSON, form-urlencoded, multipart/form-data (text + file fields), XML, text, binary
114
- - **Response parsing** — automatic JSON parsing, text fallback, content-type detection
115
- - **Two-phase execution** — `prepare()` builds the request without sending, `send()` executes it
116
- - **Custom HTTP client** — plug in fetch, axios, node-fetch, or any compatible function
117
- - **Server URL selection** — override or select from spec `servers` array
118
- - **Timeout and abort** — per-request timeout with `AbortController` integration
119
- - **Postman script support** — run `pm.test()` / `pm.expect()` scripts from `x-postman-scripts`
120
- - **Batch requests** — send multiple operations with configurable concurrency
121
- - **Browser and Node.js compatible** — works in browsers, Edge Functions, and Node.js
122
- - **Dual ESM/CJS builds** — works with `import` and `require`, with bundled TypeScript declarations
110
+ - **Full parameter serialization** — Path, query, header, and cookie parameters with RFC 6570 URI templates
111
+ - **Security resolution** — Bearer tokens, API keys, Basic auth, OAuth2, and custom schemes
112
+ - **3 HTTP clients** — fetch (default), axios, and custom client adapters
113
+ - **Request body handling** — JSON, form-data, x-www-form-urlencoded, and raw payloads
114
+ - **Response parsing** — Automatic JSON, text, blob, and stream parsing with content-type detection
115
+ - **Prepare & send** — Two-phase API for request inspection before sending
116
+ - **Type-safe operations** — Full TypeScript types for parameters, request bodies, and responses
117
+ - **Error handling** — Structured error objects with status, headers, and parsed body
118
+ - **Interceptors** — Request and response interceptors for logging, auth refresh, and retries
119
+ - **Browser & Node** — Works in browsers, Node.js, and Edge Functions with zero dependencies
120
+ - **Server selection** — Auto-select or manually specify server from OpenAPI servers
121
+ - **Content negotiation** — Automatic Accept header based on response content types
122
+ - **Upload progress** — Progress callbacks for file uploads with axios client
123
+ - **Abort support** — AbortController / AbortSignal for request cancellation
124
+ - **Dual ESM/CJS** — Works with `import` and `require`, with bundled TypeScript declarations
123
125
 
124
126
  ---
125
127
 
126
- ## API
127
-
128
- ### `createClient(options?: CreateClientOptions): OpenApiClient`
129
-
130
- Creates a new client instance.
131
-
132
- ```typescript
133
- interface CreateClientOptions {
134
- fetchFn?: (url: string, init: RequestInit) => Promise<Response>;
135
- securityValues?: Record<string, string>;
136
- serverUrl?: string;
137
- timeoutMs?: number;
138
- validateResponse?: boolean;
139
- }
140
- ```
141
-
142
- ### `client.send(options: SendOptions): Promise<SendResult>`
143
-
144
- Prepares and sends a request in one call.
145
-
146
- ### `client.prepare(options: SendOptions): PreparedRequest`
147
-
148
- Builds the request without sending. Returns a `PreparedRequest` that can be inspected or passed to `send()`.
128
+ ## API Reference
149
129
 
150
- ### `SendOptions`
130
+ ### `createClient(options?)`
151
131
 
152
- ```typescript
153
- interface SendOptions {
154
- spec: OpenApiDocument; // Parsed OpenAPI 3.2 document
155
- operationId?: string; // Operation ID (alternative to path + method)
156
- path?: string; // Path template, e.g. "/users/{id}"
157
- method?: string; // HTTP method, e.g. "get"
158
- parameters?: {
159
- path?: Record<string, unknown>;
160
- query?: Record<string, unknown>;
161
- header?: Record<string, string>;
162
- cookie?: Record<string, string>;
163
- body?: unknown;
164
- };
165
- securityValues?: Record<string, string>;
166
- serverUrl?: string;
167
- timeoutMs?: number;
168
- signal?: AbortSignal;
169
- }
170
- ```
171
-
172
- > Either `operationId` or `path` + `method` must be provided to identify the operation.
173
-
174
- ### `SendResult`
132
+ Create an OpenAPI request client.
175
133
 
176
134
  ```typescript
177
- interface SendResult extends ExecResult {
178
- response: {
179
- status: number; // HTTP status code
180
- statusText: string; // HTTP status text
181
- headers: Record<string, string>; // Response headers
182
- body?: unknown; // Parsed JSON body (if content-type is JSON)
183
- text?: string; // Raw response text
184
- };
185
- timings: {
186
- durationMs: number; // Total request duration in milliseconds
187
- };
188
- sizeBytes: number; // Response body size in bytes
189
- }
190
- ```
191
-
192
- > **Important:** Response data is under `result.response`, not directly on `result`. Use `result.response.status`, `result.response.body`, and `result.response.headers`.
193
-
194
- ### `PreparedRequest`
135
+ import { createClient } from "@powerduck/openapi-request";
195
136
 
196
- ```typescript
197
- interface PreparedRequest {
198
- url: string; // Final URL with query string
199
- method: string; // HTTP method
200
- headers: Record<string, string>; // Resolved headers
201
- body?: BodyInit; // Request body (if any)
202
- operationId?: string; // Resolved operation ID
203
- path: string; // Path template
204
- parameters: ResolvedParameters;
205
- }
137
+ const client = createClient({
138
+ client: "fetch", // "fetch" | "axios" | custom adapter
139
+ baseUrl: "https://api.example.com",
140
+ securityValues: { bearerAuth: "token" },
141
+ defaultHeaders: { "X-App": "my-app" },
142
+ timeout: 30000,
143
+ });
206
144
  ```
207
145
 
208
- ---
209
-
210
- ## Parameter Serialization
146
+ ### `client.send(options)`
211
147
 
212
- All OpenAPI 3.2 parameter styles are supported:
213
-
214
- | Location | Style | `explode: true` | `explode: false` |
215
- |---|---|---|---|
216
- | `path` | `simple` (default) | `id=5,role=admin` | `id,role=5,admin` |
217
- | `path` | `label` | `.id=5.role=admin` | `.id,role=5,admin` |
218
- | `path` | `matrix` | `;id=5;role=admin` | `;id,role=5,admin` |
219
- | `query` | `form` (default) | `id=5&role=admin` | `id=5,role=admin` |
220
- | `query` | `spaceDelimited` | `id=5%20role=admin` | `id=5%20role=admin` |
221
- | `query` | `pipeDelimited` | `id=5\|role=admin` | `id=5\|role=admin` |
222
- | `query` | `deepObject` | `user[id]=5&user[role]=admin` | — |
223
- | `header` | `simple` (default) | `id=5,role=admin` | `id,role=5,admin` |
224
- | `cookie` | `form` (default) | `id=5; role=admin` | `id=5,role=admin` |
225
-
226
- ---
227
-
228
- ## Security Schemes
229
-
230
- ### Bearer token
148
+ Send an OpenAPI operation.
231
149
 
232
150
  ```typescript
233
151
  const result = await client.send({
234
- spec,
152
+ spec: openApiDocument,
235
153
  operationId: "getUser",
236
- securityValues: { bearerAuth: "YOUR_TOKEN" },
154
+ parameters: {
155
+ path: { id: "123" },
156
+ query: { include: ["profile", "orders"] },
157
+ header: { "X-Request-ID": "abc" },
158
+ },
159
+ requestBody: { name: "Ada" },
160
+ securityValues: { bearerAuth: "token" },
161
+ serverIndex: 0,
162
+ signal: abortSignal,
237
163
  });
238
- // Sends: Authorization: Bearer YOUR_TOKEN
239
164
  ```
240
165
 
241
- ### Basic auth
242
-
243
- ```typescript
244
- const result = await client.send({
245
- spec,
246
- operationId: "getUser",
247
- securityValues: { basicAuth: "username:password" },
248
- });
249
- // Sends: Authorization: Basic <base64(username:password)>
250
- ```
166
+ ### `client.prepare(options)`
251
167
 
252
- ### API key (header)
168
+ Prepare a request without sending.
253
169
 
254
170
  ```typescript
255
- const result = await client.send({
256
- spec,
257
- operationId: "getUser",
258
- securityValues: { apiKeyHeader: "YOUR_KEY" },
171
+ const prepared = client.prepare({
172
+ spec: openApiDocument,
173
+ operationId: "listUsers",
174
+ parameters: { query: { page: 1 } },
259
175
  });
260
- // Sends: X-API-Key: YOUR_KEY (header name from spec)
261
- ```
262
176
 
263
- ### API key (query)
264
-
265
- ```typescript
266
- const result = await client.send({
267
- spec,
268
- operationId: "getUser",
269
- securityValues: { apiKeyQuery: "YOUR_KEY" },
270
- });
271
- // Appends: ?api_key=YOUR_KEY (param name from spec)
177
+ // prepared.url, prepared.method, prepared.headers, prepared.body
272
178
  ```
273
179
 
274
- ---
275
-
276
- ## Request Body Examples
180
+ ### `client.sendPrepared(prepared)`
277
181
 
278
- ### JSON
182
+ Send a previously prepared request.
279
183
 
280
184
  ```typescript
281
- const result = await client.send({
282
- spec,
283
- operationId: "createUser",
284
- parameters: {
285
- body: { name: "Ada", email: "ada@example.com" },
286
- },
287
- });
185
+ const result = await client.sendPrepared(prepared);
288
186
  ```
289
187
 
290
- ### Form URL-encoded
188
+ ### Result Type
291
189
 
292
190
  ```typescript
293
- const result = await client.send({
294
- spec,
295
- operationId: "login",
296
- parameters: {
297
- body: { username: "ada", password: "secret" },
298
- },
299
- });
191
+ interface RequestResult<T = unknown> {
192
+ response: {
193
+ status: number;
194
+ statusText: string;
195
+ headers: Record<string, string>;
196
+ body: T;
197
+ raw: Response;
198
+ };
199
+ request: {
200
+ url: string;
201
+ method: string;
202
+ headers: Record<string, string>;
203
+ body?: unknown;
204
+ };
205
+ duration: number; // milliseconds
206
+ }
300
207
  ```
301
208
 
302
- ### Multipart Form Data
209
+ ### Error Type
303
210
 
304
211
  ```typescript
305
- const result = await client.send({
306
- spec,
307
- operationId: "uploadFile",
308
- parameters: {
309
- body: {
310
- title: "My Document",
311
- file: new File(["content"], "doc.pdf", { type: "application/pdf" }),
312
- },
313
- },
314
- });
212
+ class OpenApiRequestError extends Error {
213
+ status: number;
214
+ statusText: string;
215
+ headers: Record<string, string>;
216
+ body: unknown;
217
+ request: { url: string; method: string; headers: Record<string, string> };
218
+ }
315
219
  ```
316
220
 
317
221
  ---
318
222
 
319
- ## Batch Requests
223
+ ## Interceptors
320
224
 
321
225
  ```typescript
322
226
  import { createClient } from "@powerduck/openapi-request";
323
227
 
324
- const client = createClient();
325
-
326
- const results = await client.sendBatch([
327
- { spec, operationId: "getUser", parameters: { path: { id: "1" } } },
328
- { spec, operationId: "getUser", parameters: { path: { id: "2" } } },
329
- { spec, operationId: "getUser", parameters: { path: { id: "3" } } },
330
- ], { concurrency: 2 });
331
-
332
- results.forEach((r, i) => {
333
- if ("error" in r) {
334
- console.log(`Request ${i} failed:`, r.error);
335
- } else {
336
- console.log(`Request ${i} status:`, r.response.status);
337
- }
228
+ const client = createClient({
229
+ interceptors: {
230
+ request: async (request) => {
231
+ console.log("Request:", request.method, request.url);
232
+ request.headers["X-Request-ID"] = crypto.randomUUID();
233
+ return request;
234
+ },
235
+ response: async (response) => {
236
+ console.log("Response:", response.status);
237
+ return response;
238
+ },
239
+ error: async (error) => {
240
+ if (error.status === 401) {
241
+ // Refresh token and retry
242
+ return refreshToken().then(() => client.sendPrepared(error.request));
243
+ }
244
+ throw error;
245
+ },
246
+ },
338
247
  });
339
248
  ```
340
249
 
341
250
  ---
342
251
 
343
- ## Postman Scripts
344
-
345
- Run Postman test scripts embedded in `x-postman-scripts`:
252
+ ## TypeScript Types
346
253
 
347
254
  ```typescript
348
- import { createClient, runPostmanScripts } from "@powerduck/openapi-request";
349
-
350
- const client = createClient();
351
- const result = await client.send({
352
- spec,
353
- operationId: "listUsers",
354
- });
355
-
356
- // Run pm.test() scripts from the operation's x-postman-scripts
357
- const testResults = runPostmanScripts(spec, "listUsers", result.response);
358
-
359
- console.log("Passed:", testResults.passed);
360
- console.log("Failed:", testResults.failed);
361
- for (const test of testResults.tests) {
362
- console.log(`${test.passed ? "✓" : "✗"} ${test.name}`);
363
- }
255
+ import type {
256
+ OpenApiClient,
257
+ ClientOptions,
258
+ SendOptions,
259
+ PrepareOptions,
260
+ RequestResult,
261
+ OpenApiRequestError,
262
+ ParameterMap,
263
+ SecurityValues,
264
+ HttpMethod,
265
+ } from "@powerduck/openapi-request";
364
266
  ```
365
267
 
366
268
  ---
367
269
 
368
- ## Error Handling
369
-
370
- ```typescript
371
- try {
372
- const result = await client.send({
373
- spec,
374
- operationId: "getUser",
375
- parameters: { path: { id: "123" } },
376
- });
377
-
378
- if (result.response.status >= 400) {
379
- console.error("API error:", result.response.status, result.response.body);
380
- }
381
- } catch (error) {
382
- if (error.name === "AbortError") {
383
- console.error("Request timed out or was aborted");
384
- } else if (error.name === "OpenApiRequestError") {
385
- console.error("Request preparation failed:", error.message);
386
- } else {
387
- console.error("Network error:", error);
388
- }
389
- }
390
- ```
391
-
392
- ---
393
-
394
- ## Development
395
-
396
- ```bash
397
- # Install dependencies
398
- npm install
399
-
400
- # Type check
401
- npm run typecheck
402
-
403
- # Build (ESM + CJS + type declarations)
404
- npm run build
405
-
406
- # Run tests
407
- npm test
408
-
409
- # Watch mode
410
- npx vitest watch
411
- ```
412
-
413
- ---
414
-
415
- ## Related Packages
416
-
417
- - [`@powerduck/openapi-parser`](https://www.npmjs.com/package/@powerduck/openapi-parser) — OpenAPI 3.2 parser, validator, and upgrader
418
- - [`@powerduck/openapi-codegen`](https://www.npmjs.com/package/@powerduck/openapi-codegen) — Generate runnable request examples in 21 languages
419
- - [`@powerduck/openapi-cli`](https://www.npmjs.com/package/@powerduck/openapi-cli) — CI-ready batch testing for OpenAPI documents
420
- - [`@powerduck/openapi-mcp-server`](https://www.npmjs.com/package/@powerduck/openapi-mcp-server) — Turn OpenAPI docs into MCP servers
421
- - [`@powerduck/x-to-openapi`](https://www.npmjs.com/package/@powerduck/x-to-openapi) — Convert curl commands and Postman Collections to OpenAPI 3.2
422
-
423
- ---
424
-
425
270
  ## License
426
271
 
427
- MIT
272
+ MIT © [POWERDUCK LIMITED](https://www.powerduck.com)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powerduck/openapi-request",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "OpenAPI 3.2 collection debugger with HTTP, SSE and WebSocket support, plus response write-back",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",