@better-t-stack/template-generator 3.26.0 → 3.26.1
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/index.d.mts +9 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +503 -40
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -705,8 +705,8 @@ function renameDevScriptsForAlchemy(vfs, config) {
|
|
|
705
705
|
//#region src/utils/add-deps.ts
|
|
706
706
|
const dependencyVersionMap = {
|
|
707
707
|
typescript: "^5",
|
|
708
|
-
"better-auth": "1.5.
|
|
709
|
-
"@better-auth/expo": "1.5.
|
|
708
|
+
"better-auth": "1.5.5",
|
|
709
|
+
"@better-auth/expo": "1.5.5",
|
|
710
710
|
"@clerk/backend": "^3.2.1",
|
|
711
711
|
"@clerk/express": "^2.0.5",
|
|
712
712
|
"@clerk/fastify": "^3.1.3",
|
|
@@ -776,17 +776,17 @@ const dependencyVersionMap = {
|
|
|
776
776
|
"@orpc/openapi": "^1.12.2",
|
|
777
777
|
"@orpc/zod": "^1.12.2",
|
|
778
778
|
"@orpc/tanstack-query": "^1.13.6",
|
|
779
|
-
"@trpc/tanstack-react-query": "^11.
|
|
780
|
-
"@trpc/server": "^11.
|
|
781
|
-
"@trpc/client": "^11.
|
|
779
|
+
"@trpc/tanstack-react-query": "^11.13.4",
|
|
780
|
+
"@trpc/server": "^11.13.4",
|
|
781
|
+
"@trpc/client": "^11.13.4",
|
|
782
782
|
next: "^16.2.0",
|
|
783
|
-
convex: "^1.
|
|
783
|
+
convex: "^1.33.1",
|
|
784
784
|
"@convex-dev/react-query": "^0.1.0",
|
|
785
785
|
"@convex-dev/agent": "^0.3.2",
|
|
786
786
|
"convex-svelte": "^0.0.12",
|
|
787
787
|
"convex-nuxt": "0.1.5",
|
|
788
788
|
"convex-vue": "^0.1.5",
|
|
789
|
-
"@convex-dev/better-auth": "^0.
|
|
789
|
+
"@convex-dev/better-auth": "^0.11.3",
|
|
790
790
|
"@tanstack/svelte-query": "^5.85.3",
|
|
791
791
|
"@tanstack/svelte-query-devtools": "^5.85.3",
|
|
792
792
|
"@tanstack/vue-query-devtools": "^6.1.5",
|
|
@@ -815,7 +815,7 @@ const dependencyVersionMap = {
|
|
|
815
815
|
"@t3-oss/env-core": "^0.13.1",
|
|
816
816
|
"@t3-oss/env-nextjs": "^0.13.1",
|
|
817
817
|
"@t3-oss/env-nuxt": "^0.13.1",
|
|
818
|
-
"@polar-sh/better-auth": "^1.
|
|
818
|
+
"@polar-sh/better-auth": "^1.8.3",
|
|
819
819
|
"@polar-sh/sdk": "^0.42.2"
|
|
820
820
|
};
|
|
821
821
|
/**
|
|
@@ -1335,6 +1335,7 @@ function addConvexDeps(vfs, frontend, frontendType) {
|
|
|
1335
1335
|
|
|
1336
1336
|
//#endregion
|
|
1337
1337
|
//#region src/processors/auth-deps.ts
|
|
1338
|
+
const CONVEX_BETTER_AUTH_VERSION = "1.5.3";
|
|
1338
1339
|
function processAuthDeps(vfs, config) {
|
|
1339
1340
|
const { auth, backend } = config;
|
|
1340
1341
|
if (!auth || auth === "none") return;
|
|
@@ -1396,13 +1397,13 @@ function processConvexAuthDeps(vfs, config) {
|
|
|
1396
1397
|
vfs,
|
|
1397
1398
|
packagePath: backendPath,
|
|
1398
1399
|
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
1399
|
-
customDependencies: { "better-auth":
|
|
1400
|
+
customDependencies: { "better-auth": CONVEX_BETTER_AUTH_VERSION }
|
|
1400
1401
|
});
|
|
1401
1402
|
if (hasNative) addPackageDependency({
|
|
1402
1403
|
vfs,
|
|
1403
1404
|
packagePath: backendPath,
|
|
1404
1405
|
dependencies: ["@better-auth/expo"],
|
|
1405
|
-
customDependencies: { "@better-auth/expo":
|
|
1406
|
+
customDependencies: { "@better-auth/expo": CONVEX_BETTER_AUTH_VERSION }
|
|
1406
1407
|
});
|
|
1407
1408
|
}
|
|
1408
1409
|
if (webExists) {
|
|
@@ -1410,7 +1411,7 @@ function processConvexAuthDeps(vfs, config) {
|
|
|
1410
1411
|
vfs,
|
|
1411
1412
|
packagePath: webPath,
|
|
1412
1413
|
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
1413
|
-
customDependencies: { "better-auth":
|
|
1414
|
+
customDependencies: { "better-auth": CONVEX_BETTER_AUTH_VERSION }
|
|
1414
1415
|
});
|
|
1415
1416
|
if (hasReactWebAuthForms) addPackageDependency({
|
|
1416
1417
|
vfs,
|
|
@@ -1438,8 +1439,8 @@ function processConvexAuthDeps(vfs, config) {
|
|
|
1438
1439
|
"@tanstack/react-form"
|
|
1439
1440
|
],
|
|
1440
1441
|
customDependencies: {
|
|
1441
|
-
"better-auth":
|
|
1442
|
-
"@better-auth/expo":
|
|
1442
|
+
"better-auth": CONVEX_BETTER_AUTH_VERSION,
|
|
1443
|
+
"@better-auth/expo": CONVEX_BETTER_AUTH_VERSION
|
|
1443
1444
|
}
|
|
1444
1445
|
});
|
|
1445
1446
|
}
|
|
@@ -1878,9 +1879,16 @@ function processEnvDeps(vfs, config) {
|
|
|
1878
1879
|
if (!vfs.exists(envPath)) return;
|
|
1879
1880
|
const { frontend, backend, runtime } = config;
|
|
1880
1881
|
const deps = ["zod"];
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1882
|
+
const hasNative = frontend.some((value) => [
|
|
1883
|
+
"native-bare",
|
|
1884
|
+
"native-uniwind",
|
|
1885
|
+
"native-unistyles"
|
|
1886
|
+
].includes(value));
|
|
1887
|
+
const hasNextJs = frontend.includes("next");
|
|
1888
|
+
const hasNuxt = frontend.includes("nuxt");
|
|
1889
|
+
if (hasNextJs) deps.push("@t3-oss/env-nextjs");
|
|
1890
|
+
else if (hasNuxt) deps.push("@t3-oss/env-nuxt");
|
|
1891
|
+
if (hasNative || !hasNextJs && !hasNuxt) deps.push("@t3-oss/env-core");
|
|
1884
1892
|
if (backend !== "convex" && backend !== "none" && runtime !== "workers" && !deps.includes("@t3-oss/env-core")) deps.push("@t3-oss/env-core");
|
|
1885
1893
|
addPackageDependency({
|
|
1886
1894
|
vfs,
|
|
@@ -2050,7 +2058,7 @@ function buildConvexBackendVars(frontend, auth, examples) {
|
|
|
2050
2058
|
const hasNextJs = frontend.includes("next");
|
|
2051
2059
|
const hasNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
|
|
2052
2060
|
const hasWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("tanstack-start") || hasNextJs || frontend.includes("nuxt") || frontend.includes("solid") || frontend.includes("svelte") || frontend.includes("astro");
|
|
2053
|
-
const defaultSiteUrl = hasNative && !hasWeb ? "http://localhost:8081" : "http://localhost:3001";
|
|
2061
|
+
const defaultSiteUrl = hasNative && !hasWeb ? "http://localhost:8081" : frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("svelte") ? "http://localhost:5173" : frontend.includes("astro") ? "http://localhost:4321" : "http://localhost:3001";
|
|
2054
2062
|
const vars = [];
|
|
2055
2063
|
if (examples?.includes("ai")) vars.push({
|
|
2056
2064
|
key: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
@@ -2088,7 +2096,7 @@ function buildConvexBackendVars(frontend, auth, examples) {
|
|
|
2088
2096
|
function buildConvexCommentBlocks(frontend, auth, examples) {
|
|
2089
2097
|
const hasNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
|
|
2090
2098
|
const hasWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("tanstack-start") || frontend.includes("next") || frontend.includes("nuxt") || frontend.includes("solid") || frontend.includes("svelte") || frontend.includes("astro");
|
|
2091
|
-
const defaultSiteUrl = hasNative && !hasWeb ? "http://localhost:8081" : "http://localhost:3001";
|
|
2099
|
+
const defaultSiteUrl = hasNative && !hasWeb ? "http://localhost:8081" : frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("svelte") ? "http://localhost:5173" : frontend.includes("astro") ? "http://localhost:4321" : "http://localhost:3001";
|
|
2092
2100
|
let commentBlocks = "";
|
|
2093
2101
|
if (examples?.includes("ai")) commentBlocks += `# Set Google AI API key for AI agent
|
|
2094
2102
|
# npx convex env set GOOGLE_GENERATIVE_AI_API_KEY=your_google_api_key
|
|
@@ -2101,11 +2109,12 @@ ${hasWeb || hasNative ? `# npx convex env set SITE_URL ${defaultSiteUrl}\n` : ""
|
|
|
2101
2109
|
}
|
|
2102
2110
|
function buildServerVars(backend, frontend, auth, api, database, dbSetup, runtime, webDeploy, serverDeploy, payments, examples) {
|
|
2103
2111
|
const hasReactRouter = frontend.includes("react-router");
|
|
2112
|
+
const hasTanStackRouter = frontend.includes("tanstack-router");
|
|
2104
2113
|
const hasSvelte = frontend.includes("svelte");
|
|
2105
2114
|
const hasAstro = frontend.includes("astro");
|
|
2106
2115
|
let corsOrigin = "http://localhost:3001";
|
|
2107
2116
|
if (hasAstro) corsOrigin = "http://localhost:4321";
|
|
2108
|
-
else if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
|
|
2117
|
+
else if (hasReactRouter || hasTanStackRouter || hasSvelte) corsOrigin = "http://localhost:5173";
|
|
2109
2118
|
else if (backend === "self") corsOrigin = "http://localhost:3001";
|
|
2110
2119
|
let databaseUrl = null;
|
|
2111
2120
|
if (database !== "none" && dbSetup === "none") switch (database) {
|
|
@@ -2615,6 +2624,7 @@ function generateReadmeContent(options) {
|
|
|
2615
2624
|
const { projectName, packageManager, database, auth, addons = [], orm = "drizzle", runtime = "bun", frontend = ["tanstack-router"], backend = "hono", api = "trpc", webDeploy, serverDeploy } = options;
|
|
2616
2625
|
const isConvex = backend === "convex";
|
|
2617
2626
|
const hasReactRouter = frontend.includes("react-router");
|
|
2627
|
+
const hasTanStackRouter = frontend.includes("tanstack-router");
|
|
2618
2628
|
const hasNative = hasNativeFrontend(frontend);
|
|
2619
2629
|
const hasReactWeb = frontend.some((f) => [
|
|
2620
2630
|
"tanstack-router",
|
|
@@ -2625,7 +2635,7 @@ function generateReadmeContent(options) {
|
|
|
2625
2635
|
const hasSvelte = frontend.includes("svelte");
|
|
2626
2636
|
const hasAstro = frontend.includes("astro");
|
|
2627
2637
|
const packageManagerRunCmd = `${packageManager} run`;
|
|
2628
|
-
const webPort = hasReactRouter || hasSvelte ? "5173" : hasAstro ? "4321" : "3001";
|
|
2638
|
+
const webPort = hasReactRouter || hasTanStackRouter || hasSvelte ? "5173" : hasAstro ? "4321" : "3001";
|
|
2629
2639
|
const stackDescription = generateStackDescription(frontend, backend, api, isConvex);
|
|
2630
2640
|
return `# ${projectName}
|
|
2631
2641
|
|
|
@@ -5869,7 +5879,14 @@ import { authComponent, createAuth } from "./auth";
|
|
|
5869
5879
|
const http = httpRouter();
|
|
5870
5880
|
|
|
5871
5881
|
{{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles") (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
|
|
5882
|
+
{{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
|
|
5883
|
+
authComponent.registerRoutesLazy(http, createAuth, {
|
|
5884
|
+
cors: true,
|
|
5885
|
+
trustedOrigins: [process.env.SITE_URL!],
|
|
5886
|
+
});
|
|
5887
|
+
{{else}}
|
|
5872
5888
|
authComponent.registerRoutes(http, createAuth, { cors: true });
|
|
5889
|
+
{{/if}}
|
|
5873
5890
|
{{else}}
|
|
5874
5891
|
authComponent.registerRoutes(http, createAuth);
|
|
5875
5892
|
{{/if}}
|
|
@@ -7585,6 +7602,402 @@ export const {
|
|
|
7585
7602
|
convexUrl: env.NEXT_PUBLIC_CONVEX_URL,
|
|
7586
7603
|
convexSiteUrl: env.NEXT_PUBLIC_CONVEX_SITE_URL,
|
|
7587
7604
|
});
|
|
7605
|
+
`],
|
|
7606
|
+
["auth/better-auth/convex/web/react/react-router/src/components/sign-in-form.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
7607
|
+
import { useForm } from "@tanstack/react-form";
|
|
7608
|
+
import { useNavigate } from "react-router";
|
|
7609
|
+
import { toast } from "sonner";
|
|
7610
|
+
import z from "zod";
|
|
7611
|
+
import { Button } from "@{{projectName}}/ui/components/button";
|
|
7612
|
+
import { Input } from "@{{projectName}}/ui/components/input";
|
|
7613
|
+
import { Label } from "@{{projectName}}/ui/components/label";
|
|
7614
|
+
|
|
7615
|
+
export default function SignInForm({
|
|
7616
|
+
onSwitchToSignUp,
|
|
7617
|
+
}: {
|
|
7618
|
+
onSwitchToSignUp: () => void;
|
|
7619
|
+
}) {
|
|
7620
|
+
const navigate = useNavigate();
|
|
7621
|
+
|
|
7622
|
+
const form = useForm({
|
|
7623
|
+
defaultValues: {
|
|
7624
|
+
email: "",
|
|
7625
|
+
password: "",
|
|
7626
|
+
},
|
|
7627
|
+
onSubmit: async ({ value }) => {
|
|
7628
|
+
await authClient.signIn.email(
|
|
7629
|
+
{
|
|
7630
|
+
email: value.email,
|
|
7631
|
+
password: value.password,
|
|
7632
|
+
},
|
|
7633
|
+
{
|
|
7634
|
+
onSuccess: () => {
|
|
7635
|
+
navigate("/dashboard");
|
|
7636
|
+
toast.success("Sign in successful");
|
|
7637
|
+
},
|
|
7638
|
+
onError: (error) => {
|
|
7639
|
+
toast.error(error.error.message || error.error.statusText);
|
|
7640
|
+
},
|
|
7641
|
+
},
|
|
7642
|
+
);
|
|
7643
|
+
},
|
|
7644
|
+
validators: {
|
|
7645
|
+
onSubmit: z.object({
|
|
7646
|
+
email: z.email("Invalid email address"),
|
|
7647
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
7648
|
+
}),
|
|
7649
|
+
},
|
|
7650
|
+
});
|
|
7651
|
+
|
|
7652
|
+
return (
|
|
7653
|
+
<div className="mx-auto mt-10 w-full max-w-md p-6">
|
|
7654
|
+
<h1 className="mb-6 text-center text-3xl font-bold">Welcome Back</h1>
|
|
7655
|
+
|
|
7656
|
+
<form
|
|
7657
|
+
onSubmit={(e) => {
|
|
7658
|
+
e.preventDefault();
|
|
7659
|
+
e.stopPropagation();
|
|
7660
|
+
form.handleSubmit();
|
|
7661
|
+
}}
|
|
7662
|
+
className="space-y-4"
|
|
7663
|
+
>
|
|
7664
|
+
<div>
|
|
7665
|
+
<form.Field name="email">
|
|
7666
|
+
{(field) => (
|
|
7667
|
+
<div className="space-y-2">
|
|
7668
|
+
<Label htmlFor={field.name}>Email</Label>
|
|
7669
|
+
<Input
|
|
7670
|
+
id={field.name}
|
|
7671
|
+
name={field.name}
|
|
7672
|
+
type="email"
|
|
7673
|
+
value={field.state.value}
|
|
7674
|
+
onBlur={field.handleBlur}
|
|
7675
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
7676
|
+
/>
|
|
7677
|
+
{field.state.meta.errors.map((error, index) => (
|
|
7678
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7679
|
+
{error?.message}
|
|
7680
|
+
</p>
|
|
7681
|
+
))}
|
|
7682
|
+
</div>
|
|
7683
|
+
)}
|
|
7684
|
+
</form.Field>
|
|
7685
|
+
</div>
|
|
7686
|
+
|
|
7687
|
+
<div>
|
|
7688
|
+
<form.Field name="password">
|
|
7689
|
+
{(field) => (
|
|
7690
|
+
<div className="space-y-2">
|
|
7691
|
+
<Label htmlFor={field.name}>Password</Label>
|
|
7692
|
+
<Input
|
|
7693
|
+
id={field.name}
|
|
7694
|
+
name={field.name}
|
|
7695
|
+
type="password"
|
|
7696
|
+
value={field.state.value}
|
|
7697
|
+
onBlur={field.handleBlur}
|
|
7698
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
7699
|
+
/>
|
|
7700
|
+
{field.state.meta.errors.map((error, index) => (
|
|
7701
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7702
|
+
{error?.message}
|
|
7703
|
+
</p>
|
|
7704
|
+
))}
|
|
7705
|
+
</div>
|
|
7706
|
+
)}
|
|
7707
|
+
</form.Field>
|
|
7708
|
+
</div>
|
|
7709
|
+
|
|
7710
|
+
<form.Subscribe
|
|
7711
|
+
selector={(state) => ({
|
|
7712
|
+
canSubmit: state.canSubmit,
|
|
7713
|
+
isSubmitting: state.isSubmitting,
|
|
7714
|
+
})}
|
|
7715
|
+
>
|
|
7716
|
+
{({ canSubmit, isSubmitting }) => (
|
|
7717
|
+
<Button type="submit" className="w-full" disabled={!canSubmit || isSubmitting}>
|
|
7718
|
+
{isSubmitting ? "Submitting..." : "Sign In"}
|
|
7719
|
+
</Button>
|
|
7720
|
+
)}
|
|
7721
|
+
</form.Subscribe>
|
|
7722
|
+
</form>
|
|
7723
|
+
|
|
7724
|
+
<div className="mt-4 text-center">
|
|
7725
|
+
<Button
|
|
7726
|
+
variant="link"
|
|
7727
|
+
onClick={onSwitchToSignUp}
|
|
7728
|
+
className="text-indigo-600 hover:text-indigo-800"
|
|
7729
|
+
>
|
|
7730
|
+
Need an account? Sign Up
|
|
7731
|
+
</Button>
|
|
7732
|
+
</div>
|
|
7733
|
+
</div>
|
|
7734
|
+
);
|
|
7735
|
+
}
|
|
7736
|
+
`],
|
|
7737
|
+
["auth/better-auth/convex/web/react/react-router/src/components/sign-up-form.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
7738
|
+
import { useForm } from "@tanstack/react-form";
|
|
7739
|
+
import { useNavigate } from "react-router";
|
|
7740
|
+
import { toast } from "sonner";
|
|
7741
|
+
import z from "zod";
|
|
7742
|
+
import { Button } from "@{{projectName}}/ui/components/button";
|
|
7743
|
+
import { Input } from "@{{projectName}}/ui/components/input";
|
|
7744
|
+
import { Label } from "@{{projectName}}/ui/components/label";
|
|
7745
|
+
|
|
7746
|
+
export default function SignUpForm({
|
|
7747
|
+
onSwitchToSignIn,
|
|
7748
|
+
}: {
|
|
7749
|
+
onSwitchToSignIn: () => void;
|
|
7750
|
+
}) {
|
|
7751
|
+
const navigate = useNavigate();
|
|
7752
|
+
|
|
7753
|
+
const form = useForm({
|
|
7754
|
+
defaultValues: {
|
|
7755
|
+
email: "",
|
|
7756
|
+
password: "",
|
|
7757
|
+
name: "",
|
|
7758
|
+
},
|
|
7759
|
+
onSubmit: async ({ value }) => {
|
|
7760
|
+
await authClient.signUp.email(
|
|
7761
|
+
{
|
|
7762
|
+
email: value.email,
|
|
7763
|
+
password: value.password,
|
|
7764
|
+
name: value.name,
|
|
7765
|
+
},
|
|
7766
|
+
{
|
|
7767
|
+
onSuccess: () => {
|
|
7768
|
+
navigate("/dashboard");
|
|
7769
|
+
toast.success("Sign up successful");
|
|
7770
|
+
},
|
|
7771
|
+
onError: (error) => {
|
|
7772
|
+
toast.error(error.error.message || error.error.statusText);
|
|
7773
|
+
},
|
|
7774
|
+
},
|
|
7775
|
+
);
|
|
7776
|
+
},
|
|
7777
|
+
validators: {
|
|
7778
|
+
onSubmit: z.object({
|
|
7779
|
+
name: z.string().min(2, "Name must be at least 2 characters"),
|
|
7780
|
+
email: z.email("Invalid email address"),
|
|
7781
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
7782
|
+
}),
|
|
7783
|
+
},
|
|
7784
|
+
});
|
|
7785
|
+
|
|
7786
|
+
return (
|
|
7787
|
+
<div className="mx-auto mt-10 w-full max-w-md p-6">
|
|
7788
|
+
<h1 className="mb-6 text-center text-3xl font-bold">Create Account</h1>
|
|
7789
|
+
|
|
7790
|
+
<form
|
|
7791
|
+
onSubmit={(e) => {
|
|
7792
|
+
e.preventDefault();
|
|
7793
|
+
e.stopPropagation();
|
|
7794
|
+
form.handleSubmit();
|
|
7795
|
+
}}
|
|
7796
|
+
className="space-y-4"
|
|
7797
|
+
>
|
|
7798
|
+
<div>
|
|
7799
|
+
<form.Field name="name">
|
|
7800
|
+
{(field) => (
|
|
7801
|
+
<div className="space-y-2">
|
|
7802
|
+
<Label htmlFor={field.name}>Name</Label>
|
|
7803
|
+
<Input
|
|
7804
|
+
id={field.name}
|
|
7805
|
+
name={field.name}
|
|
7806
|
+
value={field.state.value}
|
|
7807
|
+
onBlur={field.handleBlur}
|
|
7808
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
7809
|
+
/>
|
|
7810
|
+
{field.state.meta.errors.map((error, index) => (
|
|
7811
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7812
|
+
{error?.message}
|
|
7813
|
+
</p>
|
|
7814
|
+
))}
|
|
7815
|
+
</div>
|
|
7816
|
+
)}
|
|
7817
|
+
</form.Field>
|
|
7818
|
+
</div>
|
|
7819
|
+
|
|
7820
|
+
<div>
|
|
7821
|
+
<form.Field name="email">
|
|
7822
|
+
{(field) => (
|
|
7823
|
+
<div className="space-y-2">
|
|
7824
|
+
<Label htmlFor={field.name}>Email</Label>
|
|
7825
|
+
<Input
|
|
7826
|
+
id={field.name}
|
|
7827
|
+
name={field.name}
|
|
7828
|
+
type="email"
|
|
7829
|
+
value={field.state.value}
|
|
7830
|
+
onBlur={field.handleBlur}
|
|
7831
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
7832
|
+
/>
|
|
7833
|
+
{field.state.meta.errors.map((error, index) => (
|
|
7834
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7835
|
+
{error?.message}
|
|
7836
|
+
</p>
|
|
7837
|
+
))}
|
|
7838
|
+
</div>
|
|
7839
|
+
)}
|
|
7840
|
+
</form.Field>
|
|
7841
|
+
</div>
|
|
7842
|
+
|
|
7843
|
+
<div>
|
|
7844
|
+
<form.Field name="password">
|
|
7845
|
+
{(field) => (
|
|
7846
|
+
<div className="space-y-2">
|
|
7847
|
+
<Label htmlFor={field.name}>Password</Label>
|
|
7848
|
+
<Input
|
|
7849
|
+
id={field.name}
|
|
7850
|
+
name={field.name}
|
|
7851
|
+
type="password"
|
|
7852
|
+
value={field.state.value}
|
|
7853
|
+
onBlur={field.handleBlur}
|
|
7854
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
7855
|
+
/>
|
|
7856
|
+
{field.state.meta.errors.map((error, index) => (
|
|
7857
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7858
|
+
{error?.message}
|
|
7859
|
+
</p>
|
|
7860
|
+
))}
|
|
7861
|
+
</div>
|
|
7862
|
+
)}
|
|
7863
|
+
</form.Field>
|
|
7864
|
+
</div>
|
|
7865
|
+
|
|
7866
|
+
<form.Subscribe
|
|
7867
|
+
selector={(state) => ({
|
|
7868
|
+
canSubmit: state.canSubmit,
|
|
7869
|
+
isSubmitting: state.isSubmitting,
|
|
7870
|
+
})}
|
|
7871
|
+
>
|
|
7872
|
+
{({ canSubmit, isSubmitting }) => (
|
|
7873
|
+
<Button type="submit" className="w-full" disabled={!canSubmit || isSubmitting}>
|
|
7874
|
+
{isSubmitting ? "Submitting..." : "Sign Up"}
|
|
7875
|
+
</Button>
|
|
7876
|
+
)}
|
|
7877
|
+
</form.Subscribe>
|
|
7878
|
+
</form>
|
|
7879
|
+
|
|
7880
|
+
<div className="mt-4 text-center">
|
|
7881
|
+
<Button
|
|
7882
|
+
variant="link"
|
|
7883
|
+
onClick={onSwitchToSignIn}
|
|
7884
|
+
className="text-indigo-600 hover:text-indigo-800"
|
|
7885
|
+
>
|
|
7886
|
+
Already have an account? Sign In
|
|
7887
|
+
</Button>
|
|
7888
|
+
</div>
|
|
7889
|
+
</div>
|
|
7890
|
+
);
|
|
7891
|
+
}
|
|
7892
|
+
`],
|
|
7893
|
+
["auth/better-auth/convex/web/react/react-router/src/components/user-menu.tsx.hbs", `import { useNavigate } from "react-router";
|
|
7894
|
+
|
|
7895
|
+
import {
|
|
7896
|
+
DropdownMenu,
|
|
7897
|
+
DropdownMenuContent,
|
|
7898
|
+
DropdownMenuGroup,
|
|
7899
|
+
DropdownMenuItem,
|
|
7900
|
+
DropdownMenuLabel,
|
|
7901
|
+
DropdownMenuSeparator,
|
|
7902
|
+
DropdownMenuTrigger,
|
|
7903
|
+
} from "@{{projectName}}/ui/components/dropdown-menu";
|
|
7904
|
+
import { authClient } from "@/lib/auth-client";
|
|
7905
|
+
import { useQuery } from "convex/react";
|
|
7906
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
7907
|
+
|
|
7908
|
+
import { Button } from "@{{projectName}}/ui/components/button";
|
|
7909
|
+
|
|
7910
|
+
export default function UserMenu() {
|
|
7911
|
+
const navigate = useNavigate();
|
|
7912
|
+
const user = useQuery(api.auth.getCurrentUser);
|
|
7913
|
+
|
|
7914
|
+
return (
|
|
7915
|
+
<DropdownMenu>
|
|
7916
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
7917
|
+
{user?.name}
|
|
7918
|
+
</DropdownMenuTrigger>
|
|
7919
|
+
<DropdownMenuContent className="bg-card">
|
|
7920
|
+
<DropdownMenuGroup>
|
|
7921
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
7922
|
+
<DropdownMenuSeparator />
|
|
7923
|
+
<DropdownMenuItem>{user?.email}</DropdownMenuItem>
|
|
7924
|
+
<DropdownMenuItem
|
|
7925
|
+
variant="destructive"
|
|
7926
|
+
onClick={() => {
|
|
7927
|
+
authClient.signOut({
|
|
7928
|
+
fetchOptions: {
|
|
7929
|
+
onSuccess: () => {
|
|
7930
|
+
navigate("/dashboard");
|
|
7931
|
+
},
|
|
7932
|
+
},
|
|
7933
|
+
});
|
|
7934
|
+
}}
|
|
7935
|
+
>
|
|
7936
|
+
Sign Out
|
|
7937
|
+
</DropdownMenuItem>
|
|
7938
|
+
</DropdownMenuGroup>
|
|
7939
|
+
</DropdownMenuContent>
|
|
7940
|
+
</DropdownMenu>
|
|
7941
|
+
);
|
|
7942
|
+
}
|
|
7943
|
+
`],
|
|
7944
|
+
["auth/better-auth/convex/web/react/react-router/src/lib/auth-client.ts.hbs", `import { createAuthClient } from "better-auth/react";
|
|
7945
|
+
import {
|
|
7946
|
+
convexClient,
|
|
7947
|
+
crossDomainClient,
|
|
7948
|
+
} from "@convex-dev/better-auth/client/plugins";
|
|
7949
|
+
import { env } from "@{{projectName}}/env/web";
|
|
7950
|
+
|
|
7951
|
+
export const authClient = createAuthClient({
|
|
7952
|
+
baseURL: env.VITE_CONVEX_SITE_URL,
|
|
7953
|
+
plugins: [crossDomainClient(), convexClient()],
|
|
7954
|
+
});
|
|
7955
|
+
`],
|
|
7956
|
+
["auth/better-auth/convex/web/react/react-router/src/routes/dashboard.tsx.hbs", `import SignInForm from "@/components/sign-in-form";
|
|
7957
|
+
import SignUpForm from "@/components/sign-up-form";
|
|
7958
|
+
import UserMenu from "@/components/user-menu";
|
|
7959
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
7960
|
+
import {
|
|
7961
|
+
Authenticated,
|
|
7962
|
+
AuthLoading,
|
|
7963
|
+
Unauthenticated,
|
|
7964
|
+
useQuery,
|
|
7965
|
+
} from "convex/react";
|
|
7966
|
+
import { useState } from "react";
|
|
7967
|
+
|
|
7968
|
+
function PrivateDashboardContent() {
|
|
7969
|
+
const privateData = useQuery(api.privateData.get);
|
|
7970
|
+
|
|
7971
|
+
return (
|
|
7972
|
+
<div>
|
|
7973
|
+
<h1>Dashboard</h1>
|
|
7974
|
+
<p>privateData: {privateData?.message}</p>
|
|
7975
|
+
<UserMenu />
|
|
7976
|
+
</div>
|
|
7977
|
+
);
|
|
7978
|
+
}
|
|
7979
|
+
|
|
7980
|
+
export default function Dashboard() {
|
|
7981
|
+
const [showSignIn, setShowSignIn] = useState(false);
|
|
7982
|
+
|
|
7983
|
+
return (
|
|
7984
|
+
<>
|
|
7985
|
+
<Authenticated>
|
|
7986
|
+
<PrivateDashboardContent />
|
|
7987
|
+
</Authenticated>
|
|
7988
|
+
<Unauthenticated>
|
|
7989
|
+
{showSignIn ? (
|
|
7990
|
+
<SignInForm onSwitchToSignUp={() => setShowSignIn(false)} />
|
|
7991
|
+
) : (
|
|
7992
|
+
<SignUpForm onSwitchToSignIn={() => setShowSignIn(true)} />
|
|
7993
|
+
)}
|
|
7994
|
+
</Unauthenticated>
|
|
7995
|
+
<AuthLoading>
|
|
7996
|
+
<div>Loading...</div>
|
|
7997
|
+
</AuthLoading>
|
|
7998
|
+
</>
|
|
7999
|
+
);
|
|
8000
|
+
}
|
|
7588
8001
|
`],
|
|
7589
8002
|
["auth/better-auth/convex/web/react/tanstack-router/src/components/sign-in-form.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
7590
8003
|
import { useForm } from "@tanstack/react-form";
|
|
@@ -7661,8 +8074,8 @@ export default function SignInForm({
|
|
|
7661
8074
|
onBlur={field.handleBlur}
|
|
7662
8075
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
7663
8076
|
/>
|
|
7664
|
-
{field.state.meta.errors.map((error) => (
|
|
7665
|
-
<p key={error
|
|
8077
|
+
{field.state.meta.errors.map((error, index) => (
|
|
8078
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7666
8079
|
{error?.message}
|
|
7667
8080
|
</p>
|
|
7668
8081
|
))}
|
|
@@ -7684,8 +8097,8 @@ export default function SignInForm({
|
|
|
7684
8097
|
onBlur={field.handleBlur}
|
|
7685
8098
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
7686
8099
|
/>
|
|
7687
|
-
{field.state.meta.errors.map((error) => (
|
|
7688
|
-
<p key={error
|
|
8100
|
+
{field.state.meta.errors.map((error, index) => (
|
|
8101
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7689
8102
|
{error?.message}
|
|
7690
8103
|
</p>
|
|
7691
8104
|
))}
|
|
@@ -7797,8 +8210,8 @@ export default function SignUpForm({
|
|
|
7797
8210
|
onBlur={field.handleBlur}
|
|
7798
8211
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
7799
8212
|
/>
|
|
7800
|
-
{field.state.meta.errors.map((error) => (
|
|
7801
|
-
<p key={error
|
|
8213
|
+
{field.state.meta.errors.map((error, index) => (
|
|
8214
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7802
8215
|
{error?.message}
|
|
7803
8216
|
</p>
|
|
7804
8217
|
))}
|
|
@@ -7820,8 +8233,8 @@ export default function SignUpForm({
|
|
|
7820
8233
|
onBlur={field.handleBlur}
|
|
7821
8234
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
7822
8235
|
/>
|
|
7823
|
-
{field.state.meta.errors.map((error) => (
|
|
7824
|
-
<p key={error
|
|
8236
|
+
{field.state.meta.errors.map((error, index) => (
|
|
8237
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7825
8238
|
{error?.message}
|
|
7826
8239
|
</p>
|
|
7827
8240
|
))}
|
|
@@ -7843,8 +8256,8 @@ export default function SignUpForm({
|
|
|
7843
8256
|
onBlur={field.handleBlur}
|
|
7844
8257
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
7845
8258
|
/>
|
|
7846
|
-
{field.state.meta.errors.map((error) => (
|
|
7847
|
-
<p key={error
|
|
8259
|
+
{field.state.meta.errors.map((error, index) => (
|
|
8260
|
+
<p key={\`\${field.name}-error-\${index}\`} className="text-red-500">
|
|
7848
8261
|
{error?.message}
|
|
7849
8262
|
</p>
|
|
7850
8263
|
))}
|
|
@@ -7961,18 +8374,25 @@ export const Route = createFileRoute("/dashboard")({
|
|
|
7961
8374
|
component: RouteComponent,
|
|
7962
8375
|
});
|
|
7963
8376
|
|
|
8377
|
+
function PrivateDashboardContent() {
|
|
8378
|
+
const privateData = useQuery(api.privateData.get);
|
|
8379
|
+
|
|
8380
|
+
return (
|
|
8381
|
+
<div>
|
|
8382
|
+
<h1>Dashboard</h1>
|
|
8383
|
+
<p>privateData: {privateData?.message}</p>
|
|
8384
|
+
<UserMenu />
|
|
8385
|
+
</div>
|
|
8386
|
+
);
|
|
8387
|
+
}
|
|
8388
|
+
|
|
7964
8389
|
function RouteComponent() {
|
|
7965
8390
|
const [showSignIn, setShowSignIn] = useState(false);
|
|
7966
|
-
const privateData = useQuery(api.privateData.get);
|
|
7967
8391
|
|
|
7968
8392
|
return (
|
|
7969
8393
|
<>
|
|
7970
8394
|
<Authenticated>
|
|
7971
|
-
<
|
|
7972
|
-
<h1>Dashboard</h1>
|
|
7973
|
-
<p>privateData: {privateData?.message}</p>
|
|
7974
|
-
<UserMenu />
|
|
7975
|
-
</div>
|
|
8395
|
+
<PrivateDashboardContent />
|
|
7976
8396
|
</Authenticated>
|
|
7977
8397
|
<Unauthenticated>
|
|
7978
8398
|
{showSignIn ? (
|
|
@@ -24802,6 +25222,9 @@ export const unstable_settings = {
|
|
|
24802
25222
|
|
|
24803
25223
|
{{#if (eq backend "convex")}}
|
|
24804
25224
|
const convex = new ConvexReactClient(env.EXPO_PUBLIC_CONVEX_URL, {
|
|
25225
|
+
{{#if (eq auth "better-auth")}}
|
|
25226
|
+
expectAuth: true,
|
|
25227
|
+
{{/if}}
|
|
24805
25228
|
unsavedChangesWarning: false,
|
|
24806
25229
|
});
|
|
24807
25230
|
{{/if}}
|
|
@@ -25850,6 +26273,9 @@ export const unstable_settings = {
|
|
|
25850
26273
|
|
|
25851
26274
|
{{#if (eq backend "convex")}}
|
|
25852
26275
|
const convex = new ConvexReactClient(env.EXPO_PUBLIC_CONVEX_URL, {
|
|
26276
|
+
{{#if (eq auth "better-auth")}}
|
|
26277
|
+
expectAuth: true,
|
|
26278
|
+
{{/if}}
|
|
25853
26279
|
unsavedChangesWarning: false,
|
|
25854
26280
|
});
|
|
25855
26281
|
{{/if}}
|
|
@@ -27150,6 +27576,9 @@ export const unstable_settings = {
|
|
|
27150
27576
|
|
|
27151
27577
|
{{#if (eq backend "convex")}}
|
|
27152
27578
|
const convex = new ConvexReactClient(env.EXPO_PUBLIC_CONVEX_URL, {
|
|
27579
|
+
{{#if (eq auth "better-auth")}}
|
|
27580
|
+
expectAuth: true,
|
|
27581
|
+
{{/if}}
|
|
27153
27582
|
unsavedChangesWarning: false,
|
|
27154
27583
|
});
|
|
27155
27584
|
{{/if}}
|
|
@@ -27946,7 +28375,7 @@ module.exports = uniwindConfig;
|
|
|
27946
28375
|
"expo-secure-store": "~55.0.8",
|
|
27947
28376
|
"expo-status-bar": "~55.0.4",
|
|
27948
28377
|
"expo-web-browser": "~55.0.9",
|
|
27949
|
-
"heroui-native": "^1.0.0
|
|
28378
|
+
"heroui-native": "^1.0.0",
|
|
27950
28379
|
"react": "19.2.0",
|
|
27951
28380
|
"react-dom": "19.2.0",
|
|
27952
28381
|
"react-native": "0.83.2",
|
|
@@ -27960,8 +28389,8 @@ module.exports = uniwindConfig;
|
|
|
27960
28389
|
"react-native-worklets": "0.7.2",
|
|
27961
28390
|
"tailwind-merge": "^3.4.0",
|
|
27962
28391
|
"tailwind-variants": "^3.2.2",
|
|
27963
|
-
"tailwindcss": "^4.
|
|
27964
|
-
"uniwind": "^1.
|
|
28392
|
+
"tailwindcss": "^4.2.2",
|
|
28393
|
+
"uniwind": "^1.6.0"
|
|
27965
28394
|
},
|
|
27966
28395
|
"devDependencies": {
|
|
27967
28396
|
"@types/node": "^24.10.0",
|
|
@@ -28830,6 +29259,9 @@ import { ConvexReactClient } from "convex/react";
|
|
|
28830
29259
|
import { env } from "@{{projectName}}/env/web";
|
|
28831
29260
|
{{#if (eq auth "clerk")}}
|
|
28832
29261
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
29262
|
+
{{else if (eq auth "better-auth")}}
|
|
29263
|
+
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
|
|
29264
|
+
import { authClient } from "@/lib/auth-client";
|
|
28833
29265
|
{{else}}
|
|
28834
29266
|
import { ConvexProvider } from "convex/react";
|
|
28835
29267
|
{{/if}}
|
|
@@ -28899,10 +29331,18 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
28899
29331
|
{{#if (eq backend "convex")}}
|
|
28900
29332
|
{{#if (eq auth "clerk")}}
|
|
28901
29333
|
export default function App({ loaderData }: Route.ComponentProps) {
|
|
29334
|
+
{{else if (eq auth "better-auth")}}
|
|
29335
|
+
export default function App() {
|
|
28902
29336
|
{{else}}
|
|
28903
29337
|
export default function App() {
|
|
28904
29338
|
{{/if}}
|
|
29339
|
+
{{#if (eq auth "better-auth")}}
|
|
29340
|
+
const convex = new ConvexReactClient(env.VITE_CONVEX_URL, {
|
|
29341
|
+
expectAuth: true,
|
|
29342
|
+
});
|
|
29343
|
+
{{else}}
|
|
28905
29344
|
const convex = new ConvexReactClient(env.VITE_CONVEX_URL);
|
|
29345
|
+
{{/if}}
|
|
28906
29346
|
{{#if (eq auth "clerk")}}
|
|
28907
29347
|
return (
|
|
28908
29348
|
<ClerkProvider loaderData={loaderData}>
|
|
@@ -28922,6 +29362,23 @@ export default function App() {
|
|
|
28922
29362
|
</ConvexProviderWithClerk>
|
|
28923
29363
|
</ClerkProvider>
|
|
28924
29364
|
);
|
|
29365
|
+
{{else if (eq auth "better-auth")}}
|
|
29366
|
+
return (
|
|
29367
|
+
<ConvexBetterAuthProvider client={convex} authClient={authClient}>
|
|
29368
|
+
<ThemeProvider
|
|
29369
|
+
attribute="class"
|
|
29370
|
+
defaultTheme="dark"
|
|
29371
|
+
disableTransitionOnChange
|
|
29372
|
+
storageKey="vite-ui-theme"
|
|
29373
|
+
>
|
|
29374
|
+
<div className="grid grid-rows-[auto_1fr] h-svh">
|
|
29375
|
+
<Header />
|
|
29376
|
+
<Outlet />
|
|
29377
|
+
</div>
|
|
29378
|
+
<Toaster richColors />
|
|
29379
|
+
</ThemeProvider>
|
|
29380
|
+
</ConvexBetterAuthProvider>
|
|
29381
|
+
);
|
|
28925
29382
|
{{else}}
|
|
28926
29383
|
return (
|
|
28927
29384
|
<ConvexProvider client={convex}>
|
|
@@ -29340,7 +29797,13 @@ import { routeTree } from "./routeTree.gen";
|
|
|
29340
29797
|
{{else}}
|
|
29341
29798
|
import { ConvexProvider } from "convex/react";
|
|
29342
29799
|
{{/if}}
|
|
29800
|
+
{{#if (eq auth "better-auth")}}
|
|
29801
|
+
const convex = new ConvexReactClient(env.VITE_CONVEX_URL, {
|
|
29802
|
+
expectAuth: true,
|
|
29803
|
+
});
|
|
29804
|
+
{{else}}
|
|
29343
29805
|
const convex = new ConvexReactClient(env.VITE_CONVEX_URL);
|
|
29806
|
+
{{/if}}
|
|
29344
29807
|
{{/if}}
|
|
29345
29808
|
|
|
29346
29809
|
{{#if (and (eq auth "clerk") (ne backend "convex") (ne api "none"))}}
|
|
@@ -32631,7 +33094,7 @@ function SuccessPage() {
|
|
|
32631
33094
|
</div>
|
|
32632
33095
|
`]
|
|
32633
33096
|
]);
|
|
32634
|
-
const TEMPLATE_COUNT =
|
|
33097
|
+
const TEMPLATE_COUNT = 462;
|
|
32635
33098
|
|
|
32636
33099
|
//#endregion
|
|
32637
33100
|
export { EMBEDDED_TEMPLATES, GeneratorError, Handlebars, TEMPLATE_COUNT, VirtualFileSystem, dependencyVersionMap, generate, generateReproducibleCommand, isBinaryFile, processAddonTemplates, processAddonsDeps, processFileContent, processTemplateString, transformFilename, writeBtsConfigToVfs };
|