@elysiajs/openapi 1.3.11 → 1.4.0-exp.0

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.
@@ -36,9 +36,10 @@ var fromTypes = (targetFilePath, {
36
36
  instanceName,
37
37
  projectRoot = process.cwd(),
38
38
  overrideOutputPath,
39
- debug = false
39
+ debug = false,
40
+ compilerOptions,
41
+ tmpRoot = (0, import_path.join)((0, import_os.tmpdir)(), ".ElysiaAutoOpenAPI")
40
42
  } = {}) => () => {
41
- const tmpRoot = (0, import_path.join)((0, import_os.tmpdir)(), ".ElysiaAutoOpenAPI");
42
43
  try {
43
44
  if (!targetFilePath.endsWith(".ts") && !targetFilePath.endsWith(".tsx"))
44
45
  throw new Error("Only .ts files are supported");
@@ -57,25 +58,27 @@ var fromTypes = (targetFilePath, {
57
58
  (0, import_fs.mkdirSync)(tmpRoot, { recursive: true });
58
59
  const tsconfig = tsconfigPath.startsWith("/") ? tsconfigPath : (0, import_path.join)(projectRoot, tsconfigPath);
59
60
  let extendsRef = (0, import_fs.existsSync)(tsconfig) ? `"extends": "${(0, import_path.join)(projectRoot, "tsconfig.json")}",` : "";
61
+ let distDir = (0, import_path.join)(tmpRoot, "dist");
60
62
  if (typeof process !== "undefined" && process.platform === "win32") {
61
63
  extendsRef = extendsRef.replace(/\\/g, "/");
62
64
  src = src.replace(/\\/g, "/");
65
+ distDir = distDir.replace(/\\/g, "/");
63
66
  }
64
67
  (0, import_fs.writeFileSync)(
65
68
  (0, import_path.join)(tmpRoot, "tsconfig.json"),
66
69
  `{
67
70
  ${extendsRef}
68
- "compilerOptions": {
69
- "lib": ["ESNext"],
70
- "module": "ESNext",
71
- "noEmit": false,
72
- "declaration": true,
73
- "emitDeclarationOnly": true,
74
- "moduleResolution": "bundler",
75
- "skipLibCheck": true,
76
- "skipDefaultLibCheck": true,
77
- "outDir": "./dist"
78
- },
71
+ "compilerOptions": ${compilerOptions ? JSON.stringify(compilerOptions) : `{
72
+ "lib": ["ESNext"],
73
+ "module": "ESNext",
74
+ "noEmit": false,
75
+ "declaration": true,
76
+ "emitDeclarationOnly": true,
77
+ "moduleResolution": "bundler",
78
+ "skipLibCheck": true,
79
+ "skipDefaultLibCheck": true,
80
+ "outDir": "${distDir}"
81
+ }`},
79
82
  "include": ["${src}"]
80
83
  }`
81
84
  );
@@ -117,6 +120,11 @@ var fromTypes = (targetFilePath, {
117
120
  );
118
121
  console.warn(tempFiles);
119
122
  }
123
+ } else {
124
+ console.log(
125
+ "reason: root folder doesn't exists",
126
+ (0, import_path.join)(tmpRoot, "dist")
127
+ );
120
128
  }
121
129
  return;
122
130
  }
@@ -19,16 +19,19 @@ export declare const openapi: <const Enabled extends boolean = true, const Path
19
19
  macro: {};
20
20
  macroFn: {};
21
21
  parser: {};
22
+ response: {};
22
23
  }, {}, {
23
24
  derive: {};
24
25
  resolve: {};
25
26
  schema: {};
26
27
  standaloneSchema: {};
28
+ response: {};
27
29
  }, {
28
30
  derive: {};
29
31
  resolve: {};
30
32
  schema: {};
31
33
  standaloneSchema: {};
34
+ response: {};
32
35
  }>;
33
36
  export { toOpenAPISchema, withHeaders } from './openapi';
34
37
  export type { ElysiaOpenAPIConfig };
package/dist/cjs/index.js CHANGED
@@ -295,6 +295,7 @@ var Hint = Symbol.for("TypeBox.Hint");
295
295
  var Kind = Symbol.for("TypeBox.Kind");
296
296
 
297
297
  // src/openapi.ts
298
+ var import_xsschema = require("xsschema");
298
299
  var capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
299
300
  var toRef = (name) => import_elysia.t.Ref(`#/components/schemas/${name}`);
300
301
  var toOperationId = (method, paths) => {
@@ -323,7 +324,14 @@ var getLoosePath = (path) => {
323
324
  return path.slice(0, path.length - 1);
324
325
  return path + "/";
325
326
  };
326
- function toOpenAPISchema(app, exclude, references) {
327
+ var unwrapSchema = (schema) => {
328
+ if (!schema) return;
329
+ if (typeof schema === "string") schema = toRef(schema);
330
+ if (Kind in schema) return schema;
331
+ if (Kind in schema === false && schema["~standard"])
332
+ return (0, import_xsschema.toJsonSchema)(schema);
333
+ };
334
+ async function toOpenAPISchema(app, exclude, references) {
327
335
  const {
328
336
  methods: excludeMethods = ["OPTIONS"],
329
337
  staticFile: excludeStaticFile = true,
@@ -376,11 +384,11 @@ function toOpenAPISchema(app, exclude, references) {
376
384
  };
377
385
  const parameters = [];
378
386
  if (hooks.params) {
379
- if (typeof hooks.params === "string")
380
- hooks.params = toRef(hooks.params);
381
- if (hooks.params.type === "object" && hooks.params.properties) {
387
+ let params = unwrapSchema(hooks.params);
388
+ if (params) params = await params;
389
+ if (params && params.type === "object" && params.properties)
382
390
  for (const [paramName, paramSchema] of Object.entries(
383
- hooks.params.properties
391
+ params.properties
384
392
  ))
385
393
  parameters.push({
386
394
  name: paramName,
@@ -389,15 +397,14 @@ function toOpenAPISchema(app, exclude, references) {
389
397
  // Path parameters are always required
390
398
  schema: paramSchema
391
399
  });
392
- }
393
400
  }
394
401
  if (hooks.query) {
395
- if (typeof hooks.query === "string")
396
- hooks.query = toRef(hooks.query);
397
- if (hooks.query.type === "object" && hooks.query.properties) {
398
- const required = hooks.query.required || [];
402
+ let query = unwrapSchema(hooks.query);
403
+ if (query) query = await query;
404
+ if (query && query.type === "object" && query.properties) {
405
+ const required = query.required || [];
399
406
  for (const [queryName, querySchema] of Object.entries(
400
- hooks.query.properties
407
+ query.properties
401
408
  ))
402
409
  parameters.push({
403
410
  name: queryName,
@@ -408,12 +415,12 @@ function toOpenAPISchema(app, exclude, references) {
408
415
  }
409
416
  }
410
417
  if (hooks.headers) {
411
- if (typeof hooks.headers === "string")
412
- hooks.headers = toRef(hooks.headers);
413
- if (hooks.headers.type === "object" && hooks.headers.properties) {
414
- const required = hooks.headers.required || [];
418
+ let headers = unwrapSchema(hooks.query);
419
+ if (headers) headers = await headers;
420
+ if (headers && headers.type === "object" && headers.properties) {
421
+ const required = headers.required || [];
415
422
  for (const [headerName, headerSchema] of Object.entries(
416
- hooks.headers.properties
423
+ headers.properties
417
424
  ))
418
425
  parameters.push({
419
426
  name: headerName,
@@ -424,12 +431,12 @@ function toOpenAPISchema(app, exclude, references) {
424
431
  }
425
432
  }
426
433
  if (hooks.cookie) {
427
- if (typeof hooks.cookie === "string")
428
- hooks.cookie = toRef(hooks.cookie);
429
- if (hooks.cookie.type === "object" && hooks.cookie.properties) {
430
- const required = hooks.cookie.required || [];
434
+ let cookie = unwrapSchema(hooks.cookie);
435
+ if (cookie) cookie = await cookie;
436
+ if (cookie && cookie.type === "object" && cookie.properties) {
437
+ const required = cookie.required || [];
431
438
  for (const [cookieName, cookieSchema] of Object.entries(
432
- hooks.cookie.properties
439
+ cookie.properties
433
440
  ))
434
441
  parameters.push({
435
442
  name: cookieName,
@@ -441,80 +448,107 @@ function toOpenAPISchema(app, exclude, references) {
441
448
  }
442
449
  if (parameters.length > 0) operation.parameters = parameters;
443
450
  if (hooks.body && method !== "get" && method !== "head") {
444
- if (typeof hooks.body === "string") hooks.body = toRef(hooks.body);
445
- if (hooks.parse) {
446
- const content = {};
447
- const parsers = hooks.parse;
448
- for (const parser of parsers) {
449
- if (typeof parser.fn === "function") continue;
450
- switch (parser.fn) {
451
- case "text":
452
- case "text/plain":
453
- content["text/plain"] = { schema: hooks.body };
454
- continue;
455
- case "urlencoded":
456
- case "application/x-www-form-urlencoded":
457
- content["application/x-www-form-urlencoded"] = {
458
- schema: hooks.body
459
- };
460
- continue;
461
- case "json":
462
- case "application/json":
463
- content["application/json"] = { schema: hooks.body };
464
- continue;
465
- case "formdata":
466
- case "multipart/form-data":
467
- content["multipart/form-data"] = {
468
- schema: hooks.body
469
- };
470
- continue;
451
+ let body = unwrapSchema(hooks.body);
452
+ if (body) body = await body;
453
+ if (body) {
454
+ const { type: _type, description, ...options } = body;
455
+ const type = _type;
456
+ if (hooks.parse) {
457
+ const content = {};
458
+ const parsers = hooks.parse;
459
+ for (const parser of parsers) {
460
+ if (typeof parser.fn === "function") continue;
461
+ switch (parser.fn) {
462
+ case "text":
463
+ case "text/plain":
464
+ content["text/plain"] = { schema: body };
465
+ continue;
466
+ case "urlencoded":
467
+ case "application/x-www-form-urlencoded":
468
+ content["application/x-www-form-urlencoded"] = {
469
+ schema: body
470
+ };
471
+ continue;
472
+ case "json":
473
+ case "application/json":
474
+ content["application/json"] = { schema: body };
475
+ continue;
476
+ case "formdata":
477
+ case "multipart/form-data":
478
+ content["multipart/form-data"] = {
479
+ schema: body
480
+ };
481
+ continue;
482
+ }
471
483
  }
472
- }
473
- operation.requestBody = { content, required: true };
474
- } else {
475
- operation.requestBody = {
476
- content: {
477
- "application/json": {
478
- schema: hooks.body
479
- },
480
- "application/x-www-form-urlencoded": {
481
- schema: hooks.body
484
+ operation.requestBody = {
485
+ description,
486
+ content,
487
+ required: true
488
+ };
489
+ } else {
490
+ operation.requestBody = {
491
+ description,
492
+ content: type === "string" || type === "number" || type === "integer" || type === "boolean" ? {
493
+ "text/plain": body
494
+ } : {
495
+ "application/json": {
496
+ schema: body
497
+ },
498
+ "application/x-www-form-urlencoded": {
499
+ schema: body
500
+ },
501
+ "multipart/form-data": {
502
+ schema: body
503
+ }
482
504
  },
483
- "multipart/form-data": {
484
- schema: hooks.body
485
- }
486
- },
487
- required: true
488
- };
505
+ required: true
506
+ };
507
+ }
489
508
  }
490
509
  }
491
510
  if (hooks.response) {
492
511
  operation.responses = {};
493
512
  if (typeof hooks.response === "object" && !hooks.response.type && !hooks.response.$ref) {
494
513
  for (let [status, schema] of Object.entries(hooks.response)) {
495
- if (typeof schema === "string") schema = toRef(schema);
496
- const { type, examples, $ref, ...options } = schema;
514
+ let response = unwrapSchema(schema);
515
+ if (response) response = await response;
516
+ if (!response) continue;
517
+ const { type: _type, description, ...options } = response;
518
+ const type = _type;
497
519
  operation.responses[status] = {
498
- description: `Response for status ${status}`,
520
+ description: description ?? `Response for status ${status}`,
499
521
  ...options,
500
- content: type === "void" || type === "null" || type === "undefined" ? schema : {
522
+ content: type === "void" || type === "null" || type === "undefined" ? response : type === "string" || type === "number" || type === "integer" || type === "boolean" ? {
523
+ "text/plain": {
524
+ schema: response
525
+ }
526
+ } : {
501
527
  "application/json": {
502
- schema
528
+ schema: response
503
529
  }
504
530
  }
505
531
  };
506
532
  }
507
533
  } else {
508
- if (typeof hooks.response === "string")
509
- hooks.response = toRef(hooks.response);
510
- operation.responses["200"] = {
511
- description: "Successful response",
512
- content: {
513
- "application/json": {
514
- schema: hooks.response
534
+ let response = unwrapSchema(hooks.response);
535
+ if (response) response = await response;
536
+ if (response) {
537
+ const { type: _type, description, ...options } = response;
538
+ const type = _type;
539
+ operation.responses["200"] = {
540
+ description: description ?? `Response for status 200`,
541
+ content: type === "void" || type === "null" || type === "undefined" ? response : type === "string" || type === "number" || type === "integer" || type === "boolean" ? {
542
+ "text/plain": {
543
+ schema: response
544
+ }
545
+ } : {
546
+ "application/json": {
547
+ schema: response
548
+ }
515
549
  }
516
- }
517
- };
550
+ };
551
+ }
518
552
  }
519
553
  }
520
554
  for (let path of getPossiblePath(route.path)) {
@@ -545,7 +579,14 @@ function toOpenAPISchema(app, exclude, references) {
545
579
  };
546
580
  }
547
581
  }
548
- const schemas = app.getGlobalDefinitions?.().type;
582
+ const _schemas = app.getGlobalDefinitions?.().type;
583
+ const schemas = /* @__PURE__ */ Object.create(null);
584
+ if (_schemas)
585
+ for (const [name, schema] of Object.entries(_schemas)) {
586
+ let jsonSchema = unwrapSchema(schema);
587
+ if (jsonSchema instanceof Promise) jsonSchema = await jsonSchema;
588
+ if (jsonSchema) schemas[name] = jsonSchema;
589
+ }
549
590
  return {
550
591
  components: {
551
592
  schemas
@@ -611,13 +652,13 @@ var openapi = ({
611
652
  );
612
653
  }).get(
613
654
  specPath,
614
- function openAPISchema() {
655
+ async function openAPISchema() {
615
656
  if (totalRoutes === app.routes.length) return cachedSchema;
616
657
  totalRoutes = app.routes.length;
617
658
  const {
618
659
  paths,
619
660
  components: { schemas }
620
- } = toOpenAPISchema(app, exclude, references);
661
+ } = await toOpenAPISchema(app, exclude, references);
621
662
  return cachedSchema = {
622
663
  openapi: "3.0.3",
623
664
  ...documentation,
@@ -1,4 +1,4 @@
1
- import { type AnyElysia, type TSchema } from 'elysia';
1
+ import { type AnyElysia, type TSchema, type InputSchema } from 'elysia';
2
2
  import type { OpenAPIV3 } from 'openapi-types';
3
3
  import { type TProperties } from '@sinclair/typebox';
4
4
  import type { AdditionalReferences, ElysiaOpenAPIConfig } from './types';
@@ -10,17 +10,20 @@ 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
+ type MaybePromise<T> = T | Promise<T>;
14
+ export declare const unwrapSchema: (schema: InputSchema["body"]) => MaybePromise<OpenAPIV3.SchemaObject | undefined>;
13
15
  /**
14
16
  * Converts Elysia routes to OpenAPI 3.0.3 paths schema
15
17
  * @param routes Array of Elysia route objects
16
18
  * @returns OpenAPI paths object
17
19
  */
18
- export declare function toOpenAPISchema(app: AnyElysia, exclude?: ElysiaOpenAPIConfig['exclude'], references?: AdditionalReferences): {
20
+ export declare function toOpenAPISchema(app: AnyElysia, exclude?: ElysiaOpenAPIConfig['exclude'], references?: AdditionalReferences): Promise<{
19
21
  components: {
20
- schemas: Record<string, TSchema>;
22
+ schemas: any;
21
23
  };
22
24
  paths: OpenAPIV3.PathsObject<{}, {}>;
23
- };
25
+ }>;
24
26
  export declare const withHeaders: (schema: TSchema, headers: TProperties) => TSchema & {
25
27
  headers: TProperties;
26
28
  };
29
+ export {};