ponomar-ts 0.2.0-alpha.0 → 1.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +88 -26
  2. package/dist/astronomy/index.d.ts +5 -0
  3. package/dist/astronomy/index.d.ts.map +1 -0
  4. package/dist/astronomy/index.js +6 -0
  5. package/dist/astronomy/index.js.map +1 -0
  6. package/dist/astronomy/lunar.d.ts +22 -0
  7. package/dist/astronomy/lunar.d.ts.map +1 -0
  8. package/dist/astronomy/lunar.js +87 -0
  9. package/dist/astronomy/lunar.js.map +1 -0
  10. package/dist/astronomy/sunrise.d.ts +41 -0
  11. package/dist/astronomy/sunrise.d.ts.map +1 -0
  12. package/dist/astronomy/sunrise.js +111 -0
  13. package/dist/astronomy/sunrise.js.map +1 -0
  14. package/dist/bible/format.d.ts +9 -0
  15. package/dist/bible/format.d.ts.map +1 -0
  16. package/dist/bible/format.js +38 -0
  17. package/dist/bible/format.js.map +1 -0
  18. package/dist/bible/index.d.ts +5 -0
  19. package/dist/bible/index.d.ts.map +1 -0
  20. package/dist/bible/index.js +8 -0
  21. package/dist/bible/index.js.map +1 -0
  22. package/dist/bible/parse.d.ts +7 -0
  23. package/dist/bible/parse.d.ts.map +1 -0
  24. package/dist/bible/parse.js +127 -0
  25. package/dist/bible/parse.js.map +1 -0
  26. package/dist/bible/types.d.ts +34 -0
  27. package/dist/bible/types.d.ts.map +1 -0
  28. package/dist/bible/types.js +4 -0
  29. package/dist/bible/types.js.map +1 -0
  30. package/dist/data/bibleBooks.d.ts +4 -0
  31. package/dist/data/bibleBooks.d.ts.map +1 -0
  32. package/dist/data/bibleBooks.js +716 -0
  33. package/dist/data/bibleBooks.js.map +1 -0
  34. package/dist/data/index.d.ts +2 -0
  35. package/dist/data/index.d.ts.map +1 -1
  36. package/dist/data/index.js +2 -0
  37. package/dist/data/index.js.map +1 -1
  38. package/dist/data/rankOverlay.d.ts +5 -0
  39. package/dist/data/rankOverlay.d.ts.map +1 -0
  40. package/dist/data/rankOverlay.js +64 -0
  41. package/dist/data/rankOverlay.js.map +1 -0
  42. package/dist/data/types.d.ts +20 -1
  43. package/dist/data/types.d.ts.map +1 -1
  44. package/dist/engine/index.d.ts +5 -0
  45. package/dist/engine/index.d.ts.map +1 -1
  46. package/dist/engine/index.js +7 -0
  47. package/dist/engine/index.js.map +1 -1
  48. package/dist/engine/resolve.d.ts.map +1 -1
  49. package/dist/engine/resolve.js +12 -2
  50. package/dist/engine/resolve.js.map +1 -1
  51. package/dist/index.d.ts +6 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +4 -0
  54. package/dist/index.js.map +1 -1
  55. package/package.json +9 -1
  56. package/src/astronomy/index.ts +23 -0
  57. package/src/astronomy/lunar.ts +102 -0
  58. package/src/astronomy/sunrise.ts +151 -0
  59. package/src/bible/format.ts +50 -0
  60. package/src/bible/index.ts +9 -0
  61. package/src/bible/parse.ts +156 -0
  62. package/src/bible/types.ts +40 -0
  63. package/src/data/bibleBooks.ts +719 -0
  64. package/src/data/index.ts +2 -0
  65. package/src/data/rankOverlay.ts +68 -0
  66. package/src/data/types.ts +22 -1
  67. package/src/engine/index.ts +11 -0
  68. package/src/engine/resolve.ts +15 -2
  69. package/src/index.ts +21 -0
package/README.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  A TypeScript port of the [Ponomar](https://github.com/typiconman/ponomar) Orthodox liturgics engine.
4
4
 
5
- > [!WARNING]
6
- > **Status: pre-alpha.** Nothing works yet. This repository contains scaffolding
7
- > and the beginnings of a paschal-date module. Do not depend on it for anything.
5
+ > [!NOTE]
6
+ > **Status: 1.0 release candidate.** The engine is feature-complete against
7
+ > the ported subset of upstream Ponomar and covered by 305 tests. Public API
8
+ > is frozen for 1.0. Consumers should pin to `^1.0.0-rc` while the release
9
+ > soaks; the plain `1.0.0` tag will follow once no blocking issues surface.
8
10
 
9
11
  ## What this is
10
12
 
@@ -14,38 +16,50 @@ liturgical information (saints, readings, tone, fasting, feast transfer rules)
14
16
  by following the Orthodox Typicon precisely, driven by declarative XML data
15
17
  files that encode each day's rubrics.
16
18
 
17
- **ponomar-ts** aims to be a faithful port of the *engine and the data* to
18
- TypeScript, so it can be consumed by modern JS/TS applications (Node servers,
19
- Next.js apps, mobile React Native builds) without a JVM or a desktop UI.
19
+ **ponomar-ts** is a faithful port of the *engine and the data* to TypeScript,
20
+ so it can be consumed by modern JS/TS applications (Node servers, Next.js
21
+ apps, mobile React Native builds) without a JVM or a desktop UI.
20
22
 
21
23
  ### Scope
22
24
 
23
25
  Ported:
24
26
 
25
- - [ ] Paschalion (Meeus' luni-solar algorithm; Julian → Gregorian conversion)
26
- - [ ] Day / Days aggregate
27
- - [ ] Commemoration selection & feast-rank transfers
28
- - [ ] Gospel / Epistle pericope resolution (including Lukan Jump and reserves)
29
- - [ ] Oktoechos tone rotation
30
- - [ ] Fasting rule classification
31
- - [ ] XML data loader for the Ponomar `languages/` corpus
32
-
33
- Not in scope (left to the original Java project):
27
+ - [x] **Paschalion** — Meeus' luni-solar algorithm; Julian ↔ Gregorian conversion
28
+ - [x] **Julian date math** — `JDate` value type, day-of-week, addition, difference
29
+ - [x] **StringOp DSL** — lexer + Pratt parser + evaluator for `Cmd=` / `Value=` rule expressions
30
+ - [x] **Day composition** — `getLiturgicalDay(date)` → paschal + menaion saints, `dRank`, tone
31
+ - [x] **Commemoration metadata** — names, ranks, biographical info, and life prose (English)
32
+ - [x] **Gospel / Epistle readings** — `getDailyReadings` (raw) and `getOrderedLiturgyReadings` (Suppress + Class3Transfers + Saturday inversion + cross-day pull)
33
+ - [x] **Matins reading conflict resolution** — Sunday festal-vs-sequential arbitration
34
+ - [x] **Fasting rules** — `getFasting(date)` → 9 canonical levels + per-food-group permissions
35
+ - [x] **Service selection** — Little Hours template dispatch per period rules
36
+ - [x] **Service composition** — typed directive streams for every static template under `languages/xml/Services/`
37
+ - [x] **Hymn propers** — `getPropers(date)` → troparia + kontakia per commemoration
38
+ - [x] **Phrase text** — 283 static phrases (CommonPrayers / Text / Header / Command), English
39
+ - [x] **Bible reference parser** — 1169/1170 upstream refs parse cleanly (one is an upstream typo)
40
+ - [x] **Astronomy** — sunrise/sunset (SUNRISET.C) + Metonic-cycle lunar phase
41
+
42
+ Not in scope (left to the original Java project or to consumers):
34
43
 
35
44
  - Swing desktop UI
36
45
  - Font rendering and Church Slavonic typography
37
- - Full service assembly (Matins, Hours, Divine Liturgy)
46
+ - Localization (engine returns raw IDs; consumers handle i18n)
47
+ - Bible text (only reference metadata ships; text is per-locale)
38
48
  - Icon display
39
49
  - Liturgical music library
40
50
 
41
- ## Installation
51
+ See [PLAN.md](PLAN.md) for phase-by-phase implementation notes and the
52
+ "Known limitations" section for documented deviations from upstream.
42
53
 
43
- Not yet published. Once released:
54
+ ## Installation
44
55
 
45
56
  ```bash
46
- npm install ponomar-ts
57
+ npm install ponomar-ts@next
47
58
  ```
48
59
 
60
+ The `next` dist-tag tracks the 1.0 release candidates. Once `1.0.0` ships,
61
+ plain `npm install ponomar-ts` will resolve to it.
62
+
49
63
  ## Development
50
64
 
51
65
  The upstream Ponomar Java repo is vendored as a git submodule under
@@ -54,25 +68,73 @@ at build time by `scripts/codegen/`. The compiled library ships no XML and
54
68
  has no XML parser at runtime.
55
69
 
56
70
  ```powershell
57
- git clone https://github.com/Wolftrail/ponomar-ts.git
71
+ git clone https://github.com/wolfgangnothdurft/ponomar-ts.git
58
72
  cd ponomar-ts
59
73
  git submodule update --init --recursive
60
74
  npm install
61
- npm run codegen # regenerate src/data/ from vendor/ (once codegen lands)
75
+ npm run codegen # regenerate src/data/ from vendor/
62
76
  npm run typecheck
63
77
  npm test
78
+ npm run build
64
79
  ```
65
80
 
66
- See [PLAN.md](PLAN.md) for the port roadmap and phase breakdown.
81
+ See [PLAN.md](PLAN.md) for the port roadmap and phase breakdown, and
82
+ [CHANGELOG.md](CHANGELOG.md) for per-release notes.
67
83
 
68
84
  ## Usage
69
85
 
86
+ The public API takes plain `CalendarDate` literals (`{ year, month, day }`)
87
+ rather than JS `Date` objects, so the same code runs identically in Node,
88
+ the browser, and time-zone-sensitive contexts.
89
+
70
90
  ```ts
71
- // Preview API — subject to change.
72
- import { getOrthodoxPascha } from "ponomar-ts/paschalion";
91
+ import {
92
+ getOrthodoxPascha,
93
+ getLiturgicalDay,
94
+ getOrderedLiturgyReadings,
95
+ getFasting,
96
+ getHourService,
97
+ parseBibleRef,
98
+ } from "ponomar-ts";
99
+
100
+ // Paschal date for a Gregorian year
101
+ getOrthodoxPascha(2026);
102
+ // → { year: 2026, month: 4, day: 12 }
103
+
104
+ // Full liturgical day: paschal + menaion saints, dRank, tone
105
+ const day = getLiturgicalDay({ year: 2024, month: 1, day: 7 }); // Nativity (Old Calendar)
106
+ day.dRank; // 7 (Great Feast of the Lord)
107
+ day.allSaints[1]?.name?.short; // "Nativity"
108
+
109
+ // Divine Liturgy readings — canonical order, with Suppress + Class3Transfers
110
+ const readings = getOrderedLiturgyReadings({ year: 2024, month: 1, day: 7 });
111
+ readings.apostol.map(r => r.reading); // ["Gal_4:4-7"]
112
+ readings.gospel.map(r => r.reading); // ["Mt_2:1-12"]
113
+
114
+ // Fasting rule for a date
115
+ const fast = getFasting({ year: 2024, month: 3, day: 22 }); // Lenten Friday
116
+ fast.level; // "no-oil"
117
+ fast.permitted.meat; // false
118
+ fast.permitted.oil; // false
119
+
120
+ // Little Hour template dispatch (Prime, Terce, Sexte, None)
121
+ const prime = getHourService({ year: 2024, month: 4, day: 2 }, "prime");
122
+ prime.templateName; // "Prime"
123
+ prime.PFlag2; // 1 (auto-derived: Lenten)
124
+
125
+ // Bible reference parser (78 books, 163 aliases)
126
+ parseBibleRef("I Tim_3:14-4:5");
127
+ // → { book: "I_Tim", bookLabel: "I Tim", ranges: [{ start, end }] }
128
+ ```
129
+
130
+ Deep imports are encouraged when bundle size matters:
73
131
 
74
- const pascha = getOrthodoxPascha(2026);
75
- // -> { year: 2026, month: 4, day: 19 }
132
+ ```ts
133
+ import { getOrthodoxPascha } from "ponomar-ts/paschalion";
134
+ import { getFasting } from "ponomar-ts/engine";
135
+ import { parseBibleRef } from "ponomar-ts/bible";
136
+ import { getSunriseSunset, getLunarPhase } from "ponomar-ts/astronomy";
137
+ import { getLife } from "ponomar-ts/lives"; // life prose, ~1.6 MB payload
76
138
  ```
77
139
 
78
140
  ## Design goals
@@ -0,0 +1,5 @@
1
+ export { SunAltitude, formatClock, getSunriseSunset, } from "./sunrise.ts";
2
+ export type { SunriseSunsetOptions, SunriseSunsetResult, } from "./sunrise.ts";
3
+ export { LUNAR_MONTH, LUNAR_HALF_DAY, getLunarCycle, getLunarPhase, getLunarPhaseName, getNextNewMoon, getNextFullMoon, } from "./lunar.ts";
4
+ export type { LunarPhaseName } from "./lunar.ts";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/astronomy/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACN,WAAW,EACX,WAAW,EACX,gBAAgB,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EACX,oBAAoB,EACpB,mBAAmB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,WAAW,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,eAAe,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,6 @@
1
+ // Standalone astronomy helpers — sunrise/sunset and Metonic-cycle lunar
2
+ // phase. Ported from Ponomar/Sunrise.java and the lunar-phase paths in
3
+ // Ponomar/Paschalion.java.
4
+ export { SunAltitude, formatClock, getSunriseSunset, } from "./sunrise.js";
5
+ export { LUNAR_MONTH, LUNAR_HALF_DAY, getLunarCycle, getLunarPhase, getLunarPhaseName, getNextNewMoon, getNextFullMoon, } from "./lunar.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/astronomy/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uEAAuE;AACvE,2BAA2B;AAE3B,OAAO,EACN,WAAW,EACX,WAAW,EACX,gBAAgB,GAChB,MAAM,cAAc,CAAC;AAKtB,OAAO,EACN,WAAW,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,eAAe,GACf,MAAM,YAAY,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { JulianDate } from "../core/calendar/jdate.ts";
2
+ /** Mean synodic month, in days. Upstream constant. */
3
+ export declare const LUNAR_MONTH = 29.52916667;
4
+ /** Half a lunar "day" — the window around each cardinal phase within
5
+ * which {@link getLunarPhaseName} rounds to that phase. */
6
+ export declare const LUNAR_HALF_DAY = 0.016932411;
7
+ /** Names for the eight cardinal lunar phases. */
8
+ export type LunarPhaseName = "new" | "waxing-crescent" | "first-quarter" | "waxing-gibbous" | "full" | "waning-gibbous" | "last-quarter" | "waning-crescent";
9
+ /** Metonic-cycle index (1..19) for a Julian-calendar year (AD 33 or later). */
10
+ export declare function getLunarCycle(year: number): number;
11
+ /** Phase of the moon on `date`, as a fraction of the lunar month.
12
+ * 0 = new moon, 0.5 = full, wraps at 1. */
13
+ export declare function getLunarPhase(date: JulianDate): number;
14
+ /** Bucket the fractional phase into one of the eight cardinal names, using
15
+ * a +/- half-lunar-day window around each cardinal point. */
16
+ export declare function getLunarPhaseName(date: JulianDate): LunarPhaseName;
17
+ /** Julian-calendar date of the next new moon on/after `date`, floor of the
18
+ * Metonic estimate (matches upstream rounding). */
19
+ export declare function getNextNewMoon(date: JulianDate): JulianDate;
20
+ /** Julian-calendar date of the next full moon on/after `date`. */
21
+ export declare function getNextFullMoon(date: JulianDate): JulianDate;
22
+ //# sourceMappingURL=lunar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lunar.d.ts","sourceRoot":"","sources":["../../src/astronomy/lunar.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAG5D,sDAAsD;AACtD,eAAO,MAAM,WAAW,cAAc,CAAC;AAEvC;4DAC4D;AAC5D,eAAO,MAAM,cAAc,cAAc,CAAC;AAW1C,iDAAiD;AACjD,MAAM,MAAM,cAAc,GACvB,KAAK,GACL,iBAAiB,GACjB,eAAe,GACf,gBAAgB,GAChB,MAAM,GACN,gBAAgB,GAChB,cAAc,GACd,iBAAiB,CAAC;AAErB,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED;4CAC4C;AAC5C,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAetD;AAED;8DAC8D;AAC9D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAWlE;AAED;oDACoD;AACpD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAI3D;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAK5D"}
@@ -0,0 +1,87 @@
1
+ // Ported from lunar-phase paths in Ponomar/Paschalion.java. The phase is
2
+ // computed **not astronomically** but on the Metonic cycle — sufficient for
3
+ // liturgical purposes and identical to upstream. Marked "experimental" by
4
+ // upstream; guarantees are as-is.
5
+ import { julianDate, julianDateFromJdn } from "../core/calendar/jdate.js";
6
+ /** Mean synodic month, in days. Upstream constant. */
7
+ export const LUNAR_MONTH = 29.52916667;
8
+ /** Half a lunar "day" — the window around each cardinal phase within
9
+ * which {@link getLunarPhaseName} rounds to that phase. */
10
+ export const LUNAR_HALF_DAY = 0.016932411;
11
+ /** Age of the moon (days since new moon) on 1 March Julian for each of the
12
+ * 19 years of the Metonic cycle. Index 0 → cycle 1, …, index 18 → cycle 19. */
13
+ const FOUNDATION = [
14
+ 14.042016807, 25.462184874, 6.084033613, 17.966386555, 28.336134454,
15
+ 9.210084034, 20.504201681, 1.420168067, 12.294117647, 23.168067227,
16
+ 4.546218487, 15.042016807, 26.294117647, 7.630252101, 18.546218487,
17
+ 29.420168067, 11.756302521, 26.210084034, 3.042016807,
18
+ ];
19
+ /** Metonic-cycle index (1..19) for a Julian-calendar year (AD 33 or later). */
20
+ export function getLunarCycle(year) {
21
+ if (!Number.isInteger(year) || year < 33) {
22
+ throw new RangeError(`year must be an integer >= 33, got ${year}`);
23
+ }
24
+ let t = (year + 1) % 19 - 3;
25
+ if (t <= 0)
26
+ t += 19;
27
+ return t;
28
+ }
29
+ /** Phase of the moon on `date`, as a fraction of the lunar month.
30
+ * 0 = new moon, 0.5 = full, wraps at 1. */
31
+ export function getLunarPhase(date) {
32
+ const year = date.year;
33
+ const cycle = getLunarCycle(year);
34
+ const march1 = julianDate(year, 3, 1);
35
+ let diff = date.jdn - march1.jdn;
36
+ if (diff < 0) {
37
+ diff = date.jdn - julianDate(year - 1, 3, 1).jdn;
38
+ }
39
+ let remainder = mod(diff, LUNAR_MONTH);
40
+ remainder += FOUNDATION[cycle - 1];
41
+ while (remainder >= LUNAR_MONTH)
42
+ remainder -= LUNAR_MONTH;
43
+ return remainder / LUNAR_MONTH;
44
+ }
45
+ /** Bucket the fractional phase into one of the eight cardinal names, using
46
+ * a +/- half-lunar-day window around each cardinal point. */
47
+ export function getLunarPhaseName(date) {
48
+ const raw = getLunarPhase(date);
49
+ const r = LUNAR_HALF_DAY;
50
+ if (raw < r || raw > 1 - r)
51
+ return "new";
52
+ if (raw < 0.25 - r)
53
+ return "waxing-crescent";
54
+ if (raw <= 0.25 + r)
55
+ return "first-quarter";
56
+ if (raw < 0.5 - r)
57
+ return "waxing-gibbous";
58
+ if (raw <= 0.5 + r)
59
+ return "full";
60
+ if (raw < 0.75 - r)
61
+ return "waning-gibbous";
62
+ if (raw <= 0.75 + r)
63
+ return "last-quarter";
64
+ return "waning-crescent";
65
+ }
66
+ /** Julian-calendar date of the next new moon on/after `date`, floor of the
67
+ * Metonic estimate (matches upstream rounding). */
68
+ export function getNextNewMoon(date) {
69
+ const age = getLunarPhase(date) * LUNAR_MONTH;
70
+ const diff = Math.floor(LUNAR_MONTH - age);
71
+ return shiftJdn(date, diff);
72
+ }
73
+ /** Julian-calendar date of the next full moon on/after `date`. */
74
+ export function getNextFullMoon(date) {
75
+ const age = getLunarPhase(date) * LUNAR_MONTH;
76
+ let diff = Math.floor(LUNAR_MONTH / 2 - age);
77
+ if (diff < 0)
78
+ diff = Math.floor(LUNAR_MONTH / 2 - age + LUNAR_MONTH);
79
+ return shiftJdn(date, diff);
80
+ }
81
+ function mod(a, m) {
82
+ return a - m * Math.floor(a / m);
83
+ }
84
+ function shiftJdn(date, days) {
85
+ return julianDateFromJdn(date.jdn + days);
86
+ }
87
+ //# sourceMappingURL=lunar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lunar.js","sourceRoot":"","sources":["../../src/astronomy/lunar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { CalendarDate } from "../core/calendar/pcalendar.ts";
2
+ /** Standard altitude constants (in degrees below the horizon) accepted as
3
+ * `altitude` in {@link SunriseSunsetOptions}. */
4
+ export declare const SunAltitude: {
5
+ /** Sun's upper limb touches horizon with atmospheric refraction (default). */
6
+ readonly DEFAULT: -0.833;
7
+ /** Civil twilight — reading outside without artificial light is no longer possible. */
8
+ readonly CIVIL: -6;
9
+ /** Nautical twilight. */
10
+ readonly NAUTICAL: -12;
11
+ /** Amateur astronomical twilight. */
12
+ readonly AMATEUR: -15;
13
+ /** Astronomical twilight — sky completely dark. */
14
+ readonly ASTRONOMICAL: -18;
15
+ };
16
+ export interface SunriseSunsetOptions {
17
+ /** East longitude positive, west negative. Degrees. */
18
+ readonly longitude: number;
19
+ /** North latitude positive, south negative. Degrees. */
20
+ readonly latitude: number;
21
+ /** Local timezone offset from UTC in hours (positive east). */
22
+ readonly tzOffsetHours: number;
23
+ /** If true, add 1 hour for daylight-saving. Defaults to false. */
24
+ readonly isDST?: boolean;
25
+ /** Altitude of the sun at "sunset", in degrees. Defaults to -0.833. */
26
+ readonly altitude?: number;
27
+ }
28
+ export interface SunriseSunsetResult {
29
+ /** Local-time hour of sunrise (0..24). Decimal — multiply fractional part by 60 for minutes. */
30
+ readonly sunriseHours: number;
31
+ /** Local-time hour of sunset (0..24). */
32
+ readonly sunsetHours: number;
33
+ /** True when the sun never rises above the target altitude on this day. */
34
+ readonly sunAlwaysDown: boolean;
35
+ /** True when the sun never sets below the target altitude on this day. */
36
+ readonly sunAlwaysUp: boolean;
37
+ }
38
+ export declare function getSunriseSunset(date: CalendarDate, opts: SunriseSunsetOptions): SunriseSunsetResult;
39
+ /** Format a decimal-hours value as `HH:MM` (24-hour clock, zero-padded). */
40
+ export declare function formatClock(hours: number): string;
41
+ //# sourceMappingURL=sunrise.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sunrise.d.ts","sourceRoot":"","sources":["../../src/astronomy/sunrise.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAGlE;kDACkD;AAClD,eAAO,MAAM,WAAW;IACvB,8EAA8E;;IAE9E,uFAAuF;;IAEvF,yBAAyB;;IAEzB,qCAAqC;;IAErC,mDAAmD;;CAE1C,CAAC;AAEX,MAAM,WAAW,oBAAoB;IACpC,uDAAuD;IACvD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IACnC,gGAAgG;IAChG,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,yCAAyC;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC9B;AAoDD,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,oBAAoB,GACxB,mBAAmB,CAqCrB;AAQD,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIjD"}
@@ -0,0 +1,111 @@
1
+ // Ported from Ponomar/Sunrise.java (which is itself a port of Paul Schlyter's
2
+ // public-domain SUNRISET.C, 1992). Computes sunrise/sunset for any latitude,
3
+ // longitude and civil date. Standalone — no dependency on the rest of the
4
+ // engine.
5
+ import { _internal } from "../core/calendar/jdate.js";
6
+ /** Standard altitude constants (in degrees below the horizon) accepted as
7
+ * `altitude` in {@link SunriseSunsetOptions}. */
8
+ export const SunAltitude = {
9
+ /** Sun's upper limb touches horizon with atmospheric refraction (default). */
10
+ DEFAULT: -0.833,
11
+ /** Civil twilight — reading outside without artificial light is no longer possible. */
12
+ CIVIL: -6,
13
+ /** Nautical twilight. */
14
+ NAUTICAL: -12,
15
+ /** Amateur astronomical twilight. */
16
+ AMATEUR: -15,
17
+ /** Astronomical twilight — sky completely dark. */
18
+ ASTRONOMICAL: -18,
19
+ };
20
+ const INV360 = 1 / 360;
21
+ const RADEG = 180 / Math.PI;
22
+ const DEGRAD = Math.PI / 180;
23
+ const UPPER_LIMB = true;
24
+ const sind = (n) => Math.sin(n * DEGRAD);
25
+ const cosd = (n) => Math.cos(n * DEGRAD);
26
+ const acosd = (n) => RADEG * Math.acos(n);
27
+ const atan2d = (y, x) => RADEG * Math.atan2(y, x);
28
+ function revolution(angle) {
29
+ return angle - 360 * Math.floor(angle * INV360);
30
+ }
31
+ function rev180(x) {
32
+ return x - 360 * Math.floor(x * INV360 + 0.5);
33
+ }
34
+ /** JDN of 1999-12-31 Gregorian, i.e. "Jan 0.0, 2000 Gregorian". */
35
+ const JDN_JAN0_2000 = 2451544;
36
+ function gmst0(d) {
37
+ return revolution(180 + 356.047 + 282.9404 + (0.9856002585 + 4.70935e-5) * d);
38
+ }
39
+ function sunpos(d) {
40
+ const M = revolution(356.047 + 0.9856002585 * d);
41
+ const w = 282.9404 + 4.70935e-5 * d;
42
+ const e = 0.016709 - 1.151e-9 * d;
43
+ const E = M + e * RADEG * sind(M) * (1 + e * cosd(M));
44
+ const x = cosd(E) - e;
45
+ const y = Math.sqrt(1 - e * e) * sind(E);
46
+ const r = Math.sqrt(x * x + y * y);
47
+ let lon = atan2d(y, x) + w;
48
+ if (lon >= 360)
49
+ lon -= 360;
50
+ return { r, lon };
51
+ }
52
+ function sunRaDec(d) {
53
+ const { r, lon } = sunpos(d);
54
+ let x = r * cosd(lon);
55
+ let y = r * sind(lon);
56
+ const obl = 23.4393 - 3.563e-7 * d;
57
+ const z = y * sind(obl);
58
+ y *= cosd(obl);
59
+ const ra = atan2d(y, x);
60
+ const dec = atan2d(z, Math.sqrt(x * x + y * y));
61
+ return { ra, dec, r };
62
+ }
63
+ export function getSunriseSunset(date, opts) {
64
+ const { longitude, latitude } = opts;
65
+ const isDST = opts.isDST ?? false;
66
+ let altitude = opts.altitude ?? SunAltitude.DEFAULT;
67
+ const tz = opts.tzOffsetHours + (isDST ? 1 : 0);
68
+ const jdn = _internal.gregorianYmdToJdn(date.year, date.month, date.day);
69
+ const d = (jdn - JDN_JAN0_2000) + 0.5 - longitude / 360;
70
+ const sidtime = revolution(gmst0(d) + 180 + longitude);
71
+ const { ra, dec, r } = sunRaDec(d);
72
+ const tsouth = 12 - rev180(sidtime - ra) / 15;
73
+ const sradius = 0.2666 / r;
74
+ if (UPPER_LIMB)
75
+ altitude -= sradius;
76
+ const cost = (sind(altitude) - sind(latitude) * sind(dec))
77
+ / (cosd(latitude) * cosd(dec));
78
+ let t;
79
+ let sunAlwaysDown = false;
80
+ let sunAlwaysUp = false;
81
+ if (cost >= 1) {
82
+ t = 0;
83
+ sunAlwaysDown = true;
84
+ }
85
+ else if (cost <= -1) {
86
+ t = 12;
87
+ sunAlwaysUp = true;
88
+ }
89
+ else {
90
+ t = acosd(cost) / 15;
91
+ }
92
+ let sunriseHours = tsouth - t + tz;
93
+ let sunsetHours = tsouth + t + tz;
94
+ sunriseHours = normHours(sunriseHours);
95
+ sunsetHours = normHours(sunsetHours);
96
+ return { sunriseHours, sunsetHours, sunAlwaysDown, sunAlwaysUp };
97
+ }
98
+ function normHours(h) {
99
+ if (h > 24)
100
+ return h - 24;
101
+ if (h < 0)
102
+ return h + 24;
103
+ return h;
104
+ }
105
+ /** Format a decimal-hours value as `HH:MM` (24-hour clock, zero-padded). */
106
+ export function formatClock(hours) {
107
+ const h = Math.floor(hours);
108
+ const m = Math.floor((hours - h) * 60);
109
+ return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
110
+ }
111
+ //# sourceMappingURL=sunrise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sunrise.js","sourceRoot":"","sources":["../../src/astronomy/sunrise.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { BibleBook, BibleRef } from "./types.ts";
2
+ /** Look up a book by any accepted form (`Id`, `Short`, `Name`, or
3
+ * `Short`-with-underscores). Returns `undefined` when unknown. */
4
+ export declare function findBook(nameOrShort: string): BibleBook | undefined;
5
+ /** Format a `BibleRef` back to the upstream `<Label>_<Chapter>:<Spec>` form.
6
+ * Uses the original `bookLabel` from the parsed input, so
7
+ * `formatBibleRef(parseBibleRef(x)) === x` for every corpus reference. */
8
+ export declare function formatBibleRef(ref: BibleRef): string;
9
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/bible/format.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAA6B,MAAM,YAAY,CAAC;AAEjF;mEACmE;AACnE,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAInE;AAED;;2EAE2E;AAC3E,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAMpD"}
@@ -0,0 +1,38 @@
1
+ // Small helpers around `BIBLE_BOOKS`: alias lookup and canonical
2
+ // string formatting for a parsed `BibleRef`.
3
+ import { BIBLE_BOOK_ALIASES, BIBLE_BOOKS } from "../data/index.js";
4
+ /** Look up a book by any accepted form (`Id`, `Short`, `Name`, or
5
+ * `Short`-with-underscores). Returns `undefined` when unknown. */
6
+ export function findBook(nameOrShort) {
7
+ const id = BIBLE_BOOK_ALIASES[nameOrShort.trim()];
8
+ if (id === undefined)
9
+ return undefined;
10
+ return BIBLE_BOOKS[id];
11
+ }
12
+ /** Format a `BibleRef` back to the upstream `<Label>_<Chapter>:<Spec>` form.
13
+ * Uses the original `bookLabel` from the parsed input, so
14
+ * `formatBibleRef(parseBibleRef(x)) === x` for every corpus reference. */
15
+ export function formatBibleRef(ref) {
16
+ if (ref.ranges.length === 0) {
17
+ return `${ref.bookLabel}_${ref.chapter}`;
18
+ }
19
+ const spec = ref.ranges.map((r) => formatRange(r, ref.chapter)).join(", ");
20
+ return `${ref.bookLabel}_${ref.chapter}:${spec}`;
21
+ }
22
+ function formatRange(range, primaryChapter) {
23
+ const startStr = formatEndpoint(range.start, primaryChapter, /*explicitChapter*/ false);
24
+ if (range.start.chapter === range.end.chapter
25
+ && range.start.verse === range.end.verse
26
+ && range.start.part === range.end.part) {
27
+ return startStr;
28
+ }
29
+ const endStr = formatEndpoint(range.end, range.start.chapter, range.end.chapter !== range.start.chapter);
30
+ return `${startStr}-${endStr}`;
31
+ }
32
+ function formatEndpoint(ep, contextChapter, forceChapter) {
33
+ const needChapter = forceChapter || ep.chapter !== contextChapter;
34
+ const prefix = needChapter ? `${ep.chapter}:` : "";
35
+ const part = ep.part ?? "";
36
+ return `${prefix}${ep.verse}${part}`;
37
+ }
38
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/bible/format.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,6CAA6C;AAE7C,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGnE;mEACmE;AACnE,MAAM,UAAU,QAAQ,CAAC,WAAmB;IAC3C,MAAM,EAAE,GAAG,kBAAkB,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAClD,IAAI,EAAE,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACvC,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAED;;2EAE2E;AAC3E,MAAM,UAAU,cAAc,CAAC,GAAa;IAC3C,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;IAC1C,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;AAClD,CAAC;AAED,SAAS,WAAW,CAAC,KAAiB,EAAE,cAAsB;IAC7D,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,cAAc,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACxF,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,GAAG,CAAC,OAAO;WACzC,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,GAAG,CAAC,KAAK;WACrC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACzC,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,MAAM,MAAM,GAAG,cAAc,CAC5B,KAAK,CAAC,GAAG,EACT,KAAK,CAAC,KAAK,CAAC,OAAO,EACnB,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,KAAK,CAAC,KAAK,CAAC,OAAO,CACzC,CAAC;IACF,OAAO,GAAG,QAAQ,IAAI,MAAM,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,cAAc,CACtB,EAAiB,EACjB,cAAsB,EACtB,YAAqB;IAErB,MAAM,WAAW,GAAG,YAAY,IAAI,EAAE,CAAC,OAAO,KAAK,cAAc,CAAC;IAClE,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;IAC3B,OAAO,GAAG,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG,IAAI,EAAE,CAAC;AACtC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type { BibleBook, BibleRef, VerseEndpoint, VerseRange } from "./types.ts";
2
+ export { BibleRefError, parseBibleRef } from "./parse.ts";
3
+ export { findBook, formatBibleRef } from "./format.ts";
4
+ export { BIBLE_BOOKS, BIBLE_BOOK_ALIASES } from "../data/index.ts";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bible/index.ts"],"names":[],"mappings":"AAKA,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,8 @@
1
+ // Barrel for the Bible reference parser + book registry.
2
+ //
3
+ // The generated book registry lives in `src/data/bibleBooks.ts`; this
4
+ // folder wraps it with the runtime parser and a small formatter.
5
+ export { BibleRefError, parseBibleRef } from "./parse.js";
6
+ export { findBook, formatBibleRef } from "./format.js";
7
+ export { BIBLE_BOOKS, BIBLE_BOOK_ALIASES } from "../data/index.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bible/index.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,sEAAsE;AACtE,iEAAiE;AAGjE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { BibleRef } from "./types.ts";
2
+ export declare class BibleRefError extends Error {
3
+ readonly input: string;
4
+ constructor(input: string, message: string);
5
+ }
6
+ export declare function parseBibleRef(input: string): BibleRef;
7
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/bible/parse.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,QAAQ,EAA6B,MAAM,YAAY,CAAC;AAEtE,qBAAa,aAAc,SAAQ,KAAK;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACX,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAmCrD"}