@farm.js/create-app 0.1.0-beta.3 → 0.1.0-beta.31

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 (111) hide show
  1. package/README.md +31 -4
  2. package/bin/create-farm-app.js +7 -1
  3. package/dist/index.js +2893 -19
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +2889 -18
  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/_renderers/preact/farm.config.ts +12 -0
  13. package/templates/_renderers/preact/package.json +27 -0
  14. package/templates/_renderers/preact/src/app/api/greeting/route.ts +15 -0
  15. package/templates/_renderers/preact/src/app/layout.tsx +16 -0
  16. package/templates/_renderers/preact/src/app/page.tsx +66 -0
  17. package/templates/_renderers/preact/src/app/preact.css +26 -0
  18. package/templates/_renderers/preact/src/components/resource-links.tsx +71 -0
  19. package/templates/_renderers/preact/src/lib/api-client.ts +4 -0
  20. package/templates/_renderers/preact/src/lib/api.generated.ts +7 -0
  21. package/templates/_renderers/preact/tsconfig.json +25 -0
  22. package/templates/_renderers/solid/farm.config.ts +12 -0
  23. package/templates/_renderers/solid/package.json +26 -0
  24. package/templates/_renderers/solid/src/app/api/greeting/route.ts +15 -0
  25. package/templates/_renderers/solid/src/app/layout.tsx +15 -0
  26. package/templates/_renderers/solid/src/app/page.tsx +56 -0
  27. package/templates/_renderers/solid/src/app/solid.css +22 -0
  28. package/templates/_renderers/solid/src/components/resource-links.tsx +71 -0
  29. package/templates/_renderers/solid/src/lib/api-client.ts +4 -0
  30. package/templates/_renderers/solid/src/lib/api.generated.ts +7 -0
  31. package/templates/_renderers/solid/tsconfig.json +25 -0
  32. package/templates/_renderers/svelte/farm.config.ts +9 -0
  33. package/templates/_renderers/svelte/package.json +28 -0
  34. package/templates/_renderers/svelte/src/app/api/greeting/route.ts +15 -0
  35. package/templates/_renderers/svelte/src/app/layout.svelte +21 -0
  36. package/templates/_renderers/svelte/src/app/page.svelte +64 -0
  37. package/templates/_renderers/svelte/src/app/svelte.css +26 -0
  38. package/templates/_renderers/svelte/src/components/resource-links.svelte +31 -0
  39. package/templates/_renderers/svelte/src/lib/api-client.ts +4 -0
  40. package/templates/_renderers/svelte/src/lib/api.generated.ts +7 -0
  41. package/templates/_renderers/svelte/tsconfig.json +23 -0
  42. package/templates/_renderers/vue/farm.config.ts +12 -0
  43. package/templates/_renderers/vue/package.json +27 -0
  44. package/templates/_renderers/vue/src/app/api/greeting/route.ts +15 -0
  45. package/templates/_renderers/vue/src/app/layout.vue +21 -0
  46. package/templates/_renderers/vue/src/app/page.vue +69 -0
  47. package/templates/_renderers/vue/src/app/vue.css +26 -0
  48. package/templates/_renderers/vue/src/components/resource-links.vue +33 -0
  49. package/templates/_renderers/vue/src/env.d.ts +7 -0
  50. package/templates/_renderers/vue/src/lib/api-client.ts +4 -0
  51. package/templates/_renderers/vue/src/lib/api.generated.ts +7 -0
  52. package/templates/_renderers/vue/tsconfig.json +23 -0
  53. package/templates/auth/.env.example +6 -0
  54. package/templates/auth/README.md +95 -0
  55. package/templates/auth/farm.config.ts +14 -0
  56. package/templates/auth/gitignore +10 -0
  57. package/templates/auth/package.json +32 -0
  58. package/templates/auth/pnpm-workspace.yaml +13 -0
  59. package/templates/auth/src/app/dashboard/middleware.ts +13 -0
  60. package/templates/auth/src/app/dashboard/page.tsx +140 -0
  61. package/templates/auth/src/app/error.tsx +25 -0
  62. package/templates/auth/src/app/globals.css +1064 -0
  63. package/templates/auth/src/app/layout.tsx +11 -0
  64. package/templates/auth/src/app/loading.tsx +15 -0
  65. package/templates/auth/src/app/not-found.tsx +26 -0
  66. package/templates/auth/src/app/page.tsx +46 -0
  67. package/templates/auth/src/app/sign-in/page.tsx +16 -0
  68. package/templates/auth/src/app/sign-up/page.tsx +16 -0
  69. package/templates/auth/src/components/auth-form.tsx +120 -0
  70. package/templates/auth/src/components/auth-shell.tsx +21 -0
  71. package/templates/auth/src/components/resource-links.tsx +78 -0
  72. package/templates/auth/src/components/sign-out-button.tsx +45 -0
  73. package/templates/auth/src/components/site-header.tsx +46 -0
  74. package/templates/auth/tsconfig.json +20 -0
  75. package/templates/basic/farm.config.ts +3 -1
  76. package/templates/basic/gitignore +23 -0
  77. package/templates/basic/package.json +4 -2
  78. package/templates/basic/pnpm-workspace.yaml +8 -0
  79. package/templates/basic/public/favicon.svg +13 -0
  80. package/templates/basic/src/app/globals.css +241 -0
  81. package/templates/basic/src/app/layout.tsx +6 -5
  82. package/templates/basic/src/app/page.tsx +20 -61
  83. package/templates/basic/src/components/resource-links.tsx +78 -0
  84. package/templates/basic/tsconfig.json +4 -0
  85. package/templates/better-auth/.env.example +3 -0
  86. package/templates/better-auth/README.md +87 -0
  87. package/templates/better-auth/farm.config.ts +32 -0
  88. package/templates/better-auth/gitignore +10 -0
  89. package/templates/better-auth/package.json +34 -0
  90. package/templates/better-auth/pnpm-workspace.yaml +13 -0
  91. package/templates/better-auth/src/app/dashboard/middleware.ts +15 -0
  92. package/templates/better-auth/src/app/dashboard/page.tsx +188 -0
  93. package/templates/better-auth/src/app/error.tsx +25 -0
  94. package/templates/better-auth/src/app/globals.css +1064 -0
  95. package/templates/better-auth/src/app/layout.tsx +11 -0
  96. package/templates/better-auth/src/app/loading.tsx +15 -0
  97. package/templates/better-auth/src/app/not-found.tsx +26 -0
  98. package/templates/better-auth/src/app/page.tsx +50 -0
  99. package/templates/better-auth/src/app/sign-in/page.tsx +16 -0
  100. package/templates/better-auth/src/app/sign-up/page.tsx +16 -0
  101. package/templates/better-auth/src/components/auth-form.tsx +120 -0
  102. package/templates/better-auth/src/components/auth-shell.tsx +21 -0
  103. package/templates/better-auth/src/components/resource-links.tsx +78 -0
  104. package/templates/better-auth/src/components/sign-out-button.tsx +45 -0
  105. package/templates/better-auth/src/components/site-header.tsx +46 -0
  106. package/templates/better-auth/src/lib/auth-client.ts +5 -0
  107. package/templates/better-auth/src/lib/auth.ts +50 -0
  108. package/templates/better-auth/src/lib/session.ts +8 -0
  109. package/templates/better-auth/tsconfig.json +20 -0
  110. package/templates/basic/src/app/about/page.tsx +0 -32
  111. package/templates/basic/src/farm-images.d.ts +0 -59
@@ -0,0 +1,4 @@
1
+ import { createAPIClient } from "@farm.js/core/client";
2
+ import type { APIRouter } from "./api.generated";
3
+
4
+ export const api = createAPIClient<APIRouter>();
@@ -0,0 +1,7 @@
1
+ import type { POST as POST_greeting } from "../app/api/greeting/route";
2
+
3
+ export type APIRouter = {
4
+ greeting: {
5
+ post: typeof POST_greeting;
6
+ };
7
+ };
@@ -0,0 +1,23 @@
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
+ "baseUrl": ".",
10
+ "paths": {
11
+ "@/*": ["./src/*"]
12
+ },
13
+ "allowImportingTsExtensions": true,
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "strict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "noFallthroughCasesInSwitch": true
21
+ },
22
+ "include": ["src/**/*.ts", "src/**/*.svelte", "farm.config.ts"]
23
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "@farm.js/core/config";
2
+ import { vue } from "@farm.js/vue";
3
+
4
+ export default defineConfig({
5
+ renderer: vue(),
6
+ theme: {
7
+ default: "dark",
8
+ },
9
+ deploy: {
10
+ target: "vercel",
11
+ },
12
+ });
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "farm-app",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "farm dev",
7
+ "build": "farm build",
8
+ "deploy": "farm deploy",
9
+ "start": "farm start",
10
+ "type-check": "vue-tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@farm.js/core": "0.1.0-beta.31",
14
+ "@farm.js/vue": "0.1.0-beta.26",
15
+ "@fontsource-variable/geist": "5.3.0",
16
+ "@fontsource-variable/geist-mono": "5.3.0",
17
+ "vue": "3.5.41",
18
+ "zod": "4.1.12"
19
+ },
20
+ "devDependencies": {
21
+ "@farm.js/cli": "0.1.0-beta.31",
22
+ "tailwindcss": "^4.0.0",
23
+ "typescript": "^5.3.3",
24
+ "vue-tsc": "3.3.9"
25
+ },
26
+ "packageManager": "pnpm@8.12.1"
27
+ }
@@ -0,0 +1,15 @@
1
+ import { createEndpoint } from "@farm.js/core";
2
+ import { z } from "zod";
3
+
4
+ export const POST = createEndpoint(
5
+ "/api/greeting",
6
+ {
7
+ method: "POST",
8
+ body: z.object({
9
+ name: z.string().trim().min(1).max(40),
10
+ }),
11
+ },
12
+ async ({ body }) => ({
13
+ message: `Hello, ${body.name}, from the FARMJS server.`,
14
+ }),
15
+ );
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { Metadata } from "@farm.js/core";
3
+ import "./globals.css";
4
+ import "./vue.css";
5
+
6
+ export const metadata: Metadata = {
7
+ title: "FARMJS Vue App",
8
+ description: "A framework for product-integrated apps",
9
+ icons: {
10
+ icon: [{ url: "/favicon.svg", type: "image/svg+xml", sizes: "any" }],
11
+ },
12
+ };
13
+ </script>
14
+
15
+ <script setup lang="ts">
16
+ defineOptions({ inheritAttrs: false });
17
+ </script>
18
+
19
+ <template>
20
+ <slot />
21
+ </template>
@@ -0,0 +1,69 @@
1
+ <script lang="ts">
2
+ export const hydrate = true;
3
+ </script>
4
+
5
+ <script setup lang="ts">
6
+ import { ref } from "vue";
7
+ import ResourceLinks from "../components/resource-links.vue";
8
+ import { api } from "../lib/api-client";
9
+
10
+ defineOptions({ inheritAttrs: false });
11
+
12
+ const message = ref("Call a typed server function");
13
+ const pending = ref(false);
14
+ const failed = ref(false);
15
+
16
+ async function callServer() {
17
+ pending.value = true;
18
+ failed.value = false;
19
+ try {
20
+ const result = await api.greeting.post({
21
+ body: { name: "Vue" },
22
+ });
23
+ if (result.error) throw result.error;
24
+ if (!result.data) throw new Error("The server returned no greeting");
25
+ message.value = result.data.message;
26
+ } catch {
27
+ failed.value = true;
28
+ message.value = "Try the server call again";
29
+ } finally {
30
+ pending.value = false;
31
+ }
32
+ }
33
+ </script>
34
+
35
+ <template>
36
+ <main class="landing-main">
37
+ <section class="hero-section">
38
+ <div class="hero-copy">
39
+ <div class="eyebrow-row">
40
+ <span>00</span>
41
+ <span>FARMJS / Vue starter</span>
42
+ </div>
43
+
44
+ <h1>Edit <code>page.vue</code> to begin.</h1>
45
+
46
+ <div class="command-list" aria-label="Getting started">
47
+ <div class="command-row">
48
+ <span>01</span>
49
+ <code>pnpm dev</code>
50
+ </div>
51
+ <div class="command-row">
52
+ <span>02</span>
53
+ <button
54
+ type="button"
55
+ class="server-call"
56
+ :class="{ 'server-call-error': failed }"
57
+ :disabled="pending"
58
+ @click="callServer"
59
+ >
60
+ {{ pending ? "Calling…" : message }}
61
+ </button>
62
+ </div>
63
+ </div>
64
+
65
+ <ResourceLinks class="resource-links" />
66
+ </div>
67
+ </section>
68
+ </main>
69
+ </template>
@@ -0,0 +1,26 @@
1
+ .server-call {
2
+ appearance: none;
3
+ width: fit-content;
4
+ border: 0;
5
+ padding: 0;
6
+ color: var(--ink);
7
+ background: transparent;
8
+ cursor: pointer;
9
+ font-family: var(--font-mono);
10
+ font-size: 0.75rem;
11
+ text-align: left;
12
+ }
13
+
14
+ .server-call:hover:not(:disabled) {
15
+ text-decoration: underline;
16
+ text-underline-offset: 0.25rem;
17
+ }
18
+
19
+ .server-call:disabled {
20
+ color: var(--ink-soft);
21
+ cursor: wait;
22
+ }
23
+
24
+ .server-call-error {
25
+ color: #fca5a5;
26
+ }
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <nav aria-label="Starter resources">
3
+ <span class="resource-link-item">
4
+ <a href="https://farm.js.dev">
5
+ <svg
6
+ class="resource-icon"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="1.8"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ aria-hidden="true"
14
+ >
15
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" />
16
+ <path d="M14 2v6h6M8 13h8M8 17h6" />
17
+ </svg>
18
+ <span>Docs</span>
19
+ </a>
20
+ </span>
21
+ <span class="resource-link-item">
22
+ <span class="resource-separator" aria-hidden="true">/</span>
23
+ <a href="https://github.com/farming-labs/farm.js">
24
+ <svg class="resource-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
25
+ <path
26
+ 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"
27
+ />
28
+ </svg>
29
+ <span>GitHub</span>
30
+ </a>
31
+ </span>
32
+ </nav>
33
+ </template>
@@ -0,0 +1,7 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ declare module "*.vue" {
4
+ import type { DefineComponent } from "vue";
5
+ const component: DefineComponent<Record<string, never>, Record<string, never>, unknown>;
6
+ export default component;
7
+ }
@@ -0,0 +1,4 @@
1
+ import { createAPIClient } from "@farm.js/core/client";
2
+ import type { APIRouter } from "./api.generated";
3
+
4
+ export const api = createAPIClient<APIRouter>();
@@ -0,0 +1,7 @@
1
+ import type { POST as POST_greeting } from "../app/api/greeting/route";
2
+
3
+ export type APIRouter = {
4
+ greeting: {
5
+ post: typeof POST_greeting;
6
+ };
7
+ };
@@ -0,0 +1,23 @@
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
+ "baseUrl": ".",
10
+ "paths": {
11
+ "@/*": ["./src/*"]
12
+ },
13
+ "allowImportingTsExtensions": true,
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "strict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "noFallthroughCasesInSwitch": true
21
+ },
22
+ "include": ["src/**/*.ts", "src/**/*.vue", "farm.config.ts"]
23
+ }
@@ -0,0 +1,6 @@
1
+ # FARMJS Auth needs no environment variables in local development.
2
+ # Configure these values in the production deployment environment.
3
+
4
+ # FARM_AUTH_URL=https://your-app.example.com
5
+ # FARM_AUTH_SECRET=replace-with-a-random-secret-at-least-32-characters
6
+ # DATABASE_URL=postgresql://user:password@your-postgres-host/database?sslmode=require
@@ -0,0 +1,95 @@
1
+ # FARMJS Auth Starter
2
+
3
+ A standalone email/password authentication starter using FARMJS built-in authentication and
4
+ React.
5
+
6
+ This project was generated from the FARMJS Auth template included with `@farm.js/create-app`.
7
+
8
+ ## Included
9
+
10
+ - email/password sign-up and sign-in enabled by `auth: true`
11
+ - database-backed, HTTP-only session cookies
12
+ - a server-middleware-protected `/dashboard`
13
+ - `@farm.js/auth/server` request helpers and the `@farm.js/auth/client` React hook
14
+ - automatic local SQLite storage in `.farm/auth.sqlite`
15
+ - production Postgres support through `DATABASE_URL`
16
+ - a dark-first, one-page welcome screen set in Geist Sans and Geist Mono
17
+ - pending, error, unauthorized, loading, and not-found states
18
+ - exact FARMJS beta dependencies for reproducible installs
19
+
20
+ ## Quick start
21
+
22
+ ```bash
23
+ pnpm create @farm.js/app@beta my-app --template auth --typescript
24
+ cd my-app
25
+ pnpm dev
26
+ ```
27
+
28
+ Open [http://localhost:3000](http://localhost:3000), create an account, and continue to the
29
+ protected dashboard. Local development needs no database or auth environment variables; FARMJS Auth
30
+ creates and migrates `.farm/auth.sqlite` when auth is first used.
31
+
32
+ ## How it is wired
33
+
34
+ - [`farm.config.ts`](./farm.config.ts) enables email/password authentication with `auth: true`.
35
+ - [`src/app/page.tsx`](./src/app/page.tsx) reads the current session with
36
+ `auth.session()` from `@farm.js/auth/server`.
37
+ - [`src/components/auth-form.tsx`](./src/components/auth-form.tsx) uses the built-in `signIn` and
38
+ `signUp` client functions.
39
+ - [`src/app/dashboard/middleware.ts`](./src/app/dashboard/middleware.ts) redirects anonymous
40
+ requests before the dashboard route runs.
41
+ - [`src/app/dashboard/page.tsx`](./src/app/dashboard/page.tsx) uses the built-in `useAuth` React
42
+ hook to render the current account and session.
43
+
44
+ FARMJS owns the auth action routes, so the starter does not need an app-local Better Auth instance,
45
+ client factory, database pool, or catch-all API route:
46
+
47
+ ```text
48
+ /api/auth/*
49
+ ```
50
+
51
+ ## Production environment
52
+
53
+ Local development uses safe defaults. Production requires these deployment environment variables:
54
+
55
+ | Variable | Purpose |
56
+ | ------------------ | ----------------------------------------------------------- |
57
+ | `FARM_AUTH_URL` | Public application origin; inferred automatically on Vercel |
58
+ | `FARM_AUTH_SECRET` | Random secret used to sign and encrypt auth data |
59
+ | `DATABASE_URL` | Postgres connection string |
60
+
61
+ Generate a production secret with:
62
+
63
+ ```bash
64
+ openssl rand -base64 32
65
+ ```
66
+
67
+ Loading `farm.config.ts` and running `farm build` do not connect to the database. Apply the
68
+ production auth schema before serving traffic:
69
+
70
+ ```bash
71
+ pnpm auth:migrate
72
+ ```
73
+
74
+ The FARMJS deployment target is configured in [`farm.config.ts`](./farm.config.ts) for Vercel.
75
+
76
+ ## Advanced authentication
77
+
78
+ `auth: true` intentionally owns the common email/password path. If an application needs Better Auth
79
+ plugins, adapters, social providers, or callbacks, replace the top-level auth option with an
80
+ app-owned `integrations.auth` configuration using `@farm.js/better-auth`.
81
+
82
+ ## Commands
83
+
84
+ ```bash
85
+ pnpm dev # start the development server
86
+ pnpm type-check # run TypeScript checks
87
+ pnpm build # create the production build without connecting to the database
88
+ pnpm check # type-check and build
89
+ pnpm auth:migrate # apply the production auth schema
90
+ pnpm run deploy -- --prod
91
+ ```
92
+
93
+ ## License
94
+
95
+ MIT
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from "@farm.js/core";
2
+
3
+ export default defineConfig({
4
+ auth: true,
5
+ theme: {
6
+ default: "dark",
7
+ },
8
+ experimental: {
9
+ serverComponents: true,
10
+ },
11
+ deploy: {
12
+ target: "vercel",
13
+ },
14
+ });
@@ -0,0 +1,10 @@
1
+ node_modules
2
+ .farm
3
+ .vercel
4
+ .data
5
+ .env
6
+ .env.local
7
+ .env.*.local
8
+ dist
9
+ *.log
10
+ .DS_Store
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "farmjs-auth-starter",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "farm dev",
7
+ "build": "farm build",
8
+ "auth:migrate": "farm auth migrate",
9
+ "check": "pnpm type-check && pnpm build",
10
+ "deploy": "farm deploy",
11
+ "start": "farm start",
12
+ "type-check": "tsc --noEmit"
13
+ },
14
+ "dependencies": {
15
+ "@farm.js/auth": "0.1.0-beta.31",
16
+ "@farm.js/core": "0.1.0-beta.31",
17
+ "@fontsource-variable/geist": "5.3.0",
18
+ "@fontsource-variable/geist-mono": "5.3.0",
19
+ "better-call": "1.3.2",
20
+ "react": "^19.0.0",
21
+ "react-dom": "^19.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "@farm.js/cli": "0.1.0-beta.31",
25
+ "@types/node": "20.19.9",
26
+ "@types/react": "^19.0.0",
27
+ "@types/react-dom": "^19.0.0",
28
+ "tailwindcss": "^4.0.0",
29
+ "typescript": "^5.3.3"
30
+ },
31
+ "packageManager": "pnpm@11.18.0"
32
+ }
@@ -0,0 +1,13 @@
1
+ packages:
2
+ - "."
3
+
4
+ allowBuilds:
5
+ "@prisma/client": true
6
+ better-sqlite3: true
7
+ esbuild: true
8
+ sharp: true
9
+ vue-demi: true
10
+ minimumReleaseAgeExclude:
11
+ - "@farm.js/auth"
12
+ - "@farm.js/cli"
13
+ - "@farm.js/core"
@@ -0,0 +1,13 @@
1
+ import { auth } from "@farm.js/auth/server";
2
+
3
+ export const config = {
4
+ runtime: "nodejs" as const,
5
+ };
6
+
7
+ export async function middleware(request: Request) {
8
+ const user = await auth.user({ request });
9
+
10
+ if (!user) {
11
+ return Response.redirect(new URL("/sign-in", request.url), 307);
12
+ }
13
+ }
@@ -0,0 +1,140 @@
1
+ "use client";
2
+
3
+ import { useAuth } from "@farm.js/auth/client";
4
+ import { useEffect } from "react";
5
+ import { SignOutButton } from "../../components/sign-out-button";
6
+ import { SiteHeader } from "../../components/site-header";
7
+
8
+ export default function DashboardPage() {
9
+ const { isPending, session, user } = useAuth();
10
+
11
+ useEffect(() => {
12
+ if (!isPending && !user) {
13
+ window.location.replace("/sign-in");
14
+ }
15
+ }, [isPending, user]);
16
+
17
+ if (isPending) {
18
+ return (
19
+ <main className="loading-shell" aria-label="Loading account" aria-live="polite">
20
+ <div className="loading-block">
21
+ <span className="loading-label">Verifying protected session</span>
22
+ <div className="loading-line wide" />
23
+ <div className="loading-line" />
24
+ <div className="loading-grid">
25
+ <div />
26
+ <div />
27
+ </div>
28
+ </div>
29
+ </main>
30
+ );
31
+ }
32
+
33
+ if (!user || !session) {
34
+ return (
35
+ <main className="loading-shell" aria-label="Returning to sign in" aria-live="polite">
36
+ <div className="loading-block">
37
+ <span className="loading-label">Session unavailable</span>
38
+ <p className="loading-message">Returning you to the sign-in page…</p>
39
+ </div>
40
+ </main>
41
+ );
42
+ }
43
+
44
+ const createdAt = new Date(user.createdAt).toLocaleDateString("en", {
45
+ day: "numeric",
46
+ month: "short",
47
+ year: "numeric",
48
+ });
49
+ const expiresAt = new Date(session.expiresAt).toLocaleString("en", {
50
+ day: "numeric",
51
+ hour: "2-digit",
52
+ minute: "2-digit",
53
+ month: "short",
54
+ });
55
+
56
+ return (
57
+ <div className="site-frame">
58
+ <SiteHeader user={user} />
59
+
60
+ <main className="dashboard-shell">
61
+ <div className="dashboard-heading">
62
+ <div>
63
+ <span className="section-index">PROTECTED / MIDDLEWARE VERIFIED</span>
64
+ <h1>Welcome back, {user.name}.</h1>
65
+ <p>Farm route middleware verified your session before this dashboard loaded.</p>
66
+ </div>
67
+ <SignOutButton />
68
+ </div>
69
+
70
+ <section className="dashboard-grid" aria-label="Account overview">
71
+ <article className="account-panel">
72
+ <div className="panel-heading">
73
+ <span>Account</span>
74
+ <span className="status-badge">
75
+ <span className="status-dot" aria-hidden="true" />
76
+ Active
77
+ </span>
78
+ </div>
79
+ <dl className="detail-list">
80
+ <div>
81
+ <dt>Name</dt>
82
+ <dd>{user.name}</dd>
83
+ </div>
84
+ <div>
85
+ <dt>Email</dt>
86
+ <dd>{user.email}</dd>
87
+ </div>
88
+ <div>
89
+ <dt>Created</dt>
90
+ <dd>{createdAt}</dd>
91
+ </div>
92
+ </dl>
93
+ </article>
94
+
95
+ <article className="session-panel">
96
+ <div className="panel-heading">
97
+ <span>Current session</span>
98
+ <code>httpOnly cookie</code>
99
+ </div>
100
+ <dl className="detail-list">
101
+ <div>
102
+ <dt>Session ID</dt>
103
+ <dd className="mono truncate">{session.id}</dd>
104
+ </div>
105
+ <div>
106
+ <dt>Expires</dt>
107
+ <dd>{expiresAt}</dd>
108
+ </div>
109
+ <div>
110
+ <dt>Storage</dt>
111
+ <dd>SQLite locally / Postgres in production</dd>
112
+ </div>
113
+ </dl>
114
+ </article>
115
+ </section>
116
+
117
+ <section className="next-steps" aria-labelledby="next-steps-title">
118
+ <div className="section-heading compact">
119
+ <span className="section-index">NEXT / MAKE IT YOURS</span>
120
+ <h2 id="next-steps-title">The account boundary is ready for product work.</h2>
121
+ </div>
122
+ <div className="next-step-list">
123
+ <div>
124
+ <span>01</span>
125
+ <p>Set the production FARMJS Auth URL, secret, and database URL in your host.</p>
126
+ </div>
127
+ <div>
128
+ <span>02</span>
129
+ <p>Run the FARMJS Auth migration command before serving production traffic.</p>
130
+ </div>
131
+ <div>
132
+ <span>03</span>
133
+ <p>Build your first protected feature beside this dashboard route.</p>
134
+ </div>
135
+ </div>
136
+ </section>
137
+ </main>
138
+ </div>
139
+ );
140
+ }
@@ -0,0 +1,25 @@
1
+ "use client";
2
+
3
+ import type { ErrorProps } from "@farm.js/core";
4
+
5
+ export default function ErrorBoundary({ error, reset }: ErrorProps) {
6
+ const message = error instanceof Error ? error.message : "An unexpected error occurred.";
7
+
8
+ return (
9
+ <main className="error-shell">
10
+ <section className="error-panel">
11
+ <span className="section-index">ERROR / RECOVERABLE</span>
12
+ <h1>That route did not finish loading.</h1>
13
+ <p>{message}</p>
14
+ <div className="hero-actions">
15
+ <button className="button button-primary" onClick={reset} type="button">
16
+ Try again
17
+ </button>
18
+ <a className="button button-secondary" href="/">
19
+ Return home
20
+ </a>
21
+ </div>
22
+ </section>
23
+ </main>
24
+ );
25
+ }