chart2txt 0.5.2 → 0.7.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 +101 -34
- package/dist/chart2txt.d.ts +9 -0
- package/dist/chart2txt.js +30 -0
- package/dist/chart2txt.min.js +1 -1
- package/dist/config/ChartSettings.d.ts +13 -6
- package/dist/config/ChartSettings.js +36 -10
- package/dist/constants.d.ts +17 -2
- package/dist/constants.js +301 -32
- package/dist/core/analysis.d.ts +6 -0
- package/dist/core/analysis.js +235 -0
- package/dist/core/aspectPatterns.d.ts +10 -0
- package/dist/core/aspectPatterns.js +460 -0
- package/dist/core/aspects.d.ts +14 -11
- package/dist/core/aspects.js +142 -40
- package/dist/core/astrology.d.ts +8 -2
- package/dist/core/astrology.js +23 -6
- package/dist/core/dignities.d.ts +2 -0
- package/dist/core/dignities.js +71 -0
- package/dist/core/dispositors.d.ts +9 -0
- package/dist/core/dispositors.js +57 -0
- package/dist/core/grouping.d.ts +9 -0
- package/dist/core/grouping.js +45 -0
- package/dist/core/signDistributions.d.ts +21 -0
- package/dist/core/signDistributions.js +50 -0
- package/dist/core/stelliums.d.ts +10 -0
- package/dist/core/stelliums.js +108 -0
- package/dist/formatters/text/sections/angles.js +4 -4
- package/dist/formatters/text/sections/aspectPatterns.d.ts +9 -0
- package/dist/formatters/text/sections/aspectPatterns.js +199 -0
- package/dist/formatters/text/sections/aspects.d.ts +3 -6
- package/dist/formatters/text/sections/aspects.js +35 -49
- package/dist/formatters/text/sections/birthdata.js +1 -1
- package/dist/formatters/text/sections/dispositors.d.ts +8 -0
- package/dist/formatters/text/sections/dispositors.js +19 -0
- package/dist/formatters/text/sections/houseOverlays.d.ts +11 -6
- package/dist/formatters/text/sections/houseOverlays.js +38 -69
- package/dist/formatters/text/sections/houses.d.ts +6 -0
- package/dist/formatters/text/sections/houses.js +36 -0
- package/dist/formatters/text/sections/metadata.d.ts +2 -0
- package/dist/formatters/text/sections/metadata.js +54 -0
- package/dist/formatters/text/sections/planets.d.ts +3 -5
- package/dist/formatters/text/sections/planets.js +12 -38
- package/dist/formatters/text/sections/signDistributions.d.ts +9 -0
- package/dist/formatters/text/sections/signDistributions.js +21 -0
- package/dist/formatters/text/textFormatter.d.ts +4 -5
- package/dist/formatters/text/textFormatter.js +86 -112
- package/dist/index.d.ts +7 -4
- package/dist/index.js +11 -6
- package/dist/types.d.ts +159 -13
- package/dist/types.js +15 -0
- package/dist/utils/formatting.d.ts +10 -0
- package/dist/utils/formatting.js +56 -0
- package/dist/utils/houseCalculations.d.ts +10 -0
- package/dist/utils/houseCalculations.js +23 -0
- package/dist/utils/precision.d.ts +49 -0
- package/dist/utils/precision.js +71 -0
- package/dist/utils/validation.d.ts +37 -0
- package/dist/utils/validation.js +181 -0
- package/package.json +2 -1
package/dist/types.d.ts
CHANGED
|
@@ -17,30 +17,176 @@ export interface ChartData {
|
|
|
17
17
|
}
|
|
18
18
|
export type MultiChartData = ChartData[];
|
|
19
19
|
export declare function isMultiChartData(obj: ChartData | MultiChartData): obj is MultiChartData;
|
|
20
|
+
export type UnionedPoint = [Point, string];
|
|
21
|
+
export declare enum AspectClassification {
|
|
22
|
+
Major = "major",
|
|
23
|
+
Minor = "minor",
|
|
24
|
+
Esoteric = "esoteric"
|
|
25
|
+
}
|
|
26
|
+
export declare enum PlanetCategory {
|
|
27
|
+
Luminaries = "luminaries",
|
|
28
|
+
Personal = "personal",
|
|
29
|
+
Social = "social",
|
|
30
|
+
Outer = "outer",
|
|
31
|
+
Angles = "angles"
|
|
32
|
+
}
|
|
20
33
|
export interface Aspect {
|
|
21
34
|
name: string;
|
|
22
35
|
angle: number;
|
|
23
36
|
orb: number;
|
|
37
|
+
classification?: AspectClassification;
|
|
38
|
+
}
|
|
39
|
+
export type AspectStrength = 'tight' | 'moderate' | 'wide';
|
|
40
|
+
export interface AspectStrengthThresholds {
|
|
41
|
+
tight: number;
|
|
42
|
+
moderate: number;
|
|
24
43
|
}
|
|
25
44
|
export interface AspectData {
|
|
26
45
|
planetA: string;
|
|
27
46
|
planetB: string;
|
|
47
|
+
p1ChartName?: string;
|
|
48
|
+
p2ChartName?: string;
|
|
28
49
|
aspectType: string;
|
|
29
50
|
orb: number;
|
|
51
|
+
application?: 'applying' | 'separating' | 'exact';
|
|
30
52
|
}
|
|
31
|
-
export interface
|
|
53
|
+
export interface PlanetPosition {
|
|
32
54
|
name: string;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
degree: number;
|
|
56
|
+
sign: string;
|
|
57
|
+
speed?: number;
|
|
58
|
+
house?: number;
|
|
59
|
+
chartName?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface TSquare {
|
|
62
|
+
type: 'T-Square';
|
|
63
|
+
apex: PlanetPosition;
|
|
64
|
+
opposition: [PlanetPosition, PlanetPosition];
|
|
65
|
+
mode: 'Cardinal' | 'Fixed' | 'Mutable';
|
|
66
|
+
averageOrb: number;
|
|
67
|
+
}
|
|
68
|
+
export interface GrandTrine {
|
|
69
|
+
type: 'Grand Trine';
|
|
70
|
+
planets: [PlanetPosition, PlanetPosition, PlanetPosition];
|
|
71
|
+
element: 'Fire' | 'Earth' | 'Air' | 'Water';
|
|
72
|
+
averageOrb: number;
|
|
73
|
+
}
|
|
74
|
+
export interface Stellium {
|
|
75
|
+
type: 'Stellium';
|
|
76
|
+
planets: PlanetPosition[];
|
|
77
|
+
sign?: string;
|
|
78
|
+
houses: number[];
|
|
79
|
+
span: number;
|
|
80
|
+
}
|
|
81
|
+
export interface GrandCross {
|
|
82
|
+
type: 'Grand Cross';
|
|
83
|
+
planets: [PlanetPosition, PlanetPosition, PlanetPosition, PlanetPosition];
|
|
84
|
+
mode: 'Cardinal' | 'Fixed' | 'Mutable';
|
|
85
|
+
averageOrb: number;
|
|
86
|
+
}
|
|
87
|
+
export interface Yod {
|
|
88
|
+
type: 'Yod';
|
|
89
|
+
apex: PlanetPosition;
|
|
90
|
+
base: [PlanetPosition, PlanetPosition];
|
|
91
|
+
averageOrb: number;
|
|
92
|
+
}
|
|
93
|
+
export interface MysticRectangle {
|
|
94
|
+
type: 'Mystic Rectangle';
|
|
95
|
+
oppositions: [
|
|
96
|
+
[
|
|
97
|
+
PlanetPosition,
|
|
98
|
+
PlanetPosition
|
|
99
|
+
],
|
|
100
|
+
[
|
|
101
|
+
PlanetPosition,
|
|
102
|
+
PlanetPosition
|
|
103
|
+
]
|
|
104
|
+
];
|
|
105
|
+
averageOrb: number;
|
|
106
|
+
}
|
|
107
|
+
export interface Kite {
|
|
108
|
+
type: 'Kite';
|
|
109
|
+
grandTrine: [PlanetPosition, PlanetPosition, PlanetPosition];
|
|
110
|
+
opposition: PlanetPosition;
|
|
111
|
+
averageOrb: number;
|
|
112
|
+
}
|
|
113
|
+
export type AspectPattern = TSquare | GrandTrine | Stellium | GrandCross | Yod | MysticRectangle | Kite;
|
|
114
|
+
export interface ChartAnalysis {
|
|
115
|
+
chart: ChartData;
|
|
116
|
+
placements: {
|
|
117
|
+
planets: PlanetPosition[];
|
|
118
|
+
[key: string]: any;
|
|
119
|
+
};
|
|
120
|
+
aspects: AspectData[];
|
|
121
|
+
groupedAspects?: Map<string, AspectData[]>;
|
|
122
|
+
patterns: AspectPattern[];
|
|
123
|
+
stelliums: Stellium[];
|
|
124
|
+
signDistributions: {
|
|
125
|
+
elements: {
|
|
126
|
+
[key: string]: string[];
|
|
127
|
+
};
|
|
128
|
+
modalities: {
|
|
129
|
+
[key: string]: number;
|
|
130
|
+
};
|
|
131
|
+
polarities: {
|
|
132
|
+
[key: string]: number;
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
dispositors: {
|
|
136
|
+
[key: string]: string;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
export interface PairwiseAnalysis {
|
|
140
|
+
chart1: ChartData;
|
|
141
|
+
chart2: ChartData;
|
|
142
|
+
synastryAspects: AspectData[];
|
|
143
|
+
groupedSynastryAspects?: Map<string, AspectData[]>;
|
|
144
|
+
compositePatterns: AspectPattern[];
|
|
145
|
+
houseOverlays: {
|
|
146
|
+
chart1InChart2Houses: {
|
|
147
|
+
[key: string]: number;
|
|
148
|
+
};
|
|
149
|
+
chart2InChart1Houses: {
|
|
150
|
+
[key: string]: number;
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export interface GlobalAnalysis {
|
|
155
|
+
charts: ChartData[];
|
|
156
|
+
patterns: AspectPattern[];
|
|
157
|
+
}
|
|
158
|
+
export interface TransitAnalysis {
|
|
159
|
+
natalChart: ChartData;
|
|
160
|
+
transitChart: ChartData;
|
|
161
|
+
aspects: AspectData[];
|
|
162
|
+
groupedAspects?: Map<string, AspectData[]>;
|
|
163
|
+
patterns: AspectPattern[];
|
|
164
|
+
}
|
|
165
|
+
export interface AstrologicalReport {
|
|
166
|
+
settings: Settings;
|
|
167
|
+
chartAnalyses: ChartAnalysis[];
|
|
168
|
+
pairwiseAnalyses: PairwiseAnalysis[];
|
|
169
|
+
globalAnalysis?: GlobalAnalysis;
|
|
170
|
+
transitAnalyses: TransitAnalysis[];
|
|
171
|
+
globalTransitAnalysis?: GlobalAnalysis;
|
|
172
|
+
}
|
|
173
|
+
export interface AnalysisSettings {
|
|
174
|
+
aspectDefinitions?: Aspect[] | 'traditional' | 'modern' | 'tight' | 'wide';
|
|
175
|
+
skipOutOfSignAspects?: boolean;
|
|
176
|
+
includeAspectPatterns?: boolean;
|
|
177
|
+
includeSignDistributions?: boolean;
|
|
178
|
+
}
|
|
179
|
+
export interface GroupingSettings {
|
|
180
|
+
aspectStrengthThresholds?: AspectStrengthThresholds;
|
|
181
|
+
}
|
|
182
|
+
export interface FormattingSettings {
|
|
183
|
+
dateFormat?: string;
|
|
184
|
+
houseSystemName?: string;
|
|
185
|
+
}
|
|
186
|
+
export interface Settings extends AnalysisSettings, GroupingSettings, FormattingSettings {
|
|
45
187
|
}
|
|
46
188
|
export type PartialSettings = Partial<Settings>;
|
|
189
|
+
export type ChartDataWithInfo = {
|
|
190
|
+
data: ChartData;
|
|
191
|
+
index: number;
|
|
192
|
+
};
|
package/dist/types.js
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PlanetCategory = exports.AspectClassification = void 0;
|
|
3
4
|
exports.isMultiChartData = isMultiChartData;
|
|
4
5
|
function isMultiChartData(obj) {
|
|
5
6
|
return Array.isArray(obj);
|
|
6
7
|
}
|
|
8
|
+
var AspectClassification;
|
|
9
|
+
(function (AspectClassification) {
|
|
10
|
+
AspectClassification["Major"] = "major";
|
|
11
|
+
AspectClassification["Minor"] = "minor";
|
|
12
|
+
AspectClassification["Esoteric"] = "esoteric";
|
|
13
|
+
})(AspectClassification || (exports.AspectClassification = AspectClassification = {}));
|
|
14
|
+
var PlanetCategory;
|
|
15
|
+
(function (PlanetCategory) {
|
|
16
|
+
PlanetCategory["Luminaries"] = "luminaries";
|
|
17
|
+
PlanetCategory["Personal"] = "personal";
|
|
18
|
+
PlanetCategory["Social"] = "social";
|
|
19
|
+
PlanetCategory["Outer"] = "outer";
|
|
20
|
+
PlanetCategory["Angles"] = "angles";
|
|
21
|
+
})(PlanetCategory || (exports.PlanetCategory = PlanetCategory = {}));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Point, PlanetPosition } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a number to its ordinal form (1st, 2nd, 3rd, etc.)
|
|
4
|
+
* @param num The number to convert
|
|
5
|
+
* @returns The ordinal string
|
|
6
|
+
*/
|
|
7
|
+
export declare function getOrdinal(num: number): string;
|
|
8
|
+
export declare function getSign(degree: number): string;
|
|
9
|
+
export declare function getHouse(degree: number, houseCusps: number[]): number;
|
|
10
|
+
export declare function getPlanetPositions(planets: Point[], houseCusps?: number[]): PlanetPosition[];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getOrdinal = getOrdinal;
|
|
4
|
+
exports.getSign = getSign;
|
|
5
|
+
exports.getHouse = getHouse;
|
|
6
|
+
exports.getPlanetPositions = getPlanetPositions;
|
|
7
|
+
const constants_1 = require("../constants");
|
|
8
|
+
function normalizeDegree(degree) {
|
|
9
|
+
return ((degree % 360) + 360) % 360;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Converts a number to its ordinal form (1st, 2nd, 3rd, etc.)
|
|
13
|
+
* @param num The number to convert
|
|
14
|
+
* @returns The ordinal string
|
|
15
|
+
*/
|
|
16
|
+
function getOrdinal(num) {
|
|
17
|
+
const suffix = ['th', 'st', 'nd', 'rd'];
|
|
18
|
+
const v = num % 100;
|
|
19
|
+
return num + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
|
|
20
|
+
}
|
|
21
|
+
function getSign(degree) {
|
|
22
|
+
const signIndex = Math.floor(degree / 30);
|
|
23
|
+
return constants_1.ZODIAC_SIGNS[signIndex];
|
|
24
|
+
}
|
|
25
|
+
function getHouse(degree, houseCusps) {
|
|
26
|
+
for (let i = 0; i < 12; i++) {
|
|
27
|
+
const cusp1 = houseCusps[i];
|
|
28
|
+
const cusp2 = houseCusps[(i + 1) % 12];
|
|
29
|
+
if (cusp1 < cusp2) {
|
|
30
|
+
if (degree >= cusp1 && degree < cusp2) {
|
|
31
|
+
return i + 1;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
if (degree >= cusp1 || degree < cusp2) {
|
|
36
|
+
return i + 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return -1; // Should not happen
|
|
41
|
+
}
|
|
42
|
+
function getPlanetPositions(planets, houseCusps) {
|
|
43
|
+
return planets.map((planet) => {
|
|
44
|
+
const normalizedDegree = normalizeDegree(planet.degree);
|
|
45
|
+
const position = {
|
|
46
|
+
name: planet.name,
|
|
47
|
+
degree: normalizedDegree,
|
|
48
|
+
sign: getSign(normalizedDegree),
|
|
49
|
+
speed: planet.speed,
|
|
50
|
+
};
|
|
51
|
+
if (houseCusps) {
|
|
52
|
+
position.house = getHouse(normalizedDegree, houseCusps);
|
|
53
|
+
}
|
|
54
|
+
return position;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ChartData } from '../types';
|
|
2
|
+
export declare function calculateHouseOverlays(chart1: ChartData, chart2: ChartData): {
|
|
3
|
+
chart1InChart2Houses: {
|
|
4
|
+
[key: string]: number;
|
|
5
|
+
};
|
|
6
|
+
chart2InChart1Houses: {
|
|
7
|
+
[key: string]: number;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare function getHouseForPoint(degree: number, houseCusps: number[]): number;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateHouseOverlays = calculateHouseOverlays;
|
|
4
|
+
exports.getHouseForPoint = getHouseForPoint;
|
|
5
|
+
const formatting_1 = require("./formatting");
|
|
6
|
+
function calculateHouseOverlays(chart1, chart2) {
|
|
7
|
+
const chart1InChart2Houses = {};
|
|
8
|
+
if (chart2.houseCusps) {
|
|
9
|
+
for (const planet of chart1.planets) {
|
|
10
|
+
chart1InChart2Houses[planet.name] = (0, formatting_1.getHouse)(planet.degree, chart2.houseCusps);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const chart2InChart1Houses = {};
|
|
14
|
+
if (chart1.houseCusps) {
|
|
15
|
+
for (const planet of chart2.planets) {
|
|
16
|
+
chart2InChart1Houses[planet.name] = (0, formatting_1.getHouse)(planet.degree, chart1.houseCusps);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { chart1InChart2Houses, chart2InChart1Houses };
|
|
20
|
+
}
|
|
21
|
+
function getHouseForPoint(degree, houseCusps) {
|
|
22
|
+
return (0, formatting_1.getHouse)(degree, houseCusps);
|
|
23
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Floating point precision utilities for astrological calculations
|
|
3
|
+
*/
|
|
4
|
+
export declare const DEFAULT_EPSILON = 0.0001;
|
|
5
|
+
/**
|
|
6
|
+
* Compares two floating-point numbers with epsilon tolerance
|
|
7
|
+
* @param a First number
|
|
8
|
+
* @param b Second number
|
|
9
|
+
* @param epsilon Tolerance value (default: DEFAULT_EPSILON)
|
|
10
|
+
* @returns True if numbers are equal within tolerance
|
|
11
|
+
*/
|
|
12
|
+
export declare function floatEquals(a: number, b: number, epsilon?: number): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Checks if a number is close to zero within epsilon tolerance
|
|
15
|
+
* @param value The number to check
|
|
16
|
+
* @param epsilon Tolerance value (default: DEFAULT_EPSILON)
|
|
17
|
+
* @returns True if number is close to zero
|
|
18
|
+
*/
|
|
19
|
+
export declare function isNearZero(value: number, epsilon?: number): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Rounds a degree value to a reasonable precision (4 decimal places)
|
|
22
|
+
* This prevents accumulation of floating-point errors in calculations
|
|
23
|
+
* @param degrees The degree value to round
|
|
24
|
+
* @returns Rounded degree value
|
|
25
|
+
*/
|
|
26
|
+
export declare function roundDegrees(degrees: number): number;
|
|
27
|
+
/**
|
|
28
|
+
* Compares two degree values with appropriate epsilon for astrological calculations
|
|
29
|
+
* @param deg1 First degree value
|
|
30
|
+
* @param deg2 Second degree value
|
|
31
|
+
* @param epsilon Tolerance in degrees (default: 0.0001°)
|
|
32
|
+
* @returns True if degrees are equal within tolerance
|
|
33
|
+
*/
|
|
34
|
+
export declare function degreeEquals(deg1: number, deg2: number, epsilon?: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Checks if a planet is exactly on a house cusp within floating-point precision
|
|
37
|
+
* @param planetDegree Planet's degree position
|
|
38
|
+
* @param cuspDegree House cusp degree
|
|
39
|
+
* @param epsilon Tolerance in degrees (default: 0.001° = about 3.6 arc-seconds)
|
|
40
|
+
* @returns True if planet is on the cusp within tolerance
|
|
41
|
+
*/
|
|
42
|
+
export declare function isOnCusp(planetDegree: number, cuspDegree: number, epsilon?: number): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Checks if an aspect is exact within floating-point precision
|
|
45
|
+
* @param actualOrb The actual orb of the aspect
|
|
46
|
+
* @param epsilon Tolerance in degrees (default: 0.1° = 6 arc-minutes)
|
|
47
|
+
* @returns True if aspect is exact within tolerance
|
|
48
|
+
*/
|
|
49
|
+
export declare function isExactAspect(actualOrb: number, epsilon?: number): boolean;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Floating point precision utilities for astrological calculations
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_EPSILON = void 0;
|
|
7
|
+
exports.floatEquals = floatEquals;
|
|
8
|
+
exports.isNearZero = isNearZero;
|
|
9
|
+
exports.roundDegrees = roundDegrees;
|
|
10
|
+
exports.degreeEquals = degreeEquals;
|
|
11
|
+
exports.isOnCusp = isOnCusp;
|
|
12
|
+
exports.isExactAspect = isExactAspect;
|
|
13
|
+
// Default epsilon for floating-point comparisons (about 0.0036 arc-minutes)
|
|
14
|
+
exports.DEFAULT_EPSILON = 1e-4;
|
|
15
|
+
/**
|
|
16
|
+
* Compares two floating-point numbers with epsilon tolerance
|
|
17
|
+
* @param a First number
|
|
18
|
+
* @param b Second number
|
|
19
|
+
* @param epsilon Tolerance value (default: DEFAULT_EPSILON)
|
|
20
|
+
* @returns True if numbers are equal within tolerance
|
|
21
|
+
*/
|
|
22
|
+
function floatEquals(a, b, epsilon = exports.DEFAULT_EPSILON) {
|
|
23
|
+
return Math.abs(a - b) < epsilon;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Checks if a number is close to zero within epsilon tolerance
|
|
27
|
+
* @param value The number to check
|
|
28
|
+
* @param epsilon Tolerance value (default: DEFAULT_EPSILON)
|
|
29
|
+
* @returns True if number is close to zero
|
|
30
|
+
*/
|
|
31
|
+
function isNearZero(value, epsilon = exports.DEFAULT_EPSILON) {
|
|
32
|
+
return Math.abs(value) < epsilon;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Rounds a degree value to a reasonable precision (4 decimal places)
|
|
36
|
+
* This prevents accumulation of floating-point errors in calculations
|
|
37
|
+
* @param degrees The degree value to round
|
|
38
|
+
* @returns Rounded degree value
|
|
39
|
+
*/
|
|
40
|
+
function roundDegrees(degrees) {
|
|
41
|
+
return Math.round(degrees * 10000) / 10000;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Compares two degree values with appropriate epsilon for astrological calculations
|
|
45
|
+
* @param deg1 First degree value
|
|
46
|
+
* @param deg2 Second degree value
|
|
47
|
+
* @param epsilon Tolerance in degrees (default: 0.0001°)
|
|
48
|
+
* @returns True if degrees are equal within tolerance
|
|
49
|
+
*/
|
|
50
|
+
function degreeEquals(deg1, deg2, epsilon = exports.DEFAULT_EPSILON) {
|
|
51
|
+
return floatEquals(deg1, deg2, epsilon);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Checks if a planet is exactly on a house cusp within floating-point precision
|
|
55
|
+
* @param planetDegree Planet's degree position
|
|
56
|
+
* @param cuspDegree House cusp degree
|
|
57
|
+
* @param epsilon Tolerance in degrees (default: 0.001° = about 3.6 arc-seconds)
|
|
58
|
+
* @returns True if planet is on the cusp within tolerance
|
|
59
|
+
*/
|
|
60
|
+
function isOnCusp(planetDegree, cuspDegree, epsilon = 0.001) {
|
|
61
|
+
return degreeEquals(planetDegree, cuspDegree, epsilon);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Checks if an aspect is exact within floating-point precision
|
|
65
|
+
* @param actualOrb The actual orb of the aspect
|
|
66
|
+
* @param epsilon Tolerance in degrees (default: 0.1° = 6 arc-minutes)
|
|
67
|
+
* @returns True if aspect is exact within tolerance
|
|
68
|
+
*/
|
|
69
|
+
function isExactAspect(actualOrb, epsilon = 0.1) {
|
|
70
|
+
return isNearZero(actualOrb, epsilon);
|
|
71
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ChartData, Point, MultiChartData } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Validates a Point object
|
|
4
|
+
* @param point The point to validate
|
|
5
|
+
* @returns Error message if invalid, null if valid
|
|
6
|
+
*/
|
|
7
|
+
export declare function validatePoint(point: Point): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Validates an array of Points
|
|
10
|
+
* @param points The points array to validate
|
|
11
|
+
* @returns Error message if invalid, null if valid
|
|
12
|
+
*/
|
|
13
|
+
export declare function validatePoints(points: Point[]): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Validates house cusps array
|
|
16
|
+
* @param houseCusps The house cusps to validate
|
|
17
|
+
* @returns Error message if invalid, null if valid
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateHouseCusps(houseCusps: number[] | undefined): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Validates a ChartData object
|
|
22
|
+
* @param chartData The chart data to validate
|
|
23
|
+
* @returns Error message if invalid, null if valid
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateChartData(chartData: ChartData): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Validates MultiChartData
|
|
28
|
+
* @param multiChartData The multi-chart data to validate
|
|
29
|
+
* @returns Error message if invalid, null if valid
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateMultiChartData(multiChartData: MultiChartData): string | null;
|
|
32
|
+
/**
|
|
33
|
+
* Validates chart data (single or multi-chart)
|
|
34
|
+
* @param data The data to validate
|
|
35
|
+
* @returns Error message if invalid, null if valid
|
|
36
|
+
*/
|
|
37
|
+
export declare function validateInputData(data: ChartData | MultiChartData): string | null;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validatePoint = validatePoint;
|
|
4
|
+
exports.validatePoints = validatePoints;
|
|
5
|
+
exports.validateHouseCusps = validateHouseCusps;
|
|
6
|
+
exports.validateChartData = validateChartData;
|
|
7
|
+
exports.validateMultiChartData = validateMultiChartData;
|
|
8
|
+
exports.validateInputData = validateInputData;
|
|
9
|
+
/**
|
|
10
|
+
* Validates a Point object
|
|
11
|
+
* @param point The point to validate
|
|
12
|
+
* @returns Error message if invalid, null if valid
|
|
13
|
+
*/
|
|
14
|
+
function validatePoint(point) {
|
|
15
|
+
if (!point || typeof point !== 'object') {
|
|
16
|
+
return 'Point must be an object';
|
|
17
|
+
}
|
|
18
|
+
if (typeof point.name !== 'string' || point.name.trim() === '') {
|
|
19
|
+
return 'Point name must be a non-empty string';
|
|
20
|
+
}
|
|
21
|
+
if (typeof point.degree !== 'number' || !isFinite(point.degree)) {
|
|
22
|
+
return `Point ${point.name} has invalid degree value: ${point.degree}`;
|
|
23
|
+
}
|
|
24
|
+
if (point.speed !== undefined &&
|
|
25
|
+
(typeof point.speed !== 'number' || !isFinite(point.speed))) {
|
|
26
|
+
return `Point ${point.name} has invalid speed value: ${point.speed}`;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validates an array of Points
|
|
32
|
+
* @param points The points array to validate
|
|
33
|
+
* @returns Error message if invalid, null if valid
|
|
34
|
+
*/
|
|
35
|
+
function validatePoints(points) {
|
|
36
|
+
if (!Array.isArray(points)) {
|
|
37
|
+
return 'Points must be an array';
|
|
38
|
+
}
|
|
39
|
+
for (let i = 0; i < points.length; i++) {
|
|
40
|
+
const error = validatePoint(points[i]);
|
|
41
|
+
if (error) {
|
|
42
|
+
return `Point at index ${i}: ${error}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Check for duplicate point names
|
|
46
|
+
const names = points.map((p) => p.name);
|
|
47
|
+
const duplicates = names.filter((name, index) => names.indexOf(name) !== index);
|
|
48
|
+
if (duplicates.length > 0) {
|
|
49
|
+
return `Duplicate point names found: ${duplicates.join(', ')}`;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validates house cusps array
|
|
55
|
+
* @param houseCusps The house cusps to validate
|
|
56
|
+
* @returns Error message if invalid, null if valid
|
|
57
|
+
*/
|
|
58
|
+
function validateHouseCusps(houseCusps) {
|
|
59
|
+
if (houseCusps === undefined) {
|
|
60
|
+
return null; // Optional field
|
|
61
|
+
}
|
|
62
|
+
if (!Array.isArray(houseCusps)) {
|
|
63
|
+
return 'House cusps must be an array';
|
|
64
|
+
}
|
|
65
|
+
if (houseCusps.length !== 12) {
|
|
66
|
+
return `House cusps must contain exactly 12 values, got ${houseCusps.length}`;
|
|
67
|
+
}
|
|
68
|
+
for (let i = 0; i < houseCusps.length; i++) {
|
|
69
|
+
if (typeof houseCusps[i] !== 'number' || !isFinite(houseCusps[i])) {
|
|
70
|
+
return `House cusp ${i + 1} has invalid value: ${houseCusps[i]}`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validates a ChartData object
|
|
77
|
+
* @param chartData The chart data to validate
|
|
78
|
+
* @returns Error message if invalid, null if valid
|
|
79
|
+
*/
|
|
80
|
+
function validateChartData(chartData) {
|
|
81
|
+
if (!chartData || typeof chartData !== 'object') {
|
|
82
|
+
return 'Chart data must be an object';
|
|
83
|
+
}
|
|
84
|
+
if (typeof chartData.name !== 'string' || chartData.name.trim() === '') {
|
|
85
|
+
return 'Chart name must be a non-empty string';
|
|
86
|
+
}
|
|
87
|
+
const pointsError = validatePoints(chartData.planets);
|
|
88
|
+
if (pointsError) {
|
|
89
|
+
return `Planets validation failed: ${pointsError}`;
|
|
90
|
+
}
|
|
91
|
+
if (chartData.ascendant !== undefined) {
|
|
92
|
+
if (typeof chartData.ascendant !== 'number' ||
|
|
93
|
+
!isFinite(chartData.ascendant)) {
|
|
94
|
+
return `Ascendant has invalid value: ${chartData.ascendant}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (chartData.midheaven !== undefined) {
|
|
98
|
+
if (typeof chartData.midheaven !== 'number' ||
|
|
99
|
+
!isFinite(chartData.midheaven)) {
|
|
100
|
+
return `Midheaven has invalid value: ${chartData.midheaven}`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const houseCuspsError = validateHouseCusps(chartData.houseCusps);
|
|
104
|
+
if (houseCuspsError) {
|
|
105
|
+
return `House cusps validation failed: ${houseCuspsError}`;
|
|
106
|
+
}
|
|
107
|
+
if (chartData.points !== undefined) {
|
|
108
|
+
const pointsError = validatePoints(chartData.points);
|
|
109
|
+
if (pointsError) {
|
|
110
|
+
return `Additional points validation failed: ${pointsError}`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (chartData.timestamp !== undefined) {
|
|
114
|
+
if (!(chartData.timestamp instanceof Date) ||
|
|
115
|
+
isNaN(chartData.timestamp.getTime())) {
|
|
116
|
+
return 'Timestamp must be a valid Date object';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (chartData.location !== undefined) {
|
|
120
|
+
if (typeof chartData.location !== 'string') {
|
|
121
|
+
return 'Location must be a string';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (chartData.chartType !== undefined) {
|
|
125
|
+
const validTypes = ['natal', 'event', 'transit'];
|
|
126
|
+
if (!validTypes.includes(chartData.chartType)) {
|
|
127
|
+
return `Chart type must be one of: ${validTypes.join(', ')}`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Validates MultiChartData
|
|
134
|
+
* @param multiChartData The multi-chart data to validate
|
|
135
|
+
* @returns Error message if invalid, null if valid
|
|
136
|
+
*/
|
|
137
|
+
function validateMultiChartData(multiChartData) {
|
|
138
|
+
if (!Array.isArray(multiChartData)) {
|
|
139
|
+
return 'Multi-chart data must be an array';
|
|
140
|
+
}
|
|
141
|
+
if (multiChartData.length === 0) {
|
|
142
|
+
return 'Multi-chart data must contain at least one chart';
|
|
143
|
+
}
|
|
144
|
+
if (multiChartData.length > 10) {
|
|
145
|
+
return 'Multi-chart data cannot contain more than 10 charts';
|
|
146
|
+
}
|
|
147
|
+
for (let i = 0; i < multiChartData.length; i++) {
|
|
148
|
+
const error = validateChartData(multiChartData[i]);
|
|
149
|
+
if (error) {
|
|
150
|
+
return `Chart at index ${i} (${multiChartData[i]?.name || 'unnamed'}): ${error}`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Check for duplicate chart names
|
|
154
|
+
const names = multiChartData.map((chart) => chart.name);
|
|
155
|
+
const duplicates = names.filter((name, index) => names.indexOf(name) !== index);
|
|
156
|
+
if (duplicates.length > 0) {
|
|
157
|
+
return `Duplicate chart names found: ${duplicates.join(', ')}`;
|
|
158
|
+
}
|
|
159
|
+
// Validate transit charts
|
|
160
|
+
const transitCharts = multiChartData.filter((chart) => chart.chartType === 'transit');
|
|
161
|
+
if (transitCharts.length > 1) {
|
|
162
|
+
return 'Cannot have more than one transit chart';
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Validates chart data (single or multi-chart)
|
|
168
|
+
* @param data The data to validate
|
|
169
|
+
* @returns Error message if invalid, null if valid
|
|
170
|
+
*/
|
|
171
|
+
function validateInputData(data) {
|
|
172
|
+
if (!data) {
|
|
173
|
+
return 'Data is required';
|
|
174
|
+
}
|
|
175
|
+
if (Array.isArray(data)) {
|
|
176
|
+
return validateMultiChartData(data);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
return validateChartData(data);
|
|
180
|
+
}
|
|
181
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chart2txt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Convert astrological chart data to human-readable text",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"test": "jest",
|
|
13
13
|
"test:config": "ts-node test-configurations.ts",
|
|
14
14
|
"lint": "eslint src/**/*.ts",
|
|
15
|
+
"lint:fix": "eslint src/**/*.ts --fix",
|
|
15
16
|
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\""
|
|
16
17
|
},
|
|
17
18
|
"keywords": [
|