diff --git a/README.md b/README.md
index bc85d90..8ac8aeb 100644
--- a/README.md
+++ b/README.md
@@ -108,7 +108,7 @@ await fastify.ready()
| transformStaticCSP | undefined | Synchronous function to transform CSP header for static resources if the header has been previously set. |
| transformSpecification | undefined | Synchronous function to transform the swagger document. |
| transformSpecificationClone| true | Provide a deepcloned swaggerObject to transformSpecification |
- | uiConfig | {} | Configuration options for [Swagger UI](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md). Must be literal values, see [#5710](https://github.com/swagger-api/swagger-ui/issues/5710). |
+ | uiConfig | {} | Configuration options for [Swagger UI](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md). |
| uiHooks | {} | Additional hooks for the documentation's routes. You can provide the `onRequest` and `preHandler` hooks with the same [route's options](https://www.fastify.io/docs/latest/Routes/#options) interface.|
| logLevel | info | Allow to define route log level. |
@@ -121,6 +121,32 @@ The plugin will expose the documentation with the following APIs:
| `'/documentation/'` | The swagger UI |
| `'/documentation/*'` | External files that you may use in `$ref` |
+#### uiConfig
+
+To configure Swagger UI, you need to modify the `uiConfig` option.
+It's important to ensure that functions are self-contained. Keep in mind that
+you cannot modify the backend code within the `uiConfig` functions, as these
+functions are processed only by the browser. You can reference the Swagger UI
+element using `ui`, which is assigned to `window.ui`.
+
+##### Example
+```js
+const fastify = require('fastify')()
+
+await fastify.register(require('@fastify/swagger'))
+
+await fastify.register(require('@fastify/swagger-ui'), {
+ uiConfig: {
+ onComplete: function () {
+ alert('ui has type of ' + typeof ui) // 'ui has type of object'
+ alert('fastify has type of ' + typeof fastify) // 'fastify has type of undefined'
+ alert('window has type of ' + typeof window) // 'window has type of object'
+ alert('global has type of ' + typeof global) // 'global has type of undefined'
+ }
+ }
+})
+```
+
#### transformSpecification
There can be use cases, where you want to modify the swagger definition on request. E.g. you want to modify the server
diff --git a/lib/routes.js b/lib/routes.js
index 62f8fd2..668b76b 100644
--- a/lib/routes.js
+++ b/lib/routes.js
@@ -4,6 +4,7 @@ const path = require('path')
const yaml = require('yaml')
const fastifyStatic = require('@fastify/static')
const rfdc = require('rfdc')()
+const swaggerInitializer = require('./swagger-initializer')
// URI prefix to separate static assets for swagger UI
const staticPrefix = '/static'
@@ -74,23 +75,17 @@ function fastifySwagger (fastify, opts, done) {
}
})
- fastify.route({
- url: '/uiConfig',
- method: 'GET',
- schema: { hide: true },
- ...hooks,
- handler: (req, reply) => {
- reply.send(opts.uiConfig)
- }
- })
+ const swaggerInitializerContent = swaggerInitializer(opts)
fastify.route({
- url: '/initOAuth',
+ url: `${staticPrefix}/swagger-initializer.js`,
method: 'GET',
schema: { hide: true },
...hooks,
handler: (req, reply) => {
- reply.send(opts.initOAuth)
+ reply
+ .header('content-type', 'application/javascript; charset=utf-8')
+ .send(swaggerInitializerContent)
}
})
diff --git a/lib/serialize.js b/lib/serialize.js
new file mode 100644
index 0000000..84ee2d2
--- /dev/null
+++ b/lib/serialize.js
@@ -0,0 +1,69 @@
+'use strict'
+
+function serialize (value) {
+ switch (typeof value) {
+ case 'bigint':
+ return value.toString() + 'n'
+ case 'boolean':
+ return value ? 'true' : 'false'
+ case 'function':
+ return value.toString()
+ case 'number':
+ return '' + value
+ case 'object':
+ if (value === null) {
+ return 'null'
+ } else if (Array.isArray(value)) {
+ return serializeArray(value)
+ } else if (value instanceof RegExp) {
+ return `/${value.source}/${value.flags}`
+ } else if (value instanceof Date) {
+ return `new Date(${value.getTime()})`
+ } else if (value instanceof Set) {
+ return `new Set(${serializeArray(Array.from(value))})`
+ } else if (value instanceof Map) {
+ return `new Map(${serializeArray(Array.from(value))})`
+ } else {
+ return serializeObject(value)
+ }
+ case 'string':
+ return JSON.stringify(value)
+ case 'symbol':
+ return serializeSymbol(value)
+ case 'undefined':
+ return 'undefined'
+ }
+}
+const symbolRE = /Symbol\((.+)\)/
+function serializeSymbol (value) {
+ return symbolRE.test(value.toString())
+ ? `Symbol("${value.toString().match(symbolRE)[1]}")`
+ : 'Symbol()'
+}
+
+function serializeArray (value) {
+ let result = '['
+ const il = value.length
+ const last = il - 1
+ for (let i = 0; i < il; ++i) {
+ result += serialize(value[i])
+ i !== last && (result += ',')
+ }
+ return result + ']'
+}
+
+function serializeObject (value) {
+ let result = '{'
+ const keys = Object.keys(value)
+ let i = 0
+ const il = keys.length
+ const last = il - 1
+ for (; i < il; ++i) {
+ const key = keys[i]
+ result += `"${key}":${serialize(value[key])}`
+ i !== last && (result += ',')
+ }
+ return result + '}'
+}
+
+module.exports = serialize
diff --git a/lib/swagger-initializer.js b/lib/swagger-initializer.js
new file mode 100644
index 0000000..81d89a8
--- /dev/null
+++ b/lib/swagger-initializer.js
@@ -0,0 +1,36 @@
+'use strict'
+
+const serialize = require('./serialize')
+
+function swaggerInitializer (opts) {
+ return `window.onload = function () {
+ function resolveUrl(url) {
+ const anchor = document.createElement('a')
+ anchor.href = url
+ return anchor.href
+ }
+
+ const config = ${serialize(opts.uiConfig)}
+ const resConfig = Object.assign({}, {
+ dom_id: '#swagger-ui',
+ deepLinking: true,
+ presets: [
+ SwaggerUIBundle.presets.apis,
+ SwaggerUIStandalonePreset
+ ],
+ plugins: [
+ SwaggerUIBundle.plugins.DownloadUrl
+ ],
+ layout: "StandaloneLayout"
+ }, config, {
+ url: resolveUrl('./json').replace('static/json', 'json'),
+ oauth2RedirectUrl: resolveUrl('./oauth2-redirect.html')
+ });
+
+ const ui = SwaggerUIBundle(resConfig)
+ window.ui = ui
+ ui.initOAuth(${serialize(opts.initOAuth)})
+ }`
+}
+
+module.exports = swaggerInitializer
diff --git a/scripts/prepare-swagger-ui.js b/scripts/prepare-swagger-ui.js
index 714ae8f..e22bb4e 100644
--- a/scripts/prepare-swagger-ui.js
+++ b/scripts/prepare-swagger-ui.js
@@ -15,7 +15,6 @@ const filesToCopy = [
'index.html',
'index.css',
'oauth2-redirect.html',
- 'swagger-initializer.js',
'swagger-ui-bundle.js',
'swagger-ui-bundle.js.map',
'swagger-ui-standalone-preset.js',
@@ -29,55 +28,6 @@ filesToCopy.forEach(filename => {
fse.copySync(`${swaggerUiAssetPath}/${filename}`, resolve(`./static/${filename}`))
})
-fse.writeFileSync(resolve(`./${folderName}/swagger-initializer.js`), `window.onload = function () {
- function resolveUrl (url) {
- const anchor = document.createElement('a')
- anchor.href = url
- return anchor.href
- }
-
- function resolveConfig (cb) {
- return fetch(
- resolveUrl('./uiConfig').replace('${folderName}/uiConfig', 'uiConfig')
- )
- .then(res => res.json())
- .then((config) => {
- const resConfig = Object.assign({}, {
- dom_id: '#swagger-ui',
- deepLinking: true,
- presets: [
- SwaggerUIBundle.presets.apis,
- SwaggerUIStandalonePreset
- ],
- plugins: [
- SwaggerUIBundle.plugins.DownloadUrl
- ],
- layout: "StandaloneLayout"
- }, config, {
- url: resolveUrl('./json').replace('${folderName}/json', 'json'),
- oauth2RedirectUrl: resolveUrl('./oauth2-redirect.html')
- });
- return cb(resConfig);
- })
- }
-
- // Begin Swagger UI call region
- const buildUi = function (config) {
- const ui = SwaggerUIBundle(config)
- window.ui = ui
-
- fetch(resolveUrl('./initOAuth').replace('${folderName}/initOAuth', 'initOAuth'))
- .then(res => res.json())
- .then((config) => {
- ui.initOAuth(config);
- });
-
- }
- // End Swagger UI call region
-
- resolveConfig(buildUi);
-}`)
-
const sha = {
script: [],
style: []
diff --git a/test/route.test.js b/test/route.test.js
index 794cf3c..d21578a 100644
--- a/test/route.test.js
+++ b/test/route.test.js
@@ -74,62 +74,6 @@ test('/documentation/json route', async (t) => {
t.pass('valid swagger object')
})
-test('/documentation/uiConfig route', async (t) => {
- t.plan(1)
- const fastify = Fastify()
-
- const uiConfig = {
- docExpansion: 'full'
- }
-
- await fastify.register(fastifySwagger, swaggerOption)
- await fastify.register(fastifySwaggerUi, { uiConfig })
-
- fastify.get('/', () => {})
- fastify.post('/', () => {})
- fastify.get('/example', schemaQuerystring, () => {})
- fastify.post('/example', schemaBody, () => {})
- fastify.get('/parameters/:id', schemaParams, () => {})
- fastify.get('/example1', schemaSecurity, () => {})
-
- const res = await fastify.inject({
- method: 'GET',
- url: '/documentation/uiConfig'
- })
-
- const payload = JSON.parse(res.payload)
-
- t.match(payload, uiConfig, 'uiConfig should be valid')
-})
-
-test('/documentation/initOAuth route', async (t) => {
- t.plan(1)
- const fastify = Fastify()
-
- const initOAuth = {
- scopes: ['openid', 'profile', 'email', 'offline_access']
- }
-
- await fastify.register(fastifySwagger, swaggerOption)
- await fastify.register(fastifySwaggerUi, { initOAuth })
-
- fastify.get('/', () => {})
- fastify.post('/', () => {})
- fastify.get('/example', schemaQuerystring, () => {})
- fastify.post('/example', schemaBody, () => {})
- fastify.get('/parameters/:id', schemaParams, () => {})
- fastify.get('/example1', schemaSecurity, () => {})
-
- const res = await fastify.inject({
- method: 'GET',
- url: '/documentation/initOAuth'
- })
-
- const payload = JSON.parse(res.payload)
-
- t.match(payload, initOAuth, 'initOAuth should be valid')
-})
-
test('fastify.swagger should return a valid swagger yaml', async (t) => {
t.plan(3)
const fastify = Fastify()
@@ -313,7 +257,7 @@ test('with routePrefix: \'/\' should redirect to ./static/index.html', async (t)
})
test('/documentation/static/:file should send back the correct file', async (t) => {
- t.plan(22)
+ t.plan(21)
const fastify = Fastify()
await fastify.register(fastifySwagger, swaggerOption)
@@ -360,14 +304,7 @@ test('/documentation/static/:file should send back the correct file', async (t)
url: '/documentation/static/swagger-initializer.js'
})
t.equal(typeof res.payload, 'string')
- t.equal(res.headers['content-type'], 'application/javascript; charset=UTF-8')
- t.equal(
- readFileSync(
- resolve(__dirname, '..', 'static', 'swagger-initializer.js'),
- 'utf8'
- ),
- res.payload
- )
+ t.equal(res.headers['content-type'], 'application/javascript; charset=utf-8')
t.ok(res.payload.indexOf('resolveUrl') !== -1)
}
diff --git a/test/serialize.test.js b/test/serialize.test.js
new file mode 100644
index 0000000..65c431e
--- /dev/null
+++ b/test/serialize.test.js
@@ -0,0 +1,128 @@
+'use strict'
+
+const { test } = require('tap')
+const serialize = require('../lib/serialize')
+
+test('serialize', async (t) => {
+ t.plan(8)
+
+ t.test('boolean', t => {
+ t.plan(2)
+
+ t.equal(serialize(true), 'true')
+ t.equal(serialize(false), 'false')
+ })
+
+ t.test('number', t => {
+ t.plan(7)
+
+ t.equal(serialize(0), '0')
+ t.equal(serialize(1), '1')
+ t.equal(serialize(1.0), '1')
+ t.equal(serialize(1.01), '1.01')
+ t.equal(serialize(Infinity), 'Infinity')
+ t.equal(serialize(-Infinity), '-Infinity')
+ t.equal(serialize(NaN), 'NaN')
+ })
+
+ t.test('string', t => {
+ t.plan(3)
+
+ t.equal(serialize('0'), '"0"')
+ t.equal(serialize('abc'), '"abc"')
+ t.equal(serialize('"a'), '"\\\"a"') // eslint-disable-line no-useless-escape
+ })
+
+ t.test('bigint', t => {
+ t.plan(3)
+
+ t.equal(serialize(0n), '0n')
+ t.equal(serialize(1000000000n), '1000000000n')
+ t.equal(serialize(-9999n), '-9999n')
+ })
+
+ t.test('function', t => {
+ t.plan(7)
+
+ t.equal(serialize(function a () {}), 'function a () {}')
+ t.equal(serialize(async function a () {}), 'async function a () {}')
+ t.equal(serialize(() => {}), '() => {}')
+ t.equal(serialize(async () => {}), 'async () => {}')
+ t.equal(serialize(() => Date.now), '() => Date.now')
+
+ t.equal(serialize(function () {}), 'function () {}')
+ t.equal(serialize(async function () {}), 'async function () {}')
+ })
+
+ t.test('undefined', t => {
+ t.plan(1)
+
+ t.equal(serialize(undefined), 'undefined')
+ })
+
+ t.test('symbol', t => {
+ t.plan(2)
+
+ t.equal(serialize(Symbol('a')), 'Symbol("a")')
+ t.equal(serialize(Symbol()), 'Symbol()') // eslint-disable-line symbol-description
+ })
+
+ t.test('object', t => {
+ t.plan(7)
+
+ t.test('null', t => {
+ t.plan(1)
+
+ t.equal(serialize(null), 'null')
+ })
+
+ t.test('RegExp', t => {
+ t.plan(1)
+
+ t.equal(serialize(/0-9/gi), '/0-9/gi')
+ })
+
+ t.test('Date', t => {
+ t.plan(1)
+
+ t.equal(serialize(new Date(0)), 'new Date(0)')
+ })
+
+ t.test('Array', t => {
+ t.plan(5)
+
+ t.equal(serialize([]), '[]')
+ t.equal(serialize(['a']), '["a"]')
+ t.equal(serialize([1, 1n, 'a', true]), '[1,1n,"a",true]')
+ t.equal(serialize([{}]), '[{}]')
+ t.equal(serialize([{ a: [{}] }]), '[{"a":[{}]}]')
+ })
+
+ t.test('POJO', t => {
+ t.plan(3)
+
+ t.equal(serialize({}), '{}')
+ t.equal(serialize({ key: 'value' }), '{"key":"value"}')
+ t.equal(serialize({ null: null, undefined }), '{"null":null,"undefined":undefined}')
+ })
+
+ t.test('Set', t => {
+ t.plan(3)
+
+ t.equal(serialize(new Set()), 'new Set([])')
+ t.equal(serialize(new Set(['a'])), 'new Set(["a"])')
+ t.equal(serialize(new Set(['a', {}])), 'new Set(["a",{}])')
+ })
+
+ t.test('Map', t => {
+ t.plan(3)
+
+ t.equal(serialize(new Map()), 'new Map([])')
+ t.equal(serialize(new Map([['a', 1]])), 'new Map([["a",1]])')
+ const map = new Map()
+ map.set('b', 1)
+
+ t.equal(serialize(map), 'new Map([["b",1]])')
+ })
+ })
+})
diff --git a/test/static.test.js b/test/static.test.js
index d5afdf2..56c9cdc 100644
--- a/test/static.test.js
+++ b/test/static.test.js
@@ -400,104 +400,3 @@ test('/documentation/json returns cache.swaggerObject on second request in dynam
t.pass('valid swagger json')
}
})
-
-test('/documentation/uiConfig should have default', async (t) => {
- const config = {
- mode: 'static',
- specification: {
- path: './examples/example-static-specification.yaml',
- baseDir: resolve(__dirname, '..', '..', 'static')
- }
- }
-
- t.plan(2)
- const fastify = new Fastify()
- await fastify.register(fastifySwagger, config)
- await fastify.register(fastifySwaggerUi)
-
- const res = await fastify.inject({
- method: 'GET',
- url: '/documentation/uiConfig'
- })
-
- t.equal(res.statusCode, 200)
- t.equal(res.payload, '{}')
-})
-
-test('/documentation/uiConfig can be customize', async (t) => {
- t.plan(2)
- const config = {
- mode: 'static',
- specification: {
- path: './examples/example-static-specification.yaml',
- baseDir: resolve(__dirname, '..', '..', 'static')
- }
- }
-
- const fastify = new Fastify()
- await fastify.register(fastifySwagger, config)
-
- await fastify.register(fastifySwaggerUi, {
- uiConfig: {
- docExpansion: 'full'
- }
- })
-
- const res = await fastify.inject({
- method: 'GET',
- url: '/documentation/uiConfig'
- })
-
- t.equal(res.statusCode, 200)
- t.equal(res.payload, '{"docExpansion":"full"}')
-})
-
-test('/documentation/initOAuth should have default', async (t) => {
- const config = {
- mode: 'static',
- specification: {
- path: './examples/example-static-specification.yaml',
- baseDir: resolve(__dirname, '..', '..', 'static')
- }
- }
-
- t.plan(2)
- const fastify = new Fastify()
- await fastify.register(fastifySwagger, config)
- await fastify.register(fastifySwaggerUi)
-
- const res = await fastify.inject({
- method: 'GET',
- url: '/documentation/initOAuth'
- })
-
- t.equal(res.statusCode, 200)
- t.equal(res.payload, '{}')
-})
-
-test('/documentation/initOAuth can be customize', async (t) => {
- const config = {
- mode: 'static',
- specification: {
- path: './examples/example-static-specification.yaml',
- baseDir: resolve(__dirname, '..', '..', 'static')
- }
- }
-
- t.plan(2)
- const fastify = new Fastify()
- await fastify.register(fastifySwagger, config)
- await fastify.register(fastifySwaggerUi, {
- initOAuth: {
- scopes: ['openid', 'profile', 'email', 'offline_access']
- }
- })
-
- const res = await fastify.inject({
- method: 'GET',
- url: '/documentation/initOAuth'
- })
-
- t.equal(res.statusCode, 200)
- t.equal(res.payload, '{"scopes":["openid","profile","email","offline_access"]}')
-})
diff --git a/test/swagger-initializer.test.js b/test/swagger-initializer.test.js
new file mode 100644
index 0000000..6918195
--- /dev/null
+++ b/test/swagger-initializer.test.js
@@ -0,0 +1,78 @@
+'use strict'
+
+const { test } = require('tap')
+const Fastify = require('fastify')
+const fastifySwagger = require('@fastify/swagger')
+const fastifySwaggerUi = require('../index')
+
+test('/documentation/static/swagger-initializer.js should have default uiConfig', async (t) => {
+ t.plan(2)
+
+ const fastify = new Fastify()
+ await fastify.register(fastifySwagger)
+ await fastify.register(fastifySwaggerUi)
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/documentation/static/swagger-initializer.js'
+ })
+
+ t.equal(res.statusCode, 200)
+ t.ok(res.payload.includes('const config = {}'))
+})
+
+test('/documentation/static/swagger-initializer.js should have configurable uiConfig', async (t) => {
+ t.plan(2)
+
+ const fastify = new Fastify()
+ await fastify.register(fastifySwagger)
+
+ await fastify.register(fastifySwaggerUi, {
+ // eslint-disable-next-line no-undef
+ uiConfig: { onComplete: () => { alert('test') } }
+ })
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/documentation/static/swagger-initializer.js'
+ })
+
+ t.equal(res.statusCode, 200)
+ t.ok(res.payload.includes("const config = {\"onComplete\":() => { alert('test') }}"))
+})
+
+test('/documentation/static/swagger-initializer.js should have default initOAuth', async (t) => {
+ t.plan(2)
+
+ const fastify = new Fastify()
+ await fastify.register(fastifySwagger)
+ await fastify.register(fastifySwaggerUi)
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/documentation/static/swagger-initializer.js'
+ })
+
+ t.equal(res.statusCode, 200)
+ t.ok(res.payload.includes('ui.initOAuth({})'))
+})
+
+test('/documentation/static/swagger-initializer.js should have configurable initOAuth', async (t) => {
+ t.plan(2)
+
+ const fastify = new Fastify()
+ await fastify.register(fastifySwagger)
+ await fastify.register(fastifySwaggerUi, {
+ initOAuth: {
+ clientId: 'someId'
+ }
+ })
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/documentation/static/swagger-initializer.js'
+ })
+
+ t.equal(res.statusCode, 200)
+ t.ok(res.payload.includes('ui.initOAuth({"clientId":"someId"})'))
+})
diff --git a/types/index.d.ts b/types/index.d.ts
index 7e7ab7b..5843b24 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -55,41 +55,402 @@ declare namespace fastifySwaggerUi {
transformSpecificationClone?: boolean
}
- export type FastifySwaggerUiConfigOptions = Partial<{
- deepLinking: boolean
- displayOperationId: boolean
- defaultModelsExpandDepth: number
- defaultModelExpandDepth: number
- defaultModelRendering: string
- displayRequestDuration: boolean
- docExpansion: string
- filter: boolean | string
- layout: string
- maxDisplayedTags: number
- showExtensions: boolean
- showCommonExtensions: boolean
- useUnsafeMarkdown: boolean
- syntaxHighlight: {
- activate?: boolean
- theme?: string
- } | false
- tryItOutEnabled: boolean
- validatorUrl: string | null
- supportedSubmitMethods: Array<'get' | 'post' | 'put' | 'delete' | 'patch' | 'options'>
- persistAuthorization: boolean
- }>
+ type SupportedHTTPMethods = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace";
- export type FastifySwaggerInitOAuthOptions = Partial<{
- clientId: string,
- clientSecret: string,
- realm: string,
- appName: string,
- scopeSeparator: string,
- scopes: string | string[],
- additionalQueryStringParams: { [key: string]: any },
- useBasicAuthenticationWithAccessCodeGrant: boolean,
- usePkceWithAuthorizationCodeGrant: boolean
- }>
+ interface PluginsOptions {
+ /**
+ * Control behavior of plugins when targeting the same component with wrapComponent.
+ * - `legacy` (default) : last plugin takes precedence over the others
+ * - `chain` : chain wrapComponents when targeting the same core component,
+ * allowing multiple plugins to wrap the same component
+ * @default 'legacy'
+ */
+ pluginLoadType?: PluginLoadType;
+ }
+
+ type PluginLoadType = 'legacy' | 'chain';
+
+ type SorterLike =
+ | "alpha"
+ | "method"
+ | {
+ (name1: string, name2: string): number;
+ };
+
+ interface Request {
+ [prop: string]: any;
+ }
+
+ interface Response {
+ [prop: string]: any;
+ }
+
+ /**
+ * See https://swagger.io/docs/open-source-tools/swagger-ui/customization/plugin-api/
+ */
+ interface SwaggerUIPlugin {
+ (system: any): {
+ statePlugins?: {
+ [stateKey: string]: {
+ actions?: Indexable | undefined;
+ reducers?: Indexable | undefined;
+ selectors?: Indexable | undefined;
+ wrapActions?: Indexable | undefined;
+ wrapSelectors?: Indexable | undefined;
+ };
+ } | undefined;
+ components?: Indexable | undefined;
+ wrapComponents?: Indexable | undefined;
+ rootInjects?: Indexable | undefined;
+ afterLoad?: ((system: any) => any) | undefined;
+ fn?: Indexable | undefined;
+ };
+ }
+
+ interface Indexable {
+ [index: string]: any;
+ }
+
+ export type FastifySwaggerUiConfigOptions = {
+ // Core
+
+ /**
+ * URL to fetch external configuration document from.
+ */
+ configUrl?: string | undefined;
+
+ /**
+ * REQUIRED if domNode is not provided. The ID of a DOM element inside which SwaggerUI will put its user interface.
+ */
+ dom_id?: string | undefined;
+
+ /**
+ * REQUIRED if dom_id is not provided. The HTML DOM element inside which SwaggerUI will put its user interface. Overrides dom_id.
+ */
+ domNode?: HTMLElement | null | undefined;
+
+ /**
+ * A JavaScript object describing the OpenAPI definition. When used, the url parameter will not be parsed. This is useful for testing manually-generated definitions without hosting them
+ */
+ spec?: { [propName: string]: any } | undefined;
+
+ /**
+ * The URL pointing to API definition (normally swagger.json or swagger.yaml). Will be ignored if urls or spec is used.
+ */
+ url?: string | undefined;
+
+ /**
+ * An array of API definition objects ([{url: "", name: ""},{url: "", name: ""}])
+ * used by Topbar plugin. When used and Topbar plugin is enabled, the url parameter will not be parsed.
+ * Names and URLs must be unique among all items in this array, since they're used as identifiers.
+ */
+ urls?: Array<{
+ url: string;
+ name: string;
+ }> | undefined;
+
+ // Plugin system
+
+ /**
+ * The name of a component available via the plugin system to use as the top-level layout
+ * for Swagger UI.
+ */
+ layout?: string | undefined;
+
+ /**
+ * A Javascript object to configure plugin integration and behaviors
+ */
+ pluginsOptions?: PluginsOptions;
+
+ /**
+ * An array of plugin functions to use in Swagger UI.
+ */
+ plugins?: SwaggerUIPlugin[] | undefined;
+
+ /**
+ * An array of presets to use in Swagger UI.
+ * Usually, you'll want to include ApisPreset if you use this option.
+ */
+ presets?: SwaggerUIPlugin[] | undefined;
+
+ // Display
+
+ /**
+ * If set to true, enables deep linking for tags and operations.
+ * See the Deep Linking documentation for more information.
+ */
+ deepLinking?: boolean | undefined;
+
+ /**
+ * Controls the display of operationId in operations list. The default is false.
+ */
+ displayOperationId?: boolean | undefined;
+
+ /**
+ * The default expansion depth for models (set to -1 completely hide the models).
+ */
+ defaultModelsExpandDepth?: number | undefined;
+
+ /**
+ * The default expansion depth for the model on the model-example section.
+ */
+ defaultModelExpandDepth?: number | undefined;
+
+ /**
+ * Controls how the model is shown when the API is first rendered.
+ * (The user can always switch the rendering for a given model by clicking the
+ * 'Model' and 'Example Value' links.)
+ */
+ defaultModelRendering?: "example" | "model" | undefined;
+
+ /**
+ * Controls the display of the request duration (in milliseconds) for "Try it out" requests.
+ */
+ displayRequestDuration?: boolean | undefined;
+
+ /**
+ * Controls the default expansion setting for the operations and tags.
+ * It can be 'list' (expands only the tags), 'full' (expands the tags and operations)
+ * or 'none' (expands nothing).
+ */
+ docExpansion?: "list" | "full" | "none" | undefined;
+
+ /**
+ * If set, enables filtering.
+ * The top bar will show an edit box that you can use to filter the tagged operations that are shown.
+ * Can be Boolean to enable or disable, or a string, in which case filtering will be enabled
+ * using that string as the filter expression.
+ * Filtering is case sensitive matching the filter expression anywhere inside the tag.
+ */
+ filter?: boolean | string | undefined;
+
+ /**
+ * If set, limits the number of tagged operations displayed to at most this many.
+ * The default is to show all operations.
+ */
+ maxDisplayedTags?: number | undefined;
+
+ /**
+ * Apply a sort to the operation list of each API.
+ * It can be 'alpha' (sort by paths alphanumerically),
+ * 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works).
+ * Default is the order returned by the server unchanged.
+ */
+ operationsSorter?: SorterLike | undefined;
+
+ /**
+ * Controls the display of vendor extension (x-) fields and values for Operations,
+ * Parameters, Responses, and Schema.
+ */
+ showExtensions?: boolean | undefined;
+
+ /**
+ * Controls the display of extensions (pattern, maxLength, minLength, maximum, minimum) fields
+ * and values for Parameters.
+ */
+ showCommonExtensions?: boolean | undefined;
+
+ /**
+ * Apply a sort to the tag list of each API.
+ * It can be 'alpha' (sort by paths alphanumerically)
+ * or a function (see Array.prototype.sort() to learn how to write a sort function).
+ * Two tag name strings are passed to the sorter for each pass.
+ * Default is the order determined by Swagger UI.
+ */
+ tagsSorter?: SorterLike | undefined;
+
+ /**
+ * When enabled, sanitizer will leave style, class and data-* attributes untouched
+ * on all HTML Elements declared inside markdown strings.
+ * This parameter is Deprecated and will be removed in 4.0.0.
+ * @deprecated
+ */
+ useUnsafeMarkdown?: boolean | undefined;
+
+ /**
+ * Provides a mechanism to be notified when Swagger UI has finished rendering a newly provided definition.
+ */
+ onComplete?: (() => any) | undefined;
+
+ /**
+ * Set to false to deactivate syntax highlighting of payloads and cURL command,
+ * can be otherwise an object with the activate and theme properties.
+ */
+ syntaxHighlight?:
+ | false
+ | {
+ /**
+ * Whether syntax highlighting should be activated or not.
+ */
+ activate?: boolean | undefined;
+ /**
+ * Highlight.js syntax coloring theme to use. (Only these 6 styles are available.)
+ */
+ theme?: "agate" | "arta" | "monokai" | "nord" | "obsidian" | "tomorrow-night" | undefined;
+ } | undefined;
+ /**
+ * Controls whether the "Try it out" section should be enabled by default.
+ */
+ tryItOutEnabled?: boolean | undefined;
+
+ /**
+ * This is the default configuration section for the the requestSnippets plugin.
+ */
+ requestSnippets?: {
+ generators?: {
+ [genName: string]: {
+ title: string;
+ syntax: string;
+ };
+ } | undefined;
+ defaultExpanded?: boolean | undefined;
+ /**
+ * e.g. only show curl bash = ["curl_bash"]
+ */
+ languagesMask?: string[] | undefined;
+ } | undefined;
+
+ // Network
+
+ /**
+ * OAuth redirect URL.
+ */
+ oauth2RedirectUrl?: string | undefined;
+
+ /**
+ * MUST be a function. Function to intercept remote definition,
+ * "Try it out", and OAuth 2.0 requests.
+ * Accepts one argument requestInterceptor(request) and must return the modified request,
+ * or a Promise that resolves to the modified request.
+ */
+ requestInterceptor?: ((a: Request) => Request | Promise) | undefined;
+
+ /**
+ * MUST be a function. Function to intercept remote definition,
+ * "Try it out", and OAuth 2.0 responses.
+ * Accepts one argument responseInterceptor(response) and must return the modified response,
+ * or a Promise that resolves to the modified response.
+ */
+ responseInterceptor?: ((a: Response) => Response | Promise) | undefined;
+
+ /**
+ * If set to true, uses the mutated request returned from a requestInterceptor
+ * to produce the curl command in the UI, otherwise the request
+ * beforethe requestInterceptor was applied is used.
+ */
+ showMutatedRequest?: boolean | undefined;
+
+ /**
+ * List of HTTP methods that have the "Try it out" feature enabled.
+ * An empty array disables "Try it out" for all operations.
+ * This does not filter the operations from the display.
+ */
+ supportedSubmitMethods?: SupportedHTTPMethods[] | undefined;
+
+ /**
+ * By default, Swagger UI attempts to validate specs against swagger.io's online validator.
+ * You can use this parameter to set a different validator URL,
+ * for example for locally deployed validators (Validator Badge).
+ * Setting it to either none, 127.0.0.1 or localhost will disable validation.
+ */
+ validatorUrl?: string | undefined | null;
+
+ /**
+ * If set to true, enables passing credentials, as defined in the Fetch standard,
+ * in CORS requests that are sent by the browser.
+ * Note that Swagger UI cannot currently set cookies cross-domain (see swagger-js#1163)
+ * - as a result, you will have to rely on browser-supplied
+ * cookies (which this setting enables sending) that Swagger UI cannot control.
+ */
+ withCredentials?: boolean | undefined;
+
+ // Macros
+
+ /**
+ * Function to set default values to each property in model.
+ * Accepts one argument modelPropertyMacro(property), property is immutable
+ */
+ modelPropertyMacro?: ((propName: Readonly) => any) | undefined;
+
+ /**
+ * Function to set default value to parameters.
+ * Accepts two arguments parameterMacro(operation, parameter).
+ * Operation and parameter are objects passed for context, both remain immutable
+ */
+ parameterMacro?: ((operation: Readonly, parameter: Readonly) => any) | undefined;
+
+ // Authorization
+
+ /**
+ * If set to true, it persists authorization data and it would not be lost on browser close/refresh
+ */
+ persistAuthorization?: boolean | undefined;
+ }
+
+ export type FastifySwaggerInitOAuthOptions = {
+ /**
+ * Default clientId.
+ */
+ clientId?: string;
+
+ /**
+ * Never use this parameter in your production environment.
+ * It exposes crucial security information. This feature is intended for
+ * dev/test environments only.
+ * Default clientSecret.
+ */
+ clientSecret?: string,
+
+ /**
+ * realm query parameter (for oauth1) added to authorizationUrl and tokenUrl.
+ */
+ realm?: string;
+
+ /**
+ * application name, displayed in authorization popup.
+ */
+ appName?: string;
+
+ /**
+ * scope separator for passing scopes, encoded before calling, default
+ * value is a space (encoded value %20).
+ *
+ * @default ' '
+ */
+ scopeSeparator?: string;
+
+ /**
+ * string array or scope separator (i.e. space) separated string of
+ * initially selected oauth scopes
+ *
+ * @default []
+ */
+ scopes?: string | string[];
+
+ /**
+ * Additional query parameters added to authorizationUrl and tokenUrl.
+ * MUST be an object
+ */
+ additionalQueryStringParams?: { [key: string]: any };
+
+ /**
+ * Only activated for the accessCode flow. During the authorization_code
+ * request to the tokenUrl, pass the Client Password using the HTTP Basic
+ * Authentication scheme (Authorization header with Basic
+ * base64encode(client_id + client_secret)).
+ *
+ * @default false
+ */
+ useBasicAuthenticationWithAccessCodeGrant?: boolean;
+
+ /**
+ * Only applies to Authorization Code flows. Proof Key for Code Exchange
+ * brings enhanced security for OAuth public clients.
+ *
+ * @default false
+ */
+ usePkceWithAuthorizationCodeGrant?: boolean
+ }
export type FastifySwaggerUiHooksOptions = Partial<{
onRequest?: onRequestHookHandler,