@kubb/plugin-fetch 5.14.0-canary.20261109T134631 → 6.0.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/dist/index.cjs +11 -1771
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -238
- package/dist/index.d.ts +3 -238
- package/dist/index.js +12 -1749
- package/dist/index.js.map +1 -1
- package/dist/templates/client.d.ts +47 -2
- package/docs/guides/authentication.md +14 -2
- package/docs/guides/base-url.md +44 -2
- package/docs/guides/calling-operations.md +4 -0
- package/docs/guides/error-handling.md +31 -8
- package/docs/guides/interceptors.md +34 -14
- package/docs/guides/migrate-to-v6.md +235 -0
- package/docs/guides/serialization.md +117 -57
- package/docs/guides/server-sent-events.md +129 -28
- package/docs/guides/transport.md +27 -19
- package/docs/options.md +14 -4
- package/docs/overview.md +1 -1
- package/kubb-extension.json +1 -1
- package/package.json +8 -10
- package/src/index.ts +0 -1
- package/src/plugin.ts +7 -74
- package/src/types.ts +2 -2
- package/templates/client.ts +65 -84
- package/dist/client-Dn80AjdR.d.ts +0 -39
- package/dist/core-D6FAGTsL.d.ts +0 -2
- package/dist/templates/core.d.ts +0 -2
- package/dist/templates/fetch.d.ts +0 -6
- package/dist/templates/serializers.d.ts +0 -1
- package/dist/templates/standardSchema.d.ts +0 -1
- package/src/generators/clientGenerator.tsx +0 -10
- package/src/templates.ts +0 -16
- package/templates/core.ts +0 -2
- package/templates/fetch.ts +0 -2
- package/templates/serializers.ts +0 -2
- package/templates/standardSchema.ts +0 -2
|
@@ -7,7 +7,7 @@ Each event on the wire has up to four fields, and a blank line ends it:
|
|
|
7
7
|
| Field | Meaning |
|
|
8
8
|
| ------- | -------------------------------------------------------------------------------- |
|
|
9
9
|
| `event` | The event name. `message` when the server sends none. |
|
|
10
|
-
| `data` | The payload. Parsed as JSON
|
|
10
|
+
| `data` | The payload. Parsed as JSON when it is valid JSON, and kept as text otherwise. |
|
|
11
11
|
| `id` | The last event ID. It carries over to later events and resumes a dropped stream. |
|
|
12
12
|
| `retry` | The reconnect delay in milliseconds. |
|
|
13
13
|
|
|
@@ -33,7 +33,7 @@ For one payload on every event, such as a feed of pet changes. The item declares
|
|
|
33
33
|
|
|
34
34
|
### Describe it
|
|
35
35
|
|
|
36
|
-
In OpenAPI 3.2, put one `oneOf` member per event name on `itemSchema`, with a `const` on `event`.
|
|
36
|
+
In OpenAPI 3.2, put one `oneOf` member per event name on `itemSchema`, with a `const` on `event`. Point `data` at the payload schema with `$ref`. A `data` string described with `contentMediaType: application/json` and `contentSchema` is typed and parsed as that schema too:
|
|
37
37
|
|
|
38
38
|
```yaml
|
|
39
39
|
/chat:
|
|
@@ -53,13 +53,16 @@ In OpenAPI 3.2, put one `oneOf` member per event name on `itemSchema`, with a `c
|
|
|
53
53
|
id: { type: string }
|
|
54
54
|
retry: { type: integer, minimum: 0 }
|
|
55
55
|
oneOf:
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
|
|
56
|
+
- type: object
|
|
57
|
+
required: [event, data]
|
|
58
|
+
properties:
|
|
59
|
+
event: { type: string, const: message_delta }
|
|
60
|
+
data: { $ref: '#/components/schemas/MessageDelta' }
|
|
61
|
+
- type: object
|
|
62
|
+
required: [event]
|
|
63
|
+
properties:
|
|
64
|
+
event: { type: string, const: ping }
|
|
65
|
+
data: { type: string }
|
|
63
66
|
```
|
|
64
67
|
|
|
65
68
|
In OpenAPI 3.0 and 3.1, put the same `oneOf` on `schema`, with a `discriminator` on `event`. Kubb reads `schema` as one event.
|
|
@@ -173,16 +176,63 @@ for await (const { data } of stream) {
|
|
|
173
176
|
}
|
|
174
177
|
```
|
|
175
178
|
|
|
179
|
+
## JSON Lines
|
|
180
|
+
|
|
181
|
+
An operation whose only success content type is a JSON sequence, such as `application/jsonl`, `application/x-ndjson`, or `application/json-seq`, streams one item per line. Each line is parsed as JSON, so the loop gets each item as soon as its line arrives instead of waiting for the whole body. An `application/json-seq` record must fit on one line; a record that spans several lines fails to parse.
|
|
182
|
+
|
|
183
|
+
### Describe it
|
|
184
|
+
|
|
185
|
+
Point `itemSchema` at the schema of one line:
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
/logs:
|
|
189
|
+
get:
|
|
190
|
+
operationId: streamLogs
|
|
191
|
+
responses:
|
|
192
|
+
'200':
|
|
193
|
+
description: One LogEntry per line
|
|
194
|
+
content:
|
|
195
|
+
application/jsonl:
|
|
196
|
+
itemSchema:
|
|
197
|
+
$ref: '#/components/schemas/LogEntry'
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
In OpenAPI 3.0 and 3.1, `schema` on one of these media types describes one line.
|
|
201
|
+
|
|
202
|
+
### The stream
|
|
203
|
+
|
|
204
|
+
```text
|
|
205
|
+
{"level": "info", "message": "started"}
|
|
206
|
+
{"level": "warn", "message": "slow"}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The call asks for the declared media type in `Accept`. A line may end with CRLF, CR, or LF, blank lines are skipped, and a last line without a line break still counts. A line that is not valid JSON throws a `SyntaxError` from the loop.
|
|
210
|
+
|
|
211
|
+
### Use it
|
|
212
|
+
|
|
213
|
+
Each item is a `LogEntry`, with no event wrapper:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { streamLogs } from './gen/clients/streamLogs'
|
|
217
|
+
|
|
218
|
+
for await (const entry of await streamLogs()) {
|
|
219
|
+
console.info(entry.level, entry.message) // LogEntry
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`terminator`, `maxEventSize`, and `signal` work per line. `transform` only applies to Server-Sent Events. With `validator: 'zod'`, the validator runs on each line. `lastEventId` stays empty, since JSON Lines has no event IDs.
|
|
224
|
+
|
|
176
225
|
## Stream options
|
|
177
226
|
|
|
178
227
|
Pass these on the call, next to its request params:
|
|
179
228
|
|
|
180
|
-
| Option | What it does
|
|
181
|
-
| -------------- |
|
|
182
|
-
| `terminator` | Ends the stream when `data` equals this value, such as `[DONE]`. Without it, the sentinel reaches your loop.
|
|
183
|
-
| `transform` | Maps each event before validation, for example to camelCase its keys.
|
|
184
|
-
| `maxEventSize` | Largest event in characters. A larger one throws a `RangeError`. Defaults to `16 * 1024 * 1024`. Set `Infinity` to turn it off.
|
|
185
|
-
| `signal` | An `AbortSignal` that ends the request and the loop.
|
|
229
|
+
| Option | What it does |
|
|
230
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
231
|
+
| `terminator` | Ends the stream when `data` equals this value, such as `[DONE]`. Without it, the sentinel reaches your loop, or fails the `validator` when it does not match the event schema. |
|
|
232
|
+
| `transform` | Maps each event before validation, for example to camelCase its keys. |
|
|
233
|
+
| `maxEventSize` | Largest event in characters. A larger one throws a `RangeError`. Defaults to `16 * 1024 * 1024`. Set `Infinity` to turn it off. |
|
|
234
|
+
| `signal` | An `AbortSignal` that ends the request and the loop. |
|
|
235
|
+
| `eventShape` | What the validator checks: `data` for the payload, `event` for the whole event. Generated calls set it. |
|
|
186
236
|
|
|
187
237
|
To stop reading, `break` out of the loop. The runtime then cancels the reader and releases the connection.
|
|
188
238
|
|
|
@@ -202,6 +252,16 @@ const { stream } = await streamChat({
|
|
|
202
252
|
|
|
203
253
|
Set `validate: false` on the call or the client to skip it. See [Handle errors](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/error-handling) for the validation options.
|
|
204
254
|
|
|
255
|
+
## Read the response headers
|
|
256
|
+
|
|
257
|
+
A stream result carries the response `headers`, as a regular result does. When the response declares headers, such as an `X-Request-ID`, the call returns `StreamResultWithHeaders`, so `headers.get` suggests their names for the narrowed `status`:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
const result = await streamChat({ body: { prompt: 'Hi' } })
|
|
261
|
+
|
|
262
|
+
console.info(result.headers.get('X-Request-ID'))
|
|
263
|
+
```
|
|
264
|
+
|
|
205
265
|
## Resume after a drop
|
|
206
266
|
|
|
207
267
|
A dropped connection ends the loop with the transport's error. The result's `lastEventId` is the `id` of the last event the server completed, so a new call with a `Last-Event-ID` header continues where the stream stopped. The runtime does not reconnect by itself, because many streams, such as a chat completion, end on purpose, and the retry policy belongs to your app:
|
|
@@ -239,7 +299,7 @@ try {
|
|
|
239
299
|
}
|
|
240
300
|
```
|
|
241
301
|
|
|
242
|
-
With `throwOnError: false`, the call resolves instead of throwing, as a regular call does. The result then carries the `status` and the `error` body, its `data` is `undefined`, and its stream yields nothing. Check `data`
|
|
302
|
+
With `throwOnError: false`, the call resolves instead of throwing, as a regular call does. The result then carries the `status` and the `error` body, its `data` is `undefined`, and its stream yields nothing. Check `data` to tell the two apart. An empty error body sets `error` to `{ status, statusText }`. Checking `status` narrows `error` to the body the document declares for that status:
|
|
243
303
|
|
|
244
304
|
```typescript
|
|
245
305
|
const result = await streamChat({ body, throwOnError: false })
|
|
@@ -253,10 +313,51 @@ As on a regular call, `.unwrap()` resolves to the events. A failed call rejects
|
|
|
253
313
|
for await (const event of await streamChat({ body }).unwrap()) render(event)
|
|
254
314
|
```
|
|
255
315
|
|
|
256
|
-
A stream call that gets a JSON
|
|
316
|
+
A stream call that gets a JSON error body parses it like any other call. A 2xx answer that is not an event stream, such as a JSON body, throws a `TypeError` with the body as its `cause`, and an empty 2xx answer ends as an empty stream. An operation that declares another content type next to `text/event-stream` or a JSON Lines type is a normal call. See [JSON or an event stream](#json-or-an-event-stream).
|
|
257
317
|
|
|
258
318
|
Declare heartbeat and `error` events in the document, so each has a `case` in your `switch`.
|
|
259
319
|
|
|
320
|
+
## JSON or an event stream
|
|
321
|
+
|
|
322
|
+
Some APIs, such as Wikimedia EventStreams, declare the same response as `application/json` and as `text/event-stream`. Kubb generates a normal call for it, which returns a `RequestResult`. The call sends `Accept: text/event-stream`, so the server streams events. To ask for JSON, pass `contentType: { response: 'application/json' }`. Wikimedia answers JSON with an endless JSON Lines body, so also pass `parseAs: 'stream'` and read `result.data` with `parseJsonLines({ stream: result.data })` from `.kubb/client`. Without it, the call waits for a body that never ends.
|
|
323
|
+
|
|
324
|
+
Check `contentType` on the result. For `text/event-stream`, `data` is the raw `ReadableStream<Uint8Array>`. Pass it to `parseEventStream` from `.kubb/client` to read the events:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { parseEventStream } from './gen/.kubb/client'
|
|
328
|
+
import { getV2StreamRecentchange } from './gen/clients/getV2StreamRecentchange'
|
|
329
|
+
|
|
330
|
+
const result = await getV2StreamRecentchange({ signal })
|
|
331
|
+
|
|
332
|
+
if (result.contentType === 'text/event-stream') {
|
|
333
|
+
for await (const { data } of parseEventStream(result.data)) render(data)
|
|
334
|
+
} else {
|
|
335
|
+
render(result.data) // the JSON body
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
To get a stream result with `lastEventId`, send the same request with `client.stream`. It throws a `TypeError` when the server answers with JSON:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { client } from './gen/.kubb/client'
|
|
343
|
+
|
|
344
|
+
const result = await client.stream({ method: 'GET', url: '/v2/stream/recentchange', eventShape: 'data', signal })
|
|
345
|
+
for await (const event of result) render(event.data)
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Stream with the client
|
|
349
|
+
|
|
350
|
+
`client.stream(config)` runs the same interceptors and transport as `client(config)`. On a 2xx answer, it reads the body item by item and validates each item with `validator.response`. A non-2xx body is decoded whole, so `error` stays a value. Type it with the responses record, `ThrowOnError`, and the item type, which defaults to `ServerSentEvent` around the success body:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
import { client, type SuccessOf } from './gen/.kubb/client'
|
|
354
|
+
import type { StreamLogsResponses } from './gen/models/StreamLogs'
|
|
355
|
+
|
|
356
|
+
const result = await client.stream<StreamLogsResponses, true, SuccessOf<StreamLogsResponses>>({ method: 'GET', url: '/logs' })
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
To stream another format, register a `stream` handler, as [Configure serialization](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/serialization#add-a-media-type) shows.
|
|
360
|
+
|
|
260
361
|
## Return only the events
|
|
261
362
|
|
|
262
363
|
With `returnType: 'data'` in the plugin options, a stream call returns its events, as a regular call returns only its body:
|
|
@@ -269,16 +370,16 @@ pluginFetch({ returnType: 'data' })
|
|
|
269
370
|
|
|
270
371
|
Both modes read the same events. The table compares them for one call:
|
|
271
372
|
|
|
272
|
-
|
|
|
273
|
-
|
|
|
274
|
-
| The call returns
|
|
275
|
-
| Call it
|
|
276
|
-
| Loop over the events
|
|
277
|
-
| Other ways to loop
|
|
278
|
-
| `event.data`
|
|
279
|
-
| A failed call, by default
|
|
280
|
-
| With `throwOnError: false`
|
|
281
|
-
| `status`, `response`, `lastEventId` | available | only with `throwOnError: false` |
|
|
373
|
+
| | `returnType: 'full'` (default) | `returnType: 'data'` |
|
|
374
|
+
| ---------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
375
|
+
| The call returns | `Unwrappable<StreamResult<…>>` | `Promise<UnwrappedStreamResult<…>>` |
|
|
376
|
+
| Call it | `const result = await streamPetEvents({ path: { petId: 1 } })` | `const events = await streamPetEvents({ path: { petId: 1 } })` |
|
|
377
|
+
| Loop over the events | `for await (const event of result.stream)` | `for await (const event of events)` |
|
|
378
|
+
| Other ways to loop | `result`, `result.data`, or `.unwrap()` | none |
|
|
379
|
+
| `event.data` | `PetEvent` | `PetEvent` |
|
|
380
|
+
| A failed call, by default | throws a `ResponseError` | throws a `ResponseError` |
|
|
381
|
+
| With `throwOnError: false` | the full result | the full result, as in `'full'` |
|
|
382
|
+
| `status`, `headers`, `response`, `lastEventId` | available | only with `throwOnError: false` |
|
|
282
383
|
|
|
283
384
|
Loop over the events the call resolves to:
|
|
284
385
|
|
|
@@ -323,7 +424,7 @@ The events alone carry no `response` or `lastEventId`. To resume a dropped strea
|
|
|
323
424
|
|
|
324
425
|
## The runtime files
|
|
325
426
|
|
|
326
|
-
`.kubb/sse.ts` holds the
|
|
427
|
+
`.kubb/sse.ts` holds `parseEventStream`, `parseJsonLines`, and the `eventStream`, `jsonLines`, and `streamMediaTypes` media types. The `ServerSentEvent`, `TypedEvent`, `EventStreamOptions`, `StreamOptions`, `StreamResult`, and `StreamResultWithHeaders` types live in `.kubb/core.ts`. A stream operation takes `StreamOptions` and returns `StreamResult`, or `StreamResultWithHeaders` when the response declares headers, the stream versions of `Options`, `RequestResult`, and `RequestResultWithHeaders`. `.kubb/client.ts` re-exports `sse.ts`, so everything imports from the client. Kubb regenerates these files on each run, so do not edit them:
|
|
327
428
|
|
|
328
429
|
```typescript
|
|
329
430
|
import type { ServerSentEvent } from './gen/.kubb/client'
|
package/docs/guides/transport.md
CHANGED
|
@@ -6,10 +6,10 @@ The transport is a function. Use interceptors for request headers or response lo
|
|
|
6
6
|
|
|
7
7
|
## Write a transport function
|
|
8
8
|
|
|
9
|
-
`@kubb/plugin-fetch` types the transport as a function that receives a fully resolved request and returns a
|
|
9
|
+
`@kubb/plugin-fetch` types the transport as a function that receives a fully resolved request and returns a web `Response`:
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
|
-
type Transport = (request: ResolvedRequest) => Promise<TransportResult>
|
|
12
|
+
type Transport = (request: ResolvedRequest) => Promise<Response | TransportResult>
|
|
13
13
|
|
|
14
14
|
type ResolvedRequest = {
|
|
15
15
|
url: string
|
|
@@ -18,22 +18,37 @@ type ResolvedRequest = {
|
|
|
18
18
|
body?: RequestBody
|
|
19
19
|
signal?: AbortSignal
|
|
20
20
|
credentials?: RequestCredentials
|
|
21
|
-
options?:
|
|
22
|
-
|
|
21
|
+
options?: NativeOptions
|
|
22
|
+
// Set when the call streams the response. Do not buffer the body.
|
|
23
|
+
stream?: boolean
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
type TransportResult
|
|
26
|
-
|
|
27
|
-
status: number
|
|
28
|
-
statusText: string
|
|
29
|
-
headers: Headers
|
|
30
|
-
contentType?: string
|
|
26
|
+
type TransportResult = {
|
|
27
|
+
webResponse: Response
|
|
31
28
|
request: Request
|
|
32
29
|
response: Response
|
|
33
30
|
}
|
|
34
31
|
```
|
|
35
32
|
|
|
36
|
-
The
|
|
33
|
+
The `ResolvedRequest` has the URL built, the query and body encoded, and auth headers set. Return the `Response` with its body unread. Return a `TransportResult` to set the native `request` and `response` on results and errors.
|
|
34
|
+
|
|
35
|
+
## Send with another library
|
|
36
|
+
|
|
37
|
+
A library that returns a `Response`, such as [ky](https://github.com/sindresorhus/ky), needs only the send. `toRequestInit(request)` gives the `RequestInit` that `fetch` and ky take:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import ky from 'ky'
|
|
41
|
+
import { client, toRequestInit } from './gen/.kubb/client'
|
|
42
|
+
|
|
43
|
+
client.setConfig({
|
|
44
|
+
// The client splits success from error itself, so ky must not throw or retry on an error status. ky's `timeout`
|
|
45
|
+
// (10 seconds by default) only waits for the response headers and never ends a started body or stream. Turn it
|
|
46
|
+
// off and bound the whole call with `signal: AbortSignal.timeout(ms)`.
|
|
47
|
+
transport: (request) => ky(request.url, { ...toRequestInit(request), throwHttpErrors: false, retry: 0, timeout: false }),
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The library must hand back the body unread. `result.request` is then a `Request` built from the resolved request, without its body, since the library keeps its own. With a relative URL, it is the resolved request itself.
|
|
37
52
|
|
|
38
53
|
## Mock the network in tests
|
|
39
54
|
|
|
@@ -44,14 +59,7 @@ import { createClient } from './gen/.kubb/client'
|
|
|
44
59
|
import { getPetById } from './gen/clients/getPetById'
|
|
45
60
|
|
|
46
61
|
const testClient = createClient({
|
|
47
|
-
transport: async (
|
|
48
|
-
data: { id: 1, name: 'Fluffy' },
|
|
49
|
-
status: 200,
|
|
50
|
-
statusText: 'OK',
|
|
51
|
-
headers: new Headers(),
|
|
52
|
-
request: new Request(request.url),
|
|
53
|
-
response: new Response(),
|
|
54
|
-
}),
|
|
62
|
+
transport: async () => Response.json({ id: 1, name: 'Fluffy' }),
|
|
55
63
|
})
|
|
56
64
|
|
|
57
65
|
const { data } = await getPetById({ path: { petId: 1 }, client: testClient })
|
package/docs/options.md
CHANGED
|
@@ -23,7 +23,7 @@ Pass these options to `pluginFetch()`. Shared options link to [Shared plugin opt
|
|
|
23
23
|
|
|
24
24
|
### baseURL
|
|
25
25
|
|
|
26
|
-
Base URL prepended to every request. When omitted, each request uses the operation's relative path from the spec. A value with a `${...}` interpolation is emitted as a template literal in the generated client config, so `baseURL: '${process.env.API_URL}'` reads the environment variable at runtime.
|
|
26
|
+
Base URL prepended to every request. When omitted, each request uses the operation's relative path from the spec. A path or operation that declares its own `servers` uses that URL instead. See [Set the base URL](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/base-url). A value with a `${...}` interpolation is emitted as a template literal in the generated client config, so `baseURL: '${process.env.API_URL}'` reads the environment variable at runtime.
|
|
27
27
|
|
|
28
28
|
| | |
|
|
29
29
|
| -------- | -------- |
|
|
@@ -63,7 +63,7 @@ Default value. Does no validation and returns the response cast to the generated
|
|
|
63
63
|
:::
|
|
64
64
|
|
|
65
65
|
:::field{name="'zod'"}
|
|
66
|
-
Validates the success response body, and the error body
|
|
66
|
+
Validates the success response body, and the error body against the schema of its status.
|
|
67
67
|
:::
|
|
68
68
|
|
|
69
69
|
:::field{name="{ request?: 'zod', params?: 'zod', response?: 'zod' }"}
|
|
@@ -107,7 +107,7 @@ Generates a class-based SDK. Each instance takes its own client config, so envir
|
|
|
107
107
|
::field-group
|
|
108
108
|
|
|
109
109
|
:::field{name="'tag'"}
|
|
110
|
-
Default value for `sdk.mode`. Generates one class per tag, such as `PetClient`. Set `sdk.name` to also get a root class that shares one
|
|
110
|
+
Default value for `sdk.mode`. Generates one class per tag, such as `PetClient`. Set `sdk.name` to also get a root class that shares one client with every tag, used as `new PetStore(config).pet.getPetById(...)`.
|
|
111
111
|
:::
|
|
112
112
|
|
|
113
113
|
:::field{name="'flat'"}
|
|
@@ -125,6 +125,16 @@ const pet = new PetClient({ baseURL: 'https://petstore.swagger.io/v2' })
|
|
|
125
125
|
const { data } = await pet.getPetById({ path: { petId: 1 } })
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
+
Each SDK class exposes its client as the read-only `client` field. Interceptors on the root class's `client` apply to every tag:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const api = new PetStore({ baseURL: 'https://petstore.swagger.io/v2' })
|
|
132
|
+
api.client.interceptors.request.use((request) => request)
|
|
133
|
+
await api.pet.getPetById({ path: { petId: 1 } })
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A tag class also accepts an existing client, as in `new PetClient(api.client)`.
|
|
137
|
+
|
|
128
138
|
### returnType
|
|
129
139
|
|
|
130
140
|
Shape of the value a generated call resolves to.
|
|
@@ -138,7 +148,7 @@ Shape of the value a generated call resolves to.
|
|
|
138
148
|
::field-group
|
|
139
149
|
|
|
140
150
|
:::field{name="'full'"}
|
|
141
|
-
Default value. Returns `{ status, data, error, contentType, request, response }`.
|
|
151
|
+
Default value. Returns `{ status, data, error, contentType, headers, request, response }`. `headers` is the response's `Headers`. For an operation whose responses declare `headers`, its `get` suggests those names once `status` is narrowed, and the values stay strings.
|
|
142
152
|
:::
|
|
143
153
|
|
|
144
154
|
:::field{name="'data'"}
|
package/docs/overview.md
CHANGED
|
@@ -12,7 +12,7 @@ The query plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, and `@k
|
|
|
12
12
|
|
|
13
13
|
## Options worth knowing
|
|
14
14
|
|
|
15
|
-
- `baseURL` is prepended to every request. The spec's `servers` URL is not used, so without `baseURL` you set one at runtime with `client.setConfig({ baseURL })`.
|
|
15
|
+
- `baseURL` is prepended to every request. The spec's top-level `servers` URL is not used, so without `baseURL` you set one at runtime with `client.setConfig({ baseURL })`. Operations with their own `servers`, on the path or the operation, call that URL instead.
|
|
16
16
|
- `validator: 'zod'` validates response bodies with the schemas from `@kubb/plugin-zod`. Use `{ request: 'zod', response: 'zod' }` to also validate request bodies.
|
|
17
17
|
- `throwOnErrorDefault: false` returns non-2xx responses as values instead of throwing. A single call can still pass `throwOnError`.
|
|
18
18
|
- `returnType: 'data'` makes a call resolve to the success body instead of the full result. With `throwOnError: false`, the call still returns the full result.
|
package/kubb-extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/plugin-fetch",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "Generate a slim, type-safe HTTP client with Kubb based on the native Fetch api.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api-client",
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
"src",
|
|
40
40
|
"dist",
|
|
41
41
|
"templates",
|
|
42
|
+
"!templates/config.ts",
|
|
43
|
+
"!templates/core.ts",
|
|
44
|
+
"!templates/serializers.ts",
|
|
45
|
+
"!templates/standardSchema.ts",
|
|
42
46
|
"kubb-extension.json",
|
|
43
47
|
"docs",
|
|
44
48
|
"!/**/**.test.**",
|
|
@@ -67,8 +71,7 @@
|
|
|
67
71
|
"./package.json": "./package.json",
|
|
68
72
|
"./kubb-extension.json": "./kubb-extension.json",
|
|
69
73
|
"./templates/*.ts": {
|
|
70
|
-
"types": "./dist/templates/*.d.ts"
|
|
71
|
-
"default": "./templates/*.ts"
|
|
74
|
+
"types": "./dist/templates/*.d.ts"
|
|
72
75
|
},
|
|
73
76
|
"./templates/*.js": {
|
|
74
77
|
"types": "./dist/templates/*.d.ts"
|
|
@@ -80,15 +83,10 @@
|
|
|
80
83
|
"registry": "https://registry.npmjs.org/"
|
|
81
84
|
},
|
|
82
85
|
"dependencies": {
|
|
83
|
-
"@kubb/kit": "^5.
|
|
84
|
-
"@kubb/plugin-client": "^
|
|
85
|
-
"@kubb/plugin-ts": "^5.5.0-canary.20261109T134631",
|
|
86
|
-
"@kubb/plugin-zod": "^5.9.0-canary.20261109T134631",
|
|
87
|
-
"@kubb/renderer-jsx": "^5.16.0"
|
|
86
|
+
"@kubb/kit": "^5.18.6",
|
|
87
|
+
"@kubb/plugin-client": "^6.0.0"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
|
-
"@internals/client": "0.1.0-canary.20261109T134631",
|
|
91
|
-
"@internals/shared": "0.1.0-canary.20261109T134631",
|
|
92
90
|
"typescript": "^7.0.2"
|
|
93
91
|
},
|
|
94
92
|
"peerDependencies": {},
|
package/src/index.ts
CHANGED
package/src/plugin.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { addGenerator, defineExtension, injectFile, Resolver, setMacros, setResolver, useConfig } from '@kubb/kit'
|
|
1
|
+
import { fileURLToPath } from 'node:url'
|
|
2
|
+
import { defineExtension } from '@kubb/kit'
|
|
3
|
+
import { clientDependencies, resolveClientOptions, setupClient } from '@kubb/plugin-client'
|
|
5
4
|
import { compatibility } from '../kubb-extension.json'
|
|
6
5
|
import { version } from '../package.json'
|
|
7
|
-
import {
|
|
8
|
-
import { pluginZodName } from '@kubb/plugin-zod'
|
|
9
|
-
import { clientGenerator } from './generators/clientGenerator.tsx'
|
|
10
|
-
import { fetchClientTemplatePath, coreTemplatePath, serializersTemplatePath, sseTemplatePath, standardSchemaTemplatePath } from './templates.ts'
|
|
11
|
-
import type { PluginFetch, ResolvedOptions, ResolverClient } from './types.ts'
|
|
6
|
+
import type { PluginFetch } from './types.ts'
|
|
12
7
|
|
|
13
8
|
/**
|
|
14
9
|
* Canonical plugin name for `@kubb/plugin-fetch`. Used for driver lookups and cross-plugin
|
|
@@ -41,72 +36,10 @@ export const pluginFetchName = 'plugin-fetch' satisfies PluginFetch['name']
|
|
|
41
36
|
export const pluginFetch = defineExtension<PluginFetch>({
|
|
42
37
|
kind: 'plugin',
|
|
43
38
|
meta: { name: pluginFetchName, version, compatibility },
|
|
44
|
-
|
|
45
|
-
// does. `plugin-zod` is required once `validator` enables a direction, so a missing one fails setup instead of codegen.
|
|
46
|
-
dependencies: (options) => ({ [pluginTsName]: { optional: true }, [pluginZodName]: { optional: !isValidatorEnabled(options.validator) } }),
|
|
39
|
+
dependencies: clientDependencies,
|
|
47
40
|
setup(options) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
output = { path: 'clients', barrel: { type: 'named' } },
|
|
51
|
-
exclude = [],
|
|
52
|
-
include,
|
|
53
|
-
override = [],
|
|
54
|
-
baseURL,
|
|
55
|
-
throwOnErrorDefault = true,
|
|
56
|
-
validator = false,
|
|
57
|
-
returnType = 'full',
|
|
58
|
-
group,
|
|
59
|
-
sdk,
|
|
60
|
-
resolver: userResolver,
|
|
61
|
-
} = options
|
|
62
|
-
|
|
63
|
-
const resolved: ResolvedOptions = {
|
|
64
|
-
output,
|
|
65
|
-
exclude,
|
|
66
|
-
include,
|
|
67
|
-
override,
|
|
68
|
-
group: createGroupConfig(group),
|
|
69
|
-
baseURL,
|
|
70
|
-
throwOnErrorDefault,
|
|
71
|
-
validator,
|
|
72
|
-
returnType,
|
|
73
|
-
sdk: sdk ? { mode: sdk.mode ?? 'tag', name: sdk.name } : undefined,
|
|
74
|
-
resolver: userResolver ? Resolver.merge<ResolverClient>(resolverClient, userResolver) : resolverClient,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
setResolver(resolved.resolver)
|
|
78
|
-
setMacros([...defaultMacros, ...(options.macros ?? [])])
|
|
79
|
-
|
|
80
|
-
// `sdk` swaps the per-operation functions for the class-based SDK; left unset, the standalone
|
|
81
|
-
// functions (which query plugins consume) stay.
|
|
82
|
-
addGenerator(resolved.sdk ? createSdkGenerator<PluginFetch>({ sse: sseTemplatePath }) : clientGenerator)
|
|
83
|
-
|
|
84
|
-
const root = path.resolve(config.root, config.output.path)
|
|
85
|
-
|
|
86
|
-
injectFile({
|
|
87
|
-
baseName: 'serializers.ts',
|
|
88
|
-
path: path.resolve(root, '.kubb/serializers.ts'),
|
|
89
|
-
copy: serializersTemplatePath,
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
injectFile({
|
|
93
|
-
baseName: 'core.ts',
|
|
94
|
-
path: path.resolve(root, '.kubb/core.ts'),
|
|
95
|
-
copy: coreTemplatePath,
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
injectFile({
|
|
99
|
-
baseName: 'client.ts',
|
|
100
|
-
path: path.resolve(root, '.kubb/client.ts'),
|
|
101
|
-
copy: fetchClientTemplatePath,
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
injectFile({
|
|
105
|
-
baseName: 'standardSchema.ts',
|
|
106
|
-
path: path.resolve(root, '.kubb/standardSchema.ts'),
|
|
107
|
-
copy: standardSchemaTemplatePath,
|
|
108
|
-
})
|
|
109
|
-
|
|
41
|
+
const resolved = resolveClientOptions(options)
|
|
42
|
+
setupClient({ name: 'fetch', resolved, macros: options.macros, client: fileURLToPath(new URL('../templates/client.ts', import.meta.url)) })
|
|
110
43
|
return resolved
|
|
111
44
|
},
|
|
112
45
|
})
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PluginType } from '@kubb/kit'
|
|
2
|
-
import type { Options, ResolvedOptions, ResolverClient } from '@
|
|
2
|
+
import type { ClientOptions as Options, ClientResolvedOptions as ResolvedOptions, ResolverClient } from '@kubb/plugin-client'
|
|
3
3
|
|
|
4
|
-
export type { Options, ResolvedOptions, ResolverClient } from '@
|
|
4
|
+
export type { ClientOptions as Options, ClientResolvedOptions as ResolvedOptions, ResolverClient } from '@kubb/plugin-client'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* The plugin factory type for `@kubb/plugin-fetch`. Shares the options surface, resolver, and
|