@shipworthy/openapi-tanstack-query 0.0.2 → 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 +114 -61
- 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 +15 -10
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,26 +31,26 @@ 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. `@shipworthy/openapi-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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:
|
|
38
38
|
|
|
39
39
|
```sh
|
|
40
40
|
bun add -d @shipworthy/openapi-cli @shipworthy/openapi-typescript
|
|
41
41
|
```
|
|
42
42
|
|
|
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-generator
|
|
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.
|
|
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. |
|
|
54
54
|
|
|
55
55
|
The runtime entry point declares no Node.js `engines` range. The CLI runtime requirement applies to code generation.
|
|
56
56
|
|
|
@@ -66,13 +66,14 @@ you run the CLI.
|
|
|
66
66
|
and the CLI resolves `$ref`s:
|
|
67
67
|
|
|
68
68
|
```sh
|
|
69
|
-
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
|
|
70
70
|
# Or generate from a URL:
|
|
71
|
-
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
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
This writes `src/generated/hotels/types.ts` (via `@shipworthy/openapi-typescript`)
|
|
75
|
-
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.
|
|
76
77
|
|
|
77
78
|
**2. Create the client and query utils once**:
|
|
78
79
|
|
|
@@ -110,15 +111,49 @@ function Hotels() {
|
|
|
110
111
|
Inputs, response data, and error types are all inferred from the OpenAPI
|
|
111
112
|
document. You do not write type arguments.
|
|
112
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
|
+
|
|
113
148
|
## Generated output
|
|
114
149
|
|
|
115
|
-
`openapi generate tanstack-query <input> -o <dir
|
|
116
|
-
|
|
150
|
+
`openapi generate tanstack-query -i <input> -o <dir>` writes the following
|
|
151
|
+
into `<dir>/`. Every file is regenerated on every successful run:
|
|
117
152
|
|
|
118
153
|
- `types.ts`: `paths`, `components`, `operations`, and `webhooks` types from
|
|
119
|
-
`@shipworthy/openapi-typescript`.
|
|
120
|
-
|
|
121
|
-
- `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`
|
|
122
157
|
(prefix `MyPets`), it contains:
|
|
123
158
|
|
|
124
159
|
```ts
|
|
@@ -162,47 +197,56 @@ export function createMyPetsApiQueryUtils(
|
|
|
162
197
|
|
|
163
198
|
Naming rules:
|
|
164
199
|
|
|
165
|
-
-
|
|
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
|
|
166
203
|
removed and the next character is uppercased: `hotels` becomes `Hotels`, and
|
|
167
204
|
`my-pets` becomes `MyPets`.
|
|
168
205
|
- The generated exports are `create<Prefix>ApiClient`, `<Prefix>ApiClient`
|
|
169
206
|
(type), `create<Prefix>ApiQueryUtils`, `<Prefix>ApiPaths`,
|
|
170
207
|
`<Prefix>ApiComponents`, and `<Prefix>ApiOperations`.
|
|
171
|
-
- The default cache-key prefix is the raw
|
|
208
|
+
- The default cache-key prefix is the raw client name, such as `"my-pets"`.
|
|
172
209
|
- Each operation is available on the query utils object by its `operationId`,
|
|
173
210
|
for example `hotelsApi.getHotel`.
|
|
174
211
|
|
|
175
212
|
## CLI: `openapi generate tanstack-query`
|
|
176
213
|
|
|
177
|
-
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:
|
|
178
216
|
|
|
179
217
|
```
|
|
180
|
-
openapi generate tanstack-query [
|
|
218
|
+
openapi generate tanstack-query [-i <input>] -o <dir> [--name <name>] [--camel-case] [--wire-types] [--check] [TypeScript flags]
|
|
181
219
|
```
|
|
182
220
|
|
|
183
|
-
|
|
|
184
|
-
| ---------------- | ------- |
|
|
185
|
-
|
|
|
186
|
-
| `--output`, `-o` | string |
|
|
187
|
-
| `--name` | string |
|
|
188
|
-
| `--
|
|
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.`
|
|
189
232
|
|
|
190
|
-
`
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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.
|
|
194
238
|
|
|
195
239
|
Example `package.json` script:
|
|
196
240
|
|
|
197
241
|
```json
|
|
198
242
|
{
|
|
199
243
|
"scripts": {
|
|
200
|
-
"generate": "openapi generate tanstack-query openapi.json -o src/generated
|
|
244
|
+
"generate": "openapi generate tanstack-query -i openapi.json -o src/generated/hotels"
|
|
201
245
|
}
|
|
202
246
|
}
|
|
203
247
|
```
|
|
204
248
|
|
|
205
|
-
Generation fails with exit code 1, and
|
|
249
|
+
Generation fails with exit code 1, and no file is written, when:
|
|
206
250
|
|
|
207
251
|
| Error message | Cause |
|
|
208
252
|
| -------------------------------------- | -------------------------------------------------------- |
|
|
@@ -210,7 +254,7 @@ Generation fails with exit code 1, and `client.ts` is left untouched, when:
|
|
|
210
254
|
| `Duplicate operationId: <id>` | Two operations share an `operationId`. |
|
|
211
255
|
| `Unsupported operationId: <id>` | The id is `__proto__`, `constructor`, or `prototype`. |
|
|
212
256
|
| `Unsupported Swagger version: 2.x. …` | The input is Swagger 2.x. Only OpenAPI 3.x is supported. |
|
|
213
|
-
| `Globbing is unsupported.` |
|
|
257
|
+
| `Globbing is unsupported.` | The input contains `*`. |
|
|
214
258
|
|
|
215
259
|
## Runtime API (`@shipworthy/openapi-tanstack-query`)
|
|
216
260
|
|
|
@@ -233,6 +277,11 @@ type ApiClient<Paths extends {} = {}> = Client<Paths>; // @shipworthy/openapi-fe
|
|
|
233
277
|
|
|
234
278
|
This function creates an `@shipworthy/openapi-fetch` client.
|
|
235
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
|
+
|
|
236
285
|
- **`baseUrl`** (required): the root URL for all requests. A trailing slash is
|
|
237
286
|
removed.
|
|
238
287
|
- **`authToken`** (optional): a token string, or a sync or async getter.
|
|
@@ -247,6 +296,8 @@ This function creates an `@shipworthy/openapi-fetch` client.
|
|
|
247
296
|
through, including `fetch`, `Request`, `headers`, `querySerializer`,
|
|
248
297
|
`bodySerializer`, `pathSerializer`, `requestInitExt`, and standard
|
|
249
298
|
`RequestInit` fields such as `credentials`.
|
|
299
|
+
- **`caseMapping`** (optional): a generated `CaseMapping`. When provided,
|
|
300
|
+
the underlying fetch client installs the casing middleware.
|
|
250
301
|
|
|
251
302
|
The returned client is a full `openapi-fetch` client. You can call it directly
|
|
252
303
|
(`client.GET("/hotels", { params: { query } })`) or add middleware with
|
|
@@ -485,17 +536,17 @@ import {
|
|
|
485
536
|
} from "@shipworthy/openapi-tanstack-query/generator";
|
|
486
537
|
```
|
|
487
538
|
|
|
488
|
-
| Export | Signature / description
|
|
489
|
-
| ------------------------------------ |
|
|
490
|
-
| `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 `""`)
|
|
491
|
-
| `operationRoutes(model)` | `Effect<Record<string, OperationRoute>, TanstackGenerationError>`. Maps each `operationId` to `[METHOD, path]`. Fails on a missing, duplicate, or reserved (`__proto__`, `constructor`, `prototype`) `operationId`.
|
|
492
|
-
| `clientSource(prefix, name, routes)` | `string`. Prints `client.ts` source for the given export prefix, cache-key prefix, and routes.
|
|
493
|
-
| `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`. |
|
|
494
545
|
|
|
495
546
|
Programmatic generation with `@shipworthy/openapi-generator`, which is what
|
|
496
547
|
the CLI does:
|
|
497
548
|
|
|
498
|
-
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:
|
|
499
550
|
|
|
500
551
|
```ts
|
|
501
552
|
import { NodeServices } from "@effect/platform-node";
|
|
@@ -541,15 +592,15 @@ Generated files use extensionless `./types` imports and expect TypeScript `modul
|
|
|
541
592
|
|
|
542
593
|
## Troubleshooting
|
|
543
594
|
|
|
544
|
-
| Symptom
|
|
545
|
-
|
|
|
546
|
-
| `<METHOD> <path>: Missing operationId` / `Duplicate operationId: …`
|
|
547
|
-
|
|
|
548
|
-
| `
|
|
549
|
-
| CLI cannot load `@shipworthy/openapi-tanstack-query/generator`
|
|
550
|
-
| `openapi` fails to start on an older Node.js
|
|
551
|
-
| `error instanceof Error` is false in `onError`
|
|
552
|
-
| 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 })`. |
|
|
553
604
|
|
|
554
605
|
## Key facts (for AI agents)
|
|
555
606
|
|
|
@@ -559,11 +610,13 @@ Generated files use extensionless `./types` imports and expect TypeScript `modul
|
|
|
559
610
|
For `./generator`, also install the optional peers
|
|
560
611
|
`@shipworthy/openapi-generator` and `@shipworthy/openapi-transformer`.
|
|
561
612
|
- **Generate:** run
|
|
562
|
-
`openapi generate tanstack-query <spec> -o <dir> --name <name
|
|
613
|
+
`openapi generate tanstack-query -i <spec> -o <dir> [--name <name>]` from
|
|
563
614
|
`@shipworthy/openapi-cli` (`bunx --bun @shipworthy/openapi-cli`). The output is
|
|
564
|
-
`<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
|
|
565
618
|
files.
|
|
566
|
-
- **Generated exports** (Prefix is the PascalCase of
|
|
619
|
+
- **Generated exports** (Prefix is the PascalCase of the client name):
|
|
567
620
|
`create<Prefix>ApiClient`, `create<Prefix>ApiQueryUtils`,
|
|
568
621
|
`<Prefix>ApiClient`, `<Prefix>ApiPaths`, `<Prefix>ApiComponents`, and
|
|
569
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,16 +1,17 @@
|
|
|
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
|
|
9
|
-
"effect": "
|
|
8
|
+
"@shipworthy/openapi-fetch": "1.0.0",
|
|
9
|
+
"effect": "4.1.4"
|
|
10
10
|
},
|
|
11
11
|
"peerDependencies": {
|
|
12
|
-
"@shipworthy/openapi-generator": "0.0
|
|
13
|
-
"@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",
|
|
14
15
|
"@tanstack/react-query": "^5.59.0",
|
|
15
16
|
"react": "^18.0.0 || ^19.0.0"
|
|
16
17
|
},
|
|
@@ -44,15 +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": "^
|
|
53
|
+
"@types/node": "^26.6.6",
|
|
52
54
|
"react": "^19.0.0",
|
|
53
|
-
"tsdown": "0.23.
|
|
55
|
+
"tsdown": "0.23.7",
|
|
54
56
|
"typescript": "^7.0.2",
|
|
55
|
-
"vitest": "5.0
|
|
57
|
+
"vitest": "5.1.0"
|
|
56
58
|
},
|
|
57
59
|
"peerDependenciesMeta": {
|
|
58
60
|
"@shipworthy/openapi-generator": {
|
|
@@ -60,6 +62,9 @@
|
|
|
60
62
|
},
|
|
61
63
|
"@shipworthy/openapi-transformer": {
|
|
62
64
|
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"@shipworthy/openapi-typescript": {
|
|
67
|
+
"optional": true
|
|
63
68
|
}
|
|
64
69
|
},
|
|
65
70
|
"main": "./dist/index.cjs",
|