Initial Commit
This commit is contained in:
151
openspec/archive/2026-05-25-enterprise-ports/design.md
Normal file
151
openspec/archive/2026-05-25-enterprise-ports/design.md
Normal 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.
|
||||
Reference in New Issue
Block a user