Packages2 MIN READ71 / 116

Internal module guide. This module is now part of @astrology/ephemeris; use the public package README for application setup.

nasa-adapter module

@astrology/ephemeris is a reusable Node.js TypeScript workspace module implementing EphemerisAdapter from @astrology/astronomy-contracts over the public JPL Horizons API. The internal provider module exports HorizonsAdapter and HorizonsAdapterOptions. It serves tropical positions only, for Sun through Pluto: there are no houses, native sidereal modes or lunar nodes. It has no server, routes or startup probe.

The shared logger records completed operations at debug and failures at warn, with error codes and body counts only. Requests, coordinates and response text are never logged.

From the repository root (Node 22.12+ and pnpm 11):

pnpm install
pnpm build:ephemeris
pnpm --filter @astrology/ephemeris test   # offline; also part of pnpm test:ephemeris
import { PositionQuery, type EphemerisAdapter } from '@astrology/astronomy-contracts';
import { HorizonsAdapter } from './providers/nasa/index.js';

const adapter: EphemerisAdapter = new HorizonsAdapter({
  userAgent: 'my-app/1.0 (ops@example.com)',
});
const request = PositionQuery.at('2000-01-01T12:00:00Z', { zodiac: 'tropical' })
  .forBodies('sun', 'mars')
  .withSpeed()
  .build();
const { data, declaration } = await adapter.positions(request);

Each positions call sends one HTTPS request per body. The result has every body in request order and one declaration; if any body fails, the whole call rejects with an AdapterError. The adapter refuses some requests before any network call:

  • sidereal requests and houses() reject with UNSUPPORTED_FEATURE;
  • Rahu and Ketu reject with UNSUPPORTED_BODY;
  • requests with an observer reject with UNSUPPORTED_FEATURE unless you set allowTopocentric: true (a topocentric position is seen from the observer’s place, not the Earth’s centre), so coordinates are never sent to JPL by default.

Calls are bounded by a per-attempt timeout, an operation deadline, a concurrency cap, jittered retry on 429 and 5xx, and a circuit breaker. Responses must carry Horizons API version 1.2 and the expected columns. Anything else is rejected with PROVIDER_RESPONSE and is never zero-filled.

Tests replay real recorded responses and never call JPL. To refresh them deliberately:

pnpm --filter @astrology/ephemeris record:nasa-adapter

See the module design for the query template, speed steps, error mapping and the measured agreement with Swiss.