nitrostack 1.0.0 → 1.0.2

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.
Files changed (164) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/cli/index.js +4 -1
  3. package/dist/cli/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/studio/README.md +140 -0
  6. package/src/studio/app/api/auth/fetch-metadata/route.ts +71 -0
  7. package/src/studio/app/api/auth/register-client/route.ts +67 -0
  8. package/src/studio/app/api/chat/route.ts +123 -0
  9. package/src/studio/app/api/health/checks/route.ts +42 -0
  10. package/src/studio/app/api/health/route.ts +13 -0
  11. package/src/studio/app/api/init/route.ts +85 -0
  12. package/src/studio/app/api/ping/route.ts +13 -0
  13. package/src/studio/app/api/prompts/[name]/route.ts +21 -0
  14. package/src/studio/app/api/prompts/route.ts +13 -0
  15. package/src/studio/app/api/resources/[...uri]/route.ts +18 -0
  16. package/src/studio/app/api/resources/route.ts +13 -0
  17. package/src/studio/app/api/roots/route.ts +13 -0
  18. package/src/studio/app/api/sampling/route.ts +14 -0
  19. package/src/studio/app/api/tools/[name]/call/route.ts +41 -0
  20. package/src/studio/app/api/tools/route.ts +23 -0
  21. package/src/studio/app/api/widget-examples/route.ts +44 -0
  22. package/src/studio/app/auth/callback/page.tsx +160 -0
  23. package/src/studio/app/auth/page.tsx +543 -0
  24. package/src/studio/app/chat/page.tsx +530 -0
  25. package/src/studio/app/chat/page.tsx.backup +390 -0
  26. package/src/studio/app/globals.css +410 -0
  27. package/src/studio/app/health/page.tsx +177 -0
  28. package/src/studio/app/layout.tsx +48 -0
  29. package/src/studio/app/page.tsx +337 -0
  30. package/src/studio/app/page.tsx.backup +346 -0
  31. package/src/studio/app/ping/page.tsx +204 -0
  32. package/src/studio/app/prompts/page.tsx +228 -0
  33. package/src/studio/app/resources/page.tsx +313 -0
  34. package/src/studio/components/EnlargeModal.tsx +116 -0
  35. package/src/studio/components/Sidebar.tsx +133 -0
  36. package/src/studio/components/ToolCard.tsx +108 -0
  37. package/src/studio/components/WidgetRenderer.tsx +99 -0
  38. package/src/studio/lib/api.ts +207 -0
  39. package/src/studio/lib/llm-service.ts +361 -0
  40. package/src/studio/lib/mcp-client.ts +168 -0
  41. package/src/studio/lib/store.ts +192 -0
  42. package/src/studio/lib/theme-provider.tsx +50 -0
  43. package/src/studio/lib/types.ts +107 -0
  44. package/src/studio/lib/widget-loader.ts +90 -0
  45. package/src/studio/middleware.ts +27 -0
  46. package/src/studio/next.config.js +16 -0
  47. package/src/studio/package-lock.json +2696 -0
  48. package/src/studio/package.json +34 -0
  49. package/src/studio/postcss.config.mjs +10 -0
  50. package/src/studio/tailwind.config.ts +67 -0
  51. package/src/studio/tsconfig.json +41 -0
  52. package/templates/typescript-auth/.env.example +23 -0
  53. package/templates/typescript-auth/src/app.module.ts +103 -0
  54. package/templates/typescript-auth/src/db/database.ts +163 -0
  55. package/templates/typescript-auth/src/db/seed.ts +374 -0
  56. package/templates/typescript-auth/src/db/setup.ts +87 -0
  57. package/templates/typescript-auth/src/events/analytics.service.ts +52 -0
  58. package/templates/typescript-auth/src/events/notification.service.ts +40 -0
  59. package/templates/typescript-auth/src/filters/global-exception.filter.ts +28 -0
  60. package/templates/typescript-auth/src/guards/README.md +75 -0
  61. package/templates/typescript-auth/src/guards/jwt.guard.ts +105 -0
  62. package/templates/typescript-auth/src/health/database.health.ts +41 -0
  63. package/templates/typescript-auth/src/index.ts +26 -0
  64. package/templates/typescript-auth/src/interceptors/transform.interceptor.ts +24 -0
  65. package/templates/typescript-auth/src/middleware/logging.middleware.ts +42 -0
  66. package/templates/typescript-auth/src/modules/addresses/addresses.module.ts +16 -0
  67. package/templates/typescript-auth/src/modules/addresses/addresses.prompts.ts +114 -0
  68. package/templates/typescript-auth/src/modules/addresses/addresses.resources.ts +40 -0
  69. package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +241 -0
  70. package/templates/typescript-auth/src/modules/auth/auth.module.ts +16 -0
  71. package/templates/typescript-auth/src/modules/auth/auth.prompts.ts +147 -0
  72. package/templates/typescript-auth/src/modules/auth/auth.resources.ts +84 -0
  73. package/templates/typescript-auth/src/modules/auth/auth.tools.ts +139 -0
  74. package/templates/typescript-auth/src/modules/cart/cart.module.ts +16 -0
  75. package/templates/typescript-auth/src/modules/cart/cart.prompts.ts +95 -0
  76. package/templates/typescript-auth/src/modules/cart/cart.resources.ts +44 -0
  77. package/templates/typescript-auth/src/modules/cart/cart.tools.ts +281 -0
  78. package/templates/typescript-auth/src/modules/orders/orders.module.ts +16 -0
  79. package/templates/typescript-auth/src/modules/orders/orders.prompts.ts +88 -0
  80. package/templates/typescript-auth/src/modules/orders/orders.resources.ts +48 -0
  81. package/templates/typescript-auth/src/modules/orders/orders.tools.ts +281 -0
  82. package/templates/typescript-auth/src/modules/products/products.module.ts +16 -0
  83. package/templates/typescript-auth/src/modules/products/products.prompts.ts +146 -0
  84. package/templates/typescript-auth/src/modules/products/products.resources.ts +98 -0
  85. package/templates/typescript-auth/src/modules/products/products.tools.ts +266 -0
  86. package/templates/typescript-auth/src/pipes/validation.pipe.ts +42 -0
  87. package/templates/typescript-auth/src/services/database.service.ts +90 -0
  88. package/templates/typescript-auth/src/widgets/app/add-to-cart/page.tsx +122 -0
  89. package/templates/typescript-auth/src/widgets/app/address-added/page.tsx +116 -0
  90. package/templates/typescript-auth/src/widgets/app/address-deleted/page.tsx +105 -0
  91. package/templates/typescript-auth/src/widgets/app/address-list/page.tsx +139 -0
  92. package/templates/typescript-auth/src/widgets/app/address-updated/page.tsx +153 -0
  93. package/templates/typescript-auth/src/widgets/app/cart-cleared/page.tsx +86 -0
  94. package/templates/typescript-auth/src/widgets/app/cart-updated/page.tsx +116 -0
  95. package/templates/typescript-auth/src/widgets/app/categories/page.tsx +134 -0
  96. package/templates/typescript-auth/src/widgets/app/layout.tsx +21 -0
  97. package/templates/typescript-auth/src/widgets/app/login-result/page.tsx +129 -0
  98. package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +206 -0
  99. package/templates/typescript-auth/src/widgets/app/order-details/page.tsx +225 -0
  100. package/templates/typescript-auth/src/widgets/app/order-history/page.tsx +218 -0
  101. package/templates/typescript-auth/src/widgets/app/product-card/page.tsx +121 -0
  102. package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +173 -0
  103. package/templates/typescript-auth/src/widgets/app/shopping-cart/page.tsx +187 -0
  104. package/templates/typescript-auth/src/widgets/app/whoami/page.tsx +165 -0
  105. package/templates/typescript-auth/src/widgets/next.config.js +38 -0
  106. package/templates/typescript-auth/src/widgets/package.json +18 -0
  107. package/templates/typescript-auth/src/widgets/styles/ecommerce.ts +169 -0
  108. package/templates/typescript-auth/src/widgets/tsconfig.json +28 -0
  109. package/templates/typescript-auth/src/widgets/types/tool-data.ts +141 -0
  110. package/templates/typescript-auth/src/widgets/widget-manifest.json +464 -0
  111. package/templates/typescript-auth/tsconfig.json +27 -0
  112. package/templates/typescript-auth-api-key/.env +15 -0
  113. package/templates/typescript-auth-api-key/.env.example +4 -0
  114. package/templates/typescript-auth-api-key/src/app.module.ts +38 -0
  115. package/templates/typescript-auth-api-key/src/guards/apikey.guard.ts +47 -0
  116. package/templates/typescript-auth-api-key/src/guards/multi-auth.guard.ts +157 -0
  117. package/templates/typescript-auth-api-key/src/health/system.health.ts +55 -0
  118. package/templates/typescript-auth-api-key/src/index.ts +47 -0
  119. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.module.ts +12 -0
  120. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.prompts.ts +73 -0
  121. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.resources.ts +60 -0
  122. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.tools.ts +71 -0
  123. package/templates/typescript-auth-api-key/src/modules/demo/demo.module.ts +18 -0
  124. package/templates/typescript-auth-api-key/src/modules/demo/demo.tools.ts +155 -0
  125. package/templates/typescript-auth-api-key/src/modules/demo/multi-auth.tools.ts +123 -0
  126. package/templates/typescript-auth-api-key/src/widgets/app/calculator-operations/page.tsx +133 -0
  127. package/templates/typescript-auth-api-key/src/widgets/app/calculator-result/page.tsx +134 -0
  128. package/templates/typescript-auth-api-key/src/widgets/app/layout.tsx +14 -0
  129. package/templates/typescript-auth-api-key/src/widgets/next.config.js +37 -0
  130. package/templates/typescript-auth-api-key/src/widgets/package.json +24 -0
  131. package/templates/typescript-auth-api-key/src/widgets/tsconfig.json +28 -0
  132. package/templates/typescript-auth-api-key/src/widgets/widget-manifest.json +48 -0
  133. package/templates/typescript-auth-api-key/tsconfig.json +23 -0
  134. package/templates/typescript-oauth/.env.example +91 -0
  135. package/templates/typescript-oauth/src/app.module.ts +89 -0
  136. package/templates/typescript-oauth/src/guards/oauth.guard.ts +127 -0
  137. package/templates/typescript-oauth/src/index.ts +74 -0
  138. package/templates/typescript-oauth/src/modules/demo/demo.module.ts +16 -0
  139. package/templates/typescript-oauth/src/modules/demo/demo.tools.ts +190 -0
  140. package/templates/typescript-oauth/src/widgets/app/calculator-operations/page.tsx +133 -0
  141. package/templates/typescript-oauth/src/widgets/app/calculator-result/page.tsx +134 -0
  142. package/templates/typescript-oauth/src/widgets/app/layout.tsx +14 -0
  143. package/templates/typescript-oauth/src/widgets/next.config.js +37 -0
  144. package/templates/typescript-oauth/src/widgets/package.json +24 -0
  145. package/templates/typescript-oauth/src/widgets/tsconfig.json +28 -0
  146. package/templates/typescript-oauth/src/widgets/widget-manifest.json +48 -0
  147. package/templates/typescript-oauth/tsconfig.json +23 -0
  148. package/templates/typescript-starter/.env.example +4 -0
  149. package/templates/typescript-starter/src/app.module.ts +34 -0
  150. package/templates/typescript-starter/src/health/system.health.ts +55 -0
  151. package/templates/typescript-starter/src/index.ts +27 -0
  152. package/templates/typescript-starter/src/modules/calculator/calculator.module.ts +12 -0
  153. package/templates/typescript-starter/src/modules/calculator/calculator.prompts.ts +73 -0
  154. package/templates/typescript-starter/src/modules/calculator/calculator.resources.ts +60 -0
  155. package/templates/typescript-starter/src/modules/calculator/calculator.tools.ts +71 -0
  156. package/templates/typescript-starter/src/widgets/app/calculator-operations/page.tsx +133 -0
  157. package/templates/typescript-starter/src/widgets/app/calculator-result/page.tsx +134 -0
  158. package/templates/typescript-starter/src/widgets/app/layout.tsx +14 -0
  159. package/templates/typescript-starter/src/widgets/next.config.js +37 -0
  160. package/templates/typescript-starter/src/widgets/package.json +24 -0
  161. package/templates/typescript-starter/src/widgets/tsconfig.json +28 -0
  162. package/templates/typescript-starter/src/widgets/widget-manifest.json +48 -0
  163. package/templates/typescript-starter/tsconfig.json +23 -0
  164. package/LICENSE_URLS_UPDATE_COMPLETE.md +0 -388
@@ -0,0 +1,105 @@
1
+ import jwt from 'jsonwebtoken';
2
+ import { Guard, ExecutionContext, JWTPayload, JWTModule } from 'nitrostack';
3
+
4
+ /**
5
+ * JWT Guard - Validates JWT tokens and populates context.auth
6
+ *
7
+ * This is an example implementation. You can customize it for your needs.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * @Tool({ name: 'whoami', ... })
12
+ * @UseGuards(JWTGuard)
13
+ * async whoami(input: any, context: ExecutionContext) {
14
+ * // context.auth is populated with JWT payload
15
+ * return { user: context.auth };
16
+ * }
17
+ * ```
18
+ */
19
+ export class JWTGuard implements Guard {
20
+ async canActivate(context: ExecutionContext): Promise<boolean> {
21
+ try {
22
+ // Get JWT secret from JWTModule configuration
23
+ const secret = JWTModule.getSecret();
24
+ if (!secret) {
25
+ context.logger.warn('[JWTGuard] JWT secret not configured');
26
+ throw new Error('JWT authentication not configured. Set JWT_SECRET environment variable.');
27
+ }
28
+
29
+ // Extract token from context
30
+ const token = this.extractToken(context);
31
+ if (!token) {
32
+ context.logger.info('[JWTGuard] No token found in request');
33
+ throw new Error('Authentication required. Please login first and include your JWT token.');
34
+ }
35
+
36
+ // Verify token
37
+ const payload = await this.verifyToken(token, secret);
38
+ if (!payload) {
39
+ context.logger.warn('[JWTGuard] Token verification failed');
40
+ throw new Error('Invalid or expired token. Please login again.');
41
+ }
42
+
43
+ // Populate context.auth
44
+ context.auth = {
45
+ subject: payload.sub,
46
+ scopes: [],
47
+ email: payload.email,
48
+ name: payload.name,
49
+ ...payload,
50
+ };
51
+
52
+ context.logger.info(`[JWTGuard] Authentication successful for user: ${payload.sub}`);
53
+ return true;
54
+ } catch (error) {
55
+ context.logger.error('[JWTGuard] Authentication failed', error);
56
+ throw error;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Extract JWT token from execution context
62
+ * Supports multiple token locations for maximum compatibility
63
+ */
64
+ private extractToken(context: ExecutionContext): string | null {
65
+ // Priority 1: Check metadata for _jwt field (MCP protocol)
66
+ if (context.metadata?._jwt) {
67
+ return context.metadata._jwt as string;
68
+ }
69
+
70
+ // Priority 2: Check Authorization header format in metadata
71
+ if (context.metadata?.authorization) {
72
+ const auth = context.metadata.authorization as string;
73
+ if (auth.startsWith('Bearer ')) {
74
+ return auth.substring(7);
75
+ }
76
+ return auth;
77
+ }
78
+
79
+ // Priority 3: Check metadata.headers (if present)
80
+ const headers = context.metadata?.headers as any;
81
+ if (headers?.authorization) {
82
+ const auth = headers.authorization as string;
83
+ if (auth.startsWith('Bearer ')) {
84
+ return auth.substring(7);
85
+ }
86
+ return auth;
87
+ }
88
+
89
+ return null;
90
+ }
91
+
92
+ /**
93
+ * Verify JWT token
94
+ */
95
+ private async verifyToken(token: string, secret: string): Promise<JWTPayload | null> {
96
+ try {
97
+ const cleanSecret = secret.trim();
98
+ const decoded = jwt.verify(token, cleanSecret) as JWTPayload;
99
+ return decoded;
100
+ } catch (error) {
101
+ return null;
102
+ }
103
+ }
104
+ }
105
+
@@ -0,0 +1,41 @@
1
+ import { HealthCheck, HealthCheckInterface, HealthCheckResult } from 'nitrostack';
2
+ import { DatabaseService } from '../services/database.service.js';
3
+
4
+ /**
5
+ * Database Health Check
6
+ *
7
+ * Monitors database connectivity
8
+ */
9
+ @HealthCheck({
10
+ name: 'database',
11
+ description: 'Database connectivity check',
12
+ interval: 30 // Check every 30 seconds
13
+ })
14
+ export class DatabaseHealthCheck implements HealthCheckInterface {
15
+ constructor(private database: DatabaseService) {}
16
+
17
+ async check(): Promise<HealthCheckResult> {
18
+ try {
19
+ const isConnected = await this.database.ping();
20
+
21
+ if (isConnected) {
22
+ return {
23
+ status: 'up',
24
+ message: 'Database is connected and responsive',
25
+ };
26
+ } else {
27
+ return {
28
+ status: 'down',
29
+ message: 'Database connection failed',
30
+ };
31
+ }
32
+ } catch (error: any) {
33
+ return {
34
+ status: 'down',
35
+ message: 'Database health check failed',
36
+ details: error.message,
37
+ };
38
+ }
39
+ }
40
+ }
41
+
@@ -0,0 +1,26 @@
1
+ /**
2
+ * E-commerce MCP Server
3
+ *
4
+ * Main entry point for the MCP server.
5
+ * Uses the new @McpApp decorator pattern for clean, NestJS-style architecture.
6
+ */
7
+
8
+ import { McpApplicationFactory } from 'nitrostack';
9
+ import { AppModule } from './app.module.js';
10
+
11
+ /**
12
+ * Bootstrap the application
13
+ */
14
+ async function bootstrap() {
15
+ // Create and start the MCP server from AppModule
16
+ const server = await McpApplicationFactory.create(AppModule);
17
+
18
+ // Start the server
19
+ await server.start();
20
+ }
21
+
22
+ // Start the application
23
+ bootstrap().catch((error) => {
24
+ console.error('❌ Failed to start server:', error);
25
+ process.exit(1);
26
+ });
@@ -0,0 +1,24 @@
1
+ import { Interceptor, InterceptorInterface, ExecutionContext } from 'nitrostack';
2
+
3
+ /**
4
+ * Transform Interceptor
5
+ *
6
+ * Wraps all responses in a standard format with metadata
7
+ */
8
+ @Interceptor()
9
+ export class TransformInterceptor implements InterceptorInterface {
10
+ async intercept(context: ExecutionContext, next: () => Promise<any>) {
11
+ const result = await next();
12
+
13
+ return {
14
+ success: true,
15
+ data: result,
16
+ metadata: {
17
+ timestamp: new Date().toISOString(),
18
+ requestId: context.requestId,
19
+ tool: context.toolName || 'unknown',
20
+ },
21
+ };
22
+ }
23
+ }
24
+
@@ -0,0 +1,42 @@
1
+ import { Middleware, MiddlewareInterface, ExecutionContext } from 'nitrostack';
2
+
3
+ /**
4
+ * Logging Middleware
5
+ *
6
+ * Logs all tool executions with timing information
7
+ */
8
+ @Middleware()
9
+ export class LoggingMiddleware implements MiddlewareInterface {
10
+ async use(context: ExecutionContext, next: () => Promise<any>) {
11
+ const start = performance.now();
12
+ const toolName = context.toolName || 'unknown';
13
+
14
+ context.logger.info(`[${toolName}] Started`, {
15
+ requestId: context.requestId,
16
+ user: context.auth?.subject || 'anonymous',
17
+ });
18
+
19
+ try {
20
+ const result = await next();
21
+ const duration = performance.now() - start;
22
+
23
+ context.logger.info(`[${toolName}] Completed in ${duration.toFixed(2)}ms`, {
24
+ requestId: context.requestId,
25
+ duration,
26
+ });
27
+
28
+ return result;
29
+ } catch (error: any) {
30
+ const duration = performance.now() - start;
31
+
32
+ context.logger.error(`[${toolName}] Failed after ${duration.toFixed(2)}ms`, {
33
+ requestId: context.requestId,
34
+ duration,
35
+ error: error.message,
36
+ });
37
+
38
+ throw error;
39
+ }
40
+ }
41
+ }
42
+
@@ -0,0 +1,16 @@
1
+ import { Module } from 'nitrostack';
2
+ import { AddressesTools } from './addresses.tools.js';
3
+ import { AddressesResources } from './addresses.resources.js';
4
+ import { AddressesPrompts } from './addresses.prompts.js';
5
+
6
+ /**
7
+ * Addresses Module
8
+ * Provides shipping address management, resources, and prompts
9
+ */
10
+ @Module({
11
+ name: 'addresses',
12
+ description: 'Shipping address management',
13
+ controllers: [AddressesTools, AddressesResources, AddressesPrompts],
14
+ })
15
+ export class AddressesModule {}
16
+
@@ -0,0 +1,114 @@
1
+ import { PromptDecorator as Prompt, ExecutionContext } from 'nitrostack';
2
+
3
+ /**
4
+ * Addresses Prompts
5
+ * Provides helpful prompts for address management
6
+ */
7
+ export class AddressesPrompts {
8
+ /**
9
+ * Address management help prompt
10
+ */
11
+ @Prompt({
12
+ name: 'address-help',
13
+ description: 'Get help managing shipping addresses',
14
+ })
15
+ async addressHelp(args: Record<string, any>, context: ExecutionContext) {
16
+ return [
17
+ {
18
+ role: 'user' as const,
19
+ content: 'I need help managing shipping addresses',
20
+ },
21
+ {
22
+ role: 'assistant' as const,
23
+ content: `**Shipping Address Management Guide**
24
+
25
+ **Available Tools (Requires Authentication):**
26
+ 1. \`add_address\` - Add new shipping address
27
+ 2. \`get_addresses\` - View all your addresses
28
+ 3. \`set_default_address\` - Set default for orders
29
+ 4. \`delete_address\` - Remove an address
30
+
31
+ **Adding an Address:**
32
+ \`\`\`
33
+ add_address({
34
+ full_name: "John Doe",
35
+ street: "123 Main St",
36
+ city: "New York",
37
+ state: "NY",
38
+ zip_code: "10001",
39
+ country: "USA",
40
+ phone: "+1234567890",
41
+ is_default: true
42
+ })
43
+ \`\`\`
44
+
45
+ **Required Fields:**
46
+ - ✓ full_name
47
+ - ✓ street
48
+ - ✓ city
49
+ - ✓ state
50
+ - ✓ zip_code
51
+ - ✓ phone
52
+
53
+ **Optional Fields:**
54
+ - country (defaults to "USA")
55
+ - is_default (defaults to false)
56
+
57
+ **Default Address:**
58
+ - Used automatically for orders
59
+ - Only one default address per user
60
+ - Setting new default unsets old one
61
+
62
+ **Viewing Addresses:**
63
+ \`\`\`
64
+ get_addresses({})
65
+ \`\`\`
66
+ Returns all addresses, sorted by:
67
+ 1. Default address first
68
+ 2. Then by creation date
69
+
70
+ **Setting Default:**
71
+ \`\`\`
72
+ set_default_address({
73
+ address_id: "address-uuid"
74
+ })
75
+ \`\`\`
76
+
77
+ **Deleting Address:**
78
+ \`\`\`
79
+ delete_address({
80
+ address_id: "address-uuid"
81
+ })
82
+ \`\`\`
83
+ ⚠️ Cannot be undone!
84
+
85
+ **Address Format Tips:**
86
+ 📍 **Street:** Include apartment/unit if applicable
87
+ 📍 **State:** Use 2-letter code (NY, CA, TX)
88
+ 📍 **ZIP:** 5-digit or 9-digit format
89
+ 📍 **Phone:** Include country code for international
90
+
91
+ **Common Operations:**
92
+ 1. **First Time Setup:**
93
+ - Add address with is_default: true
94
+ - Will be used for all orders
95
+
96
+ 2. **Multiple Addresses:**
97
+ - Add each address separately
98
+ - Set one as default
99
+ - Choose specific address when ordering
100
+
101
+ 3. **Update Address:**
102
+ - Delete old address
103
+ - Add new address with updated info
104
+
105
+ **Tips:**
106
+ ✓ Add address before first order
107
+ ✓ Keep at least one address on file
108
+ ✓ Verify address details before saving
109
+ ✓ Use default for frequent shipping location`,
110
+ },
111
+ ];
112
+ }
113
+ }
114
+
@@ -0,0 +1,40 @@
1
+ import { ResourceDecorator as Resource, ExecutionContext } from 'nitrostack';
2
+
3
+ /**
4
+ * Addresses Resources
5
+ * Provides address-related resource endpoints
6
+ */
7
+ export class AddressesResources {
8
+ /**
9
+ * Address schema resource
10
+ */
11
+ @Resource({
12
+ uri: 'addresses://schema',
13
+ name: 'Address Schema',
14
+ description: 'Schema definition for shipping address objects',
15
+ mimeType: 'application/json',
16
+ })
17
+ async getAddressSchema(context: ExecutionContext) {
18
+ return {
19
+ type: 'json' as const,
20
+ data: {
21
+ type: 'object',
22
+ properties: {
23
+ id: { type: 'string', format: 'uuid', description: 'Address ID' },
24
+ user_id: { type: 'string', description: 'User ID' },
25
+ full_name: { type: 'string', description: 'Full name for delivery' },
26
+ street: { type: 'string', description: 'Street address' },
27
+ city: { type: 'string', description: 'City' },
28
+ state: { type: 'string', description: 'State/Province' },
29
+ zip_code: { type: 'string', description: 'ZIP/Postal code' },
30
+ country: { type: 'string', default: 'USA', description: 'Country' },
31
+ phone: { type: 'string', description: 'Contact phone number' },
32
+ is_default: { type: 'boolean', description: 'Is default shipping address' },
33
+ created_at: { type: 'string', format: 'date-time', description: 'Address creation time' },
34
+ },
35
+ required: ['full_name', 'street', 'city', 'state', 'zip_code', 'phone'],
36
+ },
37
+ };
38
+ }
39
+ }
40
+
@@ -0,0 +1,241 @@
1
+ import { ToolDecorator as Tool, Widget, UseGuards, z, ExecutionContext } from 'nitrostack';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { getDatabase } from '../../db/database.js';
4
+ import { JWTGuard } from '../../guards/jwt.guard.js';
5
+
6
+ /**
7
+ * Addresses Tools
8
+ * Handles user shipping addresses (requires authentication)
9
+ */
10
+ export class AddressesTools {
11
+ /**
12
+ * Add new address - Requires authentication
13
+ */
14
+ @Tool({
15
+ name: 'add_address',
16
+ description: 'Add a new shipping address. Requires authentication.',
17
+ inputSchema: z.object({
18
+ full_name: z.string().describe('Full name for delivery'),
19
+ street: z.string().describe('Street address'),
20
+ city: z.string().describe('City'),
21
+ state: z.string().describe('State/Province'),
22
+ zip_code: z.string().describe('ZIP/Postal code'),
23
+ country: z.string().default('USA').describe('Country'),
24
+ phone: z.string().describe('Contact phone number'),
25
+ is_default: z.boolean().default(false).describe('Set as default address'),
26
+ }),
27
+ examples: {
28
+ request: {
29
+ full_name: 'Emily Johnson',
30
+ street: '123 Main Street',
31
+ city: 'San Francisco',
32
+ state: 'CA',
33
+ zip_code: '94102',
34
+ country: 'USA',
35
+ phone: '+1-555-0100',
36
+ is_default: true
37
+ },
38
+ response: {
39
+ message: 'Address added successfully!',
40
+ addressId: 'addr-1',
41
+ isDefault: true
42
+ }
43
+ }
44
+ })
45
+ @Widget('address-added')
46
+ @UseGuards(JWTGuard)
47
+ async addAddress(input: any, context: ExecutionContext) {
48
+ const db = getDatabase();
49
+ const userId = context.auth?.subject;
50
+ const addressId = uuidv4();
51
+
52
+ // If this is set as default, unset other defaults
53
+ if (input.is_default) {
54
+ db.prepare(`
55
+ UPDATE addresses SET is_default = 0 WHERE user_id = ?
56
+ `).run(userId);
57
+ }
58
+
59
+ db.prepare(`
60
+ INSERT INTO addresses (id, user_id, full_name, street, city, state, zip_code, country, phone, is_default)
61
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
62
+ `).run(
63
+ addressId,
64
+ userId,
65
+ input.full_name,
66
+ input.street,
67
+ input.city,
68
+ input.state,
69
+ input.zip_code,
70
+ input.country,
71
+ input.phone,
72
+ input.is_default ? 1 : 0
73
+ );
74
+
75
+ context.logger.info(`Address added: ${input.city}, ${input.state}`);
76
+
77
+ return {
78
+ message: 'Address added successfully!',
79
+ addressId,
80
+ isDefault: input.is_default,
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Get all addresses - Requires authentication
86
+ */
87
+ @Tool({
88
+ name: 'get_addresses',
89
+ description: 'Get all saved shipping addresses. Requires authentication.',
90
+ inputSchema: z.object({}),
91
+ examples: {
92
+ request: {},
93
+ response: {
94
+ addresses: [
95
+ {
96
+ id: 'addr-1',
97
+ full_name: 'Emily Johnson',
98
+ street: '123 Main Street',
99
+ city: 'San Francisco',
100
+ state: 'CA',
101
+ zip_code: '94102',
102
+ country: 'USA',
103
+ phone: '+1-555-0100',
104
+ is_default: true
105
+ },
106
+ {
107
+ id: 'addr-2',
108
+ full_name: 'Emily Johnson',
109
+ street: '456 Office Blvd',
110
+ city: 'San Francisco',
111
+ state: 'CA',
112
+ zip_code: '94103',
113
+ country: 'USA',
114
+ phone: '+1-555-0100',
115
+ is_default: false
116
+ }
117
+ ]
118
+ }
119
+ }
120
+ })
121
+ @Widget('address-list')
122
+ @UseGuards(JWTGuard)
123
+ async getAddresses(input: any, context: ExecutionContext) {
124
+ const db = getDatabase();
125
+ const userId = context.auth?.subject;
126
+
127
+ const addresses: any[] = db.prepare(`
128
+ SELECT * FROM addresses WHERE user_id = ? ORDER BY is_default DESC, created_at DESC
129
+ `).all(userId);
130
+
131
+ context.logger.info(`Retrieved ${addresses.length} addresses`);
132
+
133
+ return {
134
+ addresses: addresses.map(addr => ({
135
+ ...addr,
136
+ is_default: addr.is_default === 1,
137
+ })),
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Set default address - Requires authentication
143
+ */
144
+ @Tool({
145
+ name: 'set_default_address',
146
+ description: 'Set an address as the default shipping address. Requires authentication.',
147
+ examples: {
148
+ request: {
149
+ address_id: 'addr-2'
150
+ },
151
+ response: {
152
+ message: 'Default address updated!',
153
+ addressId: 'addr-2'
154
+ }
155
+ },
156
+ inputSchema: z.object({
157
+ address_id: z.string().describe('The ID of the address to set as default'),
158
+ }),
159
+ })
160
+ @Widget('address-updated')
161
+ @UseGuards(JWTGuard)
162
+ async setDefaultAddress(input: any, context: ExecutionContext) {
163
+ const db = getDatabase();
164
+ const userId = context.auth?.subject;
165
+
166
+ // Verify address belongs to user
167
+ const address: any = db.prepare(`
168
+ SELECT * FROM addresses WHERE id = ? AND user_id = ?
169
+ `).get(input.address_id, userId);
170
+
171
+ if (!address) {
172
+ throw new Error('Address not found or you don\'t have access to it.');
173
+ }
174
+
175
+ // Unset all defaults
176
+ db.prepare(`
177
+ UPDATE addresses SET is_default = 0 WHERE user_id = ?
178
+ `).run(userId);
179
+
180
+ // Set new default
181
+ db.prepare(`
182
+ UPDATE addresses SET is_default = 1 WHERE id = ?
183
+ `).run(input.address_id);
184
+
185
+ context.logger.info(`Set default address: ${address.city}, ${address.state}`);
186
+
187
+ return {
188
+ message: 'Default address updated!',
189
+ address: {
190
+ ...address,
191
+ is_default: true,
192
+ },
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Delete address - Requires authentication
198
+ */
199
+ @Tool({
200
+ name: 'delete_address',
201
+ description: 'Delete a shipping address. Requires authentication.',
202
+ inputSchema: z.object({
203
+ address_id: z.string().describe('The ID of the address to delete'),
204
+ }),
205
+ examples: {
206
+ request: {
207
+ address_id: 'addr-2'
208
+ },
209
+ response: {
210
+ message: 'Address deleted successfully!',
211
+ addressId: 'addr-2'
212
+ }
213
+ }
214
+ })
215
+ @Widget('address-deleted')
216
+ @UseGuards(JWTGuard)
217
+ async deleteAddress(input: any, context: ExecutionContext) {
218
+ const db = getDatabase();
219
+ const userId = context.auth?.subject;
220
+
221
+ // Verify address belongs to user
222
+ const address: any = db.prepare(`
223
+ SELECT * FROM addresses WHERE id = ? AND user_id = ?
224
+ `).get(input.address_id, userId);
225
+
226
+ if (!address) {
227
+ throw new Error('Address not found or you don\'t have access to it.');
228
+ }
229
+
230
+ db.prepare(`
231
+ DELETE FROM addresses WHERE id = ?
232
+ `).run(input.address_id);
233
+
234
+ context.logger.info(`Deleted address: ${address.city}, ${address.state}`);
235
+
236
+ return {
237
+ message: 'Address deleted successfully!',
238
+ };
239
+ }
240
+ }
241
+
@@ -0,0 +1,16 @@
1
+ import { Module } from 'nitrostack';
2
+ import { AuthTools } from './auth.tools.js';
3
+ import { AuthResources } from './auth.resources.js';
4
+ import { AuthPrompts } from './auth.prompts.js';
5
+
6
+ /**
7
+ * Authentication Module
8
+ * Provides login, user profile tools, resources, and prompts
9
+ */
10
+ @Module({
11
+ name: 'auth',
12
+ description: 'Authentication and user management',
13
+ controllers: [AuthTools, AuthResources, AuthPrompts],
14
+ })
15
+ export class AuthModule {}
16
+