import { packageContext } from 'kody:runtime' import getSeries from './series.ts' import { getRepoSettings, ingestSummary } from './storage.ts' import { artResponse } from './ui/art.ts' import { needsCanonicalAppRedirect, serviceWorkerAllowedPath } from './ui/app-url.ts' import { iconSvg } from './ui/icon.ts' import { lineageManifest } from './ui/manifest.ts' import { renderAppPage } from './ui/page.ts' import { serviceWorkerScript } from './ui/sw-script.ts' import { getAppVersion } from './version.ts' /** * Hosted Lineage app: PWA chart, series API, and installable shell. */ export default { async fetch(request: Request): Promise { const url = new URL(request.url) const appBasePath = packageContext?.appBasePath || '/packages/lineage' const hostedUrl = packageContext?.hostedUrl || `https://kentcdodds.kody.run${appBasePath}` if (needsCanonicalAppRedirect(url.pathname, appBasePath)) { url.pathname = `${appBasePath}/` return Response.redirect(url, 308) } const path = stripMount(url.pathname, appBasePath) if (path === '/manifest.webmanifest') { return json(lineageManifest(appBasePath), 'application/manifest+json; charset=utf-8') } const artwork = artResponse(path) if (artwork) return artwork if (path === '/icon.svg') { return new Response(iconSvg, { headers: { 'Content-Type': 'image/svg+xml; charset=utf-8' }, }) } if (path === '/sw.js') { return new Response(serviceWorkerScript(appBasePath), { headers: { 'Content-Type': 'application/javascript; charset=utf-8', 'Cache-Control': 'no-cache', 'Service-Worker-Allowed': serviceWorkerAllowedPath(appBasePath), }, }) } if (path === '/api/series') { const series = await getSeries({ from: url.searchParams.get('from'), to: url.searchParams.get('to'), mode: (url.searchParams.get('mode') as | 'cumulative' | 'delta' | 'net' | null) ?? 'cumulative', }) return json(series) } if (path === '/api/status') { return json({ repo: await getRepoSettings(), ingest: await ingestSummary(), }) } if (path === '/api/version') { return json(await getAppVersion()) } const [repo, version] = await Promise.all([getRepoSettings(), getAppVersion()]) return new Response( renderAppPage({ appBasePath, hostedUrl, repoLabel: `${repo.owner}/${repo.repo}`, version, }), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store', }, }, ) }, } function stripMount(pathname: string, appBasePath: string): string { let rest = pathname if (pathname === appBasePath || pathname.startsWith(`${appBasePath}/`)) { rest = pathname.slice(appBasePath.length) || '/' } if (!rest.startsWith('/')) rest = `/${rest}` if (rest.length > 1 && rest.endsWith('/')) rest = rest.slice(0, -1) return rest || '/' } function json(data: unknown, contentType = 'application/json; charset=utf-8') { return new Response(JSON.stringify(data), { headers: { 'Content-Type': contentType }, }) }