@veloxts/cli 0.7.4 → 0.7.6
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 +24 -0
- package/dist/generators/base.js +5 -5
- package/dist/generators/generators/layout.js +2 -2
- package/dist/generators/generators/page.js +2 -2
- package/dist/generators/types.d.ts +6 -6
- package/dist/sync/analyzer.js +1 -1
- package/dist/sync/index.js +11 -3
- package/dist/sync/procedure-generator.js +5 -8
- package/dist/sync/prompter.js +4 -1
- package/dist/utils/paths.d.ts +2 -2
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @veloxts/cli
|
|
2
2
|
|
|
3
|
+
## 0.7.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- feat(router): custom access levels for the Resource API + advanced Architectural Patterns
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @veloxts/auth@0.7.6
|
|
10
|
+
- @veloxts/core@0.7.6
|
|
11
|
+
- @veloxts/orm@0.7.6
|
|
12
|
+
- @veloxts/router@0.7.6
|
|
13
|
+
- @veloxts/validation@0.7.6
|
|
14
|
+
|
|
15
|
+
## 0.7.5
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- fix(cli): address sync command review findings
|
|
20
|
+
- Updated dependencies
|
|
21
|
+
- @veloxts/auth@0.7.5
|
|
22
|
+
- @veloxts/core@0.7.5
|
|
23
|
+
- @veloxts/orm@0.7.5
|
|
24
|
+
- @veloxts/router@0.7.5
|
|
25
|
+
- @veloxts/validation@0.7.5
|
|
26
|
+
|
|
3
27
|
## 0.7.4
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/dist/generators/base.js
CHANGED
|
@@ -48,7 +48,7 @@ export async function detectProjectContext(cwd) {
|
|
|
48
48
|
name: 'unknown',
|
|
49
49
|
hasAuth: false,
|
|
50
50
|
database: 'sqlite',
|
|
51
|
-
projectType: '
|
|
51
|
+
projectType: 'vite',
|
|
52
52
|
isVinxiProject: false,
|
|
53
53
|
hasWeb: false,
|
|
54
54
|
};
|
|
@@ -70,11 +70,11 @@ export async function detectProjectContext(cwd) {
|
|
|
70
70
|
const isVinxiProject = hasVinxiMarker || hasAppConfig;
|
|
71
71
|
// Detect project type
|
|
72
72
|
const hasAppPagesDir = existsSync(join(cwd, 'app', 'pages'));
|
|
73
|
-
const projectType = isVinxiProject || hasAppPagesDir ? '
|
|
73
|
+
const projectType = isVinxiProject || hasAppPagesDir ? 'vinxi' : 'vite';
|
|
74
74
|
// Detect directory structure
|
|
75
|
-
const pagesDir = projectType === '
|
|
76
|
-
const layoutsDir = projectType === '
|
|
77
|
-
const actionsDir = projectType === '
|
|
75
|
+
const pagesDir = projectType === 'vinxi' ? 'app/pages' : undefined;
|
|
76
|
+
const layoutsDir = projectType === 'vinxi' ? 'app/layouts' : undefined;
|
|
77
|
+
const actionsDir = projectType === 'vinxi' ? 'app/actions' : 'src/actions';
|
|
78
78
|
return {
|
|
79
79
|
name: packageJson.name ?? 'velox-app',
|
|
80
80
|
hasAuth,
|
|
@@ -85,8 +85,8 @@ Examples:
|
|
|
85
85
|
*/
|
|
86
86
|
async generate(config) {
|
|
87
87
|
// Check if this is a full-stack project
|
|
88
|
-
if (config.project.projectType !== '
|
|
89
|
-
throw new GeneratorError(GeneratorErrorCode.PROJECT_STRUCTURE, 'Layout generator requires a full-stack VeloxTS project.', 'Create a full-stack project with: npx create-velox-app my-app --
|
|
88
|
+
if (config.project.projectType !== 'vinxi') {
|
|
89
|
+
throw new GeneratorError(GeneratorErrorCode.PROJECT_STRUCTURE, 'Layout generator requires a full-stack VeloxTS project.', 'Create a full-stack project with: npx create-velox-app my-app --rsc');
|
|
90
90
|
}
|
|
91
91
|
const context = this.createContext(config);
|
|
92
92
|
const files = [];
|
|
@@ -77,8 +77,8 @@ Examples:
|
|
|
77
77
|
*/
|
|
78
78
|
async generate(config) {
|
|
79
79
|
// Check if this is a full-stack project
|
|
80
|
-
if (config.project.projectType !== '
|
|
81
|
-
throw new GeneratorError(GeneratorErrorCode.PROJECT_STRUCTURE, 'Page generator requires a full-stack VeloxTS project.', 'Create a full-stack project with: npx create-velox-app my-app --
|
|
80
|
+
if (config.project.projectType !== 'vinxi') {
|
|
81
|
+
throw new GeneratorError(GeneratorErrorCode.PROJECT_STRUCTURE, 'Page generator requires a full-stack VeloxTS project.', 'Create a full-stack project with: npx create-velox-app my-app --rsc');
|
|
82
82
|
}
|
|
83
83
|
const context = this.createContext(config);
|
|
84
84
|
const files = [];
|
|
@@ -33,9 +33,9 @@ export interface EntityNames {
|
|
|
33
33
|
readonly humanReadablePlural: string;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Rendering strategy: 'vite' for client-side SPA, 'vinxi' for server-side RSC
|
|
37
37
|
*/
|
|
38
|
-
export type ProjectType = '
|
|
38
|
+
export type ProjectType = 'vite' | 'vinxi';
|
|
39
39
|
/**
|
|
40
40
|
* Project-level context for template generation
|
|
41
41
|
*/
|
|
@@ -46,17 +46,17 @@ export interface ProjectContext {
|
|
|
46
46
|
readonly hasAuth: boolean;
|
|
47
47
|
/** Database type from configuration */
|
|
48
48
|
readonly database: 'sqlite' | 'postgresql' | 'mysql';
|
|
49
|
-
/**
|
|
49
|
+
/** Rendering strategy: 'vite' for client-side SPA, 'vinxi' for server-side RSC */
|
|
50
50
|
readonly projectType: ProjectType;
|
|
51
51
|
/** Whether this is a Vinxi/RSC project */
|
|
52
52
|
readonly isVinxiProject: boolean;
|
|
53
53
|
/** Whether @veloxts/web is installed */
|
|
54
54
|
readonly hasWeb: boolean;
|
|
55
|
-
/** Root directory for pages (app/pages for
|
|
55
|
+
/** Root directory for pages (app/pages for vinxi, undefined for vite) */
|
|
56
56
|
readonly pagesDir?: string;
|
|
57
|
-
/** Root directory for layouts (app/layouts for
|
|
57
|
+
/** Root directory for layouts (app/layouts for vinxi, undefined for vite) */
|
|
58
58
|
readonly layoutsDir?: string;
|
|
59
|
-
/** Root directory for actions (app/actions for
|
|
59
|
+
/** Root directory for actions (app/actions for vinxi, src/actions for vite) */
|
|
60
60
|
readonly actionsDir?: string;
|
|
61
61
|
}
|
|
62
62
|
/**
|
package/dist/sync/analyzer.js
CHANGED
|
@@ -117,7 +117,7 @@ function findClosingBrace(content, openIndex) {
|
|
|
117
117
|
return i;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
-
|
|
120
|
+
throw new Error('Prisma schema has unbalanced braces — model block not properly closed');
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
123
123
|
* Parse every field-like line from a model body into `RawFieldLine` objects.
|
package/dist/sync/index.js
CHANGED
|
@@ -52,7 +52,7 @@ export async function executeSync(projectRoot, options) {
|
|
|
52
52
|
// ── Stage 3: Prompt ─────────────────────────────────────────
|
|
53
53
|
let choices;
|
|
54
54
|
if (options.force) {
|
|
55
|
-
choices = models.map((model) => buildDefaultChoices(model));
|
|
55
|
+
choices = models.map((model) => buildDefaultChoices(model, existing));
|
|
56
56
|
}
|
|
57
57
|
else {
|
|
58
58
|
const prompted = await promptAllModels(models, existing);
|
|
@@ -152,6 +152,7 @@ async function generateFiles(snapshot, plan, projectRoot, options) {
|
|
|
152
152
|
}
|
|
153
153
|
// ── Register procedures in router ───────────────────────────
|
|
154
154
|
if (!options.skipRegistration) {
|
|
155
|
+
let registrationFailures = 0;
|
|
155
156
|
for (const reg of plan.registrations) {
|
|
156
157
|
try {
|
|
157
158
|
const result = registerProcedures(projectRoot, reg.entityName, reg.procedureVarName, false);
|
|
@@ -159,14 +160,19 @@ async function generateFiles(snapshot, plan, projectRoot, options) {
|
|
|
159
160
|
registered.push(reg.procedureVarName);
|
|
160
161
|
}
|
|
161
162
|
else if (result.error) {
|
|
163
|
+
registrationFailures++;
|
|
162
164
|
errors.push(`Registration ${reg.procedureVarName}: ${result.error}`);
|
|
163
165
|
}
|
|
164
166
|
}
|
|
165
167
|
catch (err) {
|
|
168
|
+
registrationFailures++;
|
|
166
169
|
const msg = err instanceof Error ? err.message : String(err);
|
|
167
170
|
errors.push(`Registration ${reg.procedureVarName}: ${msg}`);
|
|
168
171
|
}
|
|
169
172
|
}
|
|
173
|
+
if (registrationFailures > 0 && registrationFailures === plan.registrations.length) {
|
|
174
|
+
p.log.warn('All procedure registrations failed. You may need to manually register in src/index.ts.');
|
|
175
|
+
}
|
|
170
176
|
}
|
|
171
177
|
// ── Display results ─────────────────────────────────────────
|
|
172
178
|
displayResults(created, overwritten, registered, errors, projectRoot);
|
|
@@ -237,11 +243,13 @@ function displayResults(created, overwritten, registered, errors, projectRoot) {
|
|
|
237
243
|
/**
|
|
238
244
|
* Build default ModelChoices for a model (used when --force is set).
|
|
239
245
|
* Generates all CRUD, uses output strategy, includes no relations.
|
|
246
|
+
* Sets 'regenerate' if existing files are detected, so rollback works correctly.
|
|
240
247
|
*/
|
|
241
|
-
function buildDefaultChoices(model) {
|
|
248
|
+
function buildDefaultChoices(model, existing) {
|
|
249
|
+
const hasExisting = existing.procedures.has(model.name) || existing.schemas.has(model.name);
|
|
242
250
|
return {
|
|
243
251
|
model: model.name,
|
|
244
|
-
action: 'generate',
|
|
252
|
+
action: hasExisting ? 'regenerate' : 'generate',
|
|
245
253
|
outputStrategy: 'output',
|
|
246
254
|
crud: {
|
|
247
255
|
get: true,
|
|
@@ -152,18 +152,15 @@ function generateListProcedure(plan, names) {
|
|
|
152
152
|
const lines = [];
|
|
153
153
|
lines.push(` ${procName}: procedure()`);
|
|
154
154
|
lines.push(` .input(List${names.pascalPlural}Schema)`);
|
|
155
|
-
|
|
156
|
-
lines.push(` .query(async ({ input, ctx }) => {`);
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
lines.push(` .query(async ({ input, ctx }) => {`);
|
|
160
|
-
}
|
|
155
|
+
lines.push(` .query(async ({ input, ctx }) => {`);
|
|
161
156
|
lines.push(` const { page, perPage } = input;`);
|
|
162
157
|
lines.push(` const [items, total] = await Promise.all([`);
|
|
163
158
|
lines.push(` ctx.db.${names.camel}.findMany({`);
|
|
164
159
|
lines.push(` skip: (page - 1) * perPage,`);
|
|
165
160
|
lines.push(` take: perPage,`);
|
|
166
|
-
|
|
161
|
+
if (plan.model.hasTimestamps) {
|
|
162
|
+
lines.push(` orderBy: { createdAt: 'desc' },`);
|
|
163
|
+
}
|
|
167
164
|
if (hasIncludes) {
|
|
168
165
|
lines.push(generateIncludeBlock(plan.includeRelations, 10));
|
|
169
166
|
}
|
|
@@ -204,7 +201,7 @@ function generateUpdateProcedure(_plan, names) {
|
|
|
204
201
|
const procName = `update${names.pascal}`;
|
|
205
202
|
const lines = [];
|
|
206
203
|
lines.push(` ${procName}: procedure()`);
|
|
207
|
-
lines.push(` .input(
|
|
204
|
+
lines.push(` .input(Update${names.pascal}Schema.extend({ id: z.string().uuid() }))`);
|
|
208
205
|
lines.push(` .mutation(async ({ input, ctx }) => {`);
|
|
209
206
|
lines.push(` const { id, ...data } = input;`);
|
|
210
207
|
lines.push(` return ctx.db.${names.camel}.update({ where: { id }, data });`);
|
package/dist/sync/prompter.js
CHANGED
|
@@ -163,7 +163,10 @@ async function promptOutputStrategy(model) {
|
|
|
163
163
|
message: 'Output strategy?',
|
|
164
164
|
options: [
|
|
165
165
|
{ value: 'output', label: '.output() \u2014 Same fields for all users' },
|
|
166
|
-
{
|
|
166
|
+
{
|
|
167
|
+
value: 'resource',
|
|
168
|
+
label: '.expose() \u2014 Different fields per access level (resource schema)',
|
|
169
|
+
},
|
|
167
170
|
],
|
|
168
171
|
initialValue: defaultToResource ? 'resource' : 'output',
|
|
169
172
|
});
|
package/dist/utils/paths.d.ts
CHANGED
|
@@ -73,7 +73,7 @@ export declare function isVeloxProject(cwd?: string): Promise<boolean>;
|
|
|
73
73
|
/**
|
|
74
74
|
* Project type detection result
|
|
75
75
|
*/
|
|
76
|
-
export interface
|
|
76
|
+
export interface ProjectDetectionResult {
|
|
77
77
|
/** Whether this is a Vinxi-based RSC project */
|
|
78
78
|
isVinxi: boolean;
|
|
79
79
|
/** Whether @veloxts/web is installed */
|
|
@@ -88,7 +88,7 @@ export interface ProjectType {
|
|
|
88
88
|
* 1. Vinxi markers in dependencies (vinxi, @vinxi/server-functions, @veloxts/web)
|
|
89
89
|
* 2. app.config.ts or app.config.js (Vinxi configuration)
|
|
90
90
|
*/
|
|
91
|
-
export declare function detectProjectType(cwd?: string): Promise<
|
|
91
|
+
export declare function detectProjectType(cwd?: string): Promise<ProjectDetectionResult>;
|
|
92
92
|
/**
|
|
93
93
|
* Read and parse a JSON file
|
|
94
94
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veloxts/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"description": "Developer tooling and CLI commands for VeloxTS framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"pluralize": "8.0.0",
|
|
42
42
|
"tsx": "4.21.0",
|
|
43
43
|
"yaml": "2.8.2",
|
|
44
|
-
"@veloxts/core": "0.7.
|
|
45
|
-
"@veloxts/
|
|
46
|
-
"@veloxts/
|
|
47
|
-
"@veloxts/
|
|
48
|
-
"@veloxts/
|
|
44
|
+
"@veloxts/core": "0.7.6",
|
|
45
|
+
"@veloxts/router": "0.7.6",
|
|
46
|
+
"@veloxts/orm": "0.7.6",
|
|
47
|
+
"@veloxts/validation": "0.7.6",
|
|
48
|
+
"@veloxts/auth": "0.7.6"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"@prisma/client": ">=7.0.0"
|