v3.7.0 — chapters profile-strategy refactor (#35) + academic visual baseline (#36)
Minor release. Refactors the /chapters route from a field-presence discriminator into a per-profile renderer strategy plugged into the existing PROFILES registry. Adds a visual-regression baseline for the academic /chapters route. Closes #35 and #36.
No breaking API changes. Consumer DOM output is byte-equivalent for tools profile (verified at AE=0 against all 3 pre-existing visual fixtures).
Architecture
| Layer | Owns |
|---|---|
Route (pages/chapters.astro) |
Data fetch, byPart grouping, ToolFilter wiring, CSS, inline filter script |
Renderer (per profile, src/profiles/renderers/*-chapters.ts) |
Numbering format, badge selection, sort key, data-tools value |
Registry (PROFILES[profile].chaptersRenderer) |
Dispatch point read at route render time |
Pure-function strategy — no Astro imports in renderer modules, preserving the chapter-sort.ts pattern that keeps tsup's DTS bundler stable.
Added
ChaptersRendererinterface and three implementations:toolsChaptersRenderer— numeric Part/Chapter, volatility badge, freshness affordance, tools-compared tags.academicChaptersRenderer— string-enum Part, Week N numbering, status badge.fallbackChaptersRenderer— field-presence dispatch (v3.5.2 logic, preserved for minimal / course-notes / research-portfolio safety net).
ProfileDefinition.chaptersRenderer?: ChaptersRenderer— optional field; each of the 5 shipped profiles wired to its renderer.package/tests/visual/fixture-academic-chapters/— new visual-regression fixture exercisingdefineBookConfig({ profile: 'academic', routes: { chapters: true } })end-to-end. 5 chapters across foundations / ssm-core / beyond-ssm / synthesis parts; 4 routes screenshotted at 4 viewport widths = 16 baselines.package/tests/chapters-renderer.test.mjs— 31 cases across all 3 renderers + cross-renderer agreement +chapterSortKeybackward-compat.
Changed
pages/chapters.astroprecomputes per-card render data in the frontmatter---block (sidesteps an Astro-compiler limitation where TypeScript generic casts inside{...}JSX expressions get parsed as tag-start tokens).package.jsonfilesallowlist addssrc/profiles/,src/profile-kit.ts,src/schemas.ts. Required for the refactored route's transitive imports to resolve at consumer build time — uncovered by the v3.6.5 pre-publish smoke gate before any npm ship.
Pre-publish smoke (v3.6.5 gate) caught two real bugs
- Pre-publish smoke surfaced both missing-file-in-tarball bugs before any npm publish. Process win — exactly the failure class v3.6.5 was designed to catch. Without it, v3.7.0 would have shipped a broken first build for every new consumer.
Backward compatibility
- Public API:
chapterSortKey(data)export retained; behavior unchanged. - New types added (additive):
ChaptersRenderer,PartKey,VolatilityBadge,StatusBadge,FreshnessAffordance. Three renderer instances exported. - Frontmatter schemas unchanged.
- DOM output byte-equivalent for tools profile (verified by visual regression).
Release policy
- D12 lock-step preserved:
@brandon_m_behring/create-book@3.7.0ships alongside the toolkit.