@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
@@ -71,18 +71,18 @@
71
71
  static async addEventListener(onMessage) {
72
72
  let onMessageWrapper = onMessageWrapperMap.get(onMessage);
73
73
  if (!onMessageWrapper) {
74
- onMessageWrapper = async (message) => {
75
- if (!isKnownMessage(message)) {
74
+ onMessageWrapper = (message) => {
75
+ const messageData = getWorkerMessageData(message);
76
+ if (!messageData) {
76
77
  return;
77
78
  }
78
- const parentPort3 = await getParentPort();
79
- const { type, payload } = parentPort3 ? message : message.data;
80
- onMessage(type, payload);
79
+ onMessage(messageData.type, messageData.payload);
81
80
  };
81
+ onMessageWrapperMap.set(onMessage, onMessageWrapper);
82
82
  }
83
83
  const parentPort2 = await getParentPort();
84
84
  if (parentPort2) {
85
- console.error("not implemented");
85
+ parentPort2.on("message", onMessageWrapper);
86
86
  } else {
87
87
  globalThis.addEventListener("message", onMessageWrapper);
88
88
  }
@@ -90,9 +90,12 @@
90
90
  static async removeEventListener(onMessage) {
91
91
  const onMessageWrapper = onMessageWrapperMap.get(onMessage);
92
92
  onMessageWrapperMap.delete(onMessage);
93
+ if (!onMessageWrapper) {
94
+ return;
95
+ }
93
96
  const parentPort2 = await getParentPort();
94
97
  if (parentPort2) {
95
- console.error("not implemented");
98
+ parentPort2.off("message", onMessageWrapper);
96
99
  } else {
97
100
  globalThis.removeEventListener("message", onMessageWrapper);
98
101
  }
@@ -113,44 +116,175 @@
113
116
  }
114
117
  }
115
118
  };
116
- function isKnownMessage(message) {
117
- const { type, data } = message;
118
- return type === "message" && data && typeof data.source === "string" && data.source.startsWith("loaders.gl");
119
+ function getWorkerMessageData(message) {
120
+ if (!message || typeof message !== "object") {
121
+ return null;
122
+ }
123
+ const messageData = "data" in message && message.type === "message" ? message.data : message;
124
+ return messageData && typeof messageData.source === "string" && messageData.source.startsWith("loaders.gl") ? messageData : null;
119
125
  }
120
126
 
121
- // ../loader-utils/src/lib/worker-loader-utils/create-loader-worker.ts
127
+ // ../worker-utils/src/lib/async-queue/async-queue.ts
128
+ var AsyncQueue = class {
129
+ _values;
130
+ _settlers;
131
+ _closed;
132
+ constructor() {
133
+ this._values = [];
134
+ this._settlers = [];
135
+ this._closed = false;
136
+ }
137
+ /** Return an async iterator for this queue */
138
+ [Symbol.asyncIterator]() {
139
+ return this;
140
+ }
141
+ /** Push a new value - the async iterator will yield a promise resolved to this value */
142
+ push(value) {
143
+ return this.enqueue(value);
144
+ }
145
+ /**
146
+ * Push a new value - the async iterator will yield a promise resolved to this value
147
+ * Add an error - the async iterator will yield a promise rejected with this value
148
+ */
149
+ enqueue(value) {
150
+ if (this._closed) {
151
+ throw new Error("Closed");
152
+ }
153
+ if (this._settlers.length > 0) {
154
+ if (this._values.length > 0) {
155
+ throw new Error("Illegal internal state");
156
+ }
157
+ const settler = this._settlers.shift();
158
+ if (value instanceof Error) {
159
+ settler.reject(value);
160
+ } else {
161
+ settler.resolve({ value });
162
+ }
163
+ } else {
164
+ this._values.push(value);
165
+ }
166
+ }
167
+ /** Indicate that we not waiting for more values - The async iterator will be done */
168
+ close() {
169
+ while (this._settlers.length > 0) {
170
+ const settler = this._settlers.shift();
171
+ settler.resolve({ done: true });
172
+ }
173
+ this._closed = true;
174
+ }
175
+ // ITERATOR IMPLEMENTATION
176
+ /** @returns a Promise for an IteratorResult */
177
+ next() {
178
+ if (this._values.length > 0) {
179
+ const value = this._values.shift();
180
+ if (value instanceof Error) {
181
+ return Promise.reject(value);
182
+ }
183
+ return Promise.resolve({ done: false, value });
184
+ }
185
+ if (this._closed) {
186
+ if (this._settlers.length > 0) {
187
+ throw new Error("Illegal internal state");
188
+ }
189
+ return Promise.resolve({ done: true, value: void 0 });
190
+ }
191
+ return new Promise((resolve, reject) => {
192
+ this._settlers.push({ resolve, reject });
193
+ });
194
+ }
195
+ };
196
+
197
+ // ../worker-utils/src/lib/worker-api/create-worker.ts
122
198
  var requestId = 0;
123
- async function createLoaderWorker(loader, selectLoader = () => loader) {
199
+ var activeInputBatches = null;
200
+ var activeOutputAcknowledgements = null;
201
+ async function createWorker(process, processInBatches) {
124
202
  if (!await WorkerBody.inWorkerThread()) {
125
203
  return;
126
204
  }
205
+ const context = {
206
+ process: processOnMainThread
207
+ };
127
208
  WorkerBody.onmessage = async (type, payload) => {
128
- switch (type) {
129
- case "process":
130
- try {
131
- const { input, options = {}, context = {} } = payload;
132
- const selectedLoader = selectLoader(options);
133
- const result = await parseData({
134
- loader: selectedLoader,
135
- arrayBuffer: input,
136
- options,
137
- // @ts-expect-error fetch missing
138
- context: {
139
- ...context,
140
- _parse: parseOnMainThread
141
- }
142
- });
209
+ try {
210
+ switch (type) {
211
+ case "preload":
212
+ WorkerBody.postMessage("done", {});
213
+ break;
214
+ case "process":
215
+ if (!process) {
216
+ throw new Error("Worker does not support atomic processing");
217
+ }
218
+ const result = await process(
219
+ payload.input,
220
+ payload.options || {},
221
+ context,
222
+ payload.context || {}
223
+ );
143
224
  WorkerBody.postMessage("done", { result });
144
- } catch (error) {
145
- const message = error instanceof Error ? error.message : "";
146
- WorkerBody.postMessage("error", { error: message });
147
- }
148
- break;
149
- default:
225
+ break;
226
+ case "process-in-batches":
227
+ if (!processInBatches) {
228
+ throw new Error("Worker does not support batched processing");
229
+ }
230
+ const inputBatches = new AsyncQueue();
231
+ const outputAcknowledgements = new AsyncQueue();
232
+ activeInputBatches = inputBatches;
233
+ activeOutputAcknowledgements = outputAcknowledgements;
234
+ try {
235
+ const resultIterator = processInBatches(
236
+ createDemandDrivenIterator(inputBatches),
237
+ payload.options || {},
238
+ context,
239
+ payload.context || {}
240
+ );
241
+ for await (const batch of resultIterator) {
242
+ await WorkerBody.postMessage("output-batch", { result: batch });
243
+ await outputAcknowledgements.next();
244
+ }
245
+ await WorkerBody.postMessage("done", {});
246
+ } finally {
247
+ activeInputBatches = null;
248
+ activeOutputAcknowledgements = null;
249
+ }
250
+ break;
251
+ case "input-batch":
252
+ if (!activeInputBatches) {
253
+ throw new Error("Worker has no active batched processing session");
254
+ }
255
+ activeInputBatches.push(payload.input);
256
+ break;
257
+ case "input-done":
258
+ if (!activeInputBatches) {
259
+ throw new Error("Worker has no active batched processing session");
260
+ }
261
+ activeInputBatches.close();
262
+ break;
263
+ case "output-ack":
264
+ if (!activeOutputAcknowledgements) {
265
+ throw new Error("Worker has no active batched processing session");
266
+ }
267
+ activeOutputAcknowledgements.push(void 0);
268
+ break;
269
+ default:
270
+ }
271
+ } catch (error) {
272
+ const message = error instanceof Error ? error.message : "";
273
+ WorkerBody.postMessage("error", { error: message });
150
274
  }
151
275
  };
152
276
  }
153
- function parseOnMainThread(arrayBuffer, loader, options, context) {
277
+ async function* createDemandDrivenIterator(inputBatches) {
278
+ while (true) {
279
+ await WorkerBody.postMessage("input-request", {});
280
+ const nextBatch = await inputBatches.next();
281
+ if (nextBatch.done) {
282
+ return;
283
+ }
284
+ yield nextBatch.value;
285
+ }
286
+ }
287
+ function processOnMainThread(arrayBuffer, options = {}, jobContext = {}) {
154
288
  return new Promise((resolve, reject) => {
155
289
  const id = requestId++;
156
290
  const onMessage = (type, payload2) => {
@@ -170,10 +304,107 @@
170
304
  }
171
305
  };
172
306
  WorkerBody.addEventListener(onMessage);
173
- const payload = { id, input: arrayBuffer, options };
307
+ const payload = { id, input: arrayBuffer, options, context: jobContext };
174
308
  WorkerBody.postMessage("process", payload);
175
309
  });
176
310
  }
311
+
312
+ // ../loader-utils/src/lib/worker-loader-utils/create-loader-worker.ts
313
+ async function createLoaderWorker(loader, selectLoader = () => loader) {
314
+ await createWorker(
315
+ (input, options, workerContext, loaderContext) => processLoaderWorkerData(
316
+ selectLoader(options || {}),
317
+ input,
318
+ options,
319
+ workerContext,
320
+ loaderContext
321
+ ),
322
+ (inputIterator, options, workerContext, loaderContext) => processLoaderWorkerBatches(
323
+ selectLoader(options || {}),
324
+ inputIterator,
325
+ options,
326
+ workerContext,
327
+ loaderContext
328
+ )
329
+ );
330
+ }
331
+ async function processLoaderWorkerData(loader, input, options = {}, workerContext, loaderContext = {}) {
332
+ const result = await parseData({
333
+ loader,
334
+ arrayBuffer: input,
335
+ options,
336
+ context: {
337
+ ...loaderContext,
338
+ coreApi: createWorkerCoreApi(),
339
+ _parse: createParseOnMainThread(workerContext?.process)
340
+ }
341
+ });
342
+ return loader.serializeWorkerResult ? loader.serializeWorkerResult(result, options, loaderContext) : result;
343
+ }
344
+ async function* processLoaderWorkerBatches(loader, inputIterator, options = {}, workerContext, loaderContext = {}) {
345
+ if (!loader.parseInBatches) {
346
+ throw new Error(`${loader.id} loader does not support batched parsing`);
347
+ }
348
+ const resultIterator = loader.parseInBatches(inputIterator, options, {
349
+ ...loaderContext,
350
+ coreApi: createWorkerCoreApi(),
351
+ _parse: createParseOnMainThread(workerContext?.process)
352
+ });
353
+ for await (const batch of resultIterator) {
354
+ yield loader.serializeWorkerBatch ? loader.serializeWorkerBatch(batch, options, loaderContext) : batch;
355
+ }
356
+ }
357
+ function createWorkerCoreApi() {
358
+ const unavailable = (methodName) => () => {
359
+ throw new Error(`context.coreApi.${methodName} is unavailable inside worker loaders.`);
360
+ };
361
+ return {
362
+ fetchFile: async (urlOrData, fetchOptions) => await fetch(urlOrData, fetchOptions),
363
+ parseSync: unavailable("parseSync"),
364
+ parse: unavailable("parse"),
365
+ parseFile: unavailable("parseFile"),
366
+ parseInBatches: unavailable("parseInBatches"),
367
+ load: unavailable("load"),
368
+ loadInBatches: unavailable("loadInBatches")
369
+ };
370
+ }
371
+ function createParseOnMainThread(processOnMainThread2) {
372
+ return (arrayBuffer, loaders, options, context) => {
373
+ if (!processOnMainThread2) {
374
+ throw new Error("Worker not set up to parse on main thread");
375
+ }
376
+ const parseArguments = getMainThreadParseArguments(loaders, options, context);
377
+ return processOnMainThread2(arrayBuffer, parseArguments.options, {
378
+ ...parseArguments.context,
379
+ ...Array.isArray(loaders) || loaders && isLoaderObject(loaders) ? {
380
+ _loaderIsArray: Array.isArray(loaders),
381
+ _loaderIds: (Array.isArray(loaders) ? loaders : [loaders]).map((loader) => loader.id)
382
+ } : {}
383
+ });
384
+ };
385
+ }
386
+ function getMainThreadParseArguments(loaders, options, context) {
387
+ if (options) {
388
+ return { options, context: getSerializableLoaderContext(context) };
389
+ }
390
+ if (Array.isArray(loaders) || loaders && isLoaderObject(loaders)) {
391
+ return { options: void 0, context: getSerializableLoaderContext(context) };
392
+ }
393
+ if (loaders && !Array.isArray(loaders)) {
394
+ return { options: loaders };
395
+ }
396
+ return { options: void 0, context: getSerializableLoaderContext(context) };
397
+ }
398
+ function isLoaderObject(value) {
399
+ return "id" in value && "extensions" in value;
400
+ }
401
+ function getSerializableLoaderContext(context) {
402
+ if (!context) {
403
+ return void 0;
404
+ }
405
+ const { fetch: fetch2, loaders, coreApi, _parse, _parseSync, _parseInBatches, ...serializableContext } = context;
406
+ return JSON.parse(JSON.stringify(serializableContext));
407
+ }
177
408
  async function parseData({
178
409
  loader,
179
410
  arrayBuffer,
@@ -182,9 +413,9 @@
182
413
  }) {
183
414
  let data;
184
415
  let parser;
185
- if (loader.parseSync || loader.parse) {
416
+ if (loader.parse || loader.parseSync) {
186
417
  data = arrayBuffer;
187
- parser = loader.parseSync || loader.parse;
418
+ parser = loader.parse || loader.parseSync;
188
419
  } else if (loader.parseTextSync) {
189
420
  const textDecoder = new TextDecoder();
190
421
  data = textDecoder.decode(arrayBuffer);
@@ -192,14 +423,11 @@
192
423
  } else {
193
424
  throw new Error(`Could not load data with ${loader.name} loader`);
194
425
  }
195
- const workerModules = Object.fromEntries(
196
- Object.entries(options.modules || {}).filter(([, value]) => typeof value === "string")
197
- );
198
426
  options = {
199
427
  ...options,
200
428
  modules: {
201
429
  ...loader && loader.options && loader.options.modules || {},
202
- ...workerModules
430
+ ...options.modules || {}
203
431
  },
204
432
  core: {
205
433
  ...options.core,
@@ -210,7 +438,7 @@
210
438
  }
211
439
 
212
440
  // src/null-loader.ts
213
- var VERSION = true ? "4.5.3" : "latest";
441
+ var VERSION = true ? "5.0.0-alpha.10" : "latest";
214
442
  var NullLoader = {
215
443
  dataType: null,
216
444
  batchType: null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loaders.gl/core",
3
- "version": "4.5.3",
3
+ "version": "5.0.0-alpha.10",
4
4
  "description": "The core API for working with loaders.gl loaders and writers",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -58,11 +58,11 @@
58
58
  "build-worker-node": "esbuild src/workers/null-worker.ts --outfile=dist/null-worker-node.js --bundle --platform=node --target=node16 --define:__VERSION__=\\\"$npm_package_version\\\""
59
59
  },
60
60
  "dependencies": {
61
- "@loaders.gl/loader-utils": "4.5.3",
62
- "@loaders.gl/schema": "4.5.3",
63
- "@loaders.gl/schema-utils": "4.5.3",
64
- "@loaders.gl/worker-utils": "4.5.3",
61
+ "@loaders.gl/loader-utils": "5.0.0-alpha.10",
62
+ "@loaders.gl/schema": "5.0.0-alpha.10",
63
+ "@loaders.gl/schema-utils": "5.0.0-alpha.10",
64
+ "@loaders.gl/worker-utils": "5.0.0-alpha.10",
65
65
  "@probe.gl/log": "^4.1.1"
66
66
  },
67
- "gitHead": "3ad4ab002b4a53ac5d88feb856117a8a1e8c5bb0"
67
+ "gitHead": "7cbfaef633e86356c946541dab4977b522745b6c"
68
68
  }
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export type {
7
7
  Loader,
8
8
  LoaderWithParser,
9
9
  LoaderContext,
10
+ CoreAPI,
10
11
  LoaderOptions,
11
12
  Writer,
12
13
  WriterOptions,
@@ -19,6 +20,7 @@ export type {
19
20
  FileSystem,
20
21
  RandomAccessFileSystem
21
22
  } from '@loaders.gl/loader-utils';
23
+ export type {SourceLoader} from '@loaders.gl/loader-utils';
22
24
 
23
25
  // FILE READING AND WRITING
24
26
  export {fetchFile} from './lib/fetch/fetch-file';
@@ -35,8 +37,11 @@ export {selectLoader, selectLoaderSync} from './lib/api/select-loader';
35
37
 
36
38
  // LOADING (READING + PARSING)
37
39
  export {parse} from './lib/api/parse';
40
+ export {parseFile} from './lib/api/parse-file';
38
41
  export {parseSync} from './lib/api/parse-sync';
39
42
  export {parseInBatches} from './lib/api/parse-in-batches';
43
+ export {preload, preloadSync} from './lib/api/preload';
44
+ export {coreApi} from './lib/api/core-api';
40
45
 
41
46
  export {load} from './lib/api/load';
42
47
  export {loadInBatches} from './lib/api/load-in-batches';
@@ -59,7 +64,7 @@ export {makeIterator} from './iterators/make-iterator/make-iterator';
59
64
  export {makeStream} from './iterators/make-stream/make-stream';
60
65
 
61
66
  // CORE LOADERS
62
- export {NullWorkerLoader, NullLoader} from './null-loader';
67
+ export {NullLoader} from './null-loader';
63
68
  export {JSONLoader} from '@loaders.gl/loader-utils';
64
69
 
65
70
  // EXPERIMENTAL
@@ -95,3 +100,7 @@ export {
95
100
  isReadableStream,
96
101
  isWritableStream
97
102
  } from '@loaders.gl/loader-utils';
103
+
104
+ // DEPRECATED EXPORTS
105
+ /** @deprecated Use NullLoader. */
106
+ export {NullWorkerLoader} from './null-loader';
@@ -43,30 +43,68 @@ async function* makeBrowserStreamIterator(
43
43
  const reader = stream.getReader();
44
44
 
45
45
  let nextBatchPromise: Promise<{done?: boolean; value?: Uint8Array}> | undefined;
46
+ let streamFinished = false;
47
+ let streamReadFailed = false;
48
+ let iteratorFailed = false;
46
49
 
47
50
  try {
48
51
  // eslint-disable-next-line no-constant-condition
49
52
  while (true) {
50
53
  const currentBatchPromise = nextBatchPromise || reader.read();
51
- // Issue a read for an additional batch, while we await the next batch
52
- // Idea is to make fetching happen in parallel with processing / parsing
53
- if (options?._streamReadAhead) {
54
- nextBatchPromise = reader.read();
55
- }
54
+ nextBatchPromise = undefined;
56
55
  // Read from the stream
57
56
  // value is a Uint8Array
58
- const {done, value} = await currentBatchPromise;
57
+ let batch;
58
+ try {
59
+ batch = await currentBatchPromise;
60
+ } catch (error) {
61
+ streamReadFailed = true;
62
+ throw error;
63
+ }
64
+ const {done, value} = batch;
59
65
  // Exit if we're done
60
66
  if (done) {
67
+ streamFinished = true;
61
68
  return;
62
69
  }
70
+ // Issue a read for an additional batch while the current batch is processed
71
+ if (options?._streamReadAhead) {
72
+ nextBatchPromise = reader.read();
73
+ // Observe a prefetched rejection even if the consumer stops before requesting it
74
+ void nextBatchPromise.catch(() => {});
75
+ }
63
76
  // Else yield the chunk
64
77
  yield toArrayBuffer(value);
65
78
  }
66
79
  } catch (error) {
67
- // TODO - examples makes it look like this should always be called,
68
- // but that generates exceptions so only call it if we do not reach the end
69
- reader.releaseLock();
80
+ iteratorFailed = true;
81
+ throw error;
82
+ } finally {
83
+ let cleanupFailed = false;
84
+ let cleanupError: unknown;
85
+
86
+ if (!streamFinished && !streamReadFailed) {
87
+ try {
88
+ await reader.cancel();
89
+ } catch (error) {
90
+ cleanupFailed = true;
91
+ cleanupError = error;
92
+ }
93
+ }
94
+
95
+ try {
96
+ reader.releaseLock();
97
+ } catch (error) {
98
+ if (!cleanupFailed) {
99
+ cleanupFailed = true;
100
+ cleanupError = error;
101
+ }
102
+ }
103
+
104
+ // Preserve errors from iteration instead of replacing them with cleanup errors
105
+ if (!iteratorFailed && cleanupFailed) {
106
+ throw cleanupError;
107
+ }
70
108
  }
71
109
  }
72
110
 
@@ -0,0 +1,25 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {CoreAPI} from '@loaders.gl/loader-utils';
6
+ import {fetchFile} from '../fetch/fetch-file';
7
+ import {parse} from './parse';
8
+ import {parseFile} from './parse-file';
9
+ import {parseSync} from './parse-sync';
10
+ import {parseInBatches} from './parse-in-batches';
11
+ import {load} from './load';
12
+ import {loadInBatches} from './load-in-batches';
13
+
14
+ /**
15
+ * Shared Core API surface that can be injected into contexts and sources.
16
+ */
17
+ export const coreApi: CoreAPI = Object.freeze({
18
+ fetchFile,
19
+ parse,
20
+ parseFile,
21
+ parseSync,
22
+ parseInBatches,
23
+ load,
24
+ loadInBatches
25
+ });
@@ -2,7 +2,14 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {Source, SourceArrayOptionsType, SourceArrayDataSourceType} from '@loaders.gl/loader-utils';
5
+ import {
6
+ SourceLoader,
7
+ SourceArrayOptionsType,
8
+ SourceArrayDataSourceType
9
+ } from '@loaders.gl/loader-utils';
10
+ import {resolveAuthenticationOptions} from '@loaders.gl/loader-utils';
11
+ import {coreApi} from './core-api';
12
+ import {selectLoaderSync} from './select-loader';
6
13
 
7
14
  /**
8
15
  * Creates a source from a service
@@ -13,39 +20,61 @@ import {Source, SourceArrayOptionsType, SourceArrayDataSourceType} from '@loader
13
20
  */
14
21
  // DataSourceOptionsT extends DataSourceOptions = DataSourceOptions,
15
22
  // DataSourceT extends DataSource = DataSource
16
- export function createDataSource<SourceArrayT extends Source[]>(
17
- data: string | Blob,
23
+ export function createDataSource<SourceArrayT extends SourceLoader[]>(
24
+ data: unknown,
18
25
  sources: Readonly<SourceArrayT>,
19
- options: Readonly<SourceArrayOptionsType<SourceArrayT>>
26
+ options: Readonly<
27
+ SourceArrayOptionsType<SourceArrayT>
28
+ > = {} as SourceArrayOptionsType<SourceArrayT>
20
29
  ): SourceArrayDataSourceType<SourceArrayT> {
21
- const type = options?.core?.type || (options.type as unknown as string) || 'auto';
22
- const source = type === 'auto' ? selectSource(data, sources) : getSourceOfType(type, sources);
30
+ const resolvedOptions = (options || {}) as SourceArrayOptionsType<SourceArrayT>;
31
+ const type = resolvedOptions?.core?.type || (resolvedOptions.type as unknown as string) || 'auto';
32
+ const source =
33
+ sources.length === 1
34
+ ? sources[0]
35
+ : type === 'auto'
36
+ ? (selectLoaderSync(data as never, sources as unknown as SourceLoader[], {
37
+ // `createDataSource` supports non-fetch inputs such as in-memory tables.
38
+ // Selection remains best-effort for those cases.
39
+ ...resolvedOptions,
40
+ core: {...resolvedOptions?.core, type: 'auto'}
41
+ }) as SourceArrayT[number] | null)
42
+ : getSourceOfType(type, sources);
23
43
 
24
44
  if (!source) {
25
45
  throw new Error('Not a valid source type');
26
46
  }
27
- return source.createDataSource(data, options);
28
- }
29
-
30
- // TODO - use selectSource...
31
-
32
- /** Guess service type from URL */
33
- function selectSource<SourceArrayT extends Source[]>(
34
- url: string | Blob,
35
- sources: Readonly<SourceArrayT>
36
- ): SourceArrayT[number] | null {
37
- for (const service of sources) {
38
- // @ts-expect-error
39
- if (service.testURL && service.testURL(url)) {
40
- return service;
41
- }
47
+ const contributions = resolvedOptions.core?.credentials?.length
48
+ ? source.getAuthentications?.(typeof data === 'string' ? data : '', resolvedOptions)
49
+ : undefined;
50
+ if (contributions && 'then' in contributions) {
51
+ // Avoid an unhandled rejection from a hook that cannot be awaited by this sync API.
52
+ void contributions.catch(() => {});
53
+ throw new Error('Use load() with an asynchronous getAuthentications() hook.');
42
54
  }
43
-
44
- return null;
55
+ const authenticatedOptions = resolveAuthenticationOptions(
56
+ contributions?.length
57
+ ? {
58
+ ...resolvedOptions,
59
+ core: {
60
+ ...resolvedOptions.core,
61
+ authentications: [
62
+ ...(resolvedOptions.core?.authentications || []),
63
+ ...(contributions || [])
64
+ ]
65
+ }
66
+ }
67
+ : resolvedOptions
68
+ );
69
+ return source.createDataSource(
70
+ data as string | Blob,
71
+ authenticatedOptions,
72
+ coreApi
73
+ ) as SourceArrayDataSourceType<SourceArrayT>;
45
74
  }
46
75
 
47
76
  /** Guess service type from URL */
48
- function getSourceOfType(type: string, sources: Readonly<Source[]>): Source | null {
77
+ function getSourceOfType(type: string, sources: Readonly<SourceLoader[]>): SourceLoader | null {
49
78
  for (const service of sources) {
50
79
  if (service.type === type) {
51
80
  return service;