tanstack-fetch 1.0.0 → 1.0.1
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 +53 -3
- package/dist/cli.js +22 -239
- package/dist/client.type-BE-mgD4y.d.cts +116 -0
- package/dist/client.type-D1zw5tFf.d.ts +116 -0
- package/dist/config.type-eG_cuxpu.d.cts +132 -0
- package/dist/config.type-eG_cuxpu.d.ts +132 -0
- package/dist/index.cjs +2 -881
- package/dist/index.d.cts +20 -17
- package/dist/index.d.ts +20 -17
- package/dist/index.js +2 -3
- package/dist/plugins.cjs +1 -0
- package/dist/plugins.d.cts +19 -0
- package/dist/plugins.d.ts +19 -0
- package/dist/plugins.js +1 -0
- package/dist/react.cjs +1 -951
- package/dist/react.d.cts +6 -6
- package/dist/react.d.ts +6 -6
- package/dist/react.js +1 -91
- package/dist/sse.cjs +7 -0
- package/dist/sse.d.cts +9 -0
- package/dist/sse.d.ts +9 -0
- package/dist/sse.js +7 -0
- package/package.json +19 -6
- package/dist/chunk-T27KPHCL.js +0 -867
- package/dist/chunk-T27KPHCL.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/fetch-error-B10Od9AT.d.cts +0 -257
- package/dist/fetch-error-B10Od9AT.d.ts +0 -257
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/react.cjs.map +0 -1
- package/dist/react.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# tanstack-fetch
|
|
2
2
|
|
|
3
|
-
Typed `fetch` client shaped for **TanStack Query
|
|
3
|
+
Typed `fetch` client shaped for **TanStack Query** — tiny HTTP core, optional SSE / React.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
| Import | What you get | Typical gzip |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| `tanstack-fetch` | HTTP only (`get/post/…`) | **~3.5KB** |
|
|
8
|
+
| `tanstack-fetch/sse` | + `api.sse()` | **~4.7KB** |
|
|
9
|
+
| `tanstack-fetch/plugins` | plugin factories | **~0.9KB** |
|
|
10
|
+
| `tanstack-fetch/react` | `FetchProvider` / hooks (peer: core) | **~1KB** |
|
|
6
11
|
|
|
7
12
|
```bash
|
|
8
13
|
npm install tanstack-fetch
|
|
@@ -14,6 +19,24 @@ Node 18+ (native `fetch`).
|
|
|
14
19
|
|
|
15
20
|
---
|
|
16
21
|
|
|
22
|
+
## Bundle size
|
|
23
|
+
|
|
24
|
+
Tree-shake by importing only what you need:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// smallest — HTTP for TanStack Query
|
|
28
|
+
import { createFetch } from 'tanstack-fetch'
|
|
29
|
+
|
|
30
|
+
// only when you need streams
|
|
31
|
+
import { createFetch } from 'tanstack-fetch/sse'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`yaml` is an **optional** peer (CLI YAML specs only). React is optional too.
|
|
35
|
+
|
|
36
|
+
Run `npm run size` after build to print local gzip numbers.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
17
40
|
## Two ways to configure
|
|
18
41
|
|
|
19
42
|
### 1) Simple path — `baseUrl`, token, status handlers
|
|
@@ -555,6 +578,8 @@ await api.post('/orders', { body: { sku: 'A' } }) // not retried
|
|
|
555
578
|
### `sse-resume`
|
|
556
579
|
|
|
557
580
|
```ts
|
|
581
|
+
import { createFetch } from 'tanstack-fetch/sse'
|
|
582
|
+
|
|
558
583
|
const api = createFetch({
|
|
559
584
|
baseUrl: 'https://api.example.com',
|
|
560
585
|
plugins: ['sse-resume'],
|
|
@@ -703,6 +728,13 @@ Uses `fetch` streams (not `EventSource`) — Authorization, cookies, and SSR wor
|
|
|
703
728
|
### Simple — `onMessage`
|
|
704
729
|
|
|
705
730
|
```ts
|
|
731
|
+
import { createFetch } from 'tanstack-fetch/sse'
|
|
732
|
+
|
|
733
|
+
const api = createFetch({
|
|
734
|
+
baseUrl: 'https://api.example.com',
|
|
735
|
+
plugins: ['sse-resume'],
|
|
736
|
+
})
|
|
737
|
+
|
|
706
738
|
const stream = api.sse<OrderEvent>('/orders/stream', {
|
|
707
739
|
onMessage: (data) => {
|
|
708
740
|
console.log(data) // just the payload
|
|
@@ -716,8 +748,22 @@ stream.close()
|
|
|
716
748
|
|
|
717
749
|
### React — `useSse`
|
|
718
750
|
|
|
751
|
+
Pass a client created from `tanstack-fetch/sse`:
|
|
752
|
+
|
|
719
753
|
```tsx
|
|
720
|
-
import {
|
|
754
|
+
import { createFetch } from 'tanstack-fetch/sse'
|
|
755
|
+
import { FetchProvider, useSse } from 'tanstack-fetch/react'
|
|
756
|
+
|
|
757
|
+
const api = createFetch({
|
|
758
|
+
baseUrl: import.meta.env.VITE_API_URL,
|
|
759
|
+
plugins: ['sse-resume'],
|
|
760
|
+
})
|
|
761
|
+
|
|
762
|
+
const App = () => (
|
|
763
|
+
<FetchProvider client={api}>
|
|
764
|
+
<OrdersLive />
|
|
765
|
+
</FetchProvider>
|
|
766
|
+
)
|
|
721
767
|
|
|
722
768
|
const OrdersLive = () => {
|
|
723
769
|
const { data, isConnected, error } = useSse<OrderEvent>('/orders/stream')
|
|
@@ -734,6 +780,10 @@ const OrdersLive = () => {
|
|
|
734
780
|
### Advanced — `for await`
|
|
735
781
|
|
|
736
782
|
```ts
|
|
783
|
+
import { createFetch } from 'tanstack-fetch/sse'
|
|
784
|
+
|
|
785
|
+
const api = createFetch({ baseUrl: 'https://api.example.com' })
|
|
786
|
+
|
|
737
787
|
for await (const event of api.sse<OrderEvent>('/orders/stream', { signal })) {
|
|
738
788
|
event.event
|
|
739
789
|
event.data
|
package/dist/cli.js
CHANGED
|
@@ -1,234 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return "unknown";
|
|
20
|
-
}
|
|
21
|
-
if (schema.$ref) {
|
|
22
|
-
return refName(schema.$ref);
|
|
23
|
-
}
|
|
24
|
-
if (schema.enum && schema.enum.length > 0) {
|
|
25
|
-
return schema.enum.map((value) => JSON.stringify(value)).join(" | ");
|
|
26
|
-
}
|
|
27
|
-
const typeValue = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
28
|
-
if (typeValue === "string") {
|
|
29
|
-
return "string";
|
|
30
|
-
}
|
|
31
|
-
if (typeValue === "integer" || typeValue === "number") {
|
|
32
|
-
return "number";
|
|
33
|
-
}
|
|
34
|
-
if (typeValue === "boolean") {
|
|
35
|
-
return "boolean";
|
|
36
|
-
}
|
|
37
|
-
if (typeValue === "array") {
|
|
38
|
-
return `Array<${schemaToTs(schema.items)}>`;
|
|
39
|
-
}
|
|
40
|
-
if (typeValue === "object" || schema.properties) {
|
|
41
|
-
return objectToTs(schema);
|
|
42
|
-
}
|
|
43
|
-
return "unknown";
|
|
44
|
-
};
|
|
45
|
-
var objectToTs = (schema) => {
|
|
46
|
-
const required = new Set(schema.required ?? []);
|
|
47
|
-
const fields = Object.entries(schema.properties ?? {}).map(([key, value]) => {
|
|
48
|
-
const optional = required.has(key) ? "" : "?";
|
|
49
|
-
return ` ${key}${optional}: ${schemaToTs(value)}`;
|
|
50
|
-
});
|
|
51
|
-
if (fields.length === 0) {
|
|
52
|
-
return "Record<string, unknown>";
|
|
53
|
-
}
|
|
54
|
-
return `{
|
|
55
|
-
${fields.join("\n")}
|
|
56
|
-
}`;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// src/cli/collect-operations.ts
|
|
60
|
-
var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
|
|
61
|
-
var isSseOperation = (operation) => {
|
|
62
|
-
const contents = Object.values(operation.responses ?? {}).flatMap(
|
|
63
|
-
(response) => Object.keys(response.content ?? {})
|
|
64
|
-
);
|
|
65
|
-
return contents.some((type) => type.includes("event-stream"));
|
|
66
|
-
};
|
|
67
|
-
var collectOperations = (spec) => {
|
|
68
|
-
const operations = [];
|
|
69
|
-
Object.entries(spec.paths ?? {}).forEach(([path, methods]) => {
|
|
70
|
-
HTTP_METHODS.forEach((method) => {
|
|
71
|
-
const operation = methods?.[method];
|
|
72
|
-
if (!operation) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const tag = operation.tags?.[0] ?? "api";
|
|
76
|
-
const fallbackId = `${method}_${path}`;
|
|
77
|
-
operations.push({
|
|
78
|
-
method: method.toUpperCase(),
|
|
79
|
-
path,
|
|
80
|
-
operationId: toCamel(operation.operationId ?? fallbackId),
|
|
81
|
-
tag: toCamel(tag),
|
|
82
|
-
isSse: isSseOperation(operation),
|
|
83
|
-
parameters: operation.parameters ?? [],
|
|
84
|
-
bodySchema: operation.requestBody?.content?.["application/json"]?.schema,
|
|
85
|
-
successSchema: pickSuccessSchema(operation)
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
return operations;
|
|
90
|
-
};
|
|
91
|
-
var pickSuccessSchema = (operation) => {
|
|
92
|
-
const success = operation.responses?.["200"] ?? operation.responses?.["201"];
|
|
93
|
-
const content = success?.content ?? {};
|
|
94
|
-
return content["application/json"]?.schema ?? content["text/event-stream"]?.schema;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// src/cli/generate.ts
|
|
98
|
-
var generateTypesFile = (spec) => {
|
|
99
|
-
const schemas = Object.entries(spec.components?.schemas ?? {});
|
|
100
|
-
const types = schemas.map(([name, schema]) => `type ${toPascal(name)} = ${schemaToTs(schema)}`);
|
|
101
|
-
const exports = schemas.map(([name]) => toPascal(name));
|
|
102
|
-
if (exports.length === 0) {
|
|
103
|
-
return "export {}\n";
|
|
104
|
-
}
|
|
105
|
-
const body = `${types.join("\n\n")}
|
|
106
|
-
|
|
107
|
-
export type { ${exports.join(", ")} }
|
|
108
|
-
`;
|
|
109
|
-
return body.trimStart();
|
|
110
|
-
};
|
|
111
|
-
var paramsType = (operation, kind) => {
|
|
112
|
-
const params = operation.parameters.filter((item) => item.in === kind);
|
|
113
|
-
if (params.length === 0) {
|
|
114
|
-
return void 0;
|
|
115
|
-
}
|
|
116
|
-
const fields = params.map((item) => {
|
|
117
|
-
const optional = item.required ? "" : "?";
|
|
118
|
-
return `${item.name}${optional}: ${schemaToTs(item.schema)}`;
|
|
119
|
-
});
|
|
120
|
-
return `{ ${fields.join("; ")} }`;
|
|
121
|
-
};
|
|
122
|
-
var optionsType = (operation) => {
|
|
123
|
-
const fields = [];
|
|
124
|
-
const pathType = paramsType(operation, "path");
|
|
125
|
-
const queryType = paramsType(operation, "query");
|
|
126
|
-
if (pathType) {
|
|
127
|
-
fields.push(`params: ${pathType}`);
|
|
128
|
-
}
|
|
129
|
-
if (queryType) {
|
|
130
|
-
fields.push(`query?: ${queryType}`);
|
|
131
|
-
}
|
|
132
|
-
if (operation.bodySchema) {
|
|
133
|
-
fields.push(`body: ${schemaToTs(operation.bodySchema)}`);
|
|
134
|
-
}
|
|
135
|
-
if (fields.length === 0) {
|
|
136
|
-
return "RequestOptions | undefined";
|
|
137
|
-
}
|
|
138
|
-
return `Omit<RequestOptions, 'params' | 'query' | 'body'> & { ${fields.join("; ")} }`;
|
|
139
|
-
};
|
|
140
|
-
var clientPath = (path) => path.replace(/\{([A-Za-z0-9_]+)\}/g, ":$1");
|
|
141
|
-
var methodLine = (operation) => {
|
|
142
|
-
const response = schemaToTs(operation.successSchema);
|
|
143
|
-
const options = optionsType(operation);
|
|
144
|
-
const optional = options.endsWith("| undefined") ? "?" : "";
|
|
145
|
-
if (operation.isSse) {
|
|
146
|
-
return ` ${operation.operationId}: (options${optional}: ${options}) => api.sse<${response}>('${clientPath(operation.path)}', options),`;
|
|
147
|
-
}
|
|
148
|
-
const method = operation.method.toLowerCase();
|
|
149
|
-
const call = method === "delete" ? "delete" : method;
|
|
150
|
-
return ` ${operation.operationId}: (options${optional}: ${options}) => api.${call}<${response}>('${clientPath(operation.path)}', options),`;
|
|
151
|
-
};
|
|
152
|
-
var generateClientFile = (spec, operations) => {
|
|
153
|
-
const tags = [...new Set(operations.map((item) => item.tag))];
|
|
154
|
-
const groups = tags.map((tag) => {
|
|
155
|
-
const lines = operations.filter((item) => item.tag === tag).map(methodLine);
|
|
156
|
-
return ` ${tag}: {
|
|
157
|
-
${lines.join("\n")}
|
|
158
|
-
},`;
|
|
159
|
-
});
|
|
160
|
-
const schemaNames = Object.keys(spec.components?.schemas ?? {}).map((name) => toPascal(name));
|
|
161
|
-
const typesImport = schemaNames.length > 0 ? `import type { ${schemaNames.join(", ")} } from './types'
|
|
162
|
-
` : "";
|
|
163
|
-
return `import { createFetch } from 'tanstack-fetch'
|
|
164
|
-
import type { CreateFetchOptions, RequestOptions } from 'tanstack-fetch'
|
|
165
|
-
${typesImport}
|
|
2
|
+
import{mkdir as T,writeFile as f}from"fs/promises";import{join as h}from"path";var i=e=>e.replace(/[^A-Za-z0-9]+/g," ").trim().split(" ").filter(Boolean).map(t=>t[0].toUpperCase()+t.slice(1)).join("")||"Schema",u=e=>{let t=i(e);return t[0].toLowerCase()+t.slice(1)},k=e=>{let t=e.split("/");return i(t[t.length-1]??"Schema")},p=e=>{if(!e)return"unknown";if(e.$ref)return k(e.$ref);if(e.enum&&e.enum.length>0)return e.enum.map(n=>JSON.stringify(n)).join(" | ");let t=Array.isArray(e.type)?e.type[0]:e.type;return t==="string"?"string":t==="integer"||t==="number"?"number":t==="boolean"?"boolean":t==="array"?`Array<${p(e.items)}>`:t==="object"||e.properties?C(e):"unknown"},C=e=>{let t=new Set(e.required??[]),n=Object.entries(e.properties??{}).map(([r,s])=>{let o=t.has(r)?"":"?";return` ${r}${o}: ${p(s)}`});return n.length===0?"Record<string, unknown>":`{
|
|
3
|
+
${n.join(`
|
|
4
|
+
`)}
|
|
5
|
+
}`};var j=["get","post","put","patch","delete"],b=e=>Object.values(e.responses??{}).flatMap(n=>Object.keys(n.content??{})).some(n=>n.includes("event-stream")),d=e=>{let t=[];return Object.entries(e.paths??{}).forEach(([n,r])=>{j.forEach(s=>{let o=r?.[s];if(!o)return;let a=o.tags?.[0]??"api",m=`${s}_${n}`;t.push({method:s.toUpperCase(),path:n,operationId:u(o.operationId??m),tag:u(a),isSse:b(o),parameters:o.parameters??[],bodySchema:o.requestBody?.content?.["application/json"]?.schema,successSchema:x(o)})})}),t},x=e=>{let n=(e.responses?.["200"]??e.responses?.["201"])?.content??{};return n["application/json"]?.schema??n["text/event-stream"]?.schema};var q=e=>{let t=Object.entries(e.components?.schemas??{}),n=t.map(([o,a])=>`type ${i(o)} = ${p(a)}`),r=t.map(([o])=>i(o));return r.length===0?`export {}
|
|
6
|
+
`:`${n.join(`
|
|
7
|
+
|
|
8
|
+
`)}
|
|
9
|
+
|
|
10
|
+
export type { ${r.join(", ")} }
|
|
11
|
+
`.trimStart()},g=(e,t)=>{let n=e.parameters.filter(s=>s.in===t);return n.length===0?void 0:`{ ${n.map(s=>{let o=s.required?"":"?";return`${s.name}${o}: ${p(s.schema)}`}).join("; ")} }`},F=e=>{let t=[],n=g(e,"path"),r=g(e,"query");return n&&t.push(`params: ${n}`),r&&t.push(`query?: ${r}`),e.bodySchema&&t.push(`body: ${p(e.bodySchema)}`),t.length===0?"RequestOptions | undefined":`Omit<RequestOptions, 'params' | 'query' | 'body'> & { ${t.join("; ")} }`},y=e=>e.replace(/\{([A-Za-z0-9_]+)\}/g,":$1"),I=e=>{let t=p(e.successSchema),n=F(e),r=n.endsWith("| undefined")?"?":"";if(e.isSse)return` ${e.operationId}: (options${r}: ${n}) => api.sse<${t}>('${y(e.path)}', options),`;let s=e.method.toLowerCase(),o=s==="delete"?"delete":s;return` ${e.operationId}: (options${r}: ${n}) => api.${o}<${t}>('${y(e.path)}', options),`},E=(e,t)=>{let r=[...new Set(t.map(c=>c.tag))].map(c=>{let l=t.filter(A=>A.tag===c).map(I);return` ${c}: {
|
|
12
|
+
${l.join(`
|
|
13
|
+
`)}
|
|
14
|
+
},`}),s=Object.keys(e.components?.schemas??{}).map(c=>i(c)),o=s.length>0?`import type { ${s.join(", ")} } from './types'
|
|
15
|
+
`:"";return`${t.some(c=>c.isSse)?`import { createFetch } from 'tanstack-fetch/sse'
|
|
16
|
+
import type { CreateFetchOptions, RequestOptions } from 'tanstack-fetch'`:`import { createFetch } from 'tanstack-fetch'
|
|
17
|
+
import type { CreateFetchOptions, RequestOptions } from 'tanstack-fetch'`}
|
|
18
|
+
${o}
|
|
166
19
|
const createApi = (options: CreateFetchOptions = {}) => {
|
|
167
20
|
const api = createFetch(options)
|
|
168
21
|
return {
|
|
169
|
-
${
|
|
22
|
+
${r.join(`
|
|
23
|
+
`)}
|
|
170
24
|
}
|
|
171
25
|
}
|
|
172
26
|
|
|
173
27
|
export { createApi }
|
|
174
|
-
|
|
175
|
-
};
|
|
176
|
-
var generateIndexFile = () => `import { createApi } from './client'
|
|
28
|
+
`},J=()=>`import { createApi } from './client'
|
|
177
29
|
|
|
178
30
|
export { createApi }
|
|
179
31
|
export type * from './types'
|
|
180
|
-
|
|
181
|
-
var generateClient = async (spec, outDir) => {
|
|
182
|
-
const operations = collectOperations(spec);
|
|
183
|
-
await mkdir(outDir, { recursive: true });
|
|
184
|
-
await writeFile(join(outDir, "types.ts"), generateTypesFile(spec), "utf8");
|
|
185
|
-
await writeFile(join(outDir, "client.ts"), generateClientFile(spec, operations), "utf8");
|
|
186
|
-
await writeFile(join(outDir, "index.ts"), generateIndexFile(), "utf8");
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
// src/cli/load-spec.ts
|
|
190
|
-
import { readFile } from "fs/promises";
|
|
191
|
-
import { parse as parseYaml } from "yaml";
|
|
192
|
-
var loadSpec = async (specPath) => {
|
|
193
|
-
const raw = await readFile(specPath, "utf8");
|
|
194
|
-
const parsed = specPath.endsWith(".yaml") || specPath.endsWith(".yml") ? parseYaml(raw) : JSON.parse(raw);
|
|
195
|
-
if (!parsed || typeof parsed !== "object") {
|
|
196
|
-
throw new Error("tanstack-fetch: OpenAPI spec must be an object");
|
|
197
|
-
}
|
|
198
|
-
return parsed;
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
// src/cli/parse-args.ts
|
|
202
|
-
var parseArgs = (argv) => {
|
|
203
|
-
const [command, ...rest] = argv;
|
|
204
|
-
if (!command || command === "--help" || command === "help" || command === "-h") {
|
|
205
|
-
return { command: "help" };
|
|
206
|
-
}
|
|
207
|
-
if (command !== "generate") {
|
|
208
|
-
throw new Error(`tanstack-fetch: unknown command "${command}"`);
|
|
209
|
-
}
|
|
210
|
-
const flags = /* @__PURE__ */ new Map();
|
|
211
|
-
for (let index = 0; index < rest.length; index += 1) {
|
|
212
|
-
const token = rest[index];
|
|
213
|
-
if (!token.startsWith("--")) {
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
const key = token.slice(2);
|
|
217
|
-
const value = rest[index + 1];
|
|
218
|
-
if (!value || value.startsWith("--")) {
|
|
219
|
-
throw new Error(`tanstack-fetch: missing value for --${key}`);
|
|
220
|
-
}
|
|
221
|
-
flags.set(key, value);
|
|
222
|
-
index += 1;
|
|
223
|
-
}
|
|
224
|
-
const spec = flags.get("spec");
|
|
225
|
-
const out = flags.get("out");
|
|
226
|
-
if (!spec || !out) {
|
|
227
|
-
throw new Error("tanstack-fetch: generate requires --spec and --out");
|
|
228
|
-
}
|
|
229
|
-
return { command: "generate", spec, out };
|
|
230
|
-
};
|
|
231
|
-
var helpText = `tanstack-fetch
|
|
32
|
+
`,O=async(e,t)=>{let n=d(e);await T(t,{recursive:!0}),await f(h(t,"types.ts"),q(e),"utf8"),await f(h(t,"client.ts"),E(e,n),"utf8"),await f(h(t,"index.ts"),J(),"utf8")};import{readFile as v}from"fs/promises";var L=async e=>{try{return(await import("yaml")).parse(e)}catch{throw new Error('tanstack-fetch: install optional peer "yaml" to load YAML specs (npm i yaml)')}},$=async e=>{let t=await v(e,"utf8"),n=e.endsWith(".yaml")||e.endsWith(".yml")?await L(t):JSON.parse(t);if(!n||typeof n!="object")throw new Error("tanstack-fetch: OpenAPI spec must be an object");return n};var S=e=>{let[t,...n]=e;if(!t||t==="--help"||t==="help"||t==="-h")return{command:"help"};if(t!=="generate")throw new Error(`tanstack-fetch: unknown command "${t}"`);let r=new Map;for(let a=0;a<n.length;a+=1){let m=n[a];if(!m.startsWith("--"))continue;let c=m.slice(2),l=n[a+1];if(!l||l.startsWith("--"))throw new Error(`tanstack-fetch: missing value for --${c}`);r.set(c,l),a+=1}let s=r.get("spec"),o=r.get("out");if(!s||!o)throw new Error("tanstack-fetch: generate requires --spec and --out");return{command:"generate",spec:s,out:o}},w=`tanstack-fetch
|
|
232
33
|
|
|
233
34
|
Usage:
|
|
234
35
|
tanstack-fetch generate --spec ./openapi.json --out ./src/api
|
|
@@ -236,22 +37,4 @@ Usage:
|
|
|
236
37
|
Flags:
|
|
237
38
|
--spec OpenAPI/Swagger JSON or YAML file
|
|
238
39
|
--out Directory for generated client files
|
|
239
|
-
`;
|
|
240
|
-
|
|
241
|
-
// src/cli/index.ts
|
|
242
|
-
var runCli = async (argv = process.argv.slice(2)) => {
|
|
243
|
-
const args = parseArgs(argv);
|
|
244
|
-
if (args.command === "help") {
|
|
245
|
-
console.log(helpText);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
const spec = await loadSpec(args.spec);
|
|
249
|
-
await generateClient(spec, args.out);
|
|
250
|
-
console.log(`tanstack-fetch: generated client in ${args.out}`);
|
|
251
|
-
};
|
|
252
|
-
runCli().catch((error) => {
|
|
253
|
-
const message = error instanceof Error ? error.message : "Unknown CLI error";
|
|
254
|
-
console.error(message);
|
|
255
|
-
process.exitCode = 1;
|
|
256
|
-
});
|
|
257
|
-
//# sourceMappingURL=cli.js.map
|
|
40
|
+
`;var P=async(e=process.argv.slice(2))=>{let t=S(e);if(t.command==="help"){console.log(w);return}let n=await $(t.spec);await O(n,t.out),console.log(`tanstack-fetch: generated client in ${t.out}`)};P().catch(e=>{let t=e instanceof Error?e.message:"Unknown CLI error";console.error(t),process.exitCode=1});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { M as MaybePromise, C as ClientSource, I as IncomingHeaders, b as HttpInterceptor, P as PluginName, A as AuthConfig, S as StatusHandler, g as StatusHandlers, c as HttpMethod, h as PathParams, Q as QueryParams, a as FetchErrorInfo, F as FetchResult } from './config.type-eG_cuxpu.cjs';
|
|
2
|
+
|
|
3
|
+
type SseEvent<T = unknown> = {
|
|
4
|
+
event?: string;
|
|
5
|
+
data: T;
|
|
6
|
+
id?: string;
|
|
7
|
+
retry?: number;
|
|
8
|
+
};
|
|
9
|
+
type SseSubscription = {
|
|
10
|
+
/** Stop the stream. */
|
|
11
|
+
close: () => void;
|
|
12
|
+
};
|
|
13
|
+
type SseHandlers<T = unknown> = {
|
|
14
|
+
/** Simple path — only the payload. */
|
|
15
|
+
onMessage?: (data: T, event: SseEvent<T>) => void;
|
|
16
|
+
/** Full SSE event (`event`, `data`, `id`). */
|
|
17
|
+
onEvent?: (event: SseEvent<T>) => void;
|
|
18
|
+
onOpen?: () => void;
|
|
19
|
+
onError?: (error: unknown) => void;
|
|
20
|
+
onClose?: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type RequestInterceptorConfig = {
|
|
24
|
+
use?: HttpInterceptor[];
|
|
25
|
+
eject?: string[];
|
|
26
|
+
};
|
|
27
|
+
type RequestOptions = {
|
|
28
|
+
params?: PathParams;
|
|
29
|
+
query?: QueryParams;
|
|
30
|
+
body?: unknown;
|
|
31
|
+
headers?: HeadersInit;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
/** Default `true` — matches TanStack Query `queryFn` (throw on HTTP error). */
|
|
35
|
+
throwOnError?: boolean;
|
|
36
|
+
parseAs?: 'json' | 'text' | 'blob';
|
|
37
|
+
operation?: string;
|
|
38
|
+
interceptors?: RequestInterceptorConfig;
|
|
39
|
+
};
|
|
40
|
+
type CreateFetchOptions = {
|
|
41
|
+
baseUrl?: string;
|
|
42
|
+
headers?: HeadersInit | (() => MaybePromise<HeadersInit>);
|
|
43
|
+
source?: ClientSource;
|
|
44
|
+
incoming?: IncomingHeaders | (() => MaybePromise<IncomingHeaders>);
|
|
45
|
+
timeoutMs?: number;
|
|
46
|
+
/** Default `true` for TanStack Query. Set `false` to get `FetchResult`. */
|
|
47
|
+
throwOnError?: boolean;
|
|
48
|
+
interceptors?: HttpInterceptor[];
|
|
49
|
+
plugins?: PluginName[];
|
|
50
|
+
fetch?: typeof fetch;
|
|
51
|
+
credentials?: RequestCredentials;
|
|
52
|
+
maxRetries?: number;
|
|
53
|
+
/** Simple auth: attach Bearer token on every request. */
|
|
54
|
+
getToken?: () => MaybePromise<string | null | undefined>;
|
|
55
|
+
/** Advanced auth config (overrides `getToken` when both set via `auth`). */
|
|
56
|
+
auth?: AuthConfig;
|
|
57
|
+
/** Called on HTTP 401 before the error is thrown / returned. */
|
|
58
|
+
onUnauthorized?: StatusHandler;
|
|
59
|
+
/** Called on HTTP 403. */
|
|
60
|
+
onForbidden?: StatusHandler;
|
|
61
|
+
/** Called on HTTP 404. */
|
|
62
|
+
onNotFound?: StatusHandler;
|
|
63
|
+
/** Called on HTTP 5xx (500–599). */
|
|
64
|
+
onServerError?: StatusHandler;
|
|
65
|
+
/** Advanced per-status map (`401`, `403`, `4xx`, `5xx`, `default`, …). */
|
|
66
|
+
onStatus?: StatusHandlers;
|
|
67
|
+
};
|
|
68
|
+
type ThrowingOptions = Omit<RequestOptions, 'throwOnError'> & {
|
|
69
|
+
throwOnError?: true;
|
|
70
|
+
};
|
|
71
|
+
type ResultOptions = Omit<RequestOptions, 'throwOnError'> & {
|
|
72
|
+
throwOnError: false;
|
|
73
|
+
};
|
|
74
|
+
type FetchMethod = {
|
|
75
|
+
<T>(path: string, options?: ThrowingOptions): Promise<T>;
|
|
76
|
+
<T, E = FetchErrorInfo>(path: string, options: ResultOptions): Promise<FetchResult<T, E>>;
|
|
77
|
+
};
|
|
78
|
+
type FetchRequest = {
|
|
79
|
+
<T>(method: HttpMethod, path: string, options?: ThrowingOptions): Promise<T>;
|
|
80
|
+
<T, E = FetchErrorInfo>(method: HttpMethod, path: string, options: ResultOptions): Promise<FetchResult<T, E>>;
|
|
81
|
+
};
|
|
82
|
+
type SseCallOptions<T = unknown> = RequestOptions & SseHandlers<T> & {
|
|
83
|
+
lastEventId?: string;
|
|
84
|
+
};
|
|
85
|
+
type FetchClient = {
|
|
86
|
+
use: (name: string, interceptor: Omit<HttpInterceptor, 'name'> & {
|
|
87
|
+
name?: string;
|
|
88
|
+
}, config?: {
|
|
89
|
+
order?: number;
|
|
90
|
+
}) => void;
|
|
91
|
+
eject: (name: string) => void;
|
|
92
|
+
request: FetchRequest;
|
|
93
|
+
get: FetchMethod;
|
|
94
|
+
post: FetchMethod;
|
|
95
|
+
put: FetchMethod;
|
|
96
|
+
patch: FetchMethod;
|
|
97
|
+
delete: FetchMethod;
|
|
98
|
+
/**
|
|
99
|
+
* Simple: pass `onMessage` / `onEvent` → returns `{ close }`.
|
|
100
|
+
* Advanced: no handlers → `AsyncIterable` for `for await`.
|
|
101
|
+
*/
|
|
102
|
+
sse: {
|
|
103
|
+
<T>(path: string, options: SseCallOptions<T> & ({
|
|
104
|
+
onMessage: SseHandlers<T>['onMessage'];
|
|
105
|
+
} | {
|
|
106
|
+
onEvent: SseHandlers<T>['onEvent'];
|
|
107
|
+
})): SseSubscription;
|
|
108
|
+
<T>(path: string, options?: SseCallOptions<T>): AsyncIterable<SseEvent<T>>;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
/** @deprecated Use CreateFetchOptions */
|
|
112
|
+
type CreateClientOptions = CreateFetchOptions;
|
|
113
|
+
/** @deprecated Use FetchClient */
|
|
114
|
+
type HttpClient = FetchClient;
|
|
115
|
+
|
|
116
|
+
export type { CreateFetchOptions as C, FetchClient as F, HttpClient as H, RequestOptions as R, SseCallOptions as S, CreateClientOptions as a, SseEvent as b, SseHandlers as c, SseSubscription as d };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { M as MaybePromise, C as ClientSource, I as IncomingHeaders, b as HttpInterceptor, P as PluginName, A as AuthConfig, S as StatusHandler, g as StatusHandlers, c as HttpMethod, h as PathParams, Q as QueryParams, a as FetchErrorInfo, F as FetchResult } from './config.type-eG_cuxpu.js';
|
|
2
|
+
|
|
3
|
+
type SseEvent<T = unknown> = {
|
|
4
|
+
event?: string;
|
|
5
|
+
data: T;
|
|
6
|
+
id?: string;
|
|
7
|
+
retry?: number;
|
|
8
|
+
};
|
|
9
|
+
type SseSubscription = {
|
|
10
|
+
/** Stop the stream. */
|
|
11
|
+
close: () => void;
|
|
12
|
+
};
|
|
13
|
+
type SseHandlers<T = unknown> = {
|
|
14
|
+
/** Simple path — only the payload. */
|
|
15
|
+
onMessage?: (data: T, event: SseEvent<T>) => void;
|
|
16
|
+
/** Full SSE event (`event`, `data`, `id`). */
|
|
17
|
+
onEvent?: (event: SseEvent<T>) => void;
|
|
18
|
+
onOpen?: () => void;
|
|
19
|
+
onError?: (error: unknown) => void;
|
|
20
|
+
onClose?: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type RequestInterceptorConfig = {
|
|
24
|
+
use?: HttpInterceptor[];
|
|
25
|
+
eject?: string[];
|
|
26
|
+
};
|
|
27
|
+
type RequestOptions = {
|
|
28
|
+
params?: PathParams;
|
|
29
|
+
query?: QueryParams;
|
|
30
|
+
body?: unknown;
|
|
31
|
+
headers?: HeadersInit;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
/** Default `true` — matches TanStack Query `queryFn` (throw on HTTP error). */
|
|
35
|
+
throwOnError?: boolean;
|
|
36
|
+
parseAs?: 'json' | 'text' | 'blob';
|
|
37
|
+
operation?: string;
|
|
38
|
+
interceptors?: RequestInterceptorConfig;
|
|
39
|
+
};
|
|
40
|
+
type CreateFetchOptions = {
|
|
41
|
+
baseUrl?: string;
|
|
42
|
+
headers?: HeadersInit | (() => MaybePromise<HeadersInit>);
|
|
43
|
+
source?: ClientSource;
|
|
44
|
+
incoming?: IncomingHeaders | (() => MaybePromise<IncomingHeaders>);
|
|
45
|
+
timeoutMs?: number;
|
|
46
|
+
/** Default `true` for TanStack Query. Set `false` to get `FetchResult`. */
|
|
47
|
+
throwOnError?: boolean;
|
|
48
|
+
interceptors?: HttpInterceptor[];
|
|
49
|
+
plugins?: PluginName[];
|
|
50
|
+
fetch?: typeof fetch;
|
|
51
|
+
credentials?: RequestCredentials;
|
|
52
|
+
maxRetries?: number;
|
|
53
|
+
/** Simple auth: attach Bearer token on every request. */
|
|
54
|
+
getToken?: () => MaybePromise<string | null | undefined>;
|
|
55
|
+
/** Advanced auth config (overrides `getToken` when both set via `auth`). */
|
|
56
|
+
auth?: AuthConfig;
|
|
57
|
+
/** Called on HTTP 401 before the error is thrown / returned. */
|
|
58
|
+
onUnauthorized?: StatusHandler;
|
|
59
|
+
/** Called on HTTP 403. */
|
|
60
|
+
onForbidden?: StatusHandler;
|
|
61
|
+
/** Called on HTTP 404. */
|
|
62
|
+
onNotFound?: StatusHandler;
|
|
63
|
+
/** Called on HTTP 5xx (500–599). */
|
|
64
|
+
onServerError?: StatusHandler;
|
|
65
|
+
/** Advanced per-status map (`401`, `403`, `4xx`, `5xx`, `default`, …). */
|
|
66
|
+
onStatus?: StatusHandlers;
|
|
67
|
+
};
|
|
68
|
+
type ThrowingOptions = Omit<RequestOptions, 'throwOnError'> & {
|
|
69
|
+
throwOnError?: true;
|
|
70
|
+
};
|
|
71
|
+
type ResultOptions = Omit<RequestOptions, 'throwOnError'> & {
|
|
72
|
+
throwOnError: false;
|
|
73
|
+
};
|
|
74
|
+
type FetchMethod = {
|
|
75
|
+
<T>(path: string, options?: ThrowingOptions): Promise<T>;
|
|
76
|
+
<T, E = FetchErrorInfo>(path: string, options: ResultOptions): Promise<FetchResult<T, E>>;
|
|
77
|
+
};
|
|
78
|
+
type FetchRequest = {
|
|
79
|
+
<T>(method: HttpMethod, path: string, options?: ThrowingOptions): Promise<T>;
|
|
80
|
+
<T, E = FetchErrorInfo>(method: HttpMethod, path: string, options: ResultOptions): Promise<FetchResult<T, E>>;
|
|
81
|
+
};
|
|
82
|
+
type SseCallOptions<T = unknown> = RequestOptions & SseHandlers<T> & {
|
|
83
|
+
lastEventId?: string;
|
|
84
|
+
};
|
|
85
|
+
type FetchClient = {
|
|
86
|
+
use: (name: string, interceptor: Omit<HttpInterceptor, 'name'> & {
|
|
87
|
+
name?: string;
|
|
88
|
+
}, config?: {
|
|
89
|
+
order?: number;
|
|
90
|
+
}) => void;
|
|
91
|
+
eject: (name: string) => void;
|
|
92
|
+
request: FetchRequest;
|
|
93
|
+
get: FetchMethod;
|
|
94
|
+
post: FetchMethod;
|
|
95
|
+
put: FetchMethod;
|
|
96
|
+
patch: FetchMethod;
|
|
97
|
+
delete: FetchMethod;
|
|
98
|
+
/**
|
|
99
|
+
* Simple: pass `onMessage` / `onEvent` → returns `{ close }`.
|
|
100
|
+
* Advanced: no handlers → `AsyncIterable` for `for await`.
|
|
101
|
+
*/
|
|
102
|
+
sse: {
|
|
103
|
+
<T>(path: string, options: SseCallOptions<T> & ({
|
|
104
|
+
onMessage: SseHandlers<T>['onMessage'];
|
|
105
|
+
} | {
|
|
106
|
+
onEvent: SseHandlers<T>['onEvent'];
|
|
107
|
+
})): SseSubscription;
|
|
108
|
+
<T>(path: string, options?: SseCallOptions<T>): AsyncIterable<SseEvent<T>>;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
/** @deprecated Use CreateFetchOptions */
|
|
112
|
+
type CreateClientOptions = CreateFetchOptions;
|
|
113
|
+
/** @deprecated Use FetchClient */
|
|
114
|
+
type HttpClient = FetchClient;
|
|
115
|
+
|
|
116
|
+
export type { CreateFetchOptions as C, FetchClient as F, HttpClient as H, RequestOptions as R, SseCallOptions as S, CreateClientOptions as a, SseEvent as b, SseHandlers as c, SseSubscription as d };
|