@elysiajs/openapi 1.4.2 → 1.4.3

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/bun.lock CHANGED
@@ -8,7 +8,8 @@
8
8
  "@scalar/types": "^0.2.13",
9
9
  "@sinclair/typemap": "^0.10.1",
10
10
  "@types/bun": "1.2.20",
11
- "elysia": "1.4.0",
11
+ "effect": "^3.17.13",
12
+ "elysia": "1.4.6",
12
13
  "eslint": "9.6.0",
13
14
  "openapi-types": "^12.1.3",
14
15
  "tsup": "^8.5.0",
@@ -167,6 +168,8 @@
167
168
 
168
169
  "@sinclair/typemap": ["@sinclair/typemap@0.10.1", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.30", "valibot": "^1.0.0", "zod": "^3.24.1" } }, "sha512-UXR0fhu/n3c9B6lB+SLI5t1eVpt9i9CdDrp2TajRe3LbKiUhCTZN2kSfJhjPnpc3I59jMRIhgew7+0HlMi08mg=="],
169
170
 
171
+ "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
172
+
170
173
  "@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="],
171
174
 
172
175
  "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
@@ -239,7 +242,9 @@
239
242
 
240
243
  "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
241
244
 
242
- "elysia": ["elysia@1.4.0", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.2.2", "fast-decode-uri-component": "^1.0.1" }, "optionalDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "openapi-types": ">= 12.0.0" }, "peerDependencies": { "file-type": ">= 20.0.0", "typescript": ">= 5.0.0" } }, "sha512-AVEq8cWo7+fdkbUtqVXDZ7uhjjv8K6NE6bt9oSzdocaawsW5VhJP2ArPoRYkWdJPYmS4f2FI5xWF+Tvs98yXVw=="],
245
+ "effect": ["effect@3.17.13", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-JMz5oBxs/6mu4FP9Csjub4jYMUwMLrp+IzUmSDVIzn2NoeoyOXMl7x1lghfr3dLKWffWrdnv/d8nFFdgrHXPqw=="],
246
+
247
+ "elysia": ["elysia@1.4.6", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.2.2", "fast-decode-uri-component": "^1.0.1" }, "optionalDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "openapi-types": ">= 12.0.0" }, "peerDependencies": { "file-type": ">= 20.0.0", "typescript": ">= 5.0.0" } }, "sha512-u2CorXLPs5ZXyWP+tQR+bgka/lJA4vNpB8lDE2w/sTmdaIwoPQmHEL4J3ai6OAlluWR1kfG7T9gO3EYT9D8viQ=="],
243
248
 
244
249
  "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
245
250
 
@@ -265,6 +270,8 @@
265
270
 
266
271
  "exact-mirror": ["exact-mirror@0.2.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-CrGe+4QzHZlnrXZVlo/WbUZ4qQZq8C0uATQVGVgXIrNXgHDBBNFD1VRfssRA2C9t3RYvh3MadZSdg2Wy7HBoQA=="],
267
272
 
273
+ "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
274
+
268
275
  "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
269
276
 
270
277
  "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
@@ -405,6 +412,8 @@
405
412
 
406
413
  "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
407
414
 
415
+ "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
416
+
408
417
  "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
409
418
 
410
419
  "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
@@ -1,11 +1,11 @@
1
1
  import { Elysia } from 'elysia';
2
- import type { ElysiaOpenAPIConfig, OpenAPIProvider } from './types';
2
+ import type { ElysiaOpenAPIConfig } from './types';
3
3
  /**
4
4
  * Plugin for [elysia](https://github.com/elysiajs/elysia) that auto-generate OpenAPI documentation page.
5
5
  *
6
6
  * @see https://github.com/elysiajs/elysia-swagger
7
7
  */
8
- export declare const openapi: <const Enabled extends boolean = true, const Path extends string = "/openapi", const Provider extends OpenAPIProvider = "scalar">({ enabled, path, provider, specPath, documentation, exclude, swagger, scalar, references, mapJsonSchema }?: ElysiaOpenAPIConfig<Enabled, Path, Provider>) => Elysia<"", {
8
+ export declare const openapi: <const Enabled extends boolean = true, const Path extends string = "/openapi">({ enabled, path, provider, specPath, documentation, exclude, swagger, scalar, references, mapJsonSchema }?: ElysiaOpenAPIConfig<Enabled, Path>) => Elysia<"", {
9
9
  decorator: {};
10
10
  store: {};
11
11
  derive: {};
package/dist/cjs/index.js CHANGED
@@ -317,12 +317,51 @@ var getPossiblePath = (path) => {
317
317
  }
318
318
  return paths;
319
319
  };
320
- var isValidSchema = (schema) => typeof schema === "object" && (Kind in schema && schema[Kind] !== "Unknown" || schema.type || schema.properties || schema.items);
320
+ var isValidSchema = (schema) => schema && typeof schema === "object" && (Kind in schema && schema[Kind] !== "Unknown" || schema.type || schema.properties || schema.items);
321
321
  var getLoosePath = (path) => {
322
322
  if (path.charCodeAt(path.length - 1) === 47)
323
323
  return path.slice(0, path.length - 1);
324
324
  return path + "/";
325
325
  };
326
+ var warnings = {
327
+ zod4: `import openapi from '@elysiajs/openapi'
328
+ import * as z from 'zod'
329
+
330
+ openapi({
331
+ mapJsonSchema: {
332
+ zod: z.toJSONSchema
333
+ }
334
+ })`,
335
+ zod3: `import openapi from '@elysiajs/openapi'
336
+ import { zodToJsonSchema } from 'zod-to-json-schema'
337
+
338
+ openapi({
339
+ mapJsonSchema: {
340
+ zod: zodToJsonSchema
341
+ }
342
+ })`,
343
+ valibot: `import { toJsonSchema } from '@valibot/to-json-schema'
344
+
345
+ openapi({
346
+ mapJsonSchema: {
347
+ valibot: toJsonSchema
348
+ }
349
+ })`,
350
+ effect: `import { JSONSchema } from 'effect'
351
+
352
+ openapi({
353
+ mapJsonSchema: {
354
+ effect: JSONSchema.make
355
+ }
356
+ })`
357
+ };
358
+ var warned = {};
359
+ var unwrapReference = (schema, definitions) => {
360
+ if (!schema?.$ref) return schema;
361
+ const name = schema.$ref.slice(schema.$ref.lastIndexOf("/") + 1);
362
+ if (schema.$ref && definitions[name]) schema = definitions[name];
363
+ return schema;
364
+ };
326
365
  var unwrapSchema = (schema, mapJsonSchema) => {
327
366
  if (!schema) return;
328
367
  if (typeof schema === "string") schema = toRef(schema);
@@ -331,8 +370,49 @@ var unwrapSchema = (schema, mapJsonSchema) => {
331
370
  const vendor = schema["~standard"].vendor;
332
371
  if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
333
372
  return mapJsonSchema[vendor](schema);
334
- if (vendor === "zod" || vendor === "sury")
335
- return schema.toJSONSchema?.();
373
+ switch (vendor) {
374
+ case "zod":
375
+ if (warned.zod4 || warned.zod3) break;
376
+ console.warn(
377
+ "[@elysiajs/openapi] Zod doesn't provide JSON Schema method on the schema"
378
+ );
379
+ if ("_zod" in schema) {
380
+ warned.zod4 = true;
381
+ console.warn(
382
+ "For Zod v4, please provide z.toJSONSchema as follows:\n"
383
+ );
384
+ console.warn(warnings.zod4);
385
+ } else {
386
+ warned.zod3 = true;
387
+ console.warn(
388
+ "For Zod v3, please install zod-to-json-schema package and use it like this:\n"
389
+ );
390
+ console.warn(warnings.zod3);
391
+ }
392
+ break;
393
+ case "valibot":
394
+ if (warned.valibot) break;
395
+ warned.valibot = true;
396
+ console.warn(
397
+ "[@elysiajs/openapi] Valibot require a separate package for JSON Schema conversion"
398
+ );
399
+ console.warn(
400
+ "Please install @valibot/to-json-schema package and use it like this:\n"
401
+ );
402
+ console.warn(warnings.valibot);
403
+ break;
404
+ case "effect":
405
+ if (warned.effect) break;
406
+ warned.effect = true;
407
+ console.warn(
408
+ "[@elysiajs/openapi] Effect Schema doesn't provide JSON Schema method on the schema"
409
+ );
410
+ console.warn(
411
+ "please provide JSONSchema from 'effect' package as follows:\n"
412
+ );
413
+ console.warn(warnings.effect);
414
+ break;
415
+ }
336
416
  if (vendor === "arktype")
337
417
  return schema?.toJsonSchema?.();
338
418
  return schema.toJSONSchema?.() ?? schema?.toJsonSchema?.();
@@ -345,6 +425,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
345
425
  } = exclude ?? {};
346
426
  const excludePaths = Array.isArray(exclude?.paths) ? exclude.paths : typeof exclude?.paths !== "undefined" ? [exclude.paths] : [];
347
427
  const paths = /* @__PURE__ */ Object.create(null);
428
+ const definitions = app.getGlobalDefinitions?.().type;
348
429
  const routes = app.getGlobalRoutes();
349
430
  if (references) {
350
431
  if (!Array.isArray(references)) references = [references];
@@ -359,7 +440,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
359
440
  if (excludeStaticFile && route.path.includes(".") || excludePaths.includes(route.path) || excludeMethods.includes(method))
360
441
  continue;
361
442
  const hooks = route.hooks ?? {};
362
- if (references)
443
+ if (references?.length)
363
444
  for (const reference of references) {
364
445
  if (!reference) continue;
365
446
  const refer = reference[route.path]?.[method] ?? reference[getLoosePath(route.path)]?.[method];
@@ -390,7 +471,10 @@ function toOpenAPISchema(app, exclude, references, vendors) {
390
471
  };
391
472
  const parameters = [];
392
473
  if (hooks.params) {
393
- const params = unwrapSchema(hooks.params, vendors);
474
+ const params = unwrapReference(
475
+ unwrapSchema(hooks.params, vendors),
476
+ definitions
477
+ );
394
478
  if (params && params.type === "object" && params.properties)
395
479
  for (const [paramName, paramSchema] of Object.entries(
396
480
  params.properties
@@ -404,7 +488,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
404
488
  });
405
489
  }
406
490
  if (hooks.query) {
407
- let query = unwrapSchema(hooks.query, vendors);
491
+ const query = unwrapReference(unwrapSchema(hooks.query, vendors), definitions);
408
492
  if (query && query.type === "object" && query.properties) {
409
493
  const required = query.required || [];
410
494
  for (const [queryName, querySchema] of Object.entries(
@@ -419,7 +503,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
419
503
  }
420
504
  }
421
505
  if (hooks.headers) {
422
- const headers = unwrapSchema(hooks.query, vendors);
506
+ const headers = unwrapReference(unwrapSchema(hooks.query, vendors), definitions);
423
507
  if (headers && headers.type === "object" && headers.properties) {
424
508
  const required = headers.required || [];
425
509
  for (const [headerName, headerSchema] of Object.entries(
@@ -434,7 +518,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
434
518
  }
435
519
  }
436
520
  if (hooks.cookie) {
437
- const cookie = unwrapSchema(hooks.cookie, vendors);
521
+ const cookie = unwrapReference(unwrapSchema(hooks.cookie, vendors), definitions);
438
522
  if (cookie && cookie.type === "object" && cookie.properties) {
439
523
  const required = cookie.required || [];
440
524
  for (const [cookieName, cookieSchema] of Object.entries(
@@ -578,10 +662,9 @@ function toOpenAPISchema(app, exclude, references, vendors) {
578
662
  };
579
663
  }
580
664
  }
581
- const _schemas = app.getGlobalDefinitions?.().type;
582
665
  const schemas = /* @__PURE__ */ Object.create(null);
583
- if (_schemas)
584
- for (const [name, schema] of Object.entries(_schemas)) {
666
+ if (definitions)
667
+ for (const [name, schema] of Object.entries(definitions)) {
585
668
  const jsonSchema = unwrapSchema(schema, vendors);
586
669
  if (jsonSchema) schemas[name] = jsonSchema;
587
670
  }
@@ -684,6 +767,10 @@ var openapi = ({
684
767
  };
685
768
  },
686
769
  {
770
+ error({ error }) {
771
+ console.log("[@elysiajs/openapi] error at specPath");
772
+ console.warn(error);
773
+ },
687
774
  detail: {
688
775
  hide: true
689
776
  }
@@ -60,12 +60,51 @@ var getPossiblePath = (path) => {
60
60
  }
61
61
  return paths;
62
62
  };
63
- var isValidSchema = (schema) => typeof schema === "object" && (Kind in schema && schema[Kind] !== "Unknown" || schema.type || schema.properties || schema.items);
63
+ var isValidSchema = (schema) => schema && typeof schema === "object" && (Kind in schema && schema[Kind] !== "Unknown" || schema.type || schema.properties || schema.items);
64
64
  var getLoosePath = (path) => {
65
65
  if (path.charCodeAt(path.length - 1) === 47)
66
66
  return path.slice(0, path.length - 1);
67
67
  return path + "/";
68
68
  };
69
+ var warnings = {
70
+ zod4: `import openapi from '@elysiajs/openapi'
71
+ import * as z from 'zod'
72
+
73
+ openapi({
74
+ mapJsonSchema: {
75
+ zod: z.toJSONSchema
76
+ }
77
+ })`,
78
+ zod3: `import openapi from '@elysiajs/openapi'
79
+ import { zodToJsonSchema } from 'zod-to-json-schema'
80
+
81
+ openapi({
82
+ mapJsonSchema: {
83
+ zod: zodToJsonSchema
84
+ }
85
+ })`,
86
+ valibot: `import { toJsonSchema } from '@valibot/to-json-schema'
87
+
88
+ openapi({
89
+ mapJsonSchema: {
90
+ valibot: toJsonSchema
91
+ }
92
+ })`,
93
+ effect: `import { JSONSchema } from 'effect'
94
+
95
+ openapi({
96
+ mapJsonSchema: {
97
+ effect: JSONSchema.make
98
+ }
99
+ })`
100
+ };
101
+ var warned = {};
102
+ var unwrapReference = (schema, definitions) => {
103
+ if (!schema?.$ref) return schema;
104
+ const name = schema.$ref.slice(schema.$ref.lastIndexOf("/") + 1);
105
+ if (schema.$ref && definitions[name]) schema = definitions[name];
106
+ return schema;
107
+ };
69
108
  var unwrapSchema = (schema, mapJsonSchema) => {
70
109
  if (!schema) return;
71
110
  if (typeof schema === "string") schema = toRef(schema);
@@ -74,8 +113,49 @@ var unwrapSchema = (schema, mapJsonSchema) => {
74
113
  const vendor = schema["~standard"].vendor;
75
114
  if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
76
115
  return mapJsonSchema[vendor](schema);
77
- if (vendor === "zod" || vendor === "sury")
78
- return schema.toJSONSchema?.();
116
+ switch (vendor) {
117
+ case "zod":
118
+ if (warned.zod4 || warned.zod3) break;
119
+ console.warn(
120
+ "[@elysiajs/openapi] Zod doesn't provide JSON Schema method on the schema"
121
+ );
122
+ if ("_zod" in schema) {
123
+ warned.zod4 = true;
124
+ console.warn(
125
+ "For Zod v4, please provide z.toJSONSchema as follows:\n"
126
+ );
127
+ console.warn(warnings.zod4);
128
+ } else {
129
+ warned.zod3 = true;
130
+ console.warn(
131
+ "For Zod v3, please install zod-to-json-schema package and use it like this:\n"
132
+ );
133
+ console.warn(warnings.zod3);
134
+ }
135
+ break;
136
+ case "valibot":
137
+ if (warned.valibot) break;
138
+ warned.valibot = true;
139
+ console.warn(
140
+ "[@elysiajs/openapi] Valibot require a separate package for JSON Schema conversion"
141
+ );
142
+ console.warn(
143
+ "Please install @valibot/to-json-schema package and use it like this:\n"
144
+ );
145
+ console.warn(warnings.valibot);
146
+ break;
147
+ case "effect":
148
+ if (warned.effect) break;
149
+ warned.effect = true;
150
+ console.warn(
151
+ "[@elysiajs/openapi] Effect Schema doesn't provide JSON Schema method on the schema"
152
+ );
153
+ console.warn(
154
+ "please provide JSONSchema from 'effect' package as follows:\n"
155
+ );
156
+ console.warn(warnings.effect);
157
+ break;
158
+ }
79
159
  if (vendor === "arktype")
80
160
  return schema?.toJsonSchema?.();
81
161
  return schema.toJSONSchema?.() ?? schema?.toJsonSchema?.();
@@ -88,6 +168,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
88
168
  } = exclude ?? {};
89
169
  const excludePaths = Array.isArray(exclude?.paths) ? exclude.paths : typeof exclude?.paths !== "undefined" ? [exclude.paths] : [];
90
170
  const paths = /* @__PURE__ */ Object.create(null);
171
+ const definitions = app.getGlobalDefinitions?.().type;
91
172
  const routes = app.getGlobalRoutes();
92
173
  if (references) {
93
174
  if (!Array.isArray(references)) references = [references];
@@ -102,7 +183,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
102
183
  if (excludeStaticFile && route.path.includes(".") || excludePaths.includes(route.path) || excludeMethods.includes(method))
103
184
  continue;
104
185
  const hooks = route.hooks ?? {};
105
- if (references)
186
+ if (references?.length)
106
187
  for (const reference of references) {
107
188
  if (!reference) continue;
108
189
  const refer = reference[route.path]?.[method] ?? reference[getLoosePath(route.path)]?.[method];
@@ -133,7 +214,10 @@ function toOpenAPISchema(app, exclude, references, vendors) {
133
214
  };
134
215
  const parameters = [];
135
216
  if (hooks.params) {
136
- const params = unwrapSchema(hooks.params, vendors);
217
+ const params = unwrapReference(
218
+ unwrapSchema(hooks.params, vendors),
219
+ definitions
220
+ );
137
221
  if (params && params.type === "object" && params.properties)
138
222
  for (const [paramName, paramSchema] of Object.entries(
139
223
  params.properties
@@ -147,7 +231,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
147
231
  });
148
232
  }
149
233
  if (hooks.query) {
150
- let query = unwrapSchema(hooks.query, vendors);
234
+ const query = unwrapReference(unwrapSchema(hooks.query, vendors), definitions);
151
235
  if (query && query.type === "object" && query.properties) {
152
236
  const required = query.required || [];
153
237
  for (const [queryName, querySchema] of Object.entries(
@@ -162,7 +246,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
162
246
  }
163
247
  }
164
248
  if (hooks.headers) {
165
- const headers = unwrapSchema(hooks.query, vendors);
249
+ const headers = unwrapReference(unwrapSchema(hooks.query, vendors), definitions);
166
250
  if (headers && headers.type === "object" && headers.properties) {
167
251
  const required = headers.required || [];
168
252
  for (const [headerName, headerSchema] of Object.entries(
@@ -177,7 +261,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
177
261
  }
178
262
  }
179
263
  if (hooks.cookie) {
180
- const cookie = unwrapSchema(hooks.cookie, vendors);
264
+ const cookie = unwrapReference(unwrapSchema(hooks.cookie, vendors), definitions);
181
265
  if (cookie && cookie.type === "object" && cookie.properties) {
182
266
  const required = cookie.required || [];
183
267
  for (const [cookieName, cookieSchema] of Object.entries(
@@ -321,10 +405,9 @@ function toOpenAPISchema(app, exclude, references, vendors) {
321
405
  };
322
406
  }
323
407
  }
324
- const _schemas = app.getGlobalDefinitions?.().type;
325
408
  const schemas = /* @__PURE__ */ Object.create(null);
326
- if (_schemas)
327
- for (const [name, schema] of Object.entries(_schemas)) {
409
+ if (definitions)
410
+ for (const [name, schema] of Object.entries(definitions)) {
328
411
  const jsonSchema = unwrapSchema(schema, vendors);
329
412
  if (jsonSchema) schemas[name] = jsonSchema;
330
413
  }
@@ -6,6 +6,8 @@ export type OpenAPIProvider = 'scalar' | 'swagger-ui' | null;
6
6
  type MaybeArray<T> = T | T[];
7
7
  export type MapJsonSchema = {
8
8
  [vendor: string]: Function;
9
+ } & {
10
+ [vendor in 'zod' | 'effect' | 'valibot' | 'arktype' | 'typemap' | 'yup' | 'joi']?: Function;
9
11
  };
10
12
  export type AdditionalReference = {
11
13
  [path in string]: {
@@ -21,7 +23,7 @@ export type AdditionalReference = {
21
23
  };
22
24
  };
23
25
  export type AdditionalReferences = MaybeArray<AdditionalReference | undefined | (() => AdditionalReference | undefined)>;
24
- export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extends string = '/swagger', Provider extends OpenAPIProvider = 'scalar'> {
26
+ export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extends string = '/swagger'> {
25
27
  /**
26
28
  * @default true
27
29
  */
@@ -67,7 +69,7 @@ export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extend
67
69
  * @see https://github.com/scalar/scalar
68
70
  * @see https://github.com/swagger-api/swagger-ui
69
71
  */
70
- provider?: Provider;
72
+ provider?: OpenAPIProvider;
71
73
  /**
72
74
  * Additional reference for each endpoint
73
75
  */
@@ -81,7 +83,7 @@ export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extend
81
83
  * import { toJsonSchema } from '@valibot/to-json-schema'
82
84
  *
83
85
  * openapi({
84
- * vendors: {
86
+ * mapJsonSchema: {
85
87
  * valibot: toJsonSchema
86
88
  * }
87
89
  * })
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { Elysia } from 'elysia';
2
- import type { ElysiaOpenAPIConfig, OpenAPIProvider } from './types';
2
+ import type { ElysiaOpenAPIConfig } from './types';
3
3
  /**
4
4
  * Plugin for [elysia](https://github.com/elysiajs/elysia) that auto-generate OpenAPI documentation page.
5
5
  *
6
6
  * @see https://github.com/elysiajs/elysia-swagger
7
7
  */
8
- export declare const openapi: <const Enabled extends boolean = true, const Path extends string = "/openapi", const Provider extends OpenAPIProvider = "scalar">({ enabled, path, provider, specPath, documentation, exclude, swagger, scalar, references, mapJsonSchema }?: ElysiaOpenAPIConfig<Enabled, Path, Provider>) => Elysia<"", {
8
+ export declare const openapi: <const Enabled extends boolean = true, const Path extends string = "/openapi">({ enabled, path, provider, specPath, documentation, exclude, swagger, scalar, references, mapJsonSchema }?: ElysiaOpenAPIConfig<Enabled, Path>) => Elysia<"", {
9
9
  decorator: {};
10
10
  store: {};
11
11
  derive: {};
package/dist/index.mjs CHANGED
@@ -290,12 +290,51 @@ var getPossiblePath = (path) => {
290
290
  }
291
291
  return paths;
292
292
  };
293
- var isValidSchema = (schema) => typeof schema === "object" && (Kind in schema && schema[Kind] !== "Unknown" || schema.type || schema.properties || schema.items);
293
+ var isValidSchema = (schema) => schema && typeof schema === "object" && (Kind in schema && schema[Kind] !== "Unknown" || schema.type || schema.properties || schema.items);
294
294
  var getLoosePath = (path) => {
295
295
  if (path.charCodeAt(path.length - 1) === 47)
296
296
  return path.slice(0, path.length - 1);
297
297
  return path + "/";
298
298
  };
299
+ var warnings = {
300
+ zod4: `import openapi from '@elysiajs/openapi'
301
+ import * as z from 'zod'
302
+
303
+ openapi({
304
+ mapJsonSchema: {
305
+ zod: z.toJSONSchema
306
+ }
307
+ })`,
308
+ zod3: `import openapi from '@elysiajs/openapi'
309
+ import { zodToJsonSchema } from 'zod-to-json-schema'
310
+
311
+ openapi({
312
+ mapJsonSchema: {
313
+ zod: zodToJsonSchema
314
+ }
315
+ })`,
316
+ valibot: `import { toJsonSchema } from '@valibot/to-json-schema'
317
+
318
+ openapi({
319
+ mapJsonSchema: {
320
+ valibot: toJsonSchema
321
+ }
322
+ })`,
323
+ effect: `import { JSONSchema } from 'effect'
324
+
325
+ openapi({
326
+ mapJsonSchema: {
327
+ effect: JSONSchema.make
328
+ }
329
+ })`
330
+ };
331
+ var warned = {};
332
+ var unwrapReference = (schema, definitions) => {
333
+ if (!schema?.$ref) return schema;
334
+ const name = schema.$ref.slice(schema.$ref.lastIndexOf("/") + 1);
335
+ if (schema.$ref && definitions[name]) schema = definitions[name];
336
+ return schema;
337
+ };
299
338
  var unwrapSchema = (schema, mapJsonSchema) => {
300
339
  if (!schema) return;
301
340
  if (typeof schema === "string") schema = toRef(schema);
@@ -304,8 +343,49 @@ var unwrapSchema = (schema, mapJsonSchema) => {
304
343
  const vendor = schema["~standard"].vendor;
305
344
  if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
306
345
  return mapJsonSchema[vendor](schema);
307
- if (vendor === "zod" || vendor === "sury")
308
- return schema.toJSONSchema?.();
346
+ switch (vendor) {
347
+ case "zod":
348
+ if (warned.zod4 || warned.zod3) break;
349
+ console.warn(
350
+ "[@elysiajs/openapi] Zod doesn't provide JSON Schema method on the schema"
351
+ );
352
+ if ("_zod" in schema) {
353
+ warned.zod4 = true;
354
+ console.warn(
355
+ "For Zod v4, please provide z.toJSONSchema as follows:\n"
356
+ );
357
+ console.warn(warnings.zod4);
358
+ } else {
359
+ warned.zod3 = true;
360
+ console.warn(
361
+ "For Zod v3, please install zod-to-json-schema package and use it like this:\n"
362
+ );
363
+ console.warn(warnings.zod3);
364
+ }
365
+ break;
366
+ case "valibot":
367
+ if (warned.valibot) break;
368
+ warned.valibot = true;
369
+ console.warn(
370
+ "[@elysiajs/openapi] Valibot require a separate package for JSON Schema conversion"
371
+ );
372
+ console.warn(
373
+ "Please install @valibot/to-json-schema package and use it like this:\n"
374
+ );
375
+ console.warn(warnings.valibot);
376
+ break;
377
+ case "effect":
378
+ if (warned.effect) break;
379
+ warned.effect = true;
380
+ console.warn(
381
+ "[@elysiajs/openapi] Effect Schema doesn't provide JSON Schema method on the schema"
382
+ );
383
+ console.warn(
384
+ "please provide JSONSchema from 'effect' package as follows:\n"
385
+ );
386
+ console.warn(warnings.effect);
387
+ break;
388
+ }
309
389
  if (vendor === "arktype")
310
390
  return schema?.toJsonSchema?.();
311
391
  return schema.toJSONSchema?.() ?? schema?.toJsonSchema?.();
@@ -318,6 +398,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
318
398
  } = exclude ?? {};
319
399
  const excludePaths = Array.isArray(exclude?.paths) ? exclude.paths : typeof exclude?.paths !== "undefined" ? [exclude.paths] : [];
320
400
  const paths = /* @__PURE__ */ Object.create(null);
401
+ const definitions = app.getGlobalDefinitions?.().type;
321
402
  const routes = app.getGlobalRoutes();
322
403
  if (references) {
323
404
  if (!Array.isArray(references)) references = [references];
@@ -332,7 +413,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
332
413
  if (excludeStaticFile && route.path.includes(".") || excludePaths.includes(route.path) || excludeMethods.includes(method))
333
414
  continue;
334
415
  const hooks = route.hooks ?? {};
335
- if (references)
416
+ if (references?.length)
336
417
  for (const reference of references) {
337
418
  if (!reference) continue;
338
419
  const refer = reference[route.path]?.[method] ?? reference[getLoosePath(route.path)]?.[method];
@@ -363,7 +444,10 @@ function toOpenAPISchema(app, exclude, references, vendors) {
363
444
  };
364
445
  const parameters = [];
365
446
  if (hooks.params) {
366
- const params = unwrapSchema(hooks.params, vendors);
447
+ const params = unwrapReference(
448
+ unwrapSchema(hooks.params, vendors),
449
+ definitions
450
+ );
367
451
  if (params && params.type === "object" && params.properties)
368
452
  for (const [paramName, paramSchema] of Object.entries(
369
453
  params.properties
@@ -377,7 +461,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
377
461
  });
378
462
  }
379
463
  if (hooks.query) {
380
- let query = unwrapSchema(hooks.query, vendors);
464
+ const query = unwrapReference(unwrapSchema(hooks.query, vendors), definitions);
381
465
  if (query && query.type === "object" && query.properties) {
382
466
  const required = query.required || [];
383
467
  for (const [queryName, querySchema] of Object.entries(
@@ -392,7 +476,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
392
476
  }
393
477
  }
394
478
  if (hooks.headers) {
395
- const headers = unwrapSchema(hooks.query, vendors);
479
+ const headers = unwrapReference(unwrapSchema(hooks.query, vendors), definitions);
396
480
  if (headers && headers.type === "object" && headers.properties) {
397
481
  const required = headers.required || [];
398
482
  for (const [headerName, headerSchema] of Object.entries(
@@ -407,7 +491,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
407
491
  }
408
492
  }
409
493
  if (hooks.cookie) {
410
- const cookie = unwrapSchema(hooks.cookie, vendors);
494
+ const cookie = unwrapReference(unwrapSchema(hooks.cookie, vendors), definitions);
411
495
  if (cookie && cookie.type === "object" && cookie.properties) {
412
496
  const required = cookie.required || [];
413
497
  for (const [cookieName, cookieSchema] of Object.entries(
@@ -551,10 +635,9 @@ function toOpenAPISchema(app, exclude, references, vendors) {
551
635
  };
552
636
  }
553
637
  }
554
- const _schemas = app.getGlobalDefinitions?.().type;
555
638
  const schemas = /* @__PURE__ */ Object.create(null);
556
- if (_schemas)
557
- for (const [name, schema] of Object.entries(_schemas)) {
639
+ if (definitions)
640
+ for (const [name, schema] of Object.entries(definitions)) {
558
641
  const jsonSchema = unwrapSchema(schema, vendors);
559
642
  if (jsonSchema) schemas[name] = jsonSchema;
560
643
  }
@@ -657,6 +740,10 @@ var openapi = ({
657
740
  };
658
741
  },
659
742
  {
743
+ error({ error }) {
744
+ console.log("[@elysiajs/openapi] error at specPath");
745
+ console.warn(error);
746
+ },
660
747
  detail: {
661
748
  hide: true
662
749
  }
package/dist/openapi.mjs CHANGED
@@ -31,12 +31,51 @@ var getPossiblePath = (path) => {
31
31
  }
32
32
  return paths;
33
33
  };
34
- var isValidSchema = (schema) => typeof schema === "object" && (Kind in schema && schema[Kind] !== "Unknown" || schema.type || schema.properties || schema.items);
34
+ var isValidSchema = (schema) => schema && typeof schema === "object" && (Kind in schema && schema[Kind] !== "Unknown" || schema.type || schema.properties || schema.items);
35
35
  var getLoosePath = (path) => {
36
36
  if (path.charCodeAt(path.length - 1) === 47)
37
37
  return path.slice(0, path.length - 1);
38
38
  return path + "/";
39
39
  };
40
+ var warnings = {
41
+ zod4: `import openapi from '@elysiajs/openapi'
42
+ import * as z from 'zod'
43
+
44
+ openapi({
45
+ mapJsonSchema: {
46
+ zod: z.toJSONSchema
47
+ }
48
+ })`,
49
+ zod3: `import openapi from '@elysiajs/openapi'
50
+ import { zodToJsonSchema } from 'zod-to-json-schema'
51
+
52
+ openapi({
53
+ mapJsonSchema: {
54
+ zod: zodToJsonSchema
55
+ }
56
+ })`,
57
+ valibot: `import { toJsonSchema } from '@valibot/to-json-schema'
58
+
59
+ openapi({
60
+ mapJsonSchema: {
61
+ valibot: toJsonSchema
62
+ }
63
+ })`,
64
+ effect: `import { JSONSchema } from 'effect'
65
+
66
+ openapi({
67
+ mapJsonSchema: {
68
+ effect: JSONSchema.make
69
+ }
70
+ })`
71
+ };
72
+ var warned = {};
73
+ var unwrapReference = (schema, definitions) => {
74
+ if (!schema?.$ref) return schema;
75
+ const name = schema.$ref.slice(schema.$ref.lastIndexOf("/") + 1);
76
+ if (schema.$ref && definitions[name]) schema = definitions[name];
77
+ return schema;
78
+ };
40
79
  var unwrapSchema = (schema, mapJsonSchema) => {
41
80
  if (!schema) return;
42
81
  if (typeof schema === "string") schema = toRef(schema);
@@ -45,8 +84,49 @@ var unwrapSchema = (schema, mapJsonSchema) => {
45
84
  const vendor = schema["~standard"].vendor;
46
85
  if (mapJsonSchema?.[vendor] && typeof mapJsonSchema[vendor] === "function")
47
86
  return mapJsonSchema[vendor](schema);
48
- if (vendor === "zod" || vendor === "sury")
49
- return schema.toJSONSchema?.();
87
+ switch (vendor) {
88
+ case "zod":
89
+ if (warned.zod4 || warned.zod3) break;
90
+ console.warn(
91
+ "[@elysiajs/openapi] Zod doesn't provide JSON Schema method on the schema"
92
+ );
93
+ if ("_zod" in schema) {
94
+ warned.zod4 = true;
95
+ console.warn(
96
+ "For Zod v4, please provide z.toJSONSchema as follows:\n"
97
+ );
98
+ console.warn(warnings.zod4);
99
+ } else {
100
+ warned.zod3 = true;
101
+ console.warn(
102
+ "For Zod v3, please install zod-to-json-schema package and use it like this:\n"
103
+ );
104
+ console.warn(warnings.zod3);
105
+ }
106
+ break;
107
+ case "valibot":
108
+ if (warned.valibot) break;
109
+ warned.valibot = true;
110
+ console.warn(
111
+ "[@elysiajs/openapi] Valibot require a separate package for JSON Schema conversion"
112
+ );
113
+ console.warn(
114
+ "Please install @valibot/to-json-schema package and use it like this:\n"
115
+ );
116
+ console.warn(warnings.valibot);
117
+ break;
118
+ case "effect":
119
+ if (warned.effect) break;
120
+ warned.effect = true;
121
+ console.warn(
122
+ "[@elysiajs/openapi] Effect Schema doesn't provide JSON Schema method on the schema"
123
+ );
124
+ console.warn(
125
+ "please provide JSONSchema from 'effect' package as follows:\n"
126
+ );
127
+ console.warn(warnings.effect);
128
+ break;
129
+ }
50
130
  if (vendor === "arktype")
51
131
  return schema?.toJsonSchema?.();
52
132
  return schema.toJSONSchema?.() ?? schema?.toJsonSchema?.();
@@ -59,6 +139,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
59
139
  } = exclude ?? {};
60
140
  const excludePaths = Array.isArray(exclude?.paths) ? exclude.paths : typeof exclude?.paths !== "undefined" ? [exclude.paths] : [];
61
141
  const paths = /* @__PURE__ */ Object.create(null);
142
+ const definitions = app.getGlobalDefinitions?.().type;
62
143
  const routes = app.getGlobalRoutes();
63
144
  if (references) {
64
145
  if (!Array.isArray(references)) references = [references];
@@ -73,7 +154,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
73
154
  if (excludeStaticFile && route.path.includes(".") || excludePaths.includes(route.path) || excludeMethods.includes(method))
74
155
  continue;
75
156
  const hooks = route.hooks ?? {};
76
- if (references)
157
+ if (references?.length)
77
158
  for (const reference of references) {
78
159
  if (!reference) continue;
79
160
  const refer = reference[route.path]?.[method] ?? reference[getLoosePath(route.path)]?.[method];
@@ -104,7 +185,10 @@ function toOpenAPISchema(app, exclude, references, vendors) {
104
185
  };
105
186
  const parameters = [];
106
187
  if (hooks.params) {
107
- const params = unwrapSchema(hooks.params, vendors);
188
+ const params = unwrapReference(
189
+ unwrapSchema(hooks.params, vendors),
190
+ definitions
191
+ );
108
192
  if (params && params.type === "object" && params.properties)
109
193
  for (const [paramName, paramSchema] of Object.entries(
110
194
  params.properties
@@ -118,7 +202,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
118
202
  });
119
203
  }
120
204
  if (hooks.query) {
121
- let query = unwrapSchema(hooks.query, vendors);
205
+ const query = unwrapReference(unwrapSchema(hooks.query, vendors), definitions);
122
206
  if (query && query.type === "object" && query.properties) {
123
207
  const required = query.required || [];
124
208
  for (const [queryName, querySchema] of Object.entries(
@@ -133,7 +217,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
133
217
  }
134
218
  }
135
219
  if (hooks.headers) {
136
- const headers = unwrapSchema(hooks.query, vendors);
220
+ const headers = unwrapReference(unwrapSchema(hooks.query, vendors), definitions);
137
221
  if (headers && headers.type === "object" && headers.properties) {
138
222
  const required = headers.required || [];
139
223
  for (const [headerName, headerSchema] of Object.entries(
@@ -148,7 +232,7 @@ function toOpenAPISchema(app, exclude, references, vendors) {
148
232
  }
149
233
  }
150
234
  if (hooks.cookie) {
151
- const cookie = unwrapSchema(hooks.cookie, vendors);
235
+ const cookie = unwrapReference(unwrapSchema(hooks.cookie, vendors), definitions);
152
236
  if (cookie && cookie.type === "object" && cookie.properties) {
153
237
  const required = cookie.required || [];
154
238
  for (const [cookieName, cookieSchema] of Object.entries(
@@ -292,10 +376,9 @@ function toOpenAPISchema(app, exclude, references, vendors) {
292
376
  };
293
377
  }
294
378
  }
295
- const _schemas = app.getGlobalDefinitions?.().type;
296
379
  const schemas = /* @__PURE__ */ Object.create(null);
297
- if (_schemas)
298
- for (const [name, schema] of Object.entries(_schemas)) {
380
+ if (definitions)
381
+ for (const [name, schema] of Object.entries(definitions)) {
299
382
  const jsonSchema = unwrapSchema(schema, vendors);
300
383
  if (jsonSchema) schemas[name] = jsonSchema;
301
384
  }
package/dist/types.d.ts CHANGED
@@ -6,6 +6,8 @@ export type OpenAPIProvider = 'scalar' | 'swagger-ui' | null;
6
6
  type MaybeArray<T> = T | T[];
7
7
  export type MapJsonSchema = {
8
8
  [vendor: string]: Function;
9
+ } & {
10
+ [vendor in 'zod' | 'effect' | 'valibot' | 'arktype' | 'typemap' | 'yup' | 'joi']?: Function;
9
11
  };
10
12
  export type AdditionalReference = {
11
13
  [path in string]: {
@@ -21,7 +23,7 @@ export type AdditionalReference = {
21
23
  };
22
24
  };
23
25
  export type AdditionalReferences = MaybeArray<AdditionalReference | undefined | (() => AdditionalReference | undefined)>;
24
- export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extends string = '/swagger', Provider extends OpenAPIProvider = 'scalar'> {
26
+ export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extends string = '/swagger'> {
25
27
  /**
26
28
  * @default true
27
29
  */
@@ -67,7 +69,7 @@ export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extend
67
69
  * @see https://github.com/scalar/scalar
68
70
  * @see https://github.com/swagger-api/swagger-ui
69
71
  */
70
- provider?: Provider;
72
+ provider?: OpenAPIProvider;
71
73
  /**
72
74
  * Additional reference for each endpoint
73
75
  */
@@ -81,7 +83,7 @@ export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extend
81
83
  * import { toJsonSchema } from '@valibot/to-json-schema'
82
84
  *
83
85
  * openapi({
84
- * vendors: {
86
+ * mapJsonSchema: {
85
87
  * valibot: toJsonSchema
86
88
  * }
87
89
  * })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elysiajs/openapi",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "Plugin for Elysia to auto-generate API documentation",
5
5
  "author": {
6
6
  "name": "saltyAom",
@@ -79,7 +79,8 @@
79
79
  "@scalar/types": "^0.2.13",
80
80
  "@sinclair/typemap": "^0.10.1",
81
81
  "@types/bun": "1.2.20",
82
- "elysia": "1.4.0",
82
+ "effect": "^3.17.13",
83
+ "elysia": "1.4.6",
83
84
  "eslint": "9.6.0",
84
85
  "openapi-types": "^12.1.3",
85
86
  "tsup": "^8.5.0",