@unmap/sdk 0.28.0 → 0.29.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/README.md +16 -3
- package/dist/index.d.ts +127 -3
- package/dist/index.js +69 -3
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -84,20 +84,33 @@ The data is derived from OpenStreetMap and is ODbL: show `meta.attribution` with
|
|
|
84
84
|
terrain information, not avalanche or safety advice. A key without the add-on gets a `SkiError`
|
|
85
85
|
403 `addon_required`, with the SKU in `addon`.
|
|
86
86
|
|
|
87
|
+
Modelled urban noise for six Canadian cities, with the Urban Noise add-on:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const { data, meta } = await unmap.noise.point({ lat: 51.054, lng: -114.0884 })
|
|
91
|
+
// data.lden, data.lnight (dB), data.score and data.label ("Quiet" … "Severe"),
|
|
92
|
+
// data.exceeds_who, data.dominant_source, data.sources_db
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Calgary, Edmonton, Vancouver, Toronto, Montréal and Québec City; a point outside them throws a
|
|
96
|
+
`NoiseError` 422 `outside_coverage` with the covered `cities`. A hexagon with no modelled source
|
|
97
|
+
has `lden: null`, never 0 dB. The levels are derived from OpenStreetMap and are ODbL: show
|
|
98
|
+
`meta.attribution` with them. They are modelled, not measured.
|
|
99
|
+
|
|
87
100
|
## Without a build step
|
|
88
101
|
|
|
89
102
|
The same SDK loads from a script tag, with MapLibre already inside it:
|
|
90
103
|
|
|
91
104
|
```html
|
|
92
|
-
<link rel="stylesheet" href="https://cdn.unmap.dev/sdk/0.
|
|
105
|
+
<link rel="stylesheet" href="https://cdn.unmap.dev/sdk/0.29.0/unmap.css">
|
|
93
106
|
<div id="map" style="height: 400px"></div>
|
|
94
|
-
<script src="https://cdn.unmap.dev/sdk/0.
|
|
107
|
+
<script src="https://cdn.unmap.dev/sdk/0.29.0/unmap.js"></script>
|
|
95
108
|
<script>
|
|
96
109
|
unmap.createMap({ key: 'um_live_...', container: '#map', center: [-114.07, 51.05] })
|
|
97
110
|
</script>
|
|
98
111
|
```
|
|
99
112
|
|
|
100
|
-
Everything above is on the `unmap` global: `unmap.Unmap`, `unmap.Geocoder`, `unmap.Router`, `unmap.maplibregl`. Pin the exact version in production; `/sdk/0.
|
|
113
|
+
Everything above is on the `unmap` global: `unmap.Unmap`, `unmap.Geocoder`, `unmap.Router`, `unmap.maplibregl`. Pin the exact version in production; `/sdk/0.29/` follows patches and `/sdk/latest/` follows everything.
|
|
101
114
|
|
|
102
115
|
## Options
|
|
103
116
|
|
package/dist/index.d.ts
CHANGED
|
@@ -593,7 +593,7 @@ interface LayerArchive {
|
|
|
593
593
|
* The capability a gated layer's tiles need. These are capability ids (`legalLand`), not the
|
|
594
594
|
* `addon-*` SKUs a customer buys (`addon-legal-land`).
|
|
595
595
|
*/
|
|
596
|
-
type LayerCapability = "legalLand" | "energy" | "agriculture" | "remote" | "mining" | "ski";
|
|
596
|
+
type LayerCapability = "legalLand" | "energy" | "agriculture" | "remote" | "mining" | "ski" | "urbanNoise";
|
|
597
597
|
/**
|
|
598
598
|
* One catalog layer.
|
|
599
599
|
*
|
|
@@ -607,7 +607,7 @@ interface Layer {
|
|
|
607
607
|
en: string;
|
|
608
608
|
fr: string;
|
|
609
609
|
};
|
|
610
|
-
namespace?: "core" | "survey" | "energy" | "agriculture" | "remote" | "mining" | "ski";
|
|
610
|
+
namespace?: "core" | "survey" | "energy" | "agriculture" | "remote" | "mining" | "ski" | "noise";
|
|
611
611
|
/** ISO 3166 codes where coverage is VERIFIED. `coverage` carries the prose, including gaps. */
|
|
612
612
|
jurisdiction?: string[];
|
|
613
613
|
source?: string;
|
|
@@ -999,6 +999,128 @@ declare class Ski {
|
|
|
999
999
|
private get;
|
|
1000
1000
|
}
|
|
1001
1001
|
|
|
1002
|
+
/**
|
|
1003
|
+
* Modelled urban noise (`/noise/point`), behind the Urban Noise add-on.
|
|
1004
|
+
*
|
|
1005
|
+
* const { data, meta } = await unmap.noise.point({ lat: 51.054, lng: -114.0884 });
|
|
1006
|
+
* data.lden; // 49.3 dB, day-evening-night
|
|
1007
|
+
* data.label; // "Quiet"
|
|
1008
|
+
*
|
|
1009
|
+
* Six cities: Calgary, Edmonton, Vancouver, Toronto, Montréal and Québec City. A point outside
|
|
1010
|
+
* them throws `NoiseError` 422 `outside_coverage`, with the covered cities in `cities`.
|
|
1011
|
+
*
|
|
1012
|
+
* The levels are computed from OpenStreetMap, so the data is ODbL: show `meta.attribution`
|
|
1013
|
+
* ("© OpenStreetMap contributors; noise model: PickYourPlace") wherever you show it. They are
|
|
1014
|
+
* modelled, not measured: screening data, not a compliance assessment.
|
|
1015
|
+
*
|
|
1016
|
+
* A key without the add-on gets `NoiseError` 403 `addon_required` (or `dev_only` /
|
|
1017
|
+
* `usage_limit_reached` on the dev plan), with the SKU that unlocks it in `addon`.
|
|
1018
|
+
*/
|
|
1019
|
+
type NoiseLabel = "Quiet" | "Moderate" | "Noisy" | "Very noisy" | "Severe";
|
|
1020
|
+
type NoiseSource = "road" | "rail" | "aircraft" | "commercial";
|
|
1021
|
+
/** One H3 hexagon's modelled noise, as the gateway returns it (`apps/gateway/src/noise.ts`). */
|
|
1022
|
+
interface NoisePoint {
|
|
1023
|
+
/** H3 index of the hexagon. */
|
|
1024
|
+
h3: string;
|
|
1025
|
+
/** H3 resolution: 11, about 25 m across. */
|
|
1026
|
+
resolution: number;
|
|
1027
|
+
/** The covered city: `calgary`, `edmonton`, `vancouver`, `toronto`, `montreal` or `quebec-city`. */
|
|
1028
|
+
city: string;
|
|
1029
|
+
/** Day-evening-night level, dB (EU Lden). Null where no modelled source is in range: never 0 dB. */
|
|
1030
|
+
lden: number | null;
|
|
1031
|
+
/** Night level, 23:00 to 07:00, dB. */
|
|
1032
|
+
lnight: number | null;
|
|
1033
|
+
/** 0 to 100, anchored to the WHO 2018 Environmental Noise Guidelines; 100 is quiet. */
|
|
1034
|
+
score: number | null;
|
|
1035
|
+
/** 100 minus the score. */
|
|
1036
|
+
risk: number | null;
|
|
1037
|
+
/** 80 and up Quiet, 60 Moderate, 40 Noisy, 20 Very noisy, below 20 Severe. */
|
|
1038
|
+
label: NoiseLabel | null;
|
|
1039
|
+
/** Lden above 53 dB (day) and Lnight above 45 dB (night), the WHO 2018 road-traffic levels. */
|
|
1040
|
+
exceeds_who: {
|
|
1041
|
+
day: boolean | null;
|
|
1042
|
+
night: boolean | null;
|
|
1043
|
+
};
|
|
1044
|
+
dominant_source: NoiseSource | null;
|
|
1045
|
+
/** Each source's level at the hexagon, dB; null where that source is not modelled in range. */
|
|
1046
|
+
sources_db: Record<NoiseSource, number | null>;
|
|
1047
|
+
/** Present only when `lden` is null: "no modelled source within range". */
|
|
1048
|
+
note?: string;
|
|
1049
|
+
}
|
|
1050
|
+
/** The provenance every noise response carries. `data_version` is the release that answered. */
|
|
1051
|
+
interface NoiseMeta {
|
|
1052
|
+
source?: string;
|
|
1053
|
+
source_updated_at?: string | null;
|
|
1054
|
+
source_version?: string;
|
|
1055
|
+
content_hash?: string | null;
|
|
1056
|
+
unmap_updated_at: string;
|
|
1057
|
+
data_version?: string;
|
|
1058
|
+
status: "current" | "stale" | "partial" | "unknown";
|
|
1059
|
+
/** "© OpenStreetMap contributors; noise model: PickYourPlace". Display it with the data. */
|
|
1060
|
+
attribution: string;
|
|
1061
|
+
/** "ODbL-1.0". */
|
|
1062
|
+
licence: string;
|
|
1063
|
+
}
|
|
1064
|
+
interface NoiseResponse<T> {
|
|
1065
|
+
data: T;
|
|
1066
|
+
meta: NoiseMeta;
|
|
1067
|
+
}
|
|
1068
|
+
/** A city Urban Noise covers, as a 422 `outside_coverage` lists them. */
|
|
1069
|
+
interface NoiseCity {
|
|
1070
|
+
slug: string;
|
|
1071
|
+
name: {
|
|
1072
|
+
en: string;
|
|
1073
|
+
fr: string;
|
|
1074
|
+
};
|
|
1075
|
+
/** [west, south, east, north]. */
|
|
1076
|
+
bbox: [number, number, number, number];
|
|
1077
|
+
}
|
|
1078
|
+
interface NoisePointOptions {
|
|
1079
|
+
lat: number;
|
|
1080
|
+
lng: number;
|
|
1081
|
+
signal?: AbortSignal;
|
|
1082
|
+
}
|
|
1083
|
+
interface NoiseOptions {
|
|
1084
|
+
key: string;
|
|
1085
|
+
gateway?: string;
|
|
1086
|
+
fetch?: typeof fetch;
|
|
1087
|
+
/** Retry policy for `429`, `502`, `503` and transport failures. `false` disables it. See `RetryPolicy` in `@unmap/core`. */
|
|
1088
|
+
retry?: RetryPolicy | false;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* A noise failure.
|
|
1092
|
+
*
|
|
1093
|
+
* Extends `UnmapError`. `code` is the gateway's: `addon_required`, `dev_only` or
|
|
1094
|
+
* `usage_limit_reached` on a 403 (with `addon`, the SKU that unlocks it), `outside_coverage` on a
|
|
1095
|
+
* 422 (with `cities`, what is covered), `bad_request` for a coordinate the gateway refused,
|
|
1096
|
+
* `service_unavailable` while no release is served.
|
|
1097
|
+
*/
|
|
1098
|
+
declare class NoiseError extends UnmapError {
|
|
1099
|
+
/** The add-on SKU that unlocks the data, on a 403. */
|
|
1100
|
+
readonly addon?: string | undefined;
|
|
1101
|
+
/** The covered cities, on a 422 `outside_coverage`. */
|
|
1102
|
+
readonly cities?: NoiseCity[] | undefined;
|
|
1103
|
+
constructor(status: number, message: string, code?: string, retryAfterSeconds?: number,
|
|
1104
|
+
/** The add-on SKU that unlocks the data, on a 403. */
|
|
1105
|
+
addon?: string | undefined,
|
|
1106
|
+
/** The covered cities, on a 422 `outside_coverage`. */
|
|
1107
|
+
cities?: NoiseCity[] | undefined);
|
|
1108
|
+
}
|
|
1109
|
+
/** Modelled urban noise for six Canadian cities. Needs the Urban Noise add-on. */
|
|
1110
|
+
declare class Noise {
|
|
1111
|
+
private readonly key;
|
|
1112
|
+
private readonly base;
|
|
1113
|
+
private readonly fetchImpl;
|
|
1114
|
+
private readonly retry;
|
|
1115
|
+
constructor(opts: NoiseOptions);
|
|
1116
|
+
/**
|
|
1117
|
+
* The modelled noise of the hexagon (about 25 m) at a point. Throws `NoiseError` 422
|
|
1118
|
+
* `outside_coverage` outside the six cities, and 400 for a coordinate outside WGS84, which is
|
|
1119
|
+
* checked here before any request is made.
|
|
1120
|
+
*/
|
|
1121
|
+
point(opts: NoisePointOptions): Promise<NoiseResponse<NoisePoint>>;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1002
1124
|
interface UnmapOptions {
|
|
1003
1125
|
key: string;
|
|
1004
1126
|
gateway?: string;
|
|
@@ -1073,6 +1195,8 @@ declare class Unmap {
|
|
|
1073
1195
|
readonly byod: Byod;
|
|
1074
1196
|
/** Canadian ski areas and their 3D lifts. Needs the Ski add-on; the data is ODbL (OpenStreetMap). */
|
|
1075
1197
|
readonly ski: Ski;
|
|
1198
|
+
/** Modelled urban noise for six Canadian cities. Needs the Urban Noise add-on; the data is ODbL. */
|
|
1199
|
+
readonly noise: Noise;
|
|
1076
1200
|
readonly data: {
|
|
1077
1201
|
query(opts: QueryOptions): Promise<QueryResponse>;
|
|
1078
1202
|
};
|
|
@@ -1082,4 +1206,4 @@ declare class Unmap {
|
|
|
1082
1206
|
identify(opts: IdentifyOptions): Promise<IdentifyResponse>;
|
|
1083
1207
|
}
|
|
1084
1208
|
|
|
1085
|
-
export { type AffectedRoad, Byod, type ByodList, type ByodOverlay, Catalog, CatalogError, type CatalogOptions, Coverage, type CoverageCapability, type CoverageDocument, CoverageError, type CoverageFreshness, type CoverageFreshnessPolicy, type CoverageOptions, type CoverageProvenance, type CoverageSource, type DepartureOptions, Feedback, type FeedbackCategory, type FeedbackProduct, type FeedbackReceipt, type FeedbackReport, GeoIp, type GeoIpAttribution, GeoIpError, type GeoIpFound, type GeoIpLocateOptions, type GeoIpNotFound, type GeoIpOptions, type GeoIpResult, type Layer, type LayerArchive, type LayerCapability, type NearbyStopsOptions, type RoadCondition, type RoadEvent, type RoadEventQuery, type RoadEventType, type RoadEventsResponse, type RoadSeverity, Roads, RoadsError, type RoadsOptions, Ski, type SkiActivity, type SkiArea, type SkiAreasOptions, type SkiAspectDirection, SkiError, type SkiFeature, type SkiFeatureGeometry, type SkiFeatures, type SkiLift3d, type SkiLifts3d, type SkiLngLat, type SkiMeta, type SkiOptions, type SkiPoint3d, type SkiResponse, Timezone, TimezoneError, type TimezoneLookupOptions, type TimezoneOptions, type TimezoneResult, Transit, type TransitAgency, type TransitDeparture, TransitError, type TransitMeta, type TransitMetaStatus, type TransitOptions, type TransitResponse, type TransitRoute, type TransitStop, Unmap, type UnmapOptions };
|
|
1209
|
+
export { type AffectedRoad, Byod, type ByodList, type ByodOverlay, Catalog, CatalogError, type CatalogOptions, Coverage, type CoverageCapability, type CoverageDocument, CoverageError, type CoverageFreshness, type CoverageFreshnessPolicy, type CoverageOptions, type CoverageProvenance, type CoverageSource, type DepartureOptions, Feedback, type FeedbackCategory, type FeedbackProduct, type FeedbackReceipt, type FeedbackReport, GeoIp, type GeoIpAttribution, GeoIpError, type GeoIpFound, type GeoIpLocateOptions, type GeoIpNotFound, type GeoIpOptions, type GeoIpResult, type Layer, type LayerArchive, type LayerCapability, type NearbyStopsOptions, Noise, type NoiseCity, NoiseError, type NoiseLabel, type NoiseMeta, type NoiseOptions, type NoisePoint, type NoisePointOptions, type NoiseResponse, type NoiseSource, type RoadCondition, type RoadEvent, type RoadEventQuery, type RoadEventType, type RoadEventsResponse, type RoadSeverity, Roads, RoadsError, type RoadsOptions, Ski, type SkiActivity, type SkiArea, type SkiAreasOptions, type SkiAspectDirection, SkiError, type SkiFeature, type SkiFeatureGeometry, type SkiFeatures, type SkiLift3d, type SkiLifts3d, type SkiLngLat, type SkiMeta, type SkiOptions, type SkiPoint3d, type SkiResponse, Timezone, TimezoneError, type TimezoneLookupOptions, type TimezoneOptions, type TimezoneResult, Transit, type TransitAgency, type TransitDeparture, TransitError, type TransitMeta, type TransitMetaStatus, type TransitOptions, type TransitResponse, type TransitRoute, type TransitStop, Unmap, type UnmapOptions };
|
package/dist/index.js
CHANGED
|
@@ -545,6 +545,67 @@ var Ski = class {
|
|
|
545
545
|
}
|
|
546
546
|
};
|
|
547
547
|
|
|
548
|
+
// src/noise.ts
|
|
549
|
+
import { DEFAULT_GATEWAY as DEFAULT_GATEWAY8, UnmapError as UnmapError8, fetchWithRetry as fetchWithRetry8, parseRetryAfter as parseRetryAfter8 } from "@unmap/core";
|
|
550
|
+
var NoiseError = class extends UnmapError8 {
|
|
551
|
+
constructor(status, message, code, retryAfterSeconds, addon, cities) {
|
|
552
|
+
super(status, message, code, retryAfterSeconds);
|
|
553
|
+
this.addon = addon;
|
|
554
|
+
this.cities = cities;
|
|
555
|
+
this.name = "NoiseError";
|
|
556
|
+
}
|
|
557
|
+
addon;
|
|
558
|
+
cities;
|
|
559
|
+
};
|
|
560
|
+
async function errorFrom7(res, path) {
|
|
561
|
+
const fallback = `noise ${res.status} on ${path}`;
|
|
562
|
+
let body;
|
|
563
|
+
try {
|
|
564
|
+
body = await res.json();
|
|
565
|
+
} catch {
|
|
566
|
+
body = void 0;
|
|
567
|
+
}
|
|
568
|
+
const reason = typeof body?.error === "string" ? body.error : void 0;
|
|
569
|
+
const code = typeof body?.code === "string" ? body.code : void 0;
|
|
570
|
+
const addon = typeof body?.addon === "string" ? body.addon : void 0;
|
|
571
|
+
const cities = Array.isArray(body?.cities) ? body.cities : void 0;
|
|
572
|
+
const retryAfter = parseRetryAfter8(res.headers?.get?.("retry-after") ?? null) ?? void 0;
|
|
573
|
+
return new NoiseError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter, addon, cities);
|
|
574
|
+
}
|
|
575
|
+
var Noise = class {
|
|
576
|
+
key;
|
|
577
|
+
base;
|
|
578
|
+
fetchImpl;
|
|
579
|
+
retry;
|
|
580
|
+
constructor(opts) {
|
|
581
|
+
this.key = opts.key;
|
|
582
|
+
this.base = (opts.gateway ?? DEFAULT_GATEWAY8).replace(/\/+$/, "");
|
|
583
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
584
|
+
this.retry = opts.retry;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* The modelled noise of the hexagon (about 25 m) at a point. Throws `NoiseError` 422
|
|
588
|
+
* `outside_coverage` outside the six cities, and 400 for a coordinate outside WGS84, which is
|
|
589
|
+
* checked here before any request is made.
|
|
590
|
+
*/
|
|
591
|
+
async point(opts) {
|
|
592
|
+
const { lat, lng } = opts;
|
|
593
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lng) || Math.abs(lat) > 90 || Math.abs(lng) > 180) {
|
|
594
|
+
throw new NoiseError(400, "lat and lng must be WGS84 degrees", "bad_request");
|
|
595
|
+
}
|
|
596
|
+
const path = "/noise/point";
|
|
597
|
+
const query = new URLSearchParams({ key: this.key, lat: String(lat), lng: String(lng) });
|
|
598
|
+
const res = await fetchWithRetry8(
|
|
599
|
+
this.fetchImpl,
|
|
600
|
+
`${this.base}${path}?${query}`,
|
|
601
|
+
{ signal: opts.signal, headers: { accept: "application/json" } },
|
|
602
|
+
this.retry
|
|
603
|
+
);
|
|
604
|
+
if (!res.ok) throw await errorFrom7(res, path);
|
|
605
|
+
return await res.json();
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
|
|
548
609
|
// src/index.ts
|
|
549
610
|
import { Router } from "@unmap/routing";
|
|
550
611
|
import { Elevation } from "@unmap/elevation";
|
|
@@ -552,7 +613,7 @@ export * from "@unmap/geocoding";
|
|
|
552
613
|
export * from "@unmap/routing";
|
|
553
614
|
export * from "@unmap/elevation";
|
|
554
615
|
import { createMap as createMap2, unmapStyle, DEMO_CITIES, STYLE_NAMES, maplibregl } from "@unmap/maps";
|
|
555
|
-
import { UnmapError as
|
|
616
|
+
import { UnmapError as UnmapError9, parseRetryAfter as parseRetryAfter9 } from "@unmap/core";
|
|
556
617
|
var Unmap = class {
|
|
557
618
|
geocoder;
|
|
558
619
|
router;
|
|
@@ -581,6 +642,8 @@ var Unmap = class {
|
|
|
581
642
|
byod;
|
|
582
643
|
/** Canadian ski areas and their 3D lifts. Needs the Ski add-on; the data is ODbL (OpenStreetMap). */
|
|
583
644
|
ski;
|
|
645
|
+
/** Modelled urban noise for six Canadian cities. Needs the Urban Noise add-on; the data is ODbL. */
|
|
646
|
+
noise;
|
|
584
647
|
data;
|
|
585
648
|
map;
|
|
586
649
|
constructor(opts) {
|
|
@@ -597,6 +660,7 @@ var Unmap = class {
|
|
|
597
660
|
this.layers = new Catalog(opts);
|
|
598
661
|
this.byod = new Byod(opts);
|
|
599
662
|
this.ski = new Ski(opts);
|
|
663
|
+
this.noise = new Noise(opts);
|
|
600
664
|
this.data = { query: (queryOpts) => this.geocoder.queryLayer(queryOpts) };
|
|
601
665
|
if (opts.container) {
|
|
602
666
|
this.map = createMap({ ...opts, container: opts.container });
|
|
@@ -617,6 +681,8 @@ export {
|
|
|
617
681
|
Feedback,
|
|
618
682
|
GeoIp,
|
|
619
683
|
GeoIpError,
|
|
684
|
+
Noise,
|
|
685
|
+
NoiseError,
|
|
620
686
|
Roads,
|
|
621
687
|
RoadsError,
|
|
622
688
|
STYLE_NAMES,
|
|
@@ -627,10 +693,10 @@ export {
|
|
|
627
693
|
Transit,
|
|
628
694
|
TransitError,
|
|
629
695
|
Unmap,
|
|
630
|
-
|
|
696
|
+
UnmapError9 as UnmapError,
|
|
631
697
|
createMap2 as createMap,
|
|
632
698
|
maplibregl,
|
|
633
|
-
|
|
699
|
+
parseRetryAfter9 as parseRetryAfter,
|
|
634
700
|
unmapStyle
|
|
635
701
|
};
|
|
636
702
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/roads.ts","../src/transit.ts","../src/coverage.ts","../src/geoip.ts","../src/timezone.ts","../src/catalog.ts","../src/ski.ts"],"sourcesContent":["import {\n createMap,\n type Map as MaplibreMap,\n type MapProfile,\n type UnmapControlsOptions,\n type UnmapMapOptions,\n} from \"@unmap/maps\";\nimport { Geocoder, Places, type IdentifyOptions, type IdentifyResponse, type QueryOptions, type QueryResponse } from \"@unmap/geocoding\";\nimport { Feedback, Roads } from \"./roads\";\nimport { Transit } from \"./transit\";\nimport { Coverage } from \"./coverage\";\nimport { GeoIp } from \"./geoip\";\nimport { Timezone } from \"./timezone\";\nimport { Byod, Catalog } from \"./catalog\";\nimport { Ski } from \"./ski\";\nimport { Router } from \"@unmap/routing\";\nimport { Elevation } from \"@unmap/elevation\";\nimport type { RetryPolicy } from \"@unmap/core\";\n\nexport * from \"@unmap/geocoding\";\nexport * from \"@unmap/routing\";\nexport * from \"@unmap/elevation\";\nexport * from \"./roads\";\nexport * from \"./transit\";\nexport * from \"./coverage\";\nexport * from \"./geoip\";\nexport * from \"./timezone\";\nexport * from \"./catalog\";\nexport * from \"./ski\";\nexport { createMap, unmapStyle, DEMO_CITIES, STYLE_NAMES, maplibregl } from \"@unmap/maps\";\nexport type {\n Map,\n UnmapMapOptions,\n UnmapControlsOptions,\n DemoCity,\n StyleName,\n StyleInput,\n Projection,\n MapProfile,\n} from \"@unmap/maps\";\n\nexport interface UnmapOptions {\n key: string;\n gateway?: string;\n /** Override the fetch implementation (tests, non-global environments). */\n fetch?: typeof fetch;\n /**\n * Retry policy shared by every client on this instance. `false` disables it.\n *\n * On by default for `429`, `502`, `503` and transport failures. `Retry-After` is on a `429`\n * other than `billing_required`, and on a container `503`. A `502` has none. A wait longer\n * than `maxDelayMs` (10 s) is not slept through; the error carries `retryAfterSeconds`\n * instead, because a cold land or energy container asks for 60 and parking a request handler\n * for a minute is a worse surprise than an error. Feedback submissions are never retried:\n * they create a record and carry no idempotency key.\n */\n retry?: RetryPolicy | false;\n /** If provided, a map is mounted in this container. */\n container?: string | HTMLElement;\n style?: string;\n /** \"light\" or \"dark\", light by default. Applies to a bare style name; a `<style>-<mode>` string is passed through as written and `mode` does not change it. */\n mode?: string;\n /** Label language, e.g. \"fr\". Omit to follow the request's Accept-Language. */\n lang?: string;\n /** Compact `ut1.` theme code from unmap.dev/create. Wins over the path style on the gateway. */\n theme?: string;\n /** Catalog layer ids composed onto the style (`municipalities`, `survey.dls`, …). */\n overlays?: readonly string[];\n /** Composition preset. Not a style. Explicit overlays replace the profile list. */\n profile?: MapProfile;\n /** Draw the style on 3D terrain (`?terrain=on`). Any style, flat ones included. */\n terrain?: boolean;\n /** Vertical exaggeration for `terrain`, 0.1 to 5; the gateway's default is 1. */\n exaggeration?: number;\n /** `\"globe\"` (the default) or `\"mercator\"`. See `@unmap/maps` `UnmapMapOptions.projection`. */\n projection?: UnmapMapOptions[\"projection\"];\n center?: [number, number];\n zoom?: number;\n /** On-map controls. `true` (the default) adds zoom and compass buttons; `false` adds none. */\n controls?: boolean | UnmapControlsOptions;\n}\n\n/**\n * One Stripe-style key, the whole platform. With a `container` you get a rendered map.\n * `geocoder`, `router`, `elevation`, `timezone`, and `geoip` are always available.\n *\n * const map = new Unmap({ key: \"um_live_...\", container: \"#map\", center: [-75.69, 45.42] });\n */\nexport class Unmap {\n readonly geocoder: Geocoder;\n readonly router: Router;\n readonly elevation: Elevation;\n /** IANA timezone and the UTC offset at an instant. One call, anywhere. */\n readonly timezone: Timezone;\n /** City-level location for an IP address, worldwide. Omit the address for the caller's own. */\n readonly geoip: GeoIp;\n /** Canadian points of interest. Same corpus as the geocoder, in the Place shape. */\n readonly places: Places;\n /** Closures, incidents, construction and road conditions. */\n readonly roads: Roads;\n /** Report wrong data. Never metered. */\n readonly feedback: Feedback;\n /** Scheduled public-transit agencies, stops, routes and departures. */\n readonly transit: Transit;\n /**\n * Where each capability actually works. Unauthenticated and unmetered, so checking costs\n * nothing: read it before promising a user an answer, because transit routing covers only the feeds\n * marked routing-ready and road events cover two provinces.\n */\n readonly coverage: Coverage;\n /** The named Canadian layer catalog: what exists, its licence, and what it does not cover. */\n readonly layers: Catalog;\n /** Your own GeoJSON and PMTiles overlays, stored against this key. */\n readonly byod: Byod;\n /** Canadian ski areas and their 3D lifts. Needs the Ski add-on; the data is ODbL (OpenStreetMap). */\n readonly ski: Ski;\n readonly data: { query(opts: QueryOptions): Promise<QueryResponse> };\n readonly map?: MaplibreMap;\n\n constructor(opts: UnmapOptions) {\n this.geocoder = new Geocoder(opts);\n this.router = new Router(opts);\n this.elevation = new Elevation(opts);\n this.timezone = new Timezone(opts);\n this.geoip = new GeoIp(opts);\n this.places = new Places(opts);\n this.roads = new Roads(opts);\n this.feedback = new Feedback(opts);\n this.transit = new Transit(opts);\n this.coverage = new Coverage(opts);\n this.layers = new Catalog(opts);\n this.byod = new Byod(opts);\n this.ski = new Ski(opts);\n this.data = { query: (queryOpts) => this.geocoder.queryLayer(queryOpts) };\n if (opts.container) {\n this.map = createMap({ ...opts, container: opts.container } as UnmapMapOptions);\n }\n }\n\n /** Which catalog features contain a point. See `Geocoder.identify`. */\n identify(opts: IdentifyOptions): Promise<IdentifyResponse> {\n return this.geocoder.identify(opts);\n }\n}\n\n// The shared error type and retry policy, re-exported so a caller who installed only this\n// package can `catch (e) { if (e instanceof UnmapError) }` without adding @unmap/core.\nexport { UnmapError, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/**\n * Typed clients for road events (`/incidents`, `/closures`, `/road-conditions`) and for data\n * correction (`POST /feedback`).\n *\n * These live in `@unmap/sdk` rather than in packages of their own. A twelfth and thirteenth npm\n * package would each need an entry in the changesets fixed group and a trusted publisher\n * configured on npmjs.com, which is founder work, and neither has a framework wrapper the way\n * geocoding and routing do. If a customer ever wants roads without the rest of the SDK, that is\n * the moment to split it out.\n */\n\nexport type RoadEventType =\n | \"closure\"\n | \"incident\"\n | \"construction\"\n | \"weather\"\n | \"restriction\"\n | \"ferry\"\n | \"hazard\"\n | \"special_event\"\n | \"unknown\";\n\nexport type RoadCondition =\n | \"dry\"\n | \"wet\"\n | \"snow\"\n | \"ice\"\n | \"packed_snow\"\n | \"reduced_visibility\"\n | \"travel_not_recommended\"\n | \"closed\"\n | \"unknown\";\n\nexport type RoadSeverity = \"minor\" | \"major\" | \"unknown\";\n\nexport interface AffectedRoad {\n name: string;\n direction: \"north\" | \"south\" | \"east\" | \"west\" | \"both\" | \"unknown\";\n /** True only when the publisher says closed. Never inferred from the description. */\n closed: boolean;\n from?: string;\n to?: string;\n condition: RoadCondition;\n}\n\nexport interface RoadEvent {\n /** `um:road:<jurisdiction>:<publisher id>`. */\n id: string;\n /** The publisher's own id, so a report can be taken back to them. */\n source_id: string;\n /** ISO 3166-2, e.g. `CA-BC`. */\n jurisdiction: string;\n type: RoadEventType;\n /** The publisher's own subtype strings, untranslated. */\n subtypes: string[];\n status: \"active\" | \"planned\" | \"archived\";\n severity: RoadSeverity;\n /**\n * The publisher's description, verbatim.\n *\n * Believe this over `type` when they disagree: our categories are a lossy projection of someone\n * else's taxonomy, and the sentence a driver needs is in the prose.\n */\n description: string;\n headline?: string;\n /** The worst surface condition across this event's roads. */\n condition: RoadCondition;\n roads: AffectedRoad[];\n /** GeoJSON Point or LineString, WGS84. */\n geometry: { type: string; coordinates: unknown } | null;\n starts_at: string | null;\n ends_at: string | null;\n source_updated_at: string | null;\n unmap_updated_at: string;\n source: string;\n}\n\nexport interface RoadEventsResponse {\n data: RoadEvent[];\n meta: {\n status: \"current\" | \"stale\" | \"partial\" | \"unknown\";\n attribution: string;\n unmap_updated_at: string;\n /**\n * Jurisdictions that could not be reached, each with a reason.\n *\n * **Check this.** On `/closures` an empty `data` reads as \"nothing is shut\" to someone\n * deciding whether to drive, and that is a very different answer from \"we could not reach\n * British Columbia\".\n */\n unavailable?: { jurisdiction: string; reason: string }[];\n /**\n * Feeds served from unmap's five-minute poll rather than fetched for this request (Alberta),\n * each with its age. A feed older than `max_age_seconds` is in `unavailable` instead.\n */\n polled?: {\n jurisdiction: string;\n feed: \"events\" | \"conditions\";\n polled_at: string;\n age_seconds: number;\n max_age_seconds: number;\n }[];\n };\n}\n\nexport interface RoadEventQuery {\n /** Comma-separated ISO 3166-2 codes, or an array. An uncovered code is a 400, not an empty list. */\n jurisdiction?: string | readonly string[];\n bbox?: [number, number, number, number];\n near?: [number, number];\n /** Metres from `near`. Default 25000, max 500000. */\n radius?: number;\n type?: RoadEventType | readonly RoadEventType[];\n severity?: RoadSeverity | readonly RoadSeverity[];\n status?: \"active\" | \"planned\" | \"archived\";\n /** ISO 8601. Only events the publisher changed at or after this time. */\n updated_since?: string;\n /** 1 to 500, default 100. Applied AFTER the endpoint's own narrowing. */\n limit?: number;\n signal?: AbortSignal;\n}\n\nexport interface RoadsOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n /**\n * Retry policy for `429`, `502`, `503` and transport failures. `false` disables it.\n *\n * On by default. `Retry-After` is on a `429` other than `billing_required`, and on a container\n * `503`. A `502` has none: routing and `/elevation` both use\n * `upstream_error`. With no header the client backs off itself. A wait longer than\n * `maxDelayMs` (10 s) is not slept through; the error carries `retryAfterSeconds` instead.\n * See `RetryPolicy` in `@unmap/core`.\n */\n retry?: RetryPolicy | false;\n}\n\n/**\n * A road events failure.\n *\n * Extends `UnmapError`, so `catch (e) { if (e instanceof UnmapError) }` catches this alongside\n * every other unmap client's errors. `status` and `code` carry the HTTP status and the\n * gateway's error code, and `retryAfterSeconds` comes from the `Retry-After` header.\n */\nexport class RoadsError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"RoadsError\";\n }\n}\n\nfunction list(value: string | readonly string[] | undefined): string | undefined {\n if (value === undefined) return undefined;\n return Array.isArray(value) ? value.join(\",\") : String(value);\n}\n\nasync function errorFrom(res: Response, path: string, name: string): Promise<RoadsError> {\n const fallback = `${name} ${res.status} on ${path}`;\n let body: { error?: unknown; code?: unknown } | undefined;\n try {\n body = (await res.json()) as { error?: unknown; code?: unknown };\n } catch {\n body = undefined;\n }\n const reason = typeof body?.error === \"string\" ? body.error : undefined;\n const code = typeof body?.code === \"string\" ? body.code : undefined;\n const retryAfter = parseRetryAfter(res.headers?.get(\"retry-after\") ?? null) ?? undefined;\n return new RoadsError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter);\n}\n\n/**\n * Canadian road events.\n *\n * const { data, meta } = await unmap.roads.closures({ jurisdiction: \"CA-BC\" });\n *\n * A 503 means every requested jurisdiction was unreachable; the body still carries\n * `meta.unavailable`, so the thrown error is the exception rather than the information. Prefer\n * reading `meta.unavailable` on a 200 over treating an empty list as good news.\n */\nexport class Roads {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: RoadsOptions) {\n this.retry = opts.retry;\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n /** Every current road event. */\n incidents(query: RoadEventQuery = {}): Promise<RoadEventsResponse> {\n return this.request(\"/incidents\", query);\n }\n\n /** The subset where a road is actually shut. Narrowed before `limit` is applied. */\n closures(query: RoadEventQuery = {}): Promise<RoadEventsResponse> {\n return this.request(\"/closures\", query);\n }\n\n /**\n * The subset carrying a surface condition.\n *\n * Thin for British Columbia by design: its feed reports lane availability rather than the\n * surface, so unmap answers `unknown` instead of inferring a dry road from open lanes.\n */\n conditions(query: RoadEventQuery = {}): Promise<RoadEventsResponse> {\n return this.request(\"/road-conditions\", query);\n }\n\n private async request(path: string, query: RoadEventQuery): Promise<RoadEventsResponse> {\n const q = new URLSearchParams({ key: this.key });\n const params: Record<string, string | undefined> = {\n jurisdiction: list(query.jurisdiction),\n bbox: query.bbox?.join(\",\"),\n near: query.near?.join(\",\"),\n radius: query.radius != null ? String(query.radius) : undefined,\n type: list(query.type),\n severity: list(query.severity),\n status: query.status,\n updated_since: query.updated_since,\n limit: query.limit != null ? String(query.limit) : undefined,\n };\n for (const [k, v] of Object.entries(params)) if (v != null && v !== \"\") q.set(k, v);\n\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${path}?${q}`,\n { signal: query.signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) throw await errorFrom(res, path, \"roads\");\n return (await res.json()) as RoadEventsResponse;\n }\n}\n\n// ---------------------------------------------------------------------------------------------\n// Feedback\n// ---------------------------------------------------------------------------------------------\n\nexport type FeedbackProduct =\n | \"tiles\"\n | \"terrain\"\n | \"names\"\n | \"geocoding\"\n | \"places\"\n | \"routing\"\n | \"layers\"\n | \"transit\"\n | \"roads\"\n | \"ev\";\n\nexport type FeedbackCategory =\n | \"incorrect_location\"\n | \"incorrect_address\"\n | \"missing_place\"\n | \"wrong_place_name\"\n | \"wrong_category\"\n | \"bad_route\"\n | \"missing_road_closure\"\n | \"incorrect_road_condition\"\n | \"incorrect_transit_result\"\n | \"other\";\n\nexport interface FeedbackReport {\n product: FeedbackProduct;\n category: FeedbackCategory;\n /** Up to 2000 characters, stored exactly as written. One of this or `resultId` is required. */\n description?: string;\n /** The unmap id of the result being reported. */\n resultId?: string;\n /**\n * The request being reported.\n *\n * **Only send this with the reporter's explicit agreement.** The gateway refuses it without\n * `include_context: true`, which this sets whenever `context` is present, and that flag is\n * meant to record a person's choice rather than a client default. unmap redacts query strings\n * from its own logs; a client that attached them here would undo that.\n */\n context?: unknown;\n signal?: AbortSignal;\n}\n\nexport interface FeedbackReceipt {\n /** Quote this in a follow-up. */\n id: string;\n status: string;\n created_at: string;\n /** Whether a request context was actually kept. `false` when none was offered. */\n context_stored: boolean;\n}\n\n/**\n * Report wrong data.\n *\n * await unmap.feedback.submit({\n * product: \"geocoding\",\n * category: \"incorrect_location\",\n * description: \"17 Ave SW returns a street in Edmonton.\",\n * });\n *\n * Authenticated like everything else and **never metered**: telling us our data is wrong does not\n * cost a call. At most 200 reports per account per day.\n */\nexport class Feedback {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(opts: RoadsOptions) {\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n submit(report: FeedbackReport): Promise<FeedbackReceipt> {\n return this.post(\"/feedback\", {\n product: report.product,\n category: report.category,\n ...(report.description ? { description: report.description } : {}),\n ...(report.resultId ? { result_id: report.resultId } : {}),\n ...(report.context !== undefined ? { include_context: true, context: report.context } : {}),\n }, report.signal);\n }\n\n /** A report about one place. `product` and `result_id` come from the path. */\n aboutPlace(\n placeId: string,\n report: Omit<FeedbackReport, \"product\" | \"resultId\">,\n ): Promise<FeedbackReceipt> {\n return this.post(`/places/${encodeURIComponent(placeId)}/feedback`, {\n category: report.category,\n ...(report.description ? { description: report.description } : {}),\n ...(report.context !== undefined ? { include_context: true, context: report.context } : {}),\n }, report.signal);\n }\n\n private async post(path: string, body: unknown, signal?: AbortSignal): Promise<FeedbackReceipt> {\n // Deliberately NOT routed through `fetchWithRetry`, unlike every read on this client.\n //\n // A feedback submission creates a record and carries no idempotency key, so a retry after a\n // timeout or a 503 risks filing the same report twice. Retrying a read costs a round trip;\n // retrying this costs the quality programme a duplicate, and the caller is the only one who\n // knows whether their report already landed.\n const res = await this.fetchImpl(`${this.base}${path}`, {\n method: \"POST\",\n signal,\n headers: { \"content-type\": \"application/json\", authorization: `Bearer ${this.key}` },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw await errorFrom(res, path, \"feedback\");\n return (await res.json()) as FeedbackReceipt;\n }\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\nexport type TransitMetaStatus = \"current\" | \"stale\" | \"partial\" | \"unknown\";\n\nexport interface TransitMeta {\n /** Static GTFS is scheduled data. Realtime will use a different value and updated_at. */\n schedule_type: \"scheduled\";\n source?: string;\n source_updated_at?: string | null;\n source_version?: string;\n content_hash?: string | null;\n sources?: readonly {\n id: string;\n source: string;\n source_url: string;\n attribution: string;\n source_updated_at: string | null;\n source_version: string;\n content_hash: string | null;\n status: \"current\" | \"stale\" | \"unknown\";\n }[];\n unmap_updated_at: string;\n data_version?: string;\n status: TransitMetaStatus;\n attribution: string;\n}\n\nexport interface TransitResponse<T> {\n data: T;\n meta: TransitMeta;\n}\n\nexport interface TransitAgency {\n id: string;\n feed: string;\n name: string;\n url?: string;\n timezone: string;\n lang?: string;\n phone?: string;\n fare_url?: string;\n}\n\nexport interface TransitStop {\n id: string;\n feed: string;\n name: string;\n coordinates: [number, number];\n code?: string;\n description?: string;\n zone_id?: string;\n parent_station_id?: string;\n wheelchair_boarding?: 0 | 1 | 2;\n route_ids: string[];\n /** Metres from the requested point. Present only on nearbyStops. */\n distance?: number;\n}\n\nexport interface TransitRoute {\n id: string;\n feed: string;\n agency_id: string;\n short_name?: string;\n long_name?: string;\n description?: string;\n route_type: number;\n colour?: string;\n text_colour?: string;\n url?: string;\n}\n\nexport interface TransitDeparture {\n stop_id: string;\n route_id: string;\n trip_id: string;\n headsign?: string;\n arrival: string;\n departure: string;\n stop_sequence: number;\n /** 2 = phone agency; 3 = coordinate with driver. Omitted for ordinary pickup. */\n pickup_type?: 2 | 3;\n schedule_type: \"scheduled\";\n}\n\nexport interface NearbyStopsOptions {\n /** Metres. Default 1000, range 50 to 10000. */\n radius?: number;\n /** Default 20, maximum 100. */\n limit?: number;\n signal?: AbortSignal;\n}\n\nexport interface DepartureOptions {\n /** An instant. Date values serialize to ISO; strings must include Z or an offset. Default now. */\n at?: Date | string;\n /** Minutes after at. Default 60, maximum 240. */\n window?: number;\n /** Default 50, maximum 200. */\n limit?: number;\n signal?: AbortSignal;\n}\n\nexport interface TransitOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n /**\n * Retry policy for `429`, `502`, `503` and transport failures. `false` disables it.\n *\n * On by default. `Retry-After` is on a `429` other than `billing_required`, and on a container\n * `503`. A `502` has none: routing and `/elevation` both use\n * `upstream_error`. With no header the client backs off itself. A wait longer than\n * `maxDelayMs` (10 s) is not slept through; the error carries `retryAfterSeconds` instead.\n * See `RetryPolicy` in `@unmap/core`.\n */\n retry?: RetryPolicy | false;\n}\n\n/**\n * A transit failure.\n *\n * Extends `UnmapError`, so `catch (e) { if (e instanceof UnmapError) }` catches this alongside\n * every other unmap client's errors. `status` and `code` carry the HTTP status and the\n * gateway's error code, and `retryAfterSeconds` comes from the `Retry-After` header.\n */\nexport class TransitError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"TransitError\";\n }\n}\n\n/**\n * The gateway's error body, as a `TransitError`.\n *\n * Named rather than inline since 2026-09-20: `fetchWithRetry` needs to inspect the failure to\n * decide whether to wait, and the throw needs to build the same error. Two copies of this would\n * be two chances for the retry predicate and the thrown error to disagree.\n */\nasync function errorFrom(res: Response, path: string): Promise<TransitError> {\n let body: { error?: unknown; code?: unknown } = {};\n try {\n body = (await res.json()) as typeof body;\n } catch {\n /* use the bounded fallback */\n }\n const message = typeof body.error === \"string\" ? body.error : `transit ${res.status} on ${path}`;\n const code = typeof body.code === \"string\" ? body.code : undefined;\n const retryAfter = parseRetryAfter(res.headers?.get(\"retry-after\") ?? null) ?? undefined;\n return new TransitError(res.status, message, code, retryAfter);\n}\n\n/** Static Canadian GTFS data. Scheduled departures ship before realtime. */\nexport class Transit {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: TransitOptions) {\n this.retry = opts.retry;\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n agencies(signal?: AbortSignal): Promise<TransitResponse<TransitAgency[]>> {\n return this.get(\"/transit/agencies\", undefined, signal);\n }\n\n nearbyStops(near: [number, number], options: NearbyStopsOptions = {}): Promise<TransitResponse<TransitStop[]>> {\n return this.get(\"/transit/stops\", {\n near: near.join(\",\"),\n radius: options.radius,\n limit: options.limit,\n }, options.signal);\n }\n\n getStop(id: string, signal?: AbortSignal): Promise<TransitResponse<TransitStop>> {\n return this.get(`/transit/stops/${encodeURIComponent(id)}`, undefined, signal);\n }\n\n getRoute(id: string, signal?: AbortSignal): Promise<TransitResponse<TransitRoute>> {\n return this.get(`/transit/routes/${encodeURIComponent(id)}`, undefined, signal);\n }\n\n departures(id: string, options: DepartureOptions = {}): Promise<TransitResponse<TransitDeparture[]>> {\n return this.get(`/transit/stops/${encodeURIComponent(id)}/departures`, {\n at: options.at instanceof Date ? options.at.toISOString() : options.at,\n window: options.window,\n limit: options.limit,\n }, options.signal);\n }\n\n private async get<T>(\n path: string,\n values?: Record<string, string | number | undefined>,\n signal?: AbortSignal,\n ): Promise<TransitResponse<T>> {\n const query = new URLSearchParams({ key: this.key });\n for (const [name, value] of Object.entries(values ?? {})) {\n if (value !== undefined) query.set(name, String(value));\n }\n const response = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${path}?${query}`,\n { signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!response.ok) throw await errorFrom(response, path);\n return await response.json() as TransitResponse<T>;\n }\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/**\n * `GET /coverage.json` — where each capability actually works, and where it does not.\n *\n * The document is generated from the gateway's source registry rather than written by hand, so\n * it cannot claim a capability with no registered source behind it. It is also the only place\n * that answers \"is transit routing live in this city\" before you promise a user an answer:\n * routing covers only the feeds marked routing-ready, road events cover two provinces, and Places coverage is OpenStreetMap's,\n * which is thin outside cities.\n *\n * Unauthenticated and unmetered on the gateway, which is why this client takes no key: a\n * developer deciding whether to use unmap has no key yet, and a customer checking whether a gap\n * has closed should not pay a call to be told it has not.\n */\n\n/** The one path this client calls. A literal so the gateway's SDK-parity audit can see it. */\nconst COVERAGE_PATH = \"/coverage.json\";\n\n/** Freshness of a capability or a source. `unknown` means no cadence was declared. */\nexport type CoverageFreshness = \"current\" | \"stale\" | \"unknown\";\n\n/** How a source's freshness is judged, mirrored from the gateway's `FreshnessPolicy`. */\nexport interface CoverageFreshnessPolicy {\n /** ISO 8601 duration, `static`, or `unknown`. */\n interval: string;\n basis: string;\n /** When `freshness_status` stops being true, or null when staleness cannot be asked. */\n stale_after: string | null;\n}\n\n/** Where a source's data came from and when unmap last produced it. */\nexport interface CoverageProvenance {\n source_updated_at: string | null;\n unmap_updated_at: string | null;\n build_id: string | null;\n version: string;\n content_hash: string | null;\n freshness_policy: CoverageFreshnessPolicy;\n freshness_status: CoverageFreshness;\n /** Counts the producing job measured. Omitted when it measured none. */\n measurements?: Record<string, number>;\n}\n\nexport interface CoverageSource {\n id: string;\n provider: string;\n /** Bilingual, like every human-readable field in this document. */\n dataset_name: { en: string; fr: string };\n canonical_url: string;\n licence: { en: string; fr: string };\n licence_url: string;\n attribution: string;\n jurisdiction: string[];\n /** ISO 8601 duration, e.g. `P1D`. */\n refresh_interval: string;\n last_checked_at: string;\n /** Upstream's edition date, when unmap last produced the data, which build did, and how fresh that is. */\n provenance: CoverageProvenance;\n coverage: string;\n known_gaps: { en: string; fr: string };\n quality_status: string;\n /** A note on the source, in English, present only when there is one. */\n notes?: string;\n}\n\nexport interface CoverageCapability {\n /** `tiles`, `geocoding`, `routing`, `places`, `transit`, `roads`, and so on. */\n id: string;\n display_name: { en: string; fr: string };\n /** `planned` is published as honestly as `live`: a capability absent from the page reads as \"not our problem\". */\n availability: \"live\" | \"planned\";\n /** The endpoints that serve it. Empty while planned. */\n endpoints: string[];\n coverage: { en: string; fr: string };\n /** Written to be accurate rather than flattering. Read this before promising coverage to a user. */\n known_gaps: { en: string; fr: string };\n /** ISO 3166-2 subdivisions, or `CA` for national. */\n jurisdictions: string[];\n freshness_status: CoverageFreshness;\n sources: CoverageSource[];\n}\n\nexport interface CoverageDocument {\n generated_at: string;\n /** How to read the dates below, in both official languages. */\n freshness_basis: { en: string; fr: string };\n /** Schema tag of the manifests behind `provenance`, so a consumer can pin what it parses. */\n provenance_schema: string;\n capabilities: CoverageCapability[];\n /** Every jurisdiction code any source claims. */\n jurisdictions: string[];\n}\n\nexport class CoverageError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"CoverageError\";\n }\n}\n\nexport interface CoverageOptions {\n gateway?: string;\n /** Override the fetch implementation (tests, non-global environments). */\n fetch?: typeof fetch;\n retry?: RetryPolicy | false;\n}\n\n/** Where unmap works. Takes no key: the endpoint is unauthenticated and unmetered. */\nexport class Coverage {\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: CoverageOptions = {}) {\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry;\n }\n\n /** The whole document. Cached an hour at the edge, so polling it is cheap. */\n async get(opts: { signal?: AbortSignal } = {}): Promise<CoverageDocument> {\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${COVERAGE_PATH}`,\n { signal: opts.signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) {\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n throw new CoverageError(res.status, `coverage ${res.status}`, undefined, retryAfter);\n }\n return (await res.json()) as CoverageDocument;\n }\n\n /**\n * One capability by id, or `undefined` when the document does not describe it.\n *\n * const transit = await unmap.coverage.capability(\"transit\");\n * if (!transit?.jurisdictions.includes(\"CA-ON\")) return \"not available here yet\";\n */\n async capability(id: string, opts: { signal?: AbortSignal } = {}): Promise<CoverageCapability | undefined> {\n const doc = await this.get(opts);\n return doc.capabilities.find((c) => c.id === id);\n }\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/** The one path this client calls. A literal so the gateway's SDK-parity audit can see it. */\nconst GEOIP_PATH = \"/geoip\";\n\n/** DB-IP City Lite is CC BY 4.0: show `text`, linked to `url`, wherever the result is used. */\nexport interface GeoIpAttribution {\n text: string;\n url: string;\n}\n\n/** An address the database lists. Accuracy is city level at best, often only the country. */\nexport interface GeoIpFound {\n /** The canonical form of the address that was looked up. */\n ip: string;\n found: true;\n /** ISO 3166-1 alpha-2. */\n country_code: string;\n country: string;\n /** `null` when the database has no region for the address. */\n region: string | null;\n /** `null` when the database has no city for the address. */\n city: string | null;\n /** `[longitude, latitude]`, the same order as the rest of the SDK. */\n coordinates: [number, number];\n accuracy: \"city\";\n release: string;\n attribution: GeoIpAttribution;\n}\n\n/**\n * A public address the database does not list, such as unassigned space. Private, loopback,\n * link-local and other reserved addresses are not this: like a malformed address, they throw\n * `GeoIpError` with status `400` and code `invalid_ip`.\n */\nexport interface GeoIpNotFound {\n ip: string;\n found: false;\n release: string;\n attribution: GeoIpAttribution;\n}\n\nexport type GeoIpResult = GeoIpFound | GeoIpNotFound;\n\nexport interface GeoIpLocateOptions {\n signal?: AbortSignal;\n}\n\nexport class GeoIpError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"GeoIpError\";\n }\n}\n\nexport interface GeoIpOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n retry?: RetryPolicy | false;\n}\n\n/**\n * City-level location for an IP address, worldwide.\n *\n * const here = await unmap.geoip.locate(); // the caller's own address\n * const google = await unmap.geoip.locate(\"8.8.8.8\");\n *\n * Omit the address to look up the machine making the request. From a browser that is the\n * visitor; from a server it is the server. Show `attribution` wherever you use the result.\n * A malformed, private or reserved address throws `GeoIpError` (`400`, `invalid_ip`); an\n * unavailable dataset throws one with status `503`.\n */\nexport class GeoIp {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: GeoIpOptions) {\n this.retry = opts.retry;\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n async locate(ip?: string, opts: GeoIpLocateOptions = {}): Promise<GeoIpResult> {\n const q = new URLSearchParams({ key: this.key });\n if (ip !== undefined) q.set(\"ip\", ip);\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${GEOIP_PATH}?${q}`,\n { signal: opts.signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) throw await errorFrom(res);\n return (await res.json()) as GeoIpResult;\n }\n}\n\nasync function errorFrom(res: Response): Promise<GeoIpError> {\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n let message = res.statusText || `geoip ${res.status}`;\n let code: string | undefined;\n try {\n const body = (await res.json()) as { error?: unknown; code?: unknown };\n if (typeof body.error === \"string\" && body.error) message = body.error;\n if (typeof body.code === \"string\") code = body.code;\n } catch {\n /* a non-JSON failure still carries the status */\n }\n return new GeoIpError(res.status, message, code, retryAfter);\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/** The one path this client calls. A literal so the gateway's SDK-parity audit can see it. */\nconst TIMEZONE_PATH = \"/timezone\";\n\nexport interface TimezoneResult {\n timezone: string | null;\n offset: string | null;\n offsetSeconds: number | null;\n dst: boolean | null;\n}\n\nexport interface TimezoneLookupOptions {\n /** Instant the offset is evaluated at. Omit it for the current time. */\n at?: Date;\n signal?: AbortSignal;\n}\n\nexport class TimezoneError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"TimezoneError\";\n }\n}\n\nexport interface TimezoneOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n retry?: RetryPolicy | false;\n}\n\n/**\n * IANA timezone for a coordinate.\n *\n * const zone = await unmap.timezone.lookup([-114.0719, 51.0447]);\n *\n * The coordinate is `[longitude, latitude]`, the same order as the rest of the SDK.\n * `at` selects the offset. It does not change which zone the coordinate falls in.\n */\nexport class Timezone {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: TimezoneOptions) {\n this.retry = opts.retry;\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n async lookup(coordinate: [number, number], opts: TimezoneLookupOptions = {}): Promise<TimezoneResult> {\n const [lon, lat] = coordinate;\n const q = new URLSearchParams({ key: this.key, lon: String(lon), lat: String(lat) });\n if (opts.at) q.set(\"at\", opts.at.toISOString());\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${TIMEZONE_PATH}?${q}`,\n { signal: opts.signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) throw await errorFrom(res);\n return (await res.json()) as TimezoneResult;\n }\n}\n\nasync function errorFrom(res: Response): Promise<TimezoneError> {\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n let message = res.statusText || `timezone ${res.status}`;\n let code: string | undefined;\n try {\n const body = (await res.json()) as { error?: unknown; code?: unknown };\n if (typeof body.error === \"string\" && body.error) message = body.error;\n if (typeof body.code === \"string\") code = body.code;\n } catch {\n /* a non-JSON failure still carries the status */\n }\n return new TimezoneError(res.status, message, code, retryAfter);\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/**\n * Clients for the named Canadian layer catalog (`/layers`) and for tenant overlays (`/byod`).\n *\n * Both existed as endpoints with no client until the SDK parity audit\n * (`apps/gateway/test/sdk-parity.test.ts`) went looking and found them. They were the two entries\n * in that audit's `KNOWN_GAPS`; this closes both.\n *\n * ## Why these are the two that mattered\n *\n * `@unmap/maps` already consumes layer and overlay **tiles** through a style document, which is\n * why the gap was easy to miss: maps work. What had no client was everything *around* the tiles.\n * Nothing could ask what layers exist, what a layer's licence says, or which add-on it needs, and\n * nothing could upload an overlay without hand-rolling a multipart request.\n */\n\n// ---------------------------------------------------------------------------------------------\n// The named layer catalog\n// ---------------------------------------------------------------------------------------------\n\nexport interface LayerArchive {\n id: string;\n minzoom: number;\n maxzoom: number;\n}\n\n/**\n * The capability a gated layer's tiles need. These are capability ids (`legalLand`), not the\n * `addon-*` SKUs a customer buys (`addon-legal-land`).\n */\nexport type LayerCapability = \"legalLand\" | \"energy\" | \"agriculture\" | \"remote\" | \"mining\" | \"ski\";\n\n/**\n * One catalog layer.\n *\n * `licence`, `attribution` and `known_gaps` are the reason to read this rather than hard-code a\n * layer id: they are the governance record, and a caller displaying a layer has to display its\n * attribution.\n */\nexport interface Layer {\n id: string;\n display_name: { en: string; fr: string };\n namespace?: \"core\" | \"survey\" | \"energy\" | \"agriculture\" | \"remote\" | \"mining\" | \"ski\";\n /** ISO 3166 codes where coverage is VERIFIED. `coverage` carries the prose, including gaps. */\n jurisdiction?: string[];\n source?: string;\n source_url?: string;\n licence: string;\n attribution: string;\n coverage: string;\n resolution?: string;\n updated_at?: string;\n source_updated_at?: string;\n ingestion_version?: string;\n schema_version?: string;\n freshness_policy?: string;\n known_gaps?: string;\n /**\n * The capability this layer's tiles need, or null when it is core. Reading the catalog is never\n * gated; the tiles are, and answer `403` `addon_required` on a key without the add-on.\n */\n addon?: LayerCapability | null;\n archives?: LayerArchive[];\n /** Vector source-layer name inside the archive. */\n source_layer?: string;\n /** Every source-layer, present only when the archive stacks more than one (survey grids). */\n source_layers?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------\n// Tenant overlays (BYOD)\n// ---------------------------------------------------------------------------------------------\n\nexport interface ByodOverlay {\n /** `ovl_` plus twelve hex characters. */\n id: string;\n name: string;\n kind: \"geojson\" | \"pmtiles\";\n bytes: number;\n createdAt: string;\n url: string;\n /** The `byod:<id>` token to pass to `overlays`. This is the field you actually want. */\n overlay: string;\n}\n\nexport interface ByodList {\n overlays: ByodOverlay[];\n /** The caps, from the server rather than from a constant here that could drift. */\n limits: { maxFiles: number; maxBytes: number; kinds: string[] };\n}\n\nexport interface CatalogOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n /**\n * Retry policy for reads (`429`, `502`, `503` and transport failures). `false` disables it.\n * Uploads and deletes are never retried. See `RetryPolicy` in `@unmap/core`.\n */\n retry?: RetryPolicy | false;\n}\n\n/**\n * A layer-catalog or overlay failure.\n *\n * Extends `UnmapError`, so `catch (e) { if (e instanceof UnmapError) }` catches it alongside every\n * other unmap client's errors. `code` is the gateway's: `not_found` for an unknown layer or\n * overlay id, `bad_request` for an upload the server refused (wrong type, over the size cap, or\n * over the overlay count).\n */\nexport class CatalogError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"CatalogError\";\n }\n}\n\nasync function errorFrom(res: Response, path: string): Promise<CatalogError> {\n const fallback = `catalog ${res.status} on ${path}`;\n let body: { error?: unknown; code?: unknown } | undefined;\n try {\n body = (await res.json()) as { error?: unknown; code?: unknown };\n } catch {\n body = undefined;\n }\n const reason = typeof body?.error === \"string\" ? body.error : undefined;\n const code = typeof body?.code === \"string\" ? body.code : undefined;\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n return new CatalogError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter);\n}\n\n/** The pieces both clients share: the key, the base URL, fetch, and the read path. */\nabstract class KeyedClient {\n protected readonly key: string;\n protected readonly base: string;\n protected readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: CatalogOptions) {\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry;\n }\n\n protected url(path: string, values?: Record<string, string | undefined>): string {\n const query = new URLSearchParams({ key: this.key });\n for (const [name, value] of Object.entries(values ?? {})) {\n if (value !== undefined) query.set(name, value);\n }\n return `${this.base}${path}?${query}`;\n }\n\n /** A GET, retried per the policy. Throws `CatalogError` on a non-2xx. */\n protected async read(path: string, init: RequestInit, values?: Record<string, string | undefined>): Promise<Response> {\n const res = await fetchWithRetry(this.fetchImpl, this.url(path, values), init, this.retry);\n if (!res.ok) throw await errorFrom(res, path);\n return res;\n }\n}\n\n/**\n * The named Canadian layer catalog.\n *\n * const layers = await unmap.layers.list({ jurisdiction: \"CA-AB\" });\n * const parcels = await unmap.layers.get(\"survey.dls\");\n * console.log(parcels.attribution, parcels.known_gaps);\n *\n * A retired or misspelled id is a plain `404` `not_found`, the same as one that never existed:\n * the gateway keeps no aliases for renamed layers.\n */\nexport class Catalog extends KeyedClient {\n /**\n * Every catalog layer, or only those with verified coverage in `jurisdiction`.\n *\n * A country code matches its own subdivisions (`CA` includes `CA-AB`). An unknown code is an\n * empty list, not an error: the catalog has nothing there.\n */\n async list(opts: { jurisdiction?: string; signal?: AbortSignal } = {}): Promise<Layer[]> {\n const res = await this.read(\n \"/layers\",\n { signal: opts.signal, headers: { accept: \"application/json\" } },\n { jurisdiction: opts.jurisdiction },\n );\n const body = (await res.json()) as { layers?: Layer[] } | Layer[];\n return Array.isArray(body) ? body : (body.layers ?? []);\n }\n\n /** One layer by id. Throws `CatalogError` 404 `not_found` when it does not exist. */\n async get(id: string, opts: { signal?: AbortSignal } = {}): Promise<Layer> {\n const res = await this.read(`/layers/${encodeURIComponent(id)}`, {\n signal: opts.signal,\n headers: { accept: \"application/json\" },\n });\n return (await res.json()) as Layer;\n }\n}\n\n/**\n * Tenant overlays: your own GeoJSON or PMTiles, stored against your key. `upload`, `list` and\n * `delete` need a server key; a browser key only reads an overlay by id (403 `server_key_required`).\n *\n * const { overlay } = await unmap.byod.upload(file, { name: \"Service areas\" });\n * const map = new Unmap({ key, container: \"#map\", overlays: [overlay] });\n *\n * The `overlay` field on the result is the `byod:<id>` token: that is what `overlays` takes, not\n * the bare id, and returning the whole record rather than just the id is what makes that hard to\n * get wrong.\n *\n * Hard caps are the server's (10 overlays, 5 MiB each) and are reported by `list()` rather than\n * duplicated here, so a change on the server cannot leave a stale constant in the client.\n */\nexport class Byod extends KeyedClient {\n /** Overlays on this tenant, plus the server's current limits. */\n async list(opts: { signal?: AbortSignal } = {}): Promise<ByodList> {\n const res = await this.read(\"/byod\", { signal: opts.signal, headers: { accept: \"application/json\" } });\n return (await res.json()) as ByodList;\n }\n\n /** One overlay's metadata. */\n async get(id: string, opts: { signal?: AbortSignal } = {}): Promise<ByodOverlay> {\n const res = await this.read(`/byod/${encodeURIComponent(id)}`, {\n signal: opts.signal,\n headers: { accept: \"application/json\" },\n });\n return (await res.json()) as ByodOverlay;\n }\n\n /**\n * Upload an overlay.\n *\n * Accepts a `Blob`/`File`, a `GeoJSON` object, or raw bytes. The content type is chosen from\n * what was passed rather than asked for: a plain object is GeoJSON, and anything binary is\n * PMTiles unless the caller says otherwise. Getting that wrong is a 400 from the server, so\n * inferring it here removes the most likely way to fail this call.\n *\n * Never retried: an upload mints a new overlay id and carries no idempotency key, so a retry\n * after a lost response would spend a second of the ten slots on a duplicate.\n */\n async upload(\n body: Blob | ArrayBuffer | Uint8Array | Record<string, unknown>,\n opts: { name?: string; kind?: \"geojson\" | \"pmtiles\"; signal?: AbortSignal } = {},\n ): Promise<ByodOverlay> {\n let payload: BodyInit;\n let contentType: string | undefined;\n let name: string | undefined;\n\n if (typeof Blob !== \"undefined\" && body instanceof Blob) {\n // A File carries its own name, which is a better default than anything we could invent.\n const form = new FormData();\n form.append(\"file\", body);\n if (opts.name) form.append(\"name\", opts.name);\n payload = form;\n // Deliberately unset: the runtime has to add the multipart boundary, and setting\n // content-type by hand produces a request the server cannot parse.\n contentType = undefined;\n } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n payload = body as BodyInit;\n contentType = opts.kind === \"geojson\" ? \"application/geo+json\" : \"application/vnd.pmtiles\";\n name = opts.name;\n } else {\n payload = JSON.stringify(body);\n contentType = \"application/geo+json\";\n name = opts.name;\n }\n\n const res = await this.fetchImpl(this.url(\"/byod\", { name }), {\n method: \"POST\",\n signal: opts.signal,\n headers: contentType ? { \"content-type\": contentType } : undefined,\n body: payload,\n });\n if (!res.ok) throw await errorFrom(res, \"/byod\");\n return (await res.json()) as ByodOverlay;\n }\n\n /**\n * Delete an overlay.\n *\n * Never retried: a retry after a delete whose response was lost answers `404`, which would\n * report a failure for a delete that succeeded.\n */\n async delete(id: string, opts: { signal?: AbortSignal } = {}): Promise<void> {\n const path = `/byod/${encodeURIComponent(id)}`;\n const res = await this.fetchImpl(this.url(path), { method: \"DELETE\", signal: opts.signal });\n if (!res.ok) throw await errorFrom(res, path);\n }\n\n /** The stored bytes. GeoJSON parses; PMTiles is binary and belongs to MapLibre. */\n async data(id: string, opts: { signal?: AbortSignal } = {}): Promise<Response> {\n // The raw Response, not a parsed body: one of the two kinds is a binary archive that a caller\n // streams rather than reads, and guessing which they wanted would be worse than handing it over.\n return this.read(`/byod/${encodeURIComponent(id)}/data`, { signal: opts.signal });\n }\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/**\n * Canadian ski areas (`/ski/areas`, `/ski/areas/{id}`), their 3D lifts\n * (`/ski/areas/{id}/lifts-3d`) and their runs and lifts with stable ids\n * (`/ski/areas/{id}/features`), behind the Ski add-on.\n *\n * const { data } = await unmap.ski.areas({ province: \"BC\", activity: \"downhill\" });\n * const { data: area } = await unmap.ski.area(data[0].id);\n * const { data: { features } } = await unmap.ski.features(area.id);\n *\n * Every field is derived from OpenStreetMap, so every response is ODbL: show\n * `meta.attribution` (\"© OpenStreetMap contributors\") wherever you show the data. The numbers\n * are terrain information, not avalanche or safety advice.\n *\n * A key without the add-on gets `SkiError` 403 `addon_required` (or `dev_only` /\n * `usage_limit_reached` on the dev plan), with the SKU that unlocks it in `addon`.\n */\n\nexport type SkiActivity = \"downhill\" | \"nordic\";\n\n/** The eight compass directions the aspect histogram is binned into. */\nexport type SkiAspectDirection = \"n\" | \"ne\" | \"e\" | \"se\" | \"s\" | \"sw\" | \"w\" | \"nw\";\n\n/** One ski area, as the gateway returns it (`apps/gateway/src/ski.ts`). */\nexport interface SkiArea {\n /** Stable OSM-derived id: `osm-way-<n>`, `osm-relation-<n>` or `osm-node-<n>`. */\n id: string;\n name: string;\n /** Two-letter province or territory code, or \"\" when the area could not be placed. */\n province: string;\n /** The nearest town, from our reverse geocoder. */\n locality: string;\n /** Representative point, [lng, lat]. */\n location: [number, number];\n /** The lowest lift station, where the road and the car park are; null with no lifts or runs. */\n base: [number, number] | null;\n /** [west, south, east, north]. */\n bbox: [number, number, number, number];\n activities: SkiActivity[];\n status: string | null;\n websites: string[];\n wikidata_id: string | null;\n /** From the terrain release the build sampled; null when the area has no lifts or runs to sample. */\n elevation: { top_m: number; base_m: number; vertical_m: number } | null;\n runs: { count: number; downhill_km: number; nordic_km: number };\n lifts: { count: number };\n /** Share of downhill run length facing each way, from the terrain release the build sampled. */\n aspect: Record<SkiAspectDirection, number>;\n /**\n * Fastest drive from the nearest of the three closest census metropolitan areas of 100,000 or\n * more. Null when none is within the routing engine's matrix limit.\n */\n drive: { from: string; minutes: number } | null;\n source: string;\n licence: string;\n}\n\n/** The provenance every ski response carries. `data_version` is the ski release that answered. */\nexport interface SkiMeta {\n source?: string;\n source_updated_at?: string | null;\n source_version?: string;\n content_hash?: string | null;\n sources?: readonly {\n id: string;\n source: string;\n source_url: string;\n attribution: string;\n source_updated_at: string | null;\n source_version: string;\n content_hash: string | null;\n status: \"current\" | \"stale\" | \"unknown\";\n }[];\n unmap_updated_at: string;\n data_version?: string;\n status: \"current\" | \"stale\" | \"partial\" | \"unknown\";\n /** \"© OpenStreetMap contributors\". Display it with the data: the data is ODbL. */\n attribution: string;\n}\n\nexport interface SkiResponse<T> {\n data: T;\n meta: SkiMeta;\n}\n\nexport interface SkiAreasOptions {\n /** Two-letter province or territory code (`BC`, `QC`, ...). */\n province?: string;\n activity?: SkiActivity;\n /** [west, south, east, north] in degrees; matches on the area's representative point. */\n bbox?: [number, number, number, number];\n /** Name search, accent- and case-insensitive (\"mont sainte anne\" finds \"Mont-Sainte-Anne\"). */\n q?: string;\n signal?: AbortSignal;\n}\n\n/** A point on a lift in 3D: [lng, lat, metres above sea level]. */\nexport type SkiPoint3d = [number, number, number];\n\n/** One lift, ready to draw in 3D. */\nexport interface SkiLift3d {\n /** The OSM way, as `way-123`: the same id `features()` and the tiles give the lift. */\n id: string;\n /** Null when the lift has no name in OpenStreetMap. */\n name: string | null;\n /** The OSM `aerialway=*` value: `chair_lift`, `gondola`, `t-bar`, ... */\n aerialway: string;\n /** The two ends, on the ground. */\n stations: [SkiPoint3d, SkiPoint3d];\n /** Each at the top of its tower. */\n pylons: SkiPoint3d[];\n /** The cable line, sag included. */\n cable: SkiPoint3d[];\n}\n\n/**\n * The `data` of `GET /ski/areas/{id}/lifts-3d`: one area's lifts with heights. `meta.data_version`\n * is the ski release. A known area with no lifts has `lifts: []`.\n */\nexport interface SkiLifts3d {\n schema: \"unmap.ski-lifts-3d.v1\";\n area_id: string;\n /** The terrain release the heights were sampled from. Draw on the same one. */\n terrain_release: string;\n lifts: SkiLift3d[];\n}\n\n/** A [lng, lat] position in WGS84 degrees. */\nexport type SkiLngLat = [number, number];\n\n/** A run or lift's own OpenStreetMap geometry. An area run (an OSM polygon, such as a bowl) is a polygon. */\nexport type SkiFeatureGeometry =\n | { type: \"LineString\"; coordinates: SkiLngLat[] }\n | { type: \"MultiLineString\"; coordinates: SkiLngLat[][] }\n | { type: \"Polygon\"; coordinates: SkiLngLat[][] }\n | { type: \"MultiPolygon\"; coordinates: SkiLngLat[][][] };\n\n/**\n * One run or lift with a stable id. Geometry and identifiers only: nothing here says whether it\n * is open, groomed or safe. Every key is present; the other kind's fields are null.\n */\nexport interface SkiFeature {\n /**\n * The OSM element, `way-123` or `relation-456`. The same in every release while the element\n * exists, and the `id` property of the `runs` and `lifts` tile features. Compare it as a string;\n * `osm` carries its parts.\n */\n id: string;\n kind: \"run\" | \"lift\";\n name: string | null;\n /** Runs: OSM `piste:type` (`downhill`, `nordic`, `skitour`, `sled`). Null on a lift. */\n piste_type: string | null;\n /** Runs: OSM `piste:difficulty`, or `park` for a terrain park. Null when OSM has none, and on a lift. */\n difficulty: string | null;\n /** Lifts: OSM `aerialway` (`chair_lift`, `gondola`, ...). Null on a run. */\n aerialway: string | null;\n geometry: SkiFeatureGeometry;\n /** Horizontal length in metres along the line; for an area run, its greatest extent. */\n length_m: number;\n osm: { type: \"way\" | \"relation\"; id: number };\n source: string;\n licence: string;\n}\n\n/**\n * The `data` of `GET /ski/areas/{id}/features`: every run and lift of one area, lifts first, then\n * runs, each by id. `data_version` is the ski release. A known area with nothing mapped has\n * `features: []`.\n */\nexport interface SkiFeatures {\n schema: \"unmap.ski-features.v1\";\n area_id: string;\n data_version: string;\n features: SkiFeature[];\n}\n\nexport interface SkiOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n /** Retry policy for `429`, `502`, `503` and transport failures. `false` disables it. See `RetryPolicy` in `@unmap/core`. */\n retry?: RetryPolicy | false;\n}\n\n/**\n * A ski failure.\n *\n * Extends `UnmapError`. `code` is the gateway's: `addon_required`, `dev_only` or\n * `usage_limit_reached` on a 403 (with `addon`, the SKU that unlocks it), `not_found` for an\n * unknown id, `bad_request` for a filter the gateway refused, `service_unavailable` when no ski\n * release is published.\n */\nexport class SkiError extends UnmapError {\n constructor(\n status: number,\n message: string,\n code?: string,\n retryAfterSeconds?: number,\n /** The add-on SKU that unlocks the data, on a 403. */\n readonly addon?: string,\n ) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"SkiError\";\n }\n}\n\nasync function errorFrom(res: Response, path: string): Promise<SkiError> {\n const fallback = `ski ${res.status} on ${path}`;\n let body: { error?: unknown; code?: unknown; addon?: unknown } | undefined;\n try {\n body = (await res.json()) as typeof body;\n } catch {\n body = undefined;\n }\n const reason = typeof body?.error === \"string\" ? body.error : undefined;\n const code = typeof body?.code === \"string\" ? body.code : undefined;\n const addon = typeof body?.addon === \"string\" ? body.addon : undefined;\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n return new SkiError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter, addon);\n}\n\n/** Canadian ski areas. Needs the Ski add-on. */\nexport class Ski {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: SkiOptions) {\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry;\n }\n\n /** Every ski area, or those matching the filters, sorted by id. */\n areas(opts: SkiAreasOptions = {}): Promise<SkiResponse<SkiArea[]>> {\n return this.get(\n \"/ski/areas\",\n {\n province: opts.province || undefined,\n activity: opts.activity,\n bbox: opts.bbox?.join(\",\"),\n q: opts.q || undefined,\n },\n opts.signal,\n );\n }\n\n /** One ski area. Throws `SkiError` 404 `not_found` for an unknown id. */\n area(id: string, opts: { signal?: AbortSignal } = {}): Promise<SkiResponse<SkiArea>> {\n return this.get(`/ski/areas/${encodeURIComponent(id)}`, undefined, opts.signal);\n }\n\n /** One area's lifts in 3D: stations, pylons and cable with heights. Throws `SkiError` 404 `not_found` for an unknown id. */\n lifts3d(id: string, opts: { signal?: AbortSignal } = {}): Promise<SkiResponse<SkiLifts3d>> {\n return this.get(`/ski/areas/${encodeURIComponent(id)}/lifts-3d`, undefined, opts.signal);\n }\n\n /**\n * One area's runs and lifts, each with a stable OSM-derived id, its name, kind and geometry.\n * Throws `SkiError` 404 `not_found` for an unknown id.\n */\n features(id: string, opts: { signal?: AbortSignal } = {}): Promise<SkiResponse<SkiFeatures>> {\n return this.get(`/ski/areas/${encodeURIComponent(id)}/features`, undefined, opts.signal);\n }\n\n private async get<T>(path: string, values: Record<string, string | undefined> | undefined, signal?: AbortSignal): Promise<T> {\n const query = new URLSearchParams({ key: this.key });\n for (const [name, value] of Object.entries(values ?? {})) {\n if (value !== undefined) query.set(name, value);\n }\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${path}?${query}`,\n { signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) throw await errorFrom(res, path);\n return (await res.json()) as T;\n }\n}\n"],"mappings":"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","names":["DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","createMap","UnmapError","parseRetryAfter"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/roads.ts","../src/transit.ts","../src/coverage.ts","../src/geoip.ts","../src/timezone.ts","../src/catalog.ts","../src/ski.ts","../src/noise.ts"],"sourcesContent":["import {\n createMap,\n type Map as MaplibreMap,\n type MapProfile,\n type UnmapControlsOptions,\n type UnmapMapOptions,\n} from \"@unmap/maps\";\nimport { Geocoder, Places, type IdentifyOptions, type IdentifyResponse, type QueryOptions, type QueryResponse } from \"@unmap/geocoding\";\nimport { Feedback, Roads } from \"./roads\";\nimport { Transit } from \"./transit\";\nimport { Coverage } from \"./coverage\";\nimport { GeoIp } from \"./geoip\";\nimport { Timezone } from \"./timezone\";\nimport { Byod, Catalog } from \"./catalog\";\nimport { Ski } from \"./ski\";\nimport { Noise } from \"./noise\";\nimport { Router } from \"@unmap/routing\";\nimport { Elevation } from \"@unmap/elevation\";\nimport type { RetryPolicy } from \"@unmap/core\";\n\nexport * from \"@unmap/geocoding\";\nexport * from \"@unmap/routing\";\nexport * from \"@unmap/elevation\";\nexport * from \"./roads\";\nexport * from \"./transit\";\nexport * from \"./coverage\";\nexport * from \"./geoip\";\nexport * from \"./timezone\";\nexport * from \"./catalog\";\nexport * from \"./ski\";\nexport * from \"./noise\";\nexport { createMap, unmapStyle, DEMO_CITIES, STYLE_NAMES, maplibregl } from \"@unmap/maps\";\nexport type {\n Map,\n UnmapMapOptions,\n UnmapControlsOptions,\n DemoCity,\n StyleName,\n StyleInput,\n Projection,\n MapProfile,\n} from \"@unmap/maps\";\n\nexport interface UnmapOptions {\n key: string;\n gateway?: string;\n /** Override the fetch implementation (tests, non-global environments). */\n fetch?: typeof fetch;\n /**\n * Retry policy shared by every client on this instance. `false` disables it.\n *\n * On by default for `429`, `502`, `503` and transport failures. `Retry-After` is on a `429`\n * other than `billing_required`, and on a container `503`. A `502` has none. A wait longer\n * than `maxDelayMs` (10 s) is not slept through; the error carries `retryAfterSeconds`\n * instead, because a cold land or energy container asks for 60 and parking a request handler\n * for a minute is a worse surprise than an error. Feedback submissions are never retried:\n * they create a record and carry no idempotency key.\n */\n retry?: RetryPolicy | false;\n /** If provided, a map is mounted in this container. */\n container?: string | HTMLElement;\n style?: string;\n /** \"light\" or \"dark\", light by default. Applies to a bare style name; a `<style>-<mode>` string is passed through as written and `mode` does not change it. */\n mode?: string;\n /** Label language, e.g. \"fr\". Omit to follow the request's Accept-Language. */\n lang?: string;\n /** Compact `ut1.` theme code from unmap.dev/create. Wins over the path style on the gateway. */\n theme?: string;\n /** Catalog layer ids composed onto the style (`municipalities`, `survey.dls`, …). */\n overlays?: readonly string[];\n /** Composition preset. Not a style. Explicit overlays replace the profile list. */\n profile?: MapProfile;\n /** Draw the style on 3D terrain (`?terrain=on`). Any style, flat ones included. */\n terrain?: boolean;\n /** Vertical exaggeration for `terrain`, 0.1 to 5; the gateway's default is 1. */\n exaggeration?: number;\n /** `\"globe\"` (the default) or `\"mercator\"`. See `@unmap/maps` `UnmapMapOptions.projection`. */\n projection?: UnmapMapOptions[\"projection\"];\n center?: [number, number];\n zoom?: number;\n /** On-map controls. `true` (the default) adds zoom and compass buttons; `false` adds none. */\n controls?: boolean | UnmapControlsOptions;\n}\n\n/**\n * One Stripe-style key, the whole platform. With a `container` you get a rendered map.\n * `geocoder`, `router`, `elevation`, `timezone`, and `geoip` are always available.\n *\n * const map = new Unmap({ key: \"um_live_...\", container: \"#map\", center: [-75.69, 45.42] });\n */\nexport class Unmap {\n readonly geocoder: Geocoder;\n readonly router: Router;\n readonly elevation: Elevation;\n /** IANA timezone and the UTC offset at an instant. One call, anywhere. */\n readonly timezone: Timezone;\n /** City-level location for an IP address, worldwide. Omit the address for the caller's own. */\n readonly geoip: GeoIp;\n /** Canadian points of interest. Same corpus as the geocoder, in the Place shape. */\n readonly places: Places;\n /** Closures, incidents, construction and road conditions. */\n readonly roads: Roads;\n /** Report wrong data. Never metered. */\n readonly feedback: Feedback;\n /** Scheduled public-transit agencies, stops, routes and departures. */\n readonly transit: Transit;\n /**\n * Where each capability actually works. Unauthenticated and unmetered, so checking costs\n * nothing: read it before promising a user an answer, because transit routing covers only the feeds\n * marked routing-ready and road events cover two provinces.\n */\n readonly coverage: Coverage;\n /** The named Canadian layer catalog: what exists, its licence, and what it does not cover. */\n readonly layers: Catalog;\n /** Your own GeoJSON and PMTiles overlays, stored against this key. */\n readonly byod: Byod;\n /** Canadian ski areas and their 3D lifts. Needs the Ski add-on; the data is ODbL (OpenStreetMap). */\n readonly ski: Ski;\n /** Modelled urban noise for six Canadian cities. Needs the Urban Noise add-on; the data is ODbL. */\n readonly noise: Noise;\n readonly data: { query(opts: QueryOptions): Promise<QueryResponse> };\n readonly map?: MaplibreMap;\n\n constructor(opts: UnmapOptions) {\n this.geocoder = new Geocoder(opts);\n this.router = new Router(opts);\n this.elevation = new Elevation(opts);\n this.timezone = new Timezone(opts);\n this.geoip = new GeoIp(opts);\n this.places = new Places(opts);\n this.roads = new Roads(opts);\n this.feedback = new Feedback(opts);\n this.transit = new Transit(opts);\n this.coverage = new Coverage(opts);\n this.layers = new Catalog(opts);\n this.byod = new Byod(opts);\n this.ski = new Ski(opts);\n this.noise = new Noise(opts);\n this.data = { query: (queryOpts) => this.geocoder.queryLayer(queryOpts) };\n if (opts.container) {\n this.map = createMap({ ...opts, container: opts.container } as UnmapMapOptions);\n }\n }\n\n /** Which catalog features contain a point. See `Geocoder.identify`. */\n identify(opts: IdentifyOptions): Promise<IdentifyResponse> {\n return this.geocoder.identify(opts);\n }\n}\n\n// The shared error type and retry policy, re-exported so a caller who installed only this\n// package can `catch (e) { if (e instanceof UnmapError) }` without adding @unmap/core.\nexport { UnmapError, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/**\n * Typed clients for road events (`/incidents`, `/closures`, `/road-conditions`) and for data\n * correction (`POST /feedback`).\n *\n * These live in `@unmap/sdk` rather than in packages of their own. A twelfth and thirteenth npm\n * package would each need an entry in the changesets fixed group and a trusted publisher\n * configured on npmjs.com, which is founder work, and neither has a framework wrapper the way\n * geocoding and routing do. If a customer ever wants roads without the rest of the SDK, that is\n * the moment to split it out.\n */\n\nexport type RoadEventType =\n | \"closure\"\n | \"incident\"\n | \"construction\"\n | \"weather\"\n | \"restriction\"\n | \"ferry\"\n | \"hazard\"\n | \"special_event\"\n | \"unknown\";\n\nexport type RoadCondition =\n | \"dry\"\n | \"wet\"\n | \"snow\"\n | \"ice\"\n | \"packed_snow\"\n | \"reduced_visibility\"\n | \"travel_not_recommended\"\n | \"closed\"\n | \"unknown\";\n\nexport type RoadSeverity = \"minor\" | \"major\" | \"unknown\";\n\nexport interface AffectedRoad {\n name: string;\n direction: \"north\" | \"south\" | \"east\" | \"west\" | \"both\" | \"unknown\";\n /** True only when the publisher says closed. Never inferred from the description. */\n closed: boolean;\n from?: string;\n to?: string;\n condition: RoadCondition;\n}\n\nexport interface RoadEvent {\n /** `um:road:<jurisdiction>:<publisher id>`. */\n id: string;\n /** The publisher's own id, so a report can be taken back to them. */\n source_id: string;\n /** ISO 3166-2, e.g. `CA-BC`. */\n jurisdiction: string;\n type: RoadEventType;\n /** The publisher's own subtype strings, untranslated. */\n subtypes: string[];\n status: \"active\" | \"planned\" | \"archived\";\n severity: RoadSeverity;\n /**\n * The publisher's description, verbatim.\n *\n * Believe this over `type` when they disagree: our categories are a lossy projection of someone\n * else's taxonomy, and the sentence a driver needs is in the prose.\n */\n description: string;\n headline?: string;\n /** The worst surface condition across this event's roads. */\n condition: RoadCondition;\n roads: AffectedRoad[];\n /** GeoJSON Point or LineString, WGS84. */\n geometry: { type: string; coordinates: unknown } | null;\n starts_at: string | null;\n ends_at: string | null;\n source_updated_at: string | null;\n unmap_updated_at: string;\n source: string;\n}\n\nexport interface RoadEventsResponse {\n data: RoadEvent[];\n meta: {\n status: \"current\" | \"stale\" | \"partial\" | \"unknown\";\n attribution: string;\n unmap_updated_at: string;\n /**\n * Jurisdictions that could not be reached, each with a reason.\n *\n * **Check this.** On `/closures` an empty `data` reads as \"nothing is shut\" to someone\n * deciding whether to drive, and that is a very different answer from \"we could not reach\n * British Columbia\".\n */\n unavailable?: { jurisdiction: string; reason: string }[];\n /**\n * Feeds served from unmap's five-minute poll rather than fetched for this request (Alberta),\n * each with its age. A feed older than `max_age_seconds` is in `unavailable` instead.\n */\n polled?: {\n jurisdiction: string;\n feed: \"events\" | \"conditions\";\n polled_at: string;\n age_seconds: number;\n max_age_seconds: number;\n }[];\n };\n}\n\nexport interface RoadEventQuery {\n /** Comma-separated ISO 3166-2 codes, or an array. An uncovered code is a 400, not an empty list. */\n jurisdiction?: string | readonly string[];\n bbox?: [number, number, number, number];\n near?: [number, number];\n /** Metres from `near`. Default 25000, max 500000. */\n radius?: number;\n type?: RoadEventType | readonly RoadEventType[];\n severity?: RoadSeverity | readonly RoadSeverity[];\n status?: \"active\" | \"planned\" | \"archived\";\n /** ISO 8601. Only events the publisher changed at or after this time. */\n updated_since?: string;\n /** 1 to 500, default 100. Applied AFTER the endpoint's own narrowing. */\n limit?: number;\n signal?: AbortSignal;\n}\n\nexport interface RoadsOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n /**\n * Retry policy for `429`, `502`, `503` and transport failures. `false` disables it.\n *\n * On by default. `Retry-After` is on a `429` other than `billing_required`, and on a container\n * `503`. A `502` has none: routing and `/elevation` both use\n * `upstream_error`. With no header the client backs off itself. A wait longer than\n * `maxDelayMs` (10 s) is not slept through; the error carries `retryAfterSeconds` instead.\n * See `RetryPolicy` in `@unmap/core`.\n */\n retry?: RetryPolicy | false;\n}\n\n/**\n * A road events failure.\n *\n * Extends `UnmapError`, so `catch (e) { if (e instanceof UnmapError) }` catches this alongside\n * every other unmap client's errors. `status` and `code` carry the HTTP status and the\n * gateway's error code, and `retryAfterSeconds` comes from the `Retry-After` header.\n */\nexport class RoadsError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"RoadsError\";\n }\n}\n\nfunction list(value: string | readonly string[] | undefined): string | undefined {\n if (value === undefined) return undefined;\n return Array.isArray(value) ? value.join(\",\") : String(value);\n}\n\nasync function errorFrom(res: Response, path: string, name: string): Promise<RoadsError> {\n const fallback = `${name} ${res.status} on ${path}`;\n let body: { error?: unknown; code?: unknown } | undefined;\n try {\n body = (await res.json()) as { error?: unknown; code?: unknown };\n } catch {\n body = undefined;\n }\n const reason = typeof body?.error === \"string\" ? body.error : undefined;\n const code = typeof body?.code === \"string\" ? body.code : undefined;\n const retryAfter = parseRetryAfter(res.headers?.get(\"retry-after\") ?? null) ?? undefined;\n return new RoadsError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter);\n}\n\n/**\n * Canadian road events.\n *\n * const { data, meta } = await unmap.roads.closures({ jurisdiction: \"CA-BC\" });\n *\n * A 503 means every requested jurisdiction was unreachable; the body still carries\n * `meta.unavailable`, so the thrown error is the exception rather than the information. Prefer\n * reading `meta.unavailable` on a 200 over treating an empty list as good news.\n */\nexport class Roads {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: RoadsOptions) {\n this.retry = opts.retry;\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n /** Every current road event. */\n incidents(query: RoadEventQuery = {}): Promise<RoadEventsResponse> {\n return this.request(\"/incidents\", query);\n }\n\n /** The subset where a road is actually shut. Narrowed before `limit` is applied. */\n closures(query: RoadEventQuery = {}): Promise<RoadEventsResponse> {\n return this.request(\"/closures\", query);\n }\n\n /**\n * The subset carrying a surface condition.\n *\n * Thin for British Columbia by design: its feed reports lane availability rather than the\n * surface, so unmap answers `unknown` instead of inferring a dry road from open lanes.\n */\n conditions(query: RoadEventQuery = {}): Promise<RoadEventsResponse> {\n return this.request(\"/road-conditions\", query);\n }\n\n private async request(path: string, query: RoadEventQuery): Promise<RoadEventsResponse> {\n const q = new URLSearchParams({ key: this.key });\n const params: Record<string, string | undefined> = {\n jurisdiction: list(query.jurisdiction),\n bbox: query.bbox?.join(\",\"),\n near: query.near?.join(\",\"),\n radius: query.radius != null ? String(query.radius) : undefined,\n type: list(query.type),\n severity: list(query.severity),\n status: query.status,\n updated_since: query.updated_since,\n limit: query.limit != null ? String(query.limit) : undefined,\n };\n for (const [k, v] of Object.entries(params)) if (v != null && v !== \"\") q.set(k, v);\n\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${path}?${q}`,\n { signal: query.signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) throw await errorFrom(res, path, \"roads\");\n return (await res.json()) as RoadEventsResponse;\n }\n}\n\n// ---------------------------------------------------------------------------------------------\n// Feedback\n// ---------------------------------------------------------------------------------------------\n\nexport type FeedbackProduct =\n | \"tiles\"\n | \"terrain\"\n | \"names\"\n | \"geocoding\"\n | \"places\"\n | \"routing\"\n | \"layers\"\n | \"transit\"\n | \"roads\"\n | \"ev\";\n\nexport type FeedbackCategory =\n | \"incorrect_location\"\n | \"incorrect_address\"\n | \"missing_place\"\n | \"wrong_place_name\"\n | \"wrong_category\"\n | \"bad_route\"\n | \"missing_road_closure\"\n | \"incorrect_road_condition\"\n | \"incorrect_transit_result\"\n | \"other\";\n\nexport interface FeedbackReport {\n product: FeedbackProduct;\n category: FeedbackCategory;\n /** Up to 2000 characters, stored exactly as written. One of this or `resultId` is required. */\n description?: string;\n /** The unmap id of the result being reported. */\n resultId?: string;\n /**\n * The request being reported.\n *\n * **Only send this with the reporter's explicit agreement.** The gateway refuses it without\n * `include_context: true`, which this sets whenever `context` is present, and that flag is\n * meant to record a person's choice rather than a client default. unmap redacts query strings\n * from its own logs; a client that attached them here would undo that.\n */\n context?: unknown;\n signal?: AbortSignal;\n}\n\nexport interface FeedbackReceipt {\n /** Quote this in a follow-up. */\n id: string;\n status: string;\n created_at: string;\n /** Whether a request context was actually kept. `false` when none was offered. */\n context_stored: boolean;\n}\n\n/**\n * Report wrong data.\n *\n * await unmap.feedback.submit({\n * product: \"geocoding\",\n * category: \"incorrect_location\",\n * description: \"17 Ave SW returns a street in Edmonton.\",\n * });\n *\n * Authenticated like everything else and **never metered**: telling us our data is wrong does not\n * cost a call. At most 200 reports per account per day.\n */\nexport class Feedback {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(opts: RoadsOptions) {\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n submit(report: FeedbackReport): Promise<FeedbackReceipt> {\n return this.post(\"/feedback\", {\n product: report.product,\n category: report.category,\n ...(report.description ? { description: report.description } : {}),\n ...(report.resultId ? { result_id: report.resultId } : {}),\n ...(report.context !== undefined ? { include_context: true, context: report.context } : {}),\n }, report.signal);\n }\n\n /** A report about one place. `product` and `result_id` come from the path. */\n aboutPlace(\n placeId: string,\n report: Omit<FeedbackReport, \"product\" | \"resultId\">,\n ): Promise<FeedbackReceipt> {\n return this.post(`/places/${encodeURIComponent(placeId)}/feedback`, {\n category: report.category,\n ...(report.description ? { description: report.description } : {}),\n ...(report.context !== undefined ? { include_context: true, context: report.context } : {}),\n }, report.signal);\n }\n\n private async post(path: string, body: unknown, signal?: AbortSignal): Promise<FeedbackReceipt> {\n // Deliberately NOT routed through `fetchWithRetry`, unlike every read on this client.\n //\n // A feedback submission creates a record and carries no idempotency key, so a retry after a\n // timeout or a 503 risks filing the same report twice. Retrying a read costs a round trip;\n // retrying this costs the quality programme a duplicate, and the caller is the only one who\n // knows whether their report already landed.\n const res = await this.fetchImpl(`${this.base}${path}`, {\n method: \"POST\",\n signal,\n headers: { \"content-type\": \"application/json\", authorization: `Bearer ${this.key}` },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw await errorFrom(res, path, \"feedback\");\n return (await res.json()) as FeedbackReceipt;\n }\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\nexport type TransitMetaStatus = \"current\" | \"stale\" | \"partial\" | \"unknown\";\n\nexport interface TransitMeta {\n /** Static GTFS is scheduled data. Realtime will use a different value and updated_at. */\n schedule_type: \"scheduled\";\n source?: string;\n source_updated_at?: string | null;\n source_version?: string;\n content_hash?: string | null;\n sources?: readonly {\n id: string;\n source: string;\n source_url: string;\n attribution: string;\n source_updated_at: string | null;\n source_version: string;\n content_hash: string | null;\n status: \"current\" | \"stale\" | \"unknown\";\n }[];\n unmap_updated_at: string;\n data_version?: string;\n status: TransitMetaStatus;\n attribution: string;\n}\n\nexport interface TransitResponse<T> {\n data: T;\n meta: TransitMeta;\n}\n\nexport interface TransitAgency {\n id: string;\n feed: string;\n name: string;\n url?: string;\n timezone: string;\n lang?: string;\n phone?: string;\n fare_url?: string;\n}\n\nexport interface TransitStop {\n id: string;\n feed: string;\n name: string;\n coordinates: [number, number];\n code?: string;\n description?: string;\n zone_id?: string;\n parent_station_id?: string;\n wheelchair_boarding?: 0 | 1 | 2;\n route_ids: string[];\n /** Metres from the requested point. Present only on nearbyStops. */\n distance?: number;\n}\n\nexport interface TransitRoute {\n id: string;\n feed: string;\n agency_id: string;\n short_name?: string;\n long_name?: string;\n description?: string;\n route_type: number;\n colour?: string;\n text_colour?: string;\n url?: string;\n}\n\nexport interface TransitDeparture {\n stop_id: string;\n route_id: string;\n trip_id: string;\n headsign?: string;\n arrival: string;\n departure: string;\n stop_sequence: number;\n /** 2 = phone agency; 3 = coordinate with driver. Omitted for ordinary pickup. */\n pickup_type?: 2 | 3;\n schedule_type: \"scheduled\";\n}\n\nexport interface NearbyStopsOptions {\n /** Metres. Default 1000, range 50 to 10000. */\n radius?: number;\n /** Default 20, maximum 100. */\n limit?: number;\n signal?: AbortSignal;\n}\n\nexport interface DepartureOptions {\n /** An instant. Date values serialize to ISO; strings must include Z or an offset. Default now. */\n at?: Date | string;\n /** Minutes after at. Default 60, maximum 240. */\n window?: number;\n /** Default 50, maximum 200. */\n limit?: number;\n signal?: AbortSignal;\n}\n\nexport interface TransitOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n /**\n * Retry policy for `429`, `502`, `503` and transport failures. `false` disables it.\n *\n * On by default. `Retry-After` is on a `429` other than `billing_required`, and on a container\n * `503`. A `502` has none: routing and `/elevation` both use\n * `upstream_error`. With no header the client backs off itself. A wait longer than\n * `maxDelayMs` (10 s) is not slept through; the error carries `retryAfterSeconds` instead.\n * See `RetryPolicy` in `@unmap/core`.\n */\n retry?: RetryPolicy | false;\n}\n\n/**\n * A transit failure.\n *\n * Extends `UnmapError`, so `catch (e) { if (e instanceof UnmapError) }` catches this alongside\n * every other unmap client's errors. `status` and `code` carry the HTTP status and the\n * gateway's error code, and `retryAfterSeconds` comes from the `Retry-After` header.\n */\nexport class TransitError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"TransitError\";\n }\n}\n\n/**\n * The gateway's error body, as a `TransitError`.\n *\n * Named rather than inline since 2026-09-20: `fetchWithRetry` needs to inspect the failure to\n * decide whether to wait, and the throw needs to build the same error. Two copies of this would\n * be two chances for the retry predicate and the thrown error to disagree.\n */\nasync function errorFrom(res: Response, path: string): Promise<TransitError> {\n let body: { error?: unknown; code?: unknown } = {};\n try {\n body = (await res.json()) as typeof body;\n } catch {\n /* use the bounded fallback */\n }\n const message = typeof body.error === \"string\" ? body.error : `transit ${res.status} on ${path}`;\n const code = typeof body.code === \"string\" ? body.code : undefined;\n const retryAfter = parseRetryAfter(res.headers?.get(\"retry-after\") ?? null) ?? undefined;\n return new TransitError(res.status, message, code, retryAfter);\n}\n\n/** Static Canadian GTFS data. Scheduled departures ship before realtime. */\nexport class Transit {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: TransitOptions) {\n this.retry = opts.retry;\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n agencies(signal?: AbortSignal): Promise<TransitResponse<TransitAgency[]>> {\n return this.get(\"/transit/agencies\", undefined, signal);\n }\n\n nearbyStops(near: [number, number], options: NearbyStopsOptions = {}): Promise<TransitResponse<TransitStop[]>> {\n return this.get(\"/transit/stops\", {\n near: near.join(\",\"),\n radius: options.radius,\n limit: options.limit,\n }, options.signal);\n }\n\n getStop(id: string, signal?: AbortSignal): Promise<TransitResponse<TransitStop>> {\n return this.get(`/transit/stops/${encodeURIComponent(id)}`, undefined, signal);\n }\n\n getRoute(id: string, signal?: AbortSignal): Promise<TransitResponse<TransitRoute>> {\n return this.get(`/transit/routes/${encodeURIComponent(id)}`, undefined, signal);\n }\n\n departures(id: string, options: DepartureOptions = {}): Promise<TransitResponse<TransitDeparture[]>> {\n return this.get(`/transit/stops/${encodeURIComponent(id)}/departures`, {\n at: options.at instanceof Date ? options.at.toISOString() : options.at,\n window: options.window,\n limit: options.limit,\n }, options.signal);\n }\n\n private async get<T>(\n path: string,\n values?: Record<string, string | number | undefined>,\n signal?: AbortSignal,\n ): Promise<TransitResponse<T>> {\n const query = new URLSearchParams({ key: this.key });\n for (const [name, value] of Object.entries(values ?? {})) {\n if (value !== undefined) query.set(name, String(value));\n }\n const response = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${path}?${query}`,\n { signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!response.ok) throw await errorFrom(response, path);\n return await response.json() as TransitResponse<T>;\n }\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/**\n * `GET /coverage.json` — where each capability actually works, and where it does not.\n *\n * The document is generated from the gateway's source registry rather than written by hand, so\n * it cannot claim a capability with no registered source behind it. It is also the only place\n * that answers \"is transit routing live in this city\" before you promise a user an answer:\n * routing covers only the feeds marked routing-ready, road events cover two provinces, and Places coverage is OpenStreetMap's,\n * which is thin outside cities.\n *\n * Unauthenticated and unmetered on the gateway, which is why this client takes no key: a\n * developer deciding whether to use unmap has no key yet, and a customer checking whether a gap\n * has closed should not pay a call to be told it has not.\n */\n\n/** The one path this client calls. A literal so the gateway's SDK-parity audit can see it. */\nconst COVERAGE_PATH = \"/coverage.json\";\n\n/** Freshness of a capability or a source. `unknown` means no cadence was declared. */\nexport type CoverageFreshness = \"current\" | \"stale\" | \"unknown\";\n\n/** How a source's freshness is judged, mirrored from the gateway's `FreshnessPolicy`. */\nexport interface CoverageFreshnessPolicy {\n /** ISO 8601 duration, `static`, or `unknown`. */\n interval: string;\n basis: string;\n /** When `freshness_status` stops being true, or null when staleness cannot be asked. */\n stale_after: string | null;\n}\n\n/** Where a source's data came from and when unmap last produced it. */\nexport interface CoverageProvenance {\n source_updated_at: string | null;\n unmap_updated_at: string | null;\n build_id: string | null;\n version: string;\n content_hash: string | null;\n freshness_policy: CoverageFreshnessPolicy;\n freshness_status: CoverageFreshness;\n /** Counts the producing job measured. Omitted when it measured none. */\n measurements?: Record<string, number>;\n}\n\nexport interface CoverageSource {\n id: string;\n provider: string;\n /** Bilingual, like every human-readable field in this document. */\n dataset_name: { en: string; fr: string };\n canonical_url: string;\n licence: { en: string; fr: string };\n licence_url: string;\n attribution: string;\n jurisdiction: string[];\n /** ISO 8601 duration, e.g. `P1D`. */\n refresh_interval: string;\n last_checked_at: string;\n /** Upstream's edition date, when unmap last produced the data, which build did, and how fresh that is. */\n provenance: CoverageProvenance;\n coverage: string;\n known_gaps: { en: string; fr: string };\n quality_status: string;\n /** A note on the source, in English, present only when there is one. */\n notes?: string;\n}\n\nexport interface CoverageCapability {\n /** `tiles`, `geocoding`, `routing`, `places`, `transit`, `roads`, and so on. */\n id: string;\n display_name: { en: string; fr: string };\n /** `planned` is published as honestly as `live`: a capability absent from the page reads as \"not our problem\". */\n availability: \"live\" | \"planned\";\n /** The endpoints that serve it. Empty while planned. */\n endpoints: string[];\n coverage: { en: string; fr: string };\n /** Written to be accurate rather than flattering. Read this before promising coverage to a user. */\n known_gaps: { en: string; fr: string };\n /** ISO 3166-2 subdivisions, or `CA` for national. */\n jurisdictions: string[];\n freshness_status: CoverageFreshness;\n sources: CoverageSource[];\n}\n\nexport interface CoverageDocument {\n generated_at: string;\n /** How to read the dates below, in both official languages. */\n freshness_basis: { en: string; fr: string };\n /** Schema tag of the manifests behind `provenance`, so a consumer can pin what it parses. */\n provenance_schema: string;\n capabilities: CoverageCapability[];\n /** Every jurisdiction code any source claims. */\n jurisdictions: string[];\n}\n\nexport class CoverageError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"CoverageError\";\n }\n}\n\nexport interface CoverageOptions {\n gateway?: string;\n /** Override the fetch implementation (tests, non-global environments). */\n fetch?: typeof fetch;\n retry?: RetryPolicy | false;\n}\n\n/** Where unmap works. Takes no key: the endpoint is unauthenticated and unmetered. */\nexport class Coverage {\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: CoverageOptions = {}) {\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry;\n }\n\n /** The whole document. Cached an hour at the edge, so polling it is cheap. */\n async get(opts: { signal?: AbortSignal } = {}): Promise<CoverageDocument> {\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${COVERAGE_PATH}`,\n { signal: opts.signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) {\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n throw new CoverageError(res.status, `coverage ${res.status}`, undefined, retryAfter);\n }\n return (await res.json()) as CoverageDocument;\n }\n\n /**\n * One capability by id, or `undefined` when the document does not describe it.\n *\n * const transit = await unmap.coverage.capability(\"transit\");\n * if (!transit?.jurisdictions.includes(\"CA-ON\")) return \"not available here yet\";\n */\n async capability(id: string, opts: { signal?: AbortSignal } = {}): Promise<CoverageCapability | undefined> {\n const doc = await this.get(opts);\n return doc.capabilities.find((c) => c.id === id);\n }\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/** The one path this client calls. A literal so the gateway's SDK-parity audit can see it. */\nconst GEOIP_PATH = \"/geoip\";\n\n/** DB-IP City Lite is CC BY 4.0: show `text`, linked to `url`, wherever the result is used. */\nexport interface GeoIpAttribution {\n text: string;\n url: string;\n}\n\n/** An address the database lists. Accuracy is city level at best, often only the country. */\nexport interface GeoIpFound {\n /** The canonical form of the address that was looked up. */\n ip: string;\n found: true;\n /** ISO 3166-1 alpha-2. */\n country_code: string;\n country: string;\n /** `null` when the database has no region for the address. */\n region: string | null;\n /** `null` when the database has no city for the address. */\n city: string | null;\n /** `[longitude, latitude]`, the same order as the rest of the SDK. */\n coordinates: [number, number];\n accuracy: \"city\";\n release: string;\n attribution: GeoIpAttribution;\n}\n\n/**\n * A public address the database does not list, such as unassigned space. Private, loopback,\n * link-local and other reserved addresses are not this: like a malformed address, they throw\n * `GeoIpError` with status `400` and code `invalid_ip`.\n */\nexport interface GeoIpNotFound {\n ip: string;\n found: false;\n release: string;\n attribution: GeoIpAttribution;\n}\n\nexport type GeoIpResult = GeoIpFound | GeoIpNotFound;\n\nexport interface GeoIpLocateOptions {\n signal?: AbortSignal;\n}\n\nexport class GeoIpError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"GeoIpError\";\n }\n}\n\nexport interface GeoIpOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n retry?: RetryPolicy | false;\n}\n\n/**\n * City-level location for an IP address, worldwide.\n *\n * const here = await unmap.geoip.locate(); // the caller's own address\n * const google = await unmap.geoip.locate(\"8.8.8.8\");\n *\n * Omit the address to look up the machine making the request. From a browser that is the\n * visitor; from a server it is the server. Show `attribution` wherever you use the result.\n * A malformed, private or reserved address throws `GeoIpError` (`400`, `invalid_ip`); an\n * unavailable dataset throws one with status `503`.\n */\nexport class GeoIp {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: GeoIpOptions) {\n this.retry = opts.retry;\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n async locate(ip?: string, opts: GeoIpLocateOptions = {}): Promise<GeoIpResult> {\n const q = new URLSearchParams({ key: this.key });\n if (ip !== undefined) q.set(\"ip\", ip);\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${GEOIP_PATH}?${q}`,\n { signal: opts.signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) throw await errorFrom(res);\n return (await res.json()) as GeoIpResult;\n }\n}\n\nasync function errorFrom(res: Response): Promise<GeoIpError> {\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n let message = res.statusText || `geoip ${res.status}`;\n let code: string | undefined;\n try {\n const body = (await res.json()) as { error?: unknown; code?: unknown };\n if (typeof body.error === \"string\" && body.error) message = body.error;\n if (typeof body.code === \"string\") code = body.code;\n } catch {\n /* a non-JSON failure still carries the status */\n }\n return new GeoIpError(res.status, message, code, retryAfter);\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/** The one path this client calls. A literal so the gateway's SDK-parity audit can see it. */\nconst TIMEZONE_PATH = \"/timezone\";\n\nexport interface TimezoneResult {\n timezone: string | null;\n offset: string | null;\n offsetSeconds: number | null;\n dst: boolean | null;\n}\n\nexport interface TimezoneLookupOptions {\n /** Instant the offset is evaluated at. Omit it for the current time. */\n at?: Date;\n signal?: AbortSignal;\n}\n\nexport class TimezoneError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"TimezoneError\";\n }\n}\n\nexport interface TimezoneOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n retry?: RetryPolicy | false;\n}\n\n/**\n * IANA timezone for a coordinate.\n *\n * const zone = await unmap.timezone.lookup([-114.0719, 51.0447]);\n *\n * The coordinate is `[longitude, latitude]`, the same order as the rest of the SDK.\n * `at` selects the offset. It does not change which zone the coordinate falls in.\n */\nexport class Timezone {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: TimezoneOptions) {\n this.retry = opts.retry;\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n async lookup(coordinate: [number, number], opts: TimezoneLookupOptions = {}): Promise<TimezoneResult> {\n const [lon, lat] = coordinate;\n const q = new URLSearchParams({ key: this.key, lon: String(lon), lat: String(lat) });\n if (opts.at) q.set(\"at\", opts.at.toISOString());\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${TIMEZONE_PATH}?${q}`,\n { signal: opts.signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) throw await errorFrom(res);\n return (await res.json()) as TimezoneResult;\n }\n}\n\nasync function errorFrom(res: Response): Promise<TimezoneError> {\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n let message = res.statusText || `timezone ${res.status}`;\n let code: string | undefined;\n try {\n const body = (await res.json()) as { error?: unknown; code?: unknown };\n if (typeof body.error === \"string\" && body.error) message = body.error;\n if (typeof body.code === \"string\") code = body.code;\n } catch {\n /* a non-JSON failure still carries the status */\n }\n return new TimezoneError(res.status, message, code, retryAfter);\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/**\n * Clients for the named Canadian layer catalog (`/layers`) and for tenant overlays (`/byod`).\n *\n * Both existed as endpoints with no client until the SDK parity audit\n * (`apps/gateway/test/sdk-parity.test.ts`) went looking and found them. They were the two entries\n * in that audit's `KNOWN_GAPS`; this closes both.\n *\n * ## Why these are the two that mattered\n *\n * `@unmap/maps` already consumes layer and overlay **tiles** through a style document, which is\n * why the gap was easy to miss: maps work. What had no client was everything *around* the tiles.\n * Nothing could ask what layers exist, what a layer's licence says, or which add-on it needs, and\n * nothing could upload an overlay without hand-rolling a multipart request.\n */\n\n// ---------------------------------------------------------------------------------------------\n// The named layer catalog\n// ---------------------------------------------------------------------------------------------\n\nexport interface LayerArchive {\n id: string;\n minzoom: number;\n maxzoom: number;\n}\n\n/**\n * The capability a gated layer's tiles need. These are capability ids (`legalLand`), not the\n * `addon-*` SKUs a customer buys (`addon-legal-land`).\n */\nexport type LayerCapability = \"legalLand\" | \"energy\" | \"agriculture\" | \"remote\" | \"mining\" | \"ski\" | \"urbanNoise\";\n\n/**\n * One catalog layer.\n *\n * `licence`, `attribution` and `known_gaps` are the reason to read this rather than hard-code a\n * layer id: they are the governance record, and a caller displaying a layer has to display its\n * attribution.\n */\nexport interface Layer {\n id: string;\n display_name: { en: string; fr: string };\n namespace?: \"core\" | \"survey\" | \"energy\" | \"agriculture\" | \"remote\" | \"mining\" | \"ski\" | \"noise\";\n /** ISO 3166 codes where coverage is VERIFIED. `coverage` carries the prose, including gaps. */\n jurisdiction?: string[];\n source?: string;\n source_url?: string;\n licence: string;\n attribution: string;\n coverage: string;\n resolution?: string;\n updated_at?: string;\n source_updated_at?: string;\n ingestion_version?: string;\n schema_version?: string;\n freshness_policy?: string;\n known_gaps?: string;\n /**\n * The capability this layer's tiles need, or null when it is core. Reading the catalog is never\n * gated; the tiles are, and answer `403` `addon_required` on a key without the add-on.\n */\n addon?: LayerCapability | null;\n archives?: LayerArchive[];\n /** Vector source-layer name inside the archive. */\n source_layer?: string;\n /** Every source-layer, present only when the archive stacks more than one (survey grids). */\n source_layers?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------\n// Tenant overlays (BYOD)\n// ---------------------------------------------------------------------------------------------\n\nexport interface ByodOverlay {\n /** `ovl_` plus twelve hex characters. */\n id: string;\n name: string;\n kind: \"geojson\" | \"pmtiles\";\n bytes: number;\n createdAt: string;\n url: string;\n /** The `byod:<id>` token to pass to `overlays`. This is the field you actually want. */\n overlay: string;\n}\n\nexport interface ByodList {\n overlays: ByodOverlay[];\n /** The caps, from the server rather than from a constant here that could drift. */\n limits: { maxFiles: number; maxBytes: number; kinds: string[] };\n}\n\nexport interface CatalogOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n /**\n * Retry policy for reads (`429`, `502`, `503` and transport failures). `false` disables it.\n * Uploads and deletes are never retried. See `RetryPolicy` in `@unmap/core`.\n */\n retry?: RetryPolicy | false;\n}\n\n/**\n * A layer-catalog or overlay failure.\n *\n * Extends `UnmapError`, so `catch (e) { if (e instanceof UnmapError) }` catches it alongside every\n * other unmap client's errors. `code` is the gateway's: `not_found` for an unknown layer or\n * overlay id, `bad_request` for an upload the server refused (wrong type, over the size cap, or\n * over the overlay count).\n */\nexport class CatalogError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"CatalogError\";\n }\n}\n\nasync function errorFrom(res: Response, path: string): Promise<CatalogError> {\n const fallback = `catalog ${res.status} on ${path}`;\n let body: { error?: unknown; code?: unknown } | undefined;\n try {\n body = (await res.json()) as { error?: unknown; code?: unknown };\n } catch {\n body = undefined;\n }\n const reason = typeof body?.error === \"string\" ? body.error : undefined;\n const code = typeof body?.code === \"string\" ? body.code : undefined;\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n return new CatalogError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter);\n}\n\n/** The pieces both clients share: the key, the base URL, fetch, and the read path. */\nabstract class KeyedClient {\n protected readonly key: string;\n protected readonly base: string;\n protected readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: CatalogOptions) {\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry;\n }\n\n protected url(path: string, values?: Record<string, string | undefined>): string {\n const query = new URLSearchParams({ key: this.key });\n for (const [name, value] of Object.entries(values ?? {})) {\n if (value !== undefined) query.set(name, value);\n }\n return `${this.base}${path}?${query}`;\n }\n\n /** A GET, retried per the policy. Throws `CatalogError` on a non-2xx. */\n protected async read(path: string, init: RequestInit, values?: Record<string, string | undefined>): Promise<Response> {\n const res = await fetchWithRetry(this.fetchImpl, this.url(path, values), init, this.retry);\n if (!res.ok) throw await errorFrom(res, path);\n return res;\n }\n}\n\n/**\n * The named Canadian layer catalog.\n *\n * const layers = await unmap.layers.list({ jurisdiction: \"CA-AB\" });\n * const parcels = await unmap.layers.get(\"survey.dls\");\n * console.log(parcels.attribution, parcels.known_gaps);\n *\n * A retired or misspelled id is a plain `404` `not_found`, the same as one that never existed:\n * the gateway keeps no aliases for renamed layers.\n */\nexport class Catalog extends KeyedClient {\n /**\n * Every catalog layer, or only those with verified coverage in `jurisdiction`.\n *\n * A country code matches its own subdivisions (`CA` includes `CA-AB`). An unknown code is an\n * empty list, not an error: the catalog has nothing there.\n */\n async list(opts: { jurisdiction?: string; signal?: AbortSignal } = {}): Promise<Layer[]> {\n const res = await this.read(\n \"/layers\",\n { signal: opts.signal, headers: { accept: \"application/json\" } },\n { jurisdiction: opts.jurisdiction },\n );\n const body = (await res.json()) as { layers?: Layer[] } | Layer[];\n return Array.isArray(body) ? body : (body.layers ?? []);\n }\n\n /** One layer by id. Throws `CatalogError` 404 `not_found` when it does not exist. */\n async get(id: string, opts: { signal?: AbortSignal } = {}): Promise<Layer> {\n const res = await this.read(`/layers/${encodeURIComponent(id)}`, {\n signal: opts.signal,\n headers: { accept: \"application/json\" },\n });\n return (await res.json()) as Layer;\n }\n}\n\n/**\n * Tenant overlays: your own GeoJSON or PMTiles, stored against your key. `upload`, `list` and\n * `delete` need a server key; a browser key only reads an overlay by id (403 `server_key_required`).\n *\n * const { overlay } = await unmap.byod.upload(file, { name: \"Service areas\" });\n * const map = new Unmap({ key, container: \"#map\", overlays: [overlay] });\n *\n * The `overlay` field on the result is the `byod:<id>` token: that is what `overlays` takes, not\n * the bare id, and returning the whole record rather than just the id is what makes that hard to\n * get wrong.\n *\n * Hard caps are the server's (10 overlays, 5 MiB each) and are reported by `list()` rather than\n * duplicated here, so a change on the server cannot leave a stale constant in the client.\n */\nexport class Byod extends KeyedClient {\n /** Overlays on this tenant, plus the server's current limits. */\n async list(opts: { signal?: AbortSignal } = {}): Promise<ByodList> {\n const res = await this.read(\"/byod\", { signal: opts.signal, headers: { accept: \"application/json\" } });\n return (await res.json()) as ByodList;\n }\n\n /** One overlay's metadata. */\n async get(id: string, opts: { signal?: AbortSignal } = {}): Promise<ByodOverlay> {\n const res = await this.read(`/byod/${encodeURIComponent(id)}`, {\n signal: opts.signal,\n headers: { accept: \"application/json\" },\n });\n return (await res.json()) as ByodOverlay;\n }\n\n /**\n * Upload an overlay.\n *\n * Accepts a `Blob`/`File`, a `GeoJSON` object, or raw bytes. The content type is chosen from\n * what was passed rather than asked for: a plain object is GeoJSON, and anything binary is\n * PMTiles unless the caller says otherwise. Getting that wrong is a 400 from the server, so\n * inferring it here removes the most likely way to fail this call.\n *\n * Never retried: an upload mints a new overlay id and carries no idempotency key, so a retry\n * after a lost response would spend a second of the ten slots on a duplicate.\n */\n async upload(\n body: Blob | ArrayBuffer | Uint8Array | Record<string, unknown>,\n opts: { name?: string; kind?: \"geojson\" | \"pmtiles\"; signal?: AbortSignal } = {},\n ): Promise<ByodOverlay> {\n let payload: BodyInit;\n let contentType: string | undefined;\n let name: string | undefined;\n\n if (typeof Blob !== \"undefined\" && body instanceof Blob) {\n // A File carries its own name, which is a better default than anything we could invent.\n const form = new FormData();\n form.append(\"file\", body);\n if (opts.name) form.append(\"name\", opts.name);\n payload = form;\n // Deliberately unset: the runtime has to add the multipart boundary, and setting\n // content-type by hand produces a request the server cannot parse.\n contentType = undefined;\n } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n payload = body as BodyInit;\n contentType = opts.kind === \"geojson\" ? \"application/geo+json\" : \"application/vnd.pmtiles\";\n name = opts.name;\n } else {\n payload = JSON.stringify(body);\n contentType = \"application/geo+json\";\n name = opts.name;\n }\n\n const res = await this.fetchImpl(this.url(\"/byod\", { name }), {\n method: \"POST\",\n signal: opts.signal,\n headers: contentType ? { \"content-type\": contentType } : undefined,\n body: payload,\n });\n if (!res.ok) throw await errorFrom(res, \"/byod\");\n return (await res.json()) as ByodOverlay;\n }\n\n /**\n * Delete an overlay.\n *\n * Never retried: a retry after a delete whose response was lost answers `404`, which would\n * report a failure for a delete that succeeded.\n */\n async delete(id: string, opts: { signal?: AbortSignal } = {}): Promise<void> {\n const path = `/byod/${encodeURIComponent(id)}`;\n const res = await this.fetchImpl(this.url(path), { method: \"DELETE\", signal: opts.signal });\n if (!res.ok) throw await errorFrom(res, path);\n }\n\n /** The stored bytes. GeoJSON parses; PMTiles is binary and belongs to MapLibre. */\n async data(id: string, opts: { signal?: AbortSignal } = {}): Promise<Response> {\n // The raw Response, not a parsed body: one of the two kinds is a binary archive that a caller\n // streams rather than reads, and guessing which they wanted would be worse than handing it over.\n return this.read(`/byod/${encodeURIComponent(id)}/data`, { signal: opts.signal });\n }\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/**\n * Canadian ski areas (`/ski/areas`, `/ski/areas/{id}`), their 3D lifts\n * (`/ski/areas/{id}/lifts-3d`) and their runs and lifts with stable ids\n * (`/ski/areas/{id}/features`), behind the Ski add-on.\n *\n * const { data } = await unmap.ski.areas({ province: \"BC\", activity: \"downhill\" });\n * const { data: area } = await unmap.ski.area(data[0].id);\n * const { data: { features } } = await unmap.ski.features(area.id);\n *\n * Every field is derived from OpenStreetMap, so every response is ODbL: show\n * `meta.attribution` (\"© OpenStreetMap contributors\") wherever you show the data. The numbers\n * are terrain information, not avalanche or safety advice.\n *\n * A key without the add-on gets `SkiError` 403 `addon_required` (or `dev_only` /\n * `usage_limit_reached` on the dev plan), with the SKU that unlocks it in `addon`.\n */\n\nexport type SkiActivity = \"downhill\" | \"nordic\";\n\n/** The eight compass directions the aspect histogram is binned into. */\nexport type SkiAspectDirection = \"n\" | \"ne\" | \"e\" | \"se\" | \"s\" | \"sw\" | \"w\" | \"nw\";\n\n/** One ski area, as the gateway returns it (`apps/gateway/src/ski.ts`). */\nexport interface SkiArea {\n /** Stable OSM-derived id: `osm-way-<n>`, `osm-relation-<n>` or `osm-node-<n>`. */\n id: string;\n name: string;\n /** Two-letter province or territory code, or \"\" when the area could not be placed. */\n province: string;\n /** The nearest town, from our reverse geocoder. */\n locality: string;\n /** Representative point, [lng, lat]. */\n location: [number, number];\n /** The lowest lift station, where the road and the car park are; null with no lifts or runs. */\n base: [number, number] | null;\n /** [west, south, east, north]. */\n bbox: [number, number, number, number];\n activities: SkiActivity[];\n status: string | null;\n websites: string[];\n wikidata_id: string | null;\n /** From the terrain release the build sampled; null when the area has no lifts or runs to sample. */\n elevation: { top_m: number; base_m: number; vertical_m: number } | null;\n runs: { count: number; downhill_km: number; nordic_km: number };\n lifts: { count: number };\n /** Share of downhill run length facing each way, from the terrain release the build sampled. */\n aspect: Record<SkiAspectDirection, number>;\n /**\n * Fastest drive from the nearest of the three closest census metropolitan areas of 100,000 or\n * more. Null when none is within the routing engine's matrix limit.\n */\n drive: { from: string; minutes: number } | null;\n source: string;\n licence: string;\n}\n\n/** The provenance every ski response carries. `data_version` is the ski release that answered. */\nexport interface SkiMeta {\n source?: string;\n source_updated_at?: string | null;\n source_version?: string;\n content_hash?: string | null;\n sources?: readonly {\n id: string;\n source: string;\n source_url: string;\n attribution: string;\n source_updated_at: string | null;\n source_version: string;\n content_hash: string | null;\n status: \"current\" | \"stale\" | \"unknown\";\n }[];\n unmap_updated_at: string;\n data_version?: string;\n status: \"current\" | \"stale\" | \"partial\" | \"unknown\";\n /** \"© OpenStreetMap contributors\". Display it with the data: the data is ODbL. */\n attribution: string;\n}\n\nexport interface SkiResponse<T> {\n data: T;\n meta: SkiMeta;\n}\n\nexport interface SkiAreasOptions {\n /** Two-letter province or territory code (`BC`, `QC`, ...). */\n province?: string;\n activity?: SkiActivity;\n /** [west, south, east, north] in degrees; matches on the area's representative point. */\n bbox?: [number, number, number, number];\n /** Name search, accent- and case-insensitive (\"mont sainte anne\" finds \"Mont-Sainte-Anne\"). */\n q?: string;\n signal?: AbortSignal;\n}\n\n/** A point on a lift in 3D: [lng, lat, metres above sea level]. */\nexport type SkiPoint3d = [number, number, number];\n\n/** One lift, ready to draw in 3D. */\nexport interface SkiLift3d {\n /** The OSM way, as `way-123`: the same id `features()` and the tiles give the lift. */\n id: string;\n /** Null when the lift has no name in OpenStreetMap. */\n name: string | null;\n /** The OSM `aerialway=*` value: `chair_lift`, `gondola`, `t-bar`, ... */\n aerialway: string;\n /** The two ends, on the ground. */\n stations: [SkiPoint3d, SkiPoint3d];\n /** Each at the top of its tower. */\n pylons: SkiPoint3d[];\n /** The cable line, sag included. */\n cable: SkiPoint3d[];\n}\n\n/**\n * The `data` of `GET /ski/areas/{id}/lifts-3d`: one area's lifts with heights. `meta.data_version`\n * is the ski release. A known area with no lifts has `lifts: []`.\n */\nexport interface SkiLifts3d {\n schema: \"unmap.ski-lifts-3d.v1\";\n area_id: string;\n /** The terrain release the heights were sampled from. Draw on the same one. */\n terrain_release: string;\n lifts: SkiLift3d[];\n}\n\n/** A [lng, lat] position in WGS84 degrees. */\nexport type SkiLngLat = [number, number];\n\n/** A run or lift's own OpenStreetMap geometry. An area run (an OSM polygon, such as a bowl) is a polygon. */\nexport type SkiFeatureGeometry =\n | { type: \"LineString\"; coordinates: SkiLngLat[] }\n | { type: \"MultiLineString\"; coordinates: SkiLngLat[][] }\n | { type: \"Polygon\"; coordinates: SkiLngLat[][] }\n | { type: \"MultiPolygon\"; coordinates: SkiLngLat[][][] };\n\n/**\n * One run or lift with a stable id. Geometry and identifiers only: nothing here says whether it\n * is open, groomed or safe. Every key is present; the other kind's fields are null.\n */\nexport interface SkiFeature {\n /**\n * The OSM element, `way-123` or `relation-456`. The same in every release while the element\n * exists, and the `id` property of the `runs` and `lifts` tile features. Compare it as a string;\n * `osm` carries its parts.\n */\n id: string;\n kind: \"run\" | \"lift\";\n name: string | null;\n /** Runs: OSM `piste:type` (`downhill`, `nordic`, `skitour`, `sled`). Null on a lift. */\n piste_type: string | null;\n /** Runs: OSM `piste:difficulty`, or `park` for a terrain park. Null when OSM has none, and on a lift. */\n difficulty: string | null;\n /** Lifts: OSM `aerialway` (`chair_lift`, `gondola`, ...). Null on a run. */\n aerialway: string | null;\n geometry: SkiFeatureGeometry;\n /** Horizontal length in metres along the line; for an area run, its greatest extent. */\n length_m: number;\n osm: { type: \"way\" | \"relation\"; id: number };\n source: string;\n licence: string;\n}\n\n/**\n * The `data` of `GET /ski/areas/{id}/features`: every run and lift of one area, lifts first, then\n * runs, each by id. `data_version` is the ski release. A known area with nothing mapped has\n * `features: []`.\n */\nexport interface SkiFeatures {\n schema: \"unmap.ski-features.v1\";\n area_id: string;\n data_version: string;\n features: SkiFeature[];\n}\n\nexport interface SkiOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n /** Retry policy for `429`, `502`, `503` and transport failures. `false` disables it. See `RetryPolicy` in `@unmap/core`. */\n retry?: RetryPolicy | false;\n}\n\n/**\n * A ski failure.\n *\n * Extends `UnmapError`. `code` is the gateway's: `addon_required`, `dev_only` or\n * `usage_limit_reached` on a 403 (with `addon`, the SKU that unlocks it), `not_found` for an\n * unknown id, `bad_request` for a filter the gateway refused, `service_unavailable` when no ski\n * release is published.\n */\nexport class SkiError extends UnmapError {\n constructor(\n status: number,\n message: string,\n code?: string,\n retryAfterSeconds?: number,\n /** The add-on SKU that unlocks the data, on a 403. */\n readonly addon?: string,\n ) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"SkiError\";\n }\n}\n\nasync function errorFrom(res: Response, path: string): Promise<SkiError> {\n const fallback = `ski ${res.status} on ${path}`;\n let body: { error?: unknown; code?: unknown; addon?: unknown } | undefined;\n try {\n body = (await res.json()) as typeof body;\n } catch {\n body = undefined;\n }\n const reason = typeof body?.error === \"string\" ? body.error : undefined;\n const code = typeof body?.code === \"string\" ? body.code : undefined;\n const addon = typeof body?.addon === \"string\" ? body.addon : undefined;\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n return new SkiError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter, addon);\n}\n\n/** Canadian ski areas. Needs the Ski add-on. */\nexport class Ski {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: SkiOptions) {\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry;\n }\n\n /** Every ski area, or those matching the filters, sorted by id. */\n areas(opts: SkiAreasOptions = {}): Promise<SkiResponse<SkiArea[]>> {\n return this.get(\n \"/ski/areas\",\n {\n province: opts.province || undefined,\n activity: opts.activity,\n bbox: opts.bbox?.join(\",\"),\n q: opts.q || undefined,\n },\n opts.signal,\n );\n }\n\n /** One ski area. Throws `SkiError` 404 `not_found` for an unknown id. */\n area(id: string, opts: { signal?: AbortSignal } = {}): Promise<SkiResponse<SkiArea>> {\n return this.get(`/ski/areas/${encodeURIComponent(id)}`, undefined, opts.signal);\n }\n\n /** One area's lifts in 3D: stations, pylons and cable with heights. Throws `SkiError` 404 `not_found` for an unknown id. */\n lifts3d(id: string, opts: { signal?: AbortSignal } = {}): Promise<SkiResponse<SkiLifts3d>> {\n return this.get(`/ski/areas/${encodeURIComponent(id)}/lifts-3d`, undefined, opts.signal);\n }\n\n /**\n * One area's runs and lifts, each with a stable OSM-derived id, its name, kind and geometry.\n * Throws `SkiError` 404 `not_found` for an unknown id.\n */\n features(id: string, opts: { signal?: AbortSignal } = {}): Promise<SkiResponse<SkiFeatures>> {\n return this.get(`/ski/areas/${encodeURIComponent(id)}/features`, undefined, opts.signal);\n }\n\n private async get<T>(path: string, values: Record<string, string | undefined> | undefined, signal?: AbortSignal): Promise<T> {\n const query = new URLSearchParams({ key: this.key });\n for (const [name, value] of Object.entries(values ?? {})) {\n if (value !== undefined) query.set(name, value);\n }\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${path}?${query}`,\n { signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) throw await errorFrom(res, path);\n return (await res.json()) as T;\n }\n}\n","import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter, type RetryPolicy } from \"@unmap/core\";\n\n/**\n * Modelled urban noise (`/noise/point`), behind the Urban Noise add-on.\n *\n * const { data, meta } = await unmap.noise.point({ lat: 51.054, lng: -114.0884 });\n * data.lden; // 49.3 dB, day-evening-night\n * data.label; // \"Quiet\"\n *\n * Six cities: Calgary, Edmonton, Vancouver, Toronto, Montréal and Québec City. A point outside\n * them throws `NoiseError` 422 `outside_coverage`, with the covered cities in `cities`.\n *\n * The levels are computed from OpenStreetMap, so the data is ODbL: show `meta.attribution`\n * (\"© OpenStreetMap contributors; noise model: PickYourPlace\") wherever you show it. They are\n * modelled, not measured: screening data, not a compliance assessment.\n *\n * A key without the add-on gets `NoiseError` 403 `addon_required` (or `dev_only` /\n * `usage_limit_reached` on the dev plan), with the SKU that unlocks it in `addon`.\n */\n\nexport type NoiseLabel = \"Quiet\" | \"Moderate\" | \"Noisy\" | \"Very noisy\" | \"Severe\";\nexport type NoiseSource = \"road\" | \"rail\" | \"aircraft\" | \"commercial\";\n\n/** One H3 hexagon's modelled noise, as the gateway returns it (`apps/gateway/src/noise.ts`). */\nexport interface NoisePoint {\n /** H3 index of the hexagon. */\n h3: string;\n /** H3 resolution: 11, about 25 m across. */\n resolution: number;\n /** The covered city: `calgary`, `edmonton`, `vancouver`, `toronto`, `montreal` or `quebec-city`. */\n city: string;\n /** Day-evening-night level, dB (EU Lden). Null where no modelled source is in range: never 0 dB. */\n lden: number | null;\n /** Night level, 23:00 to 07:00, dB. */\n lnight: number | null;\n /** 0 to 100, anchored to the WHO 2018 Environmental Noise Guidelines; 100 is quiet. */\n score: number | null;\n /** 100 minus the score. */\n risk: number | null;\n /** 80 and up Quiet, 60 Moderate, 40 Noisy, 20 Very noisy, below 20 Severe. */\n label: NoiseLabel | null;\n /** Lden above 53 dB (day) and Lnight above 45 dB (night), the WHO 2018 road-traffic levels. */\n exceeds_who: { day: boolean | null; night: boolean | null };\n dominant_source: NoiseSource | null;\n /** Each source's level at the hexagon, dB; null where that source is not modelled in range. */\n sources_db: Record<NoiseSource, number | null>;\n /** Present only when `lden` is null: \"no modelled source within range\". */\n note?: string;\n}\n\n/** The provenance every noise response carries. `data_version` is the release that answered. */\nexport interface NoiseMeta {\n source?: string;\n source_updated_at?: string | null;\n source_version?: string;\n content_hash?: string | null;\n unmap_updated_at: string;\n data_version?: string;\n status: \"current\" | \"stale\" | \"partial\" | \"unknown\";\n /** \"© OpenStreetMap contributors; noise model: PickYourPlace\". Display it with the data. */\n attribution: string;\n /** \"ODbL-1.0\". */\n licence: string;\n}\n\nexport interface NoiseResponse<T> {\n data: T;\n meta: NoiseMeta;\n}\n\n/** A city Urban Noise covers, as a 422 `outside_coverage` lists them. */\nexport interface NoiseCity {\n slug: string;\n name: { en: string; fr: string };\n /** [west, south, east, north]. */\n bbox: [number, number, number, number];\n}\n\nexport interface NoisePointOptions {\n lat: number;\n lng: number;\n signal?: AbortSignal;\n}\n\nexport interface NoiseOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n /** Retry policy for `429`, `502`, `503` and transport failures. `false` disables it. See `RetryPolicy` in `@unmap/core`. */\n retry?: RetryPolicy | false;\n}\n\n/**\n * A noise failure.\n *\n * Extends `UnmapError`. `code` is the gateway's: `addon_required`, `dev_only` or\n * `usage_limit_reached` on a 403 (with `addon`, the SKU that unlocks it), `outside_coverage` on a\n * 422 (with `cities`, what is covered), `bad_request` for a coordinate the gateway refused,\n * `service_unavailable` while no release is served.\n */\nexport class NoiseError extends UnmapError {\n constructor(\n status: number,\n message: string,\n code?: string,\n retryAfterSeconds?: number,\n /** The add-on SKU that unlocks the data, on a 403. */\n readonly addon?: string,\n /** The covered cities, on a 422 `outside_coverage`. */\n readonly cities?: NoiseCity[],\n ) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"NoiseError\";\n }\n}\n\nasync function errorFrom(res: Response, path: string): Promise<NoiseError> {\n const fallback = `noise ${res.status} on ${path}`;\n let body: { error?: unknown; code?: unknown; addon?: unknown; cities?: unknown } | undefined;\n try {\n body = (await res.json()) as typeof body;\n } catch {\n body = undefined;\n }\n const reason = typeof body?.error === \"string\" ? body.error : undefined;\n const code = typeof body?.code === \"string\" ? body.code : undefined;\n const addon = typeof body?.addon === \"string\" ? body.addon : undefined;\n const cities = Array.isArray(body?.cities) ? (body.cities as NoiseCity[]) : undefined;\n const retryAfter = parseRetryAfter(res.headers?.get?.(\"retry-after\") ?? null) ?? undefined;\n return new NoiseError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter, addon, cities);\n}\n\n/** Modelled urban noise for six Canadian cities. Needs the Urban Noise add-on. */\nexport class Noise {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly retry: RetryPolicy | false | undefined;\n\n constructor(opts: NoiseOptions) {\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry;\n }\n\n /**\n * The modelled noise of the hexagon (about 25 m) at a point. Throws `NoiseError` 422\n * `outside_coverage` outside the six cities, and 400 for a coordinate outside WGS84, which is\n * checked here before any request is made.\n */\n async point(opts: NoisePointOptions): Promise<NoiseResponse<NoisePoint>> {\n const { lat, lng } = opts;\n if (!Number.isFinite(lat) || !Number.isFinite(lng) || Math.abs(lat) > 90 || Math.abs(lng) > 180) {\n throw new NoiseError(400, \"lat and lng must be WGS84 degrees\", \"bad_request\");\n }\n const path = \"/noise/point\";\n const query = new URLSearchParams({ key: this.key, lat: String(lat), lng: String(lng) });\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${path}?${query}`,\n { signal: opts.signal, headers: { accept: \"application/json\" } },\n this.retry,\n );\n if (!res.ok) throw await errorFrom(res, path);\n return (await res.json()) as NoiseResponse<NoisePoint>;\n }\n}\n"],"mappings":"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","names":["DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","DEFAULT_GATEWAY","UnmapError","fetchWithRetry","parseRetryAfter","errorFrom","createMap","UnmapError","parseRetryAfter"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unmap/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "One key, the whole unmap platform: map + geocoder + router",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"maplibre-gl": "^6.9.0",
|
|
43
|
-
"@unmap/core": "0.
|
|
44
|
-
"@unmap/elevation": "0.
|
|
45
|
-
"@unmap/geocoding": "0.
|
|
46
|
-
"@unmap/maps": "0.
|
|
47
|
-
"@unmap/routing": "0.
|
|
43
|
+
"@unmap/core": "0.29.0",
|
|
44
|
+
"@unmap/elevation": "0.29.0",
|
|
45
|
+
"@unmap/geocoding": "0.29.0",
|
|
46
|
+
"@unmap/maps": "0.29.0",
|
|
47
|
+
"@unmap/routing": "0.29.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"tsup": "^8.3.0",
|