For years bauer.gg was 1 Cloudflare Worker written by hand in the dashboard’s code editor. There was no repo; the source of truth was an editor pane in the Cloudflare dashboard. This post walks through what that Worker did, the move to Astro, how the resume became the typed center of the site, the first design system, and the deploy that made the repo the only source of truth.
1. The old Worker
The whole site was 1 fetch handler, with my resume sitting at the bottom of the file as a big object literal. Content negotiation keyed on the request’s Content-Type header, not Accept, so a browser got the resume pretty-printed and colored. So did curl, unless you claimed to be sending JSON.
The coloring was a single regex pass over the stringified resume. The Worker left no repo behind, so this is reconstructed from memory rather than quoted:
json.replace(
/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
(match) => {
let cls = 'number';
if (/^"/.test(match)) {
cls = /:$/.test(match) ? 'key' : 'string';
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return `<span class="${cls}">${match}</span>`;
}
)
The regex matches 1 of 3 shapes: a quoted string, a true/false/null
literal, or a number. Every match defaults to class number; a leading quote
demotes it to string, or to key when a colon follows. Each match comes
back wrapped in a span.
5 CSS rules finished the job: strings green, numbers darkorange, booleans blue, null magenta, keys bold. That was the entire design system.
The Worker also kept a visitor counter in Workers KV, keyed on the raw client IP with no expiry. In retrospect, storing raw client IPs indefinitely was a data-retention problem, not a feature.
That held up fine as a resume endpoint. It had no answer for writing, and I had a Flappy Bird port I wanted to write about.
2. The move to Astro
The new site is Astro 5 with the Cloudflare adapter, still deployed as 1 Worker. Every route is prerendered at build time to static assets; the Worker entry point exists because the adapter emits one, not because anything renders on demand.
The client-side JavaScript is a handful of small page-local scripts: the theme toggle, the resume page’s print button, the header mark’s animation, and the wasm-bindgen glue inside the /flappy embed.
3. The resume as the source of truth
The resume survived as the center of the thing. It lives in 1 typed data
file, src/data/resume.ts, that feeds both the HTML page at
/resume and the machine-readable JSON Resume.
The JSON endpoint is a single expression, with the headers object shortened here:
export const GET: APIRoute = () =>
new Response(JSON.stringify(resume, null, 2), { headers });
Astro prerenders this route to a static file at build time.
The old content negotiation is gone on purpose. Header sniffing made caching annoying and debugging worse, and 2 explicit URLs cost nothing.
4. The first design system
The first design was a monospace document: 1 family, the system
ui-monospace stack with no webfonts, 1px borders, square corners, orange
used sparingly, and a light and dark palette. The landing page opened with an
ASCII-art banner in a pre tag instead of a hero image.
The comment at the top of global.css stated the goal: a document, not a
marketing site. Code blocks in posts got dual-theme Shiki highlighting,
github-light and github-dark-dimmed, flipped with the site theme.
The site has since been redesigned, so the page you are reading no longer looks like that. The monospace document was the first iteration.
5. The deploy that deleted the original
Deploys are GitHub Actions on push to main: npm ci, astro build,
npx wrangler deploy. The API token lives in repo secrets; the account id
sits in wrangler.jsonc, since it is not a secret.
The first deploy was a one-way door worth pausing on. Wrangler overwrites the Worker of the same name, so the moment CI ran, the hand-edited dashboard Worker was gone and the repo became the only source of truth. That was the point.
The KV counter did not make the trip. Workers analytics answers the same question without storing anyone’s IP forever.