panchang-ts 0.3.1 → 0.4.1

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
@@ -22,6 +22,7 @@ Works offline in React Native (Hermes), Node.js, and browsers.
22
22
  - **Instant mode:** Elements active at an exact moment (birth charts, muhurta selection)
23
23
  - **3 ayanamsa systems:** Lahiri (default), B.V. Raman, KP (Krishnamurti)
24
24
  - **3 languages:** English, Sanskrit (Devanagari), Hindi
25
+ - **Jyotish (Vedic astrology):** Janam Kundli with Lagna, 12 houses (whole-sign), all 9 graha positions, Navamsa (D-9) chart, Vimshottari Dasha with Antardasha breakdown
25
26
  - **React Native compatible:** Pure JS math, no native modules, tested on Hermes
26
27
  - **Fast:** ~0.1 ms names-only on Node.js; <100 ms on budget Android (Hermes)
27
28
  - **Typed:** Full TypeScript types for every result and option
@@ -185,6 +186,77 @@ const result = getDailyPanchang(
185
186
 
186
187
  ---
187
188
 
189
+ ### `computeKundli(birthDateUtc, location, options?)`
190
+
191
+ Compute a complete Janam Kundli (Vedic birth chart).
192
+
193
+ ```typescript
194
+ import { computeKundli } from 'panchang-ts';
195
+
196
+ const kundli = computeKundli(
197
+ new Date('1990-06-15T08:30:00Z'), // UTC birth moment
198
+ { latitude: 18.5204, longitude: 73.8567 }, // birth location
199
+ { ayanamsa: 'lahiri', language: 'en' },
200
+ );
201
+
202
+ // Ascendant
203
+ console.log(kundli.lagna.name); // "Karka" (Cancer)
204
+ console.log(kundli.lagnaLongitude); // 95.4 (sidereal degrees)
205
+
206
+ // Planetary positions
207
+ const sun = kundli.grahas.sun;
208
+ console.log(sun.rashi.name); // "Mithuna"
209
+ console.log(sun.nakshatra.name); // "Ardra"
210
+ console.log(sun.degreeInRashi); // 24.3
211
+ console.log(sun.isRetrograde); // false
212
+
213
+ // Houses (whole-sign)
214
+ kundli.houses.forEach(h => {
215
+ console.log(`House ${h.number}: ${h.rashi.name} — ${h.planets.join(', ')}`);
216
+ });
217
+
218
+ // Navamsa (D-9) chart
219
+ console.log(kundli.navamsa.lagna.name); // Navamsa lagna sign
220
+ kundli.navamsa.positions.forEach(p => {
221
+ console.log(p.planet, '→', p.rashi.name);
222
+ });
223
+
224
+ // Vimshottari Dasha
225
+ const dasha = kundli.dasha;
226
+ console.log(dasha.currentMahaDashaLord); // e.g. "Jupiter"
227
+ dasha.mahaDashas.forEach(md => {
228
+ console.log(md.lord, md.startDate, '→', md.endDate, `(${md.years}y)`);
229
+ md.antarDashas.forEach(ad => {
230
+ console.log(' ', ad.lord, ad.startDate, '→', ad.endDate);
231
+ });
232
+ });
233
+
234
+ // Full birth Panchang is also included
235
+ console.log(kundli.birthPanchang.tithi.name); // Tithi at birth
236
+ ```
237
+
238
+ **Returns: `KundliResult`**
239
+
240
+ | Field | Type | Description |
241
+ |-------|------|-------------|
242
+ | `lagnaLongitude` | `number` | Sidereal longitude of the Ascendant in degrees [0, 360) |
243
+ | `lagna` | `RashiInfo` | Ascendant zodiac sign |
244
+ | `houses` | `KundliHouse[]` | 12 houses; House 1 = Lagna sign (whole-sign system) |
245
+ | `grahas` | `PlanetaryPositions` | All 9 graha positions with rashi, nakshatra, house, retrograde flag |
246
+ | `navamsa` | `NavamsaChart` | D-9 divisional chart with Lagna and all planet positions |
247
+ | `dasha` | `VimshottariDashaResult` | Full Vimshottari Dasha sequence from birth with Antardasha breakdown |
248
+ | `birthPanchang` | `InstantPanchangResult` | Complete Panchang at the birth moment |
249
+
250
+ **`KundliOptions`** (optional):
251
+
252
+ | Option | Type | Default | Description |
253
+ |--------|------|---------|-------------|
254
+ | `ayanamsa` | `'lahiri' \| 'raman' \| 'krishnamurti'` | `'lahiri'` | Ayanamsa system |
255
+ | `language` | `'en' \| 'sa' \| 'hi'` | `'en'` | Language for names |
256
+ | `computeDasha` | `boolean` | `true` | Set `false` to skip Vimshottari Dasha computation |
257
+
258
+ ---
259
+
188
260
  ### `getInstantPanchang(date, location, options?)`
189
261
 
190
262
  Returns the single Panchang element active at an exact UTC moment.
@@ -259,6 +331,9 @@ import {
259
331
  computeRahuKalam, computeGulikaKalam, computeYamaganda,
260
332
  computeAbhijitMuhurta, computeBrahmaMuhurta,
261
333
  computeGowriPanchangam,
334
+ // Jyotish
335
+ computePlanetaryPositions, computeVimshottariDasha, computeLagnaLongitude,
336
+ GRAHA_ABBR,
262
337
  } from 'panchang-ts';
263
338
 
264
339
  // Sunrise/sunset
@@ -292,6 +367,21 @@ const gowri = computeGowriPanchangam(sunrise, sunset, nextSunrise, varaIndex,
292
367
  );
293
368
  // gowri.day → 8 GowriSlot (sunrise → sunset)
294
369
  // gowri.night → 8 GowriSlot (sunset → next sunrise)
370
+
371
+ // Planetary positions (all 9 grahas, sidereal)
372
+ const grahas = computePlanetaryPositions(birthDate, 'lahiri');
373
+ console.log(grahas.jupiter.rashi.name); // e.g. "Dhanu"
374
+ console.log(grahas.saturn.isRetrograde); // true/false
375
+ console.log(GRAHA_ABBR['Jupiter']); // "Ju"
376
+
377
+ // Lagna (Ascendant) longitude — useful when building a custom chart renderer
378
+ const lagnaLon = computeLagnaLongitude(birthDate, latitude, longitude, 'lahiri');
379
+
380
+ // Vimshottari Dasha — pass birth date and Moon's sidereal longitude
381
+ const moonLon = getSiderealMoonLongitude(birthDate, 'lahiri');
382
+ const dasha = computeVimshottariDasha(birthDate, moonLon);
383
+ console.log(dasha.currentMahaDashaLord); // e.g. "Rahu"
384
+ console.log(dasha.mahaDashas[0]!.antarDashas[0]!.lord); // e.g. "Rahu"
295
385
  ```
296
386
 
297
387
  ---
@@ -444,6 +534,74 @@ interface FestivalInfo {
444
534
  // Pradosha: Krishna Trayodashi only (tithi 27)
445
535
  // Sankranti: Sun within 1° past a rashi boundary (degInRashi < 1.0)
446
536
  // Fixed festivals (e.g. Diwali): matched by chandramasa index + tithi index; skipped during Adhika months
537
+
538
+ // ── Jyotish (Vedic astrology) ────────────────────────────────────────────────
539
+
540
+ type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter' | 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';
541
+
542
+ interface GrahaPosition {
543
+ planet: GrahaName;
544
+ siderealLongitude: number; // degrees [0, 360)
545
+ rashi: RashiInfo; // zodiac sign
546
+ degreeInRashi: number; // degrees within sign [0, 30)
547
+ nakshatra: NakshatraInfo; // nakshatra + pada + completion %
548
+ isRetrograde: boolean; // always false for Sun/Moon; always true for Rahu/Ketu
549
+ house: number; // 1–12 (whole-sign system, relative to Lagna)
550
+ }
551
+
552
+ interface PlanetaryPositions {
553
+ sun: GrahaPosition; moon: GrahaPosition; mars: GrahaPosition;
554
+ mercury: GrahaPosition; jupiter: GrahaPosition; venus: GrahaPosition;
555
+ saturn: GrahaPosition; rahu: GrahaPosition; ketu: GrahaPosition;
556
+ }
557
+
558
+ interface KundliHouse {
559
+ number: number; // 1–12
560
+ rashi: RashiInfo; // sign on this house cusp
561
+ planets: string[]; // short abbreviations: "Su", "Mo", "Ma", "Me", "Ju", "Ve", "Sa", "Ra", "Ke"
562
+ }
563
+
564
+ interface NavamsaPosition {
565
+ planet: GrahaName;
566
+ rashi: RashiInfo; // D-9 (Navamsa) sign for this planet
567
+ }
568
+
569
+ interface NavamsaChart {
570
+ positions: NavamsaPosition[];
571
+ lagna: RashiInfo; // Navamsa Lagna sign
572
+ }
573
+
574
+ type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars' | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
575
+
576
+ interface AntarDasha {
577
+ lord: DashaLord;
578
+ startDate: Date;
579
+ endDate: Date;
580
+ }
581
+
582
+ interface MahaDasha {
583
+ lord: DashaLord;
584
+ startDate: Date;
585
+ endDate: Date;
586
+ years: number; // full duration in years (proportional for the first/partial dasha)
587
+ antarDashas: AntarDasha[];
588
+ }
589
+
590
+ interface VimshottariDashaResult {
591
+ currentMahaDashaLord: DashaLord; // active Mahadasha as of today
592
+ currentIndex: number; // index into mahaDashas
593
+ mahaDashas: MahaDasha[]; // 9-entry sequence starting from birth
594
+ }
595
+
596
+ interface KundliResult {
597
+ lagnaLongitude: number; // sidereal ascendant in degrees [0, 360)
598
+ lagna: RashiInfo; // ascendant sign
599
+ houses: KundliHouse[]; // 12 houses (whole-sign)
600
+ grahas: PlanetaryPositions; // all 9 graha positions
601
+ navamsa: NavamsaChart; // D-9 chart
602
+ birthPanchang: InstantPanchangResult;
603
+ dasha: VimshottariDashaResult;
604
+ }
447
605
  ```
448
606
 
449
607
  ---
package/dist/index.cjs CHANGED
@@ -76,6 +76,13 @@ var JUPITER_GM = 2825345909524226e-22;
76
76
  var SATURN_GM = 8459715185680659e-23;
77
77
  var URANUS_GM = 1292024916781969e-23;
78
78
  var NEPTUNE_GM = 1524358900784276e-23;
79
+ function VerifyBoolean(b) {
80
+ if (b !== true && b !== false) {
81
+ console.trace();
82
+ throw `Value is not boolean: ${b}`;
83
+ }
84
+ return b;
85
+ }
79
86
  function VerifyNumber(x) {
80
87
  if (!Number.isFinite(x)) {
81
88
  console.trace();
@@ -1372,6 +1379,10 @@ function sidereal_time(time) {
1372
1379
  }
1373
1380
  return sidereal_time_cache.st;
1374
1381
  }
1382
+ function SiderealTime(date) {
1383
+ const time = MakeTime(date);
1384
+ return sidereal_time(time);
1385
+ }
1375
1386
  function terra(observer, st) {
1376
1387
  const phi = observer.latitude * DEG2RAD;
1377
1388
  const sinphi = Math.sin(phi);
@@ -1594,6 +1605,8 @@ function SunPosition(date) {
1594
1605
  }
1595
1606
  function Equator(body, date, observer, ofdate, aberration) {
1596
1607
  VerifyObserver(observer);
1608
+ VerifyBoolean(ofdate);
1609
+ VerifyBoolean(aberration);
1597
1610
  const time = MakeTime(date);
1598
1611
  const gc_observer = geo_pos(time, observer);
1599
1612
  const gc = GeoVector(body, time, aberration);
@@ -2099,24 +2112,30 @@ var BodyPosition = class {
2099
2112
  }
2100
2113
  };
2101
2114
  function BackdatePosition(date, observerBody, targetBody, aberration) {
2115
+ VerifyBoolean(aberration);
2102
2116
  const time = MakeTime(date);
2103
2117
  if (UserDefinedStar(targetBody)) {
2104
2118
  const tvec = HelioVector(targetBody, time);
2105
- {
2119
+ if (aberration) {
2106
2120
  const ostate = HelioState(observerBody, time);
2107
2121
  const rvec = new Vector(tvec.x - ostate.x, tvec.y - ostate.y, tvec.z - ostate.z, time);
2108
2122
  const s = C_AUDAY / rvec.Length();
2109
2123
  return new Vector(rvec.x + ostate.vx / s, rvec.y + ostate.vy / s, rvec.z + ostate.vz / s, time);
2110
2124
  }
2125
+ const ovec = HelioVector(observerBody, time);
2126
+ return new Vector(tvec.x - ovec.x, tvec.y - ovec.y, tvec.z - ovec.z, time);
2111
2127
  }
2112
2128
  let observerPos;
2113
- {
2129
+ if (aberration) {
2114
2130
  observerPos = new Vector(0, 0, 0, time);
2131
+ } else {
2132
+ observerPos = HelioVector(observerBody, time);
2115
2133
  }
2116
2134
  const bpos = new BodyPosition(observerBody, targetBody, aberration, observerPos);
2117
2135
  return CorrectLightTravel((t) => bpos.Position(t), time);
2118
2136
  }
2119
2137
  function GeoVector(body, date, aberration) {
2138
+ VerifyBoolean(aberration);
2120
2139
  const time = MakeTime(date);
2121
2140
  switch (body) {
2122
2141
  case Body.Earth:
@@ -2530,6 +2549,12 @@ function normalize360(degrees) {
2530
2549
  if (r === 0) return 0;
2531
2550
  return r < 0 ? r + 360 : r;
2532
2551
  }
2552
+ function degToRad(degrees) {
2553
+ return degrees * (Math.PI / 180);
2554
+ }
2555
+ function radToDeg(radians) {
2556
+ return radians * (180 / Math.PI);
2557
+ }
2533
2558
 
2534
2559
  // src/astronomy/moon.ts
2535
2560
  function getSiderealMoonLongitude(date, ayanamsaType) {
@@ -3994,6 +4019,296 @@ function getDailyPanchang(date, location, options) {
3994
4019
  }
3995
4020
  };
3996
4021
  }
4022
+
4023
+ // src/jyotish/planets.ts
4024
+ var GRAHA_ABBR = {
4025
+ Sun: "Su",
4026
+ Moon: "Mo",
4027
+ Mars: "Ma",
4028
+ Mercury: "Me",
4029
+ Jupiter: "Ju",
4030
+ Venus: "Ve",
4031
+ Saturn: "Sa",
4032
+ Rahu: "Ra",
4033
+ Ketu: "Ke"
4034
+ };
4035
+ function meanObliquity(T) {
4036
+ return 23.439291111 - 0.013004167 * T - 164e-9 * T * T + 504e-9 * T * T * T;
4037
+ }
4038
+ function getTropicalPlanetLongitude(body, date) {
4039
+ const vec = GeoVector(body, MakeTime(date), true);
4040
+ return Ecliptic(vec).elon;
4041
+ }
4042
+ function isRetrograde(body, date) {
4043
+ const dt = 36e5;
4044
+ const lon0 = Ecliptic(GeoVector(body, MakeTime(new Date(date.getTime() - dt)), false)).elon;
4045
+ const lon1 = Ecliptic(GeoVector(body, MakeTime(new Date(date.getTime() + dt)), false)).elon;
4046
+ let delta = lon1 - lon0;
4047
+ if (delta > 180) delta -= 360;
4048
+ if (delta < -180) delta += 360;
4049
+ return delta < 0;
4050
+ }
4051
+ function getTrueRahuLongitudeTropical(date) {
4052
+ const T = (dateToJulianDay(date) - 2451545) / 36525;
4053
+ const omega = 125.04455501 - 1934.13626197 * T + 207765e-8 * T * T;
4054
+ const F = normalize360(
4055
+ 93.27191028 + 483202.0175233 * T - 36825e-7 * T * T + 3083e-9 * T * T * T
4056
+ );
4057
+ const F_rad = F * Math.PI / 180;
4058
+ const correction = -1.4979 * Math.sin(2 * F_rad) - 0.15 * Math.sin(0 * F_rad + Math.PI * 2 * (357.5 / 360)) - 0.1226 * Math.sin(2 * (omega * Math.PI / 180)) + 0.1176 * Math.sin(2 * F_rad - 2 * (omega * Math.PI / 180));
4059
+ return normalize360(omega + correction / 60);
4060
+ }
4061
+ function buildGrahaPosition(planet, siderealLon, isRetro, nakshatraNameFn, rashiNameFn) {
4062
+ const rashiIndex = Math.floor(siderealLon / 30);
4063
+ const degreeInRashi = siderealLon - rashiIndex * 30;
4064
+ const nakIdx = Math.floor(siderealLon / NAKSHATRA_SPAN);
4065
+ return {
4066
+ planet,
4067
+ siderealLongitude: siderealLon,
4068
+ rashi: { index: rashiIndex, name: rashiNameFn(rashiIndex) },
4069
+ degreeInRashi,
4070
+ nakshatra: computeNakshatraFromLongitude(siderealLon, nakshatraNameFn(nakIdx)),
4071
+ isRetrograde: isRetro,
4072
+ house: 0
4073
+ // assigned later in computeKundli
4074
+ };
4075
+ }
4076
+ var identity = (idx) => String(idx);
4077
+ function computePlanetaryPositions(date, ayanamsaType, nakshatraName = identity, rashiName = identity) {
4078
+ const ayanamsa = computeAyanamsa(date, ayanamsaType);
4079
+ const toSidereal = (tropical) => normalize360(tropical - ayanamsa);
4080
+ const sunSid = getSiderealSunLongitude(date, ayanamsaType);
4081
+ const moonSid = getSiderealMoonLongitude(date, ayanamsaType);
4082
+ const marsTrop = getTropicalPlanetLongitude(Body.Mars, date);
4083
+ const mercTrop = getTropicalPlanetLongitude(Body.Mercury, date);
4084
+ const jupTrop = getTropicalPlanetLongitude(Body.Jupiter, date);
4085
+ const venTrop = getTropicalPlanetLongitude(Body.Venus, date);
4086
+ const satTrop = getTropicalPlanetLongitude(Body.Saturn, date);
4087
+ const rahuTrop = getTrueRahuLongitudeTropical(date);
4088
+ const ketuTrop = normalize360(rahuTrop + 180);
4089
+ const marsRetro = isRetrograde(Body.Mars, date);
4090
+ const mercRetro = isRetrograde(Body.Mercury, date);
4091
+ const jupRetro = isRetrograde(Body.Jupiter, date);
4092
+ const venRetro = isRetrograde(Body.Venus, date);
4093
+ const satRetro = isRetrograde(Body.Saturn, date);
4094
+ const g = (planet, sid, retro) => buildGrahaPosition(planet, sid, retro, nakshatraName, rashiName);
4095
+ return {
4096
+ sun: g("Sun", sunSid, false),
4097
+ moon: g("Moon", moonSid, false),
4098
+ mars: g("Mars", toSidereal(marsTrop), marsRetro),
4099
+ mercury: g("Mercury", toSidereal(mercTrop), mercRetro),
4100
+ jupiter: g("Jupiter", toSidereal(jupTrop), jupRetro),
4101
+ venus: g("Venus", toSidereal(venTrop), venRetro),
4102
+ saturn: g("Saturn", toSidereal(satTrop), satRetro),
4103
+ rahu: g("Rahu", toSidereal(rahuTrop), true),
4104
+ // always retrograde
4105
+ ketu: g("Ketu", toSidereal(ketuTrop), true)
4106
+ };
4107
+ }
4108
+
4109
+ // src/jyotish/lagna.ts
4110
+ function computeLagnaLongitude(date, latitude, longitude, ayanamsaType) {
4111
+ const gastHours = SiderealTime(MakeTime(date));
4112
+ const last_deg = normalize360(gastHours * 15 + longitude);
4113
+ const T = (dateToJulianDay(date) - 2451545) / 36525;
4114
+ const eps_deg = meanObliquity(T);
4115
+ const ramc = degToRad(last_deg);
4116
+ const eps = degToRad(eps_deg);
4117
+ const phi = degToRad(latitude);
4118
+ const y = -Math.cos(ramc);
4119
+ const x = Math.sin(ramc) * Math.cos(eps) + Math.tan(phi) * Math.sin(eps);
4120
+ let tropicalAsc = normalize360(radToDeg(Math.atan2(y, x)));
4121
+ const mcRaw = radToDeg(Math.atan2(Math.tan(ramc), Math.cos(eps)));
4122
+ const mc = normalize360(
4123
+ last_deg < 180 ? normalize360(mcRaw) : normalize360(mcRaw + 180)
4124
+ );
4125
+ const diff = normalize360(tropicalAsc - mc);
4126
+ if (diff < 90 || diff > 270) {
4127
+ tropicalAsc = normalize360(tropicalAsc + 180);
4128
+ }
4129
+ const ayanamsa = computeAyanamsa(date, ayanamsaType);
4130
+ return normalize360(tropicalAsc - ayanamsa);
4131
+ }
4132
+ function navamsaRashi(siderealLongitude) {
4133
+ const rashiIdx = Math.floor(siderealLongitude / 30);
4134
+ const degInRashi = siderealLongitude - rashiIdx * 30;
4135
+ const navamsaIndex = Math.floor(degInRashi / (30 / 9));
4136
+ const NAVAMSA_STARTS = [0, 9, 6, 3, 0, 9, 6, 3, 0, 9, 6, 3];
4137
+ return (NAVAMSA_STARTS[rashiIdx] + navamsaIndex) % 12;
4138
+ }
4139
+ function rashiFromLongitude(lon, nameFn) {
4140
+ const index = Math.floor(lon / 30);
4141
+ return { index, name: nameFn(index) };
4142
+ }
4143
+
4144
+ // src/jyotish/dasha.ts
4145
+ var DASHA_YEARS = {
4146
+ Ketu: 7,
4147
+ Venus: 20,
4148
+ Sun: 6,
4149
+ Moon: 10,
4150
+ Mars: 7,
4151
+ Rahu: 18,
4152
+ Jupiter: 16,
4153
+ Saturn: 19,
4154
+ Mercury: 17
4155
+ };
4156
+ var DASHA_ORDER = [
4157
+ "Ketu",
4158
+ "Venus",
4159
+ "Sun",
4160
+ "Moon",
4161
+ "Mars",
4162
+ "Rahu",
4163
+ "Jupiter",
4164
+ "Saturn",
4165
+ "Mercury"
4166
+ ];
4167
+ var NAKSHATRA_LORD = [
4168
+ "Ketu",
4169
+ "Venus",
4170
+ "Sun",
4171
+ "Moon",
4172
+ "Mars",
4173
+ "Rahu",
4174
+ "Jupiter",
4175
+ "Saturn",
4176
+ "Mercury",
4177
+ "Ketu",
4178
+ "Venus",
4179
+ "Sun",
4180
+ "Moon",
4181
+ "Mars",
4182
+ "Rahu",
4183
+ "Jupiter",
4184
+ "Saturn",
4185
+ "Mercury",
4186
+ "Ketu",
4187
+ "Venus",
4188
+ "Sun",
4189
+ "Moon",
4190
+ "Mars",
4191
+ "Rahu",
4192
+ "Jupiter",
4193
+ "Saturn",
4194
+ "Mercury"
4195
+ ];
4196
+ var MS_PER_YEAR = 365.25 * 24 * 3600 * 1e3;
4197
+ function computeVimshottariDasha(birthDate, moonSiderealLon) {
4198
+ const nakIdx = Math.floor(moonSiderealLon / NAKSHATRA_SPAN);
4199
+ const degInNak = moonSiderealLon - nakIdx * NAKSHATRA_SPAN;
4200
+ const elapsedFraction = degInNak / NAKSHATRA_SPAN;
4201
+ const startLord = NAKSHATRA_LORD[nakIdx];
4202
+ const startLordIdx = DASHA_ORDER.indexOf(startLord);
4203
+ const startLordYears = DASHA_YEARS[startLord];
4204
+ const balanceMs = (1 - elapsedFraction) * startLordYears * MS_PER_YEAR;
4205
+ const mahaDashas = [];
4206
+ let cursor = new Date(birthDate.getTime());
4207
+ for (let i = 0; i < 9; i++) {
4208
+ const lordIdx = (startLordIdx + i) % 9;
4209
+ const lord = DASHA_ORDER[lordIdx];
4210
+ const years = DASHA_YEARS[lord];
4211
+ const durationMs = i === 0 ? balanceMs : years * MS_PER_YEAR;
4212
+ const startDate = new Date(cursor.getTime());
4213
+ const endDate = new Date(cursor.getTime() + durationMs);
4214
+ const antarDashas = buildAntarDashas(lord, startDate, durationMs);
4215
+ mahaDashas.push({ lord, startDate, endDate, years, antarDashas });
4216
+ cursor = endDate;
4217
+ }
4218
+ const now = /* @__PURE__ */ new Date();
4219
+ const currentIndex = mahaDashas.findIndex(
4220
+ (md) => now >= md.startDate && now < md.endDate
4221
+ );
4222
+ return {
4223
+ currentMahaDashaLord: mahaDashas[Math.max(0, currentIndex)].lord,
4224
+ currentIndex: Math.max(0, currentIndex),
4225
+ mahaDashas
4226
+ };
4227
+ }
4228
+ function buildAntarDashas(mahaLord, mahaStart, mahaDurationMs) {
4229
+ const mahaIdx = DASHA_ORDER.indexOf(mahaLord);
4230
+ const antarDashas = [];
4231
+ let cursor = new Date(mahaStart.getTime());
4232
+ for (let i = 0; i < 9; i++) {
4233
+ const antarLordIdx = (mahaIdx + i) % 9;
4234
+ const antarLord = DASHA_ORDER[antarLordIdx];
4235
+ const antarYears = DASHA_YEARS[antarLord];
4236
+ const antarMs = antarYears / 120 * mahaDurationMs;
4237
+ const startDate = new Date(cursor.getTime());
4238
+ const endDate = new Date(cursor.getTime() + antarMs);
4239
+ antarDashas.push({ lord: antarLord, startDate, endDate });
4240
+ cursor = endDate;
4241
+ }
4242
+ return antarDashas;
4243
+ }
4244
+
4245
+ // src/jyotish/kundli.ts
4246
+ function computeKundli(birthDateUtc, location, options) {
4247
+ validateDate(birthDateUtc);
4248
+ validateLocation(location);
4249
+ const ayanamsa = options?.ayanamsa ?? "lahiri";
4250
+ const lang = options?.language ?? "en";
4251
+ const doDasha = options?.computeDasha !== false;
4252
+ const rashiName = (idx) => resolveMasaName(idx, lang);
4253
+ const nakshatraName = (idx) => resolveNakshatraName(idx, lang);
4254
+ const birthPanchang = getInstantPanchang(birthDateUtc, location, {
4255
+ ayanamsa,
4256
+ language: lang,
4257
+ computeEndTimes: options?.computeEndTimes ?? false,
4258
+ precision: options?.precision
4259
+ });
4260
+ const grahas = computePlanetaryPositions(birthDateUtc, ayanamsa, nakshatraName, rashiName);
4261
+ const lagnaLon = computeLagnaLongitude(
4262
+ birthDateUtc,
4263
+ location.latitude,
4264
+ location.longitude,
4265
+ ayanamsa
4266
+ );
4267
+ const lagna = rashiFromLongitude(lagnaLon, rashiName);
4268
+ const lagnaRashiIdx = lagna.index;
4269
+ const houses = Array.from({ length: 12 }, (_, i) => {
4270
+ const houseRashiIdx = (lagnaRashiIdx + i) % 12;
4271
+ return {
4272
+ number: i + 1,
4273
+ rashi: { index: houseRashiIdx, name: rashiName(houseRashiIdx) },
4274
+ planets: []
4275
+ };
4276
+ });
4277
+ const grahaList = Object.values(grahas);
4278
+ for (const graha of grahaList) {
4279
+ const houseNumber = (graha.rashi.index - lagnaRashiIdx + 12) % 12 + 1;
4280
+ graha.house = houseNumber;
4281
+ houses[houseNumber - 1].planets.push(GRAHA_ABBR[graha.planet]);
4282
+ }
4283
+ const navamsa = buildNavamsaChart(grahas, lagnaLon, rashiName);
4284
+ const dasha = doDasha ? computeVimshottariDasha(birthDateUtc, birthPanchang.siderealMoon) : { currentMahaDashaLord: "Sun", currentIndex: 0, mahaDashas: [] };
4285
+ return {
4286
+ lagnaLongitude: lagnaLon,
4287
+ lagna,
4288
+ houses,
4289
+ grahas,
4290
+ navamsa,
4291
+ birthPanchang,
4292
+ dasha
4293
+ };
4294
+ }
4295
+ function buildNavamsaChart(grahas, lagnaLon, rashiName) {
4296
+ const grahaList = Object.values(grahas);
4297
+ const positions = grahaList.map((g) => ({
4298
+ planet: g.planet,
4299
+ rashi: {
4300
+ index: navamsaRashi(g.siderealLongitude),
4301
+ name: rashiName(navamsaRashi(g.siderealLongitude))
4302
+ }
4303
+ }));
4304
+ return {
4305
+ positions,
4306
+ lagna: {
4307
+ index: navamsaRashi(lagnaLon),
4308
+ name: rashiName(navamsaRashi(lagnaLon))
4309
+ }
4310
+ };
4311
+ }
3997
4312
  /*! Bundled license information:
3998
4313
 
3999
4314
  astronomy-engine/esm/astronomy.js:
@@ -4032,12 +4347,17 @@ astronomy-engine/esm/astronomy.js:
4032
4347
  *)
4033
4348
  */
4034
4349
 
4350
+ exports.GRAHA_ABBR = GRAHA_ABBR;
4035
4351
  exports.PanchangError = PanchangError;
4036
4352
  exports.computeAbhijitMuhurta = computeAbhijitMuhurta;
4037
4353
  exports.computeBrahmaMuhurta = computeBrahmaMuhurta;
4038
4354
  exports.computeGowriPanchangam = computeGowriPanchangam;
4039
4355
  exports.computeGulikaKalam = computeGulikaKalam;
4356
+ exports.computeKundli = computeKundli;
4357
+ exports.computeLagnaLongitude = computeLagnaLongitude;
4358
+ exports.computePlanetaryPositions = computePlanetaryPositions;
4040
4359
  exports.computeRahuKalam = computeRahuKalam;
4360
+ exports.computeVimshottariDasha = computeVimshottariDasha;
4041
4361
  exports.computeYamaganda = computeYamaganda;
4042
4362
  exports.getAyanamsa = computeAyanamsa;
4043
4363
  exports.getDailyPanchang = getDailyPanchang;
package/dist/index.d.cts CHANGED
@@ -277,6 +277,157 @@ declare function getInstantPanchang(date: Date, location: GeoLocation, options?:
277
277
  */
278
278
  declare function getDailyPanchang(date: Date, location: GeoLocation, options: PanchangOptions): DailyPanchangResult;
279
279
 
280
+ type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter' | 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';
281
+ interface GrahaPosition {
282
+ /** Planet name in English */
283
+ planet: GrahaName;
284
+ /** Sidereal ecliptic longitude in degrees [0, 360) */
285
+ siderealLongitude: number;
286
+ /** Zodiac sign the planet occupies */
287
+ rashi: RashiInfo;
288
+ /** Degrees within the sign [0, 30) */
289
+ degreeInRashi: number;
290
+ /** Nakshatra the planet occupies */
291
+ nakshatra: NakshatraInfo;
292
+ /**
293
+ * True when the planet appears to move retrograde (west relative to stars).
294
+ * Always false for Sun and Moon (they never retrograde).
295
+ * Rahu/Ketu are always retrograde by definition.
296
+ */
297
+ isRetrograde: boolean;
298
+ /** 1-based house number where this planet sits (set after house assignment) */
299
+ house: number;
300
+ }
301
+ interface PlanetaryPositions {
302
+ sun: GrahaPosition;
303
+ moon: GrahaPosition;
304
+ mars: GrahaPosition;
305
+ mercury: GrahaPosition;
306
+ jupiter: GrahaPosition;
307
+ venus: GrahaPosition;
308
+ saturn: GrahaPosition;
309
+ rahu: GrahaPosition;
310
+ ketu: GrahaPosition;
311
+ }
312
+ interface KundliHouse {
313
+ /** 1–12 */
314
+ number: number;
315
+ /** Zodiac sign on the cusp of this house */
316
+ rashi: RashiInfo;
317
+ /** Short planet abbreviations occupying this house (e.g. "Su", "Mo") */
318
+ planets: string[];
319
+ }
320
+ interface NavamsaPosition {
321
+ planet: GrahaName;
322
+ /** Navamsa rashi (D-9 sign) */
323
+ rashi: RashiInfo;
324
+ }
325
+ interface NavamsaChart {
326
+ positions: NavamsaPosition[];
327
+ /** Navamsa Lagna rashi */
328
+ lagna: RashiInfo;
329
+ }
330
+ interface KundliResult {
331
+ /** Sidereal longitude of the Ascendant in degrees [0, 360) */
332
+ lagnaLongitude: number;
333
+ /** Ascendant sign */
334
+ lagna: RashiInfo;
335
+ /** 12 houses, 1st house = lagna sign */
336
+ houses: KundliHouse[];
337
+ /** All 9 graha positions with house assignments */
338
+ grahas: PlanetaryPositions;
339
+ /** Navamsa (D-9) divisional chart */
340
+ navamsa: NavamsaChart;
341
+ /** Full Panchang at the birth moment */
342
+ birthPanchang: InstantPanchangResult;
343
+ /** Vimshottari Dasha from birth */
344
+ dasha: VimshottariDashaResult;
345
+ }
346
+ type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars' | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
347
+ interface AntarDasha {
348
+ lord: DashaLord;
349
+ startDate: Date;
350
+ endDate: Date;
351
+ }
352
+ interface MahaDasha {
353
+ lord: DashaLord;
354
+ startDate: Date;
355
+ endDate: Date;
356
+ /** Duration in years */
357
+ years: number;
358
+ antarDashas: AntarDasha[];
359
+ }
360
+ interface VimshottariDashaResult {
361
+ /** The current active Mahadasha lord */
362
+ currentMahaDashaLord: DashaLord;
363
+ /** Index into mahaDashas of the current mahadasha */
364
+ currentIndex: number;
365
+ /**
366
+ * Full 120-year sequence starting from birth.
367
+ * The first entry is the dasha active at birth (possibly mid-cycle).
368
+ */
369
+ mahaDashas: MahaDasha[];
370
+ }
371
+
372
+ interface KundliOptions extends InstantPanchangOptions {
373
+ /**
374
+ * When true, compute Vimshottari Dasha periods.
375
+ * Slightly more expensive. Default: true.
376
+ */
377
+ computeDasha?: boolean;
378
+ }
379
+ /**
380
+ * Compute a complete Janam Kundli (birth chart).
381
+ *
382
+ * Returns planetary positions, Lagna, 12 houses, Navamsa (D-9) chart,
383
+ * Vimshottari Dasha periods, and the full birth Panchang.
384
+ *
385
+ * @param birthDateUtc UTC birth moment. For times stored as "local time in UTC"
386
+ * (the panchang-ts convention), pass the Date as-is.
387
+ * @param location Birth location coordinates.
388
+ * @param options Optional ayanamsa, language, computeDasha flag.
389
+ */
390
+ declare function computeKundli(birthDateUtc: Date, location: GeoLocation, options?: KundliOptions): KundliResult;
391
+
392
+ declare const GRAHA_ABBR: Record<GrahaName, string>;
393
+ /**
394
+ * Compute geocentric sidereal positions for all 9 grahas.
395
+ *
396
+ * @param date UTC instant.
397
+ * @param ayanamsaType Ayanamsa system.
398
+ * @param nakshatraName Function returning the Nakshatra name for an index.
399
+ * @param rashiName Function returning the Rashi name for an index.
400
+ */
401
+ declare function computePlanetaryPositions(date: Date, ayanamsaType: AyanamsaType, nakshatraName?: (idx: number) => string, rashiName?: (idx: number) => string): PlanetaryPositions;
402
+
403
+ /**
404
+ * Compute the complete Vimshottari Dasha sequence from the birth moment.
405
+ *
406
+ * @param birthDate UTC birth time.
407
+ * @param moonSiderealLon Sidereal longitude of the Moon at birth [0, 360).
408
+ */
409
+ declare function computeVimshottariDasha(birthDate: Date, moonSiderealLon: number): VimshottariDashaResult;
410
+
411
+ /**
412
+ * Compute the sidereal longitude of the Lagna (Ascendant) for a given
413
+ * UTC birth time and observer location.
414
+ *
415
+ * Algorithm (Meeus, Astronomical Algorithms, Ch. 14):
416
+ * 1. GAST = SiderealTime(date) in hours → degrees
417
+ * 2. LAST = GAST + observerLongitude (in degrees)
418
+ * 3. RAMC = LAST (Right Ascension of the Midheaven Culminating)
419
+ * 4. tan(λ_asc) = -cos(RAMC) / (sin(RAMC)·cos(ε) + tan(φ)·sin(ε))
420
+ * where ε = mean obliquity, φ = geographic latitude
421
+ * 5. Sidereal Lagna = normalize(λ_asc − ayanamsa)
422
+ *
423
+ * @param date UTC birth moment.
424
+ * @param latitude Observer geographic latitude in degrees (−90 to +90).
425
+ * @param longitude Observer geographic longitude in degrees (−180 to +180).
426
+ * @param ayanamsaType Ayanamsa correction system.
427
+ * @returns Sidereal ascendant longitude [0, 360).
428
+ */
429
+ declare function computeLagnaLongitude(date: Date, latitude: number, longitude: number, ayanamsaType: AyanamsaType): number;
430
+
280
431
  /**
281
432
  * Compute sunrise nearest to (and after) the given UTC search start.
282
433
  *
@@ -436,4 +587,4 @@ declare class PanchangError extends Error {
436
587
  constructor(message: string, code: PanchangErrorCode);
437
588
  }
438
589
 
439
- export { type AyanamsaType, type ChandraMasaInfo, type ChoghadiyaInfo, type ChoghadiyaQuality, type ChoghadiyaSlot, type DailyKaranaInfo, type DailyNakshatraInfo, type DailyPanchangResult, type DailyTithiInfo, type DailyYogaInfo, type FestivalInfo, type GeoLocation, type GowriInfo, type GowriSlot, type HoraInfo, type HoraSlot, type InstantPanchangOptions, type InstantPanchangResult, type KaranaInfo, type Language, type MasaInfo, type NakshatraInfo, PanchangError, type PanchangErrorCode, type PanchangOptions, type Precision, type RashiInfo, type SamvatInfo, type SpecialYogaInfo, type TimePeriod, type TithiInfo, type VaraInfo, type YogaInfo, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computeRahuKalam, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
590
+ export { type AntarDasha, type AyanamsaType, type ChandraMasaInfo, type ChoghadiyaInfo, type ChoghadiyaQuality, type ChoghadiyaSlot, type DailyKaranaInfo, type DailyNakshatraInfo, type DailyPanchangResult, type DailyTithiInfo, type DailyYogaInfo, type DashaLord, type FestivalInfo, GRAHA_ABBR, type GeoLocation, type GowriInfo, type GowriSlot, type GrahaName, type GrahaPosition, type HoraInfo, type HoraSlot, type InstantPanchangOptions, type InstantPanchangResult, type KaranaInfo, type KundliHouse, type KundliOptions, type KundliResult, type Language, type MahaDasha, type MasaInfo, type NakshatraInfo, type NavamsaChart, type NavamsaPosition, PanchangError, type PanchangErrorCode, type PanchangOptions, type PlanetaryPositions, type Precision, type RashiInfo, type SamvatInfo, type SpecialYogaInfo, type TimePeriod, type TithiInfo, type VaraInfo, type VimshottariDashaResult, type YogaInfo, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computeKundli, computeLagnaLongitude, computePlanetaryPositions, computeRahuKalam, computeVimshottariDasha, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
package/dist/index.d.ts CHANGED
@@ -277,6 +277,157 @@ declare function getInstantPanchang(date: Date, location: GeoLocation, options?:
277
277
  */
278
278
  declare function getDailyPanchang(date: Date, location: GeoLocation, options: PanchangOptions): DailyPanchangResult;
279
279
 
280
+ type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter' | 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';
281
+ interface GrahaPosition {
282
+ /** Planet name in English */
283
+ planet: GrahaName;
284
+ /** Sidereal ecliptic longitude in degrees [0, 360) */
285
+ siderealLongitude: number;
286
+ /** Zodiac sign the planet occupies */
287
+ rashi: RashiInfo;
288
+ /** Degrees within the sign [0, 30) */
289
+ degreeInRashi: number;
290
+ /** Nakshatra the planet occupies */
291
+ nakshatra: NakshatraInfo;
292
+ /**
293
+ * True when the planet appears to move retrograde (west relative to stars).
294
+ * Always false for Sun and Moon (they never retrograde).
295
+ * Rahu/Ketu are always retrograde by definition.
296
+ */
297
+ isRetrograde: boolean;
298
+ /** 1-based house number where this planet sits (set after house assignment) */
299
+ house: number;
300
+ }
301
+ interface PlanetaryPositions {
302
+ sun: GrahaPosition;
303
+ moon: GrahaPosition;
304
+ mars: GrahaPosition;
305
+ mercury: GrahaPosition;
306
+ jupiter: GrahaPosition;
307
+ venus: GrahaPosition;
308
+ saturn: GrahaPosition;
309
+ rahu: GrahaPosition;
310
+ ketu: GrahaPosition;
311
+ }
312
+ interface KundliHouse {
313
+ /** 1–12 */
314
+ number: number;
315
+ /** Zodiac sign on the cusp of this house */
316
+ rashi: RashiInfo;
317
+ /** Short planet abbreviations occupying this house (e.g. "Su", "Mo") */
318
+ planets: string[];
319
+ }
320
+ interface NavamsaPosition {
321
+ planet: GrahaName;
322
+ /** Navamsa rashi (D-9 sign) */
323
+ rashi: RashiInfo;
324
+ }
325
+ interface NavamsaChart {
326
+ positions: NavamsaPosition[];
327
+ /** Navamsa Lagna rashi */
328
+ lagna: RashiInfo;
329
+ }
330
+ interface KundliResult {
331
+ /** Sidereal longitude of the Ascendant in degrees [0, 360) */
332
+ lagnaLongitude: number;
333
+ /** Ascendant sign */
334
+ lagna: RashiInfo;
335
+ /** 12 houses, 1st house = lagna sign */
336
+ houses: KundliHouse[];
337
+ /** All 9 graha positions with house assignments */
338
+ grahas: PlanetaryPositions;
339
+ /** Navamsa (D-9) divisional chart */
340
+ navamsa: NavamsaChart;
341
+ /** Full Panchang at the birth moment */
342
+ birthPanchang: InstantPanchangResult;
343
+ /** Vimshottari Dasha from birth */
344
+ dasha: VimshottariDashaResult;
345
+ }
346
+ type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars' | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
347
+ interface AntarDasha {
348
+ lord: DashaLord;
349
+ startDate: Date;
350
+ endDate: Date;
351
+ }
352
+ interface MahaDasha {
353
+ lord: DashaLord;
354
+ startDate: Date;
355
+ endDate: Date;
356
+ /** Duration in years */
357
+ years: number;
358
+ antarDashas: AntarDasha[];
359
+ }
360
+ interface VimshottariDashaResult {
361
+ /** The current active Mahadasha lord */
362
+ currentMahaDashaLord: DashaLord;
363
+ /** Index into mahaDashas of the current mahadasha */
364
+ currentIndex: number;
365
+ /**
366
+ * Full 120-year sequence starting from birth.
367
+ * The first entry is the dasha active at birth (possibly mid-cycle).
368
+ */
369
+ mahaDashas: MahaDasha[];
370
+ }
371
+
372
+ interface KundliOptions extends InstantPanchangOptions {
373
+ /**
374
+ * When true, compute Vimshottari Dasha periods.
375
+ * Slightly more expensive. Default: true.
376
+ */
377
+ computeDasha?: boolean;
378
+ }
379
+ /**
380
+ * Compute a complete Janam Kundli (birth chart).
381
+ *
382
+ * Returns planetary positions, Lagna, 12 houses, Navamsa (D-9) chart,
383
+ * Vimshottari Dasha periods, and the full birth Panchang.
384
+ *
385
+ * @param birthDateUtc UTC birth moment. For times stored as "local time in UTC"
386
+ * (the panchang-ts convention), pass the Date as-is.
387
+ * @param location Birth location coordinates.
388
+ * @param options Optional ayanamsa, language, computeDasha flag.
389
+ */
390
+ declare function computeKundli(birthDateUtc: Date, location: GeoLocation, options?: KundliOptions): KundliResult;
391
+
392
+ declare const GRAHA_ABBR: Record<GrahaName, string>;
393
+ /**
394
+ * Compute geocentric sidereal positions for all 9 grahas.
395
+ *
396
+ * @param date UTC instant.
397
+ * @param ayanamsaType Ayanamsa system.
398
+ * @param nakshatraName Function returning the Nakshatra name for an index.
399
+ * @param rashiName Function returning the Rashi name for an index.
400
+ */
401
+ declare function computePlanetaryPositions(date: Date, ayanamsaType: AyanamsaType, nakshatraName?: (idx: number) => string, rashiName?: (idx: number) => string): PlanetaryPositions;
402
+
403
+ /**
404
+ * Compute the complete Vimshottari Dasha sequence from the birth moment.
405
+ *
406
+ * @param birthDate UTC birth time.
407
+ * @param moonSiderealLon Sidereal longitude of the Moon at birth [0, 360).
408
+ */
409
+ declare function computeVimshottariDasha(birthDate: Date, moonSiderealLon: number): VimshottariDashaResult;
410
+
411
+ /**
412
+ * Compute the sidereal longitude of the Lagna (Ascendant) for a given
413
+ * UTC birth time and observer location.
414
+ *
415
+ * Algorithm (Meeus, Astronomical Algorithms, Ch. 14):
416
+ * 1. GAST = SiderealTime(date) in hours → degrees
417
+ * 2. LAST = GAST + observerLongitude (in degrees)
418
+ * 3. RAMC = LAST (Right Ascension of the Midheaven Culminating)
419
+ * 4. tan(λ_asc) = -cos(RAMC) / (sin(RAMC)·cos(ε) + tan(φ)·sin(ε))
420
+ * where ε = mean obliquity, φ = geographic latitude
421
+ * 5. Sidereal Lagna = normalize(λ_asc − ayanamsa)
422
+ *
423
+ * @param date UTC birth moment.
424
+ * @param latitude Observer geographic latitude in degrees (−90 to +90).
425
+ * @param longitude Observer geographic longitude in degrees (−180 to +180).
426
+ * @param ayanamsaType Ayanamsa correction system.
427
+ * @returns Sidereal ascendant longitude [0, 360).
428
+ */
429
+ declare function computeLagnaLongitude(date: Date, latitude: number, longitude: number, ayanamsaType: AyanamsaType): number;
430
+
280
431
  /**
281
432
  * Compute sunrise nearest to (and after) the given UTC search start.
282
433
  *
@@ -436,4 +587,4 @@ declare class PanchangError extends Error {
436
587
  constructor(message: string, code: PanchangErrorCode);
437
588
  }
438
589
 
439
- export { type AyanamsaType, type ChandraMasaInfo, type ChoghadiyaInfo, type ChoghadiyaQuality, type ChoghadiyaSlot, type DailyKaranaInfo, type DailyNakshatraInfo, type DailyPanchangResult, type DailyTithiInfo, type DailyYogaInfo, type FestivalInfo, type GeoLocation, type GowriInfo, type GowriSlot, type HoraInfo, type HoraSlot, type InstantPanchangOptions, type InstantPanchangResult, type KaranaInfo, type Language, type MasaInfo, type NakshatraInfo, PanchangError, type PanchangErrorCode, type PanchangOptions, type Precision, type RashiInfo, type SamvatInfo, type SpecialYogaInfo, type TimePeriod, type TithiInfo, type VaraInfo, type YogaInfo, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computeRahuKalam, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
590
+ export { type AntarDasha, type AyanamsaType, type ChandraMasaInfo, type ChoghadiyaInfo, type ChoghadiyaQuality, type ChoghadiyaSlot, type DailyKaranaInfo, type DailyNakshatraInfo, type DailyPanchangResult, type DailyTithiInfo, type DailyYogaInfo, type DashaLord, type FestivalInfo, GRAHA_ABBR, type GeoLocation, type GowriInfo, type GowriSlot, type GrahaName, type GrahaPosition, type HoraInfo, type HoraSlot, type InstantPanchangOptions, type InstantPanchangResult, type KaranaInfo, type KundliHouse, type KundliOptions, type KundliResult, type Language, type MahaDasha, type MasaInfo, type NakshatraInfo, type NavamsaChart, type NavamsaPosition, PanchangError, type PanchangErrorCode, type PanchangOptions, type PlanetaryPositions, type Precision, type RashiInfo, type SamvatInfo, type SpecialYogaInfo, type TimePeriod, type TithiInfo, type VaraInfo, type VimshottariDashaResult, type YogaInfo, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computeKundli, computeLagnaLongitude, computePlanetaryPositions, computeRahuKalam, computeVimshottariDasha, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
package/dist/index.js CHANGED
@@ -74,6 +74,13 @@ var JUPITER_GM = 2825345909524226e-22;
74
74
  var SATURN_GM = 8459715185680659e-23;
75
75
  var URANUS_GM = 1292024916781969e-23;
76
76
  var NEPTUNE_GM = 1524358900784276e-23;
77
+ function VerifyBoolean(b) {
78
+ if (b !== true && b !== false) {
79
+ console.trace();
80
+ throw `Value is not boolean: ${b}`;
81
+ }
82
+ return b;
83
+ }
77
84
  function VerifyNumber(x) {
78
85
  if (!Number.isFinite(x)) {
79
86
  console.trace();
@@ -1370,6 +1377,10 @@ function sidereal_time(time) {
1370
1377
  }
1371
1378
  return sidereal_time_cache.st;
1372
1379
  }
1380
+ function SiderealTime(date) {
1381
+ const time = MakeTime(date);
1382
+ return sidereal_time(time);
1383
+ }
1373
1384
  function terra(observer, st) {
1374
1385
  const phi = observer.latitude * DEG2RAD;
1375
1386
  const sinphi = Math.sin(phi);
@@ -1592,6 +1603,8 @@ function SunPosition(date) {
1592
1603
  }
1593
1604
  function Equator(body, date, observer, ofdate, aberration) {
1594
1605
  VerifyObserver(observer);
1606
+ VerifyBoolean(ofdate);
1607
+ VerifyBoolean(aberration);
1595
1608
  const time = MakeTime(date);
1596
1609
  const gc_observer = geo_pos(time, observer);
1597
1610
  const gc = GeoVector(body, time, aberration);
@@ -2097,24 +2110,30 @@ var BodyPosition = class {
2097
2110
  }
2098
2111
  };
2099
2112
  function BackdatePosition(date, observerBody, targetBody, aberration) {
2113
+ VerifyBoolean(aberration);
2100
2114
  const time = MakeTime(date);
2101
2115
  if (UserDefinedStar(targetBody)) {
2102
2116
  const tvec = HelioVector(targetBody, time);
2103
- {
2117
+ if (aberration) {
2104
2118
  const ostate = HelioState(observerBody, time);
2105
2119
  const rvec = new Vector(tvec.x - ostate.x, tvec.y - ostate.y, tvec.z - ostate.z, time);
2106
2120
  const s = C_AUDAY / rvec.Length();
2107
2121
  return new Vector(rvec.x + ostate.vx / s, rvec.y + ostate.vy / s, rvec.z + ostate.vz / s, time);
2108
2122
  }
2123
+ const ovec = HelioVector(observerBody, time);
2124
+ return new Vector(tvec.x - ovec.x, tvec.y - ovec.y, tvec.z - ovec.z, time);
2109
2125
  }
2110
2126
  let observerPos;
2111
- {
2127
+ if (aberration) {
2112
2128
  observerPos = new Vector(0, 0, 0, time);
2129
+ } else {
2130
+ observerPos = HelioVector(observerBody, time);
2113
2131
  }
2114
2132
  const bpos = new BodyPosition(observerBody, targetBody, aberration, observerPos);
2115
2133
  return CorrectLightTravel((t) => bpos.Position(t), time);
2116
2134
  }
2117
2135
  function GeoVector(body, date, aberration) {
2136
+ VerifyBoolean(aberration);
2118
2137
  const time = MakeTime(date);
2119
2138
  switch (body) {
2120
2139
  case Body.Earth:
@@ -2528,6 +2547,12 @@ function normalize360(degrees) {
2528
2547
  if (r === 0) return 0;
2529
2548
  return r < 0 ? r + 360 : r;
2530
2549
  }
2550
+ function degToRad(degrees) {
2551
+ return degrees * (Math.PI / 180);
2552
+ }
2553
+ function radToDeg(radians) {
2554
+ return radians * (180 / Math.PI);
2555
+ }
2531
2556
 
2532
2557
  // src/astronomy/moon.ts
2533
2558
  function getSiderealMoonLongitude(date, ayanamsaType) {
@@ -3992,6 +4017,296 @@ function getDailyPanchang(date, location, options) {
3992
4017
  }
3993
4018
  };
3994
4019
  }
4020
+
4021
+ // src/jyotish/planets.ts
4022
+ var GRAHA_ABBR = {
4023
+ Sun: "Su",
4024
+ Moon: "Mo",
4025
+ Mars: "Ma",
4026
+ Mercury: "Me",
4027
+ Jupiter: "Ju",
4028
+ Venus: "Ve",
4029
+ Saturn: "Sa",
4030
+ Rahu: "Ra",
4031
+ Ketu: "Ke"
4032
+ };
4033
+ function meanObliquity(T) {
4034
+ return 23.439291111 - 0.013004167 * T - 164e-9 * T * T + 504e-9 * T * T * T;
4035
+ }
4036
+ function getTropicalPlanetLongitude(body, date) {
4037
+ const vec = GeoVector(body, MakeTime(date), true);
4038
+ return Ecliptic(vec).elon;
4039
+ }
4040
+ function isRetrograde(body, date) {
4041
+ const dt = 36e5;
4042
+ const lon0 = Ecliptic(GeoVector(body, MakeTime(new Date(date.getTime() - dt)), false)).elon;
4043
+ const lon1 = Ecliptic(GeoVector(body, MakeTime(new Date(date.getTime() + dt)), false)).elon;
4044
+ let delta = lon1 - lon0;
4045
+ if (delta > 180) delta -= 360;
4046
+ if (delta < -180) delta += 360;
4047
+ return delta < 0;
4048
+ }
4049
+ function getTrueRahuLongitudeTropical(date) {
4050
+ const T = (dateToJulianDay(date) - 2451545) / 36525;
4051
+ const omega = 125.04455501 - 1934.13626197 * T + 207765e-8 * T * T;
4052
+ const F = normalize360(
4053
+ 93.27191028 + 483202.0175233 * T - 36825e-7 * T * T + 3083e-9 * T * T * T
4054
+ );
4055
+ const F_rad = F * Math.PI / 180;
4056
+ const correction = -1.4979 * Math.sin(2 * F_rad) - 0.15 * Math.sin(0 * F_rad + Math.PI * 2 * (357.5 / 360)) - 0.1226 * Math.sin(2 * (omega * Math.PI / 180)) + 0.1176 * Math.sin(2 * F_rad - 2 * (omega * Math.PI / 180));
4057
+ return normalize360(omega + correction / 60);
4058
+ }
4059
+ function buildGrahaPosition(planet, siderealLon, isRetro, nakshatraNameFn, rashiNameFn) {
4060
+ const rashiIndex = Math.floor(siderealLon / 30);
4061
+ const degreeInRashi = siderealLon - rashiIndex * 30;
4062
+ const nakIdx = Math.floor(siderealLon / NAKSHATRA_SPAN);
4063
+ return {
4064
+ planet,
4065
+ siderealLongitude: siderealLon,
4066
+ rashi: { index: rashiIndex, name: rashiNameFn(rashiIndex) },
4067
+ degreeInRashi,
4068
+ nakshatra: computeNakshatraFromLongitude(siderealLon, nakshatraNameFn(nakIdx)),
4069
+ isRetrograde: isRetro,
4070
+ house: 0
4071
+ // assigned later in computeKundli
4072
+ };
4073
+ }
4074
+ var identity = (idx) => String(idx);
4075
+ function computePlanetaryPositions(date, ayanamsaType, nakshatraName = identity, rashiName = identity) {
4076
+ const ayanamsa = computeAyanamsa(date, ayanamsaType);
4077
+ const toSidereal = (tropical) => normalize360(tropical - ayanamsa);
4078
+ const sunSid = getSiderealSunLongitude(date, ayanamsaType);
4079
+ const moonSid = getSiderealMoonLongitude(date, ayanamsaType);
4080
+ const marsTrop = getTropicalPlanetLongitude(Body.Mars, date);
4081
+ const mercTrop = getTropicalPlanetLongitude(Body.Mercury, date);
4082
+ const jupTrop = getTropicalPlanetLongitude(Body.Jupiter, date);
4083
+ const venTrop = getTropicalPlanetLongitude(Body.Venus, date);
4084
+ const satTrop = getTropicalPlanetLongitude(Body.Saturn, date);
4085
+ const rahuTrop = getTrueRahuLongitudeTropical(date);
4086
+ const ketuTrop = normalize360(rahuTrop + 180);
4087
+ const marsRetro = isRetrograde(Body.Mars, date);
4088
+ const mercRetro = isRetrograde(Body.Mercury, date);
4089
+ const jupRetro = isRetrograde(Body.Jupiter, date);
4090
+ const venRetro = isRetrograde(Body.Venus, date);
4091
+ const satRetro = isRetrograde(Body.Saturn, date);
4092
+ const g = (planet, sid, retro) => buildGrahaPosition(planet, sid, retro, nakshatraName, rashiName);
4093
+ return {
4094
+ sun: g("Sun", sunSid, false),
4095
+ moon: g("Moon", moonSid, false),
4096
+ mars: g("Mars", toSidereal(marsTrop), marsRetro),
4097
+ mercury: g("Mercury", toSidereal(mercTrop), mercRetro),
4098
+ jupiter: g("Jupiter", toSidereal(jupTrop), jupRetro),
4099
+ venus: g("Venus", toSidereal(venTrop), venRetro),
4100
+ saturn: g("Saturn", toSidereal(satTrop), satRetro),
4101
+ rahu: g("Rahu", toSidereal(rahuTrop), true),
4102
+ // always retrograde
4103
+ ketu: g("Ketu", toSidereal(ketuTrop), true)
4104
+ };
4105
+ }
4106
+
4107
+ // src/jyotish/lagna.ts
4108
+ function computeLagnaLongitude(date, latitude, longitude, ayanamsaType) {
4109
+ const gastHours = SiderealTime(MakeTime(date));
4110
+ const last_deg = normalize360(gastHours * 15 + longitude);
4111
+ const T = (dateToJulianDay(date) - 2451545) / 36525;
4112
+ const eps_deg = meanObliquity(T);
4113
+ const ramc = degToRad(last_deg);
4114
+ const eps = degToRad(eps_deg);
4115
+ const phi = degToRad(latitude);
4116
+ const y = -Math.cos(ramc);
4117
+ const x = Math.sin(ramc) * Math.cos(eps) + Math.tan(phi) * Math.sin(eps);
4118
+ let tropicalAsc = normalize360(radToDeg(Math.atan2(y, x)));
4119
+ const mcRaw = radToDeg(Math.atan2(Math.tan(ramc), Math.cos(eps)));
4120
+ const mc = normalize360(
4121
+ last_deg < 180 ? normalize360(mcRaw) : normalize360(mcRaw + 180)
4122
+ );
4123
+ const diff = normalize360(tropicalAsc - mc);
4124
+ if (diff < 90 || diff > 270) {
4125
+ tropicalAsc = normalize360(tropicalAsc + 180);
4126
+ }
4127
+ const ayanamsa = computeAyanamsa(date, ayanamsaType);
4128
+ return normalize360(tropicalAsc - ayanamsa);
4129
+ }
4130
+ function navamsaRashi(siderealLongitude) {
4131
+ const rashiIdx = Math.floor(siderealLongitude / 30);
4132
+ const degInRashi = siderealLongitude - rashiIdx * 30;
4133
+ const navamsaIndex = Math.floor(degInRashi / (30 / 9));
4134
+ const NAVAMSA_STARTS = [0, 9, 6, 3, 0, 9, 6, 3, 0, 9, 6, 3];
4135
+ return (NAVAMSA_STARTS[rashiIdx] + navamsaIndex) % 12;
4136
+ }
4137
+ function rashiFromLongitude(lon, nameFn) {
4138
+ const index = Math.floor(lon / 30);
4139
+ return { index, name: nameFn(index) };
4140
+ }
4141
+
4142
+ // src/jyotish/dasha.ts
4143
+ var DASHA_YEARS = {
4144
+ Ketu: 7,
4145
+ Venus: 20,
4146
+ Sun: 6,
4147
+ Moon: 10,
4148
+ Mars: 7,
4149
+ Rahu: 18,
4150
+ Jupiter: 16,
4151
+ Saturn: 19,
4152
+ Mercury: 17
4153
+ };
4154
+ var DASHA_ORDER = [
4155
+ "Ketu",
4156
+ "Venus",
4157
+ "Sun",
4158
+ "Moon",
4159
+ "Mars",
4160
+ "Rahu",
4161
+ "Jupiter",
4162
+ "Saturn",
4163
+ "Mercury"
4164
+ ];
4165
+ var NAKSHATRA_LORD = [
4166
+ "Ketu",
4167
+ "Venus",
4168
+ "Sun",
4169
+ "Moon",
4170
+ "Mars",
4171
+ "Rahu",
4172
+ "Jupiter",
4173
+ "Saturn",
4174
+ "Mercury",
4175
+ "Ketu",
4176
+ "Venus",
4177
+ "Sun",
4178
+ "Moon",
4179
+ "Mars",
4180
+ "Rahu",
4181
+ "Jupiter",
4182
+ "Saturn",
4183
+ "Mercury",
4184
+ "Ketu",
4185
+ "Venus",
4186
+ "Sun",
4187
+ "Moon",
4188
+ "Mars",
4189
+ "Rahu",
4190
+ "Jupiter",
4191
+ "Saturn",
4192
+ "Mercury"
4193
+ ];
4194
+ var MS_PER_YEAR = 365.25 * 24 * 3600 * 1e3;
4195
+ function computeVimshottariDasha(birthDate, moonSiderealLon) {
4196
+ const nakIdx = Math.floor(moonSiderealLon / NAKSHATRA_SPAN);
4197
+ const degInNak = moonSiderealLon - nakIdx * NAKSHATRA_SPAN;
4198
+ const elapsedFraction = degInNak / NAKSHATRA_SPAN;
4199
+ const startLord = NAKSHATRA_LORD[nakIdx];
4200
+ const startLordIdx = DASHA_ORDER.indexOf(startLord);
4201
+ const startLordYears = DASHA_YEARS[startLord];
4202
+ const balanceMs = (1 - elapsedFraction) * startLordYears * MS_PER_YEAR;
4203
+ const mahaDashas = [];
4204
+ let cursor = new Date(birthDate.getTime());
4205
+ for (let i = 0; i < 9; i++) {
4206
+ const lordIdx = (startLordIdx + i) % 9;
4207
+ const lord = DASHA_ORDER[lordIdx];
4208
+ const years = DASHA_YEARS[lord];
4209
+ const durationMs = i === 0 ? balanceMs : years * MS_PER_YEAR;
4210
+ const startDate = new Date(cursor.getTime());
4211
+ const endDate = new Date(cursor.getTime() + durationMs);
4212
+ const antarDashas = buildAntarDashas(lord, startDate, durationMs);
4213
+ mahaDashas.push({ lord, startDate, endDate, years, antarDashas });
4214
+ cursor = endDate;
4215
+ }
4216
+ const now = /* @__PURE__ */ new Date();
4217
+ const currentIndex = mahaDashas.findIndex(
4218
+ (md) => now >= md.startDate && now < md.endDate
4219
+ );
4220
+ return {
4221
+ currentMahaDashaLord: mahaDashas[Math.max(0, currentIndex)].lord,
4222
+ currentIndex: Math.max(0, currentIndex),
4223
+ mahaDashas
4224
+ };
4225
+ }
4226
+ function buildAntarDashas(mahaLord, mahaStart, mahaDurationMs) {
4227
+ const mahaIdx = DASHA_ORDER.indexOf(mahaLord);
4228
+ const antarDashas = [];
4229
+ let cursor = new Date(mahaStart.getTime());
4230
+ for (let i = 0; i < 9; i++) {
4231
+ const antarLordIdx = (mahaIdx + i) % 9;
4232
+ const antarLord = DASHA_ORDER[antarLordIdx];
4233
+ const antarYears = DASHA_YEARS[antarLord];
4234
+ const antarMs = antarYears / 120 * mahaDurationMs;
4235
+ const startDate = new Date(cursor.getTime());
4236
+ const endDate = new Date(cursor.getTime() + antarMs);
4237
+ antarDashas.push({ lord: antarLord, startDate, endDate });
4238
+ cursor = endDate;
4239
+ }
4240
+ return antarDashas;
4241
+ }
4242
+
4243
+ // src/jyotish/kundli.ts
4244
+ function computeKundli(birthDateUtc, location, options) {
4245
+ validateDate(birthDateUtc);
4246
+ validateLocation(location);
4247
+ const ayanamsa = options?.ayanamsa ?? "lahiri";
4248
+ const lang = options?.language ?? "en";
4249
+ const doDasha = options?.computeDasha !== false;
4250
+ const rashiName = (idx) => resolveMasaName(idx, lang);
4251
+ const nakshatraName = (idx) => resolveNakshatraName(idx, lang);
4252
+ const birthPanchang = getInstantPanchang(birthDateUtc, location, {
4253
+ ayanamsa,
4254
+ language: lang,
4255
+ computeEndTimes: options?.computeEndTimes ?? false,
4256
+ precision: options?.precision
4257
+ });
4258
+ const grahas = computePlanetaryPositions(birthDateUtc, ayanamsa, nakshatraName, rashiName);
4259
+ const lagnaLon = computeLagnaLongitude(
4260
+ birthDateUtc,
4261
+ location.latitude,
4262
+ location.longitude,
4263
+ ayanamsa
4264
+ );
4265
+ const lagna = rashiFromLongitude(lagnaLon, rashiName);
4266
+ const lagnaRashiIdx = lagna.index;
4267
+ const houses = Array.from({ length: 12 }, (_, i) => {
4268
+ const houseRashiIdx = (lagnaRashiIdx + i) % 12;
4269
+ return {
4270
+ number: i + 1,
4271
+ rashi: { index: houseRashiIdx, name: rashiName(houseRashiIdx) },
4272
+ planets: []
4273
+ };
4274
+ });
4275
+ const grahaList = Object.values(grahas);
4276
+ for (const graha of grahaList) {
4277
+ const houseNumber = (graha.rashi.index - lagnaRashiIdx + 12) % 12 + 1;
4278
+ graha.house = houseNumber;
4279
+ houses[houseNumber - 1].planets.push(GRAHA_ABBR[graha.planet]);
4280
+ }
4281
+ const navamsa = buildNavamsaChart(grahas, lagnaLon, rashiName);
4282
+ const dasha = doDasha ? computeVimshottariDasha(birthDateUtc, birthPanchang.siderealMoon) : { currentMahaDashaLord: "Sun", currentIndex: 0, mahaDashas: [] };
4283
+ return {
4284
+ lagnaLongitude: lagnaLon,
4285
+ lagna,
4286
+ houses,
4287
+ grahas,
4288
+ navamsa,
4289
+ birthPanchang,
4290
+ dasha
4291
+ };
4292
+ }
4293
+ function buildNavamsaChart(grahas, lagnaLon, rashiName) {
4294
+ const grahaList = Object.values(grahas);
4295
+ const positions = grahaList.map((g) => ({
4296
+ planet: g.planet,
4297
+ rashi: {
4298
+ index: navamsaRashi(g.siderealLongitude),
4299
+ name: rashiName(navamsaRashi(g.siderealLongitude))
4300
+ }
4301
+ }));
4302
+ return {
4303
+ positions,
4304
+ lagna: {
4305
+ index: navamsaRashi(lagnaLon),
4306
+ name: rashiName(navamsaRashi(lagnaLon))
4307
+ }
4308
+ };
4309
+ }
3995
4310
  /*! Bundled license information:
3996
4311
 
3997
4312
  astronomy-engine/esm/astronomy.js:
@@ -4030,4 +4345,4 @@ astronomy-engine/esm/astronomy.js:
4030
4345
  *)
4031
4346
  */
4032
4347
 
4033
- export { PanchangError, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computeRahuKalam, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
4348
+ export { GRAHA_ABBR, PanchangError, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computeKundli, computeLagnaLongitude, computePlanetaryPositions, computeRahuKalam, computeVimshottariDasha, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panchang-ts",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Pure TypeScript Hindu Panchang calculations. Tithi, Nakshatra, Yoga, Karana, Vara, and more. Offline-first, React Native compatible.",
5
5
  "author": "Ishank",
6
6
  "license": "MIT",