Initial Commit
This commit is contained in:
100
openspec/specs/tides/spec.md
Normal file
100
openspec/specs/tides/spec.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# @suaveplan/tides — Tidal Prediction Capability
|
||||
|
||||
Universal-tier tidal prediction surface exposing the unified `TidalProvider`
|
||||
contract from `@suaveplan/types`, three concrete provider factories
|
||||
(harmonic / equilibrium-physics / ADMIRALTY API), harmonic-constituent math
|
||||
with nodal corrections, equilibrium-tide physics, and tide-station search.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Harmonic prediction engine
|
||||
|
||||
The package SHALL expose `predictHeight(constituents, time, meanSeaLevel?)`
|
||||
and `findExtremes(constituents, from, to, meanSeaLevel?)` computing tide
|
||||
height per the harmonic formula `h(t) = H₀ + Σ f(t)·Hₙ·cos(ωₙ·t + V₀ₙ(t) − Gₙ)`
|
||||
using the J2000 epoch (`2000-01-01T12:00:00Z`) as the time base.
|
||||
|
||||
#### Scenario: Empty constituent list rejected
|
||||
|
||||
- **WHEN** `predictHeight([], time)` is called with zero constituents
|
||||
- **THEN** it MUST throw `TideError` with code `NO_CONSTITUENTS`.
|
||||
|
||||
#### Scenario: Nodal corrections applied
|
||||
|
||||
- **WHEN** `predictHeight` runs over named constituents (M2, S2, K1, …)
|
||||
- **THEN** the per-constituent node factor `f` and astronomical argument
|
||||
`V₀` MUST be derived from the time argument via `nodeFactor(name, time)` /
|
||||
`astronomicalArgument(name, time)` rather than treated as static.
|
||||
|
||||
### Requirement: Harmonic-constituent registry
|
||||
|
||||
The package SHALL ship `CONSTITUENT_SPEEDS` (degrees/hour angular speeds)
|
||||
and `DOODSON_NUMBERS` tables for the canonical major constituents
|
||||
(M2, S2, N2, K2, K1, O1, P1, Q1, M4, …) re-exported from
|
||||
`./harmonic/harmonic-constituents`.
|
||||
|
||||
### Requirement: Equilibrium-tide physics
|
||||
|
||||
The package SHALL expose `equilibriumTideHeight(planet, coord, time)` and
|
||||
`findEquilibriumExtremes(planet, coord, date, signal?)` modelling the
|
||||
solid-body equilibrium tide on a `PlanetConfig` ellipsoid (consumes
|
||||
`@suaveplan/geo-planet`).
|
||||
|
||||
### Requirement: ADMIRALTY UK Hydrographic Office client
|
||||
|
||||
The package SHALL expose `AdmiraltyProvider` and `createAdmiraltyProvider`
|
||||
binding the ADMIRALTY UK Tidal API
|
||||
(`https://admiraltyapi.azure-api.net/uktidalapi/api/V1`) with:
|
||||
|
||||
- `Ocp-Apim-Subscription-Key` header taken from constructor option `apiKey`.
|
||||
- Injectable `fetchFn` (defaults to `globalThis.fetch`).
|
||||
- Configurable `cacheTtlMs` (default `3_600_000`).
|
||||
|
||||
#### Scenario: ADMIRALTY API key absent
|
||||
|
||||
- **WHEN** `createAdmiraltyProvider({ apiKey: "" })` is called
|
||||
- **THEN** the resulting provider MUST throw `TideError` (`MISSING_API_KEY`)
|
||||
on first network call.
|
||||
|
||||
### Requirement: Provider factory surface
|
||||
|
||||
The package SHALL expose three factories returning the unified
|
||||
`TidalProvider` shape from `@suaveplan/types`:
|
||||
|
||||
- `createTiconProvider(constituents, meanSeaLevel?)` — offline harmonic.
|
||||
- `createPhysicsBasedProvider(planet)` — equilibrium-tide approximation.
|
||||
- `createAdmiraltyProvider(options)` — ADMIRALTY UK API.
|
||||
|
||||
All three MUST satisfy `predictHeight(planet, coord, time)` and
|
||||
`getExtremes(planet, coord, date, signal?)`.
|
||||
|
||||
### Requirement: Tide-station search
|
||||
|
||||
The package SHALL expose `findNearestStation(stations, latitude, longitude)`
|
||||
returning `NearestStationResult` with great-circle `distanceKm` computed
|
||||
via `@suaveplan/geo` haversine math.
|
||||
|
||||
### Requirement: Typed error surface
|
||||
|
||||
The package SHALL expose a frozen `TideError` class with stable codes
|
||||
covering `NO_CONSTITUENTS`, `MISSING_API_KEY`, `STATION_NOT_FOUND`, and
|
||||
network/parse failure paths surfaced by the ADMIRALTY provider.
|
||||
|
||||
### Requirement: AbortSignal threading
|
||||
|
||||
Every async provider call (`predictHeight`, `getExtremes`,
|
||||
`getNearestStation`, `getStations`) SHALL accept and honour an optional
|
||||
`AbortSignal`, aborting in-flight ADMIRALTY HTTP requests via the underlying
|
||||
`fetchFn`.
|
||||
|
||||
### Requirement: Universal-tier purity
|
||||
|
||||
The package MUST NOT import browser-only or Node-only APIs at module top
|
||||
level — `fetch` is consumed only through the injectable `fetchFn` so the
|
||||
package runs unchanged in browser, Bun, and Node environments.
|
||||
|
||||
### Requirement: Test + coverage gates
|
||||
|
||||
`bunx turbo lint typecheck test --filter=@suaveplan/tides` MUST be green
|
||||
and `bunx turbo test:coverage --filter=@suaveplan/tides` MUST hit 100%
|
||||
on touched modules before any release.
|
||||
Reference in New Issue
Block a user