@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,639 @@
|
|
|
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 __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => index_default,
|
|
24
|
+
openapi: () => openapi,
|
|
25
|
+
toOpenAPISchema: () => toOpenAPISchema,
|
|
26
|
+
withHeaders: () => withHeaders
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
var import_elysia2 = require("elysia");
|
|
30
|
+
|
|
31
|
+
// src/swagger/index.ts
|
|
32
|
+
function isSchemaObject(schema) {
|
|
33
|
+
return "type" in schema || "properties" in schema || "items" in schema;
|
|
34
|
+
}
|
|
35
|
+
function isDateTimeProperty(key, schema) {
|
|
36
|
+
return (key === "createdAt" || key === "updatedAt") && "anyOf" in schema && Array.isArray(schema.anyOf);
|
|
37
|
+
}
|
|
38
|
+
function transformDateProperties(schema) {
|
|
39
|
+
if (!isSchemaObject(schema) || typeof schema !== "object" || schema === null)
|
|
40
|
+
return schema;
|
|
41
|
+
const newSchema = { ...schema };
|
|
42
|
+
Object.entries(newSchema).forEach(([key, value]) => {
|
|
43
|
+
if (isSchemaObject(value)) {
|
|
44
|
+
if (isDateTimeProperty(key, value)) {
|
|
45
|
+
const dateTimeFormat = value.anyOf?.find(
|
|
46
|
+
(item) => isSchemaObject(item) && item.format === "date-time"
|
|
47
|
+
);
|
|
48
|
+
if (dateTimeFormat) {
|
|
49
|
+
const dateTimeSchema = {
|
|
50
|
+
type: "string",
|
|
51
|
+
format: "date-time",
|
|
52
|
+
default: dateTimeFormat.default
|
|
53
|
+
};
|
|
54
|
+
newSchema[key] = dateTimeSchema;
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
;
|
|
58
|
+
newSchema[key] = transformDateProperties(value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return newSchema;
|
|
63
|
+
}
|
|
64
|
+
var SwaggerUIRender = (info, config) => {
|
|
65
|
+
const {
|
|
66
|
+
version = "latest",
|
|
67
|
+
theme = `https://unpkg.com/swagger-ui-dist@${version ?? "latest"}/swagger-ui.css`,
|
|
68
|
+
cdn = `https://unpkg.com/swagger-ui-dist@${version}/swagger-ui-bundle.js`,
|
|
69
|
+
autoDarkMode = true,
|
|
70
|
+
...rest
|
|
71
|
+
} = config;
|
|
72
|
+
const stringifiedOptions = JSON.stringify(
|
|
73
|
+
{
|
|
74
|
+
dom_id: "#swagger-ui",
|
|
75
|
+
...rest
|
|
76
|
+
},
|
|
77
|
+
(_, value) => typeof value === "function" ? void 0 : value
|
|
78
|
+
);
|
|
79
|
+
const options = JSON.parse(stringifiedOptions);
|
|
80
|
+
if (options.components && options.components.schemas)
|
|
81
|
+
options.components.schemas = Object.fromEntries(
|
|
82
|
+
Object.entries(options.components.schemas).map(([key, schema]) => [
|
|
83
|
+
key,
|
|
84
|
+
transformDateProperties(schema)
|
|
85
|
+
])
|
|
86
|
+
);
|
|
87
|
+
return `<!DOCTYPE html>
|
|
88
|
+
<html lang="en">
|
|
89
|
+
<head>
|
|
90
|
+
<meta charset="utf-8" />
|
|
91
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
92
|
+
<title>${info.title}</title>
|
|
93
|
+
<meta
|
|
94
|
+
name="description"
|
|
95
|
+
content="${info.description}"
|
|
96
|
+
/>
|
|
97
|
+
<meta
|
|
98
|
+
name="og:description"
|
|
99
|
+
content="${info.description}"
|
|
100
|
+
/>
|
|
101
|
+
${autoDarkMode && typeof theme === "string" ? `<style>
|
|
102
|
+
@media (prefers-color-scheme: dark) {
|
|
103
|
+
body {
|
|
104
|
+
background-color: #222;
|
|
105
|
+
color: #faf9a;
|
|
106
|
+
}
|
|
107
|
+
.swagger-ui {
|
|
108
|
+
filter: invert(92%) hue-rotate(180deg);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.swagger-ui .microlight {
|
|
112
|
+
filter: invert(100%) hue-rotate(180deg);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
</style>` : ""}
|
|
116
|
+
${typeof theme === "string" ? `<link rel="stylesheet" href="${theme}" />` : `<link rel="stylesheet" media="(prefers-color-scheme: light)" href="${theme.light}" />
|
|
117
|
+
<link rel="stylesheet" media="(prefers-color-scheme: dark)" href="${theme.dark}" />`}
|
|
118
|
+
</head>
|
|
119
|
+
<body>
|
|
120
|
+
<div id="swagger-ui"></div>
|
|
121
|
+
<script src="${cdn}" crossorigin></script>
|
|
122
|
+
<script>
|
|
123
|
+
window.onload = () => {
|
|
124
|
+
window.ui = SwaggerUIBundle(${stringifiedOptions});
|
|
125
|
+
};
|
|
126
|
+
</script>
|
|
127
|
+
</body>
|
|
128
|
+
</html>`;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// src/scalar/index.ts
|
|
132
|
+
var elysiaCSS = `.light-mode {
|
|
133
|
+
--scalar-color-1: #2a2f45;
|
|
134
|
+
--scalar-color-2: #757575;
|
|
135
|
+
--scalar-color-3: #8e8e8e;
|
|
136
|
+
--scalar-color-accent: #f06292;
|
|
137
|
+
|
|
138
|
+
--scalar-background-1: #fff;
|
|
139
|
+
--scalar-background-2: #f6f6f6;
|
|
140
|
+
--scalar-background-3: #e7e7e7;
|
|
141
|
+
|
|
142
|
+
--scalar-border-color: rgba(0, 0, 0, 0.1);
|
|
143
|
+
}
|
|
144
|
+
.dark-mode {
|
|
145
|
+
--scalar-color-1: rgba(255, 255, 255, 0.9);
|
|
146
|
+
--scalar-color-2: rgba(156, 163, 175, 1);
|
|
147
|
+
--scalar-color-3: rgba(255, 255, 255, 0.44);
|
|
148
|
+
--scalar-color-accent: #f06292;
|
|
149
|
+
|
|
150
|
+
--scalar-background-1: #111728;
|
|
151
|
+
--scalar-background-2: #1e293b;
|
|
152
|
+
--scalar-background-3: #334155;
|
|
153
|
+
--scalar-background-accent: #f062921f;
|
|
154
|
+
|
|
155
|
+
--scalar-border-color: rgba(255, 255, 255, 0.1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Document Sidebar */
|
|
159
|
+
.light-mode .t-doc__sidebar,
|
|
160
|
+
.dark-mode .t-doc__sidebar {
|
|
161
|
+
--scalar-sidebar-background-1: var(--scalar-background-1);
|
|
162
|
+
--scalar-sidebar-color-1: var(--scalar-color-1);
|
|
163
|
+
--scalar-sidebar-color-2: var(--scalar-color-2);
|
|
164
|
+
--scalar-sidebar-border-color: var(--scalar-border-color);
|
|
165
|
+
|
|
166
|
+
--scalar-sidebar-item-hover-background: var(--scalar-background-2);
|
|
167
|
+
--scalar-sidebar-item-hover-color: currentColor;
|
|
168
|
+
|
|
169
|
+
--scalar-sidebar-item-active-background: #f062921f;
|
|
170
|
+
--scalar-sidebar-color-active: var(--scalar-color-accent);
|
|
171
|
+
|
|
172
|
+
--scalar-sidebar-search-background: transparent;
|
|
173
|
+
--scalar-sidebar-search-color: var(--scalar-color-3);
|
|
174
|
+
--scalar-sidebar-search-border-color: var(--scalar-border-color);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* advanced */
|
|
178
|
+
.light-mode {
|
|
179
|
+
--scalar-button-1: rgb(49 53 56);
|
|
180
|
+
--scalar-button-1-color: #fff;
|
|
181
|
+
--scalar-button-1-hover: rgb(28 31 33);
|
|
182
|
+
|
|
183
|
+
--scalar-color-green: #069061;
|
|
184
|
+
--scalar-color-red: #ef0006;
|
|
185
|
+
--scalar-color-yellow: #edbe20;
|
|
186
|
+
--scalar-color-blue: #0082d0;
|
|
187
|
+
--scalar-color-orange: #fb892c;
|
|
188
|
+
--scalar-color-purple: #5203d1;
|
|
189
|
+
|
|
190
|
+
--scalar-scrollbar-color: rgba(0, 0, 0, 0.18);
|
|
191
|
+
--scalar-scrollbar-color-active: rgba(0, 0, 0, 0.36);
|
|
192
|
+
}
|
|
193
|
+
.dark-mode {
|
|
194
|
+
--scalar-button-1: #f6f6f6;
|
|
195
|
+
--scalar-button-1-color: #000;
|
|
196
|
+
--scalar-button-1-hover: #e7e7e7;
|
|
197
|
+
|
|
198
|
+
--scalar-color-green: #a3ffa9;
|
|
199
|
+
--scalar-color-red: #ffa3a3;
|
|
200
|
+
--scalar-color-yellow: #fffca3;
|
|
201
|
+
--scalar-color-blue: #a5d6ff;
|
|
202
|
+
--scalar-color-orange: #e2ae83;
|
|
203
|
+
--scalar-color-purple: #d2a8ff;
|
|
204
|
+
|
|
205
|
+
--scalar-scrollbar-color: rgba(255, 255, 255, 0.24);
|
|
206
|
+
--scalar-scrollbar-color-active: rgba(255, 255, 255, 0.48);
|
|
207
|
+
}
|
|
208
|
+
.section-flare {
|
|
209
|
+
width: 100%;
|
|
210
|
+
height: 400px;
|
|
211
|
+
position: absolute;
|
|
212
|
+
}
|
|
213
|
+
.section-flare-item:first-of-type:before {
|
|
214
|
+
content: "";
|
|
215
|
+
position: absolute;
|
|
216
|
+
top: 0;
|
|
217
|
+
right: 0;
|
|
218
|
+
bottom: 0;
|
|
219
|
+
left: 0;
|
|
220
|
+
--stripes: repeating-linear-gradient(100deg, #fff 0%, #fff 0%, transparent 2%, transparent 12%, #fff 17%);
|
|
221
|
+
--stripesDark: repeating-linear-gradient(100deg, #000 0%, #000 0%, transparent 10%, transparent 12%, #000 17%);
|
|
222
|
+
--rainbow: repeating-linear-gradient(100deg, #60a5fa 10%, #e879f9 16%, #5eead4 22%, #60a5fa 30%);
|
|
223
|
+
contain: strict;
|
|
224
|
+
contain-intrinsic-size: 100vw 40vh;
|
|
225
|
+
background-image: var(--stripesDark), var(--rainbow);
|
|
226
|
+
background-size: 300%, 200%;
|
|
227
|
+
background-position: 50% 50%, 50% 50%;
|
|
228
|
+
filter: opacity(20%) saturate(200%);
|
|
229
|
+
-webkit-mask-image: radial-gradient(ellipse at 100% 0%, black 40%, transparent 70%);
|
|
230
|
+
mask-image: radial-gradient(ellipse at 100% 0%, black 40%, transparent 70%);
|
|
231
|
+
pointer-events: none;
|
|
232
|
+
}
|
|
233
|
+
.section-flare-item:first-of-type:after {
|
|
234
|
+
content: "";
|
|
235
|
+
position: absolute;
|
|
236
|
+
top: 0;
|
|
237
|
+
right: 0;
|
|
238
|
+
bottom: 0;
|
|
239
|
+
left: 0;
|
|
240
|
+
background-image: var(--stripes), var(--rainbow);
|
|
241
|
+
background-size: 200%, 100%;
|
|
242
|
+
background-attachment: fixed;
|
|
243
|
+
mix-blend-mode: difference;
|
|
244
|
+
background-image: var(--stripesDark), var(--rainbow);
|
|
245
|
+
pointer-events: none;
|
|
246
|
+
}
|
|
247
|
+
.light-mode .section-flare-item:first-of-type:after,
|
|
248
|
+
.light-mode .section-flare-item:first-of-type:before {
|
|
249
|
+
background-image: var(--stripes), var(--rainbow);
|
|
250
|
+
filter: opacity(4%) saturate(200%);
|
|
251
|
+
}`;
|
|
252
|
+
var ScalarRender = (info, config) => `<!doctype html>
|
|
253
|
+
<html>
|
|
254
|
+
<head>
|
|
255
|
+
<title>${info.title}</title>
|
|
256
|
+
<meta
|
|
257
|
+
name="description"
|
|
258
|
+
content="${info.description}"
|
|
259
|
+
/>
|
|
260
|
+
<meta
|
|
261
|
+
name="og:description"
|
|
262
|
+
content="${info.description}"
|
|
263
|
+
/>
|
|
264
|
+
<meta charset="utf-8" />
|
|
265
|
+
<meta
|
|
266
|
+
name="viewport"
|
|
267
|
+
content="width=device-width, initial-scale=1" />
|
|
268
|
+
<style>
|
|
269
|
+
body {
|
|
270
|
+
margin: 0;
|
|
271
|
+
}
|
|
272
|
+
</style>
|
|
273
|
+
<style>
|
|
274
|
+
${config.customCss ?? elysiaCSS}
|
|
275
|
+
</style>
|
|
276
|
+
</head>
|
|
277
|
+
<body>
|
|
278
|
+
<script
|
|
279
|
+
id="api-reference"
|
|
280
|
+
data-url="${config.url}"
|
|
281
|
+
>
|
|
282
|
+
</script>
|
|
283
|
+
<script src="${config.cdn}" crossorigin></script>
|
|
284
|
+
</body>
|
|
285
|
+
</html>`;
|
|
286
|
+
|
|
287
|
+
// src/openapi.ts
|
|
288
|
+
var import_elysia = require("elysia");
|
|
289
|
+
var capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
|
|
290
|
+
var toRef = (name) => import_elysia.t.Ref(`#/components/schemas/${name}`);
|
|
291
|
+
var toOperationId = (method, paths) => {
|
|
292
|
+
let operationId = method.toLowerCase();
|
|
293
|
+
if (!paths || paths === "/") return operationId + "Index";
|
|
294
|
+
for (const path of paths.split("/"))
|
|
295
|
+
operationId += path.includes(":") ? "By" + capitalize(path.replace(":", "")) : capitalize(path);
|
|
296
|
+
operationId = operationId.replace(/\?/g, "Optional");
|
|
297
|
+
return operationId;
|
|
298
|
+
};
|
|
299
|
+
var optionalParamsRegex = /(\/:\w+\?)/g;
|
|
300
|
+
var getPossiblePath = (path) => {
|
|
301
|
+
const optionalParams = path.match(optionalParamsRegex);
|
|
302
|
+
if (!optionalParams) return [path];
|
|
303
|
+
const originalPath = path.replace(/\?/g, "");
|
|
304
|
+
const paths = [originalPath];
|
|
305
|
+
for (let i = 0; i < optionalParams.length; i++) {
|
|
306
|
+
const newPath = path.replace(optionalParams[i], "");
|
|
307
|
+
paths.push(...getPossiblePath(newPath));
|
|
308
|
+
}
|
|
309
|
+
return paths;
|
|
310
|
+
};
|
|
311
|
+
function toOpenAPISchema(app, exclude, references) {
|
|
312
|
+
const {
|
|
313
|
+
methods: excludeMethods = ["OPTIONS"],
|
|
314
|
+
staticFile: excludeStaticFile = true,
|
|
315
|
+
tags: excludeTags
|
|
316
|
+
} = exclude ?? {};
|
|
317
|
+
const excludePaths = Array.isArray(exclude?.paths) ? exclude.paths : typeof exclude?.paths !== "undefined" ? [exclude.paths] : [];
|
|
318
|
+
const paths = /* @__PURE__ */ Object.create(null);
|
|
319
|
+
const routes = app.getGlobalRoutes();
|
|
320
|
+
if (references) {
|
|
321
|
+
if (!Array.isArray(references)) references = [references];
|
|
322
|
+
for (let i = 0; i < references.length; i++) {
|
|
323
|
+
const reference = references[i];
|
|
324
|
+
if (typeof reference === "function") references[i] = reference();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
for (const route of routes) {
|
|
328
|
+
if (route.hooks?.detail?.hide) continue;
|
|
329
|
+
const method = route.method.toLowerCase();
|
|
330
|
+
if (excludeStaticFile && route.path.includes(".") || excludePaths.includes(route.path) || excludeMethods.includes(method))
|
|
331
|
+
continue;
|
|
332
|
+
const hooks = route.hooks ?? {};
|
|
333
|
+
if (references)
|
|
334
|
+
for (const reference of references) {
|
|
335
|
+
const refer = reference[route.path]?.[method];
|
|
336
|
+
if (!refer) continue;
|
|
337
|
+
if (!hooks.body && refer.body) hooks.body = refer.body;
|
|
338
|
+
if (!hooks.query && refer.query) hooks.query = refer.query;
|
|
339
|
+
if (!hooks.params && refer.params) hooks.params = refer.params;
|
|
340
|
+
if (!hooks.headers && refer.headers)
|
|
341
|
+
hooks.headers = refer.headers;
|
|
342
|
+
if (!hooks.response && refer.response) {
|
|
343
|
+
hooks.response = {};
|
|
344
|
+
for (const [status, schema] of Object.entries(
|
|
345
|
+
refer.response
|
|
346
|
+
))
|
|
347
|
+
if (!hooks.response[status])
|
|
348
|
+
hooks.response[status] = schema;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (excludeTags && hooks.detail.tags?.some((tag) => excludeTags?.includes(tag)))
|
|
352
|
+
continue;
|
|
353
|
+
const operation = {
|
|
354
|
+
...hooks.detail
|
|
355
|
+
};
|
|
356
|
+
const parameters = [];
|
|
357
|
+
if (hooks.params) {
|
|
358
|
+
if (typeof hooks.params === "string")
|
|
359
|
+
hooks.params = toRef(hooks.params);
|
|
360
|
+
if (hooks.params.type === "object" && hooks.params.properties) {
|
|
361
|
+
for (const [paramName, paramSchema] of Object.entries(
|
|
362
|
+
hooks.params.properties
|
|
363
|
+
))
|
|
364
|
+
parameters.push({
|
|
365
|
+
name: paramName,
|
|
366
|
+
in: "path",
|
|
367
|
+
required: true,
|
|
368
|
+
// Path parameters are always required
|
|
369
|
+
schema: paramSchema
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (hooks.query) {
|
|
374
|
+
if (typeof hooks.query === "string")
|
|
375
|
+
hooks.query = toRef(hooks.query);
|
|
376
|
+
if (hooks.query.type === "object" && hooks.query.properties) {
|
|
377
|
+
const required = hooks.query.required || [];
|
|
378
|
+
for (const [queryName, querySchema] of Object.entries(
|
|
379
|
+
hooks.query.properties
|
|
380
|
+
))
|
|
381
|
+
parameters.push({
|
|
382
|
+
name: queryName,
|
|
383
|
+
in: "query",
|
|
384
|
+
required: required.includes(queryName),
|
|
385
|
+
schema: querySchema
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (hooks.headers) {
|
|
390
|
+
if (typeof hooks.headers === "string")
|
|
391
|
+
hooks.headers = toRef(hooks.headers);
|
|
392
|
+
if (hooks.headers.type === "object" && hooks.headers.properties) {
|
|
393
|
+
const required = hooks.headers.required || [];
|
|
394
|
+
for (const [headerName, headerSchema] of Object.entries(
|
|
395
|
+
hooks.headers.properties
|
|
396
|
+
))
|
|
397
|
+
parameters.push({
|
|
398
|
+
name: headerName,
|
|
399
|
+
in: "header",
|
|
400
|
+
required: required.includes(headerName),
|
|
401
|
+
schema: headerSchema
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (hooks.cookie) {
|
|
406
|
+
if (typeof hooks.cookie === "string")
|
|
407
|
+
hooks.cookie = toRef(hooks.cookie);
|
|
408
|
+
if (hooks.cookie.type === "object" && hooks.cookie.properties) {
|
|
409
|
+
const required = hooks.cookie.required || [];
|
|
410
|
+
for (const [cookieName, cookieSchema] of Object.entries(
|
|
411
|
+
hooks.cookie.properties
|
|
412
|
+
))
|
|
413
|
+
parameters.push({
|
|
414
|
+
name: cookieName,
|
|
415
|
+
in: "cookie",
|
|
416
|
+
required: required.includes(cookieName),
|
|
417
|
+
schema: cookieSchema
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (parameters.length > 0) operation.parameters = parameters;
|
|
422
|
+
if (hooks.body) {
|
|
423
|
+
if (typeof hooks.body === "string") hooks.body = toRef(hooks.body);
|
|
424
|
+
if (hooks.parse) {
|
|
425
|
+
const content = {};
|
|
426
|
+
const parsers = hooks.parse;
|
|
427
|
+
for (const parser of parsers) {
|
|
428
|
+
if (typeof parser.fn === "function") continue;
|
|
429
|
+
switch (parser.fn) {
|
|
430
|
+
case "text":
|
|
431
|
+
case "text/plain":
|
|
432
|
+
content["text/plain"] = { schema: hooks.body };
|
|
433
|
+
continue;
|
|
434
|
+
case "urlencoded":
|
|
435
|
+
case "application/x-www-form-urlencoded":
|
|
436
|
+
content["application/x-www-form-urlencoded"] = {
|
|
437
|
+
schema: hooks.body
|
|
438
|
+
};
|
|
439
|
+
continue;
|
|
440
|
+
case "json":
|
|
441
|
+
case "application/json":
|
|
442
|
+
content["application/json"] = { schema: hooks.body };
|
|
443
|
+
continue;
|
|
444
|
+
case "formdata":
|
|
445
|
+
case "multipart/form-data":
|
|
446
|
+
content["multipart/form-data"] = {
|
|
447
|
+
schema: hooks.body
|
|
448
|
+
};
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
operation.requestBody = { content, required: true };
|
|
453
|
+
} else {
|
|
454
|
+
operation.requestBody = {
|
|
455
|
+
content: {
|
|
456
|
+
"application/json": {
|
|
457
|
+
schema: hooks.body
|
|
458
|
+
},
|
|
459
|
+
"application/x-www-form-urlencoded": {
|
|
460
|
+
schema: hooks.body
|
|
461
|
+
},
|
|
462
|
+
"multipart/form-data": {
|
|
463
|
+
schema: hooks.body
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
required: true
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (hooks.response) {
|
|
471
|
+
operation.responses = {};
|
|
472
|
+
if (typeof hooks.response === "object" && !hooks.response.type && !hooks.response.$ref) {
|
|
473
|
+
for (let [status, schema] of Object.entries(hooks.response)) {
|
|
474
|
+
if (typeof schema === "string") schema = toRef(schema);
|
|
475
|
+
const { type, examples, $ref, ...options } = schema;
|
|
476
|
+
operation.responses[status] = {
|
|
477
|
+
description: `Response for status ${status}`,
|
|
478
|
+
...options,
|
|
479
|
+
content: type === "void" || type === "null" || type === "undefined" ? schema : {
|
|
480
|
+
"application/json": {
|
|
481
|
+
schema
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
} else {
|
|
487
|
+
if (typeof hooks.response === "string")
|
|
488
|
+
hooks.response = toRef(hooks.response);
|
|
489
|
+
operation.responses["200"] = {
|
|
490
|
+
description: "Successful response",
|
|
491
|
+
content: {
|
|
492
|
+
"application/json": {
|
|
493
|
+
schema: hooks.response
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
for (let path of getPossiblePath(route.path)) {
|
|
500
|
+
const operationId = toOperationId(route.method, path);
|
|
501
|
+
path = path.replace(/:([^/]+)/g, "{$1}");
|
|
502
|
+
if (!paths[path]) paths[path] = {};
|
|
503
|
+
const current = paths[path];
|
|
504
|
+
if (method !== "all") {
|
|
505
|
+
current[method] = {
|
|
506
|
+
...operation,
|
|
507
|
+
operationId
|
|
508
|
+
};
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
for (const method2 of [
|
|
512
|
+
"get",
|
|
513
|
+
"post",
|
|
514
|
+
"put",
|
|
515
|
+
"delete",
|
|
516
|
+
"patch",
|
|
517
|
+
"head",
|
|
518
|
+
"options",
|
|
519
|
+
"trace"
|
|
520
|
+
])
|
|
521
|
+
current[method2] = {
|
|
522
|
+
...operation,
|
|
523
|
+
operationId
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
const schemas = app.getGlobalDefinitions?.().type;
|
|
528
|
+
return {
|
|
529
|
+
components: {
|
|
530
|
+
schemas
|
|
531
|
+
},
|
|
532
|
+
paths
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
var withHeaders = (schema, headers) => Object.assign(schema, {
|
|
536
|
+
headers
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// src/index.ts
|
|
540
|
+
var openapi = ({
|
|
541
|
+
enabled = true,
|
|
542
|
+
path = "/openapi",
|
|
543
|
+
provider = "scalar",
|
|
544
|
+
specPath = `${path}/json`,
|
|
545
|
+
documentation = {},
|
|
546
|
+
exclude,
|
|
547
|
+
swagger,
|
|
548
|
+
scalar,
|
|
549
|
+
references
|
|
550
|
+
} = {}) => {
|
|
551
|
+
if (!enabled) return new import_elysia2.Elysia({ name: "@elysiajs/openapi" });
|
|
552
|
+
const info = {
|
|
553
|
+
title: "Elysia Documentation",
|
|
554
|
+
description: "Development documentation",
|
|
555
|
+
version: "0.0.0",
|
|
556
|
+
...documentation.info
|
|
557
|
+
};
|
|
558
|
+
const relativePath = specPath.startsWith("/") ? specPath.slice(1) : specPath;
|
|
559
|
+
let totalRoutes = 0;
|
|
560
|
+
let cachedSchema;
|
|
561
|
+
const app = new import_elysia2.Elysia({ name: "@elysiajs/openapi" }).use((app2) => {
|
|
562
|
+
if (provider === null) return app2;
|
|
563
|
+
return app2.get(
|
|
564
|
+
path,
|
|
565
|
+
new Response(
|
|
566
|
+
provider === "swagger-ui" ? SwaggerUIRender(info, {
|
|
567
|
+
url: relativePath,
|
|
568
|
+
dom_id: "#swagger-ui",
|
|
569
|
+
version: "latest",
|
|
570
|
+
autoDarkMode: true,
|
|
571
|
+
...swagger
|
|
572
|
+
}) : ScalarRender(info, {
|
|
573
|
+
url: relativePath,
|
|
574
|
+
version: "latest",
|
|
575
|
+
cdn: `https://cdn.jsdelivr.net/npm/@scalar/api-reference@${scalar?.version ?? "latest"}/dist/browser/standalone.min.js`,
|
|
576
|
+
...scalar,
|
|
577
|
+
_integration: "elysiajs"
|
|
578
|
+
}),
|
|
579
|
+
{
|
|
580
|
+
headers: {
|
|
581
|
+
"content-type": "text/html; charset=utf8"
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
),
|
|
585
|
+
{
|
|
586
|
+
detail: {
|
|
587
|
+
hide: true
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
}).get(
|
|
592
|
+
specPath,
|
|
593
|
+
function openAPISchema() {
|
|
594
|
+
if (totalRoutes === app.routes.length) return cachedSchema;
|
|
595
|
+
totalRoutes = app.routes.length;
|
|
596
|
+
const {
|
|
597
|
+
paths,
|
|
598
|
+
components: { schemas }
|
|
599
|
+
} = toOpenAPISchema(app, exclude, references);
|
|
600
|
+
return cachedSchema = {
|
|
601
|
+
openapi: "3.0.3",
|
|
602
|
+
...documentation,
|
|
603
|
+
tags: !exclude?.tags ? documentation.tags : documentation.tags?.filter(
|
|
604
|
+
(tag) => !exclude.tags?.includes(tag.name)
|
|
605
|
+
),
|
|
606
|
+
info: {
|
|
607
|
+
title: "Elysia Documentation",
|
|
608
|
+
description: "Development documentation",
|
|
609
|
+
version: "0.0.0",
|
|
610
|
+
...documentation.info
|
|
611
|
+
},
|
|
612
|
+
paths: {
|
|
613
|
+
...paths,
|
|
614
|
+
...documentation.paths
|
|
615
|
+
},
|
|
616
|
+
components: {
|
|
617
|
+
...documentation.components,
|
|
618
|
+
schemas: {
|
|
619
|
+
...schemas,
|
|
620
|
+
...documentation.components?.schemas
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
detail: {
|
|
627
|
+
hide: true
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
);
|
|
631
|
+
return app;
|
|
632
|
+
};
|
|
633
|
+
var index_default = openapi;
|
|
634
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
635
|
+
0 && (module.exports = {
|
|
636
|
+
openapi,
|
|
637
|
+
toOpenAPISchema,
|
|
638
|
+
withHeaders
|
|
639
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type AnyElysia, type TSchema } from 'elysia';
|
|
2
|
+
import type { OpenAPIV3 } from 'openapi-types';
|
|
3
|
+
import type { TProperties } from '@sinclair/typebox';
|
|
4
|
+
import type { AdditionalReferences, ElysiaOpenAPIConfig } from './types';
|
|
5
|
+
export declare const capitalize: (word: string) => string;
|
|
6
|
+
/**
|
|
7
|
+
* Get all possible paths of a path with optional parameters
|
|
8
|
+
* @param {string} path
|
|
9
|
+
* @returns {string[]} paths
|
|
10
|
+
*/
|
|
11
|
+
export declare const getPossiblePath: (path: string) => string[];
|
|
12
|
+
/**
|
|
13
|
+
* Converts Elysia routes to OpenAPI 3.0.3 paths schema
|
|
14
|
+
* @param routes Array of Elysia route objects
|
|
15
|
+
* @returns OpenAPI paths object
|
|
16
|
+
*/
|
|
17
|
+
export declare function toOpenAPISchema(app: AnyElysia, exclude?: ElysiaOpenAPIConfig['exclude'], references?: AdditionalReferences): {
|
|
18
|
+
components: {
|
|
19
|
+
schemas: Record<string, TSchema>;
|
|
20
|
+
};
|
|
21
|
+
paths: OpenAPIV3.PathsObject<{}, {}>;
|
|
22
|
+
};
|
|
23
|
+
export declare const withHeaders: (schema: TSchema, headers: TProperties) => TSchema & {
|
|
24
|
+
headers: TProperties;
|
|
25
|
+
};
|