create-velox-app 0.4.14 → 0.6.23

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 (113) hide show
  1. package/README.md +2 -43
  2. package/dist/cli.js +23 -13
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.js +26 -8
  5. package/dist/index.js.map +1 -1
  6. package/dist/templates/auth.d.ts.map +1 -1
  7. package/dist/templates/auth.js +24 -0
  8. package/dist/templates/auth.js.map +1 -1
  9. package/dist/templates/fullstack.d.ts +15 -0
  10. package/dist/templates/fullstack.d.ts.map +1 -0
  11. package/dist/templates/fullstack.js +110 -0
  12. package/dist/templates/fullstack.js.map +1 -0
  13. package/dist/templates/index.d.ts +1 -1
  14. package/dist/templates/index.d.ts.map +1 -1
  15. package/dist/templates/index.js +27 -5
  16. package/dist/templates/index.js.map +1 -1
  17. package/dist/templates/placeholders.d.ts +6 -1
  18. package/dist/templates/placeholders.d.ts.map +1 -1
  19. package/dist/templates/placeholders.js +15 -5
  20. package/dist/templates/placeholders.js.map +1 -1
  21. package/dist/templates/rsc.d.ts +15 -0
  22. package/dist/templates/rsc.d.ts.map +1 -0
  23. package/dist/templates/rsc.js +192 -0
  24. package/dist/templates/rsc.js.map +1 -0
  25. package/dist/templates/shared/root.d.ts +1 -0
  26. package/dist/templates/shared/root.d.ts.map +1 -1
  27. package/dist/templates/shared/root.js +4 -0
  28. package/dist/templates/shared/root.js.map +1 -1
  29. package/dist/templates/spa.d.ts +12 -0
  30. package/dist/templates/spa.d.ts.map +1 -0
  31. package/dist/templates/spa.js +101 -0
  32. package/dist/templates/spa.js.map +1 -0
  33. package/dist/templates/trpc.d.ts.map +1 -1
  34. package/dist/templates/trpc.js +16 -0
  35. package/dist/templates/trpc.js.map +1 -1
  36. package/dist/templates/types.d.ts +14 -1
  37. package/dist/templates/types.d.ts.map +1 -1
  38. package/dist/templates/types.js +35 -10
  39. package/dist/templates/types.js.map +1 -1
  40. package/package.json +3 -3
  41. package/src/templates/source/api/config/auth.ts +2 -2
  42. package/src/templates/source/api/config/database.ts +44 -10
  43. package/src/templates/source/api/index.auth.ts +10 -16
  44. package/src/templates/source/api/index.default.ts +10 -9
  45. package/src/templates/source/api/index.trpc.ts +9 -28
  46. package/src/templates/source/api/package.auth.json +7 -6
  47. package/src/templates/source/api/package.default.json +5 -4
  48. package/src/templates/source/api/prisma/schema.auth.prisma +3 -2
  49. package/src/templates/source/api/prisma/schema.default.prisma +3 -2
  50. package/src/templates/source/api/prisma.config.ts +7 -1
  51. package/src/templates/source/api/procedures/auth.ts +38 -66
  52. package/src/templates/source/api/procedures/health.ts +4 -9
  53. package/src/templates/source/api/procedures/users.auth.ts +7 -11
  54. package/src/templates/source/api/procedures/users.default.ts +7 -11
  55. package/src/templates/source/api/router.auth.ts +31 -0
  56. package/src/templates/source/api/router.default.ts +29 -0
  57. package/src/templates/source/api/router.trpc.ts +37 -0
  58. package/src/templates/source/api/router.types.auth.ts +88 -0
  59. package/src/templates/source/api/router.types.default.ts +73 -0
  60. package/src/templates/source/api/router.types.trpc.ts +73 -0
  61. package/src/templates/source/api/routes.auth.ts +66 -0
  62. package/src/templates/source/api/routes.default.ts +53 -0
  63. package/src/templates/source/api/schemas/auth.ts +79 -0
  64. package/src/templates/source/api/schemas/health.ts +21 -0
  65. package/src/templates/source/api/schemas/user.ts +62 -12
  66. package/src/templates/source/api/utils/auth.ts +157 -0
  67. package/src/templates/source/root/.cursorrules +187 -0
  68. package/src/templates/source/root/CLAUDE.auth.md +264 -0
  69. package/src/templates/source/root/CLAUDE.default.md +185 -0
  70. package/src/templates/source/root/package.json +7 -1
  71. package/src/templates/source/rsc/CLAUDE.md +104 -0
  72. package/src/templates/source/rsc/app/actions/posts.ts +93 -0
  73. package/src/templates/source/rsc/app/actions/users.ts +83 -0
  74. package/src/templates/source/rsc/app/layouts/dashboard.tsx +127 -0
  75. package/src/templates/source/rsc/app/layouts/marketing.tsx +25 -0
  76. package/src/templates/source/rsc/app/layouts/minimal.tsx +30 -0
  77. package/src/templates/source/rsc/app/layouts/root.tsx +241 -0
  78. package/src/templates/source/rsc/app/pages/(dashboard)/profile.tsx +71 -0
  79. package/src/templates/source/rsc/app/pages/(dashboard)/settings.tsx +104 -0
  80. package/src/templates/source/rsc/app/pages/(marketing)/about.tsx +52 -0
  81. package/src/templates/source/rsc/app/pages/_not-found.tsx +149 -0
  82. package/src/templates/source/rsc/app/pages/docs/[...slug].tsx +211 -0
  83. package/src/templates/source/rsc/app/pages/index.tsx +50 -0
  84. package/src/templates/source/rsc/app/pages/print.tsx +80 -0
  85. package/src/templates/source/rsc/app/pages/users/[id]/posts/[postId].tsx +89 -0
  86. package/src/templates/source/rsc/app/pages/users/[id]/posts/index.tsx +76 -0
  87. package/src/templates/source/rsc/app/pages/users/[id]/posts/new.tsx +79 -0
  88. package/src/templates/source/rsc/app/pages/users/[id].tsx +64 -0
  89. package/src/templates/source/rsc/app/pages/users/_layout.tsx +104 -0
  90. package/src/templates/source/rsc/app/pages/users.tsx +44 -0
  91. package/src/templates/source/rsc/app.config.ts +12 -0
  92. package/src/templates/source/rsc/env.example +6 -0
  93. package/src/templates/source/rsc/gitignore +34 -0
  94. package/src/templates/source/rsc/package.json +41 -0
  95. package/src/templates/source/rsc/prisma/schema.prisma +34 -0
  96. package/src/templates/source/rsc/prisma.config.ts +22 -0
  97. package/src/templates/source/rsc/public/favicon.svg +4 -0
  98. package/src/templates/source/rsc/src/api/database.ts +72 -0
  99. package/src/templates/source/rsc/src/api/handler.ts +53 -0
  100. package/src/templates/source/rsc/src/api/procedures/health.ts +48 -0
  101. package/src/templates/source/rsc/src/api/procedures/posts.ts +151 -0
  102. package/src/templates/source/rsc/src/api/procedures/users.ts +87 -0
  103. package/src/templates/source/rsc/src/api/schemas/post.ts +53 -0
  104. package/src/templates/source/rsc/src/api/schemas/user.ts +38 -0
  105. package/src/templates/source/rsc/src/entry.client.tsx +28 -0
  106. package/src/templates/source/rsc/src/entry.server.tsx +304 -0
  107. package/src/templates/source/rsc/tsconfig.json +24 -0
  108. package/src/templates/source/web/App.module.css +1 -1
  109. package/src/templates/source/web/api.ts +8 -1
  110. package/src/templates/source/web/main.tsx +4 -4
  111. package/src/templates/source/web/package.json +6 -6
  112. package/src/templates/source/web/routes/__root.tsx +2 -2
  113. package/src/templates/source/web/routes/index.auth.tsx +3 -0
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Auth Utilities
3
+ *
4
+ * Shared utilities for authentication that don't require database access.
5
+ * These are safe to import from procedures without pulling in server-only code.
6
+ */
7
+
8
+ // ============================================================================
9
+ // Role Parsing
10
+ // ============================================================================
11
+
12
+ const ALLOWED_ROLES = ['user', 'admin', 'moderator', 'editor'] as const;
13
+
14
+ export function parseUserRoles(rolesJson: string | null): string[] {
15
+ if (!rolesJson) return ['user'];
16
+
17
+ try {
18
+ const parsed: unknown = JSON.parse(rolesJson);
19
+
20
+ if (!Array.isArray(parsed)) {
21
+ return ['user'];
22
+ }
23
+
24
+ const validRoles = parsed
25
+ .filter((role): role is string => typeof role === 'string')
26
+ .filter((role) => ALLOWED_ROLES.includes(role as (typeof ALLOWED_ROLES)[number]));
27
+
28
+ return validRoles.length > 0 ? validRoles : ['user'];
29
+ } catch {
30
+ return ['user'];
31
+ }
32
+ }
33
+
34
+ // ============================================================================
35
+ // Token Revocation Store
36
+ // ============================================================================
37
+
38
+ /**
39
+ * In-memory token revocation store.
40
+ *
41
+ * PRODUCTION NOTE: Replace with Redis or database-backed store for:
42
+ * - Persistence across server restarts
43
+ * - Horizontal scaling (multiple server instances)
44
+ */
45
+ class InMemoryTokenStore {
46
+ private revokedTokens: Map<string, number> = new Map();
47
+ private usedRefreshTokens: Map<string, string> = new Map();
48
+ private cleanupInterval: NodeJS.Timeout | null = null;
49
+
50
+ constructor() {
51
+ this.cleanupInterval = setInterval(() => this.cleanup(), 5 * 60 * 1000);
52
+ }
53
+
54
+ revoke(jti: string, expiresInMs: number = 7 * 24 * 60 * 60 * 1000): void {
55
+ this.revokedTokens.set(jti, Date.now() + expiresInMs);
56
+ }
57
+
58
+ isRevoked(jti: string): boolean {
59
+ const expiry = this.revokedTokens.get(jti);
60
+ if (!expiry) return false;
61
+ if (Date.now() > expiry) {
62
+ this.revokedTokens.delete(jti);
63
+ return false;
64
+ }
65
+ return true;
66
+ }
67
+
68
+ markRefreshTokenUsed(jti: string, userId: string): void {
69
+ this.usedRefreshTokens.set(jti, userId);
70
+ setTimeout(() => this.usedRefreshTokens.delete(jti), 7 * 24 * 60 * 60 * 1000);
71
+ }
72
+
73
+ isRefreshTokenUsed(jti: string): string | undefined {
74
+ return this.usedRefreshTokens.get(jti);
75
+ }
76
+
77
+ revokeAllUserTokens(userId: string): void {
78
+ console.warn(
79
+ `[Security] Token reuse detected for user ${userId}. ` +
80
+ 'All tokens should be revoked. Implement proper user->token mapping for production.'
81
+ );
82
+ }
83
+
84
+ private cleanup(): void {
85
+ const now = Date.now();
86
+ for (const [jti, expiry] of this.revokedTokens.entries()) {
87
+ if (now > expiry) {
88
+ this.revokedTokens.delete(jti);
89
+ }
90
+ }
91
+ }
92
+
93
+ destroy(): void {
94
+ if (this.cleanupInterval) {
95
+ clearInterval(this.cleanupInterval);
96
+ this.cleanupInterval = null;
97
+ }
98
+ }
99
+ }
100
+
101
+ export const tokenStore = new InMemoryTokenStore();
102
+
103
+ // ============================================================================
104
+ // JWT Configuration Helper
105
+ // ============================================================================
106
+
107
+ /**
108
+ * Gets required JWT secrets from environment variables.
109
+ * Throws a clear error in production if secrets are not configured.
110
+ */
111
+ export function getJwtSecrets(): { jwtSecret: string; refreshSecret: string } {
112
+ const jwtSecret = process.env.JWT_SECRET;
113
+ const refreshSecret = process.env.JWT_REFRESH_SECRET;
114
+
115
+ const isDevelopment = process.env.NODE_ENV !== 'production';
116
+
117
+ if (!jwtSecret || !refreshSecret) {
118
+ if (isDevelopment) {
119
+ console.warn(
120
+ '\n' +
121
+ '='.repeat(70) +
122
+ '\n' +
123
+ ' WARNING: JWT secrets not configured!\n' +
124
+ ' Using temporary development secrets. DO NOT USE IN PRODUCTION!\n' +
125
+ '\n' +
126
+ ' To configure secrets, add to .env:\n' +
127
+ ' JWT_SECRET=<generate with: openssl rand -base64 64>\n' +
128
+ ' JWT_REFRESH_SECRET=<generate with: openssl rand -base64 64>\n' +
129
+ '='.repeat(70) +
130
+ '\n'
131
+ );
132
+ return {
133
+ jwtSecret:
134
+ jwtSecret || `dev-only-jwt-secret-${Math.random().toString(36).substring(2).repeat(4)}`,
135
+ refreshSecret:
136
+ refreshSecret ||
137
+ `dev-only-refresh-secret-${Math.random().toString(36).substring(2).repeat(4)}`,
138
+ };
139
+ }
140
+
141
+ throw new Error(
142
+ '\n' +
143
+ 'CRITICAL: JWT secrets are required but not configured.\n' +
144
+ '\n' +
145
+ 'Required environment variables:\n' +
146
+ ' - JWT_SECRET: Secret for signing access tokens (64+ characters)\n' +
147
+ ' - JWT_REFRESH_SECRET: Secret for signing refresh tokens (64+ characters)\n' +
148
+ '\n' +
149
+ 'Generate secure secrets with:\n' +
150
+ ' openssl rand -base64 64\n' +
151
+ '\n' +
152
+ 'Add them to your environment or .env file before starting the server.\n'
153
+ );
154
+ }
155
+
156
+ return { jwtSecret, refreshSecret };
157
+ }
@@ -0,0 +1,187 @@
1
+ # VeloxTS Project - Cursor AI Rules
2
+
3
+ This is a VeloxTS full-stack TypeScript application.
4
+
5
+ ## Project Type
6
+
7
+ - **Framework**: VeloxTS (Laravel-inspired TypeScript framework)
8
+ - **Backend**: Fastify + VeloxTS procedures (apps/api)
9
+ - **Frontend**: React + Vite + TanStack Router (apps/web)
10
+ - **Database**: Prisma with SQLite
11
+ - **Validation**: Zod schemas
12
+
13
+ ## Architecture Rules
14
+
15
+ ### Backend (apps/api)
16
+
17
+ Procedures in `src/procedures/` define API endpoints. Naming conventions determine HTTP methods:
18
+
19
+ - `get*`, `find*` → GET (single resource)
20
+ - `list*` → GET (collection)
21
+ - `create*`, `add*` → POST
22
+ - `update*`, `edit*` → PUT
23
+ - `patch*` → PATCH
24
+ - `delete*`, `remove*` → DELETE
25
+
26
+ Schemas in `src/schemas/` use Zod for validation.
27
+
28
+ Context available in procedures:
29
+ - `ctx.db` - Prisma client
30
+ - `ctx.request` - Fastify request
31
+ - `ctx.reply` - Fastify reply
32
+ /* @if auth */
33
+ - `ctx.user` - Authenticated user (if logged in)
34
+ /* @endif auth */
35
+
36
+ ### Frontend (apps/web)
37
+
38
+ Routes in `src/routes/` use TanStack Router file-based routing.
39
+ API calls use `@veloxts/client` hooks: `useQuery`, `useMutation`.
40
+ Types flow from backend automatically - no codegen needed.
41
+
42
+ ## Code Style
43
+
44
+ ### TypeScript Strictness
45
+
46
+ CRITICAL - Never violate these rules:
47
+ - NEVER use `any` type
48
+ - NEVER use `as any` assertions
49
+ - NEVER use `@ts-ignore` or `@ts-expect-error`
50
+ - Use `unknown` with type guards instead
51
+ - Use proper generics and type inference
52
+
53
+ ### Procedure Pattern
54
+
55
+ ```typescript
56
+ import { defineProcedures, procedure, z } from '@veloxts/velox';
57
+
58
+ export const entityProcedures = defineProcedures('entities', {
59
+ getEntity: procedure()
60
+ .input(z.object({ id: z.string().uuid() }))
61
+ .output(EntitySchema)
62
+ .query(async ({ input, ctx }) => {
63
+ return ctx.db.entity.findUnique({ where: { id: input.id } });
64
+ }),
65
+
66
+ createEntity: procedure()
67
+ .input(CreateEntitySchema)
68
+ .output(EntitySchema)
69
+ .mutation(async ({ input, ctx }) => {
70
+ return ctx.db.entity.create({ data: input });
71
+ }),
72
+ });
73
+ ```
74
+
75
+ ### Schema Pattern
76
+
77
+ ```typescript
78
+ import { z } from '@veloxts/velox';
79
+
80
+ export const EntitySchema = z.object({
81
+ id: z.string().uuid(),
82
+ name: z.string().min(1).max(255),
83
+ createdAt: z.date(),
84
+ });
85
+
86
+ export type Entity = z.infer<typeof EntitySchema>;
87
+
88
+ export const CreateEntitySchema = EntitySchema.omit({
89
+ id: true,
90
+ createdAt: true,
91
+ });
92
+
93
+ export type CreateEntity = z.infer<typeof CreateEntitySchema>;
94
+ ```
95
+
96
+ /* @if auth */
97
+ ### Protected Procedures
98
+
99
+ ```typescript
100
+ import { authenticated, hasRole } from '@veloxts/auth';
101
+
102
+ // Require authentication
103
+ const getProfile = procedure()
104
+ .guard(authenticated)
105
+ .query(({ ctx }) => ctx.user);
106
+
107
+ // Require role
108
+ const adminOnly = procedure()
109
+ .guard(hasRole('admin'))
110
+ .mutation(({ ctx, input }) => { /* ... */ });
111
+ ```
112
+ /* @endif auth */
113
+
114
+ ## CLI Commands
115
+
116
+ Common commands for development:
117
+
118
+ ```bash
119
+ # Development
120
+ __RUN_CMD__ dev # Start dev server with HMR
121
+ __RUN_CMD__ velox dev --verbose # With timing info
122
+
123
+ # Code generation
124
+ __RUN_CMD__ velox make resource Post --crud # Full CRUD resource
125
+ __RUN_CMD__ velox make procedure Users # Just procedure
126
+ __RUN_CMD__ velox make schema Post # Just schema
127
+
128
+ # Database
129
+ __RUN_CMD__ velox migrate run # Run migrations
130
+ __RUN_CMD__ velox migrate status # Check status
131
+ __RUN_CMD__ velox db seed # Run seeders
132
+ __RUN_CMD__ db:push # Push schema (Prisma)
133
+ __RUN_CMD__ db:studio # Open Prisma Studio
134
+ ```
135
+
136
+ ## File Naming Conventions
137
+
138
+ - Procedures: `src/procedures/{entity}.ts` (plural, kebab-case for multi-word)
139
+ - Schemas: `src/schemas/{entity}.ts` (singular, kebab-case)
140
+ - Routes: `src/routes/{path}.tsx`
141
+ - Components: `src/components/{Name}.tsx` (PascalCase)
142
+
143
+ ## Import Conventions
144
+
145
+ ```typescript
146
+ // VeloxTS imports (use umbrella package)
147
+ import { defineProcedures, procedure, z } from '@veloxts/velox';
148
+ /* @if auth */
149
+ import { authenticated, hasRole } from '@veloxts/auth';
150
+ /* @endif auth */
151
+
152
+ // Database client from config
153
+ import { db } from '../config/database';
154
+
155
+ // Local schemas
156
+ import { UserSchema, CreateUserSchema } from '../schemas/user';
157
+ ```
158
+
159
+ ## Error Handling
160
+
161
+ Use structured errors with codes:
162
+
163
+ ```typescript
164
+ import { VeloxError } from '@veloxts/core';
165
+
166
+ // Not found
167
+ throw VeloxError.notFound('User', userId);
168
+
169
+ // Validation
170
+ throw VeloxError.validation('Invalid email format');
171
+
172
+ // Unauthorized
173
+ throw VeloxError.unauthorized('Login required');
174
+
175
+ // Forbidden
176
+ throw VeloxError.forbidden('Cannot access this resource');
177
+ ```
178
+
179
+ ## JSON Output
180
+
181
+ All CLI commands support `--json` for AI tooling:
182
+
183
+ ```bash
184
+ velox migrate status --json
185
+ velox make resource Post --json --dry-run
186
+ velox procedures list --json
187
+ ```
@@ -136,6 +136,85 @@ JWT_REFRESH_SECRET=<64+ chars> # Generate: openssl rand -base64 64
136
136
  | `patchUser` | PATCH | `/users/:id` |
137
137
  | `deleteUser` | DELETE | `/users/:id` |
138
138
 
139
+ ## Guards and Policies
140
+
141
+ ### Using Guards
142
+
143
+ Guards protect procedures from unauthorized access:
144
+
145
+ ```typescript
146
+ import { authenticated, hasRole, hasPermission, allOf, anyOf } from '@veloxts/auth';
147
+
148
+ // Require authentication
149
+ const getProfile = procedure()
150
+ .guard(authenticated)
151
+ .query(({ ctx }) => ctx.user);
152
+
153
+ // Require specific role
154
+ const adminDashboard = procedure()
155
+ .guard(hasRole('admin'))
156
+ .query(({ ctx }) => { /* ... */ });
157
+
158
+ // Require permission
159
+ const deletePost = procedure()
160
+ .guard(hasPermission('posts.delete'))
161
+ .mutation(({ ctx, input }) => { /* ... */ });
162
+
163
+ // Combine guards (AND logic)
164
+ const adminWithPermission = procedure()
165
+ .guard(allOf([hasRole('admin'), hasPermission('users.manage')]))
166
+ .mutation(({ ctx, input }) => { /* ... */ });
167
+
168
+ // Any of guards (OR logic)
169
+ const moderatorOrAdmin = procedure()
170
+ .guard(anyOf([hasRole('admin'), hasRole('moderator')]))
171
+ .mutation(({ ctx, input }) => { /* ... */ });
172
+ ```
173
+
174
+ ### Available Guards
175
+
176
+ | Guard | Description |
177
+ |-------|-------------|
178
+ | `authenticated` | Requires logged-in user |
179
+ | `emailVerified` | Requires verified email |
180
+ | `hasRole(role)` | Checks user role |
181
+ | `hasPermission(perm)` | Checks user permission |
182
+ | `hasAnyPermission(perms)` | Any permission matches |
183
+ | `allOf(guards)` | All guards must pass |
184
+ | `anyOf(guards)` | Any guard must pass |
185
+ | `not(guard)` | Inverts guard result |
186
+
187
+ ### Resource Policies
188
+
189
+ Define authorization rules for resources:
190
+
191
+ ```typescript
192
+ import { definePolicy, registerPolicy, authorize } from '@veloxts/auth';
193
+
194
+ // Define policy for Post resource
195
+ const PostPolicy = definePolicy<User, Post>({
196
+ view: () => true,
197
+ create: (user) => user.emailVerified,
198
+ update: (user, post) => user.id === post.authorId,
199
+ delete: (user, post) => user.id === post.authorId || user.role === 'admin',
200
+ });
201
+
202
+ // Register the policy
203
+ registerPolicy('Post', PostPolicy);
204
+
205
+ // Use in procedures
206
+ const deletePost = procedure()
207
+ .guard(authenticated)
208
+ .mutation(async ({ ctx, input }) => {
209
+ const post = await ctx.db.post.findUnique({ where: { id: input.id } });
210
+
211
+ // Throws 403 if unauthorized
212
+ await authorize(ctx.user, 'delete', 'Post', post);
213
+
214
+ return ctx.db.post.delete({ where: { id: input.id } });
215
+ });
216
+ ```
217
+
139
218
  ## Database
140
219
 
141
220
  After schema changes:
@@ -146,3 +225,188 @@ __RUN_CMD__ db:generate # Regenerate client
146
225
  ```
147
226
 
148
227
  Access via context: `ctx.db.user.findMany()`
228
+
229
+ ## Code Generation
230
+
231
+ ### Available Generators
232
+
233
+ | Generator | Alias | Description |
234
+ |-----------|-------|-------------|
235
+ | `procedure` | `p` | API procedure with queries/mutations |
236
+ | `schema` | `s` | Zod validation schema |
237
+ | `model` | `m` | Prisma model definition |
238
+ | `migration` | `mig` | Database migration file |
239
+ | `test` | `t` | Unit/integration test file |
240
+ | `resource` | `r` | Complete CRUD resource (all above) |
241
+ | `seeder` | `seed` | Database seeder |
242
+ | `factory` | `f` | Test data factory |
243
+
244
+ ### Usage Examples
245
+
246
+ ```bash
247
+ # Generate a complete CRUD resource
248
+ __RUN_CMD__ velox make resource Post --crud
249
+
250
+ # Generate just a procedure
251
+ __RUN_CMD__ velox make procedure Users --crud
252
+
253
+ # Generate with soft-delete support
254
+ __RUN_CMD__ velox m r Comment --soft-delete
255
+
256
+ # Preview without writing files
257
+ __RUN_CMD__ velox make --dry-run resource Post
258
+
259
+ # JSON output for scripting
260
+ __RUN_CMD__ velox make resource Post --json
261
+ ```
262
+
263
+ ### Generator Options
264
+
265
+ **Common Options:**
266
+ - `--dry-run, -d` - Preview changes without writing
267
+ - `--force, -f` - Overwrite existing files
268
+ - `--json` - Output results as JSON
269
+
270
+ **Resource/Procedure Options:**
271
+ - `--crud, -c` - Generate full CRUD operations
272
+ - `--paginated, -P` - Include pagination for list
273
+ - `--soft-delete, -s` - Add soft delete support
274
+ - `--timestamps, -t` - Include timestamps (default: true)
275
+
276
+ ## Migration Runner
277
+
278
+ ### Commands
279
+
280
+ | Command | Description |
281
+ |---------|-------------|
282
+ | `velox migrate status` | Show migration status |
283
+ | `velox migrate run` | Run pending migrations |
284
+ | `velox migrate rollback` | Rollback last migration |
285
+ | `velox migrate fresh` | Drop all tables and re-run |
286
+ | `velox migrate reset` | Rollback all then re-run |
287
+
288
+ ### Usage
289
+
290
+ ```bash
291
+ # Check status
292
+ __RUN_CMD__ velox migrate status
293
+
294
+ # Run pending migrations
295
+ __RUN_CMD__ velox migrate run
296
+
297
+ # Development mode (creates migration from schema diff)
298
+ __RUN_CMD__ velox migrate run --dev
299
+
300
+ # Rollback last migration
301
+ __RUN_CMD__ velox migrate rollback
302
+
303
+ # Fresh database
304
+ __RUN_CMD__ velox migrate fresh
305
+
306
+ # JSON output
307
+ __RUN_CMD__ velox migrate status --json
308
+ ```
309
+
310
+ ## Database Seeding
311
+
312
+ ### Commands
313
+
314
+ ```bash
315
+ # Run all seeders
316
+ __RUN_CMD__ velox db seed
317
+
318
+ # Run specific seeder
319
+ __RUN_CMD__ velox db seed UserSeeder
320
+
321
+ # Fresh seed (truncate first)
322
+ __RUN_CMD__ velox db seed --fresh
323
+
324
+ # Preview
325
+ __RUN_CMD__ velox db seed --dry-run
326
+ ```
327
+
328
+ ### Seeder Example
329
+
330
+ ```typescript
331
+ // apps/api/src/database/seeders/UserSeeder.ts
332
+ import type { Seeder } from '@veloxts/cli';
333
+
334
+ export const UserSeeder: Seeder = {
335
+ name: 'UserSeeder',
336
+ dependencies: [],
337
+
338
+ async run(db) {
339
+ await db.user.createMany({
340
+ data: [
341
+ { email: 'admin@example.com', name: 'Admin' },
342
+ { email: 'user@example.com', name: 'User' },
343
+ ],
344
+ });
345
+ },
346
+ };
347
+ ```
348
+
349
+ ## Error Handling
350
+
351
+ VeloxTS uses structured error codes for AI tooling:
352
+
353
+ ```typescript
354
+ // Error format: VeloxError[E1001]: Message
355
+ // E1xxx - Core errors
356
+ // E2xxx - Generator errors
357
+ // E3xxx - Seeding errors
358
+ // E4xxx - Migration errors
359
+ // E5xxx - Dev server errors
360
+ ```
361
+
362
+ ### Common Patterns
363
+
364
+ ```typescript
365
+ import { VeloxError } from '@veloxts/core';
366
+
367
+ const getUser = procedure()
368
+ .input(z.object({ id: z.string().uuid() }))
369
+ .query(async ({ ctx, input }) => {
370
+ const user = await ctx.db.user.findUnique({ where: { id: input.id } });
371
+
372
+ if (!user) {
373
+ throw VeloxError.notFound('User', input.id);
374
+ }
375
+
376
+ return user;
377
+ });
378
+ ```
379
+
380
+ ## Development Workflow
381
+
382
+ ### Hot Module Replacement (HMR)
383
+
384
+ ```bash
385
+ # Default: HMR enabled
386
+ __RUN_CMD__ velox dev
387
+
388
+ # With verbose timing
389
+ __RUN_CMD__ velox dev --verbose
390
+
391
+ # Disable HMR
392
+ __RUN_CMD__ velox dev --no-hmr
393
+ ```
394
+
395
+ ### CLI JSON Output
396
+
397
+ All CLI commands support `--json` for scripting:
398
+
399
+ ```bash
400
+ velox migrate status --json
401
+ velox db seed --json --dry-run
402
+ velox procedures list --json
403
+ ```
404
+
405
+ ### Recommended Flow
406
+
407
+ 1. Define Zod schemas in `apps/api/src/schemas/`
408
+ 2. Generate resource: `velox make resource Post --crud`
409
+ 3. Customize generated procedures as needed
410
+ 4. Run migrations: `velox migrate run --dev`
411
+ 5. Seed data: `velox db seed --fresh`
412
+ 6. Test endpoints with Thunder Client or curl