@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.
- package/LICENSE +7 -0
- package/README.md +101 -0
- package/bun.lock +537 -0
- package/bunfig.toml +2 -0
- package/dist/cjs/gen/index.js +127 -0
- package/dist/cjs/index.d.ts +35 -0
- package/dist/cjs/index.js +639 -0
- package/dist/cjs/openapi.d.ts +25 -0
- package/dist/cjs/openapi.js +285 -0
- package/dist/cjs/scalar/index.js +183 -0
- package/dist/cjs/swagger/index.js +129 -0
- package/dist/cjs/swagger/types.js +18 -0
- package/dist/cjs/types.d.ts +137 -0
- package/dist/cjs/types.js +18 -0
- package/dist/gen/index.d.ts +36 -0
- package/dist/gen/index.mjs +108 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.mjs +612 -0
- package/dist/openapi.d.ts +25 -0
- package/dist/openapi.mjs +257 -0
- package/dist/scalar/index.d.ts +3 -0
- package/dist/scalar/index.mjs +158 -0
- package/dist/swagger/index.d.ts +7 -0
- package/dist/swagger/index.mjs +103 -0
- package/dist/swagger/types.d.ts +274 -0
- package/dist/swagger/types.mjs +0 -0
- package/dist/types.d.ts +137 -0
- package/dist/types.mjs +0 -0
- package/package.json +92 -0
|
@@ -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
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -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;
|