create-velox-app 0.6.31 → 0.6.52
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/CHANGELOG.md +126 -0
- package/GUIDE.md +230 -0
- package/dist/cli.js +1 -0
- package/dist/index.js +14 -4
- package/dist/templates/auth.js +10 -0
- package/dist/templates/index.js +30 -1
- package/dist/templates/placeholders.js +0 -3
- package/dist/templates/rsc-auth.d.ts +12 -0
- package/dist/templates/rsc-auth.js +208 -0
- package/dist/templates/rsc.js +40 -1
- package/dist/templates/shared/css-generator.d.ts +26 -0
- package/dist/templates/shared/css-generator.js +553 -0
- package/dist/templates/shared/index.d.ts +3 -0
- package/dist/templates/shared/index.js +3 -0
- package/dist/templates/shared/rsc-styles.d.ts +54 -0
- package/dist/templates/shared/rsc-styles.js +68 -0
- package/dist/templates/shared/theme.d.ts +133 -0
- package/dist/templates/shared/theme.js +141 -0
- package/dist/templates/spa.js +10 -0
- package/dist/templates/trpc.js +10 -0
- package/dist/templates/types.d.ts +2 -1
- package/dist/templates/types.js +6 -0
- package/package.json +6 -3
- package/src/templates/source/api/config/database.ts +13 -32
- package/src/templates/source/api/docker-compose.yml +21 -0
- package/src/templates/source/root/CLAUDE.auth.md +6 -0
- package/src/templates/source/root/CLAUDE.default.md +6 -0
- package/src/templates/source/rsc/CLAUDE.md +56 -2
- package/src/templates/source/rsc/app/actions/posts.ts +1 -1
- package/src/templates/source/rsc/app/actions/users.ts +111 -20
- package/src/templates/source/rsc/app/layouts/dashboard.tsx +21 -16
- package/src/templates/source/rsc/app/layouts/marketing.tsx +34 -0
- package/src/templates/source/rsc/app/layouts/minimal-content.tsx +21 -0
- package/src/templates/source/rsc/app/layouts/minimal.tsx +86 -5
- package/src/templates/source/rsc/app/layouts/root.tsx +148 -44
- package/src/templates/source/rsc/docker-compose.yml +21 -0
- package/src/templates/source/rsc/package.json +3 -3
- package/src/templates/source/rsc/src/api/database.ts +13 -32
- package/src/templates/source/rsc/src/api/handler.ts +1 -1
- package/src/templates/source/rsc/src/entry.client.tsx +65 -18
- package/src/templates/source/rsc-auth/CLAUDE.md +230 -0
- package/src/templates/source/rsc-auth/app/actions/auth.ts +112 -0
- package/src/templates/source/rsc-auth/app/actions/users.ts +289 -0
- package/src/templates/source/rsc-auth/app/layouts/dashboard.tsx +132 -0
- package/src/templates/source/rsc-auth/app/layouts/marketing.tsx +59 -0
- package/src/templates/source/rsc-auth/app/layouts/minimal-content.tsx +21 -0
- package/src/templates/source/rsc-auth/app/layouts/minimal.tsx +111 -0
- package/src/templates/source/rsc-auth/app/layouts/root.tsx +355 -0
- package/src/templates/source/rsc-auth/app/pages/_not-found.tsx +15 -0
- package/src/templates/source/rsc-auth/app/pages/auth/login.tsx +198 -0
- package/src/templates/source/rsc-auth/app/pages/auth/register.tsx +225 -0
- package/src/templates/source/rsc-auth/app/pages/dashboard/index.tsx +267 -0
- package/src/templates/source/rsc-auth/app/pages/index.tsx +83 -0
- package/src/templates/source/rsc-auth/app/pages/users.tsx +47 -0
- package/src/templates/source/rsc-auth/app.config.ts +12 -0
- package/src/templates/source/rsc-auth/docker-compose.yml +21 -0
- package/src/templates/source/rsc-auth/env.example +11 -0
- package/src/templates/source/rsc-auth/gitignore +34 -0
- package/src/templates/source/rsc-auth/package.json +44 -0
- package/src/templates/source/rsc-auth/prisma/schema.prisma +23 -0
- package/src/templates/source/rsc-auth/prisma.config.ts +22 -0
- package/src/templates/source/rsc-auth/public/favicon.svg +4 -0
- package/src/templates/source/rsc-auth/src/api/database.ts +129 -0
- package/src/templates/source/rsc-auth/src/api/handler.ts +85 -0
- package/src/templates/source/rsc-auth/src/api/procedures/auth.ts +262 -0
- package/src/templates/source/rsc-auth/src/api/procedures/health.ts +48 -0
- package/src/templates/source/rsc-auth/src/api/procedures/users.ts +87 -0
- package/src/templates/source/rsc-auth/src/api/schemas/auth.ts +79 -0
- package/src/templates/source/rsc-auth/src/api/schemas/user.ts +38 -0
- package/src/templates/source/rsc-auth/src/api/utils/auth.ts +157 -0
- package/src/templates/source/rsc-auth/src/entry.client.tsx +63 -0
- package/src/templates/source/rsc-auth/src/entry.server.tsx +262 -0
- package/src/templates/source/rsc-auth/tsconfig.json +24 -0
- package/src/templates/source/shared/scripts/check-client-imports.sh +75 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VeloxTS Design System
|
|
3
|
+
*
|
|
4
|
+
* Dark mode theme constants extracted from the SPA template.
|
|
5
|
+
* This is the single source of truth for all RSC template styling.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Color palette - Dark mode with cyan accents
|
|
9
|
+
*/
|
|
10
|
+
export declare const colors: {
|
|
11
|
+
readonly background: "#0a0a0a";
|
|
12
|
+
readonly backgroundAlt: "#111";
|
|
13
|
+
readonly surface: "#111";
|
|
14
|
+
readonly surfaceHover: "#1a1a1a";
|
|
15
|
+
readonly surfaceAlt: "#1a1a1a";
|
|
16
|
+
readonly border: "#222";
|
|
17
|
+
readonly borderHover: "#333";
|
|
18
|
+
readonly borderFocus: "#00d9ff";
|
|
19
|
+
readonly text: "#ededed";
|
|
20
|
+
readonly textMuted: "#888";
|
|
21
|
+
readonly textDimmed: "#666";
|
|
22
|
+
readonly textInverse: "#000";
|
|
23
|
+
readonly accent: "#00d9ff";
|
|
24
|
+
readonly accentHover: "rgba(0, 217, 255, 0.8)";
|
|
25
|
+
readonly accentDimmed: "rgba(0, 217, 255, 0.6)";
|
|
26
|
+
readonly success: "#00d9ff";
|
|
27
|
+
readonly error: "#ff4444";
|
|
28
|
+
readonly errorBg: "#2a1111";
|
|
29
|
+
readonly errorText: "#ff6666";
|
|
30
|
+
readonly warning: "#ffaa00";
|
|
31
|
+
readonly warningBg: "#2a2211";
|
|
32
|
+
readonly info: "#00d9ff";
|
|
33
|
+
readonly selection: "#00d9ff";
|
|
34
|
+
readonly selectionText: "#000";
|
|
35
|
+
readonly scrollbarTrack: "#111";
|
|
36
|
+
readonly scrollbarThumb: "#333";
|
|
37
|
+
readonly scrollbarThumbHover: "#444";
|
|
38
|
+
readonly codeBg: "#1a1a1a";
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Typography
|
|
42
|
+
*/
|
|
43
|
+
export declare const typography: {
|
|
44
|
+
readonly fontFamily: {
|
|
45
|
+
readonly sans: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Open Sans\", \"Helvetica Neue\", sans-serif";
|
|
46
|
+
readonly mono: "\"SF Mono\", \"Fira Code\", \"Fira Mono\", Menlo, Monaco, \"Courier New\", monospace";
|
|
47
|
+
};
|
|
48
|
+
readonly fontSize: {
|
|
49
|
+
readonly xs: "0.625rem";
|
|
50
|
+
readonly sm: "0.75rem";
|
|
51
|
+
readonly base: "0.875rem";
|
|
52
|
+
readonly md: "0.9rem";
|
|
53
|
+
readonly lg: "1rem";
|
|
54
|
+
readonly xl: "1.25rem";
|
|
55
|
+
readonly '2xl': "1.5rem";
|
|
56
|
+
readonly '3xl': "2rem";
|
|
57
|
+
readonly '4xl': "2.5rem";
|
|
58
|
+
readonly '5xl': "3rem";
|
|
59
|
+
};
|
|
60
|
+
readonly fontWeight: {
|
|
61
|
+
readonly normal: "400";
|
|
62
|
+
readonly medium: "500";
|
|
63
|
+
readonly semibold: "600";
|
|
64
|
+
readonly bold: "700";
|
|
65
|
+
};
|
|
66
|
+
readonly lineHeight: {
|
|
67
|
+
readonly tight: "1.2";
|
|
68
|
+
readonly normal: "1.6";
|
|
69
|
+
readonly relaxed: "1.8";
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Spacing scale (based on 0.25rem = 4px)
|
|
74
|
+
*/
|
|
75
|
+
export declare const spacing: {
|
|
76
|
+
readonly 0: "0";
|
|
77
|
+
readonly 1: "0.25rem";
|
|
78
|
+
readonly 2: "0.5rem";
|
|
79
|
+
readonly 3: "0.75rem";
|
|
80
|
+
readonly 4: "1rem";
|
|
81
|
+
readonly 5: "1.25rem";
|
|
82
|
+
readonly 6: "1.5rem";
|
|
83
|
+
readonly 8: "2rem";
|
|
84
|
+
readonly 10: "2.5rem";
|
|
85
|
+
readonly 12: "3rem";
|
|
86
|
+
readonly 16: "4rem";
|
|
87
|
+
readonly 20: "5rem";
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Layout constants
|
|
91
|
+
*/
|
|
92
|
+
export declare const layout: {
|
|
93
|
+
readonly maxWidth: "1200px";
|
|
94
|
+
readonly maxWidthNarrow: "1000px";
|
|
95
|
+
readonly maxWidthWide: "1400px";
|
|
96
|
+
readonly navHeight: "64px";
|
|
97
|
+
readonly sidebarWidth: "240px";
|
|
98
|
+
readonly borderRadius: {
|
|
99
|
+
readonly sm: "4px";
|
|
100
|
+
readonly md: "6px";
|
|
101
|
+
readonly lg: "8px";
|
|
102
|
+
readonly xl: "12px";
|
|
103
|
+
};
|
|
104
|
+
readonly boxShadow: {
|
|
105
|
+
readonly sm: "0 1px 2px rgba(0, 0, 0, 0.3)";
|
|
106
|
+
readonly md: "0 2px 8px rgba(0, 0, 0, 0.4)";
|
|
107
|
+
readonly lg: "0 4px 16px rgba(0, 0, 0, 0.5)";
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Transitions
|
|
112
|
+
*/
|
|
113
|
+
export declare const transitions: {
|
|
114
|
+
readonly fast: "0.1s";
|
|
115
|
+
readonly normal: "0.2s";
|
|
116
|
+
readonly slow: "0.3s";
|
|
117
|
+
readonly ease: "ease";
|
|
118
|
+
readonly easeIn: "ease-in";
|
|
119
|
+
readonly easeOut: "ease-out";
|
|
120
|
+
readonly easeInOut: "ease-in-out";
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Z-index layers
|
|
124
|
+
*/
|
|
125
|
+
export declare const zIndex: {
|
|
126
|
+
readonly base: 0;
|
|
127
|
+
readonly dropdown: 10;
|
|
128
|
+
readonly sticky: 100;
|
|
129
|
+
readonly fixed: 200;
|
|
130
|
+
readonly modal: 300;
|
|
131
|
+
readonly popover: 400;
|
|
132
|
+
readonly tooltip: 500;
|
|
133
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VeloxTS Design System
|
|
3
|
+
*
|
|
4
|
+
* Dark mode theme constants extracted from the SPA template.
|
|
5
|
+
* This is the single source of truth for all RSC template styling.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Color palette - Dark mode with cyan accents
|
|
9
|
+
*/
|
|
10
|
+
export const colors = {
|
|
11
|
+
// Backgrounds
|
|
12
|
+
background: '#0a0a0a',
|
|
13
|
+
backgroundAlt: '#111',
|
|
14
|
+
surface: '#111',
|
|
15
|
+
surfaceHover: '#1a1a1a',
|
|
16
|
+
surfaceAlt: '#1a1a1a',
|
|
17
|
+
// Borders
|
|
18
|
+
border: '#222',
|
|
19
|
+
borderHover: '#333',
|
|
20
|
+
borderFocus: '#00d9ff',
|
|
21
|
+
// Text
|
|
22
|
+
text: '#ededed',
|
|
23
|
+
textMuted: '#888',
|
|
24
|
+
textDimmed: '#666',
|
|
25
|
+
textInverse: '#000',
|
|
26
|
+
// Accent & Brand
|
|
27
|
+
accent: '#00d9ff',
|
|
28
|
+
accentHover: 'rgba(0, 217, 255, 0.8)',
|
|
29
|
+
accentDimmed: 'rgba(0, 217, 255, 0.6)',
|
|
30
|
+
// Semantic Colors
|
|
31
|
+
success: '#00d9ff',
|
|
32
|
+
error: '#ff4444',
|
|
33
|
+
errorBg: '#2a1111',
|
|
34
|
+
errorText: '#ff6666',
|
|
35
|
+
warning: '#ffaa00',
|
|
36
|
+
warningBg: '#2a2211',
|
|
37
|
+
info: '#00d9ff',
|
|
38
|
+
// Selection
|
|
39
|
+
selection: '#00d9ff',
|
|
40
|
+
selectionText: '#000',
|
|
41
|
+
// Scrollbar
|
|
42
|
+
scrollbarTrack: '#111',
|
|
43
|
+
scrollbarThumb: '#333',
|
|
44
|
+
scrollbarThumbHover: '#444',
|
|
45
|
+
// Code blocks
|
|
46
|
+
codeBg: '#1a1a1a',
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Typography
|
|
50
|
+
*/
|
|
51
|
+
export const typography = {
|
|
52
|
+
fontFamily: {
|
|
53
|
+
sans: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
|
|
54
|
+
mono: '"SF Mono", "Fira Code", "Fira Mono", Menlo, Monaco, "Courier New", monospace',
|
|
55
|
+
},
|
|
56
|
+
fontSize: {
|
|
57
|
+
xs: '0.625rem', // 10px
|
|
58
|
+
sm: '0.75rem', // 12px
|
|
59
|
+
base: '0.875rem', // 14px
|
|
60
|
+
md: '0.9rem', // 14.4px
|
|
61
|
+
lg: '1rem', // 16px
|
|
62
|
+
xl: '1.25rem', // 20px
|
|
63
|
+
'2xl': '1.5rem', // 24px
|
|
64
|
+
'3xl': '2rem', // 32px
|
|
65
|
+
'4xl': '2.5rem', // 40px
|
|
66
|
+
'5xl': '3rem', // 48px
|
|
67
|
+
},
|
|
68
|
+
fontWeight: {
|
|
69
|
+
normal: '400',
|
|
70
|
+
medium: '500',
|
|
71
|
+
semibold: '600',
|
|
72
|
+
bold: '700',
|
|
73
|
+
},
|
|
74
|
+
lineHeight: {
|
|
75
|
+
tight: '1.2',
|
|
76
|
+
normal: '1.6',
|
|
77
|
+
relaxed: '1.8',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Spacing scale (based on 0.25rem = 4px)
|
|
82
|
+
*/
|
|
83
|
+
export const spacing = {
|
|
84
|
+
0: '0',
|
|
85
|
+
1: '0.25rem', // 4px
|
|
86
|
+
2: '0.5rem', // 8px
|
|
87
|
+
3: '0.75rem', // 12px
|
|
88
|
+
4: '1rem', // 16px
|
|
89
|
+
5: '1.25rem', // 20px
|
|
90
|
+
6: '1.5rem', // 24px
|
|
91
|
+
8: '2rem', // 32px
|
|
92
|
+
10: '2.5rem', // 40px
|
|
93
|
+
12: '3rem', // 48px
|
|
94
|
+
16: '4rem', // 64px
|
|
95
|
+
20: '5rem', // 80px
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Layout constants
|
|
99
|
+
*/
|
|
100
|
+
export const layout = {
|
|
101
|
+
maxWidth: '1200px',
|
|
102
|
+
maxWidthNarrow: '1000px',
|
|
103
|
+
maxWidthWide: '1400px',
|
|
104
|
+
navHeight: '64px',
|
|
105
|
+
sidebarWidth: '240px',
|
|
106
|
+
borderRadius: {
|
|
107
|
+
sm: '4px',
|
|
108
|
+
md: '6px',
|
|
109
|
+
lg: '8px',
|
|
110
|
+
xl: '12px',
|
|
111
|
+
},
|
|
112
|
+
boxShadow: {
|
|
113
|
+
sm: '0 1px 2px rgba(0, 0, 0, 0.3)',
|
|
114
|
+
md: '0 2px 8px rgba(0, 0, 0, 0.4)',
|
|
115
|
+
lg: '0 4px 16px rgba(0, 0, 0, 0.5)',
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Transitions
|
|
120
|
+
*/
|
|
121
|
+
export const transitions = {
|
|
122
|
+
fast: '0.1s',
|
|
123
|
+
normal: '0.2s',
|
|
124
|
+
slow: '0.3s',
|
|
125
|
+
ease: 'ease',
|
|
126
|
+
easeIn: 'ease-in',
|
|
127
|
+
easeOut: 'ease-out',
|
|
128
|
+
easeInOut: 'ease-in-out',
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Z-index layers
|
|
132
|
+
*/
|
|
133
|
+
export const zIndex = {
|
|
134
|
+
base: 0,
|
|
135
|
+
dropdown: 10,
|
|
136
|
+
sticky: 100,
|
|
137
|
+
fixed: 200,
|
|
138
|
+
modal: 300,
|
|
139
|
+
popover: 400,
|
|
140
|
+
tooltip: 500,
|
|
141
|
+
};
|
package/dist/templates/spa.js
CHANGED
|
@@ -65,6 +65,9 @@ function generateRoutes() {
|
|
|
65
65
|
function generateApiTypesDts() {
|
|
66
66
|
return compileTemplate('api/types.d.ts', DEFAULT_CONFIG);
|
|
67
67
|
}
|
|
68
|
+
function generateDockerCompose(config) {
|
|
69
|
+
return compileTemplate('api/docker-compose.yml', config);
|
|
70
|
+
}
|
|
68
71
|
// ============================================================================
|
|
69
72
|
// SPA Template Generator
|
|
70
73
|
// ============================================================================
|
|
@@ -92,6 +95,13 @@ export function generateSpaTemplate(config) {
|
|
|
92
95
|
{ path: 'apps/api/src/routes.ts', content: generateRoutes() },
|
|
93
96
|
{ path: 'apps/api/src/types.d.ts', content: generateApiTypesDts() },
|
|
94
97
|
];
|
|
98
|
+
// Add docker-compose for PostgreSQL
|
|
99
|
+
if (config.database === 'postgresql') {
|
|
100
|
+
files.push({
|
|
101
|
+
path: 'apps/api/docker-compose.yml',
|
|
102
|
+
content: generateDockerCompose(config),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
95
105
|
// Add root workspace files
|
|
96
106
|
const rootFiles = generateRootFiles(config, false);
|
|
97
107
|
// Add web package files
|
package/dist/templates/trpc.js
CHANGED
|
@@ -73,6 +73,9 @@ function generateRoutes() {
|
|
|
73
73
|
function generateApiTypesDts() {
|
|
74
74
|
return compileTemplate('api/types.d.ts', DEFAULT_CONFIG);
|
|
75
75
|
}
|
|
76
|
+
function generateDockerCompose(config) {
|
|
77
|
+
return compileTemplate('api/docker-compose.yml', config);
|
|
78
|
+
}
|
|
76
79
|
// ============================================================================
|
|
77
80
|
// tRPC Template Generator
|
|
78
81
|
// ============================================================================
|
|
@@ -100,6 +103,13 @@ export function generateTrpcTemplate(config) {
|
|
|
100
103
|
{ path: 'apps/api/src/routes.ts', content: generateRoutes() },
|
|
101
104
|
{ path: 'apps/api/src/types.d.ts', content: generateApiTypesDts() },
|
|
102
105
|
];
|
|
106
|
+
// Add docker-compose for PostgreSQL
|
|
107
|
+
if (config.database === 'postgresql') {
|
|
108
|
+
files.push({
|
|
109
|
+
path: 'apps/api/docker-compose.yml',
|
|
110
|
+
content: generateDockerCompose(config),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
103
113
|
// Add root workspace files (use false for isAuthTemplate)
|
|
104
114
|
const rootFiles = generateRootFiles(config, false);
|
|
105
115
|
// Add web package files (use false for isAuthTemplate)
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* - `auth` - SPA + API with JWT authentication
|
|
11
11
|
* - `trpc` - SPA + API with tRPC integration
|
|
12
12
|
* - `rsc` (alias: `fullstack`) - React Server Components with Vinxi
|
|
13
|
+
* - `rsc-auth` - RSC with JWT authentication and validated() server actions
|
|
13
14
|
*/
|
|
14
|
-
export type TemplateType = 'spa' | 'auth' | 'trpc' | 'rsc';
|
|
15
|
+
export type TemplateType = 'spa' | 'auth' | 'trpc' | 'rsc' | 'rsc-auth';
|
|
15
16
|
/**
|
|
16
17
|
* Template aliases for backward compatibility
|
|
17
18
|
*/
|
package/dist/templates/types.js
CHANGED
|
@@ -92,6 +92,12 @@ export const TEMPLATE_METADATA = {
|
|
|
92
92
|
description: 'React Server Components with Vinxi + embedded Fastify',
|
|
93
93
|
hint: 'Unified server/client with file-based routing and streaming',
|
|
94
94
|
},
|
|
95
|
+
'rsc-auth': {
|
|
96
|
+
type: 'rsc-auth',
|
|
97
|
+
label: 'RSC + Auth',
|
|
98
|
+
description: 'RSC with JWT authentication and validated() server actions',
|
|
99
|
+
hint: 'Full-stack auth with rate limiting, CSRF, and role-based access',
|
|
100
|
+
},
|
|
95
101
|
};
|
|
96
102
|
/**
|
|
97
103
|
* Get all available template types
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-velox-app",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.52",
|
|
4
4
|
"description": "Project scaffolder for VeloxTS framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,8 +29,9 @@
|
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "25.0.3",
|
|
32
|
+
"@vitest/coverage-v8": "4.0.16",
|
|
32
33
|
"typescript": "5.9.3",
|
|
33
|
-
"vitest": "4.0.
|
|
34
|
+
"vitest": "4.0.16"
|
|
34
35
|
},
|
|
35
36
|
"keywords": [
|
|
36
37
|
"velox",
|
|
@@ -59,6 +60,8 @@
|
|
|
59
60
|
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
60
61
|
"test": "vitest run",
|
|
61
62
|
"test:watch": "vitest",
|
|
62
|
-
"
|
|
63
|
+
"test:coverage": "vitest run --coverage",
|
|
64
|
+
"smoke-test": "./scripts/smoke-test.sh",
|
|
65
|
+
"verify-publish": "./scripts/verify-publish.sh"
|
|
63
66
|
}
|
|
64
67
|
}
|
|
@@ -20,7 +20,6 @@ import type { PrismaBetterSqlite3 as PrismaBetterSqlite3Type } from '@prisma/ada
|
|
|
20
20
|
import type { PrismaPg as PrismaPgType } from '@prisma/adapter-pg';
|
|
21
21
|
/* @endif postgresql */
|
|
22
22
|
import type { PrismaClient as PrismaClientType } from '@prisma/client';
|
|
23
|
-
import type { Pool as PoolType } from 'pg';
|
|
24
23
|
|
|
25
24
|
const require = createRequire(import.meta.url);
|
|
26
25
|
/* @if sqlite */
|
|
@@ -32,9 +31,6 @@ const { PrismaBetterSqlite3 } = require('@prisma/adapter-better-sqlite3') as {
|
|
|
32
31
|
const { PrismaPg } = require('@prisma/adapter-pg') as {
|
|
33
32
|
PrismaPg: typeof PrismaPgType;
|
|
34
33
|
};
|
|
35
|
-
const { Pool } = require('pg') as {
|
|
36
|
-
Pool: typeof PoolType;
|
|
37
|
-
};
|
|
38
34
|
/* @endif postgresql */
|
|
39
35
|
const { PrismaClient } = require('@prisma/client') as {
|
|
40
36
|
PrismaClient: typeof PrismaClientType;
|
|
@@ -44,10 +40,6 @@ declare global {
|
|
|
44
40
|
// Allow global `var` declarations for hot reload in development
|
|
45
41
|
// eslint-disable-next-line no-var
|
|
46
42
|
var __db: PrismaClient | undefined;
|
|
47
|
-
/* @if postgresql */
|
|
48
|
-
// eslint-disable-next-line no-var
|
|
49
|
-
var __pool: InstanceType<typeof PoolType> | undefined;
|
|
50
|
-
/* @endif postgresql */
|
|
51
43
|
}
|
|
52
44
|
|
|
53
45
|
/* @if sqlite */
|
|
@@ -72,32 +64,24 @@ function createPrismaClient(): PrismaClient {
|
|
|
72
64
|
/* @endif sqlite */
|
|
73
65
|
/* @if postgresql */
|
|
74
66
|
/**
|
|
75
|
-
* Create a PostgreSQL
|
|
76
|
-
*
|
|
67
|
+
* Create a Prisma client instance using the PostgreSQL adapter.
|
|
68
|
+
*
|
|
69
|
+
* Prisma 7 Breaking Change:
|
|
70
|
+
* - PrismaPg now takes connectionString directly (not a Pool instance)
|
|
71
|
+
* - Pool management is handled internally by the adapter
|
|
77
72
|
*/
|
|
78
|
-
function
|
|
79
|
-
const
|
|
73
|
+
function createPrismaClient(): PrismaClient {
|
|
74
|
+
const connectionString = process.env.DATABASE_URL;
|
|
80
75
|
|
|
81
|
-
if (!
|
|
76
|
+
if (!connectionString) {
|
|
82
77
|
throw new Error(
|
|
83
78
|
'[VeloxTS] DATABASE_URL environment variable is not set. ' +
|
|
84
79
|
'Ensure .env file exists with DATABASE_URL defined.'
|
|
85
80
|
);
|
|
86
81
|
}
|
|
87
82
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
max: 10, // Maximum connections in pool
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Create a Prisma client instance using the PostgreSQL adapter.
|
|
96
|
-
* Uses connection pooling for efficient database access.
|
|
97
|
-
*/
|
|
98
|
-
function createPrismaClient(pool: InstanceType<typeof PoolType>): PrismaClient {
|
|
99
|
-
// Prisma 7 requires driver adapters for direct connections
|
|
100
|
-
const adapter = new PrismaPg(pool);
|
|
83
|
+
// Prisma 7: Pass connectionString directly to PrismaPg (not a Pool)
|
|
84
|
+
const adapter = new PrismaPg({ connectionString });
|
|
101
85
|
return new PrismaClient({ adapter });
|
|
102
86
|
}
|
|
103
87
|
/* @endif postgresql */
|
|
@@ -111,19 +95,16 @@ if (process.env.NODE_ENV !== 'production') {
|
|
|
111
95
|
}
|
|
112
96
|
/* @endif sqlite */
|
|
113
97
|
/* @if postgresql */
|
|
114
|
-
// Use global
|
|
115
|
-
const
|
|
116
|
-
export const db = globalThis.__db ?? createPrismaClient(pool);
|
|
98
|
+
// Use global singleton for hot reload in development
|
|
99
|
+
export const db = globalThis.__db ?? createPrismaClient();
|
|
117
100
|
|
|
118
101
|
if (process.env.NODE_ENV !== 'production') {
|
|
119
|
-
globalThis.__pool = pool;
|
|
120
102
|
globalThis.__db = db;
|
|
121
103
|
}
|
|
122
104
|
|
|
123
|
-
// Graceful shutdown -
|
|
105
|
+
// Graceful shutdown - disconnect Prisma on process exit
|
|
124
106
|
const shutdown = async () => {
|
|
125
107
|
await db.$disconnect();
|
|
126
|
-
await pool.end();
|
|
127
108
|
process.exit(0);
|
|
128
109
|
};
|
|
129
110
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:16-alpine
|
|
4
|
+
container_name: __PROJECT_NAME__-postgres
|
|
5
|
+
restart: unless-stopped
|
|
6
|
+
environment:
|
|
7
|
+
POSTGRES_USER: ${DATABASE_USER:-user}
|
|
8
|
+
POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-password}
|
|
9
|
+
POSTGRES_DB: ${DATABASE_NAME:-__PROJECT_NAME__}
|
|
10
|
+
ports:
|
|
11
|
+
- "${DATABASE_PORT:-5432}:5432"
|
|
12
|
+
volumes:
|
|
13
|
+
- postgres_data:/var/lib/postgresql/data
|
|
14
|
+
healthcheck:
|
|
15
|
+
test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USER:-user} -d ${DATABASE_NAME:-__PROJECT_NAME__}"]
|
|
16
|
+
interval: 10s
|
|
17
|
+
timeout: 5s
|
|
18
|
+
retries: 5
|
|
19
|
+
|
|
20
|
+
volumes:
|
|
21
|
+
postgres_data:
|
|
@@ -69,6 +69,12 @@ export const postProcedures = procedures('posts', {
|
|
|
69
69
|
|
|
70
70
|
Then register in `src/procedures/index.ts` and add to collections in `src/index.ts`.
|
|
71
71
|
|
|
72
|
+
## Prisma 7 Configuration
|
|
73
|
+
|
|
74
|
+
This project uses Prisma 7 which has breaking changes:
|
|
75
|
+
- Database URL is configured in `prisma.config.ts`, NOT in `schema.prisma`
|
|
76
|
+
- NEVER add `url` property to the datasource block in `schema.prisma`
|
|
77
|
+
|
|
72
78
|
### Frontend Development (apps/web)
|
|
73
79
|
|
|
74
80
|
**Creating a new route:**
|
|
@@ -68,6 +68,12 @@ export const postProcedures = procedures('posts', {
|
|
|
68
68
|
|
|
69
69
|
Then register in `src/procedures/index.ts` and add to collections in `src/index.ts`.
|
|
70
70
|
|
|
71
|
+
## Prisma 7 Configuration
|
|
72
|
+
|
|
73
|
+
This project uses Prisma 7 which has breaking changes:
|
|
74
|
+
- Database URL is configured in `prisma.config.ts`, NOT in `schema.prisma`
|
|
75
|
+
- NEVER add `url` property to the datasource block in `schema.prisma`
|
|
76
|
+
|
|
71
77
|
### Frontend Development (apps/web)
|
|
72
78
|
|
|
73
79
|
**Creating a new route:**
|
|
@@ -44,15 +44,69 @@ __PROJECT_NAME__/
|
|
|
44
44
|
|
|
45
45
|
### Server Actions
|
|
46
46
|
- Defined in `app/actions/` with `'use server'` directive
|
|
47
|
-
- Type-safe with Zod validation
|
|
47
|
+
- Type-safe with Zod validation
|
|
48
48
|
- Can be called directly from client components
|
|
49
49
|
|
|
50
|
+
### Validated Actions (Recommended)
|
|
51
|
+
Use `validated()` for secure server actions with built-in protection:
|
|
52
|
+
```typescript
|
|
53
|
+
// app/actions/users.ts
|
|
54
|
+
'use server';
|
|
55
|
+
import { validated, validatedMutation, validatedQuery } from '@veloxts/web/server';
|
|
56
|
+
import { z } from 'zod';
|
|
57
|
+
|
|
58
|
+
// Public query (no auth required)
|
|
59
|
+
export const searchUsers = validatedQuery(
|
|
60
|
+
z.object({ query: z.string().optional() }),
|
|
61
|
+
async (input) => {
|
|
62
|
+
return db.user.findMany({ where: { name: { contains: input.query } } });
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Protected mutation (requires auth by default)
|
|
67
|
+
export const updateUser = validatedMutation(
|
|
68
|
+
z.object({ id: z.string(), name: z.string() }),
|
|
69
|
+
async (input, ctx) => {
|
|
70
|
+
// ctx.user is typed and available
|
|
71
|
+
return db.user.update({ where: { id: input.id }, data: input });
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Custom security options
|
|
76
|
+
export const createUser = validated(
|
|
77
|
+
CreateUserSchema,
|
|
78
|
+
async (input) => { /* ... */ },
|
|
79
|
+
{
|
|
80
|
+
rateLimit: { maxRequests: 10, windowMs: 60_000 },
|
|
81
|
+
maxInputSize: 10 * 1024,
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// With role-based authorization
|
|
86
|
+
export const deleteUser = validated(
|
|
87
|
+
DeleteUserSchema,
|
|
88
|
+
async (input) => { /* ... */ },
|
|
89
|
+
{
|
|
90
|
+
requireAuth: true,
|
|
91
|
+
requireRoles: ['admin'],
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Security features:
|
|
97
|
+
- **Input validation** - Zod schema validation
|
|
98
|
+
- **Input sanitization** - Prototype pollution prevention
|
|
99
|
+
- **Input size limits** - DoS protection (default 1MB)
|
|
100
|
+
- **Rate limiting** - Sliding window per IP
|
|
101
|
+
- **Authentication** - Optional via `requireAuth: true`
|
|
102
|
+
- **Authorization** - Custom callbacks via `authorize`
|
|
103
|
+
|
|
50
104
|
### Procedure Bridge Pattern
|
|
51
105
|
Server actions can bridge to API procedures for code reuse:
|
|
52
106
|
```typescript
|
|
53
107
|
// app/actions/posts.ts
|
|
54
108
|
'use server';
|
|
55
|
-
import { action } from '@veloxts/web';
|
|
109
|
+
import { action } from '@veloxts/web/server';
|
|
56
110
|
import { postProcedures } from '@/api/procedures/posts';
|
|
57
111
|
|
|
58
112
|
export const createPost = action.fromProcedure(
|