@loaders.gl/core 4.4.0-alpha.2 → 4.4.0

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.
Files changed (144) hide show
  1. package/README.md +4 -0
  2. package/dist/core-addons/write-file-browser.js +1 -0
  3. package/dist/core-addons/write-file-browser.js.map +1 -0
  4. package/dist/dist.dev.js +641 -286
  5. package/dist/dist.min.js +3 -3
  6. package/dist/index.cjs +411 -262
  7. package/dist/index.cjs.map +4 -4
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -1
  11. package/dist/index.js.map +1 -0
  12. package/dist/iterators/batch-iterators/timed-batch-iterator.js +1 -0
  13. package/dist/iterators/batch-iterators/timed-batch-iterator.js.map +1 -0
  14. package/dist/iterators/make-iterator/make-array-buffer-iterator.js +1 -0
  15. package/dist/iterators/make-iterator/make-array-buffer-iterator.js.map +1 -0
  16. package/dist/iterators/make-iterator/make-blob-iterator.js +1 -0
  17. package/dist/iterators/make-iterator/make-blob-iterator.js.map +1 -0
  18. package/dist/iterators/make-iterator/make-iterator.d.ts.map +1 -1
  19. package/dist/iterators/make-iterator/make-iterator.js +7 -3
  20. package/dist/iterators/make-iterator/make-iterator.js.map +1 -0
  21. package/dist/iterators/make-iterator/make-stream-iterator.js +1 -0
  22. package/dist/iterators/make-iterator/make-stream-iterator.js.map +1 -0
  23. package/dist/iterators/make-iterator/make-string-iterator.d.ts.map +1 -1
  24. package/dist/iterators/make-iterator/make-string-iterator.js +3 -1
  25. package/dist/iterators/make-iterator/make-string-iterator.js.map +1 -0
  26. package/dist/iterators/make-stream/make-stream.js +1 -0
  27. package/dist/iterators/make-stream/make-stream.js.map +1 -0
  28. package/dist/lib/api/create-data-source.js +1 -0
  29. package/dist/lib/api/create-data-source.js.map +1 -0
  30. package/dist/lib/api/encode-table.d.ts.map +1 -1
  31. package/dist/lib/api/encode-table.js +3 -2
  32. package/dist/lib/api/encode-table.js.map +1 -0
  33. package/dist/lib/api/encode.d.ts.map +1 -1
  34. package/dist/lib/api/encode.js +3 -2
  35. package/dist/lib/api/encode.js.map +1 -0
  36. package/dist/lib/api/load-in-batches.js +1 -0
  37. package/dist/lib/api/load-in-batches.js.map +1 -0
  38. package/dist/lib/api/load.d.ts.map +1 -1
  39. package/dist/lib/api/load.js +15 -1
  40. package/dist/lib/api/load.js.map +1 -0
  41. package/dist/lib/api/loader-options.js +1 -0
  42. package/dist/lib/api/loader-options.js.map +1 -0
  43. package/dist/lib/api/parse-in-batches.d.ts.map +1 -1
  44. package/dist/lib/api/parse-in-batches.js +7 -6
  45. package/dist/lib/api/parse-in-batches.js.map +1 -0
  46. package/dist/lib/api/parse-sync.d.ts.map +1 -1
  47. package/dist/lib/api/parse-sync.js +4 -3
  48. package/dist/lib/api/parse-sync.js.map +1 -0
  49. package/dist/lib/api/parse.d.ts.map +1 -1
  50. package/dist/lib/api/parse.js +8 -9
  51. package/dist/lib/api/parse.js.map +1 -0
  52. package/dist/lib/api/register-loaders.js +1 -0
  53. package/dist/lib/api/register-loaders.js.map +1 -0
  54. package/dist/lib/api/select-loader.d.ts +3 -3
  55. package/dist/lib/api/select-loader.d.ts.map +1 -1
  56. package/dist/lib/api/select-loader.js +36 -15
  57. package/dist/lib/api/select-loader.js.map +1 -0
  58. package/dist/lib/api/select-source.js +1 -0
  59. package/dist/lib/api/select-source.js.map +1 -0
  60. package/dist/lib/common.js +1 -0
  61. package/dist/lib/common.js.map +1 -0
  62. package/dist/lib/fetch/fetch-error-message.js +1 -0
  63. package/dist/lib/fetch/fetch-error-message.js.map +1 -0
  64. package/dist/lib/fetch/fetch-error.js +1 -0
  65. package/dist/lib/fetch/fetch-error.js.map +1 -0
  66. package/dist/lib/fetch/fetch-file.js +1 -0
  67. package/dist/lib/fetch/fetch-file.js.map +1 -0
  68. package/dist/lib/fetch/read-array-buffer.js +1 -0
  69. package/dist/lib/fetch/read-array-buffer.js.map +1 -0
  70. package/dist/lib/filesystems/browser-filesystem.js +1 -0
  71. package/dist/lib/filesystems/browser-filesystem.js.map +1 -0
  72. package/dist/lib/filesystems/read-array-buffer.js +1 -0
  73. package/dist/lib/filesystems/read-array-buffer.js.map +1 -0
  74. package/dist/lib/init.js +2 -1
  75. package/dist/lib/init.js.map +1 -0
  76. package/dist/lib/loader-utils/check-errors.js +1 -0
  77. package/dist/lib/loader-utils/check-errors.js.map +1 -0
  78. package/dist/lib/loader-utils/get-data.d.ts +18 -1
  79. package/dist/lib/loader-utils/get-data.d.ts.map +1 -1
  80. package/dist/lib/loader-utils/get-data.js +47 -38
  81. package/dist/lib/loader-utils/get-data.js.map +1 -0
  82. package/dist/lib/loader-utils/get-fetch-function.d.ts.map +1 -1
  83. package/dist/lib/loader-utils/get-fetch-function.js +7 -5
  84. package/dist/lib/loader-utils/get-fetch-function.js.map +1 -0
  85. package/dist/lib/loader-utils/loader-context.d.ts +2 -2
  86. package/dist/lib/loader-utils/loader-context.d.ts.map +1 -1
  87. package/dist/lib/loader-utils/loader-context.js +1 -0
  88. package/dist/lib/loader-utils/loader-context.js.map +1 -0
  89. package/dist/lib/loader-utils/loggers.js +1 -0
  90. package/dist/lib/loader-utils/loggers.js.map +1 -0
  91. package/dist/lib/loader-utils/normalize-loader.js +1 -0
  92. package/dist/lib/loader-utils/normalize-loader.js.map +1 -0
  93. package/dist/lib/loader-utils/option-defaults.d.ts +22 -0
  94. package/dist/lib/loader-utils/option-defaults.d.ts.map +1 -1
  95. package/dist/lib/loader-utils/option-defaults.js +63 -33
  96. package/dist/lib/loader-utils/option-defaults.js.map +1 -0
  97. package/dist/lib/loader-utils/option-utils.d.ts +10 -4
  98. package/dist/lib/loader-utils/option-utils.d.ts.map +1 -1
  99. package/dist/lib/loader-utils/option-utils.js +123 -19
  100. package/dist/lib/loader-utils/option-utils.js.map +1 -0
  101. package/dist/lib/progress/fetch-progress.js +1 -0
  102. package/dist/lib/progress/fetch-progress.js.map +1 -0
  103. package/dist/lib/utils/mime-type-utils.js +1 -0
  104. package/dist/lib/utils/mime-type-utils.js.map +1 -0
  105. package/dist/lib/utils/resource-utils.d.ts.map +1 -1
  106. package/dist/lib/utils/resource-utils.js +8 -10
  107. package/dist/lib/utils/resource-utils.js.map +1 -0
  108. package/dist/lib/utils/response-utils.js +2 -1
  109. package/dist/lib/utils/response-utils.js.map +1 -0
  110. package/dist/lib/utils/url-utils.js +1 -0
  111. package/dist/lib/utils/url-utils.js.map +1 -0
  112. package/dist/null-loader.d.ts +3 -3
  113. package/dist/null-loader.d.ts.map +1 -1
  114. package/dist/null-loader.js +2 -1
  115. package/dist/null-loader.js.map +1 -0
  116. package/dist/null-worker-node.js +5 -2
  117. package/dist/null-worker.js +5 -2
  118. package/dist/workers/null-worker-node.js +1 -0
  119. package/dist/workers/null-worker-node.js.map +1 -0
  120. package/dist/workers/null-worker.js +1 -0
  121. package/dist/workers/null-worker.js.map +1 -0
  122. package/package.json +13 -10
  123. package/src/index.ts +1 -1
  124. package/src/iterators/make-iterator/make-iterator.ts +8 -5
  125. package/src/iterators/make-iterator/make-string-iterator.ts +2 -1
  126. package/src/lib/api/encode-table.ts +2 -1
  127. package/src/lib/api/encode.ts +8 -2
  128. package/src/lib/api/load.ts +15 -1
  129. package/src/lib/api/parse-in-batches.ts +26 -19
  130. package/src/lib/api/parse-sync.ts +6 -5
  131. package/src/lib/api/parse.ts +15 -11
  132. package/src/lib/api/select-loader.ts +68 -24
  133. package/src/lib/loader-utils/get-data.ts +62 -48
  134. package/src/lib/loader-utils/get-fetch-function.ts +6 -5
  135. package/src/lib/loader-utils/loader-context.ts +2 -2
  136. package/src/lib/loader-utils/option-defaults.ts +68 -35
  137. package/src/lib/loader-utils/option-utils.ts +146 -29
  138. package/src/lib/utils/resource-utils.ts +9 -12
  139. package/src/lib/utils/response-utils.ts +2 -2
  140. package/src/null-loader.ts +6 -3
  141. package/dist/javascript-utils/is-type.d.ts +0 -21
  142. package/dist/javascript-utils/is-type.d.ts.map +0 -1
  143. package/dist/javascript-utils/is-type.js +0 -25
  144. package/src/javascript-utils/is-type.ts +0 -51
@@ -9,21 +9,30 @@ import type {
9
9
  Loader,
10
10
  LoaderOptions
11
11
  } from '@loaders.gl/loader-utils';
12
- import {concatenateArrayBuffersAsync} from '@loaders.gl/loader-utils';
13
12
  import {
13
+ concatenateArrayBuffersAsync,
14
+ isPromise,
14
15
  isResponse,
15
16
  isReadableStream,
16
17
  isAsyncIterable,
17
18
  isIterable,
18
19
  isIterator,
19
20
  isBlob,
20
- isBuffer
21
- } from '../../javascript-utils/is-type';
21
+ isBuffer,
22
+ isArrayBufferLike,
23
+ toArrayBuffer,
24
+ toArrayBufferView
25
+ } from '@loaders.gl/loader-utils';
22
26
  import {makeIterator} from '../../iterators/make-iterator/make-iterator';
23
27
  import {checkResponse, makeResponse} from '../utils/response-utils';
24
28
 
25
29
  const ERR_DATA = 'Cannot convert supplied data type';
26
30
 
31
+ /**
32
+ * Returns an {@link ArrayBuffer} or string from the provided data synchronously.
33
+ * Supports `ArrayBuffer`, `ArrayBufferView`, and `ArrayBufferLike` (e.g. `SharedArrayBuffer`)
34
+ * while preserving typed array view offsets.
35
+ */
27
36
  // eslint-disable-next-line complexity
28
37
  export function getArrayBufferOrStringFromDataSync(
29
38
  data: SyncDataType,
@@ -35,52 +44,32 @@ export function getArrayBufferOrStringFromDataSync(
35
44
  }
36
45
 
37
46
  if (isBuffer(data)) {
38
- // @ts-ignore
39
47
  data = data.buffer;
40
48
  }
41
49
 
42
- if (data instanceof ArrayBuffer) {
43
- const arrayBuffer = data;
44
- if (loader.text && !loader.binary) {
45
- const textDecoder = new TextDecoder('utf8');
46
- return textDecoder.decode(arrayBuffer);
47
- }
48
- return arrayBuffer;
49
- }
50
-
51
- // We may need to handle offsets
52
- if (ArrayBuffer.isView(data)) {
53
- // TextDecoder is invoked on typed arrays and will handle offsets
50
+ if (isArrayBufferLike(data)) {
51
+ const bufferSource = toArrayBufferView(data);
54
52
  if (loader.text && !loader.binary) {
55
53
  const textDecoder = new TextDecoder('utf8');
56
- return textDecoder.decode(data);
57
- }
58
-
59
- let arrayBuffer = data.buffer;
60
-
61
- // Since we are returning the underlying arrayBuffer, we must create a new copy
62
- // if this typed array / Buffer is a partial view into the ArryayBuffer
63
- // TODO - this is a potentially unnecessary copy
64
- const byteLength = data.byteLength || data.length;
65
- if (data.byteOffset !== 0 || byteLength !== arrayBuffer.byteLength) {
66
- // console.warn(`loaders.gl copying arraybuffer of length ${byteLength}`);
67
- arrayBuffer = arrayBuffer.slice(data.byteOffset, data.byteOffset + byteLength);
54
+ return textDecoder.decode(bufferSource);
68
55
  }
69
- return arrayBuffer;
56
+ return toArrayBuffer(bufferSource);
70
57
  }
71
58
 
72
59
  throw new Error(ERR_DATA);
73
60
  }
74
61
 
75
- // Convert async iterator to a promise
62
+ /**
63
+ * Resolves the provided data into an {@link ArrayBuffer} or string asynchronously.
64
+ * Accepts the full {@link DataType} surface including responses and async iterables.
65
+ */
76
66
  export async function getArrayBufferOrStringFromData(
77
67
  data: DataType,
78
68
  loader: Loader,
79
69
  options: LoaderOptions
80
70
  ): Promise<ArrayBuffer | string> {
81
- const isArrayBuffer = data instanceof ArrayBuffer || ArrayBuffer.isView(data);
82
- if (typeof data === 'string' || isArrayBuffer) {
83
- return getArrayBufferOrStringFromDataSync(data as string | ArrayBuffer, loader, options);
71
+ if (typeof data === 'string' || isArrayBufferLike(data)) {
72
+ return getArrayBufferOrStringFromDataSync(data as SyncDataType, loader, options);
84
73
  }
85
74
 
86
75
  // Blobs and files are FileReader compatible
@@ -89,9 +78,8 @@ export async function getArrayBufferOrStringFromData(
89
78
  }
90
79
 
91
80
  if (isResponse(data)) {
92
- const response = data as Response;
93
- await checkResponse(response);
94
- return loader.binary ? await response.arrayBuffer() : await response.text();
81
+ await checkResponse(data);
82
+ return loader.binary ? await data.arrayBuffer() : await data.text();
95
83
  }
96
84
 
97
85
  if (isReadableStream(data)) {
@@ -101,29 +89,41 @@ export async function getArrayBufferOrStringFromData(
101
89
 
102
90
  if (isIterable(data) || isAsyncIterable(data)) {
103
91
  // Assume arrayBuffer iterator - attempt to concatenate
104
- return concatenateArrayBuffersAsync(data as AsyncIterable<ArrayBuffer>);
92
+ return concatenateArrayBuffersAsync(data as AsyncIterable<ArrayBufferLike>);
105
93
  }
106
94
 
107
95
  throw new Error(ERR_DATA);
108
96
  }
109
97
 
98
+ /**
99
+ * Normalizes batchable inputs into async iterables for batch parsing flows.
100
+ * Supports synchronous iterables, async iterables, fetch responses, readable streams, and
101
+ * single binary chunks (including typed array views and `ArrayBufferLike` values).
102
+ */
110
103
  export async function getAsyncIterableFromData(
111
104
  data: BatchableDataType,
112
105
  options: LoaderOptions
113
- ): Promise<AsyncIterable<ArrayBuffer> | Iterable<ArrayBuffer>> {
106
+ ): Promise<
107
+ AsyncIterable<ArrayBufferLike | ArrayBufferView> | Iterable<ArrayBufferLike | ArrayBufferView>
108
+ > {
109
+ if (isPromise(data)) {
110
+ data = await data;
111
+ }
112
+
114
113
  if (isIterator(data)) {
115
114
  return data as AsyncIterable<ArrayBuffer>;
116
115
  }
117
116
 
118
117
  if (isResponse(data)) {
119
- const response = data as Response;
120
118
  // Note Since this function is not async, we currently can't load error message, just status
121
- await checkResponse(response);
119
+ await checkResponse(data);
122
120
  // TODO - bug in polyfill, body can be a Promise under Node.js
123
121
  // eslint-disable-next-line @typescript-eslint/await-thenable
124
- const body = await response.body;
125
- // TODO - body can be null?
126
- return makeIterator(body as ReadableStream<Uint8Array>, options as any);
122
+ const body = await data.body;
123
+ if (!body) {
124
+ throw new Error(ERR_DATA);
125
+ }
126
+ return makeIterator(body, options as any);
127
127
  }
128
128
 
129
129
  if (isBlob(data) || isReadableStream(data)) {
@@ -131,38 +131,52 @@ export async function getAsyncIterableFromData(
131
131
  }
132
132
 
133
133
  if (isAsyncIterable(data)) {
134
- return data as AsyncIterable<ArrayBuffer>;
134
+ return data as AsyncIterable<ArrayBufferLike | ArrayBufferView>;
135
135
  }
136
136
 
137
+ if (isIterable(data)) {
138
+ return data as Iterable<ArrayBufferLike | ArrayBufferView>;
139
+ }
140
+
141
+ // @ts-expect-error TODO - fix type mess
137
142
  return getIterableFromData(data);
138
143
  }
139
144
 
145
+ /**
146
+ * Returns a readable stream for streaming loader inputs when available.
147
+ */
140
148
  export async function getReadableStream(data: BatchableDataType): Promise<ReadableStream> {
141
149
  if (isReadableStream(data)) {
142
150
  return data as ReadableStream;
143
151
  }
144
152
  if (isResponse(data)) {
145
153
  // @ts-ignore
154
+ if (!data.body) {
155
+ throw new Error(ERR_DATA);
156
+ }
146
157
  return data.body;
147
158
  }
148
159
  const response = await makeResponse(data);
149
160
  // @ts-ignore
161
+ if (!response.body) {
162
+ throw new Error(ERR_DATA);
163
+ }
150
164
  return response.body;
151
165
  }
152
166
 
153
167
  // HELPERS
154
168
 
155
- function getIterableFromData(data) {
169
+ function getIterableFromData(data: string | ArrayBuffer | SharedArrayBuffer | ArrayBufferView) {
156
170
  // generate an iterator that emits a single chunk
157
171
  if (ArrayBuffer.isView(data)) {
158
172
  return (function* oneChunk() {
159
- yield data.buffer;
173
+ yield toArrayBuffer(data);
160
174
  })();
161
175
  }
162
176
 
163
- if (data instanceof ArrayBuffer) {
177
+ if (isArrayBufferLike(data)) {
164
178
  return (function* oneChunk() {
165
- yield data;
179
+ yield toArrayBuffer(data);
166
180
  })();
167
181
  }
168
182
 
@@ -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 '../../javascript-utils/is-type';
6
+ import {isObject} from '@loaders.gl/loader-utils';
7
7
  import {fetchFile} from '../fetch/fetch-file';
8
8
  import {getGlobalLoaderOptions} from './option-utils';
9
9
 
@@ -19,15 +19,16 @@ export function getFetchFunction(
19
19
  const globalOptions = getGlobalLoaderOptions();
20
20
 
21
21
  const loaderOptions = options || globalOptions;
22
+ const fetchOption = loaderOptions.fetch ?? loaderOptions.core?.fetch;
22
23
 
23
24
  // options.fetch can be a function
24
- if (typeof loaderOptions.fetch === 'function') {
25
- return loaderOptions.fetch;
25
+ if (typeof fetchOption === 'function') {
26
+ return fetchOption;
26
27
  }
27
28
 
28
29
  // options.fetch can be an options object
29
- if (isObject(loaderOptions.fetch)) {
30
- return (url) => fetchFile(url, loaderOptions.fetch as RequestInit);
30
+ if (isObject(fetchOption)) {
31
+ return (url) => fetchFile(url, fetchOption as RequestInit);
31
32
  }
32
33
 
33
34
  // else refer to context (from parent loader) if available
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import type {Loader, LoaderOptions, LoaderContext} from '@loaders.gl/loader-utils';
5
+ import type {Loader, LoaderContext, StrictLoaderOptions} from '@loaders.gl/loader-utils';
6
6
  import {getFetchFunction} from './get-fetch-function';
7
7
  import {extractQueryString, stripQueryString} from '../utils/url-utils';
8
8
  import {path} from '@loaders.gl/loader-utils';
@@ -21,7 +21,7 @@ type LoaderContextProps = Omit<LoaderContext, 'fetch'> & Partial<Pick<LoaderCont
21
21
  */
22
22
  export function getLoaderContext(
23
23
  context: LoaderContextProps,
24
- options: LoaderOptions,
24
+ options: StrictLoaderOptions,
25
25
  parentContext: LoaderContext | null
26
26
  ): LoaderContext {
27
27
  // For recursive calls, we already have a context
@@ -7,44 +7,77 @@ import {isBrowser} from '@loaders.gl/loader-utils';
7
7
  import {ConsoleLog} from './loggers';
8
8
 
9
9
  export const DEFAULT_LOADER_OPTIONS: LoaderOptions = {
10
- // baseUri
11
- fetch: null,
12
- mimeType: undefined,
13
- nothrow: false,
14
- log: new ConsoleLog(), // A probe.gl compatible (`log.log()()` syntax) that just logs to console
15
- useLocalLibraries: false,
16
-
17
- CDN: 'https://unpkg.com/@loaders.gl',
18
- worker: true, // By default, use worker if provided by loader.
19
- maxConcurrency: 3, // How many worker instances should be created for each loader.
20
- maxMobileConcurrency: 1, // How many worker instances should be created for each loader on mobile devices.
21
- reuseWorkers: isBrowser, // By default reuse workers in browser (Node.js refuses to terminate if browsers are running)
22
- _nodeWorkers: false, // By default do not support node workers
23
- _workerType: '', // 'test' to use locally generated workers
24
-
25
- limit: 0,
26
- _limitMB: 0,
27
- batchSize: 'auto',
28
- batchDebounceMs: 0,
29
- metadata: false, // TODO - currently only implemented for parseInBatches, adds initial metadata batch,
30
- transforms: []
10
+ core: {
11
+ baseUrl: undefined,
12
+ // baseUrl
13
+ fetch: null,
14
+ mimeType: undefined,
15
+ fallbackMimeType: undefined,
16
+ ignoreRegisteredLoaders: undefined,
17
+ nothrow: false,
18
+ log: new ConsoleLog(), // A probe.gl compatible (`log.log()()` syntax) that just logs to console
19
+ useLocalLibraries: false,
20
+
21
+ CDN: 'https://unpkg.com/@loaders.gl',
22
+ worker: true, // By default, use worker if provided by loader.
23
+ maxConcurrency: 3, // How many worker instances should be created for each loader.
24
+ maxMobileConcurrency: 1, // How many worker instances should be created for each loader on mobile devices.
25
+ reuseWorkers: isBrowser, // By default reuse workers in browser (Node.js refuses to terminate if browsers are running)
26
+ _nodeWorkers: false, // By default do not support node workers
27
+ _workerType: '', // 'test' to use locally generated workers
28
+
29
+ limit: 0,
30
+ _limitMB: 0,
31
+ batchSize: 'auto',
32
+ batchDebounceMs: 0,
33
+ metadata: false, // TODO - currently only implemented for parseInBatches, adds initial metadata batch,
34
+ transforms: []
35
+ }
31
36
  };
32
37
 
33
38
  export const REMOVED_LOADER_OPTIONS = {
39
+ // deprecated top-level alias
40
+ baseUri: 'core.baseUrl',
41
+ fetch: 'core.fetch',
42
+ mimeType: 'core.mimeType',
43
+ fallbackMimeType: 'core.fallbackMimeType',
44
+ ignoreRegisteredLoaders: 'core.ignoreRegisteredLoaders',
45
+ nothrow: 'core.nothrow',
46
+ log: 'core.log',
47
+ useLocalLibraries: 'core.useLocalLibraries',
48
+
49
+ CDN: 'core.CDN',
50
+ worker: 'core.worker',
51
+ maxConcurrency: 'core.maxConcurrency',
52
+ maxMobileConcurrency: 'core.maxMobileConcurrency',
53
+ reuseWorkers: 'core.reuseWorkers',
54
+ _nodeWorkers: 'core.nodeWorkers',
55
+ _workerType: 'core._workerType',
56
+ _worker: 'core._workerType',
57
+
58
+ limit: 'core.limit',
59
+ _limitMB: 'core._limitMB',
60
+ batchSize: 'core.batchSize',
61
+ batchDebounceMs: 'core.batchDebounceMs',
62
+ metadata: 'core.metadata',
63
+ transforms: 'core.transforms',
64
+
65
+ // Older deprecations
34
66
  throws: 'nothrow',
35
67
  dataType: '(no longer used)',
36
- uri: 'baseUri',
37
- // Warn if fetch options are used on top-level
38
- method: 'fetch.method',
39
- headers: 'fetch.headers',
40
- body: 'fetch.body',
41
- mode: 'fetch.mode',
42
- credentials: 'fetch.credentials',
43
- cache: 'fetch.cache',
44
- redirect: 'fetch.redirect',
45
- referrer: 'fetch.referrer',
46
- referrerPolicy: 'fetch.referrerPolicy',
47
- integrity: 'fetch.integrity',
48
- keepalive: 'fetch.keepalive',
49
- signal: 'fetch.signal'
68
+ uri: 'core.baseUrl',
69
+
70
+ // Warn if fetch options are used on toplevel
71
+ method: 'core.fetch.method',
72
+ headers: 'core.fetch.headers',
73
+ body: 'core.fetch.body',
74
+ mode: 'core.fetch.mode',
75
+ credentials: 'core.fetch.credentials',
76
+ cache: 'core.fetch.cache',
77
+ redirect: 'core.fetch.redirect',
78
+ referrer: 'core.fetch.referrer',
79
+ referrerPolicy: 'core.fetch.referrerPolicy',
80
+ integrity: 'core.fetch.integrity',
81
+ keepalive: 'core.fetch.keepalive',
82
+ signal: 'core.fetch.signal'
50
83
  };
@@ -2,10 +2,42 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {Loader, LoaderOptions, registerJSModules} from '@loaders.gl/loader-utils';
6
- import {isPureObject, isObject} from '../../javascript-utils/is-type';
5
+ import {
6
+ Loader,
7
+ LoaderOptions,
8
+ registerJSModules,
9
+ isPureObject,
10
+ isObject,
11
+ StrictLoaderOptions,
12
+ path
13
+ } from '@loaders.gl/loader-utils';
7
14
  import {probeLog, NullLog} from './loggers';
8
15
  import {DEFAULT_LOADER_OPTIONS, REMOVED_LOADER_OPTIONS} from './option-defaults';
16
+ import {stripQueryString} from '../utils/url-utils';
17
+
18
+ const CORE_LOADER_OPTION_KEYS = [
19
+ 'baseUrl',
20
+ 'fetch',
21
+ 'mimeType',
22
+ 'fallbackMimeType',
23
+ 'ignoreRegisteredLoaders',
24
+ 'nothrow',
25
+ 'log',
26
+ 'useLocalLibraries',
27
+ 'CDN',
28
+ 'worker',
29
+ 'maxConcurrency',
30
+ 'maxMobileConcurrency',
31
+ 'reuseWorkers',
32
+ '_nodeWorkers',
33
+ '_workerType',
34
+ 'limit',
35
+ '_limitMB',
36
+ 'batchSize',
37
+ 'batchDebounceMs',
38
+ 'metadata',
39
+ 'transforms'
40
+ ] as const;
9
41
 
10
42
  /**
11
43
  * Global state for loaders.gl. Stored on `globalThis.loaders._state`
@@ -37,11 +69,14 @@ export function getGlobalLoaderState(): GlobalLoaderState {
37
69
  * NOTE: This use case is not reliable but can help when testing new versions of loaders.gl with existing frameworks
38
70
  * @returns global loader options merged with default loader options
39
71
  */
40
- export function getGlobalLoaderOptions(): LoaderOptions {
72
+ export function getGlobalLoaderOptions(): StrictLoaderOptions {
41
73
  const state = getGlobalLoaderState();
42
74
  // Ensure all default loader options from this library are mentioned
43
- state.globalOptions = state.globalOptions || {...DEFAULT_LOADER_OPTIONS};
44
- return state.globalOptions;
75
+ state.globalOptions = state.globalOptions || {
76
+ ...DEFAULT_LOADER_OPTIONS,
77
+ core: {...DEFAULT_LOADER_OPTIONS.core}
78
+ };
79
+ return normalizeLoaderOptions(state.globalOptions);
45
80
  }
46
81
 
47
82
  /**
@@ -58,7 +93,7 @@ export function setGlobalOptions(options: LoaderOptions): void {
58
93
  }
59
94
 
60
95
  /**
61
- * Merges options with global opts and loader defaults, also injects baseUri
96
+ * Merges options with global opts and loader defaults, also injects baseUrl
62
97
  * @param options
63
98
  * @param loader
64
99
  * @param loaders
@@ -69,12 +104,31 @@ export function normalizeOptions(
69
104
  loader: Loader,
70
105
  loaders?: Loader[],
71
106
  url?: string
72
- ): LoaderOptions {
107
+ ): StrictLoaderOptions {
73
108
  loaders = loaders || [];
74
109
  loaders = Array.isArray(loaders) ? loaders : [loaders];
75
110
 
76
111
  validateOptions(options, loaders);
77
- return normalizeOptionsInternal(loader, options, url);
112
+ return normalizeLoaderOptions(normalizeOptionsInternal(loader, options, url));
113
+ }
114
+
115
+ /**
116
+ * Returns a copy of the provided options with deprecated top-level core fields moved into `core`
117
+ * and removed from the top level. This keeps global options from leaking deprecated aliases into
118
+ * loader-specific option maps during normalization.
119
+ */
120
+ export function normalizeLoaderOptions(options: LoaderOptions): StrictLoaderOptions {
121
+ const normalized = cloneLoaderOptions(options);
122
+ moveDeprecatedTopLevelOptionsToCore(normalized);
123
+ for (const key of CORE_LOADER_OPTION_KEYS) {
124
+ if (normalized.core && normalized.core[key] !== undefined) {
125
+ delete (normalized as Record<string, unknown>)[key];
126
+ }
127
+ }
128
+ if (normalized.core && normalized.core._workerType !== undefined) {
129
+ delete (normalized as any)._worker;
130
+ }
131
+ return normalized as StrictLoaderOptions;
78
132
  }
79
133
 
80
134
  // VALIDATE OPTIONS
@@ -89,10 +143,8 @@ function validateOptions(options: LoaderOptions, loaders: Loader[]): void {
89
143
  validateOptionsObject(options, null, DEFAULT_LOADER_OPTIONS, REMOVED_LOADER_OPTIONS, loaders);
90
144
  for (const loader of loaders) {
91
145
  // Get the scoped, loader specific options from the user supplied options
92
- const idOptions: Record<string, unknown> = ((options && options[loader.id]) || {}) as Record<
93
- string,
94
- unknown
95
- >;
146
+ const idOptions: Record<string, unknown> =
147
+ ((options && options[loader.id]) as Record<string, unknown>) || {};
96
148
 
97
149
  // Get scoped, loader specific default and deprecated options from the selected loader
98
150
  const loaderOptions = (loader.options && loader.options[loader.id]) || {};
@@ -125,14 +177,18 @@ function validateOptionsObject(
125
177
  if (!(key in defaultOptions) && !isBaseUriOption && !isWorkerUrlOption) {
126
178
  // Issue deprecation warnings
127
179
  if (key in deprecatedOptions) {
128
- probeLog.warn(
129
- `${loaderName} loader option \'${prefix}${key}\' no longer supported, use \'${deprecatedOptions[key]}\'`
130
- )();
180
+ if (probeLog.level > 0) {
181
+ probeLog.warn(
182
+ `${loaderName} loader option \'${prefix}${key}\' no longer supported, use \'${deprecatedOptions[key]}\'`
183
+ )();
184
+ }
131
185
  } else if (!isSubOptions) {
132
- const suggestion = findSimilarOption(key, loaders);
133
- probeLog.warn(
134
- `${loaderName} loader option \'${prefix}${key}\' not recognized. ${suggestion}`
135
- )();
186
+ if (probeLog.level > 0) {
187
+ const suggestion = findSimilarOption(key, loaders);
188
+ probeLog.warn(
189
+ `${loaderName} loader option \'${prefix}${key}\' not recognized. ${suggestion}`
190
+ )();
191
+ }
136
192
  }
137
193
  }
138
194
  }
@@ -165,16 +221,23 @@ function normalizeOptionsInternal(
165
221
  const loaderDefaultOptions = loader.options || {};
166
222
 
167
223
  const mergedOptions = {...loaderDefaultOptions};
168
-
169
- addUrlOptions(mergedOptions, url);
224
+ if (loaderDefaultOptions.core) {
225
+ mergedOptions.core = {...loaderDefaultOptions.core};
226
+ }
227
+ moveDeprecatedTopLevelOptionsToCore(mergedOptions);
170
228
 
171
229
  // LOGGING: options.log can be set to `null` to defeat logging
172
- if (mergedOptions.log === null) {
173
- mergedOptions.log = new NullLog();
230
+ if (mergedOptions.core?.log === null) {
231
+ mergedOptions.core = {...mergedOptions.core, log: new NullLog()};
174
232
  }
175
233
 
176
- mergeNestedFields(mergedOptions, getGlobalLoaderOptions());
177
- mergeNestedFields(mergedOptions, options);
234
+ mergeNestedFields(mergedOptions, normalizeLoaderOptions(getGlobalLoaderOptions()));
235
+
236
+ const userOptions = normalizeLoaderOptions(options);
237
+ mergeNestedFields(mergedOptions, userOptions);
238
+
239
+ addUrlOptions(mergedOptions, url);
240
+ addDeprecatedTopLevelOptions(mergedOptions);
178
241
 
179
242
  return mergedOptions;
180
243
  }
@@ -201,14 +264,68 @@ function mergeNestedFields(mergedOptions: LoaderOptions, options: LoaderOptions)
201
264
 
202
265
  /**
203
266
  * Harvest information from the url
204
- * @deprecated This is mainly there to support a hack in the GLTFLoader
205
- * TODO - baseUri should be a directory, i.e. remove file component from baseUri
267
+ * @deprecated This is mainly there to support loaders that still resolve from options
206
268
  * TODO - extract extension?
207
269
  * TODO - extract query parameters?
208
270
  * TODO - should these be injected on context instead of options?
209
271
  */
210
272
  function addUrlOptions(options: LoaderOptions, url?: string): void {
211
- if (url && !('baseUri' in options)) {
212
- options.baseUri = url;
273
+ if (!url) {
274
+ return;
275
+ }
276
+ const hasCoreBaseUrl = options.core?.baseUrl !== undefined;
277
+ if (!hasCoreBaseUrl) {
278
+ options.core ||= {};
279
+ options.core.baseUrl = path.dirname(stripQueryString(url));
280
+ }
281
+ }
282
+
283
+ function cloneLoaderOptions(options: LoaderOptions): LoaderOptions {
284
+ const clonedOptions = {...options};
285
+ if (options.core) {
286
+ clonedOptions.core = {...options.core};
287
+ }
288
+ return clonedOptions;
289
+ }
290
+
291
+ function moveDeprecatedTopLevelOptionsToCore(options: LoaderOptions): void {
292
+ if (options.baseUri !== undefined) {
293
+ options.core ||= {};
294
+ if (options.core.baseUrl === undefined) {
295
+ options.core.baseUrl = options.baseUri;
296
+ }
297
+ }
298
+
299
+ for (const key of CORE_LOADER_OPTION_KEYS) {
300
+ if ((options as Record<string, unknown>)[key] !== undefined) {
301
+ const coreOptions = (options.core = options.core || {});
302
+ const coreRecord = coreOptions as Record<string, unknown>;
303
+ // Treat deprecated top-level core options as aliases to `options.core`, but never override an explicitly
304
+ // provided `options.core` value.
305
+ if (coreRecord[key] === undefined) {
306
+ coreRecord[key] = (options as Record<string, unknown>)[key];
307
+ }
308
+ }
309
+ }
310
+
311
+ // Support the older internal `_worker` alias (used by some tests and integrations) for `_workerType`.
312
+ const workerTypeAlias = (options as any)._worker;
313
+ if (workerTypeAlias !== undefined) {
314
+ options.core ||= {};
315
+ if (options.core._workerType === undefined) {
316
+ options.core._workerType = workerTypeAlias;
317
+ }
318
+ }
319
+ }
320
+
321
+ function addDeprecatedTopLevelOptions(options: LoaderOptions): void {
322
+ const coreOptions = options.core as Record<string, unknown> | undefined;
323
+ if (!coreOptions) {
324
+ return;
325
+ }
326
+ for (const key of CORE_LOADER_OPTION_KEYS) {
327
+ if (coreOptions[key] !== undefined) {
328
+ (options as Record<string, unknown>)[key] = coreOptions[key];
329
+ }
213
330
  }
214
331
  }