@loaders.gl/core 5.0.0-alpha.2 → 5.0.0-alpha.4

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/index.cjs CHANGED
@@ -270,17 +270,24 @@ var WorkerJob = class {
270
270
  this.isRunning = false;
271
271
  this._reject(error);
272
272
  }
273
- /** Terminates the worker executing this job and rejects its result with an abort error. */
274
- abort() {
273
+ /**
274
+ * Terminates the worker executing this job and rejects its result.
275
+ * @param reason Abort reason supplied by the caller, or a cross-runtime `AbortError`.
276
+ */
277
+ abort(reason) {
275
278
  if (!this.isRunning) {
276
279
  return;
277
280
  }
278
- const error = new Error(`Worker job "${this.name}" was aborted`);
279
- error.name = "AbortError";
281
+ const error = reason ?? createAbortError(`Worker job "${this.name}" was aborted`);
280
282
  this.workerThread.destroy();
281
283
  this.error(error);
282
284
  }
283
285
  };
286
+ function createAbortError(message) {
287
+ const error = new Error(message);
288
+ error.name = "AbortError";
289
+ return error;
290
+ }
284
291
 
285
292
  // ../worker-utils/src/lib/node/worker_threads-browser.ts
286
293
  var NodeWorker = class {
@@ -748,7 +755,7 @@ function getWorkerName(worker) {
748
755
  function getWorkerURL(worker, options = {}) {
749
756
  var _a;
750
757
  const workerOptions = options[worker.id] || {};
751
- const workerFile = isBrowser2 ? `${worker.id}-worker.js` : `${worker.id}-worker-node.js`;
758
+ const workerFile = isBrowser2 ? `${worker.id}-worker.js` : worker.workerNode || `${worker.id}-worker-node.js`;
752
759
  let url = workerOptions.workerUrl;
753
760
  if (!url && typeof worker.worker === "string") {
754
761
  url = worker.worker;
@@ -815,7 +822,7 @@ async function processOnWorker(worker, data, options = {}, context = {}, jobCont
815
822
  // eslint-disable-next-line
816
823
  onMessage.bind(null, context)
817
824
  );
818
- const abortJob = () => job.abort();
825
+ const abortJob = () => job.abort(getAbortReason(options.signal));
819
826
  (_a = options.signal) == null ? void 0 : _a.addEventListener("abort", abortJob, { once: true });
820
827
  if ((_b = options.signal) == null ? void 0 : _b.aborted) {
821
828
  abortJob();
@@ -841,9 +848,15 @@ function throwIfAborted(signal) {
841
848
  if (!(signal == null ? void 0 : signal.aborted)) {
842
849
  return;
843
850
  }
844
- const error = new Error("Worker job was aborted");
851
+ throw getAbortReason(signal);
852
+ }
853
+ function getAbortReason(signal) {
854
+ return (signal == null ? void 0 : signal.reason) ?? createAbortError2("Worker job was aborted");
855
+ }
856
+ function createAbortError2(message) {
857
+ const error = new Error(message);
845
858
  error.name = "AbortError";
846
- throw error;
859
+ return error;
847
860
  }
848
861
  async function onMessage(context, job, type, payload) {
849
862
  switch (type) {
@@ -898,12 +911,12 @@ function canParseWithWorker(loader, options) {
898
911
  }
899
912
  return Boolean(canProcessOnWorker(loader, workerOptions));
900
913
  }
901
- async function parseWithWorker(loader, data, options, context, parseOnMainThread) {
902
- const signal = getWorkerAbortSignal(options);
914
+ async function parseWithWorker(loader, data, options, context, parseOnMainThread, signal) {
915
+ const workerSignal = signal || getWorkerAbortSignal(options);
903
916
  const result = await processOnWorker(
904
917
  loader,
905
918
  data,
906
- { ...getWorkerOptions(options), signal },
919
+ { ...getWorkerOptions(options), signal: workerSignal },
907
920
  {
908
921
  process: async (input, processOptions, _workerContext, parseContext) => {
909
922
  if (!parseOnMainThread) {
@@ -1261,6 +1274,115 @@ var RequestScheduler = class {
1261
1274
  }
1262
1275
  };
1263
1276
 
1277
+ // ../loader-utils/src/lib/request-utils/request-credentials.ts
1278
+ var AUTHENTICATED_FETCH = /* @__PURE__ */ Symbol("loaders.gl.authenticated-fetch");
1279
+ var refreshPromises = /* @__PURE__ */ new WeakMap();
1280
+ function createAuthenticatedFetch(options) {
1281
+ const baseFetch = options.fetch || ((url, requestOptions) => fetch(url, requestOptions));
1282
+ if (!options.credentials.length) return baseFetch;
1283
+ const existingMetadata = baseFetch[AUTHENTICATED_FETCH];
1284
+ if (existingMetadata === options.credentials) return baseFetch;
1285
+ const authenticatedFetch = async (url, requestOptions = {}) => {
1286
+ var _a, _b, _c;
1287
+ const resolvedURL = parseHTTPURL(url);
1288
+ if (!resolvedURL) return baseFetch(url, requestOptions);
1289
+ const authorization = await authorizeRequest(resolvedURL, requestOptions, options.credentials);
1290
+ const response = await baseFetch(authorization.url, authorization.options);
1291
+ const refreshableCredentials = authorization.credentials.filter(
1292
+ ({ credential }) => typeof credential.token === "function" && credential.refreshStatusCodes.includes(response.status)
1293
+ );
1294
+ if (!refreshableCredentials.length || !isReplayableRequest(requestOptions)) return response;
1295
+ if ((_a = requestOptions.signal) == null ? void 0 : _a.aborted) throw createAbortError3();
1296
+ const refreshedTokens = /* @__PURE__ */ new Map();
1297
+ await Promise.all(
1298
+ refreshableCredentials.map(async ({ credential }) => {
1299
+ const token = await refreshCredential(credential, url, response);
1300
+ if (token) refreshedTokens.set(credential, token);
1301
+ })
1302
+ );
1303
+ if (!refreshedTokens.size) return response;
1304
+ await ((_b = response.body) == null ? void 0 : _b.cancel().catch(() => {
1305
+ }));
1306
+ if ((_c = requestOptions.signal) == null ? void 0 : _c.aborted) throw createAbortError3();
1307
+ const replayAuthorization = await authorizeRequest(
1308
+ resolvedURL,
1309
+ requestOptions,
1310
+ options.credentials,
1311
+ refreshedTokens,
1312
+ authorization.credentials
1313
+ );
1314
+ return baseFetch(replayAuthorization.url, replayAuthorization.options);
1315
+ };
1316
+ Object.defineProperty(authenticatedFetch, AUTHENTICATED_FETCH, {
1317
+ value: options.credentials
1318
+ });
1319
+ return authenticatedFetch;
1320
+ }
1321
+ async function authorizeRequest(sourceURL, requestOptions, credentials, tokenOverrides = /* @__PURE__ */ new Map(), previousCredentials = []) {
1322
+ const url = new URL(sourceURL);
1323
+ const headers = new Headers(requestOptions.headers);
1324
+ const resolvedCredentials = [];
1325
+ const previousTokens = new Map(
1326
+ previousCredentials.map(({ credential, token }) => [credential, token])
1327
+ );
1328
+ for (const credential of credentials) {
1329
+ if (!credential.origins.includes(url.origin)) continue;
1330
+ const overrideToken = tokenOverrides.get(credential);
1331
+ const previousToken = previousTokens.get(credential);
1332
+ const ownsQueryParameter = credential.type === "query-parameter" && previousToken !== void 0;
1333
+ const ownsHeader = credential.type === "header" && previousToken !== void 0;
1334
+ if (credential.type === "query-parameter" && url.searchParams.has(credential.name) && !ownsQueryParameter || credential.type === "header" && headers.has(credential.name) && !ownsHeader) {
1335
+ continue;
1336
+ }
1337
+ const token = overrideToken ?? previousToken ?? await resolveToken(credential.token, { url: sourceURL.toString(), reason: "request" });
1338
+ if (!token) continue;
1339
+ if (credential.type === "query-parameter") {
1340
+ url.searchParams.set(credential.name, token);
1341
+ } else {
1342
+ headers.set(credential.name, `${credential.prefix || ""}${token}`);
1343
+ }
1344
+ resolvedCredentials.push({ credential, token });
1345
+ }
1346
+ return {
1347
+ url: url.toString(),
1348
+ options: { ...requestOptions, headers },
1349
+ credentials: resolvedCredentials
1350
+ };
1351
+ }
1352
+ async function refreshCredential(credential, url, response) {
1353
+ const existingPromise = refreshPromises.get(credential);
1354
+ if (existingPromise) return existingPromise;
1355
+ const tokenProvider = credential.token;
1356
+ const promise = Promise.resolve(
1357
+ tokenProvider({
1358
+ url,
1359
+ reason: "refresh",
1360
+ response: { status: response.status, headers: response.headers }
1361
+ })
1362
+ ).finally(() => refreshPromises.delete(credential));
1363
+ refreshPromises.set(credential, promise);
1364
+ return promise;
1365
+ }
1366
+ async function resolveToken(token, context) {
1367
+ return typeof token === "function" ? token(context) : token;
1368
+ }
1369
+ function parseHTTPURL(url) {
1370
+ try {
1371
+ const parsedURL = new URL(url);
1372
+ return ["http:", "https:"].includes(parsedURL.protocol) ? parsedURL : null;
1373
+ } catch {
1374
+ return null;
1375
+ }
1376
+ }
1377
+ function isReplayableRequest(options) {
1378
+ const method = (options.method || "GET").toUpperCase();
1379
+ if (!["GET", "HEAD", "OPTIONS", "PUT", "DELETE"].includes(method)) return false;
1380
+ return !(options.body && typeof ReadableStream !== "undefined" && options.body instanceof ReadableStream);
1381
+ }
1382
+ function createAbortError3() {
1383
+ return new DOMException("The operation was aborted.", "AbortError");
1384
+ }
1385
+
1264
1386
  // ../loader-utils/src/lib/path-utils/file-aliases.ts
1265
1387
  var pathPrefix = "";
1266
1388
  var fileAliases = {};
@@ -2742,20 +2864,33 @@ function getIterableFromData(data) {
2742
2864
 
2743
2865
  // dist/lib/loader-utils/get-fetch-function.js
2744
2866
  function getFetchFunction(options, context) {
2745
- var _a;
2867
+ var _a, _b;
2746
2868
  const globalOptions = getGlobalLoaderOptions();
2747
2869
  const loaderOptions = options || globalOptions;
2748
2870
  const fetchOption = loaderOptions.fetch ?? ((_a = loaderOptions.core) == null ? void 0 : _a.fetch);
2871
+ let fetchFunction;
2749
2872
  if (typeof fetchOption === "function") {
2750
- return fetchOption;
2751
- }
2752
- if (isObject(fetchOption)) {
2753
- return (url) => fetchFile(url, fetchOption);
2873
+ fetchFunction = fetchOption;
2874
+ } else if (isObject(fetchOption)) {
2875
+ fetchFunction = (url, requestOptions) => fetchFile(url, mergeFetchOptions(fetchOption, requestOptions));
2876
+ } else if (context == null ? void 0 : context.fetch) {
2877
+ fetchFunction = context.fetch;
2878
+ } else {
2879
+ fetchFunction = fetchFile;
2754
2880
  }
2755
- if (context == null ? void 0 : context.fetch) {
2756
- return context == null ? void 0 : context.fetch;
2881
+ return createAuthenticatedFetch({
2882
+ fetch: fetchFunction,
2883
+ credentials: ((_b = loaderOptions.core) == null ? void 0 : _b.credentials) || []
2884
+ });
2885
+ }
2886
+ function mergeFetchOptions(defaultOptions, requestOptions) {
2887
+ const options = { ...defaultOptions, ...requestOptions };
2888
+ if (defaultOptions.headers || (requestOptions == null ? void 0 : requestOptions.headers)) {
2889
+ const headers = new Headers(defaultOptions.headers);
2890
+ new Headers(requestOptions == null ? void 0 : requestOptions.headers).forEach((value, key) => headers.set(key, value));
2891
+ options.headers = headers;
2757
2892
  }
2758
- return fetchFile;
2893
+ return options;
2759
2894
  }
2760
2895
 
2761
2896
  // dist/lib/api/load-loader.js
@@ -2952,7 +3087,7 @@ function isTable(table) {
2952
3087
  case "geojson-table":
2953
3088
  return Array.isArray(table.features);
2954
3089
  case "columnar-table":
2955
- return table.data && typeof table.data === "object";
3090
+ return Boolean(table.data && typeof table.data === "object");
2956
3091
  case "arrow-table":
2957
3092
  return Boolean(((_a = table == null ? void 0 : table.data) == null ? void 0 : _a.numRows) !== void 0);
2958
3093
  default:
@@ -3148,6 +3283,12 @@ async function load(url, loaders, options, context) {
3148
3283
  loadInBatches
3149
3284
  });
3150
3285
  }
3286
+ if (selectedLoader && typeof url === "string") {
3287
+ const loaderImplementation = await getLoaderImplementation(selectedLoader, resolvedOptions, url);
3288
+ const parseUrl = loaderImplementation.parseUrl;
3289
+ if (parseUrl)
3290
+ return await parseUrl(url, resolvedOptions, context);
3291
+ }
3151
3292
  }
3152
3293
  const fetch2 = getFetchFunction(resolvedOptions);
3153
3294
  let data = url;
@@ -3510,7 +3651,7 @@ function makeStream(source, options) {
3510
3651
  }
3511
3652
 
3512
3653
  // dist/null-loader.js
3513
- var VERSION4 = true ? "5.0.0-alpha.2" : "latest";
3654
+ var VERSION4 = true ? "5.0.0-alpha.4" : "latest";
3514
3655
  var NullWorkerLoader = {
3515
3656
  dataType: null,
3516
3657
  batchType: null,
@@ -3628,18 +3769,29 @@ var BrowserFileSystem = class {
3628
3769
  if (path.includes("://")) {
3629
3770
  return this._fetch(path, options);
3630
3771
  }
3631
- const file = this.files[path];
3772
+ const file = this._getFile(path, true);
3632
3773
  if (!file) {
3633
3774
  return new Response(path, { status: 400, statusText: "NOT FOUND" });
3634
3775
  }
3635
3776
  const headers = new Headers(options == null ? void 0 : options.headers);
3636
3777
  const range = headers.get("Range");
3637
- const bytes = range && /bytes=($1)-($2)/.exec(range);
3778
+ const bytes = range && /bytes=(\d+)-(\d+)/.exec(range);
3638
3779
  if (bytes) {
3639
3780
  const start = parseInt(bytes[1]);
3640
- const end = parseInt(bytes[2]);
3641
- const data = await file.slice(start, end).arrayBuffer();
3642
- const response2 = new Response(data);
3781
+ const end = Math.min(parseInt(bytes[2]), file.size - 1);
3782
+ if (start > end) {
3783
+ const response3 = new Response(null, {
3784
+ status: 416,
3785
+ headers: { "Content-Range": `bytes */${file.size}` }
3786
+ });
3787
+ Object.defineProperty(response3, "url", { value: path });
3788
+ return response3;
3789
+ }
3790
+ const data = await file.slice(start, end + 1).arrayBuffer();
3791
+ const response2 = new Response(data, {
3792
+ status: 206,
3793
+ headers: { "Content-Range": `bytes ${start}-${end}/${file.size}` }
3794
+ });
3643
3795
  Object.defineProperty(response2, "url", { value: path });
3644
3796
  return response2;
3645
3797
  }
@@ -3663,7 +3815,7 @@ var BrowserFileSystem = class {
3663
3815
  * Return information (size) about files in this file system
3664
3816
  */
3665
3817
  async stat(path, options) {
3666
- const file = this.files[path];
3818
+ const file = this._getFile(path, false);
3667
3819
  if (!file) {
3668
3820
  throw new Error(path);
3669
3821
  }
@@ -3673,19 +3825,22 @@ var BrowserFileSystem = class {
3673
3825
  * Just removes the file from the list
3674
3826
  */
3675
3827
  async unlink(path) {
3676
- delete this.files[path];
3677
- delete this.lowerCaseFiles[path];
3678
- this.usedFiles[path] = true;
3828
+ const file = this._getFile(path, false);
3829
+ if (file) {
3830
+ delete this.files[file.name];
3831
+ delete this.lowerCaseFiles[file.name.toLowerCase()];
3832
+ this.usedFiles[file.name] = true;
3833
+ }
3679
3834
  }
3680
3835
  // implements IRandomAccessFileSystem
3681
3836
  // RANDOM ACCESS
3682
3837
  async openReadableFile(pathname, flags) {
3683
- return new BlobFile(this.files[pathname]);
3838
+ return new BlobFile(this._getFile(pathname, true));
3684
3839
  }
3685
3840
  // PRIVATE
3686
3841
  // Supports case independent paths, and file usage tracking
3687
3842
  _getFile(path, used) {
3688
- const file = this.files[path] || this.lowerCaseFiles[path];
3843
+ const file = this.files[path] || this.lowerCaseFiles[path.toLowerCase()];
3689
3844
  if (file && used) {
3690
3845
  this.usedFiles[path] = true;
3691
3846
  }