Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
# Design: enterprise-ports
## Module layout
```
packages/geospatial/logistics/ports/
package.json
tsconfig.lib.json
vite.config.ts
vitest.config.ts
README.md
src/
index.ts
constants/index.ts # EARTH_RADIUS_NM, DEFAULT_SPEED_KNOTS, VESSEL_CLASS_SPEEDS
types/
port.types.ts
port.types.md
error-codes/
error-codes.ts
error-codes.md
error-codes.test.ts
index.ts
schemas/
schemas.ts
schemas.md
schemas.test.ts
index.ts
otel/
otel.ts
otel.md
otel.test.ts
index.ts
data/
sea-ports-asia/
sea-ports-europe/
sea-ports-americas/
sea-ports-africa-mena/
sea-ports-oceania/
sea-ports/
airports/
rail-terminals/
all-ports/
shipping-lanes/
distance/
distance.ts # great-circle wrapper, nm/km conversions
distance.md
distance.test.ts
index.ts
find-port/
find-port.ts
find-port.md
find-port.test.ts
index.ts
nearest-port/
nearest-port.ts
nearest-port.md
nearest-port.test.ts
index.ts
spatial-index/
spatial-index.ts
spatial-index.md
spatial-index.test.ts
index.ts
lane-lookup/
lane-lookup.ts
lane-lookup.md
lane-lookup.test.ts
index.ts
lane-distance/
lane-distance.ts
lane-distance.md
lane-distance.test.ts
index.ts
lane-progress/
lane-progress.ts
lane-progress.md
lane-progress.test.ts
index.ts
eta/
eta.ts
eta.md
eta.test.ts
index.ts
property.test.ts
```
## Distance helper
`distanceNm(a, b)` and `distanceKm(a, b)` wrap
`@suaveplan/geo`'s `greatCircleDistance` against a WGS84-radius
spherical earth (semi-major axis 6_371_000 m) via `defineEllipsoid`. The
genesis `greatCircleDistance` returns metres by default; this package
converts via the geo `convertDistance` utility. We bind a single
`EARTH_SPHERE` ellipsoid once at module load — no allocation per call.
## Error mapping
Each main-branch `new PortError("msg", "CODE")` call maps to a typed
subclass:
| main code | genesis class |
|---|---|
| `NO_PORTS` | `PortsNoPortsError` |
| `INVALID_RADIUS` | `PortsInvalidRadiusError` |
| `INSUFFICIENT_WAYPOINTS` | `PortsInsufficientWaypointsError` |
| `INVALID_SPEED` | `PortsInvalidSpeedError` |
| `MISSING_SPEED` | `PortsMissingSpeedError` |
| _(new)_ | `PortsPortNotFoundError` — strict variant of `findPort` |
| _(new)_ | `PortsLaneNotFoundError` — strict variant of `getShippingLane` |
| _(new)_ | `PortsInvalidLaneError` — Zod schema rejected |
All extend `PortsError extends SuaveplanError<PortsCode>` with i18n
message keys.
## Spatial index
`PortSpatialIndex` keys ports by `floor(latitude / 5)` and
`floor(longitude / 5)` into a `Map<string, Port[]>`. Query for a
coordinate fetches the 3×3 cell neighbourhood and runs exact great-circle
distance on the candidate set. For ~120 curated ports this is overkill in
absolute terms but keeps the public API stable as the dataset grows.
## OTel instruments
Module-scope meter `@suaveplan/ports@0.1.0`:
- Counter `ports.lookup_total` — labels `kind=findPort|byCountry|byType|search`.
- Counter `ports.lane_lookup_total` — labels `mode=sea|rail|air|all`.
- Counter `ports.eta_total` — labels `confidence=high|medium|low`.
- Histogram `ports.find_nearest_duration_ms`.
- Histogram `ports.lane_distance_duration_ms`.
- Histogram `ports.eta_duration_ms`.
`refreshPortsMeters()` re-resolves every instrument against the current
global `MeterProvider` after tests wire in-memory exporters.
## Clock injection
`estimateETA(coord, lane, speedKnotsOverride?, opts?)`:
`opts = { departureTime?: Date; clock?: Clock }`. If `departureTime` is
omitted and `clock` is supplied → `new Date(clock.now())`; else `new
Date()`. Tests pass a `FakeClock` from `@suaveplan/testing` for
determinism.
## Data shape stability
The migrated dataset preserves every record from the main branch
(coordinates, throughputs, vessel limits, IATA/UIC codes) — adding any
new fields would break consumer expectations. The data is split into
per-region files exactly mirroring the main layout so future curated
updates can be diffed cleanly.