Add Vespers' ferial Preces, sourced from Tridentine 1906/1910
Monastic 1617 has no ferial Preces at Vespers, same gap Lauds already had — the fuller "Preces feriales Vespera" is byte-for-byte the Lauds ferial Preces with Psalm 129 swapped for Psalm 50 in the source, live- verified against the same clean Advent feria already used for Lauds. The short Sunday/feast form turns out to be identical at both hours too, so it's reused directly rather than duplicated. Factors the shared ferial-or-vigil predicate out of hours/lauds.ts into resolve-common.ts's isFerialOrVigil so both hours' -preces cases resolve it the same way instead of keeping two copies in sync by hand. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,7 @@ function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
case 'vespers-psalmody':
|
||||
case 'vespers-office':
|
||||
case 'magnificat':
|
||||
case 'vespers-preces':
|
||||
throw new Error(`Compline's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-19
@@ -18,6 +18,7 @@ import {
|
||||
ALWAYS_OVERRIDE_TEMPORAL_IDS,
|
||||
verifiedText,
|
||||
seasonalOfficeSuffix,
|
||||
isFerialOrVigil,
|
||||
} from './resolve-common';
|
||||
import laudsDefinitionData from '../data/hours/lauds.yml';
|
||||
import laudsAntiphonsData from '../data/hours/lauds-antiphons.yml';
|
||||
@@ -317,25 +318,7 @@ function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
return [{ kind: 'preces', text: resolveCommon(id), label: getMarianAntiphonLabel(id) }];
|
||||
}
|
||||
case 'lauds-preces': {
|
||||
// Deliberately not `isSundayOrFeast` -- that treats any sanctoral
|
||||
// winner, Vigils included, as a "feast." Here a Vigil needs to land
|
||||
// on the ferial side instead (per direct instruction), so it's
|
||||
// checked for explicitly: a Vigil-ranked sanctoral winner, or a
|
||||
// bare temporal winner that isn't Sunday and isn't a named
|
||||
// temporal identity like Marian Saturday or Christ the King (which
|
||||
// aren't Sunday or a sanctoral winner either, but still aren't a
|
||||
// bare ferial office -- same reasoning as the Cross suffrage's own
|
||||
// gate just above). Also excludes any day within an active octave
|
||||
// -- live-verified (Tridentine 1910, day 3 of St. Lawrence's own
|
||||
// octave): "Preces Feriales{omittitur}", same exclusion the
|
||||
// suffrages already have, for the same reason -- an octave day
|
||||
// isn't a bare ferial office even though nothing else is winning
|
||||
// it outright.
|
||||
const isVigil = day.winner.kind === 'sanctoral' && day.winner.rank === 'vigil';
|
||||
const isBareFeria = day.winner.kind === 'temporal' && !getTemporalFeastRecord(day.winner.id);
|
||||
const isWithinAnOctave = activeOctavesFor(day.date).length > 0;
|
||||
const isFerialOrVigil = day.weekday !== 'sunday' && !isWithinAnOctave && (isVigil || isBareFeria);
|
||||
const id = isFerialOrVigil ? 'lauds-preces-feriales' : 'lauds-short-litany';
|
||||
const id = isFerialOrVigil(day) ? 'lauds-preces-feriales' : 'lauds-short-litany';
|
||||
return [{ kind: 'preces', text: resolveCommon(id) }];
|
||||
}
|
||||
// Not used by Lauds.
|
||||
@@ -354,6 +337,7 @@ function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
case 'vespers-psalmody':
|
||||
case 'vespers-office':
|
||||
case 'magnificat':
|
||||
case 'vespers-preces':
|
||||
throw new Error(`Lauds' ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
case 'vespers-psalmody':
|
||||
case 'vespers-office':
|
||||
case 'magnificat':
|
||||
case 'vespers-preces':
|
||||
throw new Error(`None's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ function resolvePart(part: HourPart, date: string, day: LiturgicalDay): Resolved
|
||||
case 'vespers-psalmody':
|
||||
case 'vespers-office':
|
||||
case 'magnificat':
|
||||
case 'vespers-preces':
|
||||
throw new Error(`Prime's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { Commemoration, DayWinner, LiturgicalDay, Weekday } from '../calend
|
||||
import type { ProperText } from '../propers';
|
||||
import { getCommonProper, getTemporalProper } from '../propers';
|
||||
import { getSaintRecord } from '../calendar/feasts';
|
||||
import { resolveActiveOctave } from '../calendar';
|
||||
import { resolveActiveOctave, activeOctavesFor } from '../calendar';
|
||||
import { getTemporalFeastRecord } from '../calendar/temporal-feasts';
|
||||
import { splitAntiphon } from './antiphon';
|
||||
import vespersMagnificatAntiphonsData from '../data/hours/vespers-magnificat-antiphons.yml';
|
||||
|
||||
@@ -105,6 +106,31 @@ export function isSundayOrFeastOffice(day: LiturgicalDay): boolean {
|
||||
return day.weekday === 'sunday' || resolveOfficeWinner(day).kind === 'sanctoral';
|
||||
}
|
||||
|
||||
/**
|
||||
* A day/hour is "ferial or vigil" — the predicate both Lauds' and Vespers'
|
||||
* ferial Preces (`lauds-preces`/`vespers-preces`) key off to pick the
|
||||
* fuller Tridentine 1906/1910 litany over the short Sunday/feast one.
|
||||
* Deliberately not `isSundayOrFeast`/`isSundayOrFeastOffice` — those treat
|
||||
* any sanctoral winner, Vigils included, as a "feast." Here a Vigil needs
|
||||
* to land on the ferial side instead (per direct instruction), so it's
|
||||
* checked for explicitly: a Vigil-ranked sanctoral winner, or a bare
|
||||
* temporal winner that isn't Sunday and isn't a named temporal identity
|
||||
* like Marian Saturday or Christ the King (which aren't Sunday or a
|
||||
* sanctoral winner either, but still aren't a bare ferial office — same
|
||||
* reasoning as Lauds' own Cross-suffrage gate). Also excludes any day
|
||||
* within an active octave — live-verified (Tridentine 1910, day 3 of St.
|
||||
* Lawrence's own octave, both Lauds and Vespers): "Preces Feriales
|
||||
* {omittitur}", same exclusion the suffrages already have, for the same
|
||||
* reason — an octave day isn't a bare ferial office even though nothing
|
||||
* else is winning it outright.
|
||||
*/
|
||||
export function isFerialOrVigil(day: LiturgicalDay): boolean {
|
||||
const isVigil = day.winner.kind === 'sanctoral' && day.winner.rank === 'vigil';
|
||||
const isBareFeria = day.winner.kind === 'temporal' && !getTemporalFeastRecord(day.winner.id);
|
||||
const isWithinAnOctave = activeOctavesFor(day.date).length > 0;
|
||||
return day.weekday !== 'sunday' && !isWithinAnOctave && (isVigil || isBareFeria);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id to look up for today's office winner's (resolveOfficeWinner)
|
||||
* minor-hour content (antiphon + chapter) — `${hourId}-antiphon-${id}`/
|
||||
|
||||
@@ -70,6 +70,7 @@ function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
case 'vespers-psalmody':
|
||||
case 'vespers-office':
|
||||
case 'magnificat':
|
||||
case 'vespers-preces':
|
||||
throw new Error(`Sext's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
case 'vespers-psalmody':
|
||||
case 'vespers-office':
|
||||
case 'magnificat':
|
||||
case 'vespers-preces':
|
||||
throw new Error(`Terce's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,19 @@ export type HourPart =
|
||||
// deliberately not the shared `isSundayOrFeast` (a Vigil counts as a
|
||||
// "feast" there; here it needs to count as ferial-like instead).
|
||||
| { kind: 'lauds-preces' }
|
||||
// Vespers only. Mirrors 'lauds-preces' exactly — the Tridentine
|
||||
// 1906/1910 "Preces feriales Vespera" (source: `@:Preces feriales
|
||||
// Laudes:s/129/50/` — byte-for-byte the Lauds ferial Preces with Psalm
|
||||
// 129 swapped for Psalm 50, live-verified against a clean Advent feria
|
||||
// 2026-12-17) on a ferial or vigil day, replacing the short Sunday/feast
|
||||
// litany otherwise said before the day's collect(s) — see hours/
|
||||
// resolve-common.ts's isFerialOrVigil (shared with 'lauds-preces') and
|
||||
// hours/vespers.ts's 'vespers-preces' case. Unlike Lauds, the short form
|
||||
// here reuses `lauds-short-litany` directly rather than a Vespers-named
|
||||
// duplicate — confirmed byte-identical by live query at both hours,
|
||||
// same reuse discipline as data/hours/vespers.yml's `lauds-conclusio`
|
||||
// closing.
|
||||
| { kind: 'vespers-preces' }
|
||||
// Lauds/Vespers. The day's own collect *and* one more per commemoration
|
||||
// (calendar/types.ts's LiturgicalDay.commemorations) — see hours/
|
||||
// resolve-common.ts's getDayCollects. Distinct from the singular
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
resolveOfficeWinner,
|
||||
verifiedText,
|
||||
seasonalOfficeSuffix,
|
||||
isFerialOrVigil,
|
||||
} from './resolve-common';
|
||||
import vespersDefinitionData from '../data/hours/vespers.yml';
|
||||
import vespersAntiphonsData from '../data/hours/vespers-antiphons.yml';
|
||||
@@ -122,6 +123,10 @@ function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
{ kind: 'antiphon', text: full },
|
||||
];
|
||||
}
|
||||
case 'vespers-preces': {
|
||||
const id = isFerialOrVigil(day) ? 'vespers-preces-feriales' : 'lauds-short-litany';
|
||||
return [{ kind: 'preces', text: resolveCommon(id) }];
|
||||
}
|
||||
case 'day-collects':
|
||||
return getDayCollects(day);
|
||||
case 'preces':
|
||||
|
||||
Reference in New Issue
Block a user