@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.
@@ -50,23 +50,34 @@ export class BrowserFileSystem implements FileSystem {
50
50
  }
51
51
 
52
52
  // Local fetches are served from the list of files
53
- const file = this.files[path];
53
+ const file = this._getFile(path, true);
54
54
  if (!file) {
55
55
  return new Response(path, {status: 400, statusText: 'NOT FOUND'});
56
56
  }
57
57
 
58
58
  const headers = new Headers(options?.headers);
59
59
  const range = headers.get('Range');
60
- const bytes = range && /bytes=($1)-($2)/.exec(range);
60
+ const bytes = range && /bytes=(\d+)-(\d+)/.exec(range);
61
61
 
62
62
  if (bytes) {
63
63
  const start = parseInt(bytes[1]);
64
- const end = parseInt(bytes[2]);
64
+ const end = Math.min(parseInt(bytes[2]), file.size - 1);
65
+ if (start > end) {
66
+ const response = new Response(null, {
67
+ status: 416,
68
+ headers: {'Content-Range': `bytes */${file.size}`}
69
+ });
70
+ Object.defineProperty(response, 'url', {value: path});
71
+ return response;
72
+ }
65
73
  // The trick when reading File objects is to read successive "slices" of the File
66
74
  // Per spec https://w3c.github.io/FileAPI/, slicing a File should only update the start and end fields
67
75
  // Actually reading from file should happen in `readAsArrayBuffer` (and as far we can tell it does)
68
- const data = await file.slice(start, end).arrayBuffer();
69
- const response = new Response(data);
76
+ const data = await file.slice(start, end + 1).arrayBuffer();
77
+ const response = new Response(data, {
78
+ status: 206,
79
+ headers: {'Content-Range': `bytes ${start}-${end}/${file.size}`}
80
+ });
70
81
  Object.defineProperty(response, 'url', {value: path});
71
82
  return response;
72
83
  }
@@ -95,7 +106,7 @@ export class BrowserFileSystem implements FileSystem {
95
106
  * Return information (size) about files in this file system
96
107
  */
97
108
  async stat(path: string, options?: object): Promise<{size: number}> {
98
- const file = this.files[path];
109
+ const file = this._getFile(path, false);
99
110
  if (!file) {
100
111
  throw new Error(path);
101
112
  }
@@ -106,16 +117,19 @@ export class BrowserFileSystem implements FileSystem {
106
117
  * Just removes the file from the list
107
118
  */
108
119
  async unlink(path: string): Promise<void> {
109
- delete this.files[path];
110
- delete this.lowerCaseFiles[path];
111
- this.usedFiles[path] = true;
120
+ const file = this._getFile(path, false);
121
+ if (file) {
122
+ delete this.files[file.name];
123
+ delete this.lowerCaseFiles[file.name.toLowerCase()];
124
+ this.usedFiles[file.name] = true;
125
+ }
112
126
  }
113
127
 
114
128
  // implements IRandomAccessFileSystem
115
129
 
116
130
  // RANDOM ACCESS
117
131
  async openReadableFile(pathname: string, flags: unknown): Promise<ReadableFile> {
118
- return new BlobFile(this.files[pathname]);
132
+ return new BlobFile(this._getFile(pathname, true));
119
133
  }
120
134
 
121
135
  // PRIVATE
@@ -123,7 +137,7 @@ export class BrowserFileSystem implements FileSystem {
123
137
  // Supports case independent paths, and file usage tracking
124
138
  _getFile(path: string, used: boolean): File {
125
139
  // Prefer case match, but fall back to case independent.
126
- const file = this.files[path] || this.lowerCaseFiles[path];
140
+ const file = this.files[path] || this.lowerCaseFiles[path.toLowerCase()];
127
141
  if (file && used) {
128
142
  this.usedFiles[path] = true;
129
143
  }
@@ -3,7 +3,7 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import type {LoaderContext, LoaderOptions, FetchLike} from '@loaders.gl/loader-utils';
6
- import {isObject} from '@loaders.gl/loader-utils';
6
+ import {createAuthenticatedFetch, isObject} from '@loaders.gl/loader-utils';
7
7
  import {fetchFile} from '../fetch/fetch-file';
8
8
  import {getGlobalLoaderOptions} from './option-utils';
9
9
 
@@ -21,22 +21,33 @@ export function getFetchFunction(
21
21
 
22
22
  const loaderOptions = options || globalOptions;
23
23
  const fetchOption = loaderOptions.fetch ?? loaderOptions.core?.fetch;
24
+ let fetchFunction: FetchLike;
24
25
 
25
26
  // options.fetch can be a function
26
27
  if (typeof fetchOption === 'function') {
27
- return fetchOption;
28
+ fetchFunction = fetchOption;
29
+ } else if (isObject(fetchOption)) {
30
+ fetchFunction = (url, requestOptions) =>
31
+ fetchFile(url, mergeFetchOptions(fetchOption as RequestInit, requestOptions));
32
+ } else if (context?.fetch) {
33
+ fetchFunction = context.fetch;
34
+ } else {
35
+ fetchFunction = fetchFile;
28
36
  }
29
37
 
30
- // options.fetch can be an options object
31
- if (isObject(fetchOption)) {
32
- return url => fetchFile(url, fetchOption as RequestInit);
33
- }
38
+ return createAuthenticatedFetch({
39
+ fetch: fetchFunction,
40
+ credentials: loaderOptions.core?.credentials || []
41
+ });
42
+ }
34
43
 
35
- // else refer to context (from parent loader) if available
36
- if (context?.fetch) {
37
- return context?.fetch;
44
+ /** Combines static and per-request fetch options without replacing either header collection. */
45
+ function mergeFetchOptions(defaultOptions: RequestInit, requestOptions?: RequestInit): RequestInit {
46
+ const options = {...defaultOptions, ...requestOptions};
47
+ if (defaultOptions.headers || requestOptions?.headers) {
48
+ const headers = new Headers(defaultOptions.headers);
49
+ new Headers(requestOptions?.headers).forEach((value, key) => headers.set(key, value));
50
+ options.headers = headers;
38
51
  }
39
-
40
- // else return the default fetch function
41
- return fetchFile;
52
+ return options;
42
53
  }