@farm.js/create-app 0.1.0-beta.7 → 0.1.0-beta.71

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 (173) hide show
  1. package/README.md +38 -6
  2. package/bin/create-farm-app.js +7 -2
  3. package/dist/index.js +3298 -16
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +3294 -14
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/utils.js +9 -6
  8. package/dist/utils.js.map +1 -1
  9. package/dist/utils.mjs +9 -6
  10. package/dist/utils.mjs.map +1 -1
  11. package/package.json +2 -2
  12. package/templates/_integrations/better-auth/preact/farm.config.ts +17 -0
  13. package/templates/_integrations/better-auth/preact/src/app/dashboard/page.tsx +73 -0
  14. package/templates/_integrations/better-auth/preact/src/app/layout.tsx +14 -0
  15. package/templates/_integrations/better-auth/preact/src/app/page.tsx +36 -0
  16. package/templates/_integrations/better-auth/preact/src/app/sign-in/page.tsx +10 -0
  17. package/templates/_integrations/better-auth/preact/src/app/sign-up/page.tsx +10 -0
  18. package/templates/_integrations/better-auth/preact/src/components/auth-form.tsx +92 -0
  19. package/templates/_integrations/better-auth/preact/src/components/auth-shell.tsx +16 -0
  20. package/templates/_integrations/better-auth/preact/src/lib/auth-client.ts +5 -0
  21. package/templates/_integrations/better-auth/solid/farm.config.ts +17 -0
  22. package/templates/_integrations/better-auth/solid/src/app/dashboard/page.tsx +67 -0
  23. package/templates/_integrations/better-auth/solid/src/app/layout.tsx +14 -0
  24. package/templates/_integrations/better-auth/solid/src/app/page.tsx +36 -0
  25. package/templates/_integrations/better-auth/solid/src/app/sign-in/page.tsx +10 -0
  26. package/templates/_integrations/better-auth/solid/src/app/sign-up/page.tsx +10 -0
  27. package/templates/_integrations/better-auth/solid/src/components/auth-form.tsx +91 -0
  28. package/templates/_integrations/better-auth/solid/src/components/auth-shell.tsx +16 -0
  29. package/templates/_integrations/better-auth/solid/src/lib/auth-client.ts +5 -0
  30. package/templates/_integrations/better-auth/svelte/farm.config.ts +17 -0
  31. package/templates/_integrations/better-auth/svelte/src/app/dashboard/page.svelte +62 -0
  32. package/templates/_integrations/better-auth/svelte/src/app/layout.svelte +20 -0
  33. package/templates/_integrations/better-auth/svelte/src/app/page.svelte +32 -0
  34. package/templates/_integrations/better-auth/svelte/src/app/sign-in/page.svelte +10 -0
  35. package/templates/_integrations/better-auth/svelte/src/app/sign-up/page.svelte +10 -0
  36. package/templates/_integrations/better-auth/svelte/src/components/auth-form.svelte +83 -0
  37. package/templates/_integrations/better-auth/svelte/src/components/auth-shell.svelte +16 -0
  38. package/templates/_integrations/better-auth/svelte/src/components/resource-links.svelte +47 -0
  39. package/templates/_integrations/better-auth/svelte/src/lib/auth-client.ts +5 -0
  40. package/templates/_integrations/better-auth/vue/farm.config.ts +17 -0
  41. package/templates/_integrations/better-auth/vue/src/app/dashboard/page.vue +62 -0
  42. package/templates/_integrations/better-auth/vue/src/app/layout.vue +20 -0
  43. package/templates/_integrations/better-auth/vue/src/app/page.vue +34 -0
  44. package/templates/_integrations/better-auth/vue/src/app/sign-in/page.vue +12 -0
  45. package/templates/_integrations/better-auth/vue/src/app/sign-up/page.vue +12 -0
  46. package/templates/_integrations/better-auth/vue/src/components/auth-form.vue +86 -0
  47. package/templates/_integrations/better-auth/vue/src/components/auth-shell.vue +15 -0
  48. package/templates/_integrations/better-auth/vue/src/components/resource-links.vue +43 -0
  49. package/templates/_integrations/better-auth/vue/src/lib/auth-client.ts +5 -0
  50. package/templates/_renderers/preact/farm.config.ts +12 -0
  51. package/templates/_renderers/preact/package.json +26 -0
  52. package/templates/_renderers/preact/src/app/api/greeting/route.ts +15 -0
  53. package/templates/_renderers/preact/src/app/layout.tsx +16 -0
  54. package/templates/_renderers/preact/src/app/page.tsx +66 -0
  55. package/templates/_renderers/preact/src/app/preact.css +26 -0
  56. package/templates/_renderers/preact/src/components/resource-links.tsx +71 -0
  57. package/templates/_renderers/preact/src/lib/api-client.ts +4 -0
  58. package/templates/_renderers/preact/src/lib/api.generated.ts +7 -0
  59. package/templates/_renderers/preact/tsconfig.json +25 -0
  60. package/templates/_renderers/solid/farm.config.ts +12 -0
  61. package/templates/_renderers/solid/package.json +25 -0
  62. package/templates/_renderers/solid/src/app/api/greeting/route.ts +15 -0
  63. package/templates/_renderers/solid/src/app/layout.tsx +15 -0
  64. package/templates/_renderers/solid/src/app/page.tsx +56 -0
  65. package/templates/_renderers/solid/src/app/solid.css +22 -0
  66. package/templates/_renderers/solid/src/components/resource-links.tsx +71 -0
  67. package/templates/_renderers/solid/src/lib/api-client.ts +4 -0
  68. package/templates/_renderers/solid/src/lib/api.generated.ts +7 -0
  69. package/templates/_renderers/solid/tsconfig.json +25 -0
  70. package/templates/_renderers/svelte/farm.config.ts +9 -0
  71. package/templates/_renderers/svelte/package.json +28 -0
  72. package/templates/_renderers/svelte/src/app/api/greeting/route.ts +15 -0
  73. package/templates/_renderers/svelte/src/app/layout.svelte +21 -0
  74. package/templates/_renderers/svelte/src/app/page.svelte +64 -0
  75. package/templates/_renderers/svelte/src/app/svelte.css +26 -0
  76. package/templates/_renderers/svelte/src/components/resource-links.svelte +31 -0
  77. package/templates/_renderers/svelte/src/lib/api-client.ts +4 -0
  78. package/templates/_renderers/svelte/src/lib/api.generated.ts +7 -0
  79. package/templates/_renderers/svelte/tsconfig.json +23 -0
  80. package/templates/_renderers/vue/farm.config.ts +12 -0
  81. package/templates/_renderers/vue/package.json +26 -0
  82. package/templates/_renderers/vue/src/app/api/greeting/route.ts +15 -0
  83. package/templates/_renderers/vue/src/app/layout.vue +21 -0
  84. package/templates/_renderers/vue/src/app/page.vue +69 -0
  85. package/templates/_renderers/vue/src/app/vue.css +26 -0
  86. package/templates/_renderers/vue/src/components/resource-links.vue +33 -0
  87. package/templates/_renderers/vue/src/env.d.ts +7 -0
  88. package/templates/_renderers/vue/src/lib/api-client.ts +4 -0
  89. package/templates/_renderers/vue/src/lib/api.generated.ts +7 -0
  90. package/templates/_renderers/vue/tsconfig.json +23 -0
  91. package/templates/auth/.env.example +6 -0
  92. package/templates/auth/README.md +95 -0
  93. package/templates/auth/docs.config.ts +33 -0
  94. package/templates/auth/docs.json +36 -0
  95. package/templates/auth/farm.config.ts +20 -0
  96. package/templates/auth/gitignore +10 -0
  97. package/templates/auth/package.json +34 -0
  98. package/templates/auth/pnpm-workspace.yaml +13 -0
  99. package/templates/auth/src/app/dashboard/middleware.ts +13 -0
  100. package/templates/auth/src/app/dashboard/page.tsx +140 -0
  101. package/templates/auth/src/app/docs/page.md +29 -0
  102. package/templates/auth/src/app/error.tsx +25 -0
  103. package/templates/auth/src/app/globals.css +1064 -0
  104. package/templates/auth/src/app/layout.tsx +11 -0
  105. package/templates/auth/src/app/loading.tsx +15 -0
  106. package/templates/auth/src/app/not-found.tsx +26 -0
  107. package/templates/auth/src/app/page.tsx +46 -0
  108. package/templates/auth/src/app/sign-in/page.tsx +16 -0
  109. package/templates/auth/src/app/sign-up/page.tsx +16 -0
  110. package/templates/auth/src/components/auth-form.tsx +120 -0
  111. package/templates/auth/src/components/auth-shell.tsx +21 -0
  112. package/templates/auth/src/components/resource-links.tsx +78 -0
  113. package/templates/auth/src/components/sign-out-button.tsx +45 -0
  114. package/templates/auth/src/components/site-header.tsx +46 -0
  115. package/templates/auth/tsconfig.json +20 -0
  116. package/templates/basic/docs.config.ts +33 -0
  117. package/templates/basic/docs.json +36 -0
  118. package/templates/basic/farm.config.ts +9 -0
  119. package/templates/basic/package.json +7 -3
  120. package/templates/basic/pnpm-workspace.yaml +8 -0
  121. package/templates/basic/src/app/docs/page.md +29 -0
  122. package/templates/basic/src/app/globals.css +241 -0
  123. package/templates/basic/src/app/layout.tsx +3 -5
  124. package/templates/basic/src/app/page.tsx +20 -61
  125. package/templates/basic/src/components/resource-links.tsx +78 -0
  126. package/templates/basic/tsconfig.json +4 -0
  127. package/templates/better-auth/.env.example +3 -0
  128. package/templates/better-auth/README.md +89 -0
  129. package/templates/better-auth/docs.config.ts +33 -0
  130. package/templates/better-auth/docs.json +36 -0
  131. package/templates/better-auth/farm.config.ts +38 -0
  132. package/templates/better-auth/gitignore +10 -0
  133. package/templates/better-auth/package.json +36 -0
  134. package/templates/better-auth/pnpm-workspace.yaml +13 -0
  135. package/templates/better-auth/src/app/dashboard/middleware.ts +15 -0
  136. package/templates/better-auth/src/app/dashboard/page.tsx +188 -0
  137. package/templates/better-auth/src/app/docs/page.md +29 -0
  138. package/templates/better-auth/src/app/error.tsx +25 -0
  139. package/templates/better-auth/src/app/globals.css +1064 -0
  140. package/templates/better-auth/src/app/layout.tsx +11 -0
  141. package/templates/better-auth/src/app/loading.tsx +15 -0
  142. package/templates/better-auth/src/app/not-found.tsx +26 -0
  143. package/templates/better-auth/src/app/page.tsx +50 -0
  144. package/templates/better-auth/src/app/sign-in/page.tsx +16 -0
  145. package/templates/better-auth/src/app/sign-up/page.tsx +16 -0
  146. package/templates/better-auth/src/components/auth-form.tsx +120 -0
  147. package/templates/better-auth/src/components/auth-shell.tsx +21 -0
  148. package/templates/better-auth/src/components/resource-links.tsx +78 -0
  149. package/templates/better-auth/src/components/sign-out-button.tsx +45 -0
  150. package/templates/better-auth/src/components/site-header.tsx +46 -0
  151. package/templates/better-auth/src/lib/auth-client.ts +5 -0
  152. package/templates/better-auth/src/lib/auth.ts +50 -0
  153. package/templates/better-auth/src/lib/session.ts +8 -0
  154. package/templates/better-auth/tsconfig.json +20 -0
  155. package/templates/react-compiler/README.md +62 -0
  156. package/templates/react-compiler/docs.config.ts +33 -0
  157. package/templates/react-compiler/docs.json +36 -0
  158. package/templates/react-compiler/farm.config.ts +29 -0
  159. package/templates/react-compiler/gitignore +23 -0
  160. package/templates/react-compiler/package.json +34 -0
  161. package/templates/react-compiler/pnpm-workspace.yaml +8 -0
  162. package/templates/react-compiler/public/favicon.svg +5 -0
  163. package/templates/react-compiler/scripts/verify-experiment.mjs +113 -0
  164. package/templates/react-compiler/src/app/docs/page.md +29 -0
  165. package/templates/react-compiler/src/app/globals.css +470 -0
  166. package/templates/react-compiler/src/app/layout.tsx +22 -0
  167. package/templates/react-compiler/src/app/page.tsx +47 -0
  168. package/templates/react-compiler/src/components/compiler-comparison.tsx +93 -0
  169. package/templates/react-compiler/src/components/resource-links.tsx +78 -0
  170. package/templates/react-compiler/src/farm.d.ts +74 -0
  171. package/templates/react-compiler/src/lib/api.generated.ts +10 -0
  172. package/templates/react-compiler/tsconfig.json +24 -0
  173. package/templates/basic/src/app/about/page.tsx +0 -32
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import pc from \"picocolors\";\n\nexport const logger = {\n info: (message: string) => console.log(`${pc.blue(\"ℹ\")} ${message}`),\n success: (message: string) => console.log(`${pc.green(\"✓\")} ${message}`),\n warn: (message: string) => console.warn(`${pc.yellow(\"⚠\")} ${message}`),\n error: (message: string) => console.error(`${pc.red(\"✗\")} ${message}`),\n};\n\nexport function showBanner() {\n const art = [\n \" _______ \",\n \"| ___ |__ _ _ __ _ __ ___ \",\n \"| |_ /| / _` | '__| '_ ` _ \\\\ \",\n \"| _ \\\\| | (_| | | | | | | | | \",\n \"|_| \\\\_\\\\_|\\\\__,_|_| |_| |_| |_|\",\n ];\n\n console.log(\"\");\n for (const line of art) {\n console.log(pc.cyan(line));\n }\n console.log(pc.bold(pc.green(\"Create Farm.js App\")) + pc.dim(\" modern React meta-framework\"));\n console.log(\"\");\n}\n"],"mappings":";;AAEA,MAAa,SAAS;CACpB,OAAO,YAAoB,QAAQ,IAAI,GAAG,GAAG,KAAK,GAAG,EAAE,GAAG,SAAS;CACnE,UAAU,YAAoB,QAAQ,IAAI,GAAG,GAAG,MAAM,GAAG,EAAE,GAAG,SAAS;CACvE,OAAO,YAAoB,QAAQ,KAAK,GAAG,GAAG,OAAO,GAAG,EAAE,GAAG,SAAS;CACtE,QAAQ,YAAoB,QAAQ,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,GAAG,SAAS;AACvE;AAEA,SAAgB,aAAa;CAC3B,MAAM,MAAM;EACV;EACA;EACA;EACA;EACA;CACF;CAEA,QAAQ,IAAI,EAAE;CACd,KAAK,MAAM,QAAQ,KACjB,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC;CAE3B,QAAQ,IAAI,GAAG,KAAK,GAAG,MAAM,oBAAoB,CAAC,IAAI,GAAG,IAAI,+BAA+B,CAAC;CAC7F,QAAQ,IAAI,EAAE;AAChB"}
1
+ {"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import pc from \"picocolors\";\n\nexport const logger = {\n info: (message: string) => console.log(`${pc.blue(\"ℹ\")} ${message}`),\n success: (message: string) => console.log(`${pc.green(\"✓\")} ${message}`),\n warn: (message: string) => console.warn(`${pc.yellow(\"⚠\")} ${message}`),\n error: (message: string) => console.error(`${pc.red(\"✗\")} ${message}`),\n};\n\nexport function showBanner() {\n const art = [\n \"░██████████ ░███ ░█████████ ░███ ░███ ░█████ ░██████\",\n \"░██ ░██░██ ░██ ░██ ░████ ░████ ░██ ░██ ░██\",\n \"░██ ░██ ░██ ░██ ░██ ░██░██ ░██░██ ░██ ░██\",\n \"░█████████ ░█████████ ░█████████ ░██ ░████ ░██ ░██ ░████████\",\n \"░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██\",\n \"░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██\",\n \"░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██████ ░██████\",\n ];\n\n console.log(\"\");\n for (const line of art) {\n console.log(pc.cyan(line));\n }\n console.log(\"\");\n console.log(\n pc.bold(pc.green(\"Create FARMJS App\")) + pc.dim(\" a framework for product-integrated apps\"),\n );\n console.log(\"\");\n}\n"],"mappings":";;AAEA,MAAa,SAAS;CACpB,OAAO,YAAoB,QAAQ,IAAI,GAAG,GAAG,KAAK,GAAG,EAAE,GAAG,SAAS;CACnE,UAAU,YAAoB,QAAQ,IAAI,GAAG,GAAG,MAAM,GAAG,EAAE,GAAG,SAAS;CACvE,OAAO,YAAoB,QAAQ,KAAK,GAAG,GAAG,OAAO,GAAG,EAAE,GAAG,SAAS;CACtE,QAAQ,YAAoB,QAAQ,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,GAAG,SAAS;AACvE;AAEA,SAAgB,aAAa;CAC3B,MAAM,MAAM;EACV;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CAEA,QAAQ,IAAI,EAAE;CACd,KAAK,MAAM,QAAQ,KACjB,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC;CAE3B,QAAQ,IAAI,EAAE;CACd,QAAQ,IACN,GAAG,KAAK,GAAG,MAAM,mBAAmB,CAAC,IAAI,GAAG,IAAI,2CAA2C,CAC7F;CACA,QAAQ,IAAI,EAAE;AAChB"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@farm.js/create-app",
3
- "version": "0.1.0-beta.7",
4
- "description": "Create a new Farm.js application",
3
+ "version": "0.1.0-beta.71",
4
+ "description": "Create a new FARMJS application",
5
5
  "keywords": [
6
6
  "@farm.js/create-app",
7
7
  "create-app",
@@ -0,0 +1,17 @@
1
+ import { betterAuth } from "@farm.js/better-auth";
2
+ import { defineConfig } from "@farm.js/core/config";
3
+ import { preact } from "@farm.js/preact";
4
+ import { auth } from "./src/lib/auth.ts";
5
+
6
+ export default defineConfig({
7
+ renderer: preact(),
8
+ theme: {
9
+ default: "dark",
10
+ },
11
+ integrations: {
12
+ auth: betterAuth({ instance: auth }),
13
+ },
14
+ deploy: {
15
+ target: "vercel",
16
+ },
17
+ });
@@ -0,0 +1,73 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "preact/hooks";
4
+ import { authClient } from "../../lib/auth-client";
5
+
6
+ type SessionData = NonNullable<Awaited<ReturnType<typeof authClient.getSession>>["data"]>;
7
+
8
+ export default function DashboardPage() {
9
+ const [session, setSession] = useState<SessionData | null>(null);
10
+ const [pending, setPending] = useState(true);
11
+ const [error, setError] = useState<string | null>(null);
12
+
13
+ useEffect(() => {
14
+ let active = true;
15
+ authClient
16
+ .getSession()
17
+ .then((response) => {
18
+ if (!active) return;
19
+ if (response.error || !response.data?.user) {
20
+ window.location.replace("/sign-in");
21
+ return;
22
+ }
23
+ setSession(response.data);
24
+ setPending(false);
25
+ })
26
+ .catch(() => window.location.replace("/sign-in"));
27
+ return () => {
28
+ active = false;
29
+ };
30
+ }, []);
31
+
32
+ async function signOut() {
33
+ setPending(true);
34
+ setError(null);
35
+ const response = await authClient.signOut();
36
+ if (response.error) {
37
+ setError(response.error.message ?? "Could not sign out.");
38
+ setPending(false);
39
+ return;
40
+ }
41
+ window.location.assign("/");
42
+ }
43
+
44
+ if (!session) {
45
+ return (
46
+ <main className="loading-shell" aria-live="polite">
47
+ <div className="loading-block">
48
+ <span className="loading-label">Verifying protected session</span>
49
+ <div className="loading-line wide" />
50
+ <div className="loading-line" />
51
+ </div>
52
+ </main>
53
+ );
54
+ }
55
+
56
+ return (
57
+ <main className="dashboard-shell">
58
+ <div className="dashboard-heading">
59
+ <div>
60
+ <span className="section-index">PROTECTED / MIDDLEWARE VERIFIED</span>
61
+ <h1>Welcome back, {session.user.name}.</h1>
62
+ <p>{session.user.email}</p>
63
+ </div>
64
+ <div className="sign-out-control">
65
+ <button className="button button-secondary" disabled={pending} onClick={signOut}>
66
+ {pending ? "Signing out…" : "Sign out"}
67
+ </button>
68
+ {error ? <span className="inline-error">{error}</span> : null}
69
+ </div>
70
+ </div>
71
+ </main>
72
+ );
73
+ }
@@ -0,0 +1,14 @@
1
+ import type { LayoutProps, Metadata } from "@farm.js/core";
2
+ import "./globals.css";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "FARMJS Preact Better Auth Starter",
6
+ description: "Better Auth with FARMJS and Preact",
7
+ icons: {
8
+ icon: [{ url: "/favicon.svg", type: "image/svg+xml", sizes: "any" }],
9
+ },
10
+ };
11
+
12
+ export default function RootLayout({ children }: LayoutProps) {
13
+ return <>{children}</>;
14
+ }
@@ -0,0 +1,36 @@
1
+ import { ResourceLinks } from "../components/resource-links";
2
+
3
+ export default function HomePage() {
4
+ return (
5
+ <main className="landing-main">
6
+ <section className="hero-section">
7
+ <div className="hero-copy">
8
+ <div className="eyebrow-row">
9
+ <span>00</span>
10
+ <span>FARMJS / Preact + Better Auth</span>
11
+ </div>
12
+
13
+ <h1>
14
+ Connect Postgres. Start at <code>/sign-up</code>.
15
+ </h1>
16
+
17
+ <div className="command-list" aria-label="Development commands">
18
+ <div className="command-row">
19
+ <span>01</span>
20
+ <code>cp .env.example .env.local</code>
21
+ </div>
22
+ <div className="command-row">
23
+ <span>02</span>
24
+ <code>pnpm dev</code>
25
+ </div>
26
+ </div>
27
+
28
+ <ResourceLinks
29
+ className="resource-links"
30
+ primary={{ href: "/sign-up", label: "Get started" }}
31
+ />
32
+ </div>
33
+ </section>
34
+ </main>
35
+ );
36
+ }
@@ -0,0 +1,10 @@
1
+ import { AuthForm } from "../../components/auth-form";
2
+ import { AuthShell } from "../../components/auth-shell";
3
+
4
+ export default function SignInPage() {
5
+ return (
6
+ <AuthShell>
7
+ <AuthForm mode="sign-in" />
8
+ </AuthShell>
9
+ );
10
+ }
@@ -0,0 +1,10 @@
1
+ import { AuthForm } from "../../components/auth-form";
2
+ import { AuthShell } from "../../components/auth-shell";
3
+
4
+ export default function SignUpPage() {
5
+ return (
6
+ <AuthShell>
7
+ <AuthForm mode="sign-up" />
8
+ </AuthShell>
9
+ );
10
+ }
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ import type { JSX } from "preact";
4
+ import { useState } from "preact/hooks";
5
+ import { authClient } from "../lib/auth-client";
6
+
7
+ export function AuthForm({ mode }: { mode: "sign-in" | "sign-up" }) {
8
+ const isSignUp = mode === "sign-up";
9
+ const [pending, setPending] = useState(false);
10
+ const [error, setError] = useState<string | null>(null);
11
+
12
+ async function submit(event: JSX.TargetedSubmitEvent<HTMLFormElement>) {
13
+ event.preventDefault();
14
+ setError(null);
15
+ setPending(true);
16
+
17
+ const formData = new FormData(event.currentTarget);
18
+ const email = String(formData.get("email") ?? "").trim();
19
+ const password = String(formData.get("password") ?? "");
20
+ const name = String(formData.get("name") ?? "").trim();
21
+
22
+ try {
23
+ const response = isSignUp
24
+ ? await authClient.signUp.email({ email, name, password })
25
+ : await authClient.signIn.email({ email, password });
26
+ if (response.error) {
27
+ setError(response.error.message ?? "We could not complete that request.");
28
+ setPending(false);
29
+ return;
30
+ }
31
+ window.location.assign("/dashboard");
32
+ } catch {
33
+ setError("The authentication service could not be reached. Please try again.");
34
+ setPending(false);
35
+ }
36
+ }
37
+
38
+ return (
39
+ <div className="auth-form-panel">
40
+ <div className="auth-form-heading">
41
+ <span className="section-index">{isSignUp ? "01 / SIGN UP" : "01 / SIGN IN"}</span>
42
+ <h1>{isSignUp ? "Create account." : "Welcome back."}</h1>
43
+ </div>
44
+
45
+ <form className="auth-form" onSubmit={submit}>
46
+ {isSignUp ? (
47
+ <label className="field">
48
+ <span>
49
+ <small>01</small> Name
50
+ </span>
51
+ <input autoComplete="name" minLength={2} name="name" required type="text" />
52
+ </label>
53
+ ) : null}
54
+ <label className="field">
55
+ <span>
56
+ <small>{isSignUp ? "02" : "01"}</small> Email
57
+ </span>
58
+ <input autoComplete="email" name="email" required type="email" />
59
+ </label>
60
+ <label className="field">
61
+ <span>
62
+ <small>{isSignUp ? "03" : "02"}</small> Password
63
+ </span>
64
+ <input
65
+ autoComplete={isSignUp ? "new-password" : "current-password"}
66
+ minLength={8}
67
+ name="password"
68
+ required
69
+ type="password"
70
+ />
71
+ </label>
72
+
73
+ {error ? (
74
+ <div className="form-error" role="alert">
75
+ <strong>Authentication failed</strong>
76
+ <span>{error}</span>
77
+ </div>
78
+ ) : null}
79
+
80
+ <button className="button button-primary auth-submit" disabled={pending} type="submit">
81
+ <span>{pending ? "Please wait…" : isSignUp ? "Create account" : "Sign in"}</span>
82
+ <span aria-hidden="true">{pending ? "···" : "→"}</span>
83
+ </button>
84
+ </form>
85
+
86
+ <p className="auth-switch">
87
+ {isSignUp ? "Have an account?" : "Need an account?"}{" "}
88
+ <a href={isSignUp ? "/sign-in" : "/sign-up"}>{isSignUp ? "Sign in →" : "Sign up →"}</a>
89
+ </p>
90
+ </div>
91
+ );
92
+ }
@@ -0,0 +1,16 @@
1
+ import type { ComponentChildren } from "preact";
2
+ import { ResourceLinks } from "./resource-links";
3
+
4
+ export function AuthShell({ children }: { children: ComponentChildren }) {
5
+ return (
6
+ <main className="auth-shell">
7
+ <a className="auth-home-link" href="/" aria-label="Back to starter home">
8
+ <span>00</span>
9
+ <span>FARMJS / Preact + Better Auth</span>
10
+ </a>
11
+
12
+ <section className="auth-form-side">{children}</section>
13
+ <ResourceLinks className="auth-resource-links" />
14
+ </main>
15
+ );
16
+ }
@@ -0,0 +1,5 @@
1
+ import { createAuthClient } from "better-auth/client";
2
+
3
+ export const authClient = createAuthClient({
4
+ baseURL: "",
5
+ });
@@ -0,0 +1,17 @@
1
+ import { betterAuth } from "@farm.js/better-auth";
2
+ import { defineConfig } from "@farm.js/core/config";
3
+ import { solid } from "@farm.js/solid";
4
+ import { auth } from "./src/lib/auth.ts";
5
+
6
+ export default defineConfig({
7
+ renderer: solid(),
8
+ theme: {
9
+ default: "dark",
10
+ },
11
+ integrations: {
12
+ auth: betterAuth({ instance: auth }),
13
+ },
14
+ deploy: {
15
+ target: "vercel",
16
+ },
17
+ });
@@ -0,0 +1,67 @@
1
+ "use client";
2
+
3
+ import { createSignal, onMount, Show } from "solid-js";
4
+ import { authClient } from "../../lib/auth-client";
5
+
6
+ type SessionData = NonNullable<Awaited<ReturnType<typeof authClient.getSession>>["data"]>;
7
+
8
+ export default function DashboardPage() {
9
+ const [session, setSession] = createSignal<SessionData | null>(null);
10
+ const [pending, setPending] = createSignal(true);
11
+ const [error, setError] = createSignal<string | null>(null);
12
+
13
+ onMount(async () => {
14
+ const response = await authClient.getSession();
15
+ if (response.error || !response.data?.user) {
16
+ window.location.replace("/sign-in");
17
+ return;
18
+ }
19
+ setSession(response.data);
20
+ setPending(false);
21
+ });
22
+
23
+ async function signOut() {
24
+ setPending(true);
25
+ setError(null);
26
+ const response = await authClient.signOut();
27
+ if (response.error) {
28
+ setError(response.error.message ?? "Could not sign out.");
29
+ setPending(false);
30
+ return;
31
+ }
32
+ window.location.assign("/");
33
+ }
34
+
35
+ return (
36
+ <Show
37
+ when={session()}
38
+ fallback={
39
+ <main class="loading-shell" aria-live="polite">
40
+ <div class="loading-block">
41
+ <span class="loading-label">Verifying protected session</span>
42
+ <div class="loading-line wide" />
43
+ <div class="loading-line" />
44
+ </div>
45
+ </main>
46
+ }
47
+ >
48
+ {(current) => (
49
+ <main class="dashboard-shell">
50
+ <div class="dashboard-heading">
51
+ <div>
52
+ <span class="section-index">PROTECTED / MIDDLEWARE VERIFIED</span>
53
+ <h1>Welcome back, {current().user.name}.</h1>
54
+ <p>{current().user.email}</p>
55
+ </div>
56
+ <div class="sign-out-control">
57
+ <button class="button button-secondary" disabled={pending()} onClick={signOut}>
58
+ {pending() ? "Signing out…" : "Sign out"}
59
+ </button>
60
+ {error() ? <span class="inline-error">{error()}</span> : null}
61
+ </div>
62
+ </div>
63
+ </main>
64
+ )}
65
+ </Show>
66
+ );
67
+ }
@@ -0,0 +1,14 @@
1
+ import type { LayoutProps, Metadata } from "@farm.js/core";
2
+ import "./globals.css";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "FARMJS Solid Better Auth Starter",
6
+ description: "Better Auth with FARMJS and Solid",
7
+ icons: {
8
+ icon: [{ url: "/favicon.svg", type: "image/svg+xml", sizes: "any" }],
9
+ },
10
+ };
11
+
12
+ export default function RootLayout(props: LayoutProps) {
13
+ return <>{props.children}</>;
14
+ }
@@ -0,0 +1,36 @@
1
+ import { ResourceLinks } from "../components/resource-links";
2
+
3
+ export default function HomePage() {
4
+ return (
5
+ <main class="landing-main">
6
+ <section class="hero-section">
7
+ <div class="hero-copy">
8
+ <div class="eyebrow-row">
9
+ <span>00</span>
10
+ <span>FARMJS / Solid + Better Auth</span>
11
+ </div>
12
+
13
+ <h1>
14
+ Connect Postgres. Start at <code>/sign-up</code>.
15
+ </h1>
16
+
17
+ <div class="command-list" aria-label="Development commands">
18
+ <div class="command-row">
19
+ <span>01</span>
20
+ <code>cp .env.example .env.local</code>
21
+ </div>
22
+ <div class="command-row">
23
+ <span>02</span>
24
+ <code>pnpm dev</code>
25
+ </div>
26
+ </div>
27
+
28
+ <ResourceLinks
29
+ className="resource-links"
30
+ primary={{ href: "/sign-up", label: "Get started" }}
31
+ />
32
+ </div>
33
+ </section>
34
+ </main>
35
+ );
36
+ }
@@ -0,0 +1,10 @@
1
+ import { AuthForm } from "../../components/auth-form";
2
+ import { AuthShell } from "../../components/auth-shell";
3
+
4
+ export default function SignInPage() {
5
+ return (
6
+ <AuthShell>
7
+ <AuthForm mode="sign-in" />
8
+ </AuthShell>
9
+ );
10
+ }
@@ -0,0 +1,10 @@
1
+ import { AuthForm } from "../../components/auth-form";
2
+ import { AuthShell } from "../../components/auth-shell";
3
+
4
+ export default function SignUpPage() {
5
+ return (
6
+ <AuthShell>
7
+ <AuthForm mode="sign-up" />
8
+ </AuthShell>
9
+ );
10
+ }
@@ -0,0 +1,91 @@
1
+ "use client";
2
+
3
+ import { createSignal } from "solid-js";
4
+ import { authClient } from "../lib/auth-client";
5
+
6
+ export function AuthForm(props: { mode: "sign-in" | "sign-up" }) {
7
+ const isSignUp = () => props.mode === "sign-up";
8
+ const [pending, setPending] = createSignal(false);
9
+ const [error, setError] = createSignal<string | null>(null);
10
+
11
+ async function submit(event: SubmitEvent) {
12
+ event.preventDefault();
13
+ setError(null);
14
+ setPending(true);
15
+
16
+ const formData = new FormData(event.currentTarget as HTMLFormElement);
17
+ const email = String(formData.get("email") ?? "").trim();
18
+ const password = String(formData.get("password") ?? "");
19
+ const name = String(formData.get("name") ?? "").trim();
20
+
21
+ try {
22
+ const response = isSignUp()
23
+ ? await authClient.signUp.email({ email, name, password })
24
+ : await authClient.signIn.email({ email, password });
25
+ if (response.error) {
26
+ setError(response.error.message ?? "We could not complete that request.");
27
+ setPending(false);
28
+ return;
29
+ }
30
+ window.location.assign("/dashboard");
31
+ } catch {
32
+ setError("The authentication service could not be reached. Please try again.");
33
+ setPending(false);
34
+ }
35
+ }
36
+
37
+ return (
38
+ <div class="auth-form-panel">
39
+ <div class="auth-form-heading">
40
+ <span class="section-index">{isSignUp() ? "01 / SIGN UP" : "01 / SIGN IN"}</span>
41
+ <h1>{isSignUp() ? "Create account." : "Welcome back."}</h1>
42
+ </div>
43
+
44
+ <form class="auth-form" onSubmit={submit}>
45
+ {isSignUp() ? (
46
+ <label class="field">
47
+ <span>
48
+ <small>01</small> Name
49
+ </span>
50
+ <input autocomplete="name" minlength={2} name="name" required type="text" />
51
+ </label>
52
+ ) : null}
53
+ <label class="field">
54
+ <span>
55
+ <small>{isSignUp() ? "02" : "01"}</small> Email
56
+ </span>
57
+ <input autocomplete="email" name="email" required type="email" />
58
+ </label>
59
+ <label class="field">
60
+ <span>
61
+ <small>{isSignUp() ? "03" : "02"}</small> Password
62
+ </span>
63
+ <input
64
+ autocomplete={isSignUp() ? "new-password" : "current-password"}
65
+ minlength={8}
66
+ name="password"
67
+ required
68
+ type="password"
69
+ />
70
+ </label>
71
+
72
+ {error() ? (
73
+ <div class="form-error" role="alert">
74
+ <strong>Authentication failed</strong>
75
+ <span>{error()}</span>
76
+ </div>
77
+ ) : null}
78
+
79
+ <button class="button button-primary auth-submit" disabled={pending()} type="submit">
80
+ <span>{pending() ? "Please wait…" : isSignUp() ? "Create account" : "Sign in"}</span>
81
+ <span aria-hidden="true">{pending() ? "···" : "→"}</span>
82
+ </button>
83
+ </form>
84
+
85
+ <p class="auth-switch">
86
+ {isSignUp() ? "Have an account?" : "Need an account?"}{" "}
87
+ <a href={isSignUp() ? "/sign-in" : "/sign-up"}>{isSignUp() ? "Sign in →" : "Sign up →"}</a>
88
+ </p>
89
+ </div>
90
+ );
91
+ }
@@ -0,0 +1,16 @@
1
+ import type { JSX } from "solid-js";
2
+ import { ResourceLinks } from "./resource-links";
3
+
4
+ export function AuthShell(props: { children: JSX.Element }) {
5
+ return (
6
+ <main class="auth-shell">
7
+ <a class="auth-home-link" href="/" aria-label="Back to starter home">
8
+ <span>00</span>
9
+ <span>FARMJS / Solid + Better Auth</span>
10
+ </a>
11
+
12
+ <section class="auth-form-side">{props.children}</section>
13
+ <ResourceLinks className="auth-resource-links" />
14
+ </main>
15
+ );
16
+ }
@@ -0,0 +1,5 @@
1
+ import { createAuthClient } from "better-auth/solid";
2
+
3
+ export const authClient = createAuthClient({
4
+ baseURL: "",
5
+ });
@@ -0,0 +1,17 @@
1
+ import { betterAuth } from "@farm.js/better-auth";
2
+ import { defineConfig } from "@farm.js/core/config";
3
+ import { svelte } from "@farm.js/svelte";
4
+ import { auth } from "./src/lib/auth.ts";
5
+
6
+ export default defineConfig({
7
+ renderer: svelte(),
8
+ theme: {
9
+ default: "dark",
10
+ },
11
+ integrations: {
12
+ auth: betterAuth({ instance: auth }),
13
+ },
14
+ deploy: {
15
+ target: "vercel",
16
+ },
17
+ });
@@ -0,0 +1,62 @@
1
+ <script module lang="ts">
2
+ export const hydrate = true;
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ import { onMount } from "svelte";
7
+ import { authClient } from "../../lib/auth-client";
8
+
9
+ type SessionData = NonNullable<Awaited<ReturnType<typeof authClient.getSession>>["data"]>;
10
+
11
+ let session = $state<SessionData | null>(null);
12
+ let pending = $state(true);
13
+ let error = $state<string | null>(null);
14
+
15
+ onMount(async () => {
16
+ const response = await authClient.getSession();
17
+ if (response.error || !response.data?.user) {
18
+ window.location.replace("/sign-in");
19
+ return;
20
+ }
21
+ session = response.data;
22
+ pending = false;
23
+ });
24
+
25
+ async function signOut() {
26
+ pending = true;
27
+ error = null;
28
+ const response = await authClient.signOut();
29
+ if (response.error) {
30
+ error = response.error.message ?? "Could not sign out.";
31
+ pending = false;
32
+ return;
33
+ }
34
+ window.location.assign("/");
35
+ }
36
+ </script>
37
+
38
+ {#if !session}
39
+ <main class="loading-shell" aria-live="polite">
40
+ <div class="loading-block">
41
+ <span class="loading-label">Verifying protected session</span>
42
+ <div class="loading-line wide"></div>
43
+ <div class="loading-line"></div>
44
+ </div>
45
+ </main>
46
+ {:else}
47
+ <main class="dashboard-shell">
48
+ <div class="dashboard-heading">
49
+ <div>
50
+ <span class="section-index">PROTECTED / MIDDLEWARE VERIFIED</span>
51
+ <h1>Welcome back, {session.user.name}.</h1>
52
+ <p>{session.user.email}</p>
53
+ </div>
54
+ <div class="sign-out-control">
55
+ <button class="button button-secondary" disabled={pending} onclick={signOut}>
56
+ {pending ? "Signing out…" : "Sign out"}
57
+ </button>
58
+ {#if error}<span class="inline-error">{error}</span>{/if}
59
+ </div>
60
+ </div>
61
+ </main>
62
+ {/if}