@brandon_m_behring/book-scaffold-astro 4.8.0 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +4 -1
- package/components/XRef.astro +27 -24
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +6 -0
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.mjs +6 -0
- package/package.json +1 -1
- package/scripts/build-labels.mjs +8 -5
- package/src/schemas.ts +3 -0
package/CLAUDE.md
CHANGED
|
@@ -25,6 +25,8 @@ When in doubt, run `grep BOOK_PROFILE .env astro.config.mjs src/content.config.t
|
|
|
25
25
|
|
|
26
26
|
## Frontmatter schemas
|
|
27
27
|
|
|
28
|
+
**Universal field (v4.9.0):** every profile accepts an optional `slug:` string that overrides the URL. A file `99-appendix.mdx` with `slug: appendix` is served at `/chapters/appendix/` — Astro's glob loader maps frontmatter `slug` → `entry.id`, and cross-references (`<XRef>`, via `build-labels`) resolve to the same path. Omit it and the URL falls back to the filename. Use it to keep numbered filenames for ordering while publishing clean URLs.
|
|
29
|
+
|
|
28
30
|
### Academic profile (`src/content.config.ts:academicChapterSchema`)
|
|
29
31
|
|
|
30
32
|
```yaml
|
|
@@ -34,6 +36,7 @@ part: foundations # required: foundations|ssm-core|beyond-ssm|integration
|
|
|
34
36
|
title: "..." # string, required
|
|
35
37
|
status: implemented # required: implemented|chapter_only|prose_only|code_only|reading_only|scaffolded|planned
|
|
36
38
|
# optional:
|
|
39
|
+
slug: ch01-introduction # clean URL override; else filename → /chapters/<slug>/
|
|
37
40
|
roadmap_lines: [10, 42] # [start, end] line refs into roadmap.md
|
|
38
41
|
code_path: experiments/jax/week01/foo.py
|
|
39
42
|
tests_path: experiments/jax/week01/test_foo.py
|
|
@@ -54,7 +57,7 @@ volatility: architectural-pattern # required: stable-principle|architectural-pa
|
|
|
54
57
|
tools_compared: [claude-code] # required, ≥1 of: claude-code|gemini-cli|codex-cli|cross-tool
|
|
55
58
|
last_verified: 2026-05-18 # date, required
|
|
56
59
|
sources: [] # array of source-manifest keys
|
|
57
|
-
# optional: description, draft, updated
|
|
60
|
+
# optional: slug (clean URL override), description, draft, updated
|
|
58
61
|
---
|
|
59
62
|
```
|
|
60
63
|
|
package/components/XRef.astro
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
2
|
+
// XRef — resolves a `\cref{label}` LaTeX reference to a hyperlink.
|
|
3
|
+
//
|
|
4
|
+
// Reads src/data/labels.json built by scripts/build-labels.mjs
|
|
5
|
+
// (Phase 2.6). For each known id, the map provides:
|
|
6
|
+
// { href: "/chapters/week04#thm-w4-stability", display: "Theorem 4.2" }
|
|
7
|
+
//
|
|
8
|
+
// Runtime: renders `<a href>` for known ids; renders an inline `[?id]`
|
|
9
|
+
// placeholder for unknown ids so the Astro dev server stays running while
|
|
10
|
+
// chapters are being authored or labels are being added.
|
|
11
|
+
//
|
|
12
|
+
// CI: `book-scaffold validate` (Phase 2.6, shipped) catches unknown ids
|
|
13
|
+
// and **fails the build with a non-zero exit code** before unresolved
|
|
14
|
+
// placeholders can reach production. The placeholder is a dev-ergonomic
|
|
15
|
+
// affordance, not a soft-degradation path on the deploy critical line.
|
|
16
|
+
//
|
|
17
|
+
// Bootstrapping note: when porting a book chapter-by-chapter, early
|
|
18
|
+
// chapters that reference yet-to-be-ported targets need either plain-prose
|
|
19
|
+
// substitutes or a temporarily-commented `{/* <XRef …/> */}` until the
|
|
20
|
+
// target chapter (and its `id="…"` attributes) exists. MDX uses JSX-style
|
|
21
|
+
// expression comments — the HTML `<` + bang + `--` form is NOT valid MDX.
|
|
22
|
+
//
|
|
23
|
+
// NB: these are `//` line comments, not a `/** */` block, on purpose — the
|
|
24
|
+
// literal `*/` in the example above would otherwise close a block comment
|
|
25
|
+
// early and break esbuild on every MDX import (the v4.9.0 fix).
|
|
26
|
+
//
|
|
27
|
+
// Usage:
|
|
28
|
+
// By <XRef id="thm:w4:stability" />, the discretized eigenvalues …
|
|
26
29
|
type LabelEntry = { href: string; display: string };
|
|
27
30
|
|
|
28
31
|
// Resolve labels.json from the consumer's project root (Vite resolves `/`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AstroUserConfig, AstroIntegration } from 'astro';
|
|
2
|
-
import { c as BookConfigOptions, f as BookScaffoldIntegrationOptions, Y as volatilityLevels, h as ChaptersRenderer, o as Style } from './types-
|
|
3
|
-
export { A as AcademicChapter, B as BOOK_PRESETS, a as BOOK_PROFILES, b as BookConfigError, d as BookPreset, e as BookProfile, g as BookSchemasOptions, C as ChapterFor, i as CourseNotesChapter, F as FreshnessAffordance, j as FrontmatterRouteConfig, M as MinimalChapter, P as PartKey, k as PartialRouteToggles, l as ProfileDefinition, m as Provenance, R as ResearchPortfolioChapter, n as RouteToggles, S as StatusBadge, p as StyleInput, T as ToolsChapter, V as VolatilityBadge, q as academicChapterSchema, r as academicParts, s as changeKinds, t as changelogSchema, u as chapterStatus, v as citationBackstops, w as composeStyles, x as courseNotesChapterSchema, y as defineProfile, z as defineStyle, D as minimalChapterSchema, E as normalizeFrontmatterConfig, G as patternCategories, H as patternsSchema, I as provenanceObject, J as provenanceSchema, K as researchPortfolioChapterSchema, L as resolvePreset, N as resolveProfile, O as sourceTiers, Q as sourceTiersResearch, U as sourcesSchema, W as toolSlugs, X as toolsChapterSchema } from './types-
|
|
2
|
+
import { c as BookConfigOptions, f as BookScaffoldIntegrationOptions, Y as volatilityLevels, h as ChaptersRenderer, o as Style } from './types-CULHImU4.js';
|
|
3
|
+
export { A as AcademicChapter, B as BOOK_PRESETS, a as BOOK_PROFILES, b as BookConfigError, d as BookPreset, e as BookProfile, g as BookSchemasOptions, C as ChapterFor, i as CourseNotesChapter, F as FreshnessAffordance, j as FrontmatterRouteConfig, M as MinimalChapter, P as PartKey, k as PartialRouteToggles, l as ProfileDefinition, m as Provenance, R as ResearchPortfolioChapter, n as RouteToggles, S as StatusBadge, p as StyleInput, T as ToolsChapter, V as VolatilityBadge, q as academicChapterSchema, r as academicParts, s as changeKinds, t as changelogSchema, u as chapterStatus, v as citationBackstops, w as composeStyles, x as courseNotesChapterSchema, y as defineProfile, z as defineStyle, D as minimalChapterSchema, E as normalizeFrontmatterConfig, G as patternCategories, H as patternsSchema, I as provenanceObject, J as provenanceSchema, K as researchPortfolioChapterSchema, L as resolvePreset, N as resolveProfile, O as sourceTiers, Q as sourceTiersResearch, U as sourcesSchema, W as toolSlugs, X as toolsChapterSchema } from './types-CULHImU4.js';
|
|
4
4
|
import 'astro/zod';
|
|
5
5
|
|
|
6
6
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -184,6 +184,8 @@ var academicChapterSchema = z.object({
|
|
|
184
184
|
week: z.number().int().min(1).max(99),
|
|
185
185
|
part: z.enum(academicParts),
|
|
186
186
|
title: z.string().min(1),
|
|
187
|
+
slug: z.string().optional(),
|
|
188
|
+
// v4.9.0: explicit URL slug override (else filename → entry.id)
|
|
187
189
|
status: z.enum(chapterStatus),
|
|
188
190
|
roadmap_lines: z.tuple([z.number().int(), z.number().int()]).optional(),
|
|
189
191
|
code_path: z.string().optional(),
|
|
@@ -203,6 +205,8 @@ var academicChapterSchema = z.object({
|
|
|
203
205
|
});
|
|
204
206
|
var toolsChapterSchema = z.object({
|
|
205
207
|
title: z.string().min(1),
|
|
208
|
+
slug: z.string().optional(),
|
|
209
|
+
// v4.9.0: explicit URL slug override (else filename → entry.id)
|
|
206
210
|
part: z.number().int().min(0).max(10),
|
|
207
211
|
chapter: z.number().int().min(0).max(99),
|
|
208
212
|
volatility: z.enum(volatilityLevels),
|
|
@@ -226,6 +230,8 @@ var sourceTiersResearch = ["T1", "T2", "T3", "T4"];
|
|
|
226
230
|
var courseNotesChapterSchema = z.object({
|
|
227
231
|
// Identity
|
|
228
232
|
title: z.string().min(1),
|
|
233
|
+
slug: z.string().optional(),
|
|
234
|
+
// v4.9.0: explicit URL slug override (else filename → entry.id)
|
|
229
235
|
chapter: z.number().int().min(0).max(99),
|
|
230
236
|
part: z.number().int().min(0).max(20).default(1),
|
|
231
237
|
description: z.string().optional(),
|
package/dist/schemas.d.ts
CHANGED
package/dist/schemas.mjs
CHANGED
|
@@ -68,6 +68,8 @@ var academicChapterSchema = z.object({
|
|
|
68
68
|
week: z.number().int().min(1).max(99),
|
|
69
69
|
part: z.enum(academicParts),
|
|
70
70
|
title: z.string().min(1),
|
|
71
|
+
slug: z.string().optional(),
|
|
72
|
+
// v4.9.0: explicit URL slug override (else filename → entry.id)
|
|
71
73
|
status: z.enum(chapterStatus),
|
|
72
74
|
roadmap_lines: z.tuple([z.number().int(), z.number().int()]).optional(),
|
|
73
75
|
code_path: z.string().optional(),
|
|
@@ -87,6 +89,8 @@ var academicChapterSchema = z.object({
|
|
|
87
89
|
});
|
|
88
90
|
var toolsChapterSchema = z.object({
|
|
89
91
|
title: z.string().min(1),
|
|
92
|
+
slug: z.string().optional(),
|
|
93
|
+
// v4.9.0: explicit URL slug override (else filename → entry.id)
|
|
90
94
|
part: z.number().int().min(0).max(10),
|
|
91
95
|
chapter: z.number().int().min(0).max(99),
|
|
92
96
|
volatility: z.enum(volatilityLevels),
|
|
@@ -110,6 +114,8 @@ var sourceTiersResearch = ["T1", "T2", "T3", "T4"];
|
|
|
110
114
|
var courseNotesChapterSchema = z.object({
|
|
111
115
|
// Identity
|
|
112
116
|
title: z.string().min(1),
|
|
117
|
+
slug: z.string().optional(),
|
|
118
|
+
// v4.9.0: explicit URL slug override (else filename → entry.id)
|
|
113
119
|
chapter: z.number().int().min(0).max(99),
|
|
114
120
|
part: z.number().int().min(0).max(20).default(1),
|
|
115
121
|
description: z.string().optional(),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brandon_m_behring/book-scaffold-astro",
|
|
3
3
|
"description": "Astro 6 + MDX toolkit for long-form technical books. Profile-aware (academic / tools / minimal); ships Tufte typography, KaTeX, BibTeX citations, Pagefind, Cloudflare Workers deploy. See PACKAGE_DESIGN.md for the API contract.",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.9.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Brandon Behring",
|
package/scripts/build-labels.mjs
CHANGED
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
* - tools profile: `chapter` field (number).
|
|
16
16
|
* - academic profile: `week` field (number).
|
|
17
17
|
*
|
|
18
|
-
* Slug used for the href
|
|
19
|
-
* the consumer's pages
|
|
20
|
-
* using `[...slug].astro`
|
|
21
|
-
* filenames identically
|
|
18
|
+
* Slug used for the href: the chapter's frontmatter `slug:` if set,
|
|
19
|
+
* else filename minus `.mdx`. The href shape mirrors the consumer's pages
|
|
20
|
+
* router: `/chapters/<slug>#<id>`. Academic books using `[...slug].astro`
|
|
21
|
+
* get the same shape since Astro slugifies filenames identically when no
|
|
22
|
+
* frontmatter override is present.
|
|
22
23
|
*
|
|
23
24
|
* Optional override:
|
|
24
25
|
* <Theorem id="…" label="Custom display" />
|
|
@@ -178,7 +179,9 @@ async function main() {
|
|
|
178
179
|
const source = await readFile(file, 'utf8');
|
|
179
180
|
const fm = parseFrontmatter(source);
|
|
180
181
|
const chapterNum = chapterNumberOf(fm);
|
|
181
|
-
const slug =
|
|
182
|
+
const slug = (typeof fm.slug === 'string' && fm.slug.length > 0)
|
|
183
|
+
? fm.slug
|
|
184
|
+
: basename(file).replace(/\.mdx?$/, '');
|
|
182
185
|
|
|
183
186
|
// Per-chapter counters reset for each file.
|
|
184
187
|
const counters = {};
|
package/src/schemas.ts
CHANGED
|
@@ -116,6 +116,7 @@ export const academicChapterSchema = z.object({
|
|
|
116
116
|
week: z.number().int().min(1).max(99),
|
|
117
117
|
part: z.enum(academicParts),
|
|
118
118
|
title: z.string().min(1),
|
|
119
|
+
slug: z.string().optional(), // v4.9.0: explicit URL slug override (else filename → entry.id)
|
|
119
120
|
status: z.enum(chapterStatus),
|
|
120
121
|
roadmap_lines: z.tuple([z.number().int(), z.number().int()]).optional(),
|
|
121
122
|
code_path: z.string().optional(),
|
|
@@ -136,6 +137,7 @@ export const academicChapterSchema = z.object({
|
|
|
136
137
|
|
|
137
138
|
export const toolsChapterSchema = z.object({
|
|
138
139
|
title: z.string().min(1),
|
|
140
|
+
slug: z.string().optional(), // v4.9.0: explicit URL slug override (else filename → entry.id)
|
|
139
141
|
part: z.number().int().min(0).max(10),
|
|
140
142
|
chapter: z.number().int().min(0).max(99),
|
|
141
143
|
volatility: z.enum(volatilityLevels),
|
|
@@ -178,6 +180,7 @@ export const sourceTiersResearch = ['T1', 'T2', 'T3', 'T4'] as const;
|
|
|
178
180
|
export const courseNotesChapterSchema = z.object({
|
|
179
181
|
// Identity
|
|
180
182
|
title: z.string().min(1),
|
|
183
|
+
slug: z.string().optional(), // v4.9.0: explicit URL slug override (else filename → entry.id)
|
|
181
184
|
chapter: z.number().int().min(0).max(99),
|
|
182
185
|
part: z.number().int().min(0).max(20).default(1),
|
|
183
186
|
description: z.string().optional(),
|