@chaosity/location-client 0.5.0 → 0.6.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.
Files changed (73) hide show
  1. package/README.md +14 -0
  2. package/dist/adapters/GeoPlaces.d.ts +1 -1
  3. package/dist/auth/TokenProvider.js +3 -3
  4. package/dist/cjs/adapters/GeoPlaces.d.ts +64 -0
  5. package/dist/cjs/adapters/GeoPlaces.js +213 -0
  6. package/dist/cjs/auth/TokenProvider.d.ts +64 -0
  7. package/dist/cjs/auth/TokenProvider.js +144 -0
  8. package/dist/cjs/auth/tokenRefresh.d.ts +37 -0
  9. package/dist/cjs/auth/tokenRefresh.js +58 -0
  10. package/dist/cjs/client/GeoPlacesClient.d.ts +39 -0
  11. package/dist/cjs/client/GeoPlacesClient.js +68 -0
  12. package/dist/cjs/errors/LocationServiceException.d.ts +44 -0
  13. package/dist/cjs/errors/LocationServiceException.js +60 -0
  14. package/dist/cjs/index.d.ts +24 -0
  15. package/dist/cjs/index.js +72 -0
  16. package/dist/cjs/maps/Utils.d.ts +2 -0
  17. package/dist/cjs/maps/Utils.js +8 -0
  18. package/dist/cjs/maps/createTransformRequest.d.ts +10 -0
  19. package/dist/cjs/maps/createTransformRequest.js +43 -0
  20. package/dist/cjs/maps/mapEnums.d.ts +102 -0
  21. package/dist/cjs/maps/mapEnums.js +103 -0
  22. package/dist/cjs/maps/mapLanguage.d.ts +43 -0
  23. package/dist/cjs/maps/mapLanguage.js +75 -0
  24. package/dist/cjs/maps/mapPoi.d.ts +44 -0
  25. package/dist/cjs/maps/mapPoi.js +64 -0
  26. package/dist/cjs/maps/mapStyle.d.ts +77 -0
  27. package/dist/cjs/maps/mapStyle.js +111 -0
  28. package/dist/cjs/maps/staticMap.d.ts +85 -0
  29. package/dist/cjs/maps/staticMap.js +81 -0
  30. package/dist/cjs/package.json +3 -0
  31. package/dist/cjs/server/LocationServiceConnector.d.ts +55 -0
  32. package/dist/cjs/server/LocationServiceConnector.js +91 -0
  33. package/dist/cjs/server/getClientConfig.d.ts +40 -0
  34. package/dist/cjs/server/getClientConfig.js +130 -0
  35. package/dist/cjs/server/index.d.ts +6 -0
  36. package/dist/cjs/server/index.js +9 -0
  37. package/dist/cjs/transport/endpoints.d.ts +2 -0
  38. package/dist/cjs/transport/endpoints.js +34 -0
  39. package/dist/cjs/transport/errors.d.ts +19 -0
  40. package/dist/cjs/transport/errors.js +104 -0
  41. package/dist/cjs/transport/http.d.ts +24 -0
  42. package/dist/cjs/transport/http.js +142 -0
  43. package/dist/cjs/types/index.d.ts +34 -0
  44. package/dist/cjs/types/index.js +3 -0
  45. package/dist/cjs/utils/roundPosition.d.ts +66 -0
  46. package/dist/cjs/utils/roundPosition.js +109 -0
  47. package/dist/cjs/utils/tokenClaims.d.ts +55 -0
  48. package/dist/cjs/utils/tokenClaims.js +61 -0
  49. package/dist/client/GeoPlacesClient.d.ts +3 -3
  50. package/dist/client/GeoPlacesClient.js +4 -4
  51. package/dist/index.d.ts +22 -22
  52. package/dist/index.js +12 -12
  53. package/dist/maps/Utils.d.ts +1 -1
  54. package/dist/maps/Utils.js +1 -1
  55. package/dist/maps/createTransformRequest.js +1 -1
  56. package/dist/maps/mapLanguage.d.ts +31 -7
  57. package/dist/maps/mapLanguage.js +50 -20
  58. package/dist/maps/mapStyle.d.ts +1 -1
  59. package/dist/maps/mapStyle.js +17 -16
  60. package/dist/maps/staticMap.d.ts +1 -1
  61. package/dist/maps/staticMap.js +12 -4
  62. package/dist/server/LocationServiceConnector.d.ts +2 -2
  63. package/dist/server/LocationServiceConnector.js +6 -6
  64. package/dist/server/getClientConfig.d.ts +1 -1
  65. package/dist/server/getClientConfig.js +2 -2
  66. package/dist/server/index.d.ts +6 -6
  67. package/dist/server/index.js +3 -3
  68. package/dist/transport/endpoints.d.ts +1 -1
  69. package/dist/transport/endpoints.js +1 -1
  70. package/dist/transport/errors.d.ts +1 -1
  71. package/dist/transport/errors.js +1 -1
  72. package/dist/transport/http.js +2 -2
  73. package/package.json +33 -11
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.languageExpression = languageExpression;
4
+ exports.labelsByName = labelsByName;
5
+ exports.applyMapLanguage = applyMapLanguage;
6
+ /**
7
+ * The `text-field` expression that prefers `language`, then English, then the
8
+ * feature's default name.
9
+ *
10
+ * Based on the approach documented at:
11
+ * https://docs.aws.amazon.com/location/latest/developerguide/how-to-set-preferred-language-map.html
12
+ */
13
+ function languageExpression(language) {
14
+ return language === 'en'
15
+ ? ['coalesce', ['get', 'name:en'], ['get', 'name']]
16
+ : [
17
+ 'coalesce',
18
+ ['get', `name:${language}`],
19
+ ['get', 'name:en'],
20
+ ['get', 'name'],
21
+ ];
22
+ }
23
+ /**
24
+ * Whether a `text-field` reads a name property — the only kind of label a
25
+ * language rewrite makes sense for (#28).
26
+ *
27
+ * The AWS Standard style has 62 symbol layers with a text-field and 30 of
28
+ * them do NOT label by name: `building_label_number` reads
29
+ * `addr_housenumber`, and the 29 `shield_*` layers read `shield_text` or
30
+ * `ref`. Replacing those with a `name:<lang>` coalesce points them at a
31
+ * property their features do not carry, and the house numbers and road
32
+ * shields disappear — for `en` too. Measured against the live sandbox on
33
+ * 2026-08-29; the testbed showed it as "one map has house numbers, the
34
+ * others don't".
35
+ *
36
+ * Serialising the expression is the simplest exact test: `name`, `name:en`,
37
+ * `name_en` and a literal `{name}` template all contain it, and none of the
38
+ * non-name properties do.
39
+ */
40
+ function labelsByName(textField) {
41
+ return JSON.stringify(textField)?.includes('name') ?? false;
42
+ }
43
+ /**
44
+ * Apply a preferred display language to the name labels on a MapLibre map.
45
+ *
46
+ * AWS GeoMaps vector tiles carry `name:${lang}` properties (`name:en`,
47
+ * `name:fr`, `name:ja`, …). Every symbol layer whose `text-field` reads a
48
+ * name is rewritten to prefer the requested language, falling back to
49
+ * English then the default name. Layers that label by something else — house
50
+ * numbers, road shields — are left exactly as the style declared them.
51
+ *
52
+ * @param map - MapLibre Map instance (or any object matching the MapLike interface)
53
+ * @param language - ISO 639-1 language code (e.g. 'en', 'fr', 'de', 'ja', 'zh', 'ar')
54
+ *
55
+ * @example
56
+ * map.once('style.load', () => applyMapLanguage(map, 'fr'))
57
+ */
58
+ function applyMapLanguage(map, language) {
59
+ try {
60
+ const expression = languageExpression(language);
61
+ map.getStyle().layers.forEach((layer) => {
62
+ if (layer.type !== 'symbol')
63
+ return;
64
+ const textField = map.getLayoutProperty(layer.id, 'text-field');
65
+ if (textField === undefined || textField === null)
66
+ return;
67
+ if (!labelsByName(textField))
68
+ return;
69
+ map.setLayoutProperty(layer.id, 'text-field', expression);
70
+ });
71
+ }
72
+ catch {
73
+ // Style may not be fully loaded — call after 'style.load' event
74
+ }
75
+ }
@@ -0,0 +1,44 @@
1
+ import type { Map } from 'maplibre-gl';
2
+ /**
3
+ * Mapping of POI category names to their MapLibre layer IDs in AWS GeoMaps tiles.
4
+ * Layer IDs are stable across Standard, Monochrome, and Hybrid map styles.
5
+ */
6
+ export declare const POI_CATEGORIES: {
7
+ readonly food_drink: readonly ["poi_100_food_drink"];
8
+ readonly entertainment: readonly ["poi_200_going_out_entertainment"];
9
+ readonly sights: readonly ["poi_300_sights_museums"];
10
+ readonly transit: readonly ["poi_400_transit"];
11
+ readonly accommodations: readonly ["poi_500_accommodations"];
12
+ readonly leisure: readonly ["poi_550_leisure_outdoor"];
13
+ readonly shopping: readonly ["poi_600_shopping"];
14
+ readonly business: readonly ["poi_700_business_services"];
15
+ readonly facilities: readonly ["poi_800_facilities"];
16
+ readonly areas: readonly ["poi_900_areas_buildings"];
17
+ readonly parks: readonly ["poi_landuse_park", "poi_landuse_public_complex"];
18
+ };
19
+ export type PoiCategory = keyof typeof POI_CATEGORIES;
20
+ /**
21
+ * Set the visibility of one or more POI categories on the map.
22
+ *
23
+ * @param map - MapLibre Map instance
24
+ * @param category - POI category key or array of keys
25
+ * @param visible - Whether to show (true) or hide (false) the category
26
+ *
27
+ * @example
28
+ * // Hide transit and shopping POIs
29
+ * setPoiVisibility(map, ['transit', 'shopping'], false)
30
+ *
31
+ * // Show all food & drink POIs
32
+ * setPoiVisibility(map, 'food_drink', true)
33
+ */
34
+ export declare function setPoiVisibility(map: Map, category: PoiCategory | PoiCategory[], visible: boolean): void;
35
+ /**
36
+ * Show or hide all POI layers at once.
37
+ *
38
+ * @param map - MapLibre Map instance
39
+ * @param visible - Whether to show or hide all POIs
40
+ *
41
+ * @example
42
+ * setAllPoiVisibility(map, false) // hide everything
43
+ */
44
+ export declare function setAllPoiVisibility(map: Map, visible: boolean): void;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POI_CATEGORIES = void 0;
4
+ exports.setPoiVisibility = setPoiVisibility;
5
+ exports.setAllPoiVisibility = setAllPoiVisibility;
6
+ /**
7
+ * Mapping of POI category names to their MapLibre layer IDs in AWS GeoMaps tiles.
8
+ * Layer IDs are stable across Standard, Monochrome, and Hybrid map styles.
9
+ */
10
+ exports.POI_CATEGORIES = {
11
+ food_drink: ['poi_100_food_drink'],
12
+ entertainment: ['poi_200_going_out_entertainment'],
13
+ sights: ['poi_300_sights_museums'],
14
+ transit: ['poi_400_transit'],
15
+ accommodations: ['poi_500_accommodations'],
16
+ leisure: ['poi_550_leisure_outdoor'],
17
+ shopping: ['poi_600_shopping'],
18
+ business: ['poi_700_business_services'],
19
+ facilities: ['poi_800_facilities'],
20
+ areas: ['poi_900_areas_buildings'],
21
+ parks: ['poi_landuse_park', 'poi_landuse_public_complex'],
22
+ };
23
+ /**
24
+ * Set the visibility of one or more POI categories on the map.
25
+ *
26
+ * @param map - MapLibre Map instance
27
+ * @param category - POI category key or array of keys
28
+ * @param visible - Whether to show (true) or hide (false) the category
29
+ *
30
+ * @example
31
+ * // Hide transit and shopping POIs
32
+ * setPoiVisibility(map, ['transit', 'shopping'], false)
33
+ *
34
+ * // Show all food & drink POIs
35
+ * setPoiVisibility(map, 'food_drink', true)
36
+ */
37
+ function setPoiVisibility(map, category, visible) {
38
+ const categories = Array.isArray(category) ? category : [category];
39
+ const visibility = visible ? 'visible' : 'none';
40
+ for (const cat of categories) {
41
+ for (const layerId of exports.POI_CATEGORIES[cat]) {
42
+ try {
43
+ if (map.getLayer(layerId)) {
44
+ map.setLayoutProperty(layerId, 'visibility', visibility);
45
+ }
46
+ }
47
+ catch {
48
+ // Layer may not exist in the current map style
49
+ }
50
+ }
51
+ }
52
+ }
53
+ /**
54
+ * Show or hide all POI layers at once.
55
+ *
56
+ * @param map - MapLibre Map instance
57
+ * @param visible - Whether to show or hide all POIs
58
+ *
59
+ * @example
60
+ * setAllPoiVisibility(map, false) // hide everything
61
+ */
62
+ function setAllPoiVisibility(map, visible) {
63
+ setPoiVisibility(map, Object.keys(exports.POI_CATEGORIES), visible);
64
+ }
@@ -0,0 +1,77 @@
1
+ import type { StyleSpecification } from 'maplibre-gl';
2
+ import type { Buildings, ColorScheme, ContourDensity, MapStyle, Terrain, TrafficMode, TravelMode } from './mapEnums.js';
3
+ /**
4
+ * Options for building an AWS Location Service map style URL.
5
+ * All parameters map directly to query parameters supported by the style descriptor endpoint.
6
+ *
7
+ * Values are CASE SENSITIVE — the API rejects a wrong-cased one with a 400 that
8
+ * names the right spelling. Import the arrays from `mapEnums` to build pickers
9
+ * rather than typing the values, and the case is right by construction.
10
+ */
11
+ export interface MapStyleOptions {
12
+ /** Color scheme for the map (default: Light). Not applicable to Satellite/Hybrid styles. */
13
+ colorScheme?: ColorScheme;
14
+ /** ISO 3166-1 alpha-3 country code for political boundary perspective (e.g. 'IND', 'TUR'). */
15
+ politicalView?: string;
16
+ /** Terrain overlay type. */
17
+ terrain?: Terrain;
18
+ /** Enable 3D building extrusions. */
19
+ buildings?: Buildings;
20
+ /**
21
+ * Elevation contour line density.
22
+ *
23
+ * All of High, Low and Medium work. This was previously typed as `'Medium'`
24
+ * alone, documented as "the only value currently supported by the AWS SDK",
25
+ * which was wrong — the other two were confirmed against the live API.
26
+ */
27
+ contourDensity?: ContourDensity;
28
+ /**
29
+ * Traffic overlay.
30
+ *
31
+ * `Congestion` was previously missing here, so it could not be requested from
32
+ * TypeScript even though the API accepts it.
33
+ *
34
+ * Valid on its own, but NOT with every style: `Satellite` + `All` answers
35
+ * 400 "Traffic is not supported for style." Amazon owns that rule.
36
+ */
37
+ traffic?: TrafficMode;
38
+ /** Travel mode overlays for routing-specific features. */
39
+ travelModes?: TravelMode[];
40
+ }
41
+ /**
42
+ * Build a map style descriptor URL for the Location Service API.
43
+ *
44
+ * @param apiUrl - Base URL of the Location Service API
45
+ * @param mapStyle - Map style name (e.g. 'Standard', 'Monochrome', 'Satellite', 'Hybrid')
46
+ * @param options - Optional style parameters
47
+ * @returns Full style descriptor URL
48
+ *
49
+ * @example
50
+ * const url = buildMapStyleUrl(API_URL, 'Standard', { colorScheme: 'Dark', terrain: 'Hillshade' })
51
+ * map.setStyle(url)
52
+ */
53
+ export declare function buildMapStyleUrl(apiUrl: string, mapStyle: MapStyle, options?: MapStyleOptions): string;
54
+ /**
55
+ * Fetch the map style descriptor with authentication and apply descriptor-level modifications.
56
+ *
57
+ * Language is applied directly to the descriptor's layer definitions before MapLibre ever
58
+ * processes them, eliminating the visual flash that occurs when modifying layers post-load.
59
+ * All other style parameters (terrain, traffic, etc.) are passed as query parameters.
60
+ *
61
+ * The returned style object can be passed directly to `new maplibregl.Map({ style })` or
62
+ * `map.setStyle()`. Tile, glyph, and sprite requests still go through `transformRequest`
63
+ * for authentication — this only pre-processes the descriptor itself.
64
+ *
65
+ * @param apiUrl - Base URL of the Location Service API
66
+ * @param mapStyle - Map style name (e.g. 'Standard', 'Monochrome', 'Satellite', 'Hybrid')
67
+ * @param getToken - Callback returning the current auth token
68
+ * @param options - Style options; `language` is applied to the descriptor, all others become URL params
69
+ * @returns Modified MapLibre StyleSpecification object
70
+ *
71
+ * @example
72
+ * const style = await fetchMapStyle(API_URL, 'Standard', getToken, { colorScheme: 'Dark', language: 'fr' })
73
+ * const map = new maplibregl.Map({ style, transformRequest: createTransformRequest(API_URL, getToken) })
74
+ */
75
+ export declare function fetchMapStyle(apiUrl: string, mapStyle: MapStyle, getToken: () => string | undefined, options?: MapStyleOptions & {
76
+ language?: string;
77
+ }): Promise<StyleSpecification>;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildMapStyleUrl = buildMapStyleUrl;
4
+ exports.fetchMapStyle = fetchMapStyle;
5
+ const errors_js_1 = require("../transport/errors.js");
6
+ const mapLanguage_js_1 = require("./mapLanguage.js");
7
+ /**
8
+ * Build a map style descriptor URL for the Location Service API.
9
+ *
10
+ * @param apiUrl - Base URL of the Location Service API
11
+ * @param mapStyle - Map style name (e.g. 'Standard', 'Monochrome', 'Satellite', 'Hybrid')
12
+ * @param options - Optional style parameters
13
+ * @returns Full style descriptor URL
14
+ *
15
+ * @example
16
+ * const url = buildMapStyleUrl(API_URL, 'Standard', { colorScheme: 'Dark', terrain: 'Hillshade' })
17
+ * map.setStyle(url)
18
+ */
19
+ function buildMapStyleUrl(apiUrl, mapStyle, options = {}) {
20
+ const params = new URLSearchParams();
21
+ if (options.colorScheme)
22
+ params.set('color-scheme', options.colorScheme);
23
+ if (options.politicalView)
24
+ params.set('political-view', options.politicalView);
25
+ if (options.terrain)
26
+ params.set('terrain', options.terrain);
27
+ if (options.buildings)
28
+ params.set('buildings', options.buildings);
29
+ if (options.contourDensity)
30
+ params.set('contour-density', options.contourDensity);
31
+ if (options.traffic)
32
+ params.set('traffic', options.traffic);
33
+ if (options.travelModes?.length)
34
+ params.set('travel-modes', options.travelModes.join(','));
35
+ const qs = params.toString();
36
+ return `${apiUrl}/maps/${mapStyle}/descriptor${qs ? `?${qs}` : ''}`;
37
+ }
38
+ /**
39
+ * Fetch the map style descriptor with authentication and apply descriptor-level modifications.
40
+ *
41
+ * Language is applied directly to the descriptor's layer definitions before MapLibre ever
42
+ * processes them, eliminating the visual flash that occurs when modifying layers post-load.
43
+ * All other style parameters (terrain, traffic, etc.) are passed as query parameters.
44
+ *
45
+ * The returned style object can be passed directly to `new maplibregl.Map({ style })` or
46
+ * `map.setStyle()`. Tile, glyph, and sprite requests still go through `transformRequest`
47
+ * for authentication — this only pre-processes the descriptor itself.
48
+ *
49
+ * @param apiUrl - Base URL of the Location Service API
50
+ * @param mapStyle - Map style name (e.g. 'Standard', 'Monochrome', 'Satellite', 'Hybrid')
51
+ * @param getToken - Callback returning the current auth token
52
+ * @param options - Style options; `language` is applied to the descriptor, all others become URL params
53
+ * @returns Modified MapLibre StyleSpecification object
54
+ *
55
+ * @example
56
+ * const style = await fetchMapStyle(API_URL, 'Standard', getToken, { colorScheme: 'Dark', language: 'fr' })
57
+ * const map = new maplibregl.Map({ style, transformRequest: createTransformRequest(API_URL, getToken) })
58
+ */
59
+ async function fetchMapStyle(apiUrl, mapStyle, getToken, options = {}) {
60
+ const { language, ...styleOptions } = options;
61
+ const url = buildMapStyleUrl(apiUrl, mapStyle, styleOptions);
62
+ const token = getToken();
63
+ const response = await fetch(url, {
64
+ headers: {
65
+ Authorization: `Bearer ${token}`,
66
+ Accept: 'application/json',
67
+ },
68
+ });
69
+ if (!response.ok) {
70
+ // Read the body. The API sends `{message, code, requestId}` and the message
71
+ // is the whole point of it — for a style request it is Amazon's own
72
+ // sentence, forwarded verbatim by location-service-api#89:
73
+ //
74
+ // 400 "Traffic is not supported for style."
75
+ // 400 "light is not a supported color scheme for style Standard."
76
+ //
77
+ // This used to throw `Failed to fetch map style: 400`, discarding all of it
78
+ // two lines before anyone could read it — the same defect #89 fixed in the
79
+ // API, one layer up. Reuses parseErrorResponse so a style failure arrives as
80
+ // the same LocationServiceException as every other call in this package,
81
+ // with `code`, `statusCode` and `requestId` intact.
82
+ throw (0, errors_js_1.parseErrorResponse)(response.status, response.statusText, await response.text(), response.headers);
83
+ }
84
+ const style = (await response.json());
85
+ if (language) {
86
+ applyLanguageToDescriptor(style, language);
87
+ }
88
+ return style;
89
+ }
90
+ /**
91
+ * Apply a preferred language to the name labels within a style descriptor.
92
+ * Mutates the style in place — call before passing to MapLibre.
93
+ *
94
+ * Only a `text-field` that reads a name property is rewritten. House numbers
95
+ * (`addr_housenumber`) and road shields (`shield_text`) used to be rewritten
96
+ * too, and vanished from every map that asked for a language (#28); the rule
97
+ * lives in `labelsByName` so this and `applyMapLanguage` cannot disagree.
98
+ */
99
+ function applyLanguageToDescriptor(style, language) {
100
+ const expression = (0, mapLanguage_js_1.languageExpression)(language);
101
+ for (const layer of style.layers) {
102
+ if (layer.type !== 'symbol')
103
+ continue;
104
+ const layout = layer.layout;
105
+ if (layout?.['text-field'] === undefined)
106
+ continue;
107
+ if (!(0, mapLanguage_js_1.labelsByName)(layout['text-field']))
108
+ continue;
109
+ layout['text-field'] = expression;
110
+ }
111
+ }
@@ -0,0 +1,85 @@
1
+ import type { ColorScheme, LabelSize, MapFeatureMode, ScaleBarUnit, StaticMapStyle } from './mapEnums.js';
2
+ /**
3
+ * Static maps: build the URL, send the right headers, get a Blob.
4
+ *
5
+ * This exists because `/maps/static/{fileName}` is the one map route MapLibre
6
+ * never requests, so `createTransformRequest` never sees it and every caller was
7
+ * left to hand-roll the fetch. Ours did — `nextjs-address-finder-full`'s
8
+ * NearbySearch.tsx carried a comment explaining the rule — which is the clearest
9
+ * sign it belonged in the library (client#25, api#35).
10
+ *
11
+ * Two things make a plain fetch fail, and neither is discoverable:
12
+ *
13
+ * 1. `Accept` MUST name the exact type. Measured against the sandbox on
14
+ * 2026-08-26: `image/png` -> 200, while `image/*`, `*\/*`, and the header a
15
+ * browser sends for `<img>` all -> 406. Not even `image/*` is enough.
16
+ *
17
+ * 2. The type follows the STYLE, not the file name, and the default is
18
+ * Satellite:
19
+ *
20
+ * Standard -> image/png
21
+ * Satellite -> image/jpeg <- the default when `style` is omitted
22
+ *
23
+ * So the naive `Accept: image/png` is wrong for the DEFAULT request. The API
24
+ * hit the mirror image of this bug itself: it hard-coded image/png on the
25
+ * response and labelled every default render — a JPEG — as a PNG.
26
+ *
27
+ * `<img src="...">` cannot be made to work: it sends neither `Authorization` nor
28
+ * an acceptable `Accept`. Fetching to a Blob is the only viable shape, which is
29
+ * why this returns one rather than a URL.
30
+ */
31
+ /** `map`, or `map@2x` for a retina render. There is no file extension. */
32
+ export type StaticMapFileName = 'map' | 'map@2x';
33
+ export interface StaticMapOptions {
34
+ /** Pixels, 64-1500. */
35
+ width: number;
36
+ /** Pixels, 64-1500. */
37
+ height: number;
38
+ /** `[longitude, latitude]`. */
39
+ center?: [number, number];
40
+ /** `[west, south, east, north]`. */
41
+ boundingBox?: [number, number, number, number];
42
+ /** Positions the render must contain, as `[lng, lat]` pairs. */
43
+ boundedPositions?: Array<[number, number]>;
44
+ zoom?: number;
45
+ radius?: number;
46
+ padding?: number;
47
+ cropLabels?: boolean;
48
+ style?: StaticMapStyle;
49
+ colorScheme?: ColorScheme;
50
+ labelSize?: LabelSize;
51
+ /**
52
+ * `Enabled` | `Disabled` -- NOT a list of categories. api#35 records this
53
+ * being passed as a string array against the SDK's Enabled|Disabled type,
54
+ * working only by stringification. Typed here so that cannot recur.
55
+ */
56
+ pointsOfInterests?: MapFeatureMode;
57
+ scaleBarUnit?: ScaleBarUnit;
58
+ /** Defaults to `map`. */
59
+ fileName?: StaticMapFileName;
60
+ }
61
+ /**
62
+ * The Accept header this request must send.
63
+ *
64
+ * Exported because it is the one rule a caller cannot guess, and anyone doing
65
+ * their own fetch — a server-side render, a proxy — needs it too. Satellite is
66
+ * the default, so an absent style means JPEG.
67
+ */
68
+ export declare function staticMapAccept(style?: StaticMapStyle): string;
69
+ /** Build the request URL without fetching it. */
70
+ export declare function buildStaticMapUrl(apiUrl: string, options: StaticMapOptions): string;
71
+ /**
72
+ * Fetch a static map as a Blob.
73
+ *
74
+ * @param apiUrl Base URL of the Location Service API
75
+ * @param options Render options; exactly one of center / boundingBox / boundedPositions
76
+ * @param getToken Callback returning the current auth token
77
+ *
78
+ * @example
79
+ * const blob = await fetchStaticMap(API_URL, {
80
+ * width: 640, height: 400, center: [151.2093, -33.8688], zoom: 14,
81
+ * style: 'Standard',
82
+ * }, getToken)
83
+ * const url = URL.createObjectURL(blob) // remember to revokeObjectURL
84
+ */
85
+ export declare function fetchStaticMap(apiUrl: string, options: StaticMapOptions, getToken: () => string | undefined): Promise<Blob>;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.staticMapAccept = staticMapAccept;
4
+ exports.buildStaticMapUrl = buildStaticMapUrl;
5
+ exports.fetchStaticMap = fetchStaticMap;
6
+ const errors_js_1 = require("../transport/errors.js");
7
+ /**
8
+ * The Accept header this request must send.
9
+ *
10
+ * Exported because it is the one rule a caller cannot guess, and anyone doing
11
+ * their own fetch — a server-side render, a proxy — needs it too. Satellite is
12
+ * the default, so an absent style means JPEG.
13
+ */
14
+ function staticMapAccept(style) {
15
+ return style === 'Standard' ? 'image/png' : 'image/jpeg';
16
+ }
17
+ /** A coordinate as the API accepts it: at most six decimals, no float noise. */
18
+ const coord = (n) => String(Number(n.toFixed(6)));
19
+ /** Build the request URL without fetching it. */
20
+ function buildStaticMapUrl(apiUrl, options) {
21
+ const { width, height, center, boundingBox, boundedPositions, fileName = 'map', ...rest } = options;
22
+ const params = new URLSearchParams({
23
+ width: String(width),
24
+ height: String(height),
25
+ });
26
+ // Positions go over the wire as comma-separated numbers. api#35 records that
27
+ // passing arrays straight through worked only by accidental stringification;
28
+ // doing it explicitly here means the shape is ours, not JavaScript's default.
29
+ //
30
+ // Each number is rounded to six decimals (#29). The API caps a coordinate at
31
+ // fourteen decimals, and a value straight from `map.getCenter()` routinely
32
+ // has fifteen or sixteen — so the obvious "render what the user is looking
33
+ // at" was a 400 blaming the FORMAT. Six decimals is ~10 cm, more than a
34
+ // raster render can show.
35
+ if (center)
36
+ params.set('center', center.map(coord).join(','));
37
+ if (boundingBox)
38
+ params.set('bounding-box', boundingBox.map(coord).join(','));
39
+ if (boundedPositions) {
40
+ params.set('bounded-positions', boundedPositions.flat().map(coord).join(','));
41
+ }
42
+ // The API accepts kebab-case on the wire and camelCase for older callers;
43
+ // kebab is the documented form, so emit that.
44
+ const KEBAB = { cropLabels: 'crop-labels' };
45
+ for (const [key, value] of Object.entries(rest)) {
46
+ if (value === undefined)
47
+ continue;
48
+ params.set(KEBAB[key] ?? key, String(value));
49
+ }
50
+ return `${apiUrl}/maps/static/${fileName}?${params}`;
51
+ }
52
+ /**
53
+ * Fetch a static map as a Blob.
54
+ *
55
+ * @param apiUrl Base URL of the Location Service API
56
+ * @param options Render options; exactly one of center / boundingBox / boundedPositions
57
+ * @param getToken Callback returning the current auth token
58
+ *
59
+ * @example
60
+ * const blob = await fetchStaticMap(API_URL, {
61
+ * width: 640, height: 400, center: [151.2093, -33.8688], zoom: 14,
62
+ * style: 'Standard',
63
+ * }, getToken)
64
+ * const url = URL.createObjectURL(blob) // remember to revokeObjectURL
65
+ */
66
+ async function fetchStaticMap(apiUrl, options, getToken) {
67
+ const response = await fetch(buildStaticMapUrl(apiUrl, options), {
68
+ headers: {
69
+ Authorization: `Bearer ${getToken()}`,
70
+ Accept: staticMapAccept(options.style),
71
+ },
72
+ });
73
+ if (!response.ok) {
74
+ // Same treatment as fetchMapStyle: the API's {message, code, requestId} is
75
+ // the useful part, and a bare "failed: 400" throws it away. The messages
76
+ // here are specific and actionable -- "'width' and 'height' are required",
77
+ // "Only one of center, bounding-box or bounded-positions may be set".
78
+ throw (0, errors_js_1.parseErrorResponse)(response.status, response.statusText, await response.text());
79
+ }
80
+ return response.blob();
81
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,55 @@
1
+ import type { RequestOptions } from '../transport/http.js';
2
+ import type { AppConfigClaims } from '../utils/tokenClaims.js';
3
+ export interface ConnectorConfig {
4
+ apiUrl?: string;
5
+ token?: string;
6
+ getToken?: () => Promise<{
7
+ token: string;
8
+ }>;
9
+ /**
10
+ * Sent as the `Origin` header on every request. The API requires an Origin it
11
+ * recognises on every data request and answers 403 without one, so every
12
+ * caller was setting it by hand on each `send`; this does it once.
13
+ */
14
+ origin?: string;
15
+ }
16
+ export interface SendOptions extends RequestOptions {
17
+ headers?: Record<string, string>;
18
+ }
19
+ /**
20
+ * LocationServiceConnector — server-side connector for the Location Service API.
21
+ *
22
+ * Backend-to-backend: automatic configuration from the environment, server-side
23
+ * token management, and the same transport (timeout, cancellation, retry) as the
24
+ * browser client.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const connector = new LocationServiceConnector({ origin: 'https://app.example.com' })
29
+ * const result = await connector.send(new SearchTextCommand({ QueryText: 'Space Needle' }))
30
+ * ```
31
+ */
32
+ export declare class LocationServiceConnector {
33
+ private configPromise;
34
+ private origin?;
35
+ readonly serviceId: string;
36
+ constructor(config?: ConnectorConfig);
37
+ /** One place that knows how a token is obtained, so nothing can drift. */
38
+ private resolveToken;
39
+ /**
40
+ * This application's own configuration, as carried on the access token
41
+ * (api#65) — bias precision, and the countries it is scoped to.
42
+ *
43
+ * Provided so an application can SHOW its own settings: populate a country
44
+ * selector with the markets it actually serves, label a settings screen, and
45
+ * so on. Being a few minutes stale is cosmetic for that.
46
+ *
47
+ * It is not an entitlement check. See AppConfigClaims for why acting on
48
+ * `countries` client-side makes requests fail that would otherwise succeed.
49
+ *
50
+ * Returns `{}` when the token carries no application config, which is the
51
+ * case until one is configured in the portal.
52
+ */
53
+ getAppConfig(): Promise<AppConfigClaims>;
54
+ send<TInput, TOutput>(command: TInput, options?: SendOptions): Promise<TOutput>;
55
+ }