@loaders.gl/core 4.5.3 → 5.0.0-alpha.10

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 (127) hide show
  1. package/LICENSE +14 -3
  2. package/dist/dist.dev.js +1789 -352
  3. package/dist/dist.min.js +4 -3
  4. package/dist/index.cjs +3087 -311
  5. package/dist/index.cjs.map +3 -4
  6. package/dist/index.d.ts +8 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +7 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/iterators/make-iterator/make-stream-iterator.js +48 -9
  11. package/dist/iterators/make-iterator/make-stream-iterator.js.map +1 -1
  12. package/dist/lib/api/core-api.d.ts +6 -0
  13. package/dist/lib/api/core-api.d.ts.map +1 -0
  14. package/dist/lib/api/core-api.js +23 -0
  15. package/dist/lib/api/core-api.js.map +1 -0
  16. package/dist/lib/api/create-data-source.d.ts +2 -2
  17. package/dist/lib/api/create-data-source.d.ts.map +1 -1
  18. package/dist/lib/api/create-data-source.js +36 -14
  19. package/dist/lib/api/create-data-source.js.map +1 -1
  20. package/dist/lib/api/load-in-batches.d.ts +4 -4
  21. package/dist/lib/api/load-in-batches.d.ts.map +1 -1
  22. package/dist/lib/api/load-in-batches.js +17 -6
  23. package/dist/lib/api/load-in-batches.js.map +1 -1
  24. package/dist/lib/api/load-loader.d.ts +10 -0
  25. package/dist/lib/api/load-loader.d.ts.map +1 -0
  26. package/dist/lib/api/load-loader.js +222 -0
  27. package/dist/lib/api/load-loader.js.map +1 -0
  28. package/dist/lib/api/load.d.ts +3 -3
  29. package/dist/lib/api/load.d.ts.map +1 -1
  30. package/dist/lib/api/load.js +84 -5
  31. package/dist/lib/api/load.js.map +1 -1
  32. package/dist/lib/api/parse-file.d.ts +15 -0
  33. package/dist/lib/api/parse-file.d.ts.map +1 -0
  34. package/dist/lib/api/parse-file.js +74 -0
  35. package/dist/lib/api/parse-file.js.map +1 -0
  36. package/dist/lib/api/parse-in-batches.d.ts +2 -2
  37. package/dist/lib/api/parse-in-batches.d.ts.map +1 -1
  38. package/dist/lib/api/parse-in-batches.js +18 -7
  39. package/dist/lib/api/parse-in-batches.js.map +1 -1
  40. package/dist/lib/api/parse-sync.d.ts +2 -2
  41. package/dist/lib/api/parse-sync.d.ts.map +1 -1
  42. package/dist/lib/api/parse-sync.js +10 -1
  43. package/dist/lib/api/parse-sync.js.map +1 -1
  44. package/dist/lib/api/parse.d.ts +2 -2
  45. package/dist/lib/api/parse.d.ts.map +1 -1
  46. package/dist/lib/api/parse.js +34 -17
  47. package/dist/lib/api/parse.js.map +1 -1
  48. package/dist/lib/api/preload.d.ts +14 -0
  49. package/dist/lib/api/preload.d.ts.map +1 -0
  50. package/dist/lib/api/preload.js +13 -0
  51. package/dist/lib/api/preload.js.map +1 -0
  52. package/dist/lib/api/register-loaders.js +1 -1
  53. package/dist/lib/api/register-loaders.js.map +1 -1
  54. package/dist/lib/api/select-loader.d.ts.map +1 -1
  55. package/dist/lib/api/select-loader.js +48 -5
  56. package/dist/lib/api/select-loader.js.map +1 -1
  57. package/dist/lib/api/select-source.d.ts +3 -3
  58. package/dist/lib/api/select-source.d.ts.map +1 -1
  59. package/dist/lib/api/select-source.js +2 -5
  60. package/dist/lib/api/select-source.js.map +1 -1
  61. package/dist/lib/fetch/fetch-error-message.js +1 -1
  62. package/dist/lib/fetch/fetch-error-message.js.map +1 -1
  63. package/dist/lib/filesystems/browser-filesystem.d.ts.map +1 -1
  64. package/dist/lib/filesystems/browser-filesystem.js +25 -11
  65. package/dist/lib/filesystems/browser-filesystem.js.map +1 -1
  66. package/dist/lib/init.js +1 -1
  67. package/dist/lib/init.js.map +1 -1
  68. package/dist/lib/loader-utils/check-errors.js +1 -1
  69. package/dist/lib/loader-utils/check-errors.js.map +1 -1
  70. package/dist/lib/loader-utils/get-data.d.ts +4 -0
  71. package/dist/lib/loader-utils/get-data.d.ts.map +1 -1
  72. package/dist/lib/loader-utils/get-data.js +26 -0
  73. package/dist/lib/loader-utils/get-data.js.map +1 -1
  74. package/dist/lib/loader-utils/get-fetch-function.d.ts +1 -1
  75. package/dist/lib/loader-utils/get-fetch-function.d.ts.map +1 -1
  76. package/dist/lib/loader-utils/get-fetch-function.js +16 -10
  77. package/dist/lib/loader-utils/get-fetch-function.js.map +1 -1
  78. package/dist/lib/loader-utils/loader-context.d.ts +2 -2
  79. package/dist/lib/loader-utils/loader-context.d.ts.map +1 -1
  80. package/dist/lib/loader-utils/loader-context.js +11 -4
  81. package/dist/lib/loader-utils/loader-context.js.map +1 -1
  82. package/dist/lib/loader-utils/loggers.d.ts +19 -0
  83. package/dist/lib/loader-utils/loggers.d.ts.map +1 -1
  84. package/dist/lib/loader-utils/loggers.js +28 -2
  85. package/dist/lib/loader-utils/loggers.js.map +1 -1
  86. package/dist/lib/loader-utils/option-defaults.d.ts +1 -0
  87. package/dist/lib/loader-utils/option-defaults.d.ts.map +1 -1
  88. package/dist/lib/loader-utils/option-defaults.js +5 -0
  89. package/dist/lib/loader-utils/option-defaults.js.map +1 -1
  90. package/dist/lib/loader-utils/option-utils.d.ts +7 -0
  91. package/dist/lib/loader-utils/option-utils.d.ts.map +1 -1
  92. package/dist/lib/loader-utils/option-utils.js +91 -3
  93. package/dist/lib/loader-utils/option-utils.js.map +1 -1
  94. package/dist/lib/utils/response-utils.js +5 -14
  95. package/dist/lib/utils/response-utils.js.map +1 -1
  96. package/dist/null-loader.d.ts.map +1 -1
  97. package/dist/null-loader.js +1 -1
  98. package/dist/null-loader.js.map +1 -1
  99. package/dist/null-worker-node.js +270 -42
  100. package/dist/null-worker.js +270 -42
  101. package/package.json +6 -6
  102. package/src/index.ts +10 -1
  103. package/src/iterators/make-iterator/make-stream-iterator.ts +47 -9
  104. package/src/lib/api/core-api.ts +25 -0
  105. package/src/lib/api/create-data-source.ts +53 -24
  106. package/src/lib/api/load-in-batches.ts +33 -21
  107. package/src/lib/api/load-loader.ts +306 -0
  108. package/src/lib/api/load.ts +160 -9
  109. package/src/lib/api/parse-file.ts +156 -0
  110. package/src/lib/api/parse-in-batches.ts +35 -8
  111. package/src/lib/api/parse-sync.ts +27 -2
  112. package/src/lib/api/parse.ts +61 -20
  113. package/src/lib/api/preload.ts +46 -0
  114. package/src/lib/api/register-loaders.ts +1 -1
  115. package/src/lib/api/select-loader.ts +57 -5
  116. package/src/lib/api/select-source.ts +7 -10
  117. package/src/lib/fetch/fetch-error-message.ts +1 -1
  118. package/src/lib/filesystems/browser-filesystem.ts +25 -11
  119. package/src/lib/loader-utils/check-errors.ts +1 -1
  120. package/src/lib/loader-utils/get-data.ts +36 -0
  121. package/src/lib/loader-utils/get-fetch-function.ts +17 -15
  122. package/src/lib/loader-utils/loader-context.ts +13 -5
  123. package/src/lib/loader-utils/loggers.ts +28 -2
  124. package/src/lib/loader-utils/option-defaults.ts +5 -0
  125. package/src/lib/loader-utils/option-utils.ts +106 -3
  126. package/src/lib/utils/response-utils.ts +5 -15
  127. package/src/null-loader.ts +3 -3
@@ -3,18 +3,23 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import type {
6
- LoaderWithParser,
6
+ Loader,
7
7
  LoaderOptions,
8
8
  LoaderContext,
9
- FetchLike,
10
9
  BatchableDataType,
11
10
  LoaderBatchType,
12
- LoaderOptionsType
11
+ LoaderOptionsType,
12
+ LoaderOptionsWithShape,
13
+ LoaderShapeType
13
14
  } from '@loaders.gl/loader-utils';
15
+ import {resolveLoaderAuthenticationOptions} from '@loaders.gl/loader-utils';
14
16
  import {isLoaderObject} from '../loader-utils/normalize-loader';
15
17
  import {getFetchFunction} from '../loader-utils/get-fetch-function';
18
+ import {applySearchParamsToUrl} from '../loader-utils/option-utils';
16
19
 
17
20
  import {parseInBatches} from './parse-in-batches';
21
+ import {selectLoader} from './select-loader';
22
+ import {getLoaderImplementation} from './load-loader';
18
23
 
19
24
  type FileType = string | File | Blob | Response | (string | File | Blob | Response)[] | FileList;
20
25
 
@@ -22,8 +27,11 @@ type FileType = string | File | Blob | Response | (string | File | Blob | Respon
22
27
  * Parses `data` synchronously using a specified loader
23
28
  */
24
29
  export async function loadInBatches<
25
- LoaderT extends LoaderWithParser,
26
- OptionsT extends LoaderOptions = LoaderOptionsType<LoaderT>
30
+ LoaderT extends Loader,
31
+ OptionsT extends LoaderOptions = LoaderOptionsWithShape<
32
+ LoaderOptionsType<LoaderT>,
33
+ LoaderShapeType<LoaderT>
34
+ >
27
35
  >(
28
36
  files: FileType,
29
37
  loader: LoaderT,
@@ -40,46 +48,41 @@ export async function loadInBatches<
40
48
  */
41
49
  export function loadInBatches(
42
50
  files: FileType,
43
- loaders?: LoaderWithParser | LoaderWithParser[] | LoaderOptions,
51
+ loaders?: Loader | Loader[] | LoaderOptions,
44
52
  options?: LoaderOptions,
45
53
  context?: LoaderContext
46
54
  ): Promise<AsyncIterable<unknown>>;
47
55
 
48
56
  export function loadInBatches(
49
57
  files: FileType[] | FileList,
50
- loaders?: LoaderWithParser | LoaderWithParser[] | LoaderOptions,
58
+ loaders?: Loader | Loader[] | LoaderOptions,
51
59
  options?: LoaderOptions,
52
60
  context?: LoaderContext
53
61
  ): Promise<AsyncIterable<unknown>>[];
54
62
 
55
63
  export function loadInBatches(
56
64
  files: FileType | FileType[] | FileList,
57
- loaders?: LoaderWithParser | LoaderWithParser[] | LoaderOptions,
65
+ loaders?: Loader | Loader[] | LoaderOptions,
58
66
  options?: LoaderOptions,
59
67
  context?: LoaderContext
60
68
  ): Promise<AsyncIterable<unknown>> | Promise<AsyncIterable<unknown>>[] {
61
- let loadersArray: LoaderWithParser | LoaderWithParser[] | undefined;
69
+ let loadersArray: Loader | Loader[] | undefined;
62
70
  // Signature: load(url, options)
63
71
  if (!Array.isArray(loaders) && !isLoaderObject(loaders)) {
64
72
  context = undefined; // context not supported in short signature
65
73
  options = loaders as LoaderOptions;
66
74
  loadersArray = undefined;
67
75
  } else {
68
- loadersArray = loaders as LoaderWithParser | LoaderWithParser[] | undefined;
76
+ loadersArray = loaders as Loader | Loader[] | undefined;
69
77
  }
70
78
 
71
- // Select fetch function
72
- const fetch = getFetchFunction(options || {});
73
-
74
79
  // Single url/file
75
80
  if (!Array.isArray(files)) {
76
- return loadOneFileInBatches(files, loadersArray!, options || {}, fetch);
81
+ return loadOneFileInBatches(files, loadersArray!, options || {});
77
82
  }
78
83
 
79
84
  // Multiple URLs / files
80
- const promises = files.map((file) =>
81
- loadOneFileInBatches(file, loadersArray!, options || {}, fetch)
82
- );
85
+ const promises = files.map(file => loadOneFileInBatches(file, loadersArray!, options || {}));
83
86
 
84
87
  // No point in waiting here for all responses before starting to stream individual streams?
85
88
  return promises;
@@ -87,13 +90,22 @@ export function loadInBatches(
87
90
 
88
91
  async function loadOneFileInBatches(
89
92
  file: FileType,
90
- loaders: LoaderWithParser | LoaderWithParser[],
91
- options: LoaderOptions,
92
- fetch: FetchLike
93
+ loaders: Loader | Loader[],
94
+ options: LoaderOptions
93
95
  ): Promise<AsyncIterable<unknown>> {
94
96
  if (typeof file === 'string') {
95
97
  const url = file;
96
- const response = await fetch(url);
98
+ const loader = await selectLoader(url, loaders, {
99
+ ...options,
100
+ core: {...options.core, nothrow: true}
101
+ });
102
+ const authenticationLoader =
103
+ loader && !loader.getAuthentications && options.core?.credentials?.length
104
+ ? await getLoaderImplementation(loader, options, url)
105
+ : loader;
106
+ options = await resolveLoaderAuthenticationOptions(authenticationLoader, url, options);
107
+ const fetch = getFetchFunction(options);
108
+ const response = await fetch(applySearchParamsToUrl(url, options));
97
109
  // pick right overload
98
110
  return Array.isArray(loaders)
99
111
  ? await parseInBatches(response, loaders, options)
@@ -0,0 +1,306 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {isSourceLoader, mergeOptions} from '@loaders.gl/loader-utils';
6
+ import type {
7
+ Loader,
8
+ LoaderOptions,
9
+ LoaderWithParser,
10
+ StrictLoaderOptions
11
+ } from '@loaders.gl/loader-utils';
12
+
13
+ const loaderImplementationPromises = new Map<Loader, Map<string, Promise<LoaderWithParser>>>();
14
+ const loaderImplementations = new Map<Loader, Map<string, LoaderWithParser>>();
15
+
16
+ /** Gets a parser-bearing implementation for a loader, loading it if needed. */
17
+ export async function getLoaderImplementation(
18
+ loader: Loader,
19
+ options?: LoaderOptions,
20
+ url?: string
21
+ ): Promise<LoaderWithParser> {
22
+ if (isSourceLoader(loader)) {
23
+ const source = loader.preload ? await loader.preload(url || '', options) : loader;
24
+ if (!isSourceLoader(source)) {
25
+ throw new Error(
26
+ `${loader.id} source loader preload() did not return a runtime source loader`
27
+ );
28
+ }
29
+ return source as unknown as LoaderWithParser;
30
+ }
31
+ if (isLoaderWithParser(loader)) {
32
+ return loader;
33
+ }
34
+
35
+ const implementationCacheKey = getLoaderImplementationCacheKey(loader, options);
36
+ const loaderImplementation = loaderImplementations.get(loader)?.get(implementationCacheKey);
37
+ if (loaderImplementation) {
38
+ return loaderImplementation;
39
+ }
40
+
41
+ let loaderImplementationPromisesForLoader = loaderImplementationPromises.get(loader);
42
+ if (!loaderImplementationPromisesForLoader) {
43
+ loaderImplementationPromisesForLoader = new Map();
44
+ loaderImplementationPromises.set(loader, loaderImplementationPromisesForLoader);
45
+ }
46
+
47
+ let loaderImplementationPromise =
48
+ loaderImplementationPromisesForLoader.get(implementationCacheKey);
49
+ if (!loaderImplementationPromise) {
50
+ loaderImplementationPromise = resolveLoaderImplementation(loader, options, url)
51
+ .then(implementation => {
52
+ let loaderImplementationsForLoader = loaderImplementations.get(loader);
53
+ if (!loaderImplementationsForLoader) {
54
+ loaderImplementationsForLoader = new Map();
55
+ loaderImplementations.set(loader, loaderImplementationsForLoader);
56
+ }
57
+ loaderImplementationsForLoader.set(implementationCacheKey, implementation);
58
+ return implementation;
59
+ })
60
+ .catch(error => {
61
+ loaderImplementationPromisesForLoader.delete(implementationCacheKey);
62
+ if (loaderImplementationPromisesForLoader.size === 0) {
63
+ loaderImplementationPromises.delete(loader);
64
+ }
65
+ throw error;
66
+ });
67
+ loaderImplementationPromisesForLoader.set(implementationCacheKey, loaderImplementationPromise);
68
+ }
69
+
70
+ return await loaderImplementationPromise;
71
+ }
72
+
73
+ /** Gets a cached parser-bearing implementation for a loader without loading it. */
74
+ export function getLoaderImplementationSync(loader: Loader): LoaderWithParser | null {
75
+ if (loader.subloaders && !preparedLoaders.has(loader)) {
76
+ const prepared = preparedDefaults.get(loader);
77
+ if (prepared) return prepared;
78
+ if (!isLoaderWithParser(loader)) return null;
79
+ }
80
+ if (isLoaderWithParser(loader)) {
81
+ return loader;
82
+ }
83
+
84
+ const implementationCacheKey = getLoaderImplementationCacheKey(loader);
85
+ return loaderImplementations.get(loader)?.get(implementationCacheKey) || null;
86
+ }
87
+
88
+ /** Returns true when a loader object already includes parser methods. */
89
+ export function isLoaderWithParser(loader: Loader): loader is LoaderWithParser {
90
+ const candidate = loader as LoaderWithParser;
91
+ return Boolean(
92
+ candidate.parse ||
93
+ candidate.parseSync ||
94
+ candidate.parseInBatches ||
95
+ candidate.parseText ||
96
+ candidate.parseTextSync ||
97
+ candidate.parseFile ||
98
+ candidate.parseFileInBatches
99
+ );
100
+ }
101
+
102
+ /** Resolves a parser-bearing implementation through loader preload or the test import fallback. */
103
+ async function resolveLoaderImplementation(
104
+ loader: Loader,
105
+ options?: LoaderOptions,
106
+ url?: string
107
+ ): Promise<LoaderWithParser> {
108
+ if (loader.preload) {
109
+ const preloadedLoader = await loader.preload(url || '', options);
110
+ if (isLoaderWithParser(preloadedLoader as Loader)) {
111
+ return preloadedLoader as LoaderWithParser;
112
+ }
113
+
114
+ throw new Error(`${loader.id} loader preload() did not return a parser-bearing loader`);
115
+ }
116
+
117
+ const implementationSpecifier = getLoaderImplementationSpecifier(loader, options);
118
+ return await importLoaderImplementation(implementationSpecifier, loader.id);
119
+ }
120
+
121
+ /** Gets the dynamic implementation specifier for the test worker fallback path. */
122
+ function getLoaderImplementationSpecifier(loader: Loader, options?: LoaderOptions): string {
123
+ const workerType = options?._workerType || options?.core?._workerType;
124
+ if (workerType === 'test') {
125
+ const sourcePath = `modules/${loader.module}/src/${loader.id}-loader.ts`;
126
+ if (typeof window !== 'undefined') {
127
+ return `/${sourcePath}`;
128
+ }
129
+
130
+ if (typeof process !== 'undefined' && process.cwd) {
131
+ return new URL(sourcePath, `file://${process.cwd()}/`).toString();
132
+ }
133
+
134
+ return sourcePath;
135
+ }
136
+
137
+ throw new Error(
138
+ `${loader.id} loader does not provide a parser implementation. Import a parser-bearing loader directly, or use preload() before parse/load.`
139
+ );
140
+ }
141
+
142
+ /** Gets the cache key for option-selected parser implementations. */
143
+ function getLoaderImplementationCacheKey(loader: Loader, options?: LoaderOptions): string {
144
+ const loaderOptions = options?.[loader.id] as {backend?: unknown} | undefined;
145
+ const defaultLoaderOptions = loader.options?.[loader.id] as {backend?: unknown} | undefined;
146
+ return String(loaderOptions?.backend || defaultLoaderOptions?.backend || '');
147
+ }
148
+
149
+ /** Imports a module and finds the parser-bearing loader implementation with the requested id. */
150
+ async function importLoaderImplementation(
151
+ implementationSpecifier: string,
152
+ loaderId: string
153
+ ): Promise<LoaderWithParser> {
154
+ const moduleExports = await import(
155
+ /* webpackIgnore: true */ /* @vite-ignore */ implementationSpecifier
156
+ );
157
+
158
+ for (const exportValue of Object.values(moduleExports)) {
159
+ if (
160
+ exportValue &&
161
+ typeof exportValue === 'object' &&
162
+ (exportValue as Loader).id === loaderId &&
163
+ isLoaderWithParser(exportValue as Loader)
164
+ ) {
165
+ return exportValue as LoaderWithParser;
166
+ }
167
+ }
168
+
169
+ throw new Error(
170
+ `Could not find parser implementation for ${loaderId} in ${implementationSpecifier}`
171
+ );
172
+ }
173
+
174
+ /** Prepared loaders carry caller-local parser bindings rather than shared metadata. */
175
+ const preparedLoaders = new WeakSet<Loader>();
176
+ /** Successfully prepared default graphs used by synchronous parsing. */
177
+ const preparedDefaults = new WeakMap<Loader, LoaderWithParser>();
178
+ /** In-flight preparations for default dependency graphs. */
179
+ const subloaderPreparations = new WeakMap<Loader, Promise<LoaderWithParser>>();
180
+
181
+ /** Resolves declared dependencies and binds parser entry points to their implementations. */
182
+ export async function preloadLoaderDependencies(
183
+ loader: Loader,
184
+ options?: LoaderOptions,
185
+ url?: string
186
+ ): Promise<LoaderWithParser> {
187
+ if (preparedLoaders.has(loader)) return await getLoaderImplementation(loader, options, url);
188
+ validateSubloaderGraph(loader, options);
189
+ return await prepareLoaderDependencies(loader, options, url);
190
+ }
191
+
192
+ /** Prepares a node after the complete dependency graph has been validated. */
193
+ async function prepareLoaderDependencies(
194
+ loader: Loader,
195
+ options?: LoaderOptions,
196
+ url?: string
197
+ ): Promise<LoaderWithParser> {
198
+ // Options can select nested backends or overrides; do not share their bindings.
199
+ if (!options) {
200
+ const cached = subloaderPreparations.get(loader);
201
+ if (cached) return await cached;
202
+ }
203
+ const preparation = bindSubloaders(loader, options, url);
204
+ if (!options) {
205
+ subloaderPreparations.set(loader, preparation);
206
+ void preparation.catch(() => subloaderPreparations.delete(loader));
207
+ }
208
+ const prepared = await preparation;
209
+ if (!options) preparedDefaults.set(loader, prepared);
210
+ return prepared;
211
+ }
212
+
213
+ /** Returns declared dependencies with the shared flat override map applied. */
214
+ function getSubloaders(loader: Loader, options?: LoaderOptions): Record<string, Loader> {
215
+ const overrides = options?.core?.subloaders || {};
216
+ return Object.fromEntries(
217
+ Object.entries(loader.subloaders || {}).map(([name, dependency]) => [
218
+ name,
219
+ Object.hasOwn(overrides, name) ? overrides[name] : dependency
220
+ ])
221
+ );
222
+ }
223
+
224
+ /** Rejects cycles in the dependency graph after applying the shared overrides. */
225
+ function validateSubloaderGraph(loader: Loader, options?: LoaderOptions): void {
226
+ visitLoader(loader, []);
227
+
228
+ /** Detects cycles along each resolved dependency path. */
229
+ function visitLoader(currentLoader: Loader, ancestors: Loader[]): void {
230
+ if (ancestors.includes(currentLoader)) {
231
+ throw new Error(
232
+ `Subloader cycle: ${[...ancestors, currentLoader].map(value => value.id).join(' -> ')}`
233
+ );
234
+ }
235
+ for (const dependency of Object.values(getSubloaders(currentLoader, options))) {
236
+ visitLoader(dependency, [...ancestors, currentLoader]);
237
+ }
238
+ }
239
+ }
240
+
241
+ /** Prepares dependencies concurrently and injects them into parser calls' scoped options. */
242
+ async function bindSubloaders(
243
+ loader: Loader,
244
+ options?: LoaderOptions,
245
+ url?: string
246
+ ): Promise<LoaderWithParser> {
247
+ const [implementation, entries] = await Promise.all([
248
+ getLoaderImplementation(loader, options, url),
249
+ Promise.all(
250
+ Object.entries(getSubloaders(loader, options)).map(
251
+ async ([name, dependency]) =>
252
+ [name, await prepareLoaderDependencies(dependency, options, url)] as const
253
+ )
254
+ )
255
+ ]);
256
+ const subloaders = Object.fromEntries(entries);
257
+ // Expose preload settings as defaults before core normalizes a later parse call.
258
+ const defaultOptions = mergeOptions(
259
+ implementation.options,
260
+ (options || {}) as StrictLoaderOptions
261
+ );
262
+ defaultOptions.core = {...defaultOptions.core, worker: false};
263
+ const prepared = {...implementation, options: defaultOptions, subloaders};
264
+ delete prepared.preload;
265
+ // Function-valued dependency bindings cannot be transferred to a worker.
266
+ prepared.worker = false;
267
+ for (const method of [
268
+ 'parseBlob',
269
+ 'createDataSource',
270
+ 'parse',
271
+ 'parseSync',
272
+ 'parseText',
273
+ 'parseTextSync',
274
+ 'parseInBatches',
275
+ 'parseFile',
276
+ 'parseFileInBatches',
277
+ 'parseUrl'
278
+ ] as const) {
279
+ const parser = (implementation as Record<string, unknown>)[method];
280
+ if (typeof parser === 'function') {
281
+ (prepared as Record<string, unknown>)[method] = (
282
+ data: unknown,
283
+ parseOptions?: LoaderOptions,
284
+ context?: unknown
285
+ ) => {
286
+ const forwardedOptions = mergeOptions(options, parseOptions || {});
287
+ return parser.call(
288
+ implementation,
289
+ data,
290
+ {
291
+ ...forwardedOptions,
292
+ core: {
293
+ ...forwardedOptions.core,
294
+ subloaders: options?.core?.subloaders || parseOptions?.core?.subloaders,
295
+ worker: false
296
+ },
297
+ [loader.id]: {...(forwardedOptions[loader.id] as object), subloaders}
298
+ },
299
+ context
300
+ );
301
+ };
302
+ }
303
+ }
304
+ preparedLoaders.add(prepared);
305
+ return prepared;
306
+ }
@@ -7,17 +7,40 @@ import type {
7
7
  Loader,
8
8
  LoaderContext,
9
9
  LoaderOptions,
10
+ LoaderOptionsWithShape,
10
11
  LoaderOptionsType,
12
+ LoaderShapeType,
11
13
  LoaderReturnType,
12
14
  LoaderArrayOptionsType,
13
- LoaderArrayReturnType
15
+ LoaderArrayReturnType,
16
+ SourceLoader,
17
+ SourcePropsType,
18
+ SourceArrayOptionsType,
19
+ DataSourceOptions,
20
+ LoaderWithParser
21
+ } from '@loaders.gl/loader-utils';
22
+ import {
23
+ isBlob,
24
+ isSourceLoader,
25
+ resolveAuthenticationOptions,
26
+ resolveLoaderAuthenticationOptions
14
27
  } from '@loaders.gl/loader-utils';
15
- import {isBlob} from '@loaders.gl/loader-utils';
16
28
  import {isLoaderObject} from '../loader-utils/normalize-loader';
17
29
  import {getFetchFunction} from '../loader-utils/get-fetch-function';
18
- import {normalizeLoaderOptions} from '../loader-utils/option-utils';
30
+ import {
31
+ applySearchParamsToUrl,
32
+ normalizeLoaderOptions,
33
+ getGlobalLoaderOptions
34
+ } from '../loader-utils/option-utils';
35
+ import {fetchFile} from '../fetch/fetch-file';
19
36
 
20
37
  import {parse} from './parse';
38
+ import {parseFile} from './parse-file';
39
+ import {parseSync} from './parse-sync';
40
+ import {parseInBatches} from './parse-in-batches';
41
+ import {loadInBatches} from './load-in-batches';
42
+ import {selectLoader} from './select-loader';
43
+ import {getLoaderImplementation} from './load-loader';
21
44
 
22
45
  /**
23
46
  * Parses `data` using a specified loader
@@ -31,11 +54,14 @@ import {parse} from './parse';
31
54
 
32
55
  export async function load<
33
56
  LoaderT extends Loader,
34
- OptionsT extends LoaderOptions = LoaderOptionsType<LoaderT>
57
+ OptionsT extends LoaderOptions = LoaderOptionsWithShape<
58
+ LoaderOptionsType<LoaderT>,
59
+ LoaderShapeType<LoaderT>
60
+ >
35
61
  >(
36
62
  url: string | DataType,
37
63
  loader: LoaderT,
38
- options?: OptionsT,
64
+ options?: LoaderT extends SourceLoader ? SourcePropsType<LoaderT> & DataSourceOptions : OptionsT,
39
65
  context?: LoaderContext
40
66
  ): Promise<LoaderReturnType<LoaderT>>;
41
67
 
@@ -45,7 +71,7 @@ export async function load<
45
71
  >(
46
72
  url: string | DataType,
47
73
  loaders: LoaderArrayT,
48
- options?: OptionsT,
74
+ options?: LoaderArrayT extends SourceLoader[] ? SourceArrayOptionsType<LoaderArrayT> : OptionsT,
49
75
  context?: LoaderContext
50
76
  ): Promise<LoaderArrayReturnType<LoaderArrayT>>;
51
77
 
@@ -81,14 +107,122 @@ export async function load(
81
107
  resolvedOptions = options as LoaderOptions;
82
108
  }
83
109
 
110
+ resolvedOptions ||= getGlobalLoaderOptions();
111
+
112
+ if (!Array.isArray(resolvedLoaders) && isSourceLoader(resolvedLoaders)) {
113
+ const sourceLoader = await resolveSourceLoader(resolvedLoaders, url, resolvedOptions);
114
+ resolvedOptions = await resolveLoaderAuthenticationOptions(
115
+ resolvedLoaders.getAuthentications ? resolvedLoaders : sourceLoader,
116
+ typeof url === 'string' ? url : '',
117
+ resolvedOptions || {}
118
+ );
119
+ const runtimeCoreApi = {
120
+ fetchFile,
121
+ parse,
122
+ parseFile,
123
+ parseSync,
124
+ parseInBatches,
125
+ load,
126
+ loadInBatches
127
+ };
128
+ return sourceLoader.createDataSource(
129
+ url as string | Blob,
130
+ (resolvedOptions || {}) as LoaderOptionsType<SourceLoader>,
131
+ runtimeCoreApi
132
+ );
133
+ }
134
+
135
+ if (
136
+ Array.isArray(resolvedLoaders) &&
137
+ resolvedLoaders.length === 1 &&
138
+ isSourceLoader(resolvedLoaders[0])
139
+ ) {
140
+ const sourceLoader = await resolveSourceLoader(resolvedLoaders[0], url, resolvedOptions);
141
+ resolvedOptions = await resolveLoaderAuthenticationOptions(
142
+ resolvedLoaders[0].getAuthentications ? resolvedLoaders[0] : sourceLoader,
143
+ typeof url === 'string' ? url : '',
144
+ resolvedOptions || {}
145
+ );
146
+ const runtimeCoreApi = {
147
+ fetchFile,
148
+ parse,
149
+ parseFile,
150
+ parseSync,
151
+ parseInBatches,
152
+ load,
153
+ loadInBatches
154
+ };
155
+ return sourceLoader.createDataSource(
156
+ url as string | Blob,
157
+ (resolvedOptions || {}) as LoaderOptionsType<SourceLoader>,
158
+ runtimeCoreApi
159
+ );
160
+ }
161
+
162
+ if (typeof url === 'string' || isBlob(url)) {
163
+ const selectedLoader = await selectLoader(url, resolvedLoaders as Loader | Loader[], {
164
+ ...resolvedOptions,
165
+ core: {...resolvedOptions?.core, nothrow: true}
166
+ });
167
+
168
+ if (selectedLoader && isSourceLoader(selectedLoader)) {
169
+ const sourceLoader = await resolveSourceLoader(selectedLoader, url, resolvedOptions);
170
+ resolvedOptions = await resolveLoaderAuthenticationOptions(
171
+ selectedLoader.getAuthentications ? selectedLoader : sourceLoader,
172
+ typeof url === 'string' ? url : '',
173
+ resolvedOptions || {}
174
+ );
175
+ return sourceLoader.createDataSource(
176
+ url,
177
+ (resolvedOptions || {}) as LoaderOptionsType<SourceLoader>,
178
+ {
179
+ fetchFile,
180
+ parse,
181
+ parseFile,
182
+ parseSync,
183
+ parseInBatches,
184
+ load,
185
+ loadInBatches
186
+ }
187
+ );
188
+ }
189
+
190
+ if (selectedLoader && typeof url === 'string') {
191
+ const loaderImplementation = await getLoaderImplementation(
192
+ selectedLoader,
193
+ resolvedOptions,
194
+ url
195
+ );
196
+ resolvedOptions = await resolveLoaderAuthenticationOptions(
197
+ selectedLoader.getAuthentications ? selectedLoader : loaderImplementation,
198
+ url,
199
+ resolvedOptions || {}
200
+ );
201
+ const parseUrl = (
202
+ loaderImplementation as LoaderWithParser & {
203
+ parseUrl?: (
204
+ url: string,
205
+ options?: LoaderOptions,
206
+ context?: LoaderContext
207
+ ) => Promise<unknown>;
208
+ }
209
+ ).parseUrl;
210
+ if (parseUrl) {
211
+ const requestUrl = applySearchParamsToUrl(url, resolvedOptions);
212
+ return await parseUrl(requestUrl, resolvedOptions, context);
213
+ }
214
+ }
215
+ }
216
+
84
217
  // Select fetch function
218
+ if (resolvedOptions) resolvedOptions = resolveAuthenticationOptions(resolvedOptions);
85
219
  const fetch = getFetchFunction(resolvedOptions);
86
220
 
87
221
  // at this point, `url` could be already loaded binary data
88
222
  let data = url;
89
223
  // url is a string, fetch the url
90
224
  if (typeof url === 'string') {
91
- data = await fetch(url);
225
+ data = await fetch(applySearchParamsToUrl(url, resolvedOptions));
92
226
  // URL is Blob or File, fetchFile handles it (alt: we could generate ObjectURL here)
93
227
  }
94
228
 
@@ -114,6 +248,23 @@ export async function load(
114
248
  // Data is loaded (at least we have a `Response` object) so time to hand over to `parse`
115
249
  // return await parse(data, loaders as Loader[], options);
116
250
  return Array.isArray(resolvedLoaders)
117
- ? await parse(data, resolvedLoaders, resolvedOptions) // loader array overload
118
- : await parse(data, resolvedLoaders, resolvedOptions); // single loader overload
251
+ ? await parse(data, resolvedLoaders, resolvedOptions, context) // loader array overload
252
+ : await parse(data, resolvedLoaders, resolvedOptions, context); // single loader overload
253
+ }
254
+
255
+ /** Resolves a lightweight source loader into its runtime implementation when necessary. */
256
+ async function resolveSourceLoader(
257
+ loader: SourceLoader,
258
+ url: string | DataType,
259
+ options?: LoaderOptions
260
+ ): Promise<SourceLoader> {
261
+ if (!loader.preload) {
262
+ return loader;
263
+ }
264
+
265
+ const sourceLoader = await loader.preload(typeof url === 'string' ? url : '', options);
266
+ if (!isSourceLoader(sourceLoader)) {
267
+ throw new Error(`${loader.id} source loader preload() did not return a runtime source loader`);
268
+ }
269
+ return sourceLoader;
119
270
  }