@veloxts/cli 0.6.64 → 0.6.65

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
  # @veloxts/cli
2
2
 
3
+ ## 0.6.65
4
+
5
+ ### Patch Changes
6
+
7
+ - improve ai integration and simplify api router definition
8
+ - Updated dependencies
9
+ - @veloxts/auth@0.6.65
10
+ - @veloxts/core@0.6.65
11
+ - @veloxts/orm@0.6.65
12
+ - @veloxts/router@0.6.65
13
+ - @veloxts/validation@0.6.65
14
+
3
15
  ## 0.6.64
4
16
 
5
17
  ### Patch Changes
@@ -14,6 +14,7 @@ export { createMailGenerator, MailGenerator } from './mail.js';
14
14
  export { createMiddlewareGenerator, MiddlewareGenerator } from './middleware.js';
15
15
  export { createMigrationGenerator, MigrationGenerator } from './migration.js';
16
16
  export { createModelGenerator, ModelGenerator } from './model.js';
17
+ export { createNamespaceGenerator, NamespaceGenerator } from './namespace.js';
17
18
  export { createPageGenerator, PageGenerator } from './page.js';
18
19
  export { createPolicyGenerator, PolicyGenerator } from './policy.js';
19
20
  export { createProcedureGenerator, ProcedureGenerator } from './procedure.js';
@@ -15,6 +15,7 @@ import { createMailGenerator } from './mail.js';
15
15
  import { createMiddlewareGenerator } from './middleware.js';
16
16
  import { createMigrationGenerator } from './migration.js';
17
17
  import { createModelGenerator } from './model.js';
18
+ import { createNamespaceGenerator } from './namespace.js';
18
19
  import { createPageGenerator } from './page.js';
19
20
  import { createPolicyGenerator } from './policy.js';
20
21
  import { createProcedureGenerator } from './procedure.js';
@@ -39,6 +40,7 @@ export { createMailGenerator, MailGenerator } from './mail.js';
39
40
  export { createMiddlewareGenerator, MiddlewareGenerator } from './middleware.js';
40
41
  export { createMigrationGenerator, MigrationGenerator } from './migration.js';
41
42
  export { createModelGenerator, ModelGenerator } from './model.js';
43
+ export { createNamespaceGenerator, NamespaceGenerator } from './namespace.js';
42
44
  export { createPageGenerator, PageGenerator } from './page.js';
43
45
  export { createPolicyGenerator, PolicyGenerator } from './policy.js';
44
46
  export { createProcedureGenerator, ProcedureGenerator } from './procedure.js';
@@ -61,6 +63,8 @@ export function registerBuiltinGenerators() {
61
63
  registerGenerator(createProcedureGenerator());
62
64
  // Register model generator
63
65
  registerGenerator(createModelGenerator());
66
+ // Register namespace generator
67
+ registerGenerator(createNamespaceGenerator());
64
68
  // Register migration generator
65
69
  registerGenerator(createMigrationGenerator());
66
70
  // Register schema generator
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Namespace Generator
3
+ *
4
+ * Scaffolds a complete procedure namespace with schema file.
5
+ *
6
+ * Usage:
7
+ * velox make namespace <name> [options]
8
+ * velox m ns <name> [options]
9
+ *
10
+ * Examples:
11
+ * velox make namespace products # Empty namespace with schema
12
+ * velox make namespace orders --example # With example CRUD procedures
13
+ */
14
+ import { BaseGenerator } from '../base.js';
15
+ import { type NamespaceOptions } from '../templates/namespace.js';
16
+ import type { GeneratorConfig, GeneratorMetadata, GeneratorOption, GeneratorOutput } from '../types.js';
17
+ /**
18
+ * Namespace generator - creates procedure namespace with schema
19
+ */
20
+ export declare class NamespaceGenerator extends BaseGenerator<NamespaceOptions> {
21
+ readonly metadata: GeneratorMetadata;
22
+ readonly options: ReadonlyArray<GeneratorOption>;
23
+ /**
24
+ * Validate and transform raw options
25
+ */
26
+ validateOptions(raw: Record<string, unknown>): NamespaceOptions;
27
+ /**
28
+ * Generate namespace files
29
+ */
30
+ generate(config: GeneratorConfig<NamespaceOptions>): Promise<GeneratorOutput>;
31
+ /**
32
+ * Build post-instructions based on registration result
33
+ */
34
+ private buildPostInstructions;
35
+ }
36
+ /**
37
+ * Create a new namespace generator instance
38
+ */
39
+ export declare function createNamespaceGenerator(): NamespaceGenerator;
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Namespace Generator
3
+ *
4
+ * Scaffolds a complete procedure namespace with schema file.
5
+ *
6
+ * Usage:
7
+ * velox make namespace <name> [options]
8
+ * velox m ns <name> [options]
9
+ *
10
+ * Examples:
11
+ * velox make namespace products # Empty namespace with schema
12
+ * velox make namespace orders --example # With example CRUD procedures
13
+ */
14
+ import { BaseGenerator } from '../base.js';
15
+ import { getNamespaceInstructions, getNamespaceProcedurePath, getNamespaceSchemaPath, getNamespaceTestPath, namespaceSchemaTemplate, namespaceTemplate, namespaceTestTemplate, } from '../templates/namespace.js';
16
+ import { detectRouterPattern, isProcedureRegistered, registerProcedures, } from '../utils/router-integration.js';
17
+ // ============================================================================
18
+ // Generator Implementation
19
+ // ============================================================================
20
+ /**
21
+ * Namespace generator - creates procedure namespace with schema
22
+ */
23
+ export class NamespaceGenerator extends BaseGenerator {
24
+ metadata = {
25
+ name: 'namespace',
26
+ description: 'Generate procedure namespace + schema (for existing models or external APIs)',
27
+ longDescription: `
28
+ Scaffold a procedure namespace with a separate Zod schema file.
29
+
30
+ Use this when you already have a Prisma model or are working with external data.
31
+ For new database entities, use "velox make resource" instead (recommended).
32
+
33
+ Creates:
34
+ • Schema file (src/schemas/{kebab}.ts)
35
+ • Procedure file (src/procedures/{plural}.ts)
36
+ • Test file (src/procedures/__tests__/{plural}.test.ts)
37
+ • Auto-registers in router.ts
38
+
39
+ When to use:
40
+ ✓ You have an EXISTING Prisma model and need procedures for it
41
+ ✓ Calling an external API (no database model needed)
42
+ ✓ You want separate schema files (cleaner than inline)
43
+ ✓ Need procedure collection without Prisma injection
44
+
45
+ When NOT to use:
46
+ • Creating a new database entity → use "resource" instead (recommended)
47
+ • Adding a single procedure → use "procedure" instead
48
+
49
+ Comparison:
50
+ • "resource" = Prisma model + schema + procedures + tests (RECOMMENDED)
51
+ • "namespace" = schema + procedures (no Prisma injection)
52
+ • "procedure" = procedures only (inline schemas)
53
+
54
+ Examples:
55
+ velox make namespace products # Empty namespace for existing model
56
+ velox make namespace orders --example # With example CRUD procedures
57
+ velox m ns external-api -e # For external API integration
58
+ `,
59
+ aliases: ['ns'],
60
+ category: 'resource',
61
+ };
62
+ options = [
63
+ {
64
+ name: 'example',
65
+ short: 'e',
66
+ description: 'Include example CRUD procedures',
67
+ type: 'boolean',
68
+ default: false,
69
+ },
70
+ {
71
+ name: 'with-tests',
72
+ short: 't',
73
+ description: 'Generate test file (default: true)',
74
+ type: 'boolean',
75
+ default: true,
76
+ },
77
+ {
78
+ name: 'skip-registration',
79
+ short: 'S',
80
+ description: 'Skip auto-registering the procedure in router.ts',
81
+ type: 'boolean',
82
+ default: false,
83
+ },
84
+ ];
85
+ /**
86
+ * Validate and transform raw options
87
+ */
88
+ validateOptions(raw) {
89
+ return {
90
+ withExample: Boolean(raw.example ?? raw.withExample ?? false),
91
+ withTests: raw['with-tests'] !== false && raw.withTests !== false,
92
+ skipRegistration: Boolean(raw['skip-registration'] ?? raw.skipRegistration ?? false),
93
+ };
94
+ }
95
+ /**
96
+ * Generate namespace files
97
+ */
98
+ async generate(config) {
99
+ const context = this.createContext(config);
100
+ const { entity } = context;
101
+ const { options } = config;
102
+ // Collect files to generate
103
+ const files = [];
104
+ // Generate schema file
105
+ const schemaContent = namespaceSchemaTemplate(context);
106
+ files.push({
107
+ path: getNamespaceSchemaPath(entity.kebab),
108
+ content: schemaContent,
109
+ });
110
+ // Generate procedure file
111
+ const procedureContent = namespaceTemplate(context);
112
+ files.push({
113
+ path: getNamespaceProcedurePath(entity.plural),
114
+ content: procedureContent,
115
+ });
116
+ // Generate test file (default: true)
117
+ if (options.withTests) {
118
+ const testContent = namespaceTestTemplate(context);
119
+ files.push({
120
+ path: getNamespaceTestPath(entity.plural),
121
+ content: testContent,
122
+ });
123
+ }
124
+ // Try auto-registration if not skipped
125
+ let registrationResult = null;
126
+ if (!options.skipRegistration && !config.dryRun) {
127
+ const procedureVar = `${entity.camel}Procedures`;
128
+ // Check if already registered
129
+ if (!isProcedureRegistered(config.cwd, procedureVar)) {
130
+ // Detect router pattern
131
+ const pattern = detectRouterPattern(config.cwd);
132
+ if (pattern.type !== 'unknown') {
133
+ // Perform registration
134
+ registrationResult = registerProcedures(config.cwd, entity.kebab, procedureVar, false // not a dry run
135
+ );
136
+ }
137
+ }
138
+ }
139
+ // Generate post-creation instructions
140
+ const postInstructions = this.buildPostInstructions(entity, options, registrationResult);
141
+ return {
142
+ files,
143
+ postInstructions,
144
+ };
145
+ }
146
+ /**
147
+ * Build post-instructions based on registration result
148
+ */
149
+ buildPostInstructions(entity, options, registrationResult) {
150
+ const procedureVar = `${entity.camel}Procedures`;
151
+ // If registration was successful
152
+ if (registrationResult?.success) {
153
+ const modifiedFiles = registrationResult.modifiedFiles.map((f) => ` - ${f}`).join('\n');
154
+ const testFileInfo = options.withTests
155
+ ? `
156
+ - src/procedures/__tests__/${entity.plural}.test.ts`
157
+ : '';
158
+ let instructions = `
159
+ ✓ Namespace ${procedureVar} auto-registered!
160
+
161
+ Created files:
162
+ - src/schemas/${entity.kebab}.ts
163
+ - src/procedures/${entity.plural}.ts${testFileInfo}
164
+
165
+ Modified files:
166
+ ${modifiedFiles}`;
167
+ const nextStep = options.withExample
168
+ ? 'Customize the example procedures'
169
+ : `Add procedures to src/procedures/${entity.plural}.ts`;
170
+ const testStep = options.withTests ? '\n 4. Run tests: pnpm test' : '';
171
+ instructions += `
172
+
173
+ Next steps:
174
+ 1. Add fields to your schema in src/schemas/${entity.kebab}.ts
175
+ 2. Add the ${entity.pascal} model to your Prisma schema
176
+ 3. ${nextStep}${testStep}`;
177
+ return instructions;
178
+ }
179
+ // If registration was skipped
180
+ if (options.skipRegistration) {
181
+ return getNamespaceInstructions(entity.plural, entity.pascal, entity.kebab);
182
+ }
183
+ // If registration failed
184
+ if (registrationResult?.error) {
185
+ return `
186
+ ⚠ Auto-registration failed: ${registrationResult.error}
187
+
188
+ ${getNamespaceInstructions(entity.plural, entity.pascal, entity.kebab)}`;
189
+ }
190
+ // Default: show manual instructions
191
+ return getNamespaceInstructions(entity.plural, entity.pascal, entity.kebab);
192
+ }
193
+ }
194
+ // ============================================================================
195
+ // Export
196
+ // ============================================================================
197
+ /**
198
+ * Create a new namespace generator instance
199
+ */
200
+ export function createNamespaceGenerator() {
201
+ return new NamespaceGenerator();
202
+ }
@@ -24,17 +24,35 @@ import { detectRouterPattern, isProcedureRegistered, registerProcedures, } from
24
24
  export class ProcedureGenerator extends BaseGenerator {
25
25
  metadata = {
26
26
  name: 'procedure',
27
- description: 'Generate a procedure file for API endpoints',
27
+ description: 'Generate a procedure file (for single or additional procedures)',
28
28
  longDescription: `
29
- Scaffold a VeloxTS procedure file that defines API endpoints.
29
+ Scaffold a standalone procedure file with inline schemas.
30
30
 
31
- By default, scaffolds a simple procedure with just a get operation.
32
- Use --crud to scaffold full CRUD operations (get, list, create, update, patch, delete).
31
+ Use this for adding individual procedures or extending existing namespaces.
32
+ For new database entities, use "velox make resource" instead (recommended).
33
+
34
+ Creates:
35
+ • Procedure file (src/procedures/{plural}.ts) with inline Zod schemas
36
+ • Auto-registers in router.ts
37
+
38
+ When to use:
39
+ ✓ Adding a single procedure to your API
40
+ ✓ Quick prototyping (inline schemas, no separate files)
41
+ ✓ Extending an existing namespace with additional procedures
42
+
43
+ When NOT to use:
44
+ • Creating a new database entity → use "resource" instead (recommended)
45
+ • Want separate schema files → use "namespace" instead
46
+
47
+ Comparison:
48
+ • "resource" = Prisma model + schema + procedures + tests (RECOMMENDED)
49
+ • "namespace" = schema + procedures (separate files)
50
+ • "procedure" = procedures only (inline schemas, self-contained)
33
51
 
34
52
  Examples:
35
- velox make procedure users # Simple get procedure
36
- velox make procedure posts --crud # Full CRUD procedures
37
- velox m p comments --crud --paginated # CRUD with pagination
53
+ velox make procedure health # Single health check procedure
54
+ velox make procedure users --crud # CRUD with inline schemas
55
+ velox m p analytics # Quick procedure for analytics
38
56
  `,
39
57
  aliases: ['p', 'proc'],
40
58
  category: 'resource',
@@ -27,7 +27,37 @@ import { createSnapshot, rollback, saveOriginal, trackCreated, } from '../utils/
27
27
  // ============================================================================
28
28
  const metadata = {
29
29
  name: 'resource',
30
- description: 'Generate complete resource (model, schema, procedures, tests)',
30
+ description: 'Generate complete resource (model, schema, procedures, tests) [RECOMMENDED]',
31
+ longDescription: `
32
+ RECOMMENDED: The resource generator is the preferred way to create new entities.
33
+
34
+ This is VeloxTS's equivalent to Laravel's "php artisan make:model -a" - it scaffolds
35
+ everything you need for a new entity in one command.
36
+
37
+ Creates:
38
+ • Prisma model (auto-injected into schema.prisma)
39
+ • Zod validation schemas (src/schemas/{name}.schema.ts)
40
+ • CRUD procedures (src/procedures/{name}.ts)
41
+ • Test file (src/procedures/__tests__/{name}.test.ts)
42
+ • Auto-registers in router.ts
43
+ • Optional: runs Prisma migration
44
+
45
+ When to use:
46
+ ✓ Creating a new database entity (users, posts, products, etc.)
47
+ ✓ You want the full VeloxTS stack with tests
48
+ ✓ Default choice for most scenarios
49
+
50
+ When NOT to use:
51
+ • You have an existing Prisma model → use "namespace" instead
52
+ • You're calling an external API (no database) → use "namespace" instead
53
+ • Adding a single procedure to existing namespace → use "procedure" instead
54
+
55
+ Examples:
56
+ velox make resource Post # Full stack for Post entity
57
+ velox make resource Comment -i # Interactive field definition
58
+ velox make resource Order --soft-delete # With soft delete support
59
+ velox m r Product --auto-migrate # Auto-run migration
60
+ `,
31
61
  category: 'resource',
32
62
  aliases: ['r', 'res'],
33
63
  };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Namespace Template
3
+ *
4
+ * Generates a procedure namespace (collection) with corresponding schema file.
5
+ * Unlike the procedure generator, this creates a minimal scaffold ready for
6
+ * custom procedures rather than pre-defined CRUD operations.
7
+ */
8
+ import type { TemplateContext, TemplateFunction } from '../types.js';
9
+ export interface NamespaceOptions {
10
+ /** Skip auto-registering in router.ts */
11
+ skipRegistration: boolean;
12
+ /** Include example procedure */
13
+ withExample: boolean;
14
+ /** Generate test file */
15
+ withTests: boolean;
16
+ }
17
+ /**
18
+ * Generate namespace procedure file
19
+ */
20
+ export declare const namespaceTemplate: TemplateFunction<NamespaceOptions>;
21
+ /**
22
+ * Generate schema file for the namespace
23
+ */
24
+ export declare function namespaceSchemaTemplate(ctx: TemplateContext<NamespaceOptions>): string;
25
+ /**
26
+ * Generate test file for the namespace procedures
27
+ */
28
+ export declare function namespaceTestTemplate(ctx: TemplateContext<NamespaceOptions>): string;
29
+ /**
30
+ * Get the file path for the procedure file
31
+ */
32
+ export declare function getNamespaceProcedurePath(entityPlural: string): string;
33
+ /**
34
+ * Get the file path for the test file
35
+ */
36
+ export declare function getNamespaceTestPath(entityPlural: string): string;
37
+ /**
38
+ * Get the file path for the schema file
39
+ */
40
+ export declare function getNamespaceSchemaPath(entityKebab: string): string;
41
+ /**
42
+ * Generate post-generation instructions
43
+ */
44
+ export declare function getNamespaceInstructions(entityPlural: string, entityPascal: string, entityKebab: string): string;
@@ -0,0 +1,444 @@
1
+ /**
2
+ * Namespace Template
3
+ *
4
+ * Generates a procedure namespace (collection) with corresponding schema file.
5
+ * Unlike the procedure generator, this creates a minimal scaffold ready for
6
+ * custom procedures rather than pre-defined CRUD operations.
7
+ */
8
+ // ============================================================================
9
+ // Template Functions
10
+ // ============================================================================
11
+ /**
12
+ * Generate namespace procedure file
13
+ */
14
+ export const namespaceTemplate = (ctx) => {
15
+ const { entity, options } = ctx;
16
+ if (options.withExample) {
17
+ return generateWithExample(ctx);
18
+ }
19
+ return `/**
20
+ * ${entity.pascal} Procedures
21
+ *
22
+ * Namespace for ${entity.humanReadable}-related API endpoints.
23
+ */
24
+
25
+ import { procedure, procedures, z } from '@veloxts/velox';
26
+ import {
27
+ ${entity.pascal}Schema,
28
+ Create${entity.pascal}Input,
29
+ Update${entity.pascal}Input,
30
+ } from '../schemas/${entity.kebab}.js';
31
+
32
+ // ============================================================================
33
+ // Procedures
34
+ // ============================================================================
35
+
36
+ export const ${entity.camel}Procedures = procedures('${entity.plural}', {
37
+ // Add your procedures here
38
+ //
39
+ // Examples:
40
+ //
41
+ // get${entity.pascal}: procedure()
42
+ // .input(z.object({ id: z.string().uuid() }))
43
+ // .output(${entity.pascal}Schema.nullable())
44
+ // .query(async ({ input, ctx }) => {
45
+ // return ctx.db.${entity.camel}.findUnique({ where: { id: input.id } });
46
+ // }),
47
+ //
48
+ // list${entity.pascalPlural}: procedure()
49
+ // .output(z.array(${entity.pascal}Schema))
50
+ // .query(async ({ ctx }) => {
51
+ // return ctx.db.${entity.camel}.findMany();
52
+ // }),
53
+ //
54
+ // create${entity.pascal}: procedure()
55
+ // .input(Create${entity.pascal}Input)
56
+ // .output(${entity.pascal}Schema)
57
+ // .mutation(async ({ input, ctx }) => {
58
+ // return ctx.db.${entity.camel}.create({ data: input });
59
+ // }),
60
+ });
61
+ `;
62
+ };
63
+ /**
64
+ * Generate namespace with example procedure
65
+ */
66
+ function generateWithExample(ctx) {
67
+ const { entity } = ctx;
68
+ return `/**
69
+ * ${entity.pascal} Procedures
70
+ *
71
+ * Namespace for ${entity.humanReadable}-related API endpoints.
72
+ */
73
+
74
+ import { procedure, procedures, z } from '@veloxts/velox';
75
+ import {
76
+ ${entity.pascal}Schema,
77
+ Create${entity.pascal}Input,
78
+ Update${entity.pascal}Input,
79
+ } from '../schemas/${entity.kebab}.js';
80
+
81
+ // ============================================================================
82
+ // Procedures
83
+ // ============================================================================
84
+
85
+ export const ${entity.camel}Procedures = procedures('${entity.plural}', {
86
+ /**
87
+ * Get a single ${entity.humanReadable} by ID
88
+ * GET /${entity.plural}/:id
89
+ */
90
+ get${entity.pascal}: procedure()
91
+ .input(z.object({ id: z.string().uuid() }))
92
+ .output(${entity.pascal}Schema.nullable())
93
+ .query(async ({ input, ctx }) => {
94
+ return ctx.db.${entity.camel}.findUnique({
95
+ where: { id: input.id },
96
+ });
97
+ }),
98
+
99
+ /**
100
+ * List all ${entity.humanReadablePlural}
101
+ * GET /${entity.plural}
102
+ */
103
+ list${entity.pascalPlural}: procedure()
104
+ .output(z.array(${entity.pascal}Schema))
105
+ .query(async ({ ctx }) => {
106
+ return ctx.db.${entity.camel}.findMany();
107
+ }),
108
+
109
+ /**
110
+ * Create a new ${entity.humanReadable}
111
+ * POST /${entity.plural}
112
+ */
113
+ create${entity.pascal}: procedure()
114
+ .input(Create${entity.pascal}Input)
115
+ .output(${entity.pascal}Schema)
116
+ .mutation(async ({ input, ctx }) => {
117
+ return ctx.db.${entity.camel}.create({
118
+ data: input,
119
+ });
120
+ }),
121
+
122
+ /**
123
+ * Update an existing ${entity.humanReadable}
124
+ * PUT /${entity.plural}/:id
125
+ */
126
+ update${entity.pascal}: procedure()
127
+ .input(z.object({ id: z.string().uuid() }).merge(Update${entity.pascal}Input))
128
+ .output(${entity.pascal}Schema)
129
+ .mutation(async ({ input, ctx }) => {
130
+ const { id, ...data } = input;
131
+ return ctx.db.${entity.camel}.update({
132
+ where: { id },
133
+ data,
134
+ });
135
+ }),
136
+
137
+ /**
138
+ * Delete a ${entity.humanReadable}
139
+ * DELETE /${entity.plural}/:id
140
+ */
141
+ delete${entity.pascal}: procedure()
142
+ .input(z.object({ id: z.string().uuid() }))
143
+ .output(z.object({ success: z.boolean() }))
144
+ .mutation(async ({ input, ctx }) => {
145
+ await ctx.db.${entity.camel}.delete({
146
+ where: { id: input.id },
147
+ });
148
+ return { success: true };
149
+ }),
150
+ });
151
+ `;
152
+ }
153
+ /**
154
+ * Generate schema file for the namespace
155
+ */
156
+ export function namespaceSchemaTemplate(ctx) {
157
+ const { entity } = ctx;
158
+ return `/**
159
+ * ${entity.pascal} Schemas
160
+ *
161
+ * Zod validation schemas for ${entity.humanReadable} entities.
162
+ */
163
+
164
+ import { z } from 'zod';
165
+
166
+ // ============================================================================
167
+ // Base Schema
168
+ // ============================================================================
169
+
170
+ /**
171
+ * ${entity.pascal} entity schema
172
+ */
173
+ export const ${entity.pascal}Schema = z.object({
174
+ id: z.string().uuid(),
175
+ // TODO: Add ${entity.humanReadable} fields
176
+ createdAt: z.coerce.date(),
177
+ updatedAt: z.coerce.date(),
178
+ });
179
+
180
+ export type ${entity.pascal} = z.infer<typeof ${entity.pascal}Schema>;
181
+
182
+ // ============================================================================
183
+ // Input Schemas
184
+ // ============================================================================
185
+
186
+ /**
187
+ * Create ${entity.pascal} input
188
+ */
189
+ export const Create${entity.pascal}Input = z.object({
190
+ // TODO: Add required fields for creating a ${entity.humanReadable}
191
+ });
192
+
193
+ export type Create${entity.pascal}InputType = z.infer<typeof Create${entity.pascal}Input>;
194
+
195
+ /**
196
+ * Update ${entity.pascal} input
197
+ */
198
+ export const Update${entity.pascal}Input = z.object({
199
+ // TODO: Add fields for updating a ${entity.humanReadable}
200
+ }).partial();
201
+
202
+ export type Update${entity.pascal}InputType = z.infer<typeof Update${entity.pascal}Input>;
203
+ `;
204
+ }
205
+ // ============================================================================
206
+ // Test Template
207
+ // ============================================================================
208
+ /**
209
+ * Generate test file for the namespace procedures
210
+ */
211
+ export function namespaceTestTemplate(ctx) {
212
+ const { entity, options } = ctx;
213
+ if (options.withExample) {
214
+ return generateTestWithCrud(entity);
215
+ }
216
+ return `/**
217
+ * ${entity.pascal} Procedures - Tests
218
+ *
219
+ * Unit tests for ${entity.humanReadable} API procedures.
220
+ */
221
+
222
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
223
+ // import { ${entity.camel}Procedures } from '../${entity.plural}.js';
224
+
225
+ // ============================================================================
226
+ // Test Setup
227
+ // ============================================================================
228
+
229
+ describe('${entity.pascal} Procedures', () => {
230
+ const mockCtx = {
231
+ db: {
232
+ ${entity.camel}: {
233
+ findUnique: vi.fn(),
234
+ findMany: vi.fn(),
235
+ create: vi.fn(),
236
+ update: vi.fn(),
237
+ delete: vi.fn(),
238
+ },
239
+ },
240
+ };
241
+
242
+ beforeEach(() => {
243
+ vi.clearAllMocks();
244
+ });
245
+
246
+ // Add your tests here
247
+ // Example:
248
+ //
249
+ // describe('get${entity.pascal}', () => {
250
+ // it('should return a ${entity.humanReadable} by id', async () => {
251
+ // const mock${entity.pascal} = { id: 'test-uuid' };
252
+ // mockCtx.db.${entity.camel}.findUnique.mockResolvedValue(mock${entity.pascal});
253
+ //
254
+ // // TODO: Implement test
255
+ // });
256
+ // });
257
+
258
+ it('should have mock context ready', () => {
259
+ expect(mockCtx.db.${entity.camel}).toBeDefined();
260
+ });
261
+ });
262
+ `;
263
+ }
264
+ /**
265
+ * Generate test file with CRUD tests for --example flag
266
+ */
267
+ function generateTestWithCrud(entity) {
268
+ return `/**
269
+ * ${entity.pascal} Procedures - Tests
270
+ *
271
+ * Unit tests for ${entity.humanReadable} API procedures.
272
+ */
273
+
274
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
275
+ // import { ${entity.camel}Procedures } from '../${entity.plural}.js';
276
+
277
+ // ============================================================================
278
+ // Test Setup
279
+ // ============================================================================
280
+
281
+ describe('${entity.pascal} Procedures', () => {
282
+ const mockCtx = {
283
+ db: {
284
+ ${entity.camel}: {
285
+ findUnique: vi.fn(),
286
+ findMany: vi.fn(),
287
+ count: vi.fn(),
288
+ create: vi.fn(),
289
+ update: vi.fn(),
290
+ delete: vi.fn(),
291
+ },
292
+ },
293
+ };
294
+
295
+ beforeEach(() => {
296
+ vi.clearAllMocks();
297
+ });
298
+
299
+ // ============================================================================
300
+ // Query Tests
301
+ // ============================================================================
302
+
303
+ describe('get${entity.pascal}', () => {
304
+ it('should return a ${entity.humanReadable} by id', async () => {
305
+ const mock${entity.pascal} = {
306
+ id: 'test-uuid',
307
+ createdAt: new Date(),
308
+ updatedAt: new Date(),
309
+ };
310
+ mockCtx.db.${entity.camel}.findUnique.mockResolvedValue(mock${entity.pascal});
311
+
312
+ // TODO: Call procedure and assert
313
+ // const result = await ${entity.camel}Procedures.procedures.get${entity.pascal}.handler({
314
+ // input: { id: 'test-uuid' },
315
+ // ctx: mockCtx,
316
+ // });
317
+ // expect(result).toEqual(mock${entity.pascal});
318
+ expect(mockCtx.db.${entity.camel}.findUnique).toBeDefined();
319
+ });
320
+
321
+ it('should return null for non-existent ${entity.humanReadable}', async () => {
322
+ mockCtx.db.${entity.camel}.findUnique.mockResolvedValue(null);
323
+
324
+ // TODO: Call procedure and assert
325
+ expect(mockCtx.db.${entity.camel}.findUnique).toBeDefined();
326
+ });
327
+ });
328
+
329
+ describe('list${entity.pascalPlural}', () => {
330
+ it('should return all ${entity.humanReadablePlural}', async () => {
331
+ const mock${entity.pascalPlural} = [
332
+ { id: 'uuid-1', createdAt: new Date(), updatedAt: new Date() },
333
+ { id: 'uuid-2', createdAt: new Date(), updatedAt: new Date() },
334
+ ];
335
+ mockCtx.db.${entity.camel}.findMany.mockResolvedValue(mock${entity.pascalPlural});
336
+
337
+ // TODO: Call procedure and assert
338
+ expect(mockCtx.db.${entity.camel}.findMany).toBeDefined();
339
+ });
340
+ });
341
+
342
+ // ============================================================================
343
+ // Mutation Tests
344
+ // ============================================================================
345
+
346
+ describe('create${entity.pascal}', () => {
347
+ it('should create a new ${entity.humanReadable}', async () => {
348
+ const input = { /* TODO: Add fields */ };
349
+ const created = {
350
+ id: 'new-uuid',
351
+ ...input,
352
+ createdAt: new Date(),
353
+ updatedAt: new Date(),
354
+ };
355
+ mockCtx.db.${entity.camel}.create.mockResolvedValue(created);
356
+
357
+ // TODO: Call procedure and assert
358
+ expect(mockCtx.db.${entity.camel}.create).toBeDefined();
359
+ });
360
+ });
361
+
362
+ describe('update${entity.pascal}', () => {
363
+ it('should update an existing ${entity.humanReadable}', async () => {
364
+ const input = { id: 'test-uuid', /* TODO: Add fields */ };
365
+ const updated = {
366
+ id: 'test-uuid',
367
+ createdAt: new Date(),
368
+ updatedAt: new Date(),
369
+ };
370
+ mockCtx.db.${entity.camel}.update.mockResolvedValue(updated);
371
+
372
+ // TODO: Call procedure and assert
373
+ expect(mockCtx.db.${entity.camel}.update).toBeDefined();
374
+ });
375
+ });
376
+
377
+ describe('delete${entity.pascal}', () => {
378
+ it('should delete a ${entity.humanReadable}', async () => {
379
+ mockCtx.db.${entity.camel}.delete.mockResolvedValue(undefined);
380
+
381
+ // TODO: Call procedure and assert
382
+ expect(mockCtx.db.${entity.camel}.delete).toBeDefined();
383
+ });
384
+ });
385
+ });
386
+ `;
387
+ }
388
+ // ============================================================================
389
+ // Path Helpers
390
+ // ============================================================================
391
+ /**
392
+ * Get the file path for the procedure file
393
+ */
394
+ export function getNamespaceProcedurePath(entityPlural) {
395
+ return `src/procedures/${entityPlural}.ts`;
396
+ }
397
+ /**
398
+ * Get the file path for the test file
399
+ */
400
+ export function getNamespaceTestPath(entityPlural) {
401
+ return `src/procedures/__tests__/${entityPlural}.test.ts`;
402
+ }
403
+ /**
404
+ * Get the file path for the schema file
405
+ */
406
+ export function getNamespaceSchemaPath(entityKebab) {
407
+ return `src/schemas/${entityKebab}.ts`;
408
+ }
409
+ /**
410
+ * Generate post-generation instructions
411
+ */
412
+ export function getNamespaceInstructions(entityPlural, entityPascal, entityKebab) {
413
+ return `
414
+ 1. Add the schema export:
415
+
416
+ // src/schemas/index.ts
417
+ export * from './${entityKebab}.js';
418
+
419
+ 2. Add the procedure export:
420
+
421
+ // src/procedures/index.ts
422
+ export * from './${entityPlural}.js';
423
+
424
+ 3. Register the procedure collection:
425
+
426
+ // src/index.ts (or router.ts)
427
+ import { ${entityPlural.replace(/-/g, '')}Procedures } from './procedures/index.js';
428
+
429
+ const collections = [..., ${entityPlural.replace(/-/g, '')}Procedures];
430
+
431
+ 4. Add the Prisma model:
432
+
433
+ // prisma/schema.prisma
434
+ model ${entityPascal} {
435
+ id String @id @default(uuid())
436
+ createdAt DateTime @default(now())
437
+ updatedAt DateTime @updatedAt
438
+
439
+ @@map("${entityPlural.replace(/-/g, '_')}")
440
+ }
441
+
442
+ Then run: pnpm db:push
443
+ `;
444
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/cli",
3
- "version": "0.6.64",
3
+ "version": "0.6.65",
4
4
  "description": "Developer tooling and CLI commands for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,11 +40,11 @@
40
40
  "picocolors": "1.1.1",
41
41
  "pluralize": "8.0.0",
42
42
  "tsx": "4.21.0",
43
- "@veloxts/core": "0.6.64",
44
- "@veloxts/auth": "0.6.64",
45
- "@veloxts/orm": "0.6.64",
46
- "@veloxts/router": "0.6.64",
47
- "@veloxts/validation": "0.6.64"
43
+ "@veloxts/auth": "0.6.65",
44
+ "@veloxts/orm": "0.6.65",
45
+ "@veloxts/router": "0.6.65",
46
+ "@veloxts/validation": "0.6.65",
47
+ "@veloxts/core": "0.6.65"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@prisma/client": ">=7.0.0"