@elysiajs/openapi 1.4.10 → 1.4.12

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,7 @@ 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
14
  export declare const enumToOpenApi: <T extends TAnySchema | OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined>(_schema: T) => T;
15
15
  /**
16
16
  * Converts Elysia routes to OpenAPI 3.0.3 paths schema
package/dist/openapi.mjs CHANGED
@@ -2,11 +2,7 @@
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);
@@ -71,6 +67,212 @@ openapi({
71
67
  })`
72
68
  };
73
69
  var warned = {};
70
+ var mergeObjectSchemas = (schemas) => {
71
+ if (schemas.length === 0)
72
+ return {
73
+ schema: void 0,
74
+ notObjects: []
75
+ };
76
+ if (schemas.length === 1)
77
+ return schemas[0].type === "object" ? {
78
+ schema: schemas[0],
79
+ notObjects: []
80
+ } : {
81
+ schema: void 0,
82
+ notObjects: schemas
83
+ };
84
+ let newSchema;
85
+ const notObjects = [];
86
+ let additionalPropertiesIsTrue = false;
87
+ let additionalPropertiesIsFalse = false;
88
+ for (const schema of schemas) {
89
+ if (!schema) continue;
90
+ if (schema.type !== "object") {
91
+ notObjects.push(schema);
92
+ continue;
93
+ }
94
+ if ("additionalProperties" in schema) {
95
+ if (schema.additionalProperties === true)
96
+ additionalPropertiesIsTrue = true;
97
+ else if (schema.additionalProperties === false)
98
+ additionalPropertiesIsFalse = true;
99
+ }
100
+ if (!newSchema) {
101
+ newSchema = schema;
102
+ continue;
103
+ }
104
+ newSchema = {
105
+ ...newSchema,
106
+ ...schema,
107
+ properties: {
108
+ ...newSchema.properties,
109
+ ...schema.properties
110
+ },
111
+ required: [
112
+ ...newSchema?.required ?? [],
113
+ ...schema.required ?? []
114
+ ]
115
+ };
116
+ }
117
+ if (newSchema) {
118
+ if (newSchema.required)
119
+ newSchema.required = [...new Set(newSchema.required)];
120
+ if (additionalPropertiesIsFalse) newSchema.additionalProperties = false;
121
+ else if (additionalPropertiesIsTrue)
122
+ newSchema.additionalProperties = true;
123
+ }
124
+ return {
125
+ schema: newSchema,
126
+ notObjects
127
+ };
128
+ };
129
+ var isTSchema = (value) => {
130
+ if (!value || typeof value !== "object") return false;
131
+ if (Kind in value) return true;
132
+ const keys = Object.keys(value);
133
+ if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) {
134
+ return false;
135
+ }
136
+ return false;
137
+ };
138
+ var normalizeSchemaReference = (schema) => {
139
+ if (!schema) return void 0;
140
+ if (typeof schema !== "string") return schema;
141
+ return t.Ref(schema);
142
+ };
143
+ var mergeSchemaProperty = (existing, incoming, vendors) => {
144
+ if (!existing) return incoming;
145
+ if (!incoming) return existing;
146
+ const existingSchema = normalizeSchemaReference(existing);
147
+ let incomingSchema = normalizeSchemaReference(incoming);
148
+ if (!existingSchema) return incoming;
149
+ if (!incomingSchema) return existing;
150
+ if (!isTSchema(incomingSchema) && incomingSchema["~standard"])
151
+ incomingSchema = unwrapSchema(incomingSchema, vendors);
152
+ if (!incomingSchema) return existing;
153
+ const { schema: mergedSchema, notObjects } = mergeObjectSchemas([
154
+ existingSchema,
155
+ incomingSchema
156
+ ]);
157
+ if (notObjects.length > 0) {
158
+ if (mergedSchema) return t.Intersect([mergedSchema, ...notObjects]);
159
+ return notObjects.length === 1 ? notObjects[0] : t.Intersect(notObjects);
160
+ }
161
+ return mergedSchema;
162
+ };
163
+ var unwrapResponseSchema = (schema, vendors) => typeof schema === "string" ? normalizeSchemaReference(schema) : !schema ? void 0 : isTSchema(schema) ? schema : (
164
+ // @ts-ignore
165
+ schema["~standard"] ? unwrapSchema(schema, vendors, "output") : Object.fromEntries(
166
+ Object.entries(schema).map(([status, schema2]) => [
167
+ status,
168
+ typeof schema2 === "string" ? normalizeSchemaReference(schema2) : isTSchema(schema2) ? schema2 : unwrapSchema(schema2, vendors, "output")
169
+ ])
170
+ )
171
+ );
172
+ var mergeResponseSchema = (_existing, _incoming, vendors) => {
173
+ if (!_existing) return _incoming;
174
+ if (!_incoming) return _existing;
175
+ let existing = unwrapResponseSchema(_existing, vendors);
176
+ let incoming = unwrapResponseSchema(_incoming, vendors);
177
+ if (!existing && !incoming) return void 0;
178
+ if (incoming && !existing) return incoming;
179
+ if (existing && !incoming) return existing;
180
+ if (isTSchema(existing) || existing?.["~standard"])
181
+ existing = {
182
+ 200: existing
183
+ };
184
+ if (isTSchema(incoming) || incoming?.["~standard"])
185
+ incoming = {
186
+ 200: incoming
187
+ };
188
+ const schema = {
189
+ ...incoming
190
+ };
191
+ for (const status of Object.keys(existing ?? {})) {
192
+ const existingSchema = existing[status];
193
+ const incomingSchema = incoming[status];
194
+ if (existingSchema && incomingSchema)
195
+ schema[status] = mergeSchemaProperty(
196
+ existingSchema,
197
+ incomingSchema,
198
+ vendors
199
+ );
200
+ else if (existingSchema) schema[status] = existingSchema;
201
+ else if (incomingSchema) schema[status] = incomingSchema;
202
+ }
203
+ return schema;
204
+ };
205
+ var mergeStandaloneValidators = (hooks, vendors) => {
206
+ const merged = { ...hooks };
207
+ if (!hooks.standaloneValidator?.length) return merged;
208
+ for (const validator of hooks.standaloneValidator) {
209
+ if (validator.body)
210
+ merged.body = mergeSchemaProperty(
211
+ merged.body,
212
+ validator.body,
213
+ vendors
214
+ );
215
+ if (validator.headers)
216
+ merged.headers = mergeSchemaProperty(
217
+ merged.headers,
218
+ validator.headers,
219
+ vendors
220
+ );
221
+ if (validator.query)
222
+ merged.query = mergeSchemaProperty(
223
+ merged.query,
224
+ validator.query,
225
+ vendors
226
+ );
227
+ if (validator.params)
228
+ merged.params = mergeSchemaProperty(
229
+ merged.params,
230
+ validator.params,
231
+ vendors
232
+ );
233
+ if (validator.cookie)
234
+ merged.cookie = mergeSchemaProperty(
235
+ merged.cookie,
236
+ validator.cookie,
237
+ vendors
238
+ );
239
+ if (validator.response)
240
+ merged.response = mergeResponseSchema(
241
+ merged.response,
242
+ validator.response,
243
+ vendors
244
+ );
245
+ }
246
+ if (typeof merged.body === "string")
247
+ merged.body = normalizeSchemaReference(merged.body);
248
+ if (typeof merged.headers === "string")
249
+ merged.headers = normalizeSchemaReference(merged.headers);
250
+ if (typeof merged.query === "string")
251
+ merged.query = normalizeSchemaReference(merged.query);
252
+ if (typeof merged.params === "string")
253
+ merged.params = normalizeSchemaReference(merged.params);
254
+ if (typeof merged.cookie === "string")
255
+ merged.cookie = normalizeSchemaReference(merged.cookie);
256
+ if (merged.response && typeof merged.response !== "string") {
257
+ const response = merged.response;
258
+ if ("type" in response || "$ref" in response) {
259
+ if (typeof response === "string")
260
+ merged.response = normalizeSchemaReference(response);
261
+ } else {
262
+ for (const [status, schema] of Object.entries(response))
263
+ if (typeof schema === "string")
264
+ response[status] = normalizeSchemaReference(schema);
265
+ }
266
+ }
267
+ return merged;
268
+ };
269
+ var flattenRoutes = (routes, vendors) => routes.map((route) => {
270
+ if (!route.hooks?.standaloneValidator?.length) return route;
271
+ return {
272
+ ...route,
273
+ hooks: mergeStandaloneValidators(route.hooks, vendors)
274
+ };
275
+ });
74
276
  var unwrapReference = (schema, definitions) => {
75
277
  const ref = schema?.$ref;
76
278
  if (!ref) return schema;
@@ -78,13 +280,15 @@ var unwrapReference = (schema, definitions) => {
78
280
  if (ref && definitions[name]) schema = definitions[name];
79
281
  return enumToOpenApi(schema);
80
282
  };
81
- var unwrapSchema = (schema, mapJsonSchema) => {
283
+ var unwrapSchema = (schema, mapJsonSchema, io = "input") => {
82
284
  if (!schema) return;
83
285
  if (typeof schema === "string") schema = toRef(schema);
84
286
  if (Kind in schema) return enumToOpenApi(schema);
85
287
  if (Kind in schema || !schema?.["~standard"]) return;
86
288
  const vendor = schema["~standard"].vendor;
87
289
  try {
290
+ if (schema["~standard"]?.jsonSchema?.[io])
291
+ return schema["~standard"]?.jsonSchema?.[io]?.();
88
292
  if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
89
293
  return enumToOpenApi(mapJsonSchema[vendor](schema));
90
294
  switch (vendor) {
@@ -179,7 +383,6 @@ function toOpenAPISchema(app, exclude, references, vendors) {
179
383
  const excludePaths = Array.isArray(exclude?.paths) ? exclude.paths : typeof exclude?.paths !== "undefined" ? [exclude.paths] : [];
180
384
  const paths = /* @__PURE__ */ Object.create(null);
181
385
  const definitions = app.getGlobalDefinitions?.().type;
182
- const routes = app.getGlobalRoutes();
183
386
  if (references) {
184
387
  if (!Array.isArray(references)) references = [references];
185
388
  for (let i = 0; i < references.length; i++) {
@@ -187,6 +390,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
187
390
  if (typeof reference === "function") references[i] = reference();
188
391
  }
189
392
  }
393
+ const routes = flattenRoutes(app.getGlobalRoutes(), vendors);
190
394
  for (const route of routes) {
191
395
  if (route.hooks?.detail?.hide) continue;
192
396
  const method = route.method.toLowerCase();
@@ -376,7 +580,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
376
580
  if (typeof hooks.response === "object" && // TypeBox
377
581
  !hooks.response.type && !hooks.response.$ref && !hooks.response["~standard"]) {
378
582
  for (let [status, schema] of Object.entries(hooks.response)) {
379
- const response = unwrapSchema(schema, vendors);
583
+ const response = unwrapSchema(schema, vendors, "output");
380
584
  if (!response) continue;
381
585
  const { type, description, $ref, ..._options } = unwrapReference(response, definitions);
382
586
  operation.responses[status] = {
@@ -393,7 +597,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
393
597
  };
394
598
  }
395
599
  } else {
396
- const response = unwrapSchema(hooks.response, vendors);
600
+ const response = unwrapSchema(hooks.response, vendors, "output");
397
601
  if (response) {
398
602
  const {
399
603
  type: _type,
@@ -1,3 +1,3 @@
1
1
  import type { OpenAPIV3 } from 'openapi-types';
2
2
  import { ElysiaOpenAPIConfig } from '../types';
3
- export declare const ScalarRender: (info: OpenAPIV3.InfoObject, config: NonNullable<ElysiaOpenAPIConfig["scalar"]>) => string;
3
+ export declare const ScalarRender: (info: OpenAPIV3.InfoObject, config: NonNullable<ElysiaOpenAPIConfig["scalar"]>, embedSpec?: string) => string;
@@ -119,7 +119,7 @@ var elysiaCSS = `.light-mode {
119
119
  background-image: var(--stripes), var(--rainbow);
120
120
  filter: opacity(4%) saturate(200%);
121
121
  }`;
122
- var ScalarRender = (info, config) => `<!doctype html>
122
+ var ScalarRender = (info, config, embedSpec) => `<!doctype html>
123
123
  <html>
124
124
  <head>
125
125
  <title>${info.title}</title>
@@ -147,7 +147,14 @@ var ScalarRender = (info, config) => `<!doctype html>
147
147
  <body>
148
148
  <script
149
149
  id="api-reference"
150
- data-url="${config.url}"
150
+ data-configuration='${JSON.stringify(
151
+ Object.assign(
152
+ config,
153
+ {
154
+ content: embedSpec
155
+ }
156
+ )
157
+ )}'
151
158
  >
152
159
  </script>
153
160
  <script src="${config.cdn}" crossorigin></script>
package/dist/types.d.ts CHANGED
@@ -74,6 +74,14 @@ export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extend
74
74
  * Additional reference for each endpoint
75
75
  */
76
76
  references?: AdditionalReferences;
77
+ /**
78
+ * Embed OpenAPI schema to provider body if possible
79
+ *
80
+ * This is highly discouraged, unless you really have to inline OpenAPI schema
81
+ *
82
+ * @default false
83
+ */
84
+ embedSpec?: boolean;
77
85
  /**
78
86
  * Mapping function from Standard schema to OpenAPI schema
79
87
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elysiajs/openapi",
3
- "version": "1.4.10",
3
+ "version": "1.4.12",
4
4
  "description": "Plugin for Elysia to auto-generate API documentation",
5
5
  "author": {
6
6
  "name": "saltyAom",
@@ -80,12 +80,12 @@
80
80
  "@sinclair/typemap": "^0.10.1",
81
81
  "@types/bun": "1.2.20",
82
82
  "effect": "^3.17.13",
83
- "elysia": "1.4.6",
83
+ "elysia": "1.4.19",
84
84
  "eslint": "9.6.0",
85
85
  "openapi-types": "^12.1.3",
86
86
  "tsup": "^8.5.0",
87
87
  "typescript": "^5.9.2",
88
- "zod": "^4.1.5"
88
+ "zod": "^4.2.1"
89
89
  },
90
90
  "peerDependencies": {
91
91
  "elysia": ">= 1.4.0"