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<Response> {
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 },
})
}