spb/earth-now Public License
earth-now.co — real-time planetary dashboard: live world metrics modeled, not streamed.
TypeScript 93%
Shell 2.3%
SQL 1.4%
JavaScript 1.3%
Dockerfile 1.2%
CSS 0.8%
1/**2 * earth-now.co3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: packages/counter/src/counter-model.ts6 * Purpose: The project's central contract — CounterModel and RateFunction types shared by server, client, widget and badge7 */89/**10 * A single Fourier harmonic of the instantaneous rate.11 *12 * The rate contribution at time t is:13 * amplitude * cos(2π * order * τ / period + phase)14 * where τ is the number of seconds between t and SEASONAL_EPOCH (2000-01-01T00:00:00Z),15 * so phases are absolute calendar phases, identical for every client.16 */17export interface Harmonic {18 /** Base period of the cycle. "year" = mean Gregorian year, "week" = 604 800 s, "day" = 86 400 s. */19 period: "year" | "week" | "day";20 /** Harmonic order (1 = fundamental, 2 = half-period, ...). Must be >= 1. */21 order: number;22 /** Amplitude, in the metric's base unit per second. */23 amplitude: number;24 /** Phase offset in radians at the seasonal epoch. */25 phase: number;26}2728export type RateFunction =29 /** Constant rate: v(t) = anchorValue + perSecond * (t - anchorTime). */30 | { kind: "linear"; perSecond: number }31 /**32 * Piecewise-constant rate. Segments must be sorted by `from` ascending and the33 * first segment must start at or before anchorTime. Used e.g. for Jan-1-UTC resets.34 */35 | { kind: "piecewise"; segments: Array<{ from: string; perSecond: number }> }36 /**37 * Seasonal rate: base + Fourier harmonics. v(t) is the analytic integral of the38 * rate from anchorTime, so evaluation is exact and identical on every runtime.39 */40 | { kind: "seasonal"; base: number; harmonics: Harmonic[] }41 /**42 * Monotone-spline (PCHIP) interpolation of (time, value) knots — value knots,43 * not rate knots. Outside the knot range the value extrapolates linearly with44 * the endpoint derivative. anchorValue must equal the spline value at anchorTime.45 */46 | { kind: "spline"; knots: Array<[time: string, value: number]> };4748export interface DisplayHints {49 /** Fraction digits to render. */50 decimals: number;51 /** Hard cap on significant digits — never display more precision than the model justifies. */52 sigFigs?: number;53 /** Display unit label (SI in the data layer; conversions live here, never in the pipeline). */54 unit: string;55 /** Display conversion factor applied before formatting (e.g. 1e-9 for t → Gt). Default 1. */56 scale?: number;57}5859export interface CounterModel {60 /** e.g. "co2_emissions_ytd" — key in packages/registry. */61 metricId: string;62 /** Value at the anchor point. */63 anchorValue: number;64 /** ISO 8601 UTC of the anchor point. */65 anchorTime: string;66 /** How the value evolves from the anchor. */67 rateFn: RateFunction;68 /** 90 % CI of the value at anchorTime (widens with model-specific rules client-side). */69 uncertainty?: { low: number; high: number };70 /** Date of the last REAL observation this model is anchored on. */71 observedAt: string;72 /** Key in the source registry. */73 sourceId: string;74 /** e.g. "seasonal-spline-v2" — bump on ANY output-changing modification. */75 modelVersion: string;76 displayHints: DisplayHints;77}7879/** Epoch used to phase seasonal harmonics: 2000-01-01T00:00:00Z, in ms. */80export const SEASONAL_EPOCH_MS = Date.UTC(2000, 0, 1);8182/** Mean Gregorian year, in seconds. Shared by fitting (packages/models) and evaluation. */83export const YEAR_SECONDS = 365.2425 * 86_400;8485/** One UTC day, in seconds. */86export const DAY_SECONDS = 86_400;8788/** One week, in seconds. The seasonal epoch (2000-01-01) is a Saturday — weekly phases are relative to Saturday 00:00 UTC. */89export const WEEK_SECONDS = 604_800;9091/** Parse a strict ISO 8601 UTC timestamp to ms since epoch; throws on invalid input. */92export function parseIsoUtc(iso: string): number {93 const ms = Date.parse(iso);94 if (Number.isNaN(ms)) throw new Error(`Invalid ISO 8601 timestamp: ${iso}`);95 return ms;96}97