@shipworthy/openapi-tanstack-query 0.0.1 → 1.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/README.md +119 -67
- package/dist/generator.d.ts +4 -7
- package/dist/generator.js +17 -13
- package/dist/index.cjs +2 -4
- package/dist/index.d.cts +0 -6
- package/dist/index.d.ts +0 -6
- package/dist/index.js +2 -4
- package/package.json +13 -12
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@ Typed [TanStack Query](https://tanstack.com/query) options, keys, and mutations
|
|
|
4
4
|
for every operation in an OpenAPI 3.x document. Use it in a React (or React
|
|
5
5
|
Native) app that already uses `@tanstack/react-query`. The package is a thin
|
|
6
6
|
adapter over [`@shipworthy/openapi-fetch`](../openapi-fetch). Types come from
|
|
7
|
-
`@shipworthy/openapi-typescript`. Code generation
|
|
8
|
-
|
|
7
|
+
`@shipworthy/openapi-typescript`. Code generation writes a `client.ts` with
|
|
8
|
+
operation routes and, when requested, schema-based field mappings. You then pass the returned
|
|
9
9
|
options to TanStack's own hooks (`useQuery`, `useMutation`, `useInfiniteQuery`,
|
|
10
10
|
`useSuspenseQuery`, `queryClient.fetchQuery`, and so on). The package has no
|
|
11
11
|
hooks or React provider of its own.
|
|
@@ -31,34 +31,33 @@ hooks or React provider of its own.
|
|
|
31
31
|
bun add @shipworthy/openapi-tanstack-query @tanstack/react-query react
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Add the code generators as dev dependencies. `
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
modules, so your project can keep its own TypeScript version:
|
|
34
|
+
Add the code generators as dev dependencies. `@shipworthy/openapi-typescript` supplies
|
|
35
|
+
the schema-based case mapping used by `./generator` and generates `types.ts`.
|
|
36
|
+
The TypeScript generator bundles TypeScript 7's AST modules, so your
|
|
37
|
+
project can keep its own TypeScript version:
|
|
39
38
|
|
|
40
39
|
```sh
|
|
41
|
-
bun add -d @shipworthy/openapi-cli @shipworthy/openapi-typescript
|
|
40
|
+
bun add -d @shipworthy/openapi-cli @shipworthy/openapi-typescript
|
|
42
41
|
```
|
|
43
42
|
|
|
44
|
-
| Requirement | Value
|
|
45
|
-
| -------------------- |
|
|
46
|
-
| Peer dependencies | `@tanstack/react-query` `^5.59.0`, `react` `^18.0.0 \|\| ^19.0.0`
|
|
47
|
-
| Optional peers |
|
|
48
|
-
| Runtime dependencies | `@shipworthy/openapi-fetch` (installed automatically)
|
|
49
|
-
| Module format | `.` ships ESM and CommonJS, each with matching declarations (`.d.ts` and `.d.cts`). `./generator` is ESM only.
|
|
50
|
-
| Entry points | `.` (runtime) and `./generator` (generator plugin), compiled JavaScript with declarations in `dist/`. A `react-native` field points at `./dist/index.js`.
|
|
51
|
-
| Runtime | Any environment with `fetch`, `Request`, and `Response`, or pass your own `fetch`. No TypeScript transpilation of `node_modules` is needed.
|
|
52
|
-
| TypeScript | Declarations type-check with TypeScript `5.0` and later, using `NodeNext` (CommonJS or ESM) or `bundler` module resolution.
|
|
53
|
-
| CLI runtime | The `openapi` binary from `@shipworthy/openapi-cli` runs on **Node.js** `>=22.19.0` or **Bun** (`bunx --bun @shipworthy/openapi-cli`).
|
|
54
|
-
| OpenAPI versions | 3.0.x and 3.1.x. 3.0 documents are upgraded to 3.1 internally. Swagger 2.x is rejected.
|
|
43
|
+
| Requirement | Value |
|
|
44
|
+
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| Peer dependencies | `@tanstack/react-query` `^5.59.0`, `react` `^18.0.0 \|\| ^19.0.0` |
|
|
46
|
+
| Optional peers | `@shipworthy/openapi-typescript`, `@shipworthy/openapi-generator`, and `@shipworthy/openapi-transformer`, needed only for `./generator` (the CLI and programmatic generation) |
|
|
47
|
+
| Runtime dependencies | `@shipworthy/openapi-fetch` and `effect` (installed automatically; only `./generator` imports `effect`) |
|
|
48
|
+
| Module format | `.` ships ESM and CommonJS, each with matching declarations (`.d.ts` and `.d.cts`). `./generator` is ESM only. |
|
|
49
|
+
| Entry points | `.` (runtime) and `./generator` (generator plugin), compiled JavaScript with declarations in `dist/`. A `react-native` field points at `./dist/index.js`. |
|
|
50
|
+
| Runtime | Any environment with `fetch`, `Request`, and `Response`, or pass your own `fetch`. No TypeScript transpilation of `node_modules` is needed. |
|
|
51
|
+
| TypeScript | Declarations type-check with TypeScript `5.0` and later, using `NodeNext` (CommonJS or ESM) or `bundler` module resolution. |
|
|
52
|
+
| CLI runtime | The `openapi` binary from `@shipworthy/openapi-cli` runs on **Node.js** `>=22.19.0` or **Bun** (`bunx --bun @shipworthy/openapi-cli`). |
|
|
53
|
+
| OpenAPI versions | 3.0.x and 3.1.x. 3.0 documents are upgraded to 3.1 internally. Swagger 2.x is rejected. |
|
|
55
54
|
|
|
56
55
|
The runtime entry point declares no Node.js `engines` range. The CLI runtime requirement applies to code generation.
|
|
57
56
|
|
|
58
57
|
`@shipworthy/openapi-cli` loads the generator with
|
|
59
58
|
`import("@shipworthy/openapi-tanstack-query/generator")`. It loads
|
|
60
|
-
`@shipworthy/openapi-typescript` for `types.ts`. Both packages, plus
|
|
61
|
-
|
|
59
|
+
`@shipworthy/openapi-typescript` for `types.ts`. Both packages, plus
|
|
60
|
+
`@shipworthy/openapi-generator`, must be resolvable from the directory where
|
|
62
61
|
you run the CLI.
|
|
63
62
|
|
|
64
63
|
## Quick start
|
|
@@ -67,13 +66,14 @@ you run the CLI.
|
|
|
67
66
|
and the CLI resolves `$ref`s:
|
|
68
67
|
|
|
69
68
|
```sh
|
|
70
|
-
bunx --bun @shipworthy/openapi-cli generate tanstack-query ./openapi.json -o src/generated
|
|
69
|
+
bunx --bun @shipworthy/openapi-cli generate tanstack-query -i ./openapi.json -o src/generated/hotels
|
|
71
70
|
# Or generate from a URL:
|
|
72
|
-
bunx --bun @shipworthy/openapi-cli generate tanstack-query https://api.example.com/openapi.json -o src/generated
|
|
71
|
+
bunx --bun @shipworthy/openapi-cli generate tanstack-query -i https://api.example.com/openapi.json -o src/generated/hotels
|
|
73
72
|
```
|
|
74
73
|
|
|
75
74
|
This writes `src/generated/hotels/types.ts` (via `@shipworthy/openapi-typescript`)
|
|
76
|
-
and `src/generated/hotels/client.ts`.
|
|
75
|
+
and `src/generated/hotels/client.ts`. The client name `hotels` comes from the
|
|
76
|
+
output directory's name; pass `--name` to choose another.
|
|
77
77
|
|
|
78
78
|
**2. Create the client and query utils once**:
|
|
79
79
|
|
|
@@ -111,15 +111,49 @@ function Hotels() {
|
|
|
111
111
|
Inputs, response data, and error types are all inferred from the OpenAPI
|
|
112
112
|
document. You do not write type arguments.
|
|
113
113
|
|
|
114
|
+
### camelCase app fields
|
|
115
|
+
|
|
116
|
+
Add `--camel-case` when the API uses snake_case fields and the app should use camelCase:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
openapi generate tanstack-query -i ./openapi.json -o src/generated/hotels --camel-case
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
With this flag, `types.ts` uses names such as `minStars` and `hotelId`. The generated
|
|
123
|
+
`createHotelsApiClient` converts typed path, query, header, and cookie parameters and JSON
|
|
124
|
+
request bodies to the names declared in OpenAPI. It converts JSON success and error bodies
|
|
125
|
+
back to camelCase before returning data or placing it in the TanStack Query cache:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const hotels = useQuery(hotelsApi.getHotels.queryOptions({ input: { query: { minStars: 5 } } }));
|
|
129
|
+
// The request sends ?min_stars=5. A response field hotel_id is exposed as hotelId.
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Direct calls through the generated client use the same conversion. Dictionary keys declared by
|
|
133
|
+
`additionalProperties` or `patternProperties` keep their spelling, while declared fields inside
|
|
134
|
+
their values are converted. Operation IDs, URLs, enum values, and the raw `Response` retain their
|
|
135
|
+
API spelling. Generation fails when distinct wire fields would share a camelCase name within
|
|
136
|
+
an object, including nested fields combined by `allOf`, `anyOf`, or `oneOf`. Conversion applies
|
|
137
|
+
to `application/json` and `+json` bodies.
|
|
138
|
+
|
|
139
|
+
Use `--wire-types` with `--camel-case` to write `wire-types.ts` alongside `types.ts`. The wire file
|
|
140
|
+
has the API's field names and the same other TypeScript generation settings. For example, import
|
|
141
|
+
`components` from `./generated/hotels/wire-types` to type a raw `Response` fixture or a custom `fetch`
|
|
142
|
+
wrapper. `types.ts` remains the app-facing camelCase contract.
|
|
143
|
+
|
|
144
|
+
JSON Pointer strings such as `errors[].pointer: "/slot_id"` keep their API spelling. Undeclared
|
|
145
|
+
fields also keep their API spelling. When a response status has no matching schema and no
|
|
146
|
+
`default` response in the OpenAPI document, its error body is returned without conversion.
|
|
147
|
+
|
|
114
148
|
## Generated output
|
|
115
149
|
|
|
116
|
-
`openapi generate tanstack-query <input> -o <dir
|
|
117
|
-
|
|
150
|
+
`openapi generate tanstack-query -i <input> -o <dir>` writes the following
|
|
151
|
+
into `<dir>/`. Every file is regenerated on every successful run:
|
|
118
152
|
|
|
119
153
|
- `types.ts`: `paths`, `components`, `operations`, and `webhooks` types from
|
|
120
|
-
`@shipworthy/openapi-typescript`.
|
|
121
|
-
|
|
122
|
-
- `client.ts`:
|
|
154
|
+
`@shipworthy/openapi-typescript`.
|
|
155
|
+
- `wire-types.ts`: optional API-facing types when both `--camel-case` and `--wire-types` are set.
|
|
156
|
+
- `client.ts`: for `-o src/generated/my-pets` or `--name my-pets`
|
|
123
157
|
(prefix `MyPets`), it contains:
|
|
124
158
|
|
|
125
159
|
```ts
|
|
@@ -163,47 +197,56 @@ export function createMyPetsApiQueryUtils(
|
|
|
163
197
|
|
|
164
198
|
Naming rules:
|
|
165
199
|
|
|
166
|
-
-
|
|
200
|
+
- The client name is `--name`, or the basename of the output directory when
|
|
201
|
+
`--name` is omitted.
|
|
202
|
+
- `<Prefix>` is the PascalCase form of the client name. `-` and `_` separators are
|
|
167
203
|
removed and the next character is uppercased: `hotels` becomes `Hotels`, and
|
|
168
204
|
`my-pets` becomes `MyPets`.
|
|
169
205
|
- The generated exports are `create<Prefix>ApiClient`, `<Prefix>ApiClient`
|
|
170
206
|
(type), `create<Prefix>ApiQueryUtils`, `<Prefix>ApiPaths`,
|
|
171
207
|
`<Prefix>ApiComponents`, and `<Prefix>ApiOperations`.
|
|
172
|
-
- The default cache-key prefix is the raw
|
|
208
|
+
- The default cache-key prefix is the raw client name, such as `"my-pets"`.
|
|
173
209
|
- Each operation is available on the query utils object by its `operationId`,
|
|
174
210
|
for example `hotelsApi.getHotel`.
|
|
175
211
|
|
|
176
212
|
## CLI: `openapi generate tanstack-query`
|
|
177
213
|
|
|
178
|
-
The command is provided by `@shipworthy/openapi-cli`.
|
|
214
|
+
The command is provided by `@shipworthy/openapi-cli`. The `tanstack-query` plugin writes
|
|
215
|
+
`client.ts` and the `types.ts` it imports into the output directory:
|
|
179
216
|
|
|
180
217
|
```
|
|
181
|
-
openapi generate tanstack-query [
|
|
218
|
+
openapi generate tanstack-query [-i <input>] -o <dir> [--name <name>] [--camel-case] [--wire-types] [--check] [TypeScript flags]
|
|
182
219
|
```
|
|
183
220
|
|
|
184
|
-
|
|
|
185
|
-
| ---------------- | ------- |
|
|
186
|
-
|
|
|
187
|
-
| `--output`, `-o` | string |
|
|
188
|
-
| `--name` | string |
|
|
189
|
-
| `--
|
|
221
|
+
| Flag | Type | Default | Description |
|
|
222
|
+
| ---------------- | ------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
223
|
+
| `--input`, `-i` | string | stdin | OpenAPI file path, or an `http:`, `https:`, or `file:` URL. Glob patterns (`*`) are rejected. |
|
|
224
|
+
| `--output`, `-o` | string | required | Directory that receives `client.ts` and `types.ts`. |
|
|
225
|
+
| `--name` | string | output directory's basename | Client name. It sets the PascalCase export prefix and the default cache-key prefix. It must match `^[a-zA-Z][a-zA-Z0-9]*(?:[-_][a-zA-Z0-9]+)*$`. |
|
|
226
|
+
| `--camel-case` | boolean | `false` | Use camelCase in `types.ts` and convert declared parameter and JSON body fields in the generated client. |
|
|
227
|
+
| `--wire-types` | boolean | `false` | Also write `wire-types.ts` with API field names. Requires `--camel-case`. |
|
|
228
|
+
| `--check` | boolean | `false` | Write nothing; exit 1 when a generated file differs from the file in the output directory or is missing. |
|
|
229
|
+
|
|
230
|
+
When `--name` is omitted and the output directory's basename is not a valid name,
|
|
231
|
+
the command fails with `Cannot derive a client name from the output directory "<basename>". Pass --name.`
|
|
190
232
|
|
|
191
|
-
`
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
233
|
+
The `tanstack-query` plugin can be combined with other plugins in one run, for example
|
|
234
|
+
`openapi generate tanstack-query zod -i openapi.json -o src/generated/hotels`, which also
|
|
235
|
+
writes `zod-schema.ts` against the same `types.ts`. See the
|
|
236
|
+
[CLI reference](../openapi-cli/README.md#openapi-generate-plugins) for every flag, including
|
|
237
|
+
the TypeScript generation flags.
|
|
195
238
|
|
|
196
239
|
Example `package.json` script:
|
|
197
240
|
|
|
198
241
|
```json
|
|
199
242
|
{
|
|
200
243
|
"scripts": {
|
|
201
|
-
"generate": "openapi generate tanstack-query openapi.json -o src/generated
|
|
244
|
+
"generate": "openapi generate tanstack-query -i openapi.json -o src/generated/hotels"
|
|
202
245
|
}
|
|
203
246
|
}
|
|
204
247
|
```
|
|
205
248
|
|
|
206
|
-
Generation fails with exit code 1, and
|
|
249
|
+
Generation fails with exit code 1, and no file is written, when:
|
|
207
250
|
|
|
208
251
|
| Error message | Cause |
|
|
209
252
|
| -------------------------------------- | -------------------------------------------------------- |
|
|
@@ -211,7 +254,7 @@ Generation fails with exit code 1, and `client.ts` is left untouched, when:
|
|
|
211
254
|
| `Duplicate operationId: <id>` | Two operations share an `operationId`. |
|
|
212
255
|
| `Unsupported operationId: <id>` | The id is `__proto__`, `constructor`, or `prototype`. |
|
|
213
256
|
| `Unsupported Swagger version: 2.x. …` | The input is Swagger 2.x. Only OpenAPI 3.x is supported. |
|
|
214
|
-
| `Globbing is unsupported.` |
|
|
257
|
+
| `Globbing is unsupported.` | The input contains `*`. |
|
|
215
258
|
|
|
216
259
|
## Runtime API (`@shipworthy/openapi-tanstack-query`)
|
|
217
260
|
|
|
@@ -234,6 +277,11 @@ type ApiClient<Paths extends {} = {}> = Client<Paths>; // @shipworthy/openapi-fe
|
|
|
234
277
|
|
|
235
278
|
This function creates an `@shipworthy/openapi-fetch` client.
|
|
236
279
|
|
|
280
|
+
With `--camel-case`, the generated factory passes `caseMapping` to
|
|
281
|
+
`createApiClient<Paths>(options)`. The underlying fetch client installs the casing middleware.
|
|
282
|
+
`@shipworthy/openapi-typescript` builds the mapping from the OpenAPI schema. Use the generated
|
|
283
|
+
factory so runtime values match `types.ts`.
|
|
284
|
+
|
|
237
285
|
- **`baseUrl`** (required): the root URL for all requests. A trailing slash is
|
|
238
286
|
removed.
|
|
239
287
|
- **`authToken`** (optional): a token string, or a sync or async getter.
|
|
@@ -248,6 +296,8 @@ This function creates an `@shipworthy/openapi-fetch` client.
|
|
|
248
296
|
through, including `fetch`, `Request`, `headers`, `querySerializer`,
|
|
249
297
|
`bodySerializer`, `pathSerializer`, `requestInitExt`, and standard
|
|
250
298
|
`RequestInit` fields such as `credentials`.
|
|
299
|
+
- **`caseMapping`** (optional): a generated `CaseMapping`. When provided,
|
|
300
|
+
the underlying fetch client installs the casing middleware.
|
|
251
301
|
|
|
252
302
|
The returned client is a full `openapi-fetch` client. You can call it directly
|
|
253
303
|
(`client.GET("/hotels", { params: { query } })`) or add middleware with
|
|
@@ -486,17 +536,17 @@ import {
|
|
|
486
536
|
} from "@shipworthy/openapi-tanstack-query/generator";
|
|
487
537
|
```
|
|
488
538
|
|
|
489
|
-
| Export | Signature / description
|
|
490
|
-
| ------------------------------------ |
|
|
491
|
-
| `tanstackQuery` | `Generator<string, TanstackGenerationError>` (from `@shipworthy/openapi-generator`) with `id: "tanstack-query"`. `generate(model, options)` returns `Effect<string, TanstackGenerationError>`, which is the `client.ts` source with no header comment. Options: `prefix?: string` (export prefix, default `""`)
|
|
492
|
-
| `operationRoutes(model)` | `Effect<Record<string, OperationRoute>, TanstackGenerationError>`. Maps each `operationId` to `[METHOD, path]`. Fails on a missing, duplicate, or reserved (`__proto__`, `constructor`, `prototype`) `operationId`.
|
|
493
|
-
| `clientSource(prefix, name, routes)` | `string`. Prints `client.ts` source for the given export prefix, cache-key prefix, and routes.
|
|
494
|
-
| `TanstackGenerationError` | Effect `Schema.TaggedError` with `_tag: "TanstackGenerationError"` and `message: string`.
|
|
539
|
+
| Export | Signature / description |
|
|
540
|
+
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
541
|
+
| `tanstackQuery` | `Generator<string, TanstackGenerationError>` (from `@shipworthy/openapi-generator`) with `id: "tanstack-query"`. `generate(model, options)` returns `Effect<string, TanstackGenerationError>`, which is the `client.ts` source with no header comment. Options: `prefix?: string` (export prefix, default `""`), `name?: string` (cache-key prefix, default `prefix`), and `camelCase?: boolean` (default `false`). Other option shapes fail with `TanstackGenerationError`. |
|
|
542
|
+
| `operationRoutes(model)` | `Effect<Record<string, OperationRoute>, TanstackGenerationError>`. Maps each `operationId` to `[METHOD, path]`. Fails on a missing, duplicate, or reserved (`__proto__`, `constructor`, `prototype`) `operationId`. |
|
|
543
|
+
| `clientSource(prefix, name, routes)` | `string`. Prints `client.ts` source for the given export prefix, cache-key prefix, and routes. |
|
|
544
|
+
| `TanstackGenerationError` | Effect `Schema.TaggedError` with `_tag: "TanstackGenerationError"` and `message: string`. |
|
|
495
545
|
|
|
496
546
|
Programmatic generation with `@shipworthy/openapi-generator`, which is what
|
|
497
547
|
the CLI does:
|
|
498
548
|
|
|
499
|
-
Install `@effect/platform-node
|
|
549
|
+
Install `@effect/platform-node@4.1.4` as a dev dependency for the Node.js platform layer used in this example:
|
|
500
550
|
|
|
501
551
|
```ts
|
|
502
552
|
import { NodeServices } from "@effect/platform-node";
|
|
@@ -542,29 +592,31 @@ Generated files use extensionless `./types` imports and expect TypeScript `modul
|
|
|
542
592
|
|
|
543
593
|
## Troubleshooting
|
|
544
594
|
|
|
545
|
-
| Symptom
|
|
546
|
-
|
|
|
547
|
-
| `<METHOD> <path>: Missing operationId` / `Duplicate operationId: …`
|
|
548
|
-
|
|
|
549
|
-
| `
|
|
550
|
-
| CLI cannot load `@shipworthy/openapi-tanstack-query/generator`
|
|
551
|
-
| `openapi` fails to start on an older Node.js
|
|
552
|
-
| `error instanceof Error` is false in `onError`
|
|
553
|
-
| Two APIs with the same
|
|
595
|
+
| Symptom | Fix |
|
|
596
|
+
| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
597
|
+
| `<METHOD> <path>: Missing operationId` / `Duplicate operationId: …` | Add unique `operationId`s to the OpenAPI document. |
|
|
598
|
+
| `Invalid value for flag --name: "…". Expected: Schema validation failed: --name must start with a letter …` | Use letters and digits, with single `-` or `_` separators, such as `hotels` or `sdk-core`. |
|
|
599
|
+
| `Cannot derive a client name from the output directory "…". Pass --name.` | Rename the output directory to a valid name, or pass `--name`. |
|
|
600
|
+
| CLI cannot load `@shipworthy/openapi-tanstack-query/generator` | Install this package, `@shipworthy/openapi-typescript`, and `@shipworthy/openapi-generator` in the project. |
|
|
601
|
+
| `openapi` fails to start on an older Node.js | The CLI needs Node.js `>=22.19.0`. Upgrade Node.js, or run it with `bunx --bun @shipworthy/openapi-cli`. |
|
|
602
|
+
| `error instanceof Error` is false in `onError` | This is expected for HTTP errors: the error is the parsed error body. See [errors](#requests-responses-and-errors). |
|
|
603
|
+
| Two APIs with the same client name overwrite each other's cache entries | Pass a different `prefix` to `create<Prefix>ApiQueryUtils(client, { prefix })`. |
|
|
554
604
|
|
|
555
605
|
## Key facts (for AI agents)
|
|
556
606
|
|
|
557
607
|
- **Install:** `@shipworthy/openapi-tanstack-query`, plus peers
|
|
558
608
|
`@tanstack/react-query@^5.59.0` and `react@^18 || ^19`. The runtime entry
|
|
559
609
|
ships ESM and CommonJS with `.d.ts`/`.d.cts` declarations for TypeScript 5.0+.
|
|
560
|
-
For `./generator`, also install the optional peers
|
|
561
|
-
`@shipworthy/openapi-generator
|
|
610
|
+
For `./generator`, also install the optional peers
|
|
611
|
+
`@shipworthy/openapi-generator` and `@shipworthy/openapi-transformer`.
|
|
562
612
|
- **Generate:** run
|
|
563
|
-
`openapi generate tanstack-query <spec> -o <dir> --name <name
|
|
613
|
+
`openapi generate tanstack-query -i <spec> -o <dir> [--name <name>]` from
|
|
564
614
|
`@shipworthy/openapi-cli` (`bunx --bun @shipworthy/openapi-cli`). The output is
|
|
565
|
-
`<dir
|
|
615
|
+
`<dir>/types.ts` and `<dir>/client.ts`, regenerated on every run. `name`
|
|
616
|
+
defaults to the basename of `<dir>`. Add other plugins to the same run, e.g.
|
|
617
|
+
`openapi generate tanstack-query zod -i <spec> -o <dir>`. Do not edit generated
|
|
566
618
|
files.
|
|
567
|
-
- **Generated exports** (Prefix is the PascalCase of
|
|
619
|
+
- **Generated exports** (Prefix is the PascalCase of the client name):
|
|
568
620
|
`create<Prefix>ApiClient`, `create<Prefix>ApiQueryUtils`,
|
|
569
621
|
`<Prefix>ApiClient`, `<Prefix>ApiPaths`, `<Prefix>ApiComponents`, and
|
|
570
622
|
`<Prefix>ApiOperations`.
|
package/dist/generator.d.ts
CHANGED
|
@@ -3,17 +3,14 @@ import { Generator } from "@shipworthy/openapi-generator";
|
|
|
3
3
|
import { ParsedService } from "@shipworthy/openapi-transformer";
|
|
4
4
|
import { HttpMethod } from "@shipworthy/openapi-fetch";
|
|
5
5
|
import "@tanstack/react-query";
|
|
6
|
-
//#region src/errors.d.ts
|
|
7
6
|
declare const TanstackGenerationError_base: Schema.Class<TanstackGenerationError, Schema.TaggedStruct<"TanstackGenerationError", {
|
|
8
7
|
readonly message: Schema.String;
|
|
9
|
-
}>, import("effect/Cause").YieldableError
|
|
8
|
+
}>, import("effect/Cause").YieldableError & {
|
|
9
|
+
readonly brand: unique symbol;
|
|
10
|
+
}>;
|
|
10
11
|
export declare class TanstackGenerationError extends TanstackGenerationError_base {}
|
|
11
|
-
//#endregion
|
|
12
|
-
//#region src/query-utils.d.ts
|
|
13
12
|
type OperationRoute = readonly [Uppercase<HttpMethod>, string];
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/generator.d.ts
|
|
16
13
|
/** operationId → [METHOD, path]. Every operation needs an id. */
|
|
17
14
|
export declare const operationRoutes: (model: ParsedService) => Effect.Effect<Record<string, OperationRoute>, TanstackGenerationError, never>;
|
|
18
15
|
export declare const tanstackQuery: Generator<string, TanstackGenerationError>;
|
|
19
|
-
|
|
16
|
+
export {};
|
package/dist/generator.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { buildCaseMapping } from "@shipworthy/openapi-typescript/case-mapping";
|
|
2
|
+
import { Array, Effect, Formatter, Record, Schema, pipe } from "effect";
|
|
3
|
+
// src/errors.ts
|
|
3
4
|
var TanstackGenerationError = class extends Schema.TaggedError()("TanstackGenerationError", { message: Schema.String }) {};
|
|
4
|
-
|
|
5
|
-
//#region src/generator.ts
|
|
5
|
+
// src/generator.ts
|
|
6
6
|
const blockedOperationIds = /* @__PURE__ */ new Set([
|
|
7
7
|
"__proto__",
|
|
8
8
|
"constructor",
|
|
@@ -10,7 +10,8 @@ const blockedOperationIds = /* @__PURE__ */ new Set([
|
|
|
10
10
|
]);
|
|
11
11
|
const PluginOptions = Schema.Struct({
|
|
12
12
|
prefix: Schema.optionalKey(Schema.String),
|
|
13
|
-
name: Schema.optionalKey(Schema.String)
|
|
13
|
+
name: Schema.optionalKey(Schema.String),
|
|
14
|
+
camelCase: Schema.optionalKey(Schema.Boolean)
|
|
14
15
|
});
|
|
15
16
|
function reject(message) {
|
|
16
17
|
return new TanstackGenerationError({ message });
|
|
@@ -23,7 +24,7 @@ const operationRoutes = Effect.fnUntraced(function* (model) {
|
|
|
23
24
|
const id = operation.operationId;
|
|
24
25
|
if (!id) return yield* reject(`${label}: Missing operationId`);
|
|
25
26
|
if (blockedOperationIds.has(id)) return yield* reject(`Unsupported operationId: ${id}`);
|
|
26
|
-
if (
|
|
27
|
+
if (Record.has(routes, id)) return yield* reject(`Duplicate operationId: ${id}`);
|
|
27
28
|
routes[id] = [operation.method, operation.path];
|
|
28
29
|
return routes;
|
|
29
30
|
}));
|
|
@@ -33,25 +34,28 @@ const tanstackQuery = {
|
|
|
33
34
|
generate: Effect.fnUntraced(function* (model, options) {
|
|
34
35
|
const decoded = yield* Effect.mapError(Schema.decodeEffect(PluginOptions)(options), (cause) => reject(cause.message));
|
|
35
36
|
const prefix = decoded.prefix ?? "";
|
|
36
|
-
return clientSource(prefix, decoded.name ?? prefix, yield* operationRoutes(model));
|
|
37
|
+
return clientSource(prefix, decoded.name ?? prefix, yield* operationRoutes(model), decoded.camelCase ? yield* buildCaseMapping(model).pipe(Effect.mapError((error) => new TanstackGenerationError({ message: error.message }))) : void 0);
|
|
37
38
|
})
|
|
38
39
|
};
|
|
39
|
-
function clientSource(prefix, name, routes) {
|
|
40
|
-
|
|
40
|
+
function clientSource(prefix, name, routes, mapping) {
|
|
41
|
+
const entries = pipe(Record.collect(routes, (id, route) => ` ${Formatter.formatJson(id)}: ${Formatter.formatJson(route)},`), Array.join("\n"));
|
|
42
|
+
return `import { ${mapping ? "createApiClient, createTanstackQueryUtils, type ApiClientOptions, type TanstackQueryUtilsOptions" : "createApiClient, createTanstackQueryUtils, type TanstackQueryUtilsOptions"} } from "@shipworthy/openapi-tanstack-query";
|
|
41
43
|
import type { operations, paths } from "./types";
|
|
42
44
|
|
|
43
45
|
export type { paths as ${prefix}ApiPaths, components as ${prefix}ApiComponents, operations as ${prefix}ApiOperations } from "./types";
|
|
44
|
-
|
|
46
|
+
${mapping ? `const caseMapping = ${Formatter.formatJson(mapping, { space: 2 })} as const;
|
|
47
|
+
|
|
48
|
+
export const create${prefix}ApiClient = (options: ApiClientOptions) =>
|
|
49
|
+
createApiClient<paths>({ ...options, caseMapping });` : `export const create${prefix}ApiClient: typeof createApiClient<paths> = createApiClient<paths>;`}
|
|
45
50
|
export type ${prefix}ApiClient = ReturnType<typeof create${prefix}ApiClient>;
|
|
46
51
|
|
|
47
52
|
const routes = {
|
|
48
|
-
${
|
|
53
|
+
${entries}
|
|
49
54
|
} as const;
|
|
50
55
|
|
|
51
56
|
export function create${prefix}ApiQueryUtils(client: ${prefix}ApiClient, options?: Partial<TanstackQueryUtilsOptions>) {
|
|
52
|
-
return createTanstackQueryUtils<Pick<operations, keyof typeof routes>>(client, routes, { prefix: ${
|
|
57
|
+
return createTanstackQueryUtils<Pick<operations, keyof typeof routes>>(client, routes, { prefix: ${Formatter.formatJson(name)}, ...options });
|
|
53
58
|
}
|
|
54
59
|
`;
|
|
55
60
|
}
|
|
56
|
-
//#endregion
|
|
57
61
|
export { TanstackGenerationError, operationRoutes, tanstackQuery };
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
let _shipworthy_openapi_fetch = require("@shipworthy/openapi-fetch");
|
|
3
3
|
let _tanstack_react_query = require("@tanstack/react-query");
|
|
4
|
-
|
|
4
|
+
// src/client.ts
|
|
5
5
|
function createApiClient({ authToken, ...options }) {
|
|
6
6
|
const client = (0, _shipworthy_openapi_fetch.createClient)(options);
|
|
7
7
|
if (authToken !== void 0) client.use({ async onRequest({ request }) {
|
|
@@ -17,8 +17,7 @@ function createApiClient({ authToken, ...options }) {
|
|
|
17
17
|
} });
|
|
18
18
|
return client;
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
//#region src/query-utils.ts
|
|
20
|
+
// src/query-utils.ts
|
|
22
21
|
function cacheInput(input = {}) {
|
|
23
22
|
const { header, ...rest } = input;
|
|
24
23
|
if (!header) return rest;
|
|
@@ -102,6 +101,5 @@ function createTanstackQueryUtils(client, routes, { prefix }) {
|
|
|
102
101
|
}];
|
|
103
102
|
}));
|
|
104
103
|
}
|
|
105
|
-
//#endregion
|
|
106
104
|
exports.createApiClient = createApiClient;
|
|
107
105
|
exports.createTanstackQueryUtils = createTanstackQueryUtils;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Client, ClientOptions, ErrorResponse, HttpMethod, MediaType, RequestBodyOption, RequiredKeysOf, ResponseObjectMap, SuccessResponse } from "@shipworthy/openapi-fetch";
|
|
2
2
|
import { DataTag, InfiniteData, InfiniteQueryPageParamsOptions, QueryFunction, QueryKey, QueryObserverOptions, SkipToken, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
|
|
3
|
-
//#region src/client.d.ts
|
|
4
3
|
type AuthToken = string | undefined | null;
|
|
5
4
|
type AuthTokenProvider = () => AuthToken | Promise<AuthToken>;
|
|
6
5
|
interface ApiClientOptions extends Omit<ClientOptions, "baseUrl"> {
|
|
@@ -9,8 +8,6 @@ interface ApiClientOptions extends Omit<ClientOptions, "baseUrl"> {
|
|
|
9
8
|
}
|
|
10
9
|
export declare function createApiClient<Paths extends {}>({ authToken, ...options }: ApiClientOptions): Client<Paths>;
|
|
11
10
|
type ApiClient<Paths extends {} = {}> = Client<Paths>;
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/operation.d.ts
|
|
14
11
|
/** Preserve OpenAPI's parameter groups and add its typed request body. */
|
|
15
12
|
type OperationInput<Op> = (Op extends {
|
|
16
13
|
parameters: infer Parameters;
|
|
@@ -28,8 +25,6 @@ type JsonResponses<Op> = { [Status in keyof Responses<Op>]: Responses<Op>[Status
|
|
|
28
25
|
} : Responses<Op>[Status]; };
|
|
29
26
|
type OperationData<Op> = NormalizeData<SuccessResponse<JsonResponses<Op>, MediaType>>;
|
|
30
27
|
type OperationError<Op> = ErrorResponse<Responses<Op>, MediaType> | Error;
|
|
31
|
-
//#endregion
|
|
32
|
-
//#region src/query-utils.d.ts
|
|
33
28
|
type InputOf<O> = OperationInput<O>;
|
|
34
29
|
type DataOf<O> = OperationData<O>;
|
|
35
30
|
type ErrorOf<O> = OperationError<O>;
|
|
@@ -79,5 +74,4 @@ interface TanstackQueryUtilsOptions {
|
|
|
79
74
|
}
|
|
80
75
|
type OperationRoute = readonly [Uppercase<HttpMethod>, string];
|
|
81
76
|
export declare function createTanstackQueryUtils<Operations extends Record<string, any>>(client: Client<any>, routes: { [Name in keyof Operations]: OperationRoute; }, { prefix }: TanstackQueryUtilsOptions): { [Name in keyof Operations]: OperationQueryUtils<Operations[Name]>; };
|
|
82
|
-
//#endregion
|
|
83
77
|
export type { ApiClient, ApiClientOptions, AuthToken, AuthTokenProvider, OperationData, OperationError, OperationInput, OperationQueryUtils, OperationRoute, TanstackQueryUtilsOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Client, ClientOptions, ErrorResponse, HttpMethod, MediaType, RequestBodyOption, RequiredKeysOf, ResponseObjectMap, SuccessResponse } from "@shipworthy/openapi-fetch";
|
|
2
2
|
import { DataTag, InfiniteData, InfiniteQueryPageParamsOptions, QueryFunction, QueryKey, QueryObserverOptions, SkipToken, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
|
|
3
|
-
//#region src/client.d.ts
|
|
4
3
|
type AuthToken = string | undefined | null;
|
|
5
4
|
type AuthTokenProvider = () => AuthToken | Promise<AuthToken>;
|
|
6
5
|
interface ApiClientOptions extends Omit<ClientOptions, "baseUrl"> {
|
|
@@ -9,8 +8,6 @@ interface ApiClientOptions extends Omit<ClientOptions, "baseUrl"> {
|
|
|
9
8
|
}
|
|
10
9
|
export declare function createApiClient<Paths extends {}>({ authToken, ...options }: ApiClientOptions): Client<Paths>;
|
|
11
10
|
type ApiClient<Paths extends {} = {}> = Client<Paths>;
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/operation.d.ts
|
|
14
11
|
/** Preserve OpenAPI's parameter groups and add its typed request body. */
|
|
15
12
|
type OperationInput<Op> = (Op extends {
|
|
16
13
|
parameters: infer Parameters;
|
|
@@ -28,8 +25,6 @@ type JsonResponses<Op> = { [Status in keyof Responses<Op>]: Responses<Op>[Status
|
|
|
28
25
|
} : Responses<Op>[Status]; };
|
|
29
26
|
type OperationData<Op> = NormalizeData<SuccessResponse<JsonResponses<Op>, MediaType>>;
|
|
30
27
|
type OperationError<Op> = ErrorResponse<Responses<Op>, MediaType> | Error;
|
|
31
|
-
//#endregion
|
|
32
|
-
//#region src/query-utils.d.ts
|
|
33
28
|
type InputOf<O> = OperationInput<O>;
|
|
34
29
|
type DataOf<O> = OperationData<O>;
|
|
35
30
|
type ErrorOf<O> = OperationError<O>;
|
|
@@ -79,5 +74,4 @@ interface TanstackQueryUtilsOptions {
|
|
|
79
74
|
}
|
|
80
75
|
type OperationRoute = readonly [Uppercase<HttpMethod>, string];
|
|
81
76
|
export declare function createTanstackQueryUtils<Operations extends Record<string, any>>(client: Client<any>, routes: { [Name in keyof Operations]: OperationRoute; }, { prefix }: TanstackQueryUtilsOptions): { [Name in keyof Operations]: OperationQueryUtils<Operations[Name]>; };
|
|
82
|
-
//#endregion
|
|
83
77
|
export type { ApiClient, ApiClientOptions, AuthToken, AuthTokenProvider, OperationData, OperationError, OperationInput, OperationQueryUtils, OperationRoute, TanstackQueryUtilsOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createClient } from "@shipworthy/openapi-fetch";
|
|
2
2
|
import { skipToken } from "@tanstack/react-query";
|
|
3
|
-
|
|
3
|
+
// src/client.ts
|
|
4
4
|
function createApiClient({ authToken, ...options }) {
|
|
5
5
|
const client = createClient(options);
|
|
6
6
|
if (authToken !== void 0) client.use({ async onRequest({ request }) {
|
|
@@ -16,8 +16,7 @@ function createApiClient({ authToken, ...options }) {
|
|
|
16
16
|
} });
|
|
17
17
|
return client;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
//#region src/query-utils.ts
|
|
19
|
+
// src/query-utils.ts
|
|
21
20
|
function cacheInput(input = {}) {
|
|
22
21
|
const { header, ...rest } = input;
|
|
23
22
|
if (!header) return rest;
|
|
@@ -101,5 +100,4 @@ function createTanstackQueryUtils(client, routes, { prefix }) {
|
|
|
101
100
|
}];
|
|
102
101
|
}));
|
|
103
102
|
}
|
|
104
|
-
//#endregion
|
|
105
103
|
export { createApiClient, createTanstackQueryUtils };
|
package/package.json
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipworthy/openapi-tanstack-query",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist"
|
|
6
6
|
],
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@shipworthy/openapi-fetch": "0.0
|
|
8
|
+
"@shipworthy/openapi-fetch": "1.0.0",
|
|
9
|
+
"effect": "4.1.4"
|
|
9
10
|
},
|
|
10
11
|
"peerDependencies": {
|
|
11
|
-
"@shipworthy/openapi-generator": "0.0
|
|
12
|
-
"@shipworthy/openapi-transformer": "0.0
|
|
12
|
+
"@shipworthy/openapi-generator": "1.0.0",
|
|
13
|
+
"@shipworthy/openapi-transformer": "1.0.0",
|
|
14
|
+
"@shipworthy/openapi-typescript": "1.0.0",
|
|
13
15
|
"@tanstack/react-query": "^5.59.0",
|
|
14
|
-
"effect": "^4.0.0",
|
|
15
16
|
"react": "^18.0.0 || ^19.0.0"
|
|
16
17
|
},
|
|
17
18
|
"license": "MIT",
|
|
@@ -44,16 +45,16 @@
|
|
|
44
45
|
"test": "vitest run"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@shipworthy/openapi-generator": "0.0
|
|
48
|
-
"@shipworthy/openapi-transformer": "0.0
|
|
48
|
+
"@shipworthy/openapi-generator": "1.0.0",
|
|
49
|
+
"@shipworthy/openapi-transformer": "1.0.0",
|
|
50
|
+
"@shipworthy/openapi-typescript": "1.0.0",
|
|
49
51
|
"@shipworthy/typescript-config": "0.0.1",
|
|
50
52
|
"@tanstack/react-query": "^5.104.1",
|
|
51
|
-
"@types/node": "^
|
|
52
|
-
"effect": "^4.0.0",
|
|
53
|
+
"@types/node": "^26.6.6",
|
|
53
54
|
"react": "^19.0.0",
|
|
54
|
-
"tsdown": "0.23.
|
|
55
|
+
"tsdown": "0.23.7",
|
|
55
56
|
"typescript": "^7.0.2",
|
|
56
|
-
"vitest": "5.0
|
|
57
|
+
"vitest": "5.1.0"
|
|
57
58
|
},
|
|
58
59
|
"peerDependenciesMeta": {
|
|
59
60
|
"@shipworthy/openapi-generator": {
|
|
@@ -62,7 +63,7 @@
|
|
|
62
63
|
"@shipworthy/openapi-transformer": {
|
|
63
64
|
"optional": true
|
|
64
65
|
},
|
|
65
|
-
"
|
|
66
|
+
"@shipworthy/openapi-typescript": {
|
|
66
67
|
"optional": true
|
|
67
68
|
}
|
|
68
69
|
},
|