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.
- package/.env.example +13 -0
- package/.github/pull_request_template.md +16 -0
- package/.github/workflows/release.yml +35 -0
- package/.github/workflows/test.yml +32 -0
- package/AGENTS.md +99 -0
- package/LICENSE +18 -0
- package/NOTICE.md +45 -0
- package/README.md +301 -0
- package/SETUP.md +70 -0
- package/TESTING_SUMMARY.md +238 -0
- package/TEST_SUITE_STATUS.md +218 -0
- package/biome.json +48 -0
- package/dist/astro-service.d.ts +98 -0
- package/dist/astro-service.js +496 -0
- package/dist/chart-types.d.ts +52 -0
- package/dist/chart-types.js +51 -0
- package/dist/charts.d.ts +125 -0
- package/dist/charts.js +324 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +472 -0
- package/dist/constants.d.ts +81 -0
- package/dist/constants.js +76 -0
- package/dist/eclipses.d.ts +85 -0
- package/dist/eclipses.js +184 -0
- package/dist/ephemeris.d.ts +120 -0
- package/dist/ephemeris.js +379 -0
- package/dist/formatter.d.ts +2 -0
- package/dist/formatter.js +22 -0
- package/dist/houses.d.ts +82 -0
- package/dist/houses.js +169 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +150 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +31 -0
- package/dist/logger.d.ts +25 -0
- package/dist/logger.js +73 -0
- package/dist/profile-store.d.ts +48 -0
- package/dist/profile-store.js +156 -0
- package/dist/riseset.d.ts +82 -0
- package/dist/riseset.js +185 -0
- package/dist/storage.d.ts +10 -0
- package/dist/storage.js +40 -0
- package/dist/time-utils.d.ts +68 -0
- package/dist/time-utils.js +136 -0
- package/dist/tool-registry.d.ts +35 -0
- package/dist/tool-registry.js +307 -0
- package/dist/tool-result.d.ts +175 -0
- package/dist/tool-result.js +188 -0
- package/dist/transits.d.ts +108 -0
- package/dist/transits.js +263 -0
- package/dist/types.d.ts +450 -0
- package/dist/types.js +161 -0
- package/example-usage.md +131 -0
- package/natal-chart.json +187 -0
- package/package.json +61 -0
- package/scripts/download-ephemeris.js +115 -0
- package/setup.sh +21 -0
- package/src/astro-service.ts +710 -0
- package/src/chart-types.ts +125 -0
- package/src/charts.ts +399 -0
- package/src/cli.ts +694 -0
- package/src/constants.ts +89 -0
- package/src/eclipses.ts +226 -0
- package/src/ephemeris.ts +437 -0
- package/src/formatter.ts +25 -0
- package/src/houses.ts +202 -0
- package/src/index.ts +170 -0
- package/src/loader.ts +36 -0
- package/src/logger.ts +104 -0
- package/src/profile-store.ts +285 -0
- package/src/riseset.ts +229 -0
- package/src/time-utils.ts +167 -0
- package/src/tool-registry.ts +357 -0
- package/src/tool-result.ts +283 -0
- package/src/transits.ts +352 -0
- package/src/types.ts +547 -0
- package/tests/README.md +173 -0
- package/tests/TESTING_STRATEGY.md +178 -0
- package/tests/fixtures/bowen-yang-chart.ts +69 -0
- package/tests/fixtures/calculate-expected.ts +81 -0
- package/tests/fixtures/expected-results.ts +117 -0
- package/tests/fixtures/generate-expected-simple.ts +94 -0
- package/tests/helpers/date-fixtures.ts +15 -0
- package/tests/helpers/ephem.ts +11 -0
- package/tests/helpers/temp.ts +9 -0
- package/tests/setup.ts +11 -0
- package/tests/unit/astro-service.test.ts +323 -0
- package/tests/unit/chart-types.test.ts +18 -0
- package/tests/unit/charts-errors.test.ts +42 -0
- package/tests/unit/charts.test.ts +157 -0
- package/tests/unit/cli-commands.test.ts +82 -0
- package/tests/unit/cli-profiles.test.ts +128 -0
- package/tests/unit/cli.test.ts +191 -0
- package/tests/unit/constants.test.ts +26 -0
- package/tests/unit/correctness-critical.test.ts +408 -0
- package/tests/unit/eclipses.test.ts +108 -0
- package/tests/unit/ephemeris.test.ts +213 -0
- package/tests/unit/error-handling.test.ts +116 -0
- package/tests/unit/formatter.test.ts +29 -0
- package/tests/unit/houses-errors.test.ts +27 -0
- package/tests/unit/houses-validation.test.ts +164 -0
- package/tests/unit/houses.test.ts +205 -0
- package/tests/unit/profile-store.test.ts +163 -0
- package/tests/unit/real-user-charts.test.ts +148 -0
- package/tests/unit/riseset.test.ts +106 -0
- package/tests/unit/solver-edges.test.ts +197 -0
- package/tests/unit/time-utils-temporal.test.ts +303 -0
- package/tests/unit/time-utils.test.ts +173 -0
- package/tests/unit/tool-registry.test.ts +222 -0
- package/tests/unit/tool-result.test.ts +45 -0
- package/tests/unit/transit-correctness.test.ts +78 -0
- package/tests/unit/transits.test.ts +238 -0
- package/tests/validation/README.md +32 -0
- package/tests/validation/adapters/astrolog.ts +306 -0
- package/tests/validation/adapters/internal.ts +184 -0
- package/tests/validation/compare/eclipses.ts +47 -0
- package/tests/validation/compare/houses.ts +76 -0
- package/tests/validation/compare/positions.ts +104 -0
- package/tests/validation/compare/riseSet.ts +48 -0
- package/tests/validation/compare/roots.ts +90 -0
- package/tests/validation/compare/transits.ts +69 -0
- package/tests/validation/fixtures/astrolog-parity/core.ts +194 -0
- package/tests/validation/fixtures/eclipses/core.ts +14 -0
- package/tests/validation/fixtures/houses/core.ts +47 -0
- package/tests/validation/fixtures/positions/core.ts +159 -0
- package/tests/validation/fixtures/rise-set/core.ts +20 -0
- package/tests/validation/fixtures/roots/core.ts +47 -0
- package/tests/validation/fixtures/transits/core.ts +61 -0
- package/tests/validation/fixtures/transits/dst.ts +21 -0
- package/tests/validation/oracle.spec.ts +129 -0
- package/tests/validation/utils/denseRootOracle.ts +269 -0
- package/tests/validation/utils/fixtureTypes.ts +146 -0
- package/tests/validation/utils/report.ts +60 -0
- package/tests/validation/utils/tolerances.ts +23 -0
- package/tests/validation/validation.spec.ts +836 -0
- package/tools/color-picker.html +388 -0
- package/tsconfig.json +17 -0
- 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
|
+
}
|