@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.
- package/README.md +14 -0
- package/dist/adapters/GeoPlaces.d.ts +1 -1
- package/dist/auth/TokenProvider.js +3 -3
- package/dist/cjs/adapters/GeoPlaces.d.ts +64 -0
- package/dist/cjs/adapters/GeoPlaces.js +213 -0
- package/dist/cjs/auth/TokenProvider.d.ts +64 -0
- package/dist/cjs/auth/TokenProvider.js +144 -0
- package/dist/cjs/auth/tokenRefresh.d.ts +37 -0
- package/dist/cjs/auth/tokenRefresh.js +58 -0
- package/dist/cjs/client/GeoPlacesClient.d.ts +39 -0
- package/dist/cjs/client/GeoPlacesClient.js +68 -0
- package/dist/cjs/errors/LocationServiceException.d.ts +44 -0
- package/dist/cjs/errors/LocationServiceException.js +60 -0
- package/dist/cjs/index.d.ts +24 -0
- package/dist/cjs/index.js +72 -0
- package/dist/cjs/maps/Utils.d.ts +2 -0
- package/dist/cjs/maps/Utils.js +8 -0
- package/dist/cjs/maps/createTransformRequest.d.ts +10 -0
- package/dist/cjs/maps/createTransformRequest.js +43 -0
- package/dist/cjs/maps/mapEnums.d.ts +102 -0
- package/dist/cjs/maps/mapEnums.js +103 -0
- package/dist/cjs/maps/mapLanguage.d.ts +43 -0
- package/dist/cjs/maps/mapLanguage.js +75 -0
- package/dist/cjs/maps/mapPoi.d.ts +44 -0
- package/dist/cjs/maps/mapPoi.js +64 -0
- package/dist/cjs/maps/mapStyle.d.ts +77 -0
- package/dist/cjs/maps/mapStyle.js +111 -0
- package/dist/cjs/maps/staticMap.d.ts +85 -0
- package/dist/cjs/maps/staticMap.js +81 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/server/LocationServiceConnector.d.ts +55 -0
- package/dist/cjs/server/LocationServiceConnector.js +91 -0
- package/dist/cjs/server/getClientConfig.d.ts +40 -0
- package/dist/cjs/server/getClientConfig.js +130 -0
- package/dist/cjs/server/index.d.ts +6 -0
- package/dist/cjs/server/index.js +9 -0
- package/dist/cjs/transport/endpoints.d.ts +2 -0
- package/dist/cjs/transport/endpoints.js +34 -0
- package/dist/cjs/transport/errors.d.ts +19 -0
- package/dist/cjs/transport/errors.js +104 -0
- package/dist/cjs/transport/http.d.ts +24 -0
- package/dist/cjs/transport/http.js +142 -0
- package/dist/cjs/types/index.d.ts +34 -0
- package/dist/cjs/types/index.js +3 -0
- package/dist/cjs/utils/roundPosition.d.ts +66 -0
- package/dist/cjs/utils/roundPosition.js +109 -0
- package/dist/cjs/utils/tokenClaims.d.ts +55 -0
- package/dist/cjs/utils/tokenClaims.js +61 -0
- package/dist/client/GeoPlacesClient.d.ts +3 -3
- package/dist/client/GeoPlacesClient.js +4 -4
- package/dist/index.d.ts +22 -22
- package/dist/index.js +12 -12
- package/dist/maps/Utils.d.ts +1 -1
- package/dist/maps/Utils.js +1 -1
- package/dist/maps/createTransformRequest.js +1 -1
- package/dist/maps/mapLanguage.d.ts +31 -7
- package/dist/maps/mapLanguage.js +50 -20
- package/dist/maps/mapStyle.d.ts +1 -1
- package/dist/maps/mapStyle.js +17 -16
- package/dist/maps/staticMap.d.ts +1 -1
- package/dist/maps/staticMap.js +12 -4
- package/dist/server/LocationServiceConnector.d.ts +2 -2
- package/dist/server/LocationServiceConnector.js +6 -6
- package/dist/server/getClientConfig.d.ts +1 -1
- package/dist/server/getClientConfig.js +2 -2
- package/dist/server/index.d.ts +6 -6
- package/dist/server/index.js +3 -3
- package/dist/transport/endpoints.d.ts +1 -1
- package/dist/transport/endpoints.js +1 -1
- package/dist/transport/errors.d.ts +1 -1
- package/dist/transport/errors.js +1 -1
- package/dist/transport/http.js +2 -2
- package/package.json +33 -11
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Round coordinate arrays in command inputs so nearby callers share a server
|
|
3
|
+
* cache entry.
|
|
4
|
+
*
|
|
5
|
+
* `BiasPosition` is a hint about where to look, so collapsing it onto a grid
|
|
6
|
+
* lets two users in the same area reuse one upstream answer. `QueryPosition`
|
|
7
|
+
* is not a hint — reverse-geocode resolves an exact point a user clicked or a
|
|
8
|
+
* device reported, and rounding it returns a neighbour's address. It is left
|
|
9
|
+
* alone (RFC-0001 §2).
|
|
10
|
+
*
|
|
11
|
+
* WHY THE DEFAULT MOVED FROM 2 dp TO 3 dp
|
|
12
|
+
*
|
|
13
|
+
* 2 dp is a ~1.1 km grid, and that is not a coarser answer — it is a wrong
|
|
14
|
+
* one. Measured against Amazon Location directly, `QueryText: "cafe"` from
|
|
15
|
+
* Sydney CBD returns a completely different set of places when the bias moves
|
|
16
|
+
* 111 m:
|
|
17
|
+
*
|
|
18
|
+
* base -> Crepe de Paris | Deli Ziosa | CBD Patisserie
|
|
19
|
+
* +111 m -> Cafe Chocolat | Paradiso Cafe | Incanto Coffee
|
|
20
|
+
*
|
|
21
|
+
* So two users a kilometre apart both received whichever places were nearest
|
|
22
|
+
* the FIRST of them, with nothing in the response saying so. The server moved
|
|
23
|
+
* to 3 dp in api#21 — but this library rounds BEFORE sending, on both the
|
|
24
|
+
* browser and server paths, so the precision was already gone by the time the
|
|
25
|
+
* request arrived and that fix never reached anyone using the SDK. This is
|
|
26
|
+
* what makes it reach them.
|
|
27
|
+
*
|
|
28
|
+
* The cost is hit rate: cells shrink 100x in area. RFC-0001 §5 expected only
|
|
29
|
+
* "single-digit to low-double-digit percent" in mixed traffic, so there was
|
|
30
|
+
* little to protect, and a miss costs one upstream call while a wrong answer
|
|
31
|
+
* costs trust.
|
|
32
|
+
*/
|
|
33
|
+
/** The server's floor (api#65). A request for less is clamped up to it. */
|
|
34
|
+
export declare const DEFAULT_BIAS_DECIMALS = 3;
|
|
35
|
+
/**
|
|
36
|
+
* The band the server enforces (api#65). Mirrored here so the value this
|
|
37
|
+
* library rounds to is the value the server will actually key its cache by —
|
|
38
|
+
* rounding to something outside the band just means being wrong about what
|
|
39
|
+
* was sent.
|
|
40
|
+
*
|
|
41
|
+
* The floor is correctness: below 3 dp the nearest places are not the ones
|
|
42
|
+
* returned. The ceiling is cost: cells shrink 100x in area per decimal, so
|
|
43
|
+
* finer precision collapses the cache hit rate and every miss is a billable
|
|
44
|
+
* upstream call.
|
|
45
|
+
*/
|
|
46
|
+
export declare const MIN_BIAS_DECIMALS = 3;
|
|
47
|
+
export declare const MAX_BIAS_DECIMALS = 5;
|
|
48
|
+
/**
|
|
49
|
+
* Clamp a requested precision into the allowed band.
|
|
50
|
+
*
|
|
51
|
+
* Anything absent or malformed becomes the default rather than throwing: this
|
|
52
|
+
* runs on every request, and a surprising token claim must not break geocoding
|
|
53
|
+
* for an application that is otherwise working.
|
|
54
|
+
*/
|
|
55
|
+
export declare function clampBiasDecimals(requested?: unknown): number;
|
|
56
|
+
/**
|
|
57
|
+
* Shallow-clone the input and round position arrays that benefit from caching.
|
|
58
|
+
* Returns the original object if no position fields are present.
|
|
59
|
+
*
|
|
60
|
+
* @param input the command input
|
|
61
|
+
* @param decimals precision for this application; defaults to the floor.
|
|
62
|
+
* Comes from the `biasDecimals` JWT claim where the token
|
|
63
|
+
* carries one (api#65) — an application entitled to finer
|
|
64
|
+
* bias gets it without the caller configuring anything.
|
|
65
|
+
*/
|
|
66
|
+
export declare function roundPositionFields<T extends object>(input: T, decimals?: number): T;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Round coordinate arrays in command inputs so nearby callers share a server
|
|
4
|
+
* cache entry.
|
|
5
|
+
*
|
|
6
|
+
* `BiasPosition` is a hint about where to look, so collapsing it onto a grid
|
|
7
|
+
* lets two users in the same area reuse one upstream answer. `QueryPosition`
|
|
8
|
+
* is not a hint — reverse-geocode resolves an exact point a user clicked or a
|
|
9
|
+
* device reported, and rounding it returns a neighbour's address. It is left
|
|
10
|
+
* alone (RFC-0001 §2).
|
|
11
|
+
*
|
|
12
|
+
* WHY THE DEFAULT MOVED FROM 2 dp TO 3 dp
|
|
13
|
+
*
|
|
14
|
+
* 2 dp is a ~1.1 km grid, and that is not a coarser answer — it is a wrong
|
|
15
|
+
* one. Measured against Amazon Location directly, `QueryText: "cafe"` from
|
|
16
|
+
* Sydney CBD returns a completely different set of places when the bias moves
|
|
17
|
+
* 111 m:
|
|
18
|
+
*
|
|
19
|
+
* base -> Crepe de Paris | Deli Ziosa | CBD Patisserie
|
|
20
|
+
* +111 m -> Cafe Chocolat | Paradiso Cafe | Incanto Coffee
|
|
21
|
+
*
|
|
22
|
+
* So two users a kilometre apart both received whichever places were nearest
|
|
23
|
+
* the FIRST of them, with nothing in the response saying so. The server moved
|
|
24
|
+
* to 3 dp in api#21 — but this library rounds BEFORE sending, on both the
|
|
25
|
+
* browser and server paths, so the precision was already gone by the time the
|
|
26
|
+
* request arrived and that fix never reached anyone using the SDK. This is
|
|
27
|
+
* what makes it reach them.
|
|
28
|
+
*
|
|
29
|
+
* The cost is hit rate: cells shrink 100x in area. RFC-0001 §5 expected only
|
|
30
|
+
* "single-digit to low-double-digit percent" in mixed traffic, so there was
|
|
31
|
+
* little to protect, and a miss costs one upstream call while a wrong answer
|
|
32
|
+
* costs trust.
|
|
33
|
+
*/
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.MAX_BIAS_DECIMALS = exports.MIN_BIAS_DECIMALS = exports.DEFAULT_BIAS_DECIMALS = void 0;
|
|
36
|
+
exports.clampBiasDecimals = clampBiasDecimals;
|
|
37
|
+
exports.roundPositionFields = roundPositionFields;
|
|
38
|
+
/** The server's floor (api#65). A request for less is clamped up to it. */
|
|
39
|
+
exports.DEFAULT_BIAS_DECIMALS = 3;
|
|
40
|
+
/**
|
|
41
|
+
* The band the server enforces (api#65). Mirrored here so the value this
|
|
42
|
+
* library rounds to is the value the server will actually key its cache by —
|
|
43
|
+
* rounding to something outside the band just means being wrong about what
|
|
44
|
+
* was sent.
|
|
45
|
+
*
|
|
46
|
+
* The floor is correctness: below 3 dp the nearest places are not the ones
|
|
47
|
+
* returned. The ceiling is cost: cells shrink 100x in area per decimal, so
|
|
48
|
+
* finer precision collapses the cache hit rate and every miss is a billable
|
|
49
|
+
* upstream call.
|
|
50
|
+
*/
|
|
51
|
+
exports.MIN_BIAS_DECIMALS = 3;
|
|
52
|
+
exports.MAX_BIAS_DECIMALS = 5;
|
|
53
|
+
/**
|
|
54
|
+
* Clamp a requested precision into the allowed band.
|
|
55
|
+
*
|
|
56
|
+
* Anything absent or malformed becomes the default rather than throwing: this
|
|
57
|
+
* runs on every request, and a surprising token claim must not break geocoding
|
|
58
|
+
* for an application that is otherwise working.
|
|
59
|
+
*/
|
|
60
|
+
function clampBiasDecimals(requested) {
|
|
61
|
+
const n = Number(requested);
|
|
62
|
+
if ((typeof requested !== 'number' &&
|
|
63
|
+
!(typeof requested === 'string' && requested.trim() !== '')) ||
|
|
64
|
+
!Number.isFinite(n)) {
|
|
65
|
+
return exports.DEFAULT_BIAS_DECIMALS;
|
|
66
|
+
}
|
|
67
|
+
const floored = Math.floor(n);
|
|
68
|
+
if (floored < exports.MIN_BIAS_DECIMALS)
|
|
69
|
+
return exports.MIN_BIAS_DECIMALS;
|
|
70
|
+
if (floored > exports.MAX_BIAS_DECIMALS)
|
|
71
|
+
return exports.MAX_BIAS_DECIMALS;
|
|
72
|
+
return floored;
|
|
73
|
+
}
|
|
74
|
+
/** Known position field names and whether they should be rounded */
|
|
75
|
+
const POSITION_FIELDS = {
|
|
76
|
+
BiasPosition: true, // geocode, autocomplete, search — round for cache
|
|
77
|
+
QueryPosition: false, // reverse geocode — keep full precision
|
|
78
|
+
};
|
|
79
|
+
function roundCoord(value, decimals) {
|
|
80
|
+
const factor = 10 ** decimals;
|
|
81
|
+
return Math.round(value * factor) / factor;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Shallow-clone the input and round position arrays that benefit from caching.
|
|
85
|
+
* Returns the original object if no position fields are present.
|
|
86
|
+
*
|
|
87
|
+
* @param input the command input
|
|
88
|
+
* @param decimals precision for this application; defaults to the floor.
|
|
89
|
+
* Comes from the `biasDecimals` JWT claim where the token
|
|
90
|
+
* carries one (api#65) — an application entitled to finer
|
|
91
|
+
* bias gets it without the caller configuring anything.
|
|
92
|
+
*/
|
|
93
|
+
function roundPositionFields(input, decimals = exports.DEFAULT_BIAS_DECIMALS) {
|
|
94
|
+
if (!input || typeof input !== 'object')
|
|
95
|
+
return input;
|
|
96
|
+
const dp = clampBiasDecimals(decimals);
|
|
97
|
+
let cloned = null;
|
|
98
|
+
for (const [field, shouldRound] of Object.entries(POSITION_FIELDS)) {
|
|
99
|
+
if (!shouldRound)
|
|
100
|
+
continue;
|
|
101
|
+
const value = input[field];
|
|
102
|
+
if (!Array.isArray(value))
|
|
103
|
+
continue;
|
|
104
|
+
if (!cloned)
|
|
105
|
+
cloned = { ...input };
|
|
106
|
+
cloned[field] = value.map((v) => typeof v === 'number' && Number.isFinite(v) ? roundCoord(v, dp) : v);
|
|
107
|
+
}
|
|
108
|
+
return cloned ?? input;
|
|
109
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read advisory application config out of the access token (api#65).
|
|
3
|
+
*
|
|
4
|
+
* The API puts an application's own settings — `biasDecimals`, `countries` —
|
|
5
|
+
* into the JWT alongside `allowedDomain` and `allowedResources`, so this
|
|
6
|
+
* library can stop hard-coding values it has no other way of knowing.
|
|
7
|
+
*
|
|
8
|
+
* DELIBERATELY UNVERIFIED, and that is safe. This library has no signing key
|
|
9
|
+
* and does not need one: every claim here is re-read from the application row
|
|
10
|
+
* by the API on each request, and the API's answer is the one that counts. A
|
|
11
|
+
* forged token would fail at the authorizer long before any of this mattered.
|
|
12
|
+
* What is read here only decides how the request is SHAPED — a hint, never a
|
|
13
|
+
* permission.
|
|
14
|
+
*
|
|
15
|
+
* A JWT is signed, not encrypted, so the payload is plain base64url. Nothing
|
|
16
|
+
* secret is in it; these are the caller's own settings.
|
|
17
|
+
*/
|
|
18
|
+
export interface AppConfigClaims {
|
|
19
|
+
/**
|
|
20
|
+
* Bias precision this application is entitled to.
|
|
21
|
+
*
|
|
22
|
+
* Safe to act on: it only changes how a coordinate is rounded before
|
|
23
|
+
* sending, and the server re-rounds to its own configured value anyway. A
|
|
24
|
+
* stale value here costs precision, never correctness.
|
|
25
|
+
*/
|
|
26
|
+
biasDecimals?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Countries this application may search, ISO 3166-1 alpha-2.
|
|
29
|
+
*
|
|
30
|
+
* READ THIS, DO NOT ACT ON IT. It is here to be displayed — a country
|
|
31
|
+
* selector, a settings screen, a "this application serves AU and NZ" label.
|
|
32
|
+
*
|
|
33
|
+
* Do not inject it into requests and do not reject requests with it. The
|
|
34
|
+
* token is a snapshot up to fifteen minutes old; the API reads the scope
|
|
35
|
+
* fresh from the application row on every request. Acting on a stale value
|
|
36
|
+
* makes things WORSE, in both directions:
|
|
37
|
+
*
|
|
38
|
+
* app is now scoped to NZ, token still says AU
|
|
39
|
+
* send nothing -> API injects [NZ] -> 200
|
|
40
|
+
* inject stale [AU] -> outside scope -> 400
|
|
41
|
+
*
|
|
42
|
+
* So a request that would have succeeded fails instead. Rejecting locally
|
|
43
|
+
* has the mirror-image bug: refusing something the API would now allow.
|
|
44
|
+
* Sending nothing and letting the API scope the request is always correct.
|
|
45
|
+
*/
|
|
46
|
+
countries?: string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Decode a JWT payload without verifying it.
|
|
50
|
+
*
|
|
51
|
+
* Returns `{}` for anything unparseable. This runs on every request, so a
|
|
52
|
+
* surprising token must degrade to "no claims" rather than break geocoding
|
|
53
|
+
* for an application that is otherwise working.
|
|
54
|
+
*/
|
|
55
|
+
export declare function readAppConfigClaims(token?: string | null): AppConfigClaims;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Read advisory application config out of the access token (api#65).
|
|
4
|
+
*
|
|
5
|
+
* The API puts an application's own settings — `biasDecimals`, `countries` —
|
|
6
|
+
* into the JWT alongside `allowedDomain` and `allowedResources`, so this
|
|
7
|
+
* library can stop hard-coding values it has no other way of knowing.
|
|
8
|
+
*
|
|
9
|
+
* DELIBERATELY UNVERIFIED, and that is safe. This library has no signing key
|
|
10
|
+
* and does not need one: every claim here is re-read from the application row
|
|
11
|
+
* by the API on each request, and the API's answer is the one that counts. A
|
|
12
|
+
* forged token would fail at the authorizer long before any of this mattered.
|
|
13
|
+
* What is read here only decides how the request is SHAPED — a hint, never a
|
|
14
|
+
* permission.
|
|
15
|
+
*
|
|
16
|
+
* A JWT is signed, not encrypted, so the payload is plain base64url. Nothing
|
|
17
|
+
* secret is in it; these are the caller's own settings.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.readAppConfigClaims = readAppConfigClaims;
|
|
21
|
+
/**
|
|
22
|
+
* Decode a JWT payload without verifying it.
|
|
23
|
+
*
|
|
24
|
+
* Returns `{}` for anything unparseable. This runs on every request, so a
|
|
25
|
+
* surprising token must degrade to "no claims" rather than break geocoding
|
|
26
|
+
* for an application that is otherwise working.
|
|
27
|
+
*/
|
|
28
|
+
function readAppConfigClaims(token) {
|
|
29
|
+
if (typeof token !== 'string')
|
|
30
|
+
return {};
|
|
31
|
+
const parts = token.split('.');
|
|
32
|
+
if (parts.length !== 3)
|
|
33
|
+
return {};
|
|
34
|
+
try {
|
|
35
|
+
// base64url -> base64: JWT omits padding and swaps two characters.
|
|
36
|
+
const b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
37
|
+
const padded = b64.padEnd(b64.length + ((4 - (b64.length % 4)) % 4), '=');
|
|
38
|
+
// `atob` exists in browsers and in Node 16+, so one path serves both.
|
|
39
|
+
const json = decodeURIComponent(Array.from(atob(padded), (c) => `%${c.charCodeAt(0).toString(16).padStart(2, '0')}`).join(''));
|
|
40
|
+
const payload = JSON.parse(json);
|
|
41
|
+
const claims = {};
|
|
42
|
+
if (typeof payload.biasDecimals === 'number') {
|
|
43
|
+
claims.biasDecimals = payload.biasDecimals;
|
|
44
|
+
}
|
|
45
|
+
else if (typeof payload.biasDecimals === 'string' &&
|
|
46
|
+
payload.biasDecimals.trim() !== '') {
|
|
47
|
+
const n = Number(payload.biasDecimals);
|
|
48
|
+
if (Number.isFinite(n))
|
|
49
|
+
claims.biasDecimals = n;
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(payload.countries)) {
|
|
52
|
+
const list = payload.countries.filter((c) => typeof c === 'string');
|
|
53
|
+
if (list.length)
|
|
54
|
+
claims.countries = list;
|
|
55
|
+
}
|
|
56
|
+
return claims;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { RequestOptions } from '../transport/http';
|
|
2
|
-
import type { ClientConfig } from '../types';
|
|
3
|
-
import type { AppConfigClaims } from '../utils/tokenClaims';
|
|
1
|
+
import type { RequestOptions } from '../transport/http.js';
|
|
2
|
+
import type { ClientConfig } from '../types/index.js';
|
|
3
|
+
import type { AppConfigClaims } from '../utils/tokenClaims.js';
|
|
4
4
|
export type SendOptions = RequestOptions;
|
|
5
5
|
/**
|
|
6
6
|
* GeoPlacesClient — AWS Location Service compatible client with custom auth.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import debug from 'debug';
|
|
2
|
-
import { resolveEndpoint } from '../transport/endpoints';
|
|
3
|
-
import { requestJson } from '../transport/http';
|
|
4
|
-
import { roundPositionFields } from '../utils/roundPosition';
|
|
5
|
-
import { readAppConfigClaims } from '../utils/tokenClaims';
|
|
2
|
+
import { resolveEndpoint } from '../transport/endpoints.js';
|
|
3
|
+
import { requestJson } from '../transport/http.js';
|
|
4
|
+
import { roundPositionFields } from '../utils/roundPosition.js';
|
|
5
|
+
import { readAppConfigClaims } from '../utils/tokenClaims.js';
|
|
6
6
|
const log = debug('location-client:api');
|
|
7
7
|
/**
|
|
8
8
|
* GeoPlacesClient — AWS Location Service compatible client with custom auth.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
export { GeoPlacesClient } from './client/GeoPlacesClient';
|
|
2
|
-
export type { SendOptions } from './client/GeoPlacesClient';
|
|
3
|
-
export { DEFAULT_MAX_ATTEMPTS, DEFAULT_TIMEOUT_MS } from './transport/http';
|
|
4
|
-
export type { RequestOptions } from './transport/http';
|
|
5
|
-
export { TOKEN_REFRESH_BUFFER_SECONDS, readTokenExpiry, } from './auth/tokenRefresh';
|
|
6
|
-
export { LocationServiceException } from './errors/LocationServiceException';
|
|
7
|
-
export type { LocationServiceExceptionOptions } from './errors/LocationServiceException';
|
|
1
|
+
export { GeoPlacesClient } from './client/GeoPlacesClient.js';
|
|
2
|
+
export type { SendOptions } from './client/GeoPlacesClient.js';
|
|
3
|
+
export { DEFAULT_MAX_ATTEMPTS, DEFAULT_TIMEOUT_MS } from './transport/http.js';
|
|
4
|
+
export type { RequestOptions } from './transport/http.js';
|
|
5
|
+
export { TOKEN_REFRESH_BUFFER_SECONDS, readTokenExpiry, } from './auth/tokenRefresh.js';
|
|
6
|
+
export { LocationServiceException } from './errors/LocationServiceException.js';
|
|
7
|
+
export type { LocationServiceExceptionOptions } from './errors/LocationServiceException.js';
|
|
8
8
|
export * from '@aws-sdk/client-geo-places';
|
|
9
9
|
export * from '@aws/amazon-location-utilities-datatypes';
|
|
10
|
-
export { GeoPlaces } from './adapters/GeoPlaces';
|
|
11
|
-
export type { GeoPlacesDetailOptions, GeoPlacesOptions, } from './adapters/GeoPlaces';
|
|
12
|
-
export { createTransformRequest } from './maps/createTransformRequest';
|
|
13
|
-
export { applyMapLanguage } from './maps/mapLanguage';
|
|
14
|
-
export { POI_CATEGORIES, setAllPoiVisibility, setPoiVisibility, } from './maps/mapPoi';
|
|
15
|
-
export type { PoiCategory } from './maps/mapPoi';
|
|
16
|
-
export { buildMapStyleUrl, fetchMapStyle } from './maps/mapStyle';
|
|
17
|
-
export type { MapStyleOptions } from './maps/mapStyle';
|
|
18
|
-
export { buildStaticMapUrl, fetchStaticMap, staticMapAccept, } from './maps/staticMap';
|
|
19
|
-
export type { StaticMapFileName, StaticMapOptions } from './maps/staticMap';
|
|
20
|
-
export { BUILDINGS, COLOR_SCHEMES, CONTOUR_DENSITIES, LABEL_SIZES, MAP_FEATURE_MODES, MAP_STYLES, SCALE_BAR_UNITS, SPRITE_VARIANTS, STATIC_MAP_STYLES, TERRAINS, TRAFFIC_MODES, TRAVEL_MODES, } from './maps/mapEnums';
|
|
21
|
-
export type { Buildings, ColorScheme, ContourDensity, LabelSize, MapFeatureMode, MapStyle, ScaleBarUnit, SpriteVariant, StaticMapStyle, Terrain, TrafficMode, TravelMode, } from './maps/mapEnums';
|
|
22
|
-
export { transformRequest } from './maps/Utils';
|
|
23
|
-
export type { ClientConfig, GeoPlacesCommand, MapLike } from './types';
|
|
24
|
-
export type { AppConfigClaims } from './utils/tokenClaims';
|
|
10
|
+
export { GeoPlaces } from './adapters/GeoPlaces.js';
|
|
11
|
+
export type { GeoPlacesDetailOptions, GeoPlacesOptions, } from './adapters/GeoPlaces.js';
|
|
12
|
+
export { createTransformRequest } from './maps/createTransformRequest.js';
|
|
13
|
+
export { applyMapLanguage } from './maps/mapLanguage.js';
|
|
14
|
+
export { POI_CATEGORIES, setAllPoiVisibility, setPoiVisibility, } from './maps/mapPoi.js';
|
|
15
|
+
export type { PoiCategory } from './maps/mapPoi.js';
|
|
16
|
+
export { buildMapStyleUrl, fetchMapStyle } from './maps/mapStyle.js';
|
|
17
|
+
export type { MapStyleOptions } from './maps/mapStyle.js';
|
|
18
|
+
export { buildStaticMapUrl, fetchStaticMap, staticMapAccept, } from './maps/staticMap.js';
|
|
19
|
+
export type { StaticMapFileName, StaticMapOptions } from './maps/staticMap.js';
|
|
20
|
+
export { BUILDINGS, COLOR_SCHEMES, CONTOUR_DENSITIES, LABEL_SIZES, MAP_FEATURE_MODES, MAP_STYLES, SCALE_BAR_UNITS, SPRITE_VARIANTS, STATIC_MAP_STYLES, TERRAINS, TRAFFIC_MODES, TRAVEL_MODES, } from './maps/mapEnums.js';
|
|
21
|
+
export type { Buildings, ColorScheme, ContourDensity, LabelSize, MapFeatureMode, MapStyle, ScaleBarUnit, SpriteVariant, StaticMapStyle, Terrain, TrafficMode, TravelMode, } from './maps/mapEnums.js';
|
|
22
|
+
export { transformRequest } from './maps/Utils.js';
|
|
23
|
+
export type { ClientConfig, GeoPlacesCommand, MapLike } from './types/index.js';
|
|
24
|
+
export type { AppConfigClaims } from './utils/tokenClaims.js';
|
package/dist/index.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
// Client (Custom - uses our auth instead of AWS SigV4)
|
|
2
|
-
export { GeoPlacesClient } from './client/GeoPlacesClient';
|
|
2
|
+
export { GeoPlacesClient } from './client/GeoPlacesClient.js';
|
|
3
3
|
// Transport options — cancellation, per-attempt timeout, retry policy
|
|
4
|
-
export { DEFAULT_MAX_ATTEMPTS, DEFAULT_TIMEOUT_MS } from './transport/http';
|
|
4
|
+
export { DEFAULT_MAX_ATTEMPTS, DEFAULT_TIMEOUT_MS } from './transport/http.js';
|
|
5
5
|
// Token refresh policy — shared by the server provider and the React provider
|
|
6
|
-
export { TOKEN_REFRESH_BUFFER_SECONDS, readTokenExpiry, } from './auth/tokenRefresh';
|
|
6
|
+
export { TOKEN_REFRESH_BUFFER_SECONDS, readTokenExpiry, } from './auth/tokenRefresh.js';
|
|
7
7
|
// Errors
|
|
8
|
-
export { LocationServiceException } from './errors/LocationServiceException';
|
|
8
|
+
export { LocationServiceException } from './errors/LocationServiceException.js';
|
|
9
9
|
// Re-export AWS SDK commands and types
|
|
10
10
|
export * from '@aws-sdk/client-geo-places';
|
|
11
11
|
// Re-export AWS Location Utilities (data type conversions)
|
|
12
12
|
export * from '@aws/amazon-location-utilities-datatypes';
|
|
13
13
|
// Adapters (Custom - for MapLibre integration)
|
|
14
|
-
export { GeoPlaces } from './adapters/GeoPlaces';
|
|
14
|
+
export { GeoPlaces } from './adapters/GeoPlaces.js';
|
|
15
15
|
// Maps utilities
|
|
16
|
-
export { createTransformRequest } from './maps/createTransformRequest';
|
|
17
|
-
export { applyMapLanguage } from './maps/mapLanguage';
|
|
18
|
-
export { POI_CATEGORIES, setAllPoiVisibility, setPoiVisibility, } from './maps/mapPoi';
|
|
19
|
-
export { buildMapStyleUrl, fetchMapStyle } from './maps/mapStyle';
|
|
20
|
-
export { buildStaticMapUrl, fetchStaticMap, staticMapAccept, } from './maps/staticMap';
|
|
16
|
+
export { createTransformRequest } from './maps/createTransformRequest.js';
|
|
17
|
+
export { applyMapLanguage } from './maps/mapLanguage.js';
|
|
18
|
+
export { POI_CATEGORIES, setAllPoiVisibility, setPoiVisibility, } from './maps/mapPoi.js';
|
|
19
|
+
export { buildMapStyleUrl, fetchMapStyle } from './maps/mapStyle.js';
|
|
20
|
+
export { buildStaticMapUrl, fetchStaticMap, staticMapAccept, } from './maps/staticMap.js';
|
|
21
21
|
// Accepted values for every map parameter, as VALUES so a picker can be built
|
|
22
22
|
// from them, plus the matching types. Case sensitive — see mapEnums.ts.
|
|
23
|
-
export { BUILDINGS, COLOR_SCHEMES, CONTOUR_DENSITIES, LABEL_SIZES, MAP_FEATURE_MODES, MAP_STYLES, SCALE_BAR_UNITS, SPRITE_VARIANTS, STATIC_MAP_STYLES, TERRAINS, TRAFFIC_MODES, TRAVEL_MODES, } from './maps/mapEnums';
|
|
24
|
-
export { transformRequest } from './maps/Utils';
|
|
23
|
+
export { BUILDINGS, COLOR_SCHEMES, CONTOUR_DENSITIES, LABEL_SIZES, MAP_FEATURE_MODES, MAP_STYLES, SCALE_BAR_UNITS, SPRITE_VARIANTS, STATIC_MAP_STYLES, TERRAINS, TRAFFIC_MODES, TRAVEL_MODES, } from './maps/mapEnums.js';
|
|
24
|
+
export { transformRequest } from './maps/Utils.js';
|
|
25
25
|
// Server-only utilities are available via '@chaosity/location-client/server'
|
package/dist/maps/Utils.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ClientConfig } from '../types';
|
|
1
|
+
import type { ClientConfig } from '../types/index.js';
|
|
2
2
|
export declare function transformRequest(url: string, config: ClientConfig): import("maplibre-gl").RequestParameters | Promise<import("maplibre-gl").RequestParameters> | undefined;
|
package/dist/maps/Utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createTransformRequest } from './createTransformRequest';
|
|
1
|
+
import { createTransformRequest } from './createTransformRequest.js';
|
|
2
2
|
export function transformRequest(url, config) {
|
|
3
3
|
const token = config.getToken?.() ?? config.token;
|
|
4
4
|
return createTransformRequest(config.apiUrl, () => token)(url);
|
|
@@ -1,14 +1,38 @@
|
|
|
1
|
-
import type { MapLike } from '../types';
|
|
1
|
+
import type { MapLike } from '../types/index.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* AWS GeoMaps vector tiles include language-specific name properties in `name:${lang}` format
|
|
6
|
-
* (e.g. `name:en`, `name:fr`, `name:ja`). This function updates the `text-field` expression
|
|
7
|
-
* on every symbol layer to prefer the requested language, falling back to English then the
|
|
8
|
-
* default name if the preferred language is unavailable for a feature.
|
|
3
|
+
* The `text-field` expression that prefers `language`, then English, then the
|
|
4
|
+
* feature's default name.
|
|
9
5
|
*
|
|
10
6
|
* Based on the approach documented at:
|
|
11
7
|
* https://docs.aws.amazon.com/location/latest/developerguide/how-to-set-preferred-language-map.html
|
|
8
|
+
*/
|
|
9
|
+
export declare function languageExpression(language: string): unknown[];
|
|
10
|
+
/**
|
|
11
|
+
* Whether a `text-field` reads a name property — the only kind of label a
|
|
12
|
+
* language rewrite makes sense for (#28).
|
|
13
|
+
*
|
|
14
|
+
* The AWS Standard style has 62 symbol layers with a text-field and 30 of
|
|
15
|
+
* them do NOT label by name: `building_label_number` reads
|
|
16
|
+
* `addr_housenumber`, and the 29 `shield_*` layers read `shield_text` or
|
|
17
|
+
* `ref`. Replacing those with a `name:<lang>` coalesce points them at a
|
|
18
|
+
* property their features do not carry, and the house numbers and road
|
|
19
|
+
* shields disappear — for `en` too. Measured against the live sandbox on
|
|
20
|
+
* 2026-08-29; the testbed showed it as "one map has house numbers, the
|
|
21
|
+
* others don't".
|
|
22
|
+
*
|
|
23
|
+
* Serialising the expression is the simplest exact test: `name`, `name:en`,
|
|
24
|
+
* `name_en` and a literal `{name}` template all contain it, and none of the
|
|
25
|
+
* non-name properties do.
|
|
26
|
+
*/
|
|
27
|
+
export declare function labelsByName(textField: unknown): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Apply a preferred display language to the name labels on a MapLibre map.
|
|
30
|
+
*
|
|
31
|
+
* AWS GeoMaps vector tiles carry `name:${lang}` properties (`name:en`,
|
|
32
|
+
* `name:fr`, `name:ja`, …). Every symbol layer whose `text-field` reads a
|
|
33
|
+
* name is rewritten to prefer the requested language, falling back to
|
|
34
|
+
* English then the default name. Layers that label by something else — house
|
|
35
|
+
* numbers, road shields — are left exactly as the style declared them.
|
|
12
36
|
*
|
|
13
37
|
* @param map - MapLibre Map instance (or any object matching the MapLike interface)
|
|
14
38
|
* @param language - ISO 639-1 language code (e.g. 'en', 'fr', 'de', 'ja', 'zh', 'ar')
|
package/dist/maps/mapLanguage.js
CHANGED
|
@@ -1,13 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* AWS GeoMaps vector tiles include language-specific name properties in `name:${lang}` format
|
|
5
|
-
* (e.g. `name:en`, `name:fr`, `name:ja`). This function updates the `text-field` expression
|
|
6
|
-
* on every symbol layer to prefer the requested language, falling back to English then the
|
|
7
|
-
* default name if the preferred language is unavailable for a feature.
|
|
2
|
+
* The `text-field` expression that prefers `language`, then English, then the
|
|
3
|
+
* feature's default name.
|
|
8
4
|
*
|
|
9
5
|
* Based on the approach documented at:
|
|
10
6
|
* https://docs.aws.amazon.com/location/latest/developerguide/how-to-set-preferred-language-map.html
|
|
7
|
+
*/
|
|
8
|
+
export function languageExpression(language) {
|
|
9
|
+
return language === 'en'
|
|
10
|
+
? ['coalesce', ['get', 'name:en'], ['get', 'name']]
|
|
11
|
+
: [
|
|
12
|
+
'coalesce',
|
|
13
|
+
['get', `name:${language}`],
|
|
14
|
+
['get', 'name:en'],
|
|
15
|
+
['get', 'name'],
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Whether a `text-field` reads a name property — the only kind of label a
|
|
20
|
+
* language rewrite makes sense for (#28).
|
|
21
|
+
*
|
|
22
|
+
* The AWS Standard style has 62 symbol layers with a text-field and 30 of
|
|
23
|
+
* them do NOT label by name: `building_label_number` reads
|
|
24
|
+
* `addr_housenumber`, and the 29 `shield_*` layers read `shield_text` or
|
|
25
|
+
* `ref`. Replacing those with a `name:<lang>` coalesce points them at a
|
|
26
|
+
* property their features do not carry, and the house numbers and road
|
|
27
|
+
* shields disappear — for `en` too. Measured against the live sandbox on
|
|
28
|
+
* 2026-08-29; the testbed showed it as "one map has house numbers, the
|
|
29
|
+
* others don't".
|
|
30
|
+
*
|
|
31
|
+
* Serialising the expression is the simplest exact test: `name`, `name:en`,
|
|
32
|
+
* `name_en` and a literal `{name}` template all contain it, and none of the
|
|
33
|
+
* non-name properties do.
|
|
34
|
+
*/
|
|
35
|
+
export function labelsByName(textField) {
|
|
36
|
+
return JSON.stringify(textField)?.includes('name') ?? false;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Apply a preferred display language to the name labels on a MapLibre map.
|
|
40
|
+
*
|
|
41
|
+
* AWS GeoMaps vector tiles carry `name:${lang}` properties (`name:en`,
|
|
42
|
+
* `name:fr`, `name:ja`, …). Every symbol layer whose `text-field` reads a
|
|
43
|
+
* name is rewritten to prefer the requested language, falling back to
|
|
44
|
+
* English then the default name. Layers that label by something else — house
|
|
45
|
+
* numbers, road shields — are left exactly as the style declared them.
|
|
11
46
|
*
|
|
12
47
|
* @param map - MapLibre Map instance (or any object matching the MapLike interface)
|
|
13
48
|
* @param language - ISO 639-1 language code (e.g. 'en', 'fr', 'de', 'ja', 'zh', 'ar')
|
|
@@ -17,21 +52,16 @@
|
|
|
17
52
|
*/
|
|
18
53
|
export function applyMapLanguage(map, language) {
|
|
19
54
|
try {
|
|
20
|
-
const expression = language
|
|
21
|
-
? ['coalesce', ['get', 'name:en'], ['get', 'name']]
|
|
22
|
-
: [
|
|
23
|
-
'coalesce',
|
|
24
|
-
['get', `name:${language}`],
|
|
25
|
-
['get', 'name:en'],
|
|
26
|
-
['get', 'name'],
|
|
27
|
-
];
|
|
55
|
+
const expression = languageExpression(language);
|
|
28
56
|
map.getStyle().layers.forEach((layer) => {
|
|
29
|
-
if (layer.type
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
57
|
+
if (layer.type !== 'symbol')
|
|
58
|
+
return;
|
|
59
|
+
const textField = map.getLayoutProperty(layer.id, 'text-field');
|
|
60
|
+
if (textField === undefined || textField === null)
|
|
61
|
+
return;
|
|
62
|
+
if (!labelsByName(textField))
|
|
63
|
+
return;
|
|
64
|
+
map.setLayoutProperty(layer.id, 'text-field', expression);
|
|
35
65
|
});
|
|
36
66
|
}
|
|
37
67
|
catch {
|
package/dist/maps/mapStyle.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StyleSpecification } from 'maplibre-gl';
|
|
2
|
-
import type { Buildings, ColorScheme, ContourDensity, MapStyle, Terrain, TrafficMode, TravelMode } from './mapEnums';
|
|
2
|
+
import type { Buildings, ColorScheme, ContourDensity, MapStyle, Terrain, TrafficMode, TravelMode } from './mapEnums.js';
|
|
3
3
|
/**
|
|
4
4
|
* Options for building an AWS Location Service map style URL.
|
|
5
5
|
* All parameters map directly to query parameters supported by the style descriptor endpoint.
|
package/dist/maps/mapStyle.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { parseErrorResponse } from '../transport/errors';
|
|
1
|
+
import { parseErrorResponse } from '../transport/errors.js';
|
|
2
|
+
import { labelsByName, languageExpression } from './mapLanguage.js';
|
|
2
3
|
/**
|
|
3
4
|
* Build a map style descriptor URL for the Location Service API.
|
|
4
5
|
*
|
|
@@ -83,24 +84,24 @@ export async function fetchMapStyle(apiUrl, mapStyle, getToken, options = {}) {
|
|
|
83
84
|
return style;
|
|
84
85
|
}
|
|
85
86
|
/**
|
|
86
|
-
* Apply a preferred language to
|
|
87
|
+
* Apply a preferred language to the name labels within a style descriptor.
|
|
87
88
|
* Mutates the style in place — call before passing to MapLibre.
|
|
89
|
+
*
|
|
90
|
+
* Only a `text-field` that reads a name property is rewritten. House numbers
|
|
91
|
+
* (`addr_housenumber`) and road shields (`shield_text`) used to be rewritten
|
|
92
|
+
* too, and vanished from every map that asked for a language (#28); the rule
|
|
93
|
+
* lives in `labelsByName` so this and `applyMapLanguage` cannot disagree.
|
|
88
94
|
*/
|
|
89
95
|
function applyLanguageToDescriptor(style, language) {
|
|
90
|
-
const expression = language
|
|
91
|
-
? ['coalesce', ['get', 'name:en'], ['get', 'name']]
|
|
92
|
-
: [
|
|
93
|
-
'coalesce',
|
|
94
|
-
['get', `name:${language}`],
|
|
95
|
-
['get', 'name:en'],
|
|
96
|
-
['get', 'name'],
|
|
97
|
-
];
|
|
96
|
+
const expression = languageExpression(language);
|
|
98
97
|
for (const layer of style.layers) {
|
|
99
|
-
if (layer.type
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
if (layer.type !== 'symbol')
|
|
99
|
+
continue;
|
|
100
|
+
const layout = layer.layout;
|
|
101
|
+
if (layout?.['text-field'] === undefined)
|
|
102
|
+
continue;
|
|
103
|
+
if (!labelsByName(layout['text-field']))
|
|
104
|
+
continue;
|
|
105
|
+
layout['text-field'] = expression;
|
|
105
106
|
}
|
|
106
107
|
}
|
package/dist/maps/staticMap.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ColorScheme, LabelSize, MapFeatureMode, ScaleBarUnit, StaticMapStyle } from './mapEnums';
|
|
1
|
+
import type { ColorScheme, LabelSize, MapFeatureMode, ScaleBarUnit, StaticMapStyle } from './mapEnums.js';
|
|
2
2
|
/**
|
|
3
3
|
* Static maps: build the URL, send the right headers, get a Blob.
|
|
4
4
|
*
|