← Public packages
@kentcdodds/package-app-kit
Design tokens, PWA install/update, About/version, cache helpers, and optional realtime notes sync for Kody package apps.
import { packageContext } from 'kody:runtime'
import { createRequestId, withRequestId } from './middleware/request-id.ts'
import { relativePath } from './routes.ts'
import { icon192, icon512 } from './controllers/icons.ts'
import { manifest } from './controllers/manifest.ts'
import { version } from './controllers/version.ts'
import { notesAction } from './controllers/notes-action.ts'
import { listNotes } from './data/notes.ts'
import { renderApp } from './ssr.tsx'
// Host strips the mount before forwarding. TanStack routes that use basepath /
// remounted URLs need the hosted pathname, so remount here (Remix parity).
function remountRequest(request: Request) {
const appBasePath = String(packageContext?.appBasePath ?? '').replace(/\/+$/, '')
if (!appBasePath) return request
const url = new URL(request.url)
url.pathname = url.pathname === '/' ? appBasePath : appBasePath + url.pathname
return new Request(url, request)
}
function wantsJson(request: Request) {
const accept = (request.headers.get('accept') || '').toLowerCase()
return accept.startsWith('application/json')
}
async function dispatch(request: Request): Promise<Response> {
const appBasePath = String(packageContext?.appBasePath ?? '').replace(/\/+$/, '')
const url = new URL(request.url)
const path = relativePath(url.pathname, appBasePath)
const method = request.method.toUpperCase()
if (path === '/health') {
return Response.json({ ok: true })
}
if (path === '/api/version') {
return version()
}
if (path === '/manifest.webmanifest') {
return manifest()
}
if (path === '/icons/icon-192.png') {
return icon192()
}
if (path === '/icons/icon-512.png') {
return icon512()
}
if (path === '/notes' && method === 'POST') {
return notesAction(request, appBasePath)
}
if (path === '/notes' && method === 'GET' && wantsJson(request)) {
const notes = await listNotes()
return Response.json({ notes })
}
if (method === 'GET' || method === 'HEAD') {
return renderApp(request)
}
return new Response('Method Not Allowed', { status: 405 })
}
// Default-export { fetch } — do not export a class/router object (Worker env is not RequestInit).
export default {
async fetch(request: Request) {
const requestId = createRequestId()
try {
const response = await dispatch(remountRequest(request))
return withRequestId(response, requestId)
} catch (error) {
console.error('package app fetch failed:', error)
return withRequestId(
Response.json(
{ ok: false, error: error instanceof Error ? error.message : String(error) },
{ status: 500 },
),
requestId,
)
}
},
}