Add closing Gloria Patri after psalms, omitted during the Sacred Triduum
Psalms across every hour rendered with no closing Gloria Patri at all. Add it centrally (hours/index.ts) rather than per-hour, using a new calendar/temporal.ts isInTriduum helper so it's correctly dropped from Maundy Thursday through Holy Saturday and nowhere else — live-verified against both of this app's actual source tracks (Divino Afflatu 1954 and Tridentine 1906), not just the modern Rubrics 1960 default. Also fixes Lauds' existing but Triduum-blind canticle Gloria Patri append.
This commit is contained in:
+22
-2
@@ -1,4 +1,4 @@
|
||||
import type { HourId, ResolvedOrdo } from './types';
|
||||
import type { HourId, ResolvedOrdo, ResolvedPart, ResolvedText } from './types';
|
||||
import { HOUR_IDS } from './types';
|
||||
import * as prime from './prime';
|
||||
import * as compline from './compline';
|
||||
@@ -8,6 +8,8 @@ import * as none from './none';
|
||||
import * as lauds from './lauds';
|
||||
import * as vespers from './vespers';
|
||||
import * as matins from './matins';
|
||||
import { resolveDay } from '../calendar';
|
||||
import { resolveGloriaPatri } from './resolve-common';
|
||||
|
||||
type Resolver = (date: string) => ResolvedOrdo;
|
||||
|
||||
@@ -24,8 +26,26 @@ const registry: Record<HourId, Resolver> = {
|
||||
matins: matins.resolveOrdo,
|
||||
};
|
||||
|
||||
/** Attaches the closing Gloria Patri to every plain psalm part (recursing
|
||||
* into 'nocturn-psalmody' groups), so no per-hour builder has to remember
|
||||
* to do it — and so the Sacred Triduum omission (resolveGloriaPatri) is
|
||||
* enforced in exactly one place rather than once per hour file. */
|
||||
function withGloriaPatri(parts: ResolvedPart[], gloriaPatri: ResolvedText | undefined): ResolvedPart[] {
|
||||
return parts.map((part) => {
|
||||
if (part.kind === 'psalm') {
|
||||
return { ...part, gloriaPatri };
|
||||
}
|
||||
if (part.kind === 'nocturn-psalmody') {
|
||||
return { ...part, psalms: withGloriaPatri(part.psalms, gloriaPatri) };
|
||||
}
|
||||
return part;
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveOrdo(hourId: HourId, date: string): ResolvedOrdo {
|
||||
return registry[hourId](date);
|
||||
const ordo = registry[hourId](date);
|
||||
const gloriaPatri = resolveGloriaPatri(resolveDay(date));
|
||||
return { ...ordo, parts: withGloriaPatri(ordo.parts, gloriaPatri) };
|
||||
}
|
||||
|
||||
export { HOUR_IDS };
|
||||
|
||||
+10
-6
@@ -1,6 +1,7 @@
|
||||
import type { HourDefinition, HourPart, ResolvedOrdo, ResolvedPart, ResolvedText } from './types';
|
||||
import type { LiturgicalDay, Weekday } from '../calendar/types';
|
||||
import { resolveDay, isSundayOrFeast, activeOctavesFor } from '../calendar';
|
||||
import { isInTriduum } from '../calendar/temporal';
|
||||
import { getDayLabel } from '../calendar/day-label';
|
||||
import { getTemporalFeastRecord } from '../calendar/temporal-feasts';
|
||||
import { getPsalmVerses } from '../psalter';
|
||||
@@ -104,8 +105,10 @@ const GLORIA_PATRI = {
|
||||
* how they were transcribed, not because callers need per-verse access.
|
||||
* Appends the fixed Gloria Patri line every canticle (or canticle part)
|
||||
* ends with in real practice, same wording as benedictus.yml's own
|
||||
* trailing line. */
|
||||
function canticleText(canticleId: string, slice?: [number, number]): ResolvedText {
|
||||
* trailing line — omitted during the Sacred Triduum, live-verified against
|
||||
* both Divino Afflatu 1954 and Tridentine 1906 (Benedictus at Lauds of
|
||||
* Holy Thursday omits it same as any psalm). */
|
||||
function canticleText(canticleId: string, slice: [number, number] | undefined, omitGloriaPatri: boolean): ResolvedText {
|
||||
const canticle = getCanticle(canticleId);
|
||||
const verses = slice ? canticle.verses.slice(slice[0], slice[1]) : canticle.verses;
|
||||
const text: Partial<Record<string, string>> = {};
|
||||
@@ -115,7 +118,7 @@ function canticleText(canticleId: string, slice?: [number, number]): ResolvedTex
|
||||
if (lines.length === 0) {
|
||||
continue;
|
||||
}
|
||||
text[lang] = `${lines.join('\n')}\n${GLORIA_PATRI[lang]}`;
|
||||
text[lang] = omitGloriaPatri ? lines.join('\n') : `${lines.join('\n')}\n${GLORIA_PATRI[lang]}`;
|
||||
let worst: 'verified' | 'draft' | 'missing' = 'verified';
|
||||
for (const v of verses) {
|
||||
const s = v.status[lang] ?? 'missing';
|
||||
@@ -150,6 +153,7 @@ function resolvePsalmody(day: LiturgicalDay): ResolvedPart[] {
|
||||
const canticle = getCanticle(wd.canticle.id);
|
||||
const canticleOpening = opening(wd.canticle.antiphon);
|
||||
const canticleClosing = splitNamedAntiphon(verifiedText(wd.canticle.antiphon)).full;
|
||||
const omitGloriaPatri = isInTriduum(day.date);
|
||||
if (wd.canticle.split) {
|
||||
// Said in two pieces, own Gloria Patri each, one shared antiphon
|
||||
// framing both (opening before the first, full repeated only after
|
||||
@@ -158,19 +162,19 @@ function resolvePsalmody(day: LiturgicalDay): ResolvedPart[] {
|
||||
kind: 'canticle',
|
||||
canticleId: canticle.id,
|
||||
antiphon: canticleOpening,
|
||||
text: canticleText(canticle.id, [0, wd.canticle.split]),
|
||||
text: canticleText(canticle.id, [0, wd.canticle.split], omitGloriaPatri),
|
||||
});
|
||||
parts.push({
|
||||
kind: 'canticle',
|
||||
canticleId: canticle.id,
|
||||
text: canticleText(canticle.id, [wd.canticle.split, canticle.verses.length]),
|
||||
text: canticleText(canticle.id, [wd.canticle.split, canticle.verses.length], omitGloriaPatri),
|
||||
});
|
||||
} else {
|
||||
parts.push({
|
||||
kind: 'canticle',
|
||||
canticleId: canticle.id,
|
||||
antiphon: canticleOpening,
|
||||
text: canticleText(canticle.id),
|
||||
text: canticleText(canticle.id, undefined, omitGloriaPatri),
|
||||
});
|
||||
}
|
||||
parts.push({ kind: 'antiphon', text: canticleClosing });
|
||||
|
||||
@@ -5,6 +5,7 @@ import { getCommonProper, getTemporalProper } from '../propers';
|
||||
import { getSaintRecord } from '../calendar/feasts';
|
||||
import { resolveActiveOctave, activeOctavesFor, octaveGoverningPrivilegedDay, isAtLeast } from '../calendar';
|
||||
import { getTemporalFeastRecord } from '../calendar/temporal-feasts';
|
||||
import { isInTriduum } from '../calendar/temporal';
|
||||
import { splitAntiphon } from './antiphon';
|
||||
import vespersMagnificatAntiphonsData from '../data/hours/vespers-magnificat-antiphons.yml';
|
||||
|
||||
@@ -677,3 +678,22 @@ export function splitNamedAntiphon(antiphon: ResolvedText): {
|
||||
}
|
||||
return { incipit: { text: incipitText, status: incipitStatus }, full: { text: fullText, status: fullStatus } };
|
||||
}
|
||||
|
||||
/** Fixed wording, same as lauds.ts's weekday-canticle Gloria Patri —
|
||||
* appended after every psalm (hours/index.ts) except during the Sacred
|
||||
* Triduum. Not "verified" via verifiedText() because it's boilerplate
|
||||
* used everywhere, not a sourced proper text. */
|
||||
const GLORIA_PATRI: BilingualText = {
|
||||
la: 'V. Glória Patri, et Fílio, * et Spirítui Sancto.\nR. Sicut erat in princípio, et nunc, et semper, * et in sǽcula sæculórum. Amen.',
|
||||
en: 'V. Glory be to the Father, and to the Son, * and to the Holy Ghost.\nR. As it was in the beginning, is now, * and ever shall be, world without end. Amen.',
|
||||
};
|
||||
|
||||
/** The closing Gloria Patri for a psalm, or `undefined` during the Sacred
|
||||
* Triduum (calendar/temporal.ts's isInTriduum) when it's omitted
|
||||
* entirely — not just seasonally varied wording, an actual omission. */
|
||||
export function resolveGloriaPatri(day: LiturgicalDay): ResolvedText | undefined {
|
||||
if (isInTriduum(day.date)) {
|
||||
return undefined;
|
||||
}
|
||||
return verifiedText(GLORIA_PATRI);
|
||||
}
|
||||
|
||||
+5
-1
@@ -199,7 +199,11 @@ export interface HourDefinition {
|
||||
export type ResolvedPart =
|
||||
| { kind: 'hymn' | 'chapter' | 'responsory' | 'versicle' | 'prayer'; text: ResolvedText }
|
||||
| { kind: 'preces'; text: ResolvedText; label?: string }
|
||||
| { kind: 'psalm'; psalmNumber: number; antiphon?: ResolvedText; verses: ResolvedVerse[] }
|
||||
// `gloriaPatri` is filled in centrally (hours/index.ts, after every hour
|
||||
// builder returns) rather than per-hour, so it's absent only during the
|
||||
// Sacred Triduum (calendar/temporal.ts's isInTriduum) — never omit it
|
||||
// when building a psalm part yourself.
|
||||
| { kind: 'psalm'; psalmNumber: number; antiphon?: ResolvedText; verses: ResolvedVerse[]; gloriaPatri?: ResolvedText }
|
||||
| { kind: 'canticle'; canticleId: string; text: ResolvedText; antiphon?: ResolvedText }
|
||||
// A standalone antiphon, said on its own rather than framing a psalm —
|
||||
// see 'closing-antiphon' above.
|
||||
|
||||
Reference in New Issue
Block a user