@fluojs/openapi 1.0.0-beta.4 → 1.0.0-beta.6

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/README.ko.md CHANGED
@@ -29,7 +29,7 @@ pnpm add @fluojs/openapi
29
29
 
30
30
  ## 빠른 시작
31
31
 
32
- `OpenApiModule`을 등록하고 `sources`(또는 미리 만든 `descriptors`)를 전달해 문서에 포함할 HTTP 핸들러를 명시합니다.
32
+ `OpenApiModule`을 등록하고 `sources`, 미리 만든 `descriptors`, 또는 둘 다를 전달해 문서에 포함할 HTTP 핸들러를 명시합니다. 두 입력을 모두 제공하면 병합됩니다.
33
33
 
34
34
  ```typescript
35
35
  import { Controller, Get } from '@fluojs/http';
@@ -67,7 +67,7 @@ await app.listen(3000);
67
67
  // Swagger UI: http://localhost:3000/docs
68
68
  ```
69
69
 
70
- 컨트롤러 탐색을 직접 건너뛰고 싶다면 `descriptors: createHandlerMapping([...]).descriptors`를 대신 전달할 있습니다. `OpenApiModule`은 `@Module({ controllers: [...] })`만으로 핸들러를 자동 추론하지 않습니다.
70
+ 컨트롤러 탐색을 직접 건너뛰고 싶다면 `@fluojs/http`의 `createHandlerMapping(...)`으로 handler descriptor를 만들고 `descriptors`로 전달하세요. `OpenApiModule`은 `@Module({ controllers: [...] })`만으로 핸들러를 자동 추론하지 않습니다.
71
71
 
72
72
  ## 핵심 기능
73
73
 
@@ -89,7 +89,13 @@ HTTP 핸들러가 `@fluojs/http`의 `@Produces(...)`를 선언하면, 생성된
89
89
  같은 scheme에 대해 여러 `@ApiSecurity()` 데코레이터를 쌓으면, 해당 scheme의 scope가 하나의 누적 OpenAPI security requirement로 병합됩니다. 따라서 라우트가 `['reports:read']`와 `['reports:write', 'reports:read']`처럼 겹치는 scope를 선언해도 OAuth 스타일 요구사항은 결정적으로 유지되며, 서로 다른 scheme은 별도 requirement로 남습니다.
90
90
 
91
91
  ### 결정적인 Swagger UI 자산
92
- `ui: true`를 활성화하면 생성되는 `/docs` 페이지는 정확한 `swagger-ui-dist` 버전의 자산을 참조하여 패키지 릴리스마다 동일한 동작을 유지합니다.
92
+ `ui: true`를 활성화하면 생성되는 `/docs` 페이지는 정확한 `swagger-ui-dist` 버전의 자산을 참조하여 패키지 릴리스마다 동일한 동작을 유지합니다. 오프라인 또는 CSP 제어 환경에서 자체 호스팅 자산이 필요하면 `swaggerUiAssets.cssUrl`과 `swaggerUiAssets.jsBundleUrl`을 설정하세요. 생성된 HTML은 해당 URL을 이스케이프하며 Swagger UI 인스턴스를 `window.ui`에 노출하지 않습니다.
93
+
94
+ ### 모듈 옵션 결정성
95
+ `OpenApiModule.forRoot(...)`는 등록 시점에 옵션을 스냅샷하고 freeze합니다. 등록 후 원본 options 객체, `sources`, `descriptors`, `securitySchemes`, `extraModels`, `swaggerUiAssets`를 변경해도 제공되는 OpenAPI 문서나 `/docs` HTML은 바뀌지 않습니다. `OpenApiModule.forRootAsync(...)`도 async factory가 resolve된 뒤 같은 스냅샷을 적용하며, factory 실패는 bootstrap 중 전파됩니다.
96
+
97
+ ### Async 등록과 옵션
98
+ title/version/source 설정이 DI나 async setup에서 나오는 경우 `OpenApiModule.forRootAsync(...)`를 사용합니다. Module option에는 `sources`, `descriptors`, `securitySchemes`, `extraModels`, `defaultErrorResponsesPolicy`, `documentTransform`, `ui`, `swaggerUiAssets`가 포함됩니다. `defaultErrorResponsesPolicy`는 기본적으로 표준 error response와 `ErrorResponse` schema를 주입하며, `documentTransform`은 문서 생성 뒤 제공되기 전에 실행됩니다.
93
99
 
94
100
  ## 공개 API
95
101
 
@@ -99,6 +105,10 @@ HTTP 핸들러가 `@fluojs/http`의 `@Produces(...)`를 선언하면, 생성된
99
105
  - `ApiBearerAuth`, `ApiSecurity`: 보안 요구사항 데코레이터.
100
106
  - `ApiExcludeEndpoint`: 특정 핸들러를 문서화에서 제외.
101
107
  - `buildOpenApiDocument`: 프로그래밍 방식의 문서 빌더 (저수준).
108
+ - `OpenApiHandlerRegistry`: 고급 통합에서 문서 생성 전에 handler descriptor를 스냅샷하는 mutable descriptor registry.
109
+ - `getControllerTags`, `getMethodApiMetadata`: 고급 테스트와 통합 tooling을 위한 metadata reader.
110
+ - `OpenApiModuleOptions`, `OpenApiSwaggerUiAssetsOptions`, `BuildOpenApiDocumentOptions`, `DefaultErrorResponsesPolicy`: module과 builder integration을 위한 option type.
111
+ - `OpenApiDocument`, `OpenApiSecuritySchemeObject` 및 관련 OpenAPI shape type: 테스트, tooling, integration을 위한 typed document surface.
102
112
  - `OpenApiSchemaObject`: 명시적 `@ApiBody(...)` 및 `@ApiResponse(...)` 스키마를 위한 타입화된 스키마 표면입니다. OpenAPI 3.1 조합(`allOf`, `oneOf`, `anyOf`), 객체/배열 제약, examples/defaults, 읽기/쓰기/Deprecated 주석을 포함합니다.
103
113
 
104
114
  ## 관련 패키지
@@ -110,4 +120,4 @@ HTTP 핸들러가 `@fluojs/http`의 `@Produces(...)`를 선언하면, 생성된
110
120
  ## 예제 소스
111
121
 
112
122
  - `packages/openapi/src/openapi-module.test.ts`: 통합 테스트 및 사용 예제.
113
- - `examples/openapi-swagger`: 전체 OpenAPI 애플리케이션 예제.
123
+ - `packages/openapi/src/schema-builder.test.ts`: 문서 builder와 schema generation 예제.
package/README.md CHANGED
@@ -29,7 +29,7 @@ pnpm add @fluojs/openapi
29
29
 
30
30
  ## Quick Start
31
31
 
32
- Register the `OpenApiModule` and pass `sources` (or prebuilt `descriptors`) so the document builder knows which HTTP handlers to include.
32
+ Register the `OpenApiModule` and pass `sources`, prebuilt `descriptors`, or both so the document builder knows which HTTP handlers to include. When both inputs are provided, they are merged.
33
33
 
34
34
  ```typescript
35
35
  import { Controller, Get } from '@fluojs/http';
@@ -67,7 +67,7 @@ await app.listen(3000);
67
67
  // Swagger UI: http://localhost:3000/docs
68
68
  ```
69
69
 
70
- If you need to bypass controller discovery, pass `descriptors: createHandlerMapping([...]).descriptors` instead. `OpenApiModule` does not infer handlers from `@Module({ controllers: [...] })` on its own.
70
+ If you need to bypass controller discovery, create handler descriptors with `createHandlerMapping(...)` from `@fluojs/http` and pass them through `descriptors`. `OpenApiModule` does not infer handlers from `@Module({ controllers: [...] })` on its own.
71
71
 
72
72
  ## Core Capabilities
73
73
 
@@ -89,7 +89,13 @@ Easily document authentication requirements like Bearer tokens or API keys using
89
89
  Stacking multiple `@ApiSecurity()` decorators for the same scheme merges scopes into one cumulative OpenAPI security requirement for that scheme. This keeps OAuth-style requirements deterministic when a route declares overlapping scopes such as `['reports:read']` and `['reports:write', 'reports:read']`, while different schemes remain separate requirements.
90
90
 
91
91
  ### Deterministic Swagger UI Assets
92
- When `ui: true` is enabled, the generated `/docs` page references an exact `swagger-ui-dist` asset version so release behavior stays deterministic across package updates.
92
+ When `ui: true` is enabled, the generated `/docs` page references an exact `swagger-ui-dist` asset version so release behavior stays deterministic across package updates. If your deployment requires self-hosted assets for offline or CSP-controlled environments, set `swaggerUiAssets.cssUrl` and `swaggerUiAssets.jsBundleUrl`; the generated HTML escapes those URLs and does not expose the Swagger UI instance on `window.ui`.
93
+
94
+ ### Module Option Determinism
95
+ `OpenApiModule.forRoot(...)` snapshots and freezes its options at registration time. Mutating the original options object, `sources`, `descriptors`, `securitySchemes`, `extraModels`, or `swaggerUiAssets` after registration does not alter the served OpenAPI document or `/docs` HTML. `OpenApiModule.forRootAsync(...)` applies the same snapshot once the async factory resolves, and factory failures propagate during bootstrap.
96
+
97
+ ### Async Registration and Options
98
+ Use `OpenApiModule.forRootAsync(...)` when title/version/source configuration comes from DI or async setup. Module options include `sources`, `descriptors`, `securitySchemes`, `extraModels`, `defaultErrorResponsesPolicy`, `documentTransform`, `ui`, and `swaggerUiAssets`. `defaultErrorResponsesPolicy` defaults to injecting standard error responses and an `ErrorResponse` schema, while `documentTransform` runs after document generation and before serving.
93
99
 
94
100
  ## Public API
95
101
 
@@ -99,6 +105,10 @@ When `ui: true` is enabled, the generated `/docs` page references an exact `swag
99
105
  - `ApiBearerAuth`, `ApiSecurity`: Security requirement decorators.
100
106
  - `ApiExcludeEndpoint`: Omit specific handlers from documentation.
101
107
  - `buildOpenApiDocument`: Programmatic document builder (low-level).
108
+ - `OpenApiHandlerRegistry`: Mutable descriptor registry used by advanced integrations to snapshot handler descriptors before document generation.
109
+ - `getControllerTags`, `getMethodApiMetadata`: Metadata readers for advanced tests and integration tooling.
110
+ - `OpenApiModuleOptions`, `OpenApiSwaggerUiAssetsOptions`, `BuildOpenApiDocumentOptions`, `DefaultErrorResponsesPolicy`: Option types for module and builder integrations.
111
+ - `OpenApiDocument`, `OpenApiSecuritySchemeObject`, and related OpenAPI shape types: Typed document surface for tests, tooling, and integrations.
102
112
  - `OpenApiSchemaObject`: Typed schema surface for explicit `@ApiBody(...)` and `@ApiResponse(...)` schemas, including OpenAPI 3.1 composition (`allOf`, `oneOf`, `anyOf`), object/array constraints, examples/defaults, and read/write/deprecated annotations.
103
113
 
104
114
  ## Related Packages
@@ -110,4 +120,4 @@ When `ui: true` is enabled, the generated `/docs` page references an exact `swag
110
120
  ## Example Sources
111
121
 
112
122
  - `packages/openapi/src/openapi-module.test.ts`: Integration tests and usage examples.
113
- - `examples/openapi-swagger`: Complete OpenAPI application example.
123
+ - `packages/openapi/src/schema-builder.test.ts`: Document builder and schema generation examples.
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { getStandardMetadataBag } from '@fluojs/core/internal';
1
+ import { ensureMetadataSymbol, getStandardMetadataBag } from '@fluojs/core/internal';
2
2
 
3
3
  /**
4
4
  * User-facing operation metadata accepted by `@ApiOperation(...)`.
@@ -48,6 +48,7 @@ const openApiMethodRequestBodyKey = Symbol.for('fluo.openapi.method-request-body
48
48
  const openApiMethodSecurityKey = Symbol.for('fluo.openapi.method-security');
49
49
  const openApiMethodSecurityRequirementsKey = Symbol.for('fluo.openapi.method-security-requirements');
50
50
  const openApiMethodExcludeEndpointKey = Symbol.for('fluo.openapi.method-exclude-endpoint');
51
+ ensureMetadataSymbol();
51
52
  function getMetadataBag(target) {
52
53
  return getStandardMetadataBag(target);
53
54
  }
@@ -2,6 +2,13 @@ import { type HandlerDescriptor, type HandlerSource } from '@fluojs/http';
2
2
  import { type AsyncModuleOptions, type Constructor } from '@fluojs/core';
3
3
  import { type ModuleType } from '@fluojs/runtime';
4
4
  import { type DefaultErrorResponsesPolicy, type OpenApiDocument, type OpenApiSecuritySchemeObject } from './schema-builder.js';
5
+ /**
6
+ * Asset URLs used by the generated Swagger UI HTML page.
7
+ */
8
+ export interface OpenApiSwaggerUiAssetsOptions {
9
+ cssUrl?: string;
10
+ jsBundleUrl?: string;
11
+ }
5
12
  /**
6
13
  * Public options for `OpenApiModule.forRoot(...)` and `OpenApiModule.forRootAsync(...)`.
7
14
  *
@@ -17,6 +24,7 @@ export interface OpenApiModuleOptions {
17
24
  descriptors?: readonly HandlerDescriptor[];
18
25
  sources?: readonly HandlerSource[];
19
26
  securitySchemes?: Record<string, OpenApiSecuritySchemeObject>;
27
+ swaggerUiAssets?: OpenApiSwaggerUiAssetsOptions;
20
28
  extraModels?: Constructor[];
21
29
  documentTransform?: (document: OpenApiDocument) => OpenApiDocument;
22
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openapi-module.d.ts","sourceRoot":"","sources":["../src/openapi-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAEnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAU,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAiC,MAAM,cAAc,CAAC;AAChH,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGhE,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,2BAA2B,EACjC,MAAM,qBAAqB,CAAC;AAO7B;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,2BAA2B,CAAC,EAAE,2BAA2B,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,WAAW,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC3C,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC;IAC9D,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;IAC5B,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,eAAe,CAAC;CACpE;AAsED;;GAEG;AACH,qBAAa,aAAa;IACxB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,UAAU;IAOzD;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,kBAAkB,CAAC,oBAAoB,CAAC,GAAG,UAAU;IAQlF,OAAO,CAAC,MAAM,CAAC,YAAY;CAqE5B"}
1
+ {"version":3,"file":"openapi-module.d.ts","sourceRoot":"","sources":["../src/openapi-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAEnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAU,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAiC,MAAM,cAAc,CAAC;AAChH,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGhE,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,2BAA2B,EACjC,MAAM,qBAAqB,CAAC;AAO7B;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,2BAA2B,CAAC,EAAE,2BAA2B,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,WAAW,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC3C,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC;IAC9D,eAAe,CAAC,EAAE,6BAA6B,CAAC;IAChD,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;IAC5B,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,eAAe,CAAC;CACpE;AA6ID;;GAEG;AACH,qBAAa,aAAa;IACxB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,UAAU;IAOzD;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,kBAAkB,CAAC,oBAAoB,CAAC,GAAG,UAAU;IAQlF,OAAO,CAAC,MAAM,CAAC,YAAY;CAqE5B"}
@@ -13,6 +13,10 @@ const SWAGGER_UI_DIST_BASE_URL = `https://unpkg.com/swagger-ui-dist@${SWAGGER_UI
13
13
  const SWAGGER_UI_CSS_URL = `${SWAGGER_UI_DIST_BASE_URL}/swagger-ui.css`;
14
14
  const SWAGGER_UI_BUNDLE_JS_URL = `${SWAGGER_UI_DIST_BASE_URL}/swagger-ui-bundle.js`;
15
15
 
16
+ /**
17
+ * Asset URLs used by the generated Swagger UI HTML page.
18
+ */
19
+
16
20
  /**
17
21
  * Public options for `OpenApiModule.forRoot(...)` and `OpenApiModule.forRootAsync(...)`.
18
22
  *
@@ -24,24 +28,82 @@ const SWAGGER_UI_BUNDLE_JS_URL = `${SWAGGER_UI_DIST_BASE_URL}/swagger-ui-bundle.
24
28
  function escapeHtml(value) {
25
29
  return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;');
26
30
  }
27
- function createSwaggerUiHtml(title) {
31
+ function cloneRecord(record) {
32
+ if (!record) {
33
+ return undefined;
34
+ }
35
+ const clone = {};
36
+ for (const [key, value] of Object.entries(record)) {
37
+ clone[key] = cloneSnapshotValue(value);
38
+ }
39
+ return clone;
40
+ }
41
+ function cloneSnapshotValue(value) {
42
+ if (value === null || value === undefined) {
43
+ return value;
44
+ }
45
+ if (Array.isArray(value)) {
46
+ return value.map(entry => cloneSnapshotValue(entry));
47
+ }
48
+ if (typeof value !== 'object') {
49
+ return value;
50
+ }
51
+ const clone = {};
52
+ for (const key of Reflect.ownKeys(value)) {
53
+ clone[key] = cloneSnapshotValue(value[key]);
54
+ }
55
+ return clone;
56
+ }
57
+ function deepFreeze(value) {
58
+ if (value === null || typeof value !== 'object') {
59
+ return value;
60
+ }
61
+ for (const key of Reflect.ownKeys(value)) {
62
+ deepFreeze(value[key]);
63
+ }
64
+ return Object.freeze(value);
65
+ }
66
+ function snapshotOpenApiModuleOptions(options) {
67
+ return deepFreeze({
68
+ defaultErrorResponsesPolicy: options.defaultErrorResponsesPolicy,
69
+ descriptors: options.descriptors ? cloneSnapshotValue(options.descriptors) : undefined,
70
+ documentTransform: options.documentTransform,
71
+ extraModels: options.extraModels ? [...options.extraModels] : undefined,
72
+ securitySchemes: cloneRecord(options.securitySchemes),
73
+ sources: options.sources ? cloneSnapshotValue(options.sources) : undefined,
74
+ swaggerUiAssets: options.swaggerUiAssets ? {
75
+ ...options.swaggerUiAssets
76
+ } : undefined,
77
+ title: options.title,
78
+ ui: options.ui,
79
+ version: options.version
80
+ });
81
+ }
82
+ function resolveSwaggerUiAssets(options) {
83
+ return {
84
+ cssUrl: options.swaggerUiAssets?.cssUrl ?? SWAGGER_UI_CSS_URL,
85
+ jsBundleUrl: options.swaggerUiAssets?.jsBundleUrl ?? SWAGGER_UI_BUNDLE_JS_URL
86
+ };
87
+ }
88
+ function createSwaggerUiHtml(title, assets) {
28
89
  return `<!doctype html>
29
90
  <html lang="en">
30
91
  <head>
31
92
  <meta charset="utf-8" />
32
93
  <meta name="viewport" content="width=device-width, initial-scale=1" />
33
94
  <title>${escapeHtml(title)}</title>
34
- <link rel="stylesheet" href="${SWAGGER_UI_CSS_URL}" />
95
+ <link rel="stylesheet" href="${escapeHtml(assets.cssUrl)}" />
35
96
  </head>
36
97
  <body>
37
98
  <div id="swagger-ui"></div>
38
- <script src="${SWAGGER_UI_BUNDLE_JS_URL}" crossorigin></script>
99
+ <script src="${escapeHtml(assets.jsBundleUrl)}" crossorigin></script>
39
100
  <script>
40
101
  const specUrl = window.location.pathname.replace(/\/docs\/?$/, '/openapi.json');
41
- window.ui = SwaggerUIBundle({
102
+ const swaggerUi = SwaggerUIBundle({
42
103
  url: specUrl,
43
104
  dom_id: '#swagger-ui'
44
105
  });
106
+ void swaggerUi;
45
107
  </script>
46
108
  </body>
47
109
  </html>`;
@@ -87,7 +149,7 @@ export class OpenApiModule {
87
149
  static forRoot(options) {
88
150
  return this.createModule({
89
151
  scope: 'singleton',
90
- useValue: options
152
+ useValue: snapshotOpenApiModuleOptions(options)
91
153
  });
92
154
  }
93
155
 
@@ -112,7 +174,7 @@ export class OpenApiModule {
112
174
  return this.createModule({
113
175
  inject: options.inject,
114
176
  scope: 'singleton',
115
- useFactory: options.useFactory
177
+ useFactory: async (...deps) => snapshotOpenApiModuleOptions(await options.useFactory(...deps))
116
178
  });
117
179
  }
118
180
  static createModule(optionsProvider) {
@@ -140,7 +202,7 @@ export class OpenApiModule {
140
202
  throw new NotFoundException('Swagger UI is disabled.');
141
203
  }
142
204
  context.response.setHeader('content-type', 'text/html; charset=utf-8');
143
- return createSwaggerUiHtml(this.options.title);
205
+ return createSwaggerUiHtml(this.options.title, resolveSwaggerUiAssets(this.options));
144
206
  }
145
207
  static {
146
208
  _initClass();
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "documentation",
10
10
  "rest"
11
11
  ],
12
- "version": "1.0.0-beta.4",
12
+ "version": "1.0.0-beta.6",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -36,10 +36,10 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@fluojs/core": "^1.0.0-beta.2",
40
- "@fluojs/validation": "^1.0.0-beta.1",
41
- "@fluojs/http": "^1.0.0-beta.4",
42
- "@fluojs/runtime": "^1.0.0-beta.5"
39
+ "@fluojs/core": "^1.0.0-beta.4",
40
+ "@fluojs/validation": "^1.0.0-beta.3",
41
+ "@fluojs/http": "^1.0.0-beta.10",
42
+ "@fluojs/runtime": "^1.0.0-beta.11"
43
43
  },
44
44
  "devDependencies": {
45
45
  "vitest": "^3.2.4"