@elysiajs/openapi 1.4.11 → 1.4.13

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.
@@ -10,7 +10,12 @@ 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) => OpenAPIV3.SchemaObject | undefined;
13
+ export declare const unwrapSchema: (schema: InputSchema["body"], mapJsonSchema?: MapJsonSchema, io?: "input" | "output") => OpenAPIV3.SchemaObject | undefined;
14
+ /**
15
+ * Convert TypeBox enum-like Union schemas to OpenAPI enum schemas
16
+ *
17
+ * Otherwise, return the schema as is
18
+ */
14
19
  export declare const enumToOpenApi: <T extends TAnySchema | OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined>(_schema: T) => T;
15
20
  /**
16
21
  * Converts Elysia routes to OpenAPI 3.0.3 paths schema
@@ -32,15 +32,13 @@ module.exports = __toCommonJS(openapi_exports);
32
32
  var import_elysia = require("elysia");
33
33
 
34
34
  // node_modules/@sinclair/typebox/build/esm/type/symbols/symbols.mjs
35
- var TransformKind = Symbol.for("TypeBox.Transform");
36
- var ReadonlyKind = Symbol.for("TypeBox.Readonly");
37
- var OptionalKind = Symbol.for("TypeBox.Optional");
38
- var Hint = Symbol.for("TypeBox.Hint");
39
- var Kind = Symbol.for("TypeBox.Kind");
35
+ var Kind = /* @__PURE__ */ Symbol.for("TypeBox.Kind");
40
36
 
41
37
  // src/openapi.ts
42
38
  var capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
43
- var toRef = (name) => import_elysia.t.Ref(`#/components/schemas/${name}`);
39
+ var toRef = (name) => import_elysia.t.Ref(
40
+ name.startsWith("#/") ? name : `#/components/schemas/${name}`
41
+ );
44
42
  var toOperationId = (method, paths) => {
45
43
  let operationId = method.toLowerCase();
46
44
  if (!paths || paths === "/") return operationId + "Index";
@@ -101,6 +99,219 @@ openapi({
101
99
  })`
102
100
  };
103
101
  var warned = {};
102
+ var mergeObjectSchemas = (schemas) => {
103
+ if (schemas.length === 0)
104
+ return {
105
+ schema: void 0,
106
+ notObjects: []
107
+ };
108
+ if (schemas.length === 1)
109
+ return schemas[0].type === "object" ? {
110
+ schema: schemas[0],
111
+ notObjects: []
112
+ } : {
113
+ schema: void 0,
114
+ notObjects: schemas
115
+ };
116
+ let newSchema;
117
+ const notObjects = [];
118
+ let additionalPropertiesIsTrue = false;
119
+ let additionalPropertiesIsFalse = false;
120
+ for (const schema of schemas) {
121
+ if (!schema) continue;
122
+ if (schema.type !== "object") {
123
+ notObjects.push(schema);
124
+ continue;
125
+ }
126
+ if ("additionalProperties" in schema) {
127
+ if (schema.additionalProperties === true)
128
+ additionalPropertiesIsTrue = true;
129
+ else if (schema.additionalProperties === false)
130
+ additionalPropertiesIsFalse = true;
131
+ }
132
+ if (!newSchema) {
133
+ newSchema = schema;
134
+ continue;
135
+ }
136
+ newSchema = {
137
+ ...newSchema,
138
+ ...schema,
139
+ properties: {
140
+ ...newSchema.properties,
141
+ ...schema.properties
142
+ },
143
+ required: [
144
+ ...newSchema?.required ?? [],
145
+ ...schema.required ?? []
146
+ ]
147
+ };
148
+ }
149
+ if (newSchema) {
150
+ if (newSchema.required)
151
+ newSchema.required = [...new Set(newSchema.required)];
152
+ if (additionalPropertiesIsFalse) newSchema.additionalProperties = false;
153
+ else if (additionalPropertiesIsTrue)
154
+ newSchema.additionalProperties = true;
155
+ }
156
+ return {
157
+ schema: newSchema,
158
+ notObjects
159
+ };
160
+ };
161
+ var isTSchema = (value) => {
162
+ if (!value || typeof value !== "object") return false;
163
+ if (Kind in value) return true;
164
+ const keys = Object.keys(value);
165
+ if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) {
166
+ return false;
167
+ }
168
+ return false;
169
+ };
170
+ var normalizeSchemaReference = (schema) => {
171
+ if (!schema) return void 0;
172
+ if (typeof schema !== "string") return schema;
173
+ return toRef(schema);
174
+ };
175
+ var mergeSchemaProperty = (existing, incoming, vendors) => {
176
+ if (!existing) return incoming;
177
+ if (!incoming) return existing;
178
+ let existingSchema = normalizeSchemaReference(existing);
179
+ let incomingSchema = normalizeSchemaReference(incoming);
180
+ if (!existingSchema) return incoming;
181
+ if (!incomingSchema) return existing;
182
+ if (!isTSchema(incomingSchema) && incomingSchema["~standard"])
183
+ incomingSchema = unwrapSchema(incomingSchema, vendors);
184
+ if (!isTSchema(existingSchema) && existingSchema["~standard"])
185
+ existingSchema = unwrapSchema(existingSchema, vendors);
186
+ if (!incomingSchema) return existingSchema;
187
+ if (!existingSchema) return incomingSchema;
188
+ const { schema: mergedSchema, notObjects } = mergeObjectSchemas([
189
+ existingSchema,
190
+ incomingSchema
191
+ ]);
192
+ if (notObjects.length > 0) {
193
+ if (mergedSchema) return import_elysia.t.Intersect([mergedSchema, ...notObjects]);
194
+ return notObjects.length === 1 ? notObjects[0] : import_elysia.t.Intersect(notObjects);
195
+ }
196
+ return mergedSchema;
197
+ };
198
+ var unwrapResponseSchema = (schema, vendors) => typeof schema === "string" ? normalizeSchemaReference(schema) : !schema ? void 0 : isTSchema(schema) ? schema : (
199
+ // @ts-ignore
200
+ schema["~standard"] ? unwrapSchema(schema, vendors, "output") : Object.fromEntries(
201
+ Object.entries(schema).map(([status, schema2]) => [
202
+ status,
203
+ typeof schema2 === "string" ? normalizeSchemaReference(schema2) : isTSchema(schema2) ? schema2 : unwrapSchema(
204
+ schema2,
205
+ vendors,
206
+ "output"
207
+ )
208
+ ])
209
+ )
210
+ );
211
+ var mergeResponseSchema = (_existing, _incoming, vendors) => {
212
+ if (!_existing) return _incoming;
213
+ if (!_incoming) return _existing;
214
+ let existing = unwrapResponseSchema(_existing, vendors);
215
+ let incoming = unwrapResponseSchema(_incoming, vendors);
216
+ if (!existing && !incoming) return void 0;
217
+ if (incoming && !existing) return incoming;
218
+ if (existing && !incoming) return existing;
219
+ if (isTSchema(existing) || existing?.["~standard"])
220
+ existing = {
221
+ 200: existing
222
+ };
223
+ if (isTSchema(incoming) || incoming?.["~standard"])
224
+ incoming = {
225
+ 200: incoming
226
+ };
227
+ const schema = {
228
+ ...incoming
229
+ };
230
+ for (const status of Object.keys(existing ?? {})) {
231
+ const existingSchema = existing[status];
232
+ const incomingSchema = incoming[status];
233
+ if (existingSchema && incomingSchema)
234
+ schema[status] = mergeSchemaProperty(
235
+ existingSchema,
236
+ incomingSchema,
237
+ vendors
238
+ );
239
+ else if (existingSchema) schema[status] = existingSchema;
240
+ else if (incomingSchema) schema[status] = incomingSchema;
241
+ }
242
+ return schema;
243
+ };
244
+ var mergeStandaloneValidators = (hooks, vendors) => {
245
+ const merged = { ...hooks };
246
+ if (!hooks.standaloneValidator?.length) return merged;
247
+ for (const validator of hooks.standaloneValidator) {
248
+ if (validator.body)
249
+ merged.body = mergeSchemaProperty(
250
+ merged.body,
251
+ validator.body,
252
+ vendors
253
+ );
254
+ if (validator.headers)
255
+ merged.headers = mergeSchemaProperty(
256
+ merged.headers,
257
+ validator.headers,
258
+ vendors
259
+ );
260
+ if (validator.query)
261
+ merged.query = mergeSchemaProperty(
262
+ merged.query,
263
+ validator.query,
264
+ vendors
265
+ );
266
+ if (validator.params)
267
+ merged.params = mergeSchemaProperty(
268
+ merged.params,
269
+ validator.params,
270
+ vendors
271
+ );
272
+ if (validator.cookie)
273
+ merged.cookie = mergeSchemaProperty(
274
+ merged.cookie,
275
+ validator.cookie,
276
+ vendors
277
+ );
278
+ if (validator.response)
279
+ merged.response = mergeResponseSchema(
280
+ merged.response,
281
+ validator.response,
282
+ vendors
283
+ );
284
+ }
285
+ if (typeof merged.body === "string")
286
+ merged.body = normalizeSchemaReference(merged.body);
287
+ if (typeof merged.headers === "string")
288
+ merged.headers = normalizeSchemaReference(merged.headers);
289
+ if (typeof merged.query === "string")
290
+ merged.query = normalizeSchemaReference(merged.query);
291
+ if (typeof merged.params === "string")
292
+ merged.params = normalizeSchemaReference(merged.params);
293
+ if (typeof merged.cookie === "string")
294
+ merged.cookie = normalizeSchemaReference(merged.cookie);
295
+ if (merged.response && typeof merged.response !== "string") {
296
+ const response = merged.response;
297
+ if ("type" in response || "$ref" in response) {
298
+ if (typeof response === "string")
299
+ merged.response = normalizeSchemaReference(response);
300
+ } else {
301
+ for (const [status, schema] of Object.entries(response))
302
+ if (typeof schema === "string")
303
+ response[status] = normalizeSchemaReference(schema);
304
+ }
305
+ }
306
+ return merged;
307
+ };
308
+ var flattenRoutes = (routes, vendors) => routes.map((route) => {
309
+ if (!route.hooks?.standaloneValidator?.length) return route;
310
+ return {
311
+ ...route,
312
+ hooks: mergeStandaloneValidators(route.hooks, vendors)
313
+ };
314
+ });
104
315
  var unwrapReference = (schema, definitions) => {
105
316
  const ref = schema?.$ref;
106
317
  if (!ref) return schema;
@@ -108,13 +319,20 @@ var unwrapReference = (schema, definitions) => {
108
319
  if (ref && definitions[name]) schema = definitions[name];
109
320
  return enumToOpenApi(schema);
110
321
  };
111
- var unwrapSchema = (schema, mapJsonSchema) => {
322
+ var unwrapSchema = (schema, mapJsonSchema, io = "input") => {
112
323
  if (!schema) return;
113
324
  if (typeof schema === "string") schema = toRef(schema);
114
325
  if (Kind in schema) return enumToOpenApi(schema);
115
- if (Kind in schema || !schema?.["~standard"]) return;
326
+ if (!schema?.["~standard"] && // @ts-ignore
327
+ (schema.$schema || schema.type || schema.properties || schema.items))
328
+ return schema;
329
+ if (!schema?.["~standard"]) return;
116
330
  const vendor = schema["~standard"].vendor;
117
331
  try {
332
+ if (schema["~standard"]?.jsonSchema?.[io])
333
+ return schema["~standard"]?.jsonSchema?.[io]?.({
334
+ target: "draft-2020-12"
335
+ });
118
336
  if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
119
337
  return enumToOpenApi(mapJsonSchema[vendor](schema));
120
338
  switch (vendor) {
@@ -209,7 +427,6 @@ function toOpenAPISchema(app, exclude, references, vendors) {
209
427
  const excludePaths = Array.isArray(exclude?.paths) ? exclude.paths : typeof exclude?.paths !== "undefined" ? [exclude.paths] : [];
210
428
  const paths = /* @__PURE__ */ Object.create(null);
211
429
  const definitions = app.getGlobalDefinitions?.().type;
212
- const routes = app.getGlobalRoutes();
213
430
  if (references) {
214
431
  if (!Array.isArray(references)) references = [references];
215
432
  for (let i = 0; i < references.length; i++) {
@@ -217,6 +434,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
217
434
  if (typeof reference === "function") references[i] = reference();
218
435
  }
219
436
  }
437
+ const routes = flattenRoutes(app.getGlobalRoutes(), vendors);
220
438
  for (const route of routes) {
221
439
  if (route.hooks?.detail?.hide) continue;
222
440
  const method = route.method.toLowerCase();
@@ -406,7 +624,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
406
624
  if (typeof hooks.response === "object" && // TypeBox
407
625
  !hooks.response.type && !hooks.response.$ref && !hooks.response["~standard"]) {
408
626
  for (let [status, schema] of Object.entries(hooks.response)) {
409
- const response = unwrapSchema(schema, vendors);
627
+ const response = unwrapSchema(schema, vendors, "output");
410
628
  if (!response) continue;
411
629
  const { type, description, $ref, ..._options } = unwrapReference(response, definitions);
412
630
  operation.responses[status] = {
@@ -423,7 +641,11 @@ function toOpenAPISchema(app, exclude, references, vendors) {
423
641
  };
424
642
  }
425
643
  } else {
426
- const response = unwrapSchema(hooks.response, vendors);
644
+ const response = unwrapSchema(
645
+ hooks.response,
646
+ vendors,
647
+ "output"
648
+ );
427
649
  if (response) {
428
650
  const {
429
651
  type: _type,