Packages3 MIN READ68 / 116

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

canonical-normalizer module

@astrology/ephemeris is a TypeScript ESM workspace package downstream of any EphemerisAdapter (an adapter wrapping one ephemeris engine). It depends on @astrology/astronomy-contracts and the shared logger; importing it loads no native engine or server.

import { NormalizingAdapter } from './normalization/index.js';
import { SwissAdapter } from './providers/swiss/index.js';

const astronomy = new NormalizingAdapter(new SwissAdapter());
const result = await astronomy.positions({
  instant: '2000-01-01T12:00:00Z', bodies: ['sun', 'moon'],
  zodiac: 'sidereal', ayanamsa: 'lahiri', includeSpeed: true,
});
console.log(result.data.positions, result.provenance);

Provider setup and lifecycle remain with the caller. For already calculated answers, use new CanonicalNormalizer(options).positions(answer) or .houses(answer) directly. These methods perform no provider I/O; they emit debug summaries and warn on invalid/degraded answers through the shared logger.

The normalizer requires the shared adapter contract: canonical body ids, degrees, AU (astronomical units), daily derivatives, and apparent coordinates measured along the ecliptic of date. It validates geometry and declarations, wraps angular coordinates into [0, 360), retains body/house order and null speeds, then rounds outgoing values once. Native sidereal values (star-anchored longitudes) are already converted and receive no further subtraction. House ARMC, the right ascension of the meridian, retains its equatorial meaning.

Options are angleDecimals (default 8), distanceDecimals (12), and speedDecimals (8), each an integer from 0 to 15. A positive distance that rounds to zero is rejected. Provenance records these settings, incoming angle wraps, version, canonical units, and a detached provider declaration. Provider accuracy estimates, including unknown null, and explicit degradation are preserved; decimal places do not claim engine accuracy.

The wrapper validates requests before calling the injected provider once, then rejects mismatched body order, origin or zodiac. Provider errors propagate unchanged. Invalid options use AdapterError('INVALID_REQUEST', ...); malformed provider answers use AdapterError('INTERNAL', ...).

The broader design also describes raw-unit/frame transforms and normalizer-side ayanamsa calculation (the tropical-to-sidereal offset). Those are not implemented: current adapters already own id/unit mapping and the shared declaration only permits canonical frames and provider-applied sidereal offsets. Unsupported declarations are rejected, never guessed. This package adds no provider selection, cache, fallback, astrology or accuracy model.

From the workspace root, run pnpm test:ephemeris or pnpm check:ephemeris; these commands build workspace dependencies first. Tests cover deterministic normalization and an injected provider fixture; they do not establish astronomical accuracy.

Source ownership is split by responsibility:

  • src/positions.ts and src/houses.ts validate and normalize their respective geometry.
  • src/declaration.ts checks provider context and detaches file identities.
  • src/numeric.ts owns decimal policy, angular wrapping and negative-zero handling.
  • src/provenance.ts assembles normalization evidence; src/types.ts defines its contract.
  • src/normalizer.ts delegates normalization using one shared numeric policy and logs result summaries.
  • src/normalizing-adapter.ts owns request validation, provider invocation and response matching.
  • src/validation.ts owns shared finite-number/error guards; src/index.ts only exports the public API.

These are internal source boundaries. Consumers continue importing from @astrology/ephemeris; no provider, router or downstream code needs a new import path.

Solar events

riseSet validates observer-events declarations, integer UTC milliseconds, event ordering, requested sunset presence and the [start, end) window. It preserves null no-event results. Event timestamps receive no angular rounding; provenance retains the provider/time/file identity and normalizer policy. A provider with no optional event method rejects as unsupported.