openapi-typesafe-fetch 0.0.0 → 1.0.0-rc.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/LICENSE +22 -0
- package/README.md +52 -1
- package/dist/index.cjs +620 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +413 -0
- package/dist/index.d.mts +413 -0
- package/dist/index.d.ts +413 -0
- package/dist/index.mjs +604 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +87 -4
- package/src/index.d.ts +423 -0
- package/src/index.js +814 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Drew Powers
|
|
4
|
+
Copyright (c) 2026 Fedor Chishchin and openapi-typesafe contributors
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,54 @@
|
|
|
1
1
|
# openapi-typesafe-fetch
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A type-safe `fetch` client for types generated by [`openapi-typesafe`](../openapi-typesafe). About 8 kB minified,
|
|
4
|
+
one tiny dependency, works in browsers, Node.js, Bun and Deno, and with React, Vue, Svelte or plain JavaScript.
|
|
5
|
+
|
|
6
|
+
A maintained, drop-in successor to `openapi-fetch` (see the [repository README](../../README.md)).
|
|
7
|
+
|
|
8
|
+
> **Status: in development; not published to npm yet.**
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm i openapi-typesafe-fetch
|
|
14
|
+
npm i -D openapi-typesafe typescript
|
|
15
|
+
npx openapi-typesafe ./openapi.yaml -o ./src/api/schema.d.ts
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import createClient from "openapi-typesafe-fetch";
|
|
22
|
+
import type { paths } from "./api/schema";
|
|
23
|
+
|
|
24
|
+
const client = createClient<paths>({ baseUrl: "https://api.example.com/v1/" });
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
data, // present on a 2xx response
|
|
28
|
+
error, // present on a 4xx or 5xx response
|
|
29
|
+
} = await client.GET("/posts/{post_id}", {
|
|
30
|
+
params: { path: { post_id: "123" } },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await client.PUT("/posts", {
|
|
34
|
+
body: { title: "My new post" },
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
URLs, parameters, request bodies and responses are all checked against the schema, with no generics or type
|
|
39
|
+
assertions in your code. `GET()`, `PUT()`, `POST()` and the other methods are thin wrappers around the native
|
|
40
|
+
[Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API); you can pass your own `fetch`.
|
|
41
|
+
|
|
42
|
+
Also included: middleware (`client.use()` / `client.eject()`), custom query, path and body serializers, `parseAs`
|
|
43
|
+
for non-JSON responses, and a path-based client (`createPathBasedClient`).
|
|
44
|
+
|
|
45
|
+
## Migrating from openapi-fetch
|
|
46
|
+
|
|
47
|
+
```diff
|
|
48
|
+
- import createClient from "openapi-fetch";
|
|
49
|
+
+ import createClient from "openapi-typesafe-fetch";
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
[MIT](./LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
const PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
6
|
+
const DOT_SEGMENT_RE = /(^|\/)\.\.?(\/|$)/;
|
|
7
|
+
const JSON_MEDIA_TYPE_RE = /json/i;
|
|
8
|
+
const FORM_MEDIA_TYPE = "application/x-www-form-urlencoded";
|
|
9
|
+
const MULTIPART_MEDIA_TYPE_RE = /^multipart\/form-data(;|$)/i;
|
|
10
|
+
const NESTED_FORM_VALUE_MESSAGE = "Nested form values need your own `bodySerializer()`.";
|
|
11
|
+
const BOUNDARY_PARAMETER_RE = /;\s*boundary=/i;
|
|
12
|
+
const supportsRequestInitExt = () => {
|
|
13
|
+
return typeof process === "object" && process.versions?.undici;
|
|
14
|
+
};
|
|
15
|
+
let requestIdCount = 0;
|
|
16
|
+
function randomID() {
|
|
17
|
+
requestIdCount += 1;
|
|
18
|
+
return requestIdCount.toString(36);
|
|
19
|
+
}
|
|
20
|
+
function createClient(clientOptions) {
|
|
21
|
+
let {
|
|
22
|
+
baseUrl = "",
|
|
23
|
+
Request: CustomRequest = globalThis.Request,
|
|
24
|
+
fetch: baseFetch = globalThis.fetch,
|
|
25
|
+
querySerializer: globalQuerySerializer,
|
|
26
|
+
bodySerializer: globalBodySerializer,
|
|
27
|
+
pathSerializer: globalPathSerializer,
|
|
28
|
+
headers: baseHeaders,
|
|
29
|
+
requestInitExt = void 0,
|
|
30
|
+
...baseOptions
|
|
31
|
+
} = { ...clientOptions };
|
|
32
|
+
requestInitExt = supportsRequestInitExt() ? requestInitExt : void 0;
|
|
33
|
+
baseUrl = removeTrailingSlash(baseUrl);
|
|
34
|
+
const globalMiddlewares = [];
|
|
35
|
+
async function coreFetch(schemaPath, fetchOptions) {
|
|
36
|
+
const {
|
|
37
|
+
baseUrl: localBaseUrl,
|
|
38
|
+
fetch = baseFetch,
|
|
39
|
+
Request = CustomRequest,
|
|
40
|
+
headers,
|
|
41
|
+
params = {},
|
|
42
|
+
parseAs = "json",
|
|
43
|
+
querySerializer: requestQuerySerializer,
|
|
44
|
+
bodySerializer = globalBodySerializer ?? defaultBodySerializer,
|
|
45
|
+
pathSerializer: requestPathSerializer,
|
|
46
|
+
body,
|
|
47
|
+
middleware: requestMiddlewares = [],
|
|
48
|
+
...init
|
|
49
|
+
} = fetchOptions || {};
|
|
50
|
+
let finalBaseUrl = baseUrl;
|
|
51
|
+
if (localBaseUrl) {
|
|
52
|
+
finalBaseUrl = removeTrailingSlash(localBaseUrl) ?? baseUrl;
|
|
53
|
+
}
|
|
54
|
+
let querySerializer = typeof globalQuerySerializer === "function" ? globalQuerySerializer : createQuerySerializer(globalQuerySerializer);
|
|
55
|
+
if (requestQuerySerializer) {
|
|
56
|
+
querySerializer = typeof requestQuerySerializer === "function" ? requestQuerySerializer : createQuerySerializer({
|
|
57
|
+
...typeof globalQuerySerializer === "object" ? globalQuerySerializer : {},
|
|
58
|
+
...requestQuerySerializer
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const pathSerializer = requestPathSerializer || globalPathSerializer || defaultPathSerializer;
|
|
62
|
+
const serializedBody = body === void 0 ? void 0 : bodySerializer(
|
|
63
|
+
body,
|
|
64
|
+
// Note: we declare mergeHeaders() both here and below because it’s a bit of a chicken-or-egg situation:
|
|
65
|
+
// bodySerializer() needs all headers so we aren’t dropping ones set by the user, however,
|
|
66
|
+
// the result of this ALSO sets the lowest-priority content-type header. So we re-merge below,
|
|
67
|
+
// setting the content-type at the very beginning to be overwritten.
|
|
68
|
+
// Lastly, based on the way headers work, it’s not a simple “present-or-not” check becauase null intentionally un-sets headers.
|
|
69
|
+
mergeHeaders(baseHeaders, headers, params.header)
|
|
70
|
+
);
|
|
71
|
+
const finalHeaders = mergeHeaders(
|
|
72
|
+
// with no body, we should not to set Content-Type
|
|
73
|
+
serializedBody === void 0 || // a raw body (FormData, Blob, ...) gets its Content-Type from the platform, or none
|
|
74
|
+
isRawBody(serializedBody) ? {} : {
|
|
75
|
+
"Content-Type": "application/json"
|
|
76
|
+
},
|
|
77
|
+
baseHeaders,
|
|
78
|
+
headers,
|
|
79
|
+
params.header
|
|
80
|
+
);
|
|
81
|
+
if (serializedBody instanceof FormData && !BOUNDARY_PARAMETER_RE.test(finalHeaders.get("Content-Type"))) {
|
|
82
|
+
finalHeaders.delete("Content-Type");
|
|
83
|
+
}
|
|
84
|
+
const finalMiddlewares = [...globalMiddlewares, ...requestMiddlewares];
|
|
85
|
+
const requestInit = {
|
|
86
|
+
redirect: "follow",
|
|
87
|
+
...baseOptions,
|
|
88
|
+
...init,
|
|
89
|
+
body: serializedBody,
|
|
90
|
+
headers: finalHeaders
|
|
91
|
+
};
|
|
92
|
+
let id;
|
|
93
|
+
let options;
|
|
94
|
+
let request = new Request(
|
|
95
|
+
createFinalURL(schemaPath, { baseUrl: finalBaseUrl, params, querySerializer, pathSerializer }),
|
|
96
|
+
requestInit
|
|
97
|
+
);
|
|
98
|
+
let response;
|
|
99
|
+
for (const key in init) {
|
|
100
|
+
if (!(key in request)) {
|
|
101
|
+
request[key] = init[key];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (finalMiddlewares.length) {
|
|
105
|
+
id = randomID();
|
|
106
|
+
options = Object.freeze({
|
|
107
|
+
baseUrl: finalBaseUrl,
|
|
108
|
+
fetch,
|
|
109
|
+
parseAs,
|
|
110
|
+
querySerializer,
|
|
111
|
+
bodySerializer,
|
|
112
|
+
pathSerializer
|
|
113
|
+
});
|
|
114
|
+
for (const m of finalMiddlewares) {
|
|
115
|
+
if (m && typeof m === "object" && typeof m.onRequest === "function") {
|
|
116
|
+
const result = await m.onRequest({
|
|
117
|
+
request,
|
|
118
|
+
schemaPath,
|
|
119
|
+
params,
|
|
120
|
+
options,
|
|
121
|
+
id
|
|
122
|
+
});
|
|
123
|
+
if (result) {
|
|
124
|
+
if (isRequest(result)) {
|
|
125
|
+
request = result;
|
|
126
|
+
} else if (isResponse(result)) {
|
|
127
|
+
response = result;
|
|
128
|
+
break;
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error("onRequest: must return new Request() or Response() when modifying the request");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!response) {
|
|
137
|
+
try {
|
|
138
|
+
response = await fetch(request, requestInitExt);
|
|
139
|
+
} catch (error2) {
|
|
140
|
+
let errorAfterMiddleware = error2;
|
|
141
|
+
if (finalMiddlewares.length) {
|
|
142
|
+
for (let i = finalMiddlewares.length - 1; i >= 0; i--) {
|
|
143
|
+
const m = finalMiddlewares[i];
|
|
144
|
+
if (m && typeof m === "object" && typeof m.onError === "function") {
|
|
145
|
+
const result = await m.onError({
|
|
146
|
+
request,
|
|
147
|
+
error: errorAfterMiddleware,
|
|
148
|
+
schemaPath,
|
|
149
|
+
params,
|
|
150
|
+
options,
|
|
151
|
+
id
|
|
152
|
+
});
|
|
153
|
+
if (result) {
|
|
154
|
+
if (isResponse(result)) {
|
|
155
|
+
errorAfterMiddleware = void 0;
|
|
156
|
+
response = result;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
if (result instanceof Error) {
|
|
160
|
+
errorAfterMiddleware = result;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
throw new Error("onError: must return new Response() or instance of Error");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (errorAfterMiddleware) {
|
|
169
|
+
throw errorAfterMiddleware;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (finalMiddlewares.length) {
|
|
173
|
+
for (let i = finalMiddlewares.length - 1; i >= 0; i--) {
|
|
174
|
+
const m = finalMiddlewares[i];
|
|
175
|
+
if (m && typeof m === "object" && typeof m.onResponse === "function") {
|
|
176
|
+
const result = await m.onResponse({
|
|
177
|
+
request,
|
|
178
|
+
response,
|
|
179
|
+
schemaPath,
|
|
180
|
+
params,
|
|
181
|
+
options,
|
|
182
|
+
id
|
|
183
|
+
});
|
|
184
|
+
if (result) {
|
|
185
|
+
if (!isResponse(result)) {
|
|
186
|
+
throw new Error("onResponse: must return new Response() when modifying the response");
|
|
187
|
+
}
|
|
188
|
+
response = result;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const contentLength = response.headers.get("Content-Length");
|
|
195
|
+
if (response.status === 204 || request.method === "HEAD" || contentLength === "0" && !response.headers.get("Transfer-Encoding")?.includes("chunked")) {
|
|
196
|
+
return response.ok ? { data: void 0, response } : { error: void 0, response };
|
|
197
|
+
}
|
|
198
|
+
if (response.ok) {
|
|
199
|
+
const getResponseData = async () => {
|
|
200
|
+
if (parseAs === "stream") {
|
|
201
|
+
return response.body;
|
|
202
|
+
}
|
|
203
|
+
if (parseAs === "json" && !contentLength) {
|
|
204
|
+
const raw2 = await response.text();
|
|
205
|
+
return raw2 ? JSON.parse(raw2) : void 0;
|
|
206
|
+
}
|
|
207
|
+
return await response[parseAs]();
|
|
208
|
+
};
|
|
209
|
+
return { data: await getResponseData(), response };
|
|
210
|
+
}
|
|
211
|
+
const raw = await response.text();
|
|
212
|
+
if (!raw) {
|
|
213
|
+
return { error: void 0, response };
|
|
214
|
+
}
|
|
215
|
+
let error = raw;
|
|
216
|
+
try {
|
|
217
|
+
error = JSON.parse(raw);
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
return { error, response };
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
request(method, url, init) {
|
|
224
|
+
return coreFetch(url, { ...init, method: method.toUpperCase() });
|
|
225
|
+
},
|
|
226
|
+
/** Call a GET endpoint */
|
|
227
|
+
GET(url, init) {
|
|
228
|
+
return coreFetch(url, { ...init, method: "GET" });
|
|
229
|
+
},
|
|
230
|
+
/** Call a PUT endpoint */
|
|
231
|
+
PUT(url, init) {
|
|
232
|
+
return coreFetch(url, { ...init, method: "PUT" });
|
|
233
|
+
},
|
|
234
|
+
/** Call a POST endpoint */
|
|
235
|
+
POST(url, init) {
|
|
236
|
+
return coreFetch(url, { ...init, method: "POST" });
|
|
237
|
+
},
|
|
238
|
+
/** Call a DELETE endpoint */
|
|
239
|
+
DELETE(url, init) {
|
|
240
|
+
return coreFetch(url, { ...init, method: "DELETE" });
|
|
241
|
+
},
|
|
242
|
+
/** Call a OPTIONS endpoint */
|
|
243
|
+
OPTIONS(url, init) {
|
|
244
|
+
return coreFetch(url, { ...init, method: "OPTIONS" });
|
|
245
|
+
},
|
|
246
|
+
/** Call a HEAD endpoint */
|
|
247
|
+
HEAD(url, init) {
|
|
248
|
+
return coreFetch(url, { ...init, method: "HEAD" });
|
|
249
|
+
},
|
|
250
|
+
/** Call a PATCH endpoint */
|
|
251
|
+
PATCH(url, init) {
|
|
252
|
+
return coreFetch(url, { ...init, method: "PATCH" });
|
|
253
|
+
},
|
|
254
|
+
/** Call a TRACE endpoint */
|
|
255
|
+
TRACE(url, init) {
|
|
256
|
+
return coreFetch(url, { ...init, method: "TRACE" });
|
|
257
|
+
},
|
|
258
|
+
/** Call a QUERY endpoint (OpenAPI 3.2) */
|
|
259
|
+
QUERY(url, init) {
|
|
260
|
+
return coreFetch(url, { ...init, method: "QUERY" });
|
|
261
|
+
},
|
|
262
|
+
/** Register middleware */
|
|
263
|
+
use(...middleware) {
|
|
264
|
+
for (const m of middleware) {
|
|
265
|
+
if (!m) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (typeof m !== "object" || !("onRequest" in m || "onResponse" in m || "onError" in m)) {
|
|
269
|
+
throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse()` or `onError()`");
|
|
270
|
+
}
|
|
271
|
+
globalMiddlewares.push(m);
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
/** Unregister middleware */
|
|
275
|
+
eject(...middleware) {
|
|
276
|
+
for (const m of middleware) {
|
|
277
|
+
const i = globalMiddlewares.indexOf(m);
|
|
278
|
+
if (i !== -1) {
|
|
279
|
+
globalMiddlewares.splice(i, 1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
class PathCallForwarder {
|
|
286
|
+
constructor(client, url) {
|
|
287
|
+
this.client = client;
|
|
288
|
+
this.url = url;
|
|
289
|
+
}
|
|
290
|
+
GET = (init) => {
|
|
291
|
+
return this.client.GET(this.url, init);
|
|
292
|
+
};
|
|
293
|
+
PUT = (init) => {
|
|
294
|
+
return this.client.PUT(this.url, init);
|
|
295
|
+
};
|
|
296
|
+
POST = (init) => {
|
|
297
|
+
return this.client.POST(this.url, init);
|
|
298
|
+
};
|
|
299
|
+
DELETE = (init) => {
|
|
300
|
+
return this.client.DELETE(this.url, init);
|
|
301
|
+
};
|
|
302
|
+
OPTIONS = (init) => {
|
|
303
|
+
return this.client.OPTIONS(this.url, init);
|
|
304
|
+
};
|
|
305
|
+
HEAD = (init) => {
|
|
306
|
+
return this.client.HEAD(this.url, init);
|
|
307
|
+
};
|
|
308
|
+
PATCH = (init) => {
|
|
309
|
+
return this.client.PATCH(this.url, init);
|
|
310
|
+
};
|
|
311
|
+
TRACE = (init) => {
|
|
312
|
+
return this.client.TRACE(this.url, init);
|
|
313
|
+
};
|
|
314
|
+
QUERY = (init) => {
|
|
315
|
+
return this.client.QUERY(this.url, init);
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
class PathClientProxyHandler {
|
|
319
|
+
constructor() {
|
|
320
|
+
this.client = null;
|
|
321
|
+
}
|
|
322
|
+
// Assume the property is an URL.
|
|
323
|
+
get(coreClient, url) {
|
|
324
|
+
const forwarder = new PathCallForwarder(coreClient, url);
|
|
325
|
+
this.client[url] = forwarder;
|
|
326
|
+
return forwarder;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function wrapAsPathBasedClient(coreClient) {
|
|
330
|
+
const handler = new PathClientProxyHandler();
|
|
331
|
+
const proxy = new Proxy(coreClient, handler);
|
|
332
|
+
function Client() {
|
|
333
|
+
}
|
|
334
|
+
Client.prototype = proxy;
|
|
335
|
+
const client = new Client();
|
|
336
|
+
handler.client = client;
|
|
337
|
+
return client;
|
|
338
|
+
}
|
|
339
|
+
function createPathBasedClient(clientOptions) {
|
|
340
|
+
return wrapAsPathBasedClient(createClient(clientOptions));
|
|
341
|
+
}
|
|
342
|
+
function isHeaders(value) {
|
|
343
|
+
return typeof value?.get === "function";
|
|
344
|
+
}
|
|
345
|
+
function isRequest(value) {
|
|
346
|
+
return typeof value?.method === "string" && typeof value.url === "string" && isHeaders(value.headers);
|
|
347
|
+
}
|
|
348
|
+
function isResponse(value) {
|
|
349
|
+
return typeof value?.status === "number" && isHeaders(value.headers);
|
|
350
|
+
}
|
|
351
|
+
const KEPT_NAME_ESCAPES = /%(?:5B|5D|24|3A|40|2C)/g;
|
|
352
|
+
function encodeName(name) {
|
|
353
|
+
return encodeURIComponent(name).replace(KEPT_NAME_ESCAPES, decodeURIComponent);
|
|
354
|
+
}
|
|
355
|
+
function serializePrimitiveParam(name, value, options) {
|
|
356
|
+
if (value === void 0 || value === null) {
|
|
357
|
+
return "";
|
|
358
|
+
}
|
|
359
|
+
if (typeof value === "object") {
|
|
360
|
+
throw new Error(
|
|
361
|
+
"Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
return `${name}=${options?.allowReserved === true ? value : encodeURIComponent(value)}`;
|
|
365
|
+
}
|
|
366
|
+
function serializeObjectParam(name, value, options) {
|
|
367
|
+
if (!value || typeof value !== "object") {
|
|
368
|
+
return "";
|
|
369
|
+
}
|
|
370
|
+
const values = [];
|
|
371
|
+
const joiner = {
|
|
372
|
+
simple: ",",
|
|
373
|
+
label: ".",
|
|
374
|
+
matrix: ";"
|
|
375
|
+
}[options.style] || "&";
|
|
376
|
+
if (options.style !== "deepObject" && options.explode === false) {
|
|
377
|
+
for (const k in value) {
|
|
378
|
+
values.push(encodeName(k), options.allowReserved === true ? value[k] : encodeURIComponent(value[k]));
|
|
379
|
+
}
|
|
380
|
+
const final2 = values.join(",");
|
|
381
|
+
switch (options.style) {
|
|
382
|
+
case "form": {
|
|
383
|
+
return `${name}=${final2}`;
|
|
384
|
+
}
|
|
385
|
+
case "label": {
|
|
386
|
+
return `.${final2}`;
|
|
387
|
+
}
|
|
388
|
+
case "matrix": {
|
|
389
|
+
return `;${name}=${final2}`;
|
|
390
|
+
}
|
|
391
|
+
default: {
|
|
392
|
+
return final2;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
for (const k in value) {
|
|
397
|
+
const finalName = options.style === "deepObject" ? `${name}[${encodeName(k)}]` : encodeName(k);
|
|
398
|
+
values.push(serializePrimitiveParam(finalName, value[k], options));
|
|
399
|
+
}
|
|
400
|
+
const final = values.join(joiner);
|
|
401
|
+
return options.style === "label" || options.style === "matrix" ? `${joiner}${final}` : final;
|
|
402
|
+
}
|
|
403
|
+
function serializeArrayParam(name, value, options) {
|
|
404
|
+
if (!Array.isArray(value)) {
|
|
405
|
+
return "";
|
|
406
|
+
}
|
|
407
|
+
if (options.explode === false) {
|
|
408
|
+
const joiner2 = { form: ",", spaceDelimited: "%20", pipeDelimited: "|" }[options.style] || ",";
|
|
409
|
+
const final = (options.allowReserved === true ? value : value.map((v) => encodeURIComponent(v))).join(joiner2);
|
|
410
|
+
switch (options.style) {
|
|
411
|
+
case "simple": {
|
|
412
|
+
return final;
|
|
413
|
+
}
|
|
414
|
+
case "label": {
|
|
415
|
+
return `.${final}`;
|
|
416
|
+
}
|
|
417
|
+
case "matrix": {
|
|
418
|
+
return `;${name}=${final}`;
|
|
419
|
+
}
|
|
420
|
+
// case "spaceDelimited":
|
|
421
|
+
// case "pipeDelimited":
|
|
422
|
+
default: {
|
|
423
|
+
return `${name}=${final}`;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
const joiner = { simple: ",", label: ".", matrix: ";" }[options.style] || "&";
|
|
428
|
+
const values = [];
|
|
429
|
+
for (const v of value) {
|
|
430
|
+
if (options.style === "simple" || options.style === "label") {
|
|
431
|
+
values.push(options.allowReserved === true ? v : encodeURIComponent(v));
|
|
432
|
+
} else {
|
|
433
|
+
values.push(serializePrimitiveParam(name, v, options));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return options.style === "label" || options.style === "matrix" ? `${joiner}${values.join(joiner)}` : values.join(joiner);
|
|
437
|
+
}
|
|
438
|
+
function createQuerySerializer(options) {
|
|
439
|
+
return function querySerializer(queryParams) {
|
|
440
|
+
const search = [];
|
|
441
|
+
if (queryParams && typeof queryParams === "object") {
|
|
442
|
+
for (const key in queryParams) {
|
|
443
|
+
const value = queryParams[key];
|
|
444
|
+
const name = encodeName(key);
|
|
445
|
+
if (value === void 0 || value === null) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (Array.isArray(value)) {
|
|
449
|
+
if (value.length === 0) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
search.push(
|
|
453
|
+
serializeArrayParam(name, value, {
|
|
454
|
+
style: "form",
|
|
455
|
+
explode: true,
|
|
456
|
+
...options?.array,
|
|
457
|
+
allowReserved: options?.allowReserved || false
|
|
458
|
+
})
|
|
459
|
+
);
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (typeof value === "object") {
|
|
463
|
+
search.push(
|
|
464
|
+
serializeObjectParam(name, value, {
|
|
465
|
+
style: "deepObject",
|
|
466
|
+
explode: true,
|
|
467
|
+
...options?.object,
|
|
468
|
+
allowReserved: options?.allowReserved || false
|
|
469
|
+
})
|
|
470
|
+
);
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
search.push(serializePrimitiveParam(name, value, options));
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return search.join("&");
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function defaultPathSerializer(pathname, pathParams) {
|
|
480
|
+
let nextURL = pathname;
|
|
481
|
+
for (const match of pathname.match(PATH_PARAM_RE) ?? []) {
|
|
482
|
+
let name = match.substring(1, match.length - 1);
|
|
483
|
+
let explode = false;
|
|
484
|
+
let style = "simple";
|
|
485
|
+
if (name.endsWith("*")) {
|
|
486
|
+
explode = true;
|
|
487
|
+
name = name.substring(0, name.length - 1);
|
|
488
|
+
}
|
|
489
|
+
if (name.startsWith(".")) {
|
|
490
|
+
style = "label";
|
|
491
|
+
name = name.substring(1);
|
|
492
|
+
} else if (name.startsWith(";")) {
|
|
493
|
+
style = "matrix";
|
|
494
|
+
name = name.substring(1);
|
|
495
|
+
}
|
|
496
|
+
if (!pathParams || pathParams[name] === void 0 || pathParams[name] === null) {
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
const value = pathParams[name];
|
|
500
|
+
if (Array.isArray(value)) {
|
|
501
|
+
nextURL = nextURL.replace(match, serializeArrayParam(name, value, { style, explode }));
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
if (typeof value === "object") {
|
|
505
|
+
nextURL = nextURL.replace(match, serializeObjectParam(name, value, { style, explode }));
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
if (style === "matrix") {
|
|
509
|
+
nextURL = nextURL.replace(match, `;${serializePrimitiveParam(name, value)}`);
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
nextURL = nextURL.replace(match, style === "label" ? `.${encodeURIComponent(value)}` : encodeURIComponent(value));
|
|
513
|
+
}
|
|
514
|
+
if (DOT_SEGMENT_RE.test(nextURL)) {
|
|
515
|
+
throw new Error(`Dot path segment in ${nextURL}`);
|
|
516
|
+
}
|
|
517
|
+
return nextURL;
|
|
518
|
+
}
|
|
519
|
+
function isRawBody(body) {
|
|
520
|
+
return body instanceof FormData || body instanceof Blob || body instanceof URLSearchParams || body instanceof ArrayBuffer || ArrayBuffer.isView(body);
|
|
521
|
+
}
|
|
522
|
+
function formBody(body) {
|
|
523
|
+
for (const value of Object.values(body)) {
|
|
524
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
525
|
+
throw new Error(NESTED_FORM_VALUE_MESSAGE);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return new URLSearchParams(body).toString();
|
|
529
|
+
}
|
|
530
|
+
function multipartBody(body) {
|
|
531
|
+
const formData = new FormData();
|
|
532
|
+
for (const [name, value] of Object.entries(body)) {
|
|
533
|
+
for (const item of [value].flat()) {
|
|
534
|
+
if (item instanceof Blob) {
|
|
535
|
+
formData.append(name, item);
|
|
536
|
+
} else if (item && typeof item === "object") {
|
|
537
|
+
throw new Error(NESTED_FORM_VALUE_MESSAGE);
|
|
538
|
+
} else if (item != null) {
|
|
539
|
+
formData.append(name, String(item));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return formData;
|
|
544
|
+
}
|
|
545
|
+
function defaultBodySerializer(body, headers) {
|
|
546
|
+
if (isRawBody(body)) {
|
|
547
|
+
return body;
|
|
548
|
+
}
|
|
549
|
+
const contentType = headers ? (
|
|
550
|
+
// `typeof`, not `instanceof Function`: headers from another realm (iframe, `node:vm`) have a
|
|
551
|
+
// `get` that is not an instance of this realm's Function (upstream #2637).
|
|
552
|
+
typeof headers.get === "function" ? headers.get("Content-Type") ?? headers.get("content-type") : headers["Content-Type"] ?? headers["content-type"]
|
|
553
|
+
) : void 0;
|
|
554
|
+
if (contentType === FORM_MEDIA_TYPE) {
|
|
555
|
+
return formBody(body);
|
|
556
|
+
}
|
|
557
|
+
if (body && typeof body === "object" && MULTIPART_MEDIA_TYPE_RE.test(contentType)) {
|
|
558
|
+
return multipartBody(body);
|
|
559
|
+
}
|
|
560
|
+
if (typeof body === "string" && contentType && !JSON_MEDIA_TYPE_RE.test(contentType)) {
|
|
561
|
+
return body;
|
|
562
|
+
}
|
|
563
|
+
return JSON.stringify(body);
|
|
564
|
+
}
|
|
565
|
+
function createFinalURL(pathname, options) {
|
|
566
|
+
let finalURL = `${options.baseUrl}${pathname}`;
|
|
567
|
+
if (options.params?.path) {
|
|
568
|
+
finalURL = options.pathSerializer(finalURL, options.params.path);
|
|
569
|
+
}
|
|
570
|
+
let search = options.querySerializer(options.params.query ?? {});
|
|
571
|
+
if (search.startsWith("?")) {
|
|
572
|
+
search = search.substring(1);
|
|
573
|
+
}
|
|
574
|
+
if (search) {
|
|
575
|
+
finalURL += `${finalURL.includes("?") ? "&" : "?"}${search}`;
|
|
576
|
+
}
|
|
577
|
+
return finalURL;
|
|
578
|
+
}
|
|
579
|
+
function mergeHeaders(...allHeaders) {
|
|
580
|
+
const finalHeaders = new Headers();
|
|
581
|
+
for (const h of allHeaders) {
|
|
582
|
+
if (!h || typeof h !== "object") {
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
const iterator = isHeaders(h) ? h.entries() : Object.entries(h);
|
|
586
|
+
for (const [k, v] of iterator) {
|
|
587
|
+
if (v === null) {
|
|
588
|
+
finalHeaders.delete(k);
|
|
589
|
+
} else if (Array.isArray(v)) {
|
|
590
|
+
for (const v2 of v) {
|
|
591
|
+
finalHeaders.append(k, v2);
|
|
592
|
+
}
|
|
593
|
+
} else if (v !== void 0) {
|
|
594
|
+
finalHeaders.set(k, v);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return finalHeaders;
|
|
599
|
+
}
|
|
600
|
+
function removeTrailingSlash(url) {
|
|
601
|
+
if (url.endsWith("/")) {
|
|
602
|
+
return url.substring(0, url.length - 1);
|
|
603
|
+
}
|
|
604
|
+
return url;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
exports.createFinalURL = createFinalURL;
|
|
608
|
+
exports.createPathBasedClient = createPathBasedClient;
|
|
609
|
+
exports.createQuerySerializer = createQuerySerializer;
|
|
610
|
+
exports.default = createClient;
|
|
611
|
+
exports.defaultBodySerializer = defaultBodySerializer;
|
|
612
|
+
exports.defaultPathSerializer = defaultPathSerializer;
|
|
613
|
+
exports.mergeHeaders = mergeHeaders;
|
|
614
|
+
exports.randomID = randomID;
|
|
615
|
+
exports.removeTrailingSlash = removeTrailingSlash;
|
|
616
|
+
exports.serializeArrayParam = serializeArrayParam;
|
|
617
|
+
exports.serializeObjectParam = serializeObjectParam;
|
|
618
|
+
exports.serializePrimitiveParam = serializePrimitiveParam;
|
|
619
|
+
exports.wrapAsPathBasedClient = wrapAsPathBasedClient;
|
|
620
|
+
//# sourceMappingURL=index.cjs.map
|