@loaders.gl/json
Version:
Framework-independent loader for JSON and streaming JSON formats
4 lines • 77.9 kB
Source Map (JSON)
{
"version": 3,
"sources": ["index.js", "lib/parsers/parse-json.js", "lib/parsers/parse-json-in-batches.js", "lib/clarinet/clarinet.js", "lib/jsonpath/jsonpath.js", "lib/json-parser/json-parser.js", "lib/json-parser/streaming-json-parser.js", "json-loader.js", "lib/parsers/parse-ndjson.js", "lib/parsers/parse-ndjson-in-batches.js", "ndjson-loader.js", "lib/encoders/json-encoder.js", "json-writer.js", "geojson-loader.js", "geojson-writer.js", "lib/encoders/geojson-encoder.js", "lib/encoder-utils/encode-utils.js", "lib/encoder-utils/encode-table-row.js", "lib/encoder-utils/utf8-encoder.js"],
"sourcesContent": ["// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nexport { JSONLoader } from \"./json-loader.js\";\nexport { NDJSONLoader } from \"./ndjson-loader.js\";\nexport { JSONWriter } from \"./json-writer.js\";\nexport { GeoJSONLoader as _GeoJSONLoader, GeoJSONWorkerLoader as _GeoJSONWorkerLoader } from \"./geojson-loader.js\";\nexport { GeoJSONWriter as _GeoJSONWriter } from \"./geojson-writer.js\";\nexport { default as _JSONPath } from \"./lib/jsonpath/jsonpath.js\";\nexport { default as _ClarinetParser } from \"./lib/clarinet/clarinet.js\";\nexport { rebuildJsonObject as _rebuildJsonObject } from \"./lib/parsers/parse-json-in-batches.js\";\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { makeTableFromData } from '@loaders.gl/schema';\nexport function parseJSONSync(jsonText, options) {\n try {\n const json = JSON.parse(jsonText);\n if (options.json?.table) {\n const data = getFirstArray(json) || json;\n return makeTableFromData(data);\n }\n return json;\n }\n catch (error) {\n throw new Error('JSONLoader: failed to parse JSON');\n }\n}\nfunction getFirstArray(json) {\n if (Array.isArray(json)) {\n return json;\n }\n if (json && typeof json === 'object') {\n for (const value of Object.values(json)) {\n const array = getFirstArray(value);\n if (array) {\n return array;\n }\n }\n }\n return null;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { TableBatchBuilder } from '@loaders.gl/schema';\nimport { assert, makeTextDecoderIterator } from '@loaders.gl/loader-utils';\nimport StreamingJSONParser from \"../json-parser/streaming-json-parser.js\";\nimport JSONPath from \"../jsonpath/jsonpath.js\";\n// TODO - support batch size 0 = no batching/single batch?\n// eslint-disable-next-line max-statements, complexity\nexport async function* parseJSONInBatches(binaryAsyncIterator, options) {\n const asyncIterator = makeTextDecoderIterator(binaryAsyncIterator);\n const { metadata } = options;\n const { jsonpaths } = options.json || {};\n let isFirstChunk = true;\n // @ts-expect-error TODO fix Schema deduction\n const schema = null;\n const tableBatchBuilder = new TableBatchBuilder(schema, options);\n const parser = new StreamingJSONParser({ jsonpaths });\n for await (const chunk of asyncIterator) {\n const rows = parser.write(chunk);\n const jsonpath = rows.length > 0 && parser.getStreamingJsonPathAsString();\n if (rows.length > 0 && isFirstChunk) {\n if (metadata) {\n const initialBatch = {\n // Common fields\n shape: options?.json?.shape || 'array-row-table',\n batchType: 'partial-result',\n data: [],\n length: 0,\n bytesUsed: 0,\n // JSON additions\n container: parser.getPartialResult(),\n jsonpath\n };\n yield initialBatch;\n }\n isFirstChunk = false;\n // schema = deduceSchema(rows);\n }\n // Add the row\n for (const row of rows) {\n tableBatchBuilder.addRow(row);\n // If a batch has been completed, emit it\n const batch = tableBatchBuilder.getFullBatch({ jsonpath });\n if (batch) {\n yield batch;\n }\n }\n tableBatchBuilder.chunkComplete(chunk);\n const batch = tableBatchBuilder.getFullBatch({ jsonpath });\n if (batch) {\n yield batch;\n }\n }\n // yield final batch\n const jsonpath = parser.getStreamingJsonPathAsString();\n const batch = tableBatchBuilder.getFinalBatch({ jsonpath });\n if (batch) {\n yield batch;\n }\n if (metadata) {\n const finalBatch = {\n shape: 'json',\n batchType: 'final-result',\n container: parser.getPartialResult(),\n jsonpath: parser.getStreamingJsonPathAsString(),\n /** Data Just to avoid crashing? */\n data: [],\n length: 0\n // schema: null\n };\n yield finalBatch;\n }\n}\nexport function rebuildJsonObject(batch, data) {\n // Last batch will have this special type and will provide all the root object of the parsed file\n assert(batch.batchType === 'final-result');\n // The streamed JSON data is a top level array (jsonpath = '$'), just return the array of row objects\n if (batch.jsonpath === '$') {\n return data;\n }\n // (jsonpath !== '$') The streamed data is not a top level array, so stitch it back in to the top-level object\n if (batch.jsonpath && batch.jsonpath.length > 1) {\n const topLevelObject = batch.container;\n const streamingPath = new JSONPath(batch.jsonpath);\n streamingPath.setFieldAtPath(topLevelObject, data);\n return topLevelObject;\n }\n // No jsonpath, in this case nothing was streamed.\n return batch.container;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT AND BSD\n// Copyright (c) vis.gl contributors\n// This is a fork of the clarinet library, originally BSD license (see LICENSE file)\n// loaders.gl changes:\n// - typescript port\n// Removes the MAX_BUFFER_LENGTH, originally set to 64 * 1024\nconst MAX_BUFFER_LENGTH = Number.MAX_SAFE_INTEGER;\n// const DEBUG = false;\nvar STATE;\n(function (STATE) {\n STATE[STATE[\"BEGIN\"] = 0] = \"BEGIN\";\n STATE[STATE[\"VALUE\"] = 1] = \"VALUE\";\n STATE[STATE[\"OPEN_OBJECT\"] = 2] = \"OPEN_OBJECT\";\n STATE[STATE[\"CLOSE_OBJECT\"] = 3] = \"CLOSE_OBJECT\";\n STATE[STATE[\"OPEN_ARRAY\"] = 4] = \"OPEN_ARRAY\";\n STATE[STATE[\"CLOSE_ARRAY\"] = 5] = \"CLOSE_ARRAY\";\n STATE[STATE[\"TEXT_ESCAPE\"] = 6] = \"TEXT_ESCAPE\";\n STATE[STATE[\"STRING\"] = 7] = \"STRING\";\n STATE[STATE[\"BACKSLASH\"] = 8] = \"BACKSLASH\";\n STATE[STATE[\"END\"] = 9] = \"END\";\n STATE[STATE[\"OPEN_KEY\"] = 10] = \"OPEN_KEY\";\n STATE[STATE[\"CLOSE_KEY\"] = 11] = \"CLOSE_KEY\";\n STATE[STATE[\"TRUE\"] = 12] = \"TRUE\";\n STATE[STATE[\"TRUE2\"] = 13] = \"TRUE2\";\n STATE[STATE[\"TRUE3\"] = 14] = \"TRUE3\";\n STATE[STATE[\"FALSE\"] = 15] = \"FALSE\";\n STATE[STATE[\"FALSE2\"] = 16] = \"FALSE2\";\n STATE[STATE[\"FALSE3\"] = 17] = \"FALSE3\";\n STATE[STATE[\"FALSE4\"] = 18] = \"FALSE4\";\n STATE[STATE[\"NULL\"] = 19] = \"NULL\";\n STATE[STATE[\"NULL2\"] = 20] = \"NULL2\";\n STATE[STATE[\"NULL3\"] = 21] = \"NULL3\";\n STATE[STATE[\"NUMBER_DECIMAL_POINT\"] = 22] = \"NUMBER_DECIMAL_POINT\";\n STATE[STATE[\"NUMBER_DIGIT\"] = 23] = \"NUMBER_DIGIT\"; // [0-9]\n})(STATE || (STATE = {}));\nconst Char = {\n tab: 0x09, // \\t\n lineFeed: 0x0a, // \\n\n carriageReturn: 0x0d, // \\r\n space: 0x20, // \" \"\n doubleQuote: 0x22, // \"\n plus: 0x2b, // +\n comma: 0x2c, // ,\n minus: 0x2d, // -\n period: 0x2e, // .\n _0: 0x30, // 0\n _9: 0x39, // 9\n colon: 0x3a, // :\n E: 0x45, // E\n openBracket: 0x5b, // [\n backslash: 0x5c, // \\\n closeBracket: 0x5d, // ]\n a: 0x61, // a\n b: 0x62, // b\n e: 0x65, // e\n f: 0x66, // f\n l: 0x6c, // l\n n: 0x6e, // n\n r: 0x72, // r\n s: 0x73, // s\n t: 0x74, // t\n u: 0x75, // u\n openBrace: 0x7b, // {\n closeBrace: 0x7d // }\n};\nconst stringTokenPattern = /[\\\\\"\\n]/g;\nconst DEFAULT_OPTIONS = {\n onready: () => { },\n onopenobject: () => { },\n onkey: () => { },\n oncloseobject: () => { },\n onopenarray: () => { },\n onclosearray: () => { },\n onvalue: () => { },\n onerror: () => { },\n onend: () => { },\n onchunkparsed: () => { }\n};\nexport default class ClarinetParser {\n options = DEFAULT_OPTIONS;\n bufferCheckPosition = MAX_BUFFER_LENGTH;\n q = '';\n c = '';\n p = '';\n closed = false;\n closedRoot = false;\n sawRoot = false;\n // tag = null;\n error = null;\n state = STATE.BEGIN;\n stack = [];\n // mostly just for error reporting\n position = 0;\n column = 0;\n line = 1;\n slashed = false;\n unicodeI = 0;\n unicodeS = null;\n depth = 0;\n textNode;\n numberNode;\n constructor(options = {}) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.textNode = undefined;\n this.numberNode = '';\n this.emit('onready');\n }\n end() {\n if (this.state !== STATE.VALUE || this.depth !== 0)\n this._error('Unexpected end');\n this._closeValue();\n this.c = '';\n this.closed = true;\n this.emit('onend');\n return this;\n }\n resume() {\n this.error = null;\n return this;\n }\n close() {\n return this.write(null);\n }\n // protected\n emit(event, data) {\n // if (DEBUG) console.log('-- emit', event, data);\n this.options[event]?.(data, this);\n }\n emitNode(event, data) {\n this._closeValue();\n this.emit(event, data);\n }\n /* eslint-disable no-continue */\n // eslint-disable-next-line complexity, max-statements\n write(chunk) {\n if (this.error) {\n throw this.error;\n }\n if (this.closed) {\n return this._error('Cannot write after close. Assign an onready handler.');\n }\n if (chunk === null) {\n return this.end();\n }\n let i = 0;\n let c = chunk.charCodeAt(0);\n let p = this.p;\n // if (DEBUG) console.log(`write -> [${ chunk }]`);\n while (c) {\n p = c;\n this.c = c = chunk.charCodeAt(i++);\n // if chunk doesnt have next, like streaming char by char\n // this way we need to check if previous is really previous\n // if not we need to reset to what the this says is the previous\n // from buffer\n if (p !== c) {\n this.p = p;\n }\n else {\n p = this.p;\n }\n if (!c)\n break;\n // if (DEBUG) console.log(i, c, STATE[this.state]);\n this.position++;\n if (c === Char.lineFeed) {\n this.line++;\n this.column = 0;\n }\n else\n this.column++;\n switch (this.state) {\n case STATE.BEGIN:\n if (c === Char.openBrace)\n this.state = STATE.OPEN_OBJECT;\n else if (c === Char.openBracket)\n this.state = STATE.OPEN_ARRAY;\n else if (!isWhitespace(c)) {\n this._error('Non-whitespace before {[.');\n }\n continue;\n case STATE.OPEN_KEY:\n case STATE.OPEN_OBJECT:\n if (isWhitespace(c))\n continue;\n if (this.state === STATE.OPEN_KEY)\n this.stack.push(STATE.CLOSE_KEY);\n else if (c === Char.closeBrace) {\n this.emit('onopenobject');\n this.depth++;\n this.emit('oncloseobject');\n this.depth--;\n this.state = this.stack.pop() || STATE.VALUE;\n continue;\n }\n else\n this.stack.push(STATE.CLOSE_OBJECT);\n if (c === Char.doubleQuote)\n this.state = STATE.STRING;\n else\n this._error('Malformed object key should start with \"');\n continue;\n case STATE.CLOSE_KEY:\n case STATE.CLOSE_OBJECT:\n if (isWhitespace(c))\n continue;\n // let event = this.state === STATE.CLOSE_KEY ? 'key' : 'object';\n if (c === Char.colon) {\n if (this.state === STATE.CLOSE_OBJECT) {\n this.stack.push(STATE.CLOSE_OBJECT);\n this._closeValue('onopenobject');\n this.depth++;\n }\n else\n this._closeValue('onkey');\n this.state = STATE.VALUE;\n }\n else if (c === Char.closeBrace) {\n this.emitNode('oncloseobject');\n this.depth--;\n this.state = this.stack.pop() || STATE.VALUE;\n }\n else if (c === Char.comma) {\n if (this.state === STATE.CLOSE_OBJECT)\n this.stack.push(STATE.CLOSE_OBJECT);\n this._closeValue();\n this.state = STATE.OPEN_KEY;\n }\n else\n this._error('Bad object');\n continue;\n case STATE.OPEN_ARRAY: // after an array there always a value\n case STATE.VALUE:\n if (isWhitespace(c))\n continue;\n if (this.state === STATE.OPEN_ARRAY) {\n this.emit('onopenarray');\n this.depth++;\n this.state = STATE.VALUE;\n if (c === Char.closeBracket) {\n this.emit('onclosearray');\n this.depth--;\n this.state = this.stack.pop() || STATE.VALUE;\n continue;\n }\n else {\n this.stack.push(STATE.CLOSE_ARRAY);\n }\n }\n if (c === Char.doubleQuote)\n this.state = STATE.STRING;\n else if (c === Char.openBrace)\n this.state = STATE.OPEN_OBJECT;\n else if (c === Char.openBracket)\n this.state = STATE.OPEN_ARRAY;\n else if (c === Char.t)\n this.state = STATE.TRUE;\n else if (c === Char.f)\n this.state = STATE.FALSE;\n else if (c === Char.n)\n this.state = STATE.NULL;\n else if (c === Char.minus) {\n // keep and continue\n this.numberNode += '-';\n }\n else if (Char._0 <= c && c <= Char._9) {\n this.numberNode += String.fromCharCode(c);\n this.state = STATE.NUMBER_DIGIT;\n }\n else\n this._error('Bad value');\n continue;\n case STATE.CLOSE_ARRAY:\n if (c === Char.comma) {\n this.stack.push(STATE.CLOSE_ARRAY);\n this._closeValue('onvalue');\n this.state = STATE.VALUE;\n }\n else if (c === Char.closeBracket) {\n this.emitNode('onclosearray');\n this.depth--;\n this.state = this.stack.pop() || STATE.VALUE;\n }\n else if (isWhitespace(c))\n continue;\n else\n this._error('Bad array');\n continue;\n case STATE.STRING:\n if (this.textNode === undefined) {\n this.textNode = '';\n }\n // thanks thejh, this is an about 50% performance improvement.\n let starti = i - 1;\n let slashed = this.slashed;\n let unicodeI = this.unicodeI;\n // eslint-disable-next-line no-constant-condition, no-labels\n STRING_BIGLOOP: while (true) {\n // if (DEBUG) console.log(i, c, STATE[this.state], slashed);\n // zero means \"no unicode active\". 1-4 mean \"parse some more\". end after 4.\n while (unicodeI > 0) {\n this.unicodeS += String.fromCharCode(c);\n c = chunk.charCodeAt(i++);\n this.position++;\n if (unicodeI === 4) {\n // TODO this might be slow? well, probably not used too often anyway\n this.textNode += String.fromCharCode(parseInt(this.unicodeS, 16));\n unicodeI = 0;\n starti = i - 1;\n }\n else {\n unicodeI++;\n }\n // we can just break here: no stuff we skipped that still has to be sliced out or so\n // eslint-disable-next-line no-labels\n if (!c)\n break STRING_BIGLOOP;\n }\n if (c === Char.doubleQuote && !slashed) {\n this.state = this.stack.pop() || STATE.VALUE;\n this.textNode += chunk.substring(starti, i - 1);\n this.position += i - 1 - starti;\n break;\n }\n if (c === Char.backslash && !slashed) {\n slashed = true;\n this.textNode += chunk.substring(starti, i - 1);\n this.position += i - 1 - starti;\n c = chunk.charCodeAt(i++);\n this.position++;\n if (!c)\n break;\n }\n if (slashed) {\n slashed = false;\n if (c === Char.n) {\n this.textNode += '\\n';\n }\n else if (c === Char.r) {\n this.textNode += '\\r';\n }\n else if (c === Char.t) {\n this.textNode += '\\t';\n }\n else if (c === Char.f) {\n this.textNode += '\\f';\n }\n else if (c === Char.b) {\n this.textNode += '\\b';\n }\n else if (c === Char.u) {\n // \\uxxxx. meh!\n unicodeI = 1;\n this.unicodeS = '';\n }\n else {\n this.textNode += String.fromCharCode(c);\n }\n c = chunk.charCodeAt(i++);\n this.position++;\n starti = i - 1;\n if (!c)\n break;\n else\n continue;\n }\n stringTokenPattern.lastIndex = i;\n const reResult = stringTokenPattern.exec(chunk);\n if (reResult === null) {\n i = chunk.length + 1;\n this.textNode += chunk.substring(starti, i - 1);\n this.position += i - 1 - starti;\n break;\n }\n i = reResult.index + 1;\n c = chunk.charCodeAt(reResult.index);\n if (!c) {\n this.textNode += chunk.substring(starti, i - 1);\n this.position += i - 1 - starti;\n break;\n }\n }\n this.slashed = slashed;\n this.unicodeI = unicodeI;\n continue;\n case STATE.TRUE:\n if (c === Char.r)\n this.state = STATE.TRUE2;\n else\n this._error(`Invalid true started with t${c}`);\n continue;\n case STATE.TRUE2:\n if (c === Char.u)\n this.state = STATE.TRUE3;\n else\n this._error(`Invalid true started with tr${c}`);\n continue;\n case STATE.TRUE3:\n if (c === Char.e) {\n this.emit('onvalue', true);\n this.state = this.stack.pop() || STATE.VALUE;\n }\n else\n this._error(`Invalid true started with tru${c}`);\n continue;\n case STATE.FALSE:\n if (c === Char.a)\n this.state = STATE.FALSE2;\n else\n this._error(`Invalid false started with f${c}`);\n continue;\n case STATE.FALSE2:\n if (c === Char.l)\n this.state = STATE.FALSE3;\n else\n this._error(`Invalid false started with fa${c}`);\n continue;\n case STATE.FALSE3:\n if (c === Char.s)\n this.state = STATE.FALSE4;\n else\n this._error(`Invalid false started with fal${c}`);\n continue;\n case STATE.FALSE4:\n if (c === Char.e) {\n this.emit('onvalue', false);\n this.state = this.stack.pop() || STATE.VALUE;\n }\n else\n this._error(`Invalid false started with fals${c}`);\n continue;\n case STATE.NULL:\n if (c === Char.u)\n this.state = STATE.NULL2;\n else\n this._error(`Invalid null started with n${c}`);\n continue;\n case STATE.NULL2:\n if (c === Char.l)\n this.state = STATE.NULL3;\n else\n this._error(`Invalid null started with nu${c}`);\n continue;\n case STATE.NULL3:\n if (c === Char.l) {\n this.emit('onvalue', null);\n this.state = this.stack.pop() || STATE.VALUE;\n }\n else\n this._error(`Invalid null started with nul${c}`);\n continue;\n case STATE.NUMBER_DECIMAL_POINT:\n if (c === Char.period) {\n this.numberNode += '.';\n this.state = STATE.NUMBER_DIGIT;\n }\n else\n this._error('Leading zero not followed by .');\n continue;\n case STATE.NUMBER_DIGIT:\n if (Char._0 <= c && c <= Char._9)\n this.numberNode += String.fromCharCode(c);\n else if (c === Char.period) {\n if (this.numberNode.indexOf('.') !== -1)\n this._error('Invalid number has two dots');\n this.numberNode += '.';\n }\n else if (c === Char.e || c === Char.E) {\n if (this.numberNode.indexOf('e') !== -1 || this.numberNode.indexOf('E') !== -1)\n this._error('Invalid number has two exponential');\n this.numberNode += 'e';\n }\n else if (c === Char.plus || c === Char.minus) {\n // @ts-expect-error\n if (!(p === Char.e || p === Char.E))\n this._error('Invalid symbol in number');\n this.numberNode += String.fromCharCode(c);\n }\n else {\n this._closeNumber();\n i--; // go back one\n this.state = this.stack.pop() || STATE.VALUE;\n }\n continue;\n default:\n this._error(`Unknown state: ${this.state}`);\n }\n }\n if (this.position >= this.bufferCheckPosition) {\n checkBufferLength(this);\n }\n this.emit('onchunkparsed');\n return this;\n }\n _closeValue(event = 'onvalue') {\n if (this.textNode !== undefined) {\n this.emit(event, this.textNode);\n }\n this.textNode = undefined;\n }\n _closeNumber() {\n if (this.numberNode)\n this.emit('onvalue', parseFloat(this.numberNode));\n this.numberNode = '';\n }\n _error(message = '') {\n this._closeValue();\n message += `\\nLine: ${this.line}\\nColumn: ${this.column}\\nChar: ${this.c}`;\n const error = new Error(message);\n this.error = error;\n this.emit('onerror', error);\n }\n}\nfunction isWhitespace(c) {\n return c === Char.carriageReturn || c === Char.lineFeed || c === Char.space || c === Char.tab;\n}\nfunction checkBufferLength(parser) {\n const maxAllowed = Math.max(MAX_BUFFER_LENGTH, 10);\n let maxActual = 0;\n for (const buffer of ['textNode', 'numberNode']) {\n const len = parser[buffer] === undefined ? 0 : parser[buffer].length;\n if (len > maxAllowed) {\n switch (buffer) {\n case 'text':\n // TODO - should this be closeValue?\n // closeText(parser);\n break;\n default:\n parser._error(`Max buffer length exceeded: ${buffer}`);\n }\n }\n maxActual = Math.max(maxActual, len);\n }\n parser.bufferCheckPosition = MAX_BUFFER_LENGTH - maxActual + parser.position;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n/**\n * A parser for a minimal subset of the jsonpath standard\n * Full JSON path parsers for JS exist but are quite large (bundle size)\n *\n * Supports\n *\n * `$.component.component.component`\n */\nexport default class JSONPath {\n path;\n constructor(path = null) {\n this.path = ['$'];\n if (path instanceof JSONPath) {\n // @ts-ignore\n this.path = [...path.path];\n return;\n }\n if (Array.isArray(path)) {\n this.path.push(...path);\n return;\n }\n // Parse a string as a JSONPath\n if (typeof path === 'string') {\n this.path = path.split('.');\n if (this.path[0] !== '$') {\n throw new Error('JSONPaths must start with $');\n }\n }\n }\n clone() {\n return new JSONPath(this);\n }\n toString() {\n return this.path.join('.');\n }\n push(name) {\n this.path.push(name);\n }\n pop() {\n return this.path.pop();\n }\n set(name) {\n this.path[this.path.length - 1] = name;\n }\n equals(other) {\n if (!this || !other || this.path.length !== other.path.length) {\n return false;\n }\n for (let i = 0; i < this.path.length; ++i) {\n if (this.path[i] !== other.path[i]) {\n return false;\n }\n }\n return true;\n }\n /**\n * Sets the value pointed at by path\n * TODO - handle root path\n * @param object\n * @param value\n */\n setFieldAtPath(object, value) {\n const path = [...this.path];\n path.shift();\n const field = path.pop();\n for (const component of path) {\n object = object[component];\n }\n // @ts-ignore\n object[field] = value;\n }\n /**\n * Gets the value pointed at by path\n * TODO - handle root path\n * @param object\n */\n getFieldAtPath(object) {\n const path = [...this.path];\n path.shift();\n const field = path.pop();\n for (const component of path) {\n object = object[component];\n }\n // @ts-ignore\n return object[field];\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// @ts-nocheck\nimport ClarinetParser from \"../clarinet/clarinet.js\";\nimport JSONPath from \"../jsonpath/jsonpath.js\";\n// JSONParser builds a JSON object using the events emitted by the Clarinet parser\nexport default class JSONParser {\n parser;\n result = undefined;\n previousStates = [];\n currentState = Object.freeze({ container: [], key: null });\n jsonpath = new JSONPath();\n constructor(options) {\n this.reset();\n this.parser = new ClarinetParser({\n onready: () => {\n this.jsonpath = new JSONPath();\n this.previousStates.length = 0;\n this.currentState.container.length = 0;\n },\n onopenobject: (name) => {\n this._openObject({});\n if (typeof name !== 'undefined') {\n this.parser.emit('onkey', name);\n }\n },\n onkey: (name) => {\n this.jsonpath.set(name);\n this.currentState.key = name;\n },\n oncloseobject: () => {\n this._closeObject();\n },\n onopenarray: () => {\n this._openArray();\n },\n onclosearray: () => {\n this._closeArray();\n },\n onvalue: (value) => {\n this._pushOrSet(value);\n },\n onerror: (error) => {\n throw error;\n },\n onend: () => {\n this.result = this.currentState.container.pop();\n },\n ...options\n });\n }\n reset() {\n this.result = undefined;\n this.previousStates = [];\n this.currentState = Object.freeze({ container: [], key: null });\n this.jsonpath = new JSONPath();\n }\n write(chunk) {\n this.parser.write(chunk);\n }\n close() {\n this.parser.close();\n }\n // PRIVATE METHODS\n _pushOrSet(value) {\n const { container, key } = this.currentState;\n if (key !== null) {\n container[key] = value;\n this.currentState.key = null;\n }\n else {\n container.push(value);\n }\n }\n _openArray(newContainer = []) {\n this.jsonpath.push(null);\n this._pushOrSet(newContainer);\n this.previousStates.push(this.currentState);\n this.currentState = { container: newContainer, isArray: true, key: null };\n }\n _closeArray() {\n this.jsonpath.pop();\n this.currentState = this.previousStates.pop();\n }\n _openObject(newContainer = {}) {\n this.jsonpath.push(null);\n this._pushOrSet(newContainer);\n this.previousStates.push(this.currentState);\n this.currentState = { container: newContainer, isArray: false, key: null };\n }\n _closeObject() {\n this.jsonpath.pop();\n this.currentState = this.previousStates.pop();\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { default as JSONParser } from \"./json-parser.js\";\nimport JSONPath from \"../jsonpath/jsonpath.js\";\n/**\n * The `StreamingJSONParser` looks for the first array in the JSON structure.\n * and emits an array of chunks\n */\nexport default class StreamingJSONParser extends JSONParser {\n jsonPaths;\n streamingJsonPath = null;\n streamingArray = null;\n topLevelObject = null;\n constructor(options = {}) {\n super({\n onopenarray: () => {\n if (!this.streamingArray) {\n if (this._matchJSONPath()) {\n // @ts-ignore\n this.streamingJsonPath = this.getJsonPath().clone();\n this.streamingArray = [];\n this._openArray(this.streamingArray);\n return;\n }\n }\n this._openArray();\n },\n // Redefine onopenarray to inject value for top-level object\n onopenobject: (name) => {\n if (!this.topLevelObject) {\n this.topLevelObject = {};\n this._openObject(this.topLevelObject);\n }\n else {\n this._openObject({});\n }\n if (typeof name !== 'undefined') {\n this.parser.emit('onkey', name);\n }\n }\n });\n const jsonpaths = options.jsonpaths || [];\n this.jsonPaths = jsonpaths.map((jsonpath) => new JSONPath(jsonpath));\n }\n /**\n * write REDEFINITION\n * - super.write() chunk to parser\n * - get the contents (so far) of \"topmost-level\" array as batch of rows\n * - clear top-level array\n * - return the batch of rows\\\n */\n write(chunk) {\n super.write(chunk);\n let array = [];\n if (this.streamingArray) {\n array = [...this.streamingArray];\n this.streamingArray.length = 0;\n }\n return array;\n }\n /**\n * Returns a partially formed result object\n * Useful for returning the \"wrapper\" object when array is not top level\n * e.g. GeoJSON\n */\n getPartialResult() {\n return this.topLevelObject;\n }\n getStreamingJsonPath() {\n return this.streamingJsonPath;\n }\n getStreamingJsonPathAsString() {\n return this.streamingJsonPath && this.streamingJsonPath.toString();\n }\n getJsonPath() {\n return this.jsonpath;\n }\n // PRIVATE METHODS\n /**\n * Checks is this.getJsonPath matches the jsonpaths provided in options\n */\n _matchJSONPath() {\n const currentPath = this.getJsonPath();\n // console.debug(`Testing JSONPath`, currentPath);\n // Backwards compatibility, match any array\n // TODO implement using wildcard once that is supported\n if (this.jsonPaths.length === 0) {\n return true;\n }\n for (const jsonPath of this.jsonPaths) {\n if (jsonPath.equals(currentPath)) {\n return true;\n }\n }\n return false;\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { parseJSONSync } from \"./lib/parsers/parse-json.js\";\nimport { parseJSONInBatches } from \"./lib/parsers/parse-json-in-batches.js\";\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';\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: 'table',\n text: true,\n options: {\n json: {\n shape: undefined,\n table: false,\n jsonpaths: []\n // batchSize: 'auto'\n }\n },\n parse,\n parseTextSync,\n parseInBatches\n};\nasync function parse(arrayBuffer, options) {\n return parseTextSync(new TextDecoder().decode(arrayBuffer), options);\n}\nfunction parseTextSync(text, options) {\n const jsonOptions = { ...options, json: { ...JSONLoader.options.json, ...options?.json } };\n return parseJSONSync(text, jsonOptions);\n}\nfunction parseInBatches(asyncIterator, options) {\n const jsonOptions = { ...options, json: { ...JSONLoader.options.json, ...options?.json } };\n return parseJSONInBatches(asyncIterator, jsonOptions);\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { makeTableFromData } from '@loaders.gl/schema';\nexport function parseNDJSONSync(ndjsonText) {\n const lines = ndjsonText.trim().split('\\n');\n const parsedLines = lines.map((line, counter) => {\n try {\n return JSON.parse(line);\n }\n catch (error) {\n throw new Error(`NDJSONLoader: failed to parse JSON on line ${counter + 1}`);\n }\n });\n return makeTableFromData(parsedLines);\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { TableBatchBuilder } from '@loaders.gl/schema';\nimport { makeLineIterator, makeNumberedLineIterator, makeTextDecoderIterator } from '@loaders.gl/loader-utils';\nexport async function* parseNDJSONInBatches(binaryAsyncIterator, options) {\n const textIterator = makeTextDecoderIterator(binaryAsyncIterator);\n const lineIterator = makeLineIterator(textIterator);\n const numberedLineIterator = makeNumberedLineIterator(lineIterator);\n const schema = null;\n const shape = 'row-table';\n // @ts-ignore\n const tableBatchBuilder = new TableBatchBuilder(schema, {\n ...options,\n shape\n });\n for await (const { counter, line } of numberedLineIterator) {\n try {\n const row = JSON.parse(line);\n tableBatchBuilder.addRow(row);\n tableBatchBuilder.chunkComplete(line);\n const batch = tableBatchBuilder.getFullBatch();\n if (batch) {\n yield batch;\n }\n }\n catch (error) {\n throw new Error(`NDJSONLoader: failed to parse JSON on line ${counter}`);\n }\n }\n const batch = tableBatchBuilder.getFinalBatch();\n if (batch) {\n yield batch;\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { parseNDJSONSync } from \"./lib/parsers/parse-ndjson.js\";\nimport { parseNDJSONInBatches } from \"./lib/parsers/parse-ndjson-in-batches.js\";\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';\nexport const NDJSONLoader = {\n dataType: null,\n batchType: null,\n name: 'NDJSON',\n id: 'ndjson',\n module: 'json',\n version: VERSION,\n extensions: ['ndjson', 'jsonl'],\n mimeTypes: [\n 'application/x-ndjson',\n 'application/jsonlines', // https://docs.aws.amazon.com/sagemaker/latest/dg/cdf-inference.html#cm-batch\n 'application/json-seq'\n ],\n category: 'table',\n text: true,\n parse: async (arrayBuffer) => parseNDJSONSync(new TextDecoder().decode(arrayBuffer)),\n parseTextSync: parseNDJSONSync,\n parseInBatches: parseNDJSONInBatches,\n options: {}\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Copyright 2022 Foursquare Labs, Inc.\nimport { makeRowIterator } from '@loaders.gl/schema';\n/**\n * Encode a table as a JSON string\n */\nexport function encodeTableAsJSON(table, options) {\n const shape = options?.json?.shape || 'object-row-table';\n const strings = [];\n const rowIterator = makeRowIterator(table, shape);\n for (const row of rowIterator) {\n // Round elements etc\n // processRow(wrappedRow, table.schema);\n // const wrappedRow = options.wrapper ? options.wrapper(row) : row;\n strings.push(JSON.stringify(row));\n }\n return `[${strings.join(',')}]`;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Copyright 2022 Foursquare Labs, Inc.\nimport { encodeTableAsJSON } from \"./lib/encoders/json-encoder.js\";\nexport const JSONWriter = {\n id: 'json',\n version: 'latest',\n module: 'json',\n name: 'JSON',\n extensions: ['json'],\n mimeTypes: ['application/json'],\n options: {},\n text: true,\n encode: async (table, options) => new TextEncoder().encode(encodeTableAsJSON(table, options)).buffer,\n encodeTextSync: (table, options) => encodeTableAsJSON(table, options)\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { geojsonToBinary } from '@loaders.gl/gis';\n// import {parseJSONSync} from './lib/parsers/parse-json';\nimport { parseJSONInBatches } from \"./lib/parsers/parse-json-in-batches.js\";\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 * GeoJSON loader\n */\nexport const GeoJSONWorkerLoader = {\n dataType: null,\n batchType: null,\n name: 'GeoJSON',\n id: 'geojson',\n module: 'geojson',\n version: VERSION,\n worker: true,\n extensions: ['geojson'],\n mimeTypes: ['application/geo+json'],\n category: 'geometry',\n text: true,\n options: {\n geojson: {\n shape: 'geojson-table'\n },\n json: {\n shape: 'object-row-table',\n jsonpaths: ['$', '$.features']\n },\n gis: {\n format: 'geojson'\n }\n }\n};\nexport const GeoJSONLoader = {\n ...GeoJSONWorkerLoader,\n // @ts-expect-error\n parse,\n // @ts-expect-error\n parseTextSync,\n parseInBatches\n};\nasync function parse(arrayBuffer, options) {\n return parseTextSync(new TextDecoder().decode(arrayBuffer), options);\n}\nfunction parseTextSync(text, options) {\n // Apps can call the parse method directly, we so apply default options here\n options = { ...GeoJSONLoader.options, ...options };\n options.geojson = { ...GeoJSONLoader.options.geojson, ...options.geojson };\n options.gis = options.gis || {};\n let geojson;\n try {\n geojson = JSON.parse(text);\n }\n catch {\n geojson = {};\n }\n const table = {\n shape: 'geojson-table',\n // TODO - deduce schema from geojson\n // TODO check that parsed data is of type FeatureCollection\n type: 'FeatureCollection',\n features: geojson?.features || []\n };\n switch (options.gis.format) {\n case 'binary':\n return geojsonToBinary(table.features);\n default:\n return table;\n }\n}\nfunction parseInBatches(asyncIterator, options) {\n // Apps can call the parse method directly, we so apply default options here\n options = { ...GeoJSONLoader.options, ...options };\n options.json = { ...GeoJSONLoader.options.geojson, ...options.geojson };\n const geojsonIterator = parseJSONInBatches(asyncIterator, options);\n switch (options.gis.format) {\n case 'binary':\n return makeBinaryGeometryIterator(geojsonIterator);\n default:\n return geojsonIterator;\n }\n}\nasync function* makeBinaryGeometryIterator(geojsonIterator) {\n for await (const batch of geojsonIterator) {\n batch.data = geojsonToBinary(batch.data);\n yield batch;\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Copyright Foursquare, Inc 20222\nimport { concatenateArrayBuffersAsync } from '@loaders.gl/loader-utils';\nimport { encodeTableAsGeojsonInBatches } from \"./lib/encoders/geojson-encoder.js\";\nexport const GeoJSONWriter = {\n id: 'geojson',\n version: 'latest',\n module: 'geojson',\n name: 'GeoJSON',\n extensions: ['geojson'],\n mimeTypes: ['application/geo+json'],\n text: true,\n options: {\n geojson: {\n featureArray: false,\n geometryColumn: null\n }\n },\n async encode(table, options) {\n const tableIterator = [table];\n const batches = encodeTableAsGeojsonInBatches(tableIterator, options);\n return await concatenateArrayBuffersAsync(batches);\n },\n encodeInBatches: (tableIterator, options) => encodeTableAsGeojsonInBatches(tableIterator, options)\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Copyright 2022 Foursquare Labs, Inc.\nimport { getTableLength } from '@loaders.gl/schema';\nimport { detectGeometryColumnIndex } from \"../encoder-utils/encode-utils.js\";\nimport { encodeTableRow } from \"../encoder-utils/encode-table-row.js\";\nimport { Utf8ArrayBufferEncoder } from \"../encoder-utils/utf8-encoder.js\";\n/**\n * Encode a table as GeoJSON\n */\n// eslint-disable-next-line max-statements\nexport async function* encodeTableAsGeojsonInBatches(batchIterator, // | Iterable<TableBatch>,\ninputOpts = {}) {\n // @ts-expect-error\n const options = { geojson: {}, chunkSize: 10000, ...inputOpts };\n const utf8Encoder = new Utf8ArrayBufferEncoder(options.chunkSize);\n if (!options.geojson.featureArray) {\n utf8Encoder.push('{\\n', '\"type\": \"FeatureCollection\",\\n', '\"features\":\\n');\n }\n utf8Encoder.push('['); // Note no newline\n let geometryColumn = options.geojson.geometryColumn;\n let isFirstLine = true;\n let start = 0;\n for await (const tableBatch of batchIterator) {\n const end = start + getTableLength(tableBatch);\n // Deduce geometry column if not already done\n if (!geometryColumn) {\n geometryColumn = geometryColumn || detectGeometryColumnIndex(tableBatch);\n }\n for (let rowIndex = start; rowIndex < end; ++rowIndex) {\n // Add a comma except on final feature\n if (!isFirstLine) {\n utf8Encoder.push(',');\n }\n utf8Encoder.push('\\n');\n isFirstLine = false;\n encodeTableRow(tableBatch, rowIndex, geometryColumn, utf8Encoder);\n // eslint-disable-next-line max-depth\n if (utf8Encoder.isFull()) {\n yield utf8Encoder.getArrayBufferBatch();\n }\n start = end;\n }\n const arrayBufferBatch = utf8Encoder.getArrayBufferBatch();\n if (arrayBufferBatch.byteLength > 0) {\n yield arrayBufferBatch;\n }\n }\n utf8Encoder.push('\\n');\n // Add completing rows and emit final batch\n utf8Encoder.push(']\\n');\n if (!options.geojson.featureArray) {\n utf8Encoder.push('}');\n }\n // Note: Since we pushed a few final lines, the last batch will always exist, no need to check first\n yield utf8Encoder.getArrayBufferBatch();\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Copyright 2022 Foursquare Labs, Inc.\nimport { getTableLength, getTableNumCols, getTableRowAsArray } from '@loaders.gl/schema';\n/**\n * Attempts to identify which column contains geometry\n * Currently just returns name (key) of first object-valued column\n * @todo look for hints in schema metadata\n * @todo look for WKB\n */\nexport function detectGeometryColumnIndex(table) {\n // TODO - look for hints in schema metadata\n // look for a column named geometry\n const geometryIndex = table.schema?.fields.findIndex((field) => field.name === 'geometry') ?? -1;\n if (geometryIndex > -1) {\n return geometryIndex;\n }\n // look at the data\n // TODO - this drags in the indices\n if (getTableLength(table) > 0) {\n const row = getTableRowAsArray(table, 0);\n for (let columnIndex = 0; columnIndex < getTableNumCols(table); columnIndex++) {\n const value = row?.[columnIndex];\n if (value && typeof value === 'object') {\n return columnIndex;\n }\n }\n }\n throw new Error('Failed to detect geometry column');\n}\n/**\n * Return a row as a property (key/value) object, e