create-velox-app 0.4.14 → 0.6.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 +2 -43
- package/dist/cli.js +23 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.js +26 -8
- package/dist/index.js.map +1 -1
- package/dist/templates/auth.d.ts.map +1 -1
- package/dist/templates/auth.js +24 -0
- package/dist/templates/auth.js.map +1 -1
- package/dist/templates/fullstack.d.ts +15 -0
- package/dist/templates/fullstack.d.ts.map +1 -0
- package/dist/templates/fullstack.js +110 -0
- package/dist/templates/fullstack.js.map +1 -0
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +27 -5
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/placeholders.d.ts +6 -1
- package/dist/templates/placeholders.d.ts.map +1 -1
- package/dist/templates/placeholders.js +15 -5
- package/dist/templates/placeholders.js.map +1 -1
- package/dist/templates/rsc.d.ts +15 -0
- package/dist/templates/rsc.d.ts.map +1 -0
- package/dist/templates/rsc.js +192 -0
- package/dist/templates/rsc.js.map +1 -0
- package/dist/templates/shared/root.d.ts +1 -0
- package/dist/templates/shared/root.d.ts.map +1 -1
- package/dist/templates/shared/root.js +4 -0
- package/dist/templates/shared/root.js.map +1 -1
- package/dist/templates/spa.d.ts +12 -0
- package/dist/templates/spa.d.ts.map +1 -0
- package/dist/templates/spa.js +101 -0
- package/dist/templates/spa.js.map +1 -0
- package/dist/templates/trpc.d.ts.map +1 -1
- package/dist/templates/trpc.js +16 -0
- package/dist/templates/trpc.js.map +1 -1
- package/dist/templates/types.d.ts +14 -1
- package/dist/templates/types.d.ts.map +1 -1
- package/dist/templates/types.js +35 -10
- package/dist/templates/types.js.map +1 -1
- package/package.json +3 -3
- package/src/templates/source/api/config/auth.ts +2 -2
- package/src/templates/source/api/config/database.ts +44 -10
- package/src/templates/source/api/index.auth.ts +10 -16
- package/src/templates/source/api/index.default.ts +10 -9
- package/src/templates/source/api/index.trpc.ts +9 -28
- package/src/templates/source/api/package.auth.json +7 -6
- package/src/templates/source/api/package.default.json +5 -4
- package/src/templates/source/api/prisma/schema.auth.prisma +3 -2
- package/src/templates/source/api/prisma/schema.default.prisma +3 -2
- package/src/templates/source/api/prisma.config.ts +7 -1
- package/src/templates/source/api/procedures/auth.ts +38 -66
- package/src/templates/source/api/procedures/health.ts +4 -9
- package/src/templates/source/api/procedures/users.auth.ts +7 -11
- package/src/templates/source/api/procedures/users.default.ts +7 -11
- package/src/templates/source/api/router.auth.ts +31 -0
- package/src/templates/source/api/router.default.ts +29 -0
- package/src/templates/source/api/router.trpc.ts +37 -0
- package/src/templates/source/api/router.types.auth.ts +88 -0
- package/src/templates/source/api/router.types.default.ts +73 -0
- package/src/templates/source/api/router.types.trpc.ts +73 -0
- package/src/templates/source/api/routes.auth.ts +66 -0
- package/src/templates/source/api/routes.default.ts +53 -0
- package/src/templates/source/api/schemas/auth.ts +79 -0
- package/src/templates/source/api/schemas/health.ts +21 -0
- package/src/templates/source/api/schemas/user.ts +62 -12
- package/src/templates/source/api/utils/auth.ts +157 -0
- package/src/templates/source/root/.cursorrules +187 -0
- package/src/templates/source/root/CLAUDE.auth.md +264 -0
- package/src/templates/source/root/CLAUDE.default.md +185 -0
- package/src/templates/source/root/package.json +7 -1
- package/src/templates/source/rsc/CLAUDE.md +104 -0
- package/src/templates/source/rsc/app/actions/posts.ts +93 -0
- package/src/templates/source/rsc/app/actions/users.ts +83 -0
- package/src/templates/source/rsc/app/layouts/dashboard.tsx +127 -0
- package/src/templates/source/rsc/app/layouts/marketing.tsx +25 -0
- package/src/templates/source/rsc/app/layouts/minimal.tsx +30 -0
- package/src/templates/source/rsc/app/layouts/root.tsx +241 -0
- package/src/templates/source/rsc/app/pages/(dashboard)/profile.tsx +71 -0
- package/src/templates/source/rsc/app/pages/(dashboard)/settings.tsx +104 -0
- package/src/templates/source/rsc/app/pages/(marketing)/about.tsx +52 -0
- package/src/templates/source/rsc/app/pages/_not-found.tsx +149 -0
- package/src/templates/source/rsc/app/pages/docs/[...slug].tsx +211 -0
- package/src/templates/source/rsc/app/pages/index.tsx +50 -0
- package/src/templates/source/rsc/app/pages/print.tsx +80 -0
- package/src/templates/source/rsc/app/pages/users/[id]/posts/[postId].tsx +89 -0
- package/src/templates/source/rsc/app/pages/users/[id]/posts/index.tsx +76 -0
- package/src/templates/source/rsc/app/pages/users/[id]/posts/new.tsx +79 -0
- package/src/templates/source/rsc/app/pages/users/[id].tsx +64 -0
- package/src/templates/source/rsc/app/pages/users/_layout.tsx +104 -0
- package/src/templates/source/rsc/app/pages/users.tsx +44 -0
- package/src/templates/source/rsc/app.config.ts +12 -0
- package/src/templates/source/rsc/env.example +6 -0
- package/src/templates/source/rsc/gitignore +34 -0
- package/src/templates/source/rsc/package.json +41 -0
- package/src/templates/source/rsc/prisma/schema.prisma +34 -0
- package/src/templates/source/rsc/prisma.config.ts +22 -0
- package/src/templates/source/rsc/public/favicon.svg +4 -0
- package/src/templates/source/rsc/src/api/database.ts +72 -0
- package/src/templates/source/rsc/src/api/handler.ts +53 -0
- package/src/templates/source/rsc/src/api/procedures/health.ts +48 -0
- package/src/templates/source/rsc/src/api/procedures/posts.ts +151 -0
- package/src/templates/source/rsc/src/api/procedures/users.ts +87 -0
- package/src/templates/source/rsc/src/api/schemas/post.ts +53 -0
- package/src/templates/source/rsc/src/api/schemas/user.ts +38 -0
- package/src/templates/source/rsc/src/entry.client.tsx +28 -0
- package/src/templates/source/rsc/src/entry.server.tsx +304 -0
- package/src/templates/source/rsc/tsconfig.json +24 -0
- package/src/templates/source/web/App.module.css +1 -1
- package/src/templates/source/web/api.ts +8 -1
- package/src/templates/source/web/main.tsx +4 -4
- package/src/templates/source/web/package.json +6 -6
- package/src/templates/source/web/routes/__root.tsx +2 -2
- package/src/templates/source/web/routes/index.auth.tsx +3 -0
|
@@ -126,3 +126,188 @@ __RUN_CMD__ db:generate # Regenerate client
|
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
Access via context: `ctx.db.user.findMany()`
|
|
129
|
+
|
|
130
|
+
## Code Generation
|
|
131
|
+
|
|
132
|
+
### Available Generators
|
|
133
|
+
|
|
134
|
+
| Generator | Alias | Description |
|
|
135
|
+
|-----------|-------|-------------|
|
|
136
|
+
| `procedure` | `p` | API procedure with queries/mutations |
|
|
137
|
+
| `schema` | `s` | Zod validation schema |
|
|
138
|
+
| `model` | `m` | Prisma model definition |
|
|
139
|
+
| `migration` | `mig` | Database migration file |
|
|
140
|
+
| `test` | `t` | Unit/integration test file |
|
|
141
|
+
| `resource` | `r` | Complete CRUD resource (all above) |
|
|
142
|
+
| `seeder` | `seed` | Database seeder |
|
|
143
|
+
| `factory` | `f` | Test data factory |
|
|
144
|
+
|
|
145
|
+
### Usage Examples
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Generate a complete CRUD resource
|
|
149
|
+
__RUN_CMD__ velox make resource Post --crud
|
|
150
|
+
|
|
151
|
+
# Generate just a procedure
|
|
152
|
+
__RUN_CMD__ velox make procedure Users --crud
|
|
153
|
+
|
|
154
|
+
# Generate with soft-delete support
|
|
155
|
+
__RUN_CMD__ velox m r Comment --soft-delete
|
|
156
|
+
|
|
157
|
+
# Preview without writing files
|
|
158
|
+
__RUN_CMD__ velox make --dry-run resource Post
|
|
159
|
+
|
|
160
|
+
# JSON output for scripting
|
|
161
|
+
__RUN_CMD__ velox make resource Post --json
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Generator Options
|
|
165
|
+
|
|
166
|
+
**Common Options:**
|
|
167
|
+
- `--dry-run, -d` - Preview changes without writing
|
|
168
|
+
- `--force, -f` - Overwrite existing files
|
|
169
|
+
- `--json` - Output results as JSON
|
|
170
|
+
|
|
171
|
+
**Resource/Procedure Options:**
|
|
172
|
+
- `--crud, -c` - Generate full CRUD operations
|
|
173
|
+
- `--paginated, -P` - Include pagination for list
|
|
174
|
+
- `--soft-delete, -s` - Add soft delete support
|
|
175
|
+
- `--timestamps, -t` - Include timestamps (default: true)
|
|
176
|
+
|
|
177
|
+
## Migration Runner
|
|
178
|
+
|
|
179
|
+
### Commands
|
|
180
|
+
|
|
181
|
+
| Command | Description |
|
|
182
|
+
|---------|-------------|
|
|
183
|
+
| `velox migrate status` | Show migration status |
|
|
184
|
+
| `velox migrate run` | Run pending migrations |
|
|
185
|
+
| `velox migrate rollback` | Rollback last migration |
|
|
186
|
+
| `velox migrate fresh` | Drop all tables and re-run |
|
|
187
|
+
| `velox migrate reset` | Rollback all then re-run |
|
|
188
|
+
|
|
189
|
+
### Usage
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Check status
|
|
193
|
+
__RUN_CMD__ velox migrate status
|
|
194
|
+
|
|
195
|
+
# Run pending migrations
|
|
196
|
+
__RUN_CMD__ velox migrate run
|
|
197
|
+
|
|
198
|
+
# Development mode (creates migration from schema diff)
|
|
199
|
+
__RUN_CMD__ velox migrate run --dev
|
|
200
|
+
|
|
201
|
+
# Rollback last migration
|
|
202
|
+
__RUN_CMD__ velox migrate rollback
|
|
203
|
+
|
|
204
|
+
# Fresh database
|
|
205
|
+
__RUN_CMD__ velox migrate fresh
|
|
206
|
+
|
|
207
|
+
# JSON output
|
|
208
|
+
__RUN_CMD__ velox migrate status --json
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Database Seeding
|
|
212
|
+
|
|
213
|
+
### Commands
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Run all seeders
|
|
217
|
+
__RUN_CMD__ velox db seed
|
|
218
|
+
|
|
219
|
+
# Run specific seeder
|
|
220
|
+
__RUN_CMD__ velox db seed UserSeeder
|
|
221
|
+
|
|
222
|
+
# Fresh seed (truncate first)
|
|
223
|
+
__RUN_CMD__ velox db seed --fresh
|
|
224
|
+
|
|
225
|
+
# Preview
|
|
226
|
+
__RUN_CMD__ velox db seed --dry-run
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Seeder Example
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// apps/api/src/database/seeders/UserSeeder.ts
|
|
233
|
+
import type { Seeder } from '@veloxts/cli';
|
|
234
|
+
|
|
235
|
+
export const UserSeeder: Seeder = {
|
|
236
|
+
name: 'UserSeeder',
|
|
237
|
+
dependencies: [],
|
|
238
|
+
|
|
239
|
+
async run(db) {
|
|
240
|
+
await db.user.createMany({
|
|
241
|
+
data: [
|
|
242
|
+
{ email: 'admin@example.com', name: 'Admin' },
|
|
243
|
+
{ email: 'user@example.com', name: 'User' },
|
|
244
|
+
],
|
|
245
|
+
});
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Error Handling
|
|
251
|
+
|
|
252
|
+
VeloxTS uses structured error codes for AI tooling:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// Error format: VeloxError[E1001]: Message
|
|
256
|
+
// E1xxx - Core errors
|
|
257
|
+
// E2xxx - Generator errors
|
|
258
|
+
// E3xxx - Seeding errors
|
|
259
|
+
// E4xxx - Migration errors
|
|
260
|
+
// E5xxx - Dev server errors
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Common Patterns
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import { VeloxError } from '@veloxts/core';
|
|
267
|
+
|
|
268
|
+
const getUser = procedure()
|
|
269
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
270
|
+
.query(async ({ ctx, input }) => {
|
|
271
|
+
const user = await ctx.db.user.findUnique({ where: { id: input.id } });
|
|
272
|
+
|
|
273
|
+
if (!user) {
|
|
274
|
+
throw VeloxError.notFound('User', input.id);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return user;
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Development Workflow
|
|
282
|
+
|
|
283
|
+
### Hot Module Replacement (HMR)
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
# Default: HMR enabled
|
|
287
|
+
__RUN_CMD__ velox dev
|
|
288
|
+
|
|
289
|
+
# With verbose timing
|
|
290
|
+
__RUN_CMD__ velox dev --verbose
|
|
291
|
+
|
|
292
|
+
# Disable HMR
|
|
293
|
+
__RUN_CMD__ velox dev --no-hmr
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### CLI JSON Output
|
|
297
|
+
|
|
298
|
+
All CLI commands support `--json` for scripting:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
velox migrate status --json
|
|
302
|
+
velox db seed --json --dry-run
|
|
303
|
+
velox procedures list --json
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Recommended Flow
|
|
307
|
+
|
|
308
|
+
1. Define Zod schemas in `apps/api/src/schemas/`
|
|
309
|
+
2. Generate resource: `velox make resource Post --crud`
|
|
310
|
+
3. Customize generated procedures as needed
|
|
311
|
+
4. Run migrations: `velox migrate run --dev`
|
|
312
|
+
5. Seed data: `velox db seed --fresh`
|
|
313
|
+
6. Test endpoints with Thunder Client or curl
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
"version": "0.0.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
|
+
"workspaces": [
|
|
7
|
+
"apps/*"
|
|
8
|
+
],
|
|
6
9
|
"scripts": {
|
|
7
10
|
"dev": "__RUN_CMD__ --parallel -r dev",
|
|
8
11
|
"build": "__RUN_CMD__ -r build",
|
|
@@ -11,8 +14,11 @@
|
|
|
11
14
|
"db:generate": "__RUN_CMD__ -F api db:generate",
|
|
12
15
|
"db:studio": "__RUN_CMD__ -F api db:studio"
|
|
13
16
|
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@prisma/client-runtime-utils": "7.2.0"
|
|
19
|
+
},
|
|
14
20
|
"devDependencies": {
|
|
15
|
-
"@types/node": "25.0.
|
|
21
|
+
"@types/node": "25.0.3",
|
|
16
22
|
"typescript": "5.9.3"
|
|
17
23
|
}
|
|
18
24
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This is a VeloxTS full-stack application using React Server Components.
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
__PROJECT_NAME__/
|
|
9
|
+
├── app/ # Application layer (RSC)
|
|
10
|
+
│ ├── pages/ # File-based routing (RSC pages)
|
|
11
|
+
│ │ ├── index.tsx # Home page (/)
|
|
12
|
+
│ │ └── users.tsx # Users page (/users)
|
|
13
|
+
│ ├── layouts/ # Layout components
|
|
14
|
+
│ │ └── root.tsx # Root layout (wraps all pages)
|
|
15
|
+
│ └── actions/ # Server actions
|
|
16
|
+
│ ├── users.ts # User-related actions
|
|
17
|
+
│ └── posts.ts # Post actions (procedure bridge)
|
|
18
|
+
├── src/ # Source layer
|
|
19
|
+
│ ├── api/ # API layer (Fastify embedded in Vinxi)
|
|
20
|
+
│ │ ├── handler.ts # API handler for /api/* routes
|
|
21
|
+
│ │ ├── database.ts # Prisma client
|
|
22
|
+
│ │ ├── procedures/ # API procedure definitions
|
|
23
|
+
│ │ └── schemas/ # Zod validation schemas
|
|
24
|
+
│ ├── entry.client.tsx # Client hydration entry
|
|
25
|
+
│ └── entry.server.tsx # Server rendering entry
|
|
26
|
+
├── prisma/
|
|
27
|
+
│ └── schema.prisma # Database schema
|
|
28
|
+
├── app.config.ts # Vinxi configuration
|
|
29
|
+
└── package.json
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Key Concepts
|
|
33
|
+
|
|
34
|
+
### React Server Components (RSC)
|
|
35
|
+
- Pages in `app/pages/` are Server Components by default
|
|
36
|
+
- They run on the server and can directly access the database
|
|
37
|
+
- Use `'use client'` directive for client-side interactivity
|
|
38
|
+
|
|
39
|
+
### File-Based Routing
|
|
40
|
+
- `app/pages/index.tsx` → `/`
|
|
41
|
+
- `app/pages/users.tsx` → `/users`
|
|
42
|
+
- `app/pages/users/[id].tsx` → `/users/:id` (dynamic route)
|
|
43
|
+
- `app/pages/[...slug].tsx` → catch-all route
|
|
44
|
+
|
|
45
|
+
### Server Actions
|
|
46
|
+
- Defined in `app/actions/` with `'use server'` directive
|
|
47
|
+
- Type-safe with Zod validation via `createAction()`
|
|
48
|
+
- Can be called directly from client components
|
|
49
|
+
|
|
50
|
+
### Procedure Bridge Pattern
|
|
51
|
+
Server actions can bridge to API procedures for code reuse:
|
|
52
|
+
```typescript
|
|
53
|
+
// app/actions/posts.ts
|
|
54
|
+
'use server';
|
|
55
|
+
import { action } from '@veloxts/web';
|
|
56
|
+
import { postProcedures } from '@/api/procedures/posts';
|
|
57
|
+
|
|
58
|
+
export const createPost = action.fromProcedure(
|
|
59
|
+
postProcedures.procedures.createPost,
|
|
60
|
+
{ parseFormData: true } // Auto-parse FormData
|
|
61
|
+
);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This pattern:
|
|
65
|
+
- Reuses procedure validation, guards, and business logic
|
|
66
|
+
- Type safety flows from procedure → action → form
|
|
67
|
+
- Works with HTML forms for progressive enhancement
|
|
68
|
+
|
|
69
|
+
### API Routes
|
|
70
|
+
- All `/api/*` routes are handled by embedded Fastify
|
|
71
|
+
- Procedures defined in `src/api/procedures/`
|
|
72
|
+
- Full VeloxTS type safety and REST conventions
|
|
73
|
+
|
|
74
|
+
## Commands
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Development
|
|
78
|
+
__RUN_CMD__ dev # Start dev server with HMR
|
|
79
|
+
|
|
80
|
+
# Database
|
|
81
|
+
__RUN_CMD__ db:generate # Generate Prisma client
|
|
82
|
+
__RUN_CMD__ db:push # Push schema to database
|
|
83
|
+
__RUN_CMD__ db:migrate # Run migrations
|
|
84
|
+
__RUN_CMD__ db:studio # Open Prisma Studio
|
|
85
|
+
|
|
86
|
+
# Production
|
|
87
|
+
__RUN_CMD__ build # Build for production
|
|
88
|
+
__RUN_CMD__ start # Start production server
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
1. Run `__RUN_CMD__ db:push` to set up the database
|
|
94
|
+
2. Run `__RUN_CMD__ dev` to start the development server
|
|
95
|
+
3. Open http://localhost:__API_PORT__ in your browser
|
|
96
|
+
|
|
97
|
+
## API Endpoints
|
|
98
|
+
|
|
99
|
+
- `GET /api/health` - Health check
|
|
100
|
+
- `GET /api/users` - List all users
|
|
101
|
+
- `GET /api/users/:id` - Get user by ID
|
|
102
|
+
- `POST /api/users` - Create user
|
|
103
|
+
- `PUT /api/users/:id` - Update user
|
|
104
|
+
- `DELETE /api/users/:id` - Delete user
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Post Server Actions - Procedure Bridge Pattern
|
|
5
|
+
*
|
|
6
|
+
* Demonstrates the VeloxTS procedure bridge, which allows server actions
|
|
7
|
+
* to reuse procedure validation, guards, and business logic.
|
|
8
|
+
*
|
|
9
|
+
* This pattern bridges the gap between:
|
|
10
|
+
* - Procedures (API logic with validation and guards)
|
|
11
|
+
* - Server Actions (form submissions and direct calls)
|
|
12
|
+
*
|
|
13
|
+
* Benefits:
|
|
14
|
+
* - Single source of truth for validation (Zod schemas in procedures)
|
|
15
|
+
* - Reuse of business logic across REST API and server actions
|
|
16
|
+
* - Type safety flows from procedure to action
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* // In a server component
|
|
21
|
+
* import { createPost } from '@/actions/posts';
|
|
22
|
+
*
|
|
23
|
+
* // Call directly with typed input
|
|
24
|
+
* const result = await createPost({
|
|
25
|
+
* userId: '123',
|
|
26
|
+
* title: 'My Post',
|
|
27
|
+
* content: 'Content here',
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Or use in a form
|
|
31
|
+
* <form action={createPost}>
|
|
32
|
+
* <input name="title" />
|
|
33
|
+
* <button type="submit">Create</button>
|
|
34
|
+
* </form>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { action } from '@veloxts/web';
|
|
39
|
+
|
|
40
|
+
import { postProcedures } from '@/api/procedures/posts';
|
|
41
|
+
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Procedure-Bridged Actions
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new post for a user.
|
|
48
|
+
*
|
|
49
|
+
* This action bridges to the createPost procedure, reusing:
|
|
50
|
+
* - Input validation (CreatePostSchema)
|
|
51
|
+
* - Output validation (PostSchema)
|
|
52
|
+
* - Parent resource validation (.parent('users'))
|
|
53
|
+
* - Any guards or middleware defined on the procedure
|
|
54
|
+
*
|
|
55
|
+
* The procedure's REST endpoint is: POST /api/users/:userId/posts
|
|
56
|
+
*/
|
|
57
|
+
export const createPost = action.fromProcedure(postProcedures.procedures.createPost, {
|
|
58
|
+
parseFormData: true,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Gets a specific post.
|
|
63
|
+
*
|
|
64
|
+
* Bridges to getPost procedure which validates:
|
|
65
|
+
* - userId: UUID format
|
|
66
|
+
* - id: UUID format (post ID)
|
|
67
|
+
*/
|
|
68
|
+
export const getPost = action.fromProcedure(postProcedures.procedures.getPost);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Updates an existing post.
|
|
72
|
+
*
|
|
73
|
+
* Bridges to updatePost procedure which validates:
|
|
74
|
+
* - userId and id must exist
|
|
75
|
+
* - Only title and content can be updated
|
|
76
|
+
*/
|
|
77
|
+
export const updatePost = action.fromProcedure(postProcedures.procedures.updatePost, {
|
|
78
|
+
parseFormData: true,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Deletes a post.
|
|
83
|
+
*
|
|
84
|
+
* Bridges to deletePost procedure.
|
|
85
|
+
*/
|
|
86
|
+
export const deletePost = action.fromProcedure(postProcedures.procedures.deletePost);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Lists all posts for a user.
|
|
90
|
+
*
|
|
91
|
+
* Bridges to listPosts procedure.
|
|
92
|
+
*/
|
|
93
|
+
export const listPosts = action.fromProcedure(postProcedures.procedures.listPosts);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* User Server Actions
|
|
5
|
+
*
|
|
6
|
+
* Type-safe server actions for user operations using the VeloxTS action() helper.
|
|
7
|
+
* These can be called directly from client components with full type inference.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* // In a client component
|
|
12
|
+
* const result = await createUser({ name: 'John', email: 'john@example.com' });
|
|
13
|
+
*
|
|
14
|
+
* if (result.success) {
|
|
15
|
+
* console.log('Created user:', result.data);
|
|
16
|
+
* } else {
|
|
17
|
+
* console.log('Error:', result.error.message);
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { action } from '@veloxts/web';
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
|
|
25
|
+
import { db } from '@/api/database';
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Schemas
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Schema for creating a new user
|
|
33
|
+
*/
|
|
34
|
+
const CreateUserSchema = z.object({
|
|
35
|
+
name: z.string().min(1, 'Name is required').max(100, 'Name is too long'),
|
|
36
|
+
email: z.string().email('Invalid email address'),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Schema for deleting a user
|
|
41
|
+
*/
|
|
42
|
+
const DeleteUserSchema = z.object({
|
|
43
|
+
id: z.string().min(1, 'User ID is required'),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// Actions
|
|
48
|
+
// ============================================================================
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new user.
|
|
52
|
+
*
|
|
53
|
+
* Input is automatically validated against CreateUserSchema.
|
|
54
|
+
* Returns a discriminated union for type-safe error handling.
|
|
55
|
+
*/
|
|
56
|
+
export const createUser = action(CreateUserSchema, async (input) => {
|
|
57
|
+
// Input is fully typed as { name: string; email: string }
|
|
58
|
+
// Validation has already been performed by the action() helper
|
|
59
|
+
|
|
60
|
+
const user = await db.user.create({
|
|
61
|
+
data: {
|
|
62
|
+
name: input.name.trim(),
|
|
63
|
+
email: input.email.toLowerCase().trim(),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return user;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Deletes a user by ID.
|
|
72
|
+
*
|
|
73
|
+
* Input is automatically validated to ensure a valid ID is provided.
|
|
74
|
+
*/
|
|
75
|
+
export const deleteUser = action(DeleteUserSchema, async (input) => {
|
|
76
|
+
// Input is fully typed as { id: string }
|
|
77
|
+
|
|
78
|
+
await db.user.delete({
|
|
79
|
+
where: { id: input.id },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return { deleted: true };
|
|
83
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard Layout
|
|
3
|
+
*
|
|
4
|
+
* A nested layout for pages in the (dashboard) route group.
|
|
5
|
+
* Adds a sidebar navigation for dashboard-related pages.
|
|
6
|
+
* This is NOT a full HTML document - it wraps within RootLayout.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ReactNode } from 'react';
|
|
10
|
+
|
|
11
|
+
interface DashboardLayoutProps {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
params?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function DashboardLayout({ children }: DashboardLayoutProps) {
|
|
17
|
+
return (
|
|
18
|
+
<div className="dashboard-layout">
|
|
19
|
+
<style>{`
|
|
20
|
+
.dashboard-layout {
|
|
21
|
+
display: flex;
|
|
22
|
+
gap: 2rem;
|
|
23
|
+
min-height: 60vh;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.dashboard-sidebar {
|
|
27
|
+
width: 200px;
|
|
28
|
+
flex-shrink: 0;
|
|
29
|
+
background: white;
|
|
30
|
+
border-radius: 8px;
|
|
31
|
+
padding: 1rem;
|
|
32
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
33
|
+
height: fit-content;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.dashboard-sidebar h3 {
|
|
37
|
+
font-size: 0.75rem;
|
|
38
|
+
text-transform: uppercase;
|
|
39
|
+
color: #888;
|
|
40
|
+
margin-bottom: 0.5rem;
|
|
41
|
+
padding: 0 0.5rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.sidebar-nav {
|
|
45
|
+
list-style: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.sidebar-nav a {
|
|
49
|
+
display: block;
|
|
50
|
+
padding: 0.5rem;
|
|
51
|
+
color: #1a1a2e;
|
|
52
|
+
text-decoration: none;
|
|
53
|
+
border-radius: 4px;
|
|
54
|
+
transition: background 0.2s;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.sidebar-nav a:hover {
|
|
58
|
+
background: #f0f0f0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.sidebar-nav a.active {
|
|
62
|
+
background: #6366f1;
|
|
63
|
+
color: white;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.dashboard-content {
|
|
67
|
+
flex: 1;
|
|
68
|
+
background: white;
|
|
69
|
+
border-radius: 8px;
|
|
70
|
+
padding: 1.5rem;
|
|
71
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.dashboard-badge {
|
|
75
|
+
display: inline-block;
|
|
76
|
+
background: #6366f1;
|
|
77
|
+
color: white;
|
|
78
|
+
font-size: 0.625rem;
|
|
79
|
+
text-transform: uppercase;
|
|
80
|
+
padding: 0.25rem 0.5rem;
|
|
81
|
+
border-radius: 4px;
|
|
82
|
+
margin-bottom: 1rem;
|
|
83
|
+
}
|
|
84
|
+
`}</style>
|
|
85
|
+
|
|
86
|
+
<aside className="dashboard-sidebar">
|
|
87
|
+
<h3>Dashboard</h3>
|
|
88
|
+
<nav>
|
|
89
|
+
<ul className="sidebar-nav">
|
|
90
|
+
<li>
|
|
91
|
+
<a href="/profile">Profile</a>
|
|
92
|
+
</li>
|
|
93
|
+
<li>
|
|
94
|
+
<a href="/settings">Settings</a>
|
|
95
|
+
</li>
|
|
96
|
+
<li>
|
|
97
|
+
<a href="/settings?tab=notifications">Notifications</a>
|
|
98
|
+
</li>
|
|
99
|
+
<li>
|
|
100
|
+
<a href="/settings?tab=security">Security</a>
|
|
101
|
+
</li>
|
|
102
|
+
</ul>
|
|
103
|
+
</nav>
|
|
104
|
+
|
|
105
|
+
<h3 style={{ marginTop: '1rem' }}>Navigation</h3>
|
|
106
|
+
<nav>
|
|
107
|
+
<ul className="sidebar-nav">
|
|
108
|
+
<li>
|
|
109
|
+
<a href="/">Home</a>
|
|
110
|
+
</li>
|
|
111
|
+
<li>
|
|
112
|
+
<a href="/users">Users</a>
|
|
113
|
+
</li>
|
|
114
|
+
<li>
|
|
115
|
+
<a href="/docs/getting-started">Docs</a>
|
|
116
|
+
</li>
|
|
117
|
+
</ul>
|
|
118
|
+
</nav>
|
|
119
|
+
</aside>
|
|
120
|
+
|
|
121
|
+
<div className="dashboard-content">
|
|
122
|
+
<span className="dashboard-badge">Dashboard</span>
|
|
123
|
+
{children}
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketing Layout
|
|
3
|
+
*
|
|
4
|
+
* This layout wraps pages in the (marketing) route group.
|
|
5
|
+
* It's automatically applied to all pages in app/pages/(marketing)/*.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ReactNode } from 'react';
|
|
9
|
+
|
|
10
|
+
interface MarketingLayoutProps {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
params?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function MarketingLayout({ children }: MarketingLayoutProps) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="marketing-layout">
|
|
18
|
+
<div className="marketing-banner">
|
|
19
|
+
<span className="badge">Marketing</span>
|
|
20
|
+
<span>This page uses the marketing layout</span>
|
|
21
|
+
</div>
|
|
22
|
+
{children}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Layout
|
|
3
|
+
*
|
|
4
|
+
* A stripped-down layout for pages that need minimal chrome,
|
|
5
|
+
* such as print-friendly pages, embedded widgets, or auth pages.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ReactNode } from 'react';
|
|
9
|
+
|
|
10
|
+
interface MinimalLayoutProps {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
params?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function MinimalLayout({ children }: MinimalLayoutProps) {
|
|
16
|
+
return (
|
|
17
|
+
<html lang="en">
|
|
18
|
+
<head>
|
|
19
|
+
<meta charSet="utf-8" />
|
|
20
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
21
|
+
<title>VeloxTS</title>
|
|
22
|
+
<link rel="stylesheet" href="/_build/styles.css" />
|
|
23
|
+
</head>
|
|
24
|
+
<body className="minimal-layout">
|
|
25
|
+
{children}
|
|
26
|
+
<script src="/_build/entry.client.js" type="module" />
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
29
|
+
);
|
|
30
|
+
}
|