From 969f11dc3c5ecece45e372c0db29d7fa9557b874 Mon Sep 17 00:00:00 2001 From: lifeodyssey Date: Sat, 20 Jun 2026 02:57:28 +0800 Subject: [PATCH] fix: avoid homepage flash on share routes --- index.html | 2 ++ src/worker/index.ts | 39 ++++++++++++++++++--- tests/worker-index.test.ts | 69 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 tests/worker-index.test.ts diff --git a/index.html b/index.html index 168c85b..f885722 100644 --- a/index.html +++ b/index.html @@ -31,6 +31,7 @@
+

Share HTML — upload one HTML file, get a sandboxed shareable link

Upload a single self-contained HTML file and get a public, sandboxed preview URL. No signup required; anonymous uploads are kept for 365 days.

@@ -39,6 +40,7 @@

Use it programmatically

# returns JSON: { "share": { "share_url", "preview_url", "slug", "id" }, "claimToken" }

Full API: /openapi.json · AI guide: /llms.txt · MCP: /mcp

+
`; const html = await response.text(); - const injected = html.replace("", `${configScript}`); + const bodyAdjusted = routeFallbackHtml(html, request); + const injected = bodyAdjusted.replace("", `${configScript}`); return new Response(injected, { status: response.status, statusText: response.statusText, @@ -172,6 +173,37 @@ async function injectConfig(response: Response, env: Env): Promise { }); } +function routeFallbackHtml(html: string, request: Request): string { + const pathname = new URL(request.url).pathname; + if (!isShareRoute(pathname)) return html; + return replaceAppFallback(html, shareRouteFallback()); +} + +function isShareRoute(pathname: string): boolean { + return /^\/s\/[^/]+\/?$/.test(pathname); +} + +function replaceAppFallback(html: string, replacement: string): string { + const startMarker = ""; + const endMarker = ""; + const start = html.indexOf(startMarker); + const end = html.indexOf(endMarker); + if (start === -1 || end === -1 || end < start) return html; + + const contentStart = start + startMarker.length; + return `${html.slice(0, contentStart)}\n${replacement}\n ${html.slice(end)}`; +} + +function shareRouteFallback(): string { + return [ + '
', + '
', + '

Loading share...

', + "
", + "
", + ].join("\n"); +} + function discoveryRoute(request: Request, url: URL): Response | null { if (request.method !== "GET" && request.method !== "HEAD") { return null; @@ -308,4 +340,3 @@ export type { AuthUser } from "./auth.ts"; export { cleanTitle, sanitizeShortText, looksLikeHtml, isUploadFile, getClientIp, sha256Hex, hashText, base64Url, numberEnv, formatBytes, escapeHtml, errorMessage, logBackgroundError } from "./utils.ts"; export { maxUploadBytes, appOrigin, previewOrigin } from "./config.ts"; export { createShare, createShareRecord, checkUploadRate, listMyShares, getPublicShare, reportShare, claimShare, deleteShare, listReports, moderateShare, previewShare, previewHeaders, previewMessage } from "./shares.ts"; - diff --git a/tests/worker-index.test.ts b/tests/worker-index.test.ts new file mode 100644 index 0000000..588f88b --- /dev/null +++ b/tests/worker-index.test.ts @@ -0,0 +1,69 @@ +import assert from "node:assert/strict"; +import { test, vi } from "vitest"; + +import worker from "../src/worker/index.ts"; + +const INDEX_HTML = ` + + + Share HTML + + +
+ +
+

Share HTML — upload one HTML file, get a sandboxed shareable link

+
+ +
+ + +`; + +function makeEnv() { + return { + SUPABASE_URL: "https://proj.supabase.co", + SUPABASE_PUBLISHABLE_KEY: "publishable-key", + ASSETS: { + fetch: vi.fn(async () => new Response(INDEX_HTML, { + headers: { "content-type": "text/html" }, + })), + }, + } as any; +} + +function makeCtx() { + return { waitUntil: vi.fn() } as any; +} + +test("worker fetch: share SPA fallback does not expose homepage copy before React renders", async () => { + const env = makeEnv(); + const response = await worker.fetch( + new Request("https://sharehtml.zhenjia.dev/s/example"), + env, + makeCtx() + ); + + assert.equal(response.status, 200); + const html = await response.text(); + + assert.ok(html.includes("Loading share...")); + assert.ok(!html.includes("Share HTML — upload one HTML file")); + assert.ok(html.includes("window.__APP_CONFIG__")); +}); + +test("worker fetch: homepage SPA fallback keeps agent-readable static copy", async () => { + const env = makeEnv(); + const response = await worker.fetch( + new Request("https://sharehtml.zhenjia.dev/"), + env, + makeCtx() + ); + + assert.equal(response.status, 200); + const html = await response.text(); + + assert.ok(html.includes("Share HTML — upload one HTML file")); + assert.ok(!html.includes("Loading share...")); + assert.ok(html.includes("window.__APP_CONFIG__")); +});