Something went wrong. Try again.
A local-first event pipeline for independent agents, built on Jazz.
Something went wrong. Try again.
9.4 kB · 166 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167import fs from "node:fs/promises";import path from "node:path";import { FONT_DEBUG_ASSET_PATH } from "./font-debug.js";
export interface PublicPage { route: string; title: string; html: string;}
const PUBLIC_PAGE_FILES = new Map([ ["/", "index.md"], ["/docs", "docs/index.md"], ["/docs/architecture", "docs/architecture.md"], ["/docs/security", "docs/security.md"],]);
const MAX_PUBLIC_FILE_BYTES = 64 * 1024;const MAX_PUBLIC_FONT_BYTES = 64 * 1024;const CODE_URL = "https://tangled.org/@cameron.stream/thought-stream";
export async function loadPublicPages(projectRoot: string): Promise<Map<string, PublicPage>> { const publicRoot = path.resolve(projectRoot, "public"); const fontPath = path.resolve(publicRoot, "assets/around-regular.woff2"); if (!fontPath.startsWith(`${publicRoot}${path.sep}`)) throw new Error("Public font path escapes the allowlisted root"); const fontStat = await fs.lstat(fontPath); if (!fontStat.isFile() || fontStat.isSymbolicLink() || fontStat.size > MAX_PUBLIC_FONT_BYTES) { throw new Error("Invalid public Around font"); } const font = await fs.readFile(fontPath); if (font.length < 4 || font.subarray(0, 4).toString("ascii") !== "wOF2") { throw new Error("Invalid public Around font"); } const fontDataUrl = `data:font/woff2;base64,${font.toString("base64")}`; const pages = new Map<string, PublicPage>(); for (const [route, relativePath] of PUBLIC_PAGE_FILES) { const filePath = path.resolve(publicRoot, relativePath); if (filePath !== publicRoot && !filePath.startsWith(`${publicRoot}${path.sep}`)) { throw new Error("Public content path escapes the allowlisted root"); } const stat = await fs.lstat(filePath); if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_PUBLIC_FILE_BYTES) { throw new Error(`Invalid public content file: ${relativePath}`); } const markdown = await fs.readFile(filePath, "utf8"); const title = firstHeading(markdown); pages.set(route, { route, title, html: renderPage(route, title, markdown, fontDataUrl) }); } return pages;}
export function publicPageForRoute(pages: Map<string, PublicPage>, pathname: string): PublicPage | undefined { const normalized = pathname.length > 1 && pathname.endsWith("/") ? pathname.slice(0, -1) : pathname; return pages.get(normalized);}
function renderPage(route: string, title: string, markdown: string, fontDataUrl: string): string { const body = renderMarkdown(markdown); if (route === "/") return renderLandingPage(title, body, fontDataUrl); return renderDocumentationPage(title, body, fontDataUrl);}
function renderLandingPage(title: string, body: string, fontDataUrl: string): string { return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff"><meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0a0a0a"><title>${escapeHtml(title)}</title><meta name="description" content="Cameron's private Stream."><style>@font-face{font-family:Around;src:url("${fontDataUrl}") format("woff2");font-style:normal;font-weight:400;font-display:swap}:root{color-scheme:light dark;--bg:#fff;--text:#1a1a1a;--secondary:#2a2a2a;--muted:#555;--accent:#b497bf;--link:#72567e;--border:#e8e8e8;--font-body:Inter,system-ui,-apple-system,sans-serif;--font-display:Around,Inter,system-ui,sans-serif}*{box-sizing:border-box}html,body{min-height:100%;background:var(--bg)}body{margin:0;color:var(--text);font:16px/1.5 var(--font-body);-webkit-font-smoothing:antialiased}.landing-shell{width:min(640px,calc(100% - 40px));min-height:100svh;margin:0 auto;padding:max(20px,env(safe-area-inset-top)) 0 max(20px,env(safe-area-inset-bottom));display:grid;grid-template-rows:1fr auto}.landing-main{align-self:center;padding:10vh 0}.landing-main h1{margin:0 0 14px;font:400 clamp(2.25rem,9vw,3.25rem)/1.08 var(--font-display);font-synthesis:none;letter-spacing:0;text-align:center}.landing-main p{max-width:30rem;margin:0;color:var(--secondary)}.login-form{margin-top:28px;text-align:center}.login-form button{min-height:44px;border:1px solid var(--border);border-radius:999px;padding:9px 18px;background:transparent;color:var(--text);font:500 14px/1 var(--font-body);cursor:pointer}.login-form button:hover,.login-form button:focus-visible{border-color:var(--accent);outline:0}.landing-footer{padding:16px 0 0;font-size:12px;text-align:center}.landing-footer a{color:var(--muted);text-decoration:none}.landing-footer a:hover,.landing-footer a:focus-visible{color:var(--link);text-decoration:underline;text-underline-offset:3px}@media(prefers-color-scheme:dark){:root{--bg:#0a0a0a;--text:#e5e5e5;--secondary:#b0b0b0;--muted:#8a8a8a;--accent:#c4aacf;--link:#d4b9df;--border:#2a2a2a}}@media(max-width:480px){.landing-shell{width:calc(100% - 32px)}.landing-main{padding:8vh 0}}</style></head><body><div class="landing-shell"><main class="landing-main">${body}<form class="login-form" method="post" action="/oauth/login"><button type="submit">Log in</button></form></main><footer class="landing-footer"><a href="${CODE_URL}">code</a></footer></div><script src="${FONT_DEBUG_ASSET_PATH}" defer></script></body></html>`;}
function renderDocumentationPage(title: string, body: string, fontDataUrl: string): string { return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(title)} · Stream</title><meta name="description" content="Public documentation for Stream."><style>@font-face{font-family:Around;src:url("${fontDataUrl}") format("woff2");font-style:normal;font-weight:400;font-display:swap}:root{color-scheme:light dark;--bg:#fff;--text:#1a1a1a;--secondary:#2a2a2a;--muted:#555;--accent:#b497bf;--link:#72567e;--border:#e8e8e8;--surface:#f5f5f5;--font-body:Inter,system-ui,-apple-system,sans-serif}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:16px/1.65 var(--font-body);-webkit-font-smoothing:antialiased}header,main,footer{width:min(640px,calc(100% - 40px));margin:auto}header{display:flex;align-items:baseline;justify-content:space-between;gap:18px;padding:28px 0 18px}header>a{color:var(--text);font:400 20px/1.1 Around,Inter,system-ui,sans-serif;text-decoration:none}nav{display:flex;gap:14px;font-size:12px}nav a{color:var(--muted);text-decoration:none}nav a:hover,nav a:focus-visible{color:var(--link);text-decoration:underline;text-underline-offset:3px}main{padding:38px 0 54px}h1{margin:0 0 24px;font:400 clamp(2rem,8vw,3rem)/1.08 Around,Inter,system-ui,sans-serif}h2{margin:34px 0 10px;font-size:20px;line-height:1.25}p,ul{margin:12px 0}ul{padding-left:22px}li+li{margin-top:7px}a{color:var(--link)}code{padding:.1em .35em;border-radius:4px;background:var(--surface)}footer{padding:16px 0 max(24px,env(safe-area-inset-bottom));font-size:12px}footer a{color:var(--muted);text-decoration:none}@media(prefers-color-scheme:dark){:root{--bg:#0a0a0a;--text:#e5e5e5;--secondary:#b0b0b0;--muted:#8a8a8a;--accent:#c4aacf;--link:#d4b9df;--border:#2a2a2a;--surface:#1a1a1a}}@media(max-width:540px){header{align-items:flex-start;flex-direction:column}nav{gap:12px}}</style></head><body><header><a href="/">Stream</a><nav><a href="/docs">docs</a><a href="/docs/architecture">architecture</a><a href="/docs/security">security</a></nav></header><main>${body}</main><footer><a href="${CODE_URL}">code</a></footer><script src="${FONT_DEBUG_ASSET_PATH}" defer></script></body></html>`;}
function renderMarkdown(markdown: string): string { const blocks: string[] = []; const paragraph: string[] = []; const list: string[] = []; const flushParagraph = () => { if (paragraph.length === 0) return; blocks.push(`<p>${inlineMarkup(paragraph.join(" "))}</p>`); paragraph.length = 0; }; const flushList = () => { if (list.length === 0) return; blocks.push(`<ul>${list.map((item) => `<li>${inlineMarkup(item)}</li>`).join("")}</ul>`); list.length = 0; }; for (const rawLine of markdown.replaceAll("\r\n", "\n").split("\n")) { const line = rawLine.trim(); const heading = line.match(/^(#{1,2})\s+(.+)$/); const item = line.match(/^[-*]\s+(.+)$/); if (heading) { flushParagraph(); flushList(); const level = heading[1]!.length; blocks.push(`<h${level}>${inlineMarkup(heading[2]!)}</h${level}>`); } else if (item) { flushParagraph(); list.push(item[1]!); } else if (line === "") { flushParagraph(); flushList(); } else { flushList(); paragraph.push(line); } } flushParagraph(); flushList(); return blocks.join("\n");}
function inlineMarkup(value: string): string { return escapeHtml(value).replaceAll(/`([^`]+)`/g, "<code>$1</code>");}
function firstHeading(markdown: string): string { const heading = markdown.match(/^#\s+(.+)$/m)?.[1]?.trim(); if (!heading) throw new Error("Public content requires one level-one heading"); return heading;}
function escapeHtml(value: string): string { return value .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'");}