calendar: real occurrence/precedence resolution (feast ranks, sanctoral content)
Replaces the FeastRank placeholder with a real ordered FeastClass (the old pre-1955 six-level scale) and a new TemporalCategory (privileged/ordinary Sunday/feria). calendar/commemorations.ts's decideOccurrence encodes the actual precedence rules as explicit, commented branches per category rather than the reference engine's own opaque numeric weights — this app targets one ruleset, so the readability trade-off is worth it. Category membership (data/calendar/temporal-categories.yml) is a best-effort reconstruction of the pre-1955 tradition, not sourced from a primary text; expect corrections. calendar/feasts.ts resolves real sanctoral candidates, and resolveDay() wires it all together — occurring/temporalCategory are no longer stubs. isDoubleOrHigher (hours/antiphon.ts) is now a real comparison instead of a hardcoded false. Seeded 7 real saints/feasts, each verified directly against Divinum Officium's own rank data (not reconstructed from memory) as a small, growable start: St. Lawrence, the Assumption, St. Augustine, the Nativity of the BVM, St. Michael, All Saints, the Immaculate Conception.
This commit is contained in:
+49
-4
@@ -1,6 +1,51 @@
|
||||
// Sanctoral occurrence resolution: given a date, which saint(s) are
|
||||
// assigned via data/calendar/sanctoral-calendar.yml's day -> saint-id
|
||||
// mapping, and their rank/propers/common from data/calendar/saints/<id>.yml.
|
||||
// See calendar/temporal.ts for the separate Easter/fixed-date resolution.
|
||||
// Unused until milestone 4.
|
||||
export {};
|
||||
// mapping, and their rank from data/calendar/saints/<id>.yml. See
|
||||
// calendar/temporal.ts for the separate temporal-cycle resolution, and
|
||||
// calendar/commemorations.ts for how a candidate returned here actually
|
||||
// gets weighed against the day's temporal standing.
|
||||
//
|
||||
// Doesn't resolve clashes *among* multiple saints sharing a date — real
|
||||
// practice has its own precedence/commemoration rules for that too, not
|
||||
// modeled here. If a day has more than one candidate, calendar/index.ts
|
||||
// just picks the highest-ranked as the day's sole sanctoral contender.
|
||||
import type { FeastClass } from './types';
|
||||
import sanctoralCalendarData from '../data/calendar/sanctoral-calendar.yml';
|
||||
|
||||
interface SaintRecord {
|
||||
id: string;
|
||||
name: string;
|
||||
rank: FeastClass;
|
||||
common: string;
|
||||
propers: string | null;
|
||||
}
|
||||
|
||||
const sanctoralCalendar = sanctoralCalendarData as { days: Record<string, string[]> };
|
||||
|
||||
const saintModules = import.meta.glob<{ default: SaintRecord }>('../data/calendar/saints/*.yml', {
|
||||
eager: true,
|
||||
});
|
||||
|
||||
const saintsById = new Map<string, SaintRecord>();
|
||||
for (const mod of Object.values(saintModules)) {
|
||||
saintsById.set(mod.default.id, mod.default);
|
||||
}
|
||||
|
||||
export interface SanctoralCandidate {
|
||||
id: string;
|
||||
name: string;
|
||||
rank: FeastClass;
|
||||
}
|
||||
|
||||
/** Which saint(s) (if any) are assigned to this date. `isoDate`'s year is ignored — the sanctoral cycle repeats every civil year. */
|
||||
export function getSanctoralCandidatesFor(isoDate: string): SanctoralCandidate[] {
|
||||
const monthDay = isoDate.slice(5);
|
||||
const ids = sanctoralCalendar.days[monthDay] ?? [];
|
||||
return ids.map((id) => {
|
||||
const saint = saintsById.get(id);
|
||||
if (!saint) {
|
||||
throw new Error(`sanctoral-calendar.yml references unknown saint id '${id}'`);
|
||||
}
|
||||
return { id: saint.id, name: saint.name, rank: saint.rank };
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user