@pylonsync/create-pylon 0.11.0 → 0.11.1
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/package.json +1 -1
- package/templates/_root/AGENTS.md +30 -0
- package/templates/expo/mobile/apps/expo/README.md +1 -1
- package/templates/expo/mobile/apps/expo/package.json +55 -54
- package/templates/expo/mobile/apps/expo/src/entitlements.ts +24 -6
- package/templates/agency/app/not-found.tsx +0 -44
- package/templates/ai-chat/app/not-found.tsx +0 -44
- package/templates/ai-studio/app/not-found.tsx +0 -44
- package/templates/creator/app/not-found.tsx +0 -44
- package/templates/directory/app/not-found.tsx +0 -44
- package/templates/local-service/app/not-found.tsx +0 -44
- package/templates/restaurant/app/not-found.tsx +0 -44
- package/templates/shop/app/not-found.tsx +0 -44
- package/templates/waitlist/app/not-found.tsx +0 -44
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pylonsync/create-pylon",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Scaffold a new Pylon app — realtime backend + web/mobile/expo frontends in one command. Run via `npm create @pylonsync/pylon@latest`.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -75,9 +75,39 @@ this pass before the user has to ask.
|
|
|
75
75
|
| Call a function | `pylon fn <name> key=value` |
|
|
76
76
|
| Health snapshot | `pylon status` |
|
|
77
77
|
| Build for prod | `pylon build` |
|
|
78
|
+
| Wire in Stack0 Analytics or Feedback | `pylon add analytics --site-key <KEY>` \| `pylon add feedback` |
|
|
78
79
|
| Deploy (Pylon Cloud by default) | `pylon deploy` |
|
|
79
80
|
| Look up an error code | `pylon explain <CODE>` |
|
|
80
81
|
|
|
81
82
|
`--json` works on every command for machine-readable output. Prefer one-shot/agent-safe flags (`pylon logs --limit N`, not a blocking `--follow`).
|
|
82
83
|
|
|
84
|
+
## Stack0 Analytics and Feedback
|
|
85
|
+
|
|
86
|
+
Neither is a package you install. Both are hosted apps you point at over HTTP, and neither is wired into this template — run `pylon add` when the product needs one.
|
|
87
|
+
|
|
88
|
+
**Analytics** — `pylon add analytics --site-key <KEY>`. Do NOT hand-write this: the relay is boilerplate whose every mistake is silent.
|
|
89
|
+
|
|
90
|
+
- The relay is a FUNCTION, not a route. `/api/fn/*` is Pylon's own namespace, so `app/api/fn/ingestEvent/route.ts` is never matched, and there is no `next.config.js` here to rewrite in. Copying the Next.js recipe gets you a file that is never called.
|
|
91
|
+
- It must be an `action` (only actions get `ctx.request`) and it must forward `ctx.request.rawBody`, not `args` — the beacon carries fields the arg list does not declare.
|
|
92
|
+
- Skipping the relay and pointing the tag straight at the Analytics host does not error either: its CORS allowlist refuses the browser's preflight, which looks exactly like nobody visiting the site.
|
|
93
|
+
- `stack0Analytics()` is a browser global and Pylon renders on the server. Call it from an event handler or effect, never during a render.
|
|
94
|
+
|
|
95
|
+
**Feedback** — one tag in `app/layout.tsx`; `pylon add feedback` prints it. Reading a public board takes no credential (`POST /api/fn/portalView { slug }`); writing on someone's behalf takes a guest session (`POST /api/auth/guest`, then a bearer token), not an API key. There is no API key and no MCP server in Feedback.
|
|
96
|
+
|
|
97
|
+
Full API for both: https://www.stack0.dev/docs/sdk/analytics and /docs/sdk/feedback
|
|
98
|
+
|
|
99
|
+
## Declare the env you cannot run without
|
|
100
|
+
|
|
101
|
+
`requiredEnv` in `buildManifest({...})` makes `pylon deploy` refuse to ship when the project is missing a secret:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { requireEnv } from "@pylonsync/sdk";
|
|
105
|
+
|
|
106
|
+
requiredEnv: [
|
|
107
|
+
requireEnv("SITE_URL", "this app's public origin; baked into every absolute link it serves"),
|
|
108
|
+
],
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Declare the variables whose absence is SILENT — a public origin, a webhook secret, an API base URL. A missing database URL crashes on the first query and someone notices in a minute. A missing public origin gets baked into a script tag, served to a customer's website, and errors nowhere.
|
|
112
|
+
|
|
83
113
|
For full signatures, env vars, the complete CLI, and SSR/client/server-primitive details: **https://docs.pylonsync.com/llms-full.txt**.
|
|
@@ -13,7 +13,7 @@ app/(tabs)/index the Notes list (free cap enforced server-side)
|
|
|
13
13
|
app/(tabs)/settings account, subscription, legal, delete account
|
|
14
14
|
src/session.tsx boot + state machine
|
|
15
15
|
src/purchases.ts RevenueCat wrapper, safe in Expo Go
|
|
16
|
-
src/entitlements.ts usePro() from the synced
|
|
16
|
+
src/entitlements.ts usePro() from the synced rows of either store
|
|
17
17
|
src/analytics.ts funnel events; wire to your SDK in one place
|
|
18
18
|
src/links.ts privacy / terms / support URLs, served by apps/api
|
|
19
19
|
STORE.md the submission checklist
|
|
@@ -1,56 +1,57 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
2
|
+
"name": "@__APP_NAME_KEBAB__/expo",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"main": "expo-router/entry",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "expo start",
|
|
8
|
+
"dev": "expo start",
|
|
9
|
+
"ios": "expo run:ios",
|
|
10
|
+
"android": "expo run:android",
|
|
11
|
+
"web": "expo start --web",
|
|
12
|
+
"check": "tsc --noEmit",
|
|
13
|
+
"build:dev": "eas build --profile development --platform ios",
|
|
14
|
+
"build:preview": "eas build --profile preview --platform all",
|
|
15
|
+
"build:prod": "eas build --profile production --platform all",
|
|
16
|
+
"submit:ios": "eas submit --platform ios --latest",
|
|
17
|
+
"submit:android": "eas submit --platform android --latest",
|
|
18
|
+
"check:bundle": "expo export --platform ios --platform android --output-dir .expo-export"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@pylonsync/react": "^__PYLON_VERSION__",
|
|
22
|
+
"@pylonsync/react-native": "^__PYLON_VERSION__",
|
|
23
|
+
"@pylonsync/revenuecat": "^__PYLON_VERSION__",
|
|
24
|
+
"@pylonsync/sdk": "^__PYLON_VERSION__",
|
|
25
|
+
"@pylonsync/stripe": "^__PYLON_VERSION__",
|
|
26
|
+
"@pylonsync/sync": "^__PYLON_VERSION__",
|
|
27
|
+
"@react-native-async-storage/async-storage": "2.2.0",
|
|
28
|
+
"@react-native-community/netinfo": "12.0.1",
|
|
29
|
+
"@react-native-google-signin/google-signin": "^16.1.5",
|
|
30
|
+
"expo": "~57.0.20",
|
|
31
|
+
"expo-apple-authentication": "~57.0.1",
|
|
32
|
+
"expo-build-properties": "~57.0.17",
|
|
33
|
+
"expo-constants": "~57.0.17",
|
|
34
|
+
"expo-haptics": "~57.0.2",
|
|
35
|
+
"expo-linking": "~57.0.9",
|
|
36
|
+
"expo-router": "~57.0.19",
|
|
37
|
+
"expo-splash-screen": "~57.0.8",
|
|
38
|
+
"expo-status-bar": "~57.0.1",
|
|
39
|
+
"expo-web-browser": "~57.0.2",
|
|
40
|
+
"react": "19.2.3",
|
|
41
|
+
"react-dom": "19.2.3",
|
|
42
|
+
"react-native": "0.86.3",
|
|
43
|
+
"react-native-gesture-handler": "~2.32.0",
|
|
44
|
+
"react-native-purchases": "^10.9.0",
|
|
45
|
+
"react-native-reanimated": "4.5.1",
|
|
46
|
+
"react-native-safe-area-context": "~5.7.0",
|
|
47
|
+
"react-native-screens": "~4.26.0",
|
|
48
|
+
"react-native-web": "~0.21.0",
|
|
49
|
+
"react-native-worklets": "0.10.1"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@babel/core": "^7.29.0",
|
|
53
|
+
"@types/react": "~19.2.4",
|
|
54
|
+
"babel-preset-expo": "~57.0.10",
|
|
55
|
+
"typescript": "~6.0.3"
|
|
56
|
+
}
|
|
56
57
|
}
|
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
import { db } from "@pylonsync/react-native";
|
|
2
2
|
import { hasEntitlement, type RcEntitlementRow } from "@pylonsync/revenuecat/client";
|
|
3
|
+
import {
|
|
4
|
+
hasStripeSubscription,
|
|
5
|
+
type StripeSubscriptionRow,
|
|
6
|
+
} from "@pylonsync/stripe/entitlement";
|
|
3
7
|
|
|
4
8
|
export const PRO = "pro";
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
|
-
* Live Pro status from the
|
|
8
|
-
* the server writes the row after a purchase, on every device.
|
|
11
|
+
* Live Pro status, from whichever store the subscriber actually paid.
|
|
9
12
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
13
|
+
* Both are read, because a subscriber has exactly one of them: a purchase in
|
|
14
|
+
* the App Store or Play Store arrives as an RcEntitlement row, and one made
|
|
15
|
+
* on the website arrives as a StripeSubscription row. Reading only the first
|
|
16
|
+
* is how someone subscribes on the web and finds the app still locked — and
|
|
17
|
+
* cross-platform access is the reason to run both in the first place.
|
|
18
|
+
*
|
|
19
|
+
* An app using only one of the two plugins simply has no rows of the other
|
|
20
|
+
* kind, and the query costs nothing.
|
|
21
|
+
*
|
|
22
|
+
* Both plugin entry points here are the browser/React Native ones. The
|
|
23
|
+
* package roots are server code and must not be imported from the app.
|
|
12
24
|
*/
|
|
13
25
|
export function usePro(): { pro: boolean; loading: boolean } {
|
|
14
|
-
const
|
|
15
|
-
|
|
26
|
+
const rc = db.useQuery<RcEntitlementRow>("RcEntitlement", {});
|
|
27
|
+
const stripe = db.useQuery<StripeSubscriptionRow>("StripeSubscription", {});
|
|
28
|
+
|
|
29
|
+
// Access as soon as either source says so; still loading only while both
|
|
30
|
+
// are outstanding, so a subscriber is not shown a paywall mid-boot.
|
|
31
|
+
const pro =
|
|
32
|
+
hasEntitlement(rc.data ?? [], PRO) || hasStripeSubscription(stripe.data ?? []);
|
|
33
|
+
return { pro, loading: rc.loading && stripe.loading };
|
|
16
34
|
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link, type NotFoundProps } from "@pylonsync/react";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
|
|
5
|
-
// `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
|
|
6
|
-
// page calls `response.notFound()`.
|
|
7
|
-
//
|
|
8
|
-
// Give it real links. This boundary is also what an agent reads when it guesses
|
|
9
|
-
// a URL wrong: a markdown request for a missing page renders THIS file as
|
|
10
|
-
// markdown at 404, so the links below are how it finds its way back.
|
|
11
|
-
export default function NotFound(_props: NotFoundProps) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
|
|
14
|
-
<section>
|
|
15
|
-
<h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
|
|
16
|
-
<p className="mt-2 text-muted-foreground">
|
|
17
|
-
That URL does not exist. Try one of these:
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
<ul className="space-y-2 text-sm">
|
|
21
|
-
<li>
|
|
22
|
-
<Link className="underline underline-offset-4" href="/">
|
|
23
|
-
Home
|
|
24
|
-
</Link>
|
|
25
|
-
</li>
|
|
26
|
-
<li>
|
|
27
|
-
<a className="underline underline-offset-4" href="/sitemap.xml">
|
|
28
|
-
Sitemap
|
|
29
|
-
</a>
|
|
30
|
-
</li>
|
|
31
|
-
<li>
|
|
32
|
-
<a className="underline underline-offset-4" href="/llms.txt">
|
|
33
|
-
llms.txt
|
|
34
|
-
</a>
|
|
35
|
-
</li>
|
|
36
|
-
</ul>
|
|
37
|
-
<div>
|
|
38
|
-
<Button asChild>
|
|
39
|
-
<Link href="/">Back to home</Link>
|
|
40
|
-
</Button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link, type NotFoundProps } from "@pylonsync/react";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
|
|
5
|
-
// `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
|
|
6
|
-
// page calls `response.notFound()`.
|
|
7
|
-
//
|
|
8
|
-
// Give it real links. This boundary is also what an agent reads when it guesses
|
|
9
|
-
// a URL wrong: a markdown request for a missing page renders THIS file as
|
|
10
|
-
// markdown at 404, so the links below are how it finds its way back.
|
|
11
|
-
export default function NotFound(_props: NotFoundProps) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
|
|
14
|
-
<section>
|
|
15
|
-
<h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
|
|
16
|
-
<p className="mt-2 text-muted-foreground">
|
|
17
|
-
That URL does not exist. Try one of these:
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
<ul className="space-y-2 text-sm">
|
|
21
|
-
<li>
|
|
22
|
-
<Link className="underline underline-offset-4" href="/">
|
|
23
|
-
Home
|
|
24
|
-
</Link>
|
|
25
|
-
</li>
|
|
26
|
-
<li>
|
|
27
|
-
<a className="underline underline-offset-4" href="/sitemap.xml">
|
|
28
|
-
Sitemap
|
|
29
|
-
</a>
|
|
30
|
-
</li>
|
|
31
|
-
<li>
|
|
32
|
-
<a className="underline underline-offset-4" href="/llms.txt">
|
|
33
|
-
llms.txt
|
|
34
|
-
</a>
|
|
35
|
-
</li>
|
|
36
|
-
</ul>
|
|
37
|
-
<div>
|
|
38
|
-
<Button asChild>
|
|
39
|
-
<Link href="/">Back to home</Link>
|
|
40
|
-
</Button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link, type NotFoundProps } from "@pylonsync/react";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
|
|
5
|
-
// `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
|
|
6
|
-
// page calls `response.notFound()`.
|
|
7
|
-
//
|
|
8
|
-
// Give it real links. This boundary is also what an agent reads when it guesses
|
|
9
|
-
// a URL wrong: a markdown request for a missing page renders THIS file as
|
|
10
|
-
// markdown at 404, so the links below are how it finds its way back.
|
|
11
|
-
export default function NotFound(_props: NotFoundProps) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
|
|
14
|
-
<section>
|
|
15
|
-
<h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
|
|
16
|
-
<p className="mt-2 text-muted-foreground">
|
|
17
|
-
That URL does not exist. Try one of these:
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
<ul className="space-y-2 text-sm">
|
|
21
|
-
<li>
|
|
22
|
-
<Link className="underline underline-offset-4" href="/">
|
|
23
|
-
Home
|
|
24
|
-
</Link>
|
|
25
|
-
</li>
|
|
26
|
-
<li>
|
|
27
|
-
<a className="underline underline-offset-4" href="/sitemap.xml">
|
|
28
|
-
Sitemap
|
|
29
|
-
</a>
|
|
30
|
-
</li>
|
|
31
|
-
<li>
|
|
32
|
-
<a className="underline underline-offset-4" href="/llms.txt">
|
|
33
|
-
llms.txt
|
|
34
|
-
</a>
|
|
35
|
-
</li>
|
|
36
|
-
</ul>
|
|
37
|
-
<div>
|
|
38
|
-
<Button asChild>
|
|
39
|
-
<Link href="/">Back to home</Link>
|
|
40
|
-
</Button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link, type NotFoundProps } from "@pylonsync/react";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
|
|
5
|
-
// `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
|
|
6
|
-
// page calls `response.notFound()`.
|
|
7
|
-
//
|
|
8
|
-
// Give it real links. This boundary is also what an agent reads when it guesses
|
|
9
|
-
// a URL wrong: a markdown request for a missing page renders THIS file as
|
|
10
|
-
// markdown at 404, so the links below are how it finds its way back.
|
|
11
|
-
export default function NotFound(_props: NotFoundProps) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
|
|
14
|
-
<section>
|
|
15
|
-
<h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
|
|
16
|
-
<p className="mt-2 text-muted-foreground">
|
|
17
|
-
That URL does not exist. Try one of these:
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
<ul className="space-y-2 text-sm">
|
|
21
|
-
<li>
|
|
22
|
-
<Link className="underline underline-offset-4" href="/">
|
|
23
|
-
Home
|
|
24
|
-
</Link>
|
|
25
|
-
</li>
|
|
26
|
-
<li>
|
|
27
|
-
<a className="underline underline-offset-4" href="/sitemap.xml">
|
|
28
|
-
Sitemap
|
|
29
|
-
</a>
|
|
30
|
-
</li>
|
|
31
|
-
<li>
|
|
32
|
-
<a className="underline underline-offset-4" href="/llms.txt">
|
|
33
|
-
llms.txt
|
|
34
|
-
</a>
|
|
35
|
-
</li>
|
|
36
|
-
</ul>
|
|
37
|
-
<div>
|
|
38
|
-
<Button asChild>
|
|
39
|
-
<Link href="/">Back to home</Link>
|
|
40
|
-
</Button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link, type NotFoundProps } from "@pylonsync/react";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
|
|
5
|
-
// `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
|
|
6
|
-
// page calls `response.notFound()`.
|
|
7
|
-
//
|
|
8
|
-
// Give it real links. This boundary is also what an agent reads when it guesses
|
|
9
|
-
// a URL wrong: a markdown request for a missing page renders THIS file as
|
|
10
|
-
// markdown at 404, so the links below are how it finds its way back.
|
|
11
|
-
export default function NotFound(_props: NotFoundProps) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
|
|
14
|
-
<section>
|
|
15
|
-
<h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
|
|
16
|
-
<p className="mt-2 text-muted-foreground">
|
|
17
|
-
That URL does not exist. Try one of these:
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
<ul className="space-y-2 text-sm">
|
|
21
|
-
<li>
|
|
22
|
-
<Link className="underline underline-offset-4" href="/">
|
|
23
|
-
Home
|
|
24
|
-
</Link>
|
|
25
|
-
</li>
|
|
26
|
-
<li>
|
|
27
|
-
<a className="underline underline-offset-4" href="/sitemap.xml">
|
|
28
|
-
Sitemap
|
|
29
|
-
</a>
|
|
30
|
-
</li>
|
|
31
|
-
<li>
|
|
32
|
-
<a className="underline underline-offset-4" href="/llms.txt">
|
|
33
|
-
llms.txt
|
|
34
|
-
</a>
|
|
35
|
-
</li>
|
|
36
|
-
</ul>
|
|
37
|
-
<div>
|
|
38
|
-
<Button asChild>
|
|
39
|
-
<Link href="/">Back to home</Link>
|
|
40
|
-
</Button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link, type NotFoundProps } from "@pylonsync/react";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
|
|
5
|
-
// `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
|
|
6
|
-
// page calls `response.notFound()`.
|
|
7
|
-
//
|
|
8
|
-
// Give it real links. This boundary is also what an agent reads when it guesses
|
|
9
|
-
// a URL wrong: a markdown request for a missing page renders THIS file as
|
|
10
|
-
// markdown at 404, so the links below are how it finds its way back.
|
|
11
|
-
export default function NotFound(_props: NotFoundProps) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
|
|
14
|
-
<section>
|
|
15
|
-
<h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
|
|
16
|
-
<p className="mt-2 text-muted-foreground">
|
|
17
|
-
That URL does not exist. Try one of these:
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
<ul className="space-y-2 text-sm">
|
|
21
|
-
<li>
|
|
22
|
-
<Link className="underline underline-offset-4" href="/">
|
|
23
|
-
Home
|
|
24
|
-
</Link>
|
|
25
|
-
</li>
|
|
26
|
-
<li>
|
|
27
|
-
<a className="underline underline-offset-4" href="/sitemap.xml">
|
|
28
|
-
Sitemap
|
|
29
|
-
</a>
|
|
30
|
-
</li>
|
|
31
|
-
<li>
|
|
32
|
-
<a className="underline underline-offset-4" href="/llms.txt">
|
|
33
|
-
llms.txt
|
|
34
|
-
</a>
|
|
35
|
-
</li>
|
|
36
|
-
</ul>
|
|
37
|
-
<div>
|
|
38
|
-
<Button asChild>
|
|
39
|
-
<Link href="/">Back to home</Link>
|
|
40
|
-
</Button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link, type NotFoundProps } from "@pylonsync/react";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
|
|
5
|
-
// `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
|
|
6
|
-
// page calls `response.notFound()`.
|
|
7
|
-
//
|
|
8
|
-
// Give it real links. This boundary is also what an agent reads when it guesses
|
|
9
|
-
// a URL wrong: a markdown request for a missing page renders THIS file as
|
|
10
|
-
// markdown at 404, so the links below are how it finds its way back.
|
|
11
|
-
export default function NotFound(_props: NotFoundProps) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
|
|
14
|
-
<section>
|
|
15
|
-
<h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
|
|
16
|
-
<p className="mt-2 text-muted-foreground">
|
|
17
|
-
That URL does not exist. Try one of these:
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
<ul className="space-y-2 text-sm">
|
|
21
|
-
<li>
|
|
22
|
-
<Link className="underline underline-offset-4" href="/">
|
|
23
|
-
Home
|
|
24
|
-
</Link>
|
|
25
|
-
</li>
|
|
26
|
-
<li>
|
|
27
|
-
<a className="underline underline-offset-4" href="/sitemap.xml">
|
|
28
|
-
Sitemap
|
|
29
|
-
</a>
|
|
30
|
-
</li>
|
|
31
|
-
<li>
|
|
32
|
-
<a className="underline underline-offset-4" href="/llms.txt">
|
|
33
|
-
llms.txt
|
|
34
|
-
</a>
|
|
35
|
-
</li>
|
|
36
|
-
</ul>
|
|
37
|
-
<div>
|
|
38
|
-
<Button asChild>
|
|
39
|
-
<Link href="/">Back to home</Link>
|
|
40
|
-
</Button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link, type NotFoundProps } from "@pylonsync/react";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
|
|
5
|
-
// `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
|
|
6
|
-
// page calls `response.notFound()`.
|
|
7
|
-
//
|
|
8
|
-
// Give it real links. This boundary is also what an agent reads when it guesses
|
|
9
|
-
// a URL wrong: a markdown request for a missing page renders THIS file as
|
|
10
|
-
// markdown at 404, so the links below are how it finds its way back.
|
|
11
|
-
export default function NotFound(_props: NotFoundProps) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
|
|
14
|
-
<section>
|
|
15
|
-
<h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
|
|
16
|
-
<p className="mt-2 text-muted-foreground">
|
|
17
|
-
That URL does not exist. Try one of these:
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
<ul className="space-y-2 text-sm">
|
|
21
|
-
<li>
|
|
22
|
-
<Link className="underline underline-offset-4" href="/">
|
|
23
|
-
Home
|
|
24
|
-
</Link>
|
|
25
|
-
</li>
|
|
26
|
-
<li>
|
|
27
|
-
<a className="underline underline-offset-4" href="/sitemap.xml">
|
|
28
|
-
Sitemap
|
|
29
|
-
</a>
|
|
30
|
-
</li>
|
|
31
|
-
<li>
|
|
32
|
-
<a className="underline underline-offset-4" href="/llms.txt">
|
|
33
|
-
llms.txt
|
|
34
|
-
</a>
|
|
35
|
-
</li>
|
|
36
|
-
</ul>
|
|
37
|
-
<div>
|
|
38
|
-
<Button asChild>
|
|
39
|
-
<Link href="/">Back to home</Link>
|
|
40
|
-
</Button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link, type NotFoundProps } from "@pylonsync/react";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
|
|
5
|
-
// `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
|
|
6
|
-
// page calls `response.notFound()`.
|
|
7
|
-
//
|
|
8
|
-
// Give it real links. This boundary is also what an agent reads when it guesses
|
|
9
|
-
// a URL wrong: a markdown request for a missing page renders THIS file as
|
|
10
|
-
// markdown at 404, so the links below are how it finds its way back.
|
|
11
|
-
export default function NotFound(_props: NotFoundProps) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
|
|
14
|
-
<section>
|
|
15
|
-
<h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
|
|
16
|
-
<p className="mt-2 text-muted-foreground">
|
|
17
|
-
That URL does not exist. Try one of these:
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
<ul className="space-y-2 text-sm">
|
|
21
|
-
<li>
|
|
22
|
-
<Link className="underline underline-offset-4" href="/">
|
|
23
|
-
Home
|
|
24
|
-
</Link>
|
|
25
|
-
</li>
|
|
26
|
-
<li>
|
|
27
|
-
<a className="underline underline-offset-4" href="/sitemap.xml">
|
|
28
|
-
Sitemap
|
|
29
|
-
</a>
|
|
30
|
-
</li>
|
|
31
|
-
<li>
|
|
32
|
-
<a className="underline underline-offset-4" href="/llms.txt">
|
|
33
|
-
llms.txt
|
|
34
|
-
</a>
|
|
35
|
-
</li>
|
|
36
|
-
</ul>
|
|
37
|
-
<div>
|
|
38
|
-
<Button asChild>
|
|
39
|
-
<Link href="/">Back to home</Link>
|
|
40
|
-
</Button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|