@loaders.gl/services 5.0.0-alpha.4 → 5.0.0-alpha.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loaders.gl/services",
3
- "version": "5.0.0-alpha.4",
3
+ "version": "5.0.0-alpha.6",
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.4",
49
- "@loaders.gl/i3s": "5.0.0-alpha.4",
50
- "@loaders.gl/images": "5.0.0-alpha.4",
51
- "@loaders.gl/lerc": "5.0.0-alpha.4",
52
- "@loaders.gl/loader-utils": "5.0.0-alpha.4",
53
- "@loaders.gl/mvt": "5.0.0-alpha.4",
54
- "@loaders.gl/schema": "5.0.0-alpha.4"
52
+ "@loaders.gl/gis": "5.0.0-alpha.6",
53
+ "@loaders.gl/i3s": "5.0.0-alpha.6",
54
+ "@loaders.gl/images": "5.0.0-alpha.6",
55
+ "@loaders.gl/lerc": "5.0.0-alpha.6",
56
+ "@loaders.gl/loader-utils": "5.0.0-alpha.6",
57
+ "@loaders.gl/mvt": "5.0.0-alpha.6",
58
+ "@loaders.gl/schema": "5.0.0-alpha.6"
55
59
  },
56
60
  "peerDependencies": {
57
61
  "@loaders.gl/core": "~5.0.0-alpha.0"
58
62
  },
59
- "gitHead": "470a9cf1956b8326c92015b314f4ad8a10a3517c"
63
+ "gitHead": "9b9ea7738e0fe63e55c19c136283705f99e0b9bf"
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
 
@@ -2,12 +2,24 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import type {RequestCredential, TokenValue} from '@loaders.gl/loader-utils';
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
- /** Creates a bearer credential for the Cesium ion REST API. */
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: 'cesium-ion-access-token',
80
- origins: options.origins || ['https://api.cesium.com'],
81
- token: options.accessToken
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
  }