@elysiajs/openapi 1.4.8 → 1.4.10

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/dist/openapi.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type AnyElysia, type TSchema, type InputSchema } from 'elysia';
2
2
  import type { OpenAPIV3 } from 'openapi-types';
3
- import { type TProperties } from '@sinclair/typebox';
3
+ import { TAnySchema, type TProperties } from '@sinclair/typebox';
4
4
  import type { AdditionalReferences, ElysiaOpenAPIConfig, MapJsonSchema } from './types';
5
5
  export declare const capitalize: (word: string) => string;
6
6
  /**
@@ -11,6 +11,7 @@ export declare const capitalize: (word: string) => string;
11
11
  export declare const getPossiblePath: (path: string) => string[];
12
12
  export declare const getLoosePath: (path: string) => string;
13
13
  export declare const unwrapSchema: (schema: InputSchema["body"], mapJsonSchema?: MapJsonSchema) => OpenAPIV3.SchemaObject | undefined;
14
+ export declare const enumToOpenApi: <T extends TAnySchema | OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined>(_schema: T) => T;
14
15
  /**
15
16
  * Converts Elysia routes to OpenAPI 3.0.3 paths schema
16
17
  * @param routes Array of Elysia route objects
package/dist/openapi.mjs CHANGED
@@ -76,62 +76,98 @@ var unwrapReference = (schema, definitions) => {
76
76
  if (!ref) return schema;
77
77
  const name = ref.slice(ref.lastIndexOf("/") + 1);
78
78
  if (ref && definitions[name]) schema = definitions[name];
79
- return schema;
79
+ return enumToOpenApi(schema);
80
80
  };
81
81
  var unwrapSchema = (schema, mapJsonSchema) => {
82
82
  if (!schema) return;
83
83
  if (typeof schema === "string") schema = toRef(schema);
84
- if (Kind in schema) return schema;
84
+ if (Kind in schema) return enumToOpenApi(schema);
85
85
  if (Kind in schema || !schema?.["~standard"]) return;
86
86
  const vendor = schema["~standard"].vendor;
87
- if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
88
- return mapJsonSchema[vendor](schema);
89
- switch (vendor) {
90
- case "zod":
91
- if (warned.zod4 || warned.zod3) break;
92
- console.warn(
93
- "[@elysiajs/openapi] Zod doesn't provide JSON Schema method on the schema"
94
- );
95
- if ("_zod" in schema) {
96
- warned.zod4 = true;
87
+ try {
88
+ if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
89
+ return enumToOpenApi(mapJsonSchema[vendor](schema));
90
+ switch (vendor) {
91
+ case "zod":
92
+ if (warned.zod4 || warned.zod3) break;
97
93
  console.warn(
98
- "For Zod v4, please provide z.toJSONSchema as follows:\n"
94
+ "[@elysiajs/openapi] Zod doesn't provide JSON Schema method on the schema"
99
95
  );
100
- console.warn(warnings.zod4);
101
- } else {
102
- warned.zod3 = true;
96
+ if ("_zod" in schema) {
97
+ warned.zod4 = true;
98
+ console.warn(
99
+ "For Zod v4, please provide z.toJSONSchema as follows:\n"
100
+ );
101
+ console.warn(warnings.zod4);
102
+ } else {
103
+ warned.zod3 = true;
104
+ console.warn(
105
+ "For Zod v3, please install zod-to-json-schema package and use it like this:\n"
106
+ );
107
+ console.warn(warnings.zod3);
108
+ }
109
+ break;
110
+ case "valibot":
111
+ if (warned.valibot) break;
112
+ warned.valibot = true;
103
113
  console.warn(
104
- "For Zod v3, please install zod-to-json-schema package and use it like this:\n"
114
+ "[@elysiajs/openapi] Valibot require a separate package for JSON Schema conversion"
105
115
  );
106
- console.warn(warnings.zod3);
107
- }
108
- break;
109
- case "valibot":
110
- if (warned.valibot) break;
111
- warned.valibot = true;
112
- console.warn(
113
- "[@elysiajs/openapi] Valibot require a separate package for JSON Schema conversion"
114
- );
115
- console.warn(
116
- "Please install @valibot/to-json-schema package and use it like this:\n"
117
- );
118
- console.warn(warnings.valibot);
119
- break;
120
- case "effect":
121
- if (warned.effect) break;
122
- warned.effect = true;
123
- console.warn(
124
- "[@elysiajs/openapi] Effect Schema doesn't provide JSON Schema method on the schema"
125
- );
126
- console.warn(
127
- "please provide JSONSchema from 'effect' package as follows:\n"
128
- );
129
- console.warn(warnings.effect);
130
- break;
116
+ console.warn(
117
+ "Please install @valibot/to-json-schema package and use it like this:\n"
118
+ );
119
+ console.warn(warnings.valibot);
120
+ break;
121
+ case "effect":
122
+ if (warned.effect) break;
123
+ warned.effect = true;
124
+ console.warn(
125
+ "[@elysiajs/openapi] Effect Schema doesn't provide JSON Schema method on the schema"
126
+ );
127
+ console.warn(
128
+ "please provide JSONSchema from 'effect' package as follows:\n"
129
+ );
130
+ console.warn(warnings.effect);
131
+ break;
132
+ }
133
+ if (vendor === "arktype")
134
+ return enumToOpenApi(schema?.toJsonSchema?.());
135
+ return enumToOpenApi(
136
+ // @ts-ignore
137
+ schema.toJSONSchema?.() ?? schema?.toJsonSchema?.()
138
+ );
139
+ } catch (error) {
140
+ console.warn(error);
141
+ }
142
+ };
143
+ var enumToOpenApi = (_schema) => {
144
+ if (!_schema || typeof _schema !== "object") return _schema;
145
+ if (Kind in _schema) {
146
+ const schema2 = _schema;
147
+ if (schema2[Kind] === "Union" && schema2.anyOf && Array.isArray(schema2.anyOf) && schema2.anyOf.length > 0 && schema2.anyOf.every(
148
+ (item) => item && typeof item === "object" && item.const !== void 0
149
+ ))
150
+ return {
151
+ type: "string",
152
+ enum: schema2.anyOf.map((item) => item.const)
153
+ };
154
+ }
155
+ const schema = _schema;
156
+ if (schema.type === "object" && schema.properties) {
157
+ const properties = {};
158
+ for (const [key, value] of Object.entries(schema.properties))
159
+ properties[key] = enumToOpenApi(value);
160
+ return {
161
+ ...schema,
162
+ properties
163
+ };
131
164
  }
132
- if (vendor === "arktype")
133
- return schema?.toJsonSchema?.();
134
- return schema.toJSONSchema?.() ?? schema?.toJsonSchema?.();
165
+ if (schema.type === "array" && schema.items)
166
+ return {
167
+ ...schema,
168
+ items: enumToOpenApi(schema.items)
169
+ };
170
+ return schema;
135
171
  };
136
172
  function toOpenAPISchema(app, exclude, references, vendors) {
137
173
  let {
@@ -204,16 +240,24 @@ function toOpenAPISchema(app, exclude, references, vendors) {
204
240
  definitions
205
241
  );
206
242
  if (params && params.type === "object" && params.properties)
207
- for (const [paramName, paramSchema] of Object.entries(
208
- params.properties
209
- ))
243
+ for (const [name, schema] of Object.entries(params.properties))
210
244
  parameters.push({
211
- name: paramName,
245
+ name,
212
246
  in: "path",
213
247
  required: true,
214
248
  // Path parameters are always required
215
- schema: paramSchema
249
+ schema
216
250
  });
251
+ } else {
252
+ for (const match of route.path.matchAll(/:([^/]+)/g)) {
253
+ const name = match[1].replace("?", "");
254
+ parameters.push({
255
+ name,
256
+ in: "path",
257
+ required: true,
258
+ schema: { type: "string" }
259
+ });
260
+ }
217
261
  }
218
262
  if (hooks.query) {
219
263
  const query = unwrapReference(
@@ -222,32 +266,28 @@ function toOpenAPISchema(app, exclude, references, vendors) {
222
266
  );
223
267
  if (query && query.type === "object" && query.properties) {
224
268
  const required = query.required || [];
225
- for (const [queryName, querySchema] of Object.entries(
226
- query.properties
227
- ))
269
+ for (const [name, schema] of Object.entries(query.properties))
228
270
  parameters.push({
229
- name: queryName,
271
+ name,
230
272
  in: "query",
231
- required: required.includes(queryName),
232
- schema: querySchema
273
+ required: required.includes(name),
274
+ schema
233
275
  });
234
276
  }
235
277
  }
236
278
  if (hooks.headers) {
237
279
  const headers = unwrapReference(
238
- unwrapSchema(hooks.query, vendors),
280
+ unwrapSchema(hooks.headers, vendors),
239
281
  definitions
240
282
  );
241
283
  if (headers && headers.type === "object" && headers.properties) {
242
284
  const required = headers.required || [];
243
- for (const [headerName, headerSchema] of Object.entries(
244
- headers.properties
245
- ))
285
+ for (const [name, schema] of Object.entries(headers.properties))
246
286
  parameters.push({
247
- name: headerName,
287
+ name,
248
288
  in: "header",
249
- required: required.includes(headerName),
250
- schema: headerSchema
289
+ required: required.includes(name),
290
+ schema
251
291
  });
252
292
  }
253
293
  }
@@ -258,14 +298,12 @@ function toOpenAPISchema(app, exclude, references, vendors) {
258
298
  );
259
299
  if (cookie && cookie.type === "object" && cookie.properties) {
260
300
  const required = cookie.required || [];
261
- for (const [cookieName, cookieSchema] of Object.entries(
262
- cookie.properties
263
- ))
301
+ for (const [name, schema] of Object.entries(cookie.properties))
264
302
  parameters.push({
265
- name: cookieName,
303
+ name,
266
304
  in: "cookie",
267
- required: required.includes(cookieName),
268
- schema: cookieSchema
305
+ required: required.includes(name),
306
+ schema
269
307
  });
270
308
  }
271
309
  }
@@ -313,6 +351,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
313
351
  } else {
314
352
  operation.requestBody = {
315
353
  description,
354
+ required: true,
316
355
  content: type === "string" || type === "number" || type === "integer" || type === "boolean" ? {
317
356
  "text/plain": {
318
357
  schema: body
@@ -327,8 +366,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
327
366
  "multipart/form-data": {
328
367
  schema: body
329
368
  }
330
- },
331
- required: true
369
+ }
332
370
  };
333
371
  }
334
372
  }
@@ -340,7 +378,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
340
378
  for (let [status, schema] of Object.entries(hooks.response)) {
341
379
  const response = unwrapSchema(schema, vendors);
342
380
  if (!response) continue;
343
- const { type, description, $ref, ...options } = unwrapReference(response, definitions);
381
+ const { type, description, $ref, ..._options } = unwrapReference(response, definitions);
344
382
  operation.responses[status] = {
345
383
  description: description ?? `Response for status ${status}`,
346
384
  content: type === "void" || type === "null" || type === "undefined" ? { type, description } : type === "string" || type === "number" || type === "integer" || type === "boolean" ? {
@@ -379,7 +417,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
379
417
  }
380
418
  }
381
419
  for (let path of getPossiblePath(route.path)) {
382
- const operationId = toOperationId(route.method, path);
420
+ const operationId = hooks.detail?.operationId ?? toOperationId(route.method, path);
383
421
  path = path.replace(/:([^/]+)/g, "{$1}");
384
422
  if (!paths[path]) paths[path] = {};
385
423
  const current = paths[path];
@@ -424,6 +462,7 @@ var withHeaders = (schema, headers) => Object.assign(schema, {
424
462
  });
425
463
  export {
426
464
  capitalize,
465
+ enumToOpenApi,
427
466
  getLoosePath,
428
467
  getPossiblePath,
429
468
  toOpenAPISchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elysiajs/openapi",
3
- "version": "1.4.8",
3
+ "version": "1.4.10",
4
4
  "description": "Plugin for Elysia to auto-generate API documentation",
5
5
  "author": {
6
6
  "name": "saltyAom",