@chrtco/sdk 0.7.6 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to `@chrtco/sdk` will be documented in this file.
4
4
 
5
+ ## [0.8.0] — 2026-06-30
6
+ - **Online tile rendering now works end-to-end against `tiles.chrt.co`.** `chrt.transformRequest()` mints a short-lived ES256 tile JWT from your API key (`POST {baseUrl}/mint/tile-token`), caches and refreshes it, and attaches `Authorization: Bearer` on `tiles.chrt.co/v1/tiles/{region}/…/.mvt` requests only. This replaces the previous no-op transformer that injected `x-api-key` against a removed `api.chrt.co/v1/tiles/…?key=` path.
7
+ - **New `chrt.style(opts?)`** — returns a ready-to-use IHO **S-52** MapLibre / Mapbox GL style object (`palette`: `DAY` | `DUSK` | `NIGHT`, plus `region`, `depthUnits`, and the other atlas options), with the tile source wired in. Mutate the returned object, or build a fully custom style over `buildSources()`. S-101 portrayal is on the roadmap.
8
+ - **New `chrt.warmTiles(region?)`** — optionally pre-mint the tile token to surface `region_not_licensed` / `tile_quota_exceeded` before the map loads.
9
+ - **New config options**: `region` (default `NOAA_ENC`) and `tileBaseUrl` (default `https://tiles.chrt.co`).
10
+ - **New exports**: `TileAuth`, `TileTokenError`, and the `TileTokenResponse` / `TileTokenErrorCode` / `ChrtStyleOptions` types.
11
+ - **Deprecated**: `styleUrl()` — CHRT serves no remote `style.json`; it now throws with guidance to use `style()`. `tiles.groups()` — legacy pre-consolidation per-group catalogue. `tiles.tileUrl()` now returns the real `tiles.chrt.co` URL.
12
+ - `TransformRequest.headers` is now optional (matches MapLibre; the transformer omits headers on unauthenticated passthrough).
13
+ - **Requires** the public `POST /v1/mint/tile-token` route on `api.chrt.co` (the sibling of the existing `/v1/mint/offline-token`).
14
+
5
15
  ## [0.7.0] — 2026-05-08
6
16
  - **Atlas — dominance cascade & scale-band rendering**: per-feature dominance cascade in continuous mode, cascading band fallback, band-then-class layer emission, chart-mode per-cell cascade fallback to coarser bands, dominance filter on orientation-cross-band labels.
7
17
  - **Atlas — cartographic labels**: orientation labels + typography hierarchy.
package/README.md CHANGED
@@ -37,30 +37,56 @@ const cells = await chrt.cells.list({ source: 'NOAA' });
37
37
  const cell = await chrt.cells.get('US5MA11M');
38
38
  ```
39
39
 
40
- ## Mapbox GL JS / MapLibre
40
+ ## Maps (MapLibre GL JS / Mapbox GL JS)
41
+
42
+ `chrt.style()` returns a ready-to-use IHO **S-52** nautical style; `chrt.transformRequest()`
43
+ mints a short-lived tile token from your API key and attaches it to tile requests
44
+ automatically. Examples use MapLibre — substitute `mapbox-gl` if you're already on Mapbox.
41
45
 
42
46
  ```typescript
43
- import mapboxgl from 'mapbox-gl';
47
+ import maplibregl from 'maplibre-gl';
44
48
  import { Chrt } from '@chrtco/sdk';
45
49
 
46
50
  const chrt = new Chrt('chrt_live_...');
47
51
 
48
- const map = new mapboxgl.Map({
52
+ const map = new maplibregl.Map({
49
53
  container: 'map',
50
- style: chrt.styleUrl(),
54
+ style: chrt.style({ palette: 'DAY' }), // 'DAY' | 'DUSK' | 'NIGHT'
51
55
  center: [-70.25, 43.66],
52
56
  zoom: 12,
53
57
  transformRequest: chrt.transformRequest(),
54
58
  });
55
59
  ```
56
60
 
61
+ Tiles are served from `tiles.chrt.co` and gated by a short-lived ES256 JWT. The SDK mints
62
+ it from your key, attaches it as `Authorization: Bearer` on tile requests only, and refreshes
63
+ it before expiry — you never handle tokens directly.
64
+
65
+ ### Custom styles
66
+
67
+ `chrt.style()` returns a standard MapLibre style object you can mutate. For a fully custom
68
+ look, build your own style over CHRT's vector source with `buildSources()` and keep
69
+ `chrt.transformRequest()` for auth:
70
+
71
+ ```typescript
72
+ import { buildSources } from '@chrtco/sdk';
73
+
74
+ const sources = buildSources({ region: 'NOAA_ENC' });
75
+ // sources['NOAA_ENC'].tiles → ['https://tiles.chrt.co/v1/tiles/NOAA_ENC/{z}/{x}/{y}.mvt']
76
+ // Each S-57 class (DEPARE, LIGHTS, BOYLAT, …) is a `source-layer` inside that one source.
77
+ ```
78
+
79
+ > S-101 portrayal is on the roadmap; today `chrt.style()` emits S-52.
80
+
57
81
  ## Configuration
58
82
 
59
83
  ```typescript
60
84
  const chrt = new Chrt({
61
85
  apiKey: 'chrt_live_...',
62
- baseUrl: 'https://api.chrt.co/v1', // default
63
- retries: 2, // retry on 5xx, default 2
86
+ baseUrl: 'https://api.chrt.co/v1', // default
87
+ retries: 2, // retry on 5xx, default 2
88
+ region: 'NOAA_ENC', // default tile region
89
+ tileBaseUrl: 'https://tiles.chrt.co', // override for staging tiles
64
90
  });
65
91
  ```
66
92
 
@@ -92,20 +118,27 @@ List available ENC cells.
92
118
  ### `chrt.cells.get(cellId)`
93
119
  Get metadata for a specific cell.
94
120
 
95
- ### `chrt.tiles.styleUrl()`
96
- Returns a Mapbox/MapLibre-compatible `style.json` URL.
121
+ ### `chrt.style(opts?)`
122
+ Returns a ready-to-use MapLibre/Mapbox GL **S-52** style object. Options: `palette`
123
+ (`'DAY'` | `'DUSK'` | `'NIGHT'`), `region`, `depthUnits`, plus the other atlas style options.
97
124
 
98
- ### `chrt.tiles.tileUrl(z, x, y)`
99
- Returns a single vector tile URL.
125
+ ### `chrt.transformRequest(opts?)`
126
+ Returns a `transformRequest` that mints a short-lived tile JWT from your API key and attaches
127
+ it as `Authorization: Bearer` on `tiles.chrt.co` requests only. Pass `{ region }` to override
128
+ the default region.
100
129
 
101
- ### `chrt.tiles.groups()`
102
- List available tileset groups.
130
+ ### `chrt.warmTiles(region?)`
131
+ Pre-mints the tile token so you can surface auth errors (e.g. `region_not_licensed`,
132
+ `tile_quota_exceeded`) before the map loads. Optional — `transformRequest()` warms the cache
133
+ on its own.
103
134
 
104
- ### `chrt.styleUrl()`
105
- Shortcut for `chrt.tiles.styleUrl()`.
135
+ ### `chrt.tiles.tileUrl(z, x, y)`
136
+ Single vector tile URL template at `tiles.chrt.co`. Prefer `chrt.style()`, which wires the
137
+ source for you.
106
138
 
107
- ### `chrt.transformRequest()`
108
- Returns a `transformRequest` function for Mapbox GL / MapLibre that auto-injects the API key.
139
+ ### `chrt.styleUrl()` · `chrt.tiles.groups()` — deprecated
140
+ CHRT serves no remote `style.json` — use `chrt.style()`. Tilesets were consolidated to one
141
+ source per region (2026-04), so the per-group catalogue is legacy.
109
142
 
110
143
  ## Error Handling
111
144
 
@@ -2895,7 +2895,10 @@ var NAUTICAL_SYMBOLS = [
2895
2895
  getSvg: landmarkSpire,
2896
2896
  group: "landmarks",
2897
2897
  description: "Church spire or steeple, conspicuous from seaward. Useful for visual fixing.",
2898
- s52PlRef: "LNDMRK17",
2898
+ // CATLMK 20 alone has no dedicated PL symbol (renders POSGEN01/03); church spires carry
2899
+ // FUNCTN 20/21 and render as the religious-building symbol BUIREL01 (BUIREL13 when conspicuous)
2900
+ s52PlRef: "BUIREL01",
2901
+ s57Class: "LNDMRK",
2899
2902
  attributeKey: "CATLMK",
2900
2903
  attributeValue: 20,
2901
2904
  renderSize: "32x32",
@@ -2912,9 +2915,11 @@ var NAUTICAL_SYMBOLS = [
2912
2915
  getSvg: landmarkTower,
2913
2916
  group: "landmarks",
2914
2917
  description: "Tower conspicuous from seaward. General-purpose landmark symbol.",
2915
- s52PlRef: "LNDMRK20",
2918
+ s52PlRef: "TOWERS01",
2919
+ s57Class: "LNDMRK",
2916
2920
  attributeKey: "CATLMK",
2917
2921
  attributeValue: 17,
2922
+ // TOWERS03 = conspicuous variant
2918
2923
  renderSize: "32x32",
2919
2924
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Tower body, cap, position circle" }],
2920
2925
  lineStyles: [{ element: "Tower outline", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -2929,7 +2934,8 @@ var NAUTICAL_SYMBOLS = [
2929
2934
  getSvg: landmarkChimney,
2930
2935
  group: "landmarks",
2931
2936
  description: "Industrial chimney or smokestack visible from the sea.",
2932
- s52PlRef: "LNDMRK01",
2937
+ s52PlRef: "CHIMNY01",
2938
+ s57Class: "LNDMRK",
2933
2939
  attributeKey: "CATLMK",
2934
2940
  attributeValue: 3,
2935
2941
  renderSize: "32x32",
@@ -2946,7 +2952,8 @@ var NAUTICAL_SYMBOLS = [
2946
2952
  getSvg: landmarkFlagstaff,
2947
2953
  group: "landmarks",
2948
2954
  description: "Flagstaff or flagpole conspicuous from seaward.",
2949
- s52PlRef: "LNDMRK03",
2955
+ s52PlRef: "FLGSTF01",
2956
+ s57Class: "LNDMRK",
2950
2957
  attributeKey: "CATLMK",
2951
2958
  attributeValue: 5,
2952
2959
  renderSize: "32x32",
@@ -2966,9 +2973,11 @@ var NAUTICAL_SYMBOLS = [
2966
2973
  getSvg: landmarkMonument,
2967
2974
  group: "landmarks",
2968
2975
  description: "Monument or memorial conspicuous from the sea.",
2969
- s52PlRef: "LNDMRK05",
2976
+ s52PlRef: "MONUMT12",
2977
+ s57Class: "LNDMRK",
2970
2978
  attributeKey: "CATLMK",
2971
2979
  attributeValue: 9,
2980
+ // MONUMT02 = non-conspicuous variant
2972
2981
  renderSize: "32x32",
2973
2982
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Monument body, base, position circle" }],
2974
2983
  lineStyles: [{ element: "Body outline", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -2984,6 +2993,7 @@ var NAUTICAL_SYMBOLS = [
2984
2993
  group: "landmarks",
2985
2994
  description: "Traditional windmill with rotating vanes. Conspicuous landmark.",
2986
2995
  s52PlRef: "WNDMIL12",
2996
+ s57Class: "LNDMRK",
2987
2997
  attributeKey: "CATLMK",
2988
2998
  attributeValue: 18,
2989
2999
  renderSize: "32x32",
@@ -3001,6 +3011,7 @@ var NAUTICAL_SYMBOLS = [
3001
3011
  group: "landmarks",
3002
3012
  description: "Modern 3-blade wind turbine. Increasingly common offshore landmark.",
3003
3013
  s52PlRef: "WIMCON11",
3014
+ s57Class: "LNDMRK",
3004
3015
  attributeKey: "CATLMK",
3005
3016
  attributeValue: 19,
3006
3017
  renderSize: "32x32",
@@ -3017,9 +3028,11 @@ var NAUTICAL_SYMBOLS = [
3017
3028
  getSvg: landmarkDome,
3018
3029
  group: "landmarks",
3019
3030
  description: "Dome or cupola (e.g., observatory, mosque) visible from the sea.",
3020
- s52PlRef: "LNDMRK03",
3031
+ s52PlRef: "DOMES011",
3032
+ s57Class: "LNDMRK",
3021
3033
  attributeKey: "CATLMK",
3022
3034
  attributeValue: 15,
3035
+ // DOMES001 = non-conspicuous variant
3023
3036
  renderSize: "32x32",
3024
3037
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Dome, base, position circle" }],
3025
3038
  lineStyles: [{ element: "Base outline", width: 0.8, color: "#000000" }, { element: "Dome curve", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -3034,7 +3047,8 @@ var NAUTICAL_SYMBOLS = [
3034
3047
  getSvg: landmarkRadarScanner,
3035
3048
  group: "landmarks",
3036
3049
  description: "Radar scanner antenna conspicuous from seaward.",
3037
- s52PlRef: "LNDMRK12",
3050
+ s52PlRef: "RASCAN01",
3051
+ s57Class: "LNDMRK",
3038
3052
  attributeKey: "CATLMK",
3039
3053
  attributeValue: 16,
3040
3054
  renderSize: "32x32",
@@ -3051,7 +3065,8 @@ var NAUTICAL_SYMBOLS = [
3051
3065
  getSvg: landmarkDishAerial,
3052
3066
  group: "landmarks",
3053
3067
  description: "Satellite dish or parabolic antenna visible from the sea.",
3054
- s52PlRef: "LNDMRK12",
3068
+ s52PlRef: "DSHAER01",
3069
+ s57Class: "LNDMRK",
3055
3070
  attributeKey: "CATLMK",
3056
3071
  attributeValue: 4,
3057
3072
  renderSize: "32x32",
@@ -3069,6 +3084,7 @@ var NAUTICAL_SYMBOLS = [
3069
3084
  group: "landmarks",
3070
3085
  description: "Water tower. Elevated tank on supporting structure, conspicuous landmark.",
3071
3086
  s52PlRef: "TOWERS12",
3087
+ s57Class: "SILTNK",
3072
3088
  attributeKey: "CATSIL",
3073
3089
  attributeValue: 4,
3074
3090
  renderSize: "32x32",
@@ -3085,7 +3101,9 @@ var NAUTICAL_SYMBOLS = [
3085
3101
  getSvg: landmarkRadioTvMast,
3086
3102
  group: "landmarks",
3087
3103
  description: "Radio or television transmission mast with horizontal cross-arms.",
3088
- s52PlRef: "LNDMRK10",
3104
+ // Radio/TV masts are CATLMK 7 (mast) + FUNCTN in S-57, so both mast entries share MSTCON04
3105
+ s52PlRef: "MSTCON04",
3106
+ s57Class: "LNDMRK",
3089
3107
  attributeKey: "CATLMK",
3090
3108
  attributeValue: 7,
3091
3109
  renderSize: "32x32",
@@ -3102,7 +3120,10 @@ var NAUTICAL_SYMBOLS = [
3102
3120
  getSvg: landmarkCross,
3103
3121
  group: "landmarks",
3104
3122
  description: "Religious cross or calvary conspicuous from seaward.",
3105
- s52PlRef: "LNDMRK05",
3123
+ // The PL has no dedicated symbol for CATLMK 14 (cross/calvary) — S-52 falls back to the
3124
+ // generic position symbol POSGEN01 (POSGEN03 when conspicuous)
3125
+ s52PlRef: "POSGEN01",
3126
+ s57Class: "LNDMRK",
3106
3127
  attributeKey: "CATLMK",
3107
3128
  attributeValue: 14,
3108
3129
  renderSize: "32x32",
@@ -3119,9 +3140,11 @@ var NAUTICAL_SYMBOLS = [
3119
3140
  getSvg: landmarkMast,
3120
3141
  group: "landmarks",
3121
3142
  description: "General mast (not radio/TV). Pole with spherical top.",
3122
- s52PlRef: "LNDMRK10",
3143
+ s52PlRef: "MSTCON04",
3144
+ s57Class: "LNDMRK",
3123
3145
  attributeKey: "CATLMK",
3124
3146
  attributeValue: 7,
3147
+ // MSTCON14 = conspicuous variant
3125
3148
  renderSize: "32x32",
3126
3149
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Mast pole, top ball, position circle" }],
3127
3150
  lineStyles: [{ element: "Mast pole", width: 2, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -3137,6 +3160,7 @@ var NAUTICAL_SYMBOLS = [
3137
3160
  group: "landmarks",
3138
3161
  description: "Silo, grain elevator, or storage tank conspicuous from the sea.",
3139
3162
  s52PlRef: "SILBUI01",
3163
+ s57Class: "SILTNK",
3140
3164
  attributeKey: "CATSIL",
3141
3165
  attributeValue: 1,
3142
3166
  renderSize: "32x32",