@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.
package/dist/openapi.d.ts CHANGED
@@ -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
package/dist/openapi.mjs CHANGED
@@ -2,15 +2,13 @@
2
2
  import { t } from "elysia";
3
3
 
4
4
  // node_modules/@sinclair/typebox/build/esm/type/symbols/symbols.mjs
5
- var TransformKind = Symbol.for("TypeBox.Transform");
6
- var ReadonlyKind = Symbol.for("TypeBox.Readonly");
7
- var OptionalKind = Symbol.for("TypeBox.Optional");
8
- var Hint = Symbol.for("TypeBox.Hint");
9
- var Kind = Symbol.for("TypeBox.Kind");
5
+ var Kind = /* @__PURE__ */ Symbol.for("TypeBox.Kind");
10
6
 
11
7
  // src/openapi.ts
12
8
  var capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
13
- var toRef = (name) => t.Ref(`#/components/schemas/${name}`);
9
+ var toRef = (name) => t.Ref(
10
+ name.startsWith("#/") ? name : `#/components/schemas/${name}`
11
+ );
14
12
  var toOperationId = (method, paths) => {
15
13
  let operationId = method.toLowerCase();
16
14
  if (!paths || paths === "/") return operationId + "Index";
@@ -71,6 +69,219 @@ openapi({
71
69
  })`
72
70
  };
73
71
  var warned = {};
72
+ var mergeObjectSchemas = (schemas) => {
73
+ if (schemas.length === 0)
74
+ return {
75
+ schema: void 0,
76
+ notObjects: []
77
+ };
78
+ if (schemas.length === 1)
79
+ return schemas[0].type === "object" ? {
80
+ schema: schemas[0],
81
+ notObjects: []
82
+ } : {
83
+ schema: void 0,
84
+ notObjects: schemas
85
+ };
86
+ let newSchema;
87
+ const notObjects = [];
88
+ let additionalPropertiesIsTrue = false;
89
+ let additionalPropertiesIsFalse = false;
90
+ for (const schema of schemas) {
91
+ if (!schema) continue;
92
+ if (schema.type !== "object") {
93
+ notObjects.push(schema);
94
+ continue;
95
+ }
96
+ if ("additionalProperties" in schema) {
97
+ if (schema.additionalProperties === true)
98
+ additionalPropertiesIsTrue = true;
99
+ else if (schema.additionalProperties === false)
100
+ additionalPropertiesIsFalse = true;
101
+ }
102
+ if (!newSchema) {
103
+ newSchema = schema;
104
+ continue;
105
+ }
106
+ newSchema = {
107
+ ...newSchema,
108
+ ...schema,
109
+ properties: {
110
+ ...newSchema.properties,
111
+ ...schema.properties
112
+ },
113
+ required: [
114
+ ...newSchema?.required ?? [],
115
+ ...schema.required ?? []
116
+ ]
117
+ };
118
+ }
119
+ if (newSchema) {
120
+ if (newSchema.required)
121
+ newSchema.required = [...new Set(newSchema.required)];
122
+ if (additionalPropertiesIsFalse) newSchema.additionalProperties = false;
123
+ else if (additionalPropertiesIsTrue)
124
+ newSchema.additionalProperties = true;
125
+ }
126
+ return {
127
+ schema: newSchema,
128
+ notObjects
129
+ };
130
+ };
131
+ var isTSchema = (value) => {
132
+ if (!value || typeof value !== "object") return false;
133
+ if (Kind in value) return true;
134
+ const keys = Object.keys(value);
135
+ if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) {
136
+ return false;
137
+ }
138
+ return false;
139
+ };
140
+ var normalizeSchemaReference = (schema) => {
141
+ if (!schema) return void 0;
142
+ if (typeof schema !== "string") return schema;
143
+ return toRef(schema);
144
+ };
145
+ var mergeSchemaProperty = (existing, incoming, vendors) => {
146
+ if (!existing) return incoming;
147
+ if (!incoming) return existing;
148
+ let existingSchema = normalizeSchemaReference(existing);
149
+ let incomingSchema = normalizeSchemaReference(incoming);
150
+ if (!existingSchema) return incoming;
151
+ if (!incomingSchema) return existing;
152
+ if (!isTSchema(incomingSchema) && incomingSchema["~standard"])
153
+ incomingSchema = unwrapSchema(incomingSchema, vendors);
154
+ if (!isTSchema(existingSchema) && existingSchema["~standard"])
155
+ existingSchema = unwrapSchema(existingSchema, vendors);
156
+ if (!incomingSchema) return existingSchema;
157
+ if (!existingSchema) return incomingSchema;
158
+ const { schema: mergedSchema, notObjects } = mergeObjectSchemas([
159
+ existingSchema,
160
+ incomingSchema
161
+ ]);
162
+ if (notObjects.length > 0) {
163
+ if (mergedSchema) return t.Intersect([mergedSchema, ...notObjects]);
164
+ return notObjects.length === 1 ? notObjects[0] : t.Intersect(notObjects);
165
+ }
166
+ return mergedSchema;
167
+ };
168
+ var unwrapResponseSchema = (schema, vendors) => typeof schema === "string" ? normalizeSchemaReference(schema) : !schema ? void 0 : isTSchema(schema) ? schema : (
169
+ // @ts-ignore
170
+ schema["~standard"] ? unwrapSchema(schema, vendors, "output") : Object.fromEntries(
171
+ Object.entries(schema).map(([status, schema2]) => [
172
+ status,
173
+ typeof schema2 === "string" ? normalizeSchemaReference(schema2) : isTSchema(schema2) ? schema2 : unwrapSchema(
174
+ schema2,
175
+ vendors,
176
+ "output"
177
+ )
178
+ ])
179
+ )
180
+ );
181
+ var mergeResponseSchema = (_existing, _incoming, vendors) => {
182
+ if (!_existing) return _incoming;
183
+ if (!_incoming) return _existing;
184
+ let existing = unwrapResponseSchema(_existing, vendors);
185
+ let incoming = unwrapResponseSchema(_incoming, vendors);
186
+ if (!existing && !incoming) return void 0;
187
+ if (incoming && !existing) return incoming;
188
+ if (existing && !incoming) return existing;
189
+ if (isTSchema(existing) || existing?.["~standard"])
190
+ existing = {
191
+ 200: existing
192
+ };
193
+ if (isTSchema(incoming) || incoming?.["~standard"])
194
+ incoming = {
195
+ 200: incoming
196
+ };
197
+ const schema = {
198
+ ...incoming
199
+ };
200
+ for (const status of Object.keys(existing ?? {})) {
201
+ const existingSchema = existing[status];
202
+ const incomingSchema = incoming[status];
203
+ if (existingSchema && incomingSchema)
204
+ schema[status] = mergeSchemaProperty(
205
+ existingSchema,
206
+ incomingSchema,
207
+ vendors
208
+ );
209
+ else if (existingSchema) schema[status] = existingSchema;
210
+ else if (incomingSchema) schema[status] = incomingSchema;
211
+ }
212
+ return schema;
213
+ };
214
+ var mergeStandaloneValidators = (hooks, vendors) => {
215
+ const merged = { ...hooks };
216
+ if (!hooks.standaloneValidator?.length) return merged;
217
+ for (const validator of hooks.standaloneValidator) {
218
+ if (validator.body)
219
+ merged.body = mergeSchemaProperty(
220
+ merged.body,
221
+ validator.body,
222
+ vendors
223
+ );
224
+ if (validator.headers)
225
+ merged.headers = mergeSchemaProperty(
226
+ merged.headers,
227
+ validator.headers,
228
+ vendors
229
+ );
230
+ if (validator.query)
231
+ merged.query = mergeSchemaProperty(
232
+ merged.query,
233
+ validator.query,
234
+ vendors
235
+ );
236
+ if (validator.params)
237
+ merged.params = mergeSchemaProperty(
238
+ merged.params,
239
+ validator.params,
240
+ vendors
241
+ );
242
+ if (validator.cookie)
243
+ merged.cookie = mergeSchemaProperty(
244
+ merged.cookie,
245
+ validator.cookie,
246
+ vendors
247
+ );
248
+ if (validator.response)
249
+ merged.response = mergeResponseSchema(
250
+ merged.response,
251
+ validator.response,
252
+ vendors
253
+ );
254
+ }
255
+ if (typeof merged.body === "string")
256
+ merged.body = normalizeSchemaReference(merged.body);
257
+ if (typeof merged.headers === "string")
258
+ merged.headers = normalizeSchemaReference(merged.headers);
259
+ if (typeof merged.query === "string")
260
+ merged.query = normalizeSchemaReference(merged.query);
261
+ if (typeof merged.params === "string")
262
+ merged.params = normalizeSchemaReference(merged.params);
263
+ if (typeof merged.cookie === "string")
264
+ merged.cookie = normalizeSchemaReference(merged.cookie);
265
+ if (merged.response && typeof merged.response !== "string") {
266
+ const response = merged.response;
267
+ if ("type" in response || "$ref" in response) {
268
+ if (typeof response === "string")
269
+ merged.response = normalizeSchemaReference(response);
270
+ } else {
271
+ for (const [status, schema] of Object.entries(response))
272
+ if (typeof schema === "string")
273
+ response[status] = normalizeSchemaReference(schema);
274
+ }
275
+ }
276
+ return merged;
277
+ };
278
+ var flattenRoutes = (routes, vendors) => routes.map((route) => {
279
+ if (!route.hooks?.standaloneValidator?.length) return route;
280
+ return {
281
+ ...route,
282
+ hooks: mergeStandaloneValidators(route.hooks, vendors)
283
+ };
284
+ });
74
285
  var unwrapReference = (schema, definitions) => {
75
286
  const ref = schema?.$ref;
76
287
  if (!ref) return schema;
@@ -78,13 +289,20 @@ var unwrapReference = (schema, definitions) => {
78
289
  if (ref && definitions[name]) schema = definitions[name];
79
290
  return enumToOpenApi(schema);
80
291
  };
81
- var unwrapSchema = (schema, mapJsonSchema) => {
292
+ var unwrapSchema = (schema, mapJsonSchema, io = "input") => {
82
293
  if (!schema) return;
83
294
  if (typeof schema === "string") schema = toRef(schema);
84
295
  if (Kind in schema) return enumToOpenApi(schema);
85
- if (Kind in schema || !schema?.["~standard"]) return;
296
+ if (!schema?.["~standard"] && // @ts-ignore
297
+ (schema.$schema || schema.type || schema.properties || schema.items))
298
+ return schema;
299
+ if (!schema?.["~standard"]) return;
86
300
  const vendor = schema["~standard"].vendor;
87
301
  try {
302
+ if (schema["~standard"]?.jsonSchema?.[io])
303
+ return schema["~standard"]?.jsonSchema?.[io]?.({
304
+ target: "draft-2020-12"
305
+ });
88
306
  if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
89
307
  return enumToOpenApi(mapJsonSchema[vendor](schema));
90
308
  switch (vendor) {
@@ -179,7 +397,6 @@ function toOpenAPISchema(app, exclude, references, vendors) {
179
397
  const excludePaths = Array.isArray(exclude?.paths) ? exclude.paths : typeof exclude?.paths !== "undefined" ? [exclude.paths] : [];
180
398
  const paths = /* @__PURE__ */ Object.create(null);
181
399
  const definitions = app.getGlobalDefinitions?.().type;
182
- const routes = app.getGlobalRoutes();
183
400
  if (references) {
184
401
  if (!Array.isArray(references)) references = [references];
185
402
  for (let i = 0; i < references.length; i++) {
@@ -187,6 +404,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
187
404
  if (typeof reference === "function") references[i] = reference();
188
405
  }
189
406
  }
407
+ const routes = flattenRoutes(app.getGlobalRoutes(), vendors);
190
408
  for (const route of routes) {
191
409
  if (route.hooks?.detail?.hide) continue;
192
410
  const method = route.method.toLowerCase();
@@ -376,7 +594,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
376
594
  if (typeof hooks.response === "object" && // TypeBox
377
595
  !hooks.response.type && !hooks.response.$ref && !hooks.response["~standard"]) {
378
596
  for (let [status, schema] of Object.entries(hooks.response)) {
379
- const response = unwrapSchema(schema, vendors);
597
+ const response = unwrapSchema(schema, vendors, "output");
380
598
  if (!response) continue;
381
599
  const { type, description, $ref, ..._options } = unwrapReference(response, definitions);
382
600
  operation.responses[status] = {
@@ -393,7 +611,11 @@ function toOpenAPISchema(app, exclude, references, vendors) {
393
611
  };
394
612
  }
395
613
  } else {
396
- const response = unwrapSchema(hooks.response, vendors);
614
+ const response = unwrapSchema(
615
+ hooks.response,
616
+ vendors,
617
+ "output"
618
+ );
397
619
  if (response) {
398
620
  const {
399
621
  type: _type,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elysiajs/openapi",
3
- "version": "1.4.11",
3
+ "version": "1.4.13",
4
4
  "description": "Plugin for Elysia to auto-generate API documentation",
5
5
  "author": {
6
6
  "name": "saltyAom",
@@ -73,19 +73,18 @@
73
73
  "build": "bun build.ts",
74
74
  "release": "npm run build && npm run test && npm publish --access public"
75
75
  },
76
- "dependencies": {},
77
76
  "devDependencies": {
78
77
  "@apidevtools/swagger-parser": "^12.0.0",
79
78
  "@scalar/types": "^0.2.13",
80
79
  "@sinclair/typemap": "^0.10.1",
81
80
  "@types/bun": "1.2.20",
82
81
  "effect": "^3.17.13",
83
- "elysia": "1.4.6",
82
+ "elysia": "1.4.19",
84
83
  "eslint": "9.6.0",
85
84
  "openapi-types": "^12.1.3",
86
85
  "tsup": "^8.5.0",
87
86
  "typescript": "^5.9.2",
88
- "zod": "^4.1.5"
87
+ "zod": "^4.2.1"
89
88
  },
90
89
  "peerDependencies": {
91
90
  "elysia": ">= 1.4.0"