@brandon_m_behring/book-scaffold-astro 3.2.0 → 3.4.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/LATEX_TO_MDX_MAPPING.md +130 -0
- package/dist/index.d.ts +70 -123
- package/dist/index.mjs +372 -138
- package/dist/schemas.d.ts +36 -2
- package/dist/schemas.mjs +183 -52
- package/package.json +3 -2
- package/pages/frontmatter/[...slug].astro +48 -0
- package/pages/print.astro +9 -1
- package/recipes/12-where-to-file-issues.md +58 -0
- package/scripts/build-bib.mjs +18 -0
- package/scripts/build-figures.mjs +19 -0
- package/scripts/build-labels.mjs +20 -0
- package/scripts/render-notebooks.mjs +19 -0
- package/scripts/validate.mjs +51 -37
- package/src/lib/freshness.ts +19 -4
package/dist/index.mjs
CHANGED
|
@@ -118,9 +118,232 @@ var init_katex_macros = __esm({
|
|
|
118
118
|
import mdx from "@astrojs/mdx";
|
|
119
119
|
import preact from "@astrojs/preact";
|
|
120
120
|
|
|
121
|
+
// src/profile-kit.ts
|
|
122
|
+
function defineProfile(p) {
|
|
123
|
+
return p;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/schemas.ts
|
|
127
|
+
import { z } from "astro/zod";
|
|
128
|
+
var toolSlugs = [
|
|
129
|
+
"claude-code",
|
|
130
|
+
"gemini-cli",
|
|
131
|
+
"codex-cli",
|
|
132
|
+
"cross-tool"
|
|
133
|
+
];
|
|
134
|
+
var volatilityLevels = [
|
|
135
|
+
"stable-principle",
|
|
136
|
+
"architectural-pattern",
|
|
137
|
+
"feature-surface"
|
|
138
|
+
];
|
|
139
|
+
var sourceTiers = [
|
|
140
|
+
"T1-official",
|
|
141
|
+
"T2-release-notes",
|
|
142
|
+
"T3-practitioner",
|
|
143
|
+
"T4-conjecture"
|
|
144
|
+
];
|
|
145
|
+
var changeKinds = ["added", "removed", "changed", "deprecated"];
|
|
146
|
+
var patternCategories = [
|
|
147
|
+
"safety",
|
|
148
|
+
"scale",
|
|
149
|
+
"context",
|
|
150
|
+
"interaction",
|
|
151
|
+
"extension",
|
|
152
|
+
"other"
|
|
153
|
+
];
|
|
154
|
+
var academicParts = [
|
|
155
|
+
"foundations",
|
|
156
|
+
"ssm-core",
|
|
157
|
+
"beyond-ssm",
|
|
158
|
+
"integration",
|
|
159
|
+
"synthesis"
|
|
160
|
+
];
|
|
161
|
+
var chapterStatus = [
|
|
162
|
+
"implemented",
|
|
163
|
+
"chapter_only",
|
|
164
|
+
"reading_only",
|
|
165
|
+
"prose_only",
|
|
166
|
+
"code_only",
|
|
167
|
+
"scaffolded",
|
|
168
|
+
"planned"
|
|
169
|
+
];
|
|
170
|
+
var academicChapterSchema = z.object({
|
|
171
|
+
week: z.number().int().min(1).max(99),
|
|
172
|
+
part: z.enum(academicParts),
|
|
173
|
+
title: z.string().min(1),
|
|
174
|
+
status: z.enum(chapterStatus),
|
|
175
|
+
roadmap_lines: z.tuple([z.number().int(), z.number().int()]).optional(),
|
|
176
|
+
code_path: z.string().optional(),
|
|
177
|
+
tests_path: z.string().optional(),
|
|
178
|
+
notebook_path: z.string().optional(),
|
|
179
|
+
description: z.string().optional(),
|
|
180
|
+
draft: z.boolean().default(false)
|
|
181
|
+
});
|
|
182
|
+
var toolsChapterSchema = z.object({
|
|
183
|
+
title: z.string().min(1),
|
|
184
|
+
part: z.number().int().min(0).max(10),
|
|
185
|
+
chapter: z.number().int().min(0).max(99),
|
|
186
|
+
volatility: z.enum(volatilityLevels),
|
|
187
|
+
tools_compared: z.array(z.enum(toolSlugs)).min(1),
|
|
188
|
+
last_verified: z.date(),
|
|
189
|
+
sources: z.array(z.string()).default([]),
|
|
190
|
+
description: z.string().optional(),
|
|
191
|
+
draft: z.boolean().default(false),
|
|
192
|
+
updated: z.date().optional()
|
|
193
|
+
});
|
|
194
|
+
var minimalChapterSchema = toolsChapterSchema;
|
|
195
|
+
var courseNotesChapterSchema = z.object({
|
|
196
|
+
// Identity
|
|
197
|
+
title: z.string().min(1),
|
|
198
|
+
chapter: z.number().int().min(0).max(99),
|
|
199
|
+
part: z.number().int().min(0).max(20).default(1),
|
|
200
|
+
description: z.string().optional(),
|
|
201
|
+
// Source attribution
|
|
202
|
+
course: z.string().optional(),
|
|
203
|
+
instructor: z.string().optional(),
|
|
204
|
+
source_url: z.string().url().optional(),
|
|
205
|
+
// Pedagogy
|
|
206
|
+
learning_outcomes: z.array(
|
|
207
|
+
z.object({
|
|
208
|
+
id: z.string(),
|
|
209
|
+
verb: z.string(),
|
|
210
|
+
text: z.string()
|
|
211
|
+
})
|
|
212
|
+
).default([]),
|
|
213
|
+
tags: z.array(z.string()).default([]),
|
|
214
|
+
// Provenance + status (shared shape with tools profile)
|
|
215
|
+
last_verified: z.date(),
|
|
216
|
+
volatility: z.enum(volatilityLevels).default("architectural-pattern"),
|
|
217
|
+
sources: z.array(z.string()).default([]),
|
|
218
|
+
draft: z.boolean().default(false)
|
|
219
|
+
});
|
|
220
|
+
var sourcesSchema = z.object({
|
|
221
|
+
url: z.string().url(),
|
|
222
|
+
title: z.string().min(1),
|
|
223
|
+
author: z.string().optional(),
|
|
224
|
+
publish_date: z.date().optional(),
|
|
225
|
+
captured_at: z.date(),
|
|
226
|
+
content_hash: z.string().regex(/^sha256:[a-f0-9]+$/).optional(),
|
|
227
|
+
tier: z.enum(sourceTiers),
|
|
228
|
+
tool: z.enum(toolSlugs),
|
|
229
|
+
perma_cc: z.string().url().nullable().optional(),
|
|
230
|
+
local_cache: z.string().nullable().optional()
|
|
231
|
+
});
|
|
232
|
+
var changelogSchema = z.object({
|
|
233
|
+
tool: z.enum(toolSlugs),
|
|
234
|
+
versions: z.array(
|
|
235
|
+
z.object({
|
|
236
|
+
version: z.string().min(1),
|
|
237
|
+
date: z.date(),
|
|
238
|
+
changes: z.array(
|
|
239
|
+
z.object({
|
|
240
|
+
pattern: z.string(),
|
|
241
|
+
kind: z.enum(changeKinds),
|
|
242
|
+
note: z.string().min(1),
|
|
243
|
+
source_key: z.string().optional()
|
|
244
|
+
})
|
|
245
|
+
).default([])
|
|
246
|
+
})
|
|
247
|
+
).default([])
|
|
248
|
+
});
|
|
249
|
+
var patternsSchema = z.object({
|
|
250
|
+
name: z.string().min(1),
|
|
251
|
+
description: z.string().optional(),
|
|
252
|
+
category: z.enum(patternCategories).optional(),
|
|
253
|
+
convergence_date: z.date().nullable().optional()
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// src/profiles/academic.ts
|
|
257
|
+
var academicProfile = defineProfile({
|
|
258
|
+
name: "academic",
|
|
259
|
+
schema: academicChapterSchema,
|
|
260
|
+
routes: {
|
|
261
|
+
references: true,
|
|
262
|
+
search: true,
|
|
263
|
+
print: true,
|
|
264
|
+
chapters: false,
|
|
265
|
+
// academic consumers ship their own week-based /chapters listing
|
|
266
|
+
convergence: false,
|
|
267
|
+
// tools-profile-specific
|
|
268
|
+
frontmatter: false
|
|
269
|
+
// opt-in per book; see #7
|
|
270
|
+
},
|
|
271
|
+
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"],
|
|
272
|
+
katex: true
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// src/profiles/tools.ts
|
|
276
|
+
var toolsProfile = defineProfile({
|
|
277
|
+
name: "tools",
|
|
278
|
+
schema: toolsChapterSchema,
|
|
279
|
+
routes: {
|
|
280
|
+
references: true,
|
|
281
|
+
search: true,
|
|
282
|
+
print: true,
|
|
283
|
+
chapters: true,
|
|
284
|
+
// tools profile ships a flat chapter index
|
|
285
|
+
convergence: true,
|
|
286
|
+
// tools profile ships convergence dashboard
|
|
287
|
+
frontmatter: false
|
|
288
|
+
// opt-in per book; see #7
|
|
289
|
+
},
|
|
290
|
+
styles: [
|
|
291
|
+
"tokens.css",
|
|
292
|
+
"layout.css",
|
|
293
|
+
"callouts.css",
|
|
294
|
+
"chapter.css",
|
|
295
|
+
"typography.css",
|
|
296
|
+
"print.css",
|
|
297
|
+
"convergence.css",
|
|
298
|
+
"tool-filter.css"
|
|
299
|
+
]
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// src/profiles/minimal.ts
|
|
303
|
+
var minimalProfile = defineProfile({
|
|
304
|
+
name: "minimal",
|
|
305
|
+
schema: minimalChapterSchema,
|
|
306
|
+
routes: {
|
|
307
|
+
references: true,
|
|
308
|
+
search: true,
|
|
309
|
+
print: true,
|
|
310
|
+
chapters: false,
|
|
311
|
+
convergence: false,
|
|
312
|
+
frontmatter: false
|
|
313
|
+
// opt-in per book; see #7
|
|
314
|
+
},
|
|
315
|
+
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"]
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// src/profiles/course-notes.ts
|
|
319
|
+
var courseNotesProfile = defineProfile({
|
|
320
|
+
name: "course-notes",
|
|
321
|
+
schema: courseNotesChapterSchema,
|
|
322
|
+
routes: {
|
|
323
|
+
references: true,
|
|
324
|
+
search: true,
|
|
325
|
+
print: true,
|
|
326
|
+
chapters: false,
|
|
327
|
+
// multi-book consumers route via [book]/[slug] themselves
|
|
328
|
+
convergence: false,
|
|
329
|
+
frontmatter: false
|
|
330
|
+
// opt-in per book; see #7
|
|
331
|
+
},
|
|
332
|
+
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"]
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// src/profiles/index.ts
|
|
336
|
+
var PROFILES = {
|
|
337
|
+
academic: academicProfile,
|
|
338
|
+
tools: toolsProfile,
|
|
339
|
+
minimal: minimalProfile,
|
|
340
|
+
"course-notes": courseNotesProfile
|
|
341
|
+
};
|
|
342
|
+
var BOOK_PROFILES = Object.keys(PROFILES);
|
|
343
|
+
|
|
121
344
|
// src/types.ts
|
|
122
345
|
import { existsSync, readFileSync } from "fs";
|
|
123
|
-
var
|
|
346
|
+
var BOOK_PRESETS = BOOK_PROFILES;
|
|
124
347
|
var BookConfigError = class extends Error {
|
|
125
348
|
constructor(message) {
|
|
126
349
|
super(message);
|
|
@@ -145,74 +368,131 @@ function readEnvFile(path = ".env") {
|
|
|
145
368
|
return {};
|
|
146
369
|
}
|
|
147
370
|
}
|
|
148
|
-
function
|
|
149
|
-
let candidate =
|
|
371
|
+
function resolvePreset(explicitPreset, explicitProfile) {
|
|
372
|
+
let candidate = explicitPreset ?? explicitProfile ?? process.env.BOOK_PRESET ?? process.env.BOOK_PROFILE;
|
|
150
373
|
let source = "default";
|
|
151
|
-
if (
|
|
152
|
-
else if (process.env.BOOK_PROFILE) source = "env";
|
|
374
|
+
if (explicitPreset || explicitProfile) source = "param";
|
|
375
|
+
else if (process.env.BOOK_PRESET || process.env.BOOK_PROFILE) source = "env";
|
|
153
376
|
if (!candidate) {
|
|
154
|
-
const
|
|
377
|
+
const env = readEnvFile();
|
|
378
|
+
const fromFile = env.BOOK_PRESET ?? env.BOOK_PROFILE;
|
|
155
379
|
if (fromFile) {
|
|
156
380
|
candidate = fromFile;
|
|
157
381
|
source = "dotenv";
|
|
158
382
|
}
|
|
159
383
|
}
|
|
160
384
|
candidate = candidate ?? "minimal";
|
|
161
|
-
if (!
|
|
385
|
+
if (!BOOK_PRESETS.includes(candidate)) {
|
|
162
386
|
throw new BookConfigError(
|
|
163
|
-
`
|
|
387
|
+
`preset must be one of ${BOOK_PRESETS.join(" | ")} (got ${JSON.stringify(candidate)})`
|
|
164
388
|
);
|
|
165
389
|
}
|
|
166
390
|
if (source === "default") {
|
|
167
|
-
console.warn("book-scaffold-astro:
|
|
391
|
+
console.warn("book-scaffold-astro: BOOK_PRESET not set; falling back to 'minimal'.");
|
|
168
392
|
}
|
|
169
393
|
return candidate;
|
|
170
394
|
}
|
|
395
|
+
function resolveProfile(explicit) {
|
|
396
|
+
return resolvePreset(void 0, explicit);
|
|
397
|
+
}
|
|
171
398
|
|
|
172
399
|
// src/integration.ts
|
|
173
400
|
import { fileURLToPath } from "url";
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
];
|
|
183
|
-
var TOOLS_ONLY_STYLES = ["convergence.css", "tool-filter.css"];
|
|
184
|
-
var DEFAULT_ROUTES_ALL = [
|
|
185
|
-
{ pattern: "/references", file: "references.astro" },
|
|
186
|
-
{ pattern: "/search", file: "search.astro" },
|
|
187
|
-
{ pattern: "/print", file: "print.astro" }
|
|
188
|
-
];
|
|
189
|
-
var DEFAULT_ROUTES_TOOLS = [
|
|
190
|
-
{ pattern: "/chapters", file: "chapters.astro" },
|
|
191
|
-
{ pattern: "/convergence", file: "convergence.astro" }
|
|
401
|
+
|
|
402
|
+
// src/mdx-components-resolver.ts
|
|
403
|
+
import { existsSync as existsSync2 } from "fs";
|
|
404
|
+
import { resolve } from "path";
|
|
405
|
+
var CANDIDATE_FILES = [
|
|
406
|
+
"src/mdx-components.ts",
|
|
407
|
+
"src/mdx-components.js",
|
|
408
|
+
"src/mdx-components.mjs"
|
|
192
409
|
];
|
|
410
|
+
var VIRTUAL_ID = "virtual:book-scaffold/mdx-components";
|
|
411
|
+
var RESOLVED_ID = "\0" + VIRTUAL_ID;
|
|
412
|
+
function resolveMdxComponentsPath(consumerRoot, explicit) {
|
|
413
|
+
if (explicit) {
|
|
414
|
+
const p = resolve(consumerRoot, explicit);
|
|
415
|
+
return existsSync2(p) ? p : null;
|
|
416
|
+
}
|
|
417
|
+
for (const candidate of CANDIDATE_FILES) {
|
|
418
|
+
const p = resolve(consumerRoot, candidate);
|
|
419
|
+
if (existsSync2(p)) return p;
|
|
420
|
+
}
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
function makeMdxComponentsVitePlugin(consumerPath) {
|
|
424
|
+
return {
|
|
425
|
+
name: "book-scaffold:mdx-components",
|
|
426
|
+
enforce: "pre",
|
|
427
|
+
resolveId(id) {
|
|
428
|
+
if (id === VIRTUAL_ID) return RESOLVED_ID;
|
|
429
|
+
return null;
|
|
430
|
+
},
|
|
431
|
+
load(id) {
|
|
432
|
+
if (id !== RESOLVED_ID) return null;
|
|
433
|
+
if (consumerPath === null) {
|
|
434
|
+
return "export default {};";
|
|
435
|
+
}
|
|
436
|
+
return `export { default } from ${JSON.stringify(consumerPath)};`;
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function defineMdxComponents(components) {
|
|
441
|
+
return components;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/integration.ts
|
|
445
|
+
var PACKAGE_NAME = "@brandon_m_behring/book-scaffold-astro";
|
|
446
|
+
var ROUTE_REGISTRY = {
|
|
447
|
+
references: { pattern: "/references", file: "references.astro" },
|
|
448
|
+
search: { pattern: "/search", file: "search.astro" },
|
|
449
|
+
print: { pattern: "/print", file: "print.astro" },
|
|
450
|
+
chapters: { pattern: "/chapters", file: "chapters.astro" },
|
|
451
|
+
convergence: { pattern: "/convergence", file: "convergence.astro" },
|
|
452
|
+
// v3.4.0 (#7): consumer-collection-backed frontmatter route. Opt-in via
|
|
453
|
+
// routes: { frontmatter: true } AND content.config.ts defining the
|
|
454
|
+
// collection (use frontmatterCollection() helper from /schemas subpath).
|
|
455
|
+
frontmatter: { pattern: "/frontmatter/[slug]", file: "frontmatter/[...slug].astro" }
|
|
456
|
+
};
|
|
193
457
|
function resolvePage(file) {
|
|
194
458
|
return fileURLToPath(new URL(`../pages/${file}`, import.meta.url));
|
|
195
459
|
}
|
|
196
460
|
function bookScaffoldIntegration(opts) {
|
|
197
|
-
const { profile, extraStyles = [] } = opts;
|
|
461
|
+
const { profile, routes: userOverrides = {}, extraStyles = [], mdxComponentsModule } = opts;
|
|
462
|
+
const def = PROFILES[profile];
|
|
463
|
+
const enabledRoutes = { ...def.routes, ...userOverrides };
|
|
198
464
|
return {
|
|
199
465
|
name: "book-scaffold-astro",
|
|
200
466
|
hooks: {
|
|
201
|
-
"astro:config:setup": ({ injectScript, injectRoute }) => {
|
|
202
|
-
const styles =
|
|
467
|
+
"astro:config:setup": ({ injectScript, injectRoute, updateConfig, config }) => {
|
|
468
|
+
const styles = [...def.styles, ...extraStyles];
|
|
203
469
|
for (const sheet of styles) {
|
|
204
470
|
injectScript("page-ssr", `import '${PACKAGE_NAME}/styles/${sheet}';`);
|
|
205
471
|
}
|
|
206
|
-
if (
|
|
472
|
+
if (def.katex) {
|
|
207
473
|
injectScript("page-ssr", "import 'katex/dist/katex.min.css';");
|
|
208
474
|
}
|
|
209
|
-
const
|
|
210
|
-
|
|
475
|
+
for (const [name, on] of Object.entries(enabledRoutes)) {
|
|
476
|
+
if (!on) continue;
|
|
477
|
+
const route = ROUTE_REGISTRY[name];
|
|
478
|
+
if (!route) continue;
|
|
211
479
|
injectRoute({
|
|
212
480
|
pattern: route.pattern,
|
|
213
481
|
entrypoint: resolvePage(route.file)
|
|
214
482
|
});
|
|
215
483
|
}
|
|
484
|
+
const consumerRoot = fileURLToPath(config.root);
|
|
485
|
+
const resolvedMdxPath = resolveMdxComponentsPath(consumerRoot, mdxComponentsModule);
|
|
486
|
+
const presetLiteral = JSON.stringify(profile);
|
|
487
|
+
updateConfig({
|
|
488
|
+
vite: {
|
|
489
|
+
plugins: [makeMdxComponentsVitePlugin(resolvedMdxPath)],
|
|
490
|
+
define: {
|
|
491
|
+
"import.meta.env.BOOK_PRESET": presetLiteral,
|
|
492
|
+
"import.meta.env.BOOK_PROFILE": presetLiteral
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
});
|
|
216
496
|
}
|
|
217
497
|
}
|
|
218
498
|
};
|
|
@@ -220,7 +500,7 @@ function bookScaffoldIntegration(opts) {
|
|
|
220
500
|
|
|
221
501
|
// src/config.ts
|
|
222
502
|
async function defineBookConfig(opts) {
|
|
223
|
-
const profile =
|
|
503
|
+
const profile = resolvePreset(opts.preset, opts.profile);
|
|
224
504
|
const remarkPlugins = [];
|
|
225
505
|
const rehypePlugins = [];
|
|
226
506
|
if (profile === "academic") {
|
|
@@ -249,7 +529,14 @@ async function defineBookConfig(opts) {
|
|
|
249
529
|
const integrations = [
|
|
250
530
|
mdx(),
|
|
251
531
|
preact(),
|
|
252
|
-
bookScaffoldIntegration({
|
|
532
|
+
bookScaffoldIntegration({
|
|
533
|
+
profile,
|
|
534
|
+
routes: opts.routes,
|
|
535
|
+
// v3.3.0 — per-route override (issue #3)
|
|
536
|
+
mdxComponentsModule: opts.mdxComponentsModule,
|
|
537
|
+
// v3.3.0 — explicit mdx-components path (issue #2)
|
|
538
|
+
extraStyles: opts.extraStyles
|
|
539
|
+
}),
|
|
253
540
|
...opts.extraIntegrations ?? []
|
|
254
541
|
];
|
|
255
542
|
const userMarkdown = opts.markdown ?? {};
|
|
@@ -266,13 +553,22 @@ async function defineBookConfig(opts) {
|
|
|
266
553
|
...userMarkdown
|
|
267
554
|
};
|
|
268
555
|
const {
|
|
556
|
+
preset: _preset,
|
|
557
|
+
// v3.4.0
|
|
269
558
|
profile: _profile,
|
|
559
|
+
routes: _routes,
|
|
560
|
+
// v3.3.0
|
|
561
|
+
mdxComponentsModule: _mdxComponentsModule,
|
|
562
|
+
// v3.3.0
|
|
270
563
|
extraIntegrations: _extraIntegrations,
|
|
271
564
|
extraStyles: _extraStyles,
|
|
272
565
|
markdown: _markdown,
|
|
273
566
|
...rest
|
|
274
567
|
} = opts;
|
|
568
|
+
void _preset;
|
|
275
569
|
void _profile;
|
|
570
|
+
void _routes;
|
|
571
|
+
void _mdxComponentsModule;
|
|
276
572
|
void _extraIntegrations;
|
|
277
573
|
void _extraStyles;
|
|
278
574
|
void _markdown;
|
|
@@ -293,110 +589,41 @@ async function defineBookConfig(opts) {
|
|
|
293
589
|
return config;
|
|
294
590
|
}
|
|
295
591
|
|
|
296
|
-
// src/
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
"
|
|
300
|
-
"
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
"
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
"synthesis"
|
|
330
|
-
];
|
|
331
|
-
var chapterStatus = [
|
|
332
|
-
"implemented",
|
|
333
|
-
"chapter_only",
|
|
334
|
-
"reading_only",
|
|
335
|
-
"prose_only",
|
|
336
|
-
"code_only",
|
|
337
|
-
"scaffolded",
|
|
338
|
-
"planned"
|
|
339
|
-
];
|
|
340
|
-
var academicChapterSchema = z.object({
|
|
341
|
-
week: z.number().int().min(1).max(99),
|
|
342
|
-
part: z.enum(academicParts),
|
|
343
|
-
title: z.string().min(1),
|
|
344
|
-
status: z.enum(chapterStatus),
|
|
345
|
-
roadmap_lines: z.tuple([z.number().int(), z.number().int()]).optional(),
|
|
346
|
-
code_path: z.string().optional(),
|
|
347
|
-
tests_path: z.string().optional(),
|
|
348
|
-
notebook_path: z.string().optional(),
|
|
349
|
-
description: z.string().optional(),
|
|
350
|
-
draft: z.boolean().default(false)
|
|
351
|
-
});
|
|
352
|
-
var toolsChapterSchema = z.object({
|
|
353
|
-
title: z.string().min(1),
|
|
354
|
-
part: z.number().int().min(0).max(10),
|
|
355
|
-
chapter: z.number().int().min(0).max(99),
|
|
356
|
-
volatility: z.enum(volatilityLevels),
|
|
357
|
-
tools_compared: z.array(z.enum(toolSlugs)).min(1),
|
|
358
|
-
last_verified: z.date(),
|
|
359
|
-
sources: z.array(z.string()).default([]),
|
|
360
|
-
description: z.string().optional(),
|
|
361
|
-
draft: z.boolean().default(false),
|
|
362
|
-
updated: z.date().optional()
|
|
363
|
-
});
|
|
364
|
-
var sourcesSchema = z.object({
|
|
365
|
-
url: z.string().url(),
|
|
366
|
-
title: z.string().min(1),
|
|
367
|
-
author: z.string().optional(),
|
|
368
|
-
publish_date: z.date().optional(),
|
|
369
|
-
captured_at: z.date(),
|
|
370
|
-
content_hash: z.string().regex(/^sha256:[a-f0-9]+$/).optional(),
|
|
371
|
-
tier: z.enum(sourceTiers),
|
|
372
|
-
tool: z.enum(toolSlugs),
|
|
373
|
-
perma_cc: z.string().url().nullable().optional(),
|
|
374
|
-
local_cache: z.string().nullable().optional()
|
|
375
|
-
});
|
|
376
|
-
var changelogSchema = z.object({
|
|
377
|
-
tool: z.enum(toolSlugs),
|
|
378
|
-
versions: z.array(
|
|
379
|
-
z.object({
|
|
380
|
-
version: z.string().min(1),
|
|
381
|
-
date: z.date(),
|
|
382
|
-
changes: z.array(
|
|
383
|
-
z.object({
|
|
384
|
-
pattern: z.string(),
|
|
385
|
-
kind: z.enum(changeKinds),
|
|
386
|
-
note: z.string().min(1),
|
|
387
|
-
source_key: z.string().optional()
|
|
388
|
-
})
|
|
389
|
-
).default([])
|
|
390
|
-
})
|
|
391
|
-
).default([])
|
|
392
|
-
});
|
|
393
|
-
var patternsSchema = z.object({
|
|
394
|
-
name: z.string().min(1),
|
|
395
|
-
description: z.string().optional(),
|
|
396
|
-
category: z.enum(patternCategories).optional(),
|
|
397
|
-
convergence_date: z.date().nullable().optional()
|
|
398
|
-
});
|
|
592
|
+
// src/lib/freshness.ts
|
|
593
|
+
var THRESHOLDS = {
|
|
594
|
+
"stable-principle": 365,
|
|
595
|
+
"architectural-pattern": 180,
|
|
596
|
+
"feature-surface": 90
|
|
597
|
+
};
|
|
598
|
+
var MS_PER_DAY = 1e3 * 60 * 60 * 24;
|
|
599
|
+
function getFreshness(lastVerified, volatility, now = /* @__PURE__ */ new Date()) {
|
|
600
|
+
if (!(lastVerified instanceof Date)) return null;
|
|
601
|
+
const thresholdDays = THRESHOLDS[volatility];
|
|
602
|
+
const daysOld = Math.floor((now.getTime() - lastVerified.getTime()) / MS_PER_DAY);
|
|
603
|
+
const daysUntil = thresholdDays - daysOld;
|
|
604
|
+
let status;
|
|
605
|
+
if (daysOld < thresholdDays * 0.75) {
|
|
606
|
+
status = "fresh";
|
|
607
|
+
} else if (daysOld < thresholdDays) {
|
|
608
|
+
status = "verify-soon";
|
|
609
|
+
} else {
|
|
610
|
+
status = "stale";
|
|
611
|
+
}
|
|
612
|
+
return { status, daysOld, thresholdDays, daysUntil };
|
|
613
|
+
}
|
|
614
|
+
function freshnessLabel(f) {
|
|
615
|
+
if (f === null) return "Verification status unknown";
|
|
616
|
+
switch (f.status) {
|
|
617
|
+
case "fresh":
|
|
618
|
+
return `Fresh (${f.daysOld}d old; verify within ${f.daysUntil}d)`;
|
|
619
|
+
case "verify-soon":
|
|
620
|
+
return `Verify soon (${f.daysOld}d old; ${f.daysUntil}d until stale)`;
|
|
621
|
+
case "stale":
|
|
622
|
+
return `Stale (${f.daysOld}d old; ${Math.abs(f.daysUntil)}d past threshold)`;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
399
625
|
export {
|
|
626
|
+
BOOK_PRESETS,
|
|
400
627
|
BOOK_PROFILES,
|
|
401
628
|
BookConfigError,
|
|
402
629
|
academicChapterSchema,
|
|
@@ -405,9 +632,16 @@ export {
|
|
|
405
632
|
changeKinds,
|
|
406
633
|
changelogSchema,
|
|
407
634
|
chapterStatus,
|
|
635
|
+
courseNotesChapterSchema,
|
|
408
636
|
defineBookConfig,
|
|
637
|
+
defineMdxComponents,
|
|
638
|
+
defineProfile,
|
|
639
|
+
freshnessLabel,
|
|
640
|
+
getFreshness,
|
|
641
|
+
minimalChapterSchema,
|
|
409
642
|
patternCategories,
|
|
410
643
|
patternsSchema,
|
|
644
|
+
resolvePreset,
|
|
411
645
|
resolveProfile,
|
|
412
646
|
sourceTiers,
|
|
413
647
|
sourcesSchema,
|
package/dist/schemas.d.ts
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineCollection } from 'astro:content';
|
|
2
|
+
import { g as BookSchemasOptions } from './types-Bb97Na9S.js';
|
|
2
3
|
import 'astro';
|
|
4
|
+
import 'astro/zod';
|
|
3
5
|
|
|
6
|
+
/**
|
|
7
|
+
* v3.4.0 (closes #7): consumer-facing helper to define a `frontmatter`
|
|
8
|
+
* content collection that the scaffold's auto-injected
|
|
9
|
+
* `/frontmatter/[slug]` route can render.
|
|
10
|
+
*
|
|
11
|
+
* Usage in consumer's content.config.ts:
|
|
12
|
+
*
|
|
13
|
+
* import { defineBookSchemas, frontmatterCollection } from
|
|
14
|
+
* '@brandon_m_behring/book-scaffold-astro/schemas';
|
|
15
|
+
* import { z } from 'astro:content';
|
|
16
|
+
*
|
|
17
|
+
* export const { collections } = {
|
|
18
|
+
* collections: {
|
|
19
|
+
* ...defineBookSchemas().collections,
|
|
20
|
+
* frontmatter: frontmatterCollection(z.object({
|
|
21
|
+
* slug: z.string(),
|
|
22
|
+
* title: z.string(),
|
|
23
|
+
* order: z.number(),
|
|
24
|
+
* description: z.string().optional(),
|
|
25
|
+
* })),
|
|
26
|
+
* },
|
|
27
|
+
* };
|
|
28
|
+
*
|
|
29
|
+
* Then enable the route via `defineBookConfig({ routes: { frontmatter: true } })`
|
|
30
|
+
* and drop MDX files under `src/content/frontmatter/`. The scaffold-injected
|
|
31
|
+
* route renders each entry with the consumer's mdx-components in scope (issue #2
|
|
32
|
+
* plumbing applies).
|
|
33
|
+
*
|
|
34
|
+
* Default loader: `**\/*.{md,mdx}` under `./src/content/frontmatter` (excluding
|
|
35
|
+
* underscore-prefixed files). Override `base` via the second arg.
|
|
36
|
+
*/
|
|
37
|
+
declare function frontmatterCollection(schema: Parameters<typeof defineCollection>[0]['schema'], base?: string): unknown;
|
|
4
38
|
/**
|
|
5
39
|
* Returns the package's default content collections. Closed shape per Q5;
|
|
6
40
|
* consumer extends via object spread and Zod `.extend()` (see PACKAGE_DESIGN.md §5).
|
|
@@ -9,4 +43,4 @@ declare function defineBookSchemas(opts?: BookSchemasOptions): {
|
|
|
9
43
|
collections: Record<string, unknown>;
|
|
10
44
|
};
|
|
11
45
|
|
|
12
|
-
export { defineBookSchemas };
|
|
46
|
+
export { defineBookSchemas, frontmatterCollection };
|