Three separate resolver files (terce.ts/sext.ts/none.ts), not a shared little-hour module — they're structurally identical but kept independent by choice. Ordo verified against Divinum Officium (Monastic Tridentinum 1617): opening versicle, self-contained hymn, one antiphon framing three psalms (no closing repeat, unlike Prime), a capitulum with its own embedded short versicle/response, the day's own collect, and a closing identical across all three hours. Also fills in psalter-distribution.yml's previously-missing Sunday/ Monday/Tuesday psalm entries (Ps 118 sections for Sunday/Monday; Tuesday turns out to share Wednesday/Thursday's native gradual-psalm set, not a distinct one). Friday/Saturday's antiphons are derived from each day's first psalm rather than reusing the native gradual antiphons, since those two days already use a redistributed, non-native psalm set. hours/types.ts gains a 'day-collect' part kind — the first ordo to use the day's own Proper collect rather than a fixed text. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
# See terce-antiphons.yml for the full explanation of this table's shape,
|
||||
# sourcing, and the Friday/Saturday derivation caveat.
|
||||
sunday:
|
||||
la: "Allelúja."
|
||||
en: "Alleluia."
|
||||
monday:
|
||||
la: "Fiat manus tua."
|
||||
en: "Let thy hand be with me to save me."
|
||||
tuesday: &gradual-none-ant
|
||||
la: "Beáti omnes."
|
||||
en: "Blessed are all they."
|
||||
wednesday: *gradual-none-ant
|
||||
thursday: *gradual-none-ant
|
||||
friday:
|
||||
la: "Si vere útique justítiam loquímini."
|
||||
en: "If in very deed you speak justice."
|
||||
saturday:
|
||||
la: "Dóminus regnávit."
|
||||
en: "The Lord hath reigned."
|
||||
@@ -0,0 +1,16 @@
|
||||
# See terce.yml for the full explanation of this ordo's structure and
|
||||
# deliberate deviations — None is identical in shape, only its hymn,
|
||||
# antiphon/psalms, and capitulum differ.
|
||||
id: none
|
||||
parts:
|
||||
- kind: opening-versicle
|
||||
- kind: hymn
|
||||
textRef: { source: common, id: none-hymn }
|
||||
- kind: variable
|
||||
resolve: by-weekday
|
||||
- kind: chapter
|
||||
textRef: { source: common, id: none-capitulum-per-annum }
|
||||
- kind: day-collect
|
||||
- kind: preces
|
||||
textRef: { source: common, id: little-hour-conclusio }
|
||||
label: Conclusion
|
||||
@@ -0,0 +1,19 @@
|
||||
# See terce-antiphons.yml for the full explanation of this table's shape,
|
||||
# sourcing, and the Friday/Saturday derivation caveat.
|
||||
sunday:
|
||||
la: "Allelúja."
|
||||
en: "Alleluia."
|
||||
monday:
|
||||
la: "Áspice in me."
|
||||
en: "Look upon me."
|
||||
tuesday: &gradual-sext-ant
|
||||
la: "Qui hábitas."
|
||||
en: "Who dwellest."
|
||||
wednesday: *gradual-sext-ant
|
||||
thursday: *gradual-sext-ant
|
||||
friday:
|
||||
la: "Quid gloriáris in malítia."
|
||||
en: "Why dost thou glory in malice."
|
||||
saturday:
|
||||
la: "Fundaménta ejus in móntibus sanctis."
|
||||
en: "The foundations thereof are in the holy mountains."
|
||||
@@ -0,0 +1,16 @@
|
||||
# See terce.yml for the full explanation of this ordo's structure and
|
||||
# deliberate deviations — Sext is identical in shape, only its hymn,
|
||||
# antiphon/psalms, and capitulum differ.
|
||||
id: sext
|
||||
parts:
|
||||
- kind: opening-versicle
|
||||
- kind: hymn
|
||||
textRef: { source: common, id: sext-hymn }
|
||||
- kind: variable
|
||||
resolve: by-weekday
|
||||
- kind: chapter
|
||||
textRef: { source: common, id: sext-capitulum-per-annum }
|
||||
- kind: day-collect
|
||||
- kind: preces
|
||||
textRef: { source: common, id: little-hour-conclusio }
|
||||
label: Conclusion
|
||||
@@ -0,0 +1,38 @@
|
||||
# One antiphon per weekday, framing that day's whole psalm group (see
|
||||
# hours/terce.ts and psalter/distribution.ts's 'terce' table) — same shape
|
||||
# and convention as data/hours/prime-antiphons.yml (inline text, no
|
||||
# separate common-propers id, incipit/full split via an embedded "*" where
|
||||
# the source has one). None of these happen to have a "*" — each is a
|
||||
# short single-clause antiphon and splitAntiphon() already degrades
|
||||
# gracefully (incipit == full) when there isn't one.
|
||||
#
|
||||
# Verified against Divinum Officium (Monastic Tridentinum 1617):
|
||||
# - sunday/monday/tuesday: clean per-annum runs with no sanctoral
|
||||
# override (2026-08-09 Sun, 2026-08-31 Mon, 2026-10-13 Tue).
|
||||
# - wednesday/thursday: share Tuesday's antiphon — same native psalms
|
||||
# (119-121), confirmed identical across all three.
|
||||
# - friday/saturday: these two days' psalms were editorially
|
||||
# redistributed (see psalter-distribution.yml's long comment on
|
||||
# 'terce'), so there's no real historical antiphon for this specific
|
||||
# grouping. Per explicit decision, each is instead derived from the
|
||||
# opening phrase of that day's first psalm (Ps 41 for Friday, Ps 69 for
|
||||
# Saturday) — editorial, not independently verified against the
|
||||
# reference engine as a real antiphon, same caveat as the
|
||||
# redistribution itself.
|
||||
sunday:
|
||||
la: "Allelúja."
|
||||
en: "Alleluia."
|
||||
monday:
|
||||
la: "Ádjuva me."
|
||||
en: "Help me."
|
||||
tuesday: &gradual-terce-ant
|
||||
la: "Clamávi."
|
||||
en: "I cried unto the Lord."
|
||||
wednesday: *gradual-terce-ant
|
||||
thursday: *gradual-terce-ant
|
||||
friday:
|
||||
la: "Quemádmodum desíderat cervus."
|
||||
en: "As the hart panteth."
|
||||
saturday:
|
||||
la: "Deus, in adjutórium meum inténde."
|
||||
en: "O God, come to my assistance."
|
||||
@@ -0,0 +1,29 @@
|
||||
# Ordo verified against Divinum Officium (Monastic Tridentinum 1617), clean
|
||||
# per-annum runs with no sanctoral override (2026-08-09 Sun, 2026-08-31 Mon,
|
||||
# 2026-10-13 Tue) — see src/hours/terce.ts for how each part resolves.
|
||||
#
|
||||
# Deliberately deviates from that verified source in one place, matching
|
||||
# Prime's own precedent: the opening Pater/Ave (said silently before "Deus
|
||||
# in adjutórium") is dropped, to keep the hour shorter.
|
||||
#
|
||||
# Real structure, confirmed directly against the engine: opening versicle,
|
||||
# hymn (self-contained, no seasonal doxology swap — see terce-hymn.yml),
|
||||
# one antiphon framing all three psalms, capitulum with its own embedded
|
||||
# short versicle/response (*not* a separate elaborate responsory the way
|
||||
# Prime's chapter-responsory.ts adds one — Monastic Terce doesn't have
|
||||
# one), the day's own collect, and a closing that's identical across all
|
||||
# three Little Hours (see little-hour-conclusio.yml). No Preces, no Creed —
|
||||
# real Monastic Terce/Sext/None have neither.
|
||||
id: terce
|
||||
parts:
|
||||
- kind: opening-versicle
|
||||
- kind: hymn
|
||||
textRef: { source: common, id: terce-hymn }
|
||||
- kind: variable
|
||||
resolve: by-weekday
|
||||
- kind: chapter
|
||||
textRef: { source: common, id: terce-capitulum-per-annum }
|
||||
- kind: day-collect
|
||||
- kind: preces
|
||||
textRef: { source: common, id: little-hour-conclusio }
|
||||
label: Conclusion
|
||||
@@ -0,0 +1,23 @@
|
||||
# Verified against Divinum Officium (Monastic Tridentinum 1617), clean
|
||||
# per-annum run (2026-08-31) — identical across Terce, Sext, and None
|
||||
# (confirmed by comparing all three directly), unlike Prime's/Compline's
|
||||
# own distinct conclusios.
|
||||
id: little-hour-conclusio
|
||||
text:
|
||||
la: |
|
||||
V. Dómine, exáudi oratiónem meam.
|
||||
R. Et clamor meus ad te véniat.
|
||||
V. Benedicámus Dómino.
|
||||
R. Deo grátias.
|
||||
V. Fidélium ánimæ per misericórdiam Dei requiéscant in pace.
|
||||
R. Amen.
|
||||
en: |
|
||||
V. O Lord, hear my prayer.
|
||||
R. And let my cry come unto thee.
|
||||
V. Let us bless the Lord.
|
||||
R. Thanks be to God.
|
||||
V. May the souls of the faithful, through the mercy of God, rest in peace.
|
||||
R. Amen.
|
||||
status:
|
||||
la: verified
|
||||
en: verified
|
||||
@@ -0,0 +1,13 @@
|
||||
# Verified against Divinum Officium (Monastic Tridentinum 1617), clean
|
||||
# per-annum run (2026-08-31). See terce-capitulum-per-annum.yml for the
|
||||
# combined-block structure note and the deferred-seasonal-variant caveat.
|
||||
id: none-capitulum-per-annum
|
||||
text:
|
||||
la: "Empti enim estis prétio magno. Glorificáte et portáte Deum in córpore vestro. R. Deo grátias. V. Ab occúltis meis munda me, Dómine. R. Et ab aliénis parce servo tuo."
|
||||
en: "For you are bought with a great price. Glorify and bear God in your body. R. Thanks be to God. V. From my secret ones cleanse me, O Lord. R. And from those of others spare thy servant."
|
||||
citation:
|
||||
la: "1 Cor. 6:20"
|
||||
en: "1 Cor. 6:20"
|
||||
status:
|
||||
la: verified
|
||||
en: verified
|
||||
@@ -0,0 +1,13 @@
|
||||
# Verified against Divinum Officium (Monastic Tridentinum 1617), clean
|
||||
# per-annum run (2026-08-31). See terce-capitulum-per-annum.yml for the
|
||||
# combined-block structure note and the deferred-seasonal-variant caveat.
|
||||
id: sext-capitulum-per-annum
|
||||
text:
|
||||
la: "Alter altérius ónera portáte, et sic adimplébitis legem Christi. R. Deo grátias. V. Dóminus regit me, et nihil mihi déerit. R. In loco páscuæ ibi me collocávit."
|
||||
en: "Bear ye one another's burdens; and so you shall fulfill the law of Christ. R. Thanks be to God. V. The Lord ruleth me: and I shall want nothing. R. He hath set me in a place of pasture."
|
||||
citation:
|
||||
la: "Gal. 6:2"
|
||||
en: "Gal. 6:2"
|
||||
status:
|
||||
la: verified
|
||||
en: verified
|
||||
@@ -0,0 +1,20 @@
|
||||
# Verified against Divinum Officium (Monastic Tridentinum 1617), clean
|
||||
# per-annum runs (2026-08-31, a Monday with no sanctoral override — see
|
||||
# hours/terce.ts). "Capitulum Versus" in the source is one combined block:
|
||||
# the chapter, then R. Deo grátias, then a short versicle/response pair —
|
||||
# unlike Prime, the Little Hours have no separate elaborate short
|
||||
# responsory, so all of that is kept together here as one 'chapter' part.
|
||||
#
|
||||
# Per-annum only for now — real practice varies this by season (confirmed
|
||||
# by comparing against a Lenten Ember Wednesday run, which showed different
|
||||
# capitulum text entirely), but that seasonal table isn't built yet.
|
||||
id: terce-capitulum-per-annum
|
||||
text:
|
||||
la: "Sana me, Dómine, et sanábor: salvum me fac, et salvus ero: quóniam laus mea tu es. R. Deo grátias. V. Adjútor meus, esto, ne derelínquas me. R. Neque despícias me, Deus, salutáris meus."
|
||||
en: "Heal me, O Lord, and I shall be healed: save me, and I shall be saved, for thou art my praise. R. Thanks be to God. V. Be thou my helper, forsake me not. R. Do not thou despise me, O God my Saviour."
|
||||
citation:
|
||||
la: "Jer. 17:14"
|
||||
en: "Jer. 17:14"
|
||||
status:
|
||||
la: verified
|
||||
en: verified
|
||||
@@ -78,9 +78,9 @@ prime:
|
||||
#
|
||||
# Sunday, Monday, and Tuesday's Terce/Sext/None are untouched by any of
|
||||
# this (still whatever Divinum Officium gives natively — Ps 118 sections
|
||||
# for Sunday/Monday, 119-127 for Tuesday) and so aren't recorded here;
|
||||
# source them from the reference engine directly when those hours are
|
||||
# built, the same as Wednesday/Thursday's unchanged 119-127 below.
|
||||
# for Sunday/Monday, 119-127 for Tuesday) — now recorded below, verified
|
||||
# against clean per-annum runs with no sanctoral override (2026-08-09 Sun,
|
||||
# 2026-08-31 Mon, 2026-10-13 Tue).
|
||||
matins:
|
||||
monday:
|
||||
- { number: 32 }
|
||||
@@ -147,10 +147,19 @@ matins:
|
||||
# redistribution; source it directly from the reference engine.
|
||||
|
||||
terce:
|
||||
wednesday: &gradual-terce
|
||||
sunday:
|
||||
- { number: 118, verses: "33-40" }
|
||||
- { number: 118, verses: "41-48" }
|
||||
- { number: 118, verses: "49-56" }
|
||||
monday:
|
||||
- { number: 118, verses: "105-112" }
|
||||
- { number: 118, verses: "113-120" }
|
||||
- { number: 118, verses: "121-128" }
|
||||
tuesday: &gradual-terce
|
||||
- { number: 119 }
|
||||
- { number: 120 }
|
||||
- { number: 121 }
|
||||
wednesday: *gradual-terce
|
||||
thursday: *gradual-terce
|
||||
friday:
|
||||
- { number: 41 }
|
||||
@@ -162,10 +171,19 @@ terce:
|
||||
- { number: 81 }
|
||||
|
||||
sext:
|
||||
wednesday: &gradual-sext
|
||||
sunday:
|
||||
- { number: 118, verses: "57-64" }
|
||||
- { number: 118, verses: "65-72" }
|
||||
- { number: 118, verses: "73-80" }
|
||||
monday:
|
||||
- { number: 118, verses: "129-136" }
|
||||
- { number: 118, verses: "137-144" }
|
||||
- { number: 118, verses: "145-152" }
|
||||
tuesday: &gradual-sext
|
||||
- { number: 122 }
|
||||
- { number: 123 }
|
||||
- { number: 124 }
|
||||
wednesday: *gradual-sext
|
||||
thursday: *gradual-sext
|
||||
friday:
|
||||
- { number: 51 }
|
||||
@@ -177,10 +195,19 @@ sext:
|
||||
- { number: 97 }
|
||||
|
||||
none:
|
||||
wednesday: &gradual-none
|
||||
sunday:
|
||||
- { number: 118, verses: "81-88" }
|
||||
- { number: 118, verses: "89-96" }
|
||||
- { number: 118, verses: "97-104" }
|
||||
monday:
|
||||
- { number: 118, verses: "153-160" }
|
||||
- { number: 118, verses: "161-168" }
|
||||
- { number: 118, verses: "169-176" }
|
||||
tuesday: &gradual-none
|
||||
- { number: 125 }
|
||||
- { number: 126 }
|
||||
- { number: 127 }
|
||||
wednesday: *gradual-none
|
||||
thursday: *gradual-none
|
||||
friday:
|
||||
- { number: 57 }
|
||||
|
||||
@@ -85,6 +85,7 @@ function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
case 'closing-antiphon':
|
||||
case 'by-day-kind':
|
||||
case 'variable':
|
||||
case 'day-collect':
|
||||
throw new Error(`Compline's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
+63
-5
@@ -1,7 +1,65 @@
|
||||
import type { ResolvedOrdo } from './types';
|
||||
import type { HourDefinition, HourPart, ResolvedOrdo, ResolvedPart } from './types';
|
||||
import type { LiturgicalDay, Weekday } from '../calendar/types';
|
||||
import { resolveDay } from '../calendar';
|
||||
import { getDayLabel } from '../calendar/day-label';
|
||||
import { getPsalmsFor } from '../psalter/distribution';
|
||||
import { getPsalmVerses } from '../psalter';
|
||||
import { getOpeningVersicleId } from './opening-versicle';
|
||||
import { isDoubleOrHigher } from './antiphon';
|
||||
import { resolveCommon, getDayCollect, splitNamedAntiphon } from './resolve-common';
|
||||
import noneDefinitionData from '../data/hours/none.yml';
|
||||
import antiphonsData from '../data/hours/none-antiphons.yml';
|
||||
|
||||
// Milestone 3. Not built yet — will call into hours/shared/little-hour-shared.ts
|
||||
// alongside terce.ts and sext.ts once that's written.
|
||||
export function resolveOrdo(date: string): ResolvedOrdo {
|
||||
return { hourId: 'none', date, parts: [], notImplemented: true };
|
||||
const noneDefinition = noneDefinitionData as HourDefinition;
|
||||
const antiphons = antiphonsData as Record<Weekday, Partial<Record<string, string>>>;
|
||||
|
||||
function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
switch (part.kind) {
|
||||
case 'opening-versicle':
|
||||
return [{ kind: 'versicle', text: resolveCommon(getOpeningVersicleId(day.season, day.winner)) }];
|
||||
case 'hymn':
|
||||
// Self-contained, no appendDoxology — see none-hymn.yml.
|
||||
return [{ kind: 'hymn', text: resolveCommon(part.textRef.id) }];
|
||||
case 'chapter':
|
||||
return [{ kind: 'chapter', text: resolveCommon(part.textRef.id) }];
|
||||
case 'day-collect':
|
||||
return [{ kind: 'prayer', text: getDayCollect(day) }];
|
||||
case 'preces':
|
||||
return [{ kind: 'preces', text: resolveCommon(part.textRef.id), label: part.label }];
|
||||
case 'variable': {
|
||||
// Only 'by-weekday' applies here — one antiphon framing all three
|
||||
// psalms, no closing repeat (unlike Prime): confirmed directly
|
||||
// against the engine, Capitulum follows the third psalm immediately.
|
||||
const psalmRefs = getPsalmsFor('none', day.weekday);
|
||||
const { incipit, full } = splitNamedAntiphon(antiphons[day.weekday]);
|
||||
const opening = isDoubleOrHigher(day.winner) ? full : incipit;
|
||||
return psalmRefs.map((ref, i) => ({
|
||||
kind: 'psalm' as const,
|
||||
psalmNumber: ref.number,
|
||||
antiphon: i === 0 ? opening : undefined,
|
||||
verses: getPsalmVerses(ref.number, ref.verses).map((v) => ({ n: v.n, text: v.text, status: v.status })),
|
||||
}));
|
||||
}
|
||||
// Not used by None.
|
||||
case 'prayer':
|
||||
case 'responsory':
|
||||
case 'versicle':
|
||||
case 'psalm':
|
||||
case 'canticle':
|
||||
case 'lesson':
|
||||
case 'martyrology':
|
||||
case 'rule-reading':
|
||||
case 'by-day-kind':
|
||||
case 'creed':
|
||||
case 'closing-antiphon':
|
||||
case 'nunc-dimittis':
|
||||
case 'marian-antiphon':
|
||||
throw new Error(`None's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveOrdo(date: string): ResolvedOrdo {
|
||||
const day = resolveDay(date);
|
||||
const parts = noneDefinition.parts.flatMap((part) => resolvePart(part, day));
|
||||
return { hourId: 'none', date, parts, dayLabel: getDayLabel(day) };
|
||||
}
|
||||
|
||||
+2
-1
@@ -108,9 +108,10 @@ function resolvePart(part: HourPart, date: string, day: LiturgicalDay): Resolved
|
||||
{ kind: 'preces', text: resolveCommon('lesson-close') },
|
||||
];
|
||||
}
|
||||
// Not used by Prime — Compline-only.
|
||||
// Not used by Prime — Compline-only, or Little-Hours-only.
|
||||
case 'nunc-dimittis':
|
||||
case 'marian-antiphon':
|
||||
case 'day-collect':
|
||||
throw new Error(`Prime's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
+63
-5
@@ -1,7 +1,65 @@
|
||||
import type { ResolvedOrdo } from './types';
|
||||
import type { HourDefinition, HourPart, ResolvedOrdo, ResolvedPart } from './types';
|
||||
import type { LiturgicalDay, Weekday } from '../calendar/types';
|
||||
import { resolveDay } from '../calendar';
|
||||
import { getDayLabel } from '../calendar/day-label';
|
||||
import { getPsalmsFor } from '../psalter/distribution';
|
||||
import { getPsalmVerses } from '../psalter';
|
||||
import { getOpeningVersicleId } from './opening-versicle';
|
||||
import { isDoubleOrHigher } from './antiphon';
|
||||
import { resolveCommon, getDayCollect, splitNamedAntiphon } from './resolve-common';
|
||||
import sextDefinitionData from '../data/hours/sext.yml';
|
||||
import antiphonsData from '../data/hours/sext-antiphons.yml';
|
||||
|
||||
// Milestone 3. Not built yet — will call into hours/shared/little-hour-shared.ts
|
||||
// alongside terce.ts and none.ts once that's written.
|
||||
export function resolveOrdo(date: string): ResolvedOrdo {
|
||||
return { hourId: 'sext', date, parts: [], notImplemented: true };
|
||||
const sextDefinition = sextDefinitionData as HourDefinition;
|
||||
const antiphons = antiphonsData as Record<Weekday, Partial<Record<string, string>>>;
|
||||
|
||||
function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
switch (part.kind) {
|
||||
case 'opening-versicle':
|
||||
return [{ kind: 'versicle', text: resolveCommon(getOpeningVersicleId(day.season, day.winner)) }];
|
||||
case 'hymn':
|
||||
// Self-contained, no appendDoxology — see sext-hymn.yml.
|
||||
return [{ kind: 'hymn', text: resolveCommon(part.textRef.id) }];
|
||||
case 'chapter':
|
||||
return [{ kind: 'chapter', text: resolveCommon(part.textRef.id) }];
|
||||
case 'day-collect':
|
||||
return [{ kind: 'prayer', text: getDayCollect(day) }];
|
||||
case 'preces':
|
||||
return [{ kind: 'preces', text: resolveCommon(part.textRef.id), label: part.label }];
|
||||
case 'variable': {
|
||||
// Only 'by-weekday' applies here — one antiphon framing all three
|
||||
// psalms, no closing repeat (unlike Prime): confirmed directly
|
||||
// against the engine, Capitulum follows the third psalm immediately.
|
||||
const psalmRefs = getPsalmsFor('sext', day.weekday);
|
||||
const { incipit, full } = splitNamedAntiphon(antiphons[day.weekday]);
|
||||
const opening = isDoubleOrHigher(day.winner) ? full : incipit;
|
||||
return psalmRefs.map((ref, i) => ({
|
||||
kind: 'psalm' as const,
|
||||
psalmNumber: ref.number,
|
||||
antiphon: i === 0 ? opening : undefined,
|
||||
verses: getPsalmVerses(ref.number, ref.verses).map((v) => ({ n: v.n, text: v.text, status: v.status })),
|
||||
}));
|
||||
}
|
||||
// Not used by Sext.
|
||||
case 'prayer':
|
||||
case 'responsory':
|
||||
case 'versicle':
|
||||
case 'psalm':
|
||||
case 'canticle':
|
||||
case 'lesson':
|
||||
case 'martyrology':
|
||||
case 'rule-reading':
|
||||
case 'by-day-kind':
|
||||
case 'creed':
|
||||
case 'closing-antiphon':
|
||||
case 'nunc-dimittis':
|
||||
case 'marian-antiphon':
|
||||
throw new Error(`Sext's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveOrdo(date: string): ResolvedOrdo {
|
||||
const day = resolveDay(date);
|
||||
const parts = sextDefinition.parts.flatMap((part) => resolvePart(part, day));
|
||||
return { hourId: 'sext', date, parts, dayLabel: getDayLabel(day) };
|
||||
}
|
||||
|
||||
+63
-5
@@ -1,7 +1,65 @@
|
||||
import type { ResolvedOrdo } from './types';
|
||||
import type { HourDefinition, HourPart, ResolvedOrdo, ResolvedPart } from './types';
|
||||
import type { LiturgicalDay, Weekday } from '../calendar/types';
|
||||
import { resolveDay } from '../calendar';
|
||||
import { getDayLabel } from '../calendar/day-label';
|
||||
import { getPsalmsFor } from '../psalter/distribution';
|
||||
import { getPsalmVerses } from '../psalter';
|
||||
import { getOpeningVersicleId } from './opening-versicle';
|
||||
import { isDoubleOrHigher } from './antiphon';
|
||||
import { resolveCommon, getDayCollect, splitNamedAntiphon } from './resolve-common';
|
||||
import terceDefinitionData from '../data/hours/terce.yml';
|
||||
import antiphonsData from '../data/hours/terce-antiphons.yml';
|
||||
|
||||
// Milestone 3. Not built yet — will call into hours/shared/little-hour-shared.ts
|
||||
// alongside sext.ts and none.ts once that's written.
|
||||
export function resolveOrdo(date: string): ResolvedOrdo {
|
||||
return { hourId: 'terce', date, parts: [], notImplemented: true };
|
||||
const terceDefinition = terceDefinitionData as HourDefinition;
|
||||
const antiphons = antiphonsData as Record<Weekday, Partial<Record<string, string>>>;
|
||||
|
||||
function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
|
||||
switch (part.kind) {
|
||||
case 'opening-versicle':
|
||||
return [{ kind: 'versicle', text: resolveCommon(getOpeningVersicleId(day.season, day.winner)) }];
|
||||
case 'hymn':
|
||||
// Self-contained, no appendDoxology — see terce-hymn.yml.
|
||||
return [{ kind: 'hymn', text: resolveCommon(part.textRef.id) }];
|
||||
case 'chapter':
|
||||
return [{ kind: 'chapter', text: resolveCommon(part.textRef.id) }];
|
||||
case 'day-collect':
|
||||
return [{ kind: 'prayer', text: getDayCollect(day) }];
|
||||
case 'preces':
|
||||
return [{ kind: 'preces', text: resolveCommon(part.textRef.id), label: part.label }];
|
||||
case 'variable': {
|
||||
// Only 'by-weekday' applies here — one antiphon framing all three
|
||||
// psalms, no closing repeat (unlike Prime): confirmed directly
|
||||
// against the engine, Capitulum follows the third psalm immediately.
|
||||
const psalmRefs = getPsalmsFor('terce', day.weekday);
|
||||
const { incipit, full } = splitNamedAntiphon(antiphons[day.weekday]);
|
||||
const opening = isDoubleOrHigher(day.winner) ? full : incipit;
|
||||
return psalmRefs.map((ref, i) => ({
|
||||
kind: 'psalm' as const,
|
||||
psalmNumber: ref.number,
|
||||
antiphon: i === 0 ? opening : undefined,
|
||||
verses: getPsalmVerses(ref.number, ref.verses).map((v) => ({ n: v.n, text: v.text, status: v.status })),
|
||||
}));
|
||||
}
|
||||
// Not used by Terce.
|
||||
case 'prayer':
|
||||
case 'responsory':
|
||||
case 'versicle':
|
||||
case 'psalm':
|
||||
case 'canticle':
|
||||
case 'lesson':
|
||||
case 'martyrology':
|
||||
case 'rule-reading':
|
||||
case 'by-day-kind':
|
||||
case 'creed':
|
||||
case 'closing-antiphon':
|
||||
case 'nunc-dimittis':
|
||||
case 'marian-antiphon':
|
||||
throw new Error(`Terce's ordo doesn't support a '${part.kind}' part`);
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveOrdo(date: string): ResolvedOrdo {
|
||||
const day = resolveDay(date);
|
||||
const parts = terceDefinition.parts.flatMap((part) => resolvePart(part, day));
|
||||
return { hourId: 'terce', date, parts, dayLabel: getDayLabel(day) };
|
||||
}
|
||||
|
||||
+7
-1
@@ -87,7 +87,13 @@ export type HourPart =
|
||||
// Salve Regina. See hours/marian-antiphon.ts.
|
||||
| { kind: 'marian-antiphon' }
|
||||
// Unused by Prime/Compline/the little hours — exercised starting milestone 4.
|
||||
| { kind: 'variable'; resolve: 'by-weekday' | 'by-season' | 'by-feast-rank' };
|
||||
| { kind: 'variable'; resolve: 'by-weekday' | 'by-season' | 'by-feast-rank' }
|
||||
// The day's own Proper-of-season/Proper-of-saints collect — resolved by
|
||||
// day, not by textRef. First used by the Little Hours' "Oratio {ex
|
||||
// Proprio de Tempore}" via hours/resolve-common.ts's getDayCollect;
|
||||
// Prime/Compline both still use a fixed collect (`prayer` + textRef).
|
||||
// Renders as a plain 'prayer' ResolvedPart.
|
||||
| { kind: 'day-collect' };
|
||||
|
||||
export interface HourDefinition {
|
||||
id: HourId;
|
||||
|
||||
Reference in New Issue
Block a user