@orpc/openapi 1.14.5 → 2.0.0-beta.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 +75 -109
- package/dist/adapters/fetch/index.d.mts +20 -16
- package/dist/adapters/fetch/index.d.ts +20 -16
- package/dist/adapters/fetch/index.mjs +24 -8
- package/dist/adapters/node/index.d.mts +8 -13
- package/dist/adapters/node/index.d.ts +8 -13
- package/dist/adapters/node/index.mjs +10 -7
- package/dist/adapters/standard/index.d.mts +46 -16
- package/dist/adapters/standard/index.d.ts +46 -16
- package/dist/adapters/standard/index.mjs +9 -6
- package/dist/extensions/route.d.mts +43 -0
- package/dist/extensions/route.d.ts +43 -0
- package/dist/extensions/route.mjs +14 -0
- package/dist/helpers/index.d.mts +51 -0
- package/dist/helpers/index.d.ts +51 -0
- package/dist/helpers/index.mjs +39 -0
- package/dist/index.d.mts +92 -111
- package/dist/index.d.ts +92 -111
- package/dist/index.mjs +894 -34
- package/dist/plugins/index.d.mts +55 -51
- package/dist/plugins/index.d.ts +55 -51
- package/dist/plugins/index.mjs +147 -142
- package/dist/shared/openapi.B2SK0ZAr.mjs +359 -0
- package/dist/shared/openapi.B9PQzqBn.mjs +49 -0
- package/dist/shared/openapi.BQzzr4-4.d.ts +299 -0
- package/dist/shared/openapi.BcEtAxQj.d.mts +299 -0
- package/dist/shared/openapi.Bt87OzTt.mjs +131 -0
- package/dist/shared/openapi.C7m7NAmH.d.mts +142 -0
- package/dist/shared/openapi.C7m7NAmH.d.ts +142 -0
- package/dist/shared/openapi.C9Olbxd5.d.ts +75 -0
- package/dist/shared/openapi.CYgMBSUF.d.mts +18 -0
- package/dist/shared/openapi.CYgMBSUF.d.ts +18 -0
- package/dist/shared/openapi.CcyUEuTs.mjs +313 -0
- package/dist/shared/openapi.DmAa7YPO.mjs +275 -0
- package/dist/shared/openapi.Drd1OtzG.d.mts +75 -0
- package/package.json +29 -23
- package/dist/adapters/aws-lambda/index.d.mts +0 -20
- package/dist/adapters/aws-lambda/index.d.ts +0 -20
- package/dist/adapters/aws-lambda/index.mjs +0 -18
- package/dist/adapters/fastify/index.d.mts +0 -23
- package/dist/adapters/fastify/index.d.ts +0 -23
- package/dist/adapters/fastify/index.mjs +0 -18
- package/dist/shared/openapi.BB-W-NKv.mjs +0 -204
- package/dist/shared/openapi.BGy4N6eR.d.mts +0 -120
- package/dist/shared/openapi.BGy4N6eR.d.ts +0 -120
- package/dist/shared/openapi.BwdtJjDu.mjs +0 -878
- package/dist/shared/openapi.DwaweYRb.d.mts +0 -54
- package/dist/shared/openapi.DwaweYRb.d.ts +0 -54
package/dist/index.mjs
CHANGED
|
@@ -1,41 +1,901 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import '@orpc/
|
|
9
|
-
import '@orpc/
|
|
1
|
+
export { B as BracketNotationSerializer } from './shared/openapi.Bt87OzTt.mjs';
|
|
2
|
+
import { createContractCaller, getEventIteratorSchemaDetails } from '@orpc/contract';
|
|
3
|
+
import { g as getOpenAPIMeta } from './shared/openapi.B9PQzqBn.mjs';
|
|
4
|
+
export { o as openapi } from './shared/openapi.B9PQzqBn.mjs';
|
|
5
|
+
import { COMMON_ERROR_STATUS_MAP } from '@orpc/client';
|
|
6
|
+
export { COMMON_ERROR_STATUS_MAP } from '@orpc/client';
|
|
7
|
+
import { DelegatingJsonSchemaConverter, StandardJsonSchemaConverter, combineJsonSchemasWithComposition, isUnconstrainedSchema, extractJsonObjectSchemaEntries, combineJsonObjectSchemaEntries, isJsonPrimitiveSchema, matchArrayableJsonSchema, flattenJsonUnionSchema, ensureJsonSchemaObject, isJsonFileSchema, mapJsonSchemaRefs, encodeJsonPointerSegment, decodeJsonPointerSegment } from '@orpc/json-schema';
|
|
8
|
+
import { walkProcedureContractsAsync, DEFAULT_SUCCESS_STATUS, DEFAULT_ERROR_STATUS } from '@orpc/server';
|
|
9
|
+
import { toArray, clone, value, pathToHttpPath, mergeHttpPath, stringifyJSON, findDeepMatches, isPlainObject, isDeepEqual } from '@orpc/shared';
|
|
10
|
+
import { O as OpenAPISerializer, D as DEFAULT_OPENAPI_METHOD, g as getDynamicPathParams, a as DEFAULT_OPENAPI_INPUT_STRUCTURE, c as DEFAULT_OPENAPI_SUCCESS_DESCRIPTION, b as DEFAULT_OPENAPI_OUTPUT_STRUCTURE } from './shared/openapi.DmAa7YPO.mjs';
|
|
11
|
+
export { d as OpenAPIJsonSerializer } from './shared/openapi.DmAa7YPO.mjs';
|
|
12
|
+
import '@standardserver/core';
|
|
10
13
|
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
function createContractJsonifiedCaller(link, options = {}) {
|
|
15
|
+
return createContractCaller(link, options);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class OpenAPIGeneratorError extends TypeError {
|
|
19
|
+
}
|
|
20
|
+
class OpenAPIGenerator {
|
|
21
|
+
serializer;
|
|
22
|
+
converter;
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.serializer = options.serializer ?? new OpenAPISerializer();
|
|
25
|
+
this.converter = new DelegatingJsonSchemaConverter([
|
|
26
|
+
...toArray(options.converters),
|
|
27
|
+
new StandardJsonSchemaConverter()
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
async generate(router, options = {}) {
|
|
31
|
+
const doc = {
|
|
32
|
+
...clone(options.base),
|
|
33
|
+
openapi: options.base?.openapi ?? "3.1.2",
|
|
34
|
+
info: options.base?.info ?? { title: "API Reference", version: "0.0.0" }
|
|
35
|
+
};
|
|
36
|
+
const errors = [];
|
|
37
|
+
await walkProcedureContractsAsync(router, async (contract, path) => {
|
|
38
|
+
if (value(options.filter, contract, path) === false) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const def = contract["~orpc"];
|
|
43
|
+
const meta = getOpenAPIMeta(contract);
|
|
44
|
+
const method = (meta?.method ?? DEFAULT_OPENAPI_METHOD).toLowerCase();
|
|
45
|
+
const postPath = meta?.path ?? pathToHttpPath(path);
|
|
46
|
+
const httpPath = meta?.prefix ? mergeHttpPath(meta.prefix, postPath) : postPath;
|
|
47
|
+
const dynamicPathParams = getDynamicPathParams(httpPath);
|
|
48
|
+
const openApiPath = toOpenAPIPath(httpPath, dynamicPathParams);
|
|
49
|
+
let operationRef;
|
|
50
|
+
if (meta?.spec !== void 0 && typeof meta.spec !== "function") {
|
|
51
|
+
operationRef = meta.spec;
|
|
52
|
+
} else {
|
|
53
|
+
operationRef = {
|
|
54
|
+
operationId: meta?.operationId ?? path.join("."),
|
|
55
|
+
summary: meta?.summary,
|
|
56
|
+
description: meta?.description,
|
|
57
|
+
deprecated: meta?.deprecated,
|
|
58
|
+
tags: meta?.tags?.map((tag) => tag)
|
|
59
|
+
};
|
|
60
|
+
await this.request(doc, operationRef, def, meta, dynamicPathParams, options, path);
|
|
61
|
+
await this.successResponse(doc, operationRef, def, meta, options, path);
|
|
62
|
+
await this.errorResponse(doc, operationRef, def, meta, options);
|
|
63
|
+
}
|
|
64
|
+
if (typeof meta?.spec === "function") {
|
|
65
|
+
operationRef = meta.spec(operationRef);
|
|
66
|
+
}
|
|
67
|
+
doc.paths ??= {};
|
|
68
|
+
doc.paths[openApiPath] ??= {};
|
|
69
|
+
doc.paths[openApiPath][method] = operationRef;
|
|
70
|
+
} catch (e) {
|
|
71
|
+
if (!(e instanceof OpenAPIGeneratorError)) {
|
|
72
|
+
throw e;
|
|
73
|
+
}
|
|
74
|
+
errors.push(
|
|
75
|
+
`[OpenAPIGenerator] Error occurred while generating OpenAPI for procedure at path: ${path.join(".")}
|
|
76
|
+
${e.message}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
if (errors.length) {
|
|
81
|
+
throw new OpenAPIGeneratorError(
|
|
82
|
+
`Some error occurred during OpenAPI generation:
|
|
83
|
+
|
|
84
|
+
${errors.join("\n\n")}`
|
|
21
85
|
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
86
|
+
}
|
|
87
|
+
return this.serializer.serialize(doc, { asFormData: false, useFormDataForBlobFields: false });
|
|
88
|
+
}
|
|
89
|
+
async convertSchema(schema, direction) {
|
|
90
|
+
const [jsonSchema, optional] = await this.converter.convert(schema, direction);
|
|
91
|
+
return [strip$schemaField(jsonSchema), optional];
|
|
92
|
+
}
|
|
93
|
+
async convertSchemas(schemas, direction) {
|
|
94
|
+
if (!schemas || schemas.length <= 1) {
|
|
95
|
+
return this.convertSchema(schemas?.[0], direction);
|
|
96
|
+
}
|
|
97
|
+
const results = await Promise.all(schemas.map((s) => this.convertSchema(s, direction)));
|
|
98
|
+
const allOfSchemas = [];
|
|
99
|
+
let optional = true;
|
|
100
|
+
for (const [jsonSchema, opt] of results) {
|
|
101
|
+
allOfSchemas.push(jsonSchema);
|
|
102
|
+
if (!opt) {
|
|
103
|
+
optional = false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return [combineJsonSchemasWithComposition("allOf", allOfSchemas), optional];
|
|
107
|
+
}
|
|
108
|
+
async request(doc, ref, def, meta, dynamicPathParams, options, path) {
|
|
109
|
+
const method = meta?.method ?? DEFAULT_OPENAPI_METHOD;
|
|
110
|
+
const inputStructure = meta?.inputStructure ?? DEFAULT_OPENAPI_INPUT_STRUCTURE;
|
|
111
|
+
const inputSchemas = def.inputSchemas;
|
|
112
|
+
if (inputStructure === "compact") {
|
|
113
|
+
const eventIteratorDetails = getEventIteratorDetails(inputSchemas);
|
|
114
|
+
if (eventIteratorDetails) {
|
|
115
|
+
const [yieldSchemas, returnSchemas] = eventIteratorDetails;
|
|
116
|
+
const yieldResult = await this.convertSchemas(yieldSchemas, "input");
|
|
117
|
+
const returnResult = await this.convertSchemas(returnSchemas, "input");
|
|
118
|
+
ref.requestBody = {
|
|
119
|
+
required: true,
|
|
120
|
+
content: toEventIteratorContent(yieldResult, returnResult, doc, options)
|
|
121
|
+
};
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const dynamicParams = dynamicPathParams?.map((v) => v.parameterName);
|
|
126
|
+
const [schema, optional] = await this.convertSchemas(inputSchemas, "input");
|
|
127
|
+
if (isUnconstrainedSchema(schema) && !dynamicParams?.length) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const objectSchemaEntries = extractJsonObjectSchemaEntries(schema);
|
|
131
|
+
if (!objectSchemaEntries) {
|
|
132
|
+
if (inputStructure === "detailed") {
|
|
133
|
+
throw new OpenAPIGeneratorError(
|
|
134
|
+
`Procedure at path "${path.join(".")}" has inputStructure "detailed" but its input schema is not an object.
|
|
135
|
+
Expected shape: { params?: Record<string, unknown>, query?: Record<string, unknown>, headers?: Record<string, unknown>, body?: unknown }`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
if (method === "GET") {
|
|
139
|
+
throw new OpenAPIGeneratorError(
|
|
140
|
+
`Procedure at path "${path.join(".")}" uses method "GET" but its input schema is not an object.
|
|
141
|
+
GET procedures map all input fields to query parameters, so the schema must be an object.
|
|
142
|
+
Expected: Record<string, unknown>`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const paramsObjectSchemaEntries = inputStructure === "compact" ? objectSchemaEntries?.filter(([name]) => dynamicParams?.includes(name)) : extractJsonObjectSchemaEntries(objectSchemaEntries?.find(([name]) => name === "params")?.[1] ?? false);
|
|
147
|
+
const queryObjectSchemaEntries = inputStructure === "compact" ? method === "GET" ? objectSchemaEntries?.filter(([name]) => !dynamicParams?.includes(name)) : void 0 : extractJsonObjectSchemaEntries(objectSchemaEntries?.find(([name]) => name === "query")?.[1] ?? false);
|
|
148
|
+
const headersObjectSchemaEntries = inputStructure === "compact" ? void 0 : extractJsonObjectSchemaEntries(objectSchemaEntries?.find(([name]) => name === "headers")?.[1] ?? false);
|
|
149
|
+
const bodySchema = inputStructure === "compact" ? method === "GET" || method === "HEAD" ? void 0 : !dynamicParams?.length ? schema : objectSchemaEntries ? combineJsonObjectSchemaEntries(objectSchemaEntries?.filter(([name]) => !dynamicParams?.includes(name))) : void 0 : objectSchemaEntries?.find(([name]) => name === "body")?.[1];
|
|
150
|
+
if (dynamicParams?.length) {
|
|
151
|
+
if (!paramsObjectSchemaEntries) {
|
|
152
|
+
throw new OpenAPIGeneratorError(
|
|
153
|
+
`Procedure at path "${path.join(".")}" has dynamic path params (${dynamicParams.map((p) => p).join(", ")}) but its input schema is not an object.
|
|
154
|
+
Each dynamic param must appear as a required key in the schema.`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
dynamicParams.forEach((name) => {
|
|
158
|
+
const entry = paramsObjectSchemaEntries.find(([n]) => n === name);
|
|
159
|
+
if (!entry) {
|
|
160
|
+
throw new OpenAPIGeneratorError(
|
|
161
|
+
`Procedure at path "${path.join(".")}" is missing dynamic param "${name}" in its input schema.
|
|
162
|
+
Route params: ${dynamicParams.map((p) => `{${p}}`).join(", ")}
|
|
163
|
+
Schema keys: ${paramsObjectSchemaEntries.map(([n]) => n).join(", ") || "(none)"}`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
if (entry[2]) {
|
|
167
|
+
throw new OpenAPIGeneratorError(
|
|
168
|
+
`Procedure at path "${path.join(".")}" has dynamic param "${name}" marked as optional in its input schema, but path params must always be required in OpenAPI.`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
ref.parameters ??= [];
|
|
172
|
+
ref.parameters.push({
|
|
173
|
+
in: "path",
|
|
174
|
+
required: true,
|
|
175
|
+
name,
|
|
176
|
+
schema: toOpenAPISchema(entry[1], doc, options)
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (queryObjectSchemaEntries) {
|
|
181
|
+
queryObjectSchemaEntries.forEach(([name, schema2, optional2]) => {
|
|
182
|
+
const style = meta?.queryStyles?.[name];
|
|
183
|
+
const parameter = {
|
|
184
|
+
in: "query",
|
|
185
|
+
name,
|
|
186
|
+
schema: toOpenAPISchema(schema2, doc, options),
|
|
187
|
+
allowEmptyValue: true,
|
|
188
|
+
allowReserved: true
|
|
189
|
+
};
|
|
190
|
+
if (!optional2) {
|
|
191
|
+
parameter.required = true;
|
|
192
|
+
}
|
|
193
|
+
if (style === "comma-delimited-array" || style === "comma-delimited-object") {
|
|
194
|
+
parameter.explode = false;
|
|
195
|
+
} else if (style === "pipe-delimited-array" || style === "pipe-delimited-object") {
|
|
196
|
+
parameter.style = "pipeDelimited";
|
|
197
|
+
} else if (style === "space-delimited-array" || style === "space-delimited-object") {
|
|
198
|
+
parameter.style = "spaceDelimited";
|
|
199
|
+
} else if (style === "json") {
|
|
200
|
+
parameter.content = {
|
|
201
|
+
"application/json": { schema: parameter.schema }
|
|
202
|
+
};
|
|
203
|
+
delete parameter.schema;
|
|
204
|
+
} else if (style === void 0) {
|
|
205
|
+
if (!isJsonPrimitiveSchema(schema2)) {
|
|
206
|
+
const arrayable = matchArrayableJsonSchema(schema2);
|
|
207
|
+
if (!arrayable || !isJsonPrimitiveSchema(arrayable[0])) {
|
|
208
|
+
parameter.style = "deepObject";
|
|
209
|
+
parameter.explode = true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} else ;
|
|
213
|
+
ref.parameters ??= [];
|
|
214
|
+
ref.parameters.push(parameter);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
if (headersObjectSchemaEntries) {
|
|
218
|
+
headersObjectSchemaEntries.forEach(([name, schema2, optional2]) => {
|
|
219
|
+
ref.parameters ??= [];
|
|
220
|
+
ref.parameters.push({
|
|
221
|
+
in: "header",
|
|
222
|
+
name,
|
|
223
|
+
required: optional2 ? void 0 : true,
|
|
224
|
+
schema: toOpenAPISchema(schema2, doc, options)
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (bodySchema !== void 0) {
|
|
229
|
+
const bodyOptional = inputStructure === "compact" ? !dynamicParams?.length ? optional : objectSchemaEntries?.filter(([name]) => !dynamicParams?.includes(name)).every(([, , optional2]) => optional2) : objectSchemaEntries?.find(([name]) => name === "body")?.[2];
|
|
230
|
+
ref.requestBody = {
|
|
231
|
+
required: bodyOptional ? void 0 : true,
|
|
232
|
+
content: toBodyContent(bodySchema, doc, options)
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async successResponse(doc, ref, def, meta, options, path) {
|
|
237
|
+
const outputSchemas = def.outputSchemas;
|
|
238
|
+
const status = meta?.successStatus ?? DEFAULT_SUCCESS_STATUS;
|
|
239
|
+
const description = meta?.successDescription ?? DEFAULT_OPENAPI_SUCCESS_DESCRIPTION;
|
|
240
|
+
const outputStructure = meta?.outputStructure ?? DEFAULT_OPENAPI_OUTPUT_STRUCTURE;
|
|
241
|
+
if (outputStructure === "compact") {
|
|
242
|
+
const eventDetails = getEventIteratorDetails(outputSchemas);
|
|
243
|
+
if (eventDetails) {
|
|
244
|
+
const [yieldSchemas, returnSchemas] = eventDetails;
|
|
245
|
+
const yieldResult = await this.convertSchemas(yieldSchemas, "output");
|
|
246
|
+
const returnResult = await this.convertSchemas(returnSchemas, "output");
|
|
247
|
+
ref.responses ??= {};
|
|
248
|
+
ref.responses[status] = {
|
|
249
|
+
description,
|
|
250
|
+
content: toEventIteratorContent(yieldResult, returnResult, doc, options)
|
|
251
|
+
};
|
|
252
|
+
return;
|
|
30
253
|
}
|
|
31
|
-
throw e;
|
|
32
254
|
}
|
|
255
|
+
const [schema] = await this.convertSchemas(outputSchemas, "output");
|
|
256
|
+
if (isUnconstrainedSchema(schema) || outputStructure === "compact") {
|
|
257
|
+
ref.responses ??= {};
|
|
258
|
+
ref.responses[status] = {
|
|
259
|
+
description,
|
|
260
|
+
content: toBodyContent(schema, doc, options)
|
|
261
|
+
};
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const schemasByStatus = /* @__PURE__ */ new Map();
|
|
265
|
+
for (const item of flattenJsonUnionSchema(schema)) {
|
|
266
|
+
const objectSchemaEntries = extractJsonObjectSchemaEntries(item);
|
|
267
|
+
if (!objectSchemaEntries) {
|
|
268
|
+
throw new OpenAPIGeneratorError(
|
|
269
|
+
`Procedure at path "${path.join(".")}" has outputStructure "detailed" but its output schema is not an object.
|
|
270
|
+
Expected shape: { status: number (200-299), headers?: Record<string, unknown>, body?: unknown }`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
const statusSchema = objectSchemaEntries?.find(([name]) => name === "status")?.[1];
|
|
274
|
+
if (statusSchema !== void 0 && (typeof statusSchema !== "object" || !Number.isInteger(statusSchema.const) || statusSchema.const < 200 || statusSchema.const >= 300)) {
|
|
275
|
+
throw new OpenAPIGeneratorError(
|
|
276
|
+
`Procedure at path "${path.join(".")}" has an invalid "status" field in its outputStructure "detailed" schema.
|
|
277
|
+
Expected: a const integer in the 200-299 range
|
|
278
|
+
Received: ${stringifyJSON(statusSchema)}`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
const status2 = (statusSchema?.const || void 0) ?? meta?.successStatus ?? DEFAULT_SUCCESS_STATUS;
|
|
282
|
+
const description2 = statusSchema?.description;
|
|
283
|
+
const schemas = schemasByStatus.get(status2);
|
|
284
|
+
const headersSchema = objectSchemaEntries?.find(([name]) => name === "headers")?.[1];
|
|
285
|
+
const bodySchema = objectSchemaEntries?.find(([name]) => name === "body")?.[1];
|
|
286
|
+
if (schemas) {
|
|
287
|
+
schemas.push({ description: description2, headers: headersSchema, body: bodySchema });
|
|
288
|
+
} else {
|
|
289
|
+
schemasByStatus.set(status2, [{ description: description2, headers: headersSchema, body: bodySchema }]);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
for (const [status2, schemas] of schemasByStatus.entries()) {
|
|
293
|
+
const descriptions = schemas.map(({ description: description2 }) => description2).filter((d) => d !== void 0);
|
|
294
|
+
const responseObject = {
|
|
295
|
+
description: descriptions.length ? descriptions.join(", ") : description
|
|
296
|
+
};
|
|
297
|
+
const bodySchemas = schemas.map(({ body }) => body).filter((b) => b !== void 0);
|
|
298
|
+
if (bodySchemas.length) {
|
|
299
|
+
responseObject.content = toBodyContent(combineJsonSchemasWithComposition("anyOf", bodySchemas), doc, options);
|
|
300
|
+
}
|
|
301
|
+
const headerSchemas = schemas.map(({ headers }) => headers).filter((b) => b !== void 0);
|
|
302
|
+
if (headerSchemas.length) {
|
|
303
|
+
const entries = extractJsonObjectSchemaEntries(combineJsonSchemasWithComposition("anyOf", headerSchemas));
|
|
304
|
+
entries?.forEach(([name, schema2, optional]) => {
|
|
305
|
+
responseObject.headers ??= {};
|
|
306
|
+
responseObject.headers[name] = {
|
|
307
|
+
required: optional ? void 0 : true,
|
|
308
|
+
schema: toOpenAPISchema(schema2, doc, options)
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
ref.responses ??= {};
|
|
313
|
+
ref.responses[status2] = responseObject;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async errorResponse(doc, ref, def, meta, options) {
|
|
317
|
+
const errorStatusMap = options.errorStatusMap ?? COMMON_ERROR_STATUS_MAP;
|
|
318
|
+
const errorMap = def.errorMap;
|
|
319
|
+
const errorDefinitionsByStatus = /* @__PURE__ */ new Map();
|
|
320
|
+
for (const code in errorMap) {
|
|
321
|
+
const config = errorMap[code];
|
|
322
|
+
if (!config) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const status = errorStatusMap[code] ?? DEFAULT_ERROR_STATUS;
|
|
326
|
+
const defaultMessage = config.message;
|
|
327
|
+
const [dataJsonSchema, dataOptional] = await this.convertSchema(config.data, "output");
|
|
328
|
+
const definitions = errorDefinitionsByStatus.get(status);
|
|
329
|
+
if (definitions) {
|
|
330
|
+
definitions.push({ code, dataJsonSchema, dataOptional, defaultMessage });
|
|
331
|
+
} else {
|
|
332
|
+
errorDefinitionsByStatus.set(status, [{ code, dataJsonSchema, dataOptional, defaultMessage }]);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (errorDefinitionsByStatus.size) {
|
|
336
|
+
const undefinedErrorSchema = hoistDefs({
|
|
337
|
+
$defs: {
|
|
338
|
+
UndefinedError: {
|
|
339
|
+
type: "object",
|
|
340
|
+
properties: {
|
|
341
|
+
defined: { const: false },
|
|
342
|
+
inferable: { type: "boolean" },
|
|
343
|
+
code: { type: "string" },
|
|
344
|
+
status: { type: "number" },
|
|
345
|
+
message: { type: "string" },
|
|
346
|
+
data: {}
|
|
347
|
+
},
|
|
348
|
+
required: ["defined", "inferable", "code", "status", "message"]
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
$ref: "#/$defs/UndefinedError"
|
|
352
|
+
}, doc, options);
|
|
353
|
+
for (const [status, definitions] of errorDefinitionsByStatus.entries()) {
|
|
354
|
+
const descriptions = definitions.map(({ defaultMessage }) => defaultMessage).filter((m) => m !== void 0);
|
|
355
|
+
const customBodySchema = value(
|
|
356
|
+
options.customErrorResponseBodySchema,
|
|
357
|
+
definitions.map((def2) => ({ ...def2, dataJsonSchema: hoistDefs(def2.dataJsonSchema, doc, options) })),
|
|
358
|
+
status
|
|
359
|
+
);
|
|
360
|
+
const responseSchema = customBodySchema ?? combineJsonSchemasWithComposition("oneOf", [
|
|
361
|
+
...definitions.map(({ code, dataJsonSchema, dataOptional, defaultMessage }) => {
|
|
362
|
+
return combineJsonObjectSchemaEntries([
|
|
363
|
+
["defined", { const: true }, false],
|
|
364
|
+
["inferable", { type: "boolean" }, false],
|
|
365
|
+
["code", { const: code }, false],
|
|
366
|
+
["status", { const: status }, false],
|
|
367
|
+
["message", { type: "string", default: defaultMessage }, false],
|
|
368
|
+
["data", dataJsonSchema, dataOptional]
|
|
369
|
+
]);
|
|
370
|
+
}),
|
|
371
|
+
undefinedErrorSchema
|
|
372
|
+
]);
|
|
373
|
+
ref.responses ??= {};
|
|
374
|
+
ref.responses[status] = {
|
|
375
|
+
description: descriptions.length ? descriptions.join(", ") : status.toString(),
|
|
376
|
+
content: {
|
|
377
|
+
"application/json": {
|
|
378
|
+
schema: toOpenAPISchema(responseSchema, doc, options)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function toOpenAPISchema(schema, doc, options) {
|
|
387
|
+
return ensureJsonSchemaObject(hoistDefs(
|
|
388
|
+
schema,
|
|
389
|
+
doc,
|
|
390
|
+
options
|
|
391
|
+
));
|
|
392
|
+
}
|
|
393
|
+
function toOpenAPIPath(path, dynamicPathParams) {
|
|
394
|
+
if (!dynamicPathParams?.length) {
|
|
395
|
+
return path;
|
|
396
|
+
}
|
|
397
|
+
let normalized = "";
|
|
398
|
+
let currentIndex = 0;
|
|
399
|
+
for (const param of dynamicPathParams) {
|
|
400
|
+
normalized += path.slice(currentIndex, param.startIndex);
|
|
401
|
+
normalized += `{${param.parameterName}}`;
|
|
402
|
+
currentIndex = param.startIndex + param.segment.length;
|
|
403
|
+
}
|
|
404
|
+
normalized += path.slice(currentIndex);
|
|
405
|
+
return normalized;
|
|
406
|
+
}
|
|
407
|
+
function strip$schemaField(schema) {
|
|
408
|
+
if (typeof schema !== "object") {
|
|
409
|
+
return schema;
|
|
410
|
+
}
|
|
411
|
+
const { $schema, ...rest } = schema;
|
|
412
|
+
return rest;
|
|
413
|
+
}
|
|
414
|
+
function getEventIteratorDetails(schemas) {
|
|
415
|
+
if (!schemas || schemas.length === 0) {
|
|
416
|
+
return void 0;
|
|
417
|
+
}
|
|
418
|
+
const yieldSchemas = [];
|
|
419
|
+
const returnSchemas = [];
|
|
420
|
+
for (const s of schemas) {
|
|
421
|
+
const details = getEventIteratorSchemaDetails(s);
|
|
422
|
+
if (!details) {
|
|
423
|
+
return void 0;
|
|
424
|
+
}
|
|
425
|
+
yieldSchemas.push(details.yieldSchema);
|
|
426
|
+
if (details.returnSchema) {
|
|
427
|
+
returnSchemas.push(details.returnSchema);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return yieldSchemas.length || returnSchemas.length ? [yieldSchemas, returnSchemas] : void 0;
|
|
431
|
+
}
|
|
432
|
+
function toEventIteratorContent([yieldSchema, yieldOptional], [returnSchema, returnOptional], doc, options) {
|
|
433
|
+
const schema = combineJsonSchemasWithComposition("oneOf", [
|
|
434
|
+
combineJsonObjectSchemaEntries([
|
|
435
|
+
["event", { const: "message" }, false],
|
|
436
|
+
["data", yieldSchema, yieldOptional],
|
|
437
|
+
["id", { type: "string" }, true],
|
|
438
|
+
["retry", { type: "number" }, true]
|
|
439
|
+
]),
|
|
440
|
+
combineJsonObjectSchemaEntries([
|
|
441
|
+
["event", { const: "close" }, false],
|
|
442
|
+
["data", returnSchema, returnOptional],
|
|
443
|
+
["id", { type: "string" }, true],
|
|
444
|
+
["retry", { type: "number" }, true]
|
|
445
|
+
]),
|
|
446
|
+
{
|
|
447
|
+
type: "object",
|
|
448
|
+
properties: {
|
|
449
|
+
event: { const: "error" },
|
|
450
|
+
data: {},
|
|
451
|
+
id: { type: "string" },
|
|
452
|
+
retry: { type: "number" }
|
|
453
|
+
},
|
|
454
|
+
required: ["event"]
|
|
455
|
+
}
|
|
456
|
+
]);
|
|
457
|
+
return {
|
|
458
|
+
"text/event-stream": {
|
|
459
|
+
schema: toOpenAPISchema(schema, doc, options)
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function toBodyContent(schema, doc, options) {
|
|
464
|
+
const fileSchemasByMediaType = /* @__PURE__ */ new Map();
|
|
465
|
+
const rest = flattenJsonUnionSchema(schema).filter((s) => {
|
|
466
|
+
if (!isJsonFileSchema(s)) {
|
|
467
|
+
return !isUnconstrainedSchema(s);
|
|
468
|
+
}
|
|
469
|
+
const contentMediaType = s.contentMediaType ?? "*/*";
|
|
470
|
+
const schemas = fileSchemasByMediaType.get(contentMediaType);
|
|
471
|
+
if (schemas) {
|
|
472
|
+
schemas.push(s);
|
|
473
|
+
} else {
|
|
474
|
+
fileSchemasByMediaType.set(contentMediaType, [s]);
|
|
475
|
+
}
|
|
476
|
+
return false;
|
|
477
|
+
});
|
|
478
|
+
const content = {};
|
|
479
|
+
if (rest.length > 0) {
|
|
480
|
+
const restSchema = fileSchemasByMediaType.size ? combineJsonSchemasWithComposition("anyOf", rest) : schema;
|
|
481
|
+
const hasNestedFiles = findDeepMatches(
|
|
482
|
+
(v) => isPlainObject(v) && isJsonFileSchema(v),
|
|
483
|
+
restSchema
|
|
484
|
+
).values.length > 0;
|
|
485
|
+
const contentType = hasNestedFiles ? "multipart/form-data" : "application/json";
|
|
486
|
+
const fileSchemas = fileSchemasByMediaType.get(contentType);
|
|
487
|
+
fileSchemasByMediaType.delete(contentType);
|
|
488
|
+
content[contentType] = {
|
|
489
|
+
schema: toOpenAPISchema(combineJsonSchemasWithComposition("anyOf", [restSchema, ...toArray(fileSchemas)]), doc, options)
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
for (const [contentType, schemas] of fileSchemasByMediaType.entries()) {
|
|
493
|
+
content[contentType] = {
|
|
494
|
+
schema: toOpenAPISchema(combineJsonSchemasWithComposition("anyOf", schemas), doc, options)
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
return content;
|
|
498
|
+
}
|
|
499
|
+
function hoistDefs(schema, doc, options) {
|
|
500
|
+
if (typeof schema !== "object") {
|
|
501
|
+
return schema;
|
|
502
|
+
}
|
|
503
|
+
if (!schema.$defs) {
|
|
504
|
+
return schema;
|
|
505
|
+
}
|
|
506
|
+
const { $defs, ...rest } = schema;
|
|
507
|
+
const localDefs = {};
|
|
508
|
+
const hoistedDefs = {};
|
|
509
|
+
for (const defName of Object.keys($defs)) {
|
|
510
|
+
const defSchema = $defs[defName];
|
|
511
|
+
if (defSchema === void 0) {
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
const normalized = normalizeHoistedDefSchema(defSchema);
|
|
515
|
+
if (value(options.shouldHoistDef, defName, normalized) !== false) {
|
|
516
|
+
hoistedDefs[defName] = normalized;
|
|
517
|
+
} else {
|
|
518
|
+
localDefs[defName] = normalized;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
hoistReferencedLocalDefs(hoistedDefs, localDefs);
|
|
522
|
+
if (Object.keys(hoistedDefs).length === 0) {
|
|
523
|
+
return schema;
|
|
524
|
+
}
|
|
525
|
+
doc.components ??= {};
|
|
526
|
+
doc.components.schemas ??= {};
|
|
527
|
+
const componentsSchemas = doc.components.schemas;
|
|
528
|
+
const identityRenameMap = Object.fromEntries(
|
|
529
|
+
Object.keys(hoistedDefs).map((defName) => [defName, defName])
|
|
530
|
+
);
|
|
531
|
+
const renameMap = {};
|
|
532
|
+
const pendingSchemas = [];
|
|
533
|
+
for (const defName of Object.keys(hoistedDefs)) {
|
|
534
|
+
const cleanSchema = hoistedDefs[defName];
|
|
535
|
+
const existingSchema = componentsSchemas[defName];
|
|
536
|
+
const candidateSchemas = Object.fromEntries(
|
|
537
|
+
Object.keys(hoistedDefs).map((currentDefName) => [
|
|
538
|
+
currentDefName,
|
|
539
|
+
rewriteComponentSchemaRefs(
|
|
540
|
+
withReferencedLocalDefs(hoistedDefs[currentDefName], localDefs),
|
|
541
|
+
{
|
|
542
|
+
...identityRenameMap,
|
|
543
|
+
...renameMap
|
|
544
|
+
}
|
|
545
|
+
)
|
|
546
|
+
])
|
|
547
|
+
);
|
|
548
|
+
const prelimSchema = candidateSchemas[defName];
|
|
549
|
+
if (existingSchema !== void 0) {
|
|
550
|
+
const reusableComponentName = findReusableComponentName(componentsSchemas, defName, prelimSchema, candidateSchemas);
|
|
551
|
+
if (reusableComponentName !== void 0) {
|
|
552
|
+
renameMap[defName] = reusableComponentName;
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
const componentName = findUniqueComponentName(componentsSchemas, defName);
|
|
556
|
+
renameMap[defName] = componentName;
|
|
557
|
+
pendingSchemas.push({ cleanSchema, componentName });
|
|
558
|
+
} else {
|
|
559
|
+
const reusableComponentName = findReusableComponentName(componentsSchemas, defName, prelimSchema, candidateSchemas);
|
|
560
|
+
if (reusableComponentName !== void 0) {
|
|
561
|
+
renameMap[defName] = reusableComponentName;
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
renameMap[defName] = defName;
|
|
565
|
+
pendingSchemas.push({ cleanSchema, componentName: defName });
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
for (const { cleanSchema, componentName } of pendingSchemas) {
|
|
569
|
+
componentsSchemas[componentName] = rewriteComponentSchemaRefs(
|
|
570
|
+
withReferencedLocalDefs(cleanSchema, localDefs),
|
|
571
|
+
renameMap
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
return rewriteComponentSchemaRefs(withReferencedLocalDefs(rest, localDefs), renameMap);
|
|
575
|
+
}
|
|
576
|
+
function normalizeHoistedDefSchema(schema) {
|
|
577
|
+
let cleanSchema = typeof schema === "boolean" ? schema ? {} : { not: {} } : { ...schema };
|
|
578
|
+
if (cleanSchema.additionalProperties === false) {
|
|
579
|
+
const { additionalProperties: _ignored, ...withoutAdditionalProperties } = cleanSchema;
|
|
580
|
+
cleanSchema = withoutAdditionalProperties;
|
|
581
|
+
}
|
|
582
|
+
return cleanSchema;
|
|
583
|
+
}
|
|
584
|
+
function hoistReferencedLocalDefs(hoistedDefs, localDefs) {
|
|
585
|
+
const queue = Object.values(hoistedDefs);
|
|
586
|
+
while (queue.length > 0) {
|
|
587
|
+
const current = queue.shift();
|
|
588
|
+
if (current === void 0) {
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
visitSchemaRefs(current, (refName) => {
|
|
592
|
+
const referenced = localDefs[refName];
|
|
593
|
+
if (referenced === void 0) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
hoistedDefs[refName] = referenced;
|
|
597
|
+
delete localDefs[refName];
|
|
598
|
+
queue.push(referenced);
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
function withReferencedLocalDefs(schema, localDefs) {
|
|
603
|
+
const referencedLocalDefs = collectReferencedLocalDefNames(schema, localDefs);
|
|
604
|
+
if (referencedLocalDefs.length === 0) {
|
|
605
|
+
return schema;
|
|
606
|
+
}
|
|
607
|
+
const mergedDefs = {
|
|
608
|
+
...schema.$defs
|
|
609
|
+
};
|
|
610
|
+
for (const defName of referencedLocalDefs) {
|
|
611
|
+
mergedDefs[defName] = localDefs[defName];
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
...schema,
|
|
615
|
+
$defs: mergedDefs
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function collectReferencedLocalDefNames(schema, localDefs) {
|
|
619
|
+
if (Object.keys(localDefs).length === 0) {
|
|
620
|
+
return [];
|
|
621
|
+
}
|
|
622
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
623
|
+
const queued = /* @__PURE__ */ new Set();
|
|
624
|
+
const queue = [schema];
|
|
625
|
+
while (queue.length > 0) {
|
|
626
|
+
const current = queue.shift();
|
|
627
|
+
if (current === void 0) {
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
visitSchemaRefs(current, (refName) => {
|
|
631
|
+
if (localDefs[refName] === void 0 || referenced.has(refName)) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
referenced.add(refName);
|
|
635
|
+
if (!queued.has(refName)) {
|
|
636
|
+
queued.add(refName);
|
|
637
|
+
queue.push(localDefs[refName]);
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
return [...referenced];
|
|
642
|
+
}
|
|
643
|
+
function visitSchemaRefs(schema, onRef, seen = /* @__PURE__ */ new Set()) {
|
|
644
|
+
if (typeof schema !== "object" || schema === null) {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
if (seen.has(schema)) {
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
seen.add(schema);
|
|
651
|
+
if (typeof schema.$ref === "string") {
|
|
652
|
+
const refName = parseLocalDefRefName(schema.$ref);
|
|
653
|
+
if (refName !== void 0) {
|
|
654
|
+
onRef(refName);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
for (const keyword of ["allOf", "anyOf", "oneOf"]) {
|
|
658
|
+
if (Array.isArray(schema[keyword])) {
|
|
659
|
+
for (const item of schema[keyword]) {
|
|
660
|
+
visitSchemaRefs(item, onRef, seen);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (schema.properties) {
|
|
665
|
+
for (const property of Object.values(schema.properties)) {
|
|
666
|
+
visitSchemaRefs(property, onRef, seen);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (schema.items !== void 0) {
|
|
670
|
+
visitSchemaRefs(schema.items, onRef, seen);
|
|
671
|
+
}
|
|
672
|
+
if (typeof schema.additionalProperties === "object" && schema.additionalProperties !== null) {
|
|
673
|
+
visitSchemaRefs(schema.additionalProperties, onRef, seen);
|
|
674
|
+
}
|
|
675
|
+
if (schema.not !== void 0) {
|
|
676
|
+
visitSchemaRefs(schema.not, onRef, seen);
|
|
677
|
+
}
|
|
678
|
+
if (schema.if !== void 0) {
|
|
679
|
+
visitSchemaRefs(schema.if, onRef, seen);
|
|
680
|
+
}
|
|
681
|
+
if (schema.then !== void 0) {
|
|
682
|
+
visitSchemaRefs(schema.then, onRef, seen);
|
|
683
|
+
}
|
|
684
|
+
if (schema.else !== void 0) {
|
|
685
|
+
visitSchemaRefs(schema.else, onRef, seen);
|
|
686
|
+
}
|
|
687
|
+
if (Array.isArray(schema.prefixItems)) {
|
|
688
|
+
for (const item of schema.prefixItems) {
|
|
689
|
+
visitSchemaRefs(item, onRef, seen);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
if (schema.$defs) {
|
|
693
|
+
for (const def of Object.values(schema.$defs)) {
|
|
694
|
+
if (def !== void 0) {
|
|
695
|
+
visitSchemaRefs(def, onRef, seen);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
function findUniqueComponentName(componentsSchemas, baseName) {
|
|
701
|
+
const candidate = `${baseName}`;
|
|
702
|
+
if (componentsSchemas[candidate] === void 0)
|
|
703
|
+
return candidate;
|
|
704
|
+
let i = 2;
|
|
705
|
+
while (componentsSchemas[`${baseName}${i}`] !== void 0) {
|
|
706
|
+
i++;
|
|
707
|
+
}
|
|
708
|
+
return `${baseName}${i}`;
|
|
709
|
+
}
|
|
710
|
+
function findReusableComponentName(componentsSchemas, defName, schema, candidateSchemas) {
|
|
711
|
+
const exactMatch = componentsSchemas[defName];
|
|
712
|
+
if (exactMatch !== void 0 && areSchemasEquivalentForReuse(
|
|
713
|
+
schema,
|
|
714
|
+
exactMatch,
|
|
715
|
+
schema,
|
|
716
|
+
exactMatch,
|
|
717
|
+
candidateSchemas,
|
|
718
|
+
componentsSchemas,
|
|
719
|
+
/* @__PURE__ */ new Map([[defName, defName]]),
|
|
720
|
+
/* @__PURE__ */ new Map([[defName, defName]])
|
|
721
|
+
)) {
|
|
722
|
+
return defName;
|
|
723
|
+
}
|
|
724
|
+
for (const [componentName, componentSchema] of Object.entries(componentsSchemas)) {
|
|
725
|
+
if (componentName === defName) {
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
if (areSchemasEquivalentForReuse(
|
|
729
|
+
schema,
|
|
730
|
+
componentSchema,
|
|
731
|
+
schema,
|
|
732
|
+
componentSchema,
|
|
733
|
+
candidateSchemas,
|
|
734
|
+
componentsSchemas,
|
|
735
|
+
/* @__PURE__ */ new Map([[defName, componentName]]),
|
|
736
|
+
/* @__PURE__ */ new Map([[componentName, defName]])
|
|
737
|
+
)) {
|
|
738
|
+
return componentName;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return void 0;
|
|
742
|
+
}
|
|
743
|
+
function areSchemasEquivalentForReuse(candidate, existing, candidateRootSchema, existingRootSchema, candidateSchemas, existingSchemas, candidateToExistingComponentNames, existingToCandidateComponentNames, visited = /* @__PURE__ */ new WeakMap()) {
|
|
744
|
+
if (candidate === existing) {
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
if (typeof candidate !== typeof existing) {
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
if (candidate === null || existing === null) {
|
|
751
|
+
return candidate === existing;
|
|
752
|
+
}
|
|
753
|
+
if (typeof candidate !== "object" || typeof existing !== "object") {
|
|
754
|
+
return isDeepEqual(candidate, existing);
|
|
755
|
+
}
|
|
756
|
+
const seenExisting = visited.get(candidate);
|
|
757
|
+
if (seenExisting?.has(existing)) {
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
760
|
+
if (seenExisting) {
|
|
761
|
+
seenExisting.add(existing);
|
|
762
|
+
} else {
|
|
763
|
+
visited.set(candidate, new WeakSet([existing]));
|
|
764
|
+
}
|
|
765
|
+
if (Array.isArray(candidate) || Array.isArray(existing)) {
|
|
766
|
+
if (!Array.isArray(candidate) || !Array.isArray(existing) || candidate.length !== existing.length) {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
return candidate.every((item, index) => areSchemasEquivalentForReuse(
|
|
770
|
+
item,
|
|
771
|
+
existing[index],
|
|
772
|
+
candidateRootSchema,
|
|
773
|
+
existingRootSchema,
|
|
774
|
+
candidateSchemas,
|
|
775
|
+
existingSchemas,
|
|
776
|
+
candidateToExistingComponentNames,
|
|
777
|
+
existingToCandidateComponentNames,
|
|
778
|
+
visited
|
|
779
|
+
));
|
|
780
|
+
}
|
|
781
|
+
const candidateObject = candidate;
|
|
782
|
+
const existingObject = existing;
|
|
783
|
+
const candidateKeys = Object.keys(candidateObject).sort();
|
|
784
|
+
const existingKeys = Object.keys(existingObject).sort();
|
|
785
|
+
if (!isDeepEqual(candidateKeys, existingKeys)) {
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
return candidateKeys.every((key) => {
|
|
789
|
+
const candidateValue = candidateObject[key];
|
|
790
|
+
const existingValue = existingObject[key];
|
|
791
|
+
if (key === "$ref" && typeof candidateValue === "string" && typeof existingValue === "string") {
|
|
792
|
+
return areSchemaRefsEquivalentForReuse(
|
|
793
|
+
candidateValue,
|
|
794
|
+
existingValue,
|
|
795
|
+
candidateRootSchema,
|
|
796
|
+
existingRootSchema,
|
|
797
|
+
candidateSchemas,
|
|
798
|
+
existingSchemas,
|
|
799
|
+
candidateToExistingComponentNames,
|
|
800
|
+
existingToCandidateComponentNames,
|
|
801
|
+
visited
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
return areSchemasEquivalentForReuse(
|
|
805
|
+
candidateValue,
|
|
806
|
+
existingValue,
|
|
807
|
+
candidateRootSchema,
|
|
808
|
+
existingRootSchema,
|
|
809
|
+
candidateSchemas,
|
|
810
|
+
existingSchemas,
|
|
811
|
+
candidateToExistingComponentNames,
|
|
812
|
+
existingToCandidateComponentNames,
|
|
813
|
+
visited
|
|
814
|
+
);
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
function parseComponentRefName(ref) {
|
|
818
|
+
if (!ref.startsWith("#/components/schemas/")) {
|
|
819
|
+
return void 0;
|
|
820
|
+
}
|
|
821
|
+
return ref.slice("#/components/schemas/".length).split("/").map(decodeJsonPointerSegment).join("/");
|
|
822
|
+
}
|
|
823
|
+
function resolveSchemaComparisonRef(ref, rootSchema, componentsSchemas) {
|
|
824
|
+
const localDefName = parseLocalDefRefName(ref);
|
|
825
|
+
if (localDefName !== void 0 && typeof rootSchema === "object" && rootSchema !== null) {
|
|
826
|
+
const localDef = rootSchema.$defs?.[localDefName];
|
|
827
|
+
if (localDef !== void 0) {
|
|
828
|
+
return {
|
|
829
|
+
schema: localDef,
|
|
830
|
+
rootSchema
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
const componentName = parseComponentRefName(ref);
|
|
835
|
+
if (componentName !== void 0) {
|
|
836
|
+
const componentSchema = componentsSchemas[componentName];
|
|
837
|
+
if (componentSchema !== void 0) {
|
|
838
|
+
return {
|
|
839
|
+
schema: componentSchema,
|
|
840
|
+
rootSchema: componentSchema
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return void 0;
|
|
845
|
+
}
|
|
846
|
+
function areSchemaRefsEquivalentForReuse(candidateRef, existingRef, candidateRootSchema, existingRootSchema, candidateSchemas, existingSchemas, candidateToExistingComponentNames, existingToCandidateComponentNames, visited) {
|
|
847
|
+
const candidateComponentName = parseComponentRefName(candidateRef);
|
|
848
|
+
const existingComponentName = parseComponentRefName(existingRef);
|
|
849
|
+
if (candidateComponentName === void 0 !== (existingComponentName === void 0)) {
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
if (candidateComponentName !== void 0 && existingComponentName !== void 0) {
|
|
853
|
+
const mappedExisting = candidateToExistingComponentNames.get(candidateComponentName);
|
|
854
|
+
if (mappedExisting !== void 0 && mappedExisting !== existingComponentName) {
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
const mappedCandidate = existingToCandidateComponentNames.get(existingComponentName);
|
|
858
|
+
if (mappedCandidate !== void 0 && mappedCandidate !== candidateComponentName) {
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
candidateToExistingComponentNames.set(candidateComponentName, existingComponentName);
|
|
862
|
+
existingToCandidateComponentNames.set(existingComponentName, candidateComponentName);
|
|
863
|
+
}
|
|
864
|
+
const resolvedCandidate = resolveSchemaComparisonRef(candidateRef, candidateRootSchema, candidateSchemas);
|
|
865
|
+
const resolvedExisting = resolveSchemaComparisonRef(existingRef, existingRootSchema, existingSchemas);
|
|
866
|
+
if (resolvedCandidate === void 0 || resolvedExisting === void 0) {
|
|
867
|
+
return candidateRef === existingRef;
|
|
868
|
+
}
|
|
869
|
+
return areSchemasEquivalentForReuse(
|
|
870
|
+
resolvedCandidate.schema,
|
|
871
|
+
resolvedExisting.schema,
|
|
872
|
+
resolvedCandidate.rootSchema,
|
|
873
|
+
resolvedExisting.rootSchema,
|
|
874
|
+
candidateSchemas,
|
|
875
|
+
existingSchemas,
|
|
876
|
+
candidateToExistingComponentNames,
|
|
877
|
+
existingToCandidateComponentNames,
|
|
878
|
+
visited
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
function parseLocalDefRefName(ref) {
|
|
882
|
+
if (!ref.startsWith("#/$defs/")) {
|
|
883
|
+
return void 0;
|
|
884
|
+
}
|
|
885
|
+
return ref.slice("#/$defs/".length).split("/").map(decodeJsonPointerSegment).join("/");
|
|
886
|
+
}
|
|
887
|
+
function rewriteComponentSchemaRefs(schema, renameMap) {
|
|
888
|
+
return mapJsonSchemaRefs(schema, (ref) => {
|
|
889
|
+
const refName = parseLocalDefRefName(ref);
|
|
890
|
+
if (refName === void 0) {
|
|
891
|
+
return ref;
|
|
892
|
+
}
|
|
893
|
+
const renamedName = renameMap[refName];
|
|
894
|
+
if (renamedName === void 0) {
|
|
895
|
+
return ref;
|
|
896
|
+
}
|
|
897
|
+
return `#/components/schemas/${encodeJsonPointerSegment(renamedName)}`;
|
|
33
898
|
});
|
|
34
|
-
return createRouterClient(router, options);
|
|
35
899
|
}
|
|
36
900
|
|
|
37
|
-
|
|
38
|
-
spec: customOpenAPIOperation
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export { createJsonifiedRouterClient, customOpenAPIOperation, oo };
|
|
901
|
+
export { OpenAPIGenerator, OpenAPIGeneratorError, OpenAPISerializer, createContractJsonifiedCaller, getDynamicPathParams, getOpenAPIMeta };
|