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.
- package/README.md +88 -26
- package/dist/astronomy/index.d.ts +5 -0
- package/dist/astronomy/index.d.ts.map +1 -0
- package/dist/astronomy/index.js +6 -0
- package/dist/astronomy/index.js.map +1 -0
- package/dist/astronomy/lunar.d.ts +22 -0
- package/dist/astronomy/lunar.d.ts.map +1 -0
- package/dist/astronomy/lunar.js +87 -0
- package/dist/astronomy/lunar.js.map +1 -0
- package/dist/astronomy/sunrise.d.ts +41 -0
- package/dist/astronomy/sunrise.d.ts.map +1 -0
- package/dist/astronomy/sunrise.js +111 -0
- package/dist/astronomy/sunrise.js.map +1 -0
- package/dist/bible/format.d.ts +9 -0
- package/dist/bible/format.d.ts.map +1 -0
- package/dist/bible/format.js +38 -0
- package/dist/bible/format.js.map +1 -0
- package/dist/bible/index.d.ts +5 -0
- package/dist/bible/index.d.ts.map +1 -0
- package/dist/bible/index.js +8 -0
- package/dist/bible/index.js.map +1 -0
- package/dist/bible/parse.d.ts +7 -0
- package/dist/bible/parse.d.ts.map +1 -0
- package/dist/bible/parse.js +127 -0
- package/dist/bible/parse.js.map +1 -0
- package/dist/bible/types.d.ts +34 -0
- package/dist/bible/types.d.ts.map +1 -0
- package/dist/bible/types.js +4 -0
- package/dist/bible/types.js.map +1 -0
- package/dist/data/bibleBooks.d.ts +4 -0
- package/dist/data/bibleBooks.d.ts.map +1 -0
- package/dist/data/bibleBooks.js +716 -0
- package/dist/data/bibleBooks.js.map +1 -0
- package/dist/data/index.d.ts +2 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +2 -0
- package/dist/data/index.js.map +1 -1
- package/dist/data/rankOverlay.d.ts +5 -0
- package/dist/data/rankOverlay.d.ts.map +1 -0
- package/dist/data/rankOverlay.js +64 -0
- package/dist/data/rankOverlay.js.map +1 -0
- package/dist/data/types.d.ts +20 -1
- package/dist/data/types.d.ts.map +1 -1
- package/dist/engine/index.d.ts +5 -0
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +7 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/resolve.d.ts.map +1 -1
- package/dist/engine/resolve.js +12 -2
- package/dist/engine/resolve.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +9 -1
- package/src/astronomy/index.ts +23 -0
- package/src/astronomy/lunar.ts +102 -0
- package/src/astronomy/sunrise.ts +151 -0
- package/src/bible/format.ts +50 -0
- package/src/bible/index.ts +9 -0
- package/src/bible/parse.ts +156 -0
- package/src/bible/types.ts +40 -0
- package/src/data/bibleBooks.ts +719 -0
- package/src/data/index.ts +2 -0
- package/src/data/rankOverlay.ts +68 -0
- package/src/data/types.ts +22 -1
- package/src/engine/index.ts +11 -0
- package/src/engine/resolve.ts +15 -2
- 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
|
-
> [!
|
|
6
|
-
> **Status:
|
|
7
|
-
>
|
|
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**
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
- [
|
|
26
|
-
- [
|
|
27
|
-
- [
|
|
28
|
-
- [
|
|
29
|
-
- [
|
|
30
|
-
- [
|
|
31
|
-
- [
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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":"AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,0EAA0E;AAC1E,UAAU;AAGV,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAEtD;kDACkD;AAClD,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,8EAA8E;IAC9E,OAAO,EAAE,CAAC,KAAK;IACf,uFAAuF;IACvF,KAAK,EAAE,CAAC,CAAC;IACT,yBAAyB;IACzB,QAAQ,EAAE,CAAC,EAAE;IACb,qCAAqC;IACrC,OAAO,EAAE,CAAC,EAAE;IACZ,mDAAmD;IACnD,YAAY,EAAE,CAAC,EAAE;CACR,CAAC;AA0BX,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC;AACvB,MAAM,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC;AAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC;AAC7B,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;AACjD,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;AACjD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClD,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAElE,SAAS,UAAU,CAAC,KAAa;IAChC,OAAO,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,GAAG,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED,mEAAmE;AACnE,MAAM,aAAa,GAAG,OAAO,CAAC;AAE9B,SAAS,KAAK,CAAC,CAAS;IACvB,OAAO,UAAU,CAAC,GAAG,GAAG,OAAO,GAAG,QAAQ,GAAG,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,SAAS,MAAM,CAAC,CAAS;IACxB,MAAM,CAAC,GAAG,UAAU,CAAC,OAAO,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC;IACjD,MAAM,CAAC,GAAG,QAAQ,GAAG,UAAU,GAAG,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,QAAQ,GAAG,QAAQ,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACnC,IAAI,GAAG,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,GAAG,IAAI,GAAG;QAAE,GAAG,IAAI,GAAG,CAAC;IAC3B,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS;IAC1B,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACtB,MAAM,GAAG,GAAG,OAAO,GAAG,QAAQ,GAAG,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC/B,IAAkB,EAClB,IAA0B;IAE1B,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC;IAClC,IAAI,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,OAAO,CAAC;IACpD,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhD,MAAM,GAAG,GAAG,SAAS,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACzE,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,aAAa,CAAC,GAAG,GAAG,GAAG,SAAS,GAAG,GAAG,CAAC;IAExD,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,CAAC;IACvD,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IAC9C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,CAAC;IAC3B,IAAI,UAAU;QAAE,QAAQ,IAAI,OAAO,CAAC;IAEpC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;UACvD,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAEhC,IAAI,CAAS,CAAC;IACd,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QACf,CAAC,GAAG,CAAC,CAAC;QACN,aAAa,GAAG,IAAI,CAAC;IACtB,CAAC;SAAM,IAAI,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC;QACvB,CAAC,GAAG,EAAE,CAAC;QACP,WAAW,GAAG,IAAI,CAAC;IACpB,CAAC;SAAM,CAAC;QACP,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,CAAC;IAED,IAAI,YAAY,GAAG,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC;IACnC,IAAI,WAAW,GAAG,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC;IAClC,YAAY,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC;IACvC,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;IAErC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,SAAS,CAAC,CAAS;IAC3B,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,EAAE,CAAC;IACzB,OAAO,CAAC,CAAC;AACV,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IACvC,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACtE,CAAC"}
|
|
@@ -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"}
|