create-velox-app 0.6.96 → 0.6.98

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # create-velox-app
2
2
 
3
+ ## 0.6.98
4
+
5
+ ### Patch Changes
6
+
7
+ - feat(router): nested resource schema relations with .hasOne() / .hasMany()
8
+
9
+ ## 0.6.97
10
+
11
+ ### Patch Changes
12
+
13
+ - sync zod dependency version
14
+
3
15
  ## 0.6.96
4
16
 
5
17
  ### Patch Changes
@@ -56,6 +56,9 @@ function generateRoutesTs() {
56
56
  function generateConfigDatabase(config) {
57
57
  return compileTemplate('api/config/database.ts', config);
58
58
  }
59
+ function generateProfileProcedures() {
60
+ return compileTemplate('api/procedures/profiles.auth.ts', AUTH_CONFIG);
61
+ }
59
62
  function generateHealthProcedures() {
60
63
  return compileTemplate('api/procedures/health.ts', AUTH_CONFIG);
61
64
  }
@@ -101,6 +104,7 @@ export function generateAuthTemplate(config) {
101
104
  { path: 'apps/api/src/procedures/health.ts', content: generateHealthProcedures() },
102
105
  { path: 'apps/api/src/procedures/auth.ts', content: generateAuthProcedures() },
103
106
  { path: 'apps/api/src/procedures/users.ts', content: generateUserProceduresWithAuth() },
107
+ { path: 'apps/api/src/procedures/profiles.ts', content: generateProfileProcedures() },
104
108
  { path: 'apps/api/src/schemas/user.ts', content: generateUserSchema() },
105
109
  { path: 'apps/api/src/schemas/auth.ts', content: generateAuthSchema() },
106
110
  { path: 'apps/api/src/schemas/health.ts', content: generateHealthSchema() },
@@ -125,6 +125,9 @@ function generateUserProcedures() {
125
125
  function generateAuthProcedures() {
126
126
  return compileTemplate('rsc-auth/src/api/procedures/auth.ts', RSC_CONFIG);
127
127
  }
128
+ function generateProfileProcedures() {
129
+ return compileTemplate('rsc-auth/src/api/procedures/profiles.ts', RSC_CONFIG);
130
+ }
128
131
  // Schemas
129
132
  function generateUserSchemas() {
130
133
  return compileTemplate('rsc-auth/src/api/schemas/user.ts', RSC_CONFIG);
@@ -186,6 +189,7 @@ export function generateRscAuthTemplate(config) {
186
189
  { path: 'src/api/procedures/health.ts', content: generateHealthProcedures() },
187
190
  { path: 'src/api/procedures/users.ts', content: generateUserProcedures() },
188
191
  { path: 'src/api/procedures/auth.ts', content: generateAuthProcedures() },
192
+ { path: 'src/api/procedures/profiles.ts', content: generateProfileProcedures() },
189
193
  { path: 'src/api/schemas/user.ts', content: generateUserSchemas() },
190
194
  { path: 'src/api/schemas/auth.ts', content: generateAuthSchemas() },
191
195
  { path: 'src/api/utils/auth.ts', content: generateAuthUtils() },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-velox-app",
3
- "version": "0.6.96",
3
+ "version": "0.6.98",
4
4
  "description": "Project scaffolder for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Profile Procedures (Resource API Example)
3
+ *
4
+ * Demonstrates field-level visibility using tagged resource schemas:
5
+ * - Public: GET /api/profiles/:id → { id, name }
6
+ * Uses handler-level projection: resource(data, Schema.public)
7
+ * - Authenticated: GET /api/profiles/:id/full → { id, name, email }
8
+ * Uses procedure-level auto-projection: .resource(Schema.authenticated)
9
+ */
10
+
11
+ import {
12
+ authenticatedNarrow,
13
+ NotFoundError,
14
+ procedure,
15
+ procedures,
16
+ resource,
17
+ resourceSchema,
18
+ z,
19
+ } from '@veloxts/velox';
20
+
21
+ // ============================================================================
22
+ // Resource Schema (field-level visibility)
23
+ // ============================================================================
24
+
25
+ const UserProfileSchema = resourceSchema()
26
+ .public('id', z.string().uuid())
27
+ .public('name', z.string())
28
+ .authenticated('email', z.string().email())
29
+ .build();
30
+
31
+ // ============================================================================
32
+ // Profile Procedures
33
+ // ============================================================================
34
+
35
+ export const profileProcedures = procedures('profiles', {
36
+ // Public: GET /api/profiles/:id → { id, name }
37
+ // Handler-level projection: resource(data, Schema.public) returns projected data directly
38
+ getProfile: procedure()
39
+ .input(z.object({ id: z.string().uuid() }))
40
+ .resource(UserProfileSchema.public)
41
+ .query(async ({ input, ctx }) => {
42
+ const user = await ctx.db.user.findUnique({ where: { id: input.id } });
43
+ if (!user) throw new NotFoundError(`User '${input.id}' not found`);
44
+ return resource(user, UserProfileSchema.public);
45
+ }),
46
+
47
+ // Authenticated: GET /api/profiles/:id/full → { id, name, email }
48
+ // Procedure-level auto-projection: .resource(Schema.authenticated) auto-projects the return value
49
+ getFullProfile: procedure()
50
+ .rest({ method: 'GET', path: '/profiles/:id/full' })
51
+ .guardNarrow(authenticatedNarrow)
52
+ .input(z.object({ id: z.string().uuid() }))
53
+ .resource(UserProfileSchema.authenticated)
54
+ .query(async ({ input, ctx }) => {
55
+ const user = await ctx.db.user.findUnique({ where: { id: input.id } });
56
+ if (!user) throw new NotFoundError(`User '${input.id}' not found`);
57
+ return user;
58
+ }),
59
+ });
@@ -18,13 +18,15 @@ import { createRouter, extractRoutes } from '@veloxts/velox';
18
18
 
19
19
  import { authProcedures } from './procedures/auth.js';
20
20
  import { healthProcedures } from './procedures/health.js';
21
+ import { profileProcedures } from './procedures/profiles.js';
21
22
  import { userProcedures } from './procedures/users.js';
22
23
 
23
24
  // Create router and collections from procedure definitions
24
25
  export const { collections, router } = createRouter(
25
26
  healthProcedures,
26
27
  authProcedures,
27
- userProcedures
28
+ userProcedures,
29
+ profileProcedures
28
30
  );
29
31
 
30
32
  export type AppRouter = typeof router;
@@ -34,4 +34,8 @@ export const routes: RouteMap = {
34
34
  patchUser: { method: 'PATCH', path: '/users/:id', kind: 'mutation' },
35
35
  deleteUser: { method: 'DELETE', path: '/users/:id', kind: 'mutation' },
36
36
  },
37
+ profiles: {
38
+ getProfile: { method: 'GET', path: '/profiles/:id', kind: 'query' },
39
+ getFullProfile: { method: 'GET', path: '/profiles/:id/full', kind: 'query' },
40
+ },
37
41
  };
@@ -15,11 +15,17 @@ import { createH3ApiHandler } from '@veloxts/web/adapters';
15
15
  import { db } from './database.js';
16
16
  import { authProcedures } from './procedures/auth.js';
17
17
  import { healthProcedures } from './procedures/health.js';
18
+ import { profileProcedures } from './procedures/profiles.js';
18
19
  import { userProcedures } from './procedures/users.js';
19
20
  import { getJwtSecrets, parseUserRoles, tokenStore } from './utils/auth.js';
20
21
 
21
22
  // Export router type for frontend type safety
22
- const router = { health: healthProcedures, users: userProcedures, auth: authProcedures };
23
+ const router = {
24
+ health: healthProcedures,
25
+ users: userProcedures,
26
+ auth: authProcedures,
27
+ profiles: profileProcedures,
28
+ };
23
29
  export type AppRouter = typeof router;
24
30
 
25
31
  /**
@@ -74,7 +80,7 @@ export default createH3ApiHandler({
74
80
 
75
81
  // Register REST routes from procedures
76
82
  app.routes(
77
- rest([healthProcedures, userProcedures, authProcedures], {
83
+ rest([healthProcedures, userProcedures, authProcedures, profileProcedures], {
78
84
  prefix: '', // No prefix - Vinxi handles /api/*
79
85
  })
80
86
  );
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Profile Procedures (Resource API Example)
3
+ *
4
+ * Demonstrates field-level visibility using tagged resource schemas:
5
+ * - Public: GET /api/profiles/:id → { id, name }
6
+ * Uses handler-level projection: resource(data, Schema.public)
7
+ * - Authenticated: GET /api/profiles/:id/full → { id, name, email }
8
+ * Uses procedure-level auto-projection: .resource(Schema.authenticated)
9
+ */
10
+
11
+ import { authenticatedNarrow } from '@veloxts/auth';
12
+ import { procedure, procedures, resource, resourceSchema } from '@veloxts/router';
13
+ import { z } from 'zod';
14
+
15
+ import { db } from '../database.js';
16
+
17
+ // ============================================================================
18
+ // Resource Schema (field-level visibility)
19
+ // ============================================================================
20
+
21
+ const UserProfileSchema = resourceSchema()
22
+ .public('id', z.string().uuid())
23
+ .public('name', z.string())
24
+ .authenticated('email', z.string().email())
25
+ .build();
26
+
27
+ // ============================================================================
28
+ // Profile Procedures
29
+ // ============================================================================
30
+
31
+ export const profileProcedures = procedures('profiles', {
32
+ // Public: GET /api/profiles/:id → { id, name }
33
+ // Handler-level projection: resource(data, Schema.public) returns projected data directly
34
+ getProfile: procedure()
35
+ .input(z.object({ id: z.string().uuid() }))
36
+ .resource(UserProfileSchema.public)
37
+ .query(async ({ input }) => {
38
+ const user = await db.user.findUnique({ where: { id: input.id } });
39
+ if (!user) {
40
+ throw Object.assign(new Error('User not found'), { statusCode: 404 });
41
+ }
42
+ return resource(user, UserProfileSchema.public);
43
+ }),
44
+
45
+ // Authenticated: GET /api/profiles/:id/full → { id, name, email }
46
+ // Procedure-level auto-projection: .resource(Schema.authenticated) auto-projects the return value
47
+ getFullProfile: procedure()
48
+ .rest({ method: 'GET', path: '/profiles/:id/full' })
49
+ .guardNarrow(authenticatedNarrow)
50
+ .input(z.object({ id: z.string().uuid() }))
51
+ .resource(UserProfileSchema.authenticated)
52
+ .query(async ({ input }) => {
53
+ const user = await db.user.findUnique({ where: { id: input.id } });
54
+ if (!user) {
55
+ throw Object.assign(new Error('User not found'), { statusCode: 404 });
56
+ }
57
+ return user;
58
+ }),
59
+ });