@elysiajs/openapi 1.4.15 → 1.4.16
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 +7 -1
- package/bun.lock +8 -6
- package/dist/cjs/gen/index.js +1107 -3651
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/index.js +1412 -3750
- package/dist/cjs/openapi.d.ts +11 -8
- package/dist/cjs/openapi.js +301 -101
- package/dist/cjs/types.d.ts +11 -3
- package/dist/gen/index.d.ts +43 -1
- package/dist/gen/index.mjs +1098 -3651
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +1542 -3884
- package/dist/openapi.d.ts +11 -8
- package/dist/openapi.mjs +296 -97
- package/dist/types.d.ts +11 -3
- package/package.json +5 -3
package/dist/openapi.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type AnyElysia, type
|
|
1
|
+
import { type AnyElysia, type InputSchema } from 'elysia';
|
|
2
2
|
import type { OpenAPIV3 } from 'openapi-types';
|
|
3
|
-
import { TAnySchema
|
|
4
|
-
import type { AdditionalReferences, ElysiaOpenAPIConfig, MapJsonSchema } from './types';
|
|
3
|
+
import { TAnySchema } from '@sinclair/typebox';
|
|
4
|
+
import type { AdditionalReferences, ElysiaOpenAPIConfig, MapJsonSchema, OpenAPIVersion } from './types';
|
|
5
5
|
export declare const capitalize: (word: string) => string;
|
|
6
6
|
/**
|
|
7
7
|
* Get all possible paths of a path with optional parameters
|
|
@@ -10,24 +10,27 @@ export declare const capitalize: (word: string) => string;
|
|
|
10
10
|
*/
|
|
11
11
|
export declare const getPossiblePath: (path: string) => string[];
|
|
12
12
|
export declare const getLoosePath: (path: string) => string;
|
|
13
|
-
export declare const unwrapSchema: (schema: InputSchema["body"], mapJsonSchema?: MapJsonSchema, io?: "input" | "output") => OpenAPIV3.SchemaObject | undefined;
|
|
13
|
+
export declare const unwrapSchema: (schema: InputSchema["body"], mapJsonSchema?: MapJsonSchema, io?: "input" | "output", openapiVersion?: OpenAPIVersion) => OpenAPIV3.SchemaObject | undefined;
|
|
14
14
|
/**
|
|
15
15
|
* Convert TypeBox enum-like Union schemas to OpenAPI enum schemas
|
|
16
16
|
*
|
|
17
17
|
* Otherwise, return the schema as is
|
|
18
18
|
*/
|
|
19
19
|
export declare const enumToOpenApi: <T extends TAnySchema | OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined>(_schema: T) => T;
|
|
20
|
+
export declare const nullToOpenApi: <T>(schema: T, openapiVersion: OpenAPIVersion) => T;
|
|
20
21
|
/**
|
|
21
|
-
* Converts Elysia routes to OpenAPI
|
|
22
|
+
* Converts Elysia routes to OpenAPI paths schema
|
|
22
23
|
* @param routes Array of Elysia route objects
|
|
23
24
|
* @returns OpenAPI paths object
|
|
24
25
|
*/
|
|
25
|
-
export declare function toOpenAPISchema(app: AnyElysia, exclude?: ElysiaOpenAPIConfig['exclude'], references?: AdditionalReferences, vendors?: MapJsonSchema): {
|
|
26
|
+
export declare function toOpenAPISchema(app: AnyElysia, exclude?: ElysiaOpenAPIConfig['exclude'], references?: AdditionalReferences, vendors?: MapJsonSchema, openapiVersion?: OpenAPIVersion): {
|
|
26
27
|
components: {
|
|
27
28
|
schemas: any;
|
|
28
29
|
};
|
|
29
30
|
paths: OpenAPIV3.PathsObject<{}, {}>;
|
|
30
31
|
};
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
type ResponseHeaderSchemas = Record<string, Exclude<InputSchema['headers'], undefined>>;
|
|
33
|
+
export declare const withHeaders: <S extends Exclude<InputSchema["body"], string | undefined>, H extends ResponseHeaderSchemas>(schema: S, headers: H) => S & {
|
|
34
|
+
headers: H;
|
|
33
35
|
};
|
|
36
|
+
export {};
|
package/dist/openapi.mjs
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
// src/openapi.ts
|
|
2
2
|
import { t } from "elysia";
|
|
3
|
-
|
|
4
|
-
// node_modules/@sinclair/typebox/build/esm/type/symbols/symbols.mjs
|
|
5
|
-
var Kind = /* @__PURE__ */ Symbol.for("TypeBox.Kind");
|
|
6
|
-
|
|
7
|
-
// src/openapi.ts
|
|
3
|
+
import { Kind } from "@sinclair/typebox";
|
|
8
4
|
var capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
|
|
9
|
-
var toRef = (name) => t.Ref(
|
|
10
|
-
name.startsWith("#/") ? name : `#/components/schemas/${name}`
|
|
11
|
-
);
|
|
5
|
+
var toRef = (name) => t.Ref(name.startsWith("#/") ? name : `#/components/schemas/${name}`);
|
|
12
6
|
var toOperationId = (method, paths) => {
|
|
13
7
|
let operationId = method.toLowerCase();
|
|
14
8
|
if (!paths || paths === "/") return operationId + "Index";
|
|
@@ -142,7 +136,7 @@ var normalizeSchemaReference = (schema) => {
|
|
|
142
136
|
if (typeof schema !== "string") return schema;
|
|
143
137
|
return toRef(schema);
|
|
144
138
|
};
|
|
145
|
-
var mergeSchemaProperty = (existing, incoming, vendors) => {
|
|
139
|
+
var mergeSchemaProperty = (existing, incoming, vendors, openapiVersion = "3.1.2") => {
|
|
146
140
|
if (!existing) return incoming;
|
|
147
141
|
if (!incoming) return existing;
|
|
148
142
|
let existingSchema = normalizeSchemaReference(existing);
|
|
@@ -150,9 +144,19 @@ var mergeSchemaProperty = (existing, incoming, vendors) => {
|
|
|
150
144
|
if (!existingSchema) return incoming;
|
|
151
145
|
if (!incomingSchema) return existing;
|
|
152
146
|
if (!isTSchema(incomingSchema) && incomingSchema["~standard"])
|
|
153
|
-
incomingSchema = unwrapSchema(
|
|
147
|
+
incomingSchema = unwrapSchema(
|
|
148
|
+
incomingSchema,
|
|
149
|
+
vendors,
|
|
150
|
+
"input",
|
|
151
|
+
openapiVersion
|
|
152
|
+
);
|
|
154
153
|
if (!isTSchema(existingSchema) && existingSchema["~standard"])
|
|
155
|
-
existingSchema = unwrapSchema(
|
|
154
|
+
existingSchema = unwrapSchema(
|
|
155
|
+
existingSchema,
|
|
156
|
+
vendors,
|
|
157
|
+
"input",
|
|
158
|
+
openapiVersion
|
|
159
|
+
);
|
|
156
160
|
if (!incomingSchema) return existingSchema;
|
|
157
161
|
if (!existingSchema) return incomingSchema;
|
|
158
162
|
const { schema: mergedSchema, notObjects } = mergeObjectSchemas([
|
|
@@ -165,24 +169,30 @@ var mergeSchemaProperty = (existing, incoming, vendors) => {
|
|
|
165
169
|
}
|
|
166
170
|
return mergedSchema;
|
|
167
171
|
};
|
|
168
|
-
var unwrapResponseSchema = (schema, vendors) => typeof schema === "string" ? normalizeSchemaReference(schema) : !schema ? void 0 : isTSchema(schema) ? schema : (
|
|
172
|
+
var unwrapResponseSchema = (schema, vendors, openapiVersion = "3.1.2") => typeof schema === "string" ? normalizeSchemaReference(schema) : !schema ? void 0 : isTSchema(schema) ? schema : (
|
|
169
173
|
// @ts-ignore
|
|
170
|
-
schema["~standard"] ? unwrapSchema(
|
|
174
|
+
schema["~standard"] ? unwrapSchema(
|
|
175
|
+
schema,
|
|
176
|
+
vendors,
|
|
177
|
+
"output",
|
|
178
|
+
openapiVersion
|
|
179
|
+
) : Object.fromEntries(
|
|
171
180
|
Object.entries(schema).map(([status, schema2]) => [
|
|
172
181
|
status,
|
|
173
182
|
typeof schema2 === "string" ? normalizeSchemaReference(schema2) : isTSchema(schema2) ? schema2 : unwrapSchema(
|
|
174
183
|
schema2,
|
|
175
184
|
vendors,
|
|
176
|
-
"output"
|
|
185
|
+
"output",
|
|
186
|
+
openapiVersion
|
|
177
187
|
)
|
|
178
188
|
])
|
|
179
189
|
)
|
|
180
190
|
);
|
|
181
|
-
var mergeResponseSchema = (_existing, _incoming, vendors) => {
|
|
191
|
+
var mergeResponseSchema = (_existing, _incoming, vendors, openapiVersion = "3.1.2") => {
|
|
182
192
|
if (!_existing) return _incoming;
|
|
183
193
|
if (!_incoming) return _existing;
|
|
184
|
-
let existing = unwrapResponseSchema(_existing, vendors);
|
|
185
|
-
let incoming = unwrapResponseSchema(_incoming, vendors);
|
|
194
|
+
let existing = unwrapResponseSchema(_existing, vendors, openapiVersion);
|
|
195
|
+
let incoming = unwrapResponseSchema(_incoming, vendors, openapiVersion);
|
|
186
196
|
if (!existing && !incoming) return void 0;
|
|
187
197
|
if (incoming && !existing) return incoming;
|
|
188
198
|
if (existing && !incoming) return existing;
|
|
@@ -204,14 +214,15 @@ var mergeResponseSchema = (_existing, _incoming, vendors) => {
|
|
|
204
214
|
schema[status] = mergeSchemaProperty(
|
|
205
215
|
existingSchema,
|
|
206
216
|
incomingSchema,
|
|
207
|
-
vendors
|
|
217
|
+
vendors,
|
|
218
|
+
openapiVersion
|
|
208
219
|
);
|
|
209
220
|
else if (existingSchema) schema[status] = existingSchema;
|
|
210
221
|
else if (incomingSchema) schema[status] = incomingSchema;
|
|
211
222
|
}
|
|
212
223
|
return schema;
|
|
213
224
|
};
|
|
214
|
-
var mergeStandaloneValidators = (hooks, vendors) => {
|
|
225
|
+
var mergeStandaloneValidators = (hooks, vendors, openapiVersion = "3.1.2") => {
|
|
215
226
|
const merged = { ...hooks };
|
|
216
227
|
if (!hooks.standaloneValidator?.length) return merged;
|
|
217
228
|
for (const validator of hooks.standaloneValidator) {
|
|
@@ -219,37 +230,43 @@ var mergeStandaloneValidators = (hooks, vendors) => {
|
|
|
219
230
|
merged.body = mergeSchemaProperty(
|
|
220
231
|
merged.body,
|
|
221
232
|
validator.body,
|
|
222
|
-
vendors
|
|
233
|
+
vendors,
|
|
234
|
+
openapiVersion
|
|
223
235
|
);
|
|
224
236
|
if (validator.headers)
|
|
225
237
|
merged.headers = mergeSchemaProperty(
|
|
226
238
|
merged.headers,
|
|
227
239
|
validator.headers,
|
|
228
|
-
vendors
|
|
240
|
+
vendors,
|
|
241
|
+
openapiVersion
|
|
229
242
|
);
|
|
230
243
|
if (validator.query)
|
|
231
244
|
merged.query = mergeSchemaProperty(
|
|
232
245
|
merged.query,
|
|
233
246
|
validator.query,
|
|
234
|
-
vendors
|
|
247
|
+
vendors,
|
|
248
|
+
openapiVersion
|
|
235
249
|
);
|
|
236
250
|
if (validator.params)
|
|
237
251
|
merged.params = mergeSchemaProperty(
|
|
238
252
|
merged.params,
|
|
239
253
|
validator.params,
|
|
240
|
-
vendors
|
|
254
|
+
vendors,
|
|
255
|
+
openapiVersion
|
|
241
256
|
);
|
|
242
257
|
if (validator.cookie)
|
|
243
258
|
merged.cookie = mergeSchemaProperty(
|
|
244
259
|
merged.cookie,
|
|
245
260
|
validator.cookie,
|
|
246
|
-
vendors
|
|
261
|
+
vendors,
|
|
262
|
+
openapiVersion
|
|
247
263
|
);
|
|
248
264
|
if (validator.response)
|
|
249
265
|
merged.response = mergeResponseSchema(
|
|
250
266
|
merged.response,
|
|
251
267
|
validator.response,
|
|
252
|
-
vendors
|
|
268
|
+
vendors,
|
|
269
|
+
openapiVersion
|
|
253
270
|
);
|
|
254
271
|
}
|
|
255
272
|
if (typeof merged.body === "string")
|
|
@@ -275,36 +292,51 @@ var mergeStandaloneValidators = (hooks, vendors) => {
|
|
|
275
292
|
}
|
|
276
293
|
return merged;
|
|
277
294
|
};
|
|
278
|
-
var flattenRoutes = (routes, vendors) => routes.map((route) => {
|
|
295
|
+
var flattenRoutes = (routes, vendors, openapiVersion = "3.1.2") => routes.map((route) => {
|
|
279
296
|
if (!route.hooks?.standaloneValidator?.length) return route;
|
|
280
297
|
return {
|
|
281
298
|
...route,
|
|
282
|
-
hooks: mergeStandaloneValidators(
|
|
299
|
+
hooks: mergeStandaloneValidators(
|
|
300
|
+
route.hooks,
|
|
301
|
+
vendors,
|
|
302
|
+
openapiVersion
|
|
303
|
+
)
|
|
283
304
|
};
|
|
284
305
|
});
|
|
285
|
-
var unwrapReference = (schema, definitions) => {
|
|
306
|
+
var unwrapReference = (schema, definitions, openapiVersion = "3.1.2") => {
|
|
286
307
|
const ref = schema?.$ref;
|
|
287
308
|
if (!ref) return schema;
|
|
288
309
|
const name = ref.slice(ref.lastIndexOf("/") + 1);
|
|
289
310
|
if (ref && definitions[name]) schema = definitions[name];
|
|
290
|
-
return enumToOpenApi(schema);
|
|
311
|
+
return nullToOpenApi(enumToOpenApi(schema), openapiVersion);
|
|
291
312
|
};
|
|
292
|
-
var unwrapSchema = (schema, mapJsonSchema, io = "input") => {
|
|
313
|
+
var unwrapSchema = (schema, mapJsonSchema, io = "input", openapiVersion = "3.1.2") => {
|
|
293
314
|
if (!schema) return;
|
|
294
315
|
if (typeof schema === "string") schema = toRef(schema);
|
|
295
|
-
if (Kind in schema)
|
|
316
|
+
if (Kind in schema)
|
|
317
|
+
return nullToOpenApi(enumToOpenApi(schema), openapiVersion);
|
|
296
318
|
if (!schema?.["~standard"] && // @ts-ignore
|
|
297
319
|
(schema.$schema || schema.type || schema.properties || schema.items))
|
|
298
|
-
return schema;
|
|
320
|
+
return nullToOpenApi(schema, openapiVersion);
|
|
299
321
|
if (!schema?.["~standard"]) return;
|
|
300
|
-
const
|
|
322
|
+
const standard = schema["~standard"];
|
|
323
|
+
const vendor = standard.vendor;
|
|
301
324
|
try {
|
|
325
|
+
const jsonSchemaTarget = openapiVersion.startsWith("3.0.") ? "draft-07" : "draft-2020-12";
|
|
302
326
|
if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
|
|
303
|
-
return
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
327
|
+
return nullToOpenApi(
|
|
328
|
+
enumToOpenApi(mapJsonSchema[vendor](schema)),
|
|
329
|
+
openapiVersion
|
|
330
|
+
);
|
|
331
|
+
if (standard.jsonSchema?.[io])
|
|
332
|
+
return nullToOpenApi(
|
|
333
|
+
enumToOpenApi(
|
|
334
|
+
standard.jsonSchema[io]({
|
|
335
|
+
target: jsonSchemaTarget
|
|
336
|
+
})
|
|
337
|
+
),
|
|
338
|
+
openapiVersion
|
|
339
|
+
);
|
|
308
340
|
switch (vendor) {
|
|
309
341
|
case "zod":
|
|
310
342
|
if (warned.zod4 || warned.zod3) break;
|
|
@@ -349,10 +381,16 @@ var unwrapSchema = (schema, mapJsonSchema, io = "input") => {
|
|
|
349
381
|
break;
|
|
350
382
|
}
|
|
351
383
|
if (vendor === "arktype")
|
|
352
|
-
return
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
384
|
+
return nullToOpenApi(
|
|
385
|
+
enumToOpenApi(schema.toJsonSchema?.()),
|
|
386
|
+
openapiVersion
|
|
387
|
+
);
|
|
388
|
+
return nullToOpenApi(
|
|
389
|
+
enumToOpenApi(
|
|
390
|
+
// @ts-ignore
|
|
391
|
+
schema.toJSONSchema?.() ?? schema?.toJsonSchema?.()
|
|
392
|
+
),
|
|
393
|
+
openapiVersion
|
|
356
394
|
);
|
|
357
395
|
} catch (error) {
|
|
358
396
|
console.warn(error);
|
|
@@ -385,9 +423,154 @@ var enumToOpenApi = (_schema) => {
|
|
|
385
423
|
...schema,
|
|
386
424
|
items: enumToOpenApi(schema.items)
|
|
387
425
|
};
|
|
426
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
427
|
+
const mapped = schema.anyOf.map(
|
|
428
|
+
(item) => item && typeof item === "object" && item.type === "Date" ? { type: "string", format: "date-time" } : enumToOpenApi(item)
|
|
429
|
+
);
|
|
430
|
+
const seen = /* @__PURE__ */ new Set();
|
|
431
|
+
const deduped = mapped.filter((item) => {
|
|
432
|
+
if (!item || typeof item !== "object") return true;
|
|
433
|
+
const key = JSON.stringify(
|
|
434
|
+
Object.fromEntries(
|
|
435
|
+
Object.entries(item).sort(
|
|
436
|
+
([a], [b]) => a < b ? -1 : a > b ? 1 : 0
|
|
437
|
+
)
|
|
438
|
+
)
|
|
439
|
+
);
|
|
440
|
+
if (seen.has(key)) return false;
|
|
441
|
+
seen.add(key);
|
|
442
|
+
return true;
|
|
443
|
+
});
|
|
444
|
+
if (deduped.length === 1) return deduped[0];
|
|
445
|
+
return { ...schema, anyOf: deduped };
|
|
446
|
+
}
|
|
388
447
|
return schema;
|
|
389
448
|
};
|
|
390
|
-
|
|
449
|
+
var SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
450
|
+
"properties",
|
|
451
|
+
"patternProperties",
|
|
452
|
+
"$defs",
|
|
453
|
+
"definitions",
|
|
454
|
+
"dependentSchemas"
|
|
455
|
+
]);
|
|
456
|
+
var SCHEMA_ARRAY_KEYS = /* @__PURE__ */ new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
|
|
457
|
+
var SCHEMA_VALUE_KEYS = /* @__PURE__ */ new Set([
|
|
458
|
+
"items",
|
|
459
|
+
"additionalProperties",
|
|
460
|
+
"unevaluatedProperties",
|
|
461
|
+
"contains",
|
|
462
|
+
"not",
|
|
463
|
+
"if",
|
|
464
|
+
"then",
|
|
465
|
+
"else",
|
|
466
|
+
"propertyNames"
|
|
467
|
+
]);
|
|
468
|
+
var isSchemaObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
469
|
+
var nullToOpenApi = (schema, openapiVersion) => {
|
|
470
|
+
const normalize = (value) => {
|
|
471
|
+
if (Array.isArray(value)) return value.map(normalize);
|
|
472
|
+
if (!isSchemaObject(value)) return value;
|
|
473
|
+
const normalized = { ...value };
|
|
474
|
+
const isOpenAPI30 = openapiVersion.startsWith("3.0.");
|
|
475
|
+
for (const unionKey of ["anyOf", "oneOf"]) {
|
|
476
|
+
const union = normalized[unionKey];
|
|
477
|
+
if (!Array.isArray(union)) continue;
|
|
478
|
+
const nonNull = union.filter(
|
|
479
|
+
(item) => !isSchemaObject(item) || item.type !== "null"
|
|
480
|
+
);
|
|
481
|
+
if (nonNull.length === union.length) continue;
|
|
482
|
+
const normalizedNonNull = nonNull.map(normalize);
|
|
483
|
+
if (isOpenAPI30) {
|
|
484
|
+
delete normalized[unionKey];
|
|
485
|
+
if (normalizedNonNull.length === 1 && isSchemaObject(normalizedNonNull[0]))
|
|
486
|
+
Object.assign(normalized, normalizedNonNull[0]);
|
|
487
|
+
else if (normalizedNonNull.length)
|
|
488
|
+
normalized[unionKey] = normalizedNonNull;
|
|
489
|
+
normalized.nullable = true;
|
|
490
|
+
} else if (normalizedNonNull.length === 1 && isSchemaObject(normalizedNonNull[0]) && typeof normalizedNonNull[0].type === "string") {
|
|
491
|
+
delete normalized[unionKey];
|
|
492
|
+
Object.assign(normalized, normalizedNonNull[0]);
|
|
493
|
+
normalized.type = [normalizedNonNull[0].type, "null"];
|
|
494
|
+
} else normalized[unionKey] = union.map(normalize);
|
|
495
|
+
}
|
|
496
|
+
if (isOpenAPI30 && normalized.type === "null") {
|
|
497
|
+
delete normalized.type;
|
|
498
|
+
normalized.nullable = true;
|
|
499
|
+
} else if (isOpenAPI30 && Array.isArray(normalized.type) && normalized.type.includes("null")) {
|
|
500
|
+
const types = normalized.type.filter((type) => type !== "null");
|
|
501
|
+
normalized.nullable = true;
|
|
502
|
+
if (types.length === 1) normalized.type = types[0];
|
|
503
|
+
else if (types.length) normalized.type = types;
|
|
504
|
+
else delete normalized.type;
|
|
505
|
+
}
|
|
506
|
+
for (const [key, nested] of Object.entries(normalized)) {
|
|
507
|
+
if (SCHEMA_MAP_KEYS.has(key) && isSchemaObject(nested))
|
|
508
|
+
normalized[key] = Object.fromEntries(
|
|
509
|
+
Object.entries(nested).map(([name, child]) => [
|
|
510
|
+
name,
|
|
511
|
+
normalize(child)
|
|
512
|
+
])
|
|
513
|
+
);
|
|
514
|
+
else if (SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(nested))
|
|
515
|
+
normalized[key] = nested.map(normalize);
|
|
516
|
+
else if (SCHEMA_VALUE_KEYS.has(key) && isSchemaObject(nested))
|
|
517
|
+
normalized[key] = normalize(nested);
|
|
518
|
+
else if (key === "dependencies" && isSchemaObject(nested))
|
|
519
|
+
normalized[key] = Object.fromEntries(
|
|
520
|
+
Object.entries(nested).map(([name, child]) => [
|
|
521
|
+
name,
|
|
522
|
+
isSchemaObject(child) ? normalize(child) : child
|
|
523
|
+
])
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
return normalized;
|
|
527
|
+
};
|
|
528
|
+
return normalize(schema);
|
|
529
|
+
};
|
|
530
|
+
var toResponseHeaders = (schema, vendors, openapiVersion = "3.1.2") => {
|
|
531
|
+
if (!schema || typeof schema === "string" || !("headers" in schema) || !schema.headers)
|
|
532
|
+
return;
|
|
533
|
+
const entries = Object.entries(
|
|
534
|
+
schema.headers
|
|
535
|
+
).map(
|
|
536
|
+
([name, hs]) => [
|
|
537
|
+
name,
|
|
538
|
+
{
|
|
539
|
+
schema: unwrapSchema(
|
|
540
|
+
hs,
|
|
541
|
+
vendors,
|
|
542
|
+
"output",
|
|
543
|
+
openapiVersion
|
|
544
|
+
)
|
|
545
|
+
}
|
|
546
|
+
]
|
|
547
|
+
).filter(([, v]) => v.schema);
|
|
548
|
+
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
549
|
+
};
|
|
550
|
+
var stripHeaders = (schema) => {
|
|
551
|
+
const { headers, ...rest } = schema;
|
|
552
|
+
return rest;
|
|
553
|
+
};
|
|
554
|
+
var VOID_TYPES = /* @__PURE__ */ new Set(["void", "null", "undefined"]);
|
|
555
|
+
var PLAIN_TYPES = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean"]);
|
|
556
|
+
var toResponseContent = (schema, type, description) => VOID_TYPES.has(type) ? { type, description } : PLAIN_TYPES.has(type) ? { "text/plain": { schema } } : { "application/json": { schema } };
|
|
557
|
+
var toResponseObject = (schema, status, definitions, vendors, openapiVersion = "3.1.2") => {
|
|
558
|
+
const response = unwrapSchema(schema, vendors, "output", openapiVersion);
|
|
559
|
+
if (!response) return;
|
|
560
|
+
const responseSchema = stripHeaders(response);
|
|
561
|
+
const { type, description } = unwrapReference(
|
|
562
|
+
responseSchema,
|
|
563
|
+
definitions,
|
|
564
|
+
openapiVersion
|
|
565
|
+
);
|
|
566
|
+
const headers = toResponseHeaders(schema, vendors, openapiVersion);
|
|
567
|
+
return {
|
|
568
|
+
description: description ?? `Response for status ${status}`,
|
|
569
|
+
...headers ? { headers } : {},
|
|
570
|
+
content: toResponseContent(responseSchema, type, description)
|
|
571
|
+
};
|
|
572
|
+
};
|
|
573
|
+
function toOpenAPISchema(app, exclude, references, vendors, openapiVersion = "3.1.2") {
|
|
391
574
|
let {
|
|
392
575
|
methods: excludeMethods = ["options"],
|
|
393
576
|
staticFile: excludeStaticFile = true,
|
|
@@ -404,11 +587,19 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
404
587
|
if (typeof reference === "function") references[i] = reference();
|
|
405
588
|
}
|
|
406
589
|
}
|
|
407
|
-
const routes = flattenRoutes(app.getGlobalRoutes(), vendors);
|
|
590
|
+
const routes = flattenRoutes(app.getGlobalRoutes(), vendors, openapiVersion);
|
|
408
591
|
for (const route of routes) {
|
|
409
592
|
if (route.hooks?.detail?.hide) continue;
|
|
410
593
|
const method = route.method.toLowerCase();
|
|
411
|
-
if (excludeStaticFile && route.path.includes(".") || excludePaths.
|
|
594
|
+
if (excludeStaticFile && route.path.includes(".") || excludePaths.some((exclusion) => {
|
|
595
|
+
if (exclusion instanceof RegExp) {
|
|
596
|
+
exclusion.lastIndex = 0;
|
|
597
|
+
return exclusion.test(route.path);
|
|
598
|
+
}
|
|
599
|
+
if (typeof exclusion === "string")
|
|
600
|
+
return exclusion === route.path;
|
|
601
|
+
return false;
|
|
602
|
+
}) || excludeMethods.includes(method))
|
|
412
603
|
continue;
|
|
413
604
|
const hooks = route.hooks ?? {};
|
|
414
605
|
if (references?.length)
|
|
@@ -454,8 +645,9 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
454
645
|
const parameters = [];
|
|
455
646
|
if (hooks.params) {
|
|
456
647
|
const params = unwrapReference(
|
|
457
|
-
unwrapSchema(hooks.params, vendors),
|
|
458
|
-
definitions
|
|
648
|
+
unwrapSchema(hooks.params, vendors, "input", openapiVersion),
|
|
649
|
+
definitions,
|
|
650
|
+
openapiVersion
|
|
459
651
|
);
|
|
460
652
|
if (params && params.type === "object" && params.properties)
|
|
461
653
|
for (const [name, schema] of Object.entries(params.properties))
|
|
@@ -479,8 +671,9 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
479
671
|
}
|
|
480
672
|
if (hooks.query) {
|
|
481
673
|
const query = unwrapReference(
|
|
482
|
-
unwrapSchema(hooks.query, vendors),
|
|
483
|
-
definitions
|
|
674
|
+
unwrapSchema(hooks.query, vendors, "input", openapiVersion),
|
|
675
|
+
definitions,
|
|
676
|
+
openapiVersion
|
|
484
677
|
);
|
|
485
678
|
if (query && query.type === "object" && query.properties) {
|
|
486
679
|
const required = query.required || [];
|
|
@@ -495,8 +688,9 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
495
688
|
}
|
|
496
689
|
if (hooks.headers) {
|
|
497
690
|
const headers = unwrapReference(
|
|
498
|
-
unwrapSchema(hooks.headers, vendors),
|
|
499
|
-
definitions
|
|
691
|
+
unwrapSchema(hooks.headers, vendors, "input", openapiVersion),
|
|
692
|
+
definitions,
|
|
693
|
+
openapiVersion
|
|
500
694
|
);
|
|
501
695
|
if (headers && headers.type === "object" && headers.properties) {
|
|
502
696
|
const required = headers.required || [];
|
|
@@ -511,8 +705,9 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
511
705
|
}
|
|
512
706
|
if (hooks.cookie) {
|
|
513
707
|
const cookie = unwrapReference(
|
|
514
|
-
unwrapSchema(hooks.cookie, vendors),
|
|
515
|
-
definitions
|
|
708
|
+
unwrapSchema(hooks.cookie, vendors, "input", openapiVersion),
|
|
709
|
+
definitions,
|
|
710
|
+
openapiVersion
|
|
516
711
|
);
|
|
517
712
|
if (cookie && cookie.type === "object" && cookie.properties) {
|
|
518
713
|
const required = cookie.required || [];
|
|
@@ -527,11 +722,17 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
527
722
|
}
|
|
528
723
|
if (parameters.length > 0) operation.parameters = parameters;
|
|
529
724
|
if (hooks.body && method !== "get" && method !== "head") {
|
|
530
|
-
const body = unwrapSchema(
|
|
725
|
+
const body = unwrapSchema(
|
|
726
|
+
hooks.body,
|
|
727
|
+
vendors,
|
|
728
|
+
"input",
|
|
729
|
+
openapiVersion
|
|
730
|
+
);
|
|
531
731
|
if (body) {
|
|
532
732
|
const { type, description, $ref, ...options } = unwrapReference(
|
|
533
733
|
body,
|
|
534
|
-
definitions
|
|
734
|
+
definitions,
|
|
735
|
+
openapiVersion
|
|
535
736
|
);
|
|
536
737
|
if (hooks.parse) {
|
|
537
738
|
const content = {};
|
|
@@ -559,6 +760,12 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
559
760
|
schema: body
|
|
560
761
|
};
|
|
561
762
|
continue;
|
|
763
|
+
case "arrayBuffer":
|
|
764
|
+
case "application/octet-stream":
|
|
765
|
+
content["application/octet-stream"] = {
|
|
766
|
+
schema: body
|
|
767
|
+
};
|
|
768
|
+
continue;
|
|
562
769
|
}
|
|
563
770
|
}
|
|
564
771
|
operation.requestBody = {
|
|
@@ -592,50 +799,26 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
592
799
|
if (hooks.response) {
|
|
593
800
|
operation.responses = {};
|
|
594
801
|
if (typeof hooks.response === "object" && // TypeBox
|
|
595
|
-
!hooks.response.type && !hooks.response.$ref && !hooks.response["~standard"]) {
|
|
802
|
+
!(Kind in hooks.response) && !hooks.response.type && !hooks.response.$ref && !hooks.response["~standard"]) {
|
|
596
803
|
for (let [status, schema] of Object.entries(hooks.response)) {
|
|
597
|
-
const response =
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
}
|
|
606
|
-
} : {
|
|
607
|
-
"application/json": {
|
|
608
|
-
schema: response
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
};
|
|
804
|
+
const response = toResponseObject(
|
|
805
|
+
schema,
|
|
806
|
+
status,
|
|
807
|
+
definitions,
|
|
808
|
+
vendors,
|
|
809
|
+
openapiVersion
|
|
810
|
+
);
|
|
811
|
+
if (response) operation.responses[status] = response;
|
|
612
812
|
}
|
|
613
813
|
} else {
|
|
614
|
-
const response =
|
|
814
|
+
const response = toResponseObject(
|
|
615
815
|
hooks.response,
|
|
816
|
+
"200",
|
|
817
|
+
definitions,
|
|
616
818
|
vendors,
|
|
617
|
-
|
|
819
|
+
openapiVersion
|
|
618
820
|
);
|
|
619
|
-
if (response)
|
|
620
|
-
const {
|
|
621
|
-
type: _type,
|
|
622
|
-
description,
|
|
623
|
-
...options
|
|
624
|
-
} = unwrapReference(response, definitions);
|
|
625
|
-
const type = _type;
|
|
626
|
-
operation.responses["200"] = {
|
|
627
|
-
description: description ?? `Response for status 200`,
|
|
628
|
-
content: type === "void" || type === "null" || type === "undefined" ? { type, description } : type === "string" || type === "number" || type === "integer" || type === "boolean" ? {
|
|
629
|
-
"text/plain": {
|
|
630
|
-
schema: response
|
|
631
|
-
}
|
|
632
|
-
} : {
|
|
633
|
-
"application/json": {
|
|
634
|
-
schema: response
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
};
|
|
638
|
-
}
|
|
821
|
+
if (response) operation.responses["200"] = response;
|
|
639
822
|
}
|
|
640
823
|
}
|
|
641
824
|
for (let path of getPossiblePath(route.path)) {
|
|
@@ -669,7 +852,12 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
669
852
|
const schemas = /* @__PURE__ */ Object.create(null);
|
|
670
853
|
if (definitions)
|
|
671
854
|
for (const [name, schema] of Object.entries(definitions)) {
|
|
672
|
-
const jsonSchema = unwrapSchema(
|
|
855
|
+
const jsonSchema = unwrapSchema(
|
|
856
|
+
schema,
|
|
857
|
+
vendors,
|
|
858
|
+
"input",
|
|
859
|
+
openapiVersion
|
|
860
|
+
);
|
|
673
861
|
if (jsonSchema) schemas[name] = jsonSchema;
|
|
674
862
|
}
|
|
675
863
|
return {
|
|
@@ -679,14 +867,25 @@ function toOpenAPISchema(app, exclude, references, vendors) {
|
|
|
679
867
|
paths
|
|
680
868
|
};
|
|
681
869
|
}
|
|
682
|
-
var withHeaders = (schema, headers) =>
|
|
683
|
-
|
|
684
|
-
|
|
870
|
+
var withHeaders = (schema, headers) => {
|
|
871
|
+
const clone = Object.create(
|
|
872
|
+
Object.getPrototypeOf(schema),
|
|
873
|
+
Object.getOwnPropertyDescriptors(schema)
|
|
874
|
+
);
|
|
875
|
+
Object.defineProperty(clone, "headers", {
|
|
876
|
+
value: headers,
|
|
877
|
+
enumerable: true,
|
|
878
|
+
configurable: true,
|
|
879
|
+
writable: true
|
|
880
|
+
});
|
|
881
|
+
return clone;
|
|
882
|
+
};
|
|
685
883
|
export {
|
|
686
884
|
capitalize,
|
|
687
885
|
enumToOpenApi,
|
|
688
886
|
getLoosePath,
|
|
689
887
|
getPossiblePath,
|
|
888
|
+
nullToOpenApi,
|
|
690
889
|
toOpenAPISchema,
|
|
691
890
|
unwrapSchema,
|
|
692
891
|
withHeaders
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { TSchema } from 'elysia';
|
|
2
|
-
import type { OpenAPIV3 } from 'openapi-types';
|
|
2
|
+
import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
|
|
3
3
|
import type { ApiReferenceConfiguration } from '@scalar/types';
|
|
4
4
|
import type { SwaggerUIOptions } from './swagger/types';
|
|
5
5
|
export type OpenAPIProvider = 'scalar' | 'swagger-ui' | null;
|
|
6
|
+
export type OpenAPIVersion = `3.0.${number}` | `3.1.${number}`;
|
|
6
7
|
type MaybeArray<T> = T | T[];
|
|
8
|
+
type OpenAPIDocumentation = Omit<Partial<OpenAPIV3.Document>, 'x-express-openapi-additional-middleware' | 'x-express-openapi-validation-strict'> | Omit<Partial<OpenAPIV3_1.Document>, 'x-express-openapi-additional-middleware' | 'x-express-openapi-validation-strict'>;
|
|
7
9
|
export type MapJsonSchema = {
|
|
8
10
|
[vendor: string]: Function;
|
|
9
11
|
} & {
|
|
@@ -28,12 +30,18 @@ export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extend
|
|
|
28
30
|
* @default true
|
|
29
31
|
*/
|
|
30
32
|
enabled?: Enabled;
|
|
33
|
+
/**
|
|
34
|
+
* OpenAPI document version to emit
|
|
35
|
+
*
|
|
36
|
+
* @default '3.1.2'
|
|
37
|
+
*/
|
|
38
|
+
openapiVersion?: OpenAPIVersion;
|
|
31
39
|
/**
|
|
32
40
|
* OpenAPI config
|
|
33
41
|
*
|
|
34
|
-
* @see https://spec.openapis.org/oas/
|
|
42
|
+
* @see https://spec.openapis.org/oas/latest.html
|
|
35
43
|
*/
|
|
36
|
-
documentation?:
|
|
44
|
+
documentation?: OpenAPIDocumentation;
|
|
37
45
|
exclude?: {
|
|
38
46
|
/**
|
|
39
47
|
* Exclude methods from OpenAPI
|