@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.
- package/README.md +38 -6
- package/bin/create-farm-app.js +7 -2
- package/dist/index.js +3298 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3294 -14
- package/dist/index.mjs.map +1 -1
- package/dist/utils.js +9 -6
- package/dist/utils.js.map +1 -1
- package/dist/utils.mjs +9 -6
- package/dist/utils.mjs.map +1 -1
- package/package.json +2 -2
- package/templates/_integrations/better-auth/preact/farm.config.ts +17 -0
- package/templates/_integrations/better-auth/preact/src/app/dashboard/page.tsx +73 -0
- package/templates/_integrations/better-auth/preact/src/app/layout.tsx +14 -0
- package/templates/_integrations/better-auth/preact/src/app/page.tsx +36 -0
- package/templates/_integrations/better-auth/preact/src/app/sign-in/page.tsx +10 -0
- package/templates/_integrations/better-auth/preact/src/app/sign-up/page.tsx +10 -0
- package/templates/_integrations/better-auth/preact/src/components/auth-form.tsx +92 -0
- package/templates/_integrations/better-auth/preact/src/components/auth-shell.tsx +16 -0
- package/templates/_integrations/better-auth/preact/src/lib/auth-client.ts +5 -0
- package/templates/_integrations/better-auth/solid/farm.config.ts +17 -0
- package/templates/_integrations/better-auth/solid/src/app/dashboard/page.tsx +67 -0
- package/templates/_integrations/better-auth/solid/src/app/layout.tsx +14 -0
- package/templates/_integrations/better-auth/solid/src/app/page.tsx +36 -0
- package/templates/_integrations/better-auth/solid/src/app/sign-in/page.tsx +10 -0
- package/templates/_integrations/better-auth/solid/src/app/sign-up/page.tsx +10 -0
- package/templates/_integrations/better-auth/solid/src/components/auth-form.tsx +91 -0
- package/templates/_integrations/better-auth/solid/src/components/auth-shell.tsx +16 -0
- package/templates/_integrations/better-auth/solid/src/lib/auth-client.ts +5 -0
- package/templates/_integrations/better-auth/svelte/farm.config.ts +17 -0
- package/templates/_integrations/better-auth/svelte/src/app/dashboard/page.svelte +62 -0
- package/templates/_integrations/better-auth/svelte/src/app/layout.svelte +20 -0
- package/templates/_integrations/better-auth/svelte/src/app/page.svelte +32 -0
- package/templates/_integrations/better-auth/svelte/src/app/sign-in/page.svelte +10 -0
- package/templates/_integrations/better-auth/svelte/src/app/sign-up/page.svelte +10 -0
- package/templates/_integrations/better-auth/svelte/src/components/auth-form.svelte +83 -0
- package/templates/_integrations/better-auth/svelte/src/components/auth-shell.svelte +16 -0
- package/templates/_integrations/better-auth/svelte/src/components/resource-links.svelte +47 -0
- package/templates/_integrations/better-auth/svelte/src/lib/auth-client.ts +5 -0
- package/templates/_integrations/better-auth/vue/farm.config.ts +17 -0
- package/templates/_integrations/better-auth/vue/src/app/dashboard/page.vue +62 -0
- package/templates/_integrations/better-auth/vue/src/app/layout.vue +20 -0
- package/templates/_integrations/better-auth/vue/src/app/page.vue +34 -0
- package/templates/_integrations/better-auth/vue/src/app/sign-in/page.vue +12 -0
- package/templates/_integrations/better-auth/vue/src/app/sign-up/page.vue +12 -0
- package/templates/_integrations/better-auth/vue/src/components/auth-form.vue +86 -0
- package/templates/_integrations/better-auth/vue/src/components/auth-shell.vue +15 -0
- package/templates/_integrations/better-auth/vue/src/components/resource-links.vue +43 -0
- package/templates/_integrations/better-auth/vue/src/lib/auth-client.ts +5 -0
- package/templates/_renderers/preact/farm.config.ts +12 -0
- package/templates/_renderers/preact/package.json +26 -0
- package/templates/_renderers/preact/src/app/api/greeting/route.ts +15 -0
- package/templates/_renderers/preact/src/app/layout.tsx +16 -0
- package/templates/_renderers/preact/src/app/page.tsx +66 -0
- package/templates/_renderers/preact/src/app/preact.css +26 -0
- package/templates/_renderers/preact/src/components/resource-links.tsx +71 -0
- package/templates/_renderers/preact/src/lib/api-client.ts +4 -0
- package/templates/_renderers/preact/src/lib/api.generated.ts +7 -0
- package/templates/_renderers/preact/tsconfig.json +25 -0
- package/templates/_renderers/solid/farm.config.ts +12 -0
- package/templates/_renderers/solid/package.json +25 -0
- package/templates/_renderers/solid/src/app/api/greeting/route.ts +15 -0
- package/templates/_renderers/solid/src/app/layout.tsx +15 -0
- package/templates/_renderers/solid/src/app/page.tsx +56 -0
- package/templates/_renderers/solid/src/app/solid.css +22 -0
- package/templates/_renderers/solid/src/components/resource-links.tsx +71 -0
- package/templates/_renderers/solid/src/lib/api-client.ts +4 -0
- package/templates/_renderers/solid/src/lib/api.generated.ts +7 -0
- package/templates/_renderers/solid/tsconfig.json +25 -0
- package/templates/_renderers/svelte/farm.config.ts +9 -0
- package/templates/_renderers/svelte/package.json +28 -0
- package/templates/_renderers/svelte/src/app/api/greeting/route.ts +15 -0
- package/templates/_renderers/svelte/src/app/layout.svelte +21 -0
- package/templates/_renderers/svelte/src/app/page.svelte +64 -0
- package/templates/_renderers/svelte/src/app/svelte.css +26 -0
- package/templates/_renderers/svelte/src/components/resource-links.svelte +31 -0
- package/templates/_renderers/svelte/src/lib/api-client.ts +4 -0
- package/templates/_renderers/svelte/src/lib/api.generated.ts +7 -0
- package/templates/_renderers/svelte/tsconfig.json +23 -0
- package/templates/_renderers/vue/farm.config.ts +12 -0
- package/templates/_renderers/vue/package.json +26 -0
- package/templates/_renderers/vue/src/app/api/greeting/route.ts +15 -0
- package/templates/_renderers/vue/src/app/layout.vue +21 -0
- package/templates/_renderers/vue/src/app/page.vue +69 -0
- package/templates/_renderers/vue/src/app/vue.css +26 -0
- package/templates/_renderers/vue/src/components/resource-links.vue +33 -0
- package/templates/_renderers/vue/src/env.d.ts +7 -0
- package/templates/_renderers/vue/src/lib/api-client.ts +4 -0
- package/templates/_renderers/vue/src/lib/api.generated.ts +7 -0
- package/templates/_renderers/vue/tsconfig.json +23 -0
- package/templates/auth/.env.example +6 -0
- package/templates/auth/README.md +95 -0
- package/templates/auth/docs.config.ts +33 -0
- package/templates/auth/docs.json +36 -0
- package/templates/auth/farm.config.ts +20 -0
- package/templates/auth/gitignore +10 -0
- package/templates/auth/package.json +34 -0
- package/templates/auth/pnpm-workspace.yaml +13 -0
- package/templates/auth/src/app/dashboard/middleware.ts +13 -0
- package/templates/auth/src/app/dashboard/page.tsx +140 -0
- package/templates/auth/src/app/docs/page.md +29 -0
- package/templates/auth/src/app/error.tsx +25 -0
- package/templates/auth/src/app/globals.css +1064 -0
- package/templates/auth/src/app/layout.tsx +11 -0
- package/templates/auth/src/app/loading.tsx +15 -0
- package/templates/auth/src/app/not-found.tsx +26 -0
- package/templates/auth/src/app/page.tsx +46 -0
- package/templates/auth/src/app/sign-in/page.tsx +16 -0
- package/templates/auth/src/app/sign-up/page.tsx +16 -0
- package/templates/auth/src/components/auth-form.tsx +120 -0
- package/templates/auth/src/components/auth-shell.tsx +21 -0
- package/templates/auth/src/components/resource-links.tsx +78 -0
- package/templates/auth/src/components/sign-out-button.tsx +45 -0
- package/templates/auth/src/components/site-header.tsx +46 -0
- package/templates/auth/tsconfig.json +20 -0
- package/templates/basic/docs.config.ts +33 -0
- package/templates/basic/docs.json +36 -0
- package/templates/basic/farm.config.ts +9 -0
- package/templates/basic/package.json +7 -3
- package/templates/basic/pnpm-workspace.yaml +8 -0
- package/templates/basic/src/app/docs/page.md +29 -0
- package/templates/basic/src/app/globals.css +241 -0
- package/templates/basic/src/app/layout.tsx +3 -5
- package/templates/basic/src/app/page.tsx +20 -61
- package/templates/basic/src/components/resource-links.tsx +78 -0
- package/templates/basic/tsconfig.json +4 -0
- package/templates/better-auth/.env.example +3 -0
- package/templates/better-auth/README.md +89 -0
- package/templates/better-auth/docs.config.ts +33 -0
- package/templates/better-auth/docs.json +36 -0
- package/templates/better-auth/farm.config.ts +38 -0
- package/templates/better-auth/gitignore +10 -0
- package/templates/better-auth/package.json +36 -0
- package/templates/better-auth/pnpm-workspace.yaml +13 -0
- package/templates/better-auth/src/app/dashboard/middleware.ts +15 -0
- package/templates/better-auth/src/app/dashboard/page.tsx +188 -0
- package/templates/better-auth/src/app/docs/page.md +29 -0
- package/templates/better-auth/src/app/error.tsx +25 -0
- package/templates/better-auth/src/app/globals.css +1064 -0
- package/templates/better-auth/src/app/layout.tsx +11 -0
- package/templates/better-auth/src/app/loading.tsx +15 -0
- package/templates/better-auth/src/app/not-found.tsx +26 -0
- package/templates/better-auth/src/app/page.tsx +50 -0
- package/templates/better-auth/src/app/sign-in/page.tsx +16 -0
- package/templates/better-auth/src/app/sign-up/page.tsx +16 -0
- package/templates/better-auth/src/components/auth-form.tsx +120 -0
- package/templates/better-auth/src/components/auth-shell.tsx +21 -0
- package/templates/better-auth/src/components/resource-links.tsx +78 -0
- package/templates/better-auth/src/components/sign-out-button.tsx +45 -0
- package/templates/better-auth/src/components/site-header.tsx +46 -0
- package/templates/better-auth/src/lib/auth-client.ts +5 -0
- package/templates/better-auth/src/lib/auth.ts +50 -0
- package/templates/better-auth/src/lib/session.ts +8 -0
- package/templates/better-auth/tsconfig.json +20 -0
- package/templates/react-compiler/README.md +62 -0
- package/templates/react-compiler/docs.config.ts +33 -0
- package/templates/react-compiler/docs.json +36 -0
- package/templates/react-compiler/farm.config.ts +29 -0
- package/templates/react-compiler/gitignore +23 -0
- package/templates/react-compiler/package.json +34 -0
- package/templates/react-compiler/pnpm-workspace.yaml +8 -0
- package/templates/react-compiler/public/favicon.svg +5 -0
- package/templates/react-compiler/scripts/verify-experiment.mjs +113 -0
- package/templates/react-compiler/src/app/docs/page.md +29 -0
- package/templates/react-compiler/src/app/globals.css +470 -0
- package/templates/react-compiler/src/app/layout.tsx +22 -0
- package/templates/react-compiler/src/app/page.tsx +47 -0
- package/templates/react-compiler/src/components/compiler-comparison.tsx +93 -0
- package/templates/react-compiler/src/components/resource-links.tsx +78 -0
- package/templates/react-compiler/src/farm.d.ts +74 -0
- package/templates/react-compiler/src/lib/api.generated.ts +10 -0
- package/templates/react-compiler/tsconfig.json +24 -0
- package/templates/basic/src/app/about/page.tsx +0 -32
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LayoutProps, Metadata } from "@farm.js/core";
|
|
2
|
+
import "./globals.css";
|
|
3
|
+
|
|
4
|
+
export const metadata: Metadata = {
|
|
5
|
+
title: "FARMJS Better Auth Starter",
|
|
6
|
+
description: "A production-shaped FARMJS authentication starter powered by Better Auth.",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function RootLayout({ children }: LayoutProps) {
|
|
10
|
+
return <>{children}</>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function Loading() {
|
|
2
|
+
return (
|
|
3
|
+
<main className="loading-shell" aria-label="Loading page" aria-live="polite">
|
|
4
|
+
<div className="loading-block">
|
|
5
|
+
<span className="loading-label">Loading account</span>
|
|
6
|
+
<div className="loading-line wide" />
|
|
7
|
+
<div className="loading-line" />
|
|
8
|
+
<div className="loading-grid">
|
|
9
|
+
<div />
|
|
10
|
+
<div />
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</main>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
interface NotFoundProps {
|
|
2
|
+
pathname?: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export default function NotFound({ pathname }: NotFoundProps) {
|
|
6
|
+
return (
|
|
7
|
+
<main className="error-shell">
|
|
8
|
+
<section className="error-panel">
|
|
9
|
+
<span className="section-index">404 / NOT FOUND</span>
|
|
10
|
+
<h1>This route is outside the starter.</h1>
|
|
11
|
+
<p>
|
|
12
|
+
{pathname ? (
|
|
13
|
+
<>
|
|
14
|
+
Nothing is registered at <code>{pathname}</code>.
|
|
15
|
+
</>
|
|
16
|
+
) : (
|
|
17
|
+
"The page you requested does not exist."
|
|
18
|
+
)}
|
|
19
|
+
</p>
|
|
20
|
+
<a className="button button-primary" href="/">
|
|
21
|
+
Return home
|
|
22
|
+
</a>
|
|
23
|
+
</section>
|
|
24
|
+
</main>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ResourceLinks } from "../components/resource-links";
|
|
2
|
+
import { getServerSession } from "../lib/session";
|
|
3
|
+
|
|
4
|
+
export default async function HomePage() {
|
|
5
|
+
const session = await getServerSession();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<main className="landing-main">
|
|
9
|
+
<section className="hero-section">
|
|
10
|
+
<div className="hero-copy">
|
|
11
|
+
<div className="eyebrow-row">
|
|
12
|
+
<span>00</span>
|
|
13
|
+
<span>FARMJS / Better Auth starter</span>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<h1>
|
|
17
|
+
{session ? (
|
|
18
|
+
<>
|
|
19
|
+
Open your <code>/dashboard</code>.
|
|
20
|
+
</>
|
|
21
|
+
) : (
|
|
22
|
+
<>
|
|
23
|
+
Connect Postgres. Start at <code>/sign-up</code>.
|
|
24
|
+
</>
|
|
25
|
+
)}
|
|
26
|
+
</h1>
|
|
27
|
+
|
|
28
|
+
<div className="command-list" aria-label="Development commands">
|
|
29
|
+
<div className="command-row">
|
|
30
|
+
<span>01</span>
|
|
31
|
+
<code>cp .env.example .env.local</code>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="command-row">
|
|
34
|
+
<span>02</span>
|
|
35
|
+
<code>pnpm dev</code>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<ResourceLinks
|
|
40
|
+
className="resource-links"
|
|
41
|
+
primary={{
|
|
42
|
+
href: session ? "/dashboard" : "/sign-up",
|
|
43
|
+
label: session ? "Dashboard" : "Get started",
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
</section>
|
|
48
|
+
</main>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Metadata } from "@farm.js/core";
|
|
2
|
+
import { AuthForm } from "../../components/auth-form";
|
|
3
|
+
import { AuthShell } from "../../components/auth-shell";
|
|
4
|
+
|
|
5
|
+
export const metadata: Metadata = {
|
|
6
|
+
title: "Sign in | FARMJS Better Auth Starter",
|
|
7
|
+
description: "Sign in with Better Auth.",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default function SignInPage() {
|
|
11
|
+
return (
|
|
12
|
+
<AuthShell>
|
|
13
|
+
<AuthForm mode="sign-in" />
|
|
14
|
+
</AuthShell>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Metadata } from "@farm.js/core";
|
|
2
|
+
import { AuthForm } from "../../components/auth-form";
|
|
3
|
+
import { AuthShell } from "../../components/auth-shell";
|
|
4
|
+
|
|
5
|
+
export const metadata: Metadata = {
|
|
6
|
+
title: "Create account | FARMJS Better Auth Starter",
|
|
7
|
+
description: "Create an account with Better Auth.",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default function SignUpPage() {
|
|
11
|
+
return (
|
|
12
|
+
<AuthShell>
|
|
13
|
+
<AuthForm mode="sign-up" />
|
|
14
|
+
</AuthShell>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { authClient } from "../lib/auth-client";
|
|
5
|
+
|
|
6
|
+
interface AuthFormProps {
|
|
7
|
+
mode: "sign-in" | "sign-up";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function AuthForm({ mode }: AuthFormProps) {
|
|
11
|
+
const isSignUp = mode === "sign-up";
|
|
12
|
+
const [pending, setPending] = useState(false);
|
|
13
|
+
const [error, setError] = useState<string | null>(null);
|
|
14
|
+
|
|
15
|
+
async function submit(event: React.FormEvent<HTMLFormElement>) {
|
|
16
|
+
event.preventDefault();
|
|
17
|
+
setError(null);
|
|
18
|
+
setPending(true);
|
|
19
|
+
|
|
20
|
+
const formData = new FormData(event.currentTarget);
|
|
21
|
+
const email = String(formData.get("email") ?? "").trim();
|
|
22
|
+
const password = String(formData.get("password") ?? "");
|
|
23
|
+
const name = String(formData.get("name") ?? "").trim();
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const response = isSignUp
|
|
27
|
+
? await authClient.signUp.email({
|
|
28
|
+
email,
|
|
29
|
+
name,
|
|
30
|
+
password,
|
|
31
|
+
})
|
|
32
|
+
: await authClient.signIn.email({
|
|
33
|
+
email,
|
|
34
|
+
password,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (response.error) {
|
|
38
|
+
setError(response.error.message ?? "We could not complete that request.");
|
|
39
|
+
setPending(false);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
window.location.assign("/dashboard");
|
|
44
|
+
} catch {
|
|
45
|
+
setError("The authentication service could not be reached. Please try again.");
|
|
46
|
+
setPending(false);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className="auth-form-panel">
|
|
52
|
+
<div className="auth-form-heading">
|
|
53
|
+
<span className="section-index">{isSignUp ? "01 / SIGN UP" : "01 / SIGN IN"}</span>
|
|
54
|
+
<h1>{isSignUp ? "Create account." : "Welcome back."}</h1>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<form className="auth-form" onSubmit={submit}>
|
|
58
|
+
{isSignUp ? (
|
|
59
|
+
<label className="field">
|
|
60
|
+
<span>
|
|
61
|
+
<small>01</small> Name
|
|
62
|
+
</span>
|
|
63
|
+
<input
|
|
64
|
+
autoComplete="name"
|
|
65
|
+
minLength={2}
|
|
66
|
+
name="name"
|
|
67
|
+
placeholder="Ada Lovelace"
|
|
68
|
+
required
|
|
69
|
+
type="text"
|
|
70
|
+
/>
|
|
71
|
+
</label>
|
|
72
|
+
) : null}
|
|
73
|
+
|
|
74
|
+
<label className="field">
|
|
75
|
+
<span>
|
|
76
|
+
<small>{isSignUp ? "02" : "01"}</small> Email
|
|
77
|
+
</span>
|
|
78
|
+
<input
|
|
79
|
+
autoComplete="email"
|
|
80
|
+
name="email"
|
|
81
|
+
placeholder="you@example.com"
|
|
82
|
+
required
|
|
83
|
+
type="email"
|
|
84
|
+
/>
|
|
85
|
+
</label>
|
|
86
|
+
|
|
87
|
+
<label className="field">
|
|
88
|
+
<span>
|
|
89
|
+
<small>{isSignUp ? "03" : "02"}</small> Password
|
|
90
|
+
</span>
|
|
91
|
+
<input
|
|
92
|
+
autoComplete={isSignUp ? "new-password" : "current-password"}
|
|
93
|
+
minLength={8}
|
|
94
|
+
name="password"
|
|
95
|
+
placeholder="At least 8 characters"
|
|
96
|
+
required
|
|
97
|
+
type="password"
|
|
98
|
+
/>
|
|
99
|
+
</label>
|
|
100
|
+
|
|
101
|
+
{error ? (
|
|
102
|
+
<div className="form-error" role="alert">
|
|
103
|
+
<strong>Authentication failed</strong>
|
|
104
|
+
<span>{error}</span>
|
|
105
|
+
</div>
|
|
106
|
+
) : null}
|
|
107
|
+
|
|
108
|
+
<button className="button button-primary auth-submit" disabled={pending} type="submit">
|
|
109
|
+
<span>{pending ? "Please wait…" : isSignUp ? "Create account" : "Sign in"}</span>
|
|
110
|
+
<span aria-hidden="true">{pending ? "···" : "→"}</span>
|
|
111
|
+
</button>
|
|
112
|
+
</form>
|
|
113
|
+
|
|
114
|
+
<p className="auth-switch">
|
|
115
|
+
{isSignUp ? "Have an account?" : "Need an account?"}{" "}
|
|
116
|
+
<a href={isSignUp ? "/sign-in" : "/sign-up"}>{isSignUp ? "Sign in →" : "Sign up →"}</a>
|
|
117
|
+
</p>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { ResourceLinks } from "./resource-links";
|
|
3
|
+
|
|
4
|
+
interface AuthShellProps {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function AuthShell({ children }: AuthShellProps) {
|
|
9
|
+
return (
|
|
10
|
+
<main className="auth-shell">
|
|
11
|
+
<a className="auth-home-link" href="/" aria-label="Back to starter home">
|
|
12
|
+
<span>00</span>
|
|
13
|
+
<span>FARMJS / Better Auth starter</span>
|
|
14
|
+
</a>
|
|
15
|
+
|
|
16
|
+
<section className="auth-form-side">{children}</section>
|
|
17
|
+
|
|
18
|
+
<ResourceLinks className="auth-resource-links" />
|
|
19
|
+
</main>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
interface ResourceLinkAction {
|
|
2
|
+
href: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface ResourceLinksProps {
|
|
7
|
+
className: string;
|
|
8
|
+
primary?: ResourceLinkAction;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function DocsIcon() {
|
|
12
|
+
return (
|
|
13
|
+
<svg
|
|
14
|
+
className="resource-icon"
|
|
15
|
+
viewBox="0 0 24 24"
|
|
16
|
+
fill="none"
|
|
17
|
+
stroke="currentColor"
|
|
18
|
+
strokeWidth="1.8"
|
|
19
|
+
strokeLinecap="round"
|
|
20
|
+
strokeLinejoin="round"
|
|
21
|
+
aria-hidden="true"
|
|
22
|
+
focusable="false"
|
|
23
|
+
>
|
|
24
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" />
|
|
25
|
+
<path d="M14 2v6h6M8 13h8M8 17h6" />
|
|
26
|
+
</svg>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function GitHubIcon() {
|
|
31
|
+
return (
|
|
32
|
+
<svg
|
|
33
|
+
className="resource-icon"
|
|
34
|
+
viewBox="0 0 24 24"
|
|
35
|
+
fill="currentColor"
|
|
36
|
+
aria-hidden="true"
|
|
37
|
+
focusable="false"
|
|
38
|
+
>
|
|
39
|
+
<path d="M12 .7a11.5 11.5 0 0 0-3.64 22.41c.58.1.79-.25.79-.56v-2.23c-3.22.7-3.9-1.37-3.9-1.37-.52-1.34-1.28-1.69-1.28-1.69-1.05-.72.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.57-.29-5.27-1.29-5.27-5.68 0-1.26.45-2.28 1.18-3.09-.12-.29-.51-1.47.11-3.05 0 0 .96-.31 3.16 1.18A10.95 10.95 0 0 1 12 6.1c.98 0 1.95.13 2.87.39 2.2-1.49 3.16-1.18 3.16-1.18.62 1.58.23 2.76.11 3.05.74.81 1.18 1.83 1.18 3.09 0 4.4-2.71 5.38-5.29 5.67.42.36.79 1.07.79 2.16v3.27c0 .31.21.67.8.56A11.5 11.5 0 0 0 12 .7Z" />
|
|
40
|
+
</svg>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function ResourceSeparator() {
|
|
45
|
+
return (
|
|
46
|
+
<span className="resource-separator" aria-hidden="true">
|
|
47
|
+
/
|
|
48
|
+
</span>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function ResourceLinks({ className, primary }: ResourceLinksProps) {
|
|
53
|
+
return (
|
|
54
|
+
<nav className={className} aria-label="Starter resources">
|
|
55
|
+
{primary ? (
|
|
56
|
+
<span className="resource-link-item">
|
|
57
|
+
<a className="resource-link-primary" href={primary.href}>
|
|
58
|
+
{primary.label}
|
|
59
|
+
</a>
|
|
60
|
+
</span>
|
|
61
|
+
) : null}
|
|
62
|
+
<span className="resource-link-item">
|
|
63
|
+
{primary ? <ResourceSeparator /> : null}
|
|
64
|
+
<a href="https://farm.js.dev">
|
|
65
|
+
<DocsIcon />
|
|
66
|
+
<span>Docs</span>
|
|
67
|
+
</a>
|
|
68
|
+
</span>
|
|
69
|
+
<span className="resource-link-item">
|
|
70
|
+
<ResourceSeparator />
|
|
71
|
+
<a href="https://github.com/farming-labs/farm.js">
|
|
72
|
+
<GitHubIcon />
|
|
73
|
+
<span>GitHub</span>
|
|
74
|
+
</a>
|
|
75
|
+
</span>
|
|
76
|
+
</nav>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { authClient } from "../lib/auth-client";
|
|
5
|
+
|
|
6
|
+
export function SignOutButton() {
|
|
7
|
+
const [pending, setPending] = useState(false);
|
|
8
|
+
const [error, setError] = useState<string | null>(null);
|
|
9
|
+
|
|
10
|
+
async function signOut() {
|
|
11
|
+
setPending(true);
|
|
12
|
+
setError(null);
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const response = await authClient.signOut();
|
|
16
|
+
if (response.error) {
|
|
17
|
+
setError(response.error.message ?? "Could not sign out.");
|
|
18
|
+
setPending(false);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
window.location.assign("/");
|
|
22
|
+
} catch {
|
|
23
|
+
setError("Could not reach the authentication service.");
|
|
24
|
+
setPending(false);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="sign-out-control">
|
|
30
|
+
<button
|
|
31
|
+
className="button button-secondary"
|
|
32
|
+
disabled={pending}
|
|
33
|
+
onClick={signOut}
|
|
34
|
+
type="button"
|
|
35
|
+
>
|
|
36
|
+
{pending ? "Signing out…" : "Sign out"}
|
|
37
|
+
</button>
|
|
38
|
+
{error ? (
|
|
39
|
+
<span className="inline-error" role="alert">
|
|
40
|
+
{error}
|
|
41
|
+
</span>
|
|
42
|
+
) : null}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
interface HeaderUser {
|
|
2
|
+
email: string;
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface SiteHeaderProps {
|
|
7
|
+
user: HeaderUser | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function SiteHeader({ user }: SiteHeaderProps) {
|
|
11
|
+
return (
|
|
12
|
+
<header className="site-header">
|
|
13
|
+
<a className="brand" href="/" aria-label="FARMJS Better Auth Starter home">
|
|
14
|
+
<span className="brand-mark" aria-hidden="true">
|
|
15
|
+
F
|
|
16
|
+
</span>
|
|
17
|
+
<span>
|
|
18
|
+
<strong>FARMJS</strong>
|
|
19
|
+
<small>Better Auth Starter</small>
|
|
20
|
+
</span>
|
|
21
|
+
</a>
|
|
22
|
+
|
|
23
|
+
<nav className="header-nav" aria-label="Primary navigation">
|
|
24
|
+
<a href="https://farm.js.dev">Docs</a>
|
|
25
|
+
{user ? (
|
|
26
|
+
<a className="header-account" href="/dashboard">
|
|
27
|
+
<span className="avatar" aria-hidden="true">
|
|
28
|
+
{(user.name || user.email).slice(0, 1).toUpperCase()}
|
|
29
|
+
</span>
|
|
30
|
+
<span className="account-copy">
|
|
31
|
+
<small>Signed in</small>
|
|
32
|
+
<strong>{user.name || user.email}</strong>
|
|
33
|
+
</span>
|
|
34
|
+
</a>
|
|
35
|
+
) : (
|
|
36
|
+
<>
|
|
37
|
+
<a href="/sign-in">Sign in</a>
|
|
38
|
+
<a className="button button-small button-primary" href="/sign-up">
|
|
39
|
+
Get started
|
|
40
|
+
</a>
|
|
41
|
+
</>
|
|
42
|
+
)}
|
|
43
|
+
</nav>
|
|
44
|
+
</header>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { getMigrations } from "better-auth/db/migration";
|
|
3
|
+
import { Pool, type PoolConfig } from "pg";
|
|
4
|
+
|
|
5
|
+
const authBaseUrl = process.env.BETTER_AUTH_URL ?? "http://localhost:3000";
|
|
6
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
7
|
+
const secret = process.env.BETTER_AUTH_SECRET;
|
|
8
|
+
|
|
9
|
+
if (!databaseUrl) {
|
|
10
|
+
throw new Error("DATABASE_URL is required. Add your pooled Postgres connection string.");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!secret) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"BETTER_AUTH_SECRET is required. Copy .env.example to .env.local and generate a secret.",
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const databaseConnection = new URL(databaseUrl);
|
|
20
|
+
const enableChannelBinding = databaseConnection.searchParams.get("channel_binding") === "require";
|
|
21
|
+
|
|
22
|
+
if (databaseConnection.searchParams.get("sslmode") === "require") {
|
|
23
|
+
databaseConnection.searchParams.set("sslmode", "verify-full");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const poolConfig: PoolConfig & { enableChannelBinding: boolean } = {
|
|
27
|
+
connectionString: databaseConnection.toString(),
|
|
28
|
+
enableChannelBinding,
|
|
29
|
+
max: process.env.NODE_ENV === "production" ? 5 : 2,
|
|
30
|
+
idleTimeoutMillis: 30_000,
|
|
31
|
+
connectionTimeoutMillis: 10_000,
|
|
32
|
+
allowExitOnIdle: true,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const database = new Pool(poolConfig);
|
|
36
|
+
|
|
37
|
+
export const auth = betterAuth({
|
|
38
|
+
appName: "FARMJS Better Auth Starter",
|
|
39
|
+
baseURL: authBaseUrl,
|
|
40
|
+
database,
|
|
41
|
+
emailAndPassword: {
|
|
42
|
+
enabled: true,
|
|
43
|
+
minPasswordLength: 8,
|
|
44
|
+
},
|
|
45
|
+
secret,
|
|
46
|
+
trustedOrigins: [new URL(authBaseUrl).origin],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const migrations = await getMigrations(auth.options);
|
|
50
|
+
await migrations.runMigrations();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["src", "farm.config.ts"]
|
|
20
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# FARMJS React Compiler Starter
|
|
2
|
+
|
|
3
|
+
A focused experimental starter for FARMJS's React AOT compiler. It uses the same minimal dark shell
|
|
4
|
+
as the other Create FARMJS App starters and includes one live comparison: an eligible local state
|
|
5
|
+
update beside ordinary React reconciliation.
|
|
6
|
+
|
|
7
|
+
> The compiler is experimental. Unsupported component shapes stay on React, so the optimization is
|
|
8
|
+
> never required for correctness.
|
|
9
|
+
|
|
10
|
+
## Create this starter
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm create @farm.js/app@beta my-compiler-app --template react-compiler --typescript
|
|
14
|
+
cd my-compiler-app
|
|
15
|
+
pnpm dev
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
You can also clone the standalone
|
|
19
|
+
[farmjs-react-compiler-starter](https://github.com/farming-labs/farmjs-react-compiler-starter).
|
|
20
|
+
|
|
21
|
+
## Compiler configuration
|
|
22
|
+
|
|
23
|
+
The starter enables the compiler through the React renderer:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
renderer: react({
|
|
27
|
+
experimental: {
|
|
28
|
+
compiler: true,
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The included environment switch defaults to enabled and makes comparison builds easy:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
FARM_REACT_COMPILER=true pnpm build
|
|
37
|
+
FARM_REACT_COMPILER=false pnpm build
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The status shown on the starter page reflects the resolved value used for that build.
|
|
41
|
+
|
|
42
|
+
Components are considered automatically. Add `"use no compiler"` to a component when you need an
|
|
43
|
+
explicit React-only path.
|
|
44
|
+
|
|
45
|
+
## Verify the compiled path
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pnpm install
|
|
49
|
+
pnpm exec playwright install chromium
|
|
50
|
+
pnpm experiment
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The command builds the app, opens the production output in Chromium, checks both counters, verifies
|
|
54
|
+
that the compiled update does not execute the component again, and saves a screenshot to
|
|
55
|
+
`/tmp/farm-react-compiler-starter.png`.
|
|
56
|
+
|
|
57
|
+
For batching, multiple bindings, safe keyed-list fallback, and the full compiler-on/off benchmark,
|
|
58
|
+
see the maintained
|
|
59
|
+
[React Compiler example](https://github.com/farming-labs/farm.js/tree/main/examples/react-compiler).
|
|
60
|
+
|
|
61
|
+
Read the [React compiler guide](https://farm.js.dev/docs/renderers/react#experimental-aot-compiler)
|
|
62
|
+
for the supported component contract and rollout guidance.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
2
|
+
|
|
3
|
+
// Site-wide docs configuration, powered by the @farming-labs/farmjs framework.
|
|
4
|
+
// Farm auto-detects the installed adapter (because farm.config.ts sets
|
|
5
|
+
// `docs: { enabled: true }`), loads this file, and serves Markdown from
|
|
6
|
+
// src/app/docs.
|
|
7
|
+
//
|
|
8
|
+
// This typed config takes priority over docs.json. Keep docs.json in sync as
|
|
9
|
+
// the language-agnostic manifest: it is the fallback for frameworkless / non
|
|
10
|
+
// JS-TS setups and the source the docs cloud reads.
|
|
11
|
+
export default defineDocs({
|
|
12
|
+
entry: "docs",
|
|
13
|
+
docsPath: "/docs",
|
|
14
|
+
metadata: {
|
|
15
|
+
description: "Documentation for your Farm.js app.",
|
|
16
|
+
},
|
|
17
|
+
nav: {
|
|
18
|
+
title: "Farm App",
|
|
19
|
+
url: "/",
|
|
20
|
+
},
|
|
21
|
+
search: {
|
|
22
|
+
provider: "simple",
|
|
23
|
+
enabled: true,
|
|
24
|
+
maxResults: 12,
|
|
25
|
+
},
|
|
26
|
+
llmsTxt: {
|
|
27
|
+
enabled: true,
|
|
28
|
+
siteTitle: "Farm App Docs",
|
|
29
|
+
siteDescription: "Documentation for your Farm.js app.",
|
|
30
|
+
},
|
|
31
|
+
sitemap: true,
|
|
32
|
+
robots: true,
|
|
33
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://docs.farming-labs.dev/schema/docs.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"entry": "docs",
|
|
5
|
+
"docsPath": "/docs",
|
|
6
|
+
"docs": {
|
|
7
|
+
"mode": "framework",
|
|
8
|
+
"runtime": "farm",
|
|
9
|
+
"root": "."
|
|
10
|
+
},
|
|
11
|
+
"content": {
|
|
12
|
+
"docsRoot": "src/app/docs"
|
|
13
|
+
},
|
|
14
|
+
"site": {
|
|
15
|
+
"name": "Farm App Docs"
|
|
16
|
+
},
|
|
17
|
+
"metadata": {
|
|
18
|
+
"description": "Documentation for your Farm.js app."
|
|
19
|
+
},
|
|
20
|
+
"nav": {
|
|
21
|
+
"title": "Farm App",
|
|
22
|
+
"url": "/"
|
|
23
|
+
},
|
|
24
|
+
"search": {
|
|
25
|
+
"provider": "simple",
|
|
26
|
+
"enabled": true,
|
|
27
|
+
"maxResults": 12
|
|
28
|
+
},
|
|
29
|
+
"llmsTxt": {
|
|
30
|
+
"enabled": true,
|
|
31
|
+
"siteTitle": "Farm App Docs",
|
|
32
|
+
"siteDescription": "Documentation for your Farm.js app."
|
|
33
|
+
},
|
|
34
|
+
"sitemap": true,
|
|
35
|
+
"robots": true
|
|
36
|
+
}
|