@idealyst/cli 1.0.24 → 1.0.26

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 (48) hide show
  1. package/README.md +109 -109
  2. package/dist/index.js +233 -36
  3. package/dist/types/generators/api.d.ts +2 -0
  4. package/dist/types/generators/index.d.ts +1 -0
  5. package/dist/types/generators/utils.d.ts +18 -1
  6. package/dist/types/types.d.ts +2 -1
  7. package/package.json +1 -1
  8. package/templates/api/README.md +207 -0
  9. package/templates/api/env.example +12 -0
  10. package/templates/api/package.json +49 -0
  11. package/templates/api/prisma/schema.prisma +21 -0
  12. package/templates/api/src/context.ts +23 -0
  13. package/templates/api/src/controllers/UserController.ts +102 -0
  14. package/templates/api/src/index.ts +14 -0
  15. package/templates/api/src/lib/controller.ts +90 -0
  16. package/templates/api/src/lib/middleware.ts +170 -0
  17. package/templates/api/src/middleware/auth.ts +75 -0
  18. package/templates/api/src/middleware/common.ts +103 -0
  19. package/templates/api/src/router/index.ts +130 -0
  20. package/templates/api/src/server.ts +50 -0
  21. package/templates/api/src/trpc.ts +28 -0
  22. package/templates/api/tsconfig.json +44 -0
  23. package/templates/native/.yarnrc.yml +18 -18
  24. package/templates/native/App.tsx +23 -23
  25. package/templates/native/README.md +85 -85
  26. package/templates/native/app.json +4 -4
  27. package/templates/native/babel.config.js +9 -9
  28. package/templates/native/index.js +5 -5
  29. package/templates/native/metro.config.js +27 -27
  30. package/templates/native/package.json +9 -9
  31. package/templates/native/src/App-with-trpc.tsx +72 -0
  32. package/templates/native/src/utils/trpc.ts +127 -0
  33. package/templates/native/tsconfig.json +29 -29
  34. package/templates/shared/README.md +108 -108
  35. package/templates/shared/package.json +39 -39
  36. package/templates/shared/tsconfig.json +24 -24
  37. package/templates/web/README.md +89 -89
  38. package/templates/web/index.html +12 -12
  39. package/templates/web/package.json +55 -51
  40. package/templates/web/src/App-with-trpc.tsx +80 -0
  41. package/templates/web/src/App.tsx +14 -14
  42. package/templates/web/src/main.tsx +24 -24
  43. package/templates/web/src/utils/trpc.ts +93 -0
  44. package/templates/web/tsconfig.json +26 -26
  45. package/templates/web/vite.config.ts +68 -68
  46. package/templates/workspace/.yarnrc.yml +25 -25
  47. package/templates/workspace/README.md +79 -79
  48. package/templates/workspace/package.json +24 -24
@@ -0,0 +1,2 @@
1
+ import { GenerateProjectOptions } from '../types';
2
+ export declare function generateApiProject(options: GenerateProjectOptions): Promise<void>;
@@ -4,3 +4,4 @@ export * from './native';
4
4
  export * from './web';
5
5
  export * from './shared';
6
6
  export * from './workspace';
7
+ export * from './api';
@@ -1,7 +1,7 @@
1
1
  import { TemplateData } from '../types';
2
2
  export declare function validateProjectName(name: string): boolean;
3
3
  export declare function createPackageName(name: string): string;
4
- export declare function updateWorkspacePackageJson(projectName: string, directory: string): Promise<void>;
4
+ export declare function updateWorkspacePackageJson(workspacePath: string, directory: string): Promise<void>;
5
5
  export declare function copyTemplate(templatePath: string, destPath: string, data: TemplateData): Promise<void>;
6
6
  export declare function processTemplateFiles(dir: string, data: TemplateData): Promise<void>;
7
7
  export declare function processTemplateFile(filePath: string, data: TemplateData): Promise<void>;
@@ -10,10 +10,27 @@ export declare function runCommand(command: string, args: string[], options: {
10
10
  cwd: string;
11
11
  }): Promise<void>;
12
12
  export declare function getTemplateData(projectName: string, description?: string, appName?: string): TemplateData;
13
+ /**
14
+ * Detects if we're in a workspace root directory
15
+ */
16
+ export declare function isWorkspaceRoot(directory: string): Promise<boolean>;
17
+ /**
18
+ * Resolves the correct project path for individual projects (native, web, shared).
19
+ * Individual projects can ONLY be created within an existing workspace.
20
+ * This enforces proper monorepo structure and prevents scattered individual projects.
21
+ */
22
+ export declare function resolveProjectPath(projectName: string, directory: string): Promise<{
23
+ projectPath: string;
24
+ workspacePath: string;
25
+ }>;
13
26
  export declare function initializeReactNativeProject(projectName: string, directory: string, displayName?: string, skipInstall?: boolean): Promise<void>;
14
27
  export declare function overlayIdealystFiles(templatePath: string, projectPath: string, data: TemplateData): Promise<void>;
15
28
  export declare function mergePackageJsonDependencies(templatePath: string, projectPath: string): Promise<void>;
16
29
  export declare function promptForProjectName(): Promise<string>;
17
30
  export declare function promptForProjectType(): Promise<string>;
18
31
  export declare function promptForAppName(projectName: string): Promise<string>;
32
+ export declare function promptForTrpcIntegration(): Promise<boolean>;
33
+ export declare function copyTrpcFiles(templatePath: string, projectPath: string, data: TemplateData): Promise<void>;
34
+ export declare function copyTrpcAppComponent(templatePath: string, projectPath: string, data: TemplateData): Promise<void>;
35
+ export declare function removeTrpcDependencies(projectPath: string): Promise<void>;
19
36
  export declare function configureAndroidVectorIcons(projectPath: string): Promise<void>;
@@ -1,10 +1,11 @@
1
- export type ProjectType = 'native' | 'web' | 'shared' | 'workspace';
1
+ export type ProjectType = 'native' | 'web' | 'shared' | 'workspace' | 'api';
2
2
  export interface GenerateProjectOptions {
3
3
  name: string;
4
4
  type: ProjectType;
5
5
  directory: string;
6
6
  skipInstall: boolean;
7
7
  appName?: string;
8
+ withTrpc?: boolean;
8
9
  }
9
10
  export interface TemplateData {
10
11
  projectName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/cli",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
4
4
  "description": "CLI tool for generating Idealyst Framework projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,207 @@
1
+ # {{projectName}}
2
+
3
+ {{description}}
4
+
5
+ This API project is built with:
6
+ - **tRPC** - End-to-end typesafe APIs
7
+ - **Prisma** - Modern database toolkit
8
+ - **Zod** - TypeScript-first schema validation
9
+ - **Express.js** - Web framework for Node.js
10
+ - **TypeScript** - Type-safe JavaScript
11
+
12
+ ## Quick Start
13
+
14
+ 1. **Setup environment variables:**
15
+ ```bash
16
+ cp env.example .env
17
+ ```
18
+
19
+ 2. **Install dependencies:**
20
+ ```bash
21
+ yarn install
22
+ ```
23
+
24
+ 3. **Setup database:**
25
+ ```bash
26
+ # Generate Prisma client
27
+ yarn db:generate
28
+
29
+ # Push schema to database (for development)
30
+ yarn db:push
31
+
32
+ # Or run migrations (for production)
33
+ yarn db:migrate
34
+ ```
35
+
36
+ 4. **Start development server:**
37
+ ```bash
38
+ yarn dev
39
+ ```
40
+
41
+ The API will be available at `http://localhost:3000`
42
+
43
+ ## Available Scripts
44
+
45
+ - `yarn dev` - Start development server with hot reload
46
+ - `yarn build` - Build for production
47
+ - `yarn start` - Start production server
48
+ - `yarn db:generate` - Generate Prisma client
49
+ - `yarn db:push` - Push schema changes to database
50
+ - `yarn db:studio` - Open Prisma Studio (database GUI)
51
+ - `yarn db:migrate` - Run database migrations
52
+ - `yarn db:reset` - Reset database and run all migrations
53
+ - `yarn lint` - Run ESLint
54
+ - `yarn type-check` - Run TypeScript type checking
55
+
56
+ ## API Endpoints
57
+
58
+ ### tRPC Routes
59
+
60
+ All tRPC routes are available at `/trpc/[procedure]`
61
+
62
+ #### Example Routes
63
+ - `hello` - Simple greeting endpoint (accepts optional name parameter)
64
+ - `health` - API health check with timestamp
65
+
66
+ ### REST Endpoints
67
+ - `GET /` - API information
68
+ - `GET /health` - Health check
69
+
70
+ ## Database
71
+
72
+ This project uses SQLite by default for development. You can switch to PostgreSQL or MySQL by updating the `DATABASE_URL` in your `.env` file and the `provider` in `prisma/schema.prisma`.
73
+
74
+ ### Database Schema
75
+
76
+ The schema starts empty - you can add your own models in `prisma/schema.prisma`. Example model structure is provided in comments.
77
+
78
+ ## Development
79
+
80
+ ### Adding New Routes
81
+
82
+ You can add routes in two ways:
83
+
84
+ #### 1. Simple tRPC Procedures (Traditional)
85
+ 1. Create a new router file in `src/router/`
86
+ 2. Define your procedures with Zod schemas for validation
87
+ 3. Export the router and add it to `src/router/index.ts`
88
+
89
+ #### 2. Controller & Middleware System (Recommended)
90
+ This template includes a powerful controller and middleware system:
91
+
92
+ 1. **Create a Controller:**
93
+ ```typescript
94
+ // src/controllers/PostController.ts
95
+ import { z } from 'zod';
96
+ import { BaseController, controllerToRouter } from '../lib/controller.js';
97
+ import { requireAuth, requireAdmin } from '../middleware/auth.js';
98
+ import { logger, rateLimit } from '../middleware/common.js';
99
+
100
+ const createPostSchema = z.object({
101
+ title: z.string().min(1),
102
+ content: z.string(),
103
+ });
104
+
105
+ export class PostController extends BaseController {
106
+ // Public endpoint
107
+ getAll = this.createQuery(
108
+ z.object({ published: z.boolean().optional() }),
109
+ async (input, ctx) => {
110
+ return ctx.prisma.post.findMany({
111
+ where: { published: input.published }
112
+ });
113
+ }
114
+ );
115
+
116
+ // Protected endpoint with middleware
117
+ create = this.createMutationWithMiddleware(
118
+ createPostSchema,
119
+ [logger, rateLimit(5, 60000), requireAuth],
120
+ async (input, ctx) => {
121
+ return ctx.prisma.post.create({ data: input });
122
+ }
123
+ );
124
+
125
+ // Admin-only endpoint
126
+ delete = this.createMutationWithMiddleware(
127
+ z.object({ id: z.string() }),
128
+ [requireAuth, requireAdmin],
129
+ async (input, ctx) => {
130
+ return ctx.prisma.post.delete({ where: { id: input.id } });
131
+ }
132
+ );
133
+ }
134
+
135
+ export const postRouter = controllerToRouter({
136
+ getAll: new PostController({} as any).getAll,
137
+ create: new PostController({} as any).create,
138
+ delete: new PostController({} as any).delete,
139
+ });
140
+ ```
141
+
142
+ 2. **Add to Main Router:**
143
+ ```typescript
144
+ // src/router/index.ts
145
+ import { postRouter } from '../controllers/PostController.js';
146
+
147
+ export const appRouter = router({
148
+ posts: postRouter,
149
+ // ... other routes
150
+ });
151
+ ```
152
+
153
+ ### Available Middleware
154
+
155
+ #### Authentication
156
+ - `requireAuth` - Requires Bearer token authentication
157
+ - `requireRole(role)` - Requires specific user role
158
+ - `requireAdmin` - Requires admin role
159
+
160
+ #### Utility Middleware
161
+ - `logger` - Request/response logging with timing
162
+ - `rateLimit(maxRequests, windowMs)` - Rate limiting per IP
163
+ - `responseTime` - Adds X-Response-Time header
164
+ - `requestId` - Adds unique X-Request-ID header
165
+ - `errorHandler` - Centralized error handling
166
+
167
+ ### Example tRPC Client Usage
168
+
169
+ ```typescript
170
+ import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
171
+ import type { AppRouter } from './path/to/your/api';
172
+
173
+ const client = createTRPCProxyClient<AppRouter>({
174
+ links: [
175
+ httpBatchLink({
176
+ url: 'http://localhost:3000/trpc',
177
+ }),
178
+ ],
179
+ });
180
+
181
+ // Use the client
182
+ const greeting = await client.hello.query({ name: 'John' });
183
+ const healthStatus = await client.health.query();
184
+ ```
185
+
186
+ ## Environment Variables
187
+
188
+ Copy `env.example` to `.env` and configure:
189
+
190
+ - `DATABASE_URL` - Database connection string
191
+ - `PORT` - Server port (default: 3000)
192
+ - `NODE_ENV` - Environment (development/production)
193
+ - `CORS_ORIGIN` - CORS origin for client requests
194
+
195
+ ## Deployment
196
+
197
+ 1. Build the project: `yarn build`
198
+ 2. Set up your production database
199
+ 3. Run migrations: `yarn db:migrate`
200
+ 4. Start the server: `yarn start`
201
+
202
+ ## Learn More
203
+
204
+ - [tRPC Documentation](https://trpc.io/)
205
+ - [Prisma Documentation](https://www.prisma.io/docs/)
206
+ - [Zod Documentation](https://zod.dev/)
207
+ - [Express.js Documentation](https://expressjs.com/)
@@ -0,0 +1,12 @@
1
+ # Database
2
+ DATABASE_URL="file:./dev.db"
3
+
4
+ # Server
5
+ PORT=3000
6
+ NODE_ENV=development
7
+
8
+ # CORS
9
+ CORS_ORIGIN="http://localhost:3000"
10
+
11
+ # Optional: Database for production (uncomment and configure as needed)
12
+ # DATABASE_URL="postgresql://username:password@localhost:5432/{{projectName}}"
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "version": "{{version}}",
4
+ "description": "{{description}}",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsx watch src/server.ts",
10
+ "start": "node dist/server.js",
11
+ "db:generate": "prisma generate",
12
+ "db:push": "prisma db push",
13
+ "db:studio": "prisma studio",
14
+ "db:migrate": "prisma migrate dev",
15
+ "db:reset": "prisma migrate reset",
16
+ "lint": "eslint src --ext .ts,.tsx",
17
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
18
+ "type-check": "tsc --noEmit"
19
+ },
20
+ "dependencies": {
21
+ "@prisma/client": "^5.7.1",
22
+ "@trpc/server": "^10.44.1",
23
+ "cors": "^2.8.5",
24
+ "dotenv": "^16.3.1",
25
+ "express": "^4.18.2",
26
+ "zod": "^3.22.4"
27
+ },
28
+ "devDependencies": {
29
+ "@types/cors": "^2.8.17",
30
+ "@types/express": "^4.17.21",
31
+ "@types/node": "^20.10.4",
32
+ "@typescript-eslint/eslint-plugin": "^6.13.1",
33
+ "@typescript-eslint/parser": "^6.13.1",
34
+ "eslint": "^8.54.0",
35
+ "prisma": "^5.7.1",
36
+ "tsx": "^4.6.2",
37
+ "typescript": "^5.3.3"
38
+ },
39
+ "keywords": [
40
+ "api",
41
+ "trpc",
42
+ "prisma",
43
+ "zod",
44
+ "typescript",
45
+ "express"
46
+ ],
47
+ "author": "",
48
+ "license": "MIT"
49
+ }
@@ -0,0 +1,21 @@
1
+ // This is your Prisma schema file,
2
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
+
4
+ generator client {
5
+ provider = "prisma-client-js"
6
+ }
7
+
8
+ datasource db {
9
+ provider = "sqlite"
10
+ url = env("DATABASE_URL")
11
+ }
12
+
13
+ // Add your models here
14
+ // Example:
15
+ // model User {
16
+ // id String @id @default(cuid())
17
+ // email String @unique
18
+ // name String?
19
+ // createdAt DateTime @default(now())
20
+ // updatedAt DateTime @updatedAt
21
+ // }
@@ -0,0 +1,23 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import type { CreateExpressContextOptions } from '@trpc/server/adapters/express';
3
+
4
+ // Create Prisma client
5
+ const prisma = new PrismaClient({
6
+ log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
7
+ });
8
+
9
+ export interface Context {
10
+ prisma: PrismaClient;
11
+ req: CreateExpressContextOptions['req'];
12
+ res: CreateExpressContextOptions['res'];
13
+ }
14
+
15
+ export const createContext = ({ req, res }: CreateExpressContextOptions): Context => {
16
+ return {
17
+ prisma,
18
+ req,
19
+ res,
20
+ };
21
+ };
22
+
23
+ export type { CreateExpressContextOptions };
@@ -0,0 +1,102 @@
1
+ import { z } from 'zod';
2
+ import { BaseController, controllerToRouter } from '../lib/controller.js';
3
+ import { requireAuth, requireAdmin } from '../middleware/auth.js';
4
+
5
+ // Input schemas
6
+ const createUserSchema = z.object({
7
+ email: z.string().email(),
8
+ name: z.string().optional(),
9
+ });
10
+
11
+ const getUserSchema = z.object({
12
+ id: z.string(),
13
+ });
14
+
15
+ const updateUserSchema = z.object({
16
+ id: z.string(),
17
+ name: z.string().optional(),
18
+ });
19
+
20
+ // User controller class
21
+ export class UserController extends BaseController {
22
+ // Get all users (admin only)
23
+ getAll = this.createQueryWithMiddleware(
24
+ z.object({}),
25
+ [requireAuth, requireAdmin],
26
+ async (input, ctx) => {
27
+ // Simulate database query
28
+ return [
29
+ { id: '1', email: 'user1@example.com', name: 'User 1' },
30
+ { id: '2', email: 'user2@example.com', name: 'User 2' },
31
+ ];
32
+ }
33
+ );
34
+
35
+ // Get user by ID (authenticated users)
36
+ getById = this.createQueryWithMiddleware(
37
+ getUserSchema,
38
+ [requireAuth],
39
+ async (input, ctx) => {
40
+ // In a real app, you'd query your database
41
+ const user = { id: input.id, email: 'user@example.com', name: 'John Doe' };
42
+
43
+ if (!user) {
44
+ throw new Error('User not found');
45
+ }
46
+
47
+ return user;
48
+ }
49
+ );
50
+
51
+ // Create user (public endpoint)
52
+ create = this.createMutation(
53
+ createUserSchema,
54
+ async (input, ctx) => {
55
+ // In a real app, you'd save to database
56
+ const newUser = {
57
+ id: Math.random().toString(36),
58
+ email: input.email,
59
+ name: input.name || null,
60
+ createdAt: new Date(),
61
+ };
62
+
63
+ return newUser;
64
+ }
65
+ );
66
+
67
+ // Update user (authenticated users)
68
+ update = this.createMutationWithMiddleware(
69
+ updateUserSchema,
70
+ [requireAuth],
71
+ async (input, ctx) => {
72
+ // In a real app, you'd update the database
73
+ const updatedUser = {
74
+ id: input.id,
75
+ email: 'user@example.com', // Would come from database
76
+ name: input.name || 'Updated Name',
77
+ updatedAt: new Date(),
78
+ };
79
+
80
+ return updatedUser;
81
+ }
82
+ );
83
+
84
+ // Delete user (admin only)
85
+ delete = this.createMutationWithMiddleware(
86
+ z.object({ id: z.string() }),
87
+ [requireAuth, requireAdmin],
88
+ async (input, ctx) => {
89
+ // In a real app, you'd delete from database
90
+ return { success: true, deletedId: input.id };
91
+ }
92
+ );
93
+ }
94
+
95
+ // Export router - this would be used in your main router
96
+ export const userRouter = controllerToRouter({
97
+ getAll: new UserController({} as any).getAll,
98
+ getById: new UserController({} as any).getById,
99
+ create: new UserController({} as any).create,
100
+ update: new UserController({} as any).update,
101
+ delete: new UserController({} as any).delete,
102
+ });
@@ -0,0 +1,14 @@
1
+ // Main exports for the API
2
+ export { appRouter } from './router/index.js';
3
+ export type { AppRouter } from './router/index.js';
4
+
5
+ // Export context type for client usage
6
+ export type { Context } from './context.js';
7
+
8
+ // Export middleware for potential external usage
9
+ export * from './middleware/auth.js';
10
+ export * from './middleware/common.js';
11
+
12
+ // Export controller base classes for extensions
13
+ export { BaseController, controllerToRouter } from './lib/controller.js';
14
+ export type { MiddlewareFn } from './lib/controller.js';
@@ -0,0 +1,90 @@
1
+ import { z } from 'zod';
2
+ import type { Context } from '../context.js';
3
+ import { publicProcedure } from '../trpc.js';
4
+
5
+ // Base controller class
6
+ export abstract class BaseController {
7
+ protected ctx: Context;
8
+
9
+ constructor(ctx: Context) {
10
+ this.ctx = ctx;
11
+ }
12
+
13
+ // Helper method to create a query procedure
14
+ protected createQuery<TInput, TOutput>(
15
+ inputSchema: z.ZodSchema<TInput>,
16
+ handler: (input: TInput, ctx: Context) => Promise<TOutput> | TOutput
17
+ ) {
18
+ return publicProcedure
19
+ .input(inputSchema)
20
+ .query(async ({ input, ctx }) => {
21
+ return handler(input, ctx);
22
+ });
23
+ }
24
+
25
+ // Helper method to create a mutation procedure
26
+ protected createMutation<TInput, TOutput>(
27
+ inputSchema: z.ZodSchema<TInput>,
28
+ handler: (input: TInput, ctx: Context) => Promise<TOutput> | TOutput
29
+ ) {
30
+ return publicProcedure
31
+ .input(inputSchema)
32
+ .mutation(async ({ input, ctx }) => {
33
+ return handler(input, ctx);
34
+ });
35
+ }
36
+
37
+ // Helper method to create a query with middleware
38
+ protected createQueryWithMiddleware<TInput, TOutput>(
39
+ inputSchema: z.ZodSchema<TInput>,
40
+ middleware: MiddlewareFn[],
41
+ handler: (input: TInput, ctx: Context) => Promise<TOutput> | TOutput
42
+ ) {
43
+ let procedure = publicProcedure.input(inputSchema);
44
+
45
+ // Apply middleware
46
+ for (const mw of middleware) {
47
+ procedure = procedure.use(mw);
48
+ }
49
+
50
+ return procedure.query(async ({ input, ctx }) => {
51
+ return handler(input, ctx);
52
+ });
53
+ }
54
+
55
+ // Helper method to create a mutation with middleware
56
+ protected createMutationWithMiddleware<TInput, TOutput>(
57
+ inputSchema: z.ZodSchema<TInput>,
58
+ middleware: MiddlewareFn[],
59
+ handler: (input: TInput, ctx: Context) => Promise<TOutput> | TOutput
60
+ ) {
61
+ let procedure = publicProcedure.input(inputSchema);
62
+
63
+ // Apply middleware
64
+ for (const mw of middleware) {
65
+ procedure = procedure.use(mw);
66
+ }
67
+
68
+ return procedure.mutation(async ({ input, ctx }) => {
69
+ return handler(input, ctx);
70
+ });
71
+ }
72
+ }
73
+
74
+ // Middleware function type compatible with tRPC
75
+ export type MiddlewareFn = (opts: {
76
+ ctx: Context;
77
+ next: () => Promise<{ ctx: Context }>;
78
+ }) => Promise<{ ctx: Context }>;
79
+
80
+ // Controller method decorator type
81
+ export interface ControllerMethod {
82
+ [key: string]: ReturnType<typeof publicProcedure.query> | ReturnType<typeof publicProcedure.mutation>;
83
+ }
84
+
85
+ // Helper function to convert controller to tRPC router object
86
+ export function controllerToRouter<T extends Record<string, any>>(
87
+ controllerMethods: T
88
+ ): T {
89
+ return controllerMethods;
90
+ }