create-velox-app 0.4.7 → 0.4.9

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 (43) hide show
  1. package/dist/index.js.map +1 -1
  2. package/dist/templates/compiler.d.ts.map +1 -1
  3. package/dist/templates/compiler.js.map +1 -1
  4. package/dist/templates/index.d.ts.map +1 -1
  5. package/dist/templates/index.js +9 -1
  6. package/dist/templates/index.js.map +1 -1
  7. package/dist/templates/placeholders.d.ts +5 -0
  8. package/dist/templates/placeholders.d.ts.map +1 -1
  9. package/dist/templates/placeholders.js +14 -3
  10. package/dist/templates/placeholders.js.map +1 -1
  11. package/dist/templates/shared/web-base.d.ts +1 -0
  12. package/dist/templates/shared/web-base.d.ts.map +1 -1
  13. package/dist/templates/shared/web-base.js +5 -0
  14. package/dist/templates/shared/web-base.js.map +1 -1
  15. package/dist/templates/trpc.d.ts +15 -0
  16. package/dist/templates/trpc.d.ts.map +1 -0
  17. package/dist/templates/trpc.js +89 -0
  18. package/dist/templates/trpc.js.map +1 -0
  19. package/dist/templates/types.d.ts +1 -1
  20. package/dist/templates/types.d.ts.map +1 -1
  21. package/dist/templates/types.js +6 -0
  22. package/dist/templates/types.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/templates/source/api/config/auth.ts +7 -0
  25. package/src/templates/source/api/index.auth.ts +9 -2
  26. package/src/templates/source/api/index.default.ts +2 -1
  27. package/src/templates/source/api/index.trpc.ts +64 -0
  28. package/src/templates/source/api/prisma.config.ts +1 -0
  29. package/src/templates/source/api/procedures/auth.ts +10 -4
  30. package/src/templates/source/api/procedures/health.ts +1 -1
  31. package/src/templates/source/api/procedures/users.auth.ts +24 -68
  32. package/src/templates/source/api/procedures/users.default.ts +28 -58
  33. package/src/templates/source/api/schemas/user.ts +9 -4
  34. package/src/templates/source/web/App.module.css +6 -2
  35. package/src/templates/source/web/api.ts +42 -0
  36. package/src/templates/source/web/main.tsx +43 -12
  37. package/src/templates/source/web/package.json +2 -1
  38. package/src/templates/source/web/routes/__root.tsx +24 -6
  39. package/src/templates/source/web/routes/about.tsx +8 -2
  40. package/src/templates/source/web/routes/index.auth.tsx +22 -24
  41. package/src/templates/source/web/routes/index.default.tsx +12 -18
  42. package/src/templates/source/web/routes/users.tsx +64 -49
  43. package/src/templates/source/web/vite.config.ts +4 -3
@@ -1,8 +1,9 @@
1
1
  import { createFileRoute } from '@tanstack/react-router';
2
- import { useQuery, useMutation, useQueryClient } from '@veloxts/client/react';
2
+ import { useQueryClient } from '@veloxts/client/react';
3
3
  import { useState } from 'react';
4
- import type { AppRouter } from '../../../api/src/index.js';
4
+
5
5
  import styles from '@/App.module.css';
6
+ import { api } from '@/api';
6
7
 
7
8
  export const Route = createFileRoute('/')({
8
9
  component: HomePage,
@@ -17,18 +18,13 @@ function HomePage() {
17
18
  const [error, setError] = useState('');
18
19
 
19
20
  // Check if user is logged in
20
- const { data: user, isLoading } = useQuery<AppRouter, 'auth', 'getMe'>(
21
- 'auth',
22
- 'getMe',
23
- {},
24
- { retry: false }
25
- );
21
+ const { data: user, isLoading } = api.auth.getMe.useQuery({}, { retry: false });
26
22
 
27
- const login = useMutation<AppRouter, 'auth', 'createSession'>('auth', 'createSession', {
23
+ const login = api.auth.createSession.useMutation({
28
24
  onSuccess: (data) => {
29
25
  localStorage.setItem('token', data.accessToken);
30
26
  localStorage.setItem('refreshToken', data.refreshToken);
31
- queryClient.invalidateQueries({ queryKey: ['auth', 'getMe'] });
27
+ api.auth.getMe.invalidate(undefined, queryClient);
32
28
  setError('');
33
29
  },
34
30
  onError: (err) => {
@@ -36,11 +32,11 @@ function HomePage() {
36
32
  },
37
33
  });
38
34
 
39
- const register = useMutation<AppRouter, 'auth', 'createAccount'>('auth', 'createAccount', {
35
+ const register = api.auth.createAccount.useMutation({
40
36
  onSuccess: (data) => {
41
37
  localStorage.setItem('token', data.accessToken);
42
38
  localStorage.setItem('refreshToken', data.refreshToken);
43
- queryClient.invalidateQueries({ queryKey: ['auth', 'getMe'] });
39
+ api.auth.getMe.invalidate(undefined, queryClient);
44
40
  setError('');
45
41
  },
46
42
  onError: (err) => {
@@ -48,11 +44,11 @@ function HomePage() {
48
44
  },
49
45
  });
50
46
 
51
- const logout = useMutation<AppRouter, 'auth', 'deleteSession'>('auth', 'deleteSession', {
47
+ const logout = api.auth.deleteSession.useMutation({
52
48
  onSuccess: () => {
53
49
  localStorage.removeItem('token');
54
50
  localStorage.removeItem('refreshToken');
55
- queryClient.setQueryData(['auth', 'getMe'], null);
51
+ api.auth.getMe.setData({}, null, queryClient);
56
52
  },
57
53
  });
58
54
 
@@ -87,14 +83,18 @@ function HomePage() {
87
83
  <div className={styles.cards}>
88
84
  <div className={styles.card}>
89
85
  <h2>Your Profile</h2>
90
- <p><strong>ID:</strong> {user.id}</p>
91
- <p><strong>Roles:</strong> {user.roles?.join(', ') || 'user'}</p>
86
+ <p>
87
+ <strong>ID:</strong> {user.id}
88
+ </p>
89
+ <p>
90
+ <strong>Roles:</strong> {user.roles?.join(', ') || 'user'}
91
+ </p>
92
92
  </div>
93
93
 
94
94
  <div className={styles.card}>
95
95
  <h2>Actions</h2>
96
96
  <button
97
- onClick={() => logout.mutate()}
97
+ onClick={() => logout.mutate({})}
98
98
  className={styles.button}
99
99
  disabled={logout.isPending}
100
100
  >
@@ -111,9 +111,7 @@ function HomePage() {
111
111
  <div className={styles.container}>
112
112
  <div className={styles.hero}>
113
113
  <h1 className={styles.title}>Welcome to VeloxTS</h1>
114
- <p className={styles.subtitle}>
115
- Full-stack TypeScript with authentication.
116
- </p>
114
+ <p className={styles.subtitle}>Full-stack TypeScript with authentication.</p>
117
115
  </div>
118
116
 
119
117
  <div className={styles.authCard}>
@@ -171,14 +169,14 @@ function HomePage() {
171
169
  >
172
170
  {login.isPending || register.isPending
173
171
  ? 'Please wait...'
174
- : isLogin ? 'Sign In' : 'Create Account'}
172
+ : isLogin
173
+ ? 'Sign In'
174
+ : 'Create Account'}
175
175
  </button>
176
176
  </form>
177
177
 
178
178
  {!isLogin && (
179
- <p className={styles.formHint}>
180
- Password: 12+ chars, uppercase, lowercase, number
181
- </p>
179
+ <p className={styles.formHint}>Password: 12+ chars, uppercase, lowercase, number</p>
182
180
  )}
183
181
  </div>
184
182
  </div>
@@ -1,26 +1,20 @@
1
1
  import { createFileRoute } from '@tanstack/react-router';
2
- import { useQuery } from '@veloxts/client/react';
3
- import type { AppRouter } from '../../../api/src/index.js';
2
+
4
3
  import styles from '@/App.module.css';
4
+ import { api } from '@/api';
5
5
 
6
6
  export const Route = createFileRoute('/')({
7
7
  component: HomePage,
8
8
  });
9
9
 
10
10
  function HomePage() {
11
- const { data: health, isLoading, error } = useQuery<AppRouter, 'health', 'check'>(
12
- 'health',
13
- 'check',
14
- {}
15
- );
11
+ const { data: health, isLoading, error } = api.health.check.useQuery({});
16
12
 
17
13
  return (
18
14
  <div className={styles.container}>
19
15
  <div className={styles.hero}>
20
16
  <h1 className={styles.title}>Welcome to VeloxTS</h1>
21
- <p className={styles.subtitle}>
22
- Full-stack TypeScript, beautifully simple.
23
- </p>
17
+ <p className={styles.subtitle}>Full-stack TypeScript, beautifully simple.</p>
24
18
  </div>
25
19
 
26
20
  <div className={styles.cards}>
@@ -31,19 +25,19 @@ function HomePage() {
31
25
  ) : error ? (
32
26
  <p className={styles.error}>Disconnected</p>
33
27
  ) : (
34
- <p className={styles.success}>
35
- {health?.status === 'ok' ? 'Connected' : 'Unknown'}
36
- </p>
37
- )}
38
- {health && (
39
- <p className={styles.meta}>v{health.version}</p>
28
+ <p className={styles.success}>{health?.status === 'ok' ? 'Connected' : 'Unknown'}</p>
40
29
  )}
30
+ {health && <p className={styles.meta}>v{health.version}</p>}
41
31
  </div>
42
32
 
43
33
  <div className={styles.card}>
44
34
  <h2>Get Started</h2>
45
- <p>Edit <code>apps/api/src/procedures</code> to add API endpoints.</p>
46
- <p>Edit <code>apps/web/src/routes</code> to add pages.</p>
35
+ <p>
36
+ Edit <code>apps/api/src/procedures</code> to add API endpoints.
37
+ </p>
38
+ <p>
39
+ Edit <code>apps/web/src/routes</code> to add pages.
40
+ </p>
47
41
  </div>
48
42
 
49
43
  <div className={styles.card}>
@@ -1,70 +1,85 @@
1
1
  /**
2
- * Users Page - Demonstrates type-safe data fetching with VeloxTS hooks
2
+ * Users Page - Demonstrates React 19 patterns with VeloxTS
3
+ *
4
+ * Uses useSuspenseQuery with Suspense boundaries for cleaner component code.
5
+ * Data is guaranteed to be available when the component renders.
3
6
  */
4
7
 
5
8
  import { createFileRoute } from '@tanstack/react-router';
6
- import { useQuery } from '@veloxts/client/react';
7
- import type { AppRouter } from '../../../api/src/index.js';
9
+ import { Suspense } from 'react';
10
+ import { ErrorBoundary } from 'react-error-boundary';
11
+
8
12
  import styles from '@/App.module.css';
13
+ import { api } from '@/api';
9
14
 
10
15
  export const Route = createFileRoute('/users')({
11
16
  component: UsersPage,
12
17
  });
13
18
 
14
19
  function UsersPage() {
15
- const { data, isLoading, error } = useQuery<AppRouter, 'users', 'listUsers'>(
16
- 'users',
17
- 'listUsers',
18
- {}
19
- );
20
-
21
20
  return (
22
21
  <div className={styles.container}>
23
22
  <div className={styles.hero}>
24
23
  <h1 className={styles.title}>Users</h1>
25
- <p className={styles.subtitle}>
26
- Type-safe data fetching with VeloxTS hooks
27
- </p>
24
+ <p className={styles.subtitle}>Type-safe data fetching with React 19 Suspense</p>
28
25
  </div>
29
26
 
30
- {isLoading ? (
31
- <p className={styles.loading}>Loading users...</p>
32
- ) : error ? (
33
- <p className={styles.error}>Error: {error.message}</p>
34
- ) : (
35
- <div className={styles.tableContainer}>
36
- <table className={styles.table}>
37
- <thead>
38
- <tr>
39
- <th>Name</th>
40
- <th>Email</th>
41
- <th>Created</th>
42
- </tr>
43
- </thead>
44
- <tbody>
45
- {data?.data.map((user) => (
46
- <tr key={user.id}>
47
- <td>{user.name}</td>
48
- <td>{user.email}</td>
49
- <td>{new Date(user.createdAt).toLocaleDateString()}</td>
50
- </tr>
51
- ))}
52
- {data?.data.length === 0 && (
53
- <tr>
54
- <td colSpan={3} className={styles.emptyState}>
55
- No users found. Create one via the API!
56
- </td>
57
- </tr>
58
- )}
59
- </tbody>
60
- </table>
61
- {data && (
62
- <p className={styles.meta}>
63
- Page {data.meta.page} - {data.meta.total} total users
64
- </p>
27
+ <ErrorBoundary fallback={<UsersError />}>
28
+ <Suspense fallback={<UsersLoading />}>
29
+ <UsersTable />
30
+ </Suspense>
31
+ </ErrorBoundary>
32
+ </div>
33
+ );
34
+ }
35
+
36
+ /**
37
+ * Users table component - wrapped in Suspense
38
+ *
39
+ * With useSuspenseQuery, data is guaranteed to be available.
40
+ * No need for isLoading/error checks - handled by boundaries.
41
+ */
42
+ function UsersTable() {
43
+ const { data } = api.users.listUsers.useSuspenseQuery({});
44
+
45
+ return (
46
+ <div className={styles.tableContainer}>
47
+ <table className={styles.table}>
48
+ <thead>
49
+ <tr>
50
+ <th>Name</th>
51
+ <th>Email</th>
52
+ <th>Created</th>
53
+ </tr>
54
+ </thead>
55
+ <tbody>
56
+ {data.data.map((user) => (
57
+ <tr key={user.id}>
58
+ <td>{user.name}</td>
59
+ <td>{user.email}</td>
60
+ <td>{new Date(user.createdAt).toLocaleDateString()}</td>
61
+ </tr>
62
+ ))}
63
+ {data.data.length === 0 && (
64
+ <tr>
65
+ <td colSpan={3} className={styles.emptyState}>
66
+ No users found. Create one via the API!
67
+ </td>
68
+ </tr>
65
69
  )}
66
- </div>
67
- )}
70
+ </tbody>
71
+ </table>
72
+ <p className={styles.meta}>
73
+ Page {data.meta.page} - {data.meta.total} total users
74
+ </p>
68
75
  </div>
69
76
  );
70
77
  }
78
+
79
+ function UsersLoading() {
80
+ return <p className={styles.loading}>Loading users...</p>;
81
+ }
82
+
83
+ function UsersError() {
84
+ return <p className={styles.error}>Failed to load users. Please try again.</p>;
85
+ }
@@ -1,8 +1,9 @@
1
- import { defineConfig } from 'vite';
2
- import react from '@vitejs/plugin-react';
3
- import { tanstackRouter } from '@tanstack/router-plugin/vite';
4
1
  import path from 'node:path';
5
2
 
3
+ import { tanstackRouter } from '@tanstack/router-plugin/vite';
4
+ import react from '@vitejs/plugin-react';
5
+ import { defineConfig } from 'vite';
6
+
6
7
  export default defineConfig({
7
8
  plugins: [tanstackRouter(), react()],
8
9
  resolve: {