@silkweave/nestjs 1.9.1 → 1.10.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.9.1",
3
+ "version": "1.10.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.9.1",
23
- "@silkweave/logger": "1.9.1",
24
- "@silkweave/trpc": "1.9.1",
25
- "@silkweave/core": "1.9.1",
26
- "@silkweave/typegen": "1.9.1",
27
- "@silkweave/mcp": "1.9.1"
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"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@eslint/js": "^10.0.1",
@@ -31,8 +31,8 @@ import { ACTION_METADATA, type ActionMetadata } from '../lib/metadata.js'
31
31
  * }
32
32
  * ```
33
33
  */
34
- export function Action<I extends object = object, O extends object = object>(
35
- options: ActionMetadata<I, O>
34
+ export function Action<I extends object = object, O extends object = object, C = unknown>(
35
+ options: ActionMetadata<I, O, C>
36
36
  ): MethodDecorator {
37
37
  return SetMetadata(ACTION_METADATA, options)
38
38
  }
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
2
  import { Injectable, type Type } from '@nestjs/common'
3
3
  import { DiscoveryService, MetadataScanner, ModuleRef, Reflector } from '@nestjs/core'
4
- import { createAction, type Action, type SilkweaveContext } from '@silkweave/core'
4
+ import { createAction, type Action, type ActionKind, type SilkweaveContext, type StreamingActionInput } from '@silkweave/core'
5
5
  import { kebabCase } from 'change-case'
6
6
  import { buildIsEnabled } from './filter.js'
7
7
  import { collectGuards, runGuards } from './guards.js'
@@ -64,9 +64,40 @@ export class ActionDiscovery {
64
64
  const moduleRef = this.moduleRef
65
65
  const reflector = this.reflector
66
66
 
67
+ const applyGuards = async (context: SilkweaveContext): Promise<void> => {
68
+ if (guards.length === 0) { return }
69
+ 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)
72
+ }
73
+
67
74
  // Cast at the createAction boundary to bridge dual-zod-version installs
68
75
  // (zod@3.25 + zod@4.x can both be present transitively). Runtime is fine -
69
76
  // they share the same /v4 surface - but the structural types are distinct.
77
+ const streaming = d.method.constructor?.name === 'AsyncGeneratorFunction'
78
+
79
+ if (streaming) {
80
+ if (!d.meta.chunk) {
81
+ throw new Error(`@Action "${name}" is an async generator but has no \`chunk\` schema`)
82
+ }
83
+ const method = d.method
84
+ const instance = d.instance
85
+ return createAction({
86
+ name,
87
+ description: d.meta.description,
88
+ input: d.meta.input,
89
+ chunk: d.meta.chunk,
90
+ kind: d.meta.kind ?? 'mutation',
91
+ args: d.meta.args,
92
+ isEnabled,
93
+ toolResult: d.meta.toolResult,
94
+ run: async function* (input: object, context: SilkweaveContext) {
95
+ await applyGuards(context)
96
+ yield* (method.call(instance, input, context) as AsyncGenerator<object, void, void>)
97
+ }
98
+ } as StreamingActionInput<object, unknown, string, ActionKind>) as Action
99
+ }
100
+
70
101
  return createAction({
71
102
  name,
72
103
  description: d.meta.description,
@@ -77,11 +108,7 @@ export class ActionDiscovery {
77
108
  isEnabled,
78
109
  toolResult: d.meta.toolResult,
79
110
  run: async (input: object, context: SilkweaveContext): Promise<object> => {
80
- if (guards.length > 0) {
81
- const request = context.getOptional<unknown>('request')
82
- const response = context.getOptional<unknown>('response')
83
- await runGuards(guards, moduleRef, reflector, d.classRef, d.method, request, response)
84
- }
111
+ await applyGuards(context)
85
112
  const result = await (d.method.call(d.instance, input, context) as Promise<object>)
86
113
  return result
87
114
  }
@@ -6,7 +6,7 @@ export const ACTIONS_METADATA = '__silkweave_actions__'
6
6
 
7
7
  export type Transport = 'rest' | 'trpc' | 'mcp'
8
8
 
9
- export interface ActionMetadata<I extends object = object, O extends object = object> {
9
+ export interface ActionMetadata<I extends object = object, O extends object = object, C = unknown> {
10
10
  /** Action name. Defaults to the kebab-cased method name. */
11
11
  name?: string
12
12
  /** Human-readable description. Becomes the MCP tool description and REST OpenAPI summary. */
@@ -15,6 +15,13 @@ export interface ActionMetadata<I extends object = object, O extends object = ob
15
15
  input: z.ZodType<I> & { shape: Record<string, z.ZodTypeAny> }
16
16
  /** Optional Zod object schema for the action's output (used by tRPC type inference). */
17
17
  output?: z.ZodType<O> & { shape: Record<string, z.ZodTypeAny> }
18
+ /**
19
+ * Zod schema for individual chunks yielded by a streaming (async-generator)
20
+ * action method. Required when the decorated method is an `async function*`.
21
+ * Mirrors `Action.chunk` in @silkweave/core; typegen/trpc use it to expose
22
+ * the action as a tRPC subscription.
23
+ */
24
+ chunk?: z.ZodType<C>
18
25
  /** `'query'` (GET in REST, `.query()` in tRPC) or `'mutation'` (POST in REST, `.mutation()` in tRPC). Default: `'mutation'`. */
19
26
  kind?: ActionKind
20
27
  /** Allowlist of transports that should expose this action. Default: all registered transports. */