@idealyst/cli 1.0.23 → 1.0.25
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/README.md +109 -109
- package/dist/index.js +119 -30
- package/dist/types/generators/api.d.ts +2 -0
- package/dist/types/generators/index.d.ts +1 -0
- package/dist/types/generators/utils.d.ts +14 -1
- package/dist/types/types.d.ts +1 -1
- package/package.json +1 -1
- package/templates/api/README.md +207 -0
- package/templates/api/env.example +12 -0
- package/templates/api/package.json +49 -0
- package/templates/api/prisma/schema.prisma +21 -0
- package/templates/api/src/context.ts +23 -0
- package/templates/api/src/controllers/UserController.ts +102 -0
- package/templates/api/src/index.ts +50 -0
- package/templates/api/src/lib/controller.ts +90 -0
- package/templates/api/src/lib/middleware.ts +170 -0
- package/templates/api/src/middleware/auth.ts +75 -0
- package/templates/api/src/middleware/common.ts +103 -0
- package/templates/api/src/router/index.ts +130 -0
- package/templates/api/src/trpc.ts +28 -0
- package/templates/api/tsconfig.json +44 -0
- package/templates/native/.yarnrc.yml +18 -18
- package/templates/native/App.tsx +23 -23
- package/templates/native/README.md +85 -85
- package/templates/native/app.json +4 -4
- package/templates/native/babel.config.js +9 -9
- package/templates/native/index.js +5 -5
- package/templates/native/metro.config.js +27 -21
- package/templates/native/package.json +9 -9
- package/templates/native/tsconfig.json +29 -29
- package/templates/shared/README.md +108 -108
- package/templates/shared/package.json +39 -41
- package/templates/shared/src/index.ts +1 -11
- package/templates/shared/tsconfig.json +24 -24
- package/templates/web/README.md +89 -89
- package/templates/web/index.html +12 -12
- package/templates/web/package.json +51 -51
- package/templates/web/src/App.tsx +14 -14
- package/templates/web/src/main.tsx +24 -24
- package/templates/web/tsconfig.json +26 -26
- package/templates/web/vite.config.ts +68 -65
- package/templates/workspace/.yarnrc.yml +25 -25
- package/templates/workspace/README.md +79 -79
- package/templates/workspace/package.json +24 -24
- package/templates/shared/.yarnrc.yml +0 -19
- package/templates/shared/rollup.config.js +0 -27
- package/templates/shared/src/components/SharedComponent.tsx +0 -41
- package/templates/shared/src/components/index.ts +0 -3
- package/templates/shared/src/types/index.ts +0 -17
- package/templates/shared/src/utils/helpers.ts +0 -34
- package/templates/shared/src/utils/index.ts +0 -2
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { TRPCError } from '@trpc/server';
|
|
2
|
+
import type { Context } from '../context.js';
|
|
3
|
+
|
|
4
|
+
// Middleware function type that works with tRPC
|
|
5
|
+
export type MiddlewareFunction = (opts: {
|
|
6
|
+
ctx: Context;
|
|
7
|
+
next: () => Promise<{ ctx: Context }>;
|
|
8
|
+
}) => Promise<{ ctx: Context }>;
|
|
9
|
+
|
|
10
|
+
// Authentication middleware
|
|
11
|
+
export const requireAuth: MiddlewareFunction = async ({ ctx, next }) => {
|
|
12
|
+
// Example: Check for authorization header
|
|
13
|
+
const authHeader = ctx.req.headers.authorization;
|
|
14
|
+
|
|
15
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
16
|
+
throw new TRPCError({
|
|
17
|
+
code: 'UNAUTHORIZED',
|
|
18
|
+
message: 'Missing or invalid authorization header',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const token = authHeader.substring(7);
|
|
23
|
+
|
|
24
|
+
// Here you would validate the token (JWT, session, etc.)
|
|
25
|
+
// For this example, we'll just check if it's not empty
|
|
26
|
+
if (!token) {
|
|
27
|
+
throw new TRPCError({
|
|
28
|
+
code: 'UNAUTHORIZED',
|
|
29
|
+
message: 'Invalid token',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Add user info to context (replace with your actual user lookup)
|
|
34
|
+
const user = {
|
|
35
|
+
id: 'user-id-from-token',
|
|
36
|
+
email: 'user@example.com',
|
|
37
|
+
// ... other user properties
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return next({
|
|
41
|
+
ctx: {
|
|
42
|
+
...ctx,
|
|
43
|
+
user, // Add user to context
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Role-based authorization middleware
|
|
49
|
+
export const requireRole = (role: string): MiddlewareFunction => {
|
|
50
|
+
return async ({ ctx, next }) => {
|
|
51
|
+
const user = (ctx as any).user;
|
|
52
|
+
|
|
53
|
+
if (!user) {
|
|
54
|
+
throw new TRPCError({
|
|
55
|
+
code: 'UNAUTHORIZED',
|
|
56
|
+
message: 'Authentication required',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check if user has required role
|
|
61
|
+
if (!user.roles?.includes(role)) {
|
|
62
|
+
throw new TRPCError({
|
|
63
|
+
code: 'FORBIDDEN',
|
|
64
|
+
message: `Role '${role}' required`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return next();
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Logging middleware
|
|
73
|
+
export const logger: MiddlewareFunction = async ({ ctx, next }) => {
|
|
74
|
+
const start = Date.now();
|
|
75
|
+
|
|
76
|
+
console.log(`📝 ${ctx.req.method} ${ctx.req.url} - ${new Date().toISOString()}`);
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const result = await next();
|
|
80
|
+
const duration = Date.now() - start;
|
|
81
|
+
console.log(`✅ Request completed in ${duration}ms`);
|
|
82
|
+
return result;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
const duration = Date.now() - start;
|
|
85
|
+
console.log(`❌ Request failed in ${duration}ms:`, error);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Rate limiting middleware (simple in-memory implementation)
|
|
91
|
+
const requestCounts = new Map<string, { count: number; resetTime: number }>();
|
|
92
|
+
|
|
93
|
+
export const rateLimit = (maxRequests: number, windowMs: number): MiddlewareFunction => {
|
|
94
|
+
return async ({ ctx, next }) => {
|
|
95
|
+
const clientId = ctx.req.ip || 'unknown';
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
|
|
98
|
+
const clientData = requestCounts.get(clientId);
|
|
99
|
+
|
|
100
|
+
if (!clientData || now > clientData.resetTime) {
|
|
101
|
+
// Reset or initialize
|
|
102
|
+
requestCounts.set(clientId, {
|
|
103
|
+
count: 1,
|
|
104
|
+
resetTime: now + windowMs,
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
clientData.count++;
|
|
108
|
+
|
|
109
|
+
if (clientData.count > maxRequests) {
|
|
110
|
+
throw new TRPCError({
|
|
111
|
+
code: 'TOO_MANY_REQUESTS',
|
|
112
|
+
message: 'Rate limit exceeded',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return next();
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Validation middleware factory
|
|
122
|
+
export const validateInput = <T>(schema: import('zod').ZodSchema<T>) => {
|
|
123
|
+
const middleware: MiddlewareFunction = async ({ ctx, next }) => {
|
|
124
|
+
return next();
|
|
125
|
+
};
|
|
126
|
+
return middleware;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Error handling middleware
|
|
130
|
+
export const errorHandler: MiddlewareFunction = async ({ ctx, next }) => {
|
|
131
|
+
try {
|
|
132
|
+
return await next();
|
|
133
|
+
} catch (error) {
|
|
134
|
+
// Log the error
|
|
135
|
+
console.error('❌ API Error:', error);
|
|
136
|
+
|
|
137
|
+
// Re-throw tRPC errors as-is
|
|
138
|
+
if (error instanceof TRPCError) {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Convert unknown errors to internal server error
|
|
143
|
+
throw new TRPCError({
|
|
144
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
145
|
+
message: 'An unexpected error occurred',
|
|
146
|
+
cause: error,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// Middleware composer utility
|
|
152
|
+
export const composeMiddleware = (...middlewares: MiddlewareFunction[]): MiddlewareFunction => {
|
|
153
|
+
return async ({ ctx, next }) => {
|
|
154
|
+
let index = 0;
|
|
155
|
+
|
|
156
|
+
const runMiddleware = async (currentCtx: Context): Promise<{ ctx: Context }> => {
|
|
157
|
+
if (index >= middlewares.length) {
|
|
158
|
+
return next();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const middleware = middlewares[index++];
|
|
162
|
+
return middleware({
|
|
163
|
+
ctx: currentCtx,
|
|
164
|
+
next: () => runMiddleware(currentCtx),
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
return runMiddleware(ctx);
|
|
169
|
+
};
|
|
170
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { TRPCError } from '@trpc/server';
|
|
2
|
+
import type { Context } from '../context.js';
|
|
3
|
+
import type { MiddlewareFn } from '../lib/controller.js';
|
|
4
|
+
|
|
5
|
+
// Extended context with user
|
|
6
|
+
export interface AuthContext extends Context {
|
|
7
|
+
user: {
|
|
8
|
+
id: string;
|
|
9
|
+
email: string;
|
|
10
|
+
roles?: string[];
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Authentication middleware
|
|
15
|
+
export const requireAuth: MiddlewareFn = async ({ ctx, next }) => {
|
|
16
|
+
const authHeader = ctx.req.headers.authorization;
|
|
17
|
+
|
|
18
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
19
|
+
throw new TRPCError({
|
|
20
|
+
code: 'UNAUTHORIZED',
|
|
21
|
+
message: 'Missing or invalid authorization header',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const token = authHeader.substring(7);
|
|
26
|
+
|
|
27
|
+
// Here you would validate the token (JWT, session, etc.)
|
|
28
|
+
// For example purposes, we'll simulate token validation
|
|
29
|
+
if (!token || token === 'invalid') {
|
|
30
|
+
throw new TRPCError({
|
|
31
|
+
code: 'UNAUTHORIZED',
|
|
32
|
+
message: 'Invalid token',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Mock user lookup - replace with your actual implementation
|
|
37
|
+
const user = {
|
|
38
|
+
id: 'user-123',
|
|
39
|
+
email: 'user@example.com',
|
|
40
|
+
roles: ['user'],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return next({
|
|
44
|
+
ctx: {
|
|
45
|
+
...ctx,
|
|
46
|
+
user,
|
|
47
|
+
} as AuthContext,
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Role-based authorization middleware factory
|
|
52
|
+
export const requireRole = (requiredRole: string): MiddlewareFn => {
|
|
53
|
+
return async ({ ctx, next }) => {
|
|
54
|
+
const authCtx = ctx as AuthContext;
|
|
55
|
+
|
|
56
|
+
if (!authCtx.user) {
|
|
57
|
+
throw new TRPCError({
|
|
58
|
+
code: 'UNAUTHORIZED',
|
|
59
|
+
message: 'Authentication required',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!authCtx.user.roles?.includes(requiredRole)) {
|
|
64
|
+
throw new TRPCError({
|
|
65
|
+
code: 'FORBIDDEN',
|
|
66
|
+
message: `Role '${requiredRole}' required`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return next();
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Admin role middleware
|
|
75
|
+
export const requireAdmin = requireRole('admin');
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { Context } from '../context.js';
|
|
2
|
+
import type { MiddlewareFn } from '../lib/controller.js';
|
|
3
|
+
|
|
4
|
+
// Logging middleware
|
|
5
|
+
export const logger: MiddlewareFn = async ({ ctx, next }) => {
|
|
6
|
+
const start = Date.now();
|
|
7
|
+
const { method, url } = ctx.req;
|
|
8
|
+
|
|
9
|
+
console.log(`📝 ${method} ${url} - ${new Date().toISOString()}`);
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const result = await next();
|
|
13
|
+
const duration = Date.now() - start;
|
|
14
|
+
console.log(`✅ Request completed in ${duration}ms`);
|
|
15
|
+
return result;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
const duration = Date.now() - start;
|
|
18
|
+
console.log(`❌ Request failed in ${duration}ms:`, error);
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Simple rate limiting middleware (in-memory)
|
|
24
|
+
const requestCounts = new Map<string, { count: number; resetTime: number }>();
|
|
25
|
+
|
|
26
|
+
export const rateLimit = (maxRequests: number = 100, windowMs: number = 15 * 60 * 1000): MiddlewareFn => {
|
|
27
|
+
return async ({ ctx, next }) => {
|
|
28
|
+
const clientId = ctx.req.ip || ctx.req.socket.remoteAddress || 'unknown';
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
|
|
31
|
+
const clientData = requestCounts.get(clientId);
|
|
32
|
+
|
|
33
|
+
if (!clientData || now > clientData.resetTime) {
|
|
34
|
+
// Reset or initialize
|
|
35
|
+
requestCounts.set(clientId, {
|
|
36
|
+
count: 1,
|
|
37
|
+
resetTime: now + windowMs,
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
clientData.count++;
|
|
41
|
+
|
|
42
|
+
if (clientData.count > maxRequests) {
|
|
43
|
+
throw new Error('Rate limit exceeded');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// CORS middleware (though this should typically be handled at Express level)
|
|
52
|
+
export const cors: MiddlewareFn = async ({ ctx, next }) => {
|
|
53
|
+
// Set CORS headers (note: this is just for demonstration)
|
|
54
|
+
// In practice, use the cors Express middleware
|
|
55
|
+
ctx.res.setHeader('Access-Control-Allow-Origin', '*');
|
|
56
|
+
ctx.res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
57
|
+
ctx.res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
58
|
+
|
|
59
|
+
return next();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Error handling middleware
|
|
63
|
+
export const errorHandler: MiddlewareFn = async ({ ctx, next }) => {
|
|
64
|
+
try {
|
|
65
|
+
return await next();
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// Log the error
|
|
68
|
+
console.error('❌ API Error:', error);
|
|
69
|
+
|
|
70
|
+
// Re-throw the error (tRPC will handle it properly)
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Response time header middleware
|
|
76
|
+
export const responseTime: MiddlewareFn = async ({ ctx, next }) => {
|
|
77
|
+
const start = Date.now();
|
|
78
|
+
|
|
79
|
+
const result = await next();
|
|
80
|
+
|
|
81
|
+
const duration = Date.now() - start;
|
|
82
|
+
ctx.res.setHeader('X-Response-Time', `${duration}ms`);
|
|
83
|
+
|
|
84
|
+
return result;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Request ID middleware
|
|
88
|
+
export const requestId: MiddlewareFn = async ({ ctx, next }) => {
|
|
89
|
+
const id = Math.random().toString(36).substring(2, 15);
|
|
90
|
+
|
|
91
|
+
// Add request ID to context
|
|
92
|
+
const newCtx = {
|
|
93
|
+
...ctx,
|
|
94
|
+
requestId: id,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Set response header
|
|
98
|
+
ctx.res.setHeader('X-Request-ID', id);
|
|
99
|
+
|
|
100
|
+
return next({
|
|
101
|
+
ctx: newCtx,
|
|
102
|
+
});
|
|
103
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { router, publicProcedure } from '../trpc.js';
|
|
3
|
+
|
|
4
|
+
export const appRouter = router({
|
|
5
|
+
// Simple hello world procedure
|
|
6
|
+
hello: publicProcedure
|
|
7
|
+
.input(z.object({ name: z.string().optional() }))
|
|
8
|
+
.query(({ input }) => {
|
|
9
|
+
return {
|
|
10
|
+
greeting: `Hello ${input.name || 'World'}!`,
|
|
11
|
+
timestamp: new Date().toISOString(),
|
|
12
|
+
};
|
|
13
|
+
}),
|
|
14
|
+
|
|
15
|
+
// Health check procedure
|
|
16
|
+
health: publicProcedure.query(() => {
|
|
17
|
+
return {
|
|
18
|
+
status: 'OK',
|
|
19
|
+
timestamp: new Date().toISOString(),
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
};
|
|
22
|
+
}),
|
|
23
|
+
|
|
24
|
+
// Add your procedures here
|
|
25
|
+
// Example:
|
|
26
|
+
// users: userRouter,
|
|
27
|
+
// posts: postRouter,
|
|
28
|
+
|
|
29
|
+
// Example controller integration:
|
|
30
|
+
// Uncomment the lines below and create the corresponding controllers
|
|
31
|
+
|
|
32
|
+
// 1. Import your controllers at the top:
|
|
33
|
+
// import { userRouter } from '../controllers/UserController.js';
|
|
34
|
+
|
|
35
|
+
// 2. Add them to the router:
|
|
36
|
+
// users: userRouter,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Export type definition of API
|
|
40
|
+
export type AppRouter = typeof appRouter;
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
CONTROLLER & MIDDLEWARE SYSTEM USAGE:
|
|
44
|
+
|
|
45
|
+
This API template includes a controller and middleware system that works seamlessly with tRPC.
|
|
46
|
+
|
|
47
|
+
## Quick Start with Controllers:
|
|
48
|
+
|
|
49
|
+
1. Create a controller (see src/controllers/UserController.ts for example):
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { z } from 'zod';
|
|
53
|
+
import { BaseController, controllerToRouter } from '../lib/controller.js';
|
|
54
|
+
import { requireAuth } from '../middleware/auth.js';
|
|
55
|
+
|
|
56
|
+
export class UserController extends BaseController {
|
|
57
|
+
getAll = this.createQueryWithMiddleware(
|
|
58
|
+
z.object({}),
|
|
59
|
+
[requireAuth],
|
|
60
|
+
async (input, ctx) => {
|
|
61
|
+
return await ctx.prisma.user.findMany();
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const userRouter = controllerToRouter({
|
|
67
|
+
getAll: new UserController({} as any).getAll,
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
2. Add to main router:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { userRouter } from '../controllers/UserController.js';
|
|
75
|
+
|
|
76
|
+
export const appRouter = router({
|
|
77
|
+
users: userRouter,
|
|
78
|
+
// ... other routes
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Available Middleware:
|
|
83
|
+
|
|
84
|
+
### Authentication:
|
|
85
|
+
- `requireAuth` - Requires Bearer token
|
|
86
|
+
- `requireRole(role)` - Requires specific role
|
|
87
|
+
- `requireAdmin` - Requires admin role
|
|
88
|
+
|
|
89
|
+
### Utility:
|
|
90
|
+
- `logger` - Request/response logging
|
|
91
|
+
- `rateLimit(max, window)` - Rate limiting
|
|
92
|
+
- `responseTime` - Adds response time header
|
|
93
|
+
- `requestId` - Adds unique request ID
|
|
94
|
+
- `errorHandler` - Centralized error handling
|
|
95
|
+
|
|
96
|
+
## Usage Examples:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Public endpoint
|
|
100
|
+
getPublicData = this.createQuery(schema, handler);
|
|
101
|
+
|
|
102
|
+
// Protected endpoint
|
|
103
|
+
getPrivateData = this.createQueryWithMiddleware(
|
|
104
|
+
schema,
|
|
105
|
+
[requireAuth],
|
|
106
|
+
handler
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Admin-only endpoint
|
|
110
|
+
adminAction = this.createMutationWithMiddleware(
|
|
111
|
+
schema,
|
|
112
|
+
[requireAuth, requireAdmin],
|
|
113
|
+
handler
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Multiple middleware
|
|
117
|
+
complexEndpoint = this.createQueryWithMiddleware(
|
|
118
|
+
schema,
|
|
119
|
+
[logger, rateLimit(10, 60000), requireAuth],
|
|
120
|
+
handler
|
|
121
|
+
);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This system provides:
|
|
125
|
+
✅ Type safety with tRPC
|
|
126
|
+
✅ Reusable middleware
|
|
127
|
+
✅ Clean controller organization
|
|
128
|
+
✅ Easy testing
|
|
129
|
+
✅ Consistent error handling
|
|
130
|
+
*/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { initTRPC } from '@trpc/server';
|
|
2
|
+
import { type Context } from './context.js';
|
|
3
|
+
import { ZodError } from 'zod';
|
|
4
|
+
|
|
5
|
+
const t = initTRPC.context<Context>().create({
|
|
6
|
+
errorFormatter({ shape, error }) {
|
|
7
|
+
return {
|
|
8
|
+
...shape,
|
|
9
|
+
data: {
|
|
10
|
+
...shape.data,
|
|
11
|
+
zodError:
|
|
12
|
+
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Export reusable router and procedure helpers
|
|
19
|
+
export const router = t.router;
|
|
20
|
+
export const publicProcedure = t.procedure;
|
|
21
|
+
|
|
22
|
+
// You can create additional procedures with middleware here
|
|
23
|
+
// For example, a protected procedure that requires authentication:
|
|
24
|
+
// export const protectedProcedure = t.procedure.use(async ({ ctx, next }) => {
|
|
25
|
+
// // Add your authentication logic here
|
|
26
|
+
// // Example: check for valid session/token
|
|
27
|
+
// return next({ ctx: { ...ctx, user: { id: 'user-id' } } });
|
|
28
|
+
// });
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"allowSyntheticDefaultImports": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"checkJs": false,
|
|
10
|
+
"jsx": "preserve",
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"outDir": "./dist",
|
|
15
|
+
"rootDir": "./src",
|
|
16
|
+
"removeComments": false,
|
|
17
|
+
"strict": true,
|
|
18
|
+
"noImplicitAny": true,
|
|
19
|
+
"strictNullChecks": true,
|
|
20
|
+
"strictFunctionTypes": true,
|
|
21
|
+
"noImplicitThis": true,
|
|
22
|
+
"useUnknownInCatchVariables": true,
|
|
23
|
+
"noImplicitReturns": true,
|
|
24
|
+
"noFallthroughCasesInSwitch": true,
|
|
25
|
+
"noUncheckedIndexedAccess": true,
|
|
26
|
+
"exactOptionalPropertyTypes": true,
|
|
27
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
28
|
+
"resolveJsonModule": true,
|
|
29
|
+
"isolatedModules": true,
|
|
30
|
+
"forceConsistentCasingInFileNames": true,
|
|
31
|
+
"skipLibCheck": true
|
|
32
|
+
},
|
|
33
|
+
"include": [
|
|
34
|
+
"src/**/*"
|
|
35
|
+
],
|
|
36
|
+
"exclude": [
|
|
37
|
+
"node_modules",
|
|
38
|
+
"dist",
|
|
39
|
+
"**/*.test.ts"
|
|
40
|
+
],
|
|
41
|
+
"ts-node": {
|
|
42
|
+
"esm": true
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
nodeLinker: "node-modules"
|
|
2
|
-
|
|
3
|
-
# Enable network for package downloads
|
|
4
|
-
enableNetwork: true
|
|
5
|
-
|
|
6
|
-
# Timeout for HTTP requests (in milliseconds)
|
|
7
|
-
httpTimeout: 60000
|
|
8
|
-
|
|
9
|
-
# Number of retry attempts for HTTP requests
|
|
10
|
-
httpRetry: 3
|
|
11
|
-
|
|
12
|
-
# Registry configuration
|
|
13
|
-
npmRegistryServer: "https://registry.yarnpkg.com"
|
|
14
|
-
|
|
15
|
-
# Enable progress bars
|
|
16
|
-
enableProgressBars: true
|
|
17
|
-
|
|
18
|
-
# Enable colors in output
|
|
1
|
+
nodeLinker: "node-modules"
|
|
2
|
+
|
|
3
|
+
# Enable network for package downloads
|
|
4
|
+
enableNetwork: true
|
|
5
|
+
|
|
6
|
+
# Timeout for HTTP requests (in milliseconds)
|
|
7
|
+
httpTimeout: 60000
|
|
8
|
+
|
|
9
|
+
# Number of retry attempts for HTTP requests
|
|
10
|
+
httpRetry: 3
|
|
11
|
+
|
|
12
|
+
# Registry configuration
|
|
13
|
+
npmRegistryServer: "https://registry.yarnpkg.com"
|
|
14
|
+
|
|
15
|
+
# Enable progress bars
|
|
16
|
+
enableProgressBars: true
|
|
17
|
+
|
|
18
|
+
# Enable colors in output
|
|
19
19
|
enableColors: true
|
package/templates/native/App.tsx
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
SafeAreaView,
|
|
4
|
-
} from 'react-native';
|
|
5
|
-
|
|
6
|
-
import { ExampleStackRouter } from '@idealyst/navigation/examples';
|
|
7
|
-
import { NavigatorProvider } from '@idealyst/navigation';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function App() {
|
|
11
|
-
|
|
12
|
-
const backgroundStyle = {
|
|
13
|
-
flex: 1,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<SafeAreaView style={backgroundStyle}>
|
|
18
|
-
<NavigatorProvider route={ExampleStackRouter} />
|
|
19
|
-
</SafeAreaView>
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default App;
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
SafeAreaView,
|
|
4
|
+
} from 'react-native';
|
|
5
|
+
|
|
6
|
+
import { ExampleStackRouter } from '@idealyst/navigation/examples';
|
|
7
|
+
import { NavigatorProvider } from '@idealyst/navigation';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function App() {
|
|
11
|
+
|
|
12
|
+
const backgroundStyle = {
|
|
13
|
+
flex: 1,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<SafeAreaView style={backgroundStyle}>
|
|
18
|
+
<NavigatorProvider route={ExampleStackRouter} />
|
|
19
|
+
</SafeAreaView>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default App;
|