@silkweave/nestjs 1.9.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 +17 -0
- package/.turbo/turbo-check.log +4 -0
- package/README.md +210 -0
- package/build/index.d.mts +315 -0
- package/build/index.d.mts.map +1 -0
- package/build/index.mjs +9231 -0
- package/build/index.mjs.map +1 -0
- package/eslint.config.mjs +93 -0
- package/package.json +52 -0
- package/src/adapter/mcp.ts +55 -0
- package/src/adapter/rest.ts +179 -0
- package/src/adapter/trpc.ts +82 -0
- package/src/decorator/action.ts +38 -0
- package/src/decorator/actions.ts +25 -0
- package/src/index.ts +12 -0
- package/src/lib/discovery.ts +90 -0
- package/src/lib/executionContext.ts +76 -0
- package/src/lib/filter.ts +26 -0
- package/src/lib/guards.ts +64 -0
- package/src/lib/metadata.ts +45 -0
- package/src/lib/silkweave.module.ts +52 -0
- package/src/lib/silkweave.service.ts +58 -0
- package/src/lib/slot.ts +51 -0
- package/src/lib/types.ts +37 -0
- package/tsconfig.json +25 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.ts +7 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { ArgumentsHost, ContextType, ExecutionContext, Type } from '@nestjs/common'
|
|
2
|
+
|
|
3
|
+
/** Subset of `HttpArgumentsHost` we need — re-declared inline to avoid deep `@nestjs/common/interfaces` imports. */
|
|
4
|
+
interface HttpHost {
|
|
5
|
+
getRequest<T = unknown>(): T
|
|
6
|
+
getResponse<T = unknown>(): T
|
|
7
|
+
getNext<T = unknown>(): T
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface RpcHost {
|
|
11
|
+
getData<T = unknown>(): T
|
|
12
|
+
getContext<T = unknown>(): T
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface WsHost {
|
|
16
|
+
getClient<T = unknown>(): T
|
|
17
|
+
getData<T = unknown>(): T
|
|
18
|
+
getPattern(): string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Minimal `ExecutionContext` impl Nest guards can consume. We only need
|
|
23
|
+
* `switchToHttp()` to work for our HTTP-backed transports; the RPC/WS shims are
|
|
24
|
+
* stubbed so guards that introspect type can still call them without crashing.
|
|
25
|
+
*/
|
|
26
|
+
export class SilkweaveExecutionContext implements ExecutionContext, ArgumentsHost {
|
|
27
|
+
constructor(
|
|
28
|
+
private readonly args: unknown[],
|
|
29
|
+
private readonly classRef: Type<unknown>,
|
|
30
|
+
private readonly handler: (...handlerArgs: unknown[]) => unknown,
|
|
31
|
+
private readonly contextType: string = 'http'
|
|
32
|
+
) {}
|
|
33
|
+
|
|
34
|
+
getType<T extends string = ContextType>(): T {
|
|
35
|
+
return this.contextType as T
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getClass<T = unknown>(): Type<T> {
|
|
39
|
+
return this.classRef as Type<T>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getHandler(): (...handlerArgs: unknown[]) => unknown {
|
|
43
|
+
return this.handler
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getArgs<T extends unknown[] = unknown[]>(): T {
|
|
47
|
+
return this.args as T
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
getArgByIndex<T = unknown>(index: number): T {
|
|
51
|
+
return this.args[index] as T
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
switchToHttp(): HttpHost {
|
|
55
|
+
return {
|
|
56
|
+
getRequest: <T = unknown>(): T => this.args[0] as T,
|
|
57
|
+
getResponse: <T = unknown>(): T => this.args[1] as T,
|
|
58
|
+
getNext: <T = unknown>(): T => this.args[2] as T
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
switchToRpc(): RpcHost {
|
|
63
|
+
return {
|
|
64
|
+
getData: <T = unknown>(): T => this.args[0] as T,
|
|
65
|
+
getContext: <T = unknown>(): T => this.args[1] as T
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
switchToWs(): WsHost {
|
|
70
|
+
return {
|
|
71
|
+
getClient: <T = unknown>(): T => this.args[0] as T,
|
|
72
|
+
getData: <T = unknown>(): T => this.args[1] as T,
|
|
73
|
+
getPattern: (): string => ''
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SilkweaveContext } from '@silkweave/core'
|
|
2
|
+
import type { Transport } from './metadata.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Compile a `transports` allowlist + optional user `isEnabled` into a single
|
|
6
|
+
* `(ctx) => boolean` callback compatible with `Action.isEnabled`.
|
|
7
|
+
*
|
|
8
|
+
* - If `transports` is omitted, the action runs on every adapter.
|
|
9
|
+
* - If `transports` is set, the action is gated on `ctx.get<string>('adapter')`
|
|
10
|
+
* matching one of the listed transports. Each adapter in `@silkweave/nestjs`
|
|
11
|
+
* forks its context with `{ adapter: 'rest' | 'trpc' | 'mcp' }`.
|
|
12
|
+
* - If both `transports` and `userIsEnabled` are set, they are AND-combined.
|
|
13
|
+
*/
|
|
14
|
+
export function buildIsEnabled(
|
|
15
|
+
transports: Transport[] | undefined,
|
|
16
|
+
userIsEnabled: ((ctx: SilkweaveContext) => boolean) | undefined
|
|
17
|
+
): ((ctx: SilkweaveContext) => boolean) | undefined {
|
|
18
|
+
if (!transports && !userIsEnabled) { return undefined }
|
|
19
|
+
return (ctx) => {
|
|
20
|
+
if (transports) {
|
|
21
|
+
const adapter = ctx.getOptional<string>('adapter')
|
|
22
|
+
if (adapter && !transports.includes(adapter as Transport)) { return false }
|
|
23
|
+
}
|
|
24
|
+
return userIsEnabled ? userIsEnabled(ctx) : true
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ForbiddenException, type CanActivate, type Type } from '@nestjs/common'
|
|
2
|
+
import { GUARDS_METADATA } from '@nestjs/common/constants.js'
|
|
3
|
+
import { ModuleRef, Reflector } from '@nestjs/core'
|
|
4
|
+
import { isObservable, lastValueFrom } from 'rxjs'
|
|
5
|
+
import { SilkweaveExecutionContext } from './executionContext.js'
|
|
6
|
+
|
|
7
|
+
type GuardRef = Type<CanActivate> | CanActivate
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Read `@UseGuards(...)` metadata for both the method and its class and merge
|
|
11
|
+
* the two lists. Method-level guards run AFTER class-level guards (matching
|
|
12
|
+
* Nest's own behavior).
|
|
13
|
+
*/
|
|
14
|
+
export function collectGuards(
|
|
15
|
+
reflector: Reflector,
|
|
16
|
+
classRef: Type<unknown>,
|
|
17
|
+
handler: (...args: unknown[]) => unknown
|
|
18
|
+
): GuardRef[] {
|
|
19
|
+
const classGuards = reflector.get<GuardRef[]>(GUARDS_METADATA, classRef) ?? []
|
|
20
|
+
const methodGuards = reflector.get<GuardRef[]>(GUARDS_METADATA, handler) ?? []
|
|
21
|
+
return [...classGuards, ...methodGuards]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function resolveGuard(ref: GuardRef, moduleRef: ModuleRef): Promise<CanActivate> {
|
|
25
|
+
if (typeof ref === 'function') {
|
|
26
|
+
try {
|
|
27
|
+
return moduleRef.get(ref, { strict: false })
|
|
28
|
+
} catch {
|
|
29
|
+
return moduleRef.create(ref)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return ref
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Run the configured guards against an HTTP request. Throws `ForbiddenException`
|
|
37
|
+
* if any guard rejects, mirroring Nest's HTTP request-pipeline behavior.
|
|
38
|
+
*
|
|
39
|
+
* Pass `null` for `response` when running on top of a tRPC or MCP request that
|
|
40
|
+
* doesn't surface a raw response object; guards that introspect the response
|
|
41
|
+
* will receive `null`.
|
|
42
|
+
*/
|
|
43
|
+
export async function runGuards(
|
|
44
|
+
guards: GuardRef[],
|
|
45
|
+
moduleRef: ModuleRef,
|
|
46
|
+
reflector: Reflector,
|
|
47
|
+
classRef: Type<unknown>,
|
|
48
|
+
handler: (...args: unknown[]) => unknown,
|
|
49
|
+
request: unknown,
|
|
50
|
+
response: unknown
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
if (guards.length === 0) { return }
|
|
53
|
+
const context = new SilkweaveExecutionContext([request, response], classRef, handler, 'http')
|
|
54
|
+
for (const ref of guards) {
|
|
55
|
+
const guard = await resolveGuard(ref, moduleRef)
|
|
56
|
+
const result = guard.canActivate(context)
|
|
57
|
+
const allowed = isObservable(result) ? await lastValueFrom(result) : await Promise.resolve(result)
|
|
58
|
+
if (!allowed) {
|
|
59
|
+
throw new ForbiddenException('Forbidden resource')
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Reflector kept in the signature for future use (e.g., per-guard metadata) — unused here.
|
|
63
|
+
void reflector
|
|
64
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type Action, type ActionKind, type SilkweaveContext } from '@silkweave/core'
|
|
2
|
+
import type z from 'zod/v4'
|
|
3
|
+
|
|
4
|
+
export const ACTION_METADATA = '__silkweave_action__'
|
|
5
|
+
export const ACTIONS_METADATA = '__silkweave_actions__'
|
|
6
|
+
|
|
7
|
+
export type Transport = 'rest' | 'trpc' | 'mcp'
|
|
8
|
+
|
|
9
|
+
export interface ActionMetadata<I extends object = object, O extends object = object> {
|
|
10
|
+
/** Action name. Defaults to the kebab-cased method name. */
|
|
11
|
+
name?: string
|
|
12
|
+
/** Human-readable description. Becomes the MCP tool description and REST OpenAPI summary. */
|
|
13
|
+
description: string
|
|
14
|
+
/** Zod object schema for the action's input. */
|
|
15
|
+
input: z.ZodType<I> & { shape: Record<string, z.ZodTypeAny> }
|
|
16
|
+
/** Optional Zod object schema for the action's output (used by tRPC type inference). */
|
|
17
|
+
output?: z.ZodType<O> & { shape: Record<string, z.ZodTypeAny> }
|
|
18
|
+
/** `'query'` (GET in REST, `.query()` in tRPC) or `'mutation'` (POST in REST, `.mutation()` in tRPC). Default: `'mutation'`. */
|
|
19
|
+
kind?: ActionKind
|
|
20
|
+
/** Allowlist of transports that should expose this action. Default: all registered transports. */
|
|
21
|
+
transports?: Transport[]
|
|
22
|
+
/** Dynamic enable check (in addition to `transports`). AND-combined with the transports filter. */
|
|
23
|
+
isEnabled?: (context: SilkweaveContext) => boolean
|
|
24
|
+
/** Custom MCP `CallToolResult` formatter. See `@silkweave/mcp`'s `smartToolResult`. */
|
|
25
|
+
toolResult?: Action<I, O>['toolResult']
|
|
26
|
+
/** CLI positional-argument keys (unused for HTTP-only adapters, kept for parity with `createAction`). */
|
|
27
|
+
args?: (keyof I)[]
|
|
28
|
+
/** Custom MCP tool name override. If unset, derived from action name as PascalCase. */
|
|
29
|
+
mcpToolName?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ActionsClassMetadata {
|
|
33
|
+
/** Prefix joined to the method-level action name with a dot. e.g. `Actions('users')` + method `list` → `users.list`. */
|
|
34
|
+
prefix?: string
|
|
35
|
+
/** Class-level transports allowlist. Method-level `transports` overrides; otherwise inherits this. */
|
|
36
|
+
transports?: Transport[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ResultToolResult<O extends object = object> {
|
|
40
|
+
toolResult?: Action<object, O>['toolResult']
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type AnyActionMetadata = ActionMetadata<object, object> & ResultToolResult<object>
|
|
44
|
+
|
|
45
|
+
export const ACTION_RESPONSE_KEY = '__silkweave_response__'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Module, type DynamicModule } from '@nestjs/common'
|
|
2
|
+
import { DiscoveryModule } from '@nestjs/core'
|
|
3
|
+
import { ActionDiscovery } from './discovery.js'
|
|
4
|
+
import { SilkweaveService } from './silkweave.service.js'
|
|
5
|
+
import { SILKWEAVE_MODULE_OPTIONS, type SilkweaveModuleOptions } from './types.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Root module for `@silkweave/nestjs`.
|
|
9
|
+
*
|
|
10
|
+
* Discovers every `@Action`-decorated method across the Nest app via
|
|
11
|
+
* `DiscoveryService`, builds core `Action` objects from them, and mounts the
|
|
12
|
+
* configured adapters (`rest()`, `trpc()`, `mcp()`) onto Nest's running HTTP
|
|
13
|
+
* server during `OnApplicationBootstrap`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { Module } from '@nestjs/common'
|
|
18
|
+
* import { SilkweaveModule, rest, trpc, mcp } from '@silkweave/nestjs'
|
|
19
|
+
* import { UsersModule } from './users.module.js'
|
|
20
|
+
*
|
|
21
|
+
* @Module({
|
|
22
|
+
* imports: [
|
|
23
|
+
* SilkweaveModule.forRoot({
|
|
24
|
+
* silkweave: { name: 'app', description: 'My App', version: '1.0.0' },
|
|
25
|
+
* adapters: [
|
|
26
|
+
* rest({ basePath: '/api' }),
|
|
27
|
+
* trpc({ basePath: '/trpc' }),
|
|
28
|
+
* mcp({ basePath: '/mcp' })
|
|
29
|
+
* ]
|
|
30
|
+
* }),
|
|
31
|
+
* UsersModule
|
|
32
|
+
* ]
|
|
33
|
+
* })
|
|
34
|
+
* export class AppModule {}
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
@Module({})
|
|
38
|
+
export class SilkweaveModule {
|
|
39
|
+
static forRoot(options: SilkweaveModuleOptions): DynamicModule {
|
|
40
|
+
return {
|
|
41
|
+
module: SilkweaveModule,
|
|
42
|
+
global: true,
|
|
43
|
+
imports: [DiscoveryModule],
|
|
44
|
+
providers: [
|
|
45
|
+
{ provide: SILKWEAVE_MODULE_OPTIONS, useValue: options },
|
|
46
|
+
ActionDiscovery,
|
|
47
|
+
SilkweaveService
|
|
48
|
+
],
|
|
49
|
+
exports: [SilkweaveService]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Inject, Injectable, type OnApplicationBootstrap, type OnApplicationShutdown, type OnModuleInit } from '@nestjs/common'
|
|
2
|
+
import { HttpAdapterHost } from '@nestjs/core'
|
|
3
|
+
import { type AdapterGenerator, silkweave as createSilkweave, type Silkweave } from '@silkweave/core'
|
|
4
|
+
import { ActionDiscovery } from './discovery.js'
|
|
5
|
+
import { SILKWEAVE_MODULE_OPTIONS, type SilkweaveModuleOptions } from './types.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Coordinates the Nest lifecycle for `@silkweave/nestjs`:
|
|
9
|
+
*
|
|
10
|
+
* - `onModuleInit`: each configured adapter calls `install(host)` to reserve a
|
|
11
|
+
* placeholder middleware slot at its base path. This must happen here, not
|
|
12
|
+
* in `onApplicationBootstrap`, because Nest installs its 404 catch-all later
|
|
13
|
+
* during `init()` — routes registered after the catch-all are unreachable.
|
|
14
|
+
*
|
|
15
|
+
* - `onApplicationBootstrap`: discovers every `@Action` method, then drives the
|
|
16
|
+
* standard `silkweave().adapter(...).actions(...).start()` flow, which calls
|
|
17
|
+
* each adapter's `start(actions)` to populate the slot reserved earlier.
|
|
18
|
+
*/
|
|
19
|
+
@Injectable()
|
|
20
|
+
export class SilkweaveService implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown {
|
|
21
|
+
private builder?: Silkweave
|
|
22
|
+
private readonly generators: AdapterGenerator[] = []
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
@Inject(SILKWEAVE_MODULE_OPTIONS) private readonly options: SilkweaveModuleOptions,
|
|
26
|
+
private readonly discovery: ActionDiscovery,
|
|
27
|
+
private readonly httpAdapterHost: HttpAdapterHost
|
|
28
|
+
) {}
|
|
29
|
+
|
|
30
|
+
onModuleInit(): void {
|
|
31
|
+
for (const adapter of this.options.adapters) {
|
|
32
|
+
this.generators.push(adapter.install(this.httpAdapterHost))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async onApplicationBootstrap(): Promise<void> {
|
|
37
|
+
const actions = this.discovery.discover()
|
|
38
|
+
const builder = createSilkweave(this.options.silkweave)
|
|
39
|
+
for (const [key, value] of Object.entries(this.options.context ?? {})) {
|
|
40
|
+
builder.set(key, value)
|
|
41
|
+
}
|
|
42
|
+
for (const gen of this.generators) {
|
|
43
|
+
builder.adapter(gen)
|
|
44
|
+
}
|
|
45
|
+
builder.actions(actions)
|
|
46
|
+
await builder.start()
|
|
47
|
+
this.builder = builder
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async onApplicationShutdown(): Promise<void> {
|
|
51
|
+
this.builder = undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Access the underlying silkweave builder once bootstrap has completed. */
|
|
55
|
+
getBuilder(): Silkweave | undefined {
|
|
56
|
+
return this.builder
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/lib/slot.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'http'
|
|
2
|
+
|
|
3
|
+
export type NodeMiddleware = (
|
|
4
|
+
req: IncomingMessage,
|
|
5
|
+
res: ServerResponse,
|
|
6
|
+
next?: (err?: unknown) => void
|
|
7
|
+
) => unknown
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A mountable middleware slot that delegates to a settable handler. Used to
|
|
11
|
+
* reserve a path on Nest's HTTP server *before* its 404 catch-all is
|
|
12
|
+
* installed, then populate the real handler later in `OnApplicationBootstrap`.
|
|
13
|
+
*
|
|
14
|
+
* Before `set()` is called, the slot responds with HTTP 503.
|
|
15
|
+
*/
|
|
16
|
+
export interface MiddlewareSlot {
|
|
17
|
+
middleware: NodeMiddleware
|
|
18
|
+
set(handler: NodeMiddleware): void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createMiddlewareSlot(label: string): MiddlewareSlot {
|
|
22
|
+
let handler: NodeMiddleware = (_req, res) => {
|
|
23
|
+
res.statusCode = 503
|
|
24
|
+
res.setHeader('Content-Type', 'application/json')
|
|
25
|
+
res.end(JSON.stringify({ error: 'not_ready', message: `${label} adapter has not started yet` }))
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
middleware: (req, res, next) => handler(req, res, next),
|
|
29
|
+
set: (h) => { handler = h }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface HttpAdapterUseLike {
|
|
34
|
+
use(path: string, handler: NodeMiddleware): unknown
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Mount a middleware slot at the given base path on Nest's HTTP adapter and
|
|
39
|
+
* return the slot's `set()` callback. The slot middleware is installed
|
|
40
|
+
* synchronously so it sits in the Express stack before Nest's later-installed
|
|
41
|
+
* 404 catch-all.
|
|
42
|
+
*/
|
|
43
|
+
export function reserveSlot(
|
|
44
|
+
httpAdapter: HttpAdapterUseLike,
|
|
45
|
+
basePath: string,
|
|
46
|
+
label: string
|
|
47
|
+
): (handler: NodeMiddleware) => void {
|
|
48
|
+
const slot = createMiddlewareSlot(label)
|
|
49
|
+
httpAdapter.use(basePath, slot.middleware)
|
|
50
|
+
return slot.set
|
|
51
|
+
}
|
package/src/lib/types.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { HttpAdapterHost } from '@nestjs/core'
|
|
2
|
+
import type { AdapterGenerator, SilkweaveOptions } from '@silkweave/core'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A Silkweave NestJS adapter. Each transport (REST, tRPC, MCP) implements this
|
|
6
|
+
* shape: given a Nest `HttpAdapterHost`, it (a) immediately mounts a
|
|
7
|
+
* placeholder middleware slot on Nest's running HTTP server during
|
|
8
|
+
* `OnModuleInit` — which is critical because Nest installs its 404 catch-all
|
|
9
|
+
* later in `init()`, before `OnApplicationBootstrap` fires; routes registered
|
|
10
|
+
* after the catch-all are unreachable — and (b) returns a core
|
|
11
|
+
* `AdapterGenerator` whose `start(actions)` populates that slot with the real
|
|
12
|
+
* handler.
|
|
13
|
+
*
|
|
14
|
+
* Adapters mount onto Nest's HTTP server instead of owning their own, so Nest
|
|
15
|
+
* middleware, lifecycle hooks, and request scoping remain coherent.
|
|
16
|
+
*/
|
|
17
|
+
export interface NestSilkweaveAdapter {
|
|
18
|
+
/** Adapter discriminator — set on the silkweave context as `ctx.get('adapter')`. */
|
|
19
|
+
readonly name: 'rest' | 'trpc' | 'mcp'
|
|
20
|
+
/**
|
|
21
|
+
* Reserve the adapter's route prefix on the Nest HTTP server *now* (before
|
|
22
|
+
* Nest's 404 catch-all is installed) and return the core `AdapterGenerator`
|
|
23
|
+
* that will populate the slot during `silkweave().start()`.
|
|
24
|
+
*/
|
|
25
|
+
install(host: HttpAdapterHost): AdapterGenerator
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SilkweaveModuleOptions {
|
|
29
|
+
/** Identity for the silkweave instance — surfaced to MCP clients, OpenAPI, etc. */
|
|
30
|
+
silkweave: SilkweaveOptions
|
|
31
|
+
/** Adapters to mount. Examples: `rest()`, `trpc()`, `mcp()`. */
|
|
32
|
+
adapters: NestSilkweaveAdapter[]
|
|
33
|
+
/** Initial context keys (equivalent to chaining `.set(key, value)` on the builder). */
|
|
34
|
+
context?: Record<string, unknown>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const SILKWEAVE_MODULE_OPTIONS = '__silkweave_module_options__'
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "build",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"allowSyntheticDefaultImports": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"composite": true,
|
|
16
|
+
"declaration": true,
|
|
17
|
+
"declarationMap": true,
|
|
18
|
+
"sourceMap": true,
|
|
19
|
+
"experimentalDecorators": true,
|
|
20
|
+
"emitDecoratorMetadata": true,
|
|
21
|
+
"useDefineForClassFields": false
|
|
22
|
+
},
|
|
23
|
+
"include": ["src"],
|
|
24
|
+
"exclude": ["node_modules", "build"]
|
|
25
|
+
}
|