@shipworthy/openapi-fetch 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 +91 -31
- package/dist/index.cjs +250 -49
- package/dist/index.d.cts +64 -29
- package/dist/index.d.ts +64 -29
- package/dist/index.js +250 -49
- package/dist/transform-casing.cjs +161 -0
- package/dist/transform-casing.d.cts +138 -0
- package/dist/transform-casing.d.ts +138 -0
- package/dist/transform-casing.js +160 -0
- package/package.json +14 -4
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/headers.ts
|
|
2
2
|
/** Iterate any `HeadersOptions` form (plain object, `Headers`, or `[name, value]` tuples) as entries */
|
|
3
3
|
function headerEntries(h) {
|
|
4
4
|
if (h instanceof Headers) return h.entries();
|
|
@@ -16,8 +16,166 @@ function mergeHeaders(...allHeaders) {
|
|
|
16
16
|
}
|
|
17
17
|
return finalHeaders;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// src/middleware/transform-casing.ts
|
|
20
|
+
function expandSchemas(schemas, mapping, visited = /* @__PURE__ */ new Set()) {
|
|
21
|
+
return schemas.flatMap((schema) => {
|
|
22
|
+
const nested = schema.variants ?? [];
|
|
23
|
+
if (!schema.ref || visited.has(schema.ref)) return [schema, ...expandSchemas(nested, mapping, visited)];
|
|
24
|
+
const referenced = mapping.schemas?.[schema.ref];
|
|
25
|
+
const next = new Set(visited).add(schema.ref);
|
|
26
|
+
return [schema, ...expandSchemas(referenced ? [referenced, ...nested] : nested, mapping, next)];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function createConverter(mapping) {
|
|
30
|
+
const schemaIds = /* @__PURE__ */ new WeakMap();
|
|
31
|
+
const plans = /* @__PURE__ */ new Map();
|
|
32
|
+
let nextId = 0;
|
|
33
|
+
function planFor(schemas, direction) {
|
|
34
|
+
if (schemas.length === 0) return void 0;
|
|
35
|
+
const key = `${direction}:${schemas.map((schema) => {
|
|
36
|
+
let id = schemaIds.get(schema);
|
|
37
|
+
if (id === void 0) {
|
|
38
|
+
id = ++nextId;
|
|
39
|
+
schemaIds.set(schema, id);
|
|
40
|
+
}
|
|
41
|
+
return id;
|
|
42
|
+
}).join(",")}`;
|
|
43
|
+
const existing = plans.get(key);
|
|
44
|
+
if (existing) return existing;
|
|
45
|
+
const expanded = expandSchemas(schemas, mapping);
|
|
46
|
+
const fields = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const schema of expanded) for (const [wireName, field] of Object.entries(schema.properties ?? {})) {
|
|
48
|
+
const sourceName = direction === "toApi" ? field.name : wireName;
|
|
49
|
+
const targetName = direction === "toApi" ? wireName : field.name;
|
|
50
|
+
const current = fields.get(sourceName);
|
|
51
|
+
if (current) {
|
|
52
|
+
if (current.name !== targetName) throw new Error(`Conflicting case mapping for ${sourceName}`);
|
|
53
|
+
if (field.schema) current.schemas.push(field.schema);
|
|
54
|
+
} else fields.set(sourceName, {
|
|
55
|
+
name: targetName,
|
|
56
|
+
schemas: field.schema ? [field.schema] : []
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const plan = {
|
|
60
|
+
expanded,
|
|
61
|
+
fields,
|
|
62
|
+
declared: new Set(expanded.flatMap((schema) => schema.declared ?? [])),
|
|
63
|
+
patterns: expanded.flatMap((schema) => Object.entries(schema.patternProperties ?? {}).map(([pattern, nested]) => ({
|
|
64
|
+
regex: new RegExp(pattern),
|
|
65
|
+
schema: nested
|
|
66
|
+
}))),
|
|
67
|
+
additional: expanded.flatMap((schema) => schema.additionalProperties ? [schema.additionalProperties] : []),
|
|
68
|
+
prefixLength: expanded.reduce((max, schema) => Math.max(max, schema.prefixItems?.length ?? 0), 0)
|
|
69
|
+
};
|
|
70
|
+
plans.set(key, plan);
|
|
71
|
+
return plan;
|
|
72
|
+
}
|
|
73
|
+
function itemPlan(plan, index, direction) {
|
|
74
|
+
if (index >= plan.prefixLength) {
|
|
75
|
+
if (plan.items === void 0) plan.items = planFor(plan.expanded.flatMap((schema) => schema.items ? [schema.items] : []), direction) ?? null;
|
|
76
|
+
return plan.items ?? void 0;
|
|
77
|
+
}
|
|
78
|
+
const prefixItems = plan.prefixItems ??= [];
|
|
79
|
+
if (prefixItems[index] === void 0) prefixItems[index] = planFor(plan.expanded.flatMap((schema) => {
|
|
80
|
+
const prefix = schema.prefixItems;
|
|
81
|
+
const item = prefix && index < prefix.length ? prefix[index] : schema.items;
|
|
82
|
+
return item ? [item] : [];
|
|
83
|
+
}), direction) ?? null;
|
|
84
|
+
return prefixItems[index] ?? void 0;
|
|
85
|
+
}
|
|
86
|
+
function convertWithPlan(value, plan, direction) {
|
|
87
|
+
if (!plan || value === null || value === void 0) return value;
|
|
88
|
+
if (Array.isArray(value)) return value.map((item, index) => convertWithPlan(item, itemPlan(plan, index, direction), direction));
|
|
89
|
+
if (typeof value !== "object") return value;
|
|
90
|
+
const prototype = Object.getPrototypeOf(value);
|
|
91
|
+
if (prototype !== Object.prototype && prototype !== null) return value;
|
|
92
|
+
const convertedNames = /* @__PURE__ */ new Set();
|
|
93
|
+
const entries = Object.entries(value).map(([name, item]) => {
|
|
94
|
+
const field = plan.fields.get(name);
|
|
95
|
+
const matched = [];
|
|
96
|
+
let hasPattern = false;
|
|
97
|
+
for (const pattern of plan.patterns) if (pattern.regex.test(name)) {
|
|
98
|
+
hasPattern = true;
|
|
99
|
+
if (pattern.schema) matched.push(pattern.schema);
|
|
100
|
+
}
|
|
101
|
+
let nested;
|
|
102
|
+
if (matched.length > 0) nested = planFor([...field?.schemas ?? [], ...matched], direction);
|
|
103
|
+
else if (field) {
|
|
104
|
+
if (field.nested === void 0) field.nested = planFor(field.schemas, direction) ?? null;
|
|
105
|
+
nested = field.nested ?? void 0;
|
|
106
|
+
} else if (!plan.declared.has(name) && !hasPattern) {
|
|
107
|
+
if (plan.additionalPlan === void 0) plan.additionalPlan = planFor(plan.additional, direction) ?? null;
|
|
108
|
+
nested = plan.additionalPlan ?? void 0;
|
|
109
|
+
}
|
|
110
|
+
const targetName = field?.name ?? name;
|
|
111
|
+
if (convertedNames.has(targetName)) throw new Error(`Conflicting case mapping for ${targetName}`);
|
|
112
|
+
convertedNames.add(targetName);
|
|
113
|
+
return [targetName, convertWithPlan(item, nested, direction)];
|
|
114
|
+
});
|
|
115
|
+
return Object.fromEntries(entries);
|
|
116
|
+
}
|
|
117
|
+
return (value, schemas, direction) => {
|
|
118
|
+
if (schemas.length === 0 || value === null || value === void 0) return value;
|
|
119
|
+
return convertWithPlan(value, planFor(schemas, direction), direction);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function isJsonMediaType(mediaType) {
|
|
123
|
+
return mediaType === "application/json" || mediaType.endsWith("+json");
|
|
124
|
+
}
|
|
125
|
+
function mediaSchema(media, contentType) {
|
|
126
|
+
if (!media) return void 0;
|
|
127
|
+
const type = contentType?.split(";", 1)[0]?.trim().toLowerCase();
|
|
128
|
+
if (type && !isJsonMediaType(type)) return void 0;
|
|
129
|
+
const entries = Object.entries(media).filter(([name]) => isJsonMediaType(name.toLowerCase()));
|
|
130
|
+
const exact = entries.find(([name]) => name.toLowerCase() === type);
|
|
131
|
+
if (exact) return exact[1] ?? void 0;
|
|
132
|
+
const json = entries.find(([name]) => name.toLowerCase() === "application/json");
|
|
133
|
+
if (json) return json[1] ?? void 0;
|
|
134
|
+
return entries.length === 1 ? entries[0]?.[1] ?? void 0 : void 0;
|
|
135
|
+
}
|
|
136
|
+
function responseMedia(operation, response) {
|
|
137
|
+
const responses = operation.responses;
|
|
138
|
+
if (!responses) return void 0;
|
|
139
|
+
const status = String(response.status);
|
|
140
|
+
const range = `${status[0]}XX`;
|
|
141
|
+
return mediaSchema(Object.hasOwn(responses, status) ? responses[status] : Object.hasOwn(responses, range) ? responses[range] : responses.default, response.headers.get("content-type") ?? void 0);
|
|
142
|
+
}
|
|
143
|
+
function convertRequest(input, operation, convertValue, headers) {
|
|
144
|
+
const parameters = operation.parameters;
|
|
145
|
+
const params = input.params;
|
|
146
|
+
const bodySchema = mediaSchema(operation.requestBodies, headers.get("content-type") ?? void 0);
|
|
147
|
+
return {
|
|
148
|
+
params: parameters ? {
|
|
149
|
+
...params,
|
|
150
|
+
path: convertValue(params.path, parameters.path ? [parameters.path] : [], "toApi"),
|
|
151
|
+
query: convertValue(params.query, parameters.query ? [parameters.query] : [], "toApi"),
|
|
152
|
+
header: convertValue(params.header, parameters.header ? [parameters.header] : [], "toApi"),
|
|
153
|
+
cookie: convertValue(params.cookie, parameters.cookie ? [parameters.cookie] : [], "toApi")
|
|
154
|
+
} : params,
|
|
155
|
+
body: bodySchema && input.body !== void 0 ? convertValue(input.body, [bodySchema], "toApi") : input.body
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/** Convert generated camelCase values at the openapi-fetch request and result stages. */
|
|
159
|
+
function createCaseMappingMiddleware(mapping) {
|
|
160
|
+
const convertValue = createConverter(mapping);
|
|
161
|
+
return {
|
|
162
|
+
onRequestInit({ method, schemaPath, input, headers }) {
|
|
163
|
+
const operation = mapping.operations?.[`${method} ${schemaPath}`];
|
|
164
|
+
return operation ? convertRequest(input, operation, convertValue, headers) : void 0;
|
|
165
|
+
},
|
|
166
|
+
onResult({ method, schemaPath, result, options }) {
|
|
167
|
+
const operation = mapping.operations?.[`${method} ${schemaPath}`];
|
|
168
|
+
if (!operation) return;
|
|
169
|
+
const schema = responseMedia(operation, result.response);
|
|
170
|
+
if (!schema) return;
|
|
171
|
+
return {
|
|
172
|
+
...options.parseAs === "json" && result.data !== void 0 ? { data: convertValue(result.data, [schema], "fromApi") } : {},
|
|
173
|
+
...result.error !== void 0 ? { error: convertValue(result.error, [schema], "fromApi") } : {}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// src/serializers/body.ts
|
|
21
179
|
/** Serialize body object to string */
|
|
22
180
|
function defaultBodySerializer(body, headers) {
|
|
23
181
|
if (body instanceof FormData) return body;
|
|
@@ -26,8 +184,7 @@ function defaultBodySerializer(body, headers) {
|
|
|
26
184
|
}
|
|
27
185
|
return JSON.stringify(body);
|
|
28
186
|
}
|
|
29
|
-
|
|
30
|
-
//#region src/serializers/param.ts
|
|
187
|
+
// src/serializers/param.ts
|
|
31
188
|
/** Serialize primitive params to string */
|
|
32
189
|
function serializePrimitiveParam(name, value, options) {
|
|
33
190
|
if (value === void 0 || value === null) return "";
|
|
@@ -87,8 +244,7 @@ function serializeArrayParam(name, value, options) {
|
|
|
87
244
|
else values.push(serializePrimitiveParam(name, v, options));
|
|
88
245
|
return options.style === "label" || options.style === "matrix" ? `${joiner}${values.join(joiner)}` : values.join(joiner);
|
|
89
246
|
}
|
|
90
|
-
|
|
91
|
-
//#region src/serializers/cookie.ts
|
|
247
|
+
// src/serializers/cookie.ts
|
|
92
248
|
/**
|
|
93
249
|
* Serialize cookie params to a `Cookie` header value using the OpenAPI default
|
|
94
250
|
* (style: form, explode: true). Arrays repeat the name, objects emit one pair per key,
|
|
@@ -110,8 +266,7 @@ function serializeCookieParams(cookieParams) {
|
|
|
110
266
|
}
|
|
111
267
|
return pairs.join("; ");
|
|
112
268
|
}
|
|
113
|
-
|
|
114
|
-
//#region src/serializers/path.ts
|
|
269
|
+
// src/serializers/path.ts
|
|
115
270
|
const PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
116
271
|
/**
|
|
117
272
|
* Handle different OpenAPI 3.x serialization styles
|
|
@@ -158,8 +313,7 @@ function defaultPathSerializer(pathname, pathParams) {
|
|
|
158
313
|
}
|
|
159
314
|
return nextURL;
|
|
160
315
|
}
|
|
161
|
-
|
|
162
|
-
//#region src/serializers/query.ts
|
|
316
|
+
// src/serializers/query.ts
|
|
163
317
|
/** Serialize query params to string */
|
|
164
318
|
function createQuerySerializer(options) {
|
|
165
319
|
return function querySerializer(queryParams) {
|
|
@@ -191,8 +345,7 @@ function createQuerySerializer(options) {
|
|
|
191
345
|
return search.join("&");
|
|
192
346
|
};
|
|
193
347
|
}
|
|
194
|
-
|
|
195
|
-
//#region src/serializers/url.ts
|
|
348
|
+
// src/serializers/url.ts
|
|
196
349
|
/** Construct URL string from baseUrl and handle path and query params */
|
|
197
350
|
function createFinalURL(pathname, options) {
|
|
198
351
|
let finalURL = `${options.baseUrl}${pathname}`;
|
|
@@ -207,8 +360,7 @@ function removeTrailingSlash(url) {
|
|
|
207
360
|
if (url.endsWith("/")) return url.substring(0, url.length - 1);
|
|
208
361
|
return url;
|
|
209
362
|
}
|
|
210
|
-
|
|
211
|
-
//#region src/client.ts
|
|
363
|
+
// src/client.ts
|
|
212
364
|
function supportsRequestInitExt() {
|
|
213
365
|
const nodeProcess = globalThis.process;
|
|
214
366
|
return typeof nodeProcess === "object" && Number.parseInt(nodeProcess?.versions?.node?.substring(0, 2) ?? "") >= 18 && nodeProcess?.versions?.undici;
|
|
@@ -221,15 +373,16 @@ function randomID() {
|
|
|
221
373
|
* Create an openapi-fetch client.
|
|
222
374
|
*/
|
|
223
375
|
function createClient(clientOptions) {
|
|
224
|
-
let { baseUrl = "", Request: CustomRequest = globalThis.Request, fetch: baseFetch = globalThis.fetch, querySerializer: globalQuerySerializer, bodySerializer: globalBodySerializer, pathSerializer: globalPathSerializer, headers: baseHeaders, requestInitExt, ...baseOptions } = { ...clientOptions };
|
|
376
|
+
let { baseUrl = "", Request: CustomRequest = globalThis.Request, fetch: baseFetch = globalThis.fetch, querySerializer: globalQuerySerializer, bodySerializer: globalBodySerializer, pathSerializer: globalPathSerializer, caseMapping, headers: baseHeaders, requestInitExt, ...baseOptions } = { ...clientOptions };
|
|
225
377
|
requestInitExt = supportsRequestInitExt() ? requestInitExt : void 0;
|
|
226
378
|
baseUrl = removeTrailingSlash(baseUrl);
|
|
227
379
|
const globalMiddlewares = [];
|
|
380
|
+
if (caseMapping !== void 0) globalMiddlewares.push(createCaseMappingMiddleware(caseMapping));
|
|
228
381
|
/**
|
|
229
382
|
* Per-request fetch (keeps settings created in createClient()
|
|
230
383
|
*/
|
|
231
384
|
async function coreFetch(schemaPath, fetchOptions) {
|
|
232
|
-
const { baseUrl: localBaseUrl, fetch = baseFetch, Request = CustomRequest, headers, params = {}, parseAs = "json", querySerializer: requestQuerySerializer, bodySerializer = globalBodySerializer ?? defaultBodySerializer, pathSerializer: requestPathSerializer, body, middleware: requestMiddlewares = [], ...init } = fetchOptions || {};
|
|
385
|
+
const { baseUrl: localBaseUrl, fetch = baseFetch, Request = CustomRequest, headers, params: initialParams = {}, parseAs = "json", querySerializer: requestQuerySerializer, bodySerializer = globalBodySerializer ?? defaultBodySerializer, pathSerializer: requestPathSerializer, body: initialBody, middleware: requestMiddlewares = [], ...init } = fetchOptions || {};
|
|
233
386
|
let finalBaseUrl = baseUrl;
|
|
234
387
|
if (localBaseUrl) finalBaseUrl = removeTrailingSlash(localBaseUrl) ?? baseUrl;
|
|
235
388
|
let querySerializer = typeof globalQuerySerializer === "function" ? globalQuerySerializer : createQuerySerializer(globalQuerySerializer);
|
|
@@ -238,6 +391,41 @@ function createClient(clientOptions) {
|
|
|
238
391
|
...requestQuerySerializer
|
|
239
392
|
});
|
|
240
393
|
const pathSerializer = requestPathSerializer || globalPathSerializer || defaultPathSerializer;
|
|
394
|
+
const finalMiddlewares = [...globalMiddlewares, ...requestMiddlewares];
|
|
395
|
+
const method = String(init.method ?? "GET").toUpperCase();
|
|
396
|
+
let id;
|
|
397
|
+
let options;
|
|
398
|
+
let params = initialParams;
|
|
399
|
+
let body = initialBody;
|
|
400
|
+
if (finalMiddlewares.length) {
|
|
401
|
+
id = randomID();
|
|
402
|
+
options = Object.freeze({
|
|
403
|
+
baseUrl: finalBaseUrl,
|
|
404
|
+
fetch,
|
|
405
|
+
parseAs,
|
|
406
|
+
querySerializer,
|
|
407
|
+
bodySerializer,
|
|
408
|
+
pathSerializer
|
|
409
|
+
});
|
|
410
|
+
for (const m of finalMiddlewares) if (m && typeof m === "object" && typeof m.onRequestInit === "function") {
|
|
411
|
+
const result = await m.onRequestInit({
|
|
412
|
+
schemaPath,
|
|
413
|
+
method,
|
|
414
|
+
input: {
|
|
415
|
+
params,
|
|
416
|
+
body
|
|
417
|
+
},
|
|
418
|
+
headers: mergeHeaders(baseHeaders, headers, params.header),
|
|
419
|
+
options,
|
|
420
|
+
id
|
|
421
|
+
});
|
|
422
|
+
if (result !== void 0) {
|
|
423
|
+
if (!result || typeof result !== "object" || !("params" in result) || !("body" in result)) throw new Error("onRequestInit: must return request input when modifying it");
|
|
424
|
+
params = result.params;
|
|
425
|
+
body = result.body;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
241
429
|
const serializedBody = body === void 0 ? void 0 : bodySerializer(body, mergeHeaders(baseHeaders, headers, params.header));
|
|
242
430
|
const finalHeaders = mergeHeaders(serializedBody === void 0 || serializedBody instanceof FormData ? {} : { "Content-Type": "application/json" }, baseHeaders, headers, params.header);
|
|
243
431
|
const serializedCookies = serializeCookieParams(params.cookie);
|
|
@@ -245,7 +433,6 @@ function createClient(clientOptions) {
|
|
|
245
433
|
const existingCookie = finalHeaders.get("Cookie");
|
|
246
434
|
finalHeaders.set("Cookie", existingCookie ? `${existingCookie}; ${serializedCookies}` : serializedCookies);
|
|
247
435
|
}
|
|
248
|
-
const finalMiddlewares = [...globalMiddlewares, ...requestMiddlewares];
|
|
249
436
|
const requestInit = {
|
|
250
437
|
redirect: "follow",
|
|
251
438
|
...baseOptions,
|
|
@@ -253,8 +440,6 @@ function createClient(clientOptions) {
|
|
|
253
440
|
body: serializedBody,
|
|
254
441
|
headers: finalHeaders
|
|
255
442
|
};
|
|
256
|
-
let id;
|
|
257
|
-
let options;
|
|
258
443
|
let request = new Request(createFinalURL(schemaPath, {
|
|
259
444
|
baseUrl: finalBaseUrl,
|
|
260
445
|
params,
|
|
@@ -265,15 +450,6 @@ function createClient(clientOptions) {
|
|
|
265
450
|
/** Add custom parameters to Request object */
|
|
266
451
|
for (const key in init) if (!(key in request)) request[key] = init[key];
|
|
267
452
|
if (finalMiddlewares.length) {
|
|
268
|
-
id = randomID();
|
|
269
|
-
options = Object.freeze({
|
|
270
|
-
baseUrl: finalBaseUrl,
|
|
271
|
-
fetch,
|
|
272
|
-
parseAs,
|
|
273
|
-
querySerializer,
|
|
274
|
-
bodySerializer,
|
|
275
|
-
pathSerializer
|
|
276
|
-
});
|
|
277
453
|
for (const m of finalMiddlewares) if (m && typeof m === "object" && typeof m.onRequest === "function") {
|
|
278
454
|
const result = await m.onRequest({
|
|
279
455
|
request,
|
|
@@ -342,14 +518,15 @@ function createClient(clientOptions) {
|
|
|
342
518
|
}
|
|
343
519
|
const finalResponse = response;
|
|
344
520
|
const contentLength = finalResponse.headers.get("Content-Length");
|
|
345
|
-
|
|
521
|
+
let parsedResult;
|
|
522
|
+
if (finalResponse.status === 204 || request.method === "HEAD" || contentLength === "0" && !finalResponse.headers.get("Transfer-Encoding")?.includes("chunked")) parsedResult = finalResponse.ok ? {
|
|
346
523
|
data: void 0,
|
|
347
524
|
response: finalResponse
|
|
348
525
|
} : {
|
|
349
526
|
error: void 0,
|
|
350
527
|
response: finalResponse
|
|
351
528
|
};
|
|
352
|
-
if (finalResponse.ok) {
|
|
529
|
+
else if (finalResponse.ok) {
|
|
353
530
|
const getResponseData = async () => {
|
|
354
531
|
if (parseAs === "stream") return finalResponse.body;
|
|
355
532
|
if (parseAs === "json" && !contentLength) {
|
|
@@ -358,24 +535,50 @@ function createClient(clientOptions) {
|
|
|
358
535
|
}
|
|
359
536
|
return await finalResponse[parseAs]();
|
|
360
537
|
};
|
|
361
|
-
|
|
538
|
+
parsedResult = {
|
|
362
539
|
data: await getResponseData(),
|
|
363
540
|
response: finalResponse
|
|
364
541
|
};
|
|
542
|
+
} else {
|
|
543
|
+
const raw = await finalResponse.text();
|
|
544
|
+
if (!raw) parsedResult = {
|
|
545
|
+
error: void 0,
|
|
546
|
+
response: finalResponse
|
|
547
|
+
};
|
|
548
|
+
else {
|
|
549
|
+
let error = raw;
|
|
550
|
+
try {
|
|
551
|
+
error = JSON.parse(raw);
|
|
552
|
+
} catch {}
|
|
553
|
+
parsedResult = {
|
|
554
|
+
error,
|
|
555
|
+
response: finalResponse
|
|
556
|
+
};
|
|
557
|
+
}
|
|
365
558
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
559
|
+
for (let index = finalMiddlewares.length - 1; index >= 0; index--) {
|
|
560
|
+
const m = finalMiddlewares[index];
|
|
561
|
+
if (m && typeof m === "object" && typeof m.onResult === "function") {
|
|
562
|
+
const update = await m.onResult({
|
|
563
|
+
request,
|
|
564
|
+
schemaPath,
|
|
565
|
+
method,
|
|
566
|
+
params,
|
|
567
|
+
options,
|
|
568
|
+
id,
|
|
569
|
+
result: parsedResult
|
|
570
|
+
});
|
|
571
|
+
if (update !== void 0) {
|
|
572
|
+
if (!update || typeof update !== "object" || Object.keys(update).some((key) => key !== "data" && key !== "error")) throw new Error("onResult: must return data and/or error when modifying the result");
|
|
573
|
+
parsedResult = {
|
|
574
|
+
...parsedResult,
|
|
575
|
+
...update,
|
|
576
|
+
response: finalResponse
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return parsedResult;
|
|
379
582
|
}
|
|
380
583
|
return {
|
|
381
584
|
request(method, url, init) {
|
|
@@ -444,7 +647,7 @@ function createClient(clientOptions) {
|
|
|
444
647
|
use(...middleware) {
|
|
445
648
|
for (const m of middleware) {
|
|
446
649
|
if (!m) continue;
|
|
447
|
-
if (typeof m !== "object" || !("onRequest" in m || "onResponse" in m || "onError" in m)) throw new Error("Middleware must be an object with
|
|
650
|
+
if (typeof m !== "object" || !("onRequestInit" in m || "onRequest" in m || "onResponse" in m || "onError" in m || "onResult" in m)) throw new Error("Middleware must be an object with a supported hook");
|
|
448
651
|
globalMiddlewares.push(m);
|
|
449
652
|
}
|
|
450
653
|
},
|
|
@@ -457,8 +660,7 @@ function createClient(clientOptions) {
|
|
|
457
660
|
}
|
|
458
661
|
};
|
|
459
662
|
}
|
|
460
|
-
|
|
461
|
-
//#region src/path-client.ts
|
|
663
|
+
// src/path-client.ts
|
|
462
664
|
var PathCallForwarder = class {
|
|
463
665
|
client;
|
|
464
666
|
url;
|
|
@@ -518,5 +720,4 @@ function wrapAsPathBasedClient(coreClient) {
|
|
|
518
720
|
function createPathBasedClient(clientOptions) {
|
|
519
721
|
return wrapAsPathBasedClient(createClient(clientOptions));
|
|
520
722
|
}
|
|
521
|
-
//#endregion
|
|
522
723
|
export { createClient, createClient as default, createFinalURL, createPathBasedClient, createQuerySerializer, defaultBodySerializer, defaultPathSerializer, mergeHeaders, removeTrailingSlash, serializeArrayParam, serializeObjectParam, serializePrimitiveParam, wrapAsPathBasedClient };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
// src/middleware/transform-casing.ts
|
|
3
|
+
function expandSchemas(schemas, mapping, visited = /* @__PURE__ */ new Set()) {
|
|
4
|
+
return schemas.flatMap((schema) => {
|
|
5
|
+
const nested = schema.variants ?? [];
|
|
6
|
+
if (!schema.ref || visited.has(schema.ref)) return [schema, ...expandSchemas(nested, mapping, visited)];
|
|
7
|
+
const referenced = mapping.schemas?.[schema.ref];
|
|
8
|
+
const next = new Set(visited).add(schema.ref);
|
|
9
|
+
return [schema, ...expandSchemas(referenced ? [referenced, ...nested] : nested, mapping, next)];
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function createConverter(mapping) {
|
|
13
|
+
const schemaIds = /* @__PURE__ */ new WeakMap();
|
|
14
|
+
const plans = /* @__PURE__ */ new Map();
|
|
15
|
+
let nextId = 0;
|
|
16
|
+
function planFor(schemas, direction) {
|
|
17
|
+
if (schemas.length === 0) return void 0;
|
|
18
|
+
const key = `${direction}:${schemas.map((schema) => {
|
|
19
|
+
let id = schemaIds.get(schema);
|
|
20
|
+
if (id === void 0) {
|
|
21
|
+
id = ++nextId;
|
|
22
|
+
schemaIds.set(schema, id);
|
|
23
|
+
}
|
|
24
|
+
return id;
|
|
25
|
+
}).join(",")}`;
|
|
26
|
+
const existing = plans.get(key);
|
|
27
|
+
if (existing) return existing;
|
|
28
|
+
const expanded = expandSchemas(schemas, mapping);
|
|
29
|
+
const fields = /* @__PURE__ */ new Map();
|
|
30
|
+
for (const schema of expanded) for (const [wireName, field] of Object.entries(schema.properties ?? {})) {
|
|
31
|
+
const sourceName = direction === "toApi" ? field.name : wireName;
|
|
32
|
+
const targetName = direction === "toApi" ? wireName : field.name;
|
|
33
|
+
const current = fields.get(sourceName);
|
|
34
|
+
if (current) {
|
|
35
|
+
if (current.name !== targetName) throw new Error(`Conflicting case mapping for ${sourceName}`);
|
|
36
|
+
if (field.schema) current.schemas.push(field.schema);
|
|
37
|
+
} else fields.set(sourceName, {
|
|
38
|
+
name: targetName,
|
|
39
|
+
schemas: field.schema ? [field.schema] : []
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const plan = {
|
|
43
|
+
expanded,
|
|
44
|
+
fields,
|
|
45
|
+
declared: new Set(expanded.flatMap((schema) => schema.declared ?? [])),
|
|
46
|
+
patterns: expanded.flatMap((schema) => Object.entries(schema.patternProperties ?? {}).map(([pattern, nested]) => ({
|
|
47
|
+
regex: new RegExp(pattern),
|
|
48
|
+
schema: nested
|
|
49
|
+
}))),
|
|
50
|
+
additional: expanded.flatMap((schema) => schema.additionalProperties ? [schema.additionalProperties] : []),
|
|
51
|
+
prefixLength: expanded.reduce((max, schema) => Math.max(max, schema.prefixItems?.length ?? 0), 0)
|
|
52
|
+
};
|
|
53
|
+
plans.set(key, plan);
|
|
54
|
+
return plan;
|
|
55
|
+
}
|
|
56
|
+
function itemPlan(plan, index, direction) {
|
|
57
|
+
if (index >= plan.prefixLength) {
|
|
58
|
+
if (plan.items === void 0) plan.items = planFor(plan.expanded.flatMap((schema) => schema.items ? [schema.items] : []), direction) ?? null;
|
|
59
|
+
return plan.items ?? void 0;
|
|
60
|
+
}
|
|
61
|
+
const prefixItems = plan.prefixItems ??= [];
|
|
62
|
+
if (prefixItems[index] === void 0) prefixItems[index] = planFor(plan.expanded.flatMap((schema) => {
|
|
63
|
+
const prefix = schema.prefixItems;
|
|
64
|
+
const item = prefix && index < prefix.length ? prefix[index] : schema.items;
|
|
65
|
+
return item ? [item] : [];
|
|
66
|
+
}), direction) ?? null;
|
|
67
|
+
return prefixItems[index] ?? void 0;
|
|
68
|
+
}
|
|
69
|
+
function convertWithPlan(value, plan, direction) {
|
|
70
|
+
if (!plan || value === null || value === void 0) return value;
|
|
71
|
+
if (Array.isArray(value)) return value.map((item, index) => convertWithPlan(item, itemPlan(plan, index, direction), direction));
|
|
72
|
+
if (typeof value !== "object") return value;
|
|
73
|
+
const prototype = Object.getPrototypeOf(value);
|
|
74
|
+
if (prototype !== Object.prototype && prototype !== null) return value;
|
|
75
|
+
const convertedNames = /* @__PURE__ */ new Set();
|
|
76
|
+
const entries = Object.entries(value).map(([name, item]) => {
|
|
77
|
+
const field = plan.fields.get(name);
|
|
78
|
+
const matched = [];
|
|
79
|
+
let hasPattern = false;
|
|
80
|
+
for (const pattern of plan.patterns) if (pattern.regex.test(name)) {
|
|
81
|
+
hasPattern = true;
|
|
82
|
+
if (pattern.schema) matched.push(pattern.schema);
|
|
83
|
+
}
|
|
84
|
+
let nested;
|
|
85
|
+
if (matched.length > 0) nested = planFor([...field?.schemas ?? [], ...matched], direction);
|
|
86
|
+
else if (field) {
|
|
87
|
+
if (field.nested === void 0) field.nested = planFor(field.schemas, direction) ?? null;
|
|
88
|
+
nested = field.nested ?? void 0;
|
|
89
|
+
} else if (!plan.declared.has(name) && !hasPattern) {
|
|
90
|
+
if (plan.additionalPlan === void 0) plan.additionalPlan = planFor(plan.additional, direction) ?? null;
|
|
91
|
+
nested = plan.additionalPlan ?? void 0;
|
|
92
|
+
}
|
|
93
|
+
const targetName = field?.name ?? name;
|
|
94
|
+
if (convertedNames.has(targetName)) throw new Error(`Conflicting case mapping for ${targetName}`);
|
|
95
|
+
convertedNames.add(targetName);
|
|
96
|
+
return [targetName, convertWithPlan(item, nested, direction)];
|
|
97
|
+
});
|
|
98
|
+
return Object.fromEntries(entries);
|
|
99
|
+
}
|
|
100
|
+
return (value, schemas, direction) => {
|
|
101
|
+
if (schemas.length === 0 || value === null || value === void 0) return value;
|
|
102
|
+
return convertWithPlan(value, planFor(schemas, direction), direction);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function isJsonMediaType(mediaType) {
|
|
106
|
+
return mediaType === "application/json" || mediaType.endsWith("+json");
|
|
107
|
+
}
|
|
108
|
+
function mediaSchema(media, contentType) {
|
|
109
|
+
if (!media) return void 0;
|
|
110
|
+
const type = contentType?.split(";", 1)[0]?.trim().toLowerCase();
|
|
111
|
+
if (type && !isJsonMediaType(type)) return void 0;
|
|
112
|
+
const entries = Object.entries(media).filter(([name]) => isJsonMediaType(name.toLowerCase()));
|
|
113
|
+
const exact = entries.find(([name]) => name.toLowerCase() === type);
|
|
114
|
+
if (exact) return exact[1] ?? void 0;
|
|
115
|
+
const json = entries.find(([name]) => name.toLowerCase() === "application/json");
|
|
116
|
+
if (json) return json[1] ?? void 0;
|
|
117
|
+
return entries.length === 1 ? entries[0]?.[1] ?? void 0 : void 0;
|
|
118
|
+
}
|
|
119
|
+
function responseMedia(operation, response) {
|
|
120
|
+
const responses = operation.responses;
|
|
121
|
+
if (!responses) return void 0;
|
|
122
|
+
const status = String(response.status);
|
|
123
|
+
const range = `${status[0]}XX`;
|
|
124
|
+
return mediaSchema(Object.hasOwn(responses, status) ? responses[status] : Object.hasOwn(responses, range) ? responses[range] : responses.default, response.headers.get("content-type") ?? void 0);
|
|
125
|
+
}
|
|
126
|
+
function convertRequest(input, operation, convertValue, headers) {
|
|
127
|
+
const parameters = operation.parameters;
|
|
128
|
+
const params = input.params;
|
|
129
|
+
const bodySchema = mediaSchema(operation.requestBodies, headers.get("content-type") ?? void 0);
|
|
130
|
+
return {
|
|
131
|
+
params: parameters ? {
|
|
132
|
+
...params,
|
|
133
|
+
path: convertValue(params.path, parameters.path ? [parameters.path] : [], "toApi"),
|
|
134
|
+
query: convertValue(params.query, parameters.query ? [parameters.query] : [], "toApi"),
|
|
135
|
+
header: convertValue(params.header, parameters.header ? [parameters.header] : [], "toApi"),
|
|
136
|
+
cookie: convertValue(params.cookie, parameters.cookie ? [parameters.cookie] : [], "toApi")
|
|
137
|
+
} : params,
|
|
138
|
+
body: bodySchema && input.body !== void 0 ? convertValue(input.body, [bodySchema], "toApi") : input.body
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** Convert generated camelCase values at the openapi-fetch request and result stages. */
|
|
142
|
+
function createCaseMappingMiddleware(mapping) {
|
|
143
|
+
const convertValue = createConverter(mapping);
|
|
144
|
+
return {
|
|
145
|
+
onRequestInit({ method, schemaPath, input, headers }) {
|
|
146
|
+
const operation = mapping.operations?.[`${method} ${schemaPath}`];
|
|
147
|
+
return operation ? convertRequest(input, operation, convertValue, headers) : void 0;
|
|
148
|
+
},
|
|
149
|
+
onResult({ method, schemaPath, result, options }) {
|
|
150
|
+
const operation = mapping.operations?.[`${method} ${schemaPath}`];
|
|
151
|
+
if (!operation) return;
|
|
152
|
+
const schema = responseMedia(operation, result.response);
|
|
153
|
+
if (!schema) return;
|
|
154
|
+
return {
|
|
155
|
+
...options.parseAs === "json" && result.data !== void 0 ? { data: convertValue(result.data, [schema], "fromApi") } : {},
|
|
156
|
+
...result.error !== void 0 ? { error: convertValue(result.error, [schema], "fromApi") } : {}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
exports.createCaseMappingMiddleware = createCaseMappingMiddleware;
|