@brandon_m_behring/book-scaffold-astro 3.6.5 → 3.7.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/dist/index.d.ts +43 -3
- package/dist/index.mjs +222 -42
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.mjs +216 -5
- package/package.json +4 -1
- package/pages/chapters.astro +126 -125
- package/src/lib/chapters-renderer.ts +99 -0
- package/src/profile-kit.ts +100 -0
- package/src/profiles/academic.ts +30 -0
- package/src/profiles/course-notes.ts +32 -0
- package/src/profiles/index.ts +54 -0
- package/src/profiles/minimal.ts +27 -0
- package/src/profiles/renderers/academic-chapters.ts +102 -0
- package/src/profiles/renderers/fallback-chapters.ts +87 -0
- package/src/profiles/renderers/tools-chapters.ts +102 -0
- package/src/profiles/research-portfolio.ts +44 -0
- package/src/profiles/tools.ts +29 -0
- package/src/schemas.ts +291 -0
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,
|
|
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,
|
|
2
|
+
import { c as BookConfigOptions, f as BookScaffoldIntegrationOptions, H as volatilityLevels, h as ChaptersRenderer } from './types-C5CamFM0.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, M as MinimalChapter, P as PartKey, j as ProfileDefinition, R as ResearchPortfolioChapter, k as RouteToggles, S as StatusBadge, T as ToolsChapter, V as VolatilityBadge, l as academicChapterSchema, m as academicParts, n as changeKinds, o as changelogSchema, p as chapterStatus, q as courseNotesChapterSchema, r as defineProfile, s as minimalChapterSchema, t as patternCategories, u as patternsSchema, v as researchPortfolioChapterSchema, w as resolvePreset, x as resolveProfile, y as sourceTiers, z as sourceTiersResearch, D as sourcesSchema, E as toolSlugs, G as toolsChapterSchema } from './types-C5CamFM0.js';
|
|
4
4
|
import 'astro/zod';
|
|
5
5
|
|
|
6
6
|
declare function defineBookConfig(opts: BookConfigOptions): Promise<AstroUserConfig>;
|
|
@@ -103,4 +103,44 @@ declare function freshnessLabel(f: Freshness | null): string;
|
|
|
103
103
|
*/
|
|
104
104
|
declare function chapterSortKey(data: Record<string, unknown>): number;
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
/**
|
|
107
|
+
* src/profiles/renderers/tools-chapters.ts — ChaptersRenderer implementation
|
|
108
|
+
* for the tools profile. Owns: numeric part grouping (with Appendix splitting
|
|
109
|
+
* at part >= 6), Chapter N numbering, volatility badge, freshness affordance
|
|
110
|
+
* from last_verified + volatility class, tools_compared tags.
|
|
111
|
+
*
|
|
112
|
+
* Mirrors the pre-v3.7.0 logic in pages/chapters.astro for tools-shape
|
|
113
|
+
* chapters — DOM output is intended to be byte-equivalent so the existing
|
|
114
|
+
* visual-regression baselines (package/tests/visual/fixture/) pass without
|
|
115
|
+
* recapture.
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
declare const toolsChaptersRenderer: ChaptersRenderer;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* src/profiles/renderers/academic-chapters.ts — ChaptersRenderer implementation
|
|
122
|
+
* for the academic profile. Owns: string-enum part grouping (foundations,
|
|
123
|
+
* ssm-core, beyond-ssm, integration, synthesis), Week N numbering, status
|
|
124
|
+
* badge (7-state). No freshness/last_verified, no tools_compared.
|
|
125
|
+
*
|
|
126
|
+
* Mirrors the v3.5.2 academic branch of pages/chapters.astro — DOM output
|
|
127
|
+
* intended to match exactly so academic visual baselines stay stable.
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
declare const academicChaptersRenderer: ChaptersRenderer;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* src/profiles/renderers/fallback-chapters.ts — ChaptersRenderer used by
|
|
134
|
+
* profiles that don't ship a dedicated renderer (minimal, course-notes,
|
|
135
|
+
* research-portfolio). Dispatches by field presence — exactly the v3.5.2
|
|
136
|
+
* logic that lived inline in pages/chapters.astro before #35.
|
|
137
|
+
*
|
|
138
|
+
* Safety net for shapes we haven't designed for explicitly. If a consumer
|
|
139
|
+
* opts a course-notes or research-portfolio book into `routes.chapters: true`,
|
|
140
|
+
* the fallback renders reasonably without crashing. Custom output for those
|
|
141
|
+
* profiles is a v4+ extension point (consumer-overridable renderer).
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
declare const fallbackChaptersRenderer: ChaptersRenderer;
|
|
145
|
+
|
|
146
|
+
export { BookConfigOptions, BookScaffoldIntegrationOptions, ChaptersRenderer, type Freshness, type FreshnessStatus, type VolatilityLevel, academicChaptersRenderer, bookScaffoldIntegration, chapterSortKey, defineBookConfig, defineMdxComponents, fallbackChaptersRenderer, freshnessLabel, getFreshness, toolsChaptersRenderer, volatilityLevels };
|
package/dist/index.mjs
CHANGED
|
@@ -307,6 +307,63 @@ var patternsSchema = z.object({
|
|
|
307
307
|
convergence_date: z.date().nullable().optional()
|
|
308
308
|
});
|
|
309
309
|
|
|
310
|
+
// src/profiles/renderers/academic-chapters.ts
|
|
311
|
+
var ACADEMIC_PART_ORDINAL = {
|
|
312
|
+
foundations: 1,
|
|
313
|
+
"ssm-core": 2,
|
|
314
|
+
"beyond-ssm": 3,
|
|
315
|
+
integration: 4,
|
|
316
|
+
synthesis: 5
|
|
317
|
+
};
|
|
318
|
+
var UNKNOWN_PART_ORDINAL = 99;
|
|
319
|
+
function titleCase(part) {
|
|
320
|
+
return part.split("-").map((w) => w.length > 0 ? w.charAt(0).toUpperCase() + w.slice(1) : "").join(" ");
|
|
321
|
+
}
|
|
322
|
+
var academicChaptersRenderer = {
|
|
323
|
+
partKey(data) {
|
|
324
|
+
return data.part ?? "";
|
|
325
|
+
},
|
|
326
|
+
formatPartLabel(part) {
|
|
327
|
+
if (typeof part === "string" && part.length > 0) {
|
|
328
|
+
return titleCase(part);
|
|
329
|
+
}
|
|
330
|
+
return String(part);
|
|
331
|
+
},
|
|
332
|
+
isAppendix(_part) {
|
|
333
|
+
return false;
|
|
334
|
+
},
|
|
335
|
+
formatChapterNumber(data, _appendix) {
|
|
336
|
+
const week = data.week ?? 0;
|
|
337
|
+
return `Week ${week}`;
|
|
338
|
+
},
|
|
339
|
+
getToolsAttr(_data) {
|
|
340
|
+
return "cross-tool";
|
|
341
|
+
},
|
|
342
|
+
getVolatilityData(_data) {
|
|
343
|
+
return null;
|
|
344
|
+
},
|
|
345
|
+
getStatusData(data) {
|
|
346
|
+
const status = data.status;
|
|
347
|
+
if (!status) return null;
|
|
348
|
+
return { status, label: status };
|
|
349
|
+
},
|
|
350
|
+
getFreshnessData(_data, _now) {
|
|
351
|
+
return null;
|
|
352
|
+
},
|
|
353
|
+
getVerifiedDateLabel(_data) {
|
|
354
|
+
return null;
|
|
355
|
+
},
|
|
356
|
+
getToolsCompared(_data) {
|
|
357
|
+
return [];
|
|
358
|
+
},
|
|
359
|
+
sortKey(data) {
|
|
360
|
+
const partRaw = data.part;
|
|
361
|
+
const partOrdinal = typeof partRaw === "string" ? ACADEMIC_PART_ORDINAL[partRaw] ?? UNKNOWN_PART_ORDINAL : typeof partRaw === "number" ? partRaw : UNKNOWN_PART_ORDINAL;
|
|
362
|
+
const week = typeof data.week === "number" ? data.week : 0;
|
|
363
|
+
return partOrdinal * 1e3 + week;
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
310
367
|
// src/profiles/academic.ts
|
|
311
368
|
var academicProfile = defineProfile({
|
|
312
369
|
name: "academic",
|
|
@@ -323,9 +380,104 @@ var academicProfile = defineProfile({
|
|
|
323
380
|
// opt-in per book; see #7
|
|
324
381
|
},
|
|
325
382
|
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"],
|
|
326
|
-
katex: true
|
|
383
|
+
katex: true,
|
|
384
|
+
chaptersRenderer: academicChaptersRenderer
|
|
385
|
+
// v3.7.0 (#35) — owns /chapters semantics if consumer opts in via routes.chapters
|
|
327
386
|
});
|
|
328
387
|
|
|
388
|
+
// src/lib/freshness.ts
|
|
389
|
+
var THRESHOLDS = {
|
|
390
|
+
"stable-principle": 365,
|
|
391
|
+
"architectural-pattern": 180,
|
|
392
|
+
"feature-surface": 90
|
|
393
|
+
};
|
|
394
|
+
var MS_PER_DAY = 1e3 * 60 * 60 * 24;
|
|
395
|
+
function getFreshness(lastVerified, volatility, now = /* @__PURE__ */ new Date()) {
|
|
396
|
+
if (!(lastVerified instanceof Date)) return null;
|
|
397
|
+
const thresholdDays = THRESHOLDS[volatility];
|
|
398
|
+
const daysOld = Math.floor((now.getTime() - lastVerified.getTime()) / MS_PER_DAY);
|
|
399
|
+
const daysUntil = thresholdDays - daysOld;
|
|
400
|
+
let status;
|
|
401
|
+
if (daysOld < thresholdDays * 0.75) {
|
|
402
|
+
status = "fresh";
|
|
403
|
+
} else if (daysOld < thresholdDays) {
|
|
404
|
+
status = "verify-soon";
|
|
405
|
+
} else {
|
|
406
|
+
status = "stale";
|
|
407
|
+
}
|
|
408
|
+
return { status, daysOld, thresholdDays, daysUntil };
|
|
409
|
+
}
|
|
410
|
+
function freshnessLabel(f) {
|
|
411
|
+
if (f === null) return "Verification status unknown";
|
|
412
|
+
switch (f.status) {
|
|
413
|
+
case "fresh":
|
|
414
|
+
return `Fresh (${f.daysOld}d old; verify within ${f.daysUntil}d)`;
|
|
415
|
+
case "verify-soon":
|
|
416
|
+
return `Verify soon (${f.daysOld}d old; ${f.daysUntil}d until stale)`;
|
|
417
|
+
case "stale":
|
|
418
|
+
return `Stale (${f.daysOld}d old; ${Math.abs(f.daysUntil)}d past threshold)`;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/profiles/renderers/tools-chapters.ts
|
|
423
|
+
function formatDate(d) {
|
|
424
|
+
return d.toISOString().slice(0, 10);
|
|
425
|
+
}
|
|
426
|
+
var toolsChaptersRenderer = {
|
|
427
|
+
partKey(data) {
|
|
428
|
+
return data.part ?? 0;
|
|
429
|
+
},
|
|
430
|
+
formatPartLabel(part) {
|
|
431
|
+
if (typeof part === "number") {
|
|
432
|
+
return part >= 6 ? "Appendices" : `Part ${part}`;
|
|
433
|
+
}
|
|
434
|
+
return String(part);
|
|
435
|
+
},
|
|
436
|
+
isAppendix(part) {
|
|
437
|
+
return typeof part === "number" && part >= 6;
|
|
438
|
+
},
|
|
439
|
+
formatChapterNumber(data, appendix) {
|
|
440
|
+
const chapter = data.chapter ?? 0;
|
|
441
|
+
if (appendix) {
|
|
442
|
+
return `Appendix ${String.fromCharCode(64 + chapter).toLowerCase()}`;
|
|
443
|
+
}
|
|
444
|
+
return `Chapter ${chapter}`;
|
|
445
|
+
},
|
|
446
|
+
getToolsAttr(data) {
|
|
447
|
+
const tools = data.tools_compared ?? [];
|
|
448
|
+
return tools.join(" ");
|
|
449
|
+
},
|
|
450
|
+
getVolatilityData(data) {
|
|
451
|
+
const level = data.volatility;
|
|
452
|
+
if (!level) return null;
|
|
453
|
+
return { level, label: level };
|
|
454
|
+
},
|
|
455
|
+
getStatusData(_data) {
|
|
456
|
+
return null;
|
|
457
|
+
},
|
|
458
|
+
getFreshnessData(data, now) {
|
|
459
|
+
const lastVerified = data.last_verified;
|
|
460
|
+
const volatility = data.volatility;
|
|
461
|
+
if (!lastVerified || !volatility) return null;
|
|
462
|
+
const f = getFreshness(lastVerified, volatility, now);
|
|
463
|
+
if (!f) return null;
|
|
464
|
+
return { status: f.status, label: freshnessLabel(f) };
|
|
465
|
+
},
|
|
466
|
+
getVerifiedDateLabel(data) {
|
|
467
|
+
const lastVerified = data.last_verified;
|
|
468
|
+
if (!(lastVerified instanceof Date)) return null;
|
|
469
|
+
return `verified ${formatDate(lastVerified)}`;
|
|
470
|
+
},
|
|
471
|
+
getToolsCompared(data) {
|
|
472
|
+
return (data.tools_compared ?? []).slice();
|
|
473
|
+
},
|
|
474
|
+
sortKey(data) {
|
|
475
|
+
const part = data.part ?? 0;
|
|
476
|
+
const chapter = data.chapter ?? 0;
|
|
477
|
+
return part * 1e3 + chapter;
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
329
481
|
// src/profiles/tools.ts
|
|
330
482
|
var toolsProfile = defineProfile({
|
|
331
483
|
name: "tools",
|
|
@@ -350,9 +502,62 @@ var toolsProfile = defineProfile({
|
|
|
350
502
|
"print.css",
|
|
351
503
|
"convergence.css",
|
|
352
504
|
"tool-filter.css"
|
|
353
|
-
]
|
|
505
|
+
],
|
|
506
|
+
chaptersRenderer: toolsChaptersRenderer
|
|
507
|
+
// v3.7.0 (#35) — owns /chapters semantics for tools shape
|
|
354
508
|
});
|
|
355
509
|
|
|
510
|
+
// src/profiles/renderers/fallback-chapters.ts
|
|
511
|
+
function isToolsShape(data) {
|
|
512
|
+
return "volatility" in data && "chapter" in data;
|
|
513
|
+
}
|
|
514
|
+
function isAcademicShape(data) {
|
|
515
|
+
return "week" in data && "status" in data;
|
|
516
|
+
}
|
|
517
|
+
function dispatch(data) {
|
|
518
|
+
if (isToolsShape(data)) return toolsChaptersRenderer;
|
|
519
|
+
if (isAcademicShape(data)) return academicChaptersRenderer;
|
|
520
|
+
return toolsChaptersRenderer;
|
|
521
|
+
}
|
|
522
|
+
var fallbackChaptersRenderer = {
|
|
523
|
+
partKey(data) {
|
|
524
|
+
return data.part ?? 0;
|
|
525
|
+
},
|
|
526
|
+
formatPartLabel(part) {
|
|
527
|
+
if (typeof part === "number") {
|
|
528
|
+
return part >= 6 ? "Appendices" : `Part ${part}`;
|
|
529
|
+
}
|
|
530
|
+
return dispatch({ part }).formatPartLabel(part);
|
|
531
|
+
},
|
|
532
|
+
isAppendix(part) {
|
|
533
|
+
return typeof part === "number" && part >= 6;
|
|
534
|
+
},
|
|
535
|
+
formatChapterNumber(data, appendix) {
|
|
536
|
+
return dispatch(data).formatChapterNumber(data, appendix);
|
|
537
|
+
},
|
|
538
|
+
getToolsAttr(data) {
|
|
539
|
+
return dispatch(data).getToolsAttr(data);
|
|
540
|
+
},
|
|
541
|
+
getVolatilityData(data) {
|
|
542
|
+
return dispatch(data).getVolatilityData(data);
|
|
543
|
+
},
|
|
544
|
+
getStatusData(data) {
|
|
545
|
+
return dispatch(data).getStatusData(data);
|
|
546
|
+
},
|
|
547
|
+
getFreshnessData(data, now) {
|
|
548
|
+
return dispatch(data).getFreshnessData(data, now);
|
|
549
|
+
},
|
|
550
|
+
getVerifiedDateLabel(data) {
|
|
551
|
+
return dispatch(data).getVerifiedDateLabel(data);
|
|
552
|
+
},
|
|
553
|
+
getToolsCompared(data) {
|
|
554
|
+
return dispatch(data).getToolsCompared(data);
|
|
555
|
+
},
|
|
556
|
+
sortKey(data) {
|
|
557
|
+
return dispatch(data).sortKey(data);
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
|
|
356
561
|
// src/profiles/minimal.ts
|
|
357
562
|
var minimalProfile = defineProfile({
|
|
358
563
|
name: "minimal",
|
|
@@ -366,7 +571,9 @@ var minimalProfile = defineProfile({
|
|
|
366
571
|
frontmatter: false
|
|
367
572
|
// opt-in per book; see #7
|
|
368
573
|
},
|
|
369
|
-
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"]
|
|
574
|
+
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"],
|
|
575
|
+
// v3.7.0 (#35): minimal aliases tools schema; fallback renderer field-dispatches if a consumer opts into routes.chapters
|
|
576
|
+
chaptersRenderer: fallbackChaptersRenderer
|
|
370
577
|
});
|
|
371
578
|
|
|
372
579
|
// src/profiles/course-notes.ts
|
|
@@ -383,7 +590,9 @@ var courseNotesProfile = defineProfile({
|
|
|
383
590
|
frontmatter: false
|
|
384
591
|
// opt-in per book; see #7
|
|
385
592
|
},
|
|
386
|
-
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"]
|
|
593
|
+
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"],
|
|
594
|
+
// v3.7.0 (#35): course-notes schema has tools-style fields (chapter, volatility, sources) — fallback renderer dispatches via tools renderer
|
|
595
|
+
chaptersRenderer: fallbackChaptersRenderer
|
|
387
596
|
});
|
|
388
597
|
|
|
389
598
|
// src/profiles/research-portfolio.ts
|
|
@@ -402,8 +611,10 @@ var researchPortfolioProfile = defineProfile({
|
|
|
402
611
|
// portfolios universally need title/disclosure/banner pages
|
|
403
612
|
},
|
|
404
613
|
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"],
|
|
405
|
-
katex: true
|
|
614
|
+
katex: true,
|
|
406
615
|
// math is common in research content
|
|
616
|
+
// v3.7.0 (#35): portfolio schema is a union of academic + tools shapes — fallback renderer dispatches per chapter via field presence
|
|
617
|
+
chaptersRenderer: fallbackChaptersRenderer
|
|
407
618
|
});
|
|
408
619
|
|
|
409
620
|
// src/profiles/index.ts
|
|
@@ -668,52 +879,18 @@ async function defineBookConfig(opts) {
|
|
|
668
879
|
return config;
|
|
669
880
|
}
|
|
670
881
|
|
|
671
|
-
// src/lib/freshness.ts
|
|
672
|
-
var THRESHOLDS = {
|
|
673
|
-
"stable-principle": 365,
|
|
674
|
-
"architectural-pattern": 180,
|
|
675
|
-
"feature-surface": 90
|
|
676
|
-
};
|
|
677
|
-
var MS_PER_DAY = 1e3 * 60 * 60 * 24;
|
|
678
|
-
function getFreshness(lastVerified, volatility, now = /* @__PURE__ */ new Date()) {
|
|
679
|
-
if (!(lastVerified instanceof Date)) return null;
|
|
680
|
-
const thresholdDays = THRESHOLDS[volatility];
|
|
681
|
-
const daysOld = Math.floor((now.getTime() - lastVerified.getTime()) / MS_PER_DAY);
|
|
682
|
-
const daysUntil = thresholdDays - daysOld;
|
|
683
|
-
let status;
|
|
684
|
-
if (daysOld < thresholdDays * 0.75) {
|
|
685
|
-
status = "fresh";
|
|
686
|
-
} else if (daysOld < thresholdDays) {
|
|
687
|
-
status = "verify-soon";
|
|
688
|
-
} else {
|
|
689
|
-
status = "stale";
|
|
690
|
-
}
|
|
691
|
-
return { status, daysOld, thresholdDays, daysUntil };
|
|
692
|
-
}
|
|
693
|
-
function freshnessLabel(f) {
|
|
694
|
-
if (f === null) return "Verification status unknown";
|
|
695
|
-
switch (f.status) {
|
|
696
|
-
case "fresh":
|
|
697
|
-
return `Fresh (${f.daysOld}d old; verify within ${f.daysUntil}d)`;
|
|
698
|
-
case "verify-soon":
|
|
699
|
-
return `Verify soon (${f.daysOld}d old; ${f.daysUntil}d until stale)`;
|
|
700
|
-
case "stale":
|
|
701
|
-
return `Stale (${f.daysOld}d old; ${Math.abs(f.daysUntil)}d past threshold)`;
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
882
|
// src/lib/chapter-sort.ts
|
|
706
|
-
var
|
|
883
|
+
var ACADEMIC_PART_ORDINAL2 = {
|
|
707
884
|
foundations: 1,
|
|
708
885
|
"ssm-core": 2,
|
|
709
886
|
"beyond-ssm": 3,
|
|
710
887
|
integration: 4,
|
|
711
888
|
synthesis: 5
|
|
712
889
|
};
|
|
713
|
-
var
|
|
890
|
+
var UNKNOWN_PART_ORDINAL2 = 99;
|
|
714
891
|
function chapterSortKey(data) {
|
|
715
892
|
const partRaw = data.part;
|
|
716
|
-
const partOrdinal = typeof partRaw === "number" ? partRaw : typeof partRaw === "string" ?
|
|
893
|
+
const partOrdinal = typeof partRaw === "number" ? partRaw : typeof partRaw === "string" ? ACADEMIC_PART_ORDINAL2[partRaw] ?? UNKNOWN_PART_ORDINAL2 : UNKNOWN_PART_ORDINAL2;
|
|
717
894
|
const within = typeof data.chapter === "number" ? data.chapter : typeof data.week === "number" ? data.week : 0;
|
|
718
895
|
return partOrdinal * 1e3 + within;
|
|
719
896
|
}
|
|
@@ -722,6 +899,7 @@ export {
|
|
|
722
899
|
BOOK_PROFILES,
|
|
723
900
|
BookConfigError,
|
|
724
901
|
academicChapterSchema,
|
|
902
|
+
academicChaptersRenderer,
|
|
725
903
|
academicParts,
|
|
726
904
|
bookScaffoldIntegration,
|
|
727
905
|
changeKinds,
|
|
@@ -732,6 +910,7 @@ export {
|
|
|
732
910
|
defineBookConfig,
|
|
733
911
|
defineMdxComponents,
|
|
734
912
|
defineProfile,
|
|
913
|
+
fallbackChaptersRenderer,
|
|
735
914
|
freshnessLabel,
|
|
736
915
|
getFreshness,
|
|
737
916
|
minimalChapterSchema,
|
|
@@ -745,5 +924,6 @@ export {
|
|
|
745
924
|
sourcesSchema,
|
|
746
925
|
toolSlugs,
|
|
747
926
|
toolsChapterSchema,
|
|
927
|
+
toolsChaptersRenderer,
|
|
748
928
|
volatilityLevels
|
|
749
929
|
};
|
package/dist/schemas.d.ts
CHANGED
package/dist/schemas.mjs
CHANGED
|
@@ -192,6 +192,63 @@ var patternsSchema = z.object({
|
|
|
192
192
|
convergence_date: z.date().nullable().optional()
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
+
// src/profiles/renderers/academic-chapters.ts
|
|
196
|
+
var ACADEMIC_PART_ORDINAL = {
|
|
197
|
+
foundations: 1,
|
|
198
|
+
"ssm-core": 2,
|
|
199
|
+
"beyond-ssm": 3,
|
|
200
|
+
integration: 4,
|
|
201
|
+
synthesis: 5
|
|
202
|
+
};
|
|
203
|
+
var UNKNOWN_PART_ORDINAL = 99;
|
|
204
|
+
function titleCase(part) {
|
|
205
|
+
return part.split("-").map((w) => w.length > 0 ? w.charAt(0).toUpperCase() + w.slice(1) : "").join(" ");
|
|
206
|
+
}
|
|
207
|
+
var academicChaptersRenderer = {
|
|
208
|
+
partKey(data) {
|
|
209
|
+
return data.part ?? "";
|
|
210
|
+
},
|
|
211
|
+
formatPartLabel(part) {
|
|
212
|
+
if (typeof part === "string" && part.length > 0) {
|
|
213
|
+
return titleCase(part);
|
|
214
|
+
}
|
|
215
|
+
return String(part);
|
|
216
|
+
},
|
|
217
|
+
isAppendix(_part) {
|
|
218
|
+
return false;
|
|
219
|
+
},
|
|
220
|
+
formatChapterNumber(data, _appendix) {
|
|
221
|
+
const week = data.week ?? 0;
|
|
222
|
+
return `Week ${week}`;
|
|
223
|
+
},
|
|
224
|
+
getToolsAttr(_data) {
|
|
225
|
+
return "cross-tool";
|
|
226
|
+
},
|
|
227
|
+
getVolatilityData(_data) {
|
|
228
|
+
return null;
|
|
229
|
+
},
|
|
230
|
+
getStatusData(data) {
|
|
231
|
+
const status = data.status;
|
|
232
|
+
if (!status) return null;
|
|
233
|
+
return { status, label: status };
|
|
234
|
+
},
|
|
235
|
+
getFreshnessData(_data, _now) {
|
|
236
|
+
return null;
|
|
237
|
+
},
|
|
238
|
+
getVerifiedDateLabel(_data) {
|
|
239
|
+
return null;
|
|
240
|
+
},
|
|
241
|
+
getToolsCompared(_data) {
|
|
242
|
+
return [];
|
|
243
|
+
},
|
|
244
|
+
sortKey(data) {
|
|
245
|
+
const partRaw = data.part;
|
|
246
|
+
const partOrdinal = typeof partRaw === "string" ? ACADEMIC_PART_ORDINAL[partRaw] ?? UNKNOWN_PART_ORDINAL : typeof partRaw === "number" ? partRaw : UNKNOWN_PART_ORDINAL;
|
|
247
|
+
const week = typeof data.week === "number" ? data.week : 0;
|
|
248
|
+
return partOrdinal * 1e3 + week;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
195
252
|
// src/profiles/academic.ts
|
|
196
253
|
var academicProfile = defineProfile({
|
|
197
254
|
name: "academic",
|
|
@@ -208,9 +265,104 @@ var academicProfile = defineProfile({
|
|
|
208
265
|
// opt-in per book; see #7
|
|
209
266
|
},
|
|
210
267
|
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"],
|
|
211
|
-
katex: true
|
|
268
|
+
katex: true,
|
|
269
|
+
chaptersRenderer: academicChaptersRenderer
|
|
270
|
+
// v3.7.0 (#35) — owns /chapters semantics if consumer opts in via routes.chapters
|
|
212
271
|
});
|
|
213
272
|
|
|
273
|
+
// src/lib/freshness.ts
|
|
274
|
+
var THRESHOLDS = {
|
|
275
|
+
"stable-principle": 365,
|
|
276
|
+
"architectural-pattern": 180,
|
|
277
|
+
"feature-surface": 90
|
|
278
|
+
};
|
|
279
|
+
var MS_PER_DAY = 1e3 * 60 * 60 * 24;
|
|
280
|
+
function getFreshness(lastVerified, volatility, now = /* @__PURE__ */ new Date()) {
|
|
281
|
+
if (!(lastVerified instanceof Date)) return null;
|
|
282
|
+
const thresholdDays = THRESHOLDS[volatility];
|
|
283
|
+
const daysOld = Math.floor((now.getTime() - lastVerified.getTime()) / MS_PER_DAY);
|
|
284
|
+
const daysUntil = thresholdDays - daysOld;
|
|
285
|
+
let status;
|
|
286
|
+
if (daysOld < thresholdDays * 0.75) {
|
|
287
|
+
status = "fresh";
|
|
288
|
+
} else if (daysOld < thresholdDays) {
|
|
289
|
+
status = "verify-soon";
|
|
290
|
+
} else {
|
|
291
|
+
status = "stale";
|
|
292
|
+
}
|
|
293
|
+
return { status, daysOld, thresholdDays, daysUntil };
|
|
294
|
+
}
|
|
295
|
+
function freshnessLabel(f) {
|
|
296
|
+
if (f === null) return "Verification status unknown";
|
|
297
|
+
switch (f.status) {
|
|
298
|
+
case "fresh":
|
|
299
|
+
return `Fresh (${f.daysOld}d old; verify within ${f.daysUntil}d)`;
|
|
300
|
+
case "verify-soon":
|
|
301
|
+
return `Verify soon (${f.daysOld}d old; ${f.daysUntil}d until stale)`;
|
|
302
|
+
case "stale":
|
|
303
|
+
return `Stale (${f.daysOld}d old; ${Math.abs(f.daysUntil)}d past threshold)`;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/profiles/renderers/tools-chapters.ts
|
|
308
|
+
function formatDate(d) {
|
|
309
|
+
return d.toISOString().slice(0, 10);
|
|
310
|
+
}
|
|
311
|
+
var toolsChaptersRenderer = {
|
|
312
|
+
partKey(data) {
|
|
313
|
+
return data.part ?? 0;
|
|
314
|
+
},
|
|
315
|
+
formatPartLabel(part) {
|
|
316
|
+
if (typeof part === "number") {
|
|
317
|
+
return part >= 6 ? "Appendices" : `Part ${part}`;
|
|
318
|
+
}
|
|
319
|
+
return String(part);
|
|
320
|
+
},
|
|
321
|
+
isAppendix(part) {
|
|
322
|
+
return typeof part === "number" && part >= 6;
|
|
323
|
+
},
|
|
324
|
+
formatChapterNumber(data, appendix) {
|
|
325
|
+
const chapter = data.chapter ?? 0;
|
|
326
|
+
if (appendix) {
|
|
327
|
+
return `Appendix ${String.fromCharCode(64 + chapter).toLowerCase()}`;
|
|
328
|
+
}
|
|
329
|
+
return `Chapter ${chapter}`;
|
|
330
|
+
},
|
|
331
|
+
getToolsAttr(data) {
|
|
332
|
+
const tools = data.tools_compared ?? [];
|
|
333
|
+
return tools.join(" ");
|
|
334
|
+
},
|
|
335
|
+
getVolatilityData(data) {
|
|
336
|
+
const level = data.volatility;
|
|
337
|
+
if (!level) return null;
|
|
338
|
+
return { level, label: level };
|
|
339
|
+
},
|
|
340
|
+
getStatusData(_data) {
|
|
341
|
+
return null;
|
|
342
|
+
},
|
|
343
|
+
getFreshnessData(data, now) {
|
|
344
|
+
const lastVerified = data.last_verified;
|
|
345
|
+
const volatility = data.volatility;
|
|
346
|
+
if (!lastVerified || !volatility) return null;
|
|
347
|
+
const f = getFreshness(lastVerified, volatility, now);
|
|
348
|
+
if (!f) return null;
|
|
349
|
+
return { status: f.status, label: freshnessLabel(f) };
|
|
350
|
+
},
|
|
351
|
+
getVerifiedDateLabel(data) {
|
|
352
|
+
const lastVerified = data.last_verified;
|
|
353
|
+
if (!(lastVerified instanceof Date)) return null;
|
|
354
|
+
return `verified ${formatDate(lastVerified)}`;
|
|
355
|
+
},
|
|
356
|
+
getToolsCompared(data) {
|
|
357
|
+
return (data.tools_compared ?? []).slice();
|
|
358
|
+
},
|
|
359
|
+
sortKey(data) {
|
|
360
|
+
const part = data.part ?? 0;
|
|
361
|
+
const chapter = data.chapter ?? 0;
|
|
362
|
+
return part * 1e3 + chapter;
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
214
366
|
// src/profiles/tools.ts
|
|
215
367
|
var toolsProfile = defineProfile({
|
|
216
368
|
name: "tools",
|
|
@@ -235,9 +387,62 @@ var toolsProfile = defineProfile({
|
|
|
235
387
|
"print.css",
|
|
236
388
|
"convergence.css",
|
|
237
389
|
"tool-filter.css"
|
|
238
|
-
]
|
|
390
|
+
],
|
|
391
|
+
chaptersRenderer: toolsChaptersRenderer
|
|
392
|
+
// v3.7.0 (#35) — owns /chapters semantics for tools shape
|
|
239
393
|
});
|
|
240
394
|
|
|
395
|
+
// src/profiles/renderers/fallback-chapters.ts
|
|
396
|
+
function isToolsShape(data) {
|
|
397
|
+
return "volatility" in data && "chapter" in data;
|
|
398
|
+
}
|
|
399
|
+
function isAcademicShape(data) {
|
|
400
|
+
return "week" in data && "status" in data;
|
|
401
|
+
}
|
|
402
|
+
function dispatch(data) {
|
|
403
|
+
if (isToolsShape(data)) return toolsChaptersRenderer;
|
|
404
|
+
if (isAcademicShape(data)) return academicChaptersRenderer;
|
|
405
|
+
return toolsChaptersRenderer;
|
|
406
|
+
}
|
|
407
|
+
var fallbackChaptersRenderer = {
|
|
408
|
+
partKey(data) {
|
|
409
|
+
return data.part ?? 0;
|
|
410
|
+
},
|
|
411
|
+
formatPartLabel(part) {
|
|
412
|
+
if (typeof part === "number") {
|
|
413
|
+
return part >= 6 ? "Appendices" : `Part ${part}`;
|
|
414
|
+
}
|
|
415
|
+
return dispatch({ part }).formatPartLabel(part);
|
|
416
|
+
},
|
|
417
|
+
isAppendix(part) {
|
|
418
|
+
return typeof part === "number" && part >= 6;
|
|
419
|
+
},
|
|
420
|
+
formatChapterNumber(data, appendix) {
|
|
421
|
+
return dispatch(data).formatChapterNumber(data, appendix);
|
|
422
|
+
},
|
|
423
|
+
getToolsAttr(data) {
|
|
424
|
+
return dispatch(data).getToolsAttr(data);
|
|
425
|
+
},
|
|
426
|
+
getVolatilityData(data) {
|
|
427
|
+
return dispatch(data).getVolatilityData(data);
|
|
428
|
+
},
|
|
429
|
+
getStatusData(data) {
|
|
430
|
+
return dispatch(data).getStatusData(data);
|
|
431
|
+
},
|
|
432
|
+
getFreshnessData(data, now) {
|
|
433
|
+
return dispatch(data).getFreshnessData(data, now);
|
|
434
|
+
},
|
|
435
|
+
getVerifiedDateLabel(data) {
|
|
436
|
+
return dispatch(data).getVerifiedDateLabel(data);
|
|
437
|
+
},
|
|
438
|
+
getToolsCompared(data) {
|
|
439
|
+
return dispatch(data).getToolsCompared(data);
|
|
440
|
+
},
|
|
441
|
+
sortKey(data) {
|
|
442
|
+
return dispatch(data).sortKey(data);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
241
446
|
// src/profiles/minimal.ts
|
|
242
447
|
var minimalProfile = defineProfile({
|
|
243
448
|
name: "minimal",
|
|
@@ -251,7 +456,9 @@ var minimalProfile = defineProfile({
|
|
|
251
456
|
frontmatter: false
|
|
252
457
|
// opt-in per book; see #7
|
|
253
458
|
},
|
|
254
|
-
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"]
|
|
459
|
+
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"],
|
|
460
|
+
// v3.7.0 (#35): minimal aliases tools schema; fallback renderer field-dispatches if a consumer opts into routes.chapters
|
|
461
|
+
chaptersRenderer: fallbackChaptersRenderer
|
|
255
462
|
});
|
|
256
463
|
|
|
257
464
|
// src/profiles/course-notes.ts
|
|
@@ -268,7 +475,9 @@ var courseNotesProfile = defineProfile({
|
|
|
268
475
|
frontmatter: false
|
|
269
476
|
// opt-in per book; see #7
|
|
270
477
|
},
|
|
271
|
-
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"]
|
|
478
|
+
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"],
|
|
479
|
+
// v3.7.0 (#35): course-notes schema has tools-style fields (chapter, volatility, sources) — fallback renderer dispatches via tools renderer
|
|
480
|
+
chaptersRenderer: fallbackChaptersRenderer
|
|
272
481
|
});
|
|
273
482
|
|
|
274
483
|
// src/profiles/research-portfolio.ts
|
|
@@ -287,8 +496,10 @@ var researchPortfolioProfile = defineProfile({
|
|
|
287
496
|
// portfolios universally need title/disclosure/banner pages
|
|
288
497
|
},
|
|
289
498
|
styles: ["tokens.css", "layout.css", "callouts.css", "chapter.css", "typography.css", "print.css"],
|
|
290
|
-
katex: true
|
|
499
|
+
katex: true,
|
|
291
500
|
// math is common in research content
|
|
501
|
+
// v3.7.0 (#35): portfolio schema is a union of academic + tools shapes — fallback renderer dispatches per chapter via field presence
|
|
502
|
+
chaptersRenderer: fallbackChaptersRenderer
|
|
292
503
|
});
|
|
293
504
|
|
|
294
505
|
// src/profiles/index.ts
|
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": "3.
|
|
4
|
+
"version": "3.7.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Brandon Behring",
|
|
@@ -107,6 +107,9 @@
|
|
|
107
107
|
"files": [
|
|
108
108
|
"dist",
|
|
109
109
|
"src/lib",
|
|
110
|
+
"src/profiles",
|
|
111
|
+
"src/profile-kit.ts",
|
|
112
|
+
"src/schemas.ts",
|
|
110
113
|
"components",
|
|
111
114
|
"styles",
|
|
112
115
|
"layouts",
|