@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.
@@ -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
@@ -32,11 +32,7 @@ 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);
@@ -101,6 +97,212 @@ openapi({
101
97
  })`
102
98
  };
103
99
  var warned = {};
100
+ var mergeObjectSchemas = (schemas) => {
101
+ if (schemas.length === 0)
102
+ return {
103
+ schema: void 0,
104
+ notObjects: []
105
+ };
106
+ if (schemas.length === 1)
107
+ return schemas[0].type === "object" ? {
108
+ schema: schemas[0],
109
+ notObjects: []
110
+ } : {
111
+ schema: void 0,
112
+ notObjects: schemas
113
+ };
114
+ let newSchema;
115
+ const notObjects = [];
116
+ let additionalPropertiesIsTrue = false;
117
+ let additionalPropertiesIsFalse = false;
118
+ for (const schema of schemas) {
119
+ if (!schema) continue;
120
+ if (schema.type !== "object") {
121
+ notObjects.push(schema);
122
+ continue;
123
+ }
124
+ if ("additionalProperties" in schema) {
125
+ if (schema.additionalProperties === true)
126
+ additionalPropertiesIsTrue = true;
127
+ else if (schema.additionalProperties === false)
128
+ additionalPropertiesIsFalse = true;
129
+ }
130
+ if (!newSchema) {
131
+ newSchema = schema;
132
+ continue;
133
+ }
134
+ newSchema = {
135
+ ...newSchema,
136
+ ...schema,
137
+ properties: {
138
+ ...newSchema.properties,
139
+ ...schema.properties
140
+ },
141
+ required: [
142
+ ...newSchema?.required ?? [],
143
+ ...schema.required ?? []
144
+ ]
145
+ };
146
+ }
147
+ if (newSchema) {
148
+ if (newSchema.required)
149
+ newSchema.required = [...new Set(newSchema.required)];
150
+ if (additionalPropertiesIsFalse) newSchema.additionalProperties = false;
151
+ else if (additionalPropertiesIsTrue)
152
+ newSchema.additionalProperties = true;
153
+ }
154
+ return {
155
+ schema: newSchema,
156
+ notObjects
157
+ };
158
+ };
159
+ var isTSchema = (value) => {
160
+ if (!value || typeof value !== "object") return false;
161
+ if (Kind in value) return true;
162
+ const keys = Object.keys(value);
163
+ if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) {
164
+ return false;
165
+ }
166
+ return false;
167
+ };
168
+ var normalizeSchemaReference = (schema) => {
169
+ if (!schema) return void 0;
170
+ if (typeof schema !== "string") return schema;
171
+ return import_elysia.t.Ref(schema);
172
+ };
173
+ var mergeSchemaProperty = (existing, incoming, vendors) => {
174
+ if (!existing) return incoming;
175
+ if (!incoming) return existing;
176
+ const existingSchema = normalizeSchemaReference(existing);
177
+ let incomingSchema = normalizeSchemaReference(incoming);
178
+ if (!existingSchema) return incoming;
179
+ if (!incomingSchema) return existing;
180
+ if (!isTSchema(incomingSchema) && incomingSchema["~standard"])
181
+ incomingSchema = unwrapSchema(incomingSchema, vendors);
182
+ if (!incomingSchema) return existing;
183
+ const { schema: mergedSchema, notObjects } = mergeObjectSchemas([
184
+ existingSchema,
185
+ incomingSchema
186
+ ]);
187
+ if (notObjects.length > 0) {
188
+ if (mergedSchema) return import_elysia.t.Intersect([mergedSchema, ...notObjects]);
189
+ return notObjects.length === 1 ? notObjects[0] : import_elysia.t.Intersect(notObjects);
190
+ }
191
+ return mergedSchema;
192
+ };
193
+ var unwrapResponseSchema = (schema, vendors) => typeof schema === "string" ? normalizeSchemaReference(schema) : !schema ? void 0 : isTSchema(schema) ? schema : (
194
+ // @ts-ignore
195
+ schema["~standard"] ? unwrapSchema(schema, vendors, "output") : Object.fromEntries(
196
+ Object.entries(schema).map(([status, schema2]) => [
197
+ status,
198
+ typeof schema2 === "string" ? normalizeSchemaReference(schema2) : isTSchema(schema2) ? schema2 : unwrapSchema(schema2, vendors, "output")
199
+ ])
200
+ )
201
+ );
202
+ var mergeResponseSchema = (_existing, _incoming, vendors) => {
203
+ if (!_existing) return _incoming;
204
+ if (!_incoming) return _existing;
205
+ let existing = unwrapResponseSchema(_existing, vendors);
206
+ let incoming = unwrapResponseSchema(_incoming, vendors);
207
+ if (!existing && !incoming) return void 0;
208
+ if (incoming && !existing) return incoming;
209
+ if (existing && !incoming) return existing;
210
+ if (isTSchema(existing) || existing?.["~standard"])
211
+ existing = {
212
+ 200: existing
213
+ };
214
+ if (isTSchema(incoming) || incoming?.["~standard"])
215
+ incoming = {
216
+ 200: incoming
217
+ };
218
+ const schema = {
219
+ ...incoming
220
+ };
221
+ for (const status of Object.keys(existing ?? {})) {
222
+ const existingSchema = existing[status];
223
+ const incomingSchema = incoming[status];
224
+ if (existingSchema && incomingSchema)
225
+ schema[status] = mergeSchemaProperty(
226
+ existingSchema,
227
+ incomingSchema,
228
+ vendors
229
+ );
230
+ else if (existingSchema) schema[status] = existingSchema;
231
+ else if (incomingSchema) schema[status] = incomingSchema;
232
+ }
233
+ return schema;
234
+ };
235
+ var mergeStandaloneValidators = (hooks, vendors) => {
236
+ const merged = { ...hooks };
237
+ if (!hooks.standaloneValidator?.length) return merged;
238
+ for (const validator of hooks.standaloneValidator) {
239
+ if (validator.body)
240
+ merged.body = mergeSchemaProperty(
241
+ merged.body,
242
+ validator.body,
243
+ vendors
244
+ );
245
+ if (validator.headers)
246
+ merged.headers = mergeSchemaProperty(
247
+ merged.headers,
248
+ validator.headers,
249
+ vendors
250
+ );
251
+ if (validator.query)
252
+ merged.query = mergeSchemaProperty(
253
+ merged.query,
254
+ validator.query,
255
+ vendors
256
+ );
257
+ if (validator.params)
258
+ merged.params = mergeSchemaProperty(
259
+ merged.params,
260
+ validator.params,
261
+ vendors
262
+ );
263
+ if (validator.cookie)
264
+ merged.cookie = mergeSchemaProperty(
265
+ merged.cookie,
266
+ validator.cookie,
267
+ vendors
268
+ );
269
+ if (validator.response)
270
+ merged.response = mergeResponseSchema(
271
+ merged.response,
272
+ validator.response,
273
+ vendors
274
+ );
275
+ }
276
+ if (typeof merged.body === "string")
277
+ merged.body = normalizeSchemaReference(merged.body);
278
+ if (typeof merged.headers === "string")
279
+ merged.headers = normalizeSchemaReference(merged.headers);
280
+ if (typeof merged.query === "string")
281
+ merged.query = normalizeSchemaReference(merged.query);
282
+ if (typeof merged.params === "string")
283
+ merged.params = normalizeSchemaReference(merged.params);
284
+ if (typeof merged.cookie === "string")
285
+ merged.cookie = normalizeSchemaReference(merged.cookie);
286
+ if (merged.response && typeof merged.response !== "string") {
287
+ const response = merged.response;
288
+ if ("type" in response || "$ref" in response) {
289
+ if (typeof response === "string")
290
+ merged.response = normalizeSchemaReference(response);
291
+ } else {
292
+ for (const [status, schema] of Object.entries(response))
293
+ if (typeof schema === "string")
294
+ response[status] = normalizeSchemaReference(schema);
295
+ }
296
+ }
297
+ return merged;
298
+ };
299
+ var flattenRoutes = (routes, vendors) => routes.map((route) => {
300
+ if (!route.hooks?.standaloneValidator?.length) return route;
301
+ return {
302
+ ...route,
303
+ hooks: mergeStandaloneValidators(route.hooks, vendors)
304
+ };
305
+ });
104
306
  var unwrapReference = (schema, definitions) => {
105
307
  const ref = schema?.$ref;
106
308
  if (!ref) return schema;
@@ -108,13 +310,15 @@ var unwrapReference = (schema, definitions) => {
108
310
  if (ref && definitions[name]) schema = definitions[name];
109
311
  return enumToOpenApi(schema);
110
312
  };
111
- var unwrapSchema = (schema, mapJsonSchema) => {
313
+ var unwrapSchema = (schema, mapJsonSchema, io = "input") => {
112
314
  if (!schema) return;
113
315
  if (typeof schema === "string") schema = toRef(schema);
114
316
  if (Kind in schema) return enumToOpenApi(schema);
115
317
  if (Kind in schema || !schema?.["~standard"]) return;
116
318
  const vendor = schema["~standard"].vendor;
117
319
  try {
320
+ if (schema["~standard"]?.jsonSchema?.[io])
321
+ return schema["~standard"]?.jsonSchema?.[io]?.();
118
322
  if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
119
323
  return enumToOpenApi(mapJsonSchema[vendor](schema));
120
324
  switch (vendor) {
@@ -209,7 +413,6 @@ function toOpenAPISchema(app, exclude, references, vendors) {
209
413
  const excludePaths = Array.isArray(exclude?.paths) ? exclude.paths : typeof exclude?.paths !== "undefined" ? [exclude.paths] : [];
210
414
  const paths = /* @__PURE__ */ Object.create(null);
211
415
  const definitions = app.getGlobalDefinitions?.().type;
212
- const routes = app.getGlobalRoutes();
213
416
  if (references) {
214
417
  if (!Array.isArray(references)) references = [references];
215
418
  for (let i = 0; i < references.length; i++) {
@@ -217,6 +420,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
217
420
  if (typeof reference === "function") references[i] = reference();
218
421
  }
219
422
  }
423
+ const routes = flattenRoutes(app.getGlobalRoutes(), vendors);
220
424
  for (const route of routes) {
221
425
  if (route.hooks?.detail?.hide) continue;
222
426
  const method = route.method.toLowerCase();
@@ -406,7 +610,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
406
610
  if (typeof hooks.response === "object" && // TypeBox
407
611
  !hooks.response.type && !hooks.response.$ref && !hooks.response["~standard"]) {
408
612
  for (let [status, schema] of Object.entries(hooks.response)) {
409
- const response = unwrapSchema(schema, vendors);
613
+ const response = unwrapSchema(schema, vendors, "output");
410
614
  if (!response) continue;
411
615
  const { type, description, $ref, ..._options } = unwrapReference(response, definitions);
412
616
  operation.responses[status] = {
@@ -423,7 +627,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
423
627
  };
424
628
  }
425
629
  } else {
426
- const response = unwrapSchema(hooks.response, vendors);
630
+ const response = unwrapSchema(hooks.response, vendors, "output");
427
631
  if (response) {
428
632
  const {
429
633
  type: _type,
@@ -143,7 +143,7 @@ var elysiaCSS = `.light-mode {
143
143
  background-image: var(--stripes), var(--rainbow);
144
144
  filter: opacity(4%) saturate(200%);
145
145
  }`;
146
- var ScalarRender = (info, config) => `<!doctype html>
146
+ var ScalarRender = (info, config, embedSpec) => `<!doctype html>
147
147
  <html>
148
148
  <head>
149
149
  <title>${info.title}</title>
@@ -171,7 +171,14 @@ var ScalarRender = (info, config) => `<!doctype html>
171
171
  <body>
172
172
  <script
173
173
  id="api-reference"
174
- data-url="${config.url}"
174
+ data-configuration='${JSON.stringify(
175
+ Object.assign(
176
+ config,
177
+ {
178
+ content: embedSpec
179
+ }
180
+ )
181
+ )}'
175
182
  >
176
183
  </script>
177
184
  <script src="${config.cdn}" crossorigin></script>
@@ -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
  *