caelus 0.14.0 → 0.15.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.
@@ -10,6 +10,37 @@ export declare const SIGNS: string[];
10
10
  export declare const ASPECTS: Record<string, number>;
11
11
  export declare const DEFAULT_ORBS: Record<string, number>;
12
12
  export type HouseSystem = "placidus" | "porphyry" | "equal" | "whole_sign" | "koch" | "regiomontanus" | "campanus" | "alcabitius" | "morinus" | "meridian" | "polich_page" | "vehlow";
13
+ /** The canonical house-system ids, in a stable order (also used for error text). */
14
+ export declare const HOUSE_SYSTEMS: readonly HouseSystem[];
15
+ /** Resolve a forgiving house-system string (any case, spaces or hyphens, or a
16
+ * known alias) to a canonical {@link HouseSystem}, or throw listing the valid
17
+ * ids. Lets MCP, share links, and hand-written calls pass "whole sign",
18
+ * "Whole_Sign", "whole", etc. without tripping the strict union. */
19
+ export declare function normalizeHouseSystem(raw: string): HouseSystem;
20
+ export type Element = "fire" | "earth" | "air" | "water";
21
+ export type Modality = "cardinal" | "fixed" | "mutable";
22
+ /** Triplicity (element) of a sign: `"fire"`, `"earth"`, `"air"`, or `"water"`. */
23
+ export declare function element(sign: number | string): Element;
24
+ /** Quadruplicity (modality) of a sign: `"cardinal"`, `"fixed"`, or `"mutable"`. */
25
+ export declare function modality(sign: number | string): Modality;
26
+ /** 1-based quadrant (I–IV) of a 1-based house number: houses 1–3 -> 1, etc. */
27
+ export declare function quadrant(house: number): number;
28
+ /**
29
+ * Essential dignities a body holds in a sign: any of `"domicile"`,
30
+ * `"exaltation"`, `"detriment"`, `"fall"` (the last two are the signs opposite
31
+ * domicile and exaltation). Empty when the body is peregrine there or has no
32
+ * classical rulership (the outer planets, Chiron, the nodes).
33
+ *
34
+ * @param body Body id, e.g. `"mars"`.
35
+ * @param sign A sign index `0`–`11` (Aries = 0) or its name, e.g. `"Aries"`.
36
+ * @returns The dignities held, in the order above; empty if none.
37
+ * @example
38
+ * ```ts
39
+ * dignities("mars", "Aries"); // ["domicile"]
40
+ * dignities("sun", "Libra"); // ["fall"]
41
+ * ```
42
+ */
43
+ export declare function dignities(body: string, sign: number | string): string[];
13
44
  export type Ayanamsa = keyof typeof AYANAMSA_J2000 & string;
14
45
  export type Zodiac = "tropical" | `sidereal:${string}`;
15
46
  export interface Observer {
@@ -58,6 +89,33 @@ export interface Position {
58
89
  /** Equatorial declination, true equinox of date, degrees. */
59
90
  dec: number;
60
91
  }
92
+ /** A {@link Position} enriched with chart-relative placement, as returned per
93
+ * body by {@link Engine.chart} and {@link Engine.chartAt}. */
94
+ export interface ChartBody extends Position {
95
+ /** 1-based house the body falls in, by the chart's cusps (1–12). */
96
+ house: number;
97
+ /** Essential dignities held in the body's sign (see {@link dignities});
98
+ * empty when peregrine or for bodies without classical rulerships. */
99
+ dignities: string[];
100
+ }
101
+ /** Default chart bodies that are Chebyshev-packed, so they can fall outside
102
+ * their fitted range (and be omitted from a chart). Opt-in asteroids are
103
+ * packed too, but arrive as arbitrary ids through the string index. */
104
+ export type PackedBody = "chiron";
105
+ /** Bodies guaranteed to be in every chart: the analytic Sun–Pluto and the lunar
106
+ * nodes, which resolve across all supported epochs. (Chiron is Chebyshev-packed
107
+ * and can fall outside its fitted range, so it is *not* guaranteed.) */
108
+ export type AlwaysBody = Exclude<Body, PackedBody>;
109
+ /**
110
+ * A chart's bodies, keyed by id. The analytic core ({@link AlwaysBody}) is
111
+ * always present and needs no presence check. {@link PackedBody} bodies (Chiron)
112
+ * and any opt-in extras requested via {@link ChartOptions.bodies} may be absent
113
+ * when the instant is outside their fitted range (see {@link Chart.unavailable}),
114
+ * so those accesses are typed `ChartBody | undefined` and must be guarded.
115
+ */
116
+ export type ChartBodies = Record<AlwaysBody, ChartBody> & Partial<Record<PackedBody, ChartBody>> & {
117
+ [id: string]: ChartBody | undefined;
118
+ };
61
119
  /** One aspect between two bodies in a {@link Chart}. */
62
120
  export interface Aspect {
63
121
  /** First body id. */
@@ -81,8 +139,14 @@ export interface Chart {
81
139
  houseSystem: HouseSystem;
82
140
  /** The house system originally requested, before any polar fallback. */
83
141
  houseSystemRequested: HouseSystem;
84
- /** Apparent {@link Position} per body, keyed by body id. */
85
- bodies: Record<string, Position>;
142
+ /** Apparent position per body, enriched with house and dignities, keyed by
143
+ * body id. See {@link ChartBody}. */
144
+ bodies: ChartBodies;
145
+ /** Body ids that were requested but omitted because the instant falls outside
146
+ * their fitted range (e.g. Chiron and other Chebyshev-packed bodies before
147
+ * ~1850 or after ~2150). Empty for the usual modern dates. The analytic
148
+ * bodies (Sun through Pluto and the nodes) are always present. */
149
+ unavailable: string[];
86
150
  /** Chart angles in degrees: Ascendant, Midheaven, Vertex, East Point. */
87
151
  angles: {
88
152
  asc: number;
@@ -235,10 +299,10 @@ export declare class Engine {
235
299
  * instant and place.
236
300
  *
237
301
  * The first six arguments are calendar fields in **UT** — not local civil
238
- * time, and not a Julian Day. Passing a JD in `y` builds an instant far
239
- * outside the fitted range and throws `RangeError`; use {@link Engine.chartAt}
240
- * for a chart from a JD. For a birth time given in a local time zone, resolve
241
- * it to UT first (see the `caelus-birth` package).
302
+ * time, and not a Julian Day. Passing a JD in `y` builds an absurd instant and
303
+ * throws `RangeError`; use {@link Engine.chartAt} for a chart from a JD. For a
304
+ * birth time given in a local time zone, resolve it to UT first (see the
305
+ * `caelus-birth` package).
242
306
  *
243
307
  * @param y Year in UT, e.g. `1990` — a calendar year, not a Julian Day.
244
308
  * @param mo Month, `1`–`12`.
@@ -254,9 +318,12 @@ export declare class Engine {
254
318
  * custom orbs. Defaults to Placidus houses in the tropical zodiac.
255
319
  * @returns A {@link Chart}: `bodies`, `cusps`, `angles`, and `aspects`, plus
256
320
  * `jdUt` and the house system actually used (Placidus and Koch fall back to
257
- * whole-sign above the polar circles).
258
- * @throws RangeError if the instant lands outside the fitted range
259
- * (1800–2149) most often from passing a Julian Day where a year belongs.
321
+ * whole-sign above the polar circles). A body outside its fitted range
322
+ * (e.g. Chiron before ~1850) is omitted from `bodies` and listed in
323
+ * `unavailable` rather than failing the whole chart.
324
+ * @throws RangeError only if the instant itself is absurd — far outside any
325
+ * supported epoch — which almost always means a Julian Day was passed where
326
+ * calendar fields belong.
260
327
  * @example
261
328
  * ```ts
262
329
  * // 1990-06-10 14:30 UT at Tampa, FL (27.95° N, 82.46° W), Placidus houses
package/dist/src/chart.js CHANGED
@@ -23,7 +23,92 @@ export const ASPECTS = {
23
23
  export const DEFAULT_ORBS = {
24
24
  conjunction: 8, sextile: 4, square: 7, trine: 7, opposition: 8,
25
25
  };
26
+ /** The canonical house-system ids, in a stable order (also used for error text). */
27
+ export const HOUSE_SYSTEMS = [
28
+ "placidus", "porphyry", "equal", "whole_sign", "koch", "regiomontanus",
29
+ "campanus", "alcabitius", "morinus", "meridian", "polich_page", "vehlow",
30
+ ];
31
+ // Short or alternate names that normalization (lowercase + space/hyphen -> "_")
32
+ // can't reach on its own. "whole sign", "Polich Page", etc. already normalize to
33
+ // their canonical id, so only genuinely different spellings live here.
34
+ const HOUSE_ALIASES = {
35
+ whole: "whole_sign", signs: "whole_sign", wholesign: "whole_sign",
36
+ equal_house: "equal", porphyrius: "porphyry", placidean: "placidus",
37
+ };
38
+ /** Resolve a forgiving house-system string (any case, spaces or hyphens, or a
39
+ * known alias) to a canonical {@link HouseSystem}, or throw listing the valid
40
+ * ids. Lets MCP, share links, and hand-written calls pass "whole sign",
41
+ * "Whole_Sign", "whole", etc. without tripping the strict union. */
42
+ export function normalizeHouseSystem(raw) {
43
+ const key = raw.trim().toLowerCase().replace(/[\s-]+/g, "_");
44
+ if (HOUSE_SYSTEMS.includes(key))
45
+ return key;
46
+ const alias = HOUSE_ALIASES[key];
47
+ if (alias)
48
+ return alias;
49
+ throw new Error(`unknown house system '${raw}' (valid: ${HOUSE_SYSTEMS.join(", ")})`);
50
+ }
51
+ const ELEMENTS = ["fire", "earth", "air", "water"];
52
+ const MODALITIES = ["cardinal", "fixed", "mutable"];
53
+ /** Sign index `0`–`11` (Aries = 0) from an index or a sign name (`"Aries"`). */
54
+ function signIndex(sign) {
55
+ return typeof sign === "number" ? mod(Math.floor(sign), 12) : SIGNS.indexOf(sign);
56
+ }
57
+ /** Triplicity (element) of a sign: `"fire"`, `"earth"`, `"air"`, or `"water"`. */
58
+ export function element(sign) {
59
+ return ELEMENTS[mod(signIndex(sign), 4)];
60
+ }
61
+ /** Quadruplicity (modality) of a sign: `"cardinal"`, `"fixed"`, or `"mutable"`. */
62
+ export function modality(sign) {
63
+ return MODALITIES[mod(signIndex(sign), 3)];
64
+ }
65
+ /** 1-based quadrant (I–IV) of a 1-based house number: houses 1–3 -> 1, etc. */
66
+ export function quadrant(house) {
67
+ return Math.floor(mod(house - 1, 12) / 3) + 1;
68
+ }
69
+ // ----------------------------------------------------------- essential dignities
70
+ const DOMICILE = {
71
+ sun: [4], moon: [3], mercury: [2, 5], venus: [1, 6],
72
+ mars: [0, 7], jupiter: [8, 11], saturn: [9, 10],
73
+ };
74
+ const EXALTATION = {
75
+ sun: 0, moon: 1, mercury: 5, venus: 11, mars: 9, jupiter: 3, saturn: 6,
76
+ };
77
+ /**
78
+ * Essential dignities a body holds in a sign: any of `"domicile"`,
79
+ * `"exaltation"`, `"detriment"`, `"fall"` (the last two are the signs opposite
80
+ * domicile and exaltation). Empty when the body is peregrine there or has no
81
+ * classical rulership (the outer planets, Chiron, the nodes).
82
+ *
83
+ * @param body Body id, e.g. `"mars"`.
84
+ * @param sign A sign index `0`–`11` (Aries = 0) or its name, e.g. `"Aries"`.
85
+ * @returns The dignities held, in the order above; empty if none.
86
+ * @example
87
+ * ```ts
88
+ * dignities("mars", "Aries"); // ["domicile"]
89
+ * dignities("sun", "Libra"); // ["fall"]
90
+ * ```
91
+ */
92
+ export function dignities(body, sign) {
93
+ const idx = signIndex(sign);
94
+ const dom = DOMICILE[body] ?? [];
95
+ const out = [];
96
+ if (dom.includes(idx))
97
+ out.push("domicile");
98
+ if (EXALTATION[body] === idx)
99
+ out.push("exaltation");
100
+ if (dom.map((d) => mod(d + 6, 12)).includes(idx))
101
+ out.push("detriment");
102
+ if (body in EXALTATION && mod(EXALTATION[body] + 6, 12) === idx)
103
+ out.push("fall");
104
+ return out;
105
+ }
26
106
  const KM_PER_AU = 149597870.7;
107
+ // A generous window for the analytic models. Every real chart, however
108
+ // historical, sits well inside it; an instant far outside almost always means a
109
+ // Julian Day was passed to chart() where calendar fields belong.
110
+ const JD_SANE_MIN = -2_000_000; // ~ 10000 BC
111
+ const JD_SANE_MAX = 9_000_000; // ~ 20000 AD
27
112
  function parseZodiac(zodiac) {
28
113
  if (zodiac === "tropical")
29
114
  return null;
@@ -339,10 +424,10 @@ export class Engine {
339
424
  * instant and place.
340
425
  *
341
426
  * The first six arguments are calendar fields in **UT** — not local civil
342
- * time, and not a Julian Day. Passing a JD in `y` builds an instant far
343
- * outside the fitted range and throws `RangeError`; use {@link Engine.chartAt}
344
- * for a chart from a JD. For a birth time given in a local time zone, resolve
345
- * it to UT first (see the `caelus-birth` package).
427
+ * time, and not a Julian Day. Passing a JD in `y` builds an absurd instant and
428
+ * throws `RangeError`; use {@link Engine.chartAt} for a chart from a JD. For a
429
+ * birth time given in a local time zone, resolve it to UT first (see the
430
+ * `caelus-birth` package).
346
431
  *
347
432
  * @param y Year in UT, e.g. `1990` — a calendar year, not a Julian Day.
348
433
  * @param mo Month, `1`–`12`.
@@ -358,9 +443,12 @@ export class Engine {
358
443
  * custom orbs. Defaults to Placidus houses in the tropical zodiac.
359
444
  * @returns A {@link Chart}: `bodies`, `cusps`, `angles`, and `aspects`, plus
360
445
  * `jdUt` and the house system actually used (Placidus and Koch fall back to
361
- * whole-sign above the polar circles).
362
- * @throws RangeError if the instant lands outside the fitted range
363
- * (1800–2149) most often from passing a Julian Day where a year belongs.
446
+ * whole-sign above the polar circles). A body outside its fitted range
447
+ * (e.g. Chiron before ~1850) is omitted from `bodies` and listed in
448
+ * `unavailable` rather than failing the whole chart.
449
+ * @throws RangeError only if the instant itself is absurd — far outside any
450
+ * supported epoch — which almost always means a Julian Day was passed where
451
+ * calendar fields belong.
364
452
  * @example
365
453
  * ```ts
366
454
  * // 1990-06-10 14:30 UT at Tampa, FL (27.95° N, 82.46° W), Placidus houses
@@ -393,8 +481,12 @@ export class Engine {
393
481
  * @see {@link Engine.chart} for the calendar-field entry point.
394
482
  */
395
483
  chartAt(jdUt, lat, lonEast, opts = "placidus") {
484
+ if (!Number.isFinite(jdUt) || jdUt < JD_SANE_MIN || jdUt > JD_SANE_MAX) {
485
+ throw new RangeError(`chart instant (jd ${jdUt}) is far outside the supported range; if you ` +
486
+ `meant a calendar date, pass year/month/day to chart() rather than a Julian Day.`);
487
+ }
396
488
  const o = typeof opts === "string" ? { houseSystem: opts } : opts;
397
- const houseSystem = o.houseSystem ?? "placidus";
489
+ const houseSystem = normalizeHouseSystem(o.houseSystem ?? "placidus");
398
490
  const zodiac = o.zodiac ?? "tropical";
399
491
  const mode = parseZodiac(zodiac);
400
492
  const calc = {
@@ -406,8 +498,22 @@ export class Engine {
406
498
  ...BODIES, ...(o.bodies ?? []).filter((b) => !BODIES.includes(b)),
407
499
  ];
408
500
  const bodies = {};
409
- for (const b of names)
410
- bodies[b] = this.position(b, jdUt, calc);
501
+ const unavailable = [];
502
+ for (const b of names) {
503
+ try {
504
+ bodies[b] = this.position(b, jdUt, calc);
505
+ }
506
+ catch (e) {
507
+ // A Chebyshev-packed body (Chiron, fitted asteroids) outside its fitted
508
+ // range throws RangeError. Omit it and report it rather than discarding
509
+ // the whole chart; the analytic bodies still resolve. Any other error
510
+ // (e.g. a missing data pack) is a real fault and propagates.
511
+ if (e instanceof RangeError)
512
+ unavailable.push(b);
513
+ else
514
+ throw e;
515
+ }
516
+ }
411
517
  const [asc, mc, armc, eps] = H.angles(this.data, jdUt, lat, lonEast);
412
518
  const [vtx, east] = H.vertexEastPoint(armc, lat * DEG, eps);
413
519
  const phi = lat * DEG;
@@ -454,7 +560,7 @@ export class Engine {
454
560
  cusps = H.housesVehlow(armc, phi, eps);
455
561
  }
456
562
  else {
457
- throw new Error(`unknown house system '${houseSystem}'`);
563
+ throw new Error(`unknown house system '${houseSystem}' (valid: ${HOUSE_SYSTEMS.join(", ")})`);
458
564
  }
459
565
  }
460
566
  catch (err) {
@@ -477,21 +583,44 @@ export class Engine {
477
583
  else {
478
584
  cuspsDeg = cusps.map(outDeg);
479
585
  }
586
+ // Enrich each position with chart-relative placement (house) and the
587
+ // essential dignities of its sign, so callers don't recompute from cusps.
588
+ const chartBodies = {};
589
+ for (const b of names) {
590
+ const p = bodies[b];
591
+ if (!p)
592
+ continue; // omitted: outside its fitted range (see `unavailable`)
593
+ chartBodies[b] = {
594
+ ...p,
595
+ house: houseIndex(p.lon, cuspsDeg),
596
+ dignities: dignities(b, Math.floor(mod(p.lon, 360) / 30)),
597
+ };
598
+ }
480
599
  return {
481
600
  jdUt,
482
601
  zodiac,
483
602
  houseSystem: used,
484
603
  houseSystemRequested: houseSystem,
485
- bodies,
604
+ bodies: chartBodies,
605
+ unavailable,
486
606
  angles: {
487
607
  asc: outDeg(asc), mc: outDeg(mc),
488
608
  vertex: outDeg(vtx), eastPoint: outDeg(east),
489
609
  },
490
610
  cusps: cuspsDeg,
491
- aspects: findAspects(bodies, o.orbs ?? DEFAULT_ORBS),
611
+ aspects: findAspects(chartBodies, o.orbs ?? DEFAULT_ORBS),
492
612
  };
493
613
  }
494
614
  }
615
+ /** 1-based house for an ecliptic longitude (degrees) given the twelve cusp
616
+ * longitudes (degrees), wrapping across 0. */
617
+ function houseIndex(lon, cusps) {
618
+ for (let i = 0; i < 12; i++) {
619
+ if (mod(lon - cusps[i], 360) < mod(cusps[(i + 1) % 12] - cusps[i], 360))
620
+ return i + 1;
621
+ }
622
+ return 12;
623
+ }
495
624
  export function findAspects(bodies, orbs = DEFAULT_ORBS) {
496
625
  const out = [];
497
626
  const names = Object.keys(bodies).filter((b) => !NOT_ASPECTABLE.has(b));
@@ -511,8 +640,11 @@ export function findAspects(bodies, orbs = DEFAULT_ORBS) {
511
640
  return out;
512
641
  }
513
642
  export function fmtLon(deg) {
514
- const sign = SIGNS[Math.floor(deg / 30)];
515
- const d = mod(deg, 30);
643
+ // Normalize first: a raw or rounded longitude (e.g. exactly 360, or a small
644
+ // negative) would otherwise index SIGNS out of range and render "undefined".
645
+ const norm = mod(deg, 360);
646
+ const sign = SIGNS[Math.floor(norm / 30)];
647
+ const d = mod(norm, 30);
516
648
  const m = mod(d, 1) * 60;
517
649
  return `${String(Math.floor(d)).padStart(2)}°${String(Math.floor(m)).padStart(2, "0")}' ${sign}`;
518
650
  }
@@ -109,21 +109,6 @@ export declare function declinationAspects(engine: Engine, bodies: BodyId[], jd:
109
109
  /** |declination| minus the mean obliquity, degrees. Positive = out of bounds. */
110
110
  export declare function outOfBoundsMargin(engine: Engine, body: BodyId, jd: number): number;
111
111
  export declare function outOfBounds(engine: Engine, body: BodyId, jd: number): boolean;
112
- /**
113
- * Essential dignities a body holds in a sign: any of `"domicile"`,
114
- * `"exaltation"`, `"detriment"`, `"fall"` (the last two are the signs opposite
115
- * domicile and exaltation). Empty when the body is peregrine there.
116
- *
117
- * @param body Body id, e.g. `"mars"`.
118
- * @param sign A sign index `0`–`11` (Aries = 0) or its name, e.g. `"Aries"`.
119
- * @returns The dignities held, in the order above; empty if none.
120
- * @example
121
- * ```ts
122
- * dignities("mars", "Aries"); // ["domicile"]
123
- * dignities("sun", "Libra"); // ["fall"]
124
- * ```
125
- */
126
- export declare function dignities(body: string, sign: number | string): string[];
127
112
  export declare function dignityOf(engine: Engine, body: BodyId, jd: number, zodiac?: Zodiac): string[];
128
113
  /** Diurnal when the Sun is above the horizon at the given place. */
129
114
  export declare function isDayChart(engine: Engine, jd: number, lat: number, lonEast: number): boolean;
@@ -9,7 +9,7 @@
9
9
  * golden fixtures pin the two together.
10
10
  */
11
11
  import { mod, meanObliquity, jdTT, DEG } from "./core.js";
12
- import { SIGNS } from "./chart.js";
12
+ import { dignities } from "./chart.js";
13
13
  import { crossings } from "./events.js";
14
14
  import { azAlt } from "./pheno.js";
15
15
  export const TROPICAL_YEAR = 365.24219; // mean tropical year, days
@@ -198,44 +198,9 @@ export function outOfBounds(engine, body, jd) {
198
198
  return outOfBoundsMargin(engine, body, jd) > 0;
199
199
  }
200
200
  // ----------------------------------------------------------- dignities
201
- const DOMICILE = {
202
- sun: [4], moon: [3], mercury: [2, 5], venus: [1, 6],
203
- mars: [0, 7], jupiter: [8, 11], saturn: [9, 10],
204
- };
205
- const EXALTATION = {
206
- sun: 0, moon: 1, mercury: 5, venus: 11, mars: 9, jupiter: 3, saturn: 6,
207
- };
208
- function signIndex(sign) {
209
- return typeof sign === "number" ? sign : SIGNS.indexOf(sign);
210
- }
211
- /**
212
- * Essential dignities a body holds in a sign: any of `"domicile"`,
213
- * `"exaltation"`, `"detriment"`, `"fall"` (the last two are the signs opposite
214
- * domicile and exaltation). Empty when the body is peregrine there.
215
- *
216
- * @param body Body id, e.g. `"mars"`.
217
- * @param sign A sign index `0`–`11` (Aries = 0) or its name, e.g. `"Aries"`.
218
- * @returns The dignities held, in the order above; empty if none.
219
- * @example
220
- * ```ts
221
- * dignities("mars", "Aries"); // ["domicile"]
222
- * dignities("sun", "Libra"); // ["fall"]
223
- * ```
224
- */
225
- export function dignities(body, sign) {
226
- const idx = signIndex(sign);
227
- const dom = DOMICILE[body] ?? [];
228
- const out = [];
229
- if (dom.includes(idx))
230
- out.push("domicile");
231
- if (EXALTATION[body] === idx)
232
- out.push("exaltation");
233
- if (dom.map((d) => mod(d + 6, 12)).includes(idx))
234
- out.push("detriment");
235
- if (body in EXALTATION && mod(EXALTATION[body] + 6, 12) === idx)
236
- out.push("fall");
237
- return out;
238
- }
201
+ // The pure `dignities(body, sign)` table now lives in chart.ts (with the other
202
+ // sign primitives); re-exported through the package index from there. This
203
+ // engine-aware wrapper resolves a body's sign at an instant first.
239
204
  export function dignityOf(engine, body, jd, zodiac = "tropical") {
240
205
  const lon = engine.longitude(body, jd, { zodiac });
241
206
  return dignities(body, mod(Math.floor(lon / 30), 12));
@@ -9,6 +9,7 @@ export const TRIKONAS = [1, 5, 9];
9
9
  export const DHANA_HOUSES = [2, 5, 9, 11];
10
10
  const PURE_KENDRAS = [4, 7, 10];
11
11
  const PURE_TRIKONAS = [5, 9];
12
+ // The seven classical grahas: all analytic, so always present in a chart.
12
13
  const PLANETS = ["sun", "moon", "mars", "mercury", "jupiter", "venus", "saturn"];
13
14
  /** The traditional ruler of a sign index (0 = Aries). */
14
15
  export function signLord(sign) {
@@ -12,8 +12,8 @@
12
12
  * variant-laden yogas (Kemadruma, lordship-based raja/dhana) are left to a later
13
13
  * step. Mirrors the Python reference (astroengine/yogas.py).
14
14
  */
15
- import { Engine, BodyId, Zodiac } from "./chart.js";
16
- export declare const YOGA_PLANETS: BodyId[];
15
+ import { Engine, Zodiac, AlwaysBody } from "./chart.js";
16
+ export declare const YOGA_PLANETS: readonly AlwaysBody[];
17
17
  export interface Yoga {
18
18
  yoga: string;
19
19
  planets: string[];
package/dist/src/yogas.js CHANGED
@@ -1,10 +1,25 @@
1
- import { dignities } from "./derived.js";
1
+ /**
2
+ * astroengine yogas -- classical Vedic yogas (planetary combinations) judged on
3
+ * the sidereal rasi (D1) chart.
4
+ *
5
+ * Covers the well-defined, placement-based yogas with no textual variation: the
6
+ * five Pancha Mahapurusha yogas (a non-luminary in its own sign or exaltation
7
+ * AND in a kendra from the Ascendant -- Ruchaka/Mars, Bhadra/Mercury,
8
+ * Hamsa/Jupiter, Malavya/Venus, Shasha/Saturn); Gajakesari (Jupiter in a kendra
9
+ * from the Moon); Budha-Aditya (Sun and Mercury in one sign); and
10
+ * Chandra-Mangala (Moon and Mars in one sign). Own-sign/exaltation use the
11
+ * engine's `dignities`; houses are whole-sign from the Ascendant. The
12
+ * variant-laden yogas (Kemadruma, lordship-based raja/dhana) are left to a later
13
+ * step. Mirrors the Python reference (astroengine/yogas.py).
14
+ */
15
+ import { dignities } from "./chart.js";
2
16
  /** Pancha Mahapurusha: [yoga name, planet]. */
3
17
  const MAHAPURUSHA = [
4
18
  ["Ruchaka", "mars"], ["Bhadra", "mercury"], ["Hamsa", "jupiter"],
5
19
  ["Malavya", "venus"], ["Shasha", "saturn"],
6
20
  ];
7
21
  const KENDRA = new Set([1, 4, 7, 10]);
22
+ // The seven classical grahas: all analytic, so always present in a chart.
8
23
  export const YOGA_PLANETS = ["sun", "moon", "mars", "mercury", "jupiter", "venus", "saturn"];
9
24
  /** The placement yogas present in a chart. `signs` maps each of the seven
10
25
  * classical planets to its 0-based sign index; `ascSign` is the Ascendant's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caelus",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Astrological ephemeris engine. MIT, no AGPL, no ephemeris files. Checked against Swiss Ephemeris.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",