scripture: add a dedicated non-Psalm scripture store, migrate pure-scripture readings
Deploy / deploy (push) Successful in 46s

Adds src/scripture/ (types.ts, index.ts), a verse-addressable store mirroring
psalter/index.ts's own pattern exactly -- getScriptureVerses(book, chapter,
range) -- for non-Psalm scripture (Wisdom, Canticles, Ezekiel, Acts, Joel,
etc.). Kept as its own module, not folded into the Psalter, per 2026-08
discussion ("the psalms kind of work differently even if the logic is the
same, they do need their own store").

Octave readings can now declare `passages: [{ book, chapter, verses }]`
instead of embedding text -- each entry becomes its own paragraph, letting a
reading built from several verse ranges keep its original paragraph breaks.
getOctaveReading resolves either shape (embedded text or passages) into the
same OctaveReadingText callers already expect, so nothing downstream needs
to know which was used. Scope, per discussion: only readings that are
Scripture start to finish get this treatment -- prose that merely quotes a
verse or two (most of the patristic sermons pulled so far) keeps its
quotations embedded inline, as before.

Migrates the 9 already-committed octave readings that are pure Scripture
(Wisdom 4:7-20, Canticles 1 and 8, seven different Ezekiel excerpts across
All Saints' octave -- one of which, day 7, genuinely spans two chapters --
and Acts 19/Joel 2 for Pentecost's octave) to the new passages notation,
verified to resolve to character-identical text as before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 10:06:10 -04:00
parent 589c4be0d7
commit 49bd7f7858
30 changed files with 1003 additions and 284 deletions
+40
View File
@@ -0,0 +1,40 @@
import type { ScriptureChapter, ScriptureVerse } from './types';
// Mirrors psalter/index.ts's own loading pattern exactly, one directory
// over — see that file for why (@rollup/plugin-yaml parses at build time,
// no YAML parser shipped to the client).
const modules = import.meta.glob<{ default: ScriptureChapter }>('../data/scripture/*.yml', {
eager: true,
});
const chapters = new Map<string, ScriptureChapter>();
for (const mod of Object.values(modules)) {
chapters.set(`${mod.default.book}-${mod.default.chapter}`, mod.default);
}
export function getScriptureChapter(book: string, chapter: number): ScriptureChapter | undefined {
return chapters.get(`${book}-${chapter}`);
}
/**
* @param verseRange e.g. "2-19" — inclusive. Whole (authored) chapter when
* omitted. Unauthored book/chapter combinations resolve to an empty array
* rather than throwing — content-authoring the full non-Psalm scripture
* corpus is an ongoing, incremental task, same "resolve as pending"
* stance as everywhere else, not a error condition.
*/
export function getScriptureVerses(book: string, chapter: number, verseRange?: string): ScriptureVerse[] {
const found = getScriptureChapter(book, chapter);
if (!found) {
return [];
}
if (!verseRange) {
return found.verses;
}
const [startStr, endStr] = verseRange.split('-');
const start = Number(startStr);
const end = endStr ? Number(endStr) : start;
return found.verses.filter((v) => v.n >= start && v.n <= end);
}
export type { ScriptureChapter, ScriptureVerse } from './types';
+20
View File
@@ -0,0 +1,20 @@
import type { LanguageCode, TranslationStatus } from '../psalter/types';
export interface ScriptureVerse {
n: number;
text: Partial<Record<LanguageCode, string>>;
status: Partial<Record<LanguageCode, TranslationStatus>>;
}
/** One chapter of one (non-Psalm) book — Vulgate/Douay-Rheims numbering and
* versification, same convention as psalter/types.ts's Psalm, kept as a
* separate store rather than folded into it (2026-08 discussion: "the
* psalms kind of work differently even if the logic is the same, they do
* need their own store"). `book` is a short citation-style abbreviation
* (e.g. "ezek", "cant", "sir", "act", "joel", "sap") matching how these
* passages are already cited throughout this project's propers. */
export interface ScriptureChapter {
book: string;
chapter: number;
verses: ScriptureVerse[];
}