create-better-t-stack 2.45.4 → 2.46.0-canary.85c43fef
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/dist/cli.js +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -1
- package/dist/{src-Cun9EO6e.js → src-lN80CwOs.js} +396 -162
- package/package.json +1 -1
- package/templates/auth/better-auth/convex/backend/convex/auth.config.ts.hbs +8 -0
- package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +48 -0
- package/templates/auth/better-auth/convex/backend/convex/convex.config.ts.hbs +7 -0
- package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +12 -0
- package/templates/auth/better-auth/convex/backend/convex/privateData.ts.hbs +16 -0
- package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +3 -0
- package/templates/auth/better-auth/convex/web/react/next/src/app/dashboard/page.tsx.hbs +40 -0
- package/templates/auth/better-auth/convex/web/react/next/src/components/sign-in-form.tsx.hbs +129 -0
- package/templates/auth/better-auth/convex/web/react/next/src/components/sign-up-form.tsx.hbs +154 -0
- package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +48 -0
- package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-client.ts.hbs +6 -0
- package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +6 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/lib/auth-client.ts.hbs +10 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +43 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/sign-in-form.tsx.hbs +133 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/sign-up-form.tsx.hbs +158 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +50 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-client.ts.hbs +6 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +5 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +11 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +43 -0
- package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +163 -16
- package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +36 -2
- package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts.hbs +22 -0
- package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +6 -0
- package/templates/auth/better-auth/web/react/next/src/app/dashboard/dashboard.tsx.hbs +58 -0
- package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +31 -41
- package/templates/auth/better-auth/web/react/react-router/src/routes/dashboard.tsx.hbs +37 -3
- package/templates/auth/better-auth/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +50 -32
- package/templates/auth/better-auth/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +51 -36
- package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts.hbs +11 -0
- package/templates/auth/better-auth/web/solid/src/routes/dashboard.tsx.hbs +52 -29
- package/templates/auth/better-auth/web/svelte/src/lib/{auth-client.ts → auth-client.ts.hbs} +6 -0
- package/templates/auth/better-auth/web/svelte/src/routes/dashboard/+page.svelte.hbs +24 -3
- package/templates/backend/server/server-base/package.json.hbs +1 -1
- package/templates/extras/bunfig.toml.hbs +4 -0
- package/templates/frontend/react/next/src/components/providers.tsx.hbs +8 -0
- package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +8 -1
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +51 -0
- package/templates/frontend/react/tanstack-start/src/routes/index.tsx.hbs +2 -2
- package/templates/frontend/react/web-base/src/components/header.tsx.hbs +2 -2
- package/templates/frontend/solid/package.json.hbs +10 -10
- package/templates/payments/polar/server/base/src/lib/payments.ts.hbs +6 -0
- package/templates/payments/polar/web/nuxt/app/pages/success.vue.hbs +11 -0
- package/templates/payments/polar/web/react/next/src/app/success/page.tsx.hbs +15 -0
- package/templates/payments/polar/web/react/react-router/src/routes/success.tsx.hbs +13 -0
- package/templates/payments/polar/web/react/tanstack-router/src/routes/success.tsx.hbs +19 -0
- package/templates/payments/polar/web/react/tanstack-start/src/routes/success.tsx.hbs +19 -0
- package/templates/payments/polar/web/solid/src/routes/success.tsx.hbs +23 -0
- package/templates/payments/polar/web/svelte/src/routes/success/+page.svelte.hbs +12 -0
- package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts +0 -16
- package/templates/auth/better-auth/web/react/next/src/components/theme-provider.tsx +0 -11
- package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts +0 -5
- /package/templates/auth/better-auth/web/nuxt/app/components/{SignInForm.vue → SignInForm.vue.hbs} +0 -0
- /package/templates/auth/better-auth/web/nuxt/app/components/{SignUpForm.vue → SignUpForm.vue.hbs} +0 -0
- /package/templates/auth/better-auth/web/nuxt/app/components/{UserMenu.vue → UserMenu.vue.hbs} +0 -0
- /package/templates/auth/better-auth/web/nuxt/app/pages/{login.vue → login.vue.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/next/src/app/login/{page.tsx → page.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/next/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/next/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/next/src/components/{user-menu.tsx → user-menu.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/react-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/react-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/react-router/src/components/{user-menu.tsx → user-menu.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/react-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/svelte/src/components/{SignInForm.svelte → SignInForm.svelte.hbs} +0 -0
- /package/templates/auth/better-auth/web/svelte/src/components/{SignUpForm.svelte → SignUpForm.svelte.hbs} +0 -0
- /package/templates/auth/better-auth/web/svelte/src/components/{UserMenu.svelte → UserMenu.svelte.hbs} +0 -0
- /package/templates/auth/better-auth/web/svelte/src/routes/login/{+page.svelte → +page.svelte.hbs} +0 -0
- /package/templates/frontend/react/web-base/src/components/{loader.tsx → loader.tsx.hbs} +0 -0
|
@@ -36,6 +36,7 @@ const DEFAULT_CONFIG_BASE = {
|
|
|
36
36
|
database: "sqlite",
|
|
37
37
|
orm: "drizzle",
|
|
38
38
|
auth: "better-auth",
|
|
39
|
+
payments: "none",
|
|
39
40
|
addons: ["turborepo"],
|
|
40
41
|
examples: [],
|
|
41
42
|
git: true,
|
|
@@ -59,8 +60,8 @@ function getDefaultConfig() {
|
|
|
59
60
|
}
|
|
60
61
|
const DEFAULT_CONFIG = getDefaultConfig();
|
|
61
62
|
const dependencyVersionMap = {
|
|
62
|
-
"better-auth": "^1.3.
|
|
63
|
-
"@better-auth/expo": "^1.3.
|
|
63
|
+
"better-auth": "^1.3.10",
|
|
64
|
+
"@better-auth/expo": "^1.3.10",
|
|
64
65
|
"@clerk/nextjs": "^6.31.5",
|
|
65
66
|
"@clerk/clerk-react": "^5.45.0",
|
|
66
67
|
"@clerk/tanstack-react-start": "^0.23.1",
|
|
@@ -125,15 +126,16 @@ const dependencyVersionMap = {
|
|
|
125
126
|
"convex-svelte": "^0.0.11",
|
|
126
127
|
"convex-nuxt": "0.1.5",
|
|
127
128
|
"convex-vue": "^0.1.5",
|
|
129
|
+
"@convex-dev/better-auth": "^0.8.4",
|
|
128
130
|
"@tanstack/svelte-query": "^5.85.3",
|
|
129
131
|
"@tanstack/svelte-query-devtools": "^5.85.3",
|
|
130
132
|
"@tanstack/vue-query-devtools": "^5.83.0",
|
|
131
133
|
"@tanstack/vue-query": "^5.83.0",
|
|
132
134
|
"@tanstack/react-query-devtools": "^5.85.5",
|
|
133
135
|
"@tanstack/react-query": "^5.85.5",
|
|
134
|
-
"@tanstack/solid-query": "^5.
|
|
135
|
-
"@tanstack/solid-query-devtools": "^5.
|
|
136
|
-
"@tanstack/solid-router-devtools": "^1.131.
|
|
136
|
+
"@tanstack/solid-query": "^5.87.4",
|
|
137
|
+
"@tanstack/solid-query-devtools": "^5.87.4",
|
|
138
|
+
"@tanstack/solid-router-devtools": "^1.131.44",
|
|
137
139
|
wrangler: "^4.23.0",
|
|
138
140
|
"@cloudflare/vite-plugin": "^1.9.0",
|
|
139
141
|
"@opennextjs/cloudflare": "^1.6.5",
|
|
@@ -142,7 +144,9 @@ const dependencyVersionMap = {
|
|
|
142
144
|
"@cloudflare/workers-types": "^4.20250822.0",
|
|
143
145
|
alchemy: "^0.65.1",
|
|
144
146
|
nitropack: "^2.12.4",
|
|
145
|
-
dotenv: "^17.2.1"
|
|
147
|
+
dotenv: "^17.2.1",
|
|
148
|
+
"@polar-sh/better-auth": "^1.1.3",
|
|
149
|
+
"@polar-sh/sdk": "^0.34.16"
|
|
146
150
|
};
|
|
147
151
|
const ADDON_COMPATIBILITY = {
|
|
148
152
|
pwa: [
|
|
@@ -256,6 +260,7 @@ const AuthSchema = z.enum([
|
|
|
256
260
|
"clerk",
|
|
257
261
|
"none"
|
|
258
262
|
]).describe("Authentication provider");
|
|
263
|
+
const PaymentsSchema = z.enum(["polar", "none"]).describe("Payments provider");
|
|
259
264
|
const ProjectNameSchema = z.string().min(1, "Project name cannot be empty").max(255, "Project name must be less than 255 characters").refine((name) => name === "." || !name.startsWith("."), "Project name cannot start with a dot (except for '.')").refine((name) => name === "." || !name.startsWith("-"), "Project name cannot start with a dash").refine((name) => {
|
|
260
265
|
const invalidChars = [
|
|
261
266
|
"<",
|
|
@@ -286,28 +291,16 @@ const DirectoryConflictSchema = z.enum([
|
|
|
286
291
|
]).describe("How to handle existing directory conflicts");
|
|
287
292
|
|
|
288
293
|
//#endregion
|
|
289
|
-
//#region src/utils/
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
return { isCompatible: true };
|
|
302
|
-
}
|
|
303
|
-
function getCompatibleAddons(allAddons, frontend, existingAddons = []) {
|
|
304
|
-
return allAddons.filter((addon) => {
|
|
305
|
-
if (existingAddons.includes(addon)) return false;
|
|
306
|
-
if (addon === "none") return false;
|
|
307
|
-
const { isCompatible } = validateAddonCompatibility(addon, frontend);
|
|
308
|
-
return isCompatible;
|
|
309
|
-
});
|
|
310
|
-
}
|
|
294
|
+
//#region src/utils/compatibility.ts
|
|
295
|
+
const WEB_FRAMEWORKS = [
|
|
296
|
+
"tanstack-router",
|
|
297
|
+
"react-router",
|
|
298
|
+
"tanstack-start",
|
|
299
|
+
"next",
|
|
300
|
+
"nuxt",
|
|
301
|
+
"svelte",
|
|
302
|
+
"solid"
|
|
303
|
+
];
|
|
311
304
|
|
|
312
305
|
//#endregion
|
|
313
306
|
//#region src/utils/errors.ts
|
|
@@ -331,6 +324,120 @@ function handleError(error, fallbackMessage) {
|
|
|
331
324
|
process.exit(1);
|
|
332
325
|
}
|
|
333
326
|
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/utils/compatibility-rules.ts
|
|
329
|
+
function isWebFrontend(value) {
|
|
330
|
+
return WEB_FRAMEWORKS.includes(value);
|
|
331
|
+
}
|
|
332
|
+
function splitFrontends(values = []) {
|
|
333
|
+
const web = values.filter((f) => isWebFrontend(f));
|
|
334
|
+
const native = values.filter((f) => f === "native-nativewind" || f === "native-unistyles");
|
|
335
|
+
return {
|
|
336
|
+
web,
|
|
337
|
+
native
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function ensureSingleWebAndNative(frontends) {
|
|
341
|
+
const { web, native } = splitFrontends(frontends);
|
|
342
|
+
if (web.length > 1) exitWithError("Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid");
|
|
343
|
+
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
|
|
344
|
+
}
|
|
345
|
+
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
346
|
+
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
|
|
347
|
+
if (providedFlags.has("backend") && config.backend && config.backend !== "hono" && config.runtime === "workers") exitWithError(`Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`);
|
|
348
|
+
if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") exitWithError("Cloudflare Workers runtime (--runtime workers) is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle or Prisma ORM. Please use a different database or runtime.");
|
|
349
|
+
if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") exitWithError("Cloudflare Workers runtime (--runtime workers) is not compatible with Docker setup. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
|
|
350
|
+
if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") exitWithError("MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle or Prisma ORM. Please use a different database or runtime.");
|
|
351
|
+
if (providedFlags.has("dbSetup") && options.dbSetup === "docker" && config.runtime === "workers") exitWithError("Docker setup (--db-setup docker) is not compatible with Cloudflare Workers runtime. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
|
|
352
|
+
}
|
|
353
|
+
function validateApiFrontendCompatibility(api, frontends = []) {
|
|
354
|
+
const includesNuxt = frontends.includes("nuxt");
|
|
355
|
+
const includesSvelte = frontends.includes("svelte");
|
|
356
|
+
const includesSolid = frontends.includes("solid");
|
|
357
|
+
if ((includesNuxt || includesSvelte || includesSolid) && api === "trpc") exitWithError(`tRPC API is not supported with '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' frontend. Please use --api orpc or --api none or remove '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' from --frontend.`);
|
|
358
|
+
}
|
|
359
|
+
function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
360
|
+
if (backend === "convex" && frontend === "solid") return false;
|
|
361
|
+
if (auth === "clerk" && backend === "convex") {
|
|
362
|
+
const incompatibleFrontends = [
|
|
363
|
+
"nuxt",
|
|
364
|
+
"svelte",
|
|
365
|
+
"solid"
|
|
366
|
+
];
|
|
367
|
+
if (incompatibleFrontends.includes(frontend)) return false;
|
|
368
|
+
}
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
function allowedApisForFrontends(frontends = []) {
|
|
372
|
+
const includesNuxt = frontends.includes("nuxt");
|
|
373
|
+
const includesSvelte = frontends.includes("svelte");
|
|
374
|
+
const includesSolid = frontends.includes("solid");
|
|
375
|
+
const base = [
|
|
376
|
+
"trpc",
|
|
377
|
+
"orpc",
|
|
378
|
+
"none"
|
|
379
|
+
];
|
|
380
|
+
if (includesNuxt || includesSvelte || includesSolid) return ["orpc", "none"];
|
|
381
|
+
return base;
|
|
382
|
+
}
|
|
383
|
+
function isExampleTodoAllowed(backend, database) {
|
|
384
|
+
return !(backend !== "convex" && backend !== "none" && database === "none");
|
|
385
|
+
}
|
|
386
|
+
function isExampleAIAllowed(_backend, frontends = []) {
|
|
387
|
+
const includesSolid = frontends.includes("solid");
|
|
388
|
+
if (includesSolid) return false;
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
392
|
+
if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) exitWithError("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
|
|
393
|
+
}
|
|
394
|
+
function validateServerDeployRequiresBackend(serverDeploy, backend) {
|
|
395
|
+
if (serverDeploy && serverDeploy !== "none" && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
396
|
+
}
|
|
397
|
+
function validateAddonCompatibility(addon, frontend, _auth) {
|
|
398
|
+
const compatibleFrontends = ADDON_COMPATIBILITY[addon];
|
|
399
|
+
if (compatibleFrontends.length > 0) {
|
|
400
|
+
const hasCompatibleFrontend = frontend.some((f) => compatibleFrontends.includes(f));
|
|
401
|
+
if (!hasCompatibleFrontend) {
|
|
402
|
+
const frontendList = compatibleFrontends.join(", ");
|
|
403
|
+
return {
|
|
404
|
+
isCompatible: false,
|
|
405
|
+
reason: `${addon} addon requires one of these frontends: ${frontendList}`
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return { isCompatible: true };
|
|
410
|
+
}
|
|
411
|
+
function getCompatibleAddons(allAddons, frontend, existingAddons = [], auth) {
|
|
412
|
+
return allAddons.filter((addon) => {
|
|
413
|
+
if (existingAddons.includes(addon)) return false;
|
|
414
|
+
if (addon === "none") return false;
|
|
415
|
+
const { isCompatible } = validateAddonCompatibility(addon, frontend, auth);
|
|
416
|
+
return isCompatible;
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
function validateAddonsAgainstFrontends(addons = [], frontends = [], auth) {
|
|
420
|
+
for (const addon of addons) {
|
|
421
|
+
if (addon === "none") continue;
|
|
422
|
+
const { isCompatible, reason } = validateAddonCompatibility(addon, frontends, auth);
|
|
423
|
+
if (!isCompatible) exitWithError(`Incompatible addon/frontend combination: ${reason}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function validatePaymentsCompatibility(payments, auth, _backend, frontends = []) {
|
|
427
|
+
if (!payments || payments === "none") return;
|
|
428
|
+
if (payments === "polar") {
|
|
429
|
+
if (!auth || auth === "none" || auth !== "better-auth") exitWithError("Polar payments requires Better Auth. Please use '--auth better-auth' or choose a different payments provider.");
|
|
430
|
+
const { web } = splitFrontends(frontends);
|
|
431
|
+
if (web.length === 0 && frontends.length > 0) exitWithError("Polar payments requires a web frontend or no frontend. Please select a web frontend or choose a different payments provider.");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function validateExamplesCompatibility(examples, backend, database, frontend) {
|
|
435
|
+
const examplesArr = examples ?? [];
|
|
436
|
+
if (examplesArr.length === 0 || examplesArr.includes("none")) return;
|
|
437
|
+
if (examplesArr.includes("todo") && backend !== "convex" && backend !== "none" && database === "none") exitWithError("The 'todo' example requires a database if a backend (other than Convex) is present. Cannot use --examples todo when database is 'none' and a backend is selected.");
|
|
438
|
+
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
|
|
439
|
+
}
|
|
440
|
+
|
|
334
441
|
//#endregion
|
|
335
442
|
//#region src/prompts/addons.ts
|
|
336
443
|
function getAddonDisplay(addon) {
|
|
@@ -401,7 +508,7 @@ const ADDON_GROUPS = {
|
|
|
401
508
|
"husky"
|
|
402
509
|
]
|
|
403
510
|
};
|
|
404
|
-
async function getAddonsChoice(addons, frontends) {
|
|
511
|
+
async function getAddonsChoice(addons, frontends, auth) {
|
|
405
512
|
if (addons !== void 0) return addons;
|
|
406
513
|
const allAddons = AddonsSchema.options.filter((addon) => addon !== "none");
|
|
407
514
|
const groupedOptions = {
|
|
@@ -411,7 +518,7 @@ async function getAddonsChoice(addons, frontends) {
|
|
|
411
518
|
};
|
|
412
519
|
const frontendsArray = frontends || [];
|
|
413
520
|
for (const addon of allAddons) {
|
|
414
|
-
const { isCompatible } = validateAddonCompatibility(addon, frontendsArray);
|
|
521
|
+
const { isCompatible } = validateAddonCompatibility(addon, frontendsArray, auth);
|
|
415
522
|
if (!isCompatible) continue;
|
|
416
523
|
const { label, hint } = getAddonDisplay(addon);
|
|
417
524
|
const option = {
|
|
@@ -437,14 +544,14 @@ async function getAddonsChoice(addons, frontends) {
|
|
|
437
544
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
438
545
|
return response;
|
|
439
546
|
}
|
|
440
|
-
async function getAddonsToAdd(frontend, existingAddons = []) {
|
|
547
|
+
async function getAddonsToAdd(frontend, existingAddons = [], auth) {
|
|
441
548
|
const groupedOptions = {
|
|
442
549
|
Documentation: [],
|
|
443
550
|
Linting: [],
|
|
444
551
|
Other: []
|
|
445
552
|
};
|
|
446
553
|
const frontendArray = frontend || [];
|
|
447
|
-
const compatibleAddons = getCompatibleAddons(AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons);
|
|
554
|
+
const compatibleAddons = getCompatibleAddons(AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
|
|
448
555
|
for (const addon of compatibleAddons) {
|
|
449
556
|
const { label, hint } = getAddonDisplay(addon);
|
|
450
557
|
const option = {
|
|
@@ -470,102 +577,6 @@ async function getAddonsToAdd(frontend, existingAddons = []) {
|
|
|
470
577
|
return response;
|
|
471
578
|
}
|
|
472
579
|
|
|
473
|
-
//#endregion
|
|
474
|
-
//#region src/utils/compatibility.ts
|
|
475
|
-
const WEB_FRAMEWORKS = [
|
|
476
|
-
"tanstack-router",
|
|
477
|
-
"react-router",
|
|
478
|
-
"tanstack-start",
|
|
479
|
-
"next",
|
|
480
|
-
"nuxt",
|
|
481
|
-
"svelte",
|
|
482
|
-
"solid"
|
|
483
|
-
];
|
|
484
|
-
|
|
485
|
-
//#endregion
|
|
486
|
-
//#region src/utils/compatibility-rules.ts
|
|
487
|
-
function isWebFrontend(value) {
|
|
488
|
-
return WEB_FRAMEWORKS.includes(value);
|
|
489
|
-
}
|
|
490
|
-
function splitFrontends(values = []) {
|
|
491
|
-
const web = values.filter((f) => isWebFrontend(f));
|
|
492
|
-
const native = values.filter((f) => f === "native-nativewind" || f === "native-unistyles");
|
|
493
|
-
return {
|
|
494
|
-
web,
|
|
495
|
-
native
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
function ensureSingleWebAndNative(frontends) {
|
|
499
|
-
const { web, native } = splitFrontends(frontends);
|
|
500
|
-
if (web.length > 1) exitWithError("Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid");
|
|
501
|
-
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
|
|
502
|
-
}
|
|
503
|
-
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
504
|
-
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
|
|
505
|
-
if (providedFlags.has("backend") && config.backend && config.backend !== "hono" && config.runtime === "workers") exitWithError(`Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`);
|
|
506
|
-
if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") exitWithError("Cloudflare Workers runtime (--runtime workers) is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle or Prisma ORM. Please use a different database or runtime.");
|
|
507
|
-
if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") exitWithError("Cloudflare Workers runtime (--runtime workers) is not compatible with Docker setup. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
|
|
508
|
-
if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") exitWithError("MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle or Prisma ORM. Please use a different database or runtime.");
|
|
509
|
-
if (providedFlags.has("dbSetup") && options.dbSetup === "docker" && config.runtime === "workers") exitWithError("Docker setup (--db-setup docker) is not compatible with Cloudflare Workers runtime. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
|
|
510
|
-
}
|
|
511
|
-
function validateApiFrontendCompatibility(api, frontends = []) {
|
|
512
|
-
const includesNuxt = frontends.includes("nuxt");
|
|
513
|
-
const includesSvelte = frontends.includes("svelte");
|
|
514
|
-
const includesSolid = frontends.includes("solid");
|
|
515
|
-
if ((includesNuxt || includesSvelte || includesSolid) && api === "trpc") exitWithError(`tRPC API is not supported with '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' frontend. Please use --api orpc or --api none or remove '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' from --frontend.`);
|
|
516
|
-
}
|
|
517
|
-
function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
518
|
-
if (backend === "convex" && frontend === "solid") return false;
|
|
519
|
-
if (auth === "clerk" && backend === "convex") {
|
|
520
|
-
const incompatibleFrontends = [
|
|
521
|
-
"nuxt",
|
|
522
|
-
"svelte",
|
|
523
|
-
"solid"
|
|
524
|
-
];
|
|
525
|
-
if (incompatibleFrontends.includes(frontend)) return false;
|
|
526
|
-
}
|
|
527
|
-
return true;
|
|
528
|
-
}
|
|
529
|
-
function allowedApisForFrontends(frontends = []) {
|
|
530
|
-
const includesNuxt = frontends.includes("nuxt");
|
|
531
|
-
const includesSvelte = frontends.includes("svelte");
|
|
532
|
-
const includesSolid = frontends.includes("solid");
|
|
533
|
-
const base = [
|
|
534
|
-
"trpc",
|
|
535
|
-
"orpc",
|
|
536
|
-
"none"
|
|
537
|
-
];
|
|
538
|
-
if (includesNuxt || includesSvelte || includesSolid) return ["orpc", "none"];
|
|
539
|
-
return base;
|
|
540
|
-
}
|
|
541
|
-
function isExampleTodoAllowed(backend, database) {
|
|
542
|
-
return !(backend !== "convex" && backend !== "none" && database === "none");
|
|
543
|
-
}
|
|
544
|
-
function isExampleAIAllowed(_backend, frontends = []) {
|
|
545
|
-
const includesSolid = frontends.includes("solid");
|
|
546
|
-
if (includesSolid) return false;
|
|
547
|
-
return true;
|
|
548
|
-
}
|
|
549
|
-
function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
550
|
-
if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) exitWithError("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
|
|
551
|
-
}
|
|
552
|
-
function validateServerDeployRequiresBackend(serverDeploy, backend) {
|
|
553
|
-
if (serverDeploy && serverDeploy !== "none" && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
554
|
-
}
|
|
555
|
-
function validateAddonsAgainstFrontends(addons = [], frontends = []) {
|
|
556
|
-
for (const addon of addons) {
|
|
557
|
-
if (addon === "none") continue;
|
|
558
|
-
const { isCompatible, reason } = validateAddonCompatibility(addon, frontends);
|
|
559
|
-
if (!isCompatible) exitWithError(`Incompatible addon/frontend combination: ${reason}`);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
function validateExamplesCompatibility(examples, backend, database, frontend) {
|
|
563
|
-
const examplesArr = examples ?? [];
|
|
564
|
-
if (examplesArr.length === 0 || examplesArr.includes("none")) return;
|
|
565
|
-
if (examplesArr.includes("todo") && backend !== "convex" && backend !== "none" && database === "none") exitWithError("The 'todo' example requires a database if a backend (other than Convex) is present. Cannot use --examples todo when database is 'none' and a backend is selected.");
|
|
566
|
-
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
|
|
567
|
-
}
|
|
568
|
-
|
|
569
580
|
//#endregion
|
|
570
581
|
//#region src/prompts/api.ts
|
|
571
582
|
async function getApiChoice(Api, frontend, backend) {
|
|
@@ -602,20 +613,36 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
|
|
|
602
613
|
const unsupportedFrontends = frontend?.filter((f) => [
|
|
603
614
|
"nuxt",
|
|
604
615
|
"svelte",
|
|
605
|
-
"solid"
|
|
616
|
+
"solid",
|
|
617
|
+
"native-nativewind",
|
|
618
|
+
"native-unistyles"
|
|
606
619
|
].includes(f));
|
|
607
620
|
if (unsupportedFrontends && unsupportedFrontends.length > 0) return "none";
|
|
621
|
+
const hasReactFrontends = frontend?.some((f) => [
|
|
622
|
+
"react-router",
|
|
623
|
+
"tanstack-router",
|
|
624
|
+
"tanstack-start",
|
|
625
|
+
"next"
|
|
626
|
+
].includes(f));
|
|
627
|
+
const options = [{
|
|
628
|
+
value: "clerk",
|
|
629
|
+
label: "Clerk",
|
|
630
|
+
hint: "More than auth, Complete User Management"
|
|
631
|
+
}];
|
|
632
|
+
if (hasReactFrontends) options.unshift({
|
|
633
|
+
value: "better-auth",
|
|
634
|
+
label: "Better-Auth",
|
|
635
|
+
hint: "comprehensive auth framework for TypeScript"
|
|
636
|
+
});
|
|
637
|
+
options.push({
|
|
638
|
+
value: "none",
|
|
639
|
+
label: "None",
|
|
640
|
+
hint: "No auth"
|
|
641
|
+
});
|
|
608
642
|
const response$1 = await select({
|
|
609
643
|
message: "Select authentication provider",
|
|
610
|
-
options
|
|
611
|
-
|
|
612
|
-
label: "Clerk",
|
|
613
|
-
hint: "More than auth, Complete User Management"
|
|
614
|
-
}, {
|
|
615
|
-
value: "none",
|
|
616
|
-
label: "None"
|
|
617
|
-
}],
|
|
618
|
-
initialValue: "clerk"
|
|
644
|
+
options,
|
|
645
|
+
initialValue: "none"
|
|
619
646
|
});
|
|
620
647
|
if (isCancel(response$1)) return exitCancelled("Operation cancelled");
|
|
621
648
|
return response$1;
|
|
@@ -1037,6 +1064,30 @@ async function getPackageManagerChoice(packageManager) {
|
|
|
1037
1064
|
return response;
|
|
1038
1065
|
}
|
|
1039
1066
|
|
|
1067
|
+
//#endregion
|
|
1068
|
+
//#region src/prompts/payments.ts
|
|
1069
|
+
async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
1070
|
+
if (payments !== void 0) return payments;
|
|
1071
|
+
const isPolarCompatible = auth === "better-auth" && backend !== "convex" && (frontends?.length === 0 || splitFrontends(frontends).web.length > 0);
|
|
1072
|
+
if (!isPolarCompatible) return "none";
|
|
1073
|
+
const options = [{
|
|
1074
|
+
value: "polar",
|
|
1075
|
+
label: "Polar",
|
|
1076
|
+
hint: "Turn your software into a business. 6 lines of code."
|
|
1077
|
+
}, {
|
|
1078
|
+
value: "none",
|
|
1079
|
+
label: "None",
|
|
1080
|
+
hint: "No payments integration"
|
|
1081
|
+
}];
|
|
1082
|
+
const response = await select({
|
|
1083
|
+
message: "Select payments provider",
|
|
1084
|
+
options,
|
|
1085
|
+
initialValue: DEFAULT_CONFIG.payments
|
|
1086
|
+
});
|
|
1087
|
+
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
1088
|
+
return response;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1040
1091
|
//#endregion
|
|
1041
1092
|
//#region src/prompts/runtime.ts
|
|
1042
1093
|
async function getRuntimeChoice(runtime, backend) {
|
|
@@ -1226,7 +1277,8 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
1226
1277
|
orm: ({ results }) => getORMChoice(flags.orm, results.database !== "none", results.database, results.backend, results.runtime),
|
|
1227
1278
|
api: ({ results }) => getApiChoice(flags.api, results.frontend, results.backend),
|
|
1228
1279
|
auth: ({ results }) => getAuthChoice(flags.auth, results.database !== "none", results.backend, results.frontend),
|
|
1229
|
-
|
|
1280
|
+
payments: ({ results }) => getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend),
|
|
1281
|
+
addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend, results.auth),
|
|
1230
1282
|
examples: ({ results }) => getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api),
|
|
1231
1283
|
dbSetup: ({ results }) => getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime),
|
|
1232
1284
|
webDeploy: ({ results }) => getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend),
|
|
@@ -1245,6 +1297,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
1245
1297
|
database: result.database,
|
|
1246
1298
|
orm: result.orm,
|
|
1247
1299
|
auth: result.auth,
|
|
1300
|
+
payments: result.payments,
|
|
1248
1301
|
addons: result.addons,
|
|
1249
1302
|
examples: result.examples,
|
|
1250
1303
|
git: result.git,
|
|
@@ -1332,7 +1385,7 @@ const getLatestCLIVersion = () => {
|
|
|
1332
1385
|
*/
|
|
1333
1386
|
function isTelemetryEnabled() {
|
|
1334
1387
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1335
|
-
const BTS_TELEMETRY = "
|
|
1388
|
+
const BTS_TELEMETRY = "0";
|
|
1336
1389
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1337
1390
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1338
1391
|
return true;
|
|
@@ -1340,8 +1393,8 @@ function isTelemetryEnabled() {
|
|
|
1340
1393
|
|
|
1341
1394
|
//#endregion
|
|
1342
1395
|
//#region src/utils/analytics.ts
|
|
1343
|
-
const POSTHOG_API_KEY = "
|
|
1344
|
-
const POSTHOG_HOST = "
|
|
1396
|
+
const POSTHOG_API_KEY = "random";
|
|
1397
|
+
const POSTHOG_HOST = "random";
|
|
1345
1398
|
function generateSessionId() {
|
|
1346
1399
|
const rand = Math.random().toString(36).slice(2);
|
|
1347
1400
|
const now = Date.now().toString(36);
|
|
@@ -1388,6 +1441,7 @@ function displayConfig(config) {
|
|
|
1388
1441
|
if (config.database !== void 0) configDisplay.push(`${pc.blue("Database:")} ${String(config.database)}`);
|
|
1389
1442
|
if (config.orm !== void 0) configDisplay.push(`${pc.blue("ORM:")} ${String(config.orm)}`);
|
|
1390
1443
|
if (config.auth !== void 0) configDisplay.push(`${pc.blue("Auth:")} ${String(config.auth)}`);
|
|
1444
|
+
if (config.payments !== void 0) configDisplay.push(`${pc.blue("Payments:")} ${String(config.payments)}`);
|
|
1391
1445
|
if (config.addons !== void 0) {
|
|
1392
1446
|
const addons = Array.isArray(config.addons) ? config.addons : [config.addons];
|
|
1393
1447
|
const addonsText = addons.length > 0 && addons[0] !== void 0 ? addons.join(", ") : "none";
|
|
@@ -1426,6 +1480,7 @@ function generateReproducibleCommand(config) {
|
|
|
1426
1480
|
flags.push(`--orm ${config.orm}`);
|
|
1427
1481
|
flags.push(`--api ${config.api}`);
|
|
1428
1482
|
flags.push(`--auth ${config.auth}`);
|
|
1483
|
+
flags.push(`--payments ${config.payments}`);
|
|
1429
1484
|
if (config.addons && config.addons.length > 0) flags.push(`--addons ${config.addons.join(" ")}`);
|
|
1430
1485
|
else flags.push("--addons none");
|
|
1431
1486
|
if (config.examples && config.examples.length > 0) flags.push(`--examples ${config.examples.join(" ")}`);
|
|
@@ -1595,6 +1650,7 @@ function processFlags(options, projectName) {
|
|
|
1595
1650
|
if (options.database) config.database = options.database;
|
|
1596
1651
|
if (options.orm) config.orm = options.orm;
|
|
1597
1652
|
if (options.auth !== void 0) config.auth = options.auth;
|
|
1653
|
+
if (options.payments !== void 0) config.payments = options.payments;
|
|
1598
1654
|
if (options.git !== void 0) config.git = options.git;
|
|
1599
1655
|
if (options.install !== void 0) config.install = options.install;
|
|
1600
1656
|
if (options.runtime) config.runtime = options.runtime;
|
|
@@ -1692,7 +1748,16 @@ function validateConvexConstraints(config, providedFlags) {
|
|
|
1692
1748
|
if (has("api") && config.api !== "none") exitWithError("Convex backend requires '--api none'. Please remove the --api flag or set it to 'none'.");
|
|
1693
1749
|
if (has("dbSetup") && config.dbSetup !== "none") exitWithError("Convex backend requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.");
|
|
1694
1750
|
if (has("serverDeploy") && config.serverDeploy !== "none") exitWithError("Convex backend requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.");
|
|
1695
|
-
if (has("auth") && config.auth === "better-auth")
|
|
1751
|
+
if (has("auth") && config.auth === "better-auth") {
|
|
1752
|
+
const hasUnsupportedFrontends = config.frontend?.some((f) => [
|
|
1753
|
+
"nuxt",
|
|
1754
|
+
"svelte",
|
|
1755
|
+
"solid",
|
|
1756
|
+
"native-nativewind",
|
|
1757
|
+
"native-unistyles"
|
|
1758
|
+
].includes(f));
|
|
1759
|
+
if (hasUnsupportedFrontends) exitWithError("Better-Auth with Convex backend is not supported for non-React frontends (nuxt, svelte, solid) or native frontends (native-nativewind, native-unistyles). Please use '--auth clerk' or '--auth none'.");
|
|
1760
|
+
}
|
|
1696
1761
|
}
|
|
1697
1762
|
function validateBackendNoneConstraints(config, providedFlags) {
|
|
1698
1763
|
const { backend } = config;
|
|
@@ -1751,7 +1816,7 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1751
1816
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
1752
1817
|
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'wrangler' or 'alchemy' for --server-deploy.");
|
|
1753
1818
|
if (config.addons && config.addons.length > 0) {
|
|
1754
|
-
validateAddonsAgainstFrontends(config.addons, config.frontend);
|
|
1819
|
+
validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
1755
1820
|
config.addons = [...new Set(config.addons)];
|
|
1756
1821
|
}
|
|
1757
1822
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? []);
|
|
@@ -1761,7 +1826,8 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
1761
1826
|
validateDatabaseOrmAuth(config);
|
|
1762
1827
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
1763
1828
|
validateApiFrontendCompatibility(config.api, config.frontend);
|
|
1764
|
-
|
|
1829
|
+
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
|
|
1830
|
+
if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
1765
1831
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? []);
|
|
1766
1832
|
} catch (error) {
|
|
1767
1833
|
if (error instanceof Error) throw error;
|
|
@@ -1859,6 +1925,7 @@ async function writeBtsConfig(projectConfig) {
|
|
|
1859
1925
|
addons: projectConfig.addons,
|
|
1860
1926
|
examples: projectConfig.examples,
|
|
1861
1927
|
auth: projectConfig.auth,
|
|
1928
|
+
payments: projectConfig.payments,
|
|
1862
1929
|
packageManager: projectConfig.packageManager,
|
|
1863
1930
|
dbSetup: projectConfig.dbSetup,
|
|
1864
1931
|
api: projectConfig.api,
|
|
@@ -1937,18 +2004,18 @@ async function updateBtsConfig(projectDir, updates) {
|
|
|
1937
2004
|
//#endregion
|
|
1938
2005
|
//#region src/utils/add-package-deps.ts
|
|
1939
2006
|
const addPackageDependency = async (opts) => {
|
|
1940
|
-
const { dependencies = [], devDependencies = [], projectDir } = opts;
|
|
2007
|
+
const { dependencies = [], devDependencies = [], customDependencies = {}, customDevDependencies = {}, projectDir } = opts;
|
|
1941
2008
|
const pkgJsonPath = path.join(projectDir, "package.json");
|
|
1942
2009
|
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
1943
2010
|
if (!pkgJson.dependencies) pkgJson.dependencies = {};
|
|
1944
2011
|
if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
|
|
1945
2012
|
for (const pkgName of dependencies) {
|
|
1946
|
-
const version = dependencyVersionMap[pkgName];
|
|
2013
|
+
const version = customDependencies[pkgName] || dependencyVersionMap[pkgName];
|
|
1947
2014
|
if (version) pkgJson.dependencies[pkgName] = version;
|
|
1948
2015
|
else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
|
|
1949
2016
|
}
|
|
1950
2017
|
for (const pkgName of devDependencies) {
|
|
1951
|
-
const version = dependencyVersionMap[pkgName];
|
|
2018
|
+
const version = customDevDependencies[pkgName] || dependencyVersionMap[pkgName];
|
|
1952
2019
|
if (version) pkgJson.devDependencies[pkgName] = version;
|
|
1953
2020
|
else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
|
|
1954
2021
|
}
|
|
@@ -2483,6 +2550,7 @@ async function detectProjectConfig(projectDir) {
|
|
|
2483
2550
|
addons: btsConfig.addons,
|
|
2484
2551
|
examples: btsConfig.examples,
|
|
2485
2552
|
auth: btsConfig.auth,
|
|
2553
|
+
payments: btsConfig.payments,
|
|
2486
2554
|
packageManager: btsConfig.packageManager,
|
|
2487
2555
|
dbSetup: btsConfig.dbSetup,
|
|
2488
2556
|
api: btsConfig.api,
|
|
@@ -2809,6 +2877,29 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2809
2877
|
}
|
|
2810
2878
|
return;
|
|
2811
2879
|
}
|
|
2880
|
+
if (context.backend === "convex" && authProvider === "better-auth") {
|
|
2881
|
+
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
2882
|
+
const convexBetterAuthBackendSrc = path.join(PKG_ROOT, "templates/auth/better-auth/convex/backend");
|
|
2883
|
+
if (await fs.pathExists(convexBetterAuthBackendSrc)) {
|
|
2884
|
+
await fs.ensureDir(convexBackendDestDir);
|
|
2885
|
+
await processAndCopyFiles("**/*", convexBetterAuthBackendSrc, convexBackendDestDir, context);
|
|
2886
|
+
}
|
|
2887
|
+
if (webAppDirExists && hasReactWeb) {
|
|
2888
|
+
const convexBetterAuthWebBaseSrc = path.join(PKG_ROOT, "templates/auth/better-auth/convex/web/react/base");
|
|
2889
|
+
if (await fs.pathExists(convexBetterAuthWebBaseSrc)) await processAndCopyFiles("**/*", convexBetterAuthWebBaseSrc, webAppDir, context);
|
|
2890
|
+
const reactFramework = context.frontend.find((f) => [
|
|
2891
|
+
"tanstack-router",
|
|
2892
|
+
"react-router",
|
|
2893
|
+
"tanstack-start",
|
|
2894
|
+
"next"
|
|
2895
|
+
].includes(f));
|
|
2896
|
+
if (reactFramework) {
|
|
2897
|
+
const convexBetterAuthWebSrc = path.join(PKG_ROOT, `templates/auth/better-auth/convex/web/react/${reactFramework}`);
|
|
2898
|
+
if (await fs.pathExists(convexBetterAuthWebSrc)) await processAndCopyFiles("**/*", convexBetterAuthWebSrc, webAppDir, context);
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
return;
|
|
2902
|
+
}
|
|
2812
2903
|
if (serverAppDirExists && context.backend !== "convex") {
|
|
2813
2904
|
const authServerBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/base`);
|
|
2814
2905
|
if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, serverAppDir, context);
|
|
@@ -2863,6 +2954,49 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2863
2954
|
}
|
|
2864
2955
|
}
|
|
2865
2956
|
}
|
|
2957
|
+
async function setupPaymentsTemplate(projectDir, context) {
|
|
2958
|
+
if (!context.payments || context.payments === "none") return;
|
|
2959
|
+
const serverAppDir = path.join(projectDir, "apps/server");
|
|
2960
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
2961
|
+
const serverAppDirExists = await fs.pathExists(serverAppDir);
|
|
2962
|
+
const webAppDirExists = await fs.pathExists(webAppDir);
|
|
2963
|
+
if (serverAppDirExists && context.backend !== "convex") {
|
|
2964
|
+
const paymentsServerSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/server/base`);
|
|
2965
|
+
if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, serverAppDir, context);
|
|
2966
|
+
}
|
|
2967
|
+
const hasReactWeb = context.frontend.some((f) => [
|
|
2968
|
+
"tanstack-router",
|
|
2969
|
+
"react-router",
|
|
2970
|
+
"tanstack-start",
|
|
2971
|
+
"next"
|
|
2972
|
+
].includes(f));
|
|
2973
|
+
const hasNuxtWeb = context.frontend.includes("nuxt");
|
|
2974
|
+
const hasSvelteWeb = context.frontend.includes("svelte");
|
|
2975
|
+
const hasSolidWeb = context.frontend.includes("solid");
|
|
2976
|
+
if (webAppDirExists && (hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb)) {
|
|
2977
|
+
if (hasReactWeb) {
|
|
2978
|
+
const reactFramework = context.frontend.find((f) => [
|
|
2979
|
+
"tanstack-router",
|
|
2980
|
+
"react-router",
|
|
2981
|
+
"tanstack-start",
|
|
2982
|
+
"next"
|
|
2983
|
+
].includes(f));
|
|
2984
|
+
if (reactFramework) {
|
|
2985
|
+
const paymentsWebSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/web/react/${reactFramework}`);
|
|
2986
|
+
if (await fs.pathExists(paymentsWebSrc)) await processAndCopyFiles("**/*", paymentsWebSrc, webAppDir, context);
|
|
2987
|
+
}
|
|
2988
|
+
} else if (hasNuxtWeb) {
|
|
2989
|
+
const paymentsWebNuxtSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/web/nuxt`);
|
|
2990
|
+
if (await fs.pathExists(paymentsWebNuxtSrc)) await processAndCopyFiles("**/*", paymentsWebNuxtSrc, webAppDir, context);
|
|
2991
|
+
} else if (hasSvelteWeb) {
|
|
2992
|
+
const paymentsWebSvelteSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/web/svelte`);
|
|
2993
|
+
if (await fs.pathExists(paymentsWebSvelteSrc)) await processAndCopyFiles("**/*", paymentsWebSvelteSrc, webAppDir, context);
|
|
2994
|
+
} else if (hasSolidWeb) {
|
|
2995
|
+
const paymentsWebSolidSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/web/solid`);
|
|
2996
|
+
if (await fs.pathExists(paymentsWebSolidSrc)) await processAndCopyFiles("**/*", paymentsWebSolidSrc, webAppDir, context);
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
2866
3000
|
async function setupAddonsTemplate(projectDir, context) {
|
|
2867
3001
|
if (!context.addons || context.addons.length === 0) return;
|
|
2868
3002
|
for (const addon of context.addons) {
|
|
@@ -3057,6 +3191,7 @@ async function addAddonsToProject(input) {
|
|
|
3057
3191
|
addons: input.addons,
|
|
3058
3192
|
examples: detectedConfig.examples || [],
|
|
3059
3193
|
auth: detectedConfig.auth || "none",
|
|
3194
|
+
payments: detectedConfig.payments || "none",
|
|
3060
3195
|
git: false,
|
|
3061
3196
|
packageManager: input.packageManager || detectedConfig.packageManager || "npm",
|
|
3062
3197
|
install: input.install || false,
|
|
@@ -3843,6 +3978,7 @@ async function addDeploymentToProject(input) {
|
|
|
3843
3978
|
addons: detectedConfig.addons || [],
|
|
3844
3979
|
examples: detectedConfig.examples || [],
|
|
3845
3980
|
auth: detectedConfig.auth || "none",
|
|
3981
|
+
payments: detectedConfig.payments || "none",
|
|
3846
3982
|
git: false,
|
|
3847
3983
|
packageManager: input.packageManager || detectedConfig.packageManager || "npm",
|
|
3848
3984
|
install: input.install || false,
|
|
@@ -4155,6 +4291,35 @@ async function setupAuth(config) {
|
|
|
4155
4291
|
projectDir: clientDir
|
|
4156
4292
|
});
|
|
4157
4293
|
}
|
|
4294
|
+
if (auth === "better-auth") {
|
|
4295
|
+
const convexBackendDir = path.join(projectDir, "packages/backend");
|
|
4296
|
+
const convexBackendDirExists = await fs.pathExists(convexBackendDir);
|
|
4297
|
+
if (convexBackendDirExists) await addPackageDependency({
|
|
4298
|
+
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4299
|
+
customDependencies: { "better-auth": "1.3.8" },
|
|
4300
|
+
projectDir: convexBackendDir
|
|
4301
|
+
});
|
|
4302
|
+
if (clientDirExists) {
|
|
4303
|
+
const hasNextJs = frontend.includes("next");
|
|
4304
|
+
const hasTanStackStart = frontend.includes("tanstack-start");
|
|
4305
|
+
const hasViteReactOther = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
|
|
4306
|
+
if (hasNextJs) await addPackageDependency({
|
|
4307
|
+
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4308
|
+
customDependencies: { "better-auth": "1.3.8" },
|
|
4309
|
+
projectDir: clientDir
|
|
4310
|
+
});
|
|
4311
|
+
else if (hasTanStackStart) await addPackageDependency({
|
|
4312
|
+
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4313
|
+
customDependencies: { "better-auth": "1.3.8" },
|
|
4314
|
+
projectDir: clientDir
|
|
4315
|
+
});
|
|
4316
|
+
else if (hasViteReactOther) await addPackageDependency({
|
|
4317
|
+
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4318
|
+
customDependencies: { "better-auth": "1.3.8" },
|
|
4319
|
+
projectDir: clientDir
|
|
4320
|
+
});
|
|
4321
|
+
}
|
|
4322
|
+
}
|
|
4158
4323
|
const hasNativeWind = frontend.includes("native-nativewind");
|
|
4159
4324
|
const hasUnistyles = frontend.includes("native-unistyles");
|
|
4160
4325
|
if (auth === "clerk" && nativeDirExists && (hasNativeWind || hasUnistyles)) await addPackageDependency({
|
|
@@ -4312,6 +4477,18 @@ async function setupEnvironmentVariables(config) {
|
|
|
4312
4477
|
});
|
|
4313
4478
|
}
|
|
4314
4479
|
}
|
|
4480
|
+
if (backend === "convex" && auth === "better-auth") {
|
|
4481
|
+
if (hasNextJs) clientVars.push({
|
|
4482
|
+
key: "NEXT_PUBLIC_CONVEX_SITE_URL",
|
|
4483
|
+
value: "https://<YOUR_CONVEX_URL>",
|
|
4484
|
+
condition: true
|
|
4485
|
+
});
|
|
4486
|
+
else if (hasReactRouter || hasTanStackRouter || hasTanStackStart) clientVars.push({
|
|
4487
|
+
key: "VITE_CONVEX_SITE_URL",
|
|
4488
|
+
value: "https://<YOUR_CONVEX_URL>",
|
|
4489
|
+
condition: true
|
|
4490
|
+
});
|
|
4491
|
+
}
|
|
4315
4492
|
await addEnvVariablesToFile(path.join(clientDir, ".env"), clientVars);
|
|
4316
4493
|
}
|
|
4317
4494
|
}
|
|
@@ -4337,7 +4514,24 @@ async function setupEnvironmentVariables(config) {
|
|
|
4337
4514
|
await addEnvVariablesToFile(path.join(nativeDir, ".env"), nativeVars);
|
|
4338
4515
|
}
|
|
4339
4516
|
}
|
|
4340
|
-
if (backend === "convex")
|
|
4517
|
+
if (backend === "convex") {
|
|
4518
|
+
if (auth === "better-auth") {
|
|
4519
|
+
const convexBackendDir = path.join(projectDir, "packages/backend");
|
|
4520
|
+
if (await fs.pathExists(convexBackendDir)) {
|
|
4521
|
+
const convexBackendVars = [{
|
|
4522
|
+
key: hasTanStackStart ? "NEXT_PUBLIC_CONVEX_SITE_URL" : "VITE_CONVEX_SITE_URL",
|
|
4523
|
+
value: "",
|
|
4524
|
+
condition: true
|
|
4525
|
+
}, {
|
|
4526
|
+
key: "SITE_URL",
|
|
4527
|
+
value: "http://localhost:3001",
|
|
4528
|
+
condition: true
|
|
4529
|
+
}];
|
|
4530
|
+
await addEnvVariablesToFile(path.join(convexBackendDir, ".env.local"), convexBackendVars);
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
return;
|
|
4534
|
+
}
|
|
4341
4535
|
const serverDir = path.join(projectDir, "apps/server");
|
|
4342
4536
|
if (!await fs.pathExists(serverDir)) return;
|
|
4343
4537
|
const envPath = path.join(serverDir, ".env");
|
|
@@ -4384,6 +4578,16 @@ async function setupEnvironmentVariables(config) {
|
|
|
4384
4578
|
key: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
4385
4579
|
value: "",
|
|
4386
4580
|
condition: examples?.includes("ai") || false
|
|
4581
|
+
},
|
|
4582
|
+
{
|
|
4583
|
+
key: "POLAR_ACCESS_TOKEN",
|
|
4584
|
+
value: "",
|
|
4585
|
+
condition: config.payments === "polar"
|
|
4586
|
+
},
|
|
4587
|
+
{
|
|
4588
|
+
key: "POLAR_SUCCESS_URL",
|
|
4589
|
+
value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
|
|
4590
|
+
condition: config.payments === "polar"
|
|
4387
4591
|
}
|
|
4388
4592
|
];
|
|
4389
4593
|
await addEnvVariablesToFile(envPath, serverVars);
|
|
@@ -5612,17 +5816,6 @@ async function setupNodeRuntime(serverDir, backend) {
|
|
|
5612
5816
|
});
|
|
5613
5817
|
}
|
|
5614
5818
|
|
|
5615
|
-
//#endregion
|
|
5616
|
-
//#region src/helpers/core/convex-codegen.ts
|
|
5617
|
-
async function runConvexCodegen(projectDir, packageManager) {
|
|
5618
|
-
const backendDir = path.join(projectDir, "packages/backend");
|
|
5619
|
-
const cmd = getPackageExecutionCommand(packageManager, "convex codegen");
|
|
5620
|
-
await execa(cmd, {
|
|
5621
|
-
cwd: backendDir,
|
|
5622
|
-
shell: true
|
|
5623
|
-
});
|
|
5624
|
-
}
|
|
5625
|
-
|
|
5626
5819
|
//#endregion
|
|
5627
5820
|
//#region src/helpers/core/create-readme.ts
|
|
5628
5821
|
async function createReadme(projectDir, options) {
|
|
@@ -5944,6 +6137,39 @@ async function initializeGit(projectDir, useGit) {
|
|
|
5944
6137
|
await $({ cwd: projectDir })`git commit -m ${"initial commit"}`;
|
|
5945
6138
|
}
|
|
5946
6139
|
|
|
6140
|
+
//#endregion
|
|
6141
|
+
//#region src/helpers/core/payments-setup.ts
|
|
6142
|
+
async function setupPayments(config) {
|
|
6143
|
+
const { payments, projectDir, frontend } = config;
|
|
6144
|
+
if (!payments || payments === "none") return;
|
|
6145
|
+
const serverDir = path.join(projectDir, "apps/server");
|
|
6146
|
+
const clientDir = path.join(projectDir, "apps/web");
|
|
6147
|
+
const serverDirExists = await fs.pathExists(serverDir);
|
|
6148
|
+
const clientDirExists = await fs.pathExists(clientDir);
|
|
6149
|
+
if (!serverDirExists) return;
|
|
6150
|
+
if (payments === "polar") {
|
|
6151
|
+
await addPackageDependency({
|
|
6152
|
+
dependencies: ["@polar-sh/better-auth", "@polar-sh/sdk"],
|
|
6153
|
+
projectDir: serverDir
|
|
6154
|
+
});
|
|
6155
|
+
if (clientDirExists) {
|
|
6156
|
+
const hasWebFrontend$1 = frontend.some((f) => [
|
|
6157
|
+
"react-router",
|
|
6158
|
+
"tanstack-router",
|
|
6159
|
+
"tanstack-start",
|
|
6160
|
+
"next",
|
|
6161
|
+
"nuxt",
|
|
6162
|
+
"svelte",
|
|
6163
|
+
"solid"
|
|
6164
|
+
].includes(f));
|
|
6165
|
+
if (hasWebFrontend$1) await addPackageDependency({
|
|
6166
|
+
dependencies: ["@polar-sh/better-auth"],
|
|
6167
|
+
projectDir: clientDir
|
|
6168
|
+
});
|
|
6169
|
+
}
|
|
6170
|
+
}
|
|
6171
|
+
}
|
|
6172
|
+
|
|
5947
6173
|
//#endregion
|
|
5948
6174
|
//#region src/utils/docker-utils.ts
|
|
5949
6175
|
async function isDockerInstalled() {
|
|
@@ -6011,6 +6237,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6011
6237
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
6012
6238
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
6013
6239
|
const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
|
|
6240
|
+
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions() : "";
|
|
6014
6241
|
const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
6015
6242
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
6016
6243
|
const hasWeb = frontend?.some((f) => [
|
|
@@ -6063,6 +6290,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6063
6290
|
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
6064
6291
|
if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
|
|
6065
6292
|
if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
|
|
6293
|
+
if (polarInstructions) output += `\n${polarInstructions.trim()}\n`;
|
|
6066
6294
|
if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
|
|
6067
6295
|
if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
|
|
6068
6296
|
output += `\n${pc.bold("Update all dependencies:\n")}${pc.cyan(tazeCommand)}\n\n`;
|
|
@@ -6151,6 +6379,9 @@ function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
|
6151
6379
|
function getClerkInstructions() {
|
|
6152
6380
|
return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
|
|
6153
6381
|
}
|
|
6382
|
+
function getPolarInstructions() {
|
|
6383
|
+
return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in apps/server/.env`;
|
|
6384
|
+
}
|
|
6154
6385
|
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
6155
6386
|
const instructions = [];
|
|
6156
6387
|
if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
|
|
@@ -6355,6 +6586,7 @@ async function createProject(options, cliInput) {
|
|
|
6355
6586
|
await setupDockerComposeTemplates(projectDir, options);
|
|
6356
6587
|
}
|
|
6357
6588
|
await setupAuthTemplate(projectDir, options);
|
|
6589
|
+
if (options.payments && options.payments !== "none") await setupPaymentsTemplate(projectDir, options);
|
|
6358
6590
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
|
|
6359
6591
|
await setupAddonsTemplate(projectDir, options);
|
|
6360
6592
|
await setupDeploymentTemplates(projectDir, options);
|
|
@@ -6367,6 +6599,7 @@ async function createProject(options, cliInput) {
|
|
|
6367
6599
|
}
|
|
6368
6600
|
if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
|
|
6369
6601
|
if (options.auth && options.auth !== "none") await setupAuth(options);
|
|
6602
|
+
if (options.payments && options.payments !== "none") await setupPayments(options);
|
|
6370
6603
|
await handleExtras(projectDir, options);
|
|
6371
6604
|
await setupEnvironmentVariables(options);
|
|
6372
6605
|
await updatePackageConfigurations(projectDir, options);
|
|
@@ -6374,7 +6607,6 @@ async function createProject(options, cliInput) {
|
|
|
6374
6607
|
await setupServerDeploy(options);
|
|
6375
6608
|
await createReadme(projectDir, options);
|
|
6376
6609
|
await writeBtsConfig(options);
|
|
6377
|
-
if (isConvex) await runConvexCodegen(projectDir, options.packageManager);
|
|
6378
6610
|
log.success("Project template successfully scaffolded!");
|
|
6379
6611
|
if (options.install) await installDependencies({
|
|
6380
6612
|
projectDir,
|
|
@@ -6445,6 +6677,7 @@ async function createProjectHandler(input) {
|
|
|
6445
6677
|
addons: [],
|
|
6446
6678
|
examples: [],
|
|
6447
6679
|
auth: "none",
|
|
6680
|
+
payments: "none",
|
|
6448
6681
|
git: false,
|
|
6449
6682
|
packageManager: "npm",
|
|
6450
6683
|
install: false,
|
|
@@ -6552,7 +6785,7 @@ async function addAddonsHandler(input) {
|
|
|
6552
6785
|
const detectedConfig = await detectProjectConfig(projectDir);
|
|
6553
6786
|
if (!detectedConfig) exitWithError("Could not detect project configuration. Please ensure this is a valid Better-T-Stack project.");
|
|
6554
6787
|
if (!input.addons || input.addons.length === 0) {
|
|
6555
|
-
const addonsPrompt = await getAddonsToAdd(detectedConfig.frontend || [], detectedConfig.addons || []);
|
|
6788
|
+
const addonsPrompt = await getAddonsToAdd(detectedConfig.frontend || [], detectedConfig.addons || [], detectedConfig.auth);
|
|
6556
6789
|
if (addonsPrompt.length > 0) input.addons = addonsPrompt;
|
|
6557
6790
|
}
|
|
6558
6791
|
if (!input.webDeploy) {
|
|
@@ -6691,6 +6924,7 @@ const router = t.router({
|
|
|
6691
6924
|
database: DatabaseSchema.optional(),
|
|
6692
6925
|
orm: ORMSchema.optional(),
|
|
6693
6926
|
auth: AuthSchema.optional(),
|
|
6927
|
+
payments: PaymentsSchema.optional(),
|
|
6694
6928
|
frontend: z.array(FrontendSchema).optional(),
|
|
6695
6929
|
addons: z.array(AddonsSchema).optional(),
|
|
6696
6930
|
examples: z.array(ExamplesSchema).optional(),
|