@silkweave/nestjs 1.10.0 → 1.11.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweave/nestjs",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "Silkweave NestJS Adapter",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,12 +19,12 @@
19
19
  "cors": "^2.8.6",
20
20
  "express": "^5.2.1",
21
21
  "zod": "^3.25.0",
22
- "@silkweave/auth": "1.10.0",
23
- "@silkweave/core": "1.10.0",
24
- "@silkweave/trpc": "1.10.0",
25
- "@silkweave/mcp": "1.10.0",
26
- "@silkweave/logger": "1.10.0",
27
- "@silkweave/typegen": "1.10.0"
22
+ "@silkweave/auth": "1.11.0",
23
+ "@silkweave/core": "1.11.0",
24
+ "@silkweave/mcp": "1.11.0",
25
+ "@silkweave/logger": "1.11.0",
26
+ "@silkweave/typegen": "1.11.0",
27
+ "@silkweave/trpc": "1.11.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@eslint/js": "^10.0.1",
@@ -30,9 +30,10 @@ export class ActionDiscovery {
30
30
  * a list of core `Action` objects ready to feed into `silkweave().actions()`.
31
31
  *
32
32
  * Action invocation is wrapped to (a) run `@UseGuards` guards declared on the
33
- * method or its class against the incoming HTTP request (read from
34
- * `ctx.get('request')`) and (b) bind `this` to the resolved Nest provider so
35
- * DI-injected dependencies remain available.
33
+ * method or its class against the incoming request (read from
34
+ * `ctx.get('request')`, populated by REST/tRPC and by MCP-over-HTTP from the
35
+ * SDK's `extra.requestInfo`) and (b) bind `this` to the resolved Nest provider
36
+ * so DI-injected dependencies remain available.
36
37
  */
37
38
  discover(): Action[] {
38
39
  const discovered: DiscoveredAction[] = []
@@ -66,9 +67,16 @@ export class ActionDiscovery {
66
67
 
67
68
  const applyGuards = async (context: SilkweaveContext): Promise<void> => {
68
69
  if (guards.length === 0) { return }
70
+ // REST and tRPC populate `request`/`response`; MCP-over-HTTP populates
71
+ // `request` (a `{ headers, url, params, query }` stand-in built from the
72
+ // SDK's `extra.requestInfo`). Transports with no HTTP request at all (e.g.
73
+ // MCP stdio) get a header-less stand-in so guards reading `req.headers`
74
+ // degrade gracefully (deny) instead of dereferencing `undefined`.
69
75
  const request = context.getOptional<unknown>('request')
70
- const response = context.getOptional<unknown>('response')
71
- await runGuards(guards, moduleRef, reflector, d.classRef, d.method, request, response)
76
+ const response = context.getOptional<unknown>('response') ?? null
77
+ const hasRequest = request != null
78
+ const guardRequest = hasRequest ? request : { headers: {}, params: {}, query: {} }
79
+ await runGuards(guards, moduleRef, reflector, d.classRef, d.method, guardRequest, response, hasRequest ? 'http' : 'rpc')
72
80
  }
73
81
 
74
82
  // Cast at the createAction boundary to bridge dual-zod-version installs
package/src/lib/guards.ts CHANGED
@@ -33,12 +33,17 @@ async function resolveGuard(ref: GuardRef, moduleRef: ModuleRef): Promise<CanAct
33
33
  }
34
34
 
35
35
  /**
36
- * Run the configured guards against an HTTP request. Throws `ForbiddenException`
36
+ * Run the configured guards against a request. Throws `ForbiddenException`
37
37
  * if any guard rejects, mirroring Nest's HTTP request-pipeline behavior.
38
38
  *
39
39
  * Pass `null` for `response` when running on top of a tRPC or MCP request that
40
40
  * doesn't surface a raw response object; guards that introspect the response
41
41
  * will receive `null`.
42
+ *
43
+ * `contextType` is reflected through `ExecutionContext.getType()` so guards can
44
+ * branch on the transport. It is `'http'` for REST/tRPC and for MCP-over-HTTP
45
+ * (where a header-bearing request stand-in is available); transports without any
46
+ * HTTP request (e.g. MCP stdio) pass `'rpc'`.
42
47
  */
43
48
  export async function runGuards(
44
49
  guards: GuardRef[],
@@ -47,10 +52,11 @@ export async function runGuards(
47
52
  classRef: Type<unknown>,
48
53
  handler: (...args: unknown[]) => unknown,
49
54
  request: unknown,
50
- response: unknown
55
+ response: unknown,
56
+ contextType: 'http' | 'rpc' = 'http'
51
57
  ): Promise<void> {
52
58
  if (guards.length === 0) { return }
53
- const context = new SilkweaveExecutionContext([request, response], classRef, handler, 'http')
59
+ const context = new SilkweaveExecutionContext([request, response], classRef, handler, contextType)
54
60
  for (const ref of guards) {
55
61
  const guard = await resolveGuard(ref, moduleRef)
56
62
  const result = guard.canActivate(context)