nitrostack 1.0.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/CHANGELOG.md +227 -0
- package/CONTRIBUTING.md +182 -0
- package/LICENSE +201 -0
- package/LICENSE_URLS_UPDATE_COMPLETE.md +388 -0
- package/NOTICE +153 -0
- package/README.md +571 -0
- package/dist/auth/api-key.d.ts +118 -0
- package/dist/auth/api-key.d.ts.map +1 -0
- package/dist/auth/api-key.js +168 -0
- package/dist/auth/api-key.js.map +1 -0
- package/dist/auth/client.d.ts +151 -0
- package/dist/auth/client.d.ts.map +1 -0
- package/dist/auth/client.js +330 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/auth/index.d.ts +30 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +43 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/middleware.d.ts +95 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +260 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/auth/pkce.d.ts +53 -0
- package/dist/auth/pkce.d.ts.map +1 -0
- package/dist/auth/pkce.js +105 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/auth/quick-setup.d.ts +94 -0
- package/dist/auth/quick-setup.d.ts.map +1 -0
- package/dist/auth/quick-setup.js +210 -0
- package/dist/auth/quick-setup.js.map +1 -0
- package/dist/auth/server-integration.d.ts +97 -0
- package/dist/auth/server-integration.d.ts.map +1 -0
- package/dist/auth/server-integration.js +182 -0
- package/dist/auth/server-integration.js.map +1 -0
- package/dist/auth/server-metadata.d.ts +51 -0
- package/dist/auth/server-metadata.d.ts.map +1 -0
- package/dist/auth/server-metadata.js +106 -0
- package/dist/auth/server-metadata.js.map +1 -0
- package/dist/auth/simple-jwt.d.ts +88 -0
- package/dist/auth/simple-jwt.d.ts.map +1 -0
- package/dist/auth/simple-jwt.js +152 -0
- package/dist/auth/simple-jwt.js.map +1 -0
- package/dist/auth/token-store.d.ts +104 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +205 -0
- package/dist/auth/token-store.js.map +1 -0
- package/dist/auth/token-validation.d.ts +47 -0
- package/dist/auth/token-validation.d.ts.map +1 -0
- package/dist/auth/token-validation.js +237 -0
- package/dist/auth/token-validation.js.map +1 -0
- package/dist/auth/types.d.ts +215 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +6 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/cli/commands/build.d.ts +6 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +104 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +7 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +312 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/generate-types.d.ts +8 -0
- package/dist/cli/commands/generate-types.d.ts.map +1 -0
- package/dist/cli/commands/generate-types.js +220 -0
- package/dist/cli/commands/generate-types.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +5 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +365 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/init.d.ts +7 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +365 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/start.d.ts +6 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +61 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +47 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/mcp-dev-wrapper.d.ts +3 -0
- package/dist/cli/mcp-dev-wrapper.d.ts.map +1 -0
- package/dist/cli/mcp-dev-wrapper.js +116 -0
- package/dist/cli/mcp-dev-wrapper.js.map +1 -0
- package/dist/core/apikey-module.d.ts +69 -0
- package/dist/core/apikey-module.d.ts.map +1 -0
- package/dist/core/apikey-module.js +114 -0
- package/dist/core/apikey-module.js.map +1 -0
- package/dist/core/app-decorator.d.ts +58 -0
- package/dist/core/app-decorator.d.ts.map +1 -0
- package/dist/core/app-decorator.js +261 -0
- package/dist/core/app-decorator.js.map +1 -0
- package/dist/core/builders.d.ts +38 -0
- package/dist/core/builders.d.ts.map +1 -0
- package/dist/core/builders.js +129 -0
- package/dist/core/builders.js.map +1 -0
- package/dist/core/component.d.ts +105 -0
- package/dist/core/component.d.ts.map +1 -0
- package/dist/core/component.js +182 -0
- package/dist/core/component.js.map +1 -0
- package/dist/core/config-module.d.ts +55 -0
- package/dist/core/config-module.d.ts.map +1 -0
- package/dist/core/config-module.js +94 -0
- package/dist/core/config-module.js.map +1 -0
- package/dist/core/decorators/cache.decorator.d.ts +61 -0
- package/dist/core/decorators/cache.decorator.d.ts.map +1 -0
- package/dist/core/decorators/cache.decorator.js +115 -0
- package/dist/core/decorators/cache.decorator.js.map +1 -0
- package/dist/core/decorators/health-check.decorator.d.ts +80 -0
- package/dist/core/decorators/health-check.decorator.d.ts.map +1 -0
- package/dist/core/decorators/health-check.decorator.js +153 -0
- package/dist/core/decorators/health-check.decorator.js.map +1 -0
- package/dist/core/decorators/rate-limit.decorator.d.ts +62 -0
- package/dist/core/decorators/rate-limit.decorator.d.ts.map +1 -0
- package/dist/core/decorators/rate-limit.decorator.js +129 -0
- package/dist/core/decorators/rate-limit.decorator.js.map +1 -0
- package/dist/core/decorators.d.ts +151 -0
- package/dist/core/decorators.d.ts.map +1 -0
- package/dist/core/decorators.js +142 -0
- package/dist/core/decorators.js.map +1 -0
- package/dist/core/di/container.d.ts +42 -0
- package/dist/core/di/container.d.ts.map +1 -0
- package/dist/core/di/container.js +76 -0
- package/dist/core/di/container.js.map +1 -0
- package/dist/core/di/injectable.decorator.d.ts +35 -0
- package/dist/core/di/injectable.decorator.d.ts.map +1 -0
- package/dist/core/di/injectable.decorator.js +57 -0
- package/dist/core/di/injectable.decorator.js.map +1 -0
- package/dist/core/errors.d.ts +54 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +87 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/events/event-emitter.d.ts +50 -0
- package/dist/core/events/event-emitter.d.ts.map +1 -0
- package/dist/core/events/event-emitter.js +94 -0
- package/dist/core/events/event-emitter.js.map +1 -0
- package/dist/core/events/event.decorator.d.ts +48 -0
- package/dist/core/events/event.decorator.d.ts.map +1 -0
- package/dist/core/events/event.decorator.js +68 -0
- package/dist/core/events/event.decorator.js.map +1 -0
- package/dist/core/filters/exception-filter.decorator.d.ts +40 -0
- package/dist/core/filters/exception-filter.decorator.d.ts.map +1 -0
- package/dist/core/filters/exception-filter.decorator.js +54 -0
- package/dist/core/filters/exception-filter.decorator.js.map +1 -0
- package/dist/core/filters/exception-filter.interface.d.ts +30 -0
- package/dist/core/filters/exception-filter.interface.d.ts.map +1 -0
- package/dist/core/filters/exception-filter.interface.js +2 -0
- package/dist/core/filters/exception-filter.interface.js.map +1 -0
- package/dist/core/guards/apikey.guard.d.ts +22 -0
- package/dist/core/guards/apikey.guard.d.ts.map +1 -0
- package/dist/core/guards/apikey.guard.js +11 -0
- package/dist/core/guards/apikey.guard.js.map +1 -0
- package/dist/core/guards/guard.interface.d.ts +18 -0
- package/dist/core/guards/guard.interface.d.ts.map +1 -0
- package/dist/core/guards/guard.interface.js +2 -0
- package/dist/core/guards/guard.interface.js.map +1 -0
- package/dist/core/guards/jwt.guard.d.ts +18 -0
- package/dist/core/guards/jwt.guard.d.ts.map +1 -0
- package/dist/core/guards/jwt.guard.js +2 -0
- package/dist/core/guards/jwt.guard.js.map +1 -0
- package/dist/core/guards/oauth.guard.d.ts +35 -0
- package/dist/core/guards/oauth.guard.d.ts.map +1 -0
- package/dist/core/guards/oauth.guard.js +2 -0
- package/dist/core/guards/oauth.guard.js.map +1 -0
- package/dist/core/guards/use-guards.decorator.d.ts +25 -0
- package/dist/core/guards/use-guards.decorator.d.ts.map +1 -0
- package/dist/core/guards/use-guards.decorator.js +32 -0
- package/dist/core/guards/use-guards.decorator.js.map +1 -0
- package/dist/core/health/health-checks.resource.d.ts +14 -0
- package/dist/core/health/health-checks.resource.d.ts.map +1 -0
- package/dist/core/health/health-checks.resource.js +29 -0
- package/dist/core/health/health-checks.resource.js.map +1 -0
- package/dist/core/index.d.ts +55 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +57 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/interceptors/interceptor.decorator.d.ts +37 -0
- package/dist/core/interceptors/interceptor.decorator.d.ts.map +1 -0
- package/dist/core/interceptors/interceptor.decorator.js +51 -0
- package/dist/core/interceptors/interceptor.decorator.js.map +1 -0
- package/dist/core/interceptors/interceptor.interface.d.ts +31 -0
- package/dist/core/interceptors/interceptor.interface.d.ts.map +1 -0
- package/dist/core/interceptors/interceptor.interface.js +2 -0
- package/dist/core/interceptors/interceptor.interface.js.map +1 -0
- package/dist/core/jwt-module.d.ts +51 -0
- package/dist/core/jwt-module.d.ts.map +1 -0
- package/dist/core/jwt-module.js +52 -0
- package/dist/core/jwt-module.js.map +1 -0
- package/dist/core/logger.d.ts +18 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +51 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/middleware/middleware.decorator.d.ts +39 -0
- package/dist/core/middleware/middleware.decorator.d.ts.map +1 -0
- package/dist/core/middleware/middleware.decorator.js +53 -0
- package/dist/core/middleware/middleware.decorator.js.map +1 -0
- package/dist/core/middleware/middleware.interface.d.ts +29 -0
- package/dist/core/middleware/middleware.interface.d.ts.map +1 -0
- package/dist/core/middleware/middleware.interface.js +2 -0
- package/dist/core/middleware/middleware.interface.js.map +1 -0
- package/dist/core/module.d.ts +74 -0
- package/dist/core/module.d.ts.map +1 -0
- package/dist/core/module.js +82 -0
- package/dist/core/module.js.map +1 -0
- package/dist/core/oauth-module.d.ts +144 -0
- package/dist/core/oauth-module.d.ts.map +1 -0
- package/dist/core/oauth-module.js +190 -0
- package/dist/core/oauth-module.js.map +1 -0
- package/dist/core/pipes/pipe.decorator.d.ts +55 -0
- package/dist/core/pipes/pipe.decorator.d.ts.map +1 -0
- package/dist/core/pipes/pipe.decorator.js +85 -0
- package/dist/core/pipes/pipe.decorator.js.map +1 -0
- package/dist/core/pipes/pipe.interface.d.ts +36 -0
- package/dist/core/pipes/pipe.interface.d.ts.map +1 -0
- package/dist/core/pipes/pipe.interface.js +2 -0
- package/dist/core/pipes/pipe.interface.js.map +1 -0
- package/dist/core/prompt.d.ts +37 -0
- package/dist/core/prompt.d.ts.map +1 -0
- package/dist/core/prompt.js +76 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/resource.d.ts +42 -0
- package/dist/core/resource.d.ts.map +1 -0
- package/dist/core/resource.js +90 -0
- package/dist/core/resource.js.map +1 -0
- package/dist/core/server.d.ts +72 -0
- package/dist/core/server.d.ts.map +1 -0
- package/dist/core/server.js +406 -0
- package/dist/core/server.js.map +1 -0
- package/dist/core/tool.d.ts +78 -0
- package/dist/core/tool.d.ts.map +1 -0
- package/dist/core/tool.js +190 -0
- package/dist/core/tool.js.map +1 -0
- package/dist/core/transports/http-server.d.ts +102 -0
- package/dist/core/transports/http-server.d.ts.map +1 -0
- package/dist/core/transports/http-server.js +265 -0
- package/dist/core/transports/http-server.js.map +1 -0
- package/dist/core/types.d.ts +123 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/widgets/widget-examples.resource.d.ts +17 -0
- package/dist/core/widgets/widget-examples.resource.d.ts.map +1 -0
- package/dist/core/widgets/widget-examples.resource.js +28 -0
- package/dist/core/widgets/widget-examples.resource.js.map +1 -0
- package/dist/core/widgets/widget-registry.d.ts +56 -0
- package/dist/core/widgets/widget-registry.d.ts.map +1 -0
- package/dist/core/widgets/widget-registry.js +75 -0
- package/dist/core/widgets/widget-registry.js.map +1 -0
- package/dist/testing/index.d.ts +82 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +164 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/ui-next/index.d.ts +31 -0
- package/dist/ui-next/index.d.ts.map +1 -0
- package/dist/ui-next/index.js +687 -0
- package/dist/ui-next/index.js.map +1 -0
- package/dist/widgets/index.d.ts +9 -0
- package/dist/widgets/index.d.ts.map +1 -0
- package/dist/widgets/index.js +9 -0
- package/dist/widgets/index.js.map +1 -0
- package/dist/widgets/metadata.d.ts +53 -0
- package/dist/widgets/metadata.d.ts.map +1 -0
- package/dist/widgets/metadata.js +29 -0
- package/dist/widgets/metadata.js.map +1 -0
- package/dist/widgets/withToolData.d.ts +19 -0
- package/dist/widgets/withToolData.d.ts.map +1 -0
- package/dist/widgets/withToolData.js +240 -0
- package/dist/widgets/withToolData.js.map +1 -0
- package/jest.config.js +21 -0
- package/package.json +108 -0
- package/templates/typescript-auth/AI_AGENT_CLI_REFERENCE.md +702 -0
- package/templates/typescript-auth/AI_AGENT_SDK_REFERENCE.md +1260 -0
- package/templates/typescript-auth/README.md +400 -0
- package/templates/typescript-auth/package.json +44 -0
- package/templates/typescript-auth-api-key/AI_AGENT_CLI_REFERENCE.md +701 -0
- package/templates/typescript-auth-api-key/AI_AGENT_SDK_REFERENCE.md +1260 -0
- package/templates/typescript-auth-api-key/README.md +483 -0
- package/templates/typescript-auth-api-key/package-lock.json +124 -0
- package/templates/typescript-auth-api-key/package.json +29 -0
- package/templates/typescript-oauth/AI_AGENT_CLI_REFERENCE.md +701 -0
- package/templates/typescript-oauth/AI_AGENT_SDK_REFERENCE.md +1260 -0
- package/templates/typescript-oauth/OAUTH_SETUP.md +406 -0
- package/templates/typescript-oauth/README.md +350 -0
- package/templates/typescript-oauth/package.json +30 -0
- package/templates/typescript-starter/AI_AGENT_CLI_REFERENCE.md +701 -0
- package/templates/typescript-starter/AI_AGENT_SDK_REFERENCE.md +1260 -0
- package/templates/typescript-starter/README.md +312 -0
- package/templates/typescript-starter/package.json +32 -0
|
@@ -0,0 +1,1260 @@
|
|
|
1
|
+
# NitroStack SDK Reference - For AI Code Editors
|
|
2
|
+
|
|
3
|
+
**Comprehensive SDK reference for AI agents editing NitroStack v3.0 code**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Architecture Overview](#architecture-overview)
|
|
10
|
+
2. [Application Bootstrap](#application-bootstrap)
|
|
11
|
+
3. [Modules](#modules)
|
|
12
|
+
4. [Tools](#tools)
|
|
13
|
+
5. [Resources](#resources)
|
|
14
|
+
6. [Prompts](#prompts)
|
|
15
|
+
7. [Guards](#guards)
|
|
16
|
+
8. [Middleware](#middleware)
|
|
17
|
+
9. [Interceptors](#interceptors)
|
|
18
|
+
10. [Pipes](#pipes)
|
|
19
|
+
11. [Services & DI](#services--dependency-injection)
|
|
20
|
+
12. [Decorators Reference](#decorators-reference)
|
|
21
|
+
13. [Widgets](#widgets)
|
|
22
|
+
14. [Health Checks](#health-checks)
|
|
23
|
+
15. [Caching](#caching)
|
|
24
|
+
16. [Rate Limiting](#rate-limiting)
|
|
25
|
+
17. [Error Handling](#error-handling)
|
|
26
|
+
18. [File Structure](#file-structure)
|
|
27
|
+
19. [Import Rules](#import-rules)
|
|
28
|
+
20. [Common Patterns](#common-patterns)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Architecture Overview
|
|
33
|
+
|
|
34
|
+
NitroStack v3.0 uses **decorator-based architecture** inspired by NestJS:
|
|
35
|
+
|
|
36
|
+
- **Declarative** - Use decorators instead of factory functions
|
|
37
|
+
- **Modular** - Organize code into feature modules
|
|
38
|
+
- **DI-First** - Dependency injection for testability
|
|
39
|
+
- **Type-Safe** - Zod schemas for runtime validation
|
|
40
|
+
- **Protocol-Native** - Built for MCP protocol, not HTTP
|
|
41
|
+
|
|
42
|
+
### Key Principles
|
|
43
|
+
|
|
44
|
+
1. ✅ Use decorators (`@Tool`, `@Module`, `@Injectable`)
|
|
45
|
+
2. ✅ No manual registration (`server.tool()`, `server.resource()`)
|
|
46
|
+
3. ✅ Constructor injection for dependencies
|
|
47
|
+
4. ✅ Services contain business logic, tools are thin
|
|
48
|
+
5. ✅ ES modules with `.js` extensions in imports
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Application Bootstrap
|
|
53
|
+
|
|
54
|
+
### Root Module (`app.module.ts`)
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { McpApp, Module } from 'nitrostack';
|
|
58
|
+
import { ConfigModule } from 'nitrostack/config';
|
|
59
|
+
import { JWTModule } from 'nitrostack/jwt';
|
|
60
|
+
import { ProductsModule } from './modules/products/products.module.js';
|
|
61
|
+
import { DatabaseService } from './services/database.service.js';
|
|
62
|
+
|
|
63
|
+
@McpApp({
|
|
64
|
+
server: {
|
|
65
|
+
name: 'my-ecommerce-server',
|
|
66
|
+
version: '1.0.0',
|
|
67
|
+
description: 'E-commerce MCP server'
|
|
68
|
+
},
|
|
69
|
+
logging: {
|
|
70
|
+
level: 'info' // debug | info | warn | error
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
@Module({
|
|
74
|
+
imports: [
|
|
75
|
+
ConfigModule.forRoot(), // Environment variables
|
|
76
|
+
JWTModule.forRoot(), // JWT authentication
|
|
77
|
+
ProductsModule, // Feature modules
|
|
78
|
+
OrdersModule
|
|
79
|
+
],
|
|
80
|
+
providers: [DatabaseService], // Global services
|
|
81
|
+
controllers: [] // Global tools/resources
|
|
82
|
+
})
|
|
83
|
+
export class AppModule {}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Entry Point (`index.ts`)
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { McpApplicationFactory } from 'nitrostack';
|
|
90
|
+
import { AppModule } from './app.module.js';
|
|
91
|
+
|
|
92
|
+
// Bootstrap application
|
|
93
|
+
McpApplicationFactory.create(AppModule);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**That's it!** No manual server setup needed.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Modules
|
|
101
|
+
|
|
102
|
+
Modules organize related features into cohesive units.
|
|
103
|
+
|
|
104
|
+
### Basic Module
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { Module } from 'nitrostack';
|
|
108
|
+
import { ProductsTools } from './products.tools.js';
|
|
109
|
+
import { ProductsResources } from './products.resources.js';
|
|
110
|
+
import { ProductsPrompts } from './products.prompts.js';
|
|
111
|
+
import { ProductService } from './products.service.js';
|
|
112
|
+
|
|
113
|
+
@Module({
|
|
114
|
+
name: 'products',
|
|
115
|
+
description: 'Product catalog management',
|
|
116
|
+
controllers: [ProductsTools, ProductsResources, ProductsPrompts],
|
|
117
|
+
providers: [ProductService],
|
|
118
|
+
imports: [], // Other modules this depends on
|
|
119
|
+
exports: [] // Services to expose to other modules
|
|
120
|
+
})
|
|
121
|
+
export class ProductsModule {}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Module with Dependencies
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
@Module({
|
|
128
|
+
name: 'orders',
|
|
129
|
+
controllers: [OrdersTools],
|
|
130
|
+
providers: [OrderService],
|
|
131
|
+
imports: [ProductsModule, UserModule], // Import other modules
|
|
132
|
+
exports: [OrderService] // Make OrderService available to others
|
|
133
|
+
})
|
|
134
|
+
export class OrdersModule {}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Dynamic Modules (Advanced)
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Config module with options
|
|
141
|
+
@Module({})
|
|
142
|
+
export class ConfigModule {
|
|
143
|
+
static forRoot(options?: ConfigOptions) {
|
|
144
|
+
return {
|
|
145
|
+
module: ConfigModule,
|
|
146
|
+
providers: [
|
|
147
|
+
{
|
|
148
|
+
provide: 'CONFIG_OPTIONS',
|
|
149
|
+
useValue: options || {}
|
|
150
|
+
},
|
|
151
|
+
ConfigService
|
|
152
|
+
],
|
|
153
|
+
exports: [ConfigService]
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Tools
|
|
162
|
+
|
|
163
|
+
Tools are functions that AI models can call.
|
|
164
|
+
|
|
165
|
+
### Basic Tool
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { Tool, ExecutionContext } from 'nitrostack';
|
|
169
|
+
import { z } from 'zod';
|
|
170
|
+
|
|
171
|
+
export class ProductsTools {
|
|
172
|
+
@Tool({
|
|
173
|
+
name: 'get_product',
|
|
174
|
+
description: 'Get product details by ID',
|
|
175
|
+
inputSchema: z.object({
|
|
176
|
+
product_id: z.string().describe('Unique product identifier')
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
async getProduct(input: any, ctx: ExecutionContext) {
|
|
180
|
+
ctx.logger.info('Fetching product', { id: input.product_id });
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
id: input.product_id,
|
|
184
|
+
name: 'Example Product',
|
|
185
|
+
price: 99.99
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Tool with Examples
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
@Tool({
|
|
195
|
+
name: 'browse_products',
|
|
196
|
+
description: 'Browse products with filters',
|
|
197
|
+
inputSchema: z.object({
|
|
198
|
+
category: z.string().optional().describe('Product category'),
|
|
199
|
+
page: z.number().default(1).describe('Page number'),
|
|
200
|
+
limit: z.number().default(10).describe('Items per page')
|
|
201
|
+
}),
|
|
202
|
+
examples: {
|
|
203
|
+
request: { category: 'electronics', page: 1, limit: 10 },
|
|
204
|
+
response: {
|
|
205
|
+
products: [
|
|
206
|
+
{ id: '1', name: 'Laptop', price: 999 },
|
|
207
|
+
{ id: '2', name: 'Mouse', price: 29 }
|
|
208
|
+
],
|
|
209
|
+
total: 2,
|
|
210
|
+
page: 1
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
async browseProducts(input: any, ctx: ExecutionContext) {
|
|
215
|
+
return {
|
|
216
|
+
products: [],
|
|
217
|
+
total: 0,
|
|
218
|
+
page: input.page
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Tool with Widget
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { Tool, Widget, ExecutionContext } from 'nitrostack';
|
|
227
|
+
|
|
228
|
+
@Tool({
|
|
229
|
+
name: 'get_product',
|
|
230
|
+
description: 'Get product details',
|
|
231
|
+
inputSchema: z.object({ product_id: z.string() }),
|
|
232
|
+
examples: {
|
|
233
|
+
request: { product_id: 'prod-1' },
|
|
234
|
+
response: { id: 'prod-1', name: 'Laptop', price: 999, image_url: '/laptop.jpg' }
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
@Widget('product-card') // Link to src/widgets/app/product-card/page.tsx
|
|
238
|
+
async getProduct(input: any, ctx: ExecutionContext) {
|
|
239
|
+
return await this.productService.findById(input.product_id);
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Tool with Guards
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { Tool, UseGuards, ExecutionContext } from 'nitrostack';
|
|
247
|
+
import { JWTGuard } from '../../guards/jwt.guard.js';
|
|
248
|
+
|
|
249
|
+
@Tool({
|
|
250
|
+
name: 'create_order',
|
|
251
|
+
description: 'Create a new order',
|
|
252
|
+
inputSchema: z.object({
|
|
253
|
+
items: z.array(z.object({
|
|
254
|
+
product_id: z.string(),
|
|
255
|
+
quantity: z.number()
|
|
256
|
+
}))
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
@UseGuards(JWTGuard) // Require authentication
|
|
260
|
+
async createOrder(input: any, ctx: ExecutionContext) {
|
|
261
|
+
const userId = ctx.auth?.subject; // Set by guard
|
|
262
|
+
ctx.logger.info('Creating order', { userId, items: input.items });
|
|
263
|
+
|
|
264
|
+
return await this.orderService.create(userId, input.items);
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Tool with Caching
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import { Tool, Cache } from 'nitrostack';
|
|
272
|
+
|
|
273
|
+
@Tool({
|
|
274
|
+
name: 'get_categories',
|
|
275
|
+
description: 'Get all product categories',
|
|
276
|
+
inputSchema: z.object({})
|
|
277
|
+
})
|
|
278
|
+
@Cache({ ttl: 300 }) // Cache for 5 minutes
|
|
279
|
+
async getCategories() {
|
|
280
|
+
return await this.productService.getCategories();
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Tool with Rate Limiting
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { Tool, RateLimit } from 'nitrostack';
|
|
288
|
+
|
|
289
|
+
@Tool({
|
|
290
|
+
name: 'send_email',
|
|
291
|
+
description: 'Send email notification',
|
|
292
|
+
inputSchema: z.object({
|
|
293
|
+
to: z.string().email(),
|
|
294
|
+
subject: z.string(),
|
|
295
|
+
body: z.string()
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
@RateLimit({
|
|
299
|
+
requests: 10, // Max requests
|
|
300
|
+
window: '1m', // Time window (1m, 1h, 1d)
|
|
301
|
+
key: (ctx) => ctx.auth?.subject || 'anonymous'
|
|
302
|
+
})
|
|
303
|
+
async sendEmail(input: any, ctx: ExecutionContext) {
|
|
304
|
+
await this.emailService.send(input.to, input.subject, input.body);
|
|
305
|
+
return { success: true };
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Tool with DI
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import { Injectable } from 'nitrostack';
|
|
313
|
+
|
|
314
|
+
@Injectable()
|
|
315
|
+
export class ProductService {
|
|
316
|
+
constructor(private db: DatabaseService) {}
|
|
317
|
+
|
|
318
|
+
async findById(id: string) {
|
|
319
|
+
return this.db.queryOne('SELECT * FROM products WHERE id = ?', [id]);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export class ProductsTools {
|
|
324
|
+
constructor(private productService: ProductService) {} // Auto-injected
|
|
325
|
+
|
|
326
|
+
@Tool({ name: 'get_product' })
|
|
327
|
+
async getProduct(input: any) {
|
|
328
|
+
return await this.productService.findById(input.product_id);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Resources
|
|
336
|
+
|
|
337
|
+
Resources are data schemas AI can read.
|
|
338
|
+
|
|
339
|
+
### Basic Resource
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { Resource, ExecutionContext } from 'nitrostack';
|
|
343
|
+
|
|
344
|
+
@Resource({
|
|
345
|
+
uri: 'product://{id}',
|
|
346
|
+
name: 'Product Data',
|
|
347
|
+
description: 'Detailed product information',
|
|
348
|
+
mimeType: 'application/json'
|
|
349
|
+
})
|
|
350
|
+
async getProductResource(uri: string, ctx: ExecutionContext) {
|
|
351
|
+
const id = uri.split('://')[1]; // Extract ID from URI
|
|
352
|
+
const product = await this.productService.findById(id);
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
contents: [{
|
|
356
|
+
uri,
|
|
357
|
+
mimeType: 'application/json',
|
|
358
|
+
text: JSON.stringify(product, null, 2)
|
|
359
|
+
}]
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Resource with Widget
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
@Resource({
|
|
368
|
+
uri: 'catalog://categories',
|
|
369
|
+
name: 'Product Categories',
|
|
370
|
+
description: 'All available product categories',
|
|
371
|
+
mimeType: 'application/json',
|
|
372
|
+
examples: {
|
|
373
|
+
response: {
|
|
374
|
+
categories: ['Electronics', 'Fashion', 'Home']
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
})
|
|
378
|
+
@Widget('categories-list')
|
|
379
|
+
async getCategoriesResource(uri: string, ctx: ExecutionContext) {
|
|
380
|
+
const categories = await this.productService.getCategories();
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
contents: [{
|
|
384
|
+
uri,
|
|
385
|
+
mimeType: 'application/json',
|
|
386
|
+
text: JSON.stringify({ categories })
|
|
387
|
+
}]
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Prompts
|
|
395
|
+
|
|
396
|
+
Prompts are conversation templates for AI.
|
|
397
|
+
|
|
398
|
+
### Basic Prompt
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
import { Prompt, ExecutionContext } from 'nitrostack';
|
|
402
|
+
|
|
403
|
+
@Prompt({
|
|
404
|
+
name: 'product_review',
|
|
405
|
+
description: 'Generate product review template',
|
|
406
|
+
arguments: [
|
|
407
|
+
{
|
|
408
|
+
name: 'product_id',
|
|
409
|
+
description: 'ID of product to review',
|
|
410
|
+
required: true
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
name: 'rating',
|
|
414
|
+
description: 'Rating (1-5)',
|
|
415
|
+
required: false
|
|
416
|
+
}
|
|
417
|
+
]
|
|
418
|
+
})
|
|
419
|
+
async getReviewPrompt(args: any, ctx: ExecutionContext) {
|
|
420
|
+
const product = await this.productService.findById(args.product_id);
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
messages: [
|
|
424
|
+
{
|
|
425
|
+
role: 'user',
|
|
426
|
+
content: {
|
|
427
|
+
type: 'text',
|
|
428
|
+
text: `Write a ${args.rating || 5}-star review for: ${product.name}`
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
]
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Prompt with Context
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
@Prompt({
|
|
440
|
+
name: 'order_summary',
|
|
441
|
+
description: 'Generate order summary for customer',
|
|
442
|
+
arguments: [
|
|
443
|
+
{ name: 'order_id', description: 'Order ID', required: true }
|
|
444
|
+
]
|
|
445
|
+
})
|
|
446
|
+
async getOrderSummaryPrompt(args: any, ctx: ExecutionContext) {
|
|
447
|
+
const order = await this.orderService.findById(args.order_id);
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
messages: [
|
|
451
|
+
{
|
|
452
|
+
role: 'system',
|
|
453
|
+
content: {
|
|
454
|
+
type: 'text',
|
|
455
|
+
text: 'You are a helpful customer service assistant.'
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
role: 'user',
|
|
460
|
+
content: {
|
|
461
|
+
type: 'text',
|
|
462
|
+
text: `Summarize order #${order.id} with ${order.items.length} items, total $${order.total}`
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
]
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Guards
|
|
473
|
+
|
|
474
|
+
Guards implement authentication/authorization.
|
|
475
|
+
|
|
476
|
+
### JWT Guard
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
import { Guard, ExecutionContext, Injectable } from 'nitrostack';
|
|
480
|
+
import jwt from 'jsonwebtoken';
|
|
481
|
+
|
|
482
|
+
@Injectable()
|
|
483
|
+
export class JWTGuard implements Guard {
|
|
484
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
485
|
+
const authHeader = context.metadata?.authorization;
|
|
486
|
+
|
|
487
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const token = authHeader.substring(7);
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
const payload = jwt.verify(token, process.env.JWT_SECRET!);
|
|
495
|
+
|
|
496
|
+
// Attach auth info to context
|
|
497
|
+
context.auth = {
|
|
498
|
+
subject: payload.sub,
|
|
499
|
+
email: payload.email,
|
|
500
|
+
...payload
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
return true;
|
|
504
|
+
} catch (error) {
|
|
505
|
+
context.logger.warn('JWT verification failed', { error });
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Admin Guard
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
@Injectable()
|
|
516
|
+
export class AdminGuard implements Guard {
|
|
517
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
518
|
+
// Assumes JWTGuard ran first
|
|
519
|
+
const user = context.auth;
|
|
520
|
+
|
|
521
|
+
if (!user) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Check if user is admin
|
|
526
|
+
return user.role === 'admin';
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Usage: Multiple guards (all must pass)
|
|
531
|
+
@Tool({ name: 'delete_user' })
|
|
532
|
+
@UseGuards(JWTGuard, AdminGuard)
|
|
533
|
+
async deleteUser(input: any, ctx: ExecutionContext) {
|
|
534
|
+
// Only admins with valid JWT can call this
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## Middleware
|
|
541
|
+
|
|
542
|
+
Middleware runs before/after tool execution.
|
|
543
|
+
|
|
544
|
+
### Logging Middleware
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
import { Middleware, MiddlewareInterface, ExecutionContext } from 'nitrostack';
|
|
548
|
+
|
|
549
|
+
@Middleware()
|
|
550
|
+
export class LoggingMiddleware implements MiddlewareInterface {
|
|
551
|
+
async use(context: ExecutionContext, next: () => Promise<any>): Promise<any> {
|
|
552
|
+
const start = Date.now();
|
|
553
|
+
|
|
554
|
+
context.logger.info('Tool starting', {
|
|
555
|
+
tool: context.toolName,
|
|
556
|
+
input: context.input
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
try {
|
|
560
|
+
const result = await next();
|
|
561
|
+
|
|
562
|
+
const duration = Date.now() - start;
|
|
563
|
+
context.logger.info('Tool completed', {
|
|
564
|
+
tool: context.toolName,
|
|
565
|
+
duration: `${duration}ms`
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
return result;
|
|
569
|
+
} catch (error) {
|
|
570
|
+
const duration = Date.now() - start;
|
|
571
|
+
context.logger.error('Tool failed', {
|
|
572
|
+
tool: context.toolName,
|
|
573
|
+
duration: `${duration}ms`,
|
|
574
|
+
error
|
|
575
|
+
});
|
|
576
|
+
throw error;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Usage
|
|
582
|
+
@Tool({ name: 'my_tool' })
|
|
583
|
+
@UseMiddleware(LoggingMiddleware)
|
|
584
|
+
async myTool(input: any, ctx: ExecutionContext) {}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Auth Middleware
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
@Middleware()
|
|
591
|
+
export class AuthMiddleware implements MiddlewareInterface {
|
|
592
|
+
async use(context: ExecutionContext, next: () => Promise<any>) {
|
|
593
|
+
if (!context.auth) {
|
|
594
|
+
throw new Error('Unauthorized');
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return next();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## Interceptors
|
|
605
|
+
|
|
606
|
+
Interceptors transform requests/responses.
|
|
607
|
+
|
|
608
|
+
### Transform Interceptor
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
import { Interceptor, InterceptorInterface, ExecutionContext } from 'nitrostack';
|
|
612
|
+
|
|
613
|
+
@Interceptor()
|
|
614
|
+
export class TransformInterceptor implements InterceptorInterface {
|
|
615
|
+
async intercept(context: ExecutionContext, next: () => Promise<any>): Promise<any> {
|
|
616
|
+
const result = await next();
|
|
617
|
+
|
|
618
|
+
// Wrap all responses in standard format
|
|
619
|
+
return {
|
|
620
|
+
success: true,
|
|
621
|
+
data: result,
|
|
622
|
+
timestamp: new Date().toISOString(),
|
|
623
|
+
tool: context.toolName
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Usage
|
|
629
|
+
@Tool({ name: 'get_product' })
|
|
630
|
+
@UseInterceptors(TransformInterceptor)
|
|
631
|
+
async getProduct(input: any) {
|
|
632
|
+
return { id: '1', name: 'Product' };
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Returns:
|
|
636
|
+
// {
|
|
637
|
+
// success: true,
|
|
638
|
+
// data: { id: '1', name: 'Product' },
|
|
639
|
+
// timestamp: '2025-10-24T12:00:00Z',
|
|
640
|
+
// tool: 'get_product'
|
|
641
|
+
// }
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## Pipes
|
|
647
|
+
|
|
648
|
+
Pipes transform/validate input before handler execution.
|
|
649
|
+
|
|
650
|
+
### Validation Pipe
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
import { Pipe, PipeInterface } from 'nitrostack';
|
|
654
|
+
import { z } from 'zod';
|
|
655
|
+
|
|
656
|
+
@Pipe()
|
|
657
|
+
export class ValidationPipe implements PipeInterface {
|
|
658
|
+
async transform(value: any, metadata: any): Promise<any> {
|
|
659
|
+
if (metadata.schema) {
|
|
660
|
+
// Validate with Zod
|
|
661
|
+
return metadata.schema.parse(value);
|
|
662
|
+
}
|
|
663
|
+
return value;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
---
|
|
669
|
+
|
|
670
|
+
## Services & Dependency Injection
|
|
671
|
+
|
|
672
|
+
### Injectable Service
|
|
673
|
+
|
|
674
|
+
```typescript
|
|
675
|
+
import { Injectable } from 'nitrostack';
|
|
676
|
+
|
|
677
|
+
@Injectable()
|
|
678
|
+
export class ProductService {
|
|
679
|
+
constructor(private db: DatabaseService) {} // DI
|
|
680
|
+
|
|
681
|
+
async findById(id: string) {
|
|
682
|
+
return this.db.queryOne('SELECT * FROM products WHERE id = ?', [id]);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
async search(query: string) {
|
|
686
|
+
return this.db.query(
|
|
687
|
+
'SELECT * FROM products WHERE name LIKE ?',
|
|
688
|
+
[`%${query}%`]
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
async getCategories() {
|
|
693
|
+
return this.db.query('SELECT DISTINCT category FROM products');
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
### Using Services in Tools
|
|
699
|
+
|
|
700
|
+
```typescript
|
|
701
|
+
export class ProductsTools {
|
|
702
|
+
constructor(
|
|
703
|
+
private productService: ProductService,
|
|
704
|
+
private cacheService: CacheService
|
|
705
|
+
) {} // Both auto-injected
|
|
706
|
+
|
|
707
|
+
@Tool({ name: 'search_products' })
|
|
708
|
+
async searchProducts(input: any, ctx: ExecutionContext) {
|
|
709
|
+
const results = await this.productService.search(input.query);
|
|
710
|
+
await this.cacheService.set(`search:${input.query}`, results);
|
|
711
|
+
return results;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
718
|
+
## Decorators Reference
|
|
719
|
+
|
|
720
|
+
### Core Decorators
|
|
721
|
+
|
|
722
|
+
| Decorator | Purpose | Example |
|
|
723
|
+
|-----------|---------|---------|
|
|
724
|
+
| `@McpApp(options)` | Root application module | `@McpApp({ server: { name: 'my-server' } })` |
|
|
725
|
+
| `@Module(options)` | Feature module | `@Module({ name: 'products', controllers: [...] })` |
|
|
726
|
+
| `@Injectable()` | Mark for DI | `@Injectable() class ProductService {}` |
|
|
727
|
+
|
|
728
|
+
### Tool Decorators
|
|
729
|
+
|
|
730
|
+
| Decorator | Purpose | Example |
|
|
731
|
+
|-----------|---------|---------|
|
|
732
|
+
| `@Tool(options)` | Define tool | `@Tool({ name: 'get_product', inputSchema: z.object({}) })` |
|
|
733
|
+
| `@Resource(options)` | Define resource | `@Resource({ uri: 'product://{id}' })` |
|
|
734
|
+
| `@Prompt(options)` | Define prompt | `@Prompt({ name: 'review_prompt' })` |
|
|
735
|
+
|
|
736
|
+
### Feature Decorators
|
|
737
|
+
|
|
738
|
+
| Decorator | Purpose | Example |
|
|
739
|
+
|-----------|---------|---------|
|
|
740
|
+
| `@Widget(name)` | Link UI widget | `@Widget('product-card')` |
|
|
741
|
+
| `@UseGuards(...guards)` | Apply guards | `@UseGuards(JWTGuard, AdminGuard)` |
|
|
742
|
+
| `@UseMiddleware(...mw)` | Apply middleware | `@UseMiddleware(LoggingMiddleware)` |
|
|
743
|
+
| `@UseInterceptors(...int)` | Apply interceptors | `@UseInterceptors(TransformInterceptor)` |
|
|
744
|
+
| `@Cache(options)` | Cache responses | `@Cache({ ttl: 300 })` |
|
|
745
|
+
| `@RateLimit(options)` | Rate limiting | `@RateLimit({ requests: 10, window: '1m' })` |
|
|
746
|
+
| `@HealthCheck(name)` | Define health check | `@HealthCheck('database')` |
|
|
747
|
+
|
|
748
|
+
---
|
|
749
|
+
|
|
750
|
+
## Widgets
|
|
751
|
+
|
|
752
|
+
UI components rendered alongside tool responses.
|
|
753
|
+
|
|
754
|
+
### Widget File Structure
|
|
755
|
+
|
|
756
|
+
```
|
|
757
|
+
src/widgets/
|
|
758
|
+
├── app/
|
|
759
|
+
│ ├── product-card/
|
|
760
|
+
│ │ └── page.tsx # Widget component
|
|
761
|
+
│ ├── products-grid/
|
|
762
|
+
│ │ └── page.tsx
|
|
763
|
+
│ └── ...
|
|
764
|
+
├── types/
|
|
765
|
+
│ └── tool-data.ts # Generated types
|
|
766
|
+
├── styles/
|
|
767
|
+
│ └── ecommerce.ts # Shared inline styles
|
|
768
|
+
├── widget-manifest.json # Widget metadata
|
|
769
|
+
├── package.json
|
|
770
|
+
└── next.config.js
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Basic Widget
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
'use client';
|
|
777
|
+
|
|
778
|
+
import { useEffect, useState } from 'react';
|
|
779
|
+
|
|
780
|
+
export default function ProductCard() {
|
|
781
|
+
const [data, setData] = useState<any>(null);
|
|
782
|
+
|
|
783
|
+
useEffect(() => {
|
|
784
|
+
// Listen for data from Studio/MCP
|
|
785
|
+
const handleMessage = (event: MessageEvent) => {
|
|
786
|
+
if (event.data.type === 'toolOutput') {
|
|
787
|
+
setData(event.data.data);
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
window.addEventListener('message', handleMessage);
|
|
792
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
793
|
+
}, []);
|
|
794
|
+
|
|
795
|
+
if (!data) {
|
|
796
|
+
return <div>Loading...</div>;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return (
|
|
800
|
+
<div style={{ padding: '20px', border: '1px solid #ddd' }}>
|
|
801
|
+
<h2>{data.name}</h2>
|
|
802
|
+
<p>Price: ${data.price}</p>
|
|
803
|
+
<img src={data.image_url} alt={data.name} style={{ maxWidth: '200px' }} />
|
|
804
|
+
</div>
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
### Widget with Types
|
|
810
|
+
|
|
811
|
+
```typescript
|
|
812
|
+
'use client';
|
|
813
|
+
|
|
814
|
+
import { useEffect, useState } from 'react';
|
|
815
|
+
import { GetProductOutput } from '../../types/tool-data';
|
|
816
|
+
|
|
817
|
+
export default function ProductCard() {
|
|
818
|
+
const [data, setData] = useState<GetProductOutput | null>(null);
|
|
819
|
+
|
|
820
|
+
useEffect(() => {
|
|
821
|
+
const handleMessage = (event: MessageEvent) => {
|
|
822
|
+
if (event.data.type === 'toolOutput') {
|
|
823
|
+
setData(event.data.data as GetProductOutput);
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
window.addEventListener('message', handleMessage);
|
|
828
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
829
|
+
}, []);
|
|
830
|
+
|
|
831
|
+
if (!data) return <div>Loading...</div>;
|
|
832
|
+
|
|
833
|
+
// TypeScript knows data.name, data.price, etc.
|
|
834
|
+
return (
|
|
835
|
+
<div>
|
|
836
|
+
<h2>{data.name}</h2>
|
|
837
|
+
<p>${data.price}</p>
|
|
838
|
+
</div>
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### Widget Manifest
|
|
844
|
+
|
|
845
|
+
```json
|
|
846
|
+
{
|
|
847
|
+
"widgets": [
|
|
848
|
+
{
|
|
849
|
+
"uri": "product-card",
|
|
850
|
+
"name": "Product Card",
|
|
851
|
+
"description": "Display product details",
|
|
852
|
+
"toolName": "get_product",
|
|
853
|
+
"examples": {
|
|
854
|
+
"request": { "product_id": "1" },
|
|
855
|
+
"response": { "id": "1", "name": "Laptop", "price": 999 }
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
]
|
|
859
|
+
}
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
### Linking Widget to Tool
|
|
863
|
+
|
|
864
|
+
```typescript
|
|
865
|
+
@Tool({
|
|
866
|
+
name: 'get_product',
|
|
867
|
+
inputSchema: z.object({ product_id: z.string() })
|
|
868
|
+
})
|
|
869
|
+
@Widget('product-card') // Links to src/widgets/app/product-card/page.tsx
|
|
870
|
+
async getProduct(input: any) {
|
|
871
|
+
return { id: input.product_id, name: 'Laptop', price: 999 };
|
|
872
|
+
}
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
---
|
|
876
|
+
|
|
877
|
+
## Health Checks
|
|
878
|
+
|
|
879
|
+
Monitor system health.
|
|
880
|
+
|
|
881
|
+
### Basic Health Check
|
|
882
|
+
|
|
883
|
+
```typescript
|
|
884
|
+
import { HealthCheck, Injectable } from 'nitrostack';
|
|
885
|
+
|
|
886
|
+
@Injectable()
|
|
887
|
+
export class SystemHealthCheck {
|
|
888
|
+
@HealthCheck('system')
|
|
889
|
+
async checkSystem() {
|
|
890
|
+
const uptime = process.uptime();
|
|
891
|
+
const memory = process.memoryUsage();
|
|
892
|
+
|
|
893
|
+
return {
|
|
894
|
+
status: uptime > 0 ? 'up' : 'down',
|
|
895
|
+
message: 'System is operational',
|
|
896
|
+
details: {
|
|
897
|
+
uptime: `${Math.floor(uptime)}s`,
|
|
898
|
+
memory: `${Math.round(memory.heapUsed / 1024 / 1024)}MB`
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
### Database Health Check
|
|
906
|
+
|
|
907
|
+
```typescript
|
|
908
|
+
@Injectable()
|
|
909
|
+
export class DatabaseHealthCheck {
|
|
910
|
+
constructor(private db: DatabaseService) {}
|
|
911
|
+
|
|
912
|
+
@HealthCheck('database')
|
|
913
|
+
async checkDatabase() {
|
|
914
|
+
try {
|
|
915
|
+
await this.db.query('SELECT 1');
|
|
916
|
+
return {
|
|
917
|
+
status: 'up',
|
|
918
|
+
message: 'Database is responsive',
|
|
919
|
+
details: { connection: 'active' }
|
|
920
|
+
};
|
|
921
|
+
} catch (error) {
|
|
922
|
+
return {
|
|
923
|
+
status: 'down',
|
|
924
|
+
message: 'Database connection failed',
|
|
925
|
+
details: { error: error.message }
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
---
|
|
933
|
+
|
|
934
|
+
## Caching
|
|
935
|
+
|
|
936
|
+
Cache tool responses for performance.
|
|
937
|
+
|
|
938
|
+
```typescript
|
|
939
|
+
import { Tool, Cache } from 'nitrostack';
|
|
940
|
+
|
|
941
|
+
// Simple TTL caching
|
|
942
|
+
@Tool({ name: 'get_categories' })
|
|
943
|
+
@Cache({ ttl: 300 }) // 5 minutes
|
|
944
|
+
async getCategories() {
|
|
945
|
+
return await this.productService.getCategories();
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// Custom cache key
|
|
949
|
+
@Tool({ name: 'get_product' })
|
|
950
|
+
@Cache({
|
|
951
|
+
ttl: 60,
|
|
952
|
+
key: (input) => `product:${input.product_id}`
|
|
953
|
+
})
|
|
954
|
+
async getProduct(input: any) {
|
|
955
|
+
return await this.productService.findById(input.product_id);
|
|
956
|
+
}
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
---
|
|
960
|
+
|
|
961
|
+
## Rate Limiting
|
|
962
|
+
|
|
963
|
+
Limit tool execution frequency.
|
|
964
|
+
|
|
965
|
+
```typescript
|
|
966
|
+
import { Tool, RateLimit } from 'nitrostack';
|
|
967
|
+
|
|
968
|
+
// Per-user rate limiting
|
|
969
|
+
@Tool({ name: 'send_email' })
|
|
970
|
+
@RateLimit({
|
|
971
|
+
requests: 10, // Max 10 requests
|
|
972
|
+
window: '1m', // Per 1 minute
|
|
973
|
+
key: (ctx) => ctx.auth?.subject || 'anon' // Key by user ID
|
|
974
|
+
})
|
|
975
|
+
async sendEmail(input: any) {
|
|
976
|
+
await this.emailService.send(input.to, input.subject, input.body);
|
|
977
|
+
return { success: true };
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// Global rate limiting
|
|
981
|
+
@Tool({ name: 'expensive_operation' })
|
|
982
|
+
@RateLimit({
|
|
983
|
+
requests: 100,
|
|
984
|
+
window: '1h',
|
|
985
|
+
key: () => 'global'
|
|
986
|
+
})
|
|
987
|
+
async expensiveOperation(input: any) {
|
|
988
|
+
// ...
|
|
989
|
+
}
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
---
|
|
993
|
+
|
|
994
|
+
## Error Handling
|
|
995
|
+
|
|
996
|
+
```typescript
|
|
997
|
+
@Tool({ name: 'get_user' })
|
|
998
|
+
async getUser(input: any, ctx: ExecutionContext) {
|
|
999
|
+
const user = await this.userService.findById(input.user_id);
|
|
1000
|
+
|
|
1001
|
+
if (!user) {
|
|
1002
|
+
throw new Error('User not found');
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return user;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// Custom error class
|
|
1009
|
+
export class NotFoundError extends Error {
|
|
1010
|
+
constructor(resource: string, id: string) {
|
|
1011
|
+
super(`${resource} with ID ${id} not found`);
|
|
1012
|
+
this.name = 'NotFoundError';
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
@Tool({ name: 'get_product' })
|
|
1017
|
+
async getProduct(input: any) {
|
|
1018
|
+
const product = await this.productService.findById(input.product_id);
|
|
1019
|
+
|
|
1020
|
+
if (!product) {
|
|
1021
|
+
throw new NotFoundError('Product', input.product_id);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
return product;
|
|
1025
|
+
}
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
---
|
|
1029
|
+
|
|
1030
|
+
## File Structure
|
|
1031
|
+
|
|
1032
|
+
```
|
|
1033
|
+
src/
|
|
1034
|
+
├── modules/
|
|
1035
|
+
│ ├── auth/
|
|
1036
|
+
│ │ ├── auth.module.ts
|
|
1037
|
+
│ │ ├── auth.tools.ts
|
|
1038
|
+
│ │ ├── auth.resources.ts
|
|
1039
|
+
│ │ ├── auth.prompts.ts
|
|
1040
|
+
│ │ ├── auth.service.ts
|
|
1041
|
+
│ │ └── guards/
|
|
1042
|
+
│ │ └── jwt.guard.ts
|
|
1043
|
+
│ ├── products/
|
|
1044
|
+
│ │ ├── products.module.ts
|
|
1045
|
+
│ │ ├── products.tools.ts
|
|
1046
|
+
│ │ ├── products.resources.ts
|
|
1047
|
+
│ │ ├── products.prompts.ts
|
|
1048
|
+
│ │ └── products.service.ts
|
|
1049
|
+
│ └── ...
|
|
1050
|
+
├── services/
|
|
1051
|
+
│ ├── database.service.ts
|
|
1052
|
+
│ └── cache.service.ts
|
|
1053
|
+
├── guards/
|
|
1054
|
+
│ ├── jwt.guard.ts
|
|
1055
|
+
│ └── admin.guard.ts
|
|
1056
|
+
├── middleware/
|
|
1057
|
+
│ └── logging.middleware.ts
|
|
1058
|
+
├── interceptors/
|
|
1059
|
+
│ └── transform.interceptor.ts
|
|
1060
|
+
├── pipes/
|
|
1061
|
+
│ └── validation.pipe.ts
|
|
1062
|
+
├── health/
|
|
1063
|
+
│ ├── system.health.ts
|
|
1064
|
+
│ └── database.health.ts
|
|
1065
|
+
├── widgets/
|
|
1066
|
+
│ ├── app/
|
|
1067
|
+
│ │ ├── product-card/
|
|
1068
|
+
│ │ ├── products-grid/
|
|
1069
|
+
│ │ └── ...
|
|
1070
|
+
│ ├── types/
|
|
1071
|
+
│ │ └── tool-data.ts
|
|
1072
|
+
│ └── styles/
|
|
1073
|
+
│ └── shared.ts
|
|
1074
|
+
├── app.module.ts
|
|
1075
|
+
└── index.ts
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
---
|
|
1079
|
+
|
|
1080
|
+
## Import Rules
|
|
1081
|
+
|
|
1082
|
+
### Always Use `.js` Extensions
|
|
1083
|
+
|
|
1084
|
+
```typescript
|
|
1085
|
+
// ✅ Correct
|
|
1086
|
+
import { ProductService } from './products.service.js';
|
|
1087
|
+
import { JWTGuard } from '../auth/jwt.guard.js';
|
|
1088
|
+
import { DatabaseService } from '../../services/database.service.js';
|
|
1089
|
+
|
|
1090
|
+
// ❌ Wrong
|
|
1091
|
+
import { ProductService } from './products.service';
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
### Core Imports
|
|
1095
|
+
|
|
1096
|
+
```typescript
|
|
1097
|
+
// Core decorators
|
|
1098
|
+
import {
|
|
1099
|
+
Tool,
|
|
1100
|
+
Resource,
|
|
1101
|
+
Prompt,
|
|
1102
|
+
Module,
|
|
1103
|
+
McpApp,
|
|
1104
|
+
Injectable,
|
|
1105
|
+
UseGuards,
|
|
1106
|
+
Cache,
|
|
1107
|
+
RateLimit,
|
|
1108
|
+
HealthCheck,
|
|
1109
|
+
ExecutionContext
|
|
1110
|
+
} from 'nitrostack';
|
|
1111
|
+
|
|
1112
|
+
// Config
|
|
1113
|
+
import { ConfigModule, ConfigService } from 'nitrostack/config';
|
|
1114
|
+
|
|
1115
|
+
// JWT
|
|
1116
|
+
import { JWTModule } from 'nitrostack/jwt';
|
|
1117
|
+
|
|
1118
|
+
// Validation
|
|
1119
|
+
import { z } from 'zod';
|
|
1120
|
+
|
|
1121
|
+
// Widgets (in widget files)
|
|
1122
|
+
import { withToolData } from 'nitrostack/widgets';
|
|
1123
|
+
```
|
|
1124
|
+
|
|
1125
|
+
---
|
|
1126
|
+
|
|
1127
|
+
## Common Patterns
|
|
1128
|
+
|
|
1129
|
+
### CRUD Operations
|
|
1130
|
+
|
|
1131
|
+
```typescript
|
|
1132
|
+
export class ProductsTools {
|
|
1133
|
+
constructor(private productService: ProductService) {}
|
|
1134
|
+
|
|
1135
|
+
// Create
|
|
1136
|
+
@Tool({
|
|
1137
|
+
name: 'create_product',
|
|
1138
|
+
inputSchema: z.object({
|
|
1139
|
+
name: z.string(),
|
|
1140
|
+
price: z.number(),
|
|
1141
|
+
category: z.string()
|
|
1142
|
+
})
|
|
1143
|
+
})
|
|
1144
|
+
@UseGuards(JWTGuard, AdminGuard)
|
|
1145
|
+
async create(input: any) {
|
|
1146
|
+
return await this.productService.create(input);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// Read
|
|
1150
|
+
@Tool({
|
|
1151
|
+
name: 'get_product',
|
|
1152
|
+
inputSchema: z.object({ product_id: z.string() })
|
|
1153
|
+
})
|
|
1154
|
+
@Cache({ ttl: 60 })
|
|
1155
|
+
async get(input: any) {
|
|
1156
|
+
return await this.productService.findById(input.product_id);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// Update
|
|
1160
|
+
@Tool({
|
|
1161
|
+
name: 'update_product',
|
|
1162
|
+
inputSchema: z.object({
|
|
1163
|
+
product_id: z.string(),
|
|
1164
|
+
name: z.string().optional(),
|
|
1165
|
+
price: z.number().optional()
|
|
1166
|
+
})
|
|
1167
|
+
})
|
|
1168
|
+
@UseGuards(JWTGuard, AdminGuard)
|
|
1169
|
+
async update(input: any) {
|
|
1170
|
+
return await this.productService.update(input.product_id, input);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// Delete
|
|
1174
|
+
@Tool({
|
|
1175
|
+
name: 'delete_product',
|
|
1176
|
+
inputSchema: z.object({ product_id: z.string() })
|
|
1177
|
+
})
|
|
1178
|
+
@UseGuards(JWTGuard, AdminGuard)
|
|
1179
|
+
async delete(input: any) {
|
|
1180
|
+
await this.productService.delete(input.product_id);
|
|
1181
|
+
return { success: true };
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
```
|
|
1185
|
+
|
|
1186
|
+
### Pagination Pattern
|
|
1187
|
+
|
|
1188
|
+
```typescript
|
|
1189
|
+
@Tool({
|
|
1190
|
+
name: 'list_products',
|
|
1191
|
+
inputSchema: z.object({
|
|
1192
|
+
page: z.number().default(1),
|
|
1193
|
+
limit: z.number().default(20).max(100),
|
|
1194
|
+
category: z.string().optional()
|
|
1195
|
+
})
|
|
1196
|
+
})
|
|
1197
|
+
async listProducts(input: any) {
|
|
1198
|
+
const offset = (input.page - 1) * input.limit;
|
|
1199
|
+
|
|
1200
|
+
const products = await this.productService.list({
|
|
1201
|
+
limit: input.limit,
|
|
1202
|
+
offset,
|
|
1203
|
+
category: input.category
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
const total = await this.productService.count({ category: input.category });
|
|
1207
|
+
|
|
1208
|
+
return {
|
|
1209
|
+
products,
|
|
1210
|
+
pagination: {
|
|
1211
|
+
page: input.page,
|
|
1212
|
+
limit: input.limit,
|
|
1213
|
+
total,
|
|
1214
|
+
pages: Math.ceil(total / input.limit)
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
### Search Pattern
|
|
1221
|
+
|
|
1222
|
+
```typescript
|
|
1223
|
+
@Tool({
|
|
1224
|
+
name: 'search_products',
|
|
1225
|
+
inputSchema: z.object({
|
|
1226
|
+
query: z.string().min(1),
|
|
1227
|
+
filters: z.object({
|
|
1228
|
+
category: z.string().optional(),
|
|
1229
|
+
min_price: z.number().optional(),
|
|
1230
|
+
max_price: z.number().optional()
|
|
1231
|
+
}).optional()
|
|
1232
|
+
})
|
|
1233
|
+
})
|
|
1234
|
+
@Cache({ ttl: 30, key: (input) => `search:${input.query}:${JSON.stringify(input.filters)}` })
|
|
1235
|
+
async search(input: any) {
|
|
1236
|
+
return await this.productService.search(input.query, input.filters);
|
|
1237
|
+
}
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
---
|
|
1241
|
+
|
|
1242
|
+
## Key Rules for AI Agents
|
|
1243
|
+
|
|
1244
|
+
1. ✅ **Always use decorators** - No factory functions
|
|
1245
|
+
2. ✅ **Constructor injection** - Never use `new ClassName()`
|
|
1246
|
+
3. ✅ **Services for logic** - Tools should be thin wrappers
|
|
1247
|
+
4. ✅ **Zod for schemas** - All tool inputs must have inputSchema
|
|
1248
|
+
5. ✅ **Return JSON** - All tool outputs must be JSON-serializable
|
|
1249
|
+
6. ✅ **ES modules** - Use `.js` in imports, not `.ts`
|
|
1250
|
+
7. ✅ **ExecutionContext** - Second parameter to all handlers
|
|
1251
|
+
8. ✅ **Examples** - Always include request/response examples
|
|
1252
|
+
|
|
1253
|
+
---
|
|
1254
|
+
|
|
1255
|
+
**That's the complete NitroStack SDK reference!**
|
|
1256
|
+
|
|
1257
|
+
For more details, check:
|
|
1258
|
+
- `/docs/sdk/typescript/` - Full documentation
|
|
1259
|
+
- `/templates/typescript-starter/` - Simple example
|
|
1260
|
+
- `/templates/typescript-auth/` - Full-featured example
|