Skip to content

Commit b3a6857

Browse files
author
Sergii Solonyna
committed
Init commit v 0.1.0
0 parents  commit b3a6857

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+41677
-0
lines changed

.cursorrules

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
## Project Overview
2+
- Package: `@procoders/astrology-api-client`
3+
- Purpose: TypeScript SDK for https://api.astrology-api.io (OpenAPI spec saved in `docs-openapi.json`)
4+
- Runtime: Node.js (ES modules)
5+
- HTTP layer: Axios with request/response interceptors, automatic retry logic, strict validation
6+
- Tests: Vitest with 100% coverage enforced (`npm run test:coverage`)
7+
8+
## Directory Layout
9+
- `src/client.ts` — core client implementation
10+
- `src/types/` — strongly typed configs, requests, and responses
11+
- `src/errors/` — custom error hierarchy (`AstrologyError`)
12+
- `src/utils/validators.ts` — runtime validation helpers used prior to network calls
13+
- `tests/` — unit tests with axios-mock-adapter (keep coverage at 100%)
14+
- `examples/usage.ts` — sample usage (guarded by `RUN_ASTROLOGY_EXAMPLE` flag)
15+
- `docs-openapi.json` — locally cached OpenAPI v3 spec (use for type references)
16+
17+
## Coding Guidelines
18+
- Always prefer type-safe solutions; keep files in strict TypeScript.
19+
- Keep code DRY, KISS, and single-responsibility. Extract helpers if logic repeats.
20+
- Comments, docs, and logs MUST be written in English.
21+
- When adding HTTP endpoints ensure:
22+
- URL assembled via environment-aware base (`process.env.ASTROLOGY_API_BASE_URL` fallback)
23+
- API key header is set (`X-API-Key`)
24+
- Payload validated via `validators.ts`.
25+
- For additions touching API schema, consult `docs-openapi.json` first.
26+
- Update or add Vitest suites and maintain 100% coverage. Use axios-mock-adapter for HTTP mocks.
27+
- Run `npm run lint` and `npm run test:coverage` before completion.
28+
29+
## Documentation & Examples
30+
- README must show installation, configuration, environment variables, and code snippets.
31+
- When adding examples, wrap network calls with environment checks so examples do not run by default.
32+
33+
## Tooling
34+
- ESLint + Prettier already configured; match formatting in existing files.
35+
- Coverage enforced in `vitest.config.ts`. If coverage drops, add tests instead of raising thresholds.
36+
37+
## PR / Commit Checklist
38+
1. Update relevant types in `src/types/`.
39+
2. Keep public API exported via `src/index.ts`.
40+
3. Add/adjust tests to maintain 100% coverage.
41+
4. Document new features in README.
42+
5. Ensure `.cursorrules` stays aligned with architecture changes.
43+

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
node_modules/
2+
dist/
3+
coverage/
4+
.env
5+
.env.*
6+
.DS_Store
7+
npm-debug.log*
8+
pnpm-debug.log*
9+
vite.config.ts.timestamp-*
10+
.vitest
11+
.dist

.prettierrc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"singleQuote": true,
3+
"printWidth": 120,
4+
"semi": true,
5+
"trailingComma": "all",
6+
"arrowParens": "always"
7+
}
8+

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Procoders
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

README.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
## Astrology API TypeScript SDK
2+
3+
Type-safe Node.js client for the [Astrology API v3.0.0](https://api.astrology-api.io/rapidoc). The package ships as an ESM build, exposes a modular architecture with dedicated sub-clients per endpoint family, and enforces 100 % test coverage. Publish-ready as `@procoders/astrology-api-client`.
4+
5+
### Highlights
6+
- Axios-powered HTTP layer with retry/backoff, header normalization, and response unwrapping
7+
- Strong TypeScript types generated from the local OpenAPI spec (`docs-openapi.json`)
8+
- Exhaustive runtime validation via `src/utils/validators.ts` to prevent invalid API calls
9+
- Rich category coverage: data, charts, horoscopes, analysis, astrocartography, insights, SVG, enhanced traditional analytics, and more
10+
- Debug-friendly logging pipeline with configurable logger and opt-in verbosity
11+
12+
### Installation
13+
```bash
14+
npm install @procoders/astrology-api-client
15+
```
16+
17+
### Environment Variables
18+
| Variable | Required | Description |
19+
| --- | --- | --- |
20+
| `ASTROLOGY_API_BASE_URL` | optional | Override for the default `https://api.astrology-api.io` endpoint |
21+
| `ASTROLOGY_DEBUG` | optional | Set to `true` to enable debug logging |
22+
| `RUN_ASTROLOGY_EXAMPLE` | optional | Set to `true` to execute `examples/usage.ts` |
23+
24+
### Client Architecture
25+
```ts
26+
import { AstrologyClient } from '@procoders/astrology-api-client';
27+
28+
const client = new AstrologyClient({
29+
apiKey: 'your-api-key-here', // optional for public endpoints
30+
baseUrl: process.env.ASTROLOGY_API_BASE_URL ?? 'https://api.astrology-api.io',
31+
retry: { attempts: 2, delayMs: 250 },
32+
debug: process.env.ASTROLOGY_DEBUG === 'true',
33+
});
34+
35+
// Category sub-clients
36+
client.data.getPositions(...);
37+
client.charts.getNatalChart(...);
38+
client.analysis.getNatalReport(...);
39+
client.horoscope.getPersonalDailyHoroscope(...);
40+
client.insights.relationship.getCompatibility(...);
41+
client.svg.getNatalChartSvg(...);
42+
client.enhanced.getGlobalAnalysis(...);
43+
```
44+
45+
| Sub-client | Path prefix | Sample methods |
46+
| --- | --- | --- |
47+
| `data` | `/api/v3/data` | `getPositions`, `getGlobalPositions`, `getLunarMetrics` |
48+
| `charts` | `/api/v3/charts` | `getNatalChart`, `getTransitChart`, `getSolarReturnChart` |
49+
| `horoscope` | `/api/v3/horoscope` | `getPersonalDailyHoroscope`, `getSignWeeklyHoroscopeText` |
50+
| `analysis` | `/api/v3/analysis` | `getSynastryReport`, `getCompatibilityAnalysis`, `getProgressionReport` |
51+
| `glossary` | `/api/v3/glossary` | `getCities`, `getActivePoints`, `getHouseSystems` |
52+
| `astrocartography` | `/api/v3/astrocartography` | `getLines`, `getMap`, `getRelocationChart` |
53+
| `chinese` | `/api/v3/chinese` | `getBaZi`, `getYearlyForecast`, `getCompatibility` |
54+
| `eclipses` | `/api/v3/eclipses` | `getUpcoming`, `getNatalCheck`, `getInterpretation` |
55+
| `lunar` | `/api/v3/lunar` | `getCalendar`, `getPhases`, `getVoidOfCourse` |
56+
| `numerology` | `/api/v3/numerology` | `getCoreNumbers`, `getComprehensiveReport`, `getCompatibility` |
57+
| `tarot` | `/api/v3/tarot` | `drawCards`, `getTreeOfLife`, `getTimingAnalysis` |
58+
| `traditional` | `/api/v3/traditional` | `getAnalysis`, `getAnnualProfection`, `getLots` |
59+
| `fixedStars` | `/api/v3/fixed-stars` | `getPositions`, `getConjunctions`, `getReport` |
60+
| `insights` | `/api/v3/insights` | relationship/pet/wellness/financial/business suites |
61+
| `svg` | `/api/v3/svg` | `getNatalChartSvg`, `getSynastryChartSvg`, `getTransitChartSvg` |
62+
| `enhanced` | `/api/v3/enhanced*` | `getGlobalAnalysis`, `getPersonalAnalysis`, chart variants |
63+
64+
Each category client inherits from a shared base that enforces the API prefix contract and provides a consistent `buildUrl` helper, so request construction stays uniform across the SDK.
65+
66+
### Category Examples
67+
```ts
68+
// Data: planetary positions
69+
const subject = {
70+
name: 'Demo User',
71+
birth_data: {
72+
year: 1990,
73+
month: 5,
74+
day: 11,
75+
hour: 18,
76+
minute: 15,
77+
city: 'London',
78+
country_code: 'GB',
79+
},
80+
};
81+
const subjectA = subject;
82+
const subjectB = { ...subject, name: 'Partner' };
83+
84+
const positions = await client.data.getPositions({ subject });
85+
86+
// Charts: natal chart SVG export
87+
const natalSvg = await client.svg.getNatalChartSvg({ subject, svg_options: { theme: 'dark' } });
88+
89+
// Analysis: synastry report
90+
const synastry = await client.analysis.getSynastryReport({ subject1: subjectA, subject2: subjectB });
91+
92+
// Horoscope: personalized daily forecast
93+
const daily = await client.horoscope.getPersonalDailyHoroscope({ subject });
94+
95+
// Insights: relationship compatibility
96+
const compatibility = await client.insights.relationship.getCompatibility({ subjects: [subjectA, subjectB] });
97+
98+
// Enhanced: global traditional analysis
99+
const global = await client.enhanced.getGlobalAnalysis({
100+
options: { house_system: 'W', zodiac_type: 'Tropic', active_points: ['Sun', 'Moon', 'Mercury'], precision: 3 },
101+
orbs: { major_aspects_deg: 2 },
102+
});
103+
```
104+
105+
See `examples/usage.ts` for a full script that conditionally runs when `RUN_ASTROLOGY_EXAMPLE=true`.
106+
107+
### Debug Logging
108+
109+
Pass `debug: true` and optionally supply a custom `logger` function. Setting `ASTROLOGY_DEBUG=true` enables the same behaviour globally. The client logs request metadata, retry attempts, and responses (status and url).
110+
111+
### Testing & Linting
112+
```bash
113+
npm run lint # ESLint with @typescript-eslint + Prettier
114+
npm run test # Vitest watch mode
115+
npm run test:coverage # Vitest with V8 coverage (enforced at 100 %)
116+
npm run build # TypeScript build (emits ESM to dist/)
117+
```
118+
119+
### Project Structure
120+
```
121+
├── src/
122+
│ ├── categories/ # Modular sub-clients per API family
123+
│ ├── client.ts # Root client wiring sub-clients & interceptors
124+
│ ├── errors/AstrologyError.ts # Custom error hierarchy
125+
│ ├── types/ # Generated and hand-written typings
126+
│ └── utils/validators.ts # Runtime payload guards
127+
├── tests/unit/ # Vitest suites with axios-mock-adapter
128+
├── examples/usage.ts # Usage example (guarded by env flag)
129+
├── docs-openapi.json # Cached OpenAPI specification
130+
└── README.md
131+
```
132+
133+
### Publishing Checklist
134+
1. Ensure `npm run test:coverage` passes with 100 % coverage.
135+
2. Run `npm run build` and review the emitted `dist/` bundle.
136+
3. Update version, changelog, and documentation as needed.
137+
138+
### License
139+
Released under the MIT License. See [`LICENSE`](./LICENSE) for details.
140+

docs-openapi.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

eslint.config.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import js from '@eslint/js';
2+
import tseslint from 'typescript-eslint';
3+
import eslintConfigPrettier from 'eslint-config-prettier';
4+
5+
export default tseslint.config(
6+
{
7+
ignores: ['dist/**', 'coverage/**', 'node_modules/**'],
8+
},
9+
js.configs.recommended,
10+
...tseslint.configs.recommended,
11+
eslintConfigPrettier,
12+
{
13+
files: ['src/**/*.ts', 'tests/**/*.ts', 'examples/**/*.ts'],
14+
languageOptions: {
15+
parserOptions: {
16+
project: ['./tsconfig.json'],
17+
tsconfigRootDir: import.meta.dirname,
18+
},
19+
globals: {
20+
console: 'readonly',
21+
describe: 'readonly',
22+
it: 'readonly',
23+
expect: 'readonly',
24+
beforeEach: 'readonly',
25+
afterEach: 'readonly',
26+
beforeAll: 'readonly',
27+
vi: 'readonly',
28+
},
29+
},
30+
rules: {
31+
'@typescript-eslint/no-floating-promises': 'error',
32+
'@typescript-eslint/no-misused-promises': ['error', { checksVoidReturn: false }],
33+
'@typescript-eslint/no-unused-vars': [
34+
'error',
35+
{
36+
argsIgnorePattern: '^_',
37+
varsIgnorePattern: '^_',
38+
},
39+
],
40+
},
41+
},
42+
);
43+

examples/usage.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { AstrologyClient } from '../src';
2+
import type {
3+
DateTimeLocation,
4+
GlobalAnalysisRequest,
5+
PersonalizedHoroscopeRequest,
6+
PlanetaryPositionsRequest,
7+
Subject,
8+
SynastryReportRequest,
9+
} from '../src/types';
10+
11+
export async function runExample(): Promise<void> {
12+
const client = new AstrologyClient({
13+
baseUrl: process.env.ASTROLOGY_API_BASE_URL ?? 'https://api.astrology-api.io',
14+
retry: { attempts: 2, delayMs: 250 },
15+
debug: process.env.ASTROLOGY_DEBUG === 'true',
16+
});
17+
18+
const baseSubject: Subject = {
19+
name: 'Demo User',
20+
birth_data: {
21+
year: 1990,
22+
month: 5,
23+
day: 11,
24+
hour: 18,
25+
minute: 15,
26+
city: 'London',
27+
country_code: 'GB',
28+
timezone: 'Europe/London',
29+
},
30+
};
31+
32+
const partner: Subject = {
33+
...baseSubject,
34+
name: 'Partner',
35+
birth_data: {
36+
...baseSubject.birth_data,
37+
year: 1992,
38+
month: 3,
39+
day: 27,
40+
},
41+
};
42+
43+
const transitMoment: DateTimeLocation = {
44+
year: 2024,
45+
month: 5,
46+
day: 11,
47+
hour: 12,
48+
minute: 30,
49+
city: 'New York',
50+
country_code: 'US',
51+
timezone: 'America/New_York',
52+
};
53+
54+
const positionsRequest: PlanetaryPositionsRequest = { subject: baseSubject };
55+
const positions = await client.data.getPositions(positionsRequest);
56+
console.log(
57+
'Positions planets:',
58+
positions.planets?.map((p) => p.name),
59+
);
60+
61+
const natalChart = await client.charts.getNatalChart({ subject: baseSubject });
62+
console.log('Natal chart houses count:', natalChart.houses?.length);
63+
64+
const synastryRequest: SynastryReportRequest = {
65+
subject1: baseSubject,
66+
subject2: partner,
67+
};
68+
const synastry = await client.analysis.getSynastryReport(synastryRequest);
69+
console.log('Synastry sections:', synastry.sections?.length);
70+
71+
const horoscopeRequest: PersonalizedHoroscopeRequest = { subject: baseSubject };
72+
const horoscope = await client.horoscope.getPersonalDailyHoroscope(horoscopeRequest);
73+
console.log('Daily horoscope life areas:', horoscope.life_areas);
74+
75+
const relationshipInsights = await client.insights.relationship.getCompatibility({
76+
subjects: [baseSubject, partner],
77+
});
78+
console.log('Relationship compatibility keys:', Object.keys(relationshipInsights));
79+
80+
const svg = await client.svg.getNatalChartSvg({
81+
subject: baseSubject,
82+
svg_options: { theme: 'dark', language: 'en' },
83+
});
84+
console.log('SVG size:', svg.length);
85+
86+
const enhancedRequest: GlobalAnalysisRequest = {
87+
options: {
88+
house_system: 'W',
89+
zodiac_type: 'Tropic',
90+
precision: 3,
91+
active_points: ['Sun', 'Moon', 'Mercury'],
92+
},
93+
orbs: { major_aspects_deg: 2 },
94+
};
95+
const enhanced = await client.enhanced.getGlobalAnalysis(enhancedRequest);
96+
console.log('Enhanced planets:', enhanced.planets.length);
97+
98+
const transits = await client.charts.getTransitChart({
99+
natal_subject: baseSubject,
100+
transit_datetime: transitMoment,
101+
});
102+
console.log('Transit chart aspects:', transits.aspects?.length);
103+
}
104+
105+
if (process.env.RUN_ASTROLOGY_EXAMPLE === 'true') {
106+
runExample().catch((error) => {
107+
// eslint-disable-next-line no-console
108+
console.error('Example execution failed', error);
109+
});
110+
}

0 commit comments

Comments
 (0)