UNPKG

@loaders.gl/loader-utils

Version:

Framework-independent loaders for 3D graphics formats

4 lines 97 kB
{ "version": 3, "sources": ["index.js", "loader-types.js", "lib/env-utils/assert.js", "lib/env-utils/globals.js", "lib/log-utils/log.js", "lib/option-utils/merge-loader-options.js", "lib/module-utils/js-module-utils.js", "lib/worker-loader-utils/create-loader-worker.js", "lib/worker-loader-utils/parse-with-worker.js", "lib/worker-loader-utils/encode-with-worker.js", "lib/binary-utils/get-first-characters.js", "lib/parser-utils/parse-json.js", "lib/binary-utils/array-buffer-utils.js", "lib/binary-utils/memory-copy-utils.js", "lib/binary-utils/dataview-copy-utils.js", "lib/iterators/text-iterators.js", "lib/iterators/async-iteration.js", "lib/request-utils/request-scheduler.js", "lib/path-utils/file-aliases.js", "json-loader.js", "lib/node/buffer.browser.js", "lib/binary-utils/memory-conversion-utils.js", "lib/node/promisify.js", "lib/path-utils/path.js", "lib/path-utils/get-cwd.js", "lib/node/stream.browser.js", "lib/files/blob-file.js", "lib/files/http-file.js", "lib/files/node-file-facade.js", "lib/filesystems/node-filesystem-facade.js", "lib/file-provider/file-provider-interface.js", "lib/file-provider/file-provider.js", "lib/file-provider/file-handle-file.js", "lib/file-provider/data-view-file.js", "lib/sources/data-source.js", "lib/sources/image-source.js", "lib/sources/vector-source.js"], "sourcesContent": ["// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nexport { parseFromContext, parseSyncFromContext, parseInBatchesFromContext } from \"./loader-types.js\";\n// GENERAL UTILS\nexport { assert } from \"./lib/env-utils/assert.js\";\nexport { isBrowser, isWorker, nodeVersion, self, window, global, document } from \"./lib/env-utils/globals.js\";\nexport { log } from \"./lib/log-utils/log.js\";\n// Options and modules\nexport { mergeLoaderOptions } from \"./lib/option-utils/merge-loader-options.js\";\nexport { registerJSModules } from \"./lib/module-utils/js-module-utils.js\";\nexport { checkJSModule, getJSModule, getJSModuleOrNull } from \"./lib/module-utils/js-module-utils.js\";\n// LOADERS.GL-SPECIFIC WORKER UTILS\nexport { createLoaderWorker } from \"./lib/worker-loader-utils/create-loader-worker.js\";\nexport { parseWithWorker, canParseWithWorker } from \"./lib/worker-loader-utils/parse-with-worker.js\";\nexport { canEncodeWithWorker } from \"./lib/worker-loader-utils/encode-with-worker.js\";\n// PARSER UTILS\nexport { parseJSON } from \"./lib/parser-utils/parse-json.js\";\n// MEMORY COPY UTILS\nexport { sliceArrayBuffer, concatenateArrayBuffers, concatenateArrayBuffersFromArray, concatenateTypedArrays, compareArrayBuffers } from \"./lib/binary-utils/array-buffer-utils.js\";\nexport { padToNBytes, copyToArray, copyArrayBuffer } from \"./lib/binary-utils/memory-copy-utils.js\";\nexport { padStringToByteAlignment, copyStringToDataView, copyBinaryToDataView, copyPaddedArrayBufferToDataView, copyPaddedStringToDataView } from \"./lib/binary-utils/dataview-copy-utils.js\";\nexport { getFirstCharacters, getMagicString } from \"./lib/binary-utils/get-first-characters.js\";\n// ITERATOR UTILS\nexport { makeTextEncoderIterator, makeTextDecoderIterator, makeLineIterator, makeNumberedLineIterator } from \"./lib/iterators/text-iterators.js\";\nexport { forEach, concatenateArrayBuffersAsync } from \"./lib/iterators/async-iteration.js\";\n// REQUEST UTILS\nexport { default as RequestScheduler } from \"./lib/request-utils/request-scheduler.js\";\n// PATH HELPERS\nexport { setPathPrefix, getPathPrefix, resolvePath } from \"./lib/path-utils/file-aliases.js\";\nexport { addAliases as _addAliases } from \"./lib/path-utils/file-aliases.js\";\n// MICRO LOADERS\nexport { JSONLoader } from \"./json-loader.js\";\n// NODE support\n// Node.js emulation (can be used in browser)\n// Avoid direct use of `Buffer` which pulls in 50KB polyfill\nexport { isBuffer, toBuffer, toArrayBuffer } from \"./lib/binary-utils/memory-conversion-utils.js\";\n// Note.js wrappers (can be safely imported, but not used in browser)\n// Use instead of importing 'util' to avoid node dependencies\nexport { promisify1, promisify2 } from \"./lib/node/promisify.js\";\n// `path` replacement (avoids bundling big path polyfill)\nimport * as path from \"./lib/path-utils/path.js\";\nexport { path };\n// Use instead of importing 'stream' to avoid node dependencies`\nimport * as stream from \"./lib/node/stream.js\";\nexport { stream };\nexport { BlobFile } from \"./lib/files/blob-file.js\";\nexport { HttpFile } from \"./lib/files/http-file.js\";\nexport { NodeFileFacade as NodeFile } from \"./lib/files/node-file-facade.js\";\nexport { NodeFileSystemFacade as NodeFilesystem } from \"./lib/filesystems/node-filesystem-facade.js\";\nexport { isFileProvider } from \"./lib/file-provider/file-provider-interface.js\";\nexport { FileProvider } from \"./lib/file-provider/file-provider.js\";\nexport { FileHandleFile } from \"./lib/file-provider/file-handle-file.js\";\nexport { DataViewFile } from \"./lib/file-provider/data-view-file.js\";\nexport { DataSource } from \"./lib/sources/data-source.js\";\nexport { ImageSource } from \"./lib/sources/image-source.js\";\nexport { VectorSource } from \"./lib/sources/vector-source.js\";\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n/**\n * Parses `data` using a specified loader\n * @param data\n * @param loaders\n * @param options\n * @param context\n */\n// implementation signature\nexport async function parseFromContext(data, loaders, options, context) {\n return context._parse(data, loaders, options, context);\n}\n/**\n * Parses `data` synchronously using the specified loader, parse function provided via the loader context\n */\nexport function parseSyncFromContext(data, loader, options, context) {\n if (!context._parseSync) {\n throw new Error('parseSync');\n }\n return context._parseSync(data, loader, options, context);\n}\n/**\n * Parses `data` synchronously using a specified loader, parse function provided via the loader context\n */\nexport async function parseInBatchesFromContext(data, loader, options, context) {\n if (!context._parseInBatches) {\n throw new Error('parseInBatches');\n }\n return context._parseInBatches(data, loader, options, context);\n}\n", "/**\n * Throws an `Error` with the optional `message` if `condition` is falsy\n * @note Replacement for the external assert method to reduce bundle size\n */\nexport function assert(condition, message) {\n if (!condition) {\n throw new Error(message || 'loader assertion failed.');\n }\n}\n", "// Purpose: include this in your module to avoid\n// dependencies on micro modules like 'global' and 'is-browser';\n/* eslint-disable no-restricted-globals */\nconst globals = {\n self: typeof self !== 'undefined' && self,\n window: typeof window !== 'undefined' && window,\n global: typeof global !== 'undefined' && global,\n document: typeof document !== 'undefined' && document\n};\nconst self_ = globals.self || globals.window || globals.global || {};\nconst window_ = globals.window || globals.self || globals.global || {};\nconst global_ = globals.global || globals.self || globals.window || {};\nconst document_ = globals.document || {};\nexport { self_ as self, window_ as window, global_ as global, document_ as document };\n/** true if running in a browser */\nexport const isBrowser = \n// @ts-ignore process does not exist on browser\nBoolean(typeof process !== 'object' || String(process) !== '[object process]' || process.browser);\n/** true if running in a worker thread */\nexport const isWorker = typeof importScripts === 'function';\n// Extract node major version\nconst matches = typeof process !== 'undefined' && process.version && /v([0-9]*)/.exec(process.version);\n/** Major Node version (as a number) */\nexport const nodeVersion = (matches && parseFloat(matches[1])) || 0;\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { Log } from '@probe.gl/log';\n// Version constant cannot be imported, it needs to correspond to the build version of **this** module.\n// __VERSION__ is injected by babel-plugin-version-inline\n// @ts-ignore TS2304: Cannot find name '__VERSION__'.\nexport const VERSION = typeof \"4.3.2\" !== 'undefined' ? \"4.3.2\" : 'latest';\nconst version = VERSION[0] >= '0' && VERSION[0] <= '9' ? `v${VERSION}` : '';\n// Make sure we set the global variable\nfunction createLog() {\n const log = new Log({ id: 'loaders.gl' });\n globalThis.loaders = globalThis.loaders || {};\n globalThis.loaders.log = log;\n globalThis.loaders.version = version;\n globalThis.probe = globalThis.probe || {};\n globalThis.probe.loaders = log;\n return log;\n}\nexport const log = createLog();\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n/**\n *\n * @param baseOptions Can be undefined, in which case a fresh options object will be minted\n * @param newOptions\n * @returns\n */\nexport function mergeLoaderOptions(baseOptions, newOptions) {\n return mergeOptionsRecursively(baseOptions || {}, newOptions);\n}\nfunction mergeOptionsRecursively(baseOptions, newOptions, level = 0) {\n // Sanity check (jest test runner overwrites the console object which can lead to infinite recursion)\n if (level > 3) {\n return newOptions;\n }\n const options = { ...baseOptions };\n for (const [key, newValue] of Object.entries(newOptions)) {\n if (newValue && typeof newValue === 'object' && !Array.isArray(newValue)) {\n options[key] = mergeOptionsRecursively(options[key] || {}, newOptions[key], level + 1);\n // Object.assign(options[key] as object, newOptions[key]);\n }\n else {\n options[key] = newOptions[key];\n }\n }\n return options;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { log } from \"../log-utils/log.js\";\n/**\n * Register application-imported modules\n * These modules are typically to big to bundle, or may have issues on some bundlers/environments\n */\nexport function registerJSModules(modules) {\n globalThis.loaders ||= {};\n globalThis.loaders.modules ||= {};\n Object.assign(globalThis.loaders.modules, modules);\n}\n/**\n * Get a pre-registered application-imported module, warn if not present\n */\nexport function checkJSModule(name, caller) {\n const module = globalThis.loaders?.modules?.[name];\n if (!module) {\n log.warn(`${caller}: ${name} library not installed`)();\n }\n}\n/**\n * Get a pre-registered application-imported module, throw if not present\n */\nexport function getJSModule(name, caller) {\n const module = globalThis.loaders?.modules?.[name];\n if (!module) {\n throw new Error(`${caller}: ${name} library not installed`);\n }\n return module;\n}\n/**\n * Get a pre-registered application-imported module, return null if not present\n */\nexport function getJSModuleOrNull(name) {\n const module = globalThis.loaders?.modules?.[name];\n return module || null;\n}\n", "import { WorkerBody } from '@loaders.gl/worker-utils';\n// import {validateLoaderVersion} from './validate-loader-version';\nlet requestId = 0;\n/**\n * Set up a WebWorkerGlobalScope to talk with the main thread\n * @param loader\n */\nexport async function createLoaderWorker(loader) {\n // Check that we are actually in a worker thread\n if (!(await WorkerBody.inWorkerThread())) {\n return;\n }\n WorkerBody.onmessage = async (type, payload) => {\n switch (type) {\n case 'process':\n try {\n // validateLoaderVersion(loader, data.source.split('@')[1]);\n const { input, options = {}, context = {} } = payload;\n const result = await parseData({\n loader,\n arrayBuffer: input,\n options,\n // @ts-expect-error fetch missing\n context: {\n ...context,\n _parse: parseOnMainThread\n }\n });\n WorkerBody.postMessage('done', { result });\n }\n catch (error) {\n const message = error instanceof Error ? error.message : '';\n WorkerBody.postMessage('error', { error: message });\n }\n break;\n default:\n }\n };\n}\nfunction parseOnMainThread(arrayBuffer, loader, options, context) {\n return new Promise((resolve, reject) => {\n const id = requestId++;\n /**\n */\n const onMessage = (type, payload) => {\n if (payload.id !== id) {\n // not ours\n return;\n }\n switch (type) {\n case 'done':\n WorkerBody.removeEventListener(onMessage);\n resolve(payload.result);\n break;\n case 'error':\n WorkerBody.removeEventListener(onMessage);\n reject(payload.error);\n break;\n default:\n // ignore\n }\n };\n WorkerBody.addEventListener(onMessage);\n // Ask the main thread to decode data\n const payload = { id, input: arrayBuffer, options };\n WorkerBody.postMessage('process', payload);\n });\n}\n// TODO - Support byteOffset and byteLength (enabling parsing of embedded binaries without copies)\n// TODO - Why not support async loader.parse* funcs here?\n// TODO - Why not reuse a common function instead of reimplementing loader.parse* selection logic? Keeping loader small?\n// TODO - Lack of appropriate parser functions can be detected when we create worker, no need to wait until parse\nasync function parseData({ loader, arrayBuffer, options, context }) {\n let data;\n let parser;\n if (loader.parseSync || loader.parse) {\n data = arrayBuffer;\n parser = loader.parseSync || loader.parse;\n }\n else if (loader.parseTextSync) {\n const textDecoder = new TextDecoder();\n data = textDecoder.decode(arrayBuffer);\n parser = loader.parseTextSync;\n }\n else {\n throw new Error(`Could not load data with ${loader.name} loader`);\n }\n // TODO - proper merge in of loader options...\n options = {\n ...options,\n modules: (loader && loader.options && loader.options.modules) || {},\n worker: false\n };\n return await parser(data, { ...options }, context, loader);\n}\n", "import { isBrowser, WorkerFarm, getWorkerURL } from '@loaders.gl/worker-utils';\n/**\n * Determines if a loader can parse with worker\n * @param loader\n * @param options\n */\nexport function canParseWithWorker(loader, options) {\n if (!WorkerFarm.isSupported()) {\n return false;\n }\n // Node workers are still experimental\n if (!isBrowser && !options?._nodeWorkers) {\n return false;\n }\n return loader.worker && options?.worker;\n}\n/**\n * this function expects that the worker function sends certain messages,\n * this can be automated if the worker is wrapper by a call to createLoaderWorker in @loaders.gl/loader-utils.\n */\nexport async function parseWithWorker(loader, data, options, context, parseOnMainThread) {\n const name = loader.id; // TODO\n const url = getWorkerURL(loader, options);\n const workerFarm = WorkerFarm.getWorkerFarm(options);\n const workerPool = workerFarm.getWorkerPool({ name, url });\n // options.log object contains functions which cannot be transferred\n // context.fetch & context.parse functions cannot be transferred\n // TODO - decide how to handle logging on workers\n options = JSON.parse(JSON.stringify(options));\n context = JSON.parse(JSON.stringify(context || {}));\n const job = await workerPool.startJob('process-on-worker', \n // @ts-expect-error\n onMessage.bind(null, parseOnMainThread) // eslint-disable-line @typescript-eslint/no-misused-promises\n );\n job.postMessage('process', {\n // @ts-ignore\n input: data,\n options,\n context\n });\n const result = await job.result;\n // TODO - what is going on here?\n return await result.result;\n}\n/**\n * Handle worker's responses to the main thread\n * @param job\n * @param type\n * @param payload\n */\nasync function onMessage(parseOnMainThread, job, type, payload) {\n switch (type) {\n case 'done':\n job.done(payload);\n break;\n case 'error':\n job.error(new Error(payload.error));\n break;\n case 'process':\n // Worker is asking for main thread to parseO\n const { id, input, options } = payload;\n try {\n const result = await parseOnMainThread(input, options);\n job.postMessage('done', { id, result });\n }\n catch (error) {\n const message = error instanceof Error ? error.message : 'unknown error';\n job.postMessage('error', { id, error: message });\n }\n break;\n default:\n // eslint-disable-next-line\n console.warn(`parse-with-worker unknown message ${type}`);\n }\n}\n", "import { WorkerFarm } from '@loaders.gl/worker-utils';\nimport { isBrowser } from \"../env-utils/globals.js\";\n/**\n * Determines if a loader can parse with worker\n * @param loader\n * @param options\n */\nexport function canEncodeWithWorker(writer, options) {\n if (!WorkerFarm.isSupported()) {\n return false;\n }\n // Node workers are still experimental\n if (!isBrowser && !options?._nodeWorkers) {\n return false;\n }\n return writer.worker && options?.worker;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n/**\n * Get the first characters from a binary file (interpret the first bytes as an ASCII string)\n * @param data\n * @param length\n * @returns\n */\nexport function getFirstCharacters(data, length = 5) {\n if (typeof data === 'string') {\n return data.slice(0, length);\n }\n else if (ArrayBuffer.isView(data)) {\n // Typed Arrays can have offsets into underlying buffer\n return getMagicString(data.buffer, data.byteOffset, length);\n }\n else if (data instanceof ArrayBuffer) {\n const byteOffset = 0;\n return getMagicString(data, byteOffset, length);\n }\n return '';\n}\n/**\n * Gets a magic string from a \"file\"\n * Typically used to check or detect file format\n * @param arrayBuffer\n * @param byteOffset\n * @param length\n * @returns\n */\nexport function getMagicString(arrayBuffer, byteOffset, length) {\n if (arrayBuffer.byteLength <= byteOffset + length) {\n return '';\n }\n const dataView = new DataView(arrayBuffer);\n let magic = '';\n for (let i = 0; i < length; i++) {\n magic += String.fromCharCode(dataView.getUint8(byteOffset + i));\n }\n return magic;\n}\n", "import { getFirstCharacters } from \"../binary-utils/get-first-characters.js\";\n/**\n * Minimal JSON parser that throws more meaningful error messages\n */\nexport function parseJSON(string) {\n try {\n return JSON.parse(string);\n }\n catch (_) {\n throw new Error(`Failed to parse JSON from data starting with \"${getFirstCharacters(string)}\"`);\n }\n}\n", "/**\n * compare two binary arrays for equality\n * @param a\n * @param b\n * @param byteLength\n */\nexport function compareArrayBuffers(arrayBuffer1, arrayBuffer2, byteLength) {\n byteLength = byteLength || arrayBuffer1.byteLength;\n if (arrayBuffer1.byteLength < byteLength || arrayBuffer2.byteLength < byteLength) {\n return false;\n }\n const array1 = new Uint8Array(arrayBuffer1);\n const array2 = new Uint8Array(arrayBuffer2);\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false;\n }\n }\n return true;\n}\n/**\n * Concatenate a sequence of ArrayBuffers from arguments\n * @return A concatenated ArrayBuffer\n */\nexport function concatenateArrayBuffers(...sources) {\n return concatenateArrayBuffersFromArray(sources);\n}\n/**\n * Concatenate a sequence of ArrayBuffers from array\n * @return A concatenated ArrayBuffer\n */\nexport function concatenateArrayBuffersFromArray(sources) {\n // Make sure all inputs are wrapped in typed arrays\n const sourceArrays = sources.map((source2) => source2 instanceof ArrayBuffer ? new Uint8Array(source2) : source2);\n // Get length of all inputs\n const byteLength = sourceArrays.reduce((length, typedArray) => length + typedArray.byteLength, 0);\n // Allocate array with space for all inputs\n const result = new Uint8Array(byteLength);\n // Copy the subarrays\n let offset = 0;\n for (const sourceArray of sourceArrays) {\n result.set(sourceArray, offset);\n offset += sourceArray.byteLength;\n }\n // We work with ArrayBuffers, discard the typed array wrapper\n return result.buffer;\n}\n/**\n * Concatenate arbitrary count of typed arrays\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays\n * @param - list of arrays. All arrays should be the same type\n * @return A concatenated TypedArray\n */\nexport function concatenateTypedArrays(...typedArrays) {\n // @ts-ignore\n const arrays = typedArrays;\n // @ts-ignore\n const TypedArrayConstructor = (arrays && arrays.length > 1 && arrays[0].constructor) || null;\n if (!TypedArrayConstructor) {\n throw new Error('\"concatenateTypedArrays\" - incorrect quantity of arguments or arguments have incompatible data types');\n }\n const sumLength = arrays.reduce((acc, value) => acc + value.length, 0);\n // @ts-ignore typescript does not like dynamic constructors\n const result = new TypedArrayConstructor(sumLength);\n let offset = 0;\n for (const array of arrays) {\n result.set(array, offset);\n offset += array.length;\n }\n return result;\n}\n/**\n * Copy a view of an ArrayBuffer into new ArrayBuffer with byteOffset = 0\n * @param arrayBuffer\n * @param byteOffset\n * @param byteLength\n */\nexport function sliceArrayBuffer(arrayBuffer, byteOffset, byteLength) {\n const subArray = byteLength !== undefined\n ? new Uint8Array(arrayBuffer).subarray(byteOffset, byteOffset + byteLength)\n : new Uint8Array(arrayBuffer).subarray(byteOffset);\n const arrayCopy = new Uint8Array(subArray);\n return arrayCopy.buffer;\n}\n", "import { assert } from \"../env-utils/assert.js\";\n/**\n * Calculate new size of an arrayBuffer to be aligned to an n-byte boundary\n * This function increases `byteLength` by the minimum delta,\n * allowing the total length to be divided by `padding`\n * @param byteLength\n * @param padding\n */\nexport function padToNBytes(byteLength, padding) {\n assert(byteLength >= 0); // `Incorrect 'byteLength' value: ${byteLength}`\n assert(padding > 0); // `Incorrect 'padding' value: ${padding}`\n return (byteLength + (padding - 1)) & ~(padding - 1);\n}\n/**\n * Creates a new Uint8Array based on two different ArrayBuffers\n * @param targetBuffer The first buffer.\n * @param sourceBuffer The second buffer.\n * @return The new ArrayBuffer created out of the two.\n */\nexport function copyArrayBuffer(targetBuffer, sourceBuffer, byteOffset, byteLength = sourceBuffer.byteLength) {\n const targetArray = new Uint8Array(targetBuffer, byteOffset, byteLength);\n const sourceArray = new Uint8Array(sourceBuffer);\n targetArray.set(sourceArray);\n return targetBuffer;\n}\n/**\n * Copy from source to target at the targetOffset\n *\n * @param source - The data to copy\n * @param target - The destination to copy data into\n * @param targetOffset - The start offset into target to place the copied data\n * @returns the new offset taking into account proper padding\n */\nexport function copyToArray(source, target, targetOffset) {\n let sourceArray;\n if (source instanceof ArrayBuffer) {\n sourceArray = new Uint8Array(source);\n }\n else {\n // Pack buffer onto the big target array\n //\n // 'source.data.buffer' could be a view onto a larger buffer.\n // We MUST use this constructor to ensure the byteOffset and byteLength is\n // set to correct values from 'source.data' and not the underlying\n // buffer for target.set() to work properly.\n const srcByteOffset = source.byteOffset;\n const srcByteLength = source.byteLength;\n // In gltf parser it is set as \"arrayBuffer\" instead of \"buffer\"\n // https://github.com/visgl/loaders.gl/blob/1e3a82a0a65d7b6a67b1e60633453e5edda2960a/modules/gltf/src/lib/parse-gltf.js#L85\n sourceArray = new Uint8Array(source.buffer || source.arrayBuffer, srcByteOffset, srcByteLength);\n }\n // Pack buffer onto the big target array\n target.set(sourceArray, targetOffset);\n return targetOffset + padToNBytes(sourceArray.byteLength, 4);\n}\n", "// loaders./gl, MIT license\nimport { padToNBytes } from \"./memory-copy-utils.js\";\n/**\n * Helper function that pads a string with spaces to fit a certain byte alignment\n * @param string\n * @param byteAlignment\n * @returns\n *\n * @todo PERFORMANCE IDEA: No need to copy string twice...\n */\nexport function padStringToByteAlignment(string, byteAlignment) {\n const length = string.length;\n const paddedLength = Math.ceil(length / byteAlignment) * byteAlignment; // Round up to the required alignment\n const padding = paddedLength - length;\n let whitespace = '';\n for (let i = 0; i < padding; ++i) {\n whitespace += ' ';\n }\n return string + whitespace;\n}\n/**\n *\n * @param dataView\n * @param byteOffset\n * @param string\n * @param byteLength\n * @returns\n */\nexport function copyStringToDataView(dataView, byteOffset, string, byteLength) {\n if (dataView) {\n for (let i = 0; i < byteLength; i++) {\n dataView.setUint8(byteOffset + i, string.charCodeAt(i));\n }\n }\n return byteOffset + byteLength;\n}\nexport function copyBinaryToDataView(dataView, byteOffset, binary, byteLength) {\n if (dataView) {\n for (let i = 0; i < byteLength; i++) {\n dataView.setUint8(byteOffset + i, binary[i]);\n }\n }\n return byteOffset + byteLength;\n}\n/**\n * Copy sourceBuffer to dataView with some padding\n *\n * @param dataView - destination data container. If null - only new offset is calculated\n * @param byteOffset - destination byte offset to copy to\n * @param sourceBuffer - source data buffer\n * @param padding - pad the resulting array to multiple of \"padding\" bytes. Additional bytes are filled with 0x20 (ASCII space)\n *\n * @return new byteOffset of resulting dataView\n */\nexport function copyPaddedArrayBufferToDataView(dataView, byteOffset, sourceBuffer, padding) {\n const paddedLength = padToNBytes(sourceBuffer.byteLength, padding);\n const padLength = paddedLength - sourceBuffer.byteLength;\n if (dataView) {\n // Copy array\n const targetArray = new Uint8Array(dataView.buffer, dataView.byteOffset + byteOffset, sourceBuffer.byteLength);\n const sourceArray = new Uint8Array(sourceBuffer);\n targetArray.set(sourceArray);\n // Add PADDING\n for (let i = 0; i < padLength; ++i) {\n // json chunk is padded with spaces (ASCII 0x20)\n dataView.setUint8(byteOffset + sourceBuffer.byteLength + i, 0x20);\n }\n }\n byteOffset += paddedLength;\n return byteOffset;\n}\n/**\n * Copy string to dataView with some padding\n *\n * @param {DataView | null} dataView - destination data container. If null - only new offset is calculated\n * @param {number} byteOffset - destination byte offset to copy to\n * @param {string} string - source string\n * @param {number} padding - pad the resulting array to multiple of \"padding\" bytes. Additional bytes are filled with 0x20 (ASCII space)\n *\n * @return new byteOffset of resulting dataView\n */\nexport function copyPaddedStringToDataView(dataView, byteOffset, string, padding) {\n const textEncoder = new TextEncoder();\n // PERFORMANCE IDEA: We encode twice, once to get size and once to store\n // PERFORMANCE IDEA: Use TextEncoder.encodeInto() to avoid temporary copy\n const stringBuffer = textEncoder.encode(string);\n byteOffset = copyPaddedArrayBufferToDataView(dataView, byteOffset, stringBuffer, padding);\n return byteOffset;\n}\n", "// TextDecoder iterators\n// TextDecoder will keep any partial undecoded bytes between calls to `decode`\nexport async function* makeTextDecoderIterator(arrayBufferIterator, options = {}) {\n const textDecoder = new TextDecoder(undefined, options);\n for await (const arrayBuffer of arrayBufferIterator) {\n yield typeof arrayBuffer === 'string'\n ? arrayBuffer\n : textDecoder.decode(arrayBuffer, { stream: true });\n }\n}\n// TextEncoder iterator\n// TODO - this is not useful unless min chunk size is given\n// TextEncoder will keep any partial undecoded bytes between calls to `encode`\n// If iterator does not yield strings, assume arrayBuffer and return unencoded\nexport async function* makeTextEncoderIterator(textIterator) {\n const textEncoder = new TextEncoder();\n for await (const text of textIterator) {\n yield typeof text === 'string' ? textEncoder.encode(text) : text;\n }\n}\n/**\n * @param textIterator async iterable yielding strings\n * @returns an async iterable over lines\n * See http://2ality.com/2018/04/async-iter-nodejs.html\n */\nexport async function* makeLineIterator(textIterator) {\n let previous = '';\n for await (const textChunk of textIterator) {\n previous += textChunk;\n let eolIndex;\n while ((eolIndex = previous.indexOf('\\n')) >= 0) {\n // line includes the EOL\n const line = previous.slice(0, eolIndex + 1);\n previous = previous.slice(eolIndex + 1);\n yield line;\n }\n }\n if (previous.length > 0) {\n yield previous;\n }\n}\n/**\n * @param lineIterator async iterable yielding lines\n * @returns async iterable yielding numbered lines\n *\n * See http://2ality.com/2018/04/async-iter-nodejs.html\n */\nexport async function* makeNumberedLineIterator(lineIterator) {\n let counter = 1;\n for await (const line of lineIterator) {\n yield { counter, line };\n counter++;\n }\n}\n", "import { concatenateArrayBuffers } from \"../binary-utils/array-buffer-utils.js\";\n// GENERAL UTILITIES\n/**\n * Iterate over async iterator, without resetting iterator if end is not reached\n * - forEach intentionally does not reset iterator if exiting loop prematurely\n * so that iteration can continue in a second loop\n * - It is recommended to use a standard for-await as last loop to ensure\n * iterator gets properly reset\n *\n * TODO - optimize using sync iteration if argument is an Iterable?\n *\n * @param iterator\n * @param visitor\n */\nexport async function forEach(iterator, visitor) {\n // eslint-disable-next-line\n while (true) {\n const { done, value } = await iterator.next();\n if (done) {\n iterator.return();\n return;\n }\n const cancel = visitor(value);\n if (cancel) {\n return;\n }\n }\n}\n// Breaking big data into iterable chunks, concatenating iterable chunks into big data objects\n/**\n * Concatenates all data chunks yielded by an (async) iterator\n * This function can e.g. be used to enable atomic parsers to work on (async) iterator inputs\n */\nexport async function concatenateArrayBuffersAsync(asyncIterator) {\n const arrayBuffers = [];\n for await (const chunk of asyncIterator) {\n arrayBuffers.push(chunk);\n }\n return concatenateArrayBuffers(...arrayBuffers);\n}\nexport async function concatenateStringsAsync(asyncIterator) {\n const strings = [];\n for await (const chunk of asyncIterator) {\n strings.push(chunk);\n }\n return strings.join('');\n}\n", "import { Stats } from '@probe.gl/stats';\nconst STAT_QUEUED_REQUESTS = 'Queued Requests';\nconst STAT_ACTIVE_REQUESTS = 'Active Requests';\nconst STAT_CANCELLED_REQUESTS = 'Cancelled Requests';\nconst STAT_QUEUED_REQUESTS_EVER = 'Queued Requests Ever';\nconst STAT_ACTIVE_REQUESTS_EVER = 'Active Requests Ever';\nconst DEFAULT_PROPS = {\n id: 'request-scheduler',\n /** Specifies if the request scheduler should throttle incoming requests, mainly for comparative testing. */\n throttleRequests: true,\n /** The maximum number of simultaneous active requests. Un-throttled requests do not observe this limit. */\n maxRequests: 6,\n /**\n * Specifies a debounce time, in milliseconds. All requests are queued, until no new requests have\n * been added to the queue for this amount of time.\n */\n debounceTime: 0\n};\n/**\n * Used to issue a request, without having them \"deeply queued\" by the browser.\n * @todo - Track requests globally, across multiple servers\n */\nexport default class RequestScheduler {\n props;\n stats;\n activeRequestCount = 0;\n /** Tracks the number of active requests and prioritizes/cancels queued requests. */\n requestQueue = [];\n requestMap = new Map();\n updateTimer = null;\n constructor(props = {}) {\n this.props = { ...DEFAULT_PROPS, ...props };\n // Returns the statistics used by the request scheduler.\n this.stats = new Stats({ id: this.props.id });\n this.stats.get(STAT_QUEUED_REQUESTS);\n this.stats.get(STAT_ACTIVE_REQUESTS);\n this.stats.get(STAT_CANCELLED_REQUESTS);\n this.stats.get(STAT_QUEUED_REQUESTS_EVER);\n this.stats.get(STAT_ACTIVE_REQUESTS_EVER);\n }\n /**\n * Called by an application that wants to issue a request, without having it deeply queued by the browser\n *\n * When the returned promise resolved, it is OK for the application to issue a request.\n * The promise resolves to an object that contains a `done` method.\n * When the application's request has completed (or failed), the application must call the `done` function\n *\n * @param handle\n * @param getPriority will be called when request \"slots\" open up,\n * allowing the caller to update priority or cancel the request\n * Highest priority executes first, priority < 0 cancels the request\n * @returns a promise\n * - resolves to a object (with a `done` field) when the request can be issued without queueing,\n * - resolves to `null` if the request has been cancelled (by the callback return < 0).\n * In this case the application should not issue the request\n */\n scheduleRequest(handle, getPriority = () => 0) {\n // Allows throttling to be disabled\n if (!this.props.throttleRequests) {\n return Promise.resolve({ done: () => { } });\n }\n // dedupe\n if (this.requestMap.has(handle)) {\n return this.requestMap.get(handle);\n }\n const request = { handle, priority: 0, getPriority };\n const promise = new Promise((resolve) => {\n // @ts-ignore\n request.resolve = resolve;\n return request;\n });\n this.requestQueue.push(request);\n this.requestMap.set(handle, promise);\n this._issueNewRequests();\n return promise;\n }\n // PRIVATE\n _issueRequest(request) {\n const { handle, resolve } = request;\n let isDone = false;\n const done = () => {\n // can only be called once\n if (!isDone) {\n isDone = true;\n // Stop tracking a request - it has completed, failed, cancelled etc\n this.requestMap.delete(handle);\n this.activeRequestCount--;\n // A slot just freed up, see if any queued requests are waiting\n this._issueNewRequests();\n }\n };\n // Track this request\n this.activeRequestCount++;\n return resolve ? resolve({ done }) : Promise.resolve({ done });\n }\n /** We check requests asynchronously, to prevent multiple updates */\n _issueNewRequests() {\n if (this.updateTimer !== null) {\n clearTimeout(this.updateTimer);\n }\n this.updateTimer = setTimeout(() => this._issueNewRequestsAsync(), this.props.debounceTime);\n }\n /** Refresh all requests */\n _issueNewRequestsAsync() {\n if (this.updateTimer !== null) {\n clearTimeout(this.updateTimer);\n }\n this.updateTimer = null;\n const freeSlots = Math.max(this.props.maxRequests - this.activeRequestCount, 0);\n if (freeSlots === 0) {\n return;\n }\n this._updateAllRequests();\n // Resolve pending promises for the top-priority requests\n for (let i = 0; i < freeSlots; ++i) {\n const request = this.requestQueue.shift();\n if (request) {\n this._issueRequest(request); // eslint-disable-line @typescript-eslint/no-floating-promises\n }\n }\n // Uncomment to debug\n // console.log(`${freeSlots} free slots, ${this.requestQueue.length} queued requests`);\n }\n /** Ensure all requests have updated priorities, and that no longer valid requests are cancelled */\n _updateAllRequests() {\n const requestQueue = this.requestQueue;\n for (let i = 0; i < requestQueue.length; ++i) {\n const request = requestQueue[i];\n if (!this._updateRequest(request)) {\n // Remove the element and make sure to adjust the counter to account for shortened array\n requestQueue.splice(i, 1);\n this.requestMap.delete(request.handle);\n i--;\n }\n }\n // Sort the remaining requests based on priority\n requestQueue.sort((a, b) => a.priority - b.priority);\n }\n /** Update a single request by calling the callback */\n _updateRequest(request) {\n request.priority = request.getPriority(request.handle); // eslint-disable-line callback-return\n // by returning a negative priority, the callback cancels the request\n if (request.priority < 0) {\n request.resolve(null);\n return false;\n }\n return true;\n }\n}\n", "// Simple file alias mechanisms for tests.\nlet pathPrefix = '';\nconst fileAliases = {};\n/*\n * Set a relative path prefix\n */\nexport function setPathPrefix(prefix) {\n pathPrefix = prefix;\n}\n/*\n * Get the relative path prefix\n */\nexport function getPathPrefix() {\n return pathPrefix;\n}\n/**\n *\n * @param aliases\n *\n * Note: addAliases are an experimental export, they are only for testing of loaders.gl loaders\n * not intended as a generic aliasing mechanism\n */\nexport function addAliases(aliases) {\n Object.assign(fileAliases, aliases);\n}\n/**\n * Resolves aliases and adds path-prefix to paths\n */\nexport function resolvePath(filename) {\n for (const alias in fileAliases) {\n if (filename.startsWith(alias)) {\n const replacement = fileAliases[alias];\n filename = filename.replace(alias, replacement);\n }\n }\n if (!filename.startsWith('http://') && !filename.startsWith('https://')) {\n filename = `${pathPrefix}${filename}`;\n }\n return filename;\n}\n", "// __VERSION__ is injected by babel-plugin-version-inline\n// @ts-ignore TS2304: Cannot find name '__VERSION__'.\nconst VERSION = typeof \"4.3.2\" !== 'undefined' ? \"4.3.2\" : 'latest';\n/**\n * A JSON Micro loader (minimal bundle size)\n * Alternative to `@loaders.gl/json`\n */\nexport const JSONLoader = {\n dataType: null,\n batchType: null,\n name: 'JSON',\n id: 'json',\n module: 'json',\n version: VERSION,\n extensions: ['json', 'geojson'],\n mimeTypes: ['application/json'],\n category: 'json',\n text: true,\n parseTextSync,\n parse: async (arrayBuffer) => parseTextSync(new TextDecoder().decode(arrayBuffer)),\n options: {}\n};\n// TODO - Better error handling!\nfunction parseTextSync(text) {\n return JSON.parse(text);\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Isolates Buffer references to ensure they are only bundled under Node.js (avoids big webpack polyfill)\n// this file is selected by the package.json \"browser\" field).\n/**\n * Convert Buffer to ArrayBuffer\n * Converts Node.js `Buffer` to `ArrayBuffer` (without triggering bundler to include Buffer polyfill on browser)\n * @todo better data type\n */\nexport function toArrayBuffer(buffer) {\n return buffer;\n}\n/**\n * Convert (copy) ArrayBuffer to Buffer\n */\nexport function toBuffer(binaryData) {\n throw new Error('Buffer not supported in browser');\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport * as node from \"../node/buffer.js\";\n/**\n * Check for Node.js `Buffer` (without triggering bundler to include Buffer polyfill on browser)\n */\nexport function isBuffer(value) {\n return value && typeof value === 'object' && value.isBuffer;\n}\n/**\n * Converts to Node.js `Buffer` (without triggering bundler to include Buffer polyfill on browser)\n * @todo better data type\n */\nexport function toBuffer(data) {\n return node.toBuffer ? node.toBuffer(data) : data;\n}\n/**\n * Convert an object to an array buffer\n */\nexport function toArrayBuffer(data) {\n // Note: Should be called first, Buffers can trigger other detections below\n if (isBuffer(data)) {\n return node.toArrayBuffer(data);\n }\n if (data instanceof ArrayBuffer) {\n return data;\n }\n // Careful - Node Buffers look like Uint8Arrays (keep after isBuffer)\n if (ArrayBuffer.isView(data)) {\n if (data.byteOffset === 0 && data.byteLength === data.buffer.byteLength) {\n return data.buffer;\n }\n return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);\n }\n if (typeof data === 'string') {\n const text = data;\n const uint8Array = new TextEncoder().encode(text);\n return uint8Array.buffer;\n }\n // HACK to support Blob polyfill\n if (data && typeof data === 'object' && data._toArrayBuffer) {\n return data._toArrayBuffer();\n }\n throw new Error('toArrayBuffer');\n}\n", "// @loaders.gl, MIT license\n/**\n * Typesafe promisify implementation\n * @link https://dev.to/_gdelgado/implement-a-type-safe-version-of-node-s-promisify-in-7-lines-of-code-in-typescript-2j34\n * @param fn\n * @returns\n */\nexport function promisify1(fn) {\n return (args) => new Promise((resolve, reject) => fn(args, (error, callbackArgs) => (error ? reject(error) : resolve(callbackArgs))));\n}\nexport function promisify2(fn) {\n return (arg1, arg2) => new Promise((resolve, reject) => fn(arg1, arg2, (error, callbackArgs) => (error ? reject(error) : resolve(callbackArgs))));\n}\nexport function promisify3(fn) {\n return (arg1, arg2, arg3) => new Promise((resolve, reject) => fn(arg1, arg2, arg3, (error, callbackArgs) => (error ? reject(error) : resolve(callbackArgs))));\n}\n", "// Beginning of a minimal implementation of the Node.js path API, that doesn't pull in big polyfills.\nimport { getCWD } from \"./get-cwd.js\";\n/**\n * Replacement for Node.js path.filename\n * @param url\n */\nexport function filename(url) {\n const slashIndex = url ? url.lastIndexOf('/') : -1;\n return slashIndex >= 0 ? url.substr(slashIndex + 1) : '';\n}\n/**\n * Replacement for Node.js path.dirname\n * @param url\n */\nexport function dirname(url) {\n const slashIndex = url ? url.lastIndexOf('/') : -1;\n return slashIndex >= 0 ? url.substr(0, slashIndex) : '';\n}\n/**\n * Replacement for Node.js path.join\n * @param parts\n */\nexport function join(...parts) {\n const separator = '/';\n parts = parts.map((part, index) => {\n if (index) {\n part = part.replace(new RegExp(`^${separator}`), '');\n }\n if (index !== parts.length - 1) {\n part = part.replace(new RegExp(`${separator}$`), '');\n }\n return part;\n });\n return parts.join(separator);\n}\n/* eslint-disable no-continue */\n/**\n * https://nodejs.org/api/path.html#path_path_resolve_paths\n * @param paths A sequence of paths or path segments.\n * @return resolved path\n * Forked from BTOdell/path-resolve under MIT license\n * @see https://github.com/BTOdell/path-resolve/blob/master/LICENSE\n */\nexport function resolve(...components) {\n const paths = [];\n for (let _i = 0; _i < components.length; _i++) {\n paths[_i] = components[_i];\n }\n let resolvedPath = '';\n let resolvedAbsolute = false;\n let cwd;\n for (let i = paths.length - 1; i >= -1 && !resolvedAbsolute; i--) {\n let path;\n if (i >= 0) {\n path = paths[i];\n }\n else {\n if (cwd === undefined) {\n cwd = getCWD();\n }\n path = cwd;\n }\n // Skip empty entries\n if (path.length === 0) {\n continue;\n }\n resolvedPath = `${path}/${resolvedPath}`;\n resolvedAbsolute = path.charCodeAt(0) === SLASH;\n }\n // At this point the path should be resolved to a full absolute path, but\n // handle relative paths to be safe (might happen when process.cwd() fails)\n // Normalize the path (removes leading slash)\n resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute);\n if (resolvedAbsolute) {\n return `/${resolvedPath}`;\n }\n else if (resolvedPath.length > 0) {\n return resolvedPath;\n }\n return '.';\n}\nconst SLASH = 47;\nconst DOT = 46;\n/**\n * Resolves . and .. elements in a path with directory names\n * Forked from BTOdell/path-resolve under MIT license\n * @see https://github.com/BTOdell/path-resolve/blob/master/LICENSE\n */\n/* eslint-disable max-depth */\n// eslint-disable-next-line complexity, max-statements\nfunction normalizeStringPosix(path, allowAboveRoot) {\n let res = '';\n let lastSlash = -1;\n let dots = 0;\n let code;\n let isAboveRoot = false;\n for (let i = 0; i <= path.length; ++i) {\n if (i < path.length) {\n code = path.charCodeAt(i);\n }\n else if (code === SLASH) {\n break;\n }\n else {\n code = SLASH;\n }\n if (code === SLASH) {\n if (lastSlash === i - 1 || dots === 1) {\n // NOOP\n }\n else if (lastSlash !== i - 1 && dots === 2) {\n if (res.length < 2 ||\n !isAboveRoot ||\n res.charCodeAt(res.length - 1) !== DOT ||\n res.charCodeAt(res.length - 2) !== DOT) {\n if (res.length > 2) {\n const start = res.length - 1;\n let j = start;\n for (; j >= 0; --j) {\n if (res.charCodeAt(j) === SLASH) {\n break;\n }\n }\n if (j !== start) {\n res = j === -1 ? '' : res.slice(0, j);\n lastSlash = i;\n dots = 0;\n isAboveRoot = false;\n continue;\n }\n }\n else if (res.length === 2 || res.length === 1) {\n res = '';\n lastSlash = i;\n dots = 0;\n isAboveRoot = false;\n continue;\n }\n }\n if (allowAboveRoot) {\n if (res.length > 0) {\n res += '/..';\n }\n else {\n res = '..';\n }\n isAboveRoot = true;\n }\n }\n else {\n const slice = path.slice(lastSlash + 1, i);\n if (res.length > 0) {\n res += `/${slice}`;\n }\n else {\n res = slice;\n }\n isAboveRoot = false;\n }\n lastSlash = i;\n dots = 0;\n }\n else if (code === DOT && dots !== -1) {\n ++dots;\n }\n else {\n dots = -1;\n }\n }\n return res;\n}\n", "// loaders.gl MIT license\nexport function getCWD() {\n if (typeof process !== 'undefined' && typeof process.cwd !== 'undefined') {\n return process.cwd();\n }\n const pathname = window.location?.pathname;\n return pathname?.slice(0, pathname.lastIndexOf('/') + 1) || '';\n}\n", "export const isSupported = false;\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n/**\n * BlobFile provides a \"file like interface\" to the data in a Blob or File object\n */\nexport class BlobFile {\n handle;\n size;\n bigsize;\n url;\n constructor(