@chaosity/location-client 0.1.4 → 0.1.5
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/dist/adapters/GeoPlaces.d.ts +9 -17
- package/dist/adapters/GeoPlaces.js +118 -29
- package/dist/auth/TokenProvider.d.ts +3 -3
- package/dist/auth/TokenProvider.js +39 -15
- package/dist/client/GeoPlacesClient.js +45 -27
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/maps/Utils.d.ts +8 -0
- package/dist/maps/Utils.js +24 -0
- package/dist/maps/createTransformRequest.d.ts +10 -0
- package/dist/maps/createTransformRequest.js +40 -0
- package/dist/server/LocationServiceConnector.d.ts +46 -0
- package/dist/server/LocationServiceConnector.js +110 -0
- package/dist/server/getClientConfig.d.ts +26 -5
- package/dist/server/getClientConfig.js +46 -5
- package/dist/server/getConnector.d.ts +37 -0
- package/dist/server/getConnector.js +34 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +2 -0
- package/package.json +18 -3
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MaplibreGeocoderApi, MaplibreGeocoderApiConfig, MaplibreGeocoderFeatureResults, MaplibreGeocoderPlaceResults, MaplibreGeocoderSuggestionResults } from '@maplibre/maplibre-gl-geocoder';
|
|
2
2
|
import type { Map } from 'maplibre-gl';
|
|
3
3
|
import { GeoPlacesClient } from '../client/GeoPlacesClient';
|
|
4
4
|
/**
|
|
5
5
|
* GeoPlaces - MapLibre adapter for AWS Location Service
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* Only auth is custom (Bearer token instead of SigV4).
|
|
7
|
+
* Implements MaplibreGeocoderApi interface for compatibility with @maplibre/maplibre-gl-geocoder
|
|
9
8
|
*/
|
|
10
|
-
export declare class GeoPlaces {
|
|
9
|
+
export declare class GeoPlaces implements MaplibreGeocoderApi {
|
|
11
10
|
private client;
|
|
12
11
|
private map;
|
|
13
12
|
constructor(client: GeoPlacesClient, map: Map);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
limit?: number;
|
|
21
|
-
click?: boolean;
|
|
22
|
-
}): Promise<FeatureCollection>;
|
|
23
|
-
getSuggestions(config: {
|
|
24
|
-
query: string;
|
|
25
|
-
}): Promise<FeatureCollection>;
|
|
26
|
-
searchByPlaceId(placeId: string): Promise<FeatureCollection>;
|
|
13
|
+
private normalizeLanguage;
|
|
14
|
+
forwardGeocode(config: MaplibreGeocoderApiConfig): Promise<MaplibreGeocoderFeatureResults>;
|
|
15
|
+
reverseGeocode(config: MaplibreGeocoderApiConfig): Promise<MaplibreGeocoderFeatureResults>;
|
|
16
|
+
getSuggestions(config: MaplibreGeocoderApiConfig): Promise<MaplibreGeocoderSuggestionResults>;
|
|
17
|
+
searchByPlaceId(config: MaplibreGeocoderApiConfig): Promise<MaplibreGeocoderPlaceResults>;
|
|
18
|
+
localGeocode(config: MaplibreGeocoderApiConfig): Promise<MaplibreGeocoderFeatureResults>;
|
|
27
19
|
}
|
|
@@ -1,58 +1,147 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { GeocodeAdditionalFeature, GeocodeCommand, GetPlaceAdditionalFeature, GetPlaceCommand, ReverseGeocodeCommand } from '@aws-sdk/client-geo-places';
|
|
2
|
+
import { geocodeResponseToFeatureCollection, getPlaceResponseToFeatureCollection, reverseGeocodeResponseToFeatureCollection } from '@aws/amazon-location-utilities-datatypes';
|
|
3
3
|
/**
|
|
4
4
|
* GeoPlaces - MapLibre adapter for AWS Location Service
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* Only auth is custom (Bearer token instead of SigV4).
|
|
6
|
+
* Implements MaplibreGeocoderApi interface for compatibility with @maplibre/maplibre-gl-geocoder
|
|
8
7
|
*/
|
|
9
8
|
export class GeoPlaces {
|
|
10
9
|
constructor(client, map) {
|
|
11
10
|
this.client = client;
|
|
12
11
|
this.map = map;
|
|
13
12
|
}
|
|
13
|
+
normalizeLanguage(language) {
|
|
14
|
+
if (Array.isArray(language))
|
|
15
|
+
return language[0] || 'en';
|
|
16
|
+
if (typeof language === 'string')
|
|
17
|
+
return language;
|
|
18
|
+
return 'en';
|
|
19
|
+
}
|
|
14
20
|
async forwardGeocode(config) {
|
|
21
|
+
console.log('[GeoPlaces] forwardGeocode called with:', config);
|
|
15
22
|
const center = this.map.getCenter();
|
|
16
|
-
const
|
|
23
|
+
const biasPosition = config.proximity && config.proximity.length >= 2
|
|
24
|
+
? [config.proximity[0], config.proximity[1]]
|
|
25
|
+
: [center.lng, center.lat];
|
|
26
|
+
const commandInput = {
|
|
17
27
|
QueryText: config.query,
|
|
18
|
-
BiasPosition:
|
|
28
|
+
BiasPosition: biasPosition,
|
|
19
29
|
MaxResults: config.limit || 5,
|
|
20
|
-
Language:
|
|
21
|
-
}
|
|
30
|
+
Language: this.normalizeLanguage(config.language),
|
|
31
|
+
};
|
|
32
|
+
if (config.countries || config.bbox) {
|
|
33
|
+
commandInput.Filter = {};
|
|
34
|
+
if (config.countries)
|
|
35
|
+
commandInput.Filter.IncludeCountries = config.countries;
|
|
36
|
+
if (config.bbox)
|
|
37
|
+
commandInput.Filter.BoundingBox = config.bbox;
|
|
38
|
+
}
|
|
39
|
+
console.log('[GeoPlaces] Sending SearchTextCommand with:', commandInput);
|
|
40
|
+
const command = new GeocodeCommand(commandInput);
|
|
22
41
|
const response = await this.client.send(command);
|
|
23
|
-
|
|
42
|
+
console.log('[GeoPlaces] SearchTextCommand response:', response);
|
|
43
|
+
const result = geocodeResponseToFeatureCollection(response, { flattenProperties: true });
|
|
44
|
+
console.log('[GeoPlaces] Converted to FeatureCollection:', result);
|
|
45
|
+
return result;
|
|
24
46
|
}
|
|
25
47
|
async reverseGeocode(config) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
48
|
+
console.log('[GeoPlaces] reverseGeocode called with:', config);
|
|
49
|
+
const queryPosition = Array.isArray(config.query) && config.query.length >= 2
|
|
50
|
+
? [config.query[0], config.query[1]]
|
|
51
|
+
: [0, 0];
|
|
52
|
+
const commandInput = {
|
|
53
|
+
QueryPosition: queryPosition,
|
|
54
|
+
MaxResults: config.limit || 1,
|
|
55
|
+
Language: this.normalizeLanguage(config.language)
|
|
56
|
+
};
|
|
57
|
+
if (config.countries) {
|
|
58
|
+
commandInput.Filter = { IncludeCountries: config.countries };
|
|
59
|
+
}
|
|
60
|
+
const command = new ReverseGeocodeCommand(commandInput);
|
|
37
61
|
const response = await this.client.send(command);
|
|
38
|
-
|
|
62
|
+
const maplibreGeocoderFeatureResults = reverseGeocodeResponseToFeatureCollection(response, { flattenProperties: true });
|
|
63
|
+
maplibreGeocoderFeatureResults.features.forEach(feature => {
|
|
64
|
+
});
|
|
65
|
+
console.log('[GeoPlaces] Converted to FeatureCollection:', maplibreGeocoderFeatureResults);
|
|
66
|
+
return maplibreGeocoderFeatureResults;
|
|
39
67
|
}
|
|
40
68
|
async getSuggestions(config) {
|
|
69
|
+
// const center = this.map.getCenter()
|
|
70
|
+
// const biasPosition = config.proximity && config.proximity.length >= 2
|
|
71
|
+
// ? [config.proximity[0], config.proximity[1]]
|
|
72
|
+
// : [center.lng, center.lat]
|
|
73
|
+
// const commandInput: any = {
|
|
74
|
+
// QueryText: config.query as string,
|
|
75
|
+
// BiasPosition: biasPosition,
|
|
76
|
+
// MaxResults: config.limit || 5,
|
|
77
|
+
// Language: this.normalizeLanguage(config.language),
|
|
78
|
+
// AdditionalFeatures: [SuggestAdditionalFeature.CORE],
|
|
79
|
+
// }
|
|
80
|
+
// if (config.countries || config.bbox) {
|
|
81
|
+
// commandInput.Filter = {}
|
|
82
|
+
// if (config.countries) commandInput.Filter.IncludeCountries = config.countries
|
|
83
|
+
// if (config.bbox) commandInput.Filter.BoundingBox = config.bbox
|
|
84
|
+
// }
|
|
85
|
+
// const command = new SuggestCommand(commandInput)
|
|
86
|
+
// const response = await this.client.send(command) as SuggestResponse
|
|
41
87
|
const center = this.map.getCenter();
|
|
42
|
-
const
|
|
88
|
+
const biasPosition = config.proximity && config.proximity.length >= 2
|
|
89
|
+
? [config.proximity[0], config.proximity[1]]
|
|
90
|
+
: [center.lng, center.lat];
|
|
91
|
+
const commandInput = {
|
|
43
92
|
QueryText: config.query,
|
|
44
|
-
BiasPosition:
|
|
45
|
-
|
|
46
|
-
|
|
93
|
+
BiasPosition: biasPosition,
|
|
94
|
+
MaxResults: config.limit || 5,
|
|
95
|
+
Language: this.normalizeLanguage(config.language),
|
|
96
|
+
AdditionalFeatures: [GeocodeAdditionalFeature.SECONDARY_ADDRESSES],
|
|
97
|
+
};
|
|
98
|
+
if (config.countries || config.bbox) {
|
|
99
|
+
commandInput.Filter = {};
|
|
100
|
+
if (config.countries)
|
|
101
|
+
commandInput.Filter.IncludeCountries = Array.isArray(config.countries) ? config.countries : config.countries.split(',');
|
|
102
|
+
// if (config.types) commandInput.Filter.IncludePlaceTypes = config.types.split(',').map(type => new GeocodeFilterPlaceType)
|
|
103
|
+
}
|
|
104
|
+
console.log('[GeoPlaces] Sending SearchTextCommand with:', commandInput);
|
|
105
|
+
const command = new GeocodeCommand(commandInput);
|
|
47
106
|
const response = await this.client.send(command);
|
|
48
|
-
|
|
107
|
+
console.log('[GeoPlaces] SearchTextCommand response:', response);
|
|
108
|
+
const maplibreGeocoderSuggestionResults = { suggestions: [] };
|
|
109
|
+
response?.ResultItems?.forEach(item => {
|
|
110
|
+
const text = item?.Title || item?.Address?.Label;
|
|
111
|
+
const placeId = item?.PlaceId;
|
|
112
|
+
if (text) {
|
|
113
|
+
maplibreGeocoderSuggestionResults.suggestions.push({
|
|
114
|
+
text,
|
|
115
|
+
placeId,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
console.log('[GeoPlaces] Converted to MaplibreGeocoderSuggestionResults:', maplibreGeocoderSuggestionResults);
|
|
120
|
+
return maplibreGeocoderSuggestionResults;
|
|
49
121
|
}
|
|
50
|
-
async searchByPlaceId(
|
|
122
|
+
async searchByPlaceId(config) {
|
|
123
|
+
console.log('[GeoPlaces] searchByPlaceId called with:', config);
|
|
124
|
+
const placeId = config.query;
|
|
51
125
|
const command = new GetPlaceCommand({
|
|
52
126
|
PlaceId: placeId,
|
|
53
|
-
Language:
|
|
127
|
+
Language: this.normalizeLanguage(config.language),
|
|
128
|
+
AdditionalFeatures: [GetPlaceAdditionalFeature.ACCESS, GetPlaceAdditionalFeature.SECONDARY_ADDRESSES, GetPlaceAdditionalFeature.CONTACT, GetPlaceAdditionalFeature.TIME_ZONE],
|
|
54
129
|
});
|
|
55
130
|
const response = await this.client.send(command);
|
|
56
|
-
|
|
131
|
+
const result = getPlaceResponseToFeatureCollection(response, { flattenProperties: true });
|
|
132
|
+
console.log('[GeoPlaces] GetPlaceCommand response converted to FeatureCollection:', result);
|
|
133
|
+
const carmenGeojsonFeatures = result.features.map(feature => ({
|
|
134
|
+
...feature,
|
|
135
|
+
text: response.Title || '',
|
|
136
|
+
place_name: response.Address?.Label || '',
|
|
137
|
+
place_type: response.PlaceType ? [response.PlaceType] : [],
|
|
138
|
+
bbox: response.MapView
|
|
139
|
+
}));
|
|
140
|
+
console.log('[GeoPlaces] Converted to CarmenGeojsonFeatures:', carmenGeojsonFeatures);
|
|
141
|
+
return { place: carmenGeojsonFeatures };
|
|
142
|
+
}
|
|
143
|
+
async localGeocode(config) {
|
|
144
|
+
console.log('[GeoPlaces] localGeocode called with:', config);
|
|
145
|
+
return this.forwardGeocode(config);
|
|
57
146
|
}
|
|
58
147
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export interface TokenResponse {
|
|
2
2
|
success: boolean;
|
|
3
3
|
token?: string;
|
|
4
|
-
expiresIn?: number;
|
|
5
|
-
expiresAt?: number;
|
|
6
4
|
error?: string;
|
|
7
5
|
}
|
|
8
6
|
export interface TokenProviderConfig {
|
|
@@ -41,10 +39,12 @@ export interface TokenProviderConfig {
|
|
|
41
39
|
export declare class TokenProvider {
|
|
42
40
|
private config;
|
|
43
41
|
private cachedToken?;
|
|
44
|
-
private expiresAt?;
|
|
45
42
|
private oauth2Client;
|
|
43
|
+
private tokenPromise?;
|
|
46
44
|
constructor(config: TokenProviderConfig);
|
|
47
45
|
getToken(forceRefresh?: boolean): Promise<TokenResponse>;
|
|
46
|
+
private fetchToken;
|
|
47
|
+
private getTokenExpiry;
|
|
48
48
|
private isExpired;
|
|
49
49
|
clearCache(): void;
|
|
50
50
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ClientOAuth2 from 'client-oauth2';
|
|
2
|
+
import { decodeJwt } from 'jose';
|
|
2
3
|
import debug from 'debug';
|
|
3
4
|
const log = debug('location-client:auth');
|
|
4
5
|
/**
|
|
@@ -46,29 +47,41 @@ export class TokenProvider {
|
|
|
46
47
|
});
|
|
47
48
|
}
|
|
48
49
|
async getToken(forceRefresh = false) {
|
|
49
|
-
if (!forceRefresh && this.cachedToken &&
|
|
50
|
-
|
|
50
|
+
if (!forceRefresh && this.cachedToken && !this.isExpired()) {
|
|
51
|
+
const exp = this.getTokenExpiry();
|
|
52
|
+
log('Using cached token (expires in %ds)', exp ? Math.floor(exp - Date.now() / 1000) : 'unknown');
|
|
51
53
|
return {
|
|
52
54
|
success: true,
|
|
53
|
-
token: this.cachedToken
|
|
54
|
-
expiresAt: this.expiresAt
|
|
55
|
+
token: this.cachedToken
|
|
55
56
|
};
|
|
56
57
|
}
|
|
58
|
+
// If token fetch is already in progress, wait for it
|
|
59
|
+
if (this.tokenPromise) {
|
|
60
|
+
log('Token fetch in progress, waiting for existing request...');
|
|
61
|
+
return this.tokenPromise;
|
|
62
|
+
}
|
|
63
|
+
// Start new token fetch
|
|
57
64
|
const reason = forceRefresh ? 'forced refresh' : (this.cachedToken ? 'token expired' : 'no cached token');
|
|
58
65
|
log('Refreshing token (%s) from %s', reason, this.config.apiUrl);
|
|
66
|
+
this.tokenPromise = this.fetchToken();
|
|
67
|
+
try {
|
|
68
|
+
const result = await this.tokenPromise;
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
// Clear promise after completion (success or failure)
|
|
73
|
+
this.tokenPromise = undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async fetchToken() {
|
|
59
77
|
try {
|
|
60
78
|
const token = await this.oauth2Client.credentials.getToken();
|
|
61
|
-
const expiresIn = typeof token.data?.expires_in === 'number'
|
|
62
|
-
? token.data.expires_in
|
|
63
|
-
: parseInt(token.data?.expires_in || '3600');
|
|
64
79
|
this.cachedToken = token.accessToken;
|
|
65
|
-
|
|
66
|
-
log('Token acquired successfully (expires in %ds)',
|
|
80
|
+
const exp = this.getTokenExpiry();
|
|
81
|
+
log('Token acquired successfully (expires in %ds)', exp ? Math.floor(exp - Date.now() / 1000) : 'unknown');
|
|
67
82
|
return {
|
|
68
83
|
success: true,
|
|
69
|
-
token: this.cachedToken
|
|
70
|
-
expiresIn,
|
|
71
|
-
expiresAt: this.expiresAt,
|
|
84
|
+
token: this.cachedToken
|
|
72
85
|
};
|
|
73
86
|
}
|
|
74
87
|
catch (error) {
|
|
@@ -79,13 +92,24 @@ export class TokenProvider {
|
|
|
79
92
|
};
|
|
80
93
|
}
|
|
81
94
|
}
|
|
95
|
+
getTokenExpiry() {
|
|
96
|
+
if (!this.cachedToken)
|
|
97
|
+
return null;
|
|
98
|
+
try {
|
|
99
|
+
const decoded = decodeJwt(this.cachedToken);
|
|
100
|
+
return decoded.exp || null;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
82
106
|
isExpired(bufferSeconds = 60) {
|
|
83
|
-
|
|
107
|
+
const exp = this.getTokenExpiry();
|
|
108
|
+
if (!exp)
|
|
84
109
|
return true;
|
|
85
|
-
return Date.now() >= (
|
|
110
|
+
return Date.now() / 1000 >= (exp - bufferSeconds);
|
|
86
111
|
}
|
|
87
112
|
clearCache() {
|
|
88
113
|
this.cachedToken = undefined;
|
|
89
|
-
this.expiresAt = undefined;
|
|
90
114
|
}
|
|
91
115
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AutocompleteCommand, GeocodeCommand, GetPlaceCommand, ReverseGeocodeCommand, SearchNearbyCommand, SearchTextCommand, SuggestCommand, } from '@aws-sdk/client-geo-places';
|
|
2
1
|
import debug from 'debug';
|
|
3
2
|
const log = debug('location-client:api');
|
|
3
|
+
const logError = debug('location-client:api:error');
|
|
4
4
|
/**
|
|
5
5
|
* GeoPlacesClient - AWS Location Service compatible client with custom auth
|
|
6
6
|
*
|
|
@@ -15,7 +15,11 @@ export class GeoPlacesClient {
|
|
|
15
15
|
async send(command) {
|
|
16
16
|
const endpoint = this.getEndpoint(command);
|
|
17
17
|
const url = `${this.clientConfig.apiUrl}${endpoint}`;
|
|
18
|
-
|
|
18
|
+
const commandName = command.constructor.name;
|
|
19
|
+
const input = command.input;
|
|
20
|
+
log(`[GeoPlacesClient] Sending ${commandName} to ${endpoint}`);
|
|
21
|
+
log('[GeoPlacesClient] Request body:', JSON.stringify(input, null, 2));
|
|
22
|
+
log('Sending %s request to %s', commandName, endpoint);
|
|
19
23
|
const startTime = Date.now();
|
|
20
24
|
const response = await fetch(url, {
|
|
21
25
|
method: 'POST',
|
|
@@ -23,38 +27,52 @@ export class GeoPlacesClient {
|
|
|
23
27
|
'Content-Type': 'application/json',
|
|
24
28
|
'Authorization': `Bearer ${this.clientConfig.token}`
|
|
25
29
|
},
|
|
26
|
-
body: JSON.stringify(
|
|
30
|
+
body: JSON.stringify(input)
|
|
27
31
|
});
|
|
28
32
|
const duration = Date.now() - startTime;
|
|
29
33
|
if (!response.ok) {
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
const errorText = await response.text();
|
|
35
|
+
log(`[GeoPlacesClient] Request failed: ${response.status} ${response.statusText}`, errorText);
|
|
36
|
+
logError('Request failed: %s %s (%dms)', response.status, response.statusText, duration);
|
|
37
|
+
// Try to parse error message from response
|
|
38
|
+
let errorMessage = `API request failed: ${response.statusText}`;
|
|
39
|
+
try {
|
|
40
|
+
const errorData = JSON.parse(errorText);
|
|
41
|
+
if (errorData.message) {
|
|
42
|
+
errorMessage = errorData.message;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// If not JSON, use raw text if available
|
|
47
|
+
if (errorText)
|
|
48
|
+
errorMessage = errorText;
|
|
49
|
+
}
|
|
50
|
+
throw new Error(errorMessage);
|
|
32
51
|
}
|
|
52
|
+
const result = await response.json();
|
|
53
|
+
log(`[GeoPlacesClient] Response (${duration}ms):`, JSON.stringify(result, null, 2));
|
|
33
54
|
log('Request successful: %s (%dms)', response.status, duration);
|
|
34
|
-
return
|
|
55
|
+
return result;
|
|
35
56
|
}
|
|
36
57
|
getEndpoint(command) {
|
|
37
|
-
|
|
38
|
-
|
|
58
|
+
const commandName = command.constructor.name;
|
|
59
|
+
switch (commandName) {
|
|
60
|
+
case 'AutocompleteCommand':
|
|
61
|
+
return '/address/autocomplete';
|
|
62
|
+
case 'GeocodeCommand':
|
|
63
|
+
return '/address/geocode';
|
|
64
|
+
case 'GetPlaceCommand':
|
|
65
|
+
return '/address/place';
|
|
66
|
+
case 'ReverseGeocodeCommand':
|
|
67
|
+
return '/address/search/reverse-geocode';
|
|
68
|
+
case 'SearchNearbyCommand':
|
|
69
|
+
return '/address/search/nearby';
|
|
70
|
+
case 'SearchTextCommand':
|
|
71
|
+
return '/address/search/text';
|
|
72
|
+
case 'SuggestCommand':
|
|
73
|
+
return '/address/suggestion';
|
|
74
|
+
default:
|
|
75
|
+
throw new Error(`Unknown command type: ${commandName}`);
|
|
39
76
|
}
|
|
40
|
-
else if (command instanceof GeocodeCommand) {
|
|
41
|
-
return '/address/geocode';
|
|
42
|
-
}
|
|
43
|
-
else if (command instanceof GetPlaceCommand) {
|
|
44
|
-
return '/address/place';
|
|
45
|
-
}
|
|
46
|
-
else if (command instanceof ReverseGeocodeCommand) {
|
|
47
|
-
return '/address/search/reverse-geocode';
|
|
48
|
-
}
|
|
49
|
-
else if (command instanceof SearchNearbyCommand) {
|
|
50
|
-
return '/address/search/nearby';
|
|
51
|
-
}
|
|
52
|
-
else if (command instanceof SearchTextCommand) {
|
|
53
|
-
return '/address/search/text';
|
|
54
|
-
}
|
|
55
|
-
else if (command instanceof SuggestCommand) {
|
|
56
|
-
return '/address/suggestion';
|
|
57
|
-
}
|
|
58
|
-
throw new Error('Unknown command type');
|
|
59
77
|
}
|
|
60
78
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ export { GeoPlacesClient } from './client/GeoPlacesClient';
|
|
|
4
4
|
export * from '@aws-sdk/client-geo-places';
|
|
5
5
|
export * from '@aws/amazon-location-utilities-datatypes';
|
|
6
6
|
export { GeoPlaces } from './adapters/GeoPlaces';
|
|
7
|
+
export { createTransformRequest } from './maps/createTransformRequest';
|
|
8
|
+
export { transformRequest } from './maps/Utils';
|
|
7
9
|
export type { ClientConfig } from './types';
|
|
8
10
|
export { getClientConfig } from './server/getClientConfig';
|
|
9
11
|
export type { ServerAuthConfig } from './server/getClientConfig';
|
|
12
|
+
export { LocationServiceConnector } from './server/LocationServiceConnector';
|
|
13
|
+
export type { ConnectorConfig } from './server/LocationServiceConnector';
|
package/dist/index.js
CHANGED
|
@@ -8,5 +8,9 @@ export * from '@aws-sdk/client-geo-places';
|
|
|
8
8
|
export * from '@aws/amazon-location-utilities-datatypes';
|
|
9
9
|
// Adapters (Custom - for MapLibre integration)
|
|
10
10
|
export { GeoPlaces } from './adapters/GeoPlaces';
|
|
11
|
+
// Maps utilities
|
|
12
|
+
export { createTransformRequest } from './maps/createTransformRequest';
|
|
13
|
+
export { transformRequest } from './maps/Utils';
|
|
11
14
|
// Server-only utilities
|
|
12
15
|
export { getClientConfig } from './server/getClientConfig';
|
|
16
|
+
export { LocationServiceConnector } from './server/LocationServiceConnector';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function transformRequest(url, config) {
|
|
2
|
+
if (url.startsWith(config.apiUrl)) {
|
|
3
|
+
const headers = {
|
|
4
|
+
'Authorization': `Bearer ${config.token}`,
|
|
5
|
+
};
|
|
6
|
+
if (url.includes('/tiles/')) {
|
|
7
|
+
headers['Accept'] = 'application/x-protobuf';
|
|
8
|
+
}
|
|
9
|
+
else if (url.includes('/glyphs/')) {
|
|
10
|
+
headers['Accept'] = 'application/x-protobuf';
|
|
11
|
+
}
|
|
12
|
+
else if (url.includes('/sprites/') && url.endsWith('.png')) {
|
|
13
|
+
headers['Accept'] = 'image/png';
|
|
14
|
+
}
|
|
15
|
+
else if (url.includes('/sprites/') && url.endsWith('.json')) {
|
|
16
|
+
headers['Accept'] = 'application/json';
|
|
17
|
+
}
|
|
18
|
+
else if (url.includes('/descriptor')) {
|
|
19
|
+
headers['Accept'] = 'application/json';
|
|
20
|
+
}
|
|
21
|
+
return { url, headers };
|
|
22
|
+
}
|
|
23
|
+
return { url };
|
|
24
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RequestTransformFunction } from 'maplibre-gl';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a transformRequest function for MapLibre that adds authentication
|
|
4
|
+
* and proper Accept headers for AWS Location Service API requests.
|
|
5
|
+
*
|
|
6
|
+
* @param apiUrl - Base URL of the Location Service API
|
|
7
|
+
* @param getToken - Callback function that returns the current auth token
|
|
8
|
+
* @returns MapLibre transformRequest function
|
|
9
|
+
*/
|
|
10
|
+
export declare function createTransformRequest(apiUrl: string, getToken: () => string | undefined): RequestTransformFunction;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a transformRequest function for MapLibre that adds authentication
|
|
3
|
+
* and proper Accept headers for AWS Location Service API requests.
|
|
4
|
+
*
|
|
5
|
+
* @param apiUrl - Base URL of the Location Service API
|
|
6
|
+
* @param getToken - Callback function that returns the current auth token
|
|
7
|
+
* @returns MapLibre transformRequest function
|
|
8
|
+
*/
|
|
9
|
+
export function createTransformRequest(apiUrl, getToken) {
|
|
10
|
+
return (url, resourceType) => {
|
|
11
|
+
if (url.startsWith(apiUrl)) {
|
|
12
|
+
const token = getToken();
|
|
13
|
+
if (!token) {
|
|
14
|
+
console.warn('[createTransformRequest] No token available');
|
|
15
|
+
return { url };
|
|
16
|
+
}
|
|
17
|
+
const headers = {
|
|
18
|
+
'Authorization': `Bearer ${token}`,
|
|
19
|
+
};
|
|
20
|
+
// Set appropriate Accept headers based on resource type
|
|
21
|
+
if (url.includes('/tiles/')) {
|
|
22
|
+
headers['Accept'] = 'application/x-protobuf';
|
|
23
|
+
}
|
|
24
|
+
else if (url.includes('/glyphs/')) {
|
|
25
|
+
headers['Accept'] = 'application/x-protobuf';
|
|
26
|
+
}
|
|
27
|
+
else if (url.includes('/sprites/') && url.endsWith('.png')) {
|
|
28
|
+
headers['Accept'] = 'image/png';
|
|
29
|
+
}
|
|
30
|
+
else if (url.includes('/sprites/') && url.endsWith('.json')) {
|
|
31
|
+
headers['Accept'] = 'application/json';
|
|
32
|
+
}
|
|
33
|
+
else if (url.includes('/descriptor')) {
|
|
34
|
+
headers['Accept'] = 'application/json';
|
|
35
|
+
}
|
|
36
|
+
return { url, headers };
|
|
37
|
+
}
|
|
38
|
+
return { url };
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface ConnectorConfig {
|
|
2
|
+
apiUrl?: string;
|
|
3
|
+
token?: string;
|
|
4
|
+
getToken?: () => Promise<{
|
|
5
|
+
token: string;
|
|
6
|
+
}>;
|
|
7
|
+
}
|
|
8
|
+
export interface SendOptions {
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* LocationServiceConnector - Server-side connector for Location Service API
|
|
13
|
+
*
|
|
14
|
+
* Optimized for backend-to-backend communication with:
|
|
15
|
+
* - Automatic configuration from environment variables
|
|
16
|
+
* - Automatic Origin header handling
|
|
17
|
+
* - Server-side token management
|
|
18
|
+
* - Enhanced error handling
|
|
19
|
+
*
|
|
20
|
+
* Uses AWS SDK command classes with Bearer token authentication.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // Auto-detect from environment
|
|
25
|
+
* const connector = new LocationServiceConnector()
|
|
26
|
+
*
|
|
27
|
+
* // Or provide explicit config
|
|
28
|
+
* const connector = new LocationServiceConnector({
|
|
29
|
+
* apiUrl: 'https://api.example.com',
|
|
30
|
+
* token: 'your-token'
|
|
31
|
+
* })
|
|
32
|
+
*
|
|
33
|
+
* // Send with custom headers (including Origin)
|
|
34
|
+
* const result = await connector.send(
|
|
35
|
+
* new SearchTextCommand({ QueryText: 'Space Needle' }),
|
|
36
|
+
* { headers: { 'Origin': req.headers.origin } }
|
|
37
|
+
* )
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare class LocationServiceConnector {
|
|
41
|
+
private configPromise;
|
|
42
|
+
readonly serviceId: string;
|
|
43
|
+
constructor(config?: ConnectorConfig);
|
|
44
|
+
send<TInput, TOutput>(command: TInput, options?: SendOptions): Promise<TOutput>;
|
|
45
|
+
private getEndpoint;
|
|
46
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import { getClientConfig } from './getClientConfig';
|
|
3
|
+
const log = debug('location-client:connector');
|
|
4
|
+
/**
|
|
5
|
+
* LocationServiceConnector - Server-side connector for Location Service API
|
|
6
|
+
*
|
|
7
|
+
* Optimized for backend-to-backend communication with:
|
|
8
|
+
* - Automatic configuration from environment variables
|
|
9
|
+
* - Automatic Origin header handling
|
|
10
|
+
* - Server-side token management
|
|
11
|
+
* - Enhanced error handling
|
|
12
|
+
*
|
|
13
|
+
* Uses AWS SDK command classes with Bearer token authentication.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Auto-detect from environment
|
|
18
|
+
* const connector = new LocationServiceConnector()
|
|
19
|
+
*
|
|
20
|
+
* // Or provide explicit config
|
|
21
|
+
* const connector = new LocationServiceConnector({
|
|
22
|
+
* apiUrl: 'https://api.example.com',
|
|
23
|
+
* token: 'your-token'
|
|
24
|
+
* })
|
|
25
|
+
*
|
|
26
|
+
* // Send with custom headers (including Origin)
|
|
27
|
+
* const result = await connector.send(
|
|
28
|
+
* new SearchTextCommand({ QueryText: 'Space Needle' }),
|
|
29
|
+
* { headers: { 'Origin': req.headers.origin } }
|
|
30
|
+
* )
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class LocationServiceConnector {
|
|
34
|
+
constructor(config) {
|
|
35
|
+
this.serviceId = 'Geo Places';
|
|
36
|
+
this.configPromise = config
|
|
37
|
+
? Promise.resolve(config)
|
|
38
|
+
: getClientConfig();
|
|
39
|
+
}
|
|
40
|
+
async send(command, options) {
|
|
41
|
+
const config = await this.configPromise;
|
|
42
|
+
// Get token - ConnectorConfig may have getToken, ServerClientConfig has static token
|
|
43
|
+
const token = 'getToken' in config && config.getToken
|
|
44
|
+
? (await config.getToken()).token
|
|
45
|
+
: config.token;
|
|
46
|
+
if (!token) {
|
|
47
|
+
throw new Error('No token available');
|
|
48
|
+
}
|
|
49
|
+
const endpoint = this.getEndpoint(command);
|
|
50
|
+
const url = `${config.apiUrl}${endpoint}`;
|
|
51
|
+
const commandName = command.constructor.name;
|
|
52
|
+
const input = command.input;
|
|
53
|
+
log('Sending %s request to %s', commandName, endpoint);
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
// Merge headers: user headers, then system headers override
|
|
56
|
+
const headers = {
|
|
57
|
+
...(options?.headers || {}),
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
'Authorization': `Bearer ${token}`
|
|
60
|
+
};
|
|
61
|
+
const response = await fetch(url, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers,
|
|
64
|
+
body: JSON.stringify(input)
|
|
65
|
+
});
|
|
66
|
+
const duration = Date.now() - startTime;
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
const errorText = await response.text();
|
|
69
|
+
log('Request failed: %s %s (%dms)', response.status, response.statusText, duration);
|
|
70
|
+
// Try to parse error message from response
|
|
71
|
+
let errorMessage = `API request failed: ${response.statusText}`;
|
|
72
|
+
try {
|
|
73
|
+
const errorData = JSON.parse(errorText);
|
|
74
|
+
if (errorData.message) {
|
|
75
|
+
errorMessage = errorData.message;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// If not JSON, use raw text if available
|
|
80
|
+
if (errorText)
|
|
81
|
+
errorMessage = errorText;
|
|
82
|
+
}
|
|
83
|
+
throw new Error(errorMessage);
|
|
84
|
+
}
|
|
85
|
+
const result = await response.json();
|
|
86
|
+
log('Request successful: %s (%dms)', response.status, duration);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
getEndpoint(command) {
|
|
90
|
+
const commandName = command.constructor.name;
|
|
91
|
+
switch (commandName) {
|
|
92
|
+
case 'AutocompleteCommand':
|
|
93
|
+
return '/address/autocomplete';
|
|
94
|
+
case 'GeocodeCommand':
|
|
95
|
+
return '/address/geocode';
|
|
96
|
+
case 'GetPlaceCommand':
|
|
97
|
+
return '/address/place';
|
|
98
|
+
case 'ReverseGeocodeCommand':
|
|
99
|
+
return '/address/search/reverse-geocode';
|
|
100
|
+
case 'SearchNearbyCommand':
|
|
101
|
+
return '/address/search/nearby';
|
|
102
|
+
case 'SearchTextCommand':
|
|
103
|
+
return '/address/search/text';
|
|
104
|
+
case 'SuggestCommand':
|
|
105
|
+
return '/address/suggestion';
|
|
106
|
+
default:
|
|
107
|
+
throw new Error(`Unknown command type: ${commandName}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { ClientConfig } from '../types';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
export interface ServerAuthConfig {
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
clientId?: string;
|
|
5
|
+
clientSecret?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ServerClientConfig extends ClientConfig {
|
|
8
|
+
}
|
|
4
9
|
/**
|
|
5
10
|
* Get client configuration with OAuth2 authentication.
|
|
6
11
|
*
|
|
12
|
+
* Automatically reads from environment variables:
|
|
13
|
+
* - LOCATION_API_URL or LOCATION_SERVICE_API_URL
|
|
14
|
+
* - LOCATION_CLIENT_ID or LOCATION_SERVICE_CLIENT_ID
|
|
15
|
+
* - LOCATION_CLIENT_SECRET or LOCATION_SERVICE_CLIENT_SECRET
|
|
16
|
+
*
|
|
17
|
+
* You can override any value by passing it explicitly.
|
|
18
|
+
*
|
|
7
19
|
* WARNING: This function uses client credentials (clientId/clientSecret).
|
|
8
20
|
* Only call this from:
|
|
9
21
|
* - Next.js Server Components/Actions
|
|
@@ -12,7 +24,16 @@ export type ServerAuthConfig = TokenProviderConfig;
|
|
|
12
24
|
*
|
|
13
25
|
* NEVER call from browser/client code as it exposes credentials.
|
|
14
26
|
* For SPA projects, create your own backend endpoint that calls this.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Auto-detect from environment
|
|
30
|
+
* const config = await getClientConfig()
|
|
31
|
+
*
|
|
32
|
+
* // Or override specific values
|
|
33
|
+
* const config = await getClientConfig({ apiUrl: 'https://custom.api.com' })
|
|
34
|
+
*
|
|
35
|
+
* // Use getToken() for automatic caching and refresh
|
|
36
|
+
* const { token } = await config.getToken()
|
|
37
|
+
* const connector = new LocationServiceConnector({ apiUrl: config.apiUrl, token })
|
|
15
38
|
*/
|
|
16
|
-
export declare function getClientConfig(config
|
|
17
|
-
expiresAt?: number;
|
|
18
|
-
}>;
|
|
39
|
+
export declare function getClientConfig(config?: ServerAuthConfig): Promise<ServerClientConfig>;
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
1
2
|
import { TokenProvider } from '../auth/TokenProvider';
|
|
3
|
+
const log = debug('location-client:clientConfig');
|
|
2
4
|
/**
|
|
3
5
|
* Get client configuration with OAuth2 authentication.
|
|
4
6
|
*
|
|
7
|
+
* Automatically reads from environment variables:
|
|
8
|
+
* - LOCATION_API_URL or LOCATION_SERVICE_API_URL
|
|
9
|
+
* - LOCATION_CLIENT_ID or LOCATION_SERVICE_CLIENT_ID
|
|
10
|
+
* - LOCATION_CLIENT_SECRET or LOCATION_SERVICE_CLIENT_SECRET
|
|
11
|
+
*
|
|
12
|
+
* You can override any value by passing it explicitly.
|
|
13
|
+
*
|
|
5
14
|
* WARNING: This function uses client credentials (clientId/clientSecret).
|
|
6
15
|
* Only call this from:
|
|
7
16
|
* - Next.js Server Components/Actions
|
|
@@ -10,16 +19,48 @@ import { TokenProvider } from '../auth/TokenProvider';
|
|
|
10
19
|
*
|
|
11
20
|
* NEVER call from browser/client code as it exposes credentials.
|
|
12
21
|
* For SPA projects, create your own backend endpoint that calls this.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Auto-detect from environment
|
|
25
|
+
* const config = await getClientConfig()
|
|
26
|
+
*
|
|
27
|
+
* // Or override specific values
|
|
28
|
+
* const config = await getClientConfig({ apiUrl: 'https://custom.api.com' })
|
|
29
|
+
*
|
|
30
|
+
* // Use getToken() for automatic caching and refresh
|
|
31
|
+
* const { token } = await config.getToken()
|
|
32
|
+
* const connector = new LocationServiceConnector({ apiUrl: config.apiUrl, token })
|
|
13
33
|
*/
|
|
14
|
-
export async function getClientConfig(config) {
|
|
15
|
-
|
|
34
|
+
export async function getClientConfig(config = {}) {
|
|
35
|
+
log('[getClientConfig] Starting with config:', { hasApiUrl: !!config.apiUrl, hasClientId: !!config.clientId });
|
|
36
|
+
// Auto-detect from environment with fallbacks
|
|
37
|
+
const apiUrl = config.apiUrl ||
|
|
38
|
+
process.env.LOCATION_API_URL ||
|
|
39
|
+
process.env.LOCATION_SERVICE_API_URL;
|
|
40
|
+
const clientId = config.clientId ||
|
|
41
|
+
process.env.LOCATION_CLIENT_ID ||
|
|
42
|
+
process.env.LOCATION_SERVICE_CLIENT_ID;
|
|
43
|
+
const clientSecret = config.clientSecret ||
|
|
44
|
+
process.env.LOCATION_CLIENT_SECRET ||
|
|
45
|
+
process.env.LOCATION_SERVICE_CLIENT_SECRET;
|
|
46
|
+
log('[getClientConfig] Resolved config:', { apiUrl, clientId: clientId?.substring(0, 10) + '...' });
|
|
47
|
+
// Validate required values
|
|
48
|
+
if (!apiUrl || !clientId || !clientSecret) {
|
|
49
|
+
console.error('[getClientConfig] Missing required configuration');
|
|
50
|
+
throw new Error('Missing required configuration. Set environment variables: ' +
|
|
51
|
+
'LOCATION_API_URL, LOCATION_CLIENT_ID, LOCATION_CLIENT_SECRET');
|
|
52
|
+
}
|
|
53
|
+
log('[getClientConfig] Creating TokenProvider');
|
|
54
|
+
const provider = new TokenProvider({ apiUrl, clientId, clientSecret });
|
|
55
|
+
log('[getClientConfig] Fetching token');
|
|
16
56
|
const result = await provider.getToken();
|
|
17
57
|
if (!result.success || !result.token) {
|
|
58
|
+
console.error('[getClientConfig] Token fetch failed:', result.error);
|
|
18
59
|
throw new Error(result.error || 'Failed to get token');
|
|
19
60
|
}
|
|
61
|
+
log('[getClientConfig] Token fetched successfully, length:', result.token.length);
|
|
20
62
|
return {
|
|
21
|
-
apiUrl
|
|
22
|
-
token: result.token
|
|
23
|
-
expiresAt: result.expiresAt,
|
|
63
|
+
apiUrl,
|
|
64
|
+
token: result.token
|
|
24
65
|
};
|
|
25
66
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { LocationServiceConnector } from './LocationServiceConnector';
|
|
2
|
+
export interface ServerAuthConfig {
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
clientId?: string;
|
|
5
|
+
clientSecret?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Get LocationServiceConnector with automatic token management.
|
|
9
|
+
*
|
|
10
|
+
* Automatically reads from environment variables:
|
|
11
|
+
* - LOCATION_API_URL or LOCATION_SERVICE_API_URL
|
|
12
|
+
* - LOCATION_CLIENT_ID or LOCATION_SERVICE_CLIENT_ID
|
|
13
|
+
* - LOCATION_CLIENT_SECRET or LOCATION_SERVICE_CLIENT_SECRET
|
|
14
|
+
*
|
|
15
|
+
* You can override any value by passing it explicitly.
|
|
16
|
+
*
|
|
17
|
+
* WARNING: This function uses client credentials (clientId/clientSecret).
|
|
18
|
+
* Only call this from:
|
|
19
|
+
* - Next.js Server Components/Actions
|
|
20
|
+
* - Node.js backend servers
|
|
21
|
+
* - API routes
|
|
22
|
+
*
|
|
23
|
+
* NEVER call from browser/client code as it exposes credentials.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // Server Action for React provider
|
|
27
|
+
* 'use server'
|
|
28
|
+
* export async function getLocationConnector() {
|
|
29
|
+
* return getConnector()
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* // Or with custom config
|
|
33
|
+
* export async function getLocationConnector() {
|
|
34
|
+
* return getConnector({ apiUrl: 'https://custom.api.com' })
|
|
35
|
+
* }
|
|
36
|
+
*/
|
|
37
|
+
export declare function getConnector(config?: ServerAuthConfig): LocationServiceConnector;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { LocationServiceConnector } from './LocationServiceConnector';
|
|
2
|
+
/**
|
|
3
|
+
* Get LocationServiceConnector with automatic token management.
|
|
4
|
+
*
|
|
5
|
+
* Automatically reads from environment variables:
|
|
6
|
+
* - LOCATION_API_URL or LOCATION_SERVICE_API_URL
|
|
7
|
+
* - LOCATION_CLIENT_ID or LOCATION_SERVICE_CLIENT_ID
|
|
8
|
+
* - LOCATION_CLIENT_SECRET or LOCATION_SERVICE_CLIENT_SECRET
|
|
9
|
+
*
|
|
10
|
+
* You can override any value by passing it explicitly.
|
|
11
|
+
*
|
|
12
|
+
* WARNING: This function uses client credentials (clientId/clientSecret).
|
|
13
|
+
* Only call this from:
|
|
14
|
+
* - Next.js Server Components/Actions
|
|
15
|
+
* - Node.js backend servers
|
|
16
|
+
* - API routes
|
|
17
|
+
*
|
|
18
|
+
* NEVER call from browser/client code as it exposes credentials.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Server Action for React provider
|
|
22
|
+
* 'use server'
|
|
23
|
+
* export async function getLocationConnector() {
|
|
24
|
+
* return getConnector()
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* // Or with custom config
|
|
28
|
+
* export async function getLocationConnector() {
|
|
29
|
+
* return getConnector({ apiUrl: 'https://custom.api.com' })
|
|
30
|
+
* }
|
|
31
|
+
*/
|
|
32
|
+
export function getConnector(config) {
|
|
33
|
+
return new LocationServiceConnector(config);
|
|
34
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { getClientConfig } from './getClientConfig';
|
|
2
|
+
export type { ServerAuthConfig, ServerClientConfig } from './getClientConfig';
|
|
3
|
+
export { LocationServiceConnector } from './LocationServiceConnector';
|
|
4
|
+
export type { ConnectorConfig, SendOptions } from './LocationServiceConnector';
|
package/package.json
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaosity/location-client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Client library for Chaosity Location Service with AWS Location Service compatibility",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./server": {
|
|
14
|
+
"types": "./dist/server/index.d.ts",
|
|
15
|
+
"default": "./dist/server/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
7
18
|
"scripts": {
|
|
8
19
|
"build": "tsc",
|
|
9
20
|
"dev": "tsc --watch",
|
|
@@ -31,11 +42,13 @@
|
|
|
31
42
|
"dependencies": {
|
|
32
43
|
"@aws-sdk/client-geo-places": "^3.0.0",
|
|
33
44
|
"@aws/amazon-location-utilities-datatypes": "^1.0.0",
|
|
45
|
+
"@maplibre/maplibre-gl-geocoder": "^1.9.4",
|
|
34
46
|
"client-oauth2": "^4.3.3",
|
|
35
|
-
"debug": "^4.4.3"
|
|
47
|
+
"debug": "^4.4.3",
|
|
48
|
+
"jose": "^5.0.0"
|
|
36
49
|
},
|
|
37
50
|
"peerDependencies": {
|
|
38
|
-
"maplibre-gl": "^
|
|
51
|
+
"maplibre-gl": "^5.0.0"
|
|
39
52
|
},
|
|
40
53
|
"peerDependenciesMeta": {
|
|
41
54
|
"maplibre-gl": {
|
|
@@ -44,6 +57,8 @@
|
|
|
44
57
|
},
|
|
45
58
|
"devDependencies": {
|
|
46
59
|
"@types/debug": "^4.1.12",
|
|
60
|
+
"@types/node": "^25.0.3",
|
|
61
|
+
"maplibre-gl": "^5.0.0",
|
|
47
62
|
"typescript": "^5.0.0"
|
|
48
63
|
},
|
|
49
64
|
"files": [
|