@powerduck/openapi-request 0.2.2 → 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.
- package/README.md +192 -631
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,711 +1,272 @@
|
|
|
1
1
|
# @powerduck/openapi-request
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@powerduck/openapi-request)
|
|
4
|
+
[](https://github.com/PowerDuckie/openapi-request/blob/main/LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/@powerduck/openapi-request)
|
|
5
6
|
|
|
6
|
-
|
|
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.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Powerduck is an open-source developer tooling platform for teams building modern API workflows.
|
|
7
12
|
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
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
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### Install
|
|
18
29
|
|
|
19
30
|
```bash
|
|
20
31
|
npm install @powerduck/openapi-request
|
|
21
32
|
```
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## Quick Start
|
|
34
|
+
### Create a client and send a request
|
|
26
35
|
|
|
27
|
-
```
|
|
36
|
+
```typescript
|
|
28
37
|
import { createClient } from "@powerduck/openapi-request";
|
|
29
38
|
|
|
30
39
|
const client = createClient();
|
|
31
40
|
|
|
32
|
-
// 1. Decide how to render before sending anything.
|
|
33
|
-
const prepared = client.prepare({
|
|
34
|
-
spec: openApiDoc,
|
|
35
|
-
target: { path: "/users/{id}", method: "get" },
|
|
36
|
-
});
|
|
37
|
-
console.log(prepared.display.mode); // "response" | "event-list" | "duplex-session"
|
|
38
|
-
console.log(prepared.stream.kind); // "none" | "sse" | "grpc-unary" | ...
|
|
39
|
-
|
|
40
|
-
// 2. Send a one-shot request (HTTP / GraphQL / MCP one-shot).
|
|
41
41
|
const result = await client.send({
|
|
42
|
-
spec:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log("streaming:", info.streaming); // true for SSE, switch UI early
|
|
48
|
-
},
|
|
49
|
-
onEvent: (event) => {
|
|
50
|
-
console.log(event.event, event.data); // incremental SSE events
|
|
42
|
+
spec: openApiDocument,
|
|
43
|
+
operationId: "getUserById",
|
|
44
|
+
parameters: {
|
|
45
|
+
path: { id: "123" },
|
|
46
|
+
query: { include: "profile" },
|
|
51
47
|
},
|
|
52
48
|
});
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
session.onEvent((e) => console.log(e));
|
|
58
|
-
await session.send({ type: "ping" });
|
|
59
|
-
await session.close();
|
|
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
|
|
60
53
|
```
|
|
61
54
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
## API Reference
|
|
65
|
-
|
|
66
|
-
### `createClient(options?)`
|
|
67
|
-
|
|
68
|
-
Returns a client with `prepare`, `send`, `sendMany`, `connect`, `discover`, `writeback`, `dispose`, `probeStreamingResponse`.
|
|
69
|
-
|
|
70
|
-
| Option | Type | Default | Description |
|
|
71
|
-
|--------|------|---------|-------------|
|
|
72
|
-
| `writeBack` | `WriteBackOptions` | `{}` | Controls how responses are merged into the spec. |
|
|
73
|
-
| `response` | `ToResponseOptions` | `{}` | Controls how a live response becomes an OpenAPI Response object. |
|
|
74
|
-
|
|
75
|
-
### `client.prepare(options)` → `PreparedRequest`
|
|
76
|
-
|
|
77
|
-
Pure, synchronous. No I/O. Returns everything the UI needs to choose a renderer.
|
|
78
|
-
|
|
79
|
-
| Field | Type | Description |
|
|
80
|
-
|-------|------|-------------|
|
|
81
|
-
| `protocol` | `string` | `"http"` \| `"sse"` \| `"websocket"` \| `"grpc"` \| `"graphql"` \| `"mcp"` |
|
|
82
|
-
| `transport` | `string` | `"http"` \| `"websocket"` \| `"grpc"` \| `"stdio"` |
|
|
83
|
-
| `display.mode` | `"response"` \| `"event-list"` \| `"duplex-session"` | How to render the result. |
|
|
84
|
-
| `stream.kind` | `StreamKind` | Fine-grained streaming taxonomy. |
|
|
85
|
-
| `stream.expected` | `boolean` | True when the operation declares or implies streaming. |
|
|
86
|
-
| `openapi.extensions` | `Record<string, unknown>` | The seven `x-*` extensions derived from the operation. |
|
|
87
|
-
| `warnings` | `string[]` | Non-fatal issues found during preparation. |
|
|
88
|
-
|
|
89
|
-
### `client.send(options)` → `Promise<SendResult>`
|
|
55
|
+
### Prepare and send separately
|
|
90
56
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
### `client.connect(options)` → `ManualSession`
|
|
94
|
-
|
|
95
|
-
Opens a long-lived session for WebSocket, gRPC or MCP. See [Manual Sessions](#manual-sessions).
|
|
96
|
-
|
|
97
|
-
### `client.discover(options)` → `Promise<DiscoveryResult>`
|
|
98
|
-
|
|
99
|
-
Discovers schema/capabilities for MCP or gRPC.
|
|
100
|
-
|
|
101
|
-
| Protocol | Options |
|
|
102
|
-
|----------|---------|
|
|
103
|
-
| `mcp` | `{ protocol: "mcp", endpoint, headers?, transport?, command?, args?, cwd? }` |
|
|
104
|
-
| `grpc` | `{ protocol: "grpc", address, reflection?, protoPaths?, includeDirs?, metadata?, channelOptions? }` |
|
|
57
|
+
```typescript
|
|
58
|
+
import { createClient } from "@powerduck/openapi-request";
|
|
105
59
|
|
|
106
|
-
|
|
60
|
+
const client = createClient();
|
|
107
61
|
|
|
108
|
-
|
|
62
|
+
// Prepare builds the request without sending
|
|
63
|
+
const prepared = client.prepare({
|
|
64
|
+
spec: openApiDocument,
|
|
65
|
+
operationId: "listUsers",
|
|
66
|
+
parameters: {
|
|
67
|
+
query: { page: 1, limit: 20 },
|
|
68
|
+
},
|
|
69
|
+
});
|
|
109
70
|
|
|
110
|
-
|
|
71
|
+
console.log(prepared.url); // final URL with serialized params
|
|
72
|
+
console.log(prepared.method); // HTTP method
|
|
73
|
+
console.log(prepared.headers); // resolved headers
|
|
111
74
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
| Field | Type | Default | Description |
|
|
117
|
-
|-------|------|---------|-------------|
|
|
118
|
-
| `spec` | `OpenApiDocument` | **required** | The OpenAPI 3.2 document. |
|
|
119
|
-
| `target` | `OperationTarget` | **required** | `{ path?, method?, operationId? }` — identifies the operation. |
|
|
120
|
-
| `values` | `RequestValues` | `{}` | Path/query/header/cookie/body values to inject. |
|
|
121
|
-
| `serverUrl` | `string` | `spec.servers[0].url` | Overrides the server URL. |
|
|
122
|
-
| `serverVariables` | `Record<string,string>` | `{}` | Variables for `{var}` placeholders in the server URL. |
|
|
123
|
-
| `variables` | `Record<string,string>` | `{}` | Postman environment variables (`{{name}}`). |
|
|
124
|
-
| `globals` | `Record<string,string>` | `{}` | Postman global variables. |
|
|
125
|
-
| `localVariables` | `Record<string,string>` | `{}` | Postman local variables. |
|
|
126
|
-
| `auth` | `AuthConfig` | — | `{ type: "bearer"\|"basic"\|"apikey"\|"none", ... }`. |
|
|
127
|
-
| `scripts` | `ScriptConfig` | `{}` | Pre-request / test scripts and `x-postman-scripts` handling. |
|
|
128
|
-
| `runner` | `RuntimeRunOptions` | `{}` | Full postman-runtime passthrough. Highest precedence. |
|
|
129
|
-
| `websocket` | `WebSocketOptions` | `{}` | WebSocket-specific options. |
|
|
130
|
-
| `graphql` | `GraphQLOptions` | `{}` | GraphQL-specific options. |
|
|
131
|
-
| `mcp` | `McpOptions` | `{}` | MCP-specific options. |
|
|
132
|
-
| `grpc` | `any` | `{}` | gRPC-specific options (used by the gRPC adapter). |
|
|
133
|
-
| `timeout` | `number` | `30000` | Convenience shortcut for `runner.timeout.request`. `0` = unlimited. |
|
|
134
|
-
| `maxEvents` | `number` | `100` | Maximum streaming events to retain. |
|
|
135
|
-
| `maxStreamMs` | `number` | `30000` | Maximum streaming duration before sampling stops. |
|
|
136
|
-
| `maxResponseSize` | `number` | — | Hard byte ceiling for the response body. Rejected if `<= 0`. |
|
|
137
|
-
| `writeBack` | `boolean` | `true` | Set `false` to skip OpenAPI write-back. |
|
|
138
|
-
| `signal` | `AbortSignal` | — | Cancels the run; returns a partial result. |
|
|
139
|
-
| `onEvent` | `(event: StreamEvent) => void` | — | Incremental SSE / ndjson events. Called defensively (errors swallowed). |
|
|
140
|
-
| `onResponseStart` | `(info: ResponseStartInfo) => void` | — | Fires when headers arrive. `info.streaming` is the early SSE flag. |
|
|
141
|
-
| `onConsole` | `(log: ConsoleLog) => void` | — | Script `console.*` output. |
|
|
142
|
-
| `onAssertion` | `(a: AssertionResult) => void` | — | Individual test assertion results. |
|
|
143
|
-
| `onOpen` | `(info: {url, protocol?, headers}) => void` | — | WebSocket connection established. |
|
|
144
|
-
|
|
145
|
-
### StreamParserOptions (also on SendOptions)
|
|
146
|
-
|
|
147
|
-
| Field | Type | Default | Description |
|
|
148
|
-
|-------|------|---------|-------------|
|
|
149
|
-
| `maxBufferChars` | `number` | `4194304` (4 Mi) | Max chars buffered while waiting for an event boundary. |
|
|
150
|
-
| `maxEventChars` | `number` | `1048576` (1 Mi) | Max chars in a single event's `data`. |
|
|
151
|
-
| `inheritEventId` | `boolean` | `true` | Attach the last seen `id` to events that omit one. Set `false` for spec-faithful streams. |
|
|
75
|
+
// Send the prepared request
|
|
76
|
+
const result = await client.sendPrepared(prepared);
|
|
77
|
+
```
|
|
152
78
|
|
|
153
|
-
|
|
79
|
+
### With authentication
|
|
154
80
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
### Request JSON structure
|
|
158
|
-
|
|
159
|
-
An HTTP operation is a standard OpenAPI path item. SSE is detected automatically from the response content type or the `x-response-stream` extension.
|
|
160
|
-
|
|
161
|
-
```json
|
|
162
|
-
{
|
|
163
|
-
"openapi": "3.2.0",
|
|
164
|
-
"info": { "title": "example", "version": "1.0.0" },
|
|
165
|
-
"servers": [{ "url": "https://api.example.com" }],
|
|
166
|
-
"paths": {
|
|
167
|
-
"/users/{id}": {
|
|
168
|
-
"get": {
|
|
169
|
-
"operationId": "getUser",
|
|
170
|
-
"parameters": [
|
|
171
|
-
{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
|
|
172
|
-
],
|
|
173
|
-
"responses": {
|
|
174
|
-
"200": {
|
|
175
|
-
"description": "ok",
|
|
176
|
-
"content": { "application/json": {} }
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
"/events": {
|
|
182
|
-
"get": {
|
|
183
|
-
"operationId": "streamEvents",
|
|
184
|
-
"responses": {
|
|
185
|
-
"200": {
|
|
186
|
-
"description": "ok",
|
|
187
|
-
"content": { "text/event-stream": {} }
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
```
|
|
81
|
+
```typescript
|
|
82
|
+
import { createClient } from "@powerduck/openapi-request";
|
|
195
83
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
| Field | Type | Default | Description |
|
|
201
|
-
|-------|------|---------|-------------|
|
|
202
|
-
| `followRedirects` | `boolean` | `true` | Follow HTTP 3xx redirects. |
|
|
203
|
-
| `followOriginalHttpMethod` | `boolean` | `false` | Keep the original method on redirect (e.g. POST stays POST). When `false`, 301/302/303 downgrade to GET. |
|
|
204
|
-
| `maxRedirects` | `number` | `10` | Maximum redirect hops. Must be a non-negative integer. |
|
|
205
|
-
| `protocolVersion` | `"http1" \| "http2" \| "auto"` | `"http1"` | HTTP protocol version. `"http1"` is required for SSE chunked streaming; use `"auto"` for HTTP/2 endpoints. |
|
|
206
|
-
| `strictSSL` | `boolean` | `true` | Reject self-signed / invalid TLS certificates. |
|
|
207
|
-
| `insecureHTTPParser` | `boolean` | `false` | Allow invalid HTTP responses (e.g. malformed headers). |
|
|
208
|
-
| `maxResponseSize` | `number` | — | Hard byte ceiling for the response body. `0` is rejected (would silently yield empty streams). Omit for unbounded. |
|
|
209
|
-
| `maxHeaderSize` | `number` | — | Maximum response header size in bytes. |
|
|
210
|
-
| `useWhatWGUrlParser` | `boolean` | `true` | Use the WHATWG URL parser for request URLs. |
|
|
211
|
-
| `removeRefererHeaderOnRedirect` | `boolean` | `false` | Strip the `Referer` header when following a redirect. |
|
|
212
|
-
| `timings` | `boolean` | `true` | Collect detailed timing data (powers `firstByteMs`, `networkDurationMs`). |
|
|
213
|
-
| `verbose` | `boolean` | `true` | Keep request/response history (powers `replays`). |
|
|
214
|
-
| `implicitCacheControl` | `boolean` | `true` | Add `Cache-Control: no-cache` implicitly. |
|
|
215
|
-
| `implicitTraceHeader` | `boolean` | `true` | Add a trace header implicitly. |
|
|
216
|
-
| `disableCookies` | `boolean` | `false` | Disable the cookie jar. |
|
|
217
|
-
| `cookieJar` | `any` | — | Custom cookie jar instance. |
|
|
218
|
-
| `systemHeaders` | `Record<string,string>` | — | Headers added to every request (e.g. `User-Agent`). |
|
|
219
|
-
| `extendedRootCA` | `string` | — | Path to an additional CA bundle. |
|
|
220
|
-
| `encoding` | `null \| string` | `null` | Response body encoding. **Must be `null` for incremental SSE streaming** — the default `utf8` buffers the body to decode multi-byte boundaries. |
|
|
221
|
-
| `agents` | `{ http?, https? }` | — | Custom HTTP/HTTPS agents. Supplying agents disables the library's socket tracking (stream cancellation falls back to `run.abort()`). |
|
|
222
|
-
| `network.hostLookup` | `{ type, hostIpMap? }` | — | Custom DNS resolution. |
|
|
223
|
-
| `network.restrictedAddresses` | `Record<string,boolean>` | — | Block specific IP addresses (SSRF protection). |
|
|
224
|
-
| `maxInvokableNestedRequests` | `number` | `5` | Max nested requests from scripts (`pm.sendRequest`). |
|
|
225
|
-
| `sslKeyLogFile` | `string` | — | Path to write TLS session keys (for Wireshark debugging). |
|
|
226
|
-
|
|
227
|
-
### `runner` top-level options
|
|
228
|
-
|
|
229
|
-
| Field | Type | Default | Description |
|
|
230
|
-
|-------|------|---------|-------------|
|
|
231
|
-
| `timeout.request` | `number` | `30000` | Per-request timeout in ms. `0` = unlimited. |
|
|
232
|
-
| `timeout.script` | `number` | `15000` | Script execution timeout in ms. |
|
|
233
|
-
| `timeout.global` | `number` | computed | Global run timeout. Derived from `request + streamBudget + 15000` slack. |
|
|
234
|
-
| `iterationCount` | `number` | `1` | Number of iterations. Must be >= 1. |
|
|
235
|
-
| `data` | `Array<Record<string,unknown>>` | — | Data-driven iteration values. When provided, `iterationCount` defaults to `data.length`. |
|
|
236
|
-
| `stopOnError` | `boolean` | `false` | Halt the run on a request error. |
|
|
237
|
-
| `abortOnError` | `boolean` | `false` | Abort immediately on error (no cleanup callbacks). |
|
|
238
|
-
| `stopOnFailure` | `boolean` | `false` | Halt on a failed test assertion. |
|
|
239
|
-
| `abortOnFailure` | `boolean` | `false` | Abort immediately on assertion failure. |
|
|
240
|
-
| `environment` | `any` | built from `variables` | Postman environment. Override to supply a custom VariableScope. |
|
|
241
|
-
| `globals` | `any` | built from `globals` | Postman globals. |
|
|
242
|
-
| `localVariables` | `any` | built from `localVariables` | Postman local variables. |
|
|
243
|
-
| `proxies` | `any` | — | Postman proxy configuration list. |
|
|
244
|
-
| `systemProxy` | `(url, cb) => void` | — | Callback to resolve the system proxy for a URL. |
|
|
245
|
-
| `ignoreProxyEnvironmentVariables` | `boolean` | `false` | Ignore `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` env vars. |
|
|
246
|
-
| `certificates` | `any` | — | Client certificate list. |
|
|
247
|
-
| `delay.item` | `number` | `0` | Delay between requests in ms. |
|
|
248
|
-
| `delay.iteration` | `number` | `0` | Delay between iterations in ms. |
|
|
249
|
-
| `entrypoint.execute` | `string` | — | Name of the item to start from. |
|
|
250
|
-
| `entrypoint.lookupStrategy` | `"idOrName" \| "path"` | — | How to resolve the entrypoint. |
|
|
251
|
-
| `secretResolver` | `(ctx, cb) => void` | — | Resolve secrets referenced in the collection. |
|
|
252
|
-
| `fileResolver` | `unknown` | — | Resolve file references in request bodies. |
|
|
253
|
-
|
|
254
|
-
### SSE streaming example
|
|
255
|
-
|
|
256
|
-
```ts
|
|
257
|
-
const result = await client.send({
|
|
258
|
-
spec,
|
|
259
|
-
target: { path: "/events", method: "get" },
|
|
260
|
-
maxStreamMs: 60000,
|
|
261
|
-
maxEvents: 100,
|
|
262
|
-
onResponseStart: (info) => {
|
|
263
|
-
if (info.streaming) {
|
|
264
|
-
// Switch the UI to the event-list view immediately — do not wait
|
|
265
|
-
// for send() to resolve.
|
|
266
|
-
renderEventList();
|
|
267
|
-
}
|
|
268
|
-
},
|
|
269
|
-
onEvent: (event) => {
|
|
270
|
-
appendEvent(event); // called incrementally, ~every chunk
|
|
84
|
+
const client = createClient({
|
|
85
|
+
securityValues: {
|
|
86
|
+
bearerAuth: "your-token-here",
|
|
87
|
+
apiKey: "your-api-key",
|
|
271
88
|
},
|
|
272
89
|
});
|
|
273
90
|
|
|
274
|
-
|
|
275
|
-
|
|
91
|
+
const result = await client.send({
|
|
92
|
+
spec: openApiDocument,
|
|
93
|
+
operationId: "getProfile",
|
|
94
|
+
});
|
|
276
95
|
```
|
|
277
96
|
|
|
278
97
|
---
|
|
279
98
|
|
|
280
|
-
##
|
|
281
|
-
|
|
282
|
-
### Request JSON structure
|
|
283
|
-
|
|
284
|
-
```json
|
|
285
|
-
{
|
|
286
|
-
"paths": {
|
|
287
|
-
"/ws": {
|
|
288
|
-
"get": {
|
|
289
|
-
"operationId": "wsConnect",
|
|
290
|
-
"x-protocol": "websocket",
|
|
291
|
-
"x-ws": {
|
|
292
|
-
"url": "wss://api.example.com/ws",
|
|
293
|
-
"subprotocols": ["graphql-transport-ws"]
|
|
294
|
-
},
|
|
295
|
-
"responses": { "200": { "description": "duplex" } }
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
```
|
|
99
|
+
## Links
|
|
301
100
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
| `url` | `string` | from `x-ws.url` | Absolute `ws://` or `wss://` URL. Overrides the spec. |
|
|
307
|
-
| `subprotocols` | `string[]` | `[]` | WebSocket subprotocols to negotiate. |
|
|
308
|
-
| `headers` | `Record<string,string>` | `{}` | Extra handshake headers. |
|
|
309
|
-
| `send` | `Array<string \| object \| Uint8Array>` | — | Messages sent immediately after open. |
|
|
310
|
-
| `sendDelayMs` | `number` | — | Delay between consecutive `send` messages. |
|
|
311
|
-
| `maxMessages` | `number` | — | Stop after this many inbound messages. |
|
|
312
|
-
| `maxSessionMs` | `number` | — | Hard cap on total session duration. |
|
|
313
|
-
| `idleTimeoutMs` | `number` | — | Close when no message arrives within this window. |
|
|
314
|
-
| `keepAlive` | `{ intervalMs, payload? }` | — | Application-level ping on an interval. |
|
|
315
|
-
| `closeCode` | `number` | `1000` | Close code sent when terminating. |
|
|
316
|
-
| `closeReason` | `string` | `"Closed"` | Close reason text. |
|
|
317
|
-
| `closeTimeoutMs` | `number` | — | Grace period for the peer's close frame before destroying the socket. |
|
|
318
|
-
| `rejectUnauthorized` | `boolean` | `true` | Reject self-signed TLS certificates. |
|
|
319
|
-
| `maxPayloadBytes` | `number` | — | Cap on retained payload size per frame. |
|
|
320
|
-
| `handshakeTimeoutMs` | `number` | `15000` | Handshake timeout. |
|
|
321
|
-
| `clientOptions` | `Record<string,unknown>` | `{}` | Extra options forwarded to the `ws` constructor. |
|
|
322
|
-
|
|
323
|
-
### Manual WebSocket session
|
|
324
|
-
|
|
325
|
-
```ts
|
|
326
|
-
const session = client.connect({
|
|
327
|
-
kind: "websocket",
|
|
328
|
-
url: "wss://api.example.com/ws",
|
|
329
|
-
subprotocols: ["chat"],
|
|
330
|
-
headers: { Authorization: "Bearer xxx" },
|
|
331
|
-
});
|
|
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)
|
|
332
105
|
|
|
333
|
-
|
|
334
|
-
// e.kind: "open" | "text" | "binary" | "error" | "close" | "upgrade"
|
|
335
|
-
// e.direction: "in" | "out" | "meta"
|
|
336
|
-
// e.data: text payload (or base64 for binary)
|
|
337
|
-
});
|
|
106
|
+
---
|
|
338
107
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
108
|
+
## Features
|
|
109
|
+
|
|
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
|
|
343
125
|
|
|
344
126
|
---
|
|
345
127
|
|
|
346
|
-
##
|
|
347
|
-
|
|
348
|
-
### Request JSON structure
|
|
349
|
-
|
|
350
|
-
```json
|
|
351
|
-
{
|
|
352
|
-
"paths": {
|
|
353
|
-
"/graphql": {
|
|
354
|
-
"post": {
|
|
355
|
-
"operationId": "graphqlQuery",
|
|
356
|
-
"x-protocol": "graphql",
|
|
357
|
-
"x-graphql": {
|
|
358
|
-
"endpoint": "https://api.example.com/graphql",
|
|
359
|
-
"operationType": "query",
|
|
360
|
-
"operationName": "GetUser",
|
|
361
|
-
"query": "query GetUser($id: ID!) { user(id: $id) { name } }",
|
|
362
|
-
"variablesSchema": {
|
|
363
|
-
"type": "object",
|
|
364
|
-
"properties": { "id": { "type": "string" } },
|
|
365
|
-
"required": ["id"]
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
"requestBody": {
|
|
369
|
-
"content": { "application/json": { "schema": { "type": "object" } } }
|
|
370
|
-
},
|
|
371
|
-
"responses": { "200": { "description": "ok" } }
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
```
|
|
128
|
+
## API Reference
|
|
377
129
|
|
|
378
|
-
### `
|
|
130
|
+
### `createClient(options?)`
|
|
379
131
|
|
|
380
|
-
|
|
381
|
-
|-------|------|---------|-------------|
|
|
382
|
-
| `endpoint` | `string` | from `x-graphql.endpoint` | Absolute HTTP(S) URL of the GraphQL endpoint. |
|
|
383
|
-
| `query` | `string` | from `x-graphql.query` | Query or mutation document. Overrides the spec. |
|
|
384
|
-
| `operationName` | `string` | from `x-graphql.operationName` | Required when `query` declares more than one operation. |
|
|
385
|
-
| `variables` | `Record<string,unknown>` | `{}` | GraphQL variables. Merged over sampled values. |
|
|
386
|
-
| `headers` | `Record<string,string>` | `{}` | Extra headers, merged over `values.header` and auth. |
|
|
387
|
-
| `useGet` | `boolean` | `false` | Use HTTP GET with querystring-encoded `query`/`variables` instead of POST. |
|
|
132
|
+
Create an OpenAPI request client.
|
|
388
133
|
|
|
389
|
-
|
|
134
|
+
```typescript
|
|
135
|
+
import { createClient } from "@powerduck/openapi-request";
|
|
390
136
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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,
|
|
396
143
|
});
|
|
397
|
-
// result.discovery contains the introspected schema
|
|
398
144
|
```
|
|
399
145
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
## gRPC
|
|
403
|
-
|
|
404
|
-
### Request JSON structure
|
|
405
|
-
|
|
406
|
-
```json
|
|
407
|
-
{
|
|
408
|
-
"paths": {
|
|
409
|
-
"/grpc": {
|
|
410
|
-
"post": {
|
|
411
|
-
"operationId": "grpcCall",
|
|
412
|
-
"x-protocol": "grpc",
|
|
413
|
-
"x-grpc": {
|
|
414
|
-
"address": "127.0.0.1:50051",
|
|
415
|
-
"service": "demo.echo.Echo",
|
|
416
|
-
"method": "Say",
|
|
417
|
-
"kind": "unary",
|
|
418
|
-
"reflection": true
|
|
419
|
-
},
|
|
420
|
-
"responses": { "200": { "description": "ok" } }
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
```
|
|
146
|
+
### `client.send(options)`
|
|
426
147
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
| Kind | Streaming | Interaction | Button label |
|
|
430
|
-
|------|-----------|-------------|-------------|
|
|
431
|
-
| `unary` | none | One request → one response. Session closes automatically. | "Send and await" |
|
|
432
|
-
| `server_streaming` | server → client | One request → N responses. Session closes on stream end. | "Send and await" |
|
|
433
|
-
| `client_streaming` | client → server | N requests → one response. Send messages, then Finish. | "Send message" + "Finish" |
|
|
434
|
-
| `bidi_streaming` | both | N requests ↔ N responses. Full duplex. | "Send message" + "Finish" |
|
|
435
|
-
|
|
436
|
-
### Manual gRPC session options
|
|
437
|
-
|
|
438
|
-
| Field | Type | Default | Description |
|
|
439
|
-
|-------|------|---------|-------------|
|
|
440
|
-
| `address` | `string` | **required** | `host:port`, no scheme. |
|
|
441
|
-
| `service` | `string` | **required** | Fully-qualified service name, e.g. `"demo.echo.Echo"`. |
|
|
442
|
-
| `method` | `string` | **required** | Method name as declared in proto. Matched case-insensitively as fallback. |
|
|
443
|
-
| `reflection` | `boolean` | — | Set `true` to use server reflection. Mutually exclusive with `protoPaths`. |
|
|
444
|
-
| `protoPaths` | `string[]` | — | `.proto` files or directories. Directories are walked recursively. |
|
|
445
|
-
| `includeDirs` | `string[]` | derived | Import roots for proto-loader. |
|
|
446
|
-
| `metadata` | `Record<string, string \| string[]>` | `{}` | gRPC metadata sent on every call. |
|
|
447
|
-
| `deadlineMs` | `number` | — | Per-call deadline. Enforced by the server (yields DEADLINE_EXCEEDED). |
|
|
448
|
-
| `channelOptions` | `Record<string,unknown>` | `{}` | grpc-js channel options. |
|
|
449
|
-
| `tls` | `boolean \| GrpcTlsOptions` | — | `false`/undefined = insecure; `true` = TLS with system roots; object = custom TLS. |
|
|
450
|
-
| `loaderOptions` | `Record<string,unknown>` | `{}` | Extra proto-loader options. |
|
|
451
|
-
| `reflectionTimeoutMs` | `number` | `5000` | Budget for the whole reflection session. |
|
|
452
|
-
| `reflectionVersion` | `"v1" \| "v1alpha"` | try v1, fall back | Pin a reflection version. |
|
|
453
|
-
| `reflectionHost` | `string` | — | `host` field on reflection requests (virtual-hosted servers). |
|
|
454
|
-
|
|
455
|
-
### gRPC TLS options
|
|
456
|
-
|
|
457
|
-
| Field | Type | Description |
|
|
458
|
-
|-------|------|-------------|
|
|
459
|
-
| `rootCerts` | `Buffer` | CA bundle contents (not a path). Pass `await readFile(p)`. |
|
|
460
|
-
| `privateKey` | `Buffer` | Client key for mTLS. Must be given with `certChain`. |
|
|
461
|
-
| `certChain` | `Buffer` | Client certificate chain for mTLS. Must be given with `privateKey`. |
|
|
462
|
-
| `skipHostnameVerification` | `boolean` | Skip hostname verification only. Chain is still verified. Always produces a warning. |
|
|
463
|
-
|
|
464
|
-
### gRPC session events
|
|
465
|
-
|
|
466
|
-
| `kind` | `direction` | Payload |
|
|
467
|
-
|--------|-------------|---------|
|
|
468
|
-
| `open` | `meta` | `{ address, service, method, kind, descriptorSource }` |
|
|
469
|
-
| `metadata` | `meta` | Response headers (initial metadata). |
|
|
470
|
-
| `data` | `out` | Outbound request message. |
|
|
471
|
-
| `data` | `in` | Inbound response message. |
|
|
472
|
-
| `status` | `meta` | Terminal status: `{ code, statusName, details, metadata }` (trailers). |
|
|
473
|
-
| `error` | `meta` | Error: `{ code, statusName, details, error }`. |
|
|
474
|
-
| `end` | `meta` | Stream ended. |
|
|
475
|
-
| `close` | `meta` | Session closed by the client. |
|
|
476
|
-
|
|
477
|
-
### gRPC example
|
|
478
|
-
|
|
479
|
-
```ts
|
|
480
|
-
const session = client.connect({
|
|
481
|
-
kind: "grpc",
|
|
482
|
-
address: "127.0.0.1:50051",
|
|
483
|
-
reflection: true,
|
|
484
|
-
service: "demo.echo.Echo",
|
|
485
|
-
method: "Say",
|
|
486
|
-
});
|
|
148
|
+
Send an OpenAPI operation.
|
|
487
149
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
150
|
+
```typescript
|
|
151
|
+
const result = await client.send({
|
|
152
|
+
spec: openApiDocument,
|
|
153
|
+
operationId: "getUser",
|
|
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,
|
|
492
163
|
});
|
|
493
|
-
|
|
494
|
-
await session.open();
|
|
495
|
-
await session.send({ text: "hello" });
|
|
496
|
-
// session.state === "closed" after a unary call
|
|
497
164
|
```
|
|
498
165
|
|
|
499
|
-
|
|
166
|
+
### `client.prepare(options)`
|
|
500
167
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
### Request JSON structure
|
|
504
|
-
|
|
505
|
-
```json
|
|
506
|
-
{
|
|
507
|
-
"paths": {
|
|
508
|
-
"/mcp": {
|
|
509
|
-
"post": {
|
|
510
|
-
"operationId": "mcpCall",
|
|
511
|
-
"x-protocol": "mcp",
|
|
512
|
-
"x-transport": "http",
|
|
513
|
-
"x-mcp": {
|
|
514
|
-
"endpoint": "http://127.0.0.1:4200/mcp",
|
|
515
|
-
"method": "tools/call",
|
|
516
|
-
"name": "get_weather",
|
|
517
|
-
"arguments": { "city": "SF" }
|
|
518
|
-
},
|
|
519
|
-
"responses": { "200": { "description": "ok" } }
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
```
|
|
168
|
+
Prepare a request without sending.
|
|
525
169
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
| `endpoint` | `string` | from `x-mcp.endpoint` | Absolute http(s) URL for Streamable HTTP transport. |
|
|
532
|
-
| `headers` | `Record<string,string>` | `{}` | Extra HTTP headers. |
|
|
533
|
-
| `command` | `string` | — | stdio command (e.g. `"npx"`). Required for stdio. |
|
|
534
|
-
| `args` | `string[]` | `[]` | stdio command arguments. |
|
|
535
|
-
| `cwd` | `string` | — | Working directory for the stdio child. |
|
|
536
|
-
| `env` | `Record<string,string \| undefined>` | — | Environment for the stdio child. |
|
|
537
|
-
| `timeoutMs` | `number` | `30000` | Per-request timeout. `0` disables. |
|
|
538
|
-
| `maxBufferBytes` | `number` | — | stdio stdout buffer cap. |
|
|
539
|
-
| `maxStderrBytes` | `number` | — | stdio stderr cap before the child is killed. |
|
|
540
|
-
| `method` | `string` | from `x-mcp.method` | JSON-RPC method, e.g. `"tools/call"`. |
|
|
541
|
-
| `name` | `string` | from `x-mcp.name` | Target tool/prompt/resource name. |
|
|
542
|
-
| `arguments` | `Record<string,unknown>` | `{}` | JSON-RPC params. |
|
|
543
|
-
| `sessionId` | `string` | — | Reuse an existing MCP session ID. |
|
|
544
|
-
| `protocolVersion` | `string` | — | MCP protocol version to negotiate. |
|
|
545
|
-
| `clientInfo` | `{ name, version }` | `{ name: "powerduck", version: "0.1.0" }` | Client info sent in `initialize`. |
|
|
546
|
-
|
|
547
|
-
### Manual MCP session
|
|
548
|
-
|
|
549
|
-
```ts
|
|
550
|
-
const session = client.connect({
|
|
551
|
-
kind: "mcp",
|
|
552
|
-
transport: "streamable-http",
|
|
553
|
-
endpoint: "http://127.0.0.1:4200/mcp",
|
|
170
|
+
```typescript
|
|
171
|
+
const prepared = client.prepare({
|
|
172
|
+
spec: openApiDocument,
|
|
173
|
+
operationId: "listUsers",
|
|
174
|
+
parameters: { query: { page: 1 } },
|
|
554
175
|
});
|
|
555
176
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
// List capabilities
|
|
559
|
-
const tools = await session.listTools();
|
|
560
|
-
const prompts = await session.listPrompts();
|
|
561
|
-
const resources = await session.listResources();
|
|
177
|
+
// prepared.url, prepared.method, prepared.headers, prepared.body
|
|
178
|
+
```
|
|
562
179
|
|
|
563
|
-
|
|
564
|
-
const result = await session.callTool("get_weather", { city: "SF" });
|
|
180
|
+
### `client.sendPrepared(prepared)`
|
|
565
181
|
|
|
566
|
-
|
|
567
|
-
const raw = await session.request("tools/call", { name: "x", arguments: {} });
|
|
182
|
+
Send a previously prepared request.
|
|
568
183
|
|
|
569
|
-
|
|
184
|
+
```typescript
|
|
185
|
+
const result = await client.sendPrepared(prepared);
|
|
570
186
|
```
|
|
571
187
|
|
|
572
|
-
###
|
|
573
|
-
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
188
|
+
### Result Type
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
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
|
+
}
|
|
583
207
|
```
|
|
584
208
|
|
|
585
|
-
|
|
209
|
+
### Error Type
|
|
586
210
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
| `onEvent(listener)` | Subscribe to events. Returns `{ unsubscribe() }`. |
|
|
597
|
-
| `open()` | Connect / initialize. Rejects from non-idle states. |
|
|
598
|
-
| `send(message, options?)` | Send a message. Rejects when not open. |
|
|
599
|
-
| `close(options?)` | Close gracefully. Idempotent. |
|
|
600
|
-
| `waitForClose()` | Promise that resolves when the session closes. |
|
|
601
|
-
|
|
602
|
-
### SessionEventDTO
|
|
603
|
-
|
|
604
|
-
| Field | Type | Description |
|
|
605
|
-
|-------|------|-------------|
|
|
606
|
-
| `protocol` | `string` | Protocol name. |
|
|
607
|
-
| `transport` | `string` | Transport label. |
|
|
608
|
-
| `sessionId` | `string` | Unique session identifier. |
|
|
609
|
-
| `direction` | `"in" \| "out" \| "meta"` | Message direction. |
|
|
610
|
-
| `kind` | `string` | Event discriminator (protocol-specific). |
|
|
611
|
-
| `at` | `number` | Epoch ms timestamp. |
|
|
612
|
-
| `state` | `SessionState?` | Session state at the time of the event. |
|
|
613
|
-
| `data` | `Cloneable?` | Message payload. |
|
|
614
|
-
| `error` | `Cloneable?` | Error payload. |
|
|
615
|
-
| `meta` | `Cloneable?` | Metadata (headers, status code, etc.). |
|
|
211
|
+
```typescript
|
|
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
|
+
}
|
|
219
|
+
```
|
|
616
220
|
|
|
617
221
|
---
|
|
618
222
|
|
|
619
|
-
##
|
|
620
|
-
|
|
621
|
-
| Field | Type | Description |
|
|
622
|
-
|-------|------|-------------|
|
|
623
|
-
| `protocol` | `ProtocolName` | `"http"` \| `"sse"` \| etc. |
|
|
624
|
-
| `request` | `{ method, url, headers, body? }` | The request that was sent. |
|
|
625
|
-
| `response.status` | `number` | HTTP status code. `0` if no response. |
|
|
626
|
-
| `response.statusText` | `string` | Status text. |
|
|
627
|
-
| `response.headers` | `Record<string,string>` | Response headers (case preserved). |
|
|
628
|
-
| `response.contentType` | `string?` | Parsed content type. |
|
|
629
|
-
| `response.body` | `unknown` | Parsed body (non-streaming). |
|
|
630
|
-
| `response.text` | `string?` | Raw body text. |
|
|
631
|
-
| `response.events` | `StreamEvent[]` | Collected streaming events. |
|
|
632
|
-
| `response.timings` | `{ startedAt, endedAt, durationMs, firstByteMs?, networkDurationMs? }` | Timing data. |
|
|
633
|
-
| `response.sizeBytes` | `number` | Body bytes received. |
|
|
634
|
-
| `response.truncated` | `boolean?` | True if sampling stopped before natural completion. |
|
|
635
|
-
| `response.stopReason` | `StopReason?` | Which limit fired: `"maxEvents" \| "maxStreamMs" \| "maxResponseSize" \| "aborted" \| "hardTimeout"`. |
|
|
636
|
-
| `response.droppedEvents` | `number?` | Events discarded by the parser's size caps. |
|
|
637
|
-
| `scripts` | `ScriptReport?` | Pre-request / test script results. |
|
|
638
|
-
| `cookies` | `Array<{name, value, domain?, path?}>` | Cookies set by the response. |
|
|
639
|
-
| `replays` | `ReplayRecord[]` | Auxiliary traffic (OAuth refreshes, redirects). |
|
|
640
|
-
| `error` | `{ message, code?, name?, stack? }?` | Fatal error, if any. |
|
|
641
|
-
| `collection` | `any` | Generated Postman collection (HTTP only). |
|
|
642
|
-
| `environment` | `any` | Generated Postman environment. |
|
|
643
|
-
| `responseFragment` | `any` | OpenAPI Response object derived from the live call. |
|
|
644
|
-
| `responseStatusCode` | `string` | Status code the fragment was filed under. |
|
|
645
|
-
| `patchedSpec` | `OpenApiDocument?` | Spec with the response merged in. |
|
|
646
|
-
| `writeBackSkippedReason` | `string?` | Why write-back was skipped. |
|
|
223
|
+
## Interceptors
|
|
647
224
|
|
|
648
|
-
|
|
225
|
+
```typescript
|
|
226
|
+
import { createClient } from "@powerduck/openapi-request";
|
|
649
227
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
```ts
|
|
672
|
-
import { ProtoKitError } from "@powerduck/openapi-request";
|
|
673
|
-
|
|
674
|
-
try {
|
|
675
|
-
await client.send(options);
|
|
676
|
-
} catch (err) {
|
|
677
|
-
if (err instanceof ProtoKitError) {
|
|
678
|
-
console.error(err.code, err.message);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
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
|
+
},
|
|
247
|
+
});
|
|
681
248
|
```
|
|
682
249
|
|
|
683
250
|
---
|
|
684
251
|
|
|
685
|
-
##
|
|
686
|
-
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
252
|
+
## TypeScript Types
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
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";
|
|
692
266
|
```
|
|
693
267
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
| File | Coverage |
|
|
697
|
-
|------|----------|
|
|
698
|
-
| `tests/sse-parser.test.ts` | 29 tests — SSE parser edge cases, size caps, chunk boundaries. |
|
|
699
|
-
| `tests/runner-options.test.ts` | 19 tests — HTTP option validation and defaults. |
|
|
700
|
-
| `tests/session-hub.test.ts` | 9 tests — event hub, listener safety, ring buffer. |
|
|
701
|
-
| `tests/grpc-session.test.ts` | 10 tests — unary success/error state, streaming, lifecycle. |
|
|
702
|
-
| `tests/ws-session.test.ts` | 10 tests — connect, send/receive, error state, lifecycle. |
|
|
703
|
-
| `tests/client.test.ts` | 8 tests — prepare() protocol inference. |
|
|
704
|
-
| `tests/session.test.ts` | 6 tests — unified createManualSession routing. |
|
|
705
|
-
| `tests/integration.test.ts` | 4 tests — end-to-end HTTP + SSE streaming. |
|
|
706
|
-
| `tests/clone.test.ts` | 8 tests — payload cloning. |
|
|
707
|
-
| `tests/detect.test.ts` | 4 tests — SSE content-type detection. |
|
|
268
|
+
---
|
|
708
269
|
|
|
709
270
|
## License
|
|
710
271
|
|
|
711
|
-
MIT
|
|
272
|
+
MIT © [POWERDUCK LIMITED](https://www.powerduck.com)
|
package/package.json
CHANGED