Skip to main content

max / makenotwork

Release mnw-server v0.11.17 Two fixes, one visible and one that was hiding everything. THE PAGE HAD NO JAVASCRIPT AT ALL. `/static/dist/**` is an ES module graph and it cannot use the `?v=` fingerprint every other asset gets: the template fingerprints the entry point, but the entry's own `import './dispatch.js'` is a bare relative URL a deploy never changes. Under a week-long Cache-Control, Cloudflare served a stale `dispatch.js` beside a fresh `index.js`, the two disagreed about an export name, and the bundle died with a SyntaxError -- which takes every island with it, because core/index.ts side-effect-imports all of them. Found by loading the page in headless Chrome and reading the console, after the served files each looked correct on their own. So `/static/dist` now revalidates. `no-cache` is "cache it, check it", not "do not cache": a few KB with an ETag, answered 304. The better fix is a fingerprinted directory that relative imports inherit for free; filed. THE CAROUSEL FRAMES HAD NO EDGE. makeover-webview 0.37.0: a picture's frame is a border, not the inset bevel every other surface takes. You cannot bevel a surface you do not own -- an inset edge on a picture is drawn on top of the picture, and on light-on-light screenshots it vanished.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-14 19:35 UTC
Signed with PGP, not checked
Commit: 8210910667905d1efc3a12591a3a5e51847ba63a
Parent: bbf6e25
4 files changed, +40 insertions, -8 deletions
@@ -5196,7 +5196,7 @@
5196 5196
5197 5197 [[package]]
5198 5198 name = "makenotwork"
5199 - version = "0.11.16"
5199 + version = "0.11.17"
5200 5200 dependencies = [
5201 5201 "ammonia",
5202 5202 "anyhow",
@@ -5316,9 +5316,9 @@
5316 5316
5317 5317 [[package]]
5318 5318 name = "makeover-build"
5319 - version = "0.29.0"
5319 + version = "0.30.0"
5320 5320 source = "registry+https://github.com/rust-lang/crates.io-index"
5321 - checksum = "fd3ab91de0b8a6f7d355c931bfcf88d13f39da1712775ef0b1e9737b42620b97"
5321 + checksum = "86bea7fceaf452e085eea35efd010573c48f370655645dc3d52c805761421c37"
5322 5322 dependencies = [
5323 5323 "makeover",
5324 5324 "makeover-geometry",
@@ -5349,9 +5349,9 @@
5349 5349
5350 5350 [[package]]
5351 5351 name = "makeover-webview"
5352 - version = "0.36.0"
5352 + version = "0.37.0"
5353 5353 source = "registry+https://github.com/rust-lang/crates.io-index"
5354 - checksum = "38e3d53ed93a0e4e0c90244186320a91a59cfa3ed91bca66e6fbfc4c7eda2acc"
5354 + checksum = "678efbe9ea9634b446eff14e8b43c033a186195dd70004ef166c1684fa02aba9"
5355 5355 dependencies = [
5356 5356 "makeover-geometry",
5357 5357 "makeover-layout",
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makenotwork"
3 - version = "0.11.16"
3 + version = "0.11.17"
4 4 edition = "2024"
5 5 license = "LicenseRef-PolyForm-Noncommercial-1.0.0"
6 6 # Server binary: never published to a registry. Marks the crate private so
@@ -240,7 +240,7 @@
240 240 # Emits static/geometry.css (makeover-geometry) and static/layout.css
241 241 # (makeover-webview). The same generator GO and BB run; only the output paths
242 242 # differ, since the server serves its stylesheets rather than bundling them.
243 - makeover-build = "0.29.0"
243 + makeover-build = "0.30.0"
244 244 # Read directly for the embeds' own copy of the spacing layer, which they need
245 245 # because an iframe cannot link a stylesheet. makeover-build does not re-export
246 246 # it, so the pin lives here and has to track the one makeover-build resolves.
@@ -6,7 +6,7 @@
6 6 "license": {
7 7 "name": "PolyForm Noncommercial 1.0.0"
8 8 },
9 - "version": "0.11.16"
9 + "version": "0.11.17"
10 10 },
11 11 "paths": {
12 12 "/api/git/{owner}/{repo}/notes": {
@@ -591,6 +591,38 @@
591 591 "/api-docs/openapi.json",
592 592 <openapi::ApiDoc as utoipa::OpenApi>::openapi(),
593 593 ))
594 + // The ES module graph, which cannot use the `?v=` fingerprint.
595 + //
596 + // Every other static asset is referenced from a template with
597 + // `?v=<content hash>`, so a week-long cache is safe: a deploy changes
598 + // the URL. A module graph is the exception and it bit on 2026-08-14.
599 + // The templates fingerprint the ENTRY point (`core/index.js?v=...`),
600 + // but the entry's own `import './dispatch.js'` is a bare relative URL
601 + // that a deploy never changes. Cloudflare kept serving a seven-day-old
602 + // `dispatch.js` beside a fresh `index.js`, the two disagreed about an
603 + // export name, and the whole bundle died with a SyntaxError -- which
604 + // takes every island on the page with it, since `core/index.ts`
605 + // side-effect-imports all of them.
606 + //
607 + // `no-cache` is "cache it, revalidate every time", not "do not cache".
608 + // These files are a few KB each and carry an ETag, so the common answer
609 + // is a 304 with no body. That is the right trade for a graph whose
610 + // members must agree with each other.
611 + //
612 + // The alternative considered was serving the whole directory under a
613 + // fingerprinted path (`/static/dist-<hash>/...`), which relative
614 + // imports would inherit for free and which keeps the long cache. It is
615 + // the better answer and it is a build.rs change; filed rather than done
616 + // here, because this had a page down.
617 + .nest_service(
618 + "/static/dist",
619 + tower::ServiceBuilder::new()
620 + .layer(SetResponseHeaderLayer::overriding(
621 + axum::http::header::CACHE_CONTROL,
622 + HeaderValue::from_static("public, no-cache"),
623 + ))
624 + .service(ServeDir::new("static/dist")),
625 + )
594 626 .nest_service(
595 627 "/static",
596 628 tower::ServiceBuilder::new()