@loaders.gl/services 5.0.0-alpha.5 → 5.0.0-alpha.7
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/arcgis/arcgis-feature-server-source-loader.d.ts.map +1 -1
- package/dist/arcgis/arcgis-feature-server-source-loader.js +7 -4
- package/dist/arcgis/arcgis-feature-server-source-loader.js.map +1 -1
- package/dist/arcgis/arcgis-image-server-source-loader.js +2 -2
- package/dist/arcgis/arcgis-image-server-source-loader.js.map +1 -1
- package/dist/authentication.d.ts +14 -2
- package/dist/authentication.d.ts.map +1 -1
- package/dist/authentication.js +83 -5
- package/dist/authentication.js.map +1 -1
- package/dist/index.cjs +4423 -2069
- package/dist/index.cjs.map +3 -3
- package/package.json +13 -9
- package/src/arcgis/arcgis-feature-server-source-loader.ts +8 -4
- package/src/arcgis/arcgis-image-server-source-loader.ts +2 -2
- package/src/authentication.ts +110 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loaders.gl/services",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.7",
|
|
4
4
|
"description": "Framework-independent loaders and sources for geospatial services",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
"./bundled": {
|
|
37
37
|
"types": "./dist/bundled.d.ts",
|
|
38
38
|
"import": "./dist/bundled.js"
|
|
39
|
+
},
|
|
40
|
+
"./authentication": {
|
|
41
|
+
"types": "./dist/authentication.d.ts",
|
|
42
|
+
"import": "./dist/authentication.js"
|
|
39
43
|
}
|
|
40
44
|
},
|
|
41
45
|
"sideEffects": false,
|
|
@@ -45,16 +49,16 @@
|
|
|
45
49
|
"README.md"
|
|
46
50
|
],
|
|
47
51
|
"dependencies": {
|
|
48
|
-
"@loaders.gl/gis": "5.0.0-alpha.
|
|
49
|
-
"@loaders.gl/i3s": "5.0.0-alpha.
|
|
50
|
-
"@loaders.gl/images": "5.0.0-alpha.
|
|
51
|
-
"@loaders.gl/lerc": "5.0.0-alpha.
|
|
52
|
-
"@loaders.gl/loader-utils": "5.0.0-alpha.
|
|
53
|
-
"@loaders.gl/mvt": "5.0.0-alpha.
|
|
54
|
-
"@loaders.gl/schema": "5.0.0-alpha.
|
|
52
|
+
"@loaders.gl/gis": "5.0.0-alpha.7",
|
|
53
|
+
"@loaders.gl/i3s": "5.0.0-alpha.7",
|
|
54
|
+
"@loaders.gl/images": "5.0.0-alpha.7",
|
|
55
|
+
"@loaders.gl/lerc": "5.0.0-alpha.7",
|
|
56
|
+
"@loaders.gl/loader-utils": "5.0.0-alpha.7",
|
|
57
|
+
"@loaders.gl/mvt": "5.0.0-alpha.7",
|
|
58
|
+
"@loaders.gl/schema": "5.0.0-alpha.7"
|
|
55
59
|
},
|
|
56
60
|
"peerDependencies": {
|
|
57
61
|
"@loaders.gl/core": "~5.0.0-alpha.0"
|
|
58
62
|
},
|
|
59
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "e1e6174f9e66f4f3e695e8778f769429508342f1"
|
|
60
64
|
}
|
|
@@ -155,7 +155,9 @@ export class ArcGISVectorSource
|
|
|
155
155
|
return geoJsonTable;
|
|
156
156
|
case 'arrow':
|
|
157
157
|
default:
|
|
158
|
-
return convertFeaturesToWKBArrowTable(geoJsonTable.features
|
|
158
|
+
return convertFeaturesToWKBArrowTable(geoJsonTable.features, {
|
|
159
|
+
encodingPreference: parameters.geoarrow?.encodingPreference
|
|
160
|
+
});
|
|
159
161
|
}
|
|
160
162
|
}
|
|
161
163
|
|
|
@@ -182,13 +184,15 @@ export class ArcGISVectorSource
|
|
|
182
184
|
/** Builds a query URL from generic vector source parameters. */
|
|
183
185
|
getFeaturesURL(parameters: GetFeaturesParameters): string {
|
|
184
186
|
const defaultParameters = this.options['arcgis-feature-server']?.queryParameters || {};
|
|
185
|
-
const
|
|
187
|
+
const outputSpatialReference = normalizeArcGISSpatialReference(parameters.crs) || 4326;
|
|
188
|
+
const requestSpatialReference =
|
|
189
|
+
normalizeArcGISSpatialReference(parameters.requestCrs) || outputSpatialReference;
|
|
186
190
|
const queryParameters: ArcGISFeatureServiceQueryOptions = {
|
|
187
191
|
returnGeometry: true,
|
|
188
192
|
where: '1=1',
|
|
189
193
|
outFields: '*',
|
|
190
|
-
outSR:
|
|
191
|
-
inSR:
|
|
194
|
+
outSR: outputSpatialReference,
|
|
195
|
+
inSR: requestSpatialReference,
|
|
192
196
|
f: 'geojson',
|
|
193
197
|
...defaultParameters
|
|
194
198
|
};
|
|
@@ -114,10 +114,10 @@ export class ArcGISImageSource
|
|
|
114
114
|
|
|
115
115
|
/** Requests an image from generic ImageSource parameters. */
|
|
116
116
|
async getImage(parameters: GetImageParameters): Promise<ImageType> {
|
|
117
|
-
const {boundingBox,
|
|
117
|
+
const {boundingBox, width, height, crs, format, signal} = parameters;
|
|
118
118
|
const spatialReference = normalizeArcGISSpatialReference(crs) || '4326';
|
|
119
119
|
const imageParameters: ArcGISExportImageParameters = {
|
|
120
|
-
bbox:
|
|
120
|
+
bbox: [...boundingBox[0], ...boundingBox[1]],
|
|
121
121
|
bboxSR: spatialReference,
|
|
122
122
|
imageSR: spatialReference,
|
|
123
123
|
width,
|
package/src/authentication.ts
CHANGED
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
FetchLike,
|
|
7
|
+
RequestCredential,
|
|
8
|
+
TokenProviderContext,
|
|
9
|
+
TokenValue
|
|
10
|
+
} from '@loaders.gl/loader-utils';
|
|
6
11
|
import {
|
|
12
|
+
createAuthenticatedFetch,
|
|
7
13
|
createBearerTokenCredential,
|
|
8
14
|
createQueryParameterCredential
|
|
9
15
|
} from '@loaders.gl/loader-utils';
|
|
10
16
|
|
|
17
|
+
const CESIUM_ION_API_ORIGIN = 'https://api.cesium.com';
|
|
18
|
+
const CESIUM_ION_ASSET_ORIGINS = [
|
|
19
|
+
'https://assets.ion.cesium.com',
|
|
20
|
+
'https://assets.cesium.com'
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
11
23
|
/** Shared options for provider credential presets with known public API origins. */
|
|
12
24
|
export type ServiceCredentialOptions = {
|
|
13
25
|
/** Replaces the provider's default exact-origin allowlist. */
|
|
@@ -38,6 +50,12 @@ export type GoogleMapsCredentialOptions = ServiceCredentialOptions & {
|
|
|
38
50
|
export type CesiumIonCredentialOptions = ServiceCredentialOptions & {
|
|
39
51
|
/** Cesium ion access token or application-managed token callback. */
|
|
40
52
|
accessToken: TokenValue;
|
|
53
|
+
/** Optional asset whose endpoint-scoped token should be resolved. */
|
|
54
|
+
assetId?: number | string;
|
|
55
|
+
/** Overrides the transport used for the ion endpoint-token exchange. */
|
|
56
|
+
fetch?: FetchLike;
|
|
57
|
+
/** Whether the supplied token is account-scoped or already asset-scoped. */
|
|
58
|
+
tokenType?: 'account' | 'asset' | 'auto';
|
|
41
59
|
};
|
|
42
60
|
|
|
43
61
|
/** Creates an exact-origin ArcGIS `token` query credential. */
|
|
@@ -73,11 +91,98 @@ export function createGoogleMapsCredential(
|
|
|
73
91
|
});
|
|
74
92
|
}
|
|
75
93
|
|
|
76
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* Creates a bearer credential for the Cesium ion REST API or one Cesium ion asset.
|
|
96
|
+
*
|
|
97
|
+
* Supplying `assetId` creates an asset-origin credential. Account tokens are exchanged lazily
|
|
98
|
+
* through Cesium ion's endpoint API; legacy asset-scoped tokens are used directly. Without an
|
|
99
|
+
* `assetId`, the credential is scoped only to the ion REST API for backwards compatibility.
|
|
100
|
+
*/
|
|
77
101
|
export function createCesiumIonCredential(options: CesiumIonCredentialOptions): RequestCredential {
|
|
102
|
+
if (options.assetId === undefined || options.assetId === null) {
|
|
103
|
+
return createBearerTokenCredential({
|
|
104
|
+
id: 'cesium-ion-access-token',
|
|
105
|
+
origins: options.origins || [CESIUM_ION_API_ORIGIN],
|
|
106
|
+
token: options.accessToken
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const assetId = options.assetId;
|
|
111
|
+
const origins = options.origins || CESIUM_ION_ASSET_ORIGINS;
|
|
112
|
+
let endpointTokenPromise: Promise<string | null> | null = null;
|
|
113
|
+
|
|
78
114
|
return createBearerTokenCredential({
|
|
79
|
-
id:
|
|
80
|
-
origins
|
|
81
|
-
token:
|
|
115
|
+
id: `cesium-ion-asset-${assetId}`,
|
|
116
|
+
origins,
|
|
117
|
+
token: context => {
|
|
118
|
+
if (context.reason === 'refresh') {
|
|
119
|
+
endpointTokenPromise = null;
|
|
120
|
+
}
|
|
121
|
+
endpointTokenPromise ||= resolveCesiumIonAssetToken(options, assetId, origins, context);
|
|
122
|
+
return endpointTokenPromise;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Resolves an account token and exchanges it for an asset endpoint token when required. */
|
|
128
|
+
async function resolveCesiumIonAssetToken(
|
|
129
|
+
options: CesiumIonCredentialOptions,
|
|
130
|
+
assetId: number | string,
|
|
131
|
+
origins: readonly string[],
|
|
132
|
+
context: TokenProviderContext
|
|
133
|
+
): Promise<string | null> {
|
|
134
|
+
const endpointURL = `${CESIUM_ION_API_ORIGIN}/v1/assets/${assetId}/endpoint`;
|
|
135
|
+
const accessToken =
|
|
136
|
+
typeof options.accessToken === 'function'
|
|
137
|
+
? await options.accessToken({...context, url: endpointURL})
|
|
138
|
+
: options.accessToken;
|
|
139
|
+
if (!accessToken) return null;
|
|
140
|
+
|
|
141
|
+
const tokenType = options.tokenType || 'auto';
|
|
142
|
+
if (
|
|
143
|
+
tokenType === 'asset' ||
|
|
144
|
+
(tokenType === 'auto' && isCesiumIonAssetToken(accessToken, assetId))
|
|
145
|
+
) {
|
|
146
|
+
return accessToken;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const apiCredential = createBearerTokenCredential({
|
|
150
|
+
id: `cesium-ion-api-${assetId}`,
|
|
151
|
+
origins: [CESIUM_ION_API_ORIGIN],
|
|
152
|
+
token: accessToken
|
|
153
|
+
});
|
|
154
|
+
const ionFetch = createAuthenticatedFetch({
|
|
155
|
+
fetch: options.fetch,
|
|
156
|
+
credentials: [apiCredential]
|
|
82
157
|
});
|
|
158
|
+
const response = await ionFetch(endpointURL);
|
|
159
|
+
if (!response.ok) {
|
|
160
|
+
throw new Error(`Cesium ion asset ${assetId} endpoint request failed: ${response.status}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const endpoint = (await response.json()) as {accessToken?: unknown; url?: unknown};
|
|
164
|
+
if (typeof endpoint.url !== 'string' || !origins.includes(new URL(endpoint.url).origin)) {
|
|
165
|
+
throw new Error(`Cesium ion asset ${assetId} resolved to an unconfigured origin.`);
|
|
166
|
+
}
|
|
167
|
+
if (typeof endpoint.accessToken !== 'string' || !endpoint.accessToken) {
|
|
168
|
+
throw new Error(`Cesium ion did not return an endpoint token for asset ${assetId}.`);
|
|
169
|
+
}
|
|
170
|
+
return endpoint.accessToken;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Detects legacy JWTs whose claims scope them directly to the requested asset. */
|
|
174
|
+
function isCesiumIonAssetToken(token: string, assetId: number | string): boolean {
|
|
175
|
+
try {
|
|
176
|
+
const encodedClaims = token.split('.')[1];
|
|
177
|
+
if (!encodedClaims) return false;
|
|
178
|
+
const base64 = encodedClaims.replace(/-/g, '+').replace(/_/g, '/');
|
|
179
|
+
const paddedBase64 = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
|
|
180
|
+
const claims = JSON.parse(atob(paddedBase64)) as {assets?: unknown};
|
|
181
|
+
return (
|
|
182
|
+
Array.isArray(claims.assets) &&
|
|
183
|
+
claims.assets.some(claimAssetId => String(claimAssetId) === String(assetId))
|
|
184
|
+
);
|
|
185
|
+
} catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
83
188
|
}
|