@pylonsync/create-pylon 0.3.308 → 0.3.310
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pylonsync/create-pylon",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.310",
|
|
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"
|
|
@@ -53,6 +53,11 @@ export const generateMetadata: GenerateMetadata = async ({
|
|
|
53
53
|
};
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
+
// Public case study, no auth read → an ISR candidate. `revalidate` serves it
|
|
57
|
+
// from cache with stale-while-revalidate and makes <Link>'s prefetch reusable,
|
|
58
|
+
// so a click off the work grid hits cache instead of a live render.
|
|
59
|
+
export const revalidate = 300;
|
|
60
|
+
|
|
56
61
|
const WRAP_NARROW = "mx-auto w-full max-w-3xl px-6";
|
|
57
62
|
|
|
58
63
|
function CaseStudy({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import React, { useState } from "react";
|
|
3
|
+
import React, { useEffect, useState } from "react";
|
|
4
4
|
import { db } from "@pylonsync/react";
|
|
5
5
|
import {
|
|
6
6
|
passwordLogin,
|
|
@@ -29,6 +29,26 @@ export function AuthForm({ mode }: { mode: "login" | "signup" }) {
|
|
|
29
29
|
const [password, setPassword] = useState("");
|
|
30
30
|
const [error, setError] = useState<string | null>(null);
|
|
31
31
|
const [pending, setPending] = useState(false);
|
|
32
|
+
// OAuth buttons render only for providers the server actually has
|
|
33
|
+
// configured (GET /api/auth/providers) — an unconfigured provider's
|
|
34
|
+
// login URL is a JSON error dead-end, not a login page. `null` while
|
|
35
|
+
// loading so nothing flashes in.
|
|
36
|
+
const [providers, setProviders] = useState<string[] | null>(null);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
let alive = true;
|
|
40
|
+
fetch("/api/auth/providers")
|
|
41
|
+
.then((r) => (r.ok ? r.json() : []))
|
|
42
|
+
.then((list: Array<{ provider: string }>) => {
|
|
43
|
+
if (alive) setProviders(list.map((p) => p.provider));
|
|
44
|
+
})
|
|
45
|
+
.catch(() => {
|
|
46
|
+
if (alive) setProviders([]);
|
|
47
|
+
});
|
|
48
|
+
return () => {
|
|
49
|
+
alive = false;
|
|
50
|
+
};
|
|
51
|
+
}, []);
|
|
32
52
|
|
|
33
53
|
async function onSubmit(e: React.FormEvent) {
|
|
34
54
|
e.preventDefault();
|
|
@@ -114,22 +134,38 @@ export function AuthForm({ mode }: { mode: "login" | "signup" }) {
|
|
|
114
134
|
</button>
|
|
115
135
|
</form>
|
|
116
136
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
137
|
+
{/* Social sign-in — only for providers the server reports as
|
|
138
|
+
configured. Enable Google with PYLON_OAUTH_GOOGLE_CLIENT_ID /
|
|
139
|
+
_CLIENT_SECRET / _REDIRECT (GitHub: PYLON_OAUTH_GITHUB_*) and the
|
|
140
|
+
button appears with zero code changes. */}
|
|
141
|
+
{providers && providers.length > 0 ? (
|
|
142
|
+
<>
|
|
143
|
+
<div className="flex items-center gap-3 text-[11px] uppercase tracking-wide text-zinc-400">
|
|
144
|
+
<span className="h-px flex-1 bg-zinc-200" />
|
|
145
|
+
or continue with
|
|
146
|
+
<span className="h-px flex-1 bg-zinc-200" />
|
|
147
|
+
</div>
|
|
122
148
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
149
|
+
{providers.includes("google") ? (
|
|
150
|
+
<a
|
|
151
|
+
href="/api/auth/login/google?callback=/dashboard&redirect=1"
|
|
152
|
+
className="inline-flex h-10 w-full items-center justify-center gap-2 rounded-lg border border-zinc-300 bg-white text-sm font-medium text-zinc-900 transition-colors hover:bg-zinc-50"
|
|
153
|
+
>
|
|
154
|
+
<GoogleIcon />
|
|
155
|
+
Google
|
|
156
|
+
</a>
|
|
157
|
+
) : null}
|
|
158
|
+
{providers.includes("github") ? (
|
|
159
|
+
<a
|
|
160
|
+
href="/api/auth/login/github?callback=/dashboard&redirect=1"
|
|
161
|
+
className="inline-flex h-10 w-full items-center justify-center gap-2 rounded-lg border border-zinc-300 bg-white text-sm font-medium text-zinc-900 transition-colors hover:bg-zinc-50"
|
|
162
|
+
>
|
|
163
|
+
<GitHubIcon />
|
|
164
|
+
GitHub
|
|
165
|
+
</a>
|
|
166
|
+
) : null}
|
|
167
|
+
</>
|
|
168
|
+
) : null}
|
|
133
169
|
</div>
|
|
134
170
|
);
|
|
135
171
|
}
|
|
@@ -194,6 +230,14 @@ function LockIcon() {
|
|
|
194
230
|
);
|
|
195
231
|
}
|
|
196
232
|
|
|
233
|
+
function GitHubIcon() {
|
|
234
|
+
return (
|
|
235
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
|
|
236
|
+
<path d="M12 1C5.92 1 1 5.92 1 12c0 4.87 3.15 9 7.52 10.45.55.1.75-.24.75-.53 0-.26-.01-.96-.01-1.88-3.06.66-3.71-1.48-3.71-1.48-.5-1.27-1.22-1.61-1.22-1.61-1-.68.08-.67.08-.67 1.1.08 1.68 1.13 1.68 1.13.98 1.68 2.57 1.19 3.2.91.1-.71.38-1.19.69-1.47-2.44-.28-5.01-1.22-5.01-5.44 0-1.2.43-2.18 1.13-2.95-.11-.28-.49-1.4.11-2.91 0 0 .92-.3 3.03 1.13a10.5 10.5 0 0 1 5.52 0c2.1-1.43 3.03-1.13 3.03-1.13.6 1.51.22 2.63.11 2.91.7.77 1.13 1.75 1.13 2.95 0 4.23-2.58 5.16-5.03 5.43.39.34.74 1 .74 2.02 0 1.47-.01 2.65-.01 3.01 0 .29.2.64.76.53A11.01 11.01 0 0 0 23 12c0-6.08-4.92-11-11-11z" />
|
|
237
|
+
</svg>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
197
241
|
function GoogleIcon() {
|
|
198
242
|
return (
|
|
199
243
|
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden>
|
|
@@ -31,6 +31,14 @@ async function resolveListing(
|
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// The listing page is anonymous + public (the watch button + offer panel are
|
|
35
|
+
// client islands with their own auth), so its SSR output is shared across
|
|
36
|
+
// visitors — an ISR candidate. `revalidate` serves it from cache, emits
|
|
37
|
+
// stale-while-revalidate, and makes <Link>'s prefetch reusable so a click hits
|
|
38
|
+
// cache instead of a live render. Short TTL because a listing's sold status is
|
|
39
|
+
// time-sensitive; the realtime offer panel keeps the live bits fresh regardless.
|
|
40
|
+
export const revalidate = 60;
|
|
41
|
+
|
|
34
42
|
// Data-driven SEO: the title + description come from the listing itself,
|
|
35
43
|
// fetched on the server. `generateMetadata` is handed the same PageProps as
|
|
36
44
|
// the page (params + serverData), so it reads the row directly.
|