Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions apps/cli/src/prompts/config-prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,25 +123,6 @@ export async function gatherConfig(
},
);

if (result.backend === "convex") {
result.runtime = "none";
result.database = "none";
result.orm = "none";
result.api = "none";
result.dbSetup = "none";
result.examples = ["todo"];
}

if (result.backend === "none") {
result.runtime = "none";
result.database = "none";
result.orm = "none";
result.api = "none";
result.auth = "none";
result.dbSetup = "none";
result.examples = [];
}

return {
projectName: projectName,
projectDir: projectDir,
Expand Down
122 changes: 112 additions & 10 deletions apps/cli/src/utils/config-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,116 @@ export function validateDatabaseSetup(
}
}

export function validateConvexConstraints(
config: Partial<ProjectConfig>,
providedFlags: Set<string>,
): void {
const { backend } = config;

if (backend !== "convex") {
return;
}

const has = (k: string) => providedFlags.has(k);

if (has("runtime") && config.runtime !== "none") {
exitWithError(
"Convex backend requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.",
);
}

if (has("database") && config.database !== "none") {
exitWithError(
"Convex backend requires '--database none'. Please remove the --database flag or set it to 'none'.",
);
}

if (has("orm") && config.orm !== "none") {
exitWithError(
"Convex backend requires '--orm none'. Please remove the --orm flag or set it to 'none'.",
);
}

if (has("api") && config.api !== "none") {
exitWithError(
"Convex backend requires '--api none'. Please remove the --api flag or set it to 'none'.",
);
}

if (has("dbSetup") && config.dbSetup !== "none") {
exitWithError(
"Convex backend requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.",
);
}

if (has("serverDeploy") && config.serverDeploy !== "none") {
exitWithError(
"Convex backend requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.",
);
}

if (has("auth") && config.auth === "better-auth") {
exitWithError(
"Better-Auth is not compatible with Convex backend. Please use '--auth clerk' or '--auth none'.",
);
}
}
Comment on lines +178 to +231
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: flag-gated checks miss invalid prompt selections (e.g., Convex + Better-Auth).

These constraints only trigger when the corresponding CLI flag was provided. In interactive/prompt flows (no flags), invalid combos like backend=convex + auth=better-auth or runtime!=='none' will slip through. Enforce regardless of flags (guard with value presence to avoid false positives during partial assembly).

Apply this diff:

@@
 export function validateConvexConstraints(
   config: Partial<ProjectConfig>,
   providedFlags: Set<string>,
 ): void {
   const { backend } = config;
 
   if (backend !== "convex") {
     return;
   }
 
-  const has = (k: string) => providedFlags.has(k);
-
-  if (has("runtime") && config.runtime !== "none") {
+  if (config.runtime && config.runtime !== "none") {
     exitWithError(
       "Convex backend requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.",
     );
   }
 
-  if (has("database") && config.database !== "none") {
+  if (config.database && config.database !== "none") {
     exitWithError(
       "Convex backend requires '--database none'. Please remove the --database flag or set it to 'none'.",
     );
   }
 
-  if (has("orm") && config.orm !== "none") {
+  if (config.orm && config.orm !== "none") {
     exitWithError(
       "Convex backend requires '--orm none'. Please remove the --orm flag or set it to 'none'.",
     );
   }
 
-  if (has("api") && config.api !== "none") {
+  if (config.api && config.api !== "none") {
     exitWithError(
       "Convex backend requires '--api none'. Please remove the --api flag or set it to 'none'.",
     );
   }
 
-  if (has("dbSetup") && config.dbSetup !== "none") {
+  if (config.dbSetup && config.dbSetup !== "none") {
     exitWithError(
       "Convex backend requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.",
     );
   }
 
-  if (has("serverDeploy") && config.serverDeploy !== "none") {
+  if (config.serverDeploy && config.serverDeploy !== "none") {
     exitWithError(
       "Convex backend requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.",
     );
   }
 
-  if (has("auth") && config.auth === "better-auth") {
+  if (config.auth === "better-auth") {
     exitWithError(
       "Better-Auth is not compatible with Convex backend. Please use '--auth clerk' or '--auth none'.",
     );
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function validateConvexConstraints(
config: Partial<ProjectConfig>,
providedFlags: Set<string>,
): void {
const { backend } = config;
if (backend !== "convex") {
return;
}
const has = (k: string) => providedFlags.has(k);
if (has("runtime") && config.runtime !== "none") {
exitWithError(
"Convex backend requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.",
);
}
if (has("database") && config.database !== "none") {
exitWithError(
"Convex backend requires '--database none'. Please remove the --database flag or set it to 'none'.",
);
}
if (has("orm") && config.orm !== "none") {
exitWithError(
"Convex backend requires '--orm none'. Please remove the --orm flag or set it to 'none'.",
);
}
if (has("api") && config.api !== "none") {
exitWithError(
"Convex backend requires '--api none'. Please remove the --api flag or set it to 'none'.",
);
}
if (has("dbSetup") && config.dbSetup !== "none") {
exitWithError(
"Convex backend requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.",
);
}
if (has("serverDeploy") && config.serverDeploy !== "none") {
exitWithError(
"Convex backend requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.",
);
}
if (has("auth") && config.auth === "better-auth") {
exitWithError(
"Better-Auth is not compatible with Convex backend. Please use '--auth clerk' or '--auth none'.",
);
}
}
export function validateConvexConstraints(
config: Partial<ProjectConfig>,
providedFlags: Set<string>,
): void {
const { backend } = config;
if (backend !== "convex") {
return;
}
if (config.runtime && config.runtime !== "none") {
exitWithError(
"Convex backend requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.",
);
}
if (config.database && config.database !== "none") {
exitWithError(
"Convex backend requires '--database none'. Please remove the --database flag or set it to 'none'.",
);
}
if (config.orm && config.orm !== "none") {
exitWithError(
"Convex backend requires '--orm none'. Please remove the --orm flag or set it to 'none'.",
);
}
if (config.api && config.api !== "none") {
exitWithError(
"Convex backend requires '--api none'. Please remove the --api flag or set it to 'none'.",
);
}
if (config.dbSetup && config.dbSetup !== "none") {
exitWithError(
"Convex backend requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.",
);
}
if (config.serverDeploy && config.serverDeploy !== "none") {
exitWithError(
"Convex backend requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.",
);
}
if (config.auth === "better-auth") {
exitWithError(
"Better-Auth is not compatible with Convex backend. Please use '--auth clerk' or '--auth none'.",
);
}
}


export function validateBackendNoneConstraints(
config: Partial<ProjectConfig>,
providedFlags: Set<string>,
): void {
const { backend } = config;

if (backend !== "none") {
return;
}

const has = (k: string) => providedFlags.has(k);

if (has("runtime") && config.runtime !== "none") {
exitWithError(
"Backend 'none' requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.",
);
}

if (has("database") && config.database !== "none") {
exitWithError(
"Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.",
);
}

if (has("orm") && config.orm !== "none") {
exitWithError(
"Backend 'none' requires '--orm none'. Please remove the --orm flag or set it to 'none'.",
);
}

if (has("api") && config.api !== "none") {
exitWithError(
"Backend 'none' requires '--api none'. Please remove the --api flag or set it to 'none'.",
);
}

if (has("auth") && config.auth !== "none") {
exitWithError(
"Backend 'none' requires '--auth none'. Please remove the --auth flag or set it to 'none'.",
);
}

if (has("dbSetup") && config.dbSetup !== "none") {
exitWithError(
"Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.",
);
}

if (has("serverDeploy") && config.serverDeploy !== "none") {
exitWithError(
"Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.",
);
}
}
Comment on lines +233 to +286
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Same issue for backend='none': invalid prompt combos bypass checks.

Flag-gating allows backend='none' with api/auth/orm/etc via prompts. Make these checks value-driven, not flag-driven.

Apply this diff:

@@
 export function validateBackendNoneConstraints(
   config: Partial<ProjectConfig>,
   providedFlags: Set<string>,
 ): void {
   const { backend } = config;
 
   if (backend !== "none") {
     return;
   }
 
-  const has = (k: string) => providedFlags.has(k);
-
-  if (has("runtime") && config.runtime !== "none") {
+  if (config.runtime && config.runtime !== "none") {
     exitWithError(
       "Backend 'none' requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.",
     );
   }
 
-  if (has("database") && config.database !== "none") {
+  if (config.database && config.database !== "none") {
     exitWithError(
       "Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.",
     );
   }
 
-  if (has("orm") && config.orm !== "none") {
+  if (config.orm && config.orm !== "none") {
     exitWithError(
       "Backend 'none' requires '--orm none'. Please remove the --orm flag or set it to 'none'.",
     );
   }
 
-  if (has("api") && config.api !== "none") {
+  if (config.api && config.api !== "none") {
     exitWithError(
       "Backend 'none' requires '--api none'. Please remove the --api flag or set it to 'none'.",
     );
   }
 
-  if (has("auth") && config.auth !== "none") {
+  if (config.auth && config.auth !== "none") {
     exitWithError(
       "Backend 'none' requires '--auth none'. Please remove the --auth flag or set it to 'none'.",
     );
   }
 
-  if (has("dbSetup") && config.dbSetup !== "none") {
+  if (config.dbSetup && config.dbSetup !== "none") {
     exitWithError(
       "Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.",
     );
   }
 
-  if (has("serverDeploy") && config.serverDeploy !== "none") {
+  if (config.serverDeploy && config.serverDeploy !== "none") {
     exitWithError(
       "Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.",
     );
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function validateBackendNoneConstraints(
config: Partial<ProjectConfig>,
providedFlags: Set<string>,
): void {
const { backend } = config;
if (backend !== "none") {
return;
}
const has = (k: string) => providedFlags.has(k);
if (has("runtime") && config.runtime !== "none") {
exitWithError(
"Backend 'none' requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.",
);
}
if (has("database") && config.database !== "none") {
exitWithError(
"Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.",
);
}
if (has("orm") && config.orm !== "none") {
exitWithError(
"Backend 'none' requires '--orm none'. Please remove the --orm flag or set it to 'none'.",
);
}
if (has("api") && config.api !== "none") {
exitWithError(
"Backend 'none' requires '--api none'. Please remove the --api flag or set it to 'none'.",
);
}
if (has("auth") && config.auth !== "none") {
exitWithError(
"Backend 'none' requires '--auth none'. Please remove the --auth flag or set it to 'none'.",
);
}
if (has("dbSetup") && config.dbSetup !== "none") {
exitWithError(
"Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.",
);
}
if (has("serverDeploy") && config.serverDeploy !== "none") {
exitWithError(
"Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.",
);
}
}
export function validateBackendNoneConstraints(
config: Partial<ProjectConfig>,
providedFlags: Set<string>,
): void {
const { backend } = config;
if (backend !== "none") {
return;
}
if (config.runtime && config.runtime !== "none") {
exitWithError(
"Backend 'none' requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.",
);
}
if (config.database && config.database !== "none") {
exitWithError(
"Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.",
);
}
if (config.orm && config.orm !== "none") {
exitWithError(
"Backend 'none' requires '--orm none'. Please remove the --orm flag or set it to 'none'.",
);
}
if (config.api && config.api !== "none") {
exitWithError(
"Backend 'none' requires '--api none'. Please remove the --api flag or set it to 'none'.",
);
}
if (config.auth && config.auth !== "none") {
exitWithError(
"Backend 'none' requires '--auth none'. Please remove the --auth flag or set it to 'none'.",
);
}
if (config.dbSetup && config.dbSetup !== "none") {
exitWithError(
"Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.",
);
}
if (config.serverDeploy && config.serverDeploy !== "none") {
exitWithError(
"Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.",
);
}
}
🤖 Prompt for AI Agents
In apps/cli/src/utils/config-validation.ts around lines 233-286, the current
checks gate errors on providedFlags (so prompted values bypass them); change the
logic to be value-driven by removing the providedFlags.has() conditions and
simply validate the config fields themselves when backend === "none" (e.g. if
config.runtime !== "none" then exitWithError(...), likewise for database, orm,
api, auth, dbSetup, serverDeploy), ensuring all non-'none' config values cause
the same error regardless of whether they came from flags or interactive
prompts.


export function validateBackendConstraints(
config: Partial<ProjectConfig>,
providedFlags: Set<string>,
Expand All @@ -201,16 +311,6 @@ export function validateBackendConstraints(
}
}

if (
backend === "convex" &&
config.auth === "better-auth" &&
providedFlags.has("auth")
) {
exitWithError(
"Better-Auth is not compatible with the Convex backend. Please use '--auth clerk' or '--auth none'.",
);
}

if (
providedFlags.has("backend") &&
backend &&
Expand Down Expand Up @@ -287,6 +387,8 @@ export function validateFullConfig(
validateDatabaseOrmAuth(config, providedFlags);
validateDatabaseSetup(config, providedFlags);

validateConvexConstraints(config, providedFlags);
validateBackendNoneConstraints(config, providedFlags);
validateBackendConstraints(config, providedFlags, options);

validateFrontendConstraints(config, providedFlags);
Expand Down
1 change: 0 additions & 1 deletion apps/web/lib/cn.ts

This file was deleted.

4 changes: 4 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"clsx": "^2.1.1",
"convex": "^1.25.4",
"convex-helpers": "^0.1.104",
"culori": "^4.0.2",
"date-fns": "^4.1.0",
"fumadocs-core": "15.6.7",
"fumadocs-mdx": "11.7.3",
Expand All @@ -38,6 +39,7 @@
"nuqs": "^2.4.3",
"papaparse": "^5.5.3",
"posthog-js": "^1.258.5",
"qrcode": "^1.5.4",
"radix-ui": "^1.4.2",
"react": "^19.1.1",
"react-dom": "^19.1.1",
Expand All @@ -52,9 +54,11 @@
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.11",
"@types/culori": "^4.0.0",
"@types/mdx": "^2.0.13",
"@types/node": "24.1.0",
"@types/papaparse": "^5.3.16",
"@types/qrcode": "^1.5.5",
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"eslint": "^9.32.0",
Expand Down
11 changes: 0 additions & 11 deletions apps/web/scripts/generate-analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,23 +242,19 @@ async function generateAnalyticsData() {
cliVersionCounts[cliVersion] =
(cliVersionCounts[cliVersion] || 0) + 1;

// Auth
const auth =
row["*.properties.auth"] === "True" ? "enabled" : "disabled";
authCounts[auth] = (authCounts[auth] || 0) + 1;
if (auth === "enabled") authEnabledCount++;

// Git
const git =
row["*.properties.git"] === "True" ? "enabled" : "disabled";
gitCounts[git] = (gitCounts[git] || 0) + 1;

// Install
const install =
row["*.properties.install"] === "True" ? "enabled" : "disabled";
installCounts[install] = (installCounts[install] || 0) + 1;

// Examples
const examples = [
row["*.properties.examples.0"],
row["*.properties.examples.1"],
Expand All @@ -271,7 +267,6 @@ async function generateAnalyticsData() {
}
}

// Addons
const addons = [
row["*.properties.addons.0"],
row["*.properties.addons.1"],
Expand All @@ -288,23 +283,19 @@ async function generateAnalyticsData() {
}
}

// Runtime
const runtime = row["*.properties.runtime"] || "unknown";
runtimeCounts[runtime] = (runtimeCounts[runtime] || 0) + 1;

// Web Deploy (migrate "workers" to "wrangler")
const webDeploy = row["*.properties.webDeploy"] || "none";
const normalizedWebDeploy =
webDeploy === "workers" ? "wrangler" : webDeploy;
webDeployCounts[normalizedWebDeploy] =
(webDeployCounts[normalizedWebDeploy] || 0) + 1;

// Server Deploy
const serverDeploy = row["*.properties.serverDeploy"] || "none";
serverDeployCounts[serverDeploy] =
(serverDeployCounts[serverDeploy] || 0) + 1;

// Project type
const hasFrontend =
(frontend0 && frontend0 !== "none") ||
(frontend1 && frontend1 !== "none");
Expand All @@ -321,7 +312,6 @@ async function generateAnalyticsData() {
}
projectTypeCounts[type] = (projectTypeCounts[type] || 0) + 1;

// Stack combinations
const frontends = [frontend0, frontend1].filter(
(f) => f && f !== "none" && f !== "",
);
Expand All @@ -332,7 +322,6 @@ async function generateAnalyticsData() {
const combo = parts.length > 0 ? parts.join(" + ") : "none";
stackComboCounts[combo] = (stackComboCounts[combo] || 0) + 1;

// Database + ORM combinations
if (database !== "none" && orm !== "none") {
const combo = `${database} + ${orm}`;
dbORMComboCounts[combo] = (dbORMComboCounts[combo] || 0) + 1;
Expand Down
Loading