@yoms/create-monorepo 1.1.0 → 1.2.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/dist/index.js +1 -1
- package/package.json +14 -15
- package/templates/backend-hono/base/AGENT.md +326 -0
package/dist/index.js
CHANGED
|
@@ -656,7 +656,7 @@ async function createMonorepo(targetDir) {
|
|
|
656
656
|
|
|
657
657
|
// src/index.ts
|
|
658
658
|
var cli = cac("create-monorepo");
|
|
659
|
-
cli.command("[dir]", "Create a
|
|
659
|
+
cli.command("[dir]", "Create a new monorepo project").action(async (dir) => {
|
|
660
660
|
await createMonorepo(dir);
|
|
661
661
|
});
|
|
662
662
|
cli.help();
|
package/package.json
CHANGED
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yoms/create-monorepo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "CLI tool to scaffold monorepo projects from templates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-monorepo": "./dist/index.js"
|
|
8
8
|
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"dev": "tsx src/index.ts",
|
|
11
|
-
"build": "tsup src/index.ts --format esm --clean",
|
|
12
|
-
"typecheck": "tsc --noEmit",
|
|
13
|
-
"lint": "eslint src --ext .ts",
|
|
14
|
-
"format": "prettier --write \"src/**/*.ts\"",
|
|
15
|
-
"test:generate": "tsx scripts/test-generate.ts",
|
|
16
|
-
"test:validate": "tsx scripts/validate-templates.ts",
|
|
17
|
-
"changeset": "changeset",
|
|
18
|
-
"version": "changeset version",
|
|
19
|
-
"release": "pnpm build && changeset publish",
|
|
20
|
-
"prepublishOnly": "pnpm build && pnpm typecheck"
|
|
21
|
-
},
|
|
22
9
|
"keywords": [
|
|
23
10
|
"monorepo",
|
|
24
11
|
"template",
|
|
@@ -70,5 +57,17 @@
|
|
|
70
57
|
},
|
|
71
58
|
"engines": {
|
|
72
59
|
"node": ">=18.0.0"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"dev": "tsx src/index.ts",
|
|
63
|
+
"build": "tsup src/index.ts --format esm --clean",
|
|
64
|
+
"typecheck": "tsc --noEmit",
|
|
65
|
+
"lint": "eslint src --ext .ts",
|
|
66
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
67
|
+
"test:generate": "tsx scripts/test-generate.ts",
|
|
68
|
+
"test:validate": "tsx scripts/validate-templates.ts",
|
|
69
|
+
"changeset": "changeset",
|
|
70
|
+
"version": "changeset version && pnpm install --lockfile-only",
|
|
71
|
+
"release": "pnpm build && changeset publish"
|
|
73
72
|
}
|
|
74
|
-
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# AGENT.md - Backend API
|
|
2
|
+
|
|
3
|
+
This file provides guidance to AI agents (Claude, Cursor, etc.) when working with this backend API.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is a Hono-based backend API in a monorepo workspace. It's designed for high performance, full type safety, and production-ready patterns.
|
|
8
|
+
|
|
9
|
+
## Tech Stack
|
|
10
|
+
|
|
11
|
+
- **Framework**: Hono (lightweight, fast, type-safe)
|
|
12
|
+
- **Language**: TypeScript with strict mode
|
|
13
|
+
- **Runtime**: Node.js (compatible with Bun, Deno, Cloudflare Workers)
|
|
14
|
+
- **Validation**: Zod schemas
|
|
15
|
+
- **Logger**: Winston with structured logging
|
|
16
|
+
- **Build Tool**: tsup (fast ESM bundler)
|
|
17
|
+
- **Testing**: Vitest with coverage
|
|
18
|
+
|
|
19
|
+
## Development Commands
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Development
|
|
23
|
+
pnpm dev # Start with hot reload
|
|
24
|
+
pnpm build # Build for production
|
|
25
|
+
pnpm start # Run production build
|
|
26
|
+
pnpm test # Run tests
|
|
27
|
+
pnpm test:watch # Watch mode
|
|
28
|
+
pnpm test:coverage # Coverage report
|
|
29
|
+
pnpm typecheck # Type checking
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Project Structure
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
src/
|
|
36
|
+
├── config/ # Configuration (env, logger, db)
|
|
37
|
+
├── lib/ # Utilities and helpers
|
|
38
|
+
│ ├── errors.ts # Custom error classes
|
|
39
|
+
│ └── response.ts # Response helpers
|
|
40
|
+
├── middleware/ # Hono middleware
|
|
41
|
+
│ ├── cors.middleware.ts
|
|
42
|
+
│ ├── error.middleware.ts
|
|
43
|
+
│ ├── logger.middleware.ts
|
|
44
|
+
│ └── rate-limit.middleware.ts
|
|
45
|
+
├── routes/ # API route handlers
|
|
46
|
+
│ └── health.route.ts
|
|
47
|
+
├── services/ # Business logic (if database enabled)
|
|
48
|
+
├── types/ # TypeScript types
|
|
49
|
+
└── index.ts # App entry point
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Architecture Patterns
|
|
53
|
+
|
|
54
|
+
### Error Handling
|
|
55
|
+
|
|
56
|
+
Use custom error classes for consistent error responses:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { NotFoundError, BadRequestError } from '../lib/errors.js';
|
|
60
|
+
|
|
61
|
+
// Throw custom errors - they're automatically handled
|
|
62
|
+
throw new NotFoundError('User not found');
|
|
63
|
+
throw new BadRequestError('Invalid email format');
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Available errors:
|
|
67
|
+
- `BadRequestError` (400)
|
|
68
|
+
- `UnauthorizedError` (401)
|
|
69
|
+
- `ForbiddenError` (403)
|
|
70
|
+
- `NotFoundError` (404)
|
|
71
|
+
- `ConflictError` (409)
|
|
72
|
+
- `ValidationError` (422)
|
|
73
|
+
- `TooManyRequestsError` (429)
|
|
74
|
+
- `InternalServerError` (500)
|
|
75
|
+
|
|
76
|
+
### Response Helpers
|
|
77
|
+
|
|
78
|
+
Use standardized response helpers:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { success, created, noContent, paginated } from '../lib/response.js';
|
|
82
|
+
|
|
83
|
+
// Success response (200)
|
|
84
|
+
return success(c, { id: '123', name: 'John' });
|
|
85
|
+
|
|
86
|
+
// Created response (201)
|
|
87
|
+
return created(c, newUser);
|
|
88
|
+
|
|
89
|
+
// No content (204)
|
|
90
|
+
return noContent(c);
|
|
91
|
+
|
|
92
|
+
// Paginated response
|
|
93
|
+
return paginated(c, users, page, limit, total);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Route Structure
|
|
97
|
+
|
|
98
|
+
Follow this pattern for new routes:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { Hono } from 'hono';
|
|
102
|
+
import { zValidator } from '@hono/zod-validator';
|
|
103
|
+
import { success } from '../lib/response.js';
|
|
104
|
+
import { CreateItemSchema } from '@__PROJECT_NAME__/shared';
|
|
105
|
+
|
|
106
|
+
const items = new Hono();
|
|
107
|
+
|
|
108
|
+
// List items with pagination
|
|
109
|
+
items.get('/', zValidator('query', paginationSchema), async (c) => {
|
|
110
|
+
const { page, limit } = c.req.valid('query');
|
|
111
|
+
const result = await ItemService.getAll(page, limit);
|
|
112
|
+
return paginated(c, result.items, page, limit, result.total);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Get by ID
|
|
116
|
+
items.get('/:id', async (c) => {
|
|
117
|
+
const id = c.req.param('id');
|
|
118
|
+
const item = await ItemService.getById(id);
|
|
119
|
+
return success(c, item);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Create
|
|
123
|
+
items.post('/', zValidator('json', CreateItemSchema), async (c) => {
|
|
124
|
+
const data = c.req.valid('json');
|
|
125
|
+
const item = await ItemService.create(data);
|
|
126
|
+
return created(c, item);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
export { items };
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Service Layer Pattern
|
|
133
|
+
|
|
134
|
+
Create services for business logic:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { prisma } from '../config/database.js';
|
|
138
|
+
import { NotFoundError, ConflictError } from '../lib/errors.js';
|
|
139
|
+
|
|
140
|
+
export class ItemService {
|
|
141
|
+
static async getAll(page = 1, limit = 10) {
|
|
142
|
+
const skip = (page - 1) * limit;
|
|
143
|
+
const [items, total] = await Promise.all([
|
|
144
|
+
prisma.item.findMany({ skip, take: limit }),
|
|
145
|
+
prisma.item.count(),
|
|
146
|
+
]);
|
|
147
|
+
return { items, total, page, limit };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static async getById(id: string) {
|
|
151
|
+
const item = await prisma.item.findUnique({ where: { id } });
|
|
152
|
+
if (!item) throw new NotFoundError(`Item ${id} not found`);
|
|
153
|
+
return item;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static async create(data: CreateItem) {
|
|
157
|
+
// Check for duplicates
|
|
158
|
+
const existing = await prisma.item.findUnique({ where: { name: data.name } });
|
|
159
|
+
if (existing) throw new ConflictError('Item already exists');
|
|
160
|
+
|
|
161
|
+
return prisma.item.create({ data });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Environment Variables
|
|
167
|
+
|
|
168
|
+
All env vars are validated with Zod in `src/config/env.ts`. Add new variables:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const envSchema = z.object({
|
|
172
|
+
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
173
|
+
PORT: z.coerce.number().default(3001),
|
|
174
|
+
// Add your vars here
|
|
175
|
+
NEW_VAR: z.string().min(1),
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Rate Limiting
|
|
180
|
+
|
|
181
|
+
Apply rate limiting to routes:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { rateLimits } from '../middleware/rate-limit.middleware.js';
|
|
185
|
+
|
|
186
|
+
// Use preset limits
|
|
187
|
+
app.use('/api/*', rateLimits.standard); // 100 req/min
|
|
188
|
+
|
|
189
|
+
// Custom limit
|
|
190
|
+
app.use('/auth/login', rateLimit({
|
|
191
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
192
|
+
max: 5,
|
|
193
|
+
message: 'Too many login attempts',
|
|
194
|
+
}));
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Testing
|
|
198
|
+
|
|
199
|
+
Write tests for all routes and services:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { describe, it, expect } from 'vitest';
|
|
203
|
+
import { Hono } from 'hono';
|
|
204
|
+
import { items } from '../items.route.js';
|
|
205
|
+
|
|
206
|
+
describe('Items Route', () => {
|
|
207
|
+
const app = new Hono();
|
|
208
|
+
app.route('/items', items);
|
|
209
|
+
|
|
210
|
+
it('should list items', async () => {
|
|
211
|
+
const res = await app.request('/items?page=1&limit=10');
|
|
212
|
+
expect(res.status).toBe(200);
|
|
213
|
+
|
|
214
|
+
const json = await res.json();
|
|
215
|
+
expect(json.success).toBe(true);
|
|
216
|
+
expect(json.data).toBeInstanceOf(Array);
|
|
217
|
+
expect(json.pagination).toBeDefined();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Database (if enabled)
|
|
223
|
+
|
|
224
|
+
### Prisma Commands
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
pnpm prisma:generate # Generate Prisma client
|
|
228
|
+
pnpm prisma:migrate # Create and run migrations
|
|
229
|
+
pnpm prisma:studio # Open Prisma Studio UI
|
|
230
|
+
pnpm prisma:push # Push schema to database
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Creating Migrations
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
# After editing schema.prisma
|
|
237
|
+
pnpm prisma:migrate
|
|
238
|
+
# Enter migration name when prompted
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Type Sharing
|
|
242
|
+
|
|
243
|
+
Import shared types from the shared package:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { User, CreateUser, UpdateUser } from '@__PROJECT_NAME__/shared';
|
|
247
|
+
import { UserSchema } from '@__PROJECT_NAME__/shared';
|
|
248
|
+
|
|
249
|
+
// Use schemas for validation
|
|
250
|
+
const result = UserSchema.parse(data);
|
|
251
|
+
|
|
252
|
+
// Use types for type safety
|
|
253
|
+
const user: User = await getUser(id);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Best Practices
|
|
257
|
+
|
|
258
|
+
### DO:
|
|
259
|
+
- ✅ Use custom error classes, not raw `throw new Error()`
|
|
260
|
+
- ✅ Use response helpers for all routes
|
|
261
|
+
- ✅ Validate all input with Zod schemas
|
|
262
|
+
- ✅ Put business logic in services, not routes
|
|
263
|
+
- ✅ Write tests for new features
|
|
264
|
+
- ✅ Use type-safe imports from shared package
|
|
265
|
+
- ✅ Log important operations with structured logging
|
|
266
|
+
- ✅ Handle async operations properly with try/catch
|
|
267
|
+
|
|
268
|
+
### DON'T:
|
|
269
|
+
- ❌ Don't return raw `c.json()` - use response helpers
|
|
270
|
+
- ❌ Don't put business logic in routes - use services
|
|
271
|
+
- ❌ Don't use `any` type - maintain type safety
|
|
272
|
+
- ❌ Don't skip validation - always validate input
|
|
273
|
+
- ❌ Don't commit `.env` files - use `.env.example`
|
|
274
|
+
- ❌ Don't use blocking operations in async handlers
|
|
275
|
+
- ❌ Don't modify shared types here - edit in `packages/shared`
|
|
276
|
+
|
|
277
|
+
## Common Tasks
|
|
278
|
+
|
|
279
|
+
### Adding a New Route
|
|
280
|
+
|
|
281
|
+
1. Create route file in `src/routes/`
|
|
282
|
+
2. Create service in `src/services/` (if needed)
|
|
283
|
+
3. Add validation schemas in `packages/shared/src/schemas/`
|
|
284
|
+
4. Register route in `src/index.ts`
|
|
285
|
+
5. Write tests in `src/routes/__tests__/`
|
|
286
|
+
|
|
287
|
+
### Adding Middleware
|
|
288
|
+
|
|
289
|
+
1. Create in `src/middleware/`
|
|
290
|
+
2. Export middleware function
|
|
291
|
+
3. Apply in `src/index.ts` with `app.use()`
|
|
292
|
+
|
|
293
|
+
### Adding Environment Variables
|
|
294
|
+
|
|
295
|
+
1. Add to `.env.example`
|
|
296
|
+
2. Add to `src/config/env.ts` schema
|
|
297
|
+
3. Use via `env.YOUR_VAR`
|
|
298
|
+
|
|
299
|
+
## Debugging
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// Use structured logging
|
|
303
|
+
import { logger } from './config/logger.js';
|
|
304
|
+
|
|
305
|
+
logger.info('User logged in', { userId: user.id });
|
|
306
|
+
logger.warn('Rate limit approaching', { ip, count });
|
|
307
|
+
logger.error('Database error', { error, query });
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Performance Tips
|
|
311
|
+
|
|
312
|
+
- Use database indexes for frequently queried fields
|
|
313
|
+
- Use pagination for list endpoints
|
|
314
|
+
- Cache expensive operations with Redis (if enabled)
|
|
315
|
+
- Use `prisma.$transaction()` for multiple related operations
|
|
316
|
+
- Profile with `pnpm test:coverage` to find slow tests
|
|
317
|
+
|
|
318
|
+
## Security Checklist
|
|
319
|
+
|
|
320
|
+
- [ ] All inputs are validated with Zod
|
|
321
|
+
- [ ] Rate limiting is applied to auth endpoints
|
|
322
|
+
- [ ] Sensitive data is not logged
|
|
323
|
+
- [ ] Database queries use parameterized queries (Prisma does this)
|
|
324
|
+
- [ ] CORS is configured correctly
|
|
325
|
+
- [ ] Environment variables are validated on startup
|
|
326
|
+
- [ ] Error messages don't leak sensitive info in production
|