@unmap/sdk 0.13.0 → 0.14.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 +44 -14
- package/dist/index.d.ts +365 -7
- package/dist/index.js +256 -13
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# @unmap/sdk
|
|
2
2
|
|
|
3
|
-
One key, the whole [unmap](https://unmap.dev) platform: a MapLibre GL
|
|
4
|
-
|
|
3
|
+
One key, the whole [unmap](https://unmap.dev) platform: a worldwide MapLibre GL basemap, Canadian
|
|
4
|
+
geocoding and routing, and the rest of the API behind a single `um_live_` or `um_test_` key.
|
|
5
|
+
The basemap supports 41 upstream Protomaps label-language tags; geocoding and routing remain
|
|
6
|
+
Canada-scoped. Canadian-built, privacy-first.
|
|
5
7
|
|
|
6
8
|
```bash
|
|
7
9
|
npm i @unmap/sdk
|
|
@@ -14,7 +16,8 @@ import 'maplibre-gl/dist/maplibre-gl.css'
|
|
|
14
16
|
const map = new Unmap({ key: 'um_live_...', container: '#map', center: [-114.07, 51.05] })
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
That is the whole Hello World: a
|
|
19
|
+
That is the whole Hello World: a worldwide basemap initially centered on Calgary in the `#map`
|
|
20
|
+
element. Without a `container`
|
|
18
21
|
you still get the geocoder and router:
|
|
19
22
|
|
|
20
23
|
```ts
|
|
@@ -26,20 +29,46 @@ await unmap.router.route([-114.07, 51.05], [-113.99, 51.05], { mode: 'bicycle' }
|
|
|
26
29
|
await unmap.router.isochrone([-114.07, 51.05], { minutes: [10, 20] })
|
|
27
30
|
```
|
|
28
31
|
|
|
32
|
+
The SDK also carries the departure-time transit contract:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
await unmap.router.route(from, to, { mode: 'transit', departAt: new Date() })
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Production answers TTC trips. The type requires `departAt`; there is no `arriveBy`, transit
|
|
39
|
+
matrix, or realtime claim. Other static feeds stay schedule-only.
|
|
40
|
+
|
|
41
|
+
Static transit data has its own release gate and client:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const stops = await unmap.transit.nearbyStops([-79.3832, 43.6532], { radius: 800 })
|
|
45
|
+
const departures = stops.data[0]
|
|
46
|
+
? await unmap.transit.departures(stops.data[0].id, { at: new Date(), window: 60 })
|
|
47
|
+
: undefined
|
|
48
|
+
|
|
49
|
+
await unmap.transit.agencies()
|
|
50
|
+
await unmap.transit.getStop('um:transit:stop:ttc:1234')
|
|
51
|
+
await unmap.transit.getRoute('um:transit:route:ttc:1')
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Those methods return `{ data, meta }`, where `meta` carries source, freshness and attribution.
|
|
55
|
+
They return a retryable `503` until a versioned GTFS release is enabled. Departures are scheduled,
|
|
56
|
+
including service after midnight and `calendar_dates.txt` exceptions; they are not realtime.
|
|
57
|
+
|
|
29
58
|
## Without a build step
|
|
30
59
|
|
|
31
60
|
The same SDK loads from a script tag, with MapLibre already inside it:
|
|
32
61
|
|
|
33
62
|
```html
|
|
34
|
-
<link rel="stylesheet" href="https://cdn.unmap.dev/sdk/0.
|
|
63
|
+
<link rel="stylesheet" href="https://cdn.unmap.dev/sdk/0.14.0/unmap.css">
|
|
35
64
|
<div id="map" style="height: 400px"></div>
|
|
36
|
-
<script src="https://cdn.unmap.dev/sdk/0.
|
|
65
|
+
<script src="https://cdn.unmap.dev/sdk/0.14.0/unmap.js"></script>
|
|
37
66
|
<script>
|
|
38
67
|
unmap.createMap({ key: 'um_live_...', container: '#map', center: [-114.07, 51.05] })
|
|
39
68
|
</script>
|
|
40
69
|
```
|
|
41
70
|
|
|
42
|
-
Everything above is on the `unmap` global: `unmap.Unmap`, `unmap.Geocoder`, `unmap.Router`, `unmap.maplibregl`. Pin the exact version in production; `/sdk/0.
|
|
71
|
+
Everything above is on the `unmap` global: `unmap.Unmap`, `unmap.Geocoder`, `unmap.Router`, `unmap.maplibregl`. Pin the exact version in production; `/sdk/0.14/` follows patches and `/sdk/latest/` follows everything.
|
|
43
72
|
|
|
44
73
|
## Options
|
|
45
74
|
|
|
@@ -51,7 +80,7 @@ Everything above is on the `unmap` global: `unmap.Unmap`, `unmap.Geocoder`, `unm
|
|
|
51
80
|
| `style` | `string` | `base` (default), `muted`, `outdoor`, `blueprint`, `blush`, `orchid`, `canopy`, `lagoon`, `tropic`, `sunset`, `bold`, `pastel`, or a full style URL. |
|
|
52
81
|
| `flavor` | `string` | Older spelling of `style`: a style name, read only when `style` is omitted. |
|
|
53
82
|
| `mode` | `string` | `light` or `dark`. Selects the mode of the named style. |
|
|
54
|
-
| `lang` | `string` |
|
|
83
|
+
| `lang` | `string` | Basemap label language. Supports the 41 Protomaps language tags (including `ja`, `ko`, `zh-Hans`, and `zh-Hant`); omit to follow the browser's `Accept-Language`. |
|
|
55
84
|
| `theme` | `string` | A theme code (`u…` or `ut1.`) from [unmap.dev/create](https://unmap.dev/create). |
|
|
56
85
|
| `center` | `[lng, lat]` | Initial centre. |
|
|
57
86
|
| `zoom` | `number` | Initial zoom. |
|
|
@@ -59,8 +88,9 @@ Everything above is on the `unmap` global: `unmap.Unmap`, `unmap.Geocoder`, `unm
|
|
|
59
88
|
| `controls` | `boolean \| { position }` | Zoom and compass buttons. `true` by default; `false` for a bare canvas. |
|
|
60
89
|
|
|
61
90
|
The instance exposes `.map` (MapLibre GL `Map`, only with a `container`), `.geocoder`
|
|
62
|
-
(`@unmap/geocoding`),
|
|
63
|
-
from `@unmap/maps` is re-exported here, including
|
|
91
|
+
(`@unmap/geocoding`), `.router` (`@unmap/routing`), `.transit`, `.places`, `.roads`, `.feedback`
|
|
92
|
+
and `.elevation`. Everything from the packages and `@unmap/maps` is re-exported here, including
|
|
93
|
+
`maplibregl` for markers and popups.
|
|
64
94
|
|
|
65
95
|
## Stylesheet
|
|
66
96
|
|
|
@@ -76,12 +106,12 @@ reliably resolve inside a bundler's module graph. Bundler users make a one-time
|
|
|
76
106
|
call after copying `maplibre-gl-worker.mjs` and `maplibre-gl-shared.mjs` into their own `public/`.
|
|
77
107
|
Skip it and the map never finishes loading, with no error thrown and no failed request — see
|
|
78
108
|
[@unmap/maps](https://www.npmjs.com/package/@unmap/maps#web-worker) for the script and the call, or
|
|
79
|
-
[the quickstart](https://unmap.dev/docs/quickstart#if-the-map-never-finishes-loading).
|
|
109
|
+
[the quickstart](https://unmap.dev/docs/start/quickstart#if-the-map-never-finishes-loading).
|
|
80
110
|
|
|
81
111
|
## Docs
|
|
82
112
|
|
|
83
|
-
- [Quickstart](https://unmap.dev/docs/quickstart) and [Overview](https://unmap.dev/docs/overview)
|
|
84
|
-
- [Tiles & Styles](https://unmap.dev/docs/
|
|
85
|
-
- [Errors](https://unmap.dev/docs/errors) and [Plans & Limits](https://unmap.dev/docs/plans
|
|
113
|
+
- [Quickstart](https://unmap.dev/docs/start/quickstart) and [Overview](https://unmap.dev/docs/start/overview)
|
|
114
|
+
- [Tiles & Styles](https://unmap.dev/docs/apis/maps), [Geocoding API](https://unmap.dev/docs/apis/geocoding), [Routing API](https://unmap.dev/docs/apis/routing), [Transit API](https://unmap.dev/docs/apis/transit)
|
|
115
|
+
- [Errors](https://unmap.dev/docs/reference/errors) and [Plans & Limits](https://unmap.dev/docs/reference/plans)
|
|
86
116
|
|
|
87
|
-
Documentation en français : [unmap.dev/fr/docs](https://unmap.dev/fr/docs/quickstart).
|
|
117
|
+
Documentation en français : [unmap.dev/fr/docs](https://unmap.dev/fr/docs/start/quickstart).
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Map, MapProfile, UnmapMapOptions, UnmapControlsOptions } from '@unmap/m
|
|
|
2
2
|
export { DEMO_CITIES, DemoCity, Map, MapProfile, Projection, STYLE_NAMES, StyleInput, StyleName, UnmapControlsOptions, UnmapMapOptions, createMap, maplibregl, unmapStyle } from '@unmap/maps';
|
|
3
3
|
import { Geocoder, Places, QueryOptions, QueryResponse, IdentifyOptions, IdentifyResponse } from '@unmap/geocoding';
|
|
4
4
|
export * from '@unmap/geocoding';
|
|
5
|
+
import { RetryPolicy, UnmapError } from '@unmap/core';
|
|
6
|
+
export { RetryPolicy, UnmapError, parseRetryAfter } from '@unmap/core';
|
|
5
7
|
import { Router } from '@unmap/routing';
|
|
6
8
|
export * from '@unmap/routing';
|
|
7
9
|
import { Elevation } from '@unmap/elevation';
|
|
@@ -102,11 +104,26 @@ interface RoadsOptions {
|
|
|
102
104
|
key: string;
|
|
103
105
|
gateway?: string;
|
|
104
106
|
fetch?: typeof fetch;
|
|
107
|
+
/**
|
|
108
|
+
* Retry policy for `429`, `502`, `503` and transport failures. `false` disables it.
|
|
109
|
+
*
|
|
110
|
+
* On by default. `Retry-After` is on a `429` other than `billing_required`, and on a container
|
|
111
|
+
* `503`. A `502` has none: routing uses `routing_failed`, and `/elevation` uses
|
|
112
|
+
* `upstream_error`. With no header the client backs off itself. A wait longer than
|
|
113
|
+
* `maxDelayMs` (10 s) is not slept through; the error carries `retryAfterSeconds` instead.
|
|
114
|
+
* See `RetryPolicy` in `@unmap/core`.
|
|
115
|
+
*/
|
|
116
|
+
retry?: RetryPolicy | false;
|
|
105
117
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
/**
|
|
119
|
+
* A road events failure.
|
|
120
|
+
*
|
|
121
|
+
* Extends `UnmapError`, so `catch (e) { if (e instanceof UnmapError) }` catches this alongside
|
|
122
|
+
* every other unmap client's errors. `instanceof RoadsError` keeps working, and `status` and `code`
|
|
123
|
+
* are still where they were; `retryAfterSeconds` is new and comes from the header.
|
|
124
|
+
*/
|
|
125
|
+
declare class RoadsError extends UnmapError {
|
|
126
|
+
constructor(status: number, message: string, code?: string, retryAfterSeconds?: number);
|
|
110
127
|
}
|
|
111
128
|
/**
|
|
112
129
|
* Canadian road events.
|
|
@@ -121,6 +138,7 @@ declare class Roads {
|
|
|
121
138
|
private readonly key;
|
|
122
139
|
private readonly base;
|
|
123
140
|
private readonly fetchImpl;
|
|
141
|
+
private readonly retry;
|
|
124
142
|
constructor(opts: RoadsOptions);
|
|
125
143
|
/** Every current road event. */
|
|
126
144
|
incidents(query?: RoadEventQuery): Promise<RoadEventsResponse>;
|
|
@@ -186,11 +204,337 @@ declare class Feedback {
|
|
|
186
204
|
private post;
|
|
187
205
|
}
|
|
188
206
|
|
|
207
|
+
type TransitMetaStatus = "current" | "stale" | "partial" | "unknown";
|
|
208
|
+
interface TransitMeta {
|
|
209
|
+
/** Static GTFS is scheduled data. Realtime will use a different value and updated_at. */
|
|
210
|
+
schedule_type: "scheduled";
|
|
211
|
+
source?: string;
|
|
212
|
+
source_updated_at?: string | null;
|
|
213
|
+
source_version?: string;
|
|
214
|
+
content_hash?: string | null;
|
|
215
|
+
sources?: readonly {
|
|
216
|
+
id: string;
|
|
217
|
+
source: string;
|
|
218
|
+
source_url: string;
|
|
219
|
+
attribution: string;
|
|
220
|
+
source_updated_at: string | null;
|
|
221
|
+
source_version: string;
|
|
222
|
+
content_hash: string | null;
|
|
223
|
+
status: "current" | "stale" | "unknown";
|
|
224
|
+
}[];
|
|
225
|
+
unmap_updated_at: string;
|
|
226
|
+
data_version?: string;
|
|
227
|
+
status: TransitMetaStatus;
|
|
228
|
+
attribution: string;
|
|
229
|
+
}
|
|
230
|
+
interface TransitResponse<T> {
|
|
231
|
+
data: T;
|
|
232
|
+
meta: TransitMeta;
|
|
233
|
+
}
|
|
234
|
+
interface TransitAgency {
|
|
235
|
+
id: string;
|
|
236
|
+
feed: string;
|
|
237
|
+
name: string;
|
|
238
|
+
url?: string;
|
|
239
|
+
timezone: string;
|
|
240
|
+
lang?: string;
|
|
241
|
+
phone?: string;
|
|
242
|
+
fare_url?: string;
|
|
243
|
+
}
|
|
244
|
+
interface TransitStop {
|
|
245
|
+
id: string;
|
|
246
|
+
feed: string;
|
|
247
|
+
name: string;
|
|
248
|
+
coordinates: [number, number];
|
|
249
|
+
code?: string;
|
|
250
|
+
description?: string;
|
|
251
|
+
zone_id?: string;
|
|
252
|
+
parent_station_id?: string;
|
|
253
|
+
wheelchair_boarding?: 0 | 1 | 2;
|
|
254
|
+
route_ids: string[];
|
|
255
|
+
/** Metres from the requested point. Present only on nearbyStops. */
|
|
256
|
+
distance?: number;
|
|
257
|
+
}
|
|
258
|
+
interface TransitRoute {
|
|
259
|
+
id: string;
|
|
260
|
+
feed: string;
|
|
261
|
+
agency_id: string;
|
|
262
|
+
short_name?: string;
|
|
263
|
+
long_name?: string;
|
|
264
|
+
description?: string;
|
|
265
|
+
route_type: number;
|
|
266
|
+
colour?: string;
|
|
267
|
+
text_colour?: string;
|
|
268
|
+
url?: string;
|
|
269
|
+
}
|
|
270
|
+
interface TransitDeparture {
|
|
271
|
+
stop_id: string;
|
|
272
|
+
route_id: string;
|
|
273
|
+
trip_id: string;
|
|
274
|
+
headsign?: string;
|
|
275
|
+
arrival: string;
|
|
276
|
+
departure: string;
|
|
277
|
+
stop_sequence: number;
|
|
278
|
+
/** 2 = phone agency; 3 = coordinate with driver. Omitted for ordinary pickup. */
|
|
279
|
+
pickup_type?: 2 | 3;
|
|
280
|
+
schedule_type: "scheduled";
|
|
281
|
+
}
|
|
282
|
+
interface NearbyStopsOptions {
|
|
283
|
+
/** Metres. Default 1000, range 50 to 10000. */
|
|
284
|
+
radius?: number;
|
|
285
|
+
/** Default 20, maximum 100. */
|
|
286
|
+
limit?: number;
|
|
287
|
+
signal?: AbortSignal;
|
|
288
|
+
}
|
|
289
|
+
interface DepartureOptions {
|
|
290
|
+
/** An instant. Date values serialize to ISO; strings must include Z or an offset. Default now. */
|
|
291
|
+
at?: Date | string;
|
|
292
|
+
/** Minutes after at. Default 60, maximum 240. */
|
|
293
|
+
window?: number;
|
|
294
|
+
/** Default 50, maximum 200. */
|
|
295
|
+
limit?: number;
|
|
296
|
+
signal?: AbortSignal;
|
|
297
|
+
}
|
|
298
|
+
interface TransitOptions {
|
|
299
|
+
key: string;
|
|
300
|
+
gateway?: string;
|
|
301
|
+
fetch?: typeof fetch;
|
|
302
|
+
/**
|
|
303
|
+
* Retry policy for `429`, `502`, `503` and transport failures. `false` disables it.
|
|
304
|
+
*
|
|
305
|
+
* On by default. `Retry-After` is on a `429` other than `billing_required`, and on a container
|
|
306
|
+
* `503`. A `502` has none: routing uses `routing_failed`, and `/elevation` uses
|
|
307
|
+
* `upstream_error`. With no header the client backs off itself. A wait longer than
|
|
308
|
+
* `maxDelayMs` (10 s) is not slept through; the error carries `retryAfterSeconds` instead.
|
|
309
|
+
* See `RetryPolicy` in `@unmap/core`.
|
|
310
|
+
*/
|
|
311
|
+
retry?: RetryPolicy | false;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* A transit failure.
|
|
315
|
+
*
|
|
316
|
+
* Extends `UnmapError`, so `catch (e) { if (e instanceof UnmapError) }` catches this alongside
|
|
317
|
+
* every other unmap client's errors. `instanceof TransitError` keeps working, and `status` and `code`
|
|
318
|
+
* are still where they were; `retryAfterSeconds` is new and comes from the header.
|
|
319
|
+
*/
|
|
320
|
+
declare class TransitError extends UnmapError {
|
|
321
|
+
constructor(status: number, message: string, code?: string, retryAfterSeconds?: number);
|
|
322
|
+
}
|
|
323
|
+
/** Static Canadian GTFS data. Scheduled departures ship before realtime. */
|
|
324
|
+
declare class Transit {
|
|
325
|
+
private readonly key;
|
|
326
|
+
private readonly base;
|
|
327
|
+
private readonly fetchImpl;
|
|
328
|
+
private readonly retry;
|
|
329
|
+
constructor(opts: TransitOptions);
|
|
330
|
+
agencies(signal?: AbortSignal): Promise<TransitResponse<TransitAgency[]>>;
|
|
331
|
+
nearbyStops(near: [number, number], options?: NearbyStopsOptions): Promise<TransitResponse<TransitStop[]>>;
|
|
332
|
+
getStop(id: string, signal?: AbortSignal): Promise<TransitResponse<TransitStop>>;
|
|
333
|
+
getRoute(id: string, signal?: AbortSignal): Promise<TransitResponse<TransitRoute>>;
|
|
334
|
+
departures(id: string, options?: DepartureOptions): Promise<TransitResponse<TransitDeparture[]>>;
|
|
335
|
+
private get;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Freshness of a capability or a source. `unknown` means no cadence was declared. */
|
|
339
|
+
type CoverageFreshness = "current" | "stale" | "unknown";
|
|
340
|
+
interface CoverageSource {
|
|
341
|
+
id: string;
|
|
342
|
+
provider: string;
|
|
343
|
+
/** Bilingual, like every human-readable field in this document. */
|
|
344
|
+
dataset_name: {
|
|
345
|
+
en: string;
|
|
346
|
+
fr: string;
|
|
347
|
+
};
|
|
348
|
+
canonical_url: string;
|
|
349
|
+
licence: {
|
|
350
|
+
en: string;
|
|
351
|
+
fr: string;
|
|
352
|
+
};
|
|
353
|
+
licence_url: string;
|
|
354
|
+
attribution: string;
|
|
355
|
+
jurisdiction: string[];
|
|
356
|
+
/** ISO 8601 duration, e.g. `P1D`. */
|
|
357
|
+
refresh_interval: string;
|
|
358
|
+
last_checked_at: string;
|
|
359
|
+
last_success_at: string;
|
|
360
|
+
source_version: string;
|
|
361
|
+
content_hash: string | null;
|
|
362
|
+
freshness: CoverageFreshness;
|
|
363
|
+
stale_after: string | null;
|
|
364
|
+
coverage: string;
|
|
365
|
+
known_gaps: {
|
|
366
|
+
en: string;
|
|
367
|
+
fr: string;
|
|
368
|
+
};
|
|
369
|
+
quality_status: string;
|
|
370
|
+
}
|
|
371
|
+
interface CoverageCapability {
|
|
372
|
+
/** `tiles`, `geocoding`, `routing`, `places`, `transit`, `roads`, and so on. */
|
|
373
|
+
id: string;
|
|
374
|
+
display_name: {
|
|
375
|
+
en: string;
|
|
376
|
+
fr: string;
|
|
377
|
+
};
|
|
378
|
+
/** `planned` is published as honestly as `live`: a capability absent from the page reads as "not our problem". */
|
|
379
|
+
availability: "live" | "planned";
|
|
380
|
+
/** The endpoints that serve it. Empty while planned. */
|
|
381
|
+
endpoints: string[];
|
|
382
|
+
coverage: {
|
|
383
|
+
en: string;
|
|
384
|
+
fr: string;
|
|
385
|
+
};
|
|
386
|
+
/** Written to be accurate rather than flattering. Read this before promising coverage to a user. */
|
|
387
|
+
known_gaps: {
|
|
388
|
+
en: string;
|
|
389
|
+
fr: string;
|
|
390
|
+
};
|
|
391
|
+
/** ISO 3166-2 subdivisions, or `CA` for national. */
|
|
392
|
+
jurisdictions: string[];
|
|
393
|
+
freshness: CoverageFreshness;
|
|
394
|
+
sources: CoverageSource[];
|
|
395
|
+
}
|
|
396
|
+
interface CoverageDocument {
|
|
397
|
+
generated_at: string;
|
|
398
|
+
freshness_basis: string;
|
|
399
|
+
capabilities: CoverageCapability[];
|
|
400
|
+
jurisdictions: unknown;
|
|
401
|
+
}
|
|
402
|
+
declare class CoverageError extends UnmapError {
|
|
403
|
+
constructor(status: number, message: string, code?: string, retryAfterSeconds?: number);
|
|
404
|
+
}
|
|
405
|
+
interface CoverageOptions {
|
|
406
|
+
gateway?: string;
|
|
407
|
+
/** Override the fetch implementation (tests, non-global environments). */
|
|
408
|
+
fetch?: typeof fetch;
|
|
409
|
+
retry?: RetryPolicy | false;
|
|
410
|
+
}
|
|
411
|
+
/** Where unmap works. Takes no key: the endpoint is unauthenticated and unmetered. */
|
|
412
|
+
declare class Coverage {
|
|
413
|
+
private readonly base;
|
|
414
|
+
private readonly fetchImpl;
|
|
415
|
+
private readonly retry;
|
|
416
|
+
constructor(opts?: CoverageOptions);
|
|
417
|
+
/** The whole document. Cached an hour at the edge, so polling it is cheap. */
|
|
418
|
+
get(opts?: {
|
|
419
|
+
signal?: AbortSignal;
|
|
420
|
+
}): Promise<CoverageDocument>;
|
|
421
|
+
/**
|
|
422
|
+
* One capability by id, or `undefined` when the document does not describe it.
|
|
423
|
+
*
|
|
424
|
+
* const transit = await unmap.coverage.capability("transit");
|
|
425
|
+
* if (!transit?.jurisdictions.includes("CA-ON")) return "not available here yet";
|
|
426
|
+
*/
|
|
427
|
+
capability(id: string, opts?: {
|
|
428
|
+
signal?: AbortSignal;
|
|
429
|
+
}): Promise<CoverageCapability | undefined>;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
interface TimezoneResult {
|
|
433
|
+
timezone: string | null;
|
|
434
|
+
offset: string | null;
|
|
435
|
+
offsetSeconds: number | null;
|
|
436
|
+
dst: boolean | null;
|
|
437
|
+
}
|
|
438
|
+
interface TimezoneLookupOptions {
|
|
439
|
+
/** Instant the offset is evaluated at. Omit it for the current time. */
|
|
440
|
+
at?: Date;
|
|
441
|
+
signal?: AbortSignal;
|
|
442
|
+
}
|
|
443
|
+
declare class TimezoneError extends UnmapError {
|
|
444
|
+
constructor(status: number, message: string, code?: string, retryAfterSeconds?: number);
|
|
445
|
+
}
|
|
446
|
+
interface TimezoneOptions {
|
|
447
|
+
key: string;
|
|
448
|
+
gateway?: string;
|
|
449
|
+
fetch?: typeof fetch;
|
|
450
|
+
retry?: RetryPolicy | false;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* IANA timezone for a coordinate.
|
|
454
|
+
*
|
|
455
|
+
* const zone = await unmap.timezone.lookup([-114.0719, 51.0447]);
|
|
456
|
+
*
|
|
457
|
+
* The coordinate is `[longitude, latitude]`, the same order as the rest of the SDK.
|
|
458
|
+
* `at` selects the offset. It does not change which zone the coordinate falls in.
|
|
459
|
+
*/
|
|
460
|
+
declare class Timezone {
|
|
461
|
+
private readonly key;
|
|
462
|
+
private readonly base;
|
|
463
|
+
private readonly fetchImpl;
|
|
464
|
+
private readonly retry;
|
|
465
|
+
constructor(opts: TimezoneOptions);
|
|
466
|
+
lookup(coordinate: [number, number], opts?: TimezoneLookupOptions): Promise<TimezoneResult>;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
interface GeolocationOptions {
|
|
470
|
+
key: string;
|
|
471
|
+
gateway?: string;
|
|
472
|
+
fetch?: typeof fetch;
|
|
473
|
+
/**
|
|
474
|
+
* Retry policy for `429`, `502`, `503` and transport failures. `false` disables it.
|
|
475
|
+
*
|
|
476
|
+
* On by default. A wait longer than `maxDelayMs` (10 s) is not slept through; the error
|
|
477
|
+
* carries `retryAfterSeconds` instead.
|
|
478
|
+
*/
|
|
479
|
+
retry?: RetryPolicy | false;
|
|
480
|
+
}
|
|
481
|
+
interface IpGeolocationResult {
|
|
482
|
+
ip: string | null;
|
|
483
|
+
location: {
|
|
484
|
+
lat: number | null;
|
|
485
|
+
lng: number | null;
|
|
486
|
+
};
|
|
487
|
+
city: string | null;
|
|
488
|
+
region: string | null;
|
|
489
|
+
regionCode: string | null;
|
|
490
|
+
postalCode: string | null;
|
|
491
|
+
country: string | null;
|
|
492
|
+
continent: string | null;
|
|
493
|
+
timezone: string | null;
|
|
494
|
+
network: {
|
|
495
|
+
asn: number | null;
|
|
496
|
+
organization: string | null;
|
|
497
|
+
};
|
|
498
|
+
precision: "approximate";
|
|
499
|
+
}
|
|
500
|
+
declare class GeolocationError extends UnmapError {
|
|
501
|
+
constructor(status: number, message: string, code?: string, retryAfterSeconds?: number);
|
|
502
|
+
}
|
|
503
|
+
/** Typed client for `GET /ip`. One call, one metered request, on the shared pool. */
|
|
504
|
+
declare class Geolocation {
|
|
505
|
+
private readonly key;
|
|
506
|
+
private readonly base;
|
|
507
|
+
private readonly fetchImpl;
|
|
508
|
+
private readonly retry;
|
|
509
|
+
constructor(opts: GeolocationOptions);
|
|
510
|
+
/**
|
|
511
|
+
* Approximate country, region, city, coordinates, and timezone of this request.
|
|
512
|
+
*
|
|
513
|
+
* const location = await unmap.geolocation.ip();
|
|
514
|
+
*
|
|
515
|
+
* A field the network did not identify is `null`. The call still resolves.
|
|
516
|
+
*/
|
|
517
|
+
ip(opts?: {
|
|
518
|
+
signal?: AbortSignal;
|
|
519
|
+
}): Promise<IpGeolocationResult>;
|
|
520
|
+
}
|
|
521
|
+
|
|
189
522
|
interface UnmapOptions {
|
|
190
523
|
key: string;
|
|
191
524
|
gateway?: string;
|
|
192
525
|
/** Override the fetch implementation (tests, non-global environments). */
|
|
193
526
|
fetch?: typeof fetch;
|
|
527
|
+
/**
|
|
528
|
+
* Retry policy shared by every client on this instance. `false` disables it.
|
|
529
|
+
*
|
|
530
|
+
* On by default for `429`, `502`, `503` and transport failures. `Retry-After` is on a `429`
|
|
531
|
+
* other than `billing_required`, and on a container `503`. A `502` has none. A wait longer
|
|
532
|
+
* than `maxDelayMs` (10 s) is not slept through; the error carries `retryAfterSeconds`
|
|
533
|
+
* instead, because a cold land or energy container asks for 60 and parking a request handler
|
|
534
|
+
* for a minute is a worse surprise than an error. Feedback submissions are never retried:
|
|
535
|
+
* they create a record and carry no idempotency key.
|
|
536
|
+
*/
|
|
537
|
+
retry?: RetryPolicy | false;
|
|
194
538
|
/** If provided, a map is mounted in this container. */
|
|
195
539
|
container?: string | HTMLElement;
|
|
196
540
|
style?: string;
|
|
@@ -213,8 +557,8 @@ interface UnmapOptions {
|
|
|
213
557
|
controls?: boolean | UnmapControlsOptions;
|
|
214
558
|
}
|
|
215
559
|
/**
|
|
216
|
-
* One Stripe-style key, the whole platform. With a `container` you get a rendered map
|
|
217
|
-
* `geocoder`, `router` and `
|
|
560
|
+
* One Stripe-style key, the whole platform. With a `container` you get a rendered map.
|
|
561
|
+
* `geocoder`, `router`, `elevation`, `timezone`, and `geolocation` are always available.
|
|
218
562
|
*
|
|
219
563
|
* const map = new Unmap({ key: "um_live_...", container: "#map", center: [-75.69, 45.42] });
|
|
220
564
|
*/
|
|
@@ -222,12 +566,26 @@ declare class Unmap {
|
|
|
222
566
|
readonly geocoder: Geocoder;
|
|
223
567
|
readonly router: Router;
|
|
224
568
|
readonly elevation: Elevation;
|
|
569
|
+
/** IANA timezone and the UTC offset at an instant. One call, anywhere. */
|
|
570
|
+
readonly timezone: Timezone;
|
|
225
571
|
/** Canadian points of interest. Same corpus as the geocoder, in the Place shape. */
|
|
226
572
|
readonly places: Places;
|
|
227
573
|
/** Closures, incidents, construction and road conditions. */
|
|
228
574
|
readonly roads: Roads;
|
|
229
575
|
/** Report wrong data. Never metered. */
|
|
230
576
|
readonly feedback: Feedback;
|
|
577
|
+
/** Scheduled public-transit agencies, stops, routes and departures. */
|
|
578
|
+
readonly transit: Transit;
|
|
579
|
+
/**
|
|
580
|
+
* Where each capability actually works. Unauthenticated and unmetered, so checking costs
|
|
581
|
+
* nothing: read it before promising a user an answer, because transit routing is one agency
|
|
582
|
+
* and road events are one province.
|
|
583
|
+
*/
|
|
584
|
+
readonly coverage: Coverage;
|
|
585
|
+
/**
|
|
586
|
+
* Approximate location of the current request. Not device GPS. One call on the shared pool.
|
|
587
|
+
*/
|
|
588
|
+
readonly geolocation: Geolocation;
|
|
231
589
|
readonly data: {
|
|
232
590
|
query(opts: QueryOptions): Promise<QueryResponse>;
|
|
233
591
|
};
|
|
@@ -237,4 +595,4 @@ declare class Unmap {
|
|
|
237
595
|
identify(opts: IdentifyOptions): Promise<IdentifyResponse>;
|
|
238
596
|
}
|
|
239
597
|
|
|
240
|
-
export { type AffectedRoad, Feedback, type FeedbackCategory, type FeedbackProduct, type FeedbackReceipt, type FeedbackReport, type RoadCondition, type RoadEvent, type RoadEventQuery, type RoadEventType, type RoadEventsResponse, type RoadSeverity, Roads, RoadsError, type RoadsOptions, Unmap, type UnmapOptions };
|
|
598
|
+
export { type AffectedRoad, Coverage, type CoverageCapability, type CoverageDocument, CoverageError, type CoverageFreshness, type CoverageOptions, type CoverageSource, type DepartureOptions, Feedback, type FeedbackCategory, type FeedbackProduct, type FeedbackReceipt, type FeedbackReport, Geolocation, GeolocationError, type GeolocationOptions, type IpGeolocationResult, type NearbyStopsOptions, type RoadCondition, type RoadEvent, type RoadEventQuery, type RoadEventType, type RoadEventsResponse, type RoadSeverity, Roads, RoadsError, type RoadsOptions, 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
|
@@ -5,16 +5,12 @@ import {
|
|
|
5
5
|
import { Geocoder, Places } from "@unmap/geocoding";
|
|
6
6
|
|
|
7
7
|
// src/roads.ts
|
|
8
|
-
import { DEFAULT_GATEWAY } from "@unmap/core";
|
|
9
|
-
var RoadsError = class extends
|
|
10
|
-
constructor(status, message, code) {
|
|
11
|
-
super(message);
|
|
12
|
-
this.status = status;
|
|
13
|
-
this.code = code;
|
|
8
|
+
import { DEFAULT_GATEWAY, UnmapError, fetchWithRetry, parseRetryAfter } from "@unmap/core";
|
|
9
|
+
var RoadsError = class extends UnmapError {
|
|
10
|
+
constructor(status, message, code, retryAfterSeconds) {
|
|
11
|
+
super(status, message, code, retryAfterSeconds);
|
|
14
12
|
this.name = "RoadsError";
|
|
15
13
|
}
|
|
16
|
-
status;
|
|
17
|
-
code;
|
|
18
14
|
};
|
|
19
15
|
function list(value) {
|
|
20
16
|
if (value === void 0) return void 0;
|
|
@@ -30,13 +26,16 @@ async function errorFrom(res, path, name) {
|
|
|
30
26
|
}
|
|
31
27
|
const reason = typeof body?.error === "string" ? body.error : void 0;
|
|
32
28
|
const code = typeof body?.code === "string" ? body.code : void 0;
|
|
33
|
-
|
|
29
|
+
const retryAfter = parseRetryAfter(res.headers?.get("retry-after") ?? null) ?? void 0;
|
|
30
|
+
return new RoadsError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter);
|
|
34
31
|
}
|
|
35
32
|
var Roads = class {
|
|
36
33
|
key;
|
|
37
34
|
base;
|
|
38
35
|
fetchImpl;
|
|
36
|
+
retry;
|
|
39
37
|
constructor(opts) {
|
|
38
|
+
this.retry = opts.retry;
|
|
40
39
|
this.key = opts.key;
|
|
41
40
|
this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\/+$/, "");
|
|
42
41
|
this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
@@ -72,10 +71,12 @@ var Roads = class {
|
|
|
72
71
|
limit: query.limit != null ? String(query.limit) : void 0
|
|
73
72
|
};
|
|
74
73
|
for (const [k, v] of Object.entries(params)) if (v != null && v !== "") q.set(k, v);
|
|
75
|
-
const res = await
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
const res = await fetchWithRetry(
|
|
75
|
+
this.fetchImpl,
|
|
76
|
+
`${this.base}${path}?${q}`,
|
|
77
|
+
{ signal: query.signal, headers: { accept: "application/json" } },
|
|
78
|
+
this.retry
|
|
79
|
+
);
|
|
79
80
|
if (!res.ok) throw await errorFrom(res, path, "roads");
|
|
80
81
|
return await res.json();
|
|
81
82
|
}
|
|
@@ -118,6 +119,219 @@ var Feedback = class {
|
|
|
118
119
|
}
|
|
119
120
|
};
|
|
120
121
|
|
|
122
|
+
// src/transit.ts
|
|
123
|
+
import { DEFAULT_GATEWAY as DEFAULT_GATEWAY2, UnmapError as UnmapError2, fetchWithRetry as fetchWithRetry2, parseRetryAfter as parseRetryAfter2 } from "@unmap/core";
|
|
124
|
+
var TransitError = class extends UnmapError2 {
|
|
125
|
+
constructor(status, message, code, retryAfterSeconds) {
|
|
126
|
+
super(status, message, code, retryAfterSeconds);
|
|
127
|
+
this.name = "TransitError";
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
async function errorFrom2(res, path) {
|
|
131
|
+
let body = {};
|
|
132
|
+
try {
|
|
133
|
+
body = await res.json();
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
const message = typeof body.error === "string" ? body.error : `transit ${res.status} on ${path}`;
|
|
137
|
+
const code = typeof body.code === "string" ? body.code : void 0;
|
|
138
|
+
const retryAfter = parseRetryAfter2(res.headers?.get("retry-after") ?? null) ?? void 0;
|
|
139
|
+
return new TransitError(res.status, message, code, retryAfter);
|
|
140
|
+
}
|
|
141
|
+
var Transit = class {
|
|
142
|
+
key;
|
|
143
|
+
base;
|
|
144
|
+
fetchImpl;
|
|
145
|
+
retry;
|
|
146
|
+
constructor(opts) {
|
|
147
|
+
this.retry = opts.retry;
|
|
148
|
+
this.key = opts.key;
|
|
149
|
+
this.base = (opts.gateway ?? DEFAULT_GATEWAY2).replace(/\/+$/, "");
|
|
150
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
151
|
+
}
|
|
152
|
+
agencies(signal) {
|
|
153
|
+
return this.get("/transit/agencies", void 0, signal);
|
|
154
|
+
}
|
|
155
|
+
nearbyStops(near, options = {}) {
|
|
156
|
+
return this.get("/transit/stops", {
|
|
157
|
+
near: near.join(","),
|
|
158
|
+
radius: options.radius,
|
|
159
|
+
limit: options.limit
|
|
160
|
+
}, options.signal);
|
|
161
|
+
}
|
|
162
|
+
getStop(id, signal) {
|
|
163
|
+
return this.get(`/transit/stops/${encodeURIComponent(id)}`, void 0, signal);
|
|
164
|
+
}
|
|
165
|
+
getRoute(id, signal) {
|
|
166
|
+
return this.get(`/transit/routes/${encodeURIComponent(id)}`, void 0, signal);
|
|
167
|
+
}
|
|
168
|
+
departures(id, options = {}) {
|
|
169
|
+
return this.get(`/transit/stops/${encodeURIComponent(id)}/departures`, {
|
|
170
|
+
at: options.at instanceof Date ? options.at.toISOString() : options.at,
|
|
171
|
+
window: options.window,
|
|
172
|
+
limit: options.limit
|
|
173
|
+
}, options.signal);
|
|
174
|
+
}
|
|
175
|
+
async get(path, values, signal) {
|
|
176
|
+
const query = new URLSearchParams({ key: this.key });
|
|
177
|
+
for (const [name, value] of Object.entries(values ?? {})) {
|
|
178
|
+
if (value !== void 0) query.set(name, String(value));
|
|
179
|
+
}
|
|
180
|
+
const response = await fetchWithRetry2(
|
|
181
|
+
this.fetchImpl,
|
|
182
|
+
`${this.base}${path}?${query}`,
|
|
183
|
+
{ signal, headers: { accept: "application/json" } },
|
|
184
|
+
this.retry
|
|
185
|
+
);
|
|
186
|
+
if (!response.ok) throw await errorFrom2(response, path);
|
|
187
|
+
return await response.json();
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/coverage.ts
|
|
192
|
+
import { DEFAULT_GATEWAY as DEFAULT_GATEWAY3, UnmapError as UnmapError3, fetchWithRetry as fetchWithRetry3, parseRetryAfter as parseRetryAfter3 } from "@unmap/core";
|
|
193
|
+
var COVERAGE_PATH = "/coverage.json";
|
|
194
|
+
var CoverageError = class extends UnmapError3 {
|
|
195
|
+
constructor(status, message, code, retryAfterSeconds) {
|
|
196
|
+
super(status, message, code, retryAfterSeconds);
|
|
197
|
+
this.name = "CoverageError";
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
var Coverage = class {
|
|
201
|
+
base;
|
|
202
|
+
fetchImpl;
|
|
203
|
+
retry;
|
|
204
|
+
constructor(opts = {}) {
|
|
205
|
+
this.base = (opts.gateway ?? DEFAULT_GATEWAY3).replace(/\/+$/, "");
|
|
206
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
207
|
+
this.retry = opts.retry;
|
|
208
|
+
}
|
|
209
|
+
/** The whole document. Cached an hour at the edge, so polling it is cheap. */
|
|
210
|
+
async get(opts = {}) {
|
|
211
|
+
const res = await fetchWithRetry3(
|
|
212
|
+
this.fetchImpl,
|
|
213
|
+
`${this.base}${COVERAGE_PATH}`,
|
|
214
|
+
{ signal: opts.signal, headers: { accept: "application/json" } },
|
|
215
|
+
this.retry
|
|
216
|
+
);
|
|
217
|
+
if (!res.ok) {
|
|
218
|
+
const retryAfter = parseRetryAfter3(res.headers?.get?.("retry-after") ?? null) ?? void 0;
|
|
219
|
+
throw new CoverageError(res.status, `coverage ${res.status}`, void 0, retryAfter);
|
|
220
|
+
}
|
|
221
|
+
return await res.json();
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* One capability by id, or `undefined` when the document does not describe it.
|
|
225
|
+
*
|
|
226
|
+
* const transit = await unmap.coverage.capability("transit");
|
|
227
|
+
* if (!transit?.jurisdictions.includes("CA-ON")) return "not available here yet";
|
|
228
|
+
*/
|
|
229
|
+
async capability(id, opts = {}) {
|
|
230
|
+
const doc = await this.get(opts);
|
|
231
|
+
return doc.capabilities.find((c) => c.id === id);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// src/timezone.ts
|
|
236
|
+
import { DEFAULT_GATEWAY as DEFAULT_GATEWAY4, UnmapError as UnmapError4, fetchWithRetry as fetchWithRetry4, parseRetryAfter as parseRetryAfter4 } from "@unmap/core";
|
|
237
|
+
var TIMEZONE_PATH = "/timezone";
|
|
238
|
+
var TimezoneError = class extends UnmapError4 {
|
|
239
|
+
constructor(status, message, code, retryAfterSeconds) {
|
|
240
|
+
super(status, message, code, retryAfterSeconds);
|
|
241
|
+
this.name = "TimezoneError";
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
var Timezone = class {
|
|
245
|
+
key;
|
|
246
|
+
base;
|
|
247
|
+
fetchImpl;
|
|
248
|
+
retry;
|
|
249
|
+
constructor(opts) {
|
|
250
|
+
this.retry = opts.retry;
|
|
251
|
+
this.key = opts.key;
|
|
252
|
+
this.base = (opts.gateway ?? DEFAULT_GATEWAY4).replace(/\/+$/, "");
|
|
253
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
254
|
+
}
|
|
255
|
+
async lookup(coordinate, opts = {}) {
|
|
256
|
+
const [lon, lat] = coordinate;
|
|
257
|
+
const q = new URLSearchParams({ key: this.key, lon: String(lon), lat: String(lat) });
|
|
258
|
+
if (opts.at) q.set("at", opts.at.toISOString());
|
|
259
|
+
const res = await fetchWithRetry4(
|
|
260
|
+
this.fetchImpl,
|
|
261
|
+
`${this.base}${TIMEZONE_PATH}?${q}`,
|
|
262
|
+
{ signal: opts.signal, headers: { accept: "application/json" } },
|
|
263
|
+
this.retry
|
|
264
|
+
);
|
|
265
|
+
if (!res.ok) throw await errorFrom3(res);
|
|
266
|
+
return await res.json();
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
async function errorFrom3(res) {
|
|
270
|
+
const retryAfter = parseRetryAfter4(res.headers?.get?.("retry-after") ?? null) ?? void 0;
|
|
271
|
+
let message = res.statusText || `timezone ${res.status}`;
|
|
272
|
+
let code;
|
|
273
|
+
try {
|
|
274
|
+
const body = await res.json();
|
|
275
|
+
if (typeof body.error === "string" && body.error) message = body.error;
|
|
276
|
+
if (typeof body.code === "string") code = body.code;
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
return new TimezoneError(res.status, message, code, retryAfter);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/geolocation.ts
|
|
283
|
+
import { DEFAULT_GATEWAY as DEFAULT_GATEWAY5, UnmapError as UnmapError5, fetchWithRetry as fetchWithRetry5, parseRetryAfter as parseRetryAfter5 } from "@unmap/core";
|
|
284
|
+
var IP_PATH = "/ip";
|
|
285
|
+
var GeolocationError = class extends UnmapError5 {
|
|
286
|
+
constructor(status, message, code, retryAfterSeconds) {
|
|
287
|
+
super(status, message, code, retryAfterSeconds);
|
|
288
|
+
this.name = "GeolocationError";
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
var Geolocation = class {
|
|
292
|
+
key;
|
|
293
|
+
base;
|
|
294
|
+
fetchImpl;
|
|
295
|
+
retry;
|
|
296
|
+
constructor(opts) {
|
|
297
|
+
this.key = opts.key;
|
|
298
|
+
this.base = (opts.gateway ?? DEFAULT_GATEWAY5).replace(/\/+$/, "");
|
|
299
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
300
|
+
this.retry = opts.retry;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Approximate country, region, city, coordinates, and timezone of this request.
|
|
304
|
+
*
|
|
305
|
+
* const location = await unmap.geolocation.ip();
|
|
306
|
+
*
|
|
307
|
+
* A field the network did not identify is `null`. The call still resolves.
|
|
308
|
+
*/
|
|
309
|
+
async ip(opts = {}) {
|
|
310
|
+
const q = new URLSearchParams({ key: this.key });
|
|
311
|
+
const res = await fetchWithRetry5(
|
|
312
|
+
this.fetchImpl,
|
|
313
|
+
`${this.base}${IP_PATH}?${q}`,
|
|
314
|
+
{ signal: opts.signal, headers: { accept: "application/json" } },
|
|
315
|
+
this.retry
|
|
316
|
+
);
|
|
317
|
+
if (!res.ok) throw await errorFrom4(res);
|
|
318
|
+
return await res.json();
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
async function errorFrom4(res) {
|
|
322
|
+
const fallback = `geolocation ${res.status}`;
|
|
323
|
+
let body;
|
|
324
|
+
try {
|
|
325
|
+
body = await res.json();
|
|
326
|
+
} catch {
|
|
327
|
+
body = void 0;
|
|
328
|
+
}
|
|
329
|
+
const reason = typeof body?.error === "string" ? body.error : void 0;
|
|
330
|
+
const code = typeof body?.code === "string" ? body.code : void 0;
|
|
331
|
+
const retryAfter = parseRetryAfter5(res.headers?.get("retry-after") ?? null) ?? void 0;
|
|
332
|
+
return new GeolocationError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter);
|
|
333
|
+
}
|
|
334
|
+
|
|
121
335
|
// src/index.ts
|
|
122
336
|
import { Router } from "@unmap/routing";
|
|
123
337
|
import { Elevation } from "@unmap/elevation";
|
|
@@ -125,25 +339,44 @@ export * from "@unmap/geocoding";
|
|
|
125
339
|
export * from "@unmap/routing";
|
|
126
340
|
export * from "@unmap/elevation";
|
|
127
341
|
import { createMap as createMap2, unmapStyle, DEMO_CITIES, STYLE_NAMES, maplibregl } from "@unmap/maps";
|
|
342
|
+
import { UnmapError as UnmapError6, parseRetryAfter as parseRetryAfter6 } from "@unmap/core";
|
|
128
343
|
var Unmap = class {
|
|
129
344
|
geocoder;
|
|
130
345
|
router;
|
|
131
346
|
elevation;
|
|
347
|
+
/** IANA timezone and the UTC offset at an instant. One call, anywhere. */
|
|
348
|
+
timezone;
|
|
132
349
|
/** Canadian points of interest. Same corpus as the geocoder, in the Place shape. */
|
|
133
350
|
places;
|
|
134
351
|
/** Closures, incidents, construction and road conditions. */
|
|
135
352
|
roads;
|
|
136
353
|
/** Report wrong data. Never metered. */
|
|
137
354
|
feedback;
|
|
355
|
+
/** Scheduled public-transit agencies, stops, routes and departures. */
|
|
356
|
+
transit;
|
|
357
|
+
/**
|
|
358
|
+
* Where each capability actually works. Unauthenticated and unmetered, so checking costs
|
|
359
|
+
* nothing: read it before promising a user an answer, because transit routing is one agency
|
|
360
|
+
* and road events are one province.
|
|
361
|
+
*/
|
|
362
|
+
coverage;
|
|
363
|
+
/**
|
|
364
|
+
* Approximate location of the current request. Not device GPS. One call on the shared pool.
|
|
365
|
+
*/
|
|
366
|
+
geolocation;
|
|
138
367
|
data;
|
|
139
368
|
map;
|
|
140
369
|
constructor(opts) {
|
|
141
370
|
this.geocoder = new Geocoder(opts);
|
|
142
371
|
this.router = new Router(opts);
|
|
143
372
|
this.elevation = new Elevation(opts);
|
|
373
|
+
this.timezone = new Timezone(opts);
|
|
144
374
|
this.places = new Places(opts);
|
|
145
375
|
this.roads = new Roads(opts);
|
|
146
376
|
this.feedback = new Feedback(opts);
|
|
377
|
+
this.transit = new Transit(opts);
|
|
378
|
+
this.coverage = new Coverage(opts);
|
|
379
|
+
this.geolocation = new Geolocation(opts);
|
|
147
380
|
this.data = { query: (queryOpts) => this.geocoder.queryLayer(queryOpts) };
|
|
148
381
|
if (opts.container) {
|
|
149
382
|
this.map = createMap({ ...opts, container: opts.container });
|
|
@@ -155,14 +388,24 @@ var Unmap = class {
|
|
|
155
388
|
}
|
|
156
389
|
};
|
|
157
390
|
export {
|
|
391
|
+
Coverage,
|
|
392
|
+
CoverageError,
|
|
158
393
|
DEMO_CITIES,
|
|
159
394
|
Feedback,
|
|
395
|
+
Geolocation,
|
|
396
|
+
GeolocationError,
|
|
160
397
|
Roads,
|
|
161
398
|
RoadsError,
|
|
162
399
|
STYLE_NAMES,
|
|
400
|
+
Timezone,
|
|
401
|
+
TimezoneError,
|
|
402
|
+
Transit,
|
|
403
|
+
TransitError,
|
|
163
404
|
Unmap,
|
|
405
|
+
UnmapError6 as UnmapError,
|
|
164
406
|
createMap2 as createMap,
|
|
165
407
|
maplibregl,
|
|
408
|
+
parseRetryAfter6 as parseRetryAfter,
|
|
166
409
|
unmapStyle
|
|
167
410
|
};
|
|
168
411
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/roads.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 { Router } from \"@unmap/routing\";\nimport { Elevation } from \"@unmap/elevation\";\n\nexport * from \"@unmap/geocoding\";\nexport * from \"@unmap/routing\";\nexport * from \"@unmap/elevation\";\nexport * from \"./roads\";\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 /** If provided, a map is mounted in this container. */\n container?: string | HTMLElement;\n style?: string;\n flavor?: string;\n /** \"light\" or \"dark\". Selects the mode half of the named style. */\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 /** `\"globe\"` 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` and `elevation` 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 /** 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 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.places = new Places(opts);\n this.roads = new Roads(opts);\n this.feedback = new Feedback(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","import { DEFAULT_GATEWAY } 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}\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\nexport class RoadsError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n public readonly code?: string,\n ) {\n super(message);\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 return new RoadsError(res.status, reason ? `${reason} (${fallback})` : fallback, code);\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\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 /** 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 this.fetchImpl(`${this.base}${path}?${q}`, {\n signal: query.signal,\n headers: { accept: \"application/json\" },\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 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"],"mappings":"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","names":["createMap"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/roads.ts","../src/transit.ts","../src/coverage.ts","../src/timezone.ts","../src/geolocation.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 { Timezone } from \"./timezone\";\nimport { Geolocation } from \"./geolocation\";\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 \"./timezone\";\nexport * from \"./geolocation\";\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 flavor?: string;\n /** \"light\" or \"dark\". Selects the mode half of the named style. */\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 /** `\"globe\"` 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 `geolocation` 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 /** 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 is one agency\n * and road events are one province.\n */\n readonly coverage: Coverage;\n /**\n * Approximate location of the current request. Not device GPS. One call on the shared pool.\n */\n readonly geolocation: Geolocation;\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.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.geolocation = new Geolocation(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}\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 uses `routing_failed`, and `/elevation` uses\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. `instanceof RoadsError` keeps working, and `status` and `code`\n * are still where they were; `retryAfterSeconds` is new and comes from the 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 uses `routing_failed`, and `/elevation` uses\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. `instanceof TransitError` keeps working, and `status` and `code`\n * are still where they were; `retryAfterSeconds` is new and comes from the 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 is one agency, road events are one province, 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\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 last_success_at: string;\n source_version: string;\n content_hash: string | null;\n freshness: CoverageFreshness;\n stale_after: string | null;\n coverage: string;\n known_gaps: { en: string; fr: string };\n quality_status: 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: CoverageFreshness;\n sources: CoverageSource[];\n}\n\nexport interface CoverageDocument {\n generated_at: string;\n freshness_basis: string;\n capabilities: CoverageCapability[];\n jurisdictions: unknown;\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 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 * Approximate location of the current request (`GET /ip`).\n *\n * This is the network the request arrived on, not a device fix and not a lookup of an\n * address you choose. `precision` is always `\"approximate\"`.\n */\n\n/** The one path this client calls. A literal so the gateway's SDK-parity audit can see it. */\nconst IP_PATH = \"/ip\";\n\nexport interface GeolocationOptions {\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. A wait longer than `maxDelayMs` (10 s) is not slept through; the error\n * carries `retryAfterSeconds` instead.\n */\n retry?: RetryPolicy | false;\n}\n\nexport interface IpGeolocationResult {\n ip: string | null;\n location: {\n lat: number | null;\n lng: number | null;\n };\n city: string | null;\n region: string | null;\n regionCode: string | null;\n postalCode: string | null;\n country: string | null;\n continent: string | null;\n timezone: string | null;\n network: {\n asn: number | null;\n organization: string | null;\n };\n precision: \"approximate\";\n}\n\nexport class GeolocationError extends UnmapError {\n constructor(status: number, message: string, code?: string, retryAfterSeconds?: number) {\n super(status, message, code, retryAfterSeconds);\n this.name = \"GeolocationError\";\n }\n}\n\n/** Typed client for `GET /ip`. One call, one metered request, on the shared pool. */\nexport class Geolocation {\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: GeolocationOptions) {\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 * Approximate country, region, city, coordinates, and timezone of this request.\n *\n * const location = await unmap.geolocation.ip();\n *\n * A field the network did not identify is `null`. The call still resolves.\n */\n async ip(opts: { signal?: AbortSignal } = {}): Promise<IpGeolocationResult> {\n const q = new URLSearchParams({ key: this.key });\n const res = await fetchWithRetry(\n this.fetchImpl,\n `${this.base}${IP_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 IpGeolocationResult;\n }\n}\n\nasync function errorFrom(res: { status: number; headers?: Headers; json(): Promise<unknown> }): Promise<GeolocationError> {\n const fallback = `geolocation ${res.status}`;\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 GeolocationError(res.status, reason ? `${reason} (${fallback})` : fallback, code, retryAfter);\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","createMap","UnmapError","parseRetryAfter"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unmap/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "One key, the whole unmap platform: map + geocoder + router",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"routing",
|
|
13
13
|
"sdk"
|
|
14
14
|
],
|
|
15
|
-
"homepage": "https://unmap.dev/docs/quickstart",
|
|
15
|
+
"homepage": "https://unmap.dev/docs/start/quickstart",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
18
|
"url": "git+https://github.com/unmap-dev/unmap.git",
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"maplibre-gl": "^6.7.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.14.0",
|
|
44
|
+
"@unmap/elevation": "0.14.0",
|
|
45
|
+
"@unmap/geocoding": "0.14.0",
|
|
46
|
+
"@unmap/maps": "0.14.0",
|
|
47
|
+
"@unmap/routing": "0.14.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"tsup": "^8.3.0",
|