Move docs/ package mirror into document-navigator plugin

Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:10:00 +01:00
parent fe3fedf42e
commit 9bf56a806b
6022 changed files with 12437 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
# UniverseClient
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
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
```ts
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.