create-better-t-stack 2.45.5 → 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.
Files changed (75) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.js +1 -1
  4. package/dist/{src-Cun9EO6e.js → src-lN80CwOs.js} +396 -162
  5. package/package.json +1 -1
  6. package/templates/auth/better-auth/convex/backend/convex/auth.config.ts.hbs +8 -0
  7. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +48 -0
  8. package/templates/auth/better-auth/convex/backend/convex/convex.config.ts.hbs +7 -0
  9. package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +12 -0
  10. package/templates/auth/better-auth/convex/backend/convex/privateData.ts.hbs +16 -0
  11. package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +3 -0
  12. package/templates/auth/better-auth/convex/web/react/next/src/app/dashboard/page.tsx.hbs +40 -0
  13. package/templates/auth/better-auth/convex/web/react/next/src/components/sign-in-form.tsx.hbs +129 -0
  14. package/templates/auth/better-auth/convex/web/react/next/src/components/sign-up-form.tsx.hbs +154 -0
  15. package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +48 -0
  16. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-client.ts.hbs +6 -0
  17. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +6 -0
  18. package/templates/auth/better-auth/convex/web/react/tanstack-router/src/lib/auth-client.ts.hbs +10 -0
  19. package/templates/auth/better-auth/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +43 -0
  20. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/sign-in-form.tsx.hbs +133 -0
  21. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/sign-up-form.tsx.hbs +158 -0
  22. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +50 -0
  23. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-client.ts.hbs +6 -0
  24. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +5 -0
  25. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +11 -0
  26. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +43 -0
  27. package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +153 -6
  28. package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +36 -2
  29. package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts.hbs +22 -0
  30. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +6 -0
  31. package/templates/auth/better-auth/web/react/next/src/app/dashboard/dashboard.tsx.hbs +58 -0
  32. package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +31 -41
  33. package/templates/auth/better-auth/web/react/react-router/src/routes/dashboard.tsx.hbs +37 -3
  34. package/templates/auth/better-auth/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +50 -32
  35. package/templates/auth/better-auth/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +51 -36
  36. package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts.hbs +11 -0
  37. package/templates/auth/better-auth/web/solid/src/routes/dashboard.tsx.hbs +52 -29
  38. package/templates/auth/better-auth/web/svelte/src/lib/{auth-client.ts → auth-client.ts.hbs} +6 -0
  39. package/templates/auth/better-auth/web/svelte/src/routes/dashboard/+page.svelte.hbs +24 -3
  40. package/templates/backend/server/server-base/package.json.hbs +1 -1
  41. package/templates/extras/bunfig.toml.hbs +4 -0
  42. package/templates/frontend/react/next/src/components/providers.tsx.hbs +8 -0
  43. package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +8 -1
  44. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +51 -0
  45. package/templates/frontend/react/tanstack-start/src/routes/index.tsx.hbs +2 -2
  46. package/templates/frontend/react/web-base/src/components/header.tsx.hbs +2 -2
  47. package/templates/frontend/solid/package.json.hbs +10 -10
  48. package/templates/payments/polar/server/base/src/lib/payments.ts.hbs +6 -0
  49. package/templates/payments/polar/web/nuxt/app/pages/success.vue.hbs +11 -0
  50. package/templates/payments/polar/web/react/next/src/app/success/page.tsx.hbs +15 -0
  51. package/templates/payments/polar/web/react/react-router/src/routes/success.tsx.hbs +13 -0
  52. package/templates/payments/polar/web/react/tanstack-router/src/routes/success.tsx.hbs +19 -0
  53. package/templates/payments/polar/web/react/tanstack-start/src/routes/success.tsx.hbs +19 -0
  54. package/templates/payments/polar/web/solid/src/routes/success.tsx.hbs +23 -0
  55. package/templates/payments/polar/web/svelte/src/routes/success/+page.svelte.hbs +12 -0
  56. package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts +0 -16
  57. package/templates/auth/better-auth/web/react/next/src/components/theme-provider.tsx +0 -11
  58. package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts +0 -5
  59. /package/templates/auth/better-auth/web/nuxt/app/components/{SignInForm.vue → SignInForm.vue.hbs} +0 -0
  60. /package/templates/auth/better-auth/web/nuxt/app/components/{SignUpForm.vue → SignUpForm.vue.hbs} +0 -0
  61. /package/templates/auth/better-auth/web/nuxt/app/components/{UserMenu.vue → UserMenu.vue.hbs} +0 -0
  62. /package/templates/auth/better-auth/web/nuxt/app/pages/{login.vue → login.vue.hbs} +0 -0
  63. /package/templates/auth/better-auth/web/react/next/src/app/login/{page.tsx → page.tsx.hbs} +0 -0
  64. /package/templates/auth/better-auth/web/react/next/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  65. /package/templates/auth/better-auth/web/react/next/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  66. /package/templates/auth/better-auth/web/react/next/src/components/{user-menu.tsx → user-menu.tsx.hbs} +0 -0
  67. /package/templates/auth/better-auth/web/react/react-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  68. /package/templates/auth/better-auth/web/react/react-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  69. /package/templates/auth/better-auth/web/react/react-router/src/components/{user-menu.tsx → user-menu.tsx.hbs} +0 -0
  70. /package/templates/auth/better-auth/web/react/react-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  71. /package/templates/auth/better-auth/web/svelte/src/components/{SignInForm.svelte → SignInForm.svelte.hbs} +0 -0
  72. /package/templates/auth/better-auth/web/svelte/src/components/{SignUpForm.svelte → SignUpForm.svelte.hbs} +0 -0
  73. /package/templates/auth/better-auth/web/svelte/src/components/{UserMenu.svelte → UserMenu.svelte.hbs} +0 -0
  74. /package/templates/auth/better-auth/web/svelte/src/routes/login/{+page.svelte → +page.svelte.hbs} +0 -0
  75. /package/templates/frontend/react/web-base/src/components/{loader.tsx → loader.tsx.hbs} +0 -0
@@ -1,47 +1,37 @@
1
- "use client"
2
1
  import { authClient } from "@/lib/auth-client";
3
- {{#if (eq api "orpc")}}
4
- import { useQuery } from "@tanstack/react-query";
5
- import { orpc } from "@/utils/orpc";
6
- {{/if}}
7
- {{#if (eq api "trpc")}}
8
- import { useQuery } from "@tanstack/react-query";
9
- import { trpc } from "@/utils/trpc";
10
- {{/if}}
11
- import { useRouter } from "next/navigation";
12
- import { useEffect } from "react";
2
+ import { redirect } from "next/navigation";
3
+ import Dashboard from "./dashboard";
4
+ import { headers } from "next/headers";
13
5
 
14
- export default function Dashboard() {
15
- const router = useRouter();
16
- const { data: session, isPending } = authClient.useSession();
6
+ export default async function DashboardPage() {
7
+ const session = await authClient.getSession({
8
+ fetchOptions: {
9
+ headers: await headers()
10
+ }
11
+ });
17
12
 
18
- {{#if (eq api "orpc")}}
19
- const privateData = useQuery(orpc.privateData.queryOptions());
20
- {{/if}}
21
- {{#if (eq api "trpc")}}
22
- const privateData = useQuery(trpc.privateData.queryOptions());
23
- {{/if}}
13
+ if (!session.data) {
14
+ redirect("/login");
15
+ }
24
16
 
25
- useEffect(() => {
26
- if (!session && !isPending) {
27
- router.push("/login");
28
- }
29
- }, [session, isPending]);
17
+ {{#if (eq payments "polar")}}
18
+ const { data: customerState, error } = await authClient.customer.state({
19
+ fetchOptions: {
20
+ headers: await headers()
21
+ }
22
+ });
23
+ {{/if}}
30
24
 
31
- if (isPending) {
32
- return <div>Loading...</div>;
33
- }
34
-
35
- return (
36
- <div>
37
- <h1>Dashboard</h1>
38
- <p>Welcome {session?.user.name}</p>
39
- {{#if (eq api "orpc")}}
40
- <p>privateData: {privateData.data?.message}</p>
41
- {{/if}}
42
- {{#if (eq api "trpc")}}
43
- <p>privateData: {privateData.data?.message}</p>
44
- {{/if}}
45
- </div>
46
- );
25
+ return (
26
+ <div>
27
+ <h1>Dashboard</h1>
28
+ <p>Welcome {session.data.user.name}</p>
29
+ <Dashboard
30
+ session={session.data}
31
+ {{#if (eq payments "polar")}}
32
+ customerState={customerState}
33
+ {{/if}}
34
+ />
35
+ </div>
36
+ );
47
37
  }
@@ -1,3 +1,4 @@
1
+ import { Button } from "@/components/ui/button";
1
2
  import { authClient } from "@/lib/auth-client";
2
3
  {{#if (eq api "orpc")}}
3
4
  import { orpc } from "@/utils/orpc";
@@ -8,12 +9,15 @@ import { trpc } from "@/utils/trpc";
8
9
  {{#if ( or (eq api "orpc") (eq api "trpc"))}}
9
10
  import { useQuery } from "@tanstack/react-query";
10
11
  {{/if}}
11
- import { useEffect } from "react";
12
+ import { useEffect, useState } from "react";
12
13
  import { useNavigate } from "react-router";
13
14
 
14
15
  export default function Dashboard() {
15
16
  const { data: session, isPending } = authClient.useSession();
16
17
  const navigate = useNavigate();
18
+ {{#if (eq payments "polar")}}
19
+ const [customerState, setCustomerState] = useState<any>(null);
20
+ {{/if}}
17
21
 
18
22
  {{#if (eq api "orpc")}}
19
23
  const privateData = useQuery(orpc.privateData.queryOptions());
@@ -26,18 +30,48 @@ export default function Dashboard() {
26
30
  if (!session && !isPending) {
27
31
  navigate("/login");
28
32
  }
29
- }, [session, isPending]);
33
+ }, [session, isPending, navigate]);
34
+
35
+ {{#if (eq payments "polar")}}
36
+ useEffect(() => {
37
+ async function fetchCustomerState() {
38
+ if (session) {
39
+ const { data } = await authClient.customer.state();
40
+ setCustomerState(data);
41
+ }
42
+ }
43
+
44
+ fetchCustomerState();
45
+ }, [session]);
46
+ {{/if}}
30
47
 
31
48
  if (isPending) {
32
49
  return <div>Loading...</div>;
33
50
  }
34
51
 
52
+ {{#if (eq payments "polar")}}
53
+ const hasProSubscription = customerState?.activeSubscriptions?.length! > 0;
54
+ console.log("Active subscriptions:", customerState?.activeSubscriptions);
55
+ {{/if}}
56
+
35
57
  return (
36
58
  <div>
37
59
  <h1>Dashboard</h1>
38
60
  <p>Welcome {session?.user.name}</p>
39
61
  {{#if ( or (eq api "orpc") (eq api "trpc"))}}
40
- <p>privateData: {privateData.data?.message}</p>
62
+ <p>API: {privateData.data?.message}</p>
63
+ {{/if}}
64
+ {{#if (eq payments "polar")}}
65
+ <p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
66
+ {hasProSubscription ? (
67
+ <Button onClick={async () => await authClient.customer.portal()}>
68
+ Manage Subscription
69
+ </Button>
70
+ ) : (
71
+ <Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
72
+ Upgrade to Pro
73
+ </Button>
74
+ )}
41
75
  {{/if}}
42
76
  </div>
43
77
  );
@@ -1,3 +1,4 @@
1
+ import { Button } from "@/components/ui/button";
1
2
  import { authClient } from "@/lib/auth-client";
2
3
  {{#if (eq api "orpc")}}
3
4
  import { orpc } from "@/utils/orpc";
@@ -8,44 +9,61 @@ import { trpc } from "@/utils/trpc";
8
9
  {{#if ( or (eq api "orpc") (eq api "trpc"))}}
9
10
  import { useQuery } from "@tanstack/react-query";
10
11
  {{/if}}
11
- import { createFileRoute } from "@tanstack/react-router";
12
- import { useEffect } from "react";
12
+ import { createFileRoute, redirect } from "@tanstack/react-router";
13
13
 
14
14
  export const Route = createFileRoute("/dashboard")({
15
- component: RouteComponent,
15
+ component: RouteComponent,
16
+ beforeLoad: async () => {
17
+ const session = await authClient.getSession();
18
+ if (!session.data) {
19
+ redirect({
20
+ to: "/login",
21
+ throw: true
22
+ });
23
+ }
24
+ {{#if (eq payments "polar")}}
25
+ const {data: customerState} = await authClient.customer.state()
26
+ return { session, customerState };
27
+ {{else}}
28
+ return { session };
29
+ {{/if}}
30
+ }
16
31
  });
17
32
 
18
33
  function RouteComponent() {
19
- const { data: session, isPending } = authClient.useSession();
34
+ const { session{{#if (eq payments "polar")}}, customerState{{/if}} } = Route.useRouteContext();
20
35
 
21
- const navigate = Route.useNavigate();
36
+ {{#if (eq api "orpc")}}
37
+ const privateData = useQuery(orpc.privateData.queryOptions());
38
+ {{/if}}
39
+ {{#if (eq api "trpc")}}
40
+ const privateData = useQuery(trpc.privateData.queryOptions());
41
+ {{/if}}
22
42
 
23
- {{#if (eq api "orpc")}}
24
- const privateData = useQuery(orpc.privateData.queryOptions());
25
- {{/if}}
26
- {{#if (eq api "trpc")}}
27
- const privateData = useQuery(trpc.privateData.queryOptions());
28
- {{/if}}
43
+ {{#if (eq payments "polar")}}
44
+ const hasProSubscription = customerState?.activeSubscriptions?.length! > 0
45
+ console.log("Active subscriptions:", customerState?.activeSubscriptions)
46
+ {{/if}}
29
47
 
30
- useEffect(() => {
31
- if (!session && !isPending) {
32
- navigate({
33
- to: "/login",
34
- });
35
- }
36
- }, [session, isPending]);
37
-
38
- if (isPending) {
39
- return <div>Loading...</div>;
40
- }
41
-
42
- return (
43
- <div>
44
- <h1>Dashboard</h1>
45
- <p>Welcome {session?.user.name}</p>
46
- {{#if ( or (eq api "orpc") (eq api "trpc"))}}
47
- <p>privateData: {privateData.data?.message}</p>
48
- {{/if}}
49
- </div>
50
- );
48
+ return (
49
+ <div>
50
+ <h1>Dashboard</h1>
51
+ <p>Welcome {session.data?.user.name}</p>
52
+ {{#if ( or (eq api "orpc") (eq api "trpc"))}}
53
+ <p>API: {privateData.data?.message}</p>
54
+ {{/if}}
55
+ {{#if (eq payments "polar")}}
56
+ <p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
57
+ {hasProSubscription ? (
58
+ <Button onClick={async () => await authClient.customer.portal()}>
59
+ Manage Subscription
60
+ </Button>
61
+ ) : (
62
+ <Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
63
+ Upgrade to Pro
64
+ </Button>
65
+ )}
66
+ {{/if}}
67
+ </div>
68
+ );
51
69
  }
@@ -1,3 +1,4 @@
1
+ import { Button } from "@/components/ui/button";
1
2
  import { authClient } from "@/lib/auth-client";
2
3
  {{#if (eq api "trpc")}}
3
4
  import { useTRPC } from "@/utils/trpc";
@@ -7,48 +8,62 @@ import { useQuery } from "@tanstack/react-query";
7
8
  import { orpc } from "@/utils/orpc";
8
9
  import { useQuery } from "@tanstack/react-query";
9
10
  {{/if}}
10
- import { createFileRoute } from "@tanstack/react-router";
11
- import { useEffect } from "react";
11
+ import { createFileRoute, redirect } from "@tanstack/react-router";
12
12
 
13
13
  export const Route = createFileRoute("/dashboard")({
14
- component: RouteComponent,
14
+ component: RouteComponent,
15
+ beforeLoad: async () => {
16
+ const session = await authClient.getSession();
17
+ if (!session.data) {
18
+ redirect({
19
+ to: "/login",
20
+ throw: true
21
+ });
22
+ }
23
+ {{#if (eq payments "polar")}}
24
+ const {data: customerState} = await authClient.customer.state()
25
+ return { session, customerState };
26
+ {{else}}
27
+ return { session };
28
+ {{/if}}
29
+ }
15
30
  });
16
31
 
17
32
  function RouteComponent() {
18
- const navigate = Route.useNavigate();
19
- {{#if (eq api "trpc")}}
20
- const trpc = useTRPC();
21
- {{/if}}
22
- {{#if (eq api "orpc")}}
23
- {{/if}}
24
- const { data: session, isPending } = authClient.useSession();
33
+ const { session{{#if (eq payments "polar")}}, customerState{{/if}} } = Route.useRouteContext();
25
34
 
26
- {{#if (eq api "trpc")}}
27
- const privateData = useQuery(trpc.privateData.queryOptions());
28
- {{/if}}
29
- {{#if (eq api "orpc")}}
30
- const privateData = useQuery(orpc.privateData.queryOptions());
31
- {{/if}}
35
+ {{#if (eq api "trpc")}}
36
+ const trpc = useTRPC();
37
+ const privateData = useQuery(trpc.privateData.queryOptions());
38
+ {{/if}}
39
+ {{#if (eq api "orpc")}}
40
+ const privateData = useQuery(orpc.privateData.queryOptions());
41
+ {{/if}}
32
42
 
33
- useEffect(() => {
34
- if (!session && !isPending) {
35
- navigate({
36
- to: "/login",
37
- });
38
- }
39
- }, [session, isPending]);
43
+ {{#if (eq payments "polar")}}
44
+ const hasProSubscription = customerState?.activeSubscriptions?.length! > 0
45
+ console.log("Active subscriptions:", customerState?.activeSubscriptions)
46
+ {{/if}}
40
47
 
41
- if (isPending) {
42
- return <div>Loading...</div>;
43
- }
44
-
45
- return (
46
- <div>
47
- <h1>Dashboard</h1>
48
- <p>Welcome {session?.user.name}</p>
49
- {{#if ( or (eq api "orpc") (eq api "trpc"))}}
50
- <p>privateData: {privateData.data?.message}</p>
51
- {{/if}}
52
- </div>
53
- );
48
+ return (
49
+ <div>
50
+ <h1>Dashboard</h1>
51
+ <p>Welcome {session.data?.user.name}</p>
52
+ {{#if ( or (eq api "orpc") (eq api "trpc"))}}
53
+ <p>API: {privateData.data?.message}</p>
54
+ {{/if}}
55
+ {{#if (eq payments "polar")}}
56
+ <p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
57
+ {hasProSubscription ? (
58
+ <Button onClick={async () => await authClient.customer.portal()}>
59
+ Manage Subscription
60
+ </Button>
61
+ ) : (
62
+ <Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
63
+ Upgrade to Pro
64
+ </Button>
65
+ )}
66
+ {{/if}}
67
+ </div>
68
+ );
54
69
  }
@@ -0,0 +1,11 @@
1
+ import { createAuthClient } from "better-auth/solid";
2
+ {{#if (eq payments "polar")}}
3
+ import { polarClient } from "@polar-sh/better-auth";
4
+ {{/if}}
5
+
6
+ export const authClient = createAuthClient({
7
+ baseURL: import.meta.env.VITE_SERVER_URL,
8
+ {{#if (eq payments "polar")}}
9
+ plugins: [polarClient()]
10
+ {{/if}}
11
+ });
@@ -3,42 +3,65 @@ import { authClient } from "@/lib/auth-client";
3
3
  import { orpc } from "@/utils/orpc";
4
4
  import { useQuery } from "@tanstack/solid-query";
5
5
  {{/if}}
6
- import { createFileRoute } from "@tanstack/solid-router";
7
- import { createEffect, Show } from "solid-js";
6
+ import { createFileRoute, redirect } from "@tanstack/solid-router";
8
7
 
9
8
  export const Route = createFileRoute("/dashboard")({
10
- component: RouteComponent,
9
+ component: RouteComponent,
10
+ beforeLoad: async () => {
11
+ const session = await authClient.getSession();
12
+ if (!session.data) {
13
+ redirect({
14
+ to: "/login",
15
+ throw: true,
16
+ });
17
+ }
18
+ {{#if (eq payments "polar")}}
19
+ const { data: customerState } = await authClient.customer.state();
20
+ return { session, customerState };
21
+ {{else}}
22
+ return { session };
23
+ {{/if}}
24
+ },
11
25
  });
12
26
 
13
27
  function RouteComponent() {
14
- const session = authClient.useSession();
15
- const navigate = Route.useNavigate();
28
+ const context = Route.useRouteContext();
16
29
 
17
- {{#if (eq api "orpc")}}
18
- const privateData = useQuery(() => orpc.privateData.queryOptions());
19
- {{/if}}
30
+ const session = context().session;
31
+ {{#if (eq payments "polar")}}
32
+ const customerState = context().customerState;
33
+ {{/if}}
20
34
 
21
- createEffect(() => {
22
- if (!session().data && !session().isPending) {
23
- navigate({
24
- to: "/login",
25
- });
26
- }
27
- });
35
+ {{#if (eq api "orpc")}}
36
+ const privateData = useQuery(() => orpc.privateData.queryOptions());
37
+ {{/if}}
28
38
 
29
- return (
30
- <div>
31
- <Show when={session().isPending}>
32
- <div>Loading...</div>
33
- </Show>
39
+ {{#if (eq payments "polar")}}
40
+ const hasProSubscription = () =>
41
+ customerState?.activeSubscriptions?.length! > 0;
42
+ {{/if}}
34
43
 
35
- <Show when={!session().isPending && session().data}>
36
- <h1>Dashboard</h1>
37
- <p>Welcome {session().data?.user.name}</p>
38
- {{#if (eq api "orpc")}}
39
- <p>privateData: {privateData.data?.message}</p>
40
- {{/if}}
41
- </Show>
42
- </div>
43
- );
44
+ return (
45
+ <div>
46
+ <h1>Dashboard</h1>
47
+ <p>Welcome {session.data?.user.name}</p>
48
+ {{#if (eq api "orpc")}}
49
+ <p>API: {privateData.data?.message}</p>
50
+ {{/if}}
51
+ {{#if (eq payments "polar")}}
52
+ <p>Plan: {hasProSubscription() ? "Pro" : "Free"}</p>
53
+ {hasProSubscription() ? (
54
+ <button onClick={async () => await authClient.customer.portal()}>
55
+ Manage Subscription
56
+ </button>
57
+ ) : (
58
+ <button
59
+ onClick={async () => await authClient.checkout({ slug: "pro" })}
60
+ >
61
+ Upgrade to Pro
62
+ </button>
63
+ )}
64
+ {{/if}}
65
+ </div>
66
+ );
44
67
  }
@@ -1,6 +1,12 @@
1
1
  import { PUBLIC_SERVER_URL } from "$env/static/public";
2
2
  import { createAuthClient } from "better-auth/svelte";
3
+ {{#if (eq payments "polar")}}
4
+ import { polarClient } from "@polar-sh/better-auth";
5
+ {{/if}}
3
6
 
4
7
  export const authClient = createAuthClient({
5
8
  baseURL: PUBLIC_SERVER_URL,
9
+ {{#if (eq payments "polar")}}
10
+ plugins: [polarClient()]
11
+ {{/if}}
6
12
  });
@@ -6,7 +6,9 @@
6
6
  import { orpc } from '$lib/orpc';
7
7
  import { createQuery } from '@tanstack/svelte-query';
8
8
  {{/if}}
9
- import { get } from 'svelte/store';
9
+ {{#if (eq payments "polar")}}
10
+ let customerState: any = null;
11
+ {{/if}}
10
12
 
11
13
  const sessionQuery = authClient.useSession();
12
14
 
@@ -15,10 +17,17 @@
15
17
  {{/if}}
16
18
 
17
19
  onMount(() => {
18
- const { data: session, isPending } = get(sessionQuery);
20
+ const { data: session, isPending } = $sessionQuery;
19
21
  if (!session && !isPending) {
20
22
  goto('/login');
21
23
  }
24
+ {{#if (eq payments "polar")}}
25
+ if (session) {
26
+ authClient.customer.state().then(({ data }) => {
27
+ customerState = data;
28
+ });
29
+ }
30
+ {{/if}}
22
31
  });
23
32
  </script>
24
33
 
@@ -30,7 +39,19 @@
30
39
  <h1>Dashboard</h1>
31
40
  <p>Welcome {$sessionQuery.data.user.name}</p>
32
41
  {{#if (eq api "orpc")}}
33
- <p>privateData: {$privateDataQuery.data?.message}</p>
42
+ <p>API: {$privateDataQuery.data?.message}</p>
43
+ {{/if}}
44
+ {{#if (eq payments "polar")}}
45
+ <p>Plan: {customerState?.activeSubscriptions?.length > 0 ? "Pro" : "Free"}</p>
46
+ {#if customerState?.activeSubscriptions?.length > 0}
47
+ <button onclick={async () => await authClient.customer.portal()}>
48
+ Manage Subscription
49
+ </button>
50
+ {:else}
51
+ <button onclick={async () => await authClient.checkout({ slug: "pro" })}>
52
+ Upgrade to Pro
53
+ </button>
54
+ {/if}
34
55
  {{/if}}
35
56
  </div>
36
57
  {/if}
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  {{/if}}
19
19
  "devDependencies": {
20
- "tsdown": "^0.14.1",
20
+ "tsdown": "^0.15.1",
21
21
  "typescript": "^5.8.2"
22
22
  }
23
23
  }
@@ -1,2 +1,6 @@
1
1
  [install]
2
+ {{#if (includes frontend "nuxt")}}
3
+ # linker = "isolated" # Commented out for Nuxt compatibility
4
+ {{else}}
2
5
  linker = "isolated"
6
+ {{/if}}
@@ -5,6 +5,10 @@
5
5
  import { useAuth } from "@clerk/nextjs";
6
6
  import { ConvexReactClient } from "convex/react";
7
7
  import { ConvexProviderWithClerk } from "convex/react-clerk";
8
+ {{else if (eq auth "better-auth")}}
9
+ import { ConvexProvider, ConvexReactClient } from "convex/react";
10
+ import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
11
+ import { authClient } from "@/lib/auth-client";
8
12
  {{else}}
9
13
  import { ConvexProvider, ConvexReactClient } from "convex/react";
10
14
  {{/if}}
@@ -44,6 +48,10 @@ export default function Providers({
44
48
  <ConvexProviderWithClerk client={convex} useAuth={useAuth}>
45
49
  {children}
46
50
  </ConvexProviderWithClerk>
51
+ {{else if (eq auth "better-auth")}}
52
+ <ConvexBetterAuthProvider client={convex} authClient={authClient}>
53
+ {children}
54
+ </ConvexBetterAuthProvider>
47
55
  {{else}}
48
56
  <ConvexProvider client={convex}>{children}</ConvexProvider>
49
57
  {{/if}}
@@ -16,10 +16,15 @@ import { routeTree } from "./routeTree.gen";
16
16
  {{#if (eq auth "clerk")}}
17
17
  import { ClerkProvider, useAuth } from "@clerk/clerk-react";
18
18
  import { ConvexProviderWithClerk } from "convex/react-clerk";
19
+ {{else if (eq auth "better-auth")}}
20
+ import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
21
+ import { authClient } from "@/lib/auth-client";
19
22
  {{else}}
20
23
  import { ConvexProvider } from "convex/react";
21
24
  {{/if}}
22
- const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
25
+ const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string{{#if (eq auth "better-auth")}}, {
26
+ expectAuth: true,
27
+ }{{/if}});
23
28
  {{/if}}
24
29
 
25
30
  const router = createRouter({
@@ -57,6 +62,8 @@ const router = createRouter({
57
62
  </ConvexProviderWithClerk>
58
63
  </ClerkProvider>
59
64
  );
65
+ {{else if (eq auth "better-auth")}}
66
+ return <ConvexBetterAuthProvider client={convex} authClient={authClient}>{children}</ConvexBetterAuthProvider>;
60
67
  {{else}}
61
68
  return <ConvexProvider client={convex}>{children}</ConvexProvider>;
62
69
  {{/if}}