@silkweave/nestjs 1.11.0 → 1.12.0
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/.turbo/turbo-build.log +16 -17
- package/.turbo/turbo-prepack.log +7 -8
- package/README.md +33 -3
- package/build/index.d.mts +82 -8
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +177 -12
- package/build/index.mjs.map +1 -1
- package/package.json +15 -8
- package/src/adapter/rest.ts +25 -13
- package/src/index.ts +2 -0
- package/src/lib/discovery.ts +6 -0
- package/src/lib/metadata.ts +7 -1
- package/src/lib/openapi.ts +116 -0
- package/src/lib/swagger.ts +74 -0
- package/src/lib/types.ts +6 -0
- package/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silkweave/nestjs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Silkweave NestJS Adapter",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
"main": "build/index.mjs",
|
|
12
12
|
"peerDependencies": {
|
|
13
13
|
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
14
|
-
"@nestjs/core": "^10.0.0 || ^11.0.0"
|
|
14
|
+
"@nestjs/core": "^10.0.0 || ^11.0.0",
|
|
15
|
+
"@nestjs/swagger": "^7.0.0 || ^8.0.0 || ^11.0.0"
|
|
16
|
+
},
|
|
17
|
+
"peerDependenciesMeta": {
|
|
18
|
+
"@nestjs/swagger": {
|
|
19
|
+
"optional": true
|
|
20
|
+
}
|
|
15
21
|
},
|
|
16
22
|
"dependencies": {
|
|
17
23
|
"@trpc/server": "^11.7.1",
|
|
@@ -19,17 +25,18 @@
|
|
|
19
25
|
"cors": "^2.8.6",
|
|
20
26
|
"express": "^5.2.1",
|
|
21
27
|
"zod": "^3.25.0",
|
|
22
|
-
"@silkweave/auth": "1.
|
|
23
|
-
"@silkweave/core": "1.
|
|
24
|
-
"@silkweave/mcp": "1.
|
|
25
|
-
"@silkweave/
|
|
26
|
-
"@silkweave/typegen": "1.
|
|
27
|
-
"@silkweave/
|
|
28
|
+
"@silkweave/auth": "1.12.0",
|
|
29
|
+
"@silkweave/core": "1.12.0",
|
|
30
|
+
"@silkweave/mcp": "1.12.0",
|
|
31
|
+
"@silkweave/trpc": "1.12.0",
|
|
32
|
+
"@silkweave/typegen": "1.12.0",
|
|
33
|
+
"@silkweave/logger": "1.12.0"
|
|
28
34
|
},
|
|
29
35
|
"devDependencies": {
|
|
30
36
|
"@eslint/js": "^10.0.1",
|
|
31
37
|
"@nestjs/common": "^11.0.0",
|
|
32
38
|
"@nestjs/core": "^11.0.0",
|
|
39
|
+
"@nestjs/swagger": "^11.0.0",
|
|
33
40
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
34
41
|
"@types/cors": "^2.8.19",
|
|
35
42
|
"@types/express": "^5.0.6",
|
package/src/adapter/rest.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
2
2
|
import { HttpException } from '@nestjs/common'
|
|
3
3
|
import { AuthConfig, AuthInfo, validateToken } from '@silkweave/auth'
|
|
4
|
-
import { type Action, type SilkweaveContext, SilkweaveError } from '@silkweave/core'
|
|
4
|
+
import { type Action, actionMethod, methodHasBody, resolveActionInput, type SilkweaveContext, SilkweaveError, validateActionRouting } from '@silkweave/core'
|
|
5
5
|
import { buildLogLevels, type Logger, type LogLevel } from '@silkweave/logger'
|
|
6
6
|
import type { IncomingMessage, ServerResponse } from 'http'
|
|
7
7
|
import { z } from 'zod/v4'
|
|
@@ -28,7 +28,8 @@ export interface RestAdapterOptions {
|
|
|
28
28
|
interface RequestLike {
|
|
29
29
|
headers: Record<string, string | string[] | undefined>
|
|
30
30
|
body?: unknown
|
|
31
|
-
query?: unknown
|
|
31
|
+
query?: Record<string, unknown>
|
|
32
|
+
params?: Record<string, string | undefined>
|
|
32
33
|
url?: string
|
|
33
34
|
method?: string
|
|
34
35
|
}
|
|
@@ -116,9 +117,11 @@ function createActionHandler(
|
|
|
116
117
|
}
|
|
117
118
|
authInfo = result.auth
|
|
118
119
|
}
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
const query = reqLike.query ?? parseQuery(req.url)
|
|
121
|
+
const body = methodHasBody(actionMethod(action))
|
|
122
|
+
? (reqLike.body ?? await readJsonBody(req))
|
|
123
|
+
: undefined
|
|
124
|
+
const raw = resolveActionInput(action, { params: reqLike.params, query, body })
|
|
122
125
|
const input = action.input.parse(raw) as object
|
|
123
126
|
const result = await action.run(input, context.fork({
|
|
124
127
|
logger,
|
|
@@ -137,22 +140,31 @@ function createActionHandler(
|
|
|
137
140
|
* REST adapter for `@silkweave/nestjs`. Registers each discovered `@Action`
|
|
138
141
|
* as an individual route on Nest's HTTP adapter:
|
|
139
142
|
*
|
|
140
|
-
* -
|
|
141
|
-
* -
|
|
143
|
+
* - HTTP verb comes from `method`, else `GET` for `kind: 'query'`, else `POST`.
|
|
144
|
+
* - Path comes from `path` (joined to `basePath`, may contain `:param`
|
|
145
|
+
* placeholders), else `${basePath}/${actionName-with-slashes}`.
|
|
146
|
+
* - Input is merged from the request body, the `queryParams` query-string
|
|
147
|
+
* fields, and any `:param` path placeholders (see `resolveActionInput`).
|
|
142
148
|
*
|
|
143
|
-
* Routes show up in Nest's `RoutesResolver` logger
|
|
144
|
-
*
|
|
145
|
-
*
|
|
149
|
+
* Routes show up in Nest's `RoutesResolver` logger. They are registered
|
|
150
|
+
* directly on the HTTP adapter (not as Nest controllers), so `@nestjs/swagger`'s
|
|
151
|
+
* controller scanner does not see them - use `addSilkweaveActions()` to add them
|
|
152
|
+
* to the OpenAPI document. Works on `@nestjs/platform-express` out of the box.
|
|
153
|
+
* For `@nestjs/platform-fastify`, register `@fastify/express` first.
|
|
146
154
|
*/
|
|
147
155
|
export function rest(options: RestAdapterOptions = {}): NestSilkweaveAdapter {
|
|
156
|
+
const basePath = (options.basePath ?? '/api').replace(/\/$/, '')
|
|
148
157
|
return {
|
|
149
158
|
name: 'rest',
|
|
159
|
+
basePath,
|
|
150
160
|
register({ httpAdapter, baseContext, actions }: NestAdapterRegisterContext): void {
|
|
151
|
-
const basePath = (options.basePath ?? '/api').replace(/\/$/, '')
|
|
152
161
|
const logger = createRestLogger()
|
|
153
162
|
for (const action of actions) {
|
|
154
|
-
|
|
155
|
-
const
|
|
163
|
+
validateActionRouting(action)
|
|
164
|
+
const path = action.path
|
|
165
|
+
? `${basePath}/${action.path.replace(/^\//, '')}`
|
|
166
|
+
: `${basePath}${actionNameToPath(action.name)}`
|
|
167
|
+
const method = actionMethod(action).toLowerCase()
|
|
156
168
|
const handler = createActionHandler(action, baseContext, options.auth, logger)
|
|
157
169
|
; (httpAdapter as unknown as Record<string, (path: string, h: typeof handler) => unknown>)[method](path, handler)
|
|
158
170
|
}
|
package/src/index.ts
CHANGED
|
@@ -8,5 +8,7 @@ export * from './lib/discovery.js'
|
|
|
8
8
|
export * from './lib/filter.js'
|
|
9
9
|
export * from './lib/guards.js'
|
|
10
10
|
export * from './lib/metadata.js'
|
|
11
|
+
export * from './lib/openapi.js'
|
|
11
12
|
export * from './lib/silkweave.module.js'
|
|
13
|
+
export * from './lib/swagger.js'
|
|
12
14
|
export * from './lib/types.js'
|
package/src/lib/discovery.ts
CHANGED
|
@@ -96,6 +96,9 @@ export class ActionDiscovery {
|
|
|
96
96
|
input: d.meta.input,
|
|
97
97
|
chunk: d.meta.chunk,
|
|
98
98
|
kind: d.meta.kind ?? 'mutation',
|
|
99
|
+
method: d.meta.method,
|
|
100
|
+
path: d.meta.path,
|
|
101
|
+
queryParams: d.meta.queryParams,
|
|
99
102
|
args: d.meta.args,
|
|
100
103
|
isEnabled,
|
|
101
104
|
toolResult: d.meta.toolResult,
|
|
@@ -112,6 +115,9 @@ export class ActionDiscovery {
|
|
|
112
115
|
input: d.meta.input,
|
|
113
116
|
output: d.meta.output,
|
|
114
117
|
kind: d.meta.kind ?? 'mutation',
|
|
118
|
+
method: d.meta.method,
|
|
119
|
+
path: d.meta.path,
|
|
120
|
+
queryParams: d.meta.queryParams,
|
|
115
121
|
args: d.meta.args,
|
|
116
122
|
isEnabled,
|
|
117
123
|
toolResult: d.meta.toolResult,
|
package/src/lib/metadata.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Action, type ActionKind, type SilkweaveContext } from '@silkweave/core'
|
|
1
|
+
import { type Action, type ActionKind, type HttpMethod, type SilkweaveContext } from '@silkweave/core'
|
|
2
2
|
import type z from 'zod/v4'
|
|
3
3
|
|
|
4
4
|
export const ACTION_METADATA = '__silkweave_action__'
|
|
@@ -24,6 +24,12 @@ export interface ActionMetadata<I extends object = object, O extends object = ob
|
|
|
24
24
|
chunk?: z.ZodType<C>
|
|
25
25
|
/** `'query'` (GET in REST, `.query()` in tRPC) or `'mutation'` (POST in REST, `.mutation()` in tRPC). Default: `'mutation'`. */
|
|
26
26
|
kind?: ActionKind
|
|
27
|
+
/** HTTP verb for the REST route. Defaults to `POST` (or `GET` when `kind` is `'query'`). Overrides the `kind`-derived default. */
|
|
28
|
+
method?: HttpMethod
|
|
29
|
+
/** REST route path, optionally with `:param` placeholders (e.g. `'spaces/:spaceId/users'`). Each placeholder must be a key of `input`. Defaults to the action name with dots as slashes. */
|
|
30
|
+
path?: string
|
|
31
|
+
/** Input fields read from the URL query string instead of the request body (e.g. `['offset', 'limit']`). Each must be a key of `input`. */
|
|
32
|
+
queryParams?: (keyof I)[]
|
|
27
33
|
/** Allowlist of transports that should expose this action. Default: all registered transports. */
|
|
28
34
|
transports?: Transport[]
|
|
29
35
|
/** Dynamic enable check (in addition to `transports`). AND-combined with the transports filter. */
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type Action, actionMethod, methodHasBody, pathParamNames } from '@silkweave/core'
|
|
2
|
+
import { camelCase } from 'change-case'
|
|
3
|
+
import { z } from 'zod/v4'
|
|
4
|
+
|
|
5
|
+
export interface ActionPathsOptions {
|
|
6
|
+
/** URL prefix the `rest()` adapter mounts on. Default `'/api'`. */
|
|
7
|
+
basePath?: string
|
|
8
|
+
/** OpenAPI tag the actions are grouped under. Default `'Actions'`. */
|
|
9
|
+
tag?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface JsonSchemaObject {
|
|
13
|
+
properties?: Record<string, unknown>
|
|
14
|
+
required?: string[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Convert a route template's `:param` placeholders to OpenAPI `{param}` form. */
|
|
18
|
+
function toOpenApiPath(template: string): string {
|
|
19
|
+
return template.replace(/:([A-Za-z0-9_]+)/g, '{$1}')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The OpenAPI path key for an action - mirrors the `rest()` adapter's routing. */
|
|
23
|
+
function actionRoute(action: Action, basePath: string): string {
|
|
24
|
+
const sub = action.path ? action.path.replace(/^\//, '') : action.name.replace(/\./g, '/')
|
|
25
|
+
return toOpenApiPath(`${basePath}/${sub}`.replace(/\/{2,}/g, '/'))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** `z.toJSONSchema` tags a top-level `$schema`; drop it for a tidy OpenAPI doc. */
|
|
29
|
+
function toJsonSchema(schema: z.ZodType): Record<string, unknown> {
|
|
30
|
+
const { $schema: _schema, ...rest } = z.toJSONSchema(schema) as Record<string, unknown>
|
|
31
|
+
return rest
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function responseSchema(action: Action): unknown {
|
|
35
|
+
if (action.output) { return toJsonSchema(action.output) }
|
|
36
|
+
if (action.chunk) { return { type: 'array', items: toJsonSchema(action.chunk) } }
|
|
37
|
+
return undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface SourceSplit {
|
|
41
|
+
parameters: Array<Record<string, unknown>>
|
|
42
|
+
bodyProps: Record<string, unknown>
|
|
43
|
+
bodyRequired: string[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Split an action's input fields into OpenAPI path/query parameters and a body, matching the `rest()` adapter. */
|
|
47
|
+
function splitInputSources(action: Action, hasBody: boolean): SourceSplit {
|
|
48
|
+
const json = z.toJSONSchema(action.input) as JsonSchemaObject
|
|
49
|
+
const required = new Set(json.required ?? [])
|
|
50
|
+
const pathParams = new Set(pathParamNames(action.path))
|
|
51
|
+
const queryKeys = new Set((action.queryParams ?? []).map(String))
|
|
52
|
+
|
|
53
|
+
const split: SourceSplit = { parameters: [], bodyProps: {}, bodyRequired: [] }
|
|
54
|
+
for (const [key, schema] of Object.entries(json.properties ?? {})) {
|
|
55
|
+
if (pathParams.has(key)) {
|
|
56
|
+
split.parameters.push({ name: key, in: 'path', required: true, schema })
|
|
57
|
+
} else if (!hasBody || queryKeys.has(key)) {
|
|
58
|
+
split.parameters.push({ name: key, in: 'query', required: required.has(key), schema })
|
|
59
|
+
} else {
|
|
60
|
+
split.bodyProps[key] = schema
|
|
61
|
+
if (required.has(key)) { split.bodyRequired.push(key) }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return split
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Build the OpenAPI operation object for a single action. */
|
|
68
|
+
function buildOperation(action: Action, tag: string): Record<string, unknown> {
|
|
69
|
+
const hasBody = methodHasBody(actionMethod(action))
|
|
70
|
+
const { parameters, bodyProps, bodyRequired } = splitInputSources(action, hasBody)
|
|
71
|
+
const response = responseSchema(action)
|
|
72
|
+
|
|
73
|
+
const operation: Record<string, unknown> = {
|
|
74
|
+
tags: [tag],
|
|
75
|
+
summary: action.description,
|
|
76
|
+
operationId: camelCase(action.name),
|
|
77
|
+
responses: {
|
|
78
|
+
200: {
|
|
79
|
+
description: 'Successful response',
|
|
80
|
+
...(response ? { content: { 'application/json': { schema: response } } } : {})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (parameters.length) { operation.parameters = parameters }
|
|
85
|
+
if (hasBody && Object.keys(bodyProps).length) {
|
|
86
|
+
operation.requestBody = {
|
|
87
|
+
required: bodyRequired.length > 0,
|
|
88
|
+
content: { 'application/json': { schema: { type: 'object', properties: bodyProps, required: bodyRequired } } }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return operation
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Build an OpenAPI `paths` fragment for a set of Silkweave actions, mirroring
|
|
96
|
+
* exactly how the `rest()` adapter routes them: the same HTTP verb (`method` ??
|
|
97
|
+
* `kind`), the same `path`/`name`-derived route, and the same path/query/body
|
|
98
|
+
* field split (`pathParamNames` + `queryParams`). Schemas are inlined from the
|
|
99
|
+
* Zod input/output via `z.toJSONSchema`; no shared components are emitted.
|
|
100
|
+
*/
|
|
101
|
+
export function buildActionPaths(
|
|
102
|
+
actions: Action[],
|
|
103
|
+
options: ActionPathsOptions = {}
|
|
104
|
+
): Record<string, Record<string, unknown>> {
|
|
105
|
+
const basePath = (options.basePath ?? '/api').replace(/\/$/, '')
|
|
106
|
+
const tag = options.tag ?? 'Actions'
|
|
107
|
+
const paths: Record<string, Record<string, unknown>> = {}
|
|
108
|
+
|
|
109
|
+
for (const action of actions) {
|
|
110
|
+
const method = actionMethod(action).toLowerCase()
|
|
111
|
+
const route = actionRoute(action, basePath)
|
|
112
|
+
paths[route] = { ...(paths[route] ?? {}), [method]: buildOperation(action, tag) }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return paths
|
|
116
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type INestApplication } from '@nestjs/common'
|
|
2
|
+
import type { OpenAPIObject } from '@nestjs/swagger'
|
|
3
|
+
import { type Action, createContext } from '@silkweave/core'
|
|
4
|
+
import { ActionDiscovery } from './discovery.js'
|
|
5
|
+
import { buildActionPaths } from './openapi.js'
|
|
6
|
+
import { SILKWEAVE_MODULE_OPTIONS, type SilkweaveModuleOptions } from './types.js'
|
|
7
|
+
|
|
8
|
+
export interface SilkweaveSwaggerOptions {
|
|
9
|
+
/**
|
|
10
|
+
* URL prefix the `rest()` adapter mounts on. Defaults to the configured
|
|
11
|
+
* `rest()` adapter's `basePath`, falling back to `'/api'`.
|
|
12
|
+
*/
|
|
13
|
+
basePath?: string
|
|
14
|
+
/** OpenAPI tag the actions are grouped under. Default `'Actions'`. */
|
|
15
|
+
tag?: string
|
|
16
|
+
/**
|
|
17
|
+
* Include actions that are *not* enabled on the REST transport (gated out via
|
|
18
|
+
* `transports` / `isEnabled`). Default `false` - the document mirrors the
|
|
19
|
+
* routes the `rest()` adapter actually registers.
|
|
20
|
+
*/
|
|
21
|
+
includeDisabled?: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Merge every REST-exposed Silkweave `@Action` into a NestJS Swagger
|
|
26
|
+
* `OpenAPIObject`.
|
|
27
|
+
*
|
|
28
|
+
* `@nestjs/swagger` builds its document by scanning **controllers**, but
|
|
29
|
+
* Silkweave registers action routes directly on the HTTP adapter (so they sit
|
|
30
|
+
* ahead of controllers in the request pipeline) - which means the scanner never
|
|
31
|
+
* sees them. This helper closes that gap: it discovers the actions through the
|
|
32
|
+
* same `ActionDiscovery` provider the `rest()` adapter uses, builds OpenAPI
|
|
33
|
+
* paths with the same routing logic (`buildActionPaths`), and merges them into
|
|
34
|
+
* the document. The result stays in sync with the live routes without any
|
|
35
|
+
* dynamic controllers.
|
|
36
|
+
*
|
|
37
|
+
* Call it between `SwaggerModule.createDocument()` and `SwaggerModule.setup()`:
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const document = SwaggerModule.createDocument(app, config)
|
|
42
|
+
* addSilkweaveActions(app, document)
|
|
43
|
+
* SwaggerModule.setup('api/docs', app, document)
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function addSilkweaveActions(
|
|
47
|
+
app: INestApplication,
|
|
48
|
+
document: OpenAPIObject,
|
|
49
|
+
options: SilkweaveSwaggerOptions = {}
|
|
50
|
+
): OpenAPIObject {
|
|
51
|
+
const discovery = app.get(ActionDiscovery)
|
|
52
|
+
const moduleOptions = app.get<SilkweaveModuleOptions>(SILKWEAVE_MODULE_OPTIONS)
|
|
53
|
+
const restAdapter = moduleOptions.adapters.find((adapter) => adapter.name === 'rest')
|
|
54
|
+
const basePath = options.basePath ?? restAdapter?.basePath ?? '/api'
|
|
55
|
+
|
|
56
|
+
const allActions = discovery.discover()
|
|
57
|
+
const actions = options.includeDisabled
|
|
58
|
+
? allActions
|
|
59
|
+
: allActions.filter((action) => isRestEnabled(action, moduleOptions))
|
|
60
|
+
|
|
61
|
+
const paths = buildActionPaths(actions, { basePath, tag: options.tag })
|
|
62
|
+
|
|
63
|
+
document.paths ??= {}
|
|
64
|
+
for (const [route, item] of Object.entries(paths)) {
|
|
65
|
+
document.paths[route] = { ...(document.paths[route] ?? {}), ...item }
|
|
66
|
+
}
|
|
67
|
+
return document
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Whether an action would be registered by the `rest()` adapter (adapter: 'rest'). */
|
|
71
|
+
function isRestEnabled(action: Action, moduleOptions: SilkweaveModuleOptions): boolean {
|
|
72
|
+
if (!action.isEnabled) { return true }
|
|
73
|
+
return action.isEnabled(createContext({ ...(moduleOptions.context ?? {}), adapter: 'rest' }))
|
|
74
|
+
}
|
package/src/lib/types.ts
CHANGED
|
@@ -29,6 +29,12 @@ export interface NestAdapterRegisterContext {
|
|
|
29
29
|
export interface NestSilkweaveAdapter {
|
|
30
30
|
/** Adapter discriminator - set on the silkweave context as `ctx.get('adapter')`. */
|
|
31
31
|
readonly name: 'rest' | 'trpc' | 'mcp' | 'typegen'
|
|
32
|
+
/**
|
|
33
|
+
* URL prefix the adapter mounts on (e.g. `'/api'`). Surfaced for introspection
|
|
34
|
+
* tooling such as `addSilkweaveActions()` (OpenAPI/Swagger), which reads it to
|
|
35
|
+
* keep generated docs aligned with the live routes.
|
|
36
|
+
*/
|
|
37
|
+
readonly basePath?: string
|
|
32
38
|
/**
|
|
33
39
|
* When `true`, the adapter receives every discovered action regardless of
|
|
34
40
|
* each action's `transports` allowlist / `isEnabled` gate. Used by
|