@velajs/vela 1.8.3 → 1.8.5
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/dist/application.d.ts +11 -4
- package/dist/application.js +58 -10
- package/dist/openapi/document.js +34 -0
- package/dist/openapi/index.d.ts +3 -1
- package/dist/openapi/index.js +2 -0
- package/dist/openapi/redoc-ui.d.ts +1 -0
- package/dist/openapi/redoc-ui.js +19 -0
- package/dist/openapi/scalar-ui.d.ts +1 -1
- package/dist/openapi/scalar-ui.js +3 -2
- package/dist/openapi/swagger-ui.d.ts +1 -0
- package/dist/openapi/swagger-ui.js +23 -0
- package/dist/openapi/types.d.ts +29 -4
- package/package.json +1 -1
package/dist/application.d.ts
CHANGED
|
@@ -24,13 +24,20 @@ export declare class VelaApplication {
|
|
|
24
24
|
useGlobalInterceptors(...interceptors: InterceptorType[]): this;
|
|
25
25
|
useGlobalFilters(...filters: FilterType[]): this;
|
|
26
26
|
/**
|
|
27
|
-
* Serve a pre-built OpenAPI document
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Serve a pre-built OpenAPI document and one or more interactive doc UIs
|
|
28
|
+
* (Swagger UI, Scalar, ReDoc) on the underlying Hono app, mirroring the
|
|
29
|
+
* hono-crud docs convention. Edge-safe: each UI is a tiny self-contained
|
|
30
|
+
* HTML shell that loads its renderer from a CDN at runtime so nothing is
|
|
31
|
+
* bundled server-side and no extra dependency is required.
|
|
32
|
+
*
|
|
33
|
+
* Defaults: spec at `/openapi.json`, Scalar at `/scalar`, Swagger UI at
|
|
34
|
+
* `/docs`, ReDoc at `/redoc`. The deprecated `path` / `uiPath` aliases are
|
|
35
|
+
* retained for migration from the previous single-UI shape.
|
|
30
36
|
*
|
|
31
37
|
* ```ts
|
|
32
38
|
* const doc = createOpenApiDocument(AppModule);
|
|
33
|
-
* app.mountOpenApi({ document: doc
|
|
39
|
+
* app.mountOpenApi({ document: doc }); // Scalar @ /scalar
|
|
40
|
+
* app.mountOpenApi({ document: doc, ui: 'all' }); // swagger + scalar + redoc
|
|
34
41
|
* ```
|
|
35
42
|
*/
|
|
36
43
|
mountOpenApi(options: MountOpenApiOptions): this;
|
package/dist/application.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { hasBeforeApplicationShutdown, hasOnApplicationBootstrap, hasOnApplicationShutdown, hasOnModuleDestroy, hasOnModuleInit } from "./lifecycle/index.js";
|
|
2
2
|
import { renderScalarUi } from "./openapi/scalar-ui.js";
|
|
3
|
+
import { renderSwaggerUi } from "./openapi/swagger-ui.js";
|
|
4
|
+
import { renderRedocUi } from "./openapi/redoc-ui.js";
|
|
3
5
|
export class VelaApplication {
|
|
4
6
|
container;
|
|
5
7
|
routeManager;
|
|
@@ -54,23 +56,69 @@ export class VelaApplication {
|
|
|
54
56
|
return this;
|
|
55
57
|
}
|
|
56
58
|
/**
|
|
57
|
-
* Serve a pre-built OpenAPI document
|
|
58
|
-
*
|
|
59
|
-
*
|
|
59
|
+
* Serve a pre-built OpenAPI document and one or more interactive doc UIs
|
|
60
|
+
* (Swagger UI, Scalar, ReDoc) on the underlying Hono app, mirroring the
|
|
61
|
+
* hono-crud docs convention. Edge-safe: each UI is a tiny self-contained
|
|
62
|
+
* HTML shell that loads its renderer from a CDN at runtime so nothing is
|
|
63
|
+
* bundled server-side and no extra dependency is required.
|
|
64
|
+
*
|
|
65
|
+
* Defaults: spec at `/openapi.json`, Scalar at `/scalar`, Swagger UI at
|
|
66
|
+
* `/docs`, ReDoc at `/redoc`. The deprecated `path` / `uiPath` aliases are
|
|
67
|
+
* retained for migration from the previous single-UI shape.
|
|
60
68
|
*
|
|
61
69
|
* ```ts
|
|
62
70
|
* const doc = createOpenApiDocument(AppModule);
|
|
63
|
-
* app.mountOpenApi({ document: doc
|
|
71
|
+
* app.mountOpenApi({ document: doc }); // Scalar @ /scalar
|
|
72
|
+
* app.mountOpenApi({ document: doc, ui: 'all' }); // swagger + scalar + redoc
|
|
64
73
|
* ```
|
|
65
74
|
*/ mountOpenApi(options) {
|
|
66
75
|
const app = this.getApp();
|
|
67
|
-
const jsonPath = options.path ?? '/docs.json';
|
|
68
76
|
const doc = options.document;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
const title = options.title;
|
|
78
|
+
// Spec JSON: new default `/openapi.json`. The deprecated `path` alias is
|
|
79
|
+
// honored as an override only (does not change the default).
|
|
80
|
+
const specPath = options.specPath ?? options.path ?? '/openapi.json';
|
|
81
|
+
app.get(specPath, (c)=>c.json(doc));
|
|
82
|
+
// Normalize the requested UI set. Default is Scalar only.
|
|
83
|
+
const uiOption = options.ui;
|
|
84
|
+
let uis;
|
|
85
|
+
let singleStringUi = false;
|
|
86
|
+
if (uiOption === undefined) {
|
|
87
|
+
uis = [
|
|
88
|
+
'scalar'
|
|
89
|
+
];
|
|
90
|
+
} else if (uiOption === 'all') {
|
|
91
|
+
uis = [
|
|
92
|
+
'swagger',
|
|
93
|
+
'scalar',
|
|
94
|
+
'redoc'
|
|
95
|
+
];
|
|
96
|
+
} else if (Array.isArray(uiOption)) {
|
|
97
|
+
uis = uiOption;
|
|
98
|
+
} else {
|
|
99
|
+
uis = [
|
|
100
|
+
uiOption
|
|
101
|
+
];
|
|
102
|
+
singleStringUi = true;
|
|
103
|
+
}
|
|
104
|
+
for (const ui of uis){
|
|
105
|
+
// Back-compat: the old `{ ui: 'scalar', uiPath }` form mapped a single
|
|
106
|
+
// string UI to `uiPath`. Honor that only when exactly one UI was
|
|
107
|
+
// requested as a string.
|
|
108
|
+
const legacyPath = singleStringUi && uis.length === 1 ? options.uiPath : undefined;
|
|
109
|
+
if (ui === 'swagger') {
|
|
110
|
+
const path = legacyPath ?? options.swaggerPath ?? '/docs';
|
|
111
|
+
const html = renderSwaggerUi(specPath, title);
|
|
112
|
+
app.get(path, (c)=>c.html(html));
|
|
113
|
+
} else if (ui === 'redoc') {
|
|
114
|
+
const path = legacyPath ?? options.redocPath ?? '/redoc';
|
|
115
|
+
const html = renderRedocUi(specPath, title);
|
|
116
|
+
app.get(path, (c)=>c.html(html));
|
|
117
|
+
} else {
|
|
118
|
+
const path = legacyPath ?? options.scalarPath ?? '/scalar';
|
|
119
|
+
const html = renderScalarUi(specPath, title);
|
|
120
|
+
app.get(path, (c)=>c.html(html));
|
|
121
|
+
}
|
|
74
122
|
}
|
|
75
123
|
return this;
|
|
76
124
|
}
|
package/dist/openapi/document.js
CHANGED
|
@@ -241,6 +241,34 @@ export function createOpenApiDocument(rootModule, options = {}) {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
|
+
// Aggregate the document-root `tags` array. NestJS declares tag
|
|
245
|
+
// groups/descriptions/order via `DocumentBuilder().addTag(name, desc)`
|
|
246
|
+
// while its scanner auto-collects operation tags; we wire both halves
|
|
247
|
+
// here. Walk every operation's `tags` in first-seen order (stable: paths
|
|
248
|
+
// insertion order, then verb order), then merge with `options.tags`
|
|
249
|
+
// (caller-declared tags first — preserving their order + descriptions —
|
|
250
|
+
// followed by any purely-collected tag not already declared, as `{ name }`).
|
|
251
|
+
const seenTagNames = new Set();
|
|
252
|
+
const collectedTagNames = [];
|
|
253
|
+
for (const pathItem of Object.values(paths)){
|
|
254
|
+
for (const verb of VERB_WHITELIST){
|
|
255
|
+
const operation = pathItem[verb];
|
|
256
|
+
if (!operation?.tags) continue;
|
|
257
|
+
for (const tagName of operation.tags){
|
|
258
|
+
if (seenTagNames.has(tagName)) continue;
|
|
259
|
+
seenTagNames.add(tagName);
|
|
260
|
+
collectedTagNames.push(tagName);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const declaredTags = options.tags ?? [];
|
|
265
|
+
const declaredTagNames = new Set(declaredTags.map((t)=>t.name));
|
|
266
|
+
const tags = [
|
|
267
|
+
...declaredTags,
|
|
268
|
+
...collectedTagNames.filter((name)=>!declaredTagNames.has(name)).map((name)=>({
|
|
269
|
+
name
|
|
270
|
+
}))
|
|
271
|
+
];
|
|
244
272
|
const document = {
|
|
245
273
|
openapi: '3.1.0',
|
|
246
274
|
info: {
|
|
@@ -258,5 +286,11 @@ export function createOpenApiDocument(rootModule, options = {}) {
|
|
|
258
286
|
schemas
|
|
259
287
|
};
|
|
260
288
|
}
|
|
289
|
+
// Only attach `tags` when non-empty. Emitting `tags: []` on a tag-less
|
|
290
|
+
// spec is the exact symptom the consumer reported, so omit the key
|
|
291
|
+
// entirely in that case (mirrors the `components` handling above).
|
|
292
|
+
if (tags.length > 0) {
|
|
293
|
+
document.tags = tags;
|
|
294
|
+
}
|
|
261
295
|
return document;
|
|
262
296
|
}
|
package/dist/openapi/index.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ export { createOpenApiDocument } from './document';
|
|
|
2
2
|
export { ApiDoc, ApiTags, ApiResponse, API_DOC_METADATA, API_TAGS_METADATA, API_RESPONSES_METADATA, } from './decorators';
|
|
3
3
|
export { zodToJsonSchema } from './zod-to-json-schema';
|
|
4
4
|
export { renderScalarUi } from './scalar-ui';
|
|
5
|
-
export
|
|
5
|
+
export { renderSwaggerUi } from './swagger-ui';
|
|
6
|
+
export { renderRedocUi } from './redoc-ui';
|
|
7
|
+
export type { ApiDocMetadata, ApiResponseEntry, ApiResponseOptions, CreateOpenApiDocumentOptions, HttpVerb, JsonSchema, MountOpenApiOptions, OpenApiUi, OpenApiDocument, OpenApiInfo, OpenApiOperation, OpenApiParameter, OpenApiPathItem, OpenApiRequestBody, OpenApiResponse, } from './types';
|
package/dist/openapi/index.js
CHANGED
|
@@ -2,3 +2,5 @@ export { createOpenApiDocument } from "./document.js";
|
|
|
2
2
|
export { ApiDoc, ApiTags, ApiResponse, API_DOC_METADATA, API_TAGS_METADATA, API_RESPONSES_METADATA } from "./decorators.js";
|
|
3
3
|
export { zodToJsonSchema } from "./zod-to-json-schema.js";
|
|
4
4
|
export { renderScalarUi } from "./scalar-ui.js";
|
|
5
|
+
export { renderSwaggerUi } from "./swagger-ui.js";
|
|
6
|
+
export { renderRedocUi } from "./redoc-ui.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderRedocUi(specUrl: string, title?: string): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Minimal HTML shell that bootstraps ReDoc from a CDN. Served as a static
|
|
2
|
+
// string so it works on every edge runtime with no Node or bundler deps —
|
|
3
|
+
// ReDoc itself is loaded from a CDN at runtime.
|
|
4
|
+
export function renderRedocUi(specUrl, title) {
|
|
5
|
+
const safeUrl = specUrl.replace(/"/g, '"');
|
|
6
|
+
const safeTitle = (title ?? 'API Reference').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
7
|
+
return `<!doctype html>
|
|
8
|
+
<html>
|
|
9
|
+
<head>
|
|
10
|
+
<title>${safeTitle}</title>
|
|
11
|
+
<meta charset="utf-8" />
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<redoc spec-url="${safeUrl}"></redoc>
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js"></script>
|
|
17
|
+
</body>
|
|
18
|
+
</html>`;
|
|
19
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function renderScalarUi(jsonUrl: string): string;
|
|
1
|
+
export declare function renderScalarUi(jsonUrl: string, title?: string): string;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Minimal HTML shell that bootstraps Scalar's hosted API-reference UI.
|
|
2
2
|
// Served as a static string so it works on every edge runtime with no
|
|
3
3
|
// Node or bundler deps. Scalar itself is loaded from a CDN at runtime.
|
|
4
|
-
export function renderScalarUi(jsonUrl) {
|
|
4
|
+
export function renderScalarUi(jsonUrl, title) {
|
|
5
5
|
const safeUrl = jsonUrl.replace(/"/g, '"');
|
|
6
|
+
const safeTitle = (title ?? 'API Reference').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
6
7
|
return `<!doctype html>
|
|
7
8
|
<html>
|
|
8
9
|
<head>
|
|
9
|
-
<title
|
|
10
|
+
<title>${safeTitle}</title>
|
|
10
11
|
<meta charset="utf-8" />
|
|
11
12
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
12
13
|
</head>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderSwaggerUi(specUrl: string, title?: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Minimal HTML shell that bootstraps Swagger UI from a CDN. Served as a
|
|
2
|
+
// static string so it works on every edge runtime with no Node or bundler
|
|
3
|
+
// deps — Swagger UI itself is loaded from a CDN at runtime.
|
|
4
|
+
export function renderSwaggerUi(specUrl, title) {
|
|
5
|
+
const safeUrl = specUrl.replace(/"/g, '"');
|
|
6
|
+
const safeTitle = (title ?? 'API Reference').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
7
|
+
return `<!doctype html>
|
|
8
|
+
<html>
|
|
9
|
+
<head>
|
|
10
|
+
<title>${safeTitle}</title>
|
|
11
|
+
<meta charset="utf-8" />
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
13
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui.css" />
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<div id="swagger-ui"></div>
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui-bundle.js"></script>
|
|
18
|
+
<script>
|
|
19
|
+
window.ui = SwaggerUIBundle({ url: "${safeUrl}", dom_id: '#swagger-ui' });
|
|
20
|
+
</script>
|
|
21
|
+
</body>
|
|
22
|
+
</html>`;
|
|
23
|
+
}
|
package/dist/openapi/types.d.ts
CHANGED
|
@@ -90,14 +90,39 @@ export interface ApiResponseEntry extends ApiResponseOptions {
|
|
|
90
90
|
export interface CreateOpenApiDocumentOptions {
|
|
91
91
|
info?: Partial<OpenApiInfo>;
|
|
92
92
|
globalPrefix?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Declare top-level tag groups with descriptions and an explicit order
|
|
95
|
+
* (mirrors NestJS `DocumentBuilder().addTag(name, description)`). Tags
|
|
96
|
+
* actually used by operations but NOT declared here are still emitted —
|
|
97
|
+
* appended after the declared ones in first-seen order. Declared tags
|
|
98
|
+
* with no operations are kept (lets you pre-declare ordering/description).
|
|
99
|
+
*/
|
|
100
|
+
tags?: Array<{
|
|
101
|
+
name: string;
|
|
102
|
+
description?: string;
|
|
103
|
+
}>;
|
|
93
104
|
}
|
|
105
|
+
export type OpenApiUi = 'swagger' | 'scalar' | 'redoc';
|
|
94
106
|
export interface MountOpenApiOptions {
|
|
95
107
|
/** Pre-built OpenAPI document to serve. */
|
|
96
108
|
document: OpenApiDocument;
|
|
97
|
-
/**
|
|
109
|
+
/** OpenAPI JSON spec path. @default '/openapi.json' */
|
|
110
|
+
specPath?: string;
|
|
111
|
+
/**
|
|
112
|
+
* UI(s) to mount. 'all' mounts swagger + scalar + redoc.
|
|
113
|
+
* @default 'scalar'
|
|
114
|
+
*/
|
|
115
|
+
ui?: OpenApiUi | OpenApiUi[] | 'all';
|
|
116
|
+
/** Swagger UI path. @default '/docs' */
|
|
117
|
+
swaggerPath?: string;
|
|
118
|
+
/** Scalar path. @default '/scalar' */
|
|
119
|
+
scalarPath?: string;
|
|
120
|
+
/** ReDoc path. @default '/redoc' */
|
|
121
|
+
redocPath?: string;
|
|
122
|
+
/** Page title for the UIs. */
|
|
123
|
+
title?: string;
|
|
124
|
+
/** @deprecated use `specPath`. Back-compat alias; if set, overrides specPath default. */
|
|
98
125
|
path?: string;
|
|
99
|
-
/**
|
|
100
|
-
ui?: 'scalar';
|
|
101
|
-
/** Path the UI is served at when `ui` is set. Default `/docs`. */
|
|
126
|
+
/** @deprecated single-UI path override; applies to the single `ui` when a string. */
|
|
102
127
|
uiPath?: string;
|
|
103
128
|
}
|