panchang-ts 4.0.0 → 4.1.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 CHANGED
@@ -5,7 +5,7 @@
5
5
  Pure TypeScript Hindu Panchang (almanac), Jyotish, and Birth Chart calculations.
6
6
  Zero native dependencies. Works offline in React Native (Hermes), Node.js, and browsers.
7
7
 
8
- **Fast** (~0.1 ms names-only, ~0.5 ms full) | **Typed** (full TypeScript types) | **Offline** (pure JS math, no network) | **7,156 tests**
8
+ **Fast** (~0.1 ms names-only, ~0.5 ms full) · **Typed** (full TypeScript) · **Offline** (pure JS math) · **8,103 tests across 96 files**
9
9
 
10
10
  ---
11
11
 
@@ -13,53 +13,11 @@ Zero native dependencies. Works offline in React Native (Hermes), Node.js, and b
13
13
 
14
14
  ```bash
15
15
  npm install panchang-ts
16
- # or
17
- pnpm add panchang-ts
18
- # or
19
- yarn add panchang-ts
16
+ # or: pnpm add panchang-ts / yarn add panchang-ts
20
17
  ```
21
18
 
22
19
  ---
23
20
 
24
- ## Features at a Glance
25
-
26
- Every category below is implemented end-to-end, cross-verified against reference panchang
27
- sources, and exposed through the public API. Click a row to jump to its usage example.
28
-
29
- | Category | Features | Jump to |
30
- |----------|----------|---------|
31
- | **Pancha Anga (5 limbs)** | Tithi, Nakshatra, Yoga, Karana, Vara — with all transitions through the day | [↓](#1-pancha-anga--the-five-limbs) |
32
- | **Lunar Calendar** | Chandra Masa (Purnimanta + Amanta), Adhika (leap-month) detection, Vikram Samvat, Shaka Samvat | [↓](#2-lunar-calendar) |
33
- | **Solar Calendar** | Saura Masa, Surya Nakshatra, Sankranti (transit-based) | [↓](#3-solar-calendar) |
34
- | **Sun & Moon** | Sunrise, Sunset, Moonrise, Moonset, Chandra Rashi (Moon sign) | [↓](#4-sun--moon) |
35
- | **Auspicious Muhurta** | Brahma, Abhijit, Vijaya, Godhuli, Nishita, Madhyahna, Pratah / Sayahna Sandhya, Amrit Kala | [↓](#5-auspicious-muhurta) |
36
- | **Inauspicious Periods** | Rahu Kalam, Gulika Kalam, Yamaganda, Dur Muhurta, Varjyam, Ganda Mula, Bhadra Kala, Panchaka | [↓](#6-inauspicious-periods) |
37
- | **Time-Slot Systems** | Choghadiya (16), Gowri Panchangam / Nalla Neram (16), Hora (24), Do Ghati (30), Panchaka Rahita | [↓](#7-time-slot-systems) |
38
- | **Special Yogas** | Anandadi (28-name cycle), Amrit Siddhi, Sarvartha Siddhi, Ravi / Guru Pushya, Dwipushkar, Tripushkar, Jwalamukhi, Aadal, Vidaal, Ravi | [↓](#8-special-yogas) |
39
- | **Festivals (80+)** | Ekadashi (Smarta + Vaishnava split), Pradosha, Sankranti variants, classical festivals (Diwali, Holi, Shivaratri…), regional festivals across 21 states + Nepal | [↓](#9-festivals) |
40
- | **Eclipses (Grahan)** | Solar + lunar detection, subtype, magnitude, observer-horizon visibility, sutak window | [↓](#10-eclipses) |
41
- | **Planetary Positions** | All 9 grahas (sidereal) with rashi, nakshatra, pada, retrograde — mean or true Rahu/Ketu | [↓](#11-planetary-positions) |
42
- | **Vimshottari Dasha** | Maha → Antar → Pratyantar (3-level) breakdown from birth | [↓](#12-vimshottari-dasha) |
43
- | **Personal Transits** | Chandra Balam, Tarabala (9-tara cycle), Sade Sati (Saturn arc) | [↓](#13-personal-transits) |
44
- | **Birth Chart (Kundli)** | Lagna (sidereal), Bhava under 3 house systems, D1 / D2 / D3 / D7 / D9 / D10 / D12 / D30 charts, Planetary Dignity | [↓](#14-birth-chart-kundli) |
45
- | **Compatibility & Doshas** | Ashtakoot Guna Milan (36-point), Pathu Porutham (Tamil 10-fold), Mangal Dosha, Kaal Sarp Dosha (12 subtypes), Pitru Dosha | [↓](#15-compatibility--doshas) |
46
- | **Aspects & Strength** | Drishti (graha aspects), Shadbala (six-fold strength), Ashtakavarga (Bhinnashtaka + Sarvashtaka), Yogas (25 named), Karakas, Bhava Bala, Argala | [↓](#16-aspects--strength) |
47
- | **Annual & Sensitive Layers** | Varshaphala (Tajik annual chart + 27 Sahams), Tithi Pravesha, Arudha padas, Hora/Ghati/Bhava/Sripati lagnas, Upagrahas (Gulika, Mandi, Dhuma, Vyatipata, Parivesha, Indrachapa, Upaketu) | [↓](#16-aspects--strength) |
48
- | **KP & Prashna** | KP sub-lord at any longitude, cuspal sub-lords (Placidus-KP), KP significators, Prashna (horary) chart | [↓](#16-aspects--strength) |
49
- | **Dasha Systems** | Vimshottari (Maha→Antar→Pratyantar), Ashtottari, Yogini, Chara (Jaimini), Narayan (Jaimini with padi direction) | [↓](#17-dasha-systems) |
50
- | **Muhurta Engine** | Configurable scoring + 13 stock occasions (vivah, griha pravesh, namakarana, …) | [↓](#18-muhurta-engine) |
51
- | **Calendar Conversion** | Gregorian↔Hindu, Kali Yuga year, Hindu New Year, yearly Ekadashi / Sankranti / festival listings | [↓](#19-calendar-conversion) |
52
- | **Localization** | English + Hindi (Devanagari) on every returned name | [↓](#20-localization) |
53
- | **Configuration** | 5 ayanamsas (Lahiri, Raman, KP, True Chitrapaksha, Thirukanitham), 2 masa systems, 3 house systems, 21 regional festival scopes | [↓](#21-configuration) |
54
-
55
- ---
56
-
57
- ## Used By
58
-
59
- - [dharmagya.app](https://dharmagya.app) — Daily Panchang and Hindu calendar
60
-
61
- ---
62
-
63
21
  ## Quick Start
64
22
 
65
23
  ```typescript
@@ -70,222 +28,172 @@ const result = getDailyPanchang(
70
28
  { latitude: 23.1765, longitude: 75.7885 }, // Ujjain, India
71
29
  { timezone: 330 }, // IST = UTC+5:30 = 330 minutes
72
30
  );
73
- // result is `DailyPanchangResult | null` null only at polar latitudes
74
- // where sunrise can't be computed. Anywhere else, narrow with `if (!result) return;`
75
-
76
- console.log(result.tithis[0].name); // "Krishna Chaturdashi"
77
- console.log(result.nakshatras[0].name); // "Mrigashira"
78
- console.log(result.vara.name); // "Mangalawara"
79
- console.log(result.chandramasa.name); // "Magha"
80
- console.log(result.samvat.vikramSamvat); // 2081
31
+ // DailyPanchangResult | null. Null only at polar latitudes where
32
+ // sunrise can't be computed. Anywhere else, narrow with `if (!result) return;`.
33
+
34
+ console.log(result!.tithis[0].name); // "Krishna Chaturdashi"
35
+ console.log(result!.nakshatras[0].name); // "Mrigashira"
36
+ console.log(result!.vara.name); // "Mangalawara"
37
+ console.log(result!.chandramasa.name); // "Magha"
38
+ console.log(result!.samvat.vikramSamvat); // 2081
81
39
  ```
82
40
 
83
41
  ### Reading Output Times
84
42
 
85
- All `Date` objects in the result are **offset-adjusted** to the requested timezone. Always
86
- read time components via `getUTC*` methods — `.getHours()` would use your system zone:
43
+ All `Date` objects are **offset-adjusted** to the requested timezone. Read time
44
+ components via `getUTC*` — `.getHours()` would use your system zone:
87
45
 
88
46
  ```typescript
89
- const sunrise = result.sunrise;
47
+ const sunrise = result!.sunrise;
90
48
  const h = sunrise.getUTCHours(); // 7
91
- const m = sunrise.getUTCMinutes(); // 4
92
- // → Sunrise at 07:04 local time
93
-
94
- function fmt(d: Date) {
95
- const h = d.getUTCHours(), m = d.getUTCMinutes();
96
- return `${h}:${String(m).padStart(2, '0')}`;
97
- }
98
- fmt(result.rahuKalam.start); // "09:04"
49
+ const m = sunrise.getUTCMinutes(); // 4 → 07:04 local time
99
50
  ```
100
51
 
101
- `moonrise` and `moonset` can be `null` — the Moon occasionally does not rise or set on a
102
- given calendar day, which is normal.
52
+ `moonrise` / `moonset` can be `null` — the Moon occasionally does not rise or
53
+ set on a given calendar day, which is normal.
103
54
 
104
55
  ### `getDailyPanchang` vs `getInstantPanchang`
105
56
 
106
- | Use case | Recommended | Why |
107
- |----------|-------------|-----|
108
- | "What Panchang elements are active right now?" | `getInstantPanchang` | Single-moment snapshot; no sunrise needed |
109
- | Birth chart / muhurta picking at a specific instant | `getInstantPanchang` | Exact element at that UTC moment |
110
- | Daily calendar / almanac row for a date | `getDailyPanchang` | Lists all element transitions for the day |
111
- | Today's festivals & observances | `getDailyPanchang` | Full canonical-time festival refinement |
112
- | Rahu Kalam / Choghadiya / Gowri / Hora / muhurtas | `getDailyPanchang` | Computed from sunrise, sunset, day length |
113
- | Eclipse detection with sutak window | `getDailyPanchang` | Overlapping the day needs the day window |
114
-
115
- `getInstantPanchang` does emit `festivals`, but evaluates rules against the elements at the
116
- given instant only. It does not run canonical-time refinements (madhyahna / pradosha /
117
- nishita / chandrodaya), transit-based Sankranti, Ekadashi viddha (Smarta/Vaishnava split),
118
- or Bhadra-aware Raksha Bandhan exclusion. For reliable festival dating, use
119
- `getDailyPanchang`.
57
+ | Use case | Use |
58
+ |---|---|
59
+ | Daily calendar, festivals, muhurtas, time-slots (Choghadiya/Hora/Gowri), eclipses with sutak | `getDailyPanchang` |
60
+ | Single-moment snapshot ("what's active right now?") or birth-chart casting | `getInstantPanchang` |
120
61
 
121
- ---
62
+ `getInstantPanchang` emits a `festivals` field but only checks rule predicates
63
+ at the given instant — it skips canonical-time refinements (madhyahna /
64
+ pradosha / nishita / chandrodaya), transit-based Sankranti, and Smarta/Vaishnava
65
+ Ekadashi split. For reliable festival dating, use `getDailyPanchang`.
122
66
 
123
- # Feature Reference
67
+ ---
124
68
 
125
- Each section below shows how to access one feature category. Every feature is also
126
- returned as a field on the unified `DailyPanchangResult` from `getDailyPanchang(…)` if you
127
- prefer one call over the per-feature helpers.
69
+ ## Features at a Glance
128
70
 
129
- ## 1. Pancha Anga — the Five Limbs
71
+ | Category | Features |
72
+ |---|---|
73
+ | **Pancha Anga** | Tithi, Nakshatra, Yoga, Karana, Vara — with all intra-day transitions |
74
+ | **Lunar Calendar** | Chandra Masa (Purnimanta + Amanta), Adhika (leap) detection, Vikram + Shaka Samvat |
75
+ | **Solar Calendar** | Saura Masa, Surya Nakshatra, Sankranti (transit-based) |
76
+ | **Sun & Moon** | Sunrise, Sunset, Moonrise, Moonset (Meeus apparent-upper-limb), Chandra Rashi |
77
+ | **Auspicious Muhurta** | Brahma, Abhijit, Vijaya, Godhuli, Nishita, Madhyahna, Pratah/Sayahna Sandhya, Amrit Kala |
78
+ | **Inauspicious Periods** | Rahu Kalam, Gulika Kalam, Yamaganda, Dur Muhurta, Varjyam, Ganda Mula, Bhadra Kala, Panchaka |
79
+ | **Time-Slot Systems** | Choghadiya, Gowri Panchangam (Tamil "Nalla Neram"), Hora, Do Ghati, Panchaka Rahita |
80
+ | **Special Yogas** | Anandadi (28-cycle), Amrit Siddhi, Sarvartha Siddhi, Ravi/Guru Pushya, Dwi-/Tripushkar, Jwalamukhi, Aadal, Vidaal, Ravi |
81
+ | **Festivals (80+)** | Ekadashi (Smarta/Vaishnava split), Pradosha, Sankranti, classical (Diwali/Holi/Shivaratri…), regional across 21 states + Nepal |
82
+ | **Eclipses** | Solar/lunar detection, subtype, magnitude, horizon visibility, sutak window |
83
+ | **Planetary Positions** | All 9 grahas (sidereal) with rashi, nakshatra, pada, retrograde — mean or true Rahu/Ketu |
84
+ | **Dashas** | Vimshottari (3-level), Ashtottari, Yogini, Chara, Narayan |
85
+ | **Personal Transits** | Chandra Balam, Tarabala (9-cycle), Sade Sati |
86
+ | **Birth Chart** | Lagna, Bhava under 3 house systems, D1/D2/D3/D7/D9/D10/D12/D30, Planetary Dignity |
87
+ | **Compatibility & Doshas** | Ashtakoot (36-point), Pathu Porutham (Tamil 10-fold), Mangal, Kaal Sarp (12 subtypes), Pitru |
88
+ | **Strength & Aspects** | Drishti, Shadbala (6-fold), Ashtakavarga (Bhinna + Sarva, with reductions), Bhava Bala, Argala |
89
+ | **Yogas & Karakas** | 25 named yogas (with cancellations), 7- and 8-Karaka Jaimini |
90
+ | **Annual & Sensitive** | Varshaphala (Tajik + 27 Sahams), Tithi Pravesha, Arudha padas, Hora/Ghati/Bhava/Sripati lagnas, Upagrahas |
91
+ | **KP & Prashna** | KP sub-lord at any longitude, Placidus-KP cuspal sub-lords, KP significators, Prashna chart |
92
+ | **Muhurta Engine** | Configurable scoring + 13 stock occasions (vivah, griha pravesh, namakarana, …) |
93
+ | **Calendar Conversion** | Gregorian↔Hindu, Kali Yuga year, Hindu New Year, yearly Ekadashi/Sankranti/festival listings |
94
+ | **Localization** | English + Hindi (Devanagari) |
95
+ | **Configuration** | 5 ayanamsas (Lahiri, Raman, KP, True Chitrapaksha, Thirukanitham), 2 masa systems, 3 house systems |
130
96
 
131
- Tithi, Nakshatra, Yoga, Karana, Vara — with start / end times for every transition during
132
- the Hindu day.
97
+ ---
133
98
 
134
- ```typescript
135
- import { getDailyPanchang } from 'panchang-ts';
99
+ ## Used By
136
100
 
137
- const r = getDailyPanchang(date, location, { timezone: 330 })!;
101
+ - [dharmagya.app](https://dharmagya.app) Daily Panchang and Hindu calendar ([Play Store](https://play.google.com/store/apps/details?id=com.ishank1995.dharmagya))
138
102
 
139
- // Tithis active during the day (usually 1-2)
140
- r.tithis.forEach(t => {
141
- console.log(t.name, t.paksha, t.completionPercentage, t.endTime);
142
- });
103
+ ---
143
104
 
144
- // Nakshatras (with pada)
145
- r.nakshatras.forEach(n => console.log(n.name, n.pada, n.endTime));
105
+ # Feature Reference
146
106
 
147
- // Yogas (27-name lunisolar cycle)
148
- r.yogas.forEach(y => console.log(y.name, y.endTime));
107
+ Each daily field is also returned from `getDailyPanchang` if you prefer one call
108
+ over per-feature helpers.
149
109
 
150
- // Karanas (half-tithi; usually 2-4 per day)
151
- r.karanas.forEach(k => console.log(k.name, k.type, k.endTime));
110
+ ## Pancha Anga
152
111
 
153
- // Vara (weekday)
154
- console.log(r.vara.name, r.vara.englishName); // "Mangalawara", "Tuesday"
155
- ```
112
+ ```typescript
113
+ const r = getDailyPanchang(date, location, { timezone: 330 })!;
156
114
 
157
- For a single-instant snapshot use `getInstantPanchang`:
115
+ r.tithis.forEach(t => console.log(t.name, t.paksha, t.completionPercentage, t.endTime));
116
+ r.nakshatras.forEach(n => console.log(n.name, n.pada, n.endTime));
117
+ r.yogas.forEach(y => console.log(y.name, y.endTime));
118
+ r.karanas.forEach(k => console.log(k.name, k.type, k.endTime));
119
+ console.log(r.vara.name, r.vara.englishName); // "Mangalawara", "Tuesday"
158
120
 
159
- ```typescript
121
+ // Single-instant snapshot:
160
122
  import { getInstantPanchang } from 'panchang-ts';
161
-
162
123
  const i = getInstantPanchang(new Date(), location)!;
163
124
  console.log(i.tithi.name, i.nakshatra.name, i.yoga.name, i.karana.name, i.vara.name);
164
125
  ```
165
126
 
166
- ## 2. Lunar Calendar
167
-
168
- Chandra Masa with **Purnimanta** (North Indian, default) and **Amanta** (South Indian)
169
- naming, **Adhika** (leap-month) detection, **Vikram** and **Shaka** samvat year numbers.
127
+ ## Lunar & Solar Calendar
170
128
 
171
129
  ```typescript
172
130
  const r = getDailyPanchang(date, loc, { timezone: 330, masaSystem: 'purnimanta' })!;
173
131
 
174
- console.log(r.chandramasa.name); // "Magha" (active system)
175
- console.log(r.chandramasa.amantaName); // "Pausha" (South Indian)
176
- console.log(r.chandramasa.purnimantaName); // "Magha" (North Indian)
177
- console.log(r.chandramasa.isAdhika); // false (true during leap months)
132
+ r.chandramasa.name; // active system (default: Purnimanta / North Indian)
133
+ r.chandramasa.amantaName; // South Indian
134
+ r.chandramasa.purnimantaName; // North Indian
135
+ r.chandramasa.isAdhika; // true during leap months
136
+ r.samvat.vikramSamvat; // 2081
137
+ r.samvat.shakaSamvat; // 1946
178
138
 
179
- console.log(r.samvat.vikramSamvat); // 2081
180
- console.log(r.samvat.shakaSamvat); // 1946
139
+ r.masa.name; // current solar month (Mesha … Meena)
140
+ r.suryaNakshatra.name; // Sun's nakshatra
141
+ r.chandraRashi.name; // Moon sign
181
142
  ```
182
143
 
183
- ## 3. Solar Calendar
184
-
185
- Saura Masa (solar month), Surya Nakshatra (the Sun's nakshatra, ~13–14 day transit),
186
- Sankranti (solar-month boundary, transit-based — emitted as a festival).
187
-
188
- ```typescript
189
- const r = getDailyPanchang(date, loc, { timezone: 330 })!;
190
-
191
- console.log(r.masa.name); // "Makara" (current solar month)
192
- console.log(r.suryaNakshatra.name); // "Uttara Ashadha"
193
- console.log(r.chandraRashi.name); // "Mithuna" (Moon sign)
194
-
195
- // Sankranti and its regional variants emit through r.festivals — see §9.
196
- ```
197
-
198
- ## 4. Sun & Moon
199
-
200
- Sunrise, sunset, moonrise, moonset (Meeus apparent-upper-limb), plus Chandra Rashi
201
- (Moon's zodiac sign).
144
+ ## Sun, Moon & Muhurta
202
145
 
203
146
  ```typescript
204
147
  import { getSunrise, getSunset, getMoonrise, getMoonset } from 'panchang-ts';
205
148
 
206
- const loc = { latitude: 28.6139, longitude: 77.2090 }; // New Delhi
207
-
208
- const sunrise = getSunrise(localMidnightUtc, loc);
209
- const sunset = getSunset(sunrise, loc);
210
-
211
- // Moonrise / moonset can be null on days the Moon doesn't rise/set
212
- const moonrise = getMoonrise(localMidnightUtc, loc);
149
+ const sunrise = getSunrise(localMidnightUtc, loc);
150
+ const sunset = getSunset(sunrise, loc);
151
+ const moonrise = getMoonrise(localMidnightUtc, loc); // null on some days (normal)
213
152
  const moonset = getMoonset(localMidnightUtc, loc);
214
153
 
215
- // Or read all of them off the daily result:
216
- const r = getDailyPanchang(date, loc, { timezone: 330 })!;
217
- console.log(r.sunrise, r.sunset, r.moonrise, r.moonset, r.nextSunrise);
218
- console.log(r.dayDurationMinutes, r.nightDurationMinutes);
219
- ```
220
-
221
- ## 5. Auspicious Muhurta
222
-
223
- Classical auspicious time windows: Brahma, Abhijit, Vijaya, Godhuli, Nishita, Madhyahna,
224
- Pratah / Sayahna Sandhya, and nakshatra-keyed Amrit Kala.
225
-
226
- ```typescript
154
+ // Or read off the daily result:
227
155
  const r = getDailyPanchang(date, loc, { timezone: 330 })!;
156
+ r.sunrise; r.sunset; r.moonrise; r.moonset; r.nextSunrise;
157
+ r.dayDurationMinutes; r.nightDurationMinutes;
228
158
 
159
+ // Auspicious muhurtas
229
160
  r.brahmaMuhurta; // two muhurtas before sunrise
230
- r.abhijitMuhurta; // 8th day-muhurta, or null on Wednesday (Buddha-vara, Drik convention)
231
- r.vijayaMuhurta; // 11th day-muhurta — auspicious for success
232
- r.godhuliMuhurta; // "cow-dust" sunset muhurta
161
+ r.abhijitMuhurta; // 8th day-muhurta; null on Wednesday (Drik convention)
162
+ r.vijayaMuhurta; // 11th day-muhurta
163
+ r.godhuliMuhurta; // "cow-dust" sunset muhurta
233
164
  r.nishitaMuhurta; // midnight muhurta (Shivaratri)
234
165
  r.madhyahna; // solar noon ±24 min
235
- r.pratahSandhya; // dawn twilight, ends *at* sunrise
236
- r.sayahnaSandhya; // dusk twilight, starts *at* sunset
166
+ r.pratahSandhya; // dawn twilight, ends at sunrise
167
+ r.sayahnaSandhya; // dusk twilight, starts at sunset
237
168
  r.amritKala; // nakshatra-specific window (null when nakshatra has none)
238
-
239
- // Direct helpers:
240
- import {
241
- computeBrahmaMuhurta, computeAbhijitMuhurta, computeVijayaMuhurta,
242
- computeGodhuliMuhurta, computeNishitaMuhurta, computeMadhyahna,
243
- computePratahSandhya, computeSayahnaSandhya, computeAmritKala,
244
- } from 'panchang-ts';
245
169
  ```
246
170
 
247
- `pratahSandhya` and `sayahnaSandhya` are asymmetric — width = `nightDuration / 10`
248
- (~62–81 min depending on season), matching DrikPanchang within ±2 min.
249
-
250
- ## 6. Inauspicious Periods
171
+ `pratahSandhya` / `sayahnaSandhya` width = `nightDuration / 10` (~62–81 min).
251
172
 
252
- Rahu Kalam, Gulika Kalam, Yamaganda, Dur Muhurta (2 windows), Varjyam (BPHS-keyed
253
- ~96-min forbidden window), Ganda Mula (Moon in root nakshatras), Bhadra Kala (Vishti
254
- karana with earth/heaven/paatal location), Panchaka.
173
+ ## Inauspicious Periods
255
174
 
256
175
  ```typescript
257
176
  const r = getDailyPanchang(date, loc, { timezone: 330 })!;
258
177
 
259
178
  r.rahuKalam; // { start, end }
260
- r.gulikaKalam; // { start, end }
261
- r.yamaganda; // { start, end }
262
- r.durMuhurta; // [TimePeriod, TimePeriod] — two ~48-min windows
179
+ r.gulikaKalam;
180
+ r.yamaganda;
181
+ r.durMuhurta; // two ~48-min windows
263
182
  r.varjyam; // { start, end } | null
264
- r.gandaMula; // { active: boolean, severity: 'mild' | 'severe' | null, ... }
183
+ r.gandaMula; // { active, severity: 'mild'|'severe'|null, ... }
265
184
  r.bhadra; // { start, end, location: 'earth'|'heaven'|'paatal', isActive } | null
266
185
  r.panchaka; // boolean — Moon in last 5 nakshatras
267
-
268
- // Direct helpers (varaIndex: 0=Sun ... 6=Sat):
269
- import {
270
- computeRahuKalam, computeGulikaKalam, computeYamaganda,
271
- computeVarjyam, computeGandaMula,
272
- } from 'panchang-ts';
273
-
274
- const rahu = computeRahuKalam(sunrise, sunset, varaIndex);
275
186
  ```
276
187
 
277
- ## 7. Time-Slot Systems
278
-
279
- Four parallel slot systems covering the Hindu day:
188
+ ## Time-Slot Systems
280
189
 
281
190
  ```typescript
282
191
  const r = getDailyPanchang(date, loc, { timezone: 330 })!;
283
192
 
284
- // Choghadiya — 8 day + 8 night slots, each named (Amrit, Kaal, Shubh, Rog, ...) and rated
193
+ // Choghadiya — 8 day + 8 night named, rated slots (Amrit, Kaal, Shubh, Rog, )
285
194
  r.choghadiya.day.forEach(s => console.log(s.name, s.qualityName, s.start, s.end));
286
- r.choghadiya.night.forEach(s => console.log(s.name, s.qualityName));
287
195
 
288
- // Gowri Panchangam (Tamil "Nalla Neram") — 8 day + 8 night slots
196
+ // Gowri Panchangam ("Nalla Neram") — 8 day + 8 night Tamil slots
289
197
  r.gowriPanchangam.day.forEach(s => console.log(s.name, s.qualityName));
290
198
 
291
199
  // Hora — 12 day + 12 night planetary hours (Chaldean order)
@@ -294,193 +202,157 @@ r.hora.day.forEach(h => console.log(h.planet, h.start, h.end));
294
202
  // Do Ghati Muhurta — 15 day + 15 night ~48-min deity-keyed slots (no vara rotation)
295
203
  r.doGhatiMuhurta.day.forEach(g => console.log(g.name, g.start, g.end));
296
204
 
297
- // Panchaka Rahita — slices of the day FREE of Panchaka ([] when Panchaka pervades)
205
+ // Panchaka Rahita — slices of the day FREE of Panchaka ([] when it pervades)
298
206
  r.panchakaRahita.forEach(slice => console.log(slice.start, slice.end));
299
207
  ```
300
208
 
301
- ## 8. Special Yogas
302
-
303
- Auspicious / inauspicious yogas formed by Vara × Tithi × Nakshatra combinations and
304
- Moon-from-Sun nakshatra-distance rules. The 28-name **Anandadi Yoga** cycle is also
305
- returned at sunrise.
209
+ ## Special Yogas
306
210
 
307
211
  ```typescript
308
212
  const r = getDailyPanchang(date, loc, { timezone: 330 })!;
309
213
 
310
- // Anandadi Yoga (Vara × Nakshatra cycle of 28 names)
311
- console.log(r.anandadiYoga.name); // "Ananda"
312
-
313
- // Special yogas active today
214
+ r.anandadiYoga.name; // 28-cycle name e.g. "Ananda"
314
215
  r.specialYogas.forEach(y => {
216
+ // type: amrit_siddhi | sarvartha_siddhi | ravi_pushya | guru_pushya
217
+ // | dwipushkar | tripushkar | jwalamukhi (inauspicious)
218
+ // | aadal | vidaal | ravi (Moon-from-Sun nakshatra-distance rules)
315
219
  console.log(y.name, y.type);
316
- // type: 'amrit_siddhi' | 'sarvartha_siddhi' | 'ravi_pushya' | 'guru_pushya'
317
- // | 'dwipushkar' | 'tripushkar' ← actions doubled / tripled
318
- // | 'jwalamukhi' ← inauspicious (Muhurta-chintamani 6.32)
319
- // | 'aadal' | 'vidaal' | 'ravi' ← Moon-from-Sun nakshatra-distance rules
320
220
  });
321
221
  ```
322
222
 
323
- ## 9. Festivals
324
-
325
- **80+ festivals** spanning pan-Indian, regional, and classical observances:
326
-
327
- - **Ekadashi** — 26 named variants (Putrada, Shat Tila, Nirjala, Devshayani, …) with
328
- **Smarta / Vaishnava split** via Dashami-viddha rule; Smarta fast emits a `deferralDate`
329
- for Dwadashi.
330
- - **Pradosha** — 7 weekday-qualified variants (Som, Bhauma, Shani, …) on both pakshas.
331
- - **Sankranti** — transit-based detection plus regional variants (Pongal, Vishu, Baisakhi,
332
- Pohela Boishakh, Bohag / Magh / Kati Bihu, Uttarayan, Ayyappa Makara Jyothi, Raja
333
- Sankranti, Harela, Sair, Singh Sankranti). **Lohri** fires on the Hindu day immediately
334
- preceding Makara Sankranti under Punjab / Haryana / Himachal scopes.
335
- - **Canonical-time classical festivals** — Ganesh Chaturthi (madhyahna), Shivaratri
336
- (nishita), Diwali, Holi, Raksha Bandhan (Bhadra-aware), Karva Chauth (chandrodaya),
337
- Janmashtami, Dussehra, Navaratri, Ram Navami, Hanuman Jayanti, Akshaya Tritiya &
338
- Parashurama Jayanti (madhyahna-vyapini), Makar Sankranti.
339
- - **Regional festivals** — Gudi Padwa, Gangaur, Karaga, Bonalu, Varamahalakshmi,
340
- Bathukamma, Hariyali / Kajari / Hartalika Teej, Govardhan Puja, Bhai Dooj, Phagli,
341
- Jagannath Rath Yatra, Raja Parba 3-day arc.
342
- - **Regional & seasonal** — Chhath (4-day sequence), Vat Savitri, Upakarma, Onam.
343
- - **Monthly observances** — Masik Shivaratri, Vinayaka Chaturthi, Masik Karthigai, Pushya
344
- days, Shravan Somvar, and other month + weekday patterns.
223
+ ## Festivals (80+)
345
224
 
346
- ```typescript
347
- const r = getDailyPanchang(date, loc, { timezone: 330 })!;
225
+ Covers Ekadashi (26 variants, Smarta/Vaishnava split via Dashami-viddha; Smarta
226
+ fast emits a `deferralDate` for Dwadashi), Pradosha, Sankranti + regional
227
+ variants (Pongal, Vishu, Baisakhi, Pohela Boishakh, Bihu, Uttarayan, Lohri…),
228
+ canonical-time classical (Janmashtami, Shivaratri, Ganesh Chaturthi, Diwali,
229
+ Holi, Raksha Bandhan — Bhadra-aware, Karva Chauth, Akshaya Tritiya…),
230
+ regional (Gudi Padwa, Gangaur, Teej variants, Onam, Chhath…), monthly
231
+ observances (Masik Shivaratri, Pushya days, Shravan Somvar…).
348
232
 
233
+ ```typescript
349
234
  r.festivals.forEach(f => {
235
+ // type: major | minor | ekadashi | smarta_ekadashi | vaishnava_ekadashi
236
+ // | pradosha | sankranti | eclipse
350
237
  console.log(f.name, f.type, f.deferralDate);
351
- // type: 'major' | 'minor' | 'ekadashi'
352
- // | 'smarta_ekadashi' | 'vaishnava_ekadashi' ← Smarta sets deferralDate
353
- // | 'pradosha' | 'sankranti' | 'eclipse'
354
238
  });
355
239
  ```
356
240
 
357
- ### Regional festival scoping
241
+ ### Regional scoping
358
242
 
359
- The `region` option scopes regional festival variants to one Indian state. Pan-Indian
360
- festivals (Diwali, Holi, Raksha Bandhan, the canonical `sankranti` event) emit regardless.
243
+ `region` scopes regional variants to one Indian state. Pan-Indian festivals
244
+ emit regardless.
361
245
 
362
246
  ```typescript
363
- // Default — every regional variant emits on Makar Sankranti day:
364
- const all = getDailyPanchang(jan14, chennai, { timezone: 330 })!;
365
- all.festivals.map(f => f.name);
366
- // → ["Sankranti", "Makar Sankranti", "Pongal", "Uttarayan",
367
- // "Magh Bihu", "Ayyappa Makara Jyothi"]
368
-
369
- // Scope to Tamil Nadu — drops Bihu/Ayyappa/Uttarayan:
370
- const tn = getDailyPanchang(jan14, chennai, { timezone: 330, region: 'tamil-nadu' })!;
371
- tn.festivals.map(f => f.name);
372
- // ["Sankranti", "Makar Sankranti", "Pongal"]
373
-
374
- // Lohri fires on the Hindu day BEFORE Makara transit, scoped to Punjab/Haryana/Himachal
375
- const lohri = getDailyPanchang(jan13, amritsar, { timezone: 330, region: 'punjab' })!;
376
- lohri.festivals.some(f => f.name === 'Lohri'); // true
247
+ // All regional variants (default):
248
+ getDailyPanchang(jan14, chennai, { timezone: 330 })!.festivals.map(f => f.name);
249
+ // ["Sankranti","Makar Sankranti","Pongal","Uttarayan","Magh Bihu","Ayyappa Makara Jyothi"]
250
+
251
+ // Tamil Nadu only:
252
+ getDailyPanchang(jan14, chennai, { timezone: 330, region: 'tamil-nadu' })!
253
+ .festivals.map(f => f.name);
254
+ // ["Sankranti","Makar Sankranti","Pongal"]
255
+
256
+ // Lohri fires on the Hindu day BEFORE Makara transit, in Punjab/Haryana/Himachal scope:
257
+ getDailyPanchang(jan13, amritsar, { timezone: 330, region: 'punjab' })!
258
+ .festivals.some(f => f.name === 'Lohri'); // true
377
259
  ```
378
260
 
379
- `FestivalRegion` covers 21 Indian states + `'nepal'` + `'all'` (default). See
380
- [Types](#types--exports) for the full slug list.
381
-
382
- ## 10. Eclipses
261
+ `FestivalRegion` covers 21 Indian states + `'nepal'` + `'all'` (default). The
262
+ legacy slugs `'tamil'`, `'bengal'`, `'north-india'` are still accepted and
263
+ mapped internally.
383
264
 
384
- Solar / lunar eclipse detection with subtype, magnitude, observer-horizon visibility, and
385
- classical pre-eclipse **sutak** impurity window.
265
+ ## Eclipses
386
266
 
387
267
  ```typescript
388
268
  const r = getDailyPanchang(date, loc, { timezone: 330 })!;
389
-
390
269
  if (r.eclipse) {
391
- console.log(r.eclipse.kind); // 'solar' | 'lunar'
392
- console.log(r.eclipse.subtype); // 'partial' | 'total' | 'annular' | 'penumbral'
393
- console.log(r.eclipse.magnitude); // 0..1 fraction of disc obscured at peak
394
- console.log(r.eclipse.visibleFromLocation); // body above horizon at peak?
395
- console.log(r.eclipse.start, r.eclipse.peak, r.eclipse.end);
396
- console.log(r.eclipse.sutakStart, r.eclipse.sutakEnd);
270
+ r.eclipse.kind; // 'solar' | 'lunar'
271
+ r.eclipse.subtype; // 'partial' | 'total' | 'annular' | 'penumbral'
272
+ r.eclipse.magnitude; // 0..1 fraction obscured at peak
273
+ r.eclipse.visibleFromLocation; // body above horizon at peak?
274
+ r.eclipse.start; r.eclipse.peak; r.eclipse.end;
275
+ r.eclipse.sutakStart; r.eclipse.sutakEnd;
397
276
  // Sutak: 12 h (4 prahara) before solar, 9 h (3 prahara) before lunar
398
277
  }
399
278
 
400
- // Or look ahead:
401
279
  import { getUpcomingSolarEclipse, getUpcomingLunarEclipse } from 'panchang-ts';
402
- const next = getUpcomingSolarEclipse(new Date(), loc, /* withinDays */ 365);
280
+ const next = getUpcomingSolarEclipse(new Date(), loc, 365 /* days */);
403
281
  ```
404
282
 
405
- ## 11. Planetary Positions
406
-
407
- All 9 grahas (Sun → Saturn + Rahu / Ketu) — geocentric, sidereal — with rashi, nakshatra,
408
- pada, retrograde flag. Optional `nodeType: 'true'` upgrades Rahu / Ketu from mean node
409
- (±2° worst-case) to Meeus's dominant periodic correction (~±0.6°).
283
+ ## Planetary Positions
410
284
 
411
285
  ```typescript
412
286
  import { computePlanetaryPositions, GRAHA_ABBR } from 'panchang-ts';
413
287
 
414
- const grahas = computePlanetaryPositions(new Date(), 'lahiri');
415
- console.log(grahas.jupiter.rashi.name); // "Dhanu"
416
- console.log(grahas.jupiter.degreeInRashi); // 18.42
417
- console.log(grahas.jupiter.nakshatra.name); // "Purva Ashadha"
418
- console.log(grahas.jupiter.nakshatra.pada); // 3
419
- console.log(grahas.saturn.isRetrograde); // true / false
420
- console.log(GRAHA_ABBR['Jupiter']); // "Ju"
288
+ const g = computePlanetaryPositions(new Date(), 'lahiri');
289
+ g.jupiter.rashi.name; // "Dhanu"
290
+ g.jupiter.degreeInRashi; // 18.42
291
+ g.jupiter.nakshatra.name; // "Purva Ashadha"
292
+ g.jupiter.nakshatra.pada; // 3
293
+ g.saturn.isRetrograde;
294
+ GRAHA_ABBR['Jupiter']; // "Ju"
421
295
 
422
- // True node (more accurate Rahu / Ketu)
423
- const grahasTrue = computePlanetaryPositions(new Date(), 'lahiri', undefined, 'true');
296
+ // True node (sharper Rahu/Ketu via Meeus periodic correction)
297
+ const gT = computePlanetaryPositions(new Date(), 'lahiri', undefined, 'true');
424
298
  ```
425
299
 
426
- ## 12. Vimshottari Dasha
300
+ ## Dashas
427
301
 
428
- Maha Antar → Pratyantar (3-level) breakdown, derived from a birth moment alone or from
429
- an explicit Moon longitude.
302
+ Five classical systems:
430
303
 
431
304
  ```typescript
432
305
  import {
433
- computeVimshottariDasha, computeVimshottariDashaFromBirth,
434
- computeVimshottariPratyantar, getSiderealMoonLongitude,
306
+ computeVimshottariDashaFromBirth, computeVimshottariPratyantar,
307
+ computeAshtottariDasha, computeYoginiDasha, computeCharaDasha, computeNarayanDasha,
435
308
  } from 'panchang-ts';
436
309
 
437
- // Convenience: from birth date alone (Moon longitude derived)
438
- const dasha = computeVimshottariDashaFromBirth(birthDate, 'lahiri');
439
- console.log(dasha.currentMahaDashaLord); // "Rahu"
440
- console.log(dasha.mahaDashas[0]!.antarDashas[0]!.lord); // "Rahu"
310
+ // 1. Vimshottari 120-year, 9-lord, with 3-level Maha→Antar→Pratyantar.
311
+ const vim = computeVimshottariDashaFromBirth(birth, 'lahiri');
312
+ const pratyantars = computeVimshottariPratyantar(vim.mahaDashas[0]!.antarDashas[0]!);
441
313
 
442
- // Or pass an explicit Moon sidereal longitude
443
- const moonLon = getSiderealMoonLongitude(birthDate, 'lahiri');
444
- const dasha2 = computeVimshottariDasha(birthDate, moonLon);
314
+ // 2. Ashtottari 108-year, 8-lord cycle (no Ketu).
315
+ const ash = computeAshtottariDasha(birth, moonLon);
445
316
 
446
- // Pratyantarthird-level sub-sub-periods within an Antardasha
447
- const firstAntar = dasha.mahaDashas[0]!.antarDashas[0]!;
448
- const pratyantars = computeVimshottariPratyantar(firstAntar); // PratyantarDasha[9]
449
- ```
317
+ // 3. Yogini 36-year, 8 yoginis.
318
+ const yog = computeYoginiDasha(birth, moonLon);
319
+ yog.mahaDashas[0]!.yogini; // 'Dhanya'
320
+ yog.mahaDashas[0]!.lord; // 'Jupiter'
321
+
322
+ // 4. Chara (Jaimini) — sign-based, 9-8-7 years per modality, forward only.
323
+ const cha = computeCharaDasha(birth, loc);
450
324
 
451
- ## 13. Personal Transits
325
+ // 5. Narayan (Jaimini) — sign-based, parity-based direction.
326
+ // Vishama-pada lagna {Aries, Taurus, Gemini, Libra, Scorpio, Sag} → forward
327
+ // Sama-pada lagna {Cancer, Leo, Virgo, Capricorn, Aquarius, Pisces} → backward
328
+ const nar = computeNarayanDasha(birth, loc);
329
+ nar.direction; // 'forward' | 'backward'
452
330
 
453
- Daily transit-based favorability relative to the native's birth Moon. Pass `janmaRashi`
454
- or `janmaNakshatra` to `getDailyPanchang` and the corresponding field is added to the
455
- result; or call the helpers directly.
331
+ // Narayan variable-duration variant (Sanjay Rath):
332
+ const narV = computeNarayanDasha(birth, loc, 'lahiri', { duration: 'variable' });
333
+ narV.mahaDashas[0]!.years; // 0..12 from rashi-to-lord count (+1 exalt, -1 debil)
334
+ ```
335
+
336
+ ## Personal Transits
456
337
 
457
338
  ```typescript
458
339
  const r = getDailyPanchang(date, loc, {
459
340
  timezone: 330,
460
- janmaRashi: 3, // 0 = Mesha ... 11 = Meena
461
- janmaNakshatra: 0, // 0 = Ashwini ... 26 = Revati
341
+ janmaRashi: 3, // 0 = Mesha 11 = Meena
342
+ janmaNakshatra: 0, // 0 = Ashwini 26 = Revati
462
343
  })!;
463
-
464
- r.chandraBalam!; // { house, quality: 'strong' | 'weak', name, englishName }
344
+ r.chandraBalam!; // { house, quality: 'strong'|'weak', name, englishName }
465
345
  r.tarabala!; // { taraIndex, name, englishName, quality }
466
346
 
467
- // Direct helpers:
468
- import { computeChandraBalam, computeTarabala, computeSadeSati } from 'panchang-ts';
469
-
470
- computeChandraBalam(3 /* janma */, 6 /* transit Moon rashi */);
471
- computeTarabala(0 /* janma nakshatra */, 4 /* transit Moon nakshatra */);
472
-
473
- // Sade Sati — Saturn currently transiting 12th, 1st, or 2nd from natal Moon
474
- const sadeSati = computeSadeSati(natalMoonRashiIndex, new Date());
347
+ import { computeSadeSati } from 'panchang-ts';
348
+ const ss = computeSadeSati(natalMoonRashiIndex, new Date());
475
349
  // → { active, phase: 1|2|3|null, currentArcStart, currentArcEnd, nextArcStart }
476
350
  ```
477
351
 
478
- ## 14. Birth Chart (Kundli)
352
+ ## Birth Chart
479
353
 
480
- Sidereal **Lagna**, **Bhava** under three house systems, **D1 (Rashi)** + six classical
481
- divisional charts (**D2 Hora**, **D3 Drekkana**, **D7 Saptamsa**, **D9 Navamsa**, **D10
482
- Dasamsa**, **D12 Dwadasamsa**, **D30 Trimsamsa**) placing all 9 grahas, and **Planetary
483
- Dignity**.
354
+ Sidereal Lagna, Bhava under three house systems, D1 + six classical divisional
355
+ charts (D2/D3/D7/D9/D10/D12/D30), and Planetary Dignity.
484
356
 
485
357
  ```typescript
486
358
  import {
@@ -491,779 +363,291 @@ import {
491
363
  const birth = new Date('1995-08-15T05:30:00Z');
492
364
  const loc = { latitude: 28.6139, longitude: 77.2090 };
493
365
 
494
- // 1. Lagna (sidereal ascendant)
495
366
  const lagna = computeLagna(birth, loc, 'lahiri', 'en');
496
- // → { siderealLongitude, rashi, degreeInRashi, nakshatra, pada }
497
367
 
498
- // 2. Bhava (12 houses)
499
- // - 'whole-sign' (default classical Vedic) each rashi is one house
500
- // - 'equal' — each house spans 30° starting at lagna's exact degree
501
- // - 'placidus-kp' — true cuspal positions; throws PanchangError('CIRCUMPOLAR') > ±66.5°
368
+ // Bhava — 'whole-sign' (default) | 'equal' | 'placidus-kp'.
369
+ // Placidus-KP throws PanchangError('CIRCUMPOLAR') beyond ±66.5°.
502
370
  const houses = computeBhava(birth, loc, { houseSystem: 'whole-sign' });
503
371
 
504
- // 3. D1 (Rashi) chart lagna + bhava + 9 grahas with house placement
372
+ // D1 — full Rashi chart with 9-graha house placement.
505
373
  const d1 = computeRashiChart(birth, loc, { houseSystem: 'whole-sign' });
506
- d1.planets.find(p => p.planet === 'Jupiter')?.house; // e.g. 5
374
+ d1.planets.find(p => p.planet === 'Jupiter')?.house;
507
375
  d1.planets.find(p => p.planet === 'Saturn')?.isRetrograde;
508
376
 
509
- // 4. D9 (Navamsa) chart classical sign-based per-rashi-type rule
510
- const d9 = computeNavamsa(birth, loc);
511
-
512
- // 5. Divisional charts (D2/D3/D7/D10/D12/D30) via the unified API
513
- const d10 = computeDivisionalChart(birth, loc, 'D10'); // career
514
- const d30 = computeDivisionalChart(birth, loc, 'D30'); // misfortune
515
- // → { divisional: 'D10', lagnaRashi, planets[] }
377
+ // Divisional charts (D2 Hora, D3 Drekkana, D7 Saptamsa, D9 Navamsa,
378
+ // D10 Dasamsa, D12 Dwadasamsa, D30 Trimsamsa).
379
+ const d9 = computeNavamsa(birth, loc);
380
+ const d10 = computeDivisionalChart(birth, loc, 'D10');
381
+ const d30 = computeDivisionalChart(birth, loc, 'D30');
516
382
 
517
- // 6. Planetary dignity (BPHS Ch.3-4)
518
- computeDignity('Mars', 0); // 'moolatrikona' (Aries)
519
- computeDignity('Mars', 9); // 'exalted' (Capricorn)
520
- computeDignity('Sun', 6); // 'debilitated' (Libra)
383
+ // Planetary dignity (BPHS Ch.3-4).
384
+ computeDignity('Mars', 0); // 'moolatrikona' (Aries)
385
+ computeDignity('Mars', 9); // 'exalted' (Capricorn)
386
+ computeDignity('Sun', 6); // 'debilitated' (Libra)
521
387
  ```
522
388
 
523
- The seven divisional kinds are: **D2 Hora** (wealth), **D3 Drekkana** (siblings),
524
- **D7 Saptamsa** (children), **D9 Navamsa** (partner / dharma), **D10 Dasamsa** (career),
525
- **D12 Dwadasamsa** (parents), **D30 Trimsamsa** (misfortune). Each follows its classical
526
- per-rashi-type mapping per BPHS Ch. 6; D30 uses the non-uniform 5-segment split with
527
- Mars / Saturn / Jupiter / Mercury / Venus rulership (no Sun / Moon segments).
528
-
529
- Birth-chart helpers accept the full ayanamsa set including `'true-chitra'` (True
530
- Chitrapaksha) and `'thirukanitham'` (Tamil-Vakya). Pass via `options.ayanamsa` or the
531
- ayanamsa positional arg.
532
-
533
- ## 15. Compatibility & Doshas
389
+ Birth-chart helpers accept the full ayanamsa set including `'true-chitra'` and
390
+ `'thirukanitham'`.
534
391
 
535
- **Ashtakoot Guna Milan** (36-point marriage compatibility), **Pathu Porutham**
536
- (Tamil 10-fold counterpart — see ["Pathu Porutham" in §16](#pathu-porutham-tamil-10-fold-marriage-matching)),
537
- **Mangal Dosha** (Manglik with cancellations), **Kaal Sarp Dosha** (12 named subtypes
538
- by Rahu's house), and **Pitru Dosha** (ancestral affliction triggers).
392
+ ## Compatibility & Doshas
539
393
 
540
394
  ```typescript
541
395
  import {
542
- computeAshtakoot, computeMangalDosha,
543
- computeKaalSarp, computePitruDosha,
396
+ computeAshtakoot, computePathuPorutham,
397
+ computeMangalDosha, computeKaalSarp, computePitruDosha,
544
398
  } from 'panchang-ts';
545
399
 
546
- // Ashtakoot — from natal Moons
400
+ // Ashtakoot (North Indian, 36-point) Varna, Vashya, Tara, Yoni,
401
+ // Graha Maitri, Gana, Bhakoot, Nadi (max 1/2/3/4/5/6/7/8).
547
402
  const match = computeAshtakoot(
548
- { rashi: 4, nakshatra: 9 }, // boy: Simha / Magha
549
- { rashi: 0, nakshatra: 1 }, // girl: Mesha / Bharani
403
+ { rashi: 4, nakshatra: 9 },
404
+ { rashi: 0, nakshatra: 1 },
550
405
  );
551
406
  // → { totalScore: 0..36, koots: KootScore[8], cancellations: string[] }
552
- // Koots in canonical order: Varna, Vashya, Tara, Yoni, Graha Maitri, Gana, Bhakoot, Nadi
553
- // (max scores 1, 2, 3, 4, 5, 6, 7, 8 respectively)
554
-
555
- // Mangal Dosha — checks Mars from lagna, Moon, and Venus
556
- const mangal = computeMangalDosha(d1);
557
- // → { afflicted, fromLagna, fromMoon, fromVenus, cancellations }
558
-
559
- // Kaal Sarp Dosha — all 7 visible planets between Rahu and Ketu axis
560
- const ksd = computeKaalSarp(d1);
561
- // → { afflicted, subtype, partial, rahuHouse, ketuHouse }
562
- // subtype is one of: anant, kulik, vasuki, shankhpal, padma, mahapadma,
563
- // takshak, karkotak, shankhachud, ghatak, vishdhar, sheshnag — by Rahu's house.
564
-
565
- // Pitru Dosha — Sun + Rahu/Ketu in same house, OR Sun + Saturn in 9th
566
- const pitru = computePitruDosha(d1);
567
- // → { afflicted, reasons: string[] }
568
- ```
569
-
570
- **Documented limitations** — Mangal Dosha cancellations only cover Mars in own sign
571
- (Aries / Scorpio) or exalted (Capricorn); other classical cancellations (mutual Mangalik,
572
- Mars-Jupiter aspect, Mars-Saturn conjunction) are not applied. Ashtakoot Vashya koot is
573
- simplified to single-vashya per rashi. Pitru Dosha surfaces the two highest-frequency
574
- classical triggers (Sun + node, Sun + Saturn in 9th); the full BPHS catalog of triggers
575
- (debilitated 9th lord, 9th lord in dusthana, etc.) is out of scope.
576
-
577
- ## 16. Aspects & Strength
578
-
579
- **Drishti** (planetary aspects per BPHS Ch. 26) and **Shadbala** (six-fold strength per
580
- BPHS Ch. 27).
581
-
582
- ```typescript
583
- import { computeAspects, computeShadbala } from 'panchang-ts';
584
-
585
- // Aspects — every graha aspects the 7th house from itself; malefics gain
586
- // extra special aspects (Mars 4 + 8, Jupiter 5 + 9, Saturn 3 + 10).
587
- const aspects = computeAspects(d1);
588
- // → { Sun: [houses…], Moon: [...], …, Saturn: [...], Rahu: [...], Ketu: [...] }
589
- //
590
- // Default treats Rahu/Ketu with the 7th-only BPHS-literal rule. Pass
591
- // { nodeAspects: '5-and-9' } to extend nodes with Jupiter-like 5/9 aspects
592
- // (BV Raman / KP convention).
593
-
594
- // Shadbala — 7 visible grahas, 6 components per planet, in Virupas (60 V = 1 Rupa).
595
- const bala = computeShadbala(birth, loc);
596
- // → { Sun: { sthana, dig, kala, chesta, naisargika, drik, total }, Moon: …, … }
597
- //
598
- // Components implemented at the simplified-model level used by ProKerala /
599
- // PyJHora — Uchcha-only Sthana, Dig from directional cusp, Nathonatha + Paksha
600
- // for Kala, retrograde-bucket Chesta, fixed Naisargika rank, weighted Drik.
601
- ```
602
-
603
- Both functions return additive surface — they do **not** modify the
604
- `getDailyPanchang` / birth-chart pipelines. Call them on demand.
605
-
606
- ### Ashtakavarga
607
-
608
- **Ashtakavarga** (BPHS Ch. 66) — per-graha 12-rashi bindu grids
609
- (Bhinnashtaka) and the summed Sarvashtaka grid. Each cell counts how many
610
- of the 8 contributors (the 7 visible grahas + the lagna) donate a benefic
611
- dot to that rashi for the given receiver.
612
-
613
- ```typescript
614
- import { computeRashiChart, computeAshtakavarga } from 'panchang-ts';
615
-
616
- const chart = computeRashiChart(birth, loc);
617
- const av = computeAshtakavarga(chart);
618
-
619
- av.sarvashtaka;
620
- // → 12-cell grid (Mesha … Meena), each 0..56, total 336.
621
-
622
- av.bhinnashtaka.Jupiter;
623
- // → Jupiter's 12-cell grid; total = 56 (chart-invariant per BPHS).
624
- // Other invariant totals: Sun=47, Moon=49, Mars=39, Mercury=54,
625
- // Venus=52, Saturn=39.
626
-
627
- // Opt in to Trikona + Ekadhipatya Sodhana reductions (BPHS Ch. 67):
628
- const reduced = computeAshtakavarga(chart, { reductions: true });
629
- reduced.reduced!.sarvashtaka; // post-reduction Sarvashtaka
630
- reduced.reduced!.bhinnashtaka.Jupiter; // post-reduction Bhinnashtaka
631
- ```
632
-
633
- The BENEFIC_OFFSETS lookup table is the canonical BPHS Ch. 66 form used by
634
- every public Ashtakavarga calculator (ProKerala, AstroSage, PyJHora). Rahu
635
- and Ketu are not Ashtakavarga receivers or contributors in the classical
636
- Parashara scheme.
637
-
638
- ### Yogas (named classical combinations)
639
-
640
- **Yogas** (BPHS Chs. 36–43) — detect ~25 classical combinations from a
641
- natal chart against a fixed declarative catalog. Each entry returns a
642
- `name`, a `type` (one of `mahapurusha | lunar | solar | raja | dhana |
643
- special | cancellation | negative`), and one-or-more `reasons[]` strings
644
- describing why the rule matched.
645
-
646
- ```typescript
647
- import { computeRashiChart, computeNavamsa, computeYogas } from 'panchang-ts';
648
-
649
- const chart = computeRashiChart(birth, loc);
650
- const yogas = computeYogas(chart);
651
- // → [{ name: 'Gajakesari', type: 'lunar',
652
- // reasons: ['Jupiter in 4th from Moon (kendra)'] }, …]
653
-
654
- // Filter by type — restricts evaluation to a subset of the catalog.
655
- const raja = computeYogas(chart, { types: ['raja', 'dhana'] });
656
-
657
- // Vargottama needs the D9 — pass it via options.
658
- const d9 = computeNavamsa(birth, loc);
659
- const all = computeYogas(chart, { navamsa: d9 });
660
- ```
661
-
662
- The catalog covers Pancha Mahapurusha (Ruchaka / Bhadra / Hamsa / Malavya
663
- / Sasha), lunar yogas (Gajakesari, Sunapha, Anapha, Durudhura, Kemadruma),
664
- solar yogas (Budha-Aditya, Veshi, Vasi, Ubhayachari), Raja yogas (generic
665
- kendra/trikona-lord rule, Dharma-Karmadhipati, Vipareeta Raja, Lakshmi),
666
- Dhana yogas (2–11, 5–9, Vasumati), Vargottama, Yogakaraka, Neecha Bhanga,
667
- and Daridra. Yoga names are English/transliterated proper nouns and are
668
- intentionally **not** locale-resolved — `'Gajakesari'` reads the same in
669
- `'en'` and `'hi'`.
670
-
671
- Adding a new yoga is a data-only change in
672
- [src/jyotish/yogasCatalog.ts](src/jyotish/yogasCatalog.ts) — the engine
673
- is a thin loop over the catalog. Each rule receives the chart plus
674
- precomputed `dignity` / `aspects` (and optionally a D9 chart) and returns
675
- a `YogaMatch | null`.
676
-
677
- ### Jaimini Karakas
678
-
679
- **Karakas** (Jaimini *Upadesa Sutras* Ch. 1, Parashara variant) — the 7
680
- Chara Karakas ranked by descending degree-in-rashi of the visible grahas.
681
- Atmakaraka (highest) signifies the Self; Darakaraka (lowest) signifies
682
- spouse.
683
-
684
- ```typescript
685
- import { computeRashiChart, computeJaiminiKarakas } from 'panchang-ts';
686
-
687
- const chart = computeRashiChart(birth, loc);
688
- const k = computeJaiminiKarakas(chart);
689
- k.Atmakaraka; // graha at the highest degree-in-rashi (e.g. 'Saturn')
690
- k.Amatyakaraka; // 2nd-highest
691
- k.Darakaraka; // graha at the lowest degree-in-rashi
692
- ```
693
407
 
694
- The 7 visible grahas are ranked Rahu and Ketu are not included (this is
695
- the **7-Karaka Parashara variant**, not the reversed-Rahu 8-Karaka Jaimini
696
- variant). On the rare exact tie in degree-in-rashi, the canonical order
697
- Sun Moon → Mars → Mercury → Jupiter → Venus → Saturn breaks the tie
698
- (stable sort) the earlier graha wins the more-significant Karaka role.
699
-
700
- Karaka names are English/transliterated proper nouns and intentionally
701
- **not** locale-resolved.
702
-
703
- ### Bhava Bala
704
-
705
- **Bhava Bala** (BPHS Ch. 27) — four-source house strength for the 12 bhavas
706
- of a natal chart, in Virupas (60 V = 1 Rupa). Built on top of `computeShadbala`
707
- — the natural-strength table, drishti weights, and benefic/malefic
708
- classification are all shared.
709
-
710
- ```typescript
711
- import { computeBhavaBala } from 'panchang-ts';
712
-
713
- const bhavaBala = computeBhavaBala(birth, loc);
714
- bhavaBala.houses.length; // 12 (one entry per bhava, index 0 = bhava 1)
715
- bhavaBala.houses[0].total; // 1st-bhava total in Virupas
716
- bhavaBala.houses[9].bhavadhipati; // 10th-bhava lord's Shadbala total
717
- // → each entry: { bhavadhipati, dik, drik, sthana, total }
718
- ```
719
-
720
- `bhavadhipati` is the total Shadbala of the rashi-lord of the bhava cusp;
721
- `dik` is a fixed directional value from a simplified cardinal-anchor table
722
- (cardinal bhavas anchor at 60 / 0 / 15 / 30 V; intermediates linearly
723
- interpolated around the wheel — not the canonical BPHS Ch. 27 numbers);
724
- `drik` is the net aspect strength on the cusp from the 7 visible grahas
725
- (benefics +, malefics −, clamped ≥ 0); `sthana` sums the natural strength
726
- of grahas occupying the bhava (Mercury counted as benefic per BPHS — the
727
- "associated benefic" nuance is intentionally out of scope). Rahu and Ketu
728
- do not contribute. `total` is the arithmetic sum of the four components.
729
-
730
- ### Varshaphala (Tajik annual horoscope)
731
-
732
- **Varshaphala** is the Tajik solar-return chart for a given age of the
733
- native, with the classical analytical layer — Muntha, year lord
734
- (Varsha Pati), and the 27 core Sahams — already computed.
735
-
736
- ```typescript
737
- import { computeVarshaphala } from 'panchang-ts';
738
-
739
- const birth = new Date('1995-08-15T05:30:00Z');
740
- const loc = { latitude: 28.6139, longitude: 77.2090 };
741
-
742
- const varsha = computeVarshaphala(birth, 30, loc); // 30th solar return
743
- varsha.solarReturnInstant; // exact UTC moment Sun returns to natal lon
744
- varsha.varshaLagna.rashi.name; // ascendant rashi at the SR instant
745
- varsha.muntha.rashi; // (natalLagnaRashi + 30) mod 12
746
- varsha.muntha.lord; // rashi-lord of muntha rashi
747
- varsha.muntha.house; // muntha's house from varsha lagna (1..12)
748
- varsha.yearLord; // strongest of 4 candidates by Shadbala
749
- varsha.isDayBirth; // Sun above horizon at SR instant?
750
-
751
- // 27 Sahams (Punya, Vidya, …, Tapas) — each: { longitude, rashi, rashiName, house }
752
- varsha.sahams.Punya.longitude;
753
- varsha.sahams.Punya.house; // Punya's house from varsha lagna
754
- varsha.sahams.Vivaha.rashi; // Vivaha (marriage) Saham rashi
755
- ```
756
-
757
- **Solar-return search.** A Newton-style refinement around `birth +
758
- N × 365.25636 days` finds the UTC instant where sidereal Sun returns
759
- to its natal longitude (within 0.0001° / ~9 arcseconds). Convergence
760
- is 3–5 iterations on cooperative inputs.
761
-
762
- **Year lord (Varsha Pati).** Picked as the planet with the highest
763
- total Shadbala among:
764
- 1. Varsha lagna lord
765
- 2. Muntha lord
766
- 3. Lord of the Sun's rashi at the varsha instant
767
- 4. Triraashi Pati (3-rashi-trine ruler) of the varsha lagna's element,
768
- per the Tajik day/night table (fire→Sun day / Jupiter night;
769
- earth→Venus / Moon; air→Saturn / Mercury; water→Venus / Mars)
770
-
771
- **Sahams scope.** This is the **27-Saham core set** — Punya, Vidya,
772
- Yasas, Mitra, Karma, Vivaha, Putra, Roga, Marana, Rajya, Raja, Bandhu,
773
- Dharma, Gnati, Apamrityu, Bhratri, Matri, Pitri, Sama, Bandhana,
774
- Karyasiddhi, Vyapara, Sastra, Asha, Labha, Susha, Tapas. The extended
775
- 50-Saham list (Mahaprasna, Adhana, Krodha, Kali, …) is deferred to a
776
- later release. Day/night X-Y operand swap follows **Neelakantha**'s
777
- *Tajika Neelakanthi*; 9 Sahams (Punya, Vidya, Karma, Putra, Roga,
778
- Gnati, Bhratri, Matri, Pitri) flip on night birth. Other Tajik
779
- commentators (Hari Hara) define different swap subsets — documented
780
- in [src/jyotish/sahamsTables.ts](src/jyotish/sahamsTables.ts).
781
-
782
- **Sourcing:** Neelakantha *Tajika Neelakanthi* (1587 CE); B.V. Raman
783
- *Annual Horoscope*; Sanjay Rath *Crux of Vedic Astrology* Tajik
784
- appendix; PVR Narasimha Rao *Tajik notes* (Saptarishis Astrology).
785
- Saham names are English/transliterated proper nouns and intentionally
786
- **not** locale-resolved (same convention as Yoga names).
787
-
788
- ### Tithi Pravesha (annual soli-lunar return)
789
-
790
- **Tithi Pravesha** is the South-Indian annual chart cast at the moment
791
- in year-N when *both* (a) the sidereal Sun is in its natal sidereal
792
- sign **and** (b) the Sun-Moon angular separation equals the natal
793
- separation. The tithi is preserved exactly — `praveshTithi` always
794
- equals `natalTithi`.
795
-
796
- ```typescript
797
- import { computeTithiPravesha } from 'panchang-ts';
798
-
799
- const tp = computeTithiPravesha(
800
- new Date('1995-08-15T05:30:00Z'),
801
- 30, // 30th annual cycle
802
- { latitude: 28.6139, longitude: 77.2090 }, // New Delhi
408
+ // Opt-in Bhakoot cancellations need extra natal data:
409
+ // `lagnaRashi` enables same-lagna-lord + same-7th-lord rules;
410
+ // `navamsaRashi` enables the same-Navamsa-lord rule.
411
+ const richer = computeAshtakoot(
412
+ { rashi: 4, nakshatra: 9, lagnaRashi: 7, navamsaRashi: 2 },
413
+ { rashi: 0, nakshatra: 1, lagnaRashi: 1, navamsaRashi: 5 },
803
414
  );
804
- tp.praveshInstant; // exact UTC moment
805
- tp.natalTithi === tp.praveshTithi; // always true
806
- tp.varshaLagna.rashi.name; // ascendant at pravesha
807
- tp.planets; // 9-graha placements
808
- tp.bhava.houses; // 12 cusps under configured house system
809
- ```
810
-
811
- **Algorithm.** The implementation follows **PVR Narasimha Rao's
812
- redefinition** (Saptarishis Astrology) rather than the older calendar-
813
- anniversary heuristic:
814
-
815
- 1. Compute the natal Sun-Moon angular separation `Δ_natal`.
816
- 2. Locate the **solar-return instant** for the requested year (re-uses
817
- the Varshaphala Newton search).
818
- 3. Newton-iterate on the Moon-Sun phase deviation until
819
- `(Moon − Sun) mod 360°` equals `Δ_natal` to better than 0.0001°.
820
- 4. If Sun's rashi at that instant differs from natal Sun's rashi
821
- (closest tithi-match landed across a sign boundary), shift by one
822
- synodic month (~29.5 days) toward the solar-return centre and
823
- re-iterate. Adjacent tithi-matches are 29.53 days apart and the
824
- Sun-in-natal-sign window is 30.4 days wide, so exactly one
825
- neighbour falls in the natal sign for every fixture.
826
-
827
- The pravesha instant can therefore be up to ~30 days before or after
828
- the calendar anniversary depending on where the natal moon was in the
829
- synodic cycle.
830
-
831
- **Sourcing:** Sanjay Rath, *Tithi Pravesha* (srath.com); PVR Narasimha
832
- Rao, *Re-Defining Tithi Pravesha Chart* (Saptarishis Astrology Vol. 8).
833
-
834
- ### Arudha Lagna + 12 Arudha Padas
835
-
836
- The **Arudha pada** of a bhava is the rashi reached by counting from the
837
- bhava's lord the same number of houses as the lord is from the bhava
838
- itself. Each Arudha is the *image / reflection* of its bhava — Bhava 1's
839
- Arudha is **Arudha Lagna (AL)**, the social / public-facing self
840
- (distinct from Lagna, which is the inner / soul-rooted self).
841
-
842
- ```typescript
843
- import { computeRashiChart, computeArudhas } from 'panchang-ts';
844
415
 
845
- const chart = computeRashiChart(
846
- new Date('1995-08-15T05:30:00Z'),
847
- { latitude: 28.6139, longitude: 77.2090 },
416
+ // Pathu Porutham (Tamil/Kerala, 10-fold) — binary pass/fail per koot.
417
+ // Three vetoes (Yoni, Rajju, Vedha) flip `recommended` regardless of count.
418
+ const tp = computePathuPorutham(
419
+ { rashi: 4, nakshatra: 9 },
420
+ { rashi: 0, nakshatra: 1 },
848
421
  );
849
- const arudhas = computeArudhas(chart);
850
- arudhas.length; // 12
851
- arudhas[0]!.bhava; // 1 — this is Arudha Lagna (AL)
852
- arudhas[0]!.arudhaRashi; // 0..11
853
- arudhas[0]!.arudhaRashiName; // localized rashi name
854
- arudhas[0]!.arudhaLord; // rashi-lord of arudha rashi
855
- arudhas[6]!.bhava; // 7 Darapada (spouse pada)
422
+ tp.totalPasses; // 0..10
423
+ tp.recommended; // no veto + ≥5 passes
424
+
425
+ // Doshas
426
+ computeMangalDosha(d1);
427
+ // Mars in 1/2/4/7/8/12 from Lagna, Moon, AND Venus (Drik rule set).
428
+ // Cancellations: Mars in own sign/exalted, conjunct Jup/Moon/Venus,
429
+ // or aspected by Jupiter (5/7/9 sign-aspect).
430
+ // Severity (anshik/purna) is computed pre-cancellation.
431
+
432
+ computeKaalSarp(d1);
433
+ // 12 subtypes by Rahu's house: anant, kulik, vasuki, shankhpal, padma,
434
+ // mahapadma, takshak, karkotak, shankhachud, ghatak, vishdhar, sheshnag.
435
+
436
+ computePitruDosha(d1);
437
+ // 9-rule classical catalog: Sun+Rahu/Ketu/Saturn in 9th; 9th lord
438
+ // debilitated/combust/dusthana; Saturn or nodes in 9th from natal Moon;
439
+ // malefics (Mars/Saturn/Rahu/Ketu) in 5th from natal Moon.
856
440
  ```
857
441
 
858
- **Algorithm.** Per Jaimini *Upadesa Sutras* Ch. 1 (Sanjay Rath
859
- commentary):
860
-
861
- 1. For each bhava `B`, find the rashi the bhava's lord *occupies*.
862
- 2. Compute `D` = inclusive distance from the bhava to its lord (1..12,
863
- so lord-in-own-bhava → D=1).
864
- 3. Standard rule: count another `D` houses from the lord →
865
- `arudha = (lordRashi + D − 1) mod 12`.
866
- 4. **Two exceptions** to avoid the Arudha collapsing onto the bhava
867
- itself or its 7th:
868
- - `D == 1` (lord in own bhava) → Arudha = **10th** from lord
869
- (`lordRashi + 9 mod 12`).
870
- - `D == 7` (lord in 7th from bhava) → Arudha = **4th** from lord
871
- (`lordRashi + 3 mod 12`).
872
-
873
- `arudhaLord` is the rashi-lord of the *Arudha rashi itself* (not the
874
- original bhava's lord) — useful for analysing the pada's significations
875
- directly.
876
-
877
- **Sourcing:** Jaimini, *Upadesa Sutras* Ch. 1; Sanjay Rath, *Jaimini
878
- Maharishi's Upadesa Sutras* (commentary); BPHS Ch. 29.
879
-
880
- ### Special Lagnas (Hora / Ghati / Bhava / Sripati)
442
+ **Limitations.** Ashtakoot Vashya koot uses single-vashya per rashi.
443
+ Bhakoot Parivartana (rashi-lord exchange) cancellation needs per-graha
444
+ position data not carried by the `NatalMoon` shape and is not modelled.
881
445
 
882
- Time-derived sensitive lagnas used in classical timing analysis. Each
883
- advances at a different rate from the sunrise on or before the birth
884
- instant.
446
+ ## Strength & Aspects
885
447
 
886
448
  ```typescript
887
449
  import {
888
- computeHoraLagna, computeGhatiLagna, computeBhavaLagna, computeSripatiLagna,
450
+ computeAspects, computeShadbala, computeBhavaBala, computeAshtakavarga,
889
451
  } from 'panchang-ts';
890
452
 
891
- const birth = new Date('1995-08-15T05:30:00Z');
892
- const loc = { latitude: 28.6139, longitude: 77.2090 };
893
-
894
- const hora = computeHoraLagna(birth, loc); // 1 rashi / 1 hour from sunrise
895
- const ghati = computeGhatiLagna(birth, loc); // 1 rashi / 1 ghatika (24 min)
896
- const bhava = computeBhavaLagna(birth, loc); // 1 rashi / 5 ghatikas (2 hours)
897
- const sripati = computeSripatiLagna(birth, loc); // Sripati cusp 1 = natal lagna
898
- hora.rashi.name; // each returns a `LagnaInfo` with rashi / nakshatra / pada
899
- ```
900
-
901
- | Lagna | Rate | Period |
902
- |-------|------|--------|
903
- | Hora | 30°/hour (1 rashi / 1 hour) | 12 hours |
904
- | Ghati | 75°/hour (1 rashi / 24 min) | 4 h 48 min |
905
- | Bhava | 15°/hour (1 rashi / 2 hours) | 24 hours |
906
- | Sripati | n/a (= natal lagna) | n/a |
907
-
908
- > **Note.** Hora Lagna advances at 30°/hour (1 rashi / hour) per BPHS
909
- > Ch. 4 and *Phaladeepika* Ch. 1 — twice the rate of Bhava Lagna,
910
- > which advances at 15°/hour (1 rashi / 5 ghatikas). The two are
911
- > numerically distinct timing anchors, not interchangeable. Sripati
912
- > Lagna currently returns the natal ascendant (Sripati cusp-1 by
913
- > construction); the bhava-midpoint form is a future extension.
914
-
915
- **Sourcing:** BPHS Ch. 4; *Phaladeepika* Ch. 1; Sripati, *Sripati Paddhati*.
916
-
917
- ### Upagrahas (sub-grahas as positions)
918
-
919
- The 7 upagrahas are sensitive points used in Vedic and Tajik analysis.
920
- The library returns each one as a **longitude** + rashi + house — the
921
- position-form, distinct from Phase-2 Gulika *Kalam* (the time-form).
453
+ // Drishti every graha aspects the 7th; malefics gain extras
454
+ // (Mars 4+8, Jupiter 5+9, Saturn 3+10). Node aspect mode is configurable:
455
+ const aspects = computeAspects(d1); // BPHS 7th-only on nodes
456
+ const aspExt = computeAspects(d1, { nodeAspects: '5-and-9' }); // KP/BV Raman extension
922
457
 
923
- ```typescript
924
- import { computeUpagrahas } from 'panchang-ts';
925
-
926
- const u = computeUpagrahas(
927
- new Date('1995-08-15T05:30:00Z'),
928
- { latitude: 28.6139, longitude: 77.2090 },
929
- );
930
- u.gulika.longitude; // sidereal degrees
931
- u.gulika.rashi; // 0..11
932
- u.gulika.rashiName; // localized
933
- u.gulika.house; // whole-sign from natal lagna
934
- u.mandi.longitude; // Saturn-segment midpoint
935
- u.dhuma.longitude; // Sun + 133°20'
936
- u.upaketu.longitude; // Sun + ~150° (after the chain)
937
- ```
938
-
939
- **Gulika & Mandi.** Day birth: divide sunrise → sunset into 8 equal
940
- segments; the day-lord rotation Sun → Moon → Mars → Mercury → Jupiter
941
- → Venus → Saturn places Saturn at one segment per weekday. Gulika is
942
- the **rising ascendant at the *start*** of Saturn's segment; Mandi is
943
- the **rising ascendant at the *midpoint***. Night birth: same procedure
944
- on the sunset → next-sunrise window, with the rotation starting from
945
- the planet 5 weekdays from the day-lord (Phaladeepika Ch. 5).
946
-
947
- **Sun-derived upagrahas.** Fixed offsets from sidereal Sun:
948
-
949
- | Upagraha | Formula |
950
- |----------|---------|
951
- | Dhuma | `Sun + 133°20'` |
952
- | Vyatipata | `360° − Dhuma` |
953
- | Parivesha | `Vyatipata + 180°` |
954
- | Indrachapa | `360° − Parivesha` |
955
- | Upaketu | `Indrachapa + 16°40'` |
956
-
957
- `Dhuma + Vyatipata ≡ 360°` and `Parivesha + Indrachapa ≡ 360°` are
958
- structural invariants that hold for every chart.
959
-
960
- **Sourcing:** BPHS Ch. 5; Sanjay Rath, *Brihat Nakshatra* (upagraha
961
- section); Phaladeepika Ch. 5.
962
-
963
- ### Argala (Jaimini intervention)
458
+ // Shadbala — 7 visible grahas, 6 components, in Virupas (60 V = 1 Rupa).
459
+ // Sthana = Uchcha + Saptavargaja (D1/D2/D3/D7/D9/D12/D30 dignity sum)
460
+ // + Ojha-Yugma (rashi+navamsa parity) + Drekkana (gender decanate).
461
+ // Range [0, 420 V]. Dig is directional cusp; Kala = Nathonatha + Paksha;
462
+ // Chesta is retrograde-bucket; Naisargika is fixed rank; Drik is weighted aspects.
463
+ const bala = computeShadbala(birth, loc);
964
464
 
965
- Per-bhava arc-influence rules from Jaimini *Upadesa Sutras* Ch. 1 and
966
- BPHS Ch. 51. Planets in the 2nd, 4th, or 11th from a bhava form
967
- **Argala** (intervention / help); planets in the 3rd, 10th, or 12th
968
- form **Virodhargala** (counter-intervention).
465
+ // Bhava Bala — 12-bhava strength built on top of Shadbala.
466
+ // Per-bhava: { bhavadhipati, dik, drik, sthana, total }.
467
+ const bhavaBala = computeBhavaBala(birth, loc);
969
468
 
970
- ```typescript
971
- import { computeRashiChart, computeArgala } from 'panchang-ts';
469
+ // Ashtakavarga — 12-rashi bindu grids (BPHS Ch. 66).
470
+ const av = computeAshtakavarga(d1);
471
+ av.sarvashtaka; // 12 cells, each 0..56, total 336
472
+ av.bhinnashtaka.Jupiter; // 12-cell grid; Jupiter total = 56 (chart-invariant)
473
+ // Other invariants: Sun=47, Moon=49, Mars=39, Mercury=54, Venus=52, Saturn=39.
972
474
 
973
- const chart = computeRashiChart(
974
- new Date('1995-08-15T05:30:00Z'),
975
- { latitude: 28.6139, longitude: 77.2090 },
976
- );
977
- const argala = computeArgala(chart);
978
- argala[0]!.bhava; // 1
979
- argala[0]!.argala; // PlanetPlacement[] in 2nd / 4th / 11th from lagna
980
- argala[0]!.virodhargala; // PlanetPlacement[] in 3rd / 10th / 12th from lagna
475
+ // Trikona + Ekadhipatya Sodhana reductions (BPHS Ch. 67):
476
+ const avR = computeAshtakavarga(d1, { reductions: true });
477
+ avR.reduced!.sarvashtaka;
981
478
  ```
982
479
 
983
- **Structural invariant.** Each planet contributes to **exactly 6 of
984
- the 12 bhavas** — 3 Argala + 3 Virodhargala. The 2/4/11 and 3/10/12
985
- offsets are mutually exclusive, so a planet's house position
986
- deterministically partitions the bhava wheel into a 6-cell influence
987
- set.
988
-
989
- All 9 grahas (Sun..Saturn + Rahu + Ketu) participate. Benefic / malefic
990
- distinction is not applied — the caller can filter the returned lists
991
- by graha if their tradition requires it. The 5th-from-bhava ("primary
992
- Argala") and 9th ("primary Virodhargala") are not included in this
993
- simplified BPHS Ch. 51 form.
994
-
995
- **Sourcing:** Jaimini, *Upadesa Sutras* Ch. 1; BPHS Ch. 51.
480
+ Rahu and Ketu are not Ashtakavarga receivers or contributors (classical
481
+ Parashara scheme).
996
482
 
997
- ### Pathu Porutham (Tamil 10-fold marriage matching)
998
-
999
- Tamil/Kerala marriage compatibility test, scored across **10 koots**.
1000
- Used instead of Ashtakoot in Tamil Nadu, Kerala, and parts of Karnataka.
1001
- Each koot is binary-scored (pass / fail) per the AstroVed / Drik Tamil
1002
- convention; the aggregate score is the count of passing koots in 0..10.
1003
- Three koots (Yoni, Rajju, Vedha) are **strong vetoes** — if any fails,
1004
- `recommended` flips to false regardless of the count.
483
+ ## Yogas & Karakas
1005
484
 
1006
485
  ```typescript
1007
- import { computePathuPorutham } from 'panchang-ts';
486
+ import { computeYogas, computeJaiminiKarakas } from 'panchang-ts';
487
+
488
+ // ~25 named yogas — Pancha Mahapurusha (Ruchaka/Bhadra/Hamsa/Malavya/Sasha),
489
+ // lunar (Gajakesari, Sunapha, Anapha, Durudhura, Kemadruma), solar
490
+ // (Budha-Aditya, Veshi, Vasi, Ubhayachari), Raja (kendra/trikona-lord,
491
+ // Dharma-Karmadhipati, Vipareeta, Lakshmi), Dhana (2-11, 5-9, Vasumati),
492
+ // Vargottama, Yogakaraka, Neecha Bhanga, Daridra.
493
+ const yogas = computeYogas(d1);
494
+ // → [{ name, type, reasons[], bhanga?: { applies, reasons[] } }, …]
495
+
496
+ // Optional cancellation annotations: 5 Pancha Mahapurusha + Gajakesari
497
+ // surface `bhanga` (Sun/Moon conjunct or Jupiter combust/debilitated).
498
+ // Neecha Bhanga: dispositor in kendra from Lagna OR Moon; lord-of-
499
+ // exaltation-rashi in kendra from Lagna or Moon; mutual exchange;
500
+ // dispositor aspecting the debilitated planet.
501
+
502
+ // Filter by type / pass D9 for Vargottama:
503
+ const d9 = computeNavamsa(birth, loc);
504
+ const all = computeYogas(d1, { types: ['raja','dhana'], navamsa: d9 });
1008
505
 
1009
- const result = computePathuPorutham(
1010
- { rashi: 4, nakshatra: 9 }, // Boy: Magha / Leo
1011
- { rashi: 0, nakshatra: 1 }, // Girl: Bharani / Aries
1012
- );
1013
- result.totalPasses; // 0..10
1014
- result.recommended; // boolean (no veto + ≥5 passes)
1015
- result.poruthams[4]!.name; // 'Yoni' (a veto-level koot)
1016
- result.poruthams[8]!.veto; // true if Rajju fails
506
+ // Jaimini Karakas — Atmakaraka (highest degree-in-rashi) … Darakaraka (lowest).
507
+ const k7 = computeJaiminiKarakas(d1); // 7-graha Parashara default
508
+ const k8 = computeJaiminiKarakas(d1, { variant: '8-jaimini' }); // adds Rahu (degree reversed),
509
+ // inserts Pitrukaraka at 5th
1017
510
  ```
1018
511
 
1019
- **Koot catalog:**
1020
-
1021
- | Koot | Tests | Veto |
1022
- |----------------|-----------------------------------------------------|:----:|
1023
- | Dina | Boy→Girl nakshatra distance mod 9 ∈ {1, 3, 5, 7} | no |
1024
- | Gana | Reuses Phase 29 NAKSHATRA_GANA — Manushya↔Rakshasa fails | no |
1025
- | Mahendra | Distance ∈ {4, 7, 10, 13, 16, 19, 22, 25} | no |
1026
- | Sthree Deergha | Boy→Girl nakshatra distance > 9 | no |
1027
- | Yoni | Reuses Phase 29 NAKSHATRA_YONI / YONI_SCORE | yes |
1028
- | Rashi | Distance not in doshic set ({2,12}, {6,8}) | no |
1029
- | Rashyathipathi | Rashi-lord friendship (Naisargika Maitri) | no |
1030
- | Vasya | Reuses Phase 29 RASHI_VASHYA / VASHYA_SCORE | no |
1031
- | Rajju | Same Rajju (Pada/Kati/Nabhi/Kantha/Sira) fails | yes |
1032
- | Vedha | Same Vedha pair fails | yes |
1033
-
1034
- Pathu Porutham and Ashtakoot consume the same `NatalMoon` shape
1035
- (`{ rashi, nakshatra }`) — call both side-by-side to surface both
1036
- North-Indian and South-Indian compatibility views.
1037
-
1038
- **Sources.** *Jathaka Tatva* (Tamil); AstroVed.com 10-Porutham reference;
1039
- ProKerala's free Pathu Porutham implementation; drikpanchang.com Tamil
1040
- porutham panel.
1041
-
1042
- ### Narayan Dasha (Jaimini sign-dasha with padi direction)
1043
-
1044
- Jaimini sign-based dasha that incorporates the **vishama-pada / sama-pada**
1045
- parity rule per Sanjay Rath's *Narayana Dasa* (Sagar Publications). Cycle
1046
- direction depends on which parity-class the lagna's rashi falls into:
512
+ Yoga and Karaka names are English/transliterated proper nouns and intentionally
513
+ **not** locale-resolved.
1047
514
 
1048
- - **Vishama-pada** lagna in {Aries, Taurus, Gemini, Libra, Scorpio,
1049
- Sagittarius} → cycle proceeds **forward** (zodiacal).
1050
- - **Sama-pada** lagna in {Cancer, Leo, Virgo, Capricorn, Aquarius,
1051
- Pisces} → cycle proceeds **backward** (anti-zodiacal).
515
+ ## Annual & Sensitive Layers
1052
516
 
1053
517
  ```typescript
1054
- import { computeNarayanDasha } from 'panchang-ts';
518
+ import {
519
+ computeVarshaphala, computeTithiPravesha, computeArudhas,
520
+ computeHoraLagna, computeGhatiLagna, computeBhavaLagna, computeSripatiLagna,
521
+ computeUpagrahas, computeArgala,
522
+ } from 'panchang-ts';
1055
523
 
1056
- const narayan = computeNarayanDasha(
1057
- new Date('1995-08-15T05:30:00Z'),
1058
- { latitude: 28.6139, longitude: 77.2090 },
1059
- );
1060
- narayan.direction; // 'forward' | 'backward'
1061
- narayan.startingRashi; // lagna rashi (0..11)
1062
- narayan.mahaDashas[0]!.rashi; // same as startingRashi
1063
- narayan.mahaDashas[0]!.lord; // sign-lord of starting rashi
1064
- narayan.mahaDashas[0]!.years; // 7, 8, or 9 per modality
524
+ // Varshaphala Tajik annual chart for the Nth solar return.
525
+ const v = computeVarshaphala(birth, 30, loc);
526
+ v.solarReturnInstant;
527
+ v.varshaLagna.rashi.name;
528
+ v.muntha.rashi; v.muntha.house; // muntha = (natalLagnaRashi + 30) mod 12
529
+ v.yearLord; // strongest of 4 candidates by Shadbala
530
+ v.sahams.Punya.house;
531
+ v.sahams.Vivaha.rashi;
532
+ // 27 Sahams: Punya, Vidya, Yasas, Mitra, Karma, Vivaha, Putra, Roga, Marana,
533
+ // Rajya, Raja, Bandhu, Dharma, Gnati, Apamrityu, Bhratri, Matri, Pitri, Sama,
534
+ // Bandhana, Karyasiddhi, Vyapara, Sastra, Asha, Labha, Susha, Tapas.
535
+
536
+ // Tithi Pravesha — annual chart cast when Sun is in natal sidereal sign AND
537
+ // Sun-Moon separation equals natal separation. Preserves natal tithi exactly.
538
+ const tp = computeTithiPravesha(birth, 30, loc);
539
+ tp.natalTithi === tp.praveshTithi; // always true
540
+
541
+ // Arudha padas — image/reflection of each bhava. Arudha[0] = Arudha Lagna (AL).
542
+ const a = computeArudhas(d1);
543
+ a[0]!.bhava; // 1 — AL
544
+ a[0]!.arudhaRashi; // 0..11
545
+ a[6]!.bhava; // 7 — Darapada (spouse pada)
546
+
547
+ // Special lagnas — time-derived sensitive points from sunrise on/before birth.
548
+ computeHoraLagna(birth, loc); // 30°/hour (1 rashi/hour)
549
+ computeGhatiLagna(birth, loc); // 75°/hour (1 rashi/24 min)
550
+ computeBhavaLagna(birth, loc); // 15°/hour (1 rashi/2 hours)
551
+ computeSripatiLagna(birth, loc); // = natal lagna (cusp 1)
552
+
553
+ // Sripati cusps 2–12 (opt-in) — 4 angular cusps + trisected intermediates.
554
+ // Defined at every latitude (unlike Placidus).
555
+ const sripati = computeSripatiLagna(birth, loc, 'lahiri', 'en', { includeCusps: true });
556
+ sripati.cusps; // number[12] of bhava madhyas; cusps[0/3/6/9] = ASC/IC/DSC/MC
557
+
558
+ // Upagrahas — Gulika, Mandi (rising-asc at Saturn segment start/midpoint),
559
+ // plus Sun-derived Dhuma, Vyatipata, Parivesha, Indrachapa, Upaketu.
560
+ const u = computeUpagrahas(birth, loc);
561
+ u.gulika.longitude; u.gulika.rashi; u.gulika.house;
562
+
563
+ // Argala (Jaimini) — planets in 2/4/11 from a bhava form Argala (intervention);
564
+ // 3/10/12 form Virodhargala (counter). Each planet hits exactly 6 of 12 bhavas.
565
+ const arg = computeArgala(d1);
566
+ arg[0]!.argala; arg[0]!.virodhargala;
567
+
568
+ // Trikonargala (5/9 trine, opt-in) — Ketu reversal: 5th-from → virodhaka,
569
+ // 9th-from → source.
570
+ const argT = computeArgala(d1, { includeTrikonargala: true });
571
+ argT[0]!.trikona!.sources;
572
+ argT[0]!.trikona!.virodhakas;
1065
573
  ```
1066
574
 
1067
- Years per rashi follow the same Movable 9 / Fixed 8 / Dual 7 scheme as
1068
- Chara Dasha. The Mahadasha sequence covers ~96 years (sum of all 12
1069
- rashi durations). Lord assignment is the rashi's natural lord (Mars for
1070
- Aries / Scorpio, Venus for Taurus / Libra, etc.). Antardasha breakdown
1071
- is not exposed — the Mahadasha alone is the dominant Jaimini timing
1072
- layer for Narayan analysis.
1073
-
1074
- **Difference from Chara Dasha** (already shipped): Chara always advances
1075
- forward from lagna; Narayan applies the parity-based direction. For
1076
- vishama-pada lagnas the two systems' Mahadasha sequence is identical.
1077
-
1078
- **Sources.** Sanjay Rath, *Narayana Dasa* (Sagar Publications);
1079
- *Jaimini Upadesa Sutras* Ch. 2.
1080
-
1081
- ### KP sub-lord layer
1082
-
1083
- K.S. Krishnamurti's KP-Paddhati uses a 9-fold subdivision of each
1084
- nakshatra (243 sub-divisions across the zodiac), proportional to the
1085
- Vimshottari dasha years of the 9 planets. The sub-lord at any longitude
1086
- is one of the 9 KP planets (Sun..Saturn + Rahu + Ketu).
575
+ ## KP & Prashna
1087
576
 
1088
577
  ```typescript
1089
578
  import {
1090
579
  computeKpSubLord, computeKpCuspalSubLords, computeKpSignificators,
1091
- computeRashiChart,
580
+ computePrashnaChart,
1092
581
  } from 'panchang-ts';
1093
582
 
1094
- // 1. Sub-lord at any sidereal longitude.
1095
- const info = computeKpSubLord(45.5); // 15°30' Taurus
1096
- info.signLord; // 'Venus' (rashi lord of Taurus)
1097
- info.starLord; // 'Moon' (nakshatra lord of Rohini)
1098
- info.subLord; // KP sub-lord — depends on degree
1099
-
1100
- // 2. Cuspal sub-lords for the 12 Placidus-KP cusps.
1101
- const cusps = computeKpCuspalSubLords(
1102
- new Date('1995-08-15T05:30:00Z'),
1103
- { latitude: 28.6139, longitude: 77.2090 },
1104
- );
1105
- cusps.cusps[0]!.subLord; // sub-lord of cusp 1 (ascendant)
1106
- cusps.cusps[6]!.subLord; // sub-lord of cusp 7 (descendant)
1107
-
1108
- // 3. Significators — for each planet, the houses it signifies via the
1109
- // 4-fold KP rule (occupant + star-lord-occupant + owner + star-lord-owner).
1110
- const chart = computeRashiChart(birth, location);
1111
- const sig = computeKpSignificators(chart);
1112
- sig.byPlanet.Sun; // [houses Sun signifies]
1113
- sig.byHouse[10]; // [planets that signify the 10th house]
1114
- ```
1115
-
1116
- `computeKpCuspalSubLords` always uses the Placidus-KP house system —
1117
- the cuspal scheme KP analysis is built on. Whole-sign and equal-house
1118
- cusps fall on rashi boundaries by construction and lose the cuspal-sub-
1119
- lord granularity that drives KP timing analysis.
1120
-
1121
- **Sources.** K.S. Krishnamurti, *Krishnamurti Paddhati* (5 vols);
1122
- KP Astrology online references.
1123
-
1124
- ### Prashna foundation (horary chart)
1125
-
1126
- Cast a chart for the precise moment a question is asked, with the
1127
- querent's location as the geographic anchor. Prashna analysis proceeds
1128
- on the resulting chart in the same way as a natal chart — houses,
1129
- planets, dignity, dashas all apply to the question being asked.
1130
-
1131
- ```typescript
1132
- import { computePrashnaChart, computeKpCuspalSubLords } from 'panchang-ts';
1133
-
1134
- // Question asked at a specific UTC moment from Mumbai.
1135
- const chart = computePrashnaChart(
1136
- new Date('2026-05-09T14:30:00Z'),
1137
- { latitude: 19.0760, longitude: 72.8777 },
1138
- );
1139
- chart.lagna.rashi.name; // ascendant of the prashna
1140
- chart.bhava.system; // 'placidus-kp' (KP horary anchor) by default
1141
- chart.planets[1]!.house; // Moon's house — primary mind significator
1142
-
1143
- // KP cuspal sub-lord analysis on the prashna cusps:
1144
- const cusps = computeKpCuspalSubLords(
583
+ // KP sub-lord at any sidereal longitude (243 sub-divisions across the zodiac,
584
+ // proportional to Vimshottari years).
585
+ const info = computeKpSubLord(45.5); // 15°30' Taurus
586
+ info.signLord; // 'Venus'
587
+ info.starLord; // 'Moon'
588
+ info.subLord;
589
+
590
+ // Cuspal sub-lords (always Placidus-KP — KP's anchor scheme).
591
+ const cusps = computeKpCuspalSubLords(birth, loc);
592
+ cusps.cusps[0]!.subLord; // ascendant
593
+ cusps.cusps[6]!.subLord; // descendant
594
+
595
+ // Significators for each planet, the houses it signifies via the 4-fold KP rule
596
+ // (occupant + star-lord-occupant + owner + star-lord-owner).
597
+ const sig = computeKpSignificators(d1);
598
+ sig.byPlanet.Sun;
599
+ sig.byHouse[10];
600
+
601
+ // Prashna (horary) chart — cast at question moment from querent's location.
602
+ const pchart = computePrashnaChart(
1145
603
  new Date('2026-05-09T14:30:00Z'),
1146
604
  { latitude: 19.0760, longitude: 72.8777 },
1147
605
  );
606
+ pchart.lagna.rashi.name;
607
+ pchart.bhava.system; // 'placidus-kp' by default (KP horary anchor)
608
+ pchart.planets[1]!.house; // Moon — primary mind significator
1148
609
  ```
1149
610
 
1150
- The returned shape is identical to a natal `BirthChart`. The dedicated
1151
- function is intent-named so callers' analysis context is explicit at
1152
- the call site, and so future Prashna-specific layers (Ruling Planets,
1153
- horary numbers 1..249, significator-driven event timing) can land
1154
- additively in this module.
1155
-
1156
- **Default house system.** Defaults to `'placidus-kp'` — KP horary's
1157
- standard cuspal scheme. Pass `{ houseSystem: 'whole-sign' }` for
1158
- traditional Vedic Prashna.
611
+ Same return shape as a natal `BirthChart`. Pass `{ houseSystem: 'whole-sign' }`
612
+ to `computePrashnaChart` for traditional Vedic Prashna.
1159
613
 
1160
- **Out of scope** (deferred to a later phase):
1161
- - Ruling Planets (5-fold lord set used to refine timing in KP horary).
1162
- - Horary number mapping (KP 1..249 number → cusp sub-lord table).
1163
- - Significator-driven event timing (dasha-walk against significator sets).
1164
-
1165
- **Sources.** B. Suryanarain Rao, *Prasna Marga*; K.S. Krishnamurti,
1166
- *Horary Astrology* (KP Reader VI); Sanjay Rath, *Horary Astrology*
1167
- (srath.com).
1168
-
1169
- ## 17. Dasha Systems
1170
-
1171
- Five classical dasha systems are exposed:
1172
-
1173
- ```typescript
1174
- import {
1175
- computeVimshottariDashaFromBirth, computeVimshottariPratyantar,
1176
- computeAshtottariDasha, computeYoginiDasha, computeCharaDasha,
1177
- computeNarayanDasha,
1178
- } from 'panchang-ts';
1179
-
1180
- const birth = new Date('1995-08-15T05:30:00Z');
1181
- const loc = { latitude: 28.6139, longitude: 77.2090 };
1182
-
1183
- // 1. Vimshottari (120-year, 9-lord) — already in v1; pratyantar (3-level) added in v3
1184
- const vim = computeVimshottariDashaFromBirth(birth);
1185
- const pratyantars = computeVimshottariPratyantar(vim.mahaDashas[0].antarDashas[0]);
1186
-
1187
- // 2. Ashtottari (108-year, 8-lord, no Ketu) — used when Moon in Krishna Paksha
1188
- const moonLon = /* sidereal Moon longitude */ 145.7;
1189
- const ash = computeAshtottariDasha(birth, moonLon);
1190
- // → mahaDashas[0..7], lord cycle Sun(6)→Moon(15)→Mars(8)→Mercury(17)→
1191
- // Saturn(10)→Jupiter(19)→Rahu(12)→Venus(21)
1192
-
1193
- // 3. Yogini (36-year, 8 yoginis with planetary lords)
1194
- const yog = computeYoginiDasha(birth, moonLon);
1195
- yog.mahaDashas[0].yogini; // 'Dhanya' (Magha → nakshatra 10, 10 % 8 = 2)
1196
- yog.mahaDashas[0].lord; // 'Jupiter' (Dhanya's planet)
1197
-
1198
- // 4. Chara (Jaimini, sign-based, 9-8-7 years per modality, forward only)
1199
- const cha = computeCharaDasha(birth, loc);
1200
- cha.mahaDashas[0].rashi; // lagna's rashi
1201
- cha.mahaDashas[0].lord; // sign-lord planet
1202
- cha.mahaDashas[0].years; // 9 (movable) | 8 (fixed) | 7 (dual)
1203
-
1204
- // 5. Narayan (Jaimini, sign-based, parity-based direction per Sanjay Rath)
1205
- const nar = computeNarayanDasha(birth, loc);
1206
- nar.direction; // 'forward' (vishama-pada) | 'backward' (sama-pada)
1207
- nar.startingRashi; // lagna rashi
1208
- nar.mahaDashas[0].rashi; // == startingRashi
1209
- nar.mahaDashas[1].rashi; // depends on direction
1210
- ```
1211
-
1212
- **Sourcing:** Vimshottari per Parashara (BPHS Ch. 51); Ashtottari per Satya Acharya;
1213
- Yogini per Sanjay Rath (1999) / Charak; Chara per Jaimini Sutras Ch. 1 (9-8-7 years
1214
- variant); Narayan per Sanjay Rath, *Narayana Dasa* (Sagar Publications) — vishama /
1215
- sama-pada parity rule. Documented limitations: Chara uses the forward zodiacal
1216
- direction unconditionally (Sundar / Achyutananda variant); the parity-based variant
1217
- ships separately as `computeNarayanDasha`.
1218
-
1219
- ## 18. Muhurta Engine
1220
-
1221
- A configurable rule + scoring engine for picking auspicious dates. Ships with **13 stock
1222
- rules** (vivah, griha pravesh, namakarana, vidyarambh, vahan kharidi, annaprashan,
1223
- mundan, upanayanam, karnavedha, aksharabhyasam, seemantham, shop opening, travel
1224
- start). Each rule is a pure data declaration — write your own without touching the
1225
- engine.
614
+ ## Muhurta Engine
1226
615
 
1227
616
  ```typescript
1228
617
  import { scoreMuhurta, findAuspiciousDates, vivahRule } from 'panchang-ts';
1229
618
 
1230
- // Score a single date
1231
619
  const r = scoreMuhurta(new Date('2026-05-12'), DELHI, vivahRule, { timezone: 330 });
1232
620
  // → { date, score: 0..100, passes: boolean, reasons: string[] }
1233
621
 
1234
- // Find all auspicious dates in a range, sorted by score descending
1235
622
  const dates = findAuspiciousDates(
1236
623
  vivahRule,
1237
624
  new Date('2026-05-01'),
1238
625
  new Date('2026-05-31'),
1239
626
  DELHI,
1240
627
  { timezone: 330 },
1241
- );
1242
- // → MuhurtaDay[] with full panchang attached for each result
628
+ ); // MuhurtaDay[] sorted by score desc; full panchang attached
1243
629
 
1244
- // Custom rule
630
+ // Custom rule (pure data, no engine code needed)
1245
631
  const myRule: MuhurtaRule = {
1246
632
  occasion: 'launch_party',
1247
- auspiciousVaras: [3, 4, 5], // Wed/Thu/Fri
1248
- auspiciousNakshatras: [11, 12, 21], // Uttara Phalguni / Hasta / Shravana
633
+ auspiciousVaras: [3, 4, 5],
634
+ auspiciousNakshatras: [11, 12, 21],
1249
635
  excludeBhadra: true,
1250
636
  excludeEkadashi: true,
1251
637
  excludeAdhikaMasa: true,
1252
638
  };
1253
639
  ```
1254
640
 
1255
- Scoring model: starts at 50 (neutral), +10 per matching auspicious axis (tithi,
1256
- nakshatra, vara, yoga), -15 per matching inauspicious axis, hard exclusions
1257
- (`excludeBhadra` / `excludeEkadashi` / `excludeEclipse` / `excludeAdhikaMasa` /
1258
- `excludeGandaMula` / `excludePanchaka` / `requirePaksha` mismatch) zero the score.
1259
- Special yogas — Amrit Siddhi, Sarvartha Siddhi, Ravi Pushya, Guru Pushya — add +5;
1260
- Jwalamukhi yoga subtracts -10. Final score clamped to 0..100; `passes: true` when
1261
- score ≥ 50.
641
+ 13 stock rules: vivah, griha pravesh, namakarana, vidyarambh, vahan kharidi,
642
+ annaprashan, mundan, upanayanam, karnavedha, aksharabhyasam, seemantham, shop
643
+ opening, travel start.
1262
644
 
1263
- ## 19. Calendar Conversion
645
+ Scoring: starts at 50; +10 per matching auspicious axis (tithi / nakshatra /
646
+ vara / yoga), -15 per inauspicious axis, hard exclusions zero the score.
647
+ Special yogas (Amrit Siddhi, Sarvartha Siddhi, Ravi/Guru Pushya) add +5;
648
+ Jwalamukhi subtracts -10. Clamped 0..100; `passes: true` when score ≥ 50.
1264
649
 
1265
- Gregorian↔Hindu lunar coordinates, Kali Yuga year, regional Hindu New Year, and
1266
- yearly listings of Ekadashis / Sankrantis / festivals / eclipses.
650
+ ## Calendar Conversion
1267
651
 
1268
652
  ```typescript
1269
653
  import {
@@ -1273,323 +657,136 @@ import {
1273
657
  getFestivalsInRange, getUpcomingEclipses,
1274
658
  } from 'panchang-ts';
1275
659
 
1276
- // Gregorian → Hindu coordinates at sunrise
660
+ // Gregorian → Hindu coords at sunrise
1277
661
  const h = convertGregorianToHindu(new Date('2026-04-15'), DELHI, { timezone: 330 });
1278
- // → { tithiName, tithi (1..30), pakshaTithi (1..15), paksha,
1279
- // masaName, masaIndex, isAdhika, vikramSamvat, shakaSamvat,
1280
- // varaName, varaIndex }
1281
662
 
1282
- // Hindu → Gregorian: which Gregorian dates correspond to a (samvat, masa, paksha, tithi)?
663
+ // Hindu → Gregorian
1283
664
  const dates = convertHinduToGregorian(
1284
665
  { vikramSamvat: 2083, masaIndex: 0, paksha: 'shukla', pakshaTithi: 9 },
1285
666
  DELHI, { timezone: 330 },
1286
- );
1287
- // dates[0] → Rama Navami in VS 2083
667
+ ); // → Rama Navami in VS 2083
1288
668
 
1289
- getKaliYugaYear(new Date('2026-04-01')); // 5127
1290
- getHinduNewYear(2026, 'tamil-nadu', DELHI, { timezone: 330 }); // Puthandu
669
+ getKaliYugaYear(new Date('2026-04-01')); // 5127
670
+ getHinduNewYear(2026, 'tamil-nadu', DELHI, { timezone: 330 }); // Puthandu
1291
671
 
1292
- // Yearly listings
1293
- getEkadashiDatesForYear(2026, DELHI, { timezone: 330 }); // ~24 Date[]
1294
- getSankrantisForYear(2026, DELHI, { timezone: 330 }); // 12 SankrantiEvent[]
1295
- getFestivalsInRange(start, end, DELHI, { timezone: 330 }); // FestivalDay[]
1296
- getUpcomingEclipses(new Date(), DELHI, 5); // 5 EclipseInfo[]
672
+ getEkadashiDatesForYear(2026, DELHI, { timezone: 330 }); // ~24 Date[]
673
+ getSankrantisForYear(2026, DELHI, { timezone: 330 }); // 12 SankrantiEvent[]
674
+ getFestivalsInRange(start, end, DELHI, { timezone: 330 }); // FestivalDay[]
675
+ getUpcomingEclipses(new Date(), DELHI, 5);
1297
676
  ```
1298
677
 
1299
- `getHinduNewYear` is region-aware: Tamil Nadu / Kerala / Punjab / Bengal / Assam use
1300
- the **solar** (Mesha Sankranti) anchor; everywhere else uses **Chaitra Shukla Pratipada**
1301
- (Ugadi / Gudi Padwa / Cheti Chand). When the Pratipada is a kshaya tithi (e.g. Ugadi
1302
- 2026), the function falls back to the Amanta-Chaitra-masa boundary.
1303
-
1304
- ## 20. Localization
678
+ `getHinduNewYear` is region-aware: Tamil Nadu / Kerala / Punjab / Bengal / Assam
679
+ use the **solar** (Mesha Sankranti) anchor; elsewhere uses **Chaitra Shukla
680
+ Pratipada** (Ugadi / Gudi Padwa / Cheti Chand). When Pratipada is a kshaya
681
+ tithi (e.g. Ugadi 2026), falls back to the Amanta-Chaitra-masa boundary.
1305
682
 
1306
- All returned display names respect the `language` option. **English** and **Hindi
1307
- (Devanagari)** are supported.
683
+ ## Localization & Configuration
1308
684
 
1309
685
  ```typescript
1310
686
  const hi = getDailyPanchang(date, loc, { timezone: 330, language: 'hi' })!;
687
+ hi.tithis[0].name; // "कृष्ण चतुर्दशी"
688
+ hi.vara.name; // "मंगलवार"
689
+ hi.chandramasa.name; // "माघ"
690
+ hi.choghadiya.day[0].name; // "अमृत"
691
+ hi.vara.englishName; // "Tuesday" — englishName always English
1311
692
 
1312
- console.log(hi.tithis[0].name); // "कृष्ण चतुर्दशी"
1313
- console.log(hi.vara.name); // "मंगलवार"
1314
- console.log(hi.chandramasa.name); // "माघ"
1315
- console.log(hi.choghadiya.day[0].name); // "अमृत"
1316
-
1317
- // englishName is always English on Vara / Tarabala / Chandra Balam
1318
- console.log(hi.vara.englishName); // "Tuesday"
1319
- ```
1320
-
1321
- ## 21. Configuration
1322
-
1323
- ```typescript
693
+ // All options:
1324
694
  const r = getDailyPanchang(date, loc, {
1325
- timezone: 330, // number (UTC offset in min) or IANA string
1326
- ayanamsa: 'lahiri', // 'lahiri' | 'raman' | 'krishnamurti'
1327
- // | 'true-chitra' | 'thirukanitham'
1328
- language: 'en', // 'en' | 'hi'
1329
- masaSystem: 'purnimanta', // 'purnimanta' | 'amanta'
695
+ timezone: 330, // number (UTC offset min) or IANA string
696
+ ayanamsa: 'lahiri', // lahiri | raman | krishnamurti | true-chitra | thirukanitham
697
+ language: 'en', // en | hi
698
+ masaSystem: 'purnimanta', // purnimanta | amanta
1330
699
  region: 'all', // 21 state slugs + 'nepal' + 'all'
1331
700
  computeEndTimes: true, // false → ~5x speedup, names only
1332
- precision: 'standard', // 'standard' (15 iter) | 'high' (25 iter)
701
+ precision: 'standard', // standard (15 iter) | high (25 iter)
1333
702
  janmaRashi: undefined, // pass to add r.chandraBalam
1334
703
  janmaNakshatra: undefined, // pass to add r.tarabala
1335
704
  });
1336
705
  ```
1337
706
 
1338
- **Timezone handling** `timezone` accepts either a number (UTC offset in minutes, e.g.
1339
- `330` for IST) or an IANA zone name (e.g. `'America/New_York'`). IANA strings need `Intl`,
1340
- which older Hermes versions don't fully support — pass a number on those targets. DST
1341
- resolves automatically for IANA zones via the reference date.
707
+ **Timezone.** Number (minutes from UTC, e.g. `330` for IST) or an IANA zone
708
+ name (e.g. `'America/New_York'`). IANA strings need `Intl`, which older Hermes
709
+ versions lack — pass a number on those targets. DST resolves automatically for
710
+ IANA zones.
1342
711
 
1343
712
  ---
1344
713
 
1345
714
  ## Types & Exports
1346
715
 
1347
716
  <details>
1348
- <summary><strong>Core Types</strong> — GeoLocation, TimePeriod</summary>
717
+ <summary><strong>Core, Pancha Anga, Festivals</strong></summary>
1349
718
 
1350
719
  ```typescript
1351
- interface GeoLocation {
1352
- latitude: number; // -90 to 90
1353
- longitude: number; // -180 to 180
1354
- elevation?: number; // metres, default 0
1355
- }
1356
-
1357
- interface TimePeriod {
1358
- start: Date;
1359
- end: Date;
1360
- }
1361
- ```
1362
- </details>
1363
-
1364
- <details>
1365
- <summary><strong>Pancha Anga</strong> — TithiInfo, NakshatraInfo, YogaInfo, KaranaInfo, VaraInfo</summary>
720
+ interface GeoLocation { latitude: number; longitude: number; elevation?: number; }
721
+ interface TimePeriod { start: Date; end: Date; }
1366
722
 
1367
- ```typescript
1368
723
  interface TithiInfo {
1369
724
  index: number; // 0-29
1370
- name: string; // e.g. "Shukla Pratipada"
725
+ name: string;
1371
726
  paksha: string; // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (hi)
1372
727
  number: number; // 1-15 within the paksha
1373
728
  completionPercentage: number;
1374
729
  endTime: Date | null;
1375
730
  }
1376
-
1377
- interface NakshatraInfo {
1378
- index: number; // 0-26
1379
- name: string;
1380
- pada: number; // 1-4
1381
- degreesInNakshatra: number;
1382
- completionPercentage: number;
1383
- endTime: Date | null;
1384
- }
1385
-
1386
- interface DailyTithiInfo extends TithiInfo {
1387
- startTime: Date | null; // null when isActiveAtSunrise is true
1388
- isActiveAtSunrise: boolean;
1389
- }
1390
-
1391
- // DailyNakshatraInfo, DailyYogaInfo, DailyKaranaInfo follow the same pattern.
731
+ // NakshatraInfo, YogaInfo, KaranaInfo follow the same pattern.
732
+ // DailyTithiInfo extends with startTime + isActiveAtSunrise.
1392
733
 
1393
734
  interface VaraInfo {
1394
- index: number; // 0 = Sunday ... 6 = Saturday
1395
- name: string; // e.g. "Raviwara" (localized)
1396
- shortName: string; // e.g. "Ravi" (localized)
1397
- englishName: string; // e.g. "Sunday" (always English)
1398
- }
1399
-
1400
- interface KaranaInfo {
1401
- index: number;
1402
- name: string;
1403
- completionPercentage: number;
1404
- endTime: Date | null;
1405
- type: 'fixed' | 'movable';
1406
- }
1407
-
1408
- // endTime / startTime are null when computeEndTimes: false.
1409
- ```
1410
- </details>
1411
-
1412
- <details>
1413
- <summary><strong>Lunar Calendar</strong> — ChandraMasaInfo, SamvatInfo, MasaInfo, RashiInfo</summary>
1414
-
1415
- ```typescript
1416
- interface ChandraMasaInfo {
1417
- index: number; // 0 = Chaitra ... 11 = Phalguna (in the active system)
1418
- name: string; // follows masaSystem option
1419
- isAdhika: boolean; // true = leap/intercalary month
1420
- system: 'purnimanta' | 'amanta';
1421
- amantaIndex: number;
1422
- amantaName: string;
1423
- purnimantaIndex: number;
1424
- purnimantaName: string;
1425
- }
1426
-
1427
- interface SamvatInfo {
1428
- vikramSamvat: number; // e.g. 2081
1429
- shakaSamvat: number; // e.g. 1946
1430
- }
1431
-
1432
- interface MasaInfo {
1433
- index: number; // 0 = Mesha ... 11 = Meena (solar month)
1434
- name: string;
1435
- }
1436
-
1437
- interface RashiInfo {
1438
- index: number; // 0 = Mesha ... 11 = Meena
1439
- name: string;
1440
- }
1441
- ```
1442
- </details>
1443
-
1444
- <details>
1445
- <summary><strong>Time Slots</strong> — Choghadiya, Gowri, Hora, Do Ghati</summary>
1446
-
1447
- ```typescript
1448
- type ChoghadiyaQuality = 'auspicious' | 'inauspicious' | 'neutral';
1449
-
1450
- interface ChoghadiyaSlot extends TimePeriod {
1451
- index: number;
1452
- name: string; // e.g. "Amrit", "Kaal" (localized)
1453
- quality: ChoghadiyaQuality;
1454
- qualityName: string; // localized: "Auspicious", "शुभ"
1455
- }
1456
-
1457
- interface ChoghadiyaInfo {
1458
- day: ChoghadiyaSlot[]; // 8 slots (sunrise -> sunset)
1459
- night: ChoghadiyaSlot[]; // 8 slots (sunset -> next sunrise)
1460
- }
1461
-
1462
- // GowriSlot / GowriInfo mirror Choghadiya.
1463
-
1464
- interface HoraSlot extends TimePeriod {
1465
- planet: string; // "Sun", "Venus", "Mercury", ...
1466
- planetIndex: number; // 0-6 in Chaldean order
1467
- }
1468
-
1469
- interface HoraInfo {
1470
- day: HoraSlot[]; // 12 slots (sunrise -> sunset)
1471
- night: HoraSlot[]; // 12 slots (sunset -> next sunrise)
1472
- }
1473
-
1474
- interface DoGhatiInfo {
1475
- day: DoGhatiSlot[]; // 15 ~48-min deity-keyed slots
1476
- night: DoGhatiSlot[]; // 15 ~48-min deity-keyed slots
1477
- }
1478
- ```
1479
- </details>
1480
-
1481
- <details>
1482
- <summary><strong>Special Yogas & Festivals</strong></summary>
1483
-
1484
- ```typescript
1485
- interface SpecialYogaInfo {
1486
- name: string;
1487
- type:
1488
- | 'amrit_siddhi' | 'sarvartha_siddhi' | 'ravi_pushya' | 'guru_pushya'
1489
- | 'dwipushkar' // Bhadra-tithi + vara + nakshatra ∈ {Mrig, Chitra, Dhan} — doubled
1490
- | 'tripushkar' // same Bhadra rules + nakshatra ∈ {Krit, Punar, U.Phal, Vish, U.Ash, P.Bhad} — tripled
1491
- | 'jwalamukhi' // inauspicious — tithi × nakshatra (Muhurta-chintamani 6.32)
1492
- | 'aadal' // auspicious — Moon-from-Sun nakshatra distance
1493
- | 'vidaal' // inauspicious — Moon-from-Sun nakshatra distance
1494
- | 'ravi'; // auspicious — Moon-from-Sun nakshatra distance (27-scheme)
735
+ index: number; // 0 = Sunday 6 = Saturday
736
+ name: string; // localized (e.g. "Raviwara")
737
+ shortName: string;
738
+ englishName: string; // always English
1495
739
  }
1496
740
 
1497
741
  interface FestivalInfo {
1498
742
  name: string;
1499
- type:
1500
- | 'major' | 'minor'
1501
- | 'ekadashi' | 'smarta_ekadashi' | 'vaishnava_ekadashi'
1502
- | 'pradosha' | 'sankranti' | 'eclipse';
743
+ type: 'major' | 'minor' | 'ekadashi' | 'smarta_ekadashi' | 'vaishnava_ekadashi'
744
+ | 'pradosha' | 'sankranti' | 'eclipse';
1503
745
  description?: string;
1504
- /** Smarta-only: when Ekadashi is Dashami-viddha, the Dwadashi fast date. */
1505
- deferralDate?: Date;
746
+ deferralDate?: Date; // Smarta Ekadashi Dwadashi fast date
1506
747
  }
1507
748
 
1508
749
  type FestivalRegion =
1509
- | 'all' // default — emits every regional variant
1510
- // South
750
+ | 'all'
1511
751
  | 'tamil-nadu' | 'kerala' | 'karnataka' | 'andhra-pradesh' | 'telangana'
1512
- // East
1513
752
  | 'west-bengal' | 'odisha' | 'assam' | 'bihar' | 'jharkhand'
1514
- // West
1515
753
  | 'gujarat' | 'maharashtra' | 'goa' | 'rajasthan'
1516
- // North / Central
1517
754
  | 'punjab' | 'haryana' | 'himachal-pradesh' | 'uttarakhand'
1518
755
  | 'uttar-pradesh' | 'madhya-pradesh'
1519
- // Neighbour
1520
756
  | 'nepal';
1521
757
 
1522
- // Pre-v2.1 identifiers accepted with a one-shot deprecation warning. Removed in v3.
1523
- // 'tamil' → 'tamil-nadu'
1524
- // 'bengal' → 'west-bengal'
1525
- // 'north-india' → 'all'
1526
- type LegacyFestivalRegion = 'tamil' | 'bengal' | 'north-india';
758
+ // Legacy slugs accepted (mapped internally): 'tamil' 'tamil-nadu',
759
+ // 'bengal' → 'west-bengal', 'north-india' → 'all'.
1527
760
  ```
1528
761
 
1529
- **Region-scoped festivals** (non-exhaustive):
1530
-
1531
- | Region | Festival names (keys) |
1532
- |---|---|
1533
- | `tamil-nadu` | pongal, puthandu, varamahalakshmi |
1534
- | `kerala` | vishu, ayyappa_makara_jyothi, onam |
1535
- | `karnataka` | karaga, varamahalakshmi |
1536
- | `andhra-pradesh` | varamahalakshmi |
1537
- | `telangana` | bonalu, varamahalakshmi, bathukamma_start, bathukamma_saddula |
1538
- | `west-bengal` | pohela_boishakh, bhai_dooj |
1539
- | `odisha` | singh_sankranti, raja_pahili, raja_sankranti, raja_basi |
1540
- | `assam` | bohag_bihu, magh_bihu, kati_bihu |
1541
- | `bihar` | singh_sankranti, hariyali_teej, govardhan_puja, bhai_dooj |
1542
- | `gujarat` | uttarayan, govardhan_puja, bhai_dooj |
1543
- | `maharashtra` | gudi_padwa, hartalika_teej, bhai_dooj |
1544
- | `goa` | gudi_padwa |
1545
- | `rajasthan` | gangaur, hariyali_teej, kajari_teej, hartalika_teej, govardhan_puja, bhai_dooj |
1546
- | `punjab` | baisakhi, lohri, govardhan_puja |
1547
- | `haryana` | baisakhi, lohri, govardhan_puja, bhai_dooj |
1548
- | `himachal-pradesh` | sair, phagli, lohri |
1549
- | `uttarakhand` | harela |
1550
- | `uttar-pradesh` | hariyali_teej, kajari_teej, hartalika_teej, govardhan_puja, bhai_dooj |
1551
- | `madhya-pradesh` | hariyali_teej, kajari_teej, hartalika_teej |
1552
- | `nepal` | singh_sankranti, bhai_dooj |
1553
762
  </details>
1554
763
 
1555
764
  <details>
1556
- <summary><strong>Eclipses (Grahan)</strong> — EclipseInfo</summary>
765
+ <summary><strong>Eclipses & Bhadra</strong></summary>
1557
766
 
1558
767
  ```typescript
1559
- type EclipseSubtype = 'partial' | 'total' | 'annular' | 'penumbral';
1560
-
1561
768
  interface EclipseInfo {
1562
769
  kind: 'solar' | 'lunar';
1563
- subtype: EclipseSubtype;
1564
- start: Date; // UTC observable phase begins
1565
- peak: Date; // UTC — greatest eclipse
1566
- end: Date; // UTC — observable phase ends
770
+ subtype: 'partial' | 'total' | 'annular' | 'penumbral';
771
+ start: Date; peak: Date; end: Date;
1567
772
  visibleFromLocation: boolean;
1568
- magnitude: number; // fraction of disc obscured at peak, [0, 1]
1569
- sutakStart: Date; // 12 h before solar / 9 h before lunar
1570
- sutakEnd: Date; // coincides with eclipse end (moksha)
773
+ magnitude: number; // [0, 1] at peak
774
+ sutakStart: Date; // 12 h pre-solar / 9 h pre-lunar
775
+ sutakEnd: Date;
1571
776
  description: string;
1572
777
  }
1573
- ```
1574
- </details>
1575
778
 
1576
- <details>
1577
- <summary><strong>Bhadra Kala</strong> — BhadraInfo</summary>
1578
-
1579
- ```typescript
1580
779
  interface BhadraInfo {
1581
- start: Date;
1582
- end: Date;
1583
- /** 'earth' = malefic for all work; 'heaven' / 'paatal' = non-terrestrial, milder. */
1584
- location: 'earth' | 'heaven' | 'paatal';
1585
- /** True when Bhadra is active at some point during the Hindu day window. */
780
+ start: Date; end: Date;
781
+ location: 'earth' | 'heaven' | 'paatal'; // 'earth' = malefic for all work
1586
782
  isActive: boolean;
1587
783
  }
1588
784
  ```
785
+
1589
786
  </details>
1590
787
 
1591
788
  <details>
1592
- <summary><strong>Jyotish</strong> — Graha positions, Vimshottari Dasha, Chandra Balam, Tarabala</summary>
789
+ <summary><strong>Jyotish</strong></summary>
1593
790
 
1594
791
  ```typescript
1595
792
  type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter'
@@ -1597,49 +794,44 @@ type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter'
1597
794
 
1598
795
  interface GrahaPosition {
1599
796
  planet: GrahaName;
1600
- siderealLongitude: number; // degrees [0, 360)
797
+ siderealLongitude: number;
1601
798
  rashi: RashiInfo;
1602
- degreeInRashi: number; // [0, 30)
799
+ degreeInRashi: number;
1603
800
  nakshatra: NakshatraInfo;
1604
- isRetrograde: boolean; // always false for Sun/Moon; always true for Rahu/Ketu
1605
- }
1606
-
1607
- interface PlanetaryPositions {
1608
- sun: GrahaPosition; moon: GrahaPosition; mars: GrahaPosition;
1609
- mercury: GrahaPosition; jupiter: GrahaPosition; venus: GrahaPosition;
1610
- saturn: GrahaPosition; rahu: GrahaPosition; ketu: GrahaPosition;
801
+ isRetrograde: boolean; // always false for Sun/Moon; always true for Rahu/Ketu
1611
802
  }
1612
803
 
1613
804
  type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars'
1614
805
  | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
1615
806
 
1616
- interface AntarDasha { lord: DashaLord; startDate: Date; endDate: Date; }
1617
- interface MahaDasha { lord: DashaLord; startDate: Date; endDate: Date;
1618
- years: number; antarDashas: AntarDasha[]; }
807
+ interface MahaDasha { lord: DashaLord; startDate: Date; endDate: Date;
808
+ years: number; antarDashas: AntarDasha[]; }
1619
809
  interface VimshottariDashaResult {
1620
810
  currentMahaDashaLord: DashaLord;
1621
811
  currentIndex: number;
1622
- mahaDashas: MahaDasha[]; // 9-entry sequence starting from birth
812
+ mahaDashas: MahaDasha[];
1623
813
  }
1624
814
 
1625
815
  interface ChandraBalamInfo {
1626
- house: number; // 1 = janma rashi; 12 = rashi before janma
1627
- quality: 'strong' | 'weak'; // Shubha houses = 1,3,6,7,10,11
1628
- englishName: string; // "Shubha" | "Ashubha"
1629
- name: string; // localized
816
+ house: number; // 1 = janma rashi; 12 = rashi before janma
817
+ quality: 'strong' | 'weak'; // Shubha houses = 1,3,6,7,10,11
818
+ englishName: string; // "Shubha" | "Ashubha"
819
+ name: string;
1630
820
  }
1631
821
 
1632
822
  interface TarabalaInfo {
1633
- taraIndex: number; // 0..8 in the 9-tara cycle from janma nakshatra
1634
- englishName: string; // "Janma" | "Sampat" | "Vipat" | "Kshema" | "Pratyari"
1635
- // | "Sadhaka" | "Vadha" | "Mitra" | "Ati-Mitra"
823
+ taraIndex: number; // 0..8 in 9-tara cycle from janma nakshatra
824
+ englishName: string; // Janma | Sampat | Vipat | Kshema | Pratyari
825
+ // | Sadhaka | Vadha | Mitra | Ati-Mitra
1636
826
  name: string;
1637
827
  quality: 'auspicious' | 'inauspicious';
1638
828
  }
1639
829
  ```
830
+
1640
831
  </details>
1641
832
 
1642
- ### Full export list
833
+ <details>
834
+ <summary><strong>Full export list</strong></summary>
1643
835
 
1644
836
  ```typescript
1645
837
  // Primary entry points
@@ -1660,19 +852,27 @@ computeMadhyahna, computePratahSandhya, computeSayahnaSandhya
1660
852
  // Eclipses
1661
853
  getUpcomingSolarEclipse, getUpcomingLunarEclipse, getEclipseDuringDay
1662
854
 
1663
- // Jyotish
855
+ // Jyotish — planets, dashas, transits
1664
856
  computePlanetaryPositions, GRAHA_ABBR
1665
857
  computeVimshottariDasha, computeVimshottariDashaFromBirth, computeVimshottariPratyantar
1666
858
  computeAshtottariDasha, computeYoginiDasha, computeCharaDasha, computeNarayanDasha
1667
- computeChandraBalam, computeTarabala
859
+ computeChandraBalam, computeTarabala, computeSadeSati
860
+
861
+ // Jyotish — chart
1668
862
  computeLagna, computeBhava, computeRashiChart, computeNavamsa, computeDivisionalChart
1669
863
  computeHoraLagna, computeGhatiLagna, computeBhavaLagna, computeSripatiLagna
864
+ computeDignity
865
+
866
+ // Jyotish — strength, yogas, sensitive
1670
867
  computeAspects, computeShadbala, computeBhavaBala, computeAshtakavarga
1671
868
  computeYogas, computeJaiminiKarakas
1672
869
  computeVarshaphala, computeTithiPravesha, computeArudhas, computeUpagrahas, computeArgala
870
+
871
+ // Jyotish — compatibility, doshas
1673
872
  computeAshtakoot, computePathuPorutham
1674
873
  computeMangalDosha, computeKaalSarp, computePitruDosha
1675
- computeSadeSati, computeDignity
874
+
875
+ // KP / Prashna
1676
876
  computeKpSubLord, computeKpCuspalSubLords, computeKpSignificators
1677
877
  computePrashnaChart
1678
878
 
@@ -1691,31 +891,31 @@ getEkadashiDatesForYear, getSankrantisForYear, getFestivalsInRange, getUpcomingE
1691
891
  PanchangError
1692
892
  ```
1693
893
 
894
+ </details>
895
+
1694
896
  ---
1695
897
 
1696
898
  ## React Native / Hermes
1697
899
 
1698
- Works with Expo and bare React Native (Hermes engine). Pass `timezone` as a **number** —
1699
- IANA timezone strings (`'Asia/Kolkata'`) require `Intl`, which older Hermes versions don't
1700
- fully support.
900
+ Works with Expo and bare React Native (Hermes engine). Pass `timezone` as a
901
+ **number** — IANA strings need `Intl`, which older Hermes versions lack.
1701
902
 
1702
- **Two-pass rendering** for smooth UI:
903
+ Two-pass rendering pattern for smooth UI:
1703
904
 
1704
905
  ```typescript
1705
906
  import { getDailyPanchang } from 'panchang-ts';
1706
907
  import { InteractionManager } from 'react-native';
1707
908
 
1708
- // Pass 1 — instant, names only (~0.1 ms on Node, <100 ms on Hermes)
909
+ // Pass 1 — instant, names only (~0.1 ms Node, <100 ms Hermes)
1709
910
  const fast = getDailyPanchang(date, location, {
1710
911
  timezone: 330,
1711
912
  computeEndTimes: false,
1712
913
  });
1713
- setState(fast); // show names immediately
914
+ setState(fast);
1714
915
 
1715
- // Pass 2 — background, full with end-times (~0.5 ms on Node, <500 ms on Hermes)
916
+ // Pass 2 — background, full with end-times (~0.5 ms Node, <500 ms Hermes)
1716
917
  InteractionManager.runAfterInteractions(() => {
1717
- const full = getDailyPanchang(date, location, { timezone: 330 });
1718
- setState(full); // update with transition times
918
+ setState(getDailyPanchang(date, location, { timezone: 330 }));
1719
919
  });
1720
920
  ```
1721
921
 
@@ -1723,66 +923,54 @@ InteractionManager.runAfterInteractions(() => {
1723
923
 
1724
924
  ## Accuracy
1725
925
 
1726
- 7,156 tests passing across 81 files, including fixtures cross-verified against reference
1727
- panchang calculations spanning 2025–2026 across 10 Indian cities, plus New York, London,
1728
- Sydney, Dubai, and Singapore (diaspora fixtures cover DST transitions on
926
+ 8,103 tests across 96 files, including fixtures cross-verified against reference
927
+ panchang calculations spanning 2025–2026 across 10 Indian cities plus New York,
928
+ London, Sydney, Dubai, Singapore (diaspora fixtures cover DST on
1729
929
  `America/New_York`).
1730
930
 
1731
- | Element | Accuracy | Validation |
1732
- |---------|----------|------------|
1733
- | Sunrise / Sunset | ≤29 s observed vs reference minute-midpoint (±45 s tolerance) | 16 assertions |
1734
- | Moonrise / Moonset | Meeus apparent-upper-limb (refraction + parallax); ~3–5 min vs simpler-horizon authorities is expected | Strict fixtures |
1735
- | Tithi, Nakshatra, Yoga, Karana names | Exact match vs reference | Strict fixtures |
1736
- | Tithi / Nakshatra / Yoga / Karana end-times | ±3 min tolerance, max 2.01 min observed | 20 assertions |
1737
- | Ayanamsa | ±0.005° vs Swiss Ephemeris | Unit tests |
1738
- | Planetary positions (Sun–Saturn) | ±0.02° vs reference sidereal | Fixtures |
1739
- | Planetary positions (Rahu/Ketu, mean node) | ≤0.5° typical; ±2° tolerance | Fixtures |
1740
- | Planetary positions (Rahu/Ketu, true node) | ≤0.6° typical (Meeus periodic correction) | Fixtures |
1741
- | Rashi / Nakshatra / Retrograde flag | Exact match vs reference | Fixtures |
1742
- | Festival dates | 12 cross-verified festivals (2025–2026) see caveats below | Fixtures |
1743
- | Choghadiya / Hora / Gowri slots | Derived from sunrise/sunset inherits ±2 min | |
1744
- | Madhyahna midpoint, Anandadi Yoga, Ganda Mula active flag | Exact match across 50 reference fixtures | Cross-verify suite |
1745
- | Pratah / Sayahna Sandhya start + end | ±2 min across all 50 fixtures | Cross-verify suite |
1746
- | Varjyam start + end | ±2 min on every emit (transition days return `null` by design) | Cross-verify suite |
1747
- | Lagna sidereal longitude | Cross-checked against Jagannath Hora reference charts | Birth-chart fixtures |
1748
- | D1 (Rashi) & D9 (Navamsa) house placements | Exact match vs reference for 9-graha placement | Birth-chart fixtures |
1749
- | Ashtakoot Guna Milan total score | ±1 point per pair across 30+ matched pairs | Match fixtures |
1750
- | Sade Sati arc start / end | ±1–2 days vs authoritative ephemerides | Saturn-transit fixtures |
1751
-
1752
- **Detection sourcing notes.** **Aadal / Vidaal** follow the classical Moon-from-Sun
1753
- nakshatra-distance rule (AstroShastra, HoraSarvam, Ernst Wilhelm), NOT the popular
1754
- Tamil-Vakya weekday rule used by some online panchangs — output may differ from sites that
1755
- use the weekday rule. **Varjyam** emits the sunrise-anchored nakshatra's window only —
1756
- printed panchangs may show a second window on nakshatra-transition days. **Do Ghati
1757
- Muhurta** does not rotate by weekday: the same 30-name deity-keyed sequence applies every
1758
- day, verified against multiple reference sources for distinct weekdays.
931
+ | Element | Accuracy |
932
+ |---|---|
933
+ | Sunrise / Sunset | ≤29 s observed vs reference minute-midpoint (±45 s tolerance) |
934
+ | Moonrise / Moonset | Meeus apparent-upper-limb (refraction + parallax); ~3–5 min vs simpler-horizon authorities is expected |
935
+ | Tithi / Nakshatra / Yoga / Karana names | Exact match vs reference |
936
+ | Tithi / Nakshatra / Yoga / Karana end-times | ±3 min tolerance, max 2.01 min observed |
937
+ | Ayanamsa | ±0.005° vs Swiss Ephemeris |
938
+ | Planetary positions (Sun–Saturn) | ±0.02° sidereal |
939
+ | Planetary positions (Rahu/Ketu, mean node) | ≤0.5° typical; ±2° tolerance |
940
+ | Planetary positions (Rahu/Ketu, true node) | ≤0.6° typical (Meeus periodic correction) |
941
+ | Lagna sidereal longitude | Cross-checked against Jagannath Hora reference charts |
942
+ | D1 / D9 house placement | Exact match vs reference for 9-graha placement |
943
+ | Ashtakoot total | ±1 point per pair across 30+ matched pairs |
944
+ | Sade Sati arc start/end | ±1–2 days vs authoritative ephemerides |
945
+
946
+ **Detection notes.** **Aadal / Vidaal** follow the classical Moon-from-Sun
947
+ nakshatra-distance rule (AstroShastra, HoraSarvam, Ernst Wilhelm), NOT the
948
+ Tamil-Vakya weekday rule used by some online panchangs. **Varjyam** emits the
949
+ sunrise-anchored nakshatra's window only. **Do Ghati Muhurta** does not rotate
950
+ by weekday the same 30-name deity-keyed sequence applies every day.
1759
951
 
1760
952
  ### Festival Detection — Documented Tradeoff
1761
953
 
1762
- The library uses **tithi-at-sunrise** to resolve a festival to a calendar day. Some
1763
- traditional panchang authorities apply other classical rules (tithi-at-midnight,
1764
- madhyahna-vyapini, kshaya-tithi handling) for certain festivals; where those rules pick a
1765
- different day, our output can drift ±1 day. This is a rule-choice tradeoff, not a
1766
- computation bug.
954
+ The library uses **tithi-at-sunrise** to resolve a festival to a calendar day.
955
+ Some authorities use other classical rules for certain festivals; where those
956
+ rules pick a different day, output can drift ±1 day:
1767
957
 
1768
- | Alternative classical rule | Festivals affected |
1769
- |----------------------------|--------------------|
958
+ | Alternative rule | Affects |
959
+ |---|---|
1770
960
  | Tithi-at-midnight | Krishna Janmashtami, Maha Shivaratri, Diwali / Lakshmi Puja |
1771
- | Madhyahna-vyapini (tithi overlapping noon) | Ganesh Chaturthi on edge years, Akshaya Tritiya 2026 |
1772
- | Kshaya-tithi handling (tithi never at sunrise) | Ugadi 2026-03-19 (Pratipada is Kshaya) |
961
+ | Madhyahna-vyapini | Ganesh Chaturthi (edge years), Akshaya Tritiya 2026 |
962
+ | Kshaya-tithi handling | Ugadi 2026-03-19 (Pratipada is Kshaya) |
1773
963
 
1774
- If strict parity with a specific panchang authority matters for your use case, cross-check
1775
- the above festival set for the target year. Everything else — Holi, Ugadi (non-Kshaya
1776
- years), Rama Navami, Raksha Bandhan, Ganesh Chaturthi (normal years), Navaratri,
1777
- Dussehra, Karva Chauth, Hanuman Jayanti — matches the canonical date across 2025 and 2026
1778
- fixtures.
964
+ Everything else Holi, Ugadi (non-Kshaya years), Rama Navami, Raksha Bandhan,
965
+ Ganesh Chaturthi (normal years), Navaratri, Dussehra, Karva Chauth, Hanuman
966
+ Jayanti matches the canonical date across 2025 and 2026 fixtures.
1779
967
 
1780
968
  ---
1781
969
 
1782
970
  ## Performance
1783
971
 
1784
972
  | Mode | Node.js | Hermes (budget Android) |
1785
- |------|---------|------------------------|
973
+ |---|---|---|
1786
974
  | Names-only (`computeEndTimes: false`) | ~0.1 ms | <100 ms |
1787
975
  | Full with end-times | ~0.5 ms | <500 ms |
1788
976
 
@@ -1800,40 +988,42 @@ try {
1800
988
  getDailyPanchang(date, location, options);
1801
989
  } catch (e) {
1802
990
  if (e instanceof PanchangError) {
1803
- console.error(e.code); // e.g. 'INVALID_LATITUDE', 'INVALID_TIMEZONE'
991
+ console.error(e.code); // e.g. 'INVALID_LATITUDE', 'INVALID_TIMEZONE'
1804
992
  console.error(e.message);
1805
993
  }
1806
994
  }
1807
995
  ```
1808
996
 
1809
- Error codes: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`, `INVALID_ELEVATION`,
1810
- `INVALID_TIMEZONE`, `INVALID_AYANAMSA`, `TIMEZONE_RESOLUTION_FAILED`, `NO_SUNRISE`,
1811
- `NO_SUNSET`, `SEARCH_DIVERGED`, `CIRCUMPOLAR` (Placidus-KP houses above ±66.5°).
997
+ Error codes: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
998
+ `INVALID_ELEVATION`, `INVALID_TIMEZONE`, `INVALID_AYANAMSA`,
999
+ `TIMEZONE_RESOLUTION_FAILED`, `NO_SUNRISE`, `NO_SUNSET`, `SEARCH_DIVERGED`,
1000
+ `CIRCUMPOLAR` (Placidus-KP houses above ±66.5°).
1812
1001
 
1813
- **Polar locations (no sunrise / no sunset):** `getDailyPanchang` and `getInstantPanchang`
1814
- return `null` rather than throwing — the Hindu day is undefined when sunrise can't be
1002
+ **Polar locations.** `getDailyPanchang` and `getInstantPanchang` return `null`
1003
+ rather than throwing — the Hindu day is undefined when sunrise can't be
1815
1004
  computed. The low-level `getSunrise` / `getSunset` primitives still throw
1816
- `PanchangError(NO_SUNRISE)` / `PanchangError(NO_SUNSET)` for direct callers who need the
1817
- precise reason. `getMoonrise` / `getMoonset` return `null` (normal for the Moon).
1005
+ `PanchangError(NO_SUNRISE)` / `PanchangError(NO_SUNSET)` for direct callers
1006
+ who need the precise reason. `getMoonrise` / `getMoonset` return `null` (normal
1007
+ for the Moon).
1818
1008
 
1819
1009
  ---
1820
1010
 
1821
1011
  ## Compatibility
1822
1012
 
1823
1013
  | Environment | Support |
1824
- |-------------|---------|
1014
+ |---|---|
1825
1015
  | Node.js 18+ | Supported |
1826
1016
  | React Native (Hermes) | Supported (pass `timezone` as number) |
1827
1017
  | Expo (managed + bare) | Supported |
1828
- | Browser (modern) | Supported (ESM build) |
1018
+ | Browser (modern, ESM) | Supported |
1829
1019
  | Browser (legacy / IE) | Not supported |
1830
1020
 
1831
1021
  ---
1832
1022
 
1833
1023
  ## Acknowledgements
1834
1024
 
1835
- [astronomy-engine](https://github.com/cosinekitty/astronomy) by Don Cross — the sole
1836
- runtime dependency. MIT licensed.
1025
+ [astronomy-engine](https://github.com/cosinekitty/astronomy) by Don Cross — the
1026
+ sole runtime dependency. MIT licensed.
1837
1027
 
1838
1028
  ## License
1839
1029