panchanga 0.1.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.
@@ -0,0 +1,145 @@
1
+ /**
2
+ * src/festivals.ts — the observance-rule EVALUATOR.
3
+ *
4
+ * Two responsibilities:
5
+ *
6
+ * 1. `selectDayByPervasion` — a PURE, independently-testable selection
7
+ * function. Given a set of candidate civil days (each with its tithi
8
+ * interval ∩ the day's kāla window, plus optional nakshatra / Bhadra
9
+ * facts), it applies the precedence policy, the nakshatra filter/tie-break,
10
+ * and records Bhadra overlap. No ephemeris, no I/O — the keystone logic the
11
+ * constructed-case tests target.
12
+ *
13
+ * 2. `computeFestival` / `computeFestivals` — resolve each `Observance.kind`
14
+ * to a civil date using the real astronomy modules (`time.ts`,
15
+ * `elements.ts`). These build the candidate set for `tithi-pervades` and
16
+ * feed it to `selectDayByPervasion`; the other kinds resolve directly.
17
+ *
18
+ * NEVER SILENTLY DROP: a rule that yields no date still returns a
19
+ * `FestivalResult` (empty `date`) carrying a diagnostic, plus a top-level
20
+ * diagnostic from `computeFestivals`.
21
+ *
22
+ * SCOPE: this is the MECHANISM (Task 5a). The per-festival rule DATA
23
+ * (`src/rules.ts`) and the Drik-Panchang conformance gate are Task 5b/Phase 4.
24
+ */
25
+ import type { FestivalRule, FestivalResult, GeoLocation } from "./types.js";
26
+ /** One candidate civil day for the pervasion contest. */
27
+ export interface PervasionCandidate {
28
+ /** The civil day (a UTC instant inside that local day; identity carrier). */
29
+ day: Date;
30
+ /** The festival tithi's interval (UTC). */
31
+ tithiInterval: {
32
+ start: Date;
33
+ end: Date;
34
+ };
35
+ /** The day's relevant kāla window (UTC). */
36
+ window: {
37
+ start: Date;
38
+ end: Date;
39
+ };
40
+ /**
41
+ * Whether the required/preferred nakshatra is satisfied on this day.
42
+ * `undefined` when the rule has no nakshatra clause.
43
+ */
44
+ nakshatraOk?: boolean;
45
+ /**
46
+ * Bhadra (Viṣṭi) overlap with this day's window, when `avoidKarana:"vishti"`.
47
+ * `null` = checked, none overlaps; `undefined` = not checked.
48
+ */
49
+ bhadraOverlap?: {
50
+ start: Date;
51
+ end: Date;
52
+ } | null;
53
+ /**
54
+ * Whether this day's kāla window has ANY Bhadra-free portion, when
55
+ * `avoidKarana:"vishti"`. `false` = Bhadra covers the WHOLE window (the
56
+ * observance cannot be performed Bhadra-free on this day → disqualified);
57
+ * `true` = at least part of the window is Bhadra-free; `undefined` = not
58
+ * checked (rule has no Bhadra clause).
59
+ */
60
+ bhadraFreeWindow?: boolean;
61
+ /**
62
+ * Whether the festival tithi is live at this civil day's SUNRISE (udaya
63
+ * tithi). Used by the Bhadra branch to pick the Bhadra-free observance day
64
+ * when the tithi has already left that day's (evening) window — Drik observes
65
+ * such Bhadra-excluded Pūrṇimā festivals on the udaya-Pūrṇimā day whose
66
+ * kāla window is Bhadra-free. `undefined` when not supplied.
67
+ */
68
+ tithiAtSunrise?: boolean;
69
+ }
70
+ export type Precedence = "max-window-fraction" | "udaya" | "first" | "second";
71
+ export interface SelectOptions {
72
+ precedence: Precedence;
73
+ /** Nakshatra mode, if the rule has a nakshatra clause. */
74
+ nakshatra?: "required" | "preferred";
75
+ avoidKarana?: "vishti";
76
+ fallback?: "previous-day" | "next-day" | "nearest-window";
77
+ }
78
+ export interface SelectResult {
79
+ /** The winning candidate, or null when none qualifies. */
80
+ chosen: PervasionCandidate | null;
81
+ /** Window-coverage fraction of the chosen day (0 when none). */
82
+ coverageFraction: number;
83
+ /** The fallback that was triggered because no day pervaded, if any. */
84
+ fallbackApplied: "previous-day" | "next-day" | "nearest-window" | null;
85
+ /** Bhadra overlap recorded on the chosen day (avoidKarana:"vishti"). */
86
+ bhadraOverlap: {
87
+ start: Date;
88
+ end: Date;
89
+ } | null;
90
+ diagnostics: string[];
91
+ }
92
+ /**
93
+ * Select the observance day among `candidates` per the precedence policy.
94
+ *
95
+ * Pipeline:
96
+ * 1. `nakshatra:"required"` → drop candidates with `nakshatraOk === false`.
97
+ * 1b.`avoidKarana:"vishti"` → DISQUALIFY any candidate whose kāla window is
98
+ * wholly covered by Bhadra (Viṣṭi) — i.e. `bhadraFreeWindow === false`.
99
+ * The observance must be performed in a Bhadra-FREE window, so a fully
100
+ * Bhadra-contaminated day cannot host it. (Holikā Dahan, Rakṣā Bandhan.)
101
+ * 2. Keep only candidates whose tithi covers a positive fraction of the
102
+ * window (i.e. the tithi actually pervades the window at all). A day with
103
+ * zero coverage cannot win — that is what triggers `fallback`.
104
+ * 3. Apply the precedence policy to the survivors:
105
+ * • max-window-fraction → largest coverage fraction wins.
106
+ * • udaya → a day present at the window start beats one
107
+ * that is not; among equals, larger fraction.
108
+ * • first / second → earliest / latest survivor by day.
109
+ * 4. `nakshatra:"preferred"` → on an (near-)exact fraction tie, prefer the
110
+ * nakshatra-matching day; never overrides a clear winner.
111
+ * 5. Record the chosen day's Bhadra overlap (avoidKarana:"vishti").
112
+ * 6. If no survivor pervades but `avoidKarana:"vishti"` left exactly the
113
+ * Bhadra-free candidate(s) standing, choose the Bhadra-free day on which
114
+ * the festival tithi is the UDAYA tithi (live at sunrise). This is the
115
+ * Drik resolution for Bhadra-excluded Pūrṇimā festivals: when the natural
116
+ * pradoṣa/aparāhna-vyāpinī day is Bhadra-contaminated, the observance
117
+ * shifts to the (next) udaya-Pūrṇimā day whose window is Bhadra-free, even
118
+ * though the tithi has by then left that evening window.
119
+ * 7. Otherwise, if no survivor, set `fallbackApplied` and explain.
120
+ *
121
+ * PURE: depends only on its arguments.
122
+ */
123
+ export declare function selectDayByPervasion(candidates: PervasionCandidate[], opts: SelectOptions): SelectResult;
124
+ export interface ComputeOptions {
125
+ /** The rule set to evaluate. (Task 5b supplies the real `rules.ts`.) */
126
+ rules?: FestivalRule[];
127
+ }
128
+ /**
129
+ * Compute one festival's result. `resolved` supplies already-computed results
130
+ * for `derived` rules to reference (their `from` id → FestivalResult).
131
+ */
132
+ export declare function computeFestival(rule: FestivalRule, year: number, loc: GeoLocation, resolved?: Map<string, FestivalResult>): FestivalResult;
133
+ /**
134
+ * Compute all festivals for `(year, loc)`.
135
+ *
136
+ * Resolves non-derived rules first, then derived rules (so their references
137
+ * exist). Orders results ascending by date; undated results sort last.
138
+ * NEVER silently drops: any undated result carries a diagnostic, and a matching
139
+ * top-level diagnostic is emitted.
140
+ */
141
+ export declare function computeFestivals(year: number, loc: GeoLocation, opts?: ComputeOptions): {
142
+ results: FestivalResult[];
143
+ diagnostics: string[];
144
+ };
145
+ //# sourceMappingURL=festivals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"festivals.d.ts","sourceRoot":"","sources":["../src/festivals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EAKZ,MAAM,YAAY,CAAC;AAuCpB,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,GAAG,EAAE,IAAI,CAAC;IACV,2CAA2C;IAC3C,aAAa,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1C,4CAA4C;IAC5C,MAAM,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,CAAC;IACnC;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAClD;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,MAAM,UAAU,GAAG,qBAAqB,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE9E,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;IACrC,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,QAAQ,CAAC,EAAE,cAAc,GAAG,UAAU,GAAG,gBAAgB,CAAC;CAC3D;AAED,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAClC,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,eAAe,EAAE,cAAc,GAAG,UAAU,GAAG,gBAAgB,GAAG,IAAI,CAAC;IACvE,wEAAwE;IACxE,aAAa,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AA6CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,kBAAkB,EAAE,EAChC,IAAI,EAAE,aAAa,GAClB,YAAY,CA8Ld;AA+jBD,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,WAAW,EAChB,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACrC,cAAc,CAgGhB;AAWD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,WAAW,EAChB,IAAI,GAAE,cAAmB,GACxB;IAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,CAkDtD"}