import { AstroUserConfig, AstroIntegration, MarkdownHeading } from 'astro'; import { c as BookConfigOptions, h as BookScaffoldIntegrationOptions, d as BookCorpus, l as CorpusApparatusRoute, e as BookCorpusInput, m as CorpusBook, k as ChaptersRenderer, v as SiblingBooks, x as Style } from './types-DjqMnwHw.js'; export { B as BOOK_PRESETS, a as BOOK_PROFILES, b as BookConfigError, f as BookPreset, g as BookProfile, i as BookSchemasOptions, C as CORPUS_APPARATUS_ROUTES, j as ChapterFor, n as CorpusBookInput, F as FreshnessAffordance, o as FrontmatterRouteConfig, N as NUMBER_STYLES, p as NumberStyle, O as OgCardsConfig, P as PartKey, q as PartialRouteToggles, r as ProfileDefinition, R as ReleaseStatusConfig, s as RouteToggles, S as SecurityHeadersConfig, t as SiblingBookDescriptor, u as SiblingBookEntry, w as StatusBadge, y as StyleInput, V as VolatilityBadge, z as composeStyles, A as defineProfile, D as defineStyle, E as normalizeFrontmatterConfig, G as resolvePreset, H as resolveProfile } from './types-DjqMnwHw.js'; import { E as volatilityLevels, c as academicParts, Q as Question } from './schemas-CKipJ5Ie.js'; export { A as AcademicChapter, B as BloomLevel, C as CourseNotesChapter, G as GlossaryTerm, M as MinimalChapter, P as Provenance, a as QuestionType, R as ResearchPortfolioChapter, T as ToolsChapter, b as academicChapterSchema, d as bloomLevels, e as changeKinds, f as changelogSchema, g as chapterStatus, h as citationBackstops, i as courseNotesChapterSchema, j as glossarySchema, l as layoutModes, m as minimalChapterSchema, p as patternCategories, k as patternsSchema, n as provenanceObject, o as provenanceSchema, q as questionDifficulties, r as questionSchema, s as questionTypes, t as refineQuestion, u as refinedQuestionSchema, v as researchPortfolioChapterSchema, w as sourceTiers, x as sourceTiersResearch, y as sourcesSchema, z as toolSlugs, D as toolsChapterSchema } from './schemas-CKipJ5Ie.js'; export { KIND_LABEL, ResolvedTheoremLabel, THEOREM_KINDS, TheoremKind, TheoremLabelProps, resolveTheoremNumber, theoremLabel } from './lib/theorem-label.js'; export { ssmMacros } from './lib/katex-macros.js'; export { ChapterLike, apparatusHref, baseNoSlash, bookOf, chapterHref, isCurrentChapter, normalizeBase, slugOf } from './lib/nav-href.js'; export { D as DomainScore, E as ExamBlueprint, a as ExamQuestion, b as ExamResult, R as RoutingChapter, c as buildExamManifest, d as deriveDomainRouting, s as sampleExam, e as scoreExam, f as shuffle, g as spreadBlueprint } from './exam-manifest-DttY7kyZ.js'; export { F as FlashcardRef, b as buildFlashcardDeck } from './flashcards-okekZcl8.js'; import 'astro/zod'; /** * v4.5.0: Default portfolio backlink baked into the scaffold. Rendered in * the auto-injected `/` landing footer for every consumer that doesn't * explicitly override `portfolio` in defineBookConfig. * * Single source of truth for the brandon-behring.dev URL across all * consumers — update here, bump scaffold version, every consumer inherits * on next build. This is the intentional Brandon-specific default * discussed in plan §Phase 6-pre. Consumers outside the brandon-behring.dev * ecosystem set `portfolio: false` (no link) or pass `{ url, label }` to * override. */ declare const BRANDON_PORTFOLIO_DEFAULT: { readonly url: "https://brandon-behring.dev"; readonly label: "brandon-behring.dev"; }; declare function defineBookConfig(opts: BookConfigOptions): Promise; declare function bookScaffoldIntegration(opts: BookScaffoldIntegrationOptions): AstroIntegration; /** Pure corpus-manifest validation and lookup helpers (#80). */ /** * First-segment names owned by application routes/assets or shared content * collection roots rather than a corpus book. */ declare const RESERVED_CORPUS_BOOK_IDS: readonly ["assets", "chapters", "search", "questions", "glossary", "frontmatter", "_astro", "_og", "pagefind"]; /** * Public URL slug -> `RouteToggles` key. Keep this explicit: the public * `practice-exam` route is intentionally not the camel-cased config key. */ declare const CORPUS_APPARATUS_TOGGLE_BY_ROUTE: Readonly<{ readonly references: "references"; readonly print: "print"; readonly convergence: "convergence"; readonly tips: "tips"; readonly exercises: "exercises"; readonly 'practice-exam': "practiceExam"; readonly glossary: "glossary"; readonly flashcards: "flashcards"; readonly answers: "answers"; }>; /** * Define and eagerly validate one homogeneous-preset book corpus. * * The returned value is safe to share between `astro.config.mjs` and * `src/content.config.ts`; no filesystem or Astro virtual module is touched. */ declare function defineBookCorpus(inputValue: BookCorpusInput): BookCorpus; /** Validate that a value came from this major's `defineBookCorpus`. */ declare function assertBookCorpus(value: unknown): asserts value is BookCorpus; /** Return the effective apparatus routes for one manifest book. */ declare function corpusApparatusRoutesForBook(corpus: BookCorpus, bookId: string, inheritedRoutes?: readonly CorpusApparatusRoute[]): readonly CorpusApparatusRoute[]; /** True when a public apparatus route is enabled for one manifest book. */ declare function corpusBookHasApparatusRoute(corpus: BookCorpus, bookId: string, route: CorpusApparatusRoute, inheritedRoutes?: readonly CorpusApparatusRoute[]): boolean; /** * Select the current book's payload from a v1 corpus artifact envelope. * Single-book mode deliberately returns the input unchanged, preserving the * legacy JSON contract byte-for-byte. */ declare function selectBookArtifact(value: unknown, corpus: BookCorpus | null | undefined, bookId: string | null | undefined, artifact?: string): Payload; /** Resolve one manifest book or fail with the complete known-id set. */ declare function resolveCorpusBook(corpus: BookCorpus, id: string): CorpusBook; /** Return the registered first entry-id segment, or null outside the corpus. */ declare function corpusBookIdOf(corpus: BookCorpus, entryId: string): string | null; /** Strip the registered `/` prefix from one collection entry id. */ declare function localCorpusEntryId(corpus: BookCorpus, bookId: string, entryId: string): string; /** Filter content entries to the current registered book. */ declare function filterCorpusEntries(entries: readonly Entry[], corpus: BookCorpus | null | undefined, bookId: string | null | undefined): Entry[]; /** * Derive a collision-safe collection id from a corpus-relative source path. * A legacy `book:` field may agree with the path-derived owner, but can never * override it; disagreement fails before schema parsing can silently strip it. */ declare function corpusCollectionEntryId(corpus: BookCorpus, entry: string, data: Record, options?: { label?: string; slugField?: string; }): string; /** Resolve current book identity from canonical corpus routes. */ declare function corpusBookIdFromPath(corpus: BookCorpus, pathname: string, baseUrl?: string): string | null; /** * Identity helper — consumers wrap their mdx-components map for TS inference. * Same pattern as Vite/Astro defineConfig: a generic identity function that * preserves the exact shape (vs widening to Record). * * export default defineMdxComponents({ AnkiCard, NarrativeBox }); * * The generic constraint Record is intentionally loose: * consumer components are `.astro` files whose runtime type * (AstroComponentFactory) lives in `astro/runtime/server/index.js` — not a * public Astro export, and importing from internals creates fragility. The * looser constraint lets the helper compile cleanly across Astro versions; * IntelliSense still surfaces exact keys. */ declare function defineMdxComponents>(components: T): T; /** * src/lib/freshness.ts — volatility-aware staleness computation. * * Each chapter carries a `last_verified` date and a `volatility` class. * This module maps those to a freshness status the reader can trust. * * Thresholds chosen to align with Ch 15's source-tier audit cadences: * stable-principle → 365 days (principles drift annually at most) * architectural-pattern → 180 days (between annual and quarterly; "on * major release" isn't derivable from * frontmatter) * feature-surface → 90 days (quarterly) * * Status bands (fraction of threshold): * fresh (<75%) — green dot, unobtrusive * verify-soon (75-100%) — yellow, mild warning * stale (>100%) — amber/red, "verify before trusting" * * Example assertions (verified by visual inspection during Stage 3.1): * getFreshness(today, 'feature-surface').status === 'fresh' * getFreshness(today-70d, 'feature-surface').status === 'verify-soon' * getFreshness(today-100d, 'feature-surface').status === 'stale' * getFreshness(today-200d, 'stable-principle').status === 'fresh' * getFreshness(today-300d, 'stable-principle').status === 'verify-soon' */ type VolatilityLevel = (typeof volatilityLevels)[number]; type FreshnessStatus = 'fresh' | 'verify-soon' | 'stale'; interface Freshness { status: FreshnessStatus; daysOld: number; thresholdDays: number; /** Days until stale; negative when already stale. */ daysUntil: number; } /** * Compute freshness for a chapter given its last_verified date + volatility. * * Pure function; caller supplies `now` only in tests. Production callers omit. * * v3.3.0 (closes issue #1): tolerant of `lastVerified === undefined`. Returns * `null` instead of crashing when the chapter schema omits the field (e.g., * academic profile chapters that don't track verification dates, or consumer * schemas that don't declare last_verified). * * Callers compose with optional chaining: * const status = getFreshness(d.last_verified, d.volatility)?.status; */ declare function getFreshness(lastVerified: Date | undefined, volatility: VolatilityLevel, now?: Date): Freshness | null; /** Human-readable label for each status; used for ARIA + tooltips. * * v3.3.0: accepts `null` (the new return shape of getFreshness for undefined * inputs). Returns a sentinel "unknown" label so callers can render a neutral * affordance without a separate branch. */ declare function freshnessLabel(f: Freshness | null): string; /** * Pure-function sort key from a chapter frontmatter object. Spans both * tools and academic schemas: * * - Tools: numeric `part` (0-10), numeric `chapter` (0-99). Encodes as * `part * 1000 + chapter` so chapters within a part stay grouped. * - Academic: string-enum `part` (foundations|ssm-core|...), numeric `week` * (1-99), no `chapter`. Maps `part` to a fixed ordinal then encodes as * `partOrdinal * 1000 + week`. * * v3.5.2 (closes #24): previously the tools-only formula crashed on * academic chapters (string `part`, no `chapter`) by producing NaN sort * keys. */ declare function chapterSortKey(data: Record): number; /** * src/profiles/renderers/tools-chapters.ts — ChaptersRenderer implementation * for the tools profile. Owns: numeric part grouping (with Appendix splitting * at part >= 6), Chapter N numbering, volatility badge, freshness affordance * from last_verified + volatility class, tools_compared tags. * * Mirrors the pre-v3.7.0 logic in pages/chapters.astro for tools-shape * chapters — DOM output is intended to be byte-equivalent so the existing * visual-regression baselines (package/tests/visual/fixture/) pass without * recapture. */ declare const toolsChaptersRenderer: ChaptersRenderer; /** * src/profiles/renderers/academic-chapters.ts — ChaptersRenderer implementation * for the academic profile. Owns: string-enum part grouping (foundations, * ssm-core, beyond-ssm, integration, synthesis), Week N numbering, status * badge (7-state). No freshness/last_verified, no tools_compared. * * Mirrors the v3.5.2 academic branch of pages/chapters.astro — DOM output * intended to match exactly so academic visual baselines stay stable. */ declare const academicChaptersRenderer: ChaptersRenderer; /** * src/profiles/renderers/fallback-chapters.ts — ChaptersRenderer used by * profiles that don't ship a dedicated renderer (minimal, course-notes). * Dispatches by field presence — exactly the v3.5.2 * logic that lived inline in pages/chapters.astro before #35. * * Safety net for shapes we haven't designed for explicitly. If a consumer * opts a course-notes book into `routes.chapters: true`, * the fallback renders reasonably without crashing. Custom output for those * profiles is a v4+ extension point (consumer-overridable renderer). */ declare const fallbackChaptersRenderer: ChaptersRenderer; /** * Research-portfolio /chapters renderer. * * The portfolio schema intentionally permits numeric Parts through 20. The * generic fallback renderer inherits the tools profile's historical * `part >= 6` appendix convention, which mislabels a portfolio's sixth Part * as "Appendices" and its chapters as Appendix a/b/.... Keep the fallback's * field-shape dispatch for every other affordance, but make numeric Parts * ordinary numbered Parts throughout the portfolio schema's full range. */ declare const researchPortfolioChaptersRenderer: ChaptersRenderer; /** * src/lib/academic-parts.ts — single source of truth for academic-profile * part labels (#95). * * The academic `part` enum (src/schemas.ts: academicParts) is rendered on * three surfaces — the `/chapters` index, the Sidebar nav, and the chapter * header. Before v4.14.0 each surface carried its own copy of the * enum→label map, and they had silently diverged: `beyond-ssm` rendered * "Beyond SSM" on the index but "Beyond SSMs" in the sidebar/header. This * module is the one place that owns: * * - the base display name per part ("SSM Core", "Beyond SSMs", …), and * - the unknown/custom-part fallback (titleCase, consistently). * * The renderer uses the bare name; the Sidebar/ChapterHeader compose * "Part {roman} · {name}". Both the Roman heading prefix AND the on-page * sort order derive from the part's position in `academicParts` (the * canonical order) via `academicPartOrdinal()` below — so labels and * ordering share one source and cannot drift apart (#95 + #99). */ type AcademicPart = (typeof academicParts)[number]; /** * Base display names for the academic `part` enum. An explicit map (not * naive title-casing) so acronyms render correctly: `ssm-core` → "SSM Core", * not "Ssm Core" (#91). Keys mirror `academicParts` in src/schemas.ts. * Canonical spelling for `beyond-ssm` is the plural "Beyond SSMs" (#95). */ declare const ACADEMIC_PART_NAMES: Record; /** * Bare part name for the `/chapters` index group heading. Known parts use * the explicit map; unknown/custom parts fall back to titleCase. */ declare function academicPartName(part: string): string; /** Ordinal returned for any `part` outside `academicParts` — it sorts after * all known parts and renders with no Roman prefix. */ declare const UNKNOWN_PART_ORDINAL = 99; /** * 1-based ordinal of an academic `part`, from its position in `academicParts` * (the canonical order); `UNKNOWN_PART_ORDINAL` for anything outside the enum. * The single source for BOTH the on-page sort key (academicChaptersRenderer * and chapterSortKey) and the Roman heading prefix below — so a reorder of * `academicParts` moves sort order and labels together, never apart (#99). */ declare function academicPartOrdinal(part: string): number; /** * "Part {roman} · {name}" heading for the Sidebar and ChapterHeader. Known * parts get the Roman-ordinal prefix; unknown/custom parts (no ordinal) * fall back to the bare title-cased name — one consistent rule across both * surfaces, replacing the old divergent fallbacks (raw key vs "Part: key"). */ declare function academicPartHeading(part: string): string; /** * repo-url — resolve and build GitHub source links for CodeRef / CodeBlock. * * The repo is no longer hardcoded (#109). `buildGithubUrl` takes an explicit * `repo` + `branch`; the integration resolves them per book — from * `defineBookConfig({ githubRepo })` (override), else auto-detected from the * consumer's own `package.json` `repository` (or git remote) via * `parseRepoSlug`. When nothing resolves, the components throw rather than * emit links to the wrong repo (the old silent default was * `brandon-behring/post_transformers`). */ /** Default branch when a book configures a repo but no branch. */ declare const DEFAULT_GITHUB_BRANCH = "main"; /** * Derive an `owner/repo` slug from an npm `repository` field (string or * `{ url }`) or a raw git remote URL. Handles https, ssh, the `git+` prefix, * a trailing `.git`, and the `github:owner/repo` npm shorthand. Returns null * for anything that isn't a recognizable GitHub repo — no silent guess. */ declare function parseRepoSlug(repository: string | { url?: string; } | null | undefined): string | null; /** Extract the `origin` remote URL from the text of a `.git/config` file. */ declare function originUrlFromGitConfig(gitConfigText: string): string | null; /** * Resolve a GitHub `owner/repo` by precedence (#109) — the rule that keeps a * book from ever silently linking to the wrong repo: * 1. explicit `override` (defineBookConfig `githubRepo`) * 2. the consumer's `package.json` `repository` * 3. the `origin` remote in `.git/config` * 4. null — the components then throw rather than guess. */ declare function resolveGithubRepo(sources: { override?: string | null; packageJsonRepository?: string | { url?: string; } | null; gitConfigText?: string | null; }): string | null; /** * Build a GitHub line-anchor URL for an explicit repo + branch. * buildGithubUrl('o/r', 'main', 'a/b.py') -> https://github.com/o/r/blob/main/a/b.py * buildGithubUrl('o/r', 'main', 'a/b.py', 42) -> …/a/b.py#L42 * buildGithubUrl('o/r', 'main', 'a/b.py', 42, 58) -> …/a/b.py#L42-L58 */ declare function buildGithubUrl(repo: string, branch: string, path: string, line?: number, lineEnd?: number): string; /** * assert-prop — shared fail-loud validator for closed-union component props * (#109 / the v4.15.0 parseProps sweep). * * Astro has no runtime prop validation, so a closed-union prop given an * out-of-range value silently produces a broken render — an empty StatusBadge * label, a `poc-layout-` class that matches no CSS, NaN difficulty * markers. This is the same silent-degradation class as the #121 Theorem bug. * `assertEnumProp` converts it into a loud, actionable build-time throw. */ /** * Return `value` when it's one of `allowed`; otherwise throw an actionable * error naming the component, prop, the offending value, and the legal set. * Never returns a fallback — a closed union with no valid value is an authoring * error that should stop the build, not render something wrong. */ declare function assertEnumProp(value: unknown, allowed: readonly T[], ctx: { component: string; prop: string; }): T; /** * book-link — resolve a cross-book `` href from the consumer's * sibling-book registry (#96). * * Each scaffold book is a separate Astro app with its own `labels.json` and * deploy origin, so `` can't reach a sibling book — a cross-book ref * resolves against the wrong labels and dies. `` instead * resolves `book` against a per-consumer registry of sibling base URLs * (`defineBookConfig({ siblingBooks })`) — the single place to update when a * sibling redeploys or extracts to its own repo. An unknown `book` THROWS * rather than emitting a dead cross-origin link (fail-loud, like #109). * * #147 extends each registry entry from a URL string to the backward-compatible * `{ url, labels? }` descriptor. Runtime href resolution uses `url`; the * validator uses `labels` to check literal sibling path/fragment targets. */ /** * Resolve a link to another book inside the same corpus application. * * Chapter targets retain the scaffold's `/chapters//…` namespace; * every other relative target lives below the book landing namespace. */ declare function resolveCorpusBookHref(book: string, to: string, baseUrl?: string): string; declare function resolveBookHref(siblingBooks: SiblingBooks | null | undefined, book: string, to: string): string; /** * exam-domains — validate a question's `domain` against the consumer's closed * `examDomains` taxonomy (Tier 3, #112). * * Exam domains are PER-BOOK (Cisco security domains ≠ CompTIA objectives ≠ a * math syllabus's topics), so they can't be a hardcoded `z.enum` in the schema. * The consumer declares them once — `defineBookConfig({ examDomains: [...] })`, * threaded through the `virtual:book-scaffold/book-config` module — and a * question whose `domain` is not in that list THROWS at build rather than * silently mis-weighting a blueprint or dropping a row from the objective-map. * * Membership can't be a Zod invariant: `questionSchema` is constructed at * package-load time, outside any consumer context, so it can't see * `examDomains` (the same constraint that put `siblingBooks` validation in * lib/book-link.ts rather than the schema). So Zod checks `domain` is a * non-empty string; THIS runs at the route/build layer where the resolved * config is available. Mirrors `resolveBookHref` (lib/book-link.ts). */ declare function assertKnownDomain(examDomains: readonly string[] | null | undefined, domain: string, ctx: { id: string; }): string; /** * src/lib/questions-derive.ts — PURE grouping + objective-map derivation for the * study-guide `questions` collection (Tier 3, #112). * * No `astro:content` import, so this is safe in the Node-loaded main entry and * unit-tested directly from dist/ (tests/questions.test.mjs). The Astro-context * `getCollection` wrapper lives in questions.ts — same split as * chapter-sort.ts (pure) / chapters.ts (Astro wrapper). */ /** Stable string label for a chapter ref (number or academic-style string). */ declare function chapterLabel(chapter: number | string): string; /** Sort questions by chapter then id (stable, deterministic). Pure. */ declare function sortQuestions; }>(entries: readonly T[]): T[]; /** Group entries by `domain`, preserving input order within each bucket. */ declare function groupByDomain; }>(entries: readonly T[]): Map; /** Group entries by `chapter` (string label), preserving input order. */ declare function groupByChapter; }>(entries: readonly T[]): Map; /** * Domain → set of chapter labels that have ≥1 question in that domain. The data * backbone of `` (#117): the coverage matrix is *derived* from the * question bank, so it can't drift from a separately-maintained table — the * cheapest proof the per-book `examDomains` taxonomy is populated + consistent. */ declare function deriveObjectiveMap; }>(entries: readonly T[]): Map>; /** * Distinct chapter labels across the entries, in numeric-aware order (numeric * chapters before string slugs, each ordered) — the column set for * ``. Dedupes by label, sorts the underlying chapter values via * the same `chapterOrder` as `sortQuestions`, then labels. A bare `.sort()` on * the labels would put "10" before "2" (the chapter-column sort bug). */ declare function distinctChaptersSorted; }>(entries: readonly T[]): string[]; /** * section-map.ts — PURE heading-selection + scrollspy logic for the right-gutter * "On this page" section map (#section-map). * * No DOM, no Preact: this is the testable core under both ChapterTOC.astro (the * collapsed mobile fallback) and the SectionMap island (the sticky gutter nav). * Two pure, total functions: * * - tocHeadings: the ONE filter (h2+h3) that both the fallback TOC and the * gutter map share — a single source of truth so the two * never disagree about which headings are "on this page". * - pickActive: given the headings currently intersecting the viewport (slug * + viewport-relative top), choose which one is "active". The * island feeds it IntersectionObserver state; node:test feeds * it plain arrays. Browser geometry stays OUT of this file. * * Both are unit-tested in tests/section-map.test.mjs (node:test, no browser), * mirroring exam-engine.ts. */ /** * Filter a chapter's headings to the TOC set: depth 2–3 only. h1 is the chapter * title (rendered by ChapterHeader, not the body) and h4+ is noise in an anchor * list. This is the shared contract — ChapterTOC.astro and SectionMap.astro both * call it so the fallback and the gutter map carry IDENTICAL entries. Pure: it * copies (filter) rather than mutating the input. */ declare function tocHeadings(headings: MarkdownHeading[]): MarkdownHeading[]; /** * One visible heading, as the island measures it: its slug and its * viewport-relative top (CSS pixels — `getBoundingClientRect().top`). A negative * `top` means the heading has scrolled above the top of the viewport. */ interface VisibleHeading { slug: string; /** Viewport-relative top in px (negative = scrolled above the fold). */ top: number; } /** * Choose the active section slug from the headings currently intersecting the * viewport. * * Rule (a stable, total scrollspy): * 1. Prefer the topmost heading at or below the top of the viewport — the * visible heading with the SMALLEST non-negative `top`. That's the section * the reader has just scrolled to / is reading into. * 2. If every visible heading is above the fold (all `top` negative — e.g. a * heading STRADDLING the top edge: its top is slightly negative but its box * still overlaps the top zone, so it's intersecting yet has no non-negative * sibling below it), fall back to the one nearest the fold from above: the * GREATEST `top` (closest to 0 from the negative side). That keeps the * enclosing section lit instead of going dark mid-section. (A heading whose * box FULLY scrolled above the top has left `inView` entirely — it isn't in * `visible` at all, and is handled by the empty-set → `prev` branch below.) * 3. If NOTHING is visible (the observer reports an empty set — between two * sparse intersections, or scrolled past the last heading), retain `prev` * so the highlight is sticky rather than flickering off. * * Pure + total: no DOM, no throw, deterministic. Ties (equal `top`) resolve to * the first in iteration order, which the island passes in document order. */ declare function pickActive(visible: ReadonlyArray, prev: string | null): string | null; /** One exercise as emitted by `book-scaffold build-exercises` (src/data/exercises.json). */ interface ReviewExercise { id: string; problem: string; } /** A chapter entry — structurally what `getCollection('chapters')` yields. */ interface ReviewChapter { id: string; data: Record; } interface PartReviewGroup { chapter: string; exercises: ReviewExercise[]; } interface PartReviewSelection { groups: PartReviewGroup[]; total: number; } /** * Select a Part's exercises, in book order, joining `chapters` (filtered by * `part`) against the `byChapter` index (keyed by chapter id). `part` is matched * with `String()` coercion so `` finds numeric-`part` * chapters and vice versa (the fail-silent trap a strict `===` left). Chapters * with no exercises in the index are skipped; `total` sums the rest. */ declare function selectPartExercises(chapters: readonly ReviewChapter[], byChapter: Readonly>, part: number | string): PartReviewSelection; /** * src/styles/built-in.ts — toolkit-shipped Styles, one per BookPreset (v4.0.0). * * Each profile that the toolkit shipped in v3 (academic, tools, minimal, * course-notes, research-portfolio) is now mirrored by a built-in Style * importable by consumers: * * import { defineBookConfig, academicStyle } from '@brandon_m_behring/book-scaffold-astro'; * export default await defineBookConfig({ styles: [academicStyle], site: '...' }); * * Consumers can compose built-in styles with their own: * * import { researchPortfolioStyle, defineStyle } from '@brandon_m_behring/book-scaffold-astro'; * const guidesFamilyStyle = defineStyle({ site: 'https://guides.brandon-behring.dev/' }); * export default await defineBookConfig({ * styles: [researchPortfolioStyle, guidesFamilyStyle], * // ... * }); * * The v3 `preset: '...'` shorthand is replaced by this explicit style chain. * See MIGRATION-v3-to-v4.md. */ /** Academic preset — weekly curriculum, 7-state status, KaTeX wired, BibTeX pipeline. */ declare const academicStyle: Style; /** Tools preset — AI-CLI comparison content with volatility + sources. */ declare const toolsStyle: Style; /** Minimal preset — single-author essays / manifestos. */ declare const minimalStyle: Style; /** Course-notes preset — chapters derived from a video course / MOOC / book. */ declare const courseNotesStyle: Style; /** Research-portfolio preset — academic structure + tools-style provenance + portfolio components. */ declare const researchPortfolioStyle: Style; /** * Registry of all toolkit-shipped styles, keyed by their preset name. * * `satisfies` (TS 4.9+) keeps the inferred narrow type while validating the * shape: `BUILTIN_STYLES['academic']` resolves to `typeof academicStyle`, * not to generic `Style`. Used by the v3 → v4 migration error path * (config.ts) to construct auto-suggested replacements. */ declare const BUILTIN_STYLES: { readonly academic: Style; readonly tools: Style; readonly minimal: Style; readonly 'course-notes': Style; readonly 'research-portfolio': Style; }; /** * src/lib/define-tips.ts — `defineTips()` API for cross-volume tip registry * (v4.3.0, closes #70). * * Pragmatic Programmer-style numbered tips can be distributed across multiple * volumes (e.g., Handbook tips 1-25, Architect's Reference 26-40, Field-Guide * 41-50). Authors write `` with explicit numbers; defineTips() * lets per-volume books offset their displayed numbers + label without * renumbering source tags. * * Branded type follows the same convention as `defineStyle` (v4.0.0 D6): * type-only `unique symbol` brand, closed shape, readonly fields, no public * index signature. Consumer-side metadata goes in scoped `extra` if needed. */ declare const TipsConfigBrand: unique symbol; interface TipsConfig { /** Type-only brand for nominal typing. Set automatically by defineTips. */ readonly [TipsConfigBrand]: true; /** Internal version marker; auto-set to 1 by defineTips. */ readonly __tipsConfigVersion: 1; /** Display offset added to each `` for cross-volume coordination. * Example: Vol B with volumeOffset=25 renders `` as "Tip 26". */ readonly volumeOffset?: number; /** Optional label shown alongside tip numbers in the /tips index + TipsCard. * Example: "Vol B" → "Vol B Tip 26". */ readonly volumeLabel?: string; /** Scoped consumer-side metadata (matches defineStyle pattern). */ readonly extra?: Readonly>; } /** Input type for defineTips — omits the auto-set internal fields. */ type TipsConfigInput = Omit; /** * Identity helper that creates a typed, branded TipsConfig. * Zero runtime overhead beyond an object spread + version marker. * * Usage: * * import { defineTips } from '@brandon_m_behring/book-scaffold-astro'; * * export const tipsConfig = defineTips({ * volumeOffset: 25, * volumeLabel: 'Vol B', * }); * * Consumed by `` and `` components + the auto-injected * `/tips` route to compute display numbers from `` source tags. */ declare function defineTips(opts: TipsConfigInput): TipsConfig; export { ACADEMIC_PART_NAMES, BRANDON_PORTFOLIO_DEFAULT, BUILTIN_STYLES, BookConfigOptions, BookCorpus, BookCorpusInput, BookScaffoldIntegrationOptions, CORPUS_APPARATUS_TOGGLE_BY_ROUTE, ChaptersRenderer, CorpusApparatusRoute, CorpusBook, DEFAULT_GITHUB_BRANCH, type Freshness, type FreshnessStatus, type PartReviewGroup, type PartReviewSelection, Question, RESERVED_CORPUS_BOOK_IDS, type ReviewChapter, type ReviewExercise, SiblingBooks, Style, type TipsConfig, type TipsConfigInput, UNKNOWN_PART_ORDINAL, type VisibleHeading, type VolatilityLevel, academicChaptersRenderer, academicPartHeading, academicPartName, academicPartOrdinal, academicParts, academicStyle, assertBookCorpus, assertEnumProp, assertKnownDomain, bookScaffoldIntegration, buildGithubUrl, chapterLabel, chapterSortKey, corpusApparatusRoutesForBook, corpusBookHasApparatusRoute, corpusBookIdFromPath, corpusBookIdOf, corpusCollectionEntryId, courseNotesStyle, defineBookConfig, defineBookCorpus, defineMdxComponents, defineTips, deriveObjectiveMap, distinctChaptersSorted, fallbackChaptersRenderer, filterCorpusEntries, freshnessLabel, getFreshness, groupByChapter, groupByDomain, localCorpusEntryId, minimalStyle, originUrlFromGitConfig, parseRepoSlug, pickActive, researchPortfolioChaptersRenderer, researchPortfolioStyle, resolveBookHref, resolveCorpusBook, resolveCorpusBookHref, resolveGithubRepo, selectBookArtifact, selectPartExercises, sortQuestions, tocHeadings, toolsChaptersRenderer, toolsStyle, volatilityLevels };