@elysiajs/openapi 1.3.2

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.
@@ -0,0 +1,137 @@
1
+ import type { TSchema } from 'elysia';
2
+ import type { OpenAPIV3 } from 'openapi-types';
3
+ import type { ApiReferenceConfiguration } from '@scalar/types';
4
+ import type { SwaggerUIOptions } from './swagger/types';
5
+ export type OpenAPIProvider = 'scalar' | 'swagger-ui' | null;
6
+ type MaybeArray<T> = T | T[];
7
+ export type AdditionalReference = {
8
+ [path in string]: {
9
+ [method in string]: {
10
+ params: TSchema;
11
+ query: TSchema;
12
+ headers: TSchema;
13
+ body: TSchema;
14
+ response: {
15
+ [status in number]: TSchema;
16
+ };
17
+ };
18
+ };
19
+ };
20
+ export type AdditionalReferences = MaybeArray<AdditionalReference | undefined | (() => AdditionalReference | undefined)>;
21
+ export interface ElysiaOpenAPIConfig<Enabled extends boolean = true, Path extends string = '/swagger', Provider extends OpenAPIProvider = 'scalar'> {
22
+ /**
23
+ * @default true
24
+ */
25
+ enabled?: Enabled;
26
+ /**
27
+ * OpenAPI config
28
+ *
29
+ * @see https://spec.openapis.org/oas/v3.0.3.html
30
+ */
31
+ documentation?: Omit<Partial<OpenAPIV3.Document>, 'x-express-openapi-additional-middleware' | 'x-express-openapi-validation-strict'>;
32
+ exclude?: {
33
+ /**
34
+ * Exclude methods from OpenAPI
35
+ */
36
+ methods?: string[];
37
+ /**
38
+ * Paths to exclude from OpenAPI endpoint
39
+ *
40
+ * @default []
41
+ */
42
+ paths?: string | RegExp | (string | RegExp)[];
43
+ /**
44
+ * Determine if OpenAPI should exclude static files.
45
+ *
46
+ * @default true
47
+ */
48
+ staticFile?: boolean;
49
+ /**
50
+ * Exclude tags from OpenAPI
51
+ */
52
+ tags?: string[];
53
+ };
54
+ /**
55
+ * The endpoint to expose OpenAPI Documentation
56
+ *
57
+ * @default '/openapi'
58
+ */
59
+ path?: Path;
60
+ /**
61
+ * Choose your provider, Scalar or Swagger UI
62
+ *
63
+ * @default 'scalar'
64
+ * @see https://github.com/scalar/scalar
65
+ * @see https://github.com/swagger-api/swagger-ui
66
+ */
67
+ provider?: Provider;
68
+ /**
69
+ * Additional reference for each endpoint
70
+ */
71
+ references?: AdditionalReferences;
72
+ /**
73
+ * Scalar configuration to customize scalar
74
+ *'
75
+ * @see https://github.com/scalar/scalar/blob/main/documentation/configuration.md
76
+ */
77
+ scalar?: ApiReferenceConfiguration & {
78
+ /**
79
+ * Version to use for Scalar cdn bundle
80
+ *
81
+ * @default 'latest'
82
+ * @see https://github.com/scalar/scalar
83
+ */
84
+ version?: string;
85
+ /**
86
+ * Optional override to specifying the path for the Scalar bundle
87
+ *
88
+ * Custom URL or path to locally hosted Scalar bundle
89
+ *
90
+ * Lease blank to use default jsdeliver.net CDN
91
+ *
92
+ * @default ''
93
+ * @example 'https://unpkg.com/@scalar/api-reference@1.13.10/dist/browser/standalone.js'
94
+ * @example '/public/standalone.js'
95
+ * @see https://github.com/scalar/scalar
96
+ */
97
+ cdn?: string;
98
+ };
99
+ /**
100
+ * The endpoint to expose OpenAPI JSON specification
101
+ *
102
+ * @default '/${path}/json'
103
+ */
104
+ specPath?: string;
105
+ /**
106
+ * Options to send to SwaggerUIBundle
107
+ * Currently, options that are defined as functions such as requestInterceptor
108
+ * and onComplete are not supported.
109
+ */
110
+ swagger?: Omit<Partial<SwaggerUIOptions>, 'dom_id' | 'dom_node' | 'spec' | 'url' | 'urls' | 'layout' | 'pluginsOptions' | 'plugins' | 'presets' | 'onComplete' | 'requestInterceptor' | 'responseInterceptor' | 'modelPropertyMacro' | 'parameterMacro'> & {
111
+ /**
112
+ * Custom Swagger CSS
113
+ */
114
+ theme?: string | {
115
+ light: string;
116
+ dark: string;
117
+ };
118
+ /**
119
+ * Version to use for swagger cdn bundle
120
+ *
121
+ * @see unpkg.com/swagger-ui-dist
122
+ *
123
+ * @default 4.18.2
124
+ */
125
+ version?: string;
126
+ /**
127
+ * Using poor man dark mode 😭
128
+ */
129
+ autoDarkMode?: boolean;
130
+ /**
131
+ * Optional override to specifying the path for the Swagger UI bundle
132
+ * Custom URL or path to locally hosted Swagger UI bundle
133
+ */
134
+ cdn?: string;
135
+ };
136
+ }
137
+ export {};
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/types.ts
17
+ var types_exports = {};
18
+ module.exports = __toCommonJS(types_exports);
@@ -0,0 +1,36 @@
1
+ import { AdditionalReference } from '../types';
2
+ interface OpenAPIGeneratorOptions {
3
+ /**
4
+ * Path to tsconfig.json
5
+ * @default tsconfig.json
6
+ */
7
+ tsconfigPath?: string;
8
+ /**
9
+ * Name of the Elysia instance
10
+ *
11
+ * If multiple instances are found,
12
+ * instanceName should be provided
13
+ */
14
+ instanceName?: string;
15
+ /**
16
+ * Project root directory
17
+ *
18
+ * @default process.cwd()
19
+ */
20
+ projectRoot?: string;
21
+ }
22
+ /**
23
+ * Auto generate OpenAPI schema from Elysia instance
24
+ *
25
+ * It's expected that this command should run in project root
26
+ *
27
+ * @experimental use at your own risk
28
+ */
29
+ export declare const fromTypes: (
30
+ /**
31
+ * Path to file where Elysia instance is
32
+ *
33
+ * The path must export an Elysia instance
34
+ */
35
+ targetFilePath: string, { tsconfigPath, instanceName, projectRoot }?: OpenAPIGeneratorOptions) => () => AdditionalReference | undefined;
36
+ export {};
@@ -0,0 +1,108 @@
1
+ // src/gen/index.ts
2
+ import {
3
+ readFileSync,
4
+ mkdirSync,
5
+ writeFileSync,
6
+ rmSync,
7
+ existsSync
8
+ } from "fs";
9
+ import { TypeBox } from "@sinclair/typemap";
10
+ import { tmpdir } from "os";
11
+ import { join } from "path";
12
+ import { spawnSync } from "child_process";
13
+ var matchRoute = /: Elysia<(.*)>/gs;
14
+ var matchStatus = /(\d{3}):/gs;
15
+ var wrapStatusInQuote = (value) => value.replace(matchStatus, '"$1":');
16
+ var exec = (command, cwd) => spawnSync(command, {
17
+ shell: true,
18
+ cwd,
19
+ stdio: "inherit"
20
+ });
21
+ var fromTypes = (targetFilePath, {
22
+ tsconfigPath = "tsconfig.json",
23
+ instanceName,
24
+ projectRoot = process.cwd()
25
+ } = {}) => () => {
26
+ if (!targetFilePath.endsWith(".ts") && !targetFilePath.endsWith(".tsx"))
27
+ throw new Error("Only .ts files are supported");
28
+ const tmpRoot = join(tmpdir(), ".ElysiaAutoOpenAPI");
29
+ if (existsSync(tmpRoot))
30
+ rmSync(tmpRoot, { recursive: true, force: true });
31
+ mkdirSync(tmpRoot, { recursive: true });
32
+ const extendsRef = existsSync(join(projectRoot, "tsconfig.json")) ? `"extends": "${join(projectRoot, "tsconfig.json")}",` : "";
33
+ if (!join(projectRoot, targetFilePath))
34
+ throw new Error("Target file does not exist");
35
+ writeFileSync(
36
+ join(tmpRoot, tsconfigPath),
37
+ `{
38
+ ${extendsRef}
39
+ "compilerOptions": {
40
+ "lib": ["ESNext"],
41
+ "module": "ESNext",
42
+ "noEmit": false,
43
+ "moduleResolution": "bundler",
44
+ "skipLibCheck": true,
45
+ "skipDefaultLibCheck": true,
46
+ "emitDeclarationOnly": true,
47
+ "outDir": "./dist"
48
+ },
49
+ "include": ["${join(projectRoot, targetFilePath)}"]
50
+ }`
51
+ );
52
+ exec(`tsc`, tmpRoot);
53
+ try {
54
+ const declaration = readFileSync(
55
+ join(
56
+ tmpRoot,
57
+ "dist",
58
+ targetFilePath.replace(/.tsx$/, ".ts").replace(/.ts$/, ".d.ts")
59
+ ),
60
+ "utf8"
61
+ );
62
+ if (existsSync(tmpRoot))
63
+ rmSync(tmpRoot, { recursive: true, force: true });
64
+ let instance = declaration.match(
65
+ instanceName ? new RegExp(`${instanceName}: Elysia<(.*)`, "gs") : matchRoute
66
+ )?.[0];
67
+ if (!instance) return;
68
+ for (let i = 0; i < 3; i++)
69
+ instance = instance.slice(instance.indexOf("}, {", 3));
70
+ const routesString = wrapStatusInQuote(instance).slice(
71
+ 3,
72
+ instance.indexOf("}, {", 3)
73
+ ) + "}\n}\n";
74
+ const routes = {};
75
+ for (let route of routesString.slice(1).split("} & {")) {
76
+ route = "{" + route + "}";
77
+ let schema = TypeBox(route);
78
+ if (schema.type !== "object") continue;
79
+ const paths = [];
80
+ while (true) {
81
+ const keys = Object.keys(schema.properties);
82
+ if (!keys.length || keys.length > 1) break;
83
+ paths.push(keys[0]);
84
+ schema = schema.properties[keys[0]];
85
+ if (!schema?.properties) break;
86
+ }
87
+ const method = paths.pop();
88
+ const path = "/" + paths.join("/");
89
+ schema = schema.properties;
90
+ if (schema?.response?.type === "object") {
91
+ const responseSchema = {};
92
+ for (const key in schema.response.properties)
93
+ responseSchema[key] = schema.response.properties[key];
94
+ schema.response = responseSchema;
95
+ }
96
+ if (!routes[path]) routes[path] = {};
97
+ routes[path][method.toLowerCase()] = schema;
98
+ }
99
+ return routes;
100
+ } catch (error) {
101
+ console.warn("Failed to generate OpenAPI schema");
102
+ console.warn(error);
103
+ return;
104
+ }
105
+ };
106
+ export {
107
+ fromTypes
108
+ };
@@ -0,0 +1,35 @@
1
+ import { Elysia } from 'elysia';
2
+ import type { ElysiaOpenAPIConfig, OpenAPIProvider } from './types';
3
+ /**
4
+ * Plugin for [elysia](https://github.com/elysiajs/elysia) that auto-generate OpenAPI documentation page.
5
+ *
6
+ * @see https://github.com/elysiajs/elysia-swagger
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 }?: ElysiaOpenAPIConfig<Enabled, Path, Provider>) => Elysia<"", {
9
+ decorator: {};
10
+ store: {};
11
+ derive: {};
12
+ resolve: {};
13
+ }, {
14
+ typebox: {};
15
+ error: {};
16
+ }, {
17
+ schema: {};
18
+ standaloneSchema: {};
19
+ macro: {};
20
+ macroFn: {};
21
+ parser: {};
22
+ }, {}, {
23
+ derive: {};
24
+ resolve: {};
25
+ schema: {};
26
+ standaloneSchema: {};
27
+ }, {
28
+ derive: {};
29
+ resolve: {};
30
+ schema: {};
31
+ standaloneSchema: {};
32
+ }>;
33
+ export { toOpenAPISchema, withHeaders } from './openapi';
34
+ export type { ElysiaOpenAPIConfig };
35
+ export default openapi;