Sign-in page prefetches the Kumo bundle for the pages after it (#271)
* Sign-in page prefetches the Kumo bundle for the pages after it * Test that the sign-in page prefetches Kumo and the home page skips it
- CI / Typecheck and bundlesuccess
- CI / Unit tests (1/2)Failed
- CI / Unit tests (2/2)success
3 files changed+25−1
modifiedapps/diffinery/src/web/auth.tsx+2−1View file
| @@ -377,7 +377,8 @@ | ||
| 377 | 377 | if (p.type === "account" && p.via !== "dev") return c.redirect(next, 302); |
| 378 | 378 | return render( |
| 379 | 379 | c, |
| 380 | - { title: "Sign in" }, | |
| 380 | + // The pages people land on after signing in mount Kumo islands: fetch the bundle while they sign in. | |
| 381 | + { title: "Sign in", prefetchKumo: true }, | |
| 381 | 382 | <AuthCard title="Sign in to Diffinery"> |
| 382 | 383 | <p class="muted">Use the passkey saved on this device, your phone or your password manager.</p> |
| 383 | 384 | <PasskeyUnsupported /> |
modifiedapps/diffinery/src/web/ui.tsx+8−0View file
| @@ -313,6 +313,8 @@ | ||
| 313 | 313 | refresh?: number; |
| 314 | 314 | /** Load Cloudflare Turnstile (the sign-up page): its script and iframe come from challenges.cloudflare.com. */ |
| 315 | 315 | turnstile?: boolean; |
| 316 | + /** Fetch the Kumo islands bundle at low priority on a page without islands, for the pages that follow it (sign-in). */ | |
| 317 | + prefetchKumo?: boolean; | |
| 316 | 318 | /** Another place this page's forms may lead (CSP form-action), e.g. the CLI's loopback callback after approving a sign-in. */ |
| 317 | 319 | formAction?: string; |
| 318 | 320 | } |
| @@ -502,6 +504,12 @@ | ||
| 502 | 504 | })} |
| 503 | 505 | <style>{raw(CSS)}</style> |
| 504 | 506 | {kumo ? <link rel="stylesheet" href={kumoAssets().css} /> : null} |
| 507 | + {!kumo && opts.prefetchKumo ? ( | |
| 508 | + <> | |
| 509 | + <link rel="prefetch" href={kumoAssets().css} as="style" /> | |
| 510 | + <link rel="prefetch" href={kumoAssets().js} as="script" /> | |
| 511 | + </> | |
| 512 | + ) : null} | |
| 505 | 513 | {opts.head ?? null} |
| 506 | 514 | {opts.turnstile ? <script nonce={nonce} src={TURNSTILE_SCRIPT} async defer></script> : null} |
| 507 | 515 | </head> |
modifiedapps/diffinery/test/unit/site.test.ts+15−0View file
| @@ -149,6 +149,10 @@ | ||
| 149 | 149 | for (const href of ["/compare/github", "/compare/blacksmith", "/performance", "/agents", "/about", "/llms.txt"]) expect(html, href).toContain(`href="${href}"`); |
| 150 | 150 | }); |
| 151 | 151 | |
| 152 | + it("doesn't load the Kumo bundle, which only pages with islands use", async () => { | |
| 153 | + expect(await (await get("/")).text()).not.toContain("/-/kumo/"); | |
| 154 | + }); | |
| 155 | + | |
| 152 | 156 | it("is served as Markdown", async () => { |
| 153 | 157 | for (const res of [await get("/index.md"), await get("/", { accept: "text/markdown" })]) { |
| 154 | 158 | expect(res.status).toBe(200); |
| @@ -160,6 +164,17 @@ | ||
| 160 | 164 | }); |
| 161 | 165 | }); |
| 162 | 166 | |
| 167 | +describe("sign-in page", () => { | |
| 168 | + // The Deploy workflow checks that /login names both bundle files and that they answer. | |
| 169 | + it("prefetches the Kumo bundle for the pages after it, without loading it", async () => { | |
| 170 | + const html = await (await get("/login")).text(); | |
| 171 | + expect(html).toContain("Sign in with a passkey"); | |
| 172 | + expect(html).toMatch(/<link rel="prefetch" href="\/-\/kumo\/[a-z0-9]+\.css" as="style"\/?>/); | |
| 173 | + expect(html).toMatch(/<link rel="prefetch" href="\/-\/kumo\/[a-z0-9]+\.js" as="script"\/?>/); | |
| 174 | + expect(html).not.toMatch(/<(?:script|link rel="stylesheet")[^>]*\/-\/kumo\//); | |
| 175 | + }); | |
| 176 | +}); | |
| 177 | + | |
| 163 | 178 | describe("crawlers and AI assistants", () => { |
| 164 | 179 | it("lists every site page in llms.txt, before the docs", () => { |
| 165 | 180 | const txt = llmsTxt(ORIGIN); |