panchang-ts 0.3.0 → 0.4.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
@@ -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
  ---
@@ -554,6 +712,12 @@ occurs within the search window (this is normal for the Moon).
554
712
 
555
713
  ---
556
714
 
715
+ ## Acknowledgements
716
+
717
+ - **[astronomy-engine](https://github.com/cosinekitty/astronomy)** by Don Cross — the sole runtime dependency. Provides the astronomical algorithms used for sunrise/sunset, moonrise/moonset, and planetary longitude calculations. MIT licensed.
718
+
719
+ ---
720
+
557
721
  ## License
558
722
 
559
723
  MIT
package/dist/index.cjs CHANGED
@@ -1372,6 +1372,10 @@ function sidereal_time(time) {
1372
1372
  }
1373
1373
  return sidereal_time_cache.st;
1374
1374
  }
1375
+ function SiderealTime(date) {
1376
+ const time = MakeTime(date);
1377
+ return sidereal_time(time);
1378
+ }
1375
1379
  function terra(observer, st) {
1376
1380
  const phi = observer.latitude * DEG2RAD;
1377
1381
  const sinphi = Math.sin(phi);
@@ -2262,6 +2266,13 @@ function Search(f, t1, t2, options) {
2262
2266
  return null;
2263
2267
  }
2264
2268
  }
2269
+ function EclipticLongitude(body, date) {
2270
+ if (body === Body.Sun)
2271
+ throw "Cannot calculate heliocentric longitude of the Sun.";
2272
+ const hv = HelioVector(body, date);
2273
+ const eclip = Ecliptic(hv);
2274
+ return eclip.elon;
2275
+ }
2265
2276
  var AtmosphereInfo = class {
2266
2277
  constructor(pressure, temperature, density) {
2267
2278
  this.pressure = pressure;
@@ -2530,6 +2541,12 @@ function normalize360(degrees) {
2530
2541
  if (r === 0) return 0;
2531
2542
  return r < 0 ? r + 360 : r;
2532
2543
  }
2544
+ function degToRad(degrees) {
2545
+ return degrees * (Math.PI / 180);
2546
+ }
2547
+ function radToDeg(radians) {
2548
+ return radians * (180 / Math.PI);
2549
+ }
2533
2550
 
2534
2551
  // src/astronomy/moon.ts
2535
2552
  function getSiderealMoonLongitude(date, ayanamsaType) {
@@ -3994,6 +4011,295 @@ function getDailyPanchang(date, location, options) {
3994
4011
  }
3995
4012
  };
3996
4013
  }
4014
+
4015
+ // src/jyotish/planets.ts
4016
+ var GRAHA_ABBR = {
4017
+ Sun: "Su",
4018
+ Moon: "Mo",
4019
+ Mars: "Ma",
4020
+ Mercury: "Me",
4021
+ Jupiter: "Ju",
4022
+ Venus: "Ve",
4023
+ Saturn: "Sa",
4024
+ Rahu: "Ra",
4025
+ Ketu: "Ke"
4026
+ };
4027
+ function meanObliquity(T) {
4028
+ return 23.439291111 - 0.013004167 * T - 164e-9 * T * T + 504e-9 * T * T * T;
4029
+ }
4030
+ function getTropicalPlanetLongitude(body, date) {
4031
+ return EclipticLongitude(body, MakeTime(date));
4032
+ }
4033
+ function isRetrograde(body, date) {
4034
+ const dt = 36e5;
4035
+ const lon0 = EclipticLongitude(body, MakeTime(new Date(date.getTime() - dt)));
4036
+ const lon1 = EclipticLongitude(body, MakeTime(new Date(date.getTime() + dt)));
4037
+ let delta = lon1 - lon0;
4038
+ if (delta > 180) delta -= 360;
4039
+ if (delta < -180) delta += 360;
4040
+ return delta < 0;
4041
+ }
4042
+ function getTrueRahuLongitudeTropical(date) {
4043
+ const T = (dateToJulianDay(date) - 2451545) / 36525;
4044
+ const omega = 125.04455501 - 1934.13626197 * T + 207765e-8 * T * T;
4045
+ const F = normalize360(
4046
+ 93.27191028 + 483202.0175233 * T - 36825e-7 * T * T + 3083e-9 * T * T * T
4047
+ );
4048
+ const F_rad = F * Math.PI / 180;
4049
+ 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));
4050
+ return normalize360(omega + correction / 60);
4051
+ }
4052
+ function buildGrahaPosition(planet, siderealLon, isRetro, nakshatraNameFn, rashiNameFn) {
4053
+ const rashiIndex = Math.floor(siderealLon / 30);
4054
+ const degreeInRashi = siderealLon - rashiIndex * 30;
4055
+ const nakIdx = Math.floor(siderealLon / NAKSHATRA_SPAN);
4056
+ return {
4057
+ planet,
4058
+ siderealLongitude: siderealLon,
4059
+ rashi: { index: rashiIndex, name: rashiNameFn(rashiIndex) },
4060
+ degreeInRashi,
4061
+ nakshatra: computeNakshatraFromLongitude(siderealLon, nakshatraNameFn(nakIdx)),
4062
+ isRetrograde: isRetro,
4063
+ house: 0
4064
+ // assigned later in computeKundli
4065
+ };
4066
+ }
4067
+ var identity = (idx) => String(idx);
4068
+ function computePlanetaryPositions(date, ayanamsaType, nakshatraName = identity, rashiName = identity) {
4069
+ const ayanamsa = computeAyanamsa(date, ayanamsaType);
4070
+ const toSidereal = (tropical) => normalize360(tropical - ayanamsa);
4071
+ const sunSid = getSiderealSunLongitude(date, ayanamsaType);
4072
+ const moonSid = getSiderealMoonLongitude(date, ayanamsaType);
4073
+ const marsTrop = getTropicalPlanetLongitude(Body.Mars, date);
4074
+ const mercTrop = getTropicalPlanetLongitude(Body.Mercury, date);
4075
+ const jupTrop = getTropicalPlanetLongitude(Body.Jupiter, date);
4076
+ const venTrop = getTropicalPlanetLongitude(Body.Venus, date);
4077
+ const satTrop = getTropicalPlanetLongitude(Body.Saturn, date);
4078
+ const rahuTrop = getTrueRahuLongitudeTropical(date);
4079
+ const ketuTrop = normalize360(rahuTrop + 180);
4080
+ const marsRetro = isRetrograde(Body.Mars, date);
4081
+ const mercRetro = isRetrograde(Body.Mercury, date);
4082
+ const jupRetro = isRetrograde(Body.Jupiter, date);
4083
+ const venRetro = isRetrograde(Body.Venus, date);
4084
+ const satRetro = isRetrograde(Body.Saturn, date);
4085
+ const g = (planet, sid, retro) => buildGrahaPosition(planet, sid, retro, nakshatraName, rashiName);
4086
+ return {
4087
+ sun: g("Sun", sunSid, false),
4088
+ moon: g("Moon", moonSid, false),
4089
+ mars: g("Mars", toSidereal(marsTrop), marsRetro),
4090
+ mercury: g("Mercury", toSidereal(mercTrop), mercRetro),
4091
+ jupiter: g("Jupiter", toSidereal(jupTrop), jupRetro),
4092
+ venus: g("Venus", toSidereal(venTrop), venRetro),
4093
+ saturn: g("Saturn", toSidereal(satTrop), satRetro),
4094
+ rahu: g("Rahu", toSidereal(rahuTrop), true),
4095
+ // always retrograde
4096
+ ketu: g("Ketu", toSidereal(ketuTrop), true)
4097
+ };
4098
+ }
4099
+
4100
+ // src/jyotish/lagna.ts
4101
+ function computeLagnaLongitude(date, latitude, longitude, ayanamsaType) {
4102
+ const gastHours = SiderealTime(MakeTime(date));
4103
+ const last_deg = normalize360(gastHours * 15 + longitude);
4104
+ const T = (dateToJulianDay(date) - 2451545) / 36525;
4105
+ const eps_deg = meanObliquity(T);
4106
+ const ramc = degToRad(last_deg);
4107
+ const eps = degToRad(eps_deg);
4108
+ const phi = degToRad(latitude);
4109
+ const y = -Math.cos(ramc);
4110
+ const x = Math.sin(ramc) * Math.cos(eps) + Math.tan(phi) * Math.sin(eps);
4111
+ let tropicalAsc = normalize360(radToDeg(Math.atan2(y, x)));
4112
+ const mcRaw = radToDeg(Math.atan2(Math.tan(ramc), Math.cos(eps)));
4113
+ const mc = normalize360(
4114
+ last_deg < 180 ? normalize360(mcRaw) : normalize360(mcRaw + 180)
4115
+ );
4116
+ const diff = normalize360(tropicalAsc - mc);
4117
+ if (diff < 90 || diff > 270) {
4118
+ tropicalAsc = normalize360(tropicalAsc + 180);
4119
+ }
4120
+ const ayanamsa = computeAyanamsa(date, ayanamsaType);
4121
+ return normalize360(tropicalAsc - ayanamsa);
4122
+ }
4123
+ function navamsaRashi(siderealLongitude) {
4124
+ const rashiIdx = Math.floor(siderealLongitude / 30);
4125
+ const degInRashi = siderealLongitude - rashiIdx * 30;
4126
+ const navamsaIndex = Math.floor(degInRashi / (30 / 9));
4127
+ const NAVAMSA_STARTS = [0, 9, 6, 3, 0, 9, 6, 3, 0, 9, 6, 3];
4128
+ return (NAVAMSA_STARTS[rashiIdx] + navamsaIndex) % 12;
4129
+ }
4130
+ function rashiFromLongitude(lon, nameFn) {
4131
+ const index = Math.floor(lon / 30);
4132
+ return { index, name: nameFn(index) };
4133
+ }
4134
+
4135
+ // src/jyotish/dasha.ts
4136
+ var DASHA_YEARS = {
4137
+ Ketu: 7,
4138
+ Venus: 20,
4139
+ Sun: 6,
4140
+ Moon: 10,
4141
+ Mars: 7,
4142
+ Rahu: 18,
4143
+ Jupiter: 16,
4144
+ Saturn: 19,
4145
+ Mercury: 17
4146
+ };
4147
+ var DASHA_ORDER = [
4148
+ "Ketu",
4149
+ "Venus",
4150
+ "Sun",
4151
+ "Moon",
4152
+ "Mars",
4153
+ "Rahu",
4154
+ "Jupiter",
4155
+ "Saturn",
4156
+ "Mercury"
4157
+ ];
4158
+ var NAKSHATRA_LORD = [
4159
+ "Ketu",
4160
+ "Venus",
4161
+ "Sun",
4162
+ "Moon",
4163
+ "Mars",
4164
+ "Rahu",
4165
+ "Jupiter",
4166
+ "Saturn",
4167
+ "Mercury",
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
+ ];
4187
+ var MS_PER_YEAR = 365.25 * 24 * 3600 * 1e3;
4188
+ function computeVimshottariDasha(birthDate, moonSiderealLon) {
4189
+ const nakIdx = Math.floor(moonSiderealLon / NAKSHATRA_SPAN);
4190
+ const degInNak = moonSiderealLon - nakIdx * NAKSHATRA_SPAN;
4191
+ const elapsedFraction = degInNak / NAKSHATRA_SPAN;
4192
+ const startLord = NAKSHATRA_LORD[nakIdx];
4193
+ const startLordIdx = DASHA_ORDER.indexOf(startLord);
4194
+ const startLordYears = DASHA_YEARS[startLord];
4195
+ const balanceMs = (1 - elapsedFraction) * startLordYears * MS_PER_YEAR;
4196
+ const mahaDashas = [];
4197
+ let cursor = new Date(birthDate.getTime());
4198
+ for (let i = 0; i < 9; i++) {
4199
+ const lordIdx = (startLordIdx + i) % 9;
4200
+ const lord = DASHA_ORDER[lordIdx];
4201
+ const years = DASHA_YEARS[lord];
4202
+ const durationMs = i === 0 ? balanceMs : years * MS_PER_YEAR;
4203
+ const startDate = new Date(cursor.getTime());
4204
+ const endDate = new Date(cursor.getTime() + durationMs);
4205
+ const antarDashas = buildAntarDashas(lord, startDate, durationMs);
4206
+ mahaDashas.push({ lord, startDate, endDate, years, antarDashas });
4207
+ cursor = endDate;
4208
+ }
4209
+ const now = /* @__PURE__ */ new Date();
4210
+ const currentIndex = mahaDashas.findIndex(
4211
+ (md) => now >= md.startDate && now < md.endDate
4212
+ );
4213
+ return {
4214
+ currentMahaDashaLord: mahaDashas[Math.max(0, currentIndex)].lord,
4215
+ currentIndex: Math.max(0, currentIndex),
4216
+ mahaDashas
4217
+ };
4218
+ }
4219
+ function buildAntarDashas(mahaLord, mahaStart, mahaDurationMs) {
4220
+ const mahaIdx = DASHA_ORDER.indexOf(mahaLord);
4221
+ const antarDashas = [];
4222
+ let cursor = new Date(mahaStart.getTime());
4223
+ for (let i = 0; i < 9; i++) {
4224
+ const antarLordIdx = (mahaIdx + i) % 9;
4225
+ const antarLord = DASHA_ORDER[antarLordIdx];
4226
+ const antarYears = DASHA_YEARS[antarLord];
4227
+ const antarMs = antarYears / 120 * mahaDurationMs;
4228
+ const startDate = new Date(cursor.getTime());
4229
+ const endDate = new Date(cursor.getTime() + antarMs);
4230
+ antarDashas.push({ lord: antarLord, startDate, endDate });
4231
+ cursor = endDate;
4232
+ }
4233
+ return antarDashas;
4234
+ }
4235
+
4236
+ // src/jyotish/kundli.ts
4237
+ function computeKundli(birthDateUtc, location, options) {
4238
+ validateDate(birthDateUtc);
4239
+ validateLocation(location);
4240
+ const ayanamsa = options?.ayanamsa ?? "lahiri";
4241
+ const lang = options?.language ?? "en";
4242
+ const doDasha = options?.computeDasha !== false;
4243
+ const rashiName = (idx) => resolveMasaName(idx, lang);
4244
+ const nakshatraName = (idx) => resolveNakshatraName(idx, lang);
4245
+ const birthPanchang = getInstantPanchang(birthDateUtc, location, {
4246
+ ayanamsa,
4247
+ language: lang,
4248
+ computeEndTimes: options?.computeEndTimes ?? false,
4249
+ precision: options?.precision
4250
+ });
4251
+ const grahas = computePlanetaryPositions(birthDateUtc, ayanamsa, nakshatraName, rashiName);
4252
+ const lagnaLon = computeLagnaLongitude(
4253
+ birthDateUtc,
4254
+ location.latitude,
4255
+ location.longitude,
4256
+ ayanamsa
4257
+ );
4258
+ const lagna = rashiFromLongitude(lagnaLon, rashiName);
4259
+ const lagnaRashiIdx = lagna.index;
4260
+ const houses = Array.from({ length: 12 }, (_, i) => {
4261
+ const houseRashiIdx = (lagnaRashiIdx + i) % 12;
4262
+ return {
4263
+ number: i + 1,
4264
+ rashi: { index: houseRashiIdx, name: rashiName(houseRashiIdx) },
4265
+ planets: []
4266
+ };
4267
+ });
4268
+ const grahaList = Object.values(grahas);
4269
+ for (const graha of grahaList) {
4270
+ const houseNumber = (graha.rashi.index - lagnaRashiIdx + 12) % 12 + 1;
4271
+ graha.house = houseNumber;
4272
+ houses[houseNumber - 1].planets.push(GRAHA_ABBR[graha.planet]);
4273
+ }
4274
+ const navamsa = buildNavamsaChart(grahas, lagnaLon, rashiName);
4275
+ const dasha = doDasha ? computeVimshottariDasha(birthDateUtc, birthPanchang.siderealMoon) : { currentMahaDashaLord: "Sun", currentIndex: 0, mahaDashas: [] };
4276
+ return {
4277
+ lagnaLongitude: lagnaLon,
4278
+ lagna,
4279
+ houses,
4280
+ grahas,
4281
+ navamsa,
4282
+ birthPanchang,
4283
+ dasha
4284
+ };
4285
+ }
4286
+ function buildNavamsaChart(grahas, lagnaLon, rashiName) {
4287
+ const grahaList = Object.values(grahas);
4288
+ const positions = grahaList.map((g) => ({
4289
+ planet: g.planet,
4290
+ rashi: {
4291
+ index: navamsaRashi(g.siderealLongitude),
4292
+ name: rashiName(navamsaRashi(g.siderealLongitude))
4293
+ }
4294
+ }));
4295
+ return {
4296
+ positions,
4297
+ lagna: {
4298
+ index: navamsaRashi(lagnaLon),
4299
+ name: rashiName(navamsaRashi(lagnaLon))
4300
+ }
4301
+ };
4302
+ }
3997
4303
  /*! Bundled license information:
3998
4304
 
3999
4305
  astronomy-engine/esm/astronomy.js:
@@ -4032,12 +4338,17 @@ astronomy-engine/esm/astronomy.js:
4032
4338
  *)
4033
4339
  */
4034
4340
 
4341
+ exports.GRAHA_ABBR = GRAHA_ABBR;
4035
4342
  exports.PanchangError = PanchangError;
4036
4343
  exports.computeAbhijitMuhurta = computeAbhijitMuhurta;
4037
4344
  exports.computeBrahmaMuhurta = computeBrahmaMuhurta;
4038
4345
  exports.computeGowriPanchangam = computeGowriPanchangam;
4039
4346
  exports.computeGulikaKalam = computeGulikaKalam;
4347
+ exports.computeKundli = computeKundli;
4348
+ exports.computeLagnaLongitude = computeLagnaLongitude;
4349
+ exports.computePlanetaryPositions = computePlanetaryPositions;
4040
4350
  exports.computeRahuKalam = computeRahuKalam;
4351
+ exports.computeVimshottariDasha = computeVimshottariDasha;
4041
4352
  exports.computeYamaganda = computeYamaganda;
4042
4353
  exports.getAyanamsa = computeAyanamsa;
4043
4354
  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
@@ -1370,6 +1370,10 @@ function sidereal_time(time) {
1370
1370
  }
1371
1371
  return sidereal_time_cache.st;
1372
1372
  }
1373
+ function SiderealTime(date) {
1374
+ const time = MakeTime(date);
1375
+ return sidereal_time(time);
1376
+ }
1373
1377
  function terra(observer, st) {
1374
1378
  const phi = observer.latitude * DEG2RAD;
1375
1379
  const sinphi = Math.sin(phi);
@@ -2260,6 +2264,13 @@ function Search(f, t1, t2, options) {
2260
2264
  return null;
2261
2265
  }
2262
2266
  }
2267
+ function EclipticLongitude(body, date) {
2268
+ if (body === Body.Sun)
2269
+ throw "Cannot calculate heliocentric longitude of the Sun.";
2270
+ const hv = HelioVector(body, date);
2271
+ const eclip = Ecliptic(hv);
2272
+ return eclip.elon;
2273
+ }
2263
2274
  var AtmosphereInfo = class {
2264
2275
  constructor(pressure, temperature, density) {
2265
2276
  this.pressure = pressure;
@@ -2528,6 +2539,12 @@ function normalize360(degrees) {
2528
2539
  if (r === 0) return 0;
2529
2540
  return r < 0 ? r + 360 : r;
2530
2541
  }
2542
+ function degToRad(degrees) {
2543
+ return degrees * (Math.PI / 180);
2544
+ }
2545
+ function radToDeg(radians) {
2546
+ return radians * (180 / Math.PI);
2547
+ }
2531
2548
 
2532
2549
  // src/astronomy/moon.ts
2533
2550
  function getSiderealMoonLongitude(date, ayanamsaType) {
@@ -3992,6 +4009,295 @@ function getDailyPanchang(date, location, options) {
3992
4009
  }
3993
4010
  };
3994
4011
  }
4012
+
4013
+ // src/jyotish/planets.ts
4014
+ var GRAHA_ABBR = {
4015
+ Sun: "Su",
4016
+ Moon: "Mo",
4017
+ Mars: "Ma",
4018
+ Mercury: "Me",
4019
+ Jupiter: "Ju",
4020
+ Venus: "Ve",
4021
+ Saturn: "Sa",
4022
+ Rahu: "Ra",
4023
+ Ketu: "Ke"
4024
+ };
4025
+ function meanObliquity(T) {
4026
+ return 23.439291111 - 0.013004167 * T - 164e-9 * T * T + 504e-9 * T * T * T;
4027
+ }
4028
+ function getTropicalPlanetLongitude(body, date) {
4029
+ return EclipticLongitude(body, MakeTime(date));
4030
+ }
4031
+ function isRetrograde(body, date) {
4032
+ const dt = 36e5;
4033
+ const lon0 = EclipticLongitude(body, MakeTime(new Date(date.getTime() - dt)));
4034
+ const lon1 = EclipticLongitude(body, MakeTime(new Date(date.getTime() + dt)));
4035
+ let delta = lon1 - lon0;
4036
+ if (delta > 180) delta -= 360;
4037
+ if (delta < -180) delta += 360;
4038
+ return delta < 0;
4039
+ }
4040
+ function getTrueRahuLongitudeTropical(date) {
4041
+ const T = (dateToJulianDay(date) - 2451545) / 36525;
4042
+ const omega = 125.04455501 - 1934.13626197 * T + 207765e-8 * T * T;
4043
+ const F = normalize360(
4044
+ 93.27191028 + 483202.0175233 * T - 36825e-7 * T * T + 3083e-9 * T * T * T
4045
+ );
4046
+ const F_rad = F * Math.PI / 180;
4047
+ 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));
4048
+ return normalize360(omega + correction / 60);
4049
+ }
4050
+ function buildGrahaPosition(planet, siderealLon, isRetro, nakshatraNameFn, rashiNameFn) {
4051
+ const rashiIndex = Math.floor(siderealLon / 30);
4052
+ const degreeInRashi = siderealLon - rashiIndex * 30;
4053
+ const nakIdx = Math.floor(siderealLon / NAKSHATRA_SPAN);
4054
+ return {
4055
+ planet,
4056
+ siderealLongitude: siderealLon,
4057
+ rashi: { index: rashiIndex, name: rashiNameFn(rashiIndex) },
4058
+ degreeInRashi,
4059
+ nakshatra: computeNakshatraFromLongitude(siderealLon, nakshatraNameFn(nakIdx)),
4060
+ isRetrograde: isRetro,
4061
+ house: 0
4062
+ // assigned later in computeKundli
4063
+ };
4064
+ }
4065
+ var identity = (idx) => String(idx);
4066
+ function computePlanetaryPositions(date, ayanamsaType, nakshatraName = identity, rashiName = identity) {
4067
+ const ayanamsa = computeAyanamsa(date, ayanamsaType);
4068
+ const toSidereal = (tropical) => normalize360(tropical - ayanamsa);
4069
+ const sunSid = getSiderealSunLongitude(date, ayanamsaType);
4070
+ const moonSid = getSiderealMoonLongitude(date, ayanamsaType);
4071
+ const marsTrop = getTropicalPlanetLongitude(Body.Mars, date);
4072
+ const mercTrop = getTropicalPlanetLongitude(Body.Mercury, date);
4073
+ const jupTrop = getTropicalPlanetLongitude(Body.Jupiter, date);
4074
+ const venTrop = getTropicalPlanetLongitude(Body.Venus, date);
4075
+ const satTrop = getTropicalPlanetLongitude(Body.Saturn, date);
4076
+ const rahuTrop = getTrueRahuLongitudeTropical(date);
4077
+ const ketuTrop = normalize360(rahuTrop + 180);
4078
+ const marsRetro = isRetrograde(Body.Mars, date);
4079
+ const mercRetro = isRetrograde(Body.Mercury, date);
4080
+ const jupRetro = isRetrograde(Body.Jupiter, date);
4081
+ const venRetro = isRetrograde(Body.Venus, date);
4082
+ const satRetro = isRetrograde(Body.Saturn, date);
4083
+ const g = (planet, sid, retro) => buildGrahaPosition(planet, sid, retro, nakshatraName, rashiName);
4084
+ return {
4085
+ sun: g("Sun", sunSid, false),
4086
+ moon: g("Moon", moonSid, false),
4087
+ mars: g("Mars", toSidereal(marsTrop), marsRetro),
4088
+ mercury: g("Mercury", toSidereal(mercTrop), mercRetro),
4089
+ jupiter: g("Jupiter", toSidereal(jupTrop), jupRetro),
4090
+ venus: g("Venus", toSidereal(venTrop), venRetro),
4091
+ saturn: g("Saturn", toSidereal(satTrop), satRetro),
4092
+ rahu: g("Rahu", toSidereal(rahuTrop), true),
4093
+ // always retrograde
4094
+ ketu: g("Ketu", toSidereal(ketuTrop), true)
4095
+ };
4096
+ }
4097
+
4098
+ // src/jyotish/lagna.ts
4099
+ function computeLagnaLongitude(date, latitude, longitude, ayanamsaType) {
4100
+ const gastHours = SiderealTime(MakeTime(date));
4101
+ const last_deg = normalize360(gastHours * 15 + longitude);
4102
+ const T = (dateToJulianDay(date) - 2451545) / 36525;
4103
+ const eps_deg = meanObliquity(T);
4104
+ const ramc = degToRad(last_deg);
4105
+ const eps = degToRad(eps_deg);
4106
+ const phi = degToRad(latitude);
4107
+ const y = -Math.cos(ramc);
4108
+ const x = Math.sin(ramc) * Math.cos(eps) + Math.tan(phi) * Math.sin(eps);
4109
+ let tropicalAsc = normalize360(radToDeg(Math.atan2(y, x)));
4110
+ const mcRaw = radToDeg(Math.atan2(Math.tan(ramc), Math.cos(eps)));
4111
+ const mc = normalize360(
4112
+ last_deg < 180 ? normalize360(mcRaw) : normalize360(mcRaw + 180)
4113
+ );
4114
+ const diff = normalize360(tropicalAsc - mc);
4115
+ if (diff < 90 || diff > 270) {
4116
+ tropicalAsc = normalize360(tropicalAsc + 180);
4117
+ }
4118
+ const ayanamsa = computeAyanamsa(date, ayanamsaType);
4119
+ return normalize360(tropicalAsc - ayanamsa);
4120
+ }
4121
+ function navamsaRashi(siderealLongitude) {
4122
+ const rashiIdx = Math.floor(siderealLongitude / 30);
4123
+ const degInRashi = siderealLongitude - rashiIdx * 30;
4124
+ const navamsaIndex = Math.floor(degInRashi / (30 / 9));
4125
+ const NAVAMSA_STARTS = [0, 9, 6, 3, 0, 9, 6, 3, 0, 9, 6, 3];
4126
+ return (NAVAMSA_STARTS[rashiIdx] + navamsaIndex) % 12;
4127
+ }
4128
+ function rashiFromLongitude(lon, nameFn) {
4129
+ const index = Math.floor(lon / 30);
4130
+ return { index, name: nameFn(index) };
4131
+ }
4132
+
4133
+ // src/jyotish/dasha.ts
4134
+ var DASHA_YEARS = {
4135
+ Ketu: 7,
4136
+ Venus: 20,
4137
+ Sun: 6,
4138
+ Moon: 10,
4139
+ Mars: 7,
4140
+ Rahu: 18,
4141
+ Jupiter: 16,
4142
+ Saturn: 19,
4143
+ Mercury: 17
4144
+ };
4145
+ var DASHA_ORDER = [
4146
+ "Ketu",
4147
+ "Venus",
4148
+ "Sun",
4149
+ "Moon",
4150
+ "Mars",
4151
+ "Rahu",
4152
+ "Jupiter",
4153
+ "Saturn",
4154
+ "Mercury"
4155
+ ];
4156
+ var NAKSHATRA_LORD = [
4157
+ "Ketu",
4158
+ "Venus",
4159
+ "Sun",
4160
+ "Moon",
4161
+ "Mars",
4162
+ "Rahu",
4163
+ "Jupiter",
4164
+ "Saturn",
4165
+ "Mercury",
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
+ ];
4185
+ var MS_PER_YEAR = 365.25 * 24 * 3600 * 1e3;
4186
+ function computeVimshottariDasha(birthDate, moonSiderealLon) {
4187
+ const nakIdx = Math.floor(moonSiderealLon / NAKSHATRA_SPAN);
4188
+ const degInNak = moonSiderealLon - nakIdx * NAKSHATRA_SPAN;
4189
+ const elapsedFraction = degInNak / NAKSHATRA_SPAN;
4190
+ const startLord = NAKSHATRA_LORD[nakIdx];
4191
+ const startLordIdx = DASHA_ORDER.indexOf(startLord);
4192
+ const startLordYears = DASHA_YEARS[startLord];
4193
+ const balanceMs = (1 - elapsedFraction) * startLordYears * MS_PER_YEAR;
4194
+ const mahaDashas = [];
4195
+ let cursor = new Date(birthDate.getTime());
4196
+ for (let i = 0; i < 9; i++) {
4197
+ const lordIdx = (startLordIdx + i) % 9;
4198
+ const lord = DASHA_ORDER[lordIdx];
4199
+ const years = DASHA_YEARS[lord];
4200
+ const durationMs = i === 0 ? balanceMs : years * MS_PER_YEAR;
4201
+ const startDate = new Date(cursor.getTime());
4202
+ const endDate = new Date(cursor.getTime() + durationMs);
4203
+ const antarDashas = buildAntarDashas(lord, startDate, durationMs);
4204
+ mahaDashas.push({ lord, startDate, endDate, years, antarDashas });
4205
+ cursor = endDate;
4206
+ }
4207
+ const now = /* @__PURE__ */ new Date();
4208
+ const currentIndex = mahaDashas.findIndex(
4209
+ (md) => now >= md.startDate && now < md.endDate
4210
+ );
4211
+ return {
4212
+ currentMahaDashaLord: mahaDashas[Math.max(0, currentIndex)].lord,
4213
+ currentIndex: Math.max(0, currentIndex),
4214
+ mahaDashas
4215
+ };
4216
+ }
4217
+ function buildAntarDashas(mahaLord, mahaStart, mahaDurationMs) {
4218
+ const mahaIdx = DASHA_ORDER.indexOf(mahaLord);
4219
+ const antarDashas = [];
4220
+ let cursor = new Date(mahaStart.getTime());
4221
+ for (let i = 0; i < 9; i++) {
4222
+ const antarLordIdx = (mahaIdx + i) % 9;
4223
+ const antarLord = DASHA_ORDER[antarLordIdx];
4224
+ const antarYears = DASHA_YEARS[antarLord];
4225
+ const antarMs = antarYears / 120 * mahaDurationMs;
4226
+ const startDate = new Date(cursor.getTime());
4227
+ const endDate = new Date(cursor.getTime() + antarMs);
4228
+ antarDashas.push({ lord: antarLord, startDate, endDate });
4229
+ cursor = endDate;
4230
+ }
4231
+ return antarDashas;
4232
+ }
4233
+
4234
+ // src/jyotish/kundli.ts
4235
+ function computeKundli(birthDateUtc, location, options) {
4236
+ validateDate(birthDateUtc);
4237
+ validateLocation(location);
4238
+ const ayanamsa = options?.ayanamsa ?? "lahiri";
4239
+ const lang = options?.language ?? "en";
4240
+ const doDasha = options?.computeDasha !== false;
4241
+ const rashiName = (idx) => resolveMasaName(idx, lang);
4242
+ const nakshatraName = (idx) => resolveNakshatraName(idx, lang);
4243
+ const birthPanchang = getInstantPanchang(birthDateUtc, location, {
4244
+ ayanamsa,
4245
+ language: lang,
4246
+ computeEndTimes: options?.computeEndTimes ?? false,
4247
+ precision: options?.precision
4248
+ });
4249
+ const grahas = computePlanetaryPositions(birthDateUtc, ayanamsa, nakshatraName, rashiName);
4250
+ const lagnaLon = computeLagnaLongitude(
4251
+ birthDateUtc,
4252
+ location.latitude,
4253
+ location.longitude,
4254
+ ayanamsa
4255
+ );
4256
+ const lagna = rashiFromLongitude(lagnaLon, rashiName);
4257
+ const lagnaRashiIdx = lagna.index;
4258
+ const houses = Array.from({ length: 12 }, (_, i) => {
4259
+ const houseRashiIdx = (lagnaRashiIdx + i) % 12;
4260
+ return {
4261
+ number: i + 1,
4262
+ rashi: { index: houseRashiIdx, name: rashiName(houseRashiIdx) },
4263
+ planets: []
4264
+ };
4265
+ });
4266
+ const grahaList = Object.values(grahas);
4267
+ for (const graha of grahaList) {
4268
+ const houseNumber = (graha.rashi.index - lagnaRashiIdx + 12) % 12 + 1;
4269
+ graha.house = houseNumber;
4270
+ houses[houseNumber - 1].planets.push(GRAHA_ABBR[graha.planet]);
4271
+ }
4272
+ const navamsa = buildNavamsaChart(grahas, lagnaLon, rashiName);
4273
+ const dasha = doDasha ? computeVimshottariDasha(birthDateUtc, birthPanchang.siderealMoon) : { currentMahaDashaLord: "Sun", currentIndex: 0, mahaDashas: [] };
4274
+ return {
4275
+ lagnaLongitude: lagnaLon,
4276
+ lagna,
4277
+ houses,
4278
+ grahas,
4279
+ navamsa,
4280
+ birthPanchang,
4281
+ dasha
4282
+ };
4283
+ }
4284
+ function buildNavamsaChart(grahas, lagnaLon, rashiName) {
4285
+ const grahaList = Object.values(grahas);
4286
+ const positions = grahaList.map((g) => ({
4287
+ planet: g.planet,
4288
+ rashi: {
4289
+ index: navamsaRashi(g.siderealLongitude),
4290
+ name: rashiName(navamsaRashi(g.siderealLongitude))
4291
+ }
4292
+ }));
4293
+ return {
4294
+ positions,
4295
+ lagna: {
4296
+ index: navamsaRashi(lagnaLon),
4297
+ name: rashiName(navamsaRashi(lagnaLon))
4298
+ }
4299
+ };
4300
+ }
3995
4301
  /*! Bundled license information:
3996
4302
 
3997
4303
  astronomy-engine/esm/astronomy.js:
@@ -4030,4 +4336,4 @@ astronomy-engine/esm/astronomy.js:
4030
4336
  *)
4031
4337
  */
4032
4338
 
4033
- export { PanchangError, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computeRahuKalam, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
4339
+ 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.0",
3
+ "version": "0.4.0",
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",