Files
skills/docs/geospatial/geo/universe/src/client/client.md
2026-05-29 08:20:56 +01:00

5.0 KiB

UniverseClient

(no submodules)

Purpose

UniverseClient is the single composition point that aggregates solar, star, tidal, and weather provider implementations into one cohesive UniverseScene snapshot for a given observer at a given instant. It owns no domain knowledge of its own — every datum on the returned scene is delegated to a provider the consumer registered. This indirection lets the same client power a real-time astronomy app, a deterministic game world, a server-rendered ephemeris page, or a unit test, simply by swapping providers.

The client is intentionally minimal: no caching, no retry, no fan-out; it propagates the caller's AbortSignal to every registered provider and assembles their results into one UniverseScene value. Tides are skipped when the observer is in space (no surface to compute against). Unregistered domains contribute empty arrays (bodies, stars) or are omitted entirely (tides, weather).

Features

  • Fluent registration: registerSolar / registerStars / registerTides / registerWeather chain on this.
  • One-shot scene assembly via getScene(observer, time, signal?).
  • AbortSignal forwarded to every provider call.
  • Tides automatically skipped for kind: "space" observers.
  • Optional fields (tides, weather) only present when their provider is registered and produces a value.
  • Provider-agnostic — interfaces only, zero base classes.

Usage: Basic Example

import { UniverseClient } from "@suaveplan/universe";

const client = new UniverseClient();
const scene = await client.getScene(
    { kind: "space", heliocentricVec: { x: 0, y: 0, z: 0 }, frame: "icrs" },
    new Date()
);
// scene.bodies === [], scene.stars === [], scene.tides/weather undefined

Usage: Advanced Example

import { UniverseClient, type UniverseScene } from "@suaveplan/universe";
import type {
    SolarProvider,
    StarProvider,
    WeatherProvider,
} from "@suaveplan/universe";

const solar: SolarProvider = {
    async getBodies() {
        return [];
    },
    async getBody(name) {
        return {
            name,
            type: "star",
            altitude: 45,
            azimuth: 180,
            apparentMagnitude: -26.7,
            angularDiameterArcmin: 32,
            distanceAu: 1,
            heliocentricPosition: { x: 0, y: 0, z: 0 },
        };
    },
    async getSunrise() {
        return new Date("2026-01-01T06:00:00Z");
    },
    async getSunset() {
        return new Date("2026-01-01T18:00:00Z");
    },
};

const stars: StarProvider = {
    async getVisibleStars() {
        return [];
    },
    async queryCone() {
        return [];
    },
    async getStar() {
        return null;
    },
};

const weather: WeatherProvider = {
    async getCurrent() {
        return { temperature: 18, pressure: 1015, humidity: 60 };
    },
};

const controller = new AbortController();
const client = new UniverseClient()
    .registerSolar(solar)
    .registerStars(stars)
    .registerWeather(weather);

const scene: UniverseScene = await client.getScene(
    {
        kind: "surface",
        planet: {
            name: "Earth",
            mass: 1,
            radius: 1,
            polarRadius: 1,
            surfaceGravity: 1,
            rotationPeriod: 1,
            axialTilt: 23.4,
            atmosphere: null,
            satellites: [],
        },
        coord: { latitude: 51.5, longitude: -0.12 },
    },
    new Date(),
    controller.signal
);

API Reference

class UniverseClient

Method Returns Description
registerSolar(provider) this Register a SolarProvider.
registerStars(provider) this Register a StarProvider.
registerTides(provider) this Register a TidalProvider.
registerWeather(provider) this Register a WeatherProvider.
getScene(observer, time, signal?) Promise<UniverseScene> Assemble the scene. Forwards signal to every provider.

interface UniverseScene

Property Type Notes
bodies CelestialBody[] Empty when no solar provider.
stars ObservedStar[] Empty when no star provider.
tides? TideExtreme[] Omitted for space observers and when no tidal provider.
weather? WeatherState Omitted when no weather provider.

The client throws (propagates) any error a provider throws, including AbortError from a fired signal.

Implementation Notes

UniverseClient is a coordinator, not a cache. Each getScene call invokes every registered provider unconditionally (subject to the space-tide skip). Consumers that need memoisation, retry, or rate limiting should compose those policies into the providers themselves rather than wrapping the client. Re-exporting the four provider interfaces and WeatherState from this module (in addition to @suaveplan/universe/providers/providers) is intentional so consumers can import { type SolarProvider } from "@suaveplan/universe" without reaching into a sub-path.