ether-to-astro 1.0.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.
Files changed (138) hide show
  1. package/.env.example +13 -0
  2. package/.github/pull_request_template.md +16 -0
  3. package/.github/workflows/release.yml +35 -0
  4. package/.github/workflows/test.yml +32 -0
  5. package/AGENTS.md +99 -0
  6. package/LICENSE +18 -0
  7. package/NOTICE.md +45 -0
  8. package/README.md +301 -0
  9. package/SETUP.md +70 -0
  10. package/TESTING_SUMMARY.md +238 -0
  11. package/TEST_SUITE_STATUS.md +218 -0
  12. package/biome.json +48 -0
  13. package/dist/astro-service.d.ts +98 -0
  14. package/dist/astro-service.js +496 -0
  15. package/dist/chart-types.d.ts +52 -0
  16. package/dist/chart-types.js +51 -0
  17. package/dist/charts.d.ts +125 -0
  18. package/dist/charts.js +324 -0
  19. package/dist/cli.d.ts +7 -0
  20. package/dist/cli.js +472 -0
  21. package/dist/constants.d.ts +81 -0
  22. package/dist/constants.js +76 -0
  23. package/dist/eclipses.d.ts +85 -0
  24. package/dist/eclipses.js +184 -0
  25. package/dist/ephemeris.d.ts +120 -0
  26. package/dist/ephemeris.js +379 -0
  27. package/dist/formatter.d.ts +2 -0
  28. package/dist/formatter.js +22 -0
  29. package/dist/houses.d.ts +82 -0
  30. package/dist/houses.js +169 -0
  31. package/dist/index.d.ts +14 -0
  32. package/dist/index.js +150 -0
  33. package/dist/loader.d.ts +2 -0
  34. package/dist/loader.js +31 -0
  35. package/dist/logger.d.ts +25 -0
  36. package/dist/logger.js +73 -0
  37. package/dist/profile-store.d.ts +48 -0
  38. package/dist/profile-store.js +156 -0
  39. package/dist/riseset.d.ts +82 -0
  40. package/dist/riseset.js +185 -0
  41. package/dist/storage.d.ts +10 -0
  42. package/dist/storage.js +40 -0
  43. package/dist/time-utils.d.ts +68 -0
  44. package/dist/time-utils.js +136 -0
  45. package/dist/tool-registry.d.ts +35 -0
  46. package/dist/tool-registry.js +307 -0
  47. package/dist/tool-result.d.ts +175 -0
  48. package/dist/tool-result.js +188 -0
  49. package/dist/transits.d.ts +108 -0
  50. package/dist/transits.js +263 -0
  51. package/dist/types.d.ts +450 -0
  52. package/dist/types.js +161 -0
  53. package/example-usage.md +131 -0
  54. package/natal-chart.json +187 -0
  55. package/package.json +61 -0
  56. package/scripts/download-ephemeris.js +115 -0
  57. package/setup.sh +21 -0
  58. package/src/astro-service.ts +710 -0
  59. package/src/chart-types.ts +125 -0
  60. package/src/charts.ts +399 -0
  61. package/src/cli.ts +694 -0
  62. package/src/constants.ts +89 -0
  63. package/src/eclipses.ts +226 -0
  64. package/src/ephemeris.ts +437 -0
  65. package/src/formatter.ts +25 -0
  66. package/src/houses.ts +202 -0
  67. package/src/index.ts +170 -0
  68. package/src/loader.ts +36 -0
  69. package/src/logger.ts +104 -0
  70. package/src/profile-store.ts +285 -0
  71. package/src/riseset.ts +229 -0
  72. package/src/time-utils.ts +167 -0
  73. package/src/tool-registry.ts +357 -0
  74. package/src/tool-result.ts +283 -0
  75. package/src/transits.ts +352 -0
  76. package/src/types.ts +547 -0
  77. package/tests/README.md +173 -0
  78. package/tests/TESTING_STRATEGY.md +178 -0
  79. package/tests/fixtures/bowen-yang-chart.ts +69 -0
  80. package/tests/fixtures/calculate-expected.ts +81 -0
  81. package/tests/fixtures/expected-results.ts +117 -0
  82. package/tests/fixtures/generate-expected-simple.ts +94 -0
  83. package/tests/helpers/date-fixtures.ts +15 -0
  84. package/tests/helpers/ephem.ts +11 -0
  85. package/tests/helpers/temp.ts +9 -0
  86. package/tests/setup.ts +11 -0
  87. package/tests/unit/astro-service.test.ts +323 -0
  88. package/tests/unit/chart-types.test.ts +18 -0
  89. package/tests/unit/charts-errors.test.ts +42 -0
  90. package/tests/unit/charts.test.ts +157 -0
  91. package/tests/unit/cli-commands.test.ts +82 -0
  92. package/tests/unit/cli-profiles.test.ts +128 -0
  93. package/tests/unit/cli.test.ts +191 -0
  94. package/tests/unit/constants.test.ts +26 -0
  95. package/tests/unit/correctness-critical.test.ts +408 -0
  96. package/tests/unit/eclipses.test.ts +108 -0
  97. package/tests/unit/ephemeris.test.ts +213 -0
  98. package/tests/unit/error-handling.test.ts +116 -0
  99. package/tests/unit/formatter.test.ts +29 -0
  100. package/tests/unit/houses-errors.test.ts +27 -0
  101. package/tests/unit/houses-validation.test.ts +164 -0
  102. package/tests/unit/houses.test.ts +205 -0
  103. package/tests/unit/profile-store.test.ts +163 -0
  104. package/tests/unit/real-user-charts.test.ts +148 -0
  105. package/tests/unit/riseset.test.ts +106 -0
  106. package/tests/unit/solver-edges.test.ts +197 -0
  107. package/tests/unit/time-utils-temporal.test.ts +303 -0
  108. package/tests/unit/time-utils.test.ts +173 -0
  109. package/tests/unit/tool-registry.test.ts +222 -0
  110. package/tests/unit/tool-result.test.ts +45 -0
  111. package/tests/unit/transit-correctness.test.ts +78 -0
  112. package/tests/unit/transits.test.ts +238 -0
  113. package/tests/validation/README.md +32 -0
  114. package/tests/validation/adapters/astrolog.ts +306 -0
  115. package/tests/validation/adapters/internal.ts +184 -0
  116. package/tests/validation/compare/eclipses.ts +47 -0
  117. package/tests/validation/compare/houses.ts +76 -0
  118. package/tests/validation/compare/positions.ts +104 -0
  119. package/tests/validation/compare/riseSet.ts +48 -0
  120. package/tests/validation/compare/roots.ts +90 -0
  121. package/tests/validation/compare/transits.ts +69 -0
  122. package/tests/validation/fixtures/astrolog-parity/core.ts +194 -0
  123. package/tests/validation/fixtures/eclipses/core.ts +14 -0
  124. package/tests/validation/fixtures/houses/core.ts +47 -0
  125. package/tests/validation/fixtures/positions/core.ts +159 -0
  126. package/tests/validation/fixtures/rise-set/core.ts +20 -0
  127. package/tests/validation/fixtures/roots/core.ts +47 -0
  128. package/tests/validation/fixtures/transits/core.ts +61 -0
  129. package/tests/validation/fixtures/transits/dst.ts +21 -0
  130. package/tests/validation/oracle.spec.ts +129 -0
  131. package/tests/validation/utils/denseRootOracle.ts +269 -0
  132. package/tests/validation/utils/fixtureTypes.ts +146 -0
  133. package/tests/validation/utils/report.ts +60 -0
  134. package/tests/validation/utils/tolerances.ts +23 -0
  135. package/tests/validation/validation.spec.ts +836 -0
  136. package/tools/color-picker.html +388 -0
  137. package/tsconfig.json +17 -0
  138. package/vitest.config.ts +31 -0
@@ -0,0 +1,146 @@
1
+ import type { PlanetName } from '../../../src/types.js';
2
+
3
+ export interface NormalizedBody {
4
+ body: PlanetName | string;
5
+ longitude: number;
6
+ latitude?: number;
7
+ speed?: number;
8
+ retrograde?: boolean;
9
+ }
10
+
11
+ export interface NormalizedHouseResult {
12
+ system: string;
13
+ cusps: number[];
14
+ ascendant: number;
15
+ mc: number;
16
+ }
17
+
18
+ export interface NormalizedTransit {
19
+ transitingPlanet: string;
20
+ natalPlanet: string;
21
+ aspect: string;
22
+ orb: number;
23
+ exactTime?: string;
24
+ exactTimeStatus?: 'within_preview' | 'outside_preview' | 'not_found' | 'unsupported_body';
25
+ isApplying: boolean;
26
+ }
27
+
28
+ export interface NormalizedRoot {
29
+ jd: number;
30
+ isoUtc: string;
31
+ }
32
+
33
+ export interface NormalizedRiseSet {
34
+ body: string;
35
+ rise?: string;
36
+ set?: string;
37
+ upperMeridianTransit?: string;
38
+ lowerMeridianTransit?: string;
39
+ }
40
+
41
+ export interface NormalizedEclipse {
42
+ type: 'solar' | 'lunar';
43
+ eclipseType: string;
44
+ maxTime: string;
45
+ }
46
+
47
+ export interface PositionFixture {
48
+ name: string;
49
+ isoUtc: string;
50
+ planetIds: number[];
51
+ expected: NormalizedBody[];
52
+ }
53
+
54
+ export interface AstrologPositionParityFixture {
55
+ name: string;
56
+ isoUtc: string;
57
+ planetIds: number[];
58
+ }
59
+
60
+ export interface HouseFixture {
61
+ name: string;
62
+ isoUtc: string;
63
+ latitude: number;
64
+ longitude: number;
65
+ houseSystem: string;
66
+ expected: NormalizedHouseResult;
67
+ }
68
+
69
+ export interface AstrologHouseParityFixture {
70
+ name: string;
71
+ isoUtc: string;
72
+ latitude: number;
73
+ longitude: number;
74
+ houseSystem: 'P' | 'W';
75
+ expectFallbackToWholeSign?: boolean;
76
+ }
77
+
78
+ export interface RootFixture {
79
+ name: string;
80
+ planetId: number;
81
+ startIsoUtc: string;
82
+ endIsoUtc: string;
83
+ targetLongitude?: number;
84
+ targetFromStartLongitude?: boolean;
85
+ targetFromSampledMinimum?: {
86
+ samples: number;
87
+ };
88
+ expectedMinRoots?: number;
89
+ expectedMaxRoots?: number;
90
+ }
91
+
92
+ export interface TransitFixture {
93
+ name: string;
94
+ currentIsoUtc: string;
95
+ transitingPlanetId: number;
96
+ natalPlanetId: number;
97
+ natalOffsetDegrees: number;
98
+ expectedAspect: 'conjunction' | 'opposition' | 'square' | 'trine' | 'sextile';
99
+ expectedIsApplying?: boolean;
100
+ expectExactTimeStatus?:
101
+ | 'within_preview'
102
+ | 'outside_preview'
103
+ | 'not_found'
104
+ | 'unsupported_body'
105
+ | 'undefined';
106
+ }
107
+
108
+ export interface AstrologTransitSnapshotFixture {
109
+ name: string;
110
+ currentIsoUtc: string;
111
+ transitingPlanetId: number;
112
+ natalPlanetId: number;
113
+ natalOffsetDegrees: number;
114
+ expectedAspect: 'conjunction' | 'opposition' | 'square' | 'trine' | 'sextile';
115
+ maxOrb: number;
116
+ }
117
+
118
+ export interface RiseSetFixture {
119
+ name: string;
120
+ isoUtc: string;
121
+ latitude: number;
122
+ longitude: number;
123
+ planetId: number;
124
+ expectedNoRiseSet?: boolean;
125
+ }
126
+
127
+ export interface EclipseFixture {
128
+ name: string;
129
+ startIsoUtc: string;
130
+ type: 'solar' | 'lunar';
131
+ }
132
+
133
+ export interface AstrologEdgeParityFixture {
134
+ name: string;
135
+ isoUtc: string;
136
+ planetIds: number[];
137
+ local?: {
138
+ year: number;
139
+ month: number;
140
+ day: number;
141
+ hour: number;
142
+ minute: number;
143
+ };
144
+ timezone?: string;
145
+ disambiguation?: 'compatible' | 'earlier' | 'later' | 'reject';
146
+ }
@@ -0,0 +1,60 @@
1
+ export interface ValidationMismatch {
2
+ fixture: string;
3
+ subsystem: string;
4
+ expected: unknown;
5
+ actual: unknown;
6
+ delta: number | string | null;
7
+ tolerance: number | string;
8
+ message: string;
9
+ capability?: 'available' | 'unavailable';
10
+ validation?: 'executed' | 'skipped_intentionally';
11
+ details?: unknown;
12
+ }
13
+
14
+ export class ValidationReport {
15
+ readonly hardFailures: ValidationMismatch[] = [];
16
+ readonly warnings: ValidationMismatch[] = [];
17
+ generatedAt?: string;
18
+
19
+ addHard(mismatch: ValidationMismatch): void {
20
+ this.hardFailures.push(mismatch);
21
+ }
22
+
23
+ addWarning(mismatch: ValidationMismatch): void {
24
+ this.warnings.push(mismatch);
25
+ }
26
+
27
+ flushWarningsToConsole(): void {
28
+ for (const w of this.warnings) {
29
+ // Keep warnings readable but compact.
30
+ console.warn(
31
+ `[validation warning] fixture=${w.fixture} subsystem=${w.subsystem} message=${w.message} expected=${JSON.stringify(w.expected)} actual=${JSON.stringify(w.actual)} delta=${w.delta} tolerance=${w.tolerance}`
32
+ );
33
+ }
34
+ }
35
+
36
+ toJson(): string {
37
+ const wallClockIso = new Date(Date.now()).toISOString();
38
+ return JSON.stringify(
39
+ {
40
+ hardFailures: this.hardFailures,
41
+ warnings: this.warnings,
42
+ generatedAt: this.generatedAt ?? wallClockIso,
43
+ },
44
+ null,
45
+ 2
46
+ );
47
+ }
48
+ }
49
+
50
+ export function formatMismatch(mismatch: ValidationMismatch): string {
51
+ return [
52
+ `fixture=${mismatch.fixture}`,
53
+ `subsystem=${mismatch.subsystem}`,
54
+ `message=${mismatch.message}`,
55
+ `expected=${JSON.stringify(mismatch.expected)}`,
56
+ `actual=${JSON.stringify(mismatch.actual)}`,
57
+ `delta=${mismatch.delta}`,
58
+ `tolerance=${mismatch.tolerance}`,
59
+ ].join(' | ');
60
+ }
@@ -0,0 +1,23 @@
1
+ export const TOLERANCES = {
2
+ astrologPositionLongitudeDeg: 0.02,
3
+ astrologHouseDeg: 0.2,
4
+ positionLongitudeDeg: 0.0001,
5
+ positionLatitudeDeg: 0.0001,
6
+ positionSpeedDegPerDay: 0.0001,
7
+ houseDeg: 0.01,
8
+ rootPreferredMinutes: 2,
9
+ rootHardMinutes: 10,
10
+ riseSetMinutes: 1,
11
+ eclipseMinutes: 1,
12
+ dedupeMinutes: 1,
13
+ };
14
+
15
+ export const MINUTES_TO_DAYS = 1 / 1440;
16
+
17
+ export function minutesToDays(minutes: number): number {
18
+ return minutes * MINUTES_TO_DAYS;
19
+ }
20
+
21
+ export function minutesBetweenIso(aIso: string, bIso: string): number {
22
+ return Math.abs(new Date(aIso).getTime() - new Date(bIso).getTime()) / 60000;
23
+ }