create-better-t-stack 2.45.5 → 2.46.0

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 (49) 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-NOw0j6Z9.js} +272 -132
  5. package/package.json +1 -1
  6. package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +153 -6
  7. package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +36 -2
  8. package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts.hbs +22 -0
  9. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +6 -0
  10. package/templates/auth/better-auth/web/react/next/src/app/dashboard/dashboard.tsx.hbs +58 -0
  11. package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +31 -41
  12. package/templates/auth/better-auth/web/react/react-router/src/routes/dashboard.tsx.hbs +37 -3
  13. package/templates/auth/better-auth/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +50 -32
  14. package/templates/auth/better-auth/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +51 -36
  15. package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts.hbs +11 -0
  16. package/templates/auth/better-auth/web/solid/src/routes/dashboard.tsx.hbs +52 -29
  17. package/templates/auth/better-auth/web/svelte/src/lib/{auth-client.ts → auth-client.ts.hbs} +6 -0
  18. package/templates/auth/better-auth/web/svelte/src/routes/dashboard/+page.svelte.hbs +24 -3
  19. package/templates/backend/server/server-base/package.json.hbs +1 -1
  20. package/templates/extras/bunfig.toml.hbs +4 -0
  21. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +4 -0
  22. package/templates/frontend/solid/package.json.hbs +10 -10
  23. package/templates/payments/polar/server/base/src/lib/payments.ts.hbs +6 -0
  24. package/templates/payments/polar/web/nuxt/app/pages/success.vue.hbs +11 -0
  25. package/templates/payments/polar/web/react/next/src/app/success/page.tsx.hbs +15 -0
  26. package/templates/payments/polar/web/react/react-router/src/routes/success.tsx.hbs +13 -0
  27. package/templates/payments/polar/web/react/tanstack-router/src/routes/success.tsx.hbs +19 -0
  28. package/templates/payments/polar/web/react/tanstack-start/src/routes/success.tsx.hbs +19 -0
  29. package/templates/payments/polar/web/solid/src/routes/success.tsx.hbs +23 -0
  30. package/templates/payments/polar/web/svelte/src/routes/success/+page.svelte.hbs +12 -0
  31. package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts +0 -16
  32. package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts +0 -5
  33. /package/templates/auth/better-auth/web/nuxt/app/components/{SignInForm.vue → SignInForm.vue.hbs} +0 -0
  34. /package/templates/auth/better-auth/web/nuxt/app/components/{SignUpForm.vue → SignUpForm.vue.hbs} +0 -0
  35. /package/templates/auth/better-auth/web/nuxt/app/components/{UserMenu.vue → UserMenu.vue.hbs} +0 -0
  36. /package/templates/auth/better-auth/web/nuxt/app/pages/{login.vue → login.vue.hbs} +0 -0
  37. /package/templates/auth/better-auth/web/react/next/src/app/login/{page.tsx → page.tsx.hbs} +0 -0
  38. /package/templates/auth/better-auth/web/react/next/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  39. /package/templates/auth/better-auth/web/react/next/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  40. /package/templates/auth/better-auth/web/react/next/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  41. /package/templates/auth/better-auth/web/react/next/src/components/{user-menu.tsx → user-menu.tsx.hbs} +0 -0
  42. /package/templates/auth/better-auth/web/react/react-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  43. /package/templates/auth/better-auth/web/react/react-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  44. /package/templates/auth/better-auth/web/react/react-router/src/components/{user-menu.tsx → user-menu.tsx.hbs} +0 -0
  45. /package/templates/auth/better-auth/web/react/react-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  46. /package/templates/auth/better-auth/web/svelte/src/components/{SignInForm.svelte → SignInForm.svelte.hbs} +0 -0
  47. /package/templates/auth/better-auth/web/svelte/src/components/{SignUpForm.svelte → SignUpForm.svelte.hbs} +0 -0
  48. /package/templates/auth/better-auth/web/svelte/src/components/{UserMenu.svelte → UserMenu.svelte.hbs} +0 -0
  49. /package/templates/auth/better-auth/web/svelte/src/routes/login/{+page.svelte → +page.svelte.hbs} +0 -0
@@ -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}}
@@ -17,6 +17,10 @@ import appCss from "../index.css?url";
17
17
  import type { QueryClient } from "@tanstack/react-query";
18
18
  import type { ConvexQueryClient } from "@convex-dev/react-query";
19
19
  import type { ConvexReactClient } from "convex/react";
20
+ {{else}}
21
+ {{#if (or (eq api "trpc") (eq api "orpc"))}}
22
+ import type { QueryClient } from "@tanstack/react-query";
23
+ {{/if}}
20
24
  {{/if}}
21
25
  import Loader from "@/components/loader";
22
26
 
@@ -9,18 +9,18 @@
9
9
  "test": "vitest run"
10
10
  },
11
11
  "dependencies": {
12
- "@tailwindcss/vite": "^4.0.6",
13
- "@tanstack/router-plugin": "^1.109.2",
14
- "@tanstack/solid-form": "^1.9.0",
15
- "@tanstack/solid-router": "^1.110.0",
16
- "lucide-solid": "^0.507.0",
17
- "solid-js": "^1.9.4",
18
- "tailwindcss": "^4.0.6",
12
+ "@tailwindcss/vite": "^4.1.13",
13
+ "@tanstack/router-plugin": "^1.131.44",
14
+ "@tanstack/solid-form": "^1.20.0",
15
+ "@tanstack/solid-router": "^1.131.44",
16
+ "lucide-solid": "^0.544.0",
17
+ "solid-js": "^1.9.9",
18
+ "tailwindcss": "^4.1.13",
19
19
  "zod": "^4.0.2"
20
20
  },
21
21
  "devDependencies": {
22
- "typescript": "^5.7.2",
23
- "vite": "^7.0.2",
24
- "vite-plugin-solid": "^2.11.2"
22
+ "typescript": "^5.9.2",
23
+ "vite": "^7.1.5",
24
+ "vite-plugin-solid": "^2.11.8"
25
25
  }
26
26
  }
@@ -0,0 +1,6 @@
1
+ import { Polar } from "@polar-sh/sdk";
2
+
3
+ export const polarClient = new Polar({
4
+ accessToken: process.env.POLAR_ACCESS_TOKEN,
5
+ server: "sandbox",
6
+ });
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts">
2
+ const route = useRoute()
3
+ const checkout_id = route.query.checkout_id as string
4
+ </script>
5
+
6
+ <template>
7
+ <div class="container mx-auto px-4 py-8">
8
+ <h1 class="text-2xl font-bold mb-4">Payment Successful!</h1>
9
+ <p v-if="checkout_id">Checkout ID: \{{ checkout_id }}</p>
10
+ </div>
11
+ </template>
@@ -0,0 +1,15 @@
1
+ export default async function SuccessPage({
2
+ searchParams,
3
+ }: {
4
+ searchParams: Promise<{ checkout_id: string }>
5
+ }) {
6
+ const params = await searchParams;
7
+ const checkout_id = params.checkout_id;
8
+
9
+ return (
10
+ <div className="px-4 py-8">
11
+ <h1>Payment Successful!</h1>
12
+ {checkout_id && <p>Checkout ID: {checkout_id}</p>}
13
+ </div>
14
+ );
15
+ }
@@ -0,0 +1,13 @@
1
+ import { useSearchParams } from "react-router";
2
+
3
+ export default function SuccessPage() {
4
+ const [searchParams] = useSearchParams();
5
+ const checkout_id = searchParams.get("checkout_id");
6
+
7
+ return (
8
+ <div className="container mx-auto px-4 py-8">
9
+ <h1>Payment Successful!</h1>
10
+ {checkout_id && <p>Checkout ID: {checkout_id}</p>}
11
+ </div>
12
+ );
13
+ }
@@ -0,0 +1,19 @@
1
+ import { createFileRoute, useSearch } from "@tanstack/react-router";
2
+
3
+ export const Route = createFileRoute("/success")({
4
+ component: SuccessPage,
5
+ validateSearch: (search) => ({
6
+ checkout_id: search.checkout_id as string,
7
+ }),
8
+ });
9
+
10
+ function SuccessPage() {
11
+ const { checkout_id } = useSearch({ from: "/success" });
12
+
13
+ return (
14
+ <div className="container mx-auto px-4 py-8">
15
+ <h1>Payment Successful!</h1>
16
+ {checkout_id && <p>Checkout ID: {checkout_id}</p>}
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,19 @@
1
+ import { createFileRoute, useSearch } from "@tanstack/react-router";
2
+
3
+ export const Route = createFileRoute("/success")({
4
+ component: SuccessPage,
5
+ validateSearch: (search) => ({
6
+ checkout_id: search.checkout_id as string,
7
+ }),
8
+ });
9
+
10
+ function SuccessPage() {
11
+ const { checkout_id } = useSearch({ from: "/success" });
12
+
13
+ return (
14
+ <div className="container mx-auto px-4 py-8">
15
+ <h1>Payment Successful!</h1>
16
+ {checkout_id && <p>Checkout ID: {checkout_id}</p>}
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,23 @@
1
+ import { createFileRoute } from "@tanstack/solid-router";
2
+ import { Show } from "solid-js";
3
+
4
+ export const Route = createFileRoute("/success")({
5
+ component: SuccessPage,
6
+ validateSearch: (search) => ({
7
+ checkout_id: search.checkout_id as string,
8
+ }),
9
+ });
10
+
11
+ function SuccessPage() {
12
+ const searchParams = Route.useSearch();
13
+ const checkout_id = searchParams().checkout_id;
14
+
15
+ return (
16
+ <div class="container mx-auto px-4 py-8">
17
+ <h1>Payment Successful!</h1>
18
+ <Show when={checkout_id}>
19
+ <p>Checkout ID: {checkout_id}</p>
20
+ </Show>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import { page } from '$app/stores';
3
+
4
+ const checkout_id = $page.url.searchParams.get('checkout_id');
5
+ </script>
6
+
7
+ <div class="container mx-auto px-4 py-8">
8
+ <h1>Payment Successful!</h1>
9
+ {#if checkout_id}
10
+ <p>Checkout ID: {checkout_id}</p>
11
+ {/if}
12
+ </div>
@@ -1,16 +0,0 @@
1
- import { createAuthClient } from "better-auth/vue";
2
-
3
- export default defineNuxtPlugin(nuxtApp => {
4
- const config = useRuntimeConfig()
5
- const serverUrl = config.public.serverURL
6
-
7
- const authClient = createAuthClient({
8
- baseURL: serverUrl
9
- })
10
-
11
- return {
12
- provide: {
13
- authClient: authClient
14
- }
15
- }
16
- })
@@ -1,5 +0,0 @@
1
- import { createAuthClient } from "better-auth/solid";
2
-
3
- export const authClient = createAuthClient({
4
- baseURL: import.meta.env.VITE_SERVER_URL,
5
- });