@silkweave/nestjs 2.0.0 → 2.2.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/README.md +14 -2
- package/build/index.d.mts +37 -5
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +60 -16
- package/build/index.mjs.map +1 -1
- package/package.json +18 -11
- package/.turbo/turbo-build.log +0 -16
- package/.turbo/turbo-check.log +0 -3
- package/.turbo/turbo-lint.log +0 -2
- package/.turbo/turbo-prepack.log +0 -18
- package/eslint.config.mjs +0 -93
- package/src/adapter/mcp.ts +0 -102
- package/src/decorator/mcp.ts +0 -39
- package/src/index.ts +0 -10
- package/src/lib/controllerDiscovery.ts +0 -201
- package/src/lib/executionContext.ts +0 -76
- package/src/lib/guards.ts +0 -70
- package/src/lib/metadata.ts +0 -38
- package/src/lib/rebind.ts +0 -117
- package/src/lib/reflect/classValidator.ts +0 -56
- package/src/lib/reflect/openapi.ts +0 -87
- package/src/lib/reflect/params.ts +0 -63
- package/src/lib/reflect/route.ts +0 -58
- package/src/lib/reflect/schema.ts +0 -270
- package/src/lib/reflect/swagger.ts +0 -39
- package/src/lib/silkweave.module.ts +0 -75
- package/src/lib/types.ts +0 -61
- package/tsconfig.json +0 -25
- package/tsconfig.tsbuildinfo +0 -1
- package/tsdown.config.ts +0 -7
package/README.md
CHANGED
|
@@ -174,15 +174,27 @@ Native NestJS `@UseGuards()` on `@Mcp` methods run before the handler is invoked
|
|
|
174
174
|
|
|
175
175
|
**Headers over MCP.** The inbound tool-call request is surfaced as a stand-in `{ headers, url, params, query }` object built from the MCP SDK's `extra.requestInfo`, so a header-based guard - e.g. one reading `getRequest().headers['x-api-key']` - works unchanged. `ExecutionContext.getType()` is `'http'` whenever a request is available (and `'rpc'` for transports with none, e.g. MCP stdio). Caveats:
|
|
176
176
|
|
|
177
|
-
-
|
|
177
|
+
- **Headers**, the request `url`, and **URL path params** cross the MCP boundary. `getRequest().params` is populated from the route's reflected `@Param` fields (as raw strings, like Express), so a path-scoped guard - e.g. one reading `getRequest().params['id']` to fence a key to one resource - works the same over MCP as over REST. `getRequest().query` is still empty (no query string over MCP).
|
|
178
178
|
- A guard that denies (returns `false` or throws) produces a clean MCP tool error (`ForbiddenException`), not an HTTP 500.
|
|
179
179
|
- For OAuth 2.1 / bearer-token MCP auth, prefer `mcp({ auth })` and read the resolved identity from the silkweave context (`ctx.get('auth')`); the header stand-in is for custom request-reading guards.
|
|
180
180
|
|
|
181
|
+
**Global guards (opt-in).** App-global guards - registered via `app.useGlobalGuards(new X())` or `{ provide: APP_GUARD, useClass }` - do **not** run on tool calls by default. Opt them in by class with `globalGuards`:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
SilkweaveModule.forRoot({
|
|
185
|
+
silkweave: { name: 'app', description: 'My App', version: '1.0.0' },
|
|
186
|
+
adapters: [mcp({ basePath: '/mcp' })],
|
|
187
|
+
globalGuards: [ApiKeyGuard] // runs before each method/class @UseGuards; throttler etc. deliberately excluded
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The allow-list is explicit-by-class on purpose - a blanket "run every global" would also fire unrelated globals (e.g. a `ThrottlerGuard`, which assumes a writable response MCP doesn't provide). Listed guards run **before** the method/class `@UseGuards`, mirroring Nest's request pipeline. They see the same request stand-in as method guards: headers, `url`, and path `params` (populated from reflected `@Param` fields); only `query`/IP-derived logic won't apply over MCP.
|
|
192
|
+
|
|
181
193
|
Controllers are normal Nest providers - inject services via the constructor as usual.
|
|
182
194
|
|
|
183
195
|
## What does *not* run
|
|
184
196
|
|
|
185
|
-
Because the handler is invoked directly (not through Nest's HTTP request pipeline), the following do **not** apply on a tool call - only `@UseGuards()` and parameter-bound pipes do:
|
|
197
|
+
Because the handler is invoked directly (not through Nest's HTTP request pipeline), the following do **not** apply on a tool call - only `@UseGuards()` (plus any opted-in `globalGuards`) and parameter-bound pipes do:
|
|
186
198
|
|
|
187
199
|
- Globally-registered `ValidationPipe` / interceptors / exception filters (MCP input is instead validated against the reflected Zod schema).
|
|
188
200
|
- DTO class instantiation - whole-DTO `@Body()`/`@Query()` arguments arrive as plain objects, so `@Transform` and DTO methods do not fire.
|
package/build/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CanActivate, DynamicModule, MiddlewareConsumer, NestModule, Type } from "@nestjs/common";
|
|
2
|
-
import { DiscoveryService, HttpAdapterHost, MetadataScanner, ModuleRef, Reflector } from "@nestjs/core";
|
|
2
|
+
import { ApplicationConfig, DiscoveryService, HttpAdapterHost, MetadataScanner, ModuleRef, Reflector } from "@nestjs/core";
|
|
3
3
|
import { Action, SilkweaveContext, SilkweaveOptions } from "@silkweave/core";
|
|
4
4
|
import { z } from "zod/v4";
|
|
5
5
|
import { AuthConfig } from "@silkweave/auth";
|
|
@@ -137,6 +137,19 @@ interface SilkweaveModuleOptions {
|
|
|
137
137
|
* reflection when an operation or field isn't present.
|
|
138
138
|
*/
|
|
139
139
|
openapi?: OpenApiDocument;
|
|
140
|
+
/**
|
|
141
|
+
* Opt-in allow-list of app-global guard classes (registered via
|
|
142
|
+
* `app.useGlobalGuards()` or `{ provide: APP_GUARD, useClass }`) to run on
|
|
143
|
+
* every MCP tool call, before each method/class `@UseGuards`. Listed by
|
|
144
|
+
* class - a blanket "run all globals" is deliberately not offered, since
|
|
145
|
+
* unrelated globals (e.g. a `ThrottlerGuard` that needs a writable response)
|
|
146
|
+
* would misbehave over MCP. Empty/omitted ⇒ no global guards run.
|
|
147
|
+
*
|
|
148
|
+
* Note: over MCP the request stand-in is headers-only (`params`/`query` are
|
|
149
|
+
* empty), so per-session or IP-derived guard logic won't apply; header-based
|
|
150
|
+
* authentication still works.
|
|
151
|
+
*/
|
|
152
|
+
globalGuards?: Type<CanActivate>[];
|
|
140
153
|
}
|
|
141
154
|
declare const SILKWEAVE_MODULE_OPTIONS = "__silkweave_module_options__";
|
|
142
155
|
//#endregion
|
|
@@ -248,15 +261,17 @@ declare class ControllerDiscovery {
|
|
|
248
261
|
private readonly scanner;
|
|
249
262
|
private readonly reflector;
|
|
250
263
|
private readonly moduleRef;
|
|
251
|
-
|
|
264
|
+
private readonly appConfig;
|
|
265
|
+
constructor(discovery: DiscoveryService, scanner: MetadataScanner, reflector: Reflector, moduleRef: ModuleRef, appConfig: ApplicationConfig);
|
|
252
266
|
/**
|
|
253
267
|
* Walk every Nest provider/controller, find methods annotated with `@Mcp`,
|
|
254
268
|
* and build a core `Action` per method whose input schema is reflected from
|
|
255
269
|
* the route + parameter decorators (+ optional OpenAPI document) and whose
|
|
256
270
|
* `run` re-binds the validated input back into the method's positional
|
|
257
|
-
* arguments (with `@UseGuards` guards
|
|
271
|
+
* arguments (with `@UseGuards` guards - and any opted-in `globalGuards` -
|
|
272
|
+
* applied first).
|
|
258
273
|
*/
|
|
259
|
-
discover(openapi?: OpenApiDocument): Action[];
|
|
274
|
+
discover(openapi?: OpenApiDocument, globalGuards?: Type<CanActivate>[]): Action[];
|
|
260
275
|
private toAction;
|
|
261
276
|
/** Build the merged Zod input shape and the per-argument re-bind plan. */
|
|
262
277
|
private buildInput;
|
|
@@ -264,6 +279,23 @@ declare class ControllerDiscovery {
|
|
|
264
279
|
//#endregion
|
|
265
280
|
//#region src/lib/guards.d.ts
|
|
266
281
|
type GuardRef = Type<CanActivate> | CanActivate;
|
|
282
|
+
/**
|
|
283
|
+
* Collect the app's global guards that match an opt-in allow-list of classes.
|
|
284
|
+
*
|
|
285
|
+
* Reads both registration styles Nest exposes via `ApplicationConfig`:
|
|
286
|
+
* `useGlobalGuards(new X())` instances (`getGlobalGuards()`) and
|
|
287
|
+
* `{ provide: APP_GUARD, useClass }` DI guards (`getGlobalRequestGuards()`,
|
|
288
|
+
* which yields `InstanceWrapper`s - we read `.instance` off each).
|
|
289
|
+
*
|
|
290
|
+
* The allow-list is intentionally explicit-by-class: a blanket "run every
|
|
291
|
+
* global" would also fire unrelated globals (e.g. a `ThrottlerGuard` that
|
|
292
|
+
* assumes a writable response) on every tool call. An empty allow-list runs
|
|
293
|
+
* no globals, preserving the prior behavior.
|
|
294
|
+
*
|
|
295
|
+
* Call this at tool-call time, not at discovery time: `APP_GUARD` instances
|
|
296
|
+
* aren't populated until `app.init()` finishes.
|
|
297
|
+
*/
|
|
298
|
+
declare function collectGlobalGuards(appConfig: ApplicationConfig, allowList: Type<CanActivate>[]): CanActivate[];
|
|
267
299
|
/**
|
|
268
300
|
* Read `@UseGuards(...)` metadata for both the method and its class and merge
|
|
269
301
|
* the two lists. Method-level guards run AFTER class-level guards (matching
|
|
@@ -372,5 +404,5 @@ declare class SilkweaveModule implements NestModule {
|
|
|
372
404
|
configure(_consumer: MiddlewareConsumer): void;
|
|
373
405
|
}
|
|
374
406
|
//#endregion
|
|
375
|
-
export { Binding, ControllerDiscovery, FieldDesc, MCP_METADATA, Mcp, McpAdapterOptions, McpMetadata, NestAdapterRegisterContext, NestSilkweaveAdapter, OpenApiDocument, OpenApiLookup, SILKWEAVE_MODULE_OPTIONS, SilkweaveModule, SilkweaveModuleOptions, apiPropertyToField, buildOpenApiLookup, classValidatorToField, collectGuards, designTypeToField, fieldToZod, invokeRebound, mcp, mergeField, normalizeEnum, openApiFields, openapiSchemaToField, reflectDtoFields, runGuards, specialBinding, swaggerParamToField, typeTokenToBase };
|
|
407
|
+
export { Binding, ControllerDiscovery, FieldDesc, MCP_METADATA, Mcp, McpAdapterOptions, McpMetadata, NestAdapterRegisterContext, NestSilkweaveAdapter, OpenApiDocument, OpenApiLookup, SILKWEAVE_MODULE_OPTIONS, SilkweaveModule, SilkweaveModuleOptions, apiPropertyToField, buildOpenApiLookup, classValidatorToField, collectGlobalGuards, collectGuards, designTypeToField, fieldToZod, invokeRebound, mcp, mergeField, normalizeEnum, openApiFields, openapiSchemaToField, reflectDtoFields, runGuards, specialBinding, swaggerParamToField, typeTokenToBase };
|
|
376
408
|
//# sourceMappingURL=index.d.mts.map
|
package/build/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/lib/reflect/schema.ts","../src/lib/reflect/openapi.ts","../src/lib/types.ts","../src/adapter/mcp.ts","../src/lib/metadata.ts","../src/decorator/mcp.ts","../src/lib/controllerDiscovery.ts","../src/lib/guards.ts","../src/lib/rebind.ts","../src/lib/silkweave.module.ts"],"mappings":";;;;;;;;;;;;;;;UAeiB,SAAA;EACf,IAAA;EACA,QAAA;EACA,WAAA;EACA,IAAA;EACA,KAAA,GAAQ,SAAA;EACR,GAAA;EACA,GAAA;EACA,SAAA;EACA,SAAA;EACA,MAAA;EACA,OAAA;AAAA;;iBAac,UAAA,CAAW,IAAA,EAAM,SAAA,EAAW,IAAA,EAAM,SAAA,GAAY,SAAA;;iBA2C9C,UAAA,CAAW,CAAA,EAAG,SAAA,GAAY,CAAA,CAAE,OAAA;;iBAc5B,aAAA,CAAc,KAAA;;iBAWd,eAAA,CAAgB,IAAA,YAAgB,SAAA;;iBAkBhC,iBAAA,CAAkB,IAAA,YAAgB,SAAA;;iBAQlC,mBAAA,CAAoB,CAAA,EAAG,MAAA,gBAAsB,SAAA;;iBAgB7C,kBAAA,CAAmB,CAAA,EAAG,MAAA,gBAAsB,SAAA;;iBA4D5C,qBAAA,CAAsB,KAAA,EAAO,KAAA;EAAQ,IAAA;EAAe,IAAA;EAAe,WAAA;AAAA,KAA6B,SAAA;AA/HhH;AAAA,iBAsIgB,oBAAA,CAAqB,MAAA,EAAQ,MAAA,gBAAsB,SAAA;;;;;;;;iBA4BnD,gBAAA,CAAiB,OAAA,QAAe,MAAA,SAAe,SAAA;;;;;;;;UC5O9C,eAAA;EACf,KAAA,GAAQ,MAAA,SAAe,MAAA;EACvB,UAAA;IAAe,OAAA,GAAU,MAAA;EAAA;AAAA;AAAA,UAGV,aAAA;EACf,GAAA,EAAK,eAAA;EDIL;ECFA,UAAA,EAAY,GAAA;AAAA;;iBAIE,kBAAA,CAAmB,GAAA,EAAK,eAAA,GAAkB,aAAA;;;;;;;iBAwC1C,aAAA,CAAc,MAAA,EAAQ,aAAA,EAAe,MAAA,UAAgB,WAAA,WAAsB,MAAA,SAAe,SAAA
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/lib/reflect/schema.ts","../src/lib/reflect/openapi.ts","../src/lib/types.ts","../src/adapter/mcp.ts","../src/lib/metadata.ts","../src/decorator/mcp.ts","../src/lib/controllerDiscovery.ts","../src/lib/guards.ts","../src/lib/rebind.ts","../src/lib/silkweave.module.ts"],"mappings":"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"}
|
package/build/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { authMiddleware, mcpCors, mcpTransport, oauthRoutes, protectedResourceMetadata, sideloadResource } from "@silkweave/mcp";
|
|
3
3
|
import express from "express";
|
|
4
4
|
import { ForbiddenException, Inject, Injectable, Module, SetMetadata } from "@nestjs/common";
|
|
5
|
-
import { DiscoveryModule, DiscoveryService, HttpAdapterHost, MetadataScanner, ModuleRef, Reflector } from "@nestjs/core";
|
|
5
|
+
import { ApplicationConfig, DiscoveryModule, DiscoveryService, HttpAdapterHost, MetadataScanner, ModuleRef, Reflector } from "@nestjs/core";
|
|
6
6
|
import { createAction, createContext } from "@silkweave/core";
|
|
7
7
|
import { z } from "zod/v4";
|
|
8
8
|
import { GUARDS_METADATA, METHOD_METADATA, PATH_METADATA, ROUTE_ARGS_METADATA } from "@nestjs/common/constants.js";
|
|
@@ -8761,6 +8761,26 @@ var SilkweaveExecutionContext = class {
|
|
|
8761
8761
|
//#endregion
|
|
8762
8762
|
//#region src/lib/guards.ts
|
|
8763
8763
|
/**
|
|
8764
|
+
* Collect the app's global guards that match an opt-in allow-list of classes.
|
|
8765
|
+
*
|
|
8766
|
+
* Reads both registration styles Nest exposes via `ApplicationConfig`:
|
|
8767
|
+
* `useGlobalGuards(new X())` instances (`getGlobalGuards()`) and
|
|
8768
|
+
* `{ provide: APP_GUARD, useClass }` DI guards (`getGlobalRequestGuards()`,
|
|
8769
|
+
* which yields `InstanceWrapper`s - we read `.instance` off each).
|
|
8770
|
+
*
|
|
8771
|
+
* The allow-list is intentionally explicit-by-class: a blanket "run every
|
|
8772
|
+
* global" would also fire unrelated globals (e.g. a `ThrottlerGuard` that
|
|
8773
|
+
* assumes a writable response) on every tool call. An empty allow-list runs
|
|
8774
|
+
* no globals, preserving the prior behavior.
|
|
8775
|
+
*
|
|
8776
|
+
* Call this at tool-call time, not at discovery time: `APP_GUARD` instances
|
|
8777
|
+
* aren't populated until `app.init()` finishes.
|
|
8778
|
+
*/
|
|
8779
|
+
function collectGlobalGuards(appConfig, allowList) {
|
|
8780
|
+
if (allowList.length === 0) return [];
|
|
8781
|
+
return [...appConfig.getGlobalGuards(), ...appConfig.getGlobalRequestGuards().map((w) => w.instance)].filter((g) => g != null).filter((g) => allowList.some((c) => g instanceof c));
|
|
8782
|
+
}
|
|
8783
|
+
/**
|
|
8764
8784
|
* Read `@UseGuards(...)` metadata for both the method and its class and merge
|
|
8765
8785
|
* the two lists. Method-level guards run AFTER class-level guards (matching
|
|
8766
8786
|
* Nest's own behavior).
|
|
@@ -9340,22 +9360,24 @@ function __decorate(decorators, target, key, desc) {
|
|
|
9340
9360
|
}
|
|
9341
9361
|
//#endregion
|
|
9342
9362
|
//#region src/lib/controllerDiscovery.ts
|
|
9343
|
-
var _ref$1, _ref2$1, _ref3, _ref4;
|
|
9363
|
+
var _ref$1, _ref2$1, _ref3, _ref4, _ref5;
|
|
9344
9364
|
let ControllerDiscovery = class ControllerDiscovery {
|
|
9345
|
-
constructor(discovery, scanner, reflector, moduleRef) {
|
|
9365
|
+
constructor(discovery, scanner, reflector, moduleRef, appConfig) {
|
|
9346
9366
|
this.discovery = discovery;
|
|
9347
9367
|
this.scanner = scanner;
|
|
9348
9368
|
this.reflector = reflector;
|
|
9349
9369
|
this.moduleRef = moduleRef;
|
|
9370
|
+
this.appConfig = appConfig;
|
|
9350
9371
|
}
|
|
9351
9372
|
/**
|
|
9352
9373
|
* Walk every Nest provider/controller, find methods annotated with `@Mcp`,
|
|
9353
9374
|
* and build a core `Action` per method whose input schema is reflected from
|
|
9354
9375
|
* the route + parameter decorators (+ optional OpenAPI document) and whose
|
|
9355
9376
|
* `run` re-binds the validated input back into the method's positional
|
|
9356
|
-
* arguments (with `@UseGuards` guards
|
|
9377
|
+
* arguments (with `@UseGuards` guards - and any opted-in `globalGuards` -
|
|
9378
|
+
* applied first).
|
|
9357
9379
|
*/
|
|
9358
|
-
discover(openapi) {
|
|
9380
|
+
discover(openapi, globalGuards = []) {
|
|
9359
9381
|
const lookup = openapi ? buildOpenApiLookup(openapi) : void 0;
|
|
9360
9382
|
const discovered = [];
|
|
9361
9383
|
for (const wrapper of this.discovery.getProviders().concat(this.discovery.getControllers())) {
|
|
@@ -9378,9 +9400,9 @@ let ControllerDiscovery = class ControllerDiscovery {
|
|
|
9378
9400
|
});
|
|
9379
9401
|
}
|
|
9380
9402
|
}
|
|
9381
|
-
return discovered.map((d) => this.toAction(d, lookup));
|
|
9403
|
+
return discovered.map((d) => this.toAction(d, lookup, globalGuards));
|
|
9382
9404
|
}
|
|
9383
|
-
toAction(d, lookup) {
|
|
9405
|
+
toAction(d, lookup, globalGuards) {
|
|
9384
9406
|
const proto = Object.getPrototypeOf(d.instance);
|
|
9385
9407
|
const route = reflectRoute(d.classRef, d.method);
|
|
9386
9408
|
const slots = readParamSlots(d.classRef, d.methodName, proto);
|
|
@@ -9392,20 +9414,24 @@ let ControllerDiscovery = class ControllerDiscovery {
|
|
|
9392
9414
|
const description = d.meta.description ?? operation.description ?? `${d.methodName} (${route.method} /${route.path})`;
|
|
9393
9415
|
const applyParamPipes = d.meta.pipes !== "skip";
|
|
9394
9416
|
const guards = collectGuards(this.reflector, d.classRef, d.method);
|
|
9395
|
-
const
|
|
9417
|
+
const pathFields = pathParamFields(bindings);
|
|
9418
|
+
const { moduleRef, reflector, appConfig } = this;
|
|
9396
9419
|
const classRef = d.classRef;
|
|
9397
9420
|
const method = d.method;
|
|
9398
9421
|
const instance = d.instance;
|
|
9399
|
-
const applyGuards = async (context) => {
|
|
9400
|
-
|
|
9422
|
+
const applyGuards = async (context, input) => {
|
|
9423
|
+
const all = [...collectGlobalGuards(appConfig, globalGuards), ...guards];
|
|
9424
|
+
if (all.length === 0) return;
|
|
9401
9425
|
const request = context.getOptional("request");
|
|
9402
9426
|
const response = context.getOptional("response") ?? null;
|
|
9403
9427
|
const hasRequest = request != null;
|
|
9404
|
-
|
|
9428
|
+
const guardRequest = hasRequest ? request : {
|
|
9405
9429
|
headers: {},
|
|
9406
9430
|
params: {},
|
|
9407
9431
|
query: {}
|
|
9408
|
-
}
|
|
9432
|
+
};
|
|
9433
|
+
populatePathParams(guardRequest, pathFields, input);
|
|
9434
|
+
await runGuards(all, moduleRef, reflector, classRef, method, guardRequest, response, hasRequest ? "http" : "rpc");
|
|
9409
9435
|
};
|
|
9410
9436
|
return createAction({
|
|
9411
9437
|
name,
|
|
@@ -9413,7 +9439,7 @@ let ControllerDiscovery = class ControllerDiscovery {
|
|
|
9413
9439
|
input: z.object(shape),
|
|
9414
9440
|
isEnabled: (ctx) => ctx.getOptional("adapter") === "mcp",
|
|
9415
9441
|
run: async (input, context) => {
|
|
9416
|
-
await applyGuards(context);
|
|
9442
|
+
await applyGuards(context, input);
|
|
9417
9443
|
return await invokeRebound(method, instance, input, bindings, context.getOptional("request"), applyParamPipes) ?? {};
|
|
9418
9444
|
}
|
|
9419
9445
|
});
|
|
@@ -9448,8 +9474,26 @@ ControllerDiscovery = __decorate([Injectable(), __decorateMetadata("design:param
|
|
|
9448
9474
|
typeof (_ref$1 = typeof DiscoveryService !== "undefined" && DiscoveryService) === "function" ? _ref$1 : Object,
|
|
9449
9475
|
typeof (_ref2$1 = typeof MetadataScanner !== "undefined" && MetadataScanner) === "function" ? _ref2$1 : Object,
|
|
9450
9476
|
typeof (_ref3 = typeof Reflector !== "undefined" && Reflector) === "function" ? _ref3 : Object,
|
|
9451
|
-
typeof (_ref4 = typeof ModuleRef !== "undefined" && ModuleRef) === "function" ? _ref4 : Object
|
|
9477
|
+
typeof (_ref4 = typeof ModuleRef !== "undefined" && ModuleRef) === "function" ? _ref4 : Object,
|
|
9478
|
+
typeof (_ref5 = typeof ApplicationConfig !== "undefined" && ApplicationConfig) === "function" ? _ref5 : Object
|
|
9452
9479
|
])], ControllerDiscovery);
|
|
9480
|
+
/** Input field names that originate from the URL path (`@Param`), per the re-bind plan. */
|
|
9481
|
+
function pathParamFields(bindings) {
|
|
9482
|
+
const fields = [];
|
|
9483
|
+
for (const b of bindings) if (b.kind === "params") fields.push(...b.fields);
|
|
9484
|
+
else if (b.kind === "value" && b.source === "path") fields.push(b.field);
|
|
9485
|
+
return fields;
|
|
9486
|
+
}
|
|
9487
|
+
/**
|
|
9488
|
+
* Fill `request.params` with the reflected path fields from the validated input,
|
|
9489
|
+
* matching what Express would populate over REST (raw string values). Only adds
|
|
9490
|
+
* keys that are absent, so a real REST request's params are never overwritten.
|
|
9491
|
+
*/
|
|
9492
|
+
function populatePathParams(request, pathFields, input) {
|
|
9493
|
+
if (pathFields.length === 0 || typeof request !== "object" || request === null) return;
|
|
9494
|
+
const params = request.params ??= {};
|
|
9495
|
+
for (const field of pathFields) if (!(field in params) && input[field] !== void 0) params[field] = String(input[field]);
|
|
9496
|
+
}
|
|
9453
9497
|
function designTypeAt(designTypes, index) {
|
|
9454
9498
|
const ctor = designTypes[index];
|
|
9455
9499
|
if (ctor === String) return { type: "string" };
|
|
@@ -9555,7 +9599,7 @@ let SilkweaveModule = _SilkweaveModule = class SilkweaveModule {
|
|
|
9555
9599
|
configure(_consumer) {
|
|
9556
9600
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
9557
9601
|
if (!httpAdapter) throw new Error("@silkweave/nestjs: HttpAdapterHost.httpAdapter is not available.");
|
|
9558
|
-
const allActions = this.discovery.discover(this.options.openapi);
|
|
9602
|
+
const allActions = this.discovery.discover(this.options.openapi, this.options.globalGuards);
|
|
9559
9603
|
for (const adapter of this.options.adapters) {
|
|
9560
9604
|
const baseContext = createContext({
|
|
9561
9605
|
...this.options.context ?? {},
|
|
@@ -9581,6 +9625,6 @@ SilkweaveModule = _SilkweaveModule = __decorate([
|
|
|
9581
9625
|
])
|
|
9582
9626
|
], SilkweaveModule);
|
|
9583
9627
|
//#endregion
|
|
9584
|
-
export { ControllerDiscovery, MCP_METADATA, Mcp, SILKWEAVE_MODULE_OPTIONS, SilkweaveModule, apiPropertyToField, buildOpenApiLookup, classValidatorToField, collectGuards, designTypeToField, fieldToZod, invokeRebound, mcp, mergeField, normalizeEnum, openApiFields, openapiSchemaToField, reflectDtoFields, runGuards, specialBinding, swaggerParamToField, typeTokenToBase };
|
|
9628
|
+
export { ControllerDiscovery, MCP_METADATA, Mcp, SILKWEAVE_MODULE_OPTIONS, SilkweaveModule, apiPropertyToField, buildOpenApiLookup, classValidatorToField, collectGlobalGuards, collectGuards, designTypeToField, fieldToZod, invokeRebound, mcp, mergeField, normalizeEnum, openApiFields, openapiSchemaToField, reflectDtoFields, runGuards, specialBinding, swaggerParamToField, typeTokenToBase };
|
|
9585
9629
|
|
|
9586
9630
|
//# sourceMappingURL=index.mjs.map
|