chart2txt 0.7.0 → 0.8.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.
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OrbResolver = void 0;
4
+ const types_1 = require("../types");
5
+ /**
6
+ * Default planet categorization mapping
7
+ */
8
+ const DEFAULT_PLANET_MAPPING = {
9
+ Sun: types_1.PlanetCategory.Luminaries,
10
+ Moon: types_1.PlanetCategory.Luminaries,
11
+ Mercury: types_1.PlanetCategory.Personal,
12
+ Venus: types_1.PlanetCategory.Personal,
13
+ Mars: types_1.PlanetCategory.Personal,
14
+ Jupiter: types_1.PlanetCategory.Social,
15
+ Saturn: types_1.PlanetCategory.Social,
16
+ Uranus: types_1.PlanetCategory.Outer,
17
+ Neptune: types_1.PlanetCategory.Outer,
18
+ Pluto: types_1.PlanetCategory.Outer,
19
+ Ascendant: types_1.PlanetCategory.Angles,
20
+ Midheaven: types_1.PlanetCategory.Angles,
21
+ MC: types_1.PlanetCategory.Angles,
22
+ ASC: types_1.PlanetCategory.Angles,
23
+ };
24
+ /**
25
+ * Resolves orbs using hierarchical rules with caching for performance
26
+ */
27
+ class OrbResolver {
28
+ constructor(orbConfiguration = {}) {
29
+ this.orbCache = new Map();
30
+ this.orbConfiguration = orbConfiguration;
31
+ this.planetMapping = {
32
+ ...DEFAULT_PLANET_MAPPING,
33
+ ...orbConfiguration.planetMapping,
34
+ };
35
+ }
36
+ /**
37
+ * Resolves the appropriate orb for a given context
38
+ */
39
+ resolveOrb(context) {
40
+ const cacheKey = this.generateCacheKey(context);
41
+ // Check cache first
42
+ if (this.orbCache.has(cacheKey)) {
43
+ return this.orbCache.get(cacheKey);
44
+ }
45
+ const orb = this.calculateOrb(context);
46
+ this.orbCache.set(cacheKey, orb);
47
+ return orb;
48
+ }
49
+ /**
50
+ * Clears the orb cache (useful when configuration changes)
51
+ */
52
+ clearCache() {
53
+ this.orbCache.clear();
54
+ }
55
+ /**
56
+ * Gets the planet category for a given planet name
57
+ */
58
+ getPlanetCategory(planetName) {
59
+ return this.planetMapping[planetName];
60
+ }
61
+ calculateOrb(context) {
62
+ let baseOrb = this.getBaseOrb(context);
63
+ // Apply aspect classification multipliers
64
+ baseOrb = this.applyClassificationMultipliers(baseOrb, context.aspect);
65
+ // Apply contextual multipliers (synastry, transit, etc.)
66
+ baseOrb = this.applyContextualMultipliers(baseOrb, context);
67
+ // Ensure minimum constraints
68
+ baseOrb = this.applyConstraints(baseOrb, context.aspect);
69
+ // Round to reasonable precision
70
+ return Math.round(baseOrb * 10) / 10;
71
+ }
72
+ getBaseOrb(context) {
73
+ const planetAName = typeof context.planetA === 'string'
74
+ ? context.planetA
75
+ : context.planetA.name;
76
+ const planetBName = typeof context.planetB === 'string'
77
+ ? context.planetB
78
+ : context.planetB.name;
79
+ // Try to get planet-specific orbs
80
+ const orbA = this.getPlanetSpecificOrb(planetAName, context.aspect);
81
+ const orbB = this.getPlanetSpecificOrb(planetBName, context.aspect);
82
+ // Use the larger of the two planet-specific orbs, or aspect default
83
+ if (orbA !== null && orbB !== null) {
84
+ return Math.max(orbA, orbB);
85
+ }
86
+ else if (orbA !== null) {
87
+ return orbA;
88
+ }
89
+ else if (orbB !== null) {
90
+ return orbB;
91
+ }
92
+ // Fall back to aspect's default orb
93
+ return context.aspect.orb;
94
+ }
95
+ getPlanetSpecificOrb(planetName, aspect) {
96
+ const planetCategory = this.getPlanetCategory(planetName);
97
+ if (!planetCategory)
98
+ return null;
99
+ const categoryRules = this.orbConfiguration.planetCategories?.[planetCategory];
100
+ if (!categoryRules)
101
+ return null;
102
+ // Try aspect-specific orb first
103
+ const aspectOrb = categoryRules.aspectOrbs?.[aspect.name];
104
+ if (aspectOrb !== undefined)
105
+ return aspectOrb;
106
+ // Fall back to category default orb
107
+ return categoryRules.defaultOrb ?? null;
108
+ }
109
+ applyClassificationMultipliers(orb, aspect) {
110
+ if (!aspect.classification)
111
+ return orb;
112
+ const classificationRules = this.orbConfiguration.aspectClassification?.[aspect.classification];
113
+ if (!classificationRules?.orbMultiplier)
114
+ return orb;
115
+ return orb * classificationRules.orbMultiplier;
116
+ }
117
+ applyContextualMultipliers(orb, context) {
118
+ if (!context.chartType || context.chartType === 'natal')
119
+ return orb;
120
+ // Map 'transit' to 'transits' for the configuration key
121
+ const configKey = context.chartType === 'transit' ? 'transits' : context.chartType;
122
+ const contextRules = this.orbConfiguration.contextualOrbs?.[configKey];
123
+ if (!contextRules)
124
+ return orb;
125
+ // Apply aspect-specific multiplier if available
126
+ const aspectMultiplier = contextRules.aspectMultipliers?.[context.aspect.name];
127
+ if (aspectMultiplier) {
128
+ return orb * aspectMultiplier;
129
+ }
130
+ // Fall back to general multiplier
131
+ if (contextRules.orbMultiplier) {
132
+ return orb * contextRules.orbMultiplier;
133
+ }
134
+ return orb;
135
+ }
136
+ applyConstraints(orb, aspect) {
137
+ const classificationRules = aspect.classification
138
+ ? this.orbConfiguration.aspectClassification?.[aspect.classification]
139
+ : undefined;
140
+ let constrainedOrb = orb;
141
+ // Apply minimum constraint
142
+ const minOrb = classificationRules?.minOrb;
143
+ if (minOrb !== undefined && constrainedOrb < minOrb) {
144
+ constrainedOrb = minOrb;
145
+ }
146
+ // Apply maximum constraint
147
+ const maxOrb = classificationRules?.maxOrb;
148
+ if (maxOrb !== undefined && constrainedOrb > maxOrb) {
149
+ constrainedOrb = maxOrb;
150
+ }
151
+ // Global fallback if somehow orb is still invalid
152
+ if (constrainedOrb <= 0) {
153
+ constrainedOrb = this.orbConfiguration.globalFallbackOrb ?? 3;
154
+ }
155
+ return constrainedOrb;
156
+ }
157
+ generateCacheKey(context) {
158
+ const planetAName = typeof context.planetA === 'string'
159
+ ? context.planetA
160
+ : context.planetA.name;
161
+ const planetBName = typeof context.planetB === 'string'
162
+ ? context.planetB
163
+ : context.planetB.name;
164
+ return `${planetAName}-${planetBName}-${context.aspect.name}-${context.chartType || 'natal'}`;
165
+ }
166
+ }
167
+ exports.OrbResolver = OrbResolver;
@@ -35,16 +35,19 @@ function calculateSignDistributions(planets, ascendant) {
35
35
  return { elements, modalities, polarities };
36
36
  }
37
37
  function formatElementDistribution(elements) {
38
- return Object.entries(elements).map(([element, planets]) => {
38
+ const parts = Object.entries(elements).map(([element, planets]) => {
39
39
  if (planets.length === 0) {
40
40
  return `${element}: 0`;
41
41
  }
42
42
  return `${element}: ${planets.length} (${planets.join(', ')})`;
43
43
  });
44
+ return [parts.join(' | ')];
44
45
  }
45
46
  function formatModalityDistribution(modalities) {
46
- return Object.entries(modalities).map(([modality, count]) => `${modality}: ${count}`);
47
+ const parts = Object.entries(modalities).map(([modality, count]) => `${modality}: ${count}`);
48
+ return [parts.join(' | ')];
47
49
  }
48
50
  function formatPolarityDistribution(polarities) {
49
- return Object.entries(polarities).map(([polarity, count]) => `${polarity}: ${count}`);
51
+ const parts = Object.entries(polarities).map(([polarity, count]) => `${polarity}: ${count}`);
52
+ return [parts.join(' | ')];
50
53
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Generates the [DISPOSITOR TREE] section of the chart output.
3
- * @param dispositors A map of planet names to their full dispositor chain string.
3
+ * @param dispositors A map of planet names to their full dispositor chain string, or summary data.
4
4
  * @returns An array of strings for the output.
5
5
  */
6
6
  export declare function generateDispositorsOutput(dispositors: {
@@ -3,10 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateDispositorsOutput = generateDispositorsOutput;
4
4
  /**
5
5
  * Generates the [DISPOSITOR TREE] section of the chart output.
6
- * @param dispositors A map of planet names to their full dispositor chain string.
6
+ * @param dispositors A map of planet names to their full dispositor chain string, or summary data.
7
7
  * @returns An array of strings for the output.
8
8
  */
9
9
  function generateDispositorsOutput(dispositors) {
10
+ // Check if this is summary format (finals mode)
11
+ if (dispositors['summary']) {
12
+ return [`[DISPOSITORS] ${dispositors['summary']}`];
13
+ }
14
+ // Traditional format
10
15
  const output = ['[DISPOSITOR TREE]'];
11
16
  if (Object.keys(dispositors).length === 0) {
12
17
  output.push('No dispositor data available.');
@@ -21,7 +21,9 @@ const processSingleChartOutput = (analysis, settings, chartTitlePrefix) => {
21
21
  outputLines.push(...(0, angles_1.generateAnglesOutput)(chart.ascendant, chart.midheaven));
22
22
  outputLines.push(...(0, houses_1.generateHousesOutput)(chart.houseCusps));
23
23
  outputLines.push(...(0, planets_1.generatePlanetsOutput)(placements.planets));
24
- outputLines.push(...(0, dispositors_1.generateDispositorsOutput)(dispositors));
24
+ if (settings.includeDispositors) {
25
+ outputLines.push(...(0, dispositors_1.generateDispositorsOutput)(dispositors));
26
+ }
25
27
  if (settings.includeSignDistributions) {
26
28
  outputLines.push(...(0, signDistributions_1.generateElementDistributionOutput)(signDistributions.elements));
27
29
  outputLines.push(...(0, signDistributions_1.generateModalityDistributionOutput)(signDistributions.modalities));
@@ -56,8 +58,10 @@ const processChartPairOutput = (analysis, settings) => {
56
58
  outputLines.push(...(0, aspectPatterns_1.generateAspectPatternsOutput)(compositePatterns, `${chart1.name}-${chart2.name} Composite`, true));
57
59
  }
58
60
  outputLines.push('');
59
- outputLines.push(...(0, houseOverlays_1.generateHouseOverlaysOutput)(houseOverlays, chart1.name, chart2.name));
60
- outputLines.push('');
61
+ if (settings.includeHouseOverlays) {
62
+ outputLines.push(...(0, houseOverlays_1.generateHouseOverlaysOutput)(houseOverlays, chart1.name, chart2.name));
63
+ outputLines.push('');
64
+ }
61
65
  return outputLines;
62
66
  };
63
67
  const processGlobalPatternsOutput = (analysis, isTransit = false) => {
@@ -0,0 +1,239 @@
1
+ /**
2
+ * humandesign2txt
3
+ * Converts Human Design chart data to human-readable text for LLM consumption.
4
+ */
5
+ export interface PlanetPosition {
6
+ name: string;
7
+ longitude: number;
8
+ speed: number;
9
+ }
10
+ export interface CalculationResult {
11
+ planets: PlanetPosition[];
12
+ ascendant: number;
13
+ midheaven: number;
14
+ houseCusps: number[];
15
+ date: string;
16
+ time: string;
17
+ location: {
18
+ latitude: number;
19
+ longitude: number;
20
+ };
21
+ timezone?: string;
22
+ }
23
+ export interface HumanDesignApiResponse {
24
+ personality: CalculationResult;
25
+ design: CalculationResult;
26
+ metadata: {
27
+ designUtcDateTime: string;
28
+ solarArcDegrees: number;
29
+ personalitySunLongitude: number;
30
+ designSunLongitude: number;
31
+ };
32
+ }
33
+ export interface Activation {
34
+ planet: string;
35
+ gate: number;
36
+ line: number;
37
+ }
38
+ export interface Channel {
39
+ gates: [number, number];
40
+ name: string;
41
+ centers: [string, string];
42
+ }
43
+ export interface HumanDesignChart {
44
+ name: string;
45
+ location: string;
46
+ date: string;
47
+ time: string;
48
+ type: string;
49
+ strategy: string;
50
+ authority: string;
51
+ definition: string;
52
+ definitionIslands: string[][];
53
+ profile: string;
54
+ profileName: string;
55
+ incarnationCross: string;
56
+ definedCenters: string[];
57
+ undefinedCenters: string[];
58
+ openCenters: string[];
59
+ activeChannels: Channel[];
60
+ hangingGates: Array<{
61
+ gate: number;
62
+ center: string;
63
+ }>;
64
+ allGates: Map<number, {
65
+ center: string;
66
+ sources: string[];
67
+ }>;
68
+ personalityActivations: Activation[];
69
+ designActivations: Activation[];
70
+ }
71
+ /**
72
+ * The 64 gates in I Ching wheel order (starting from 0° Aries after 58° adjustment)
73
+ */
74
+ export declare const GATES: number[];
75
+ /**
76
+ * Gate names (I Ching / Human Design names)
77
+ */
78
+ export declare const GATE_NAMES: Record<number, string>;
79
+ /**
80
+ * Which center each gate belongs to
81
+ */
82
+ export declare const GATE_CENTERS: Record<number, string>;
83
+ /**
84
+ * All 36 channels with their gate pairs, names, and connected centers
85
+ */
86
+ export declare const CHANNELS: Channel[];
87
+ /**
88
+ * Profile names by line combination
89
+ */
90
+ export declare const PROFILE_NAMES: Record<string, string>;
91
+ /**
92
+ * Incarnation Cross names by Sun gate (simplified - Right Angle crosses)
93
+ * Format: { gateNumber: "Cross Name" }
94
+ */
95
+ export declare const INCARNATION_CROSSES: Record<number, string>;
96
+ /**
97
+ * Strategy by Type
98
+ */
99
+ export declare const STRATEGY_BY_TYPE: Record<string, string>;
100
+ /**
101
+ * All 9 centers
102
+ */
103
+ export declare const ALL_CENTERS: string[];
104
+ /**
105
+ * Convert a planetary longitude to gate and line
106
+ */
107
+ export declare function longitudeToGateLine(longitude: number): {
108
+ gate: number;
109
+ line: number;
110
+ };
111
+ /**
112
+ * Get the opposite gate (180° across the wheel)
113
+ */
114
+ export declare function oppositeGate(gate: number): number;
115
+ /**
116
+ * Calculate all activations from planetary positions
117
+ */
118
+ export declare function calculateActivations(planets: PlanetPosition[]): Activation[];
119
+ /**
120
+ * Get all unique gates from activations, tracking their sources
121
+ */
122
+ export declare function getAllGates(personalityActivations: Activation[], designActivations: Activation[]): Map<number, {
123
+ center: string;
124
+ sources: string[];
125
+ }>;
126
+ /**
127
+ * Determine which channels are active (both gates present)
128
+ */
129
+ export declare function getActiveChannels(allGates: Map<number, {
130
+ center: string;
131
+ sources: string[];
132
+ }>): Channel[];
133
+ /**
134
+ * Determine center status: Defined, Undefined, or Open
135
+ */
136
+ export declare function getCenterStatus(activeChannels: Channel[], allGates: Map<number, {
137
+ center: string;
138
+ sources: string[];
139
+ }>): {
140
+ defined: string[];
141
+ undefined: string[];
142
+ open: string[];
143
+ };
144
+ /**
145
+ * Calculate Human Design Type
146
+ */
147
+ export declare function calculateType(definedCenters: string[], activeChannels: Channel[]): string;
148
+ /**
149
+ * Calculate Inner Authority
150
+ */
151
+ export declare function calculateAuthority(definedCenters: string[], activeChannels: Channel[]): string;
152
+ /**
153
+ * Calculate Definition type (how centers are connected)
154
+ */
155
+ export declare function calculateDefinition(activeChannels: Channel[], definedCenters: string[]): string;
156
+ /**
157
+ * Get definition islands - groups of connected defined centers
158
+ */
159
+ export declare function getDefinitionIslands(activeChannels: Channel[], definedCenters: string[]): string[][];
160
+ /**
161
+ * Get hanging gates - gates that are not part of a complete channel
162
+ */
163
+ export declare function getHangingGates(allGates: Map<number, {
164
+ center: string;
165
+ sources: string[];
166
+ }>, activeChannels: Channel[]): Array<{
167
+ gate: number;
168
+ center: string;
169
+ }>;
170
+ /**
171
+ * Calculate Profile from personality and design sun lines
172
+ */
173
+ export declare function calculateProfile(personalitySunLine: number, designSunLine: number): string;
174
+ /**
175
+ * Get Incarnation Cross
176
+ */
177
+ export declare function getIncarnationCross(personalitySunGate: number, personalityEarthGate: number, designSunGate: number, designEarthGate: number, personalitySunLine: number, designSunLine: number): string;
178
+ export interface HumanDesign2TxtOptions {
179
+ name?: string;
180
+ location?: string;
181
+ }
182
+ /**
183
+ * Main entry point: Convert Human Design API response to formatted text
184
+ */
185
+ export declare function humandesign2txt(apiResponse: HumanDesignApiResponse, options?: HumanDesign2TxtOptions): string;
186
+ export interface ChannelConnection {
187
+ channel: Channel;
188
+ type: 'electromagnetic' | 'companionship' | 'dominance' | 'compromise';
189
+ person1Gates: number[];
190
+ person2Gates: number[];
191
+ description: string;
192
+ }
193
+ export interface GateConnection {
194
+ gate: number;
195
+ gateName: string;
196
+ center: string;
197
+ type: 'shared' | 'unique_person1' | 'unique_person2';
198
+ }
199
+ export interface HumanDesignPartnership {
200
+ person1: HumanDesignChart;
201
+ person2: HumanDesignChart;
202
+ electromagneticChannels: ChannelConnection[];
203
+ companionshipChannels: ChannelConnection[];
204
+ dominanceChannels: ChannelConnection[];
205
+ compromiseChannels: ChannelConnection[];
206
+ compositeDefinedCenters: string[];
207
+ compositeUndefinedCenters: string[];
208
+ compositeOpenCenters: string[];
209
+ compositeChannels: Channel[];
210
+ compositeType: string;
211
+ compositeStrategy: string;
212
+ compositeDefinition: string;
213
+ compositeDefinitionIslands: string[][];
214
+ channelsPerson1Only: Channel[];
215
+ channelsPerson2Only: Channel[];
216
+ channelsOnlyTogether: Channel[];
217
+ sharedGates: GateConnection[];
218
+ uniqueGatesPerson1: GateConnection[];
219
+ uniqueGatesPerson2: GateConnection[];
220
+ }
221
+ /**
222
+ * Analyze the relationship between two Human Design charts
223
+ */
224
+ export declare function analyzePartnership(chart1: HumanDesignChart, chart2: HumanDesignChart): HumanDesignPartnership;
225
+ /**
226
+ * Build a HumanDesignChart object from API response
227
+ */
228
+ export declare function buildChart(apiResponse: HumanDesignApiResponse, options?: HumanDesign2TxtOptions): HumanDesignChart;
229
+ export interface HumanDesignPartnership2TxtOptions {
230
+ person1Name?: string;
231
+ person1Location?: string;
232
+ person2Name?: string;
233
+ person2Location?: string;
234
+ }
235
+ /**
236
+ * Main entry point for partnership analysis: Convert two Human Design API responses to formatted text
237
+ */
238
+ export declare function humandesignPartnership2txt(apiResponse1: HumanDesignApiResponse, apiResponse2: HumanDesignApiResponse, options?: HumanDesignPartnership2TxtOptions): string;
239
+ export default humandesign2txt;