@loaders.gl/json
Version:
Framework-independent loader for JSON and streaming JSON formats
1,496 lines (1,473 loc) • 45.3 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// dist/index.js
var dist_exports = {};
__export(dist_exports, {
JSONLoader: () => JSONLoader,
JSONWriter: () => JSONWriter,
NDJSONLoader: () => NDJSONLoader,
_ClarinetParser: () => ClarinetParser,
_GeoJSONLoader: () => GeoJSONLoader,
_GeoJSONWorkerLoader: () => GeoJSONWorkerLoader,
_GeoJSONWriter: () => GeoJSONWriter,
_JSONPath: () => JSONPath,
_rebuildJsonObject: () => rebuildJsonObject
});
module.exports = __toCommonJS(dist_exports);
// dist/lib/parsers/parse-json.js
var import_schema_utils = require("@loaders.gl/schema-utils");
function parseJSONSync(jsonText, options) {
var _a;
try {
const json = JSON.parse(jsonText);
if ((_a = options.json) == null ? void 0 : _a.table) {
const data = getFirstArray(json) || json;
return (0, import_schema_utils.makeTableFromData)(data);
}
return json;
} catch (error) {
throw new Error("JSONLoader: failed to parse JSON");
}
}
function getFirstArray(json) {
if (Array.isArray(json)) {
return json;
}
if (json && typeof json === "object") {
for (const value of Object.values(json)) {
const array = getFirstArray(value);
if (array) {
return array;
}
}
}
return null;
}
// dist/lib/parsers/parse-json-in-batches.js
var import_schema_utils2 = require("@loaders.gl/schema-utils");
var import_loader_utils = require("@loaders.gl/loader-utils");
// dist/lib/clarinet/clarinet.js
var MAX_BUFFER_LENGTH = Number.MAX_SAFE_INTEGER;
var STATE;
(function(STATE2) {
STATE2[STATE2["BEGIN"] = 0] = "BEGIN";
STATE2[STATE2["VALUE"] = 1] = "VALUE";
STATE2[STATE2["OPEN_OBJECT"] = 2] = "OPEN_OBJECT";
STATE2[STATE2["CLOSE_OBJECT"] = 3] = "CLOSE_OBJECT";
STATE2[STATE2["OPEN_ARRAY"] = 4] = "OPEN_ARRAY";
STATE2[STATE2["CLOSE_ARRAY"] = 5] = "CLOSE_ARRAY";
STATE2[STATE2["TEXT_ESCAPE"] = 6] = "TEXT_ESCAPE";
STATE2[STATE2["STRING"] = 7] = "STRING";
STATE2[STATE2["BACKSLASH"] = 8] = "BACKSLASH";
STATE2[STATE2["END"] = 9] = "END";
STATE2[STATE2["OPEN_KEY"] = 10] = "OPEN_KEY";
STATE2[STATE2["CLOSE_KEY"] = 11] = "CLOSE_KEY";
STATE2[STATE2["TRUE"] = 12] = "TRUE";
STATE2[STATE2["TRUE2"] = 13] = "TRUE2";
STATE2[STATE2["TRUE3"] = 14] = "TRUE3";
STATE2[STATE2["FALSE"] = 15] = "FALSE";
STATE2[STATE2["FALSE2"] = 16] = "FALSE2";
STATE2[STATE2["FALSE3"] = 17] = "FALSE3";
STATE2[STATE2["FALSE4"] = 18] = "FALSE4";
STATE2[STATE2["NULL"] = 19] = "NULL";
STATE2[STATE2["NULL2"] = 20] = "NULL2";
STATE2[STATE2["NULL3"] = 21] = "NULL3";
STATE2[STATE2["NUMBER_DECIMAL_POINT"] = 22] = "NUMBER_DECIMAL_POINT";
STATE2[STATE2["NUMBER_DIGIT"] = 23] = "NUMBER_DIGIT";
})(STATE || (STATE = {}));
var Char = {
tab: 9,
// \t
lineFeed: 10,
// \n
carriageReturn: 13,
// \r
space: 32,
// " "
doubleQuote: 34,
// "
plus: 43,
// +
comma: 44,
// ,
minus: 45,
// -
period: 46,
// .
_0: 48,
// 0
_9: 57,
// 9
colon: 58,
// :
E: 69,
// E
openBracket: 91,
// [
backslash: 92,
// \
closeBracket: 93,
// ]
a: 97,
// a
b: 98,
// b
e: 101,
// e
f: 102,
// f
l: 108,
// l
n: 110,
// n
r: 114,
// r
s: 115,
// s
t: 116,
// t
u: 117,
// u
openBrace: 123,
// {
closeBrace: 125
// }
};
var stringTokenPattern = /[\\"\n]/g;
var DEFAULT_OPTIONS = {
onready: () => {
},
onopenobject: () => {
},
onkey: () => {
},
oncloseobject: () => {
},
onopenarray: () => {
},
onclosearray: () => {
},
onvalue: () => {
},
onerror: () => {
},
onend: () => {
},
onchunkparsed: () => {
}
};
var ClarinetParser = class {
options = DEFAULT_OPTIONS;
bufferCheckPosition = MAX_BUFFER_LENGTH;
q = "";
c = "";
p = "";
closed = false;
closedRoot = false;
sawRoot = false;
// tag = null;
error = null;
state = STATE.BEGIN;
stack = [];
// mostly just for error reporting
position = 0;
column = 0;
line = 1;
slashed = false;
unicodeI = 0;
unicodeS = null;
depth = 0;
textNode;
numberNode;
constructor(options = {}) {
this.options = { ...DEFAULT_OPTIONS, ...options };
this.textNode = void 0;
this.numberNode = "";
this.emit("onready");
}
end() {
if (this.state !== STATE.VALUE || this.depth !== 0)
this._error("Unexpected end");
this._closeValue();
this.c = "";
this.closed = true;
this.emit("onend");
return this;
}
resume() {
this.error = null;
return this;
}
close() {
return this.write(null);
}
// protected
emit(event, data) {
var _a, _b;
(_b = (_a = this.options)[event]) == null ? void 0 : _b.call(_a, data, this);
}
emitNode(event, data) {
this._closeValue();
this.emit(event, data);
}
/* eslint-disable no-continue */
// eslint-disable-next-line complexity, max-statements
write(chunk) {
if (this.error) {
throw this.error;
}
if (this.closed) {
return this._error("Cannot write after close. Assign an onready handler.");
}
if (chunk === null) {
return this.end();
}
let i = 0;
let c = chunk.charCodeAt(0);
let p = this.p;
while (c) {
p = c;
this.c = c = chunk.charCodeAt(i++);
if (p !== c) {
this.p = p;
} else {
p = this.p;
}
if (!c)
break;
this.position++;
if (c === Char.lineFeed) {
this.line++;
this.column = 0;
} else
this.column++;
switch (this.state) {
case STATE.BEGIN:
if (c === Char.openBrace)
this.state = STATE.OPEN_OBJECT;
else if (c === Char.openBracket)
this.state = STATE.OPEN_ARRAY;
else if (!isWhitespace(c)) {
this._error("Non-whitespace before {[.");
}
continue;
case STATE.OPEN_KEY:
case STATE.OPEN_OBJECT:
if (isWhitespace(c))
continue;
if (this.state === STATE.OPEN_KEY)
this.stack.push(STATE.CLOSE_KEY);
else if (c === Char.closeBrace) {
this.emit("onopenobject");
this.depth++;
this.emit("oncloseobject");
this.depth--;
this.state = this.stack.pop() || STATE.VALUE;
continue;
} else
this.stack.push(STATE.CLOSE_OBJECT);
if (c === Char.doubleQuote)
this.state = STATE.STRING;
else
this._error('Malformed object key should start with "');
continue;
case STATE.CLOSE_KEY:
case STATE.CLOSE_OBJECT:
if (isWhitespace(c))
continue;
if (c === Char.colon) {
if (this.state === STATE.CLOSE_OBJECT) {
this.stack.push(STATE.CLOSE_OBJECT);
this._closeValue("onopenobject");
this.depth++;
} else
this._closeValue("onkey");
this.state = STATE.VALUE;
} else if (c === Char.closeBrace) {
this.emitNode("oncloseobject");
this.depth--;
this.state = this.stack.pop() || STATE.VALUE;
} else if (c === Char.comma) {
if (this.state === STATE.CLOSE_OBJECT)
this.stack.push(STATE.CLOSE_OBJECT);
this._closeValue();
this.state = STATE.OPEN_KEY;
} else
this._error("Bad object");
continue;
case STATE.OPEN_ARRAY:
case STATE.VALUE:
if (isWhitespace(c))
continue;
if (this.state === STATE.OPEN_ARRAY) {
this.emit("onopenarray");
this.depth++;
this.state = STATE.VALUE;
if (c === Char.closeBracket) {
this.emit("onclosearray");
this.depth--;
this.state = this.stack.pop() || STATE.VALUE;
continue;
} else {
this.stack.push(STATE.CLOSE_ARRAY);
}
}
if (c === Char.doubleQuote)
this.state = STATE.STRING;
else if (c === Char.openBrace)
this.state = STATE.OPEN_OBJECT;
else if (c === Char.openBracket)
this.state = STATE.OPEN_ARRAY;
else if (c === Char.t)
this.state = STATE.TRUE;
else if (c === Char.f)
this.state = STATE.FALSE;
else if (c === Char.n)
this.state = STATE.NULL;
else if (c === Char.minus) {
this.numberNode += "-";
} else if (Char._0 <= c && c <= Char._9) {
this.numberNode += String.fromCharCode(c);
this.state = STATE.NUMBER_DIGIT;
} else
this._error("Bad value");
continue;
case STATE.CLOSE_ARRAY:
if (c === Char.comma) {
this.stack.push(STATE.CLOSE_ARRAY);
this._closeValue("onvalue");
this.state = STATE.VALUE;
} else if (c === Char.closeBracket) {
this.emitNode("onclosearray");
this.depth--;
this.state = this.stack.pop() || STATE.VALUE;
} else if (isWhitespace(c))
continue;
else
this._error("Bad array");
continue;
case STATE.STRING:
if (this.textNode === void 0) {
this.textNode = "";
}
let starti = i - 1;
let slashed = this.slashed;
let unicodeI = this.unicodeI;
STRING_BIGLOOP:
while (true) {
while (unicodeI > 0) {
this.unicodeS += String.fromCharCode(c);
c = chunk.charCodeAt(i++);
this.position++;
if (unicodeI === 4) {
this.textNode += String.fromCharCode(parseInt(this.unicodeS, 16));
unicodeI = 0;
starti = i - 1;
} else {
unicodeI++;
}
if (!c)
break STRING_BIGLOOP;
}
if (c === Char.doubleQuote && !slashed) {
this.state = this.stack.pop() || STATE.VALUE;
this.textNode += chunk.substring(starti, i - 1);
this.position += i - 1 - starti;
break;
}
if (c === Char.backslash && !slashed) {
slashed = true;
this.textNode += chunk.substring(starti, i - 1);
this.position += i - 1 - starti;
c = chunk.charCodeAt(i++);
this.position++;
if (!c)
break;
}
if (slashed) {
slashed = false;
if (c === Char.n) {
this.textNode += "\n";
} else if (c === Char.r) {
this.textNode += "\r";
} else if (c === Char.t) {
this.textNode += " ";
} else if (c === Char.f) {
this.textNode += "\f";
} else if (c === Char.b) {
this.textNode += "\b";
} else if (c === Char.u) {
unicodeI = 1;
this.unicodeS = "";
} else {
this.textNode += String.fromCharCode(c);
}
c = chunk.charCodeAt(i++);
this.position++;
starti = i - 1;
if (!c)
break;
else
continue;
}
stringTokenPattern.lastIndex = i;
const reResult = stringTokenPattern.exec(chunk);
if (reResult === null) {
i = chunk.length + 1;
this.textNode += chunk.substring(starti, i - 1);
this.position += i - 1 - starti;
break;
}
i = reResult.index + 1;
c = chunk.charCodeAt(reResult.index);
if (!c) {
this.textNode += chunk.substring(starti, i - 1);
this.position += i - 1 - starti;
break;
}
}
this.slashed = slashed;
this.unicodeI = unicodeI;
continue;
case STATE.TRUE:
if (c === Char.r)
this.state = STATE.TRUE2;
else
this._error(`Invalid true started with t${c}`);
continue;
case STATE.TRUE2:
if (c === Char.u)
this.state = STATE.TRUE3;
else
this._error(`Invalid true started with tr${c}`);
continue;
case STATE.TRUE3:
if (c === Char.e) {
this.emit("onvalue", true);
this.state = this.stack.pop() || STATE.VALUE;
} else
this._error(`Invalid true started with tru${c}`);
continue;
case STATE.FALSE:
if (c === Char.a)
this.state = STATE.FALSE2;
else
this._error(`Invalid false started with f${c}`);
continue;
case STATE.FALSE2:
if (c === Char.l)
this.state = STATE.FALSE3;
else
this._error(`Invalid false started with fa${c}`);
continue;
case STATE.FALSE3:
if (c === Char.s)
this.state = STATE.FALSE4;
else
this._error(`Invalid false started with fal${c}`);
continue;
case STATE.FALSE4:
if (c === Char.e) {
this.emit("onvalue", false);
this.state = this.stack.pop() || STATE.VALUE;
} else
this._error(`Invalid false started with fals${c}`);
continue;
case STATE.NULL:
if (c === Char.u)
this.state = STATE.NULL2;
else
this._error(`Invalid null started with n${c}`);
continue;
case STATE.NULL2:
if (c === Char.l)
this.state = STATE.NULL3;
else
this._error(`Invalid null started with nu${c}`);
continue;
case STATE.NULL3:
if (c === Char.l) {
this.emit("onvalue", null);
this.state = this.stack.pop() || STATE.VALUE;
} else
this._error(`Invalid null started with nul${c}`);
continue;
case STATE.NUMBER_DECIMAL_POINT:
if (c === Char.period) {
this.numberNode += ".";
this.state = STATE.NUMBER_DIGIT;
} else
this._error("Leading zero not followed by .");
continue;
case STATE.NUMBER_DIGIT:
if (Char._0 <= c && c <= Char._9)
this.numberNode += String.fromCharCode(c);
else if (c === Char.period) {
if (this.numberNode.indexOf(".") !== -1)
this._error("Invalid number has two dots");
this.numberNode += ".";
} else if (c === Char.e || c === Char.E) {
if (this.numberNode.indexOf("e") !== -1 || this.numberNode.indexOf("E") !== -1)
this._error("Invalid number has two exponential");
this.numberNode += "e";
} else if (c === Char.plus || c === Char.minus) {
if (!(p === Char.e || p === Char.E))
this._error("Invalid symbol in number");
this.numberNode += String.fromCharCode(c);
} else {
this._closeNumber();
i--;
this.state = this.stack.pop() || STATE.VALUE;
}
continue;
default:
this._error(`Unknown state: ${this.state}`);
}
}
if (this.position >= this.bufferCheckPosition) {
checkBufferLength(this);
}
this.emit("onchunkparsed");
return this;
}
_closeValue(event = "onvalue") {
if (this.textNode !== void 0) {
this.emit(event, this.textNode);
}
this.textNode = void 0;
}
_closeNumber() {
if (this.numberNode)
this.emit("onvalue", parseFloat(this.numberNode));
this.numberNode = "";
}
_error(message = "") {
this._closeValue();
message += `
Line: ${this.line}
Column: ${this.column}
Char: ${this.c}`;
const error = new Error(message);
this.error = error;
this.emit("onerror", error);
}
};
function isWhitespace(c) {
return c === Char.carriageReturn || c === Char.lineFeed || c === Char.space || c === Char.tab;
}
function checkBufferLength(parser) {
const maxAllowed = Math.max(MAX_BUFFER_LENGTH, 10);
let maxActual = 0;
for (const buffer of ["textNode", "numberNode"]) {
const len = parser[buffer] === void 0 ? 0 : parser[buffer].length;
if (len > maxAllowed) {
switch (buffer) {
case "text":
break;
default:
parser._error(`Max buffer length exceeded: ${buffer}`);
}
}
maxActual = Math.max(maxActual, len);
}
parser.bufferCheckPosition = MAX_BUFFER_LENGTH - maxActual + parser.position;
}
// dist/lib/jsonpath/jsonpath.js
var JSONPath = class {
path;
constructor(path = null) {
this.path = parseJsonPath(path);
}
clone() {
return new JSONPath(this);
}
toString() {
return formatJsonPath(this.path);
}
push(name) {
this.path.push(name);
}
pop() {
return this.path.pop();
}
set(name) {
this.path[this.path.length - 1] = name;
}
equals(other) {
if (!this || !other || this.path.length !== other.path.length) {
return false;
}
for (let i = 0; i < this.path.length; ++i) {
if (this.path[i] !== other.path[i]) {
return false;
}
}
return true;
}
/**
* Sets the value pointed at by path
* TODO - handle root path
* @param object
* @param value
*/
setFieldAtPath(object, value) {
const path = [...this.path];
path.shift();
const field = path.pop();
for (const component of path) {
object = object[component];
}
object[field] = value;
}
/**
* Gets the value pointed at by path
* TODO - handle root path
* @param object
*/
getFieldAtPath(object) {
const path = [...this.path];
path.shift();
const field = path.pop();
for (const component of path) {
object = object[component];
}
return object[field];
}
};
function parseJsonPath(path) {
if (path instanceof JSONPath) {
return [...path.path];
}
if (Array.isArray(path)) {
return ["$"].concat(path);
}
if (typeof path === "string") {
return parseJsonPathString(path);
}
return ["$"];
}
function parseJsonPathString(pathString) {
const trimmedPath = pathString.trim();
if (!trimmedPath.startsWith("$")) {
throw new Error("JSONPath must start with $");
}
const segments = ["$"];
let index = 1;
let arrayElementSelectorEncountered = false;
while (index < trimmedPath.length) {
const character = trimmedPath[index];
if (character === ".") {
if (arrayElementSelectorEncountered) {
throw new Error("JSONPath cannot select fields after array element selectors");
}
index += 1;
if (trimmedPath[index] === ".") {
throw new Error("JSONPath descendant selectors (..) are not supported");
}
const { value, nextIndex, isWildcard } = parseDotSegment(trimmedPath, index);
if (isWildcard) {
if (nextIndex < trimmedPath.length) {
throw new Error("JSONPath wildcard selectors must terminate the path");
}
arrayElementSelectorEncountered = true;
index = nextIndex;
continue;
}
segments.push(value);
index = nextIndex;
continue;
}
if (character === "[") {
const parsedSegment = parseBracketSegment(trimmedPath, index);
if (parsedSegment.type === "property") {
if (arrayElementSelectorEncountered) {
throw new Error("JSONPath cannot select fields after array element selectors");
}
segments.push(parsedSegment.value);
} else {
arrayElementSelectorEncountered = true;
}
index = parsedSegment.nextIndex;
continue;
}
if (character === "@") {
throw new Error("JSONPath current node selector (@) is not supported");
}
if (character.trim() === "") {
index += 1;
continue;
}
throw new Error(`Unexpected character "${character}" in JSONPath`);
}
return segments;
}
function parseDotSegment(pathString, startIndex) {
if (startIndex >= pathString.length) {
throw new Error("JSONPath cannot end with a period");
}
if (pathString[startIndex] === "*") {
return { value: "*", nextIndex: startIndex + 1, isWildcard: true };
}
const firstCharacter = pathString[startIndex];
if (firstCharacter === "@") {
throw new Error("JSONPath current node selector (@) is not supported");
}
if (!isIdentifierStartCharacter(firstCharacter)) {
throw new Error("JSONPath property names after period must start with a letter, $ or _");
}
let endIndex = startIndex + 1;
while (endIndex < pathString.length && isIdentifierCharacter(pathString[endIndex])) {
endIndex++;
}
if (endIndex === startIndex) {
throw new Error("JSONPath is missing a property name after period");
}
return {
value: pathString.slice(startIndex, endIndex),
nextIndex: endIndex,
isWildcard: false
};
}
function parseBracketSegment(pathString, startIndex) {
const contentStartIndex = startIndex + 1;
if (contentStartIndex >= pathString.length) {
throw new Error("JSONPath has unterminated bracket");
}
const firstCharacter = pathString[contentStartIndex];
if (firstCharacter === "'" || firstCharacter === '"') {
const { value, nextIndex } = parseBracketProperty(pathString, contentStartIndex);
return { type: "property", value, nextIndex };
}
const closingBracketIndex = pathString.indexOf("]", contentStartIndex);
if (closingBracketIndex === -1) {
throw new Error("JSONPath has unterminated bracket");
}
const content = pathString.slice(contentStartIndex, closingBracketIndex).trim();
const unsupportedSelectorMessage = getUnsupportedBracketSelectorMessage(content);
if (unsupportedSelectorMessage) {
throw new Error(unsupportedSelectorMessage);
}
if (content === "*") {
return { type: "array-selector", nextIndex: closingBracketIndex + 1 };
}
if (/^\d+$/.test(content)) {
throw new Error("JSONPath array index selectors are not supported");
}
if (/^\d*\s*:\s*\d*(\s*:\s*\d*)?$/.test(content)) {
return { type: "array-selector", nextIndex: closingBracketIndex + 1 };
}
throw new Error(`Unsupported bracket selector "[${content}]" in JSONPath`);
}
function getUnsupportedBracketSelectorMessage(content) {
if (!content.length) {
return "JSONPath bracket selectors cannot be empty";
}
if (content.startsWith("(")) {
return "JSONPath script selectors are not supported";
}
if (content.startsWith("?")) {
return "JSONPath filter selectors are not supported";
}
if (content.includes(",")) {
return "JSONPath union selectors are not supported";
}
if (content.startsWith("@") || content.includes("@.")) {
return "JSONPath current node selector (@) is not supported";
}
return null;
}
function parseBracketProperty(pathString, startIndex) {
const quoteCharacter = pathString[startIndex];
let index = startIndex + 1;
let value = "";
let terminated = false;
while (index < pathString.length) {
const character = pathString[index];
if (character === "\\") {
index += 1;
if (index >= pathString.length) {
break;
}
value += pathString[index];
index += 1;
continue;
}
if (character === quoteCharacter) {
terminated = true;
index += 1;
break;
}
value += character;
index += 1;
}
if (!terminated) {
throw new Error("JSONPath string in bracket property selector is unterminated");
}
while (index < pathString.length && pathString[index].trim() === "") {
index += 1;
}
if (pathString[index] !== "]") {
throw new Error("JSONPath property selectors must end with ]");
}
if (!value.length) {
throw new Error("JSONPath property selectors cannot be empty");
}
return { value, nextIndex: index + 1 };
}
function isIdentifierCharacter(character) {
return /[a-zA-Z0-9$_]/.test(character);
}
function isIdentifierStartCharacter(character) {
return /[a-zA-Z_$]/.test(character);
}
function isIdentifierSegment(segment) {
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(segment);
}
function formatJsonPath(path) {
return path.map((segment, index) => {
if (index === 0) {
return segment;
}
if (segment === "*") {
return ".*";
}
if (isIdentifierSegment(segment)) {
return `.${segment}`;
}
const escapedSegment = segment.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
return `['${escapedSegment}']`;
}).join("");
}
// dist/lib/json-parser/json-parser.js
var JSONParser = class {
parser;
result = void 0;
previousStates = [];
currentState = Object.freeze({ container: [], key: null });
jsonpath = new JSONPath();
constructor(options) {
this.reset();
this.parser = new ClarinetParser({
onready: () => {
this.jsonpath = new JSONPath();
this.previousStates.length = 0;
this.currentState.container.length = 0;
},
onopenobject: (name) => {
this._openObject({});
if (typeof name !== "undefined") {
this.parser.emit("onkey", name);
}
},
onkey: (name) => {
this.jsonpath.set(name);
this.currentState.key = name;
},
oncloseobject: () => {
this._closeObject();
},
onopenarray: () => {
this._openArray();
},
onclosearray: () => {
this._closeArray();
},
onvalue: (value) => {
this._pushOrSet(value);
},
onerror: (error) => {
throw error;
},
onend: () => {
this.result = this.currentState.container.pop();
},
...options
});
}
reset() {
this.result = void 0;
this.previousStates = [];
this.currentState = Object.freeze({ container: [], key: null });
this.jsonpath = new JSONPath();
}
write(chunk) {
this.parser.write(chunk);
}
close() {
this.parser.close();
}
// PRIVATE METHODS
_pushOrSet(value) {
const { container, key } = this.currentState;
if (key !== null) {
container[key] = value;
this.currentState.key = null;
} else {
container.push(value);
}
}
_openArray(newContainer = []) {
this.jsonpath.push(null);
this._pushOrSet(newContainer);
this.previousStates.push(this.currentState);
this.currentState = { container: newContainer, isArray: true, key: null };
}
_closeArray() {
this.jsonpath.pop();
this.currentState = this.previousStates.pop();
}
_openObject(newContainer = {}) {
this.jsonpath.push(null);
this._pushOrSet(newContainer);
this.previousStates.push(this.currentState);
this.currentState = { container: newContainer, isArray: false, key: null };
}
_closeObject() {
this.jsonpath.pop();
this.currentState = this.previousStates.pop();
}
};
// dist/lib/json-parser/streaming-json-parser.js
var StreamingJSONParser = class extends JSONParser {
jsonPaths;
streamingJsonPath = null;
streamingArray = null;
topLevelObject = null;
constructor(options = {}) {
super({
onopenarray: () => {
if (!this.streamingArray) {
if (this._matchJSONPath()) {
this.streamingJsonPath = this.getJsonPath().clone();
this.streamingArray = [];
this._openArray(this.streamingArray);
return;
}
}
this._openArray();
},
// Redefine onopenarray to inject value for top-level object
onopenobject: (name) => {
if (!this.topLevelObject) {
this.topLevelObject = {};
this._openObject(this.topLevelObject);
} else {
this._openObject({});
}
if (typeof name !== "undefined") {
this.parser.emit("onkey", name);
}
}
});
const jsonpaths = options.jsonpaths || [];
this.jsonPaths = jsonpaths.map((jsonpath) => new JSONPath(jsonpath));
}
/**
* write REDEFINITION
* - super.write() chunk to parser
* - get the contents (so far) of "topmost-level" array as batch of rows
* - clear top-level array
* - return the batch of rows\
*/
write(chunk) {
super.write(chunk);
let array = [];
if (this.streamingArray) {
array = [...this.streamingArray];
this.streamingArray.length = 0;
}
return array;
}
/**
* Returns a partially formed result object
* Useful for returning the "wrapper" object when array is not top level
* e.g. GeoJSON
*/
getPartialResult() {
return this.topLevelObject;
}
getStreamingJsonPath() {
return this.streamingJsonPath;
}
getStreamingJsonPathAsString() {
return this.streamingJsonPath && this.streamingJsonPath.toString();
}
getJsonPath() {
return this.jsonpath;
}
// PRIVATE METHODS
/**
* Checks is this.getJsonPath matches the jsonpaths provided in options
*/
_matchJSONPath() {
const currentPath = this.getJsonPath();
if (this.jsonPaths.length === 0) {
return true;
}
for (const jsonPath of this.jsonPaths) {
if (jsonPath.equals(currentPath)) {
return true;
}
}
return false;
}
};
// dist/lib/parsers/parse-json-in-batches.js
async function* parseJSONInBatches(binaryAsyncIterator, options) {
var _a, _b;
const asyncIterator = (0, import_loader_utils.makeTextDecoderIterator)((0, import_loader_utils.toArrayBufferIterator)(binaryAsyncIterator));
const metadata = Boolean(((_a = options == null ? void 0 : options.core) == null ? void 0 : _a.metadata) || (options == null ? void 0 : options.metadata));
const { jsonpaths } = options.json || {};
let isFirstChunk = true;
const schema = null;
const tableBatchBuilder = new import_schema_utils2.TableBatchBuilder(schema, options == null ? void 0 : options.core);
const parser = new StreamingJSONParser({ jsonpaths });
for await (const chunk of asyncIterator) {
const rows = parser.write(chunk);
const jsonpath2 = rows.length > 0 && parser.getStreamingJsonPathAsString();
if (rows.length > 0 && isFirstChunk) {
if (metadata) {
const initialBatch = {
// Common fields
shape: ((_b = options == null ? void 0 : options.json) == null ? void 0 : _b.shape) || "array-row-table",
batchType: "partial-result",
data: [],
length: 0,
bytesUsed: 0,
// JSON additions
container: parser.getPartialResult(),
jsonpath: jsonpath2
};
yield initialBatch;
}
isFirstChunk = false;
}
for (const row of rows) {
tableBatchBuilder.addRow(row);
const batch3 = tableBatchBuilder.getFullBatch({ jsonpath: jsonpath2 });
if (batch3) {
yield batch3;
}
}
tableBatchBuilder.chunkComplete(chunk);
const batch2 = tableBatchBuilder.getFullBatch({ jsonpath: jsonpath2 });
if (batch2) {
yield batch2;
}
}
const jsonpath = parser.getStreamingJsonPathAsString();
const batch = tableBatchBuilder.getFinalBatch({ jsonpath });
if (batch) {
yield batch;
}
if (metadata) {
const finalBatch = {
shape: "json",
batchType: "final-result",
container: parser.getPartialResult(),
jsonpath: parser.getStreamingJsonPathAsString(),
/** Data Just to avoid crashing? */
data: [],
length: 0
// schema: null
};
yield finalBatch;
}
}
function rebuildJsonObject(batch, data) {
(0, import_loader_utils.assert)(batch.batchType === "final-result");
if (batch.jsonpath === "$") {
return data;
}
if (batch.jsonpath && batch.jsonpath.length > 1) {
const topLevelObject = batch.container;
const streamingPath = new JSONPath(batch.jsonpath);
streamingPath.setFieldAtPath(topLevelObject, data);
return topLevelObject;
}
return batch.container;
}
// dist/json-loader.js
var VERSION = true ? "4.4.3" : "latest";
var JSONLoader = {
dataType: null,
batchType: null,
name: "JSON",
id: "json",
module: "json",
version: VERSION,
extensions: ["json", "geojson"],
mimeTypes: ["application/json"],
category: "table",
text: true,
options: {
json: {
shape: void 0,
table: false,
jsonpaths: []
// batchSize: 'auto'
}
},
parse,
parseTextSync,
parseInBatches
};
async function parse(arrayBuffer, options) {
return parseTextSync(new TextDecoder().decode(arrayBuffer), options);
}
function parseTextSync(text, options) {
const jsonOptions = { ...options, json: { ...JSONLoader.options.json, ...options == null ? void 0 : options.json } };
return parseJSONSync(text, jsonOptions);
}
function parseInBatches(asyncIterator, options) {
const jsonOptions = { ...options, json: { ...JSONLoader.options.json, ...options == null ? void 0 : options.json } };
return parseJSONInBatches(asyncIterator, jsonOptions);
}
// dist/lib/parsers/parse-ndjson.js
var import_schema_utils3 = require("@loaders.gl/schema-utils");
function parseNDJSONSync(ndjsonText) {
const lines = ndjsonText.trim().split("\n");
const parsedLines = lines.map((line, counter) => {
try {
return JSON.parse(line);
} catch (error) {
throw new Error(`NDJSONLoader: failed to parse JSON on line ${counter + 1}`);
}
});
return (0, import_schema_utils3.makeTableFromData)(parsedLines);
}
// dist/lib/parsers/parse-ndjson-in-batches.js
var import_schema_utils4 = require("@loaders.gl/schema-utils");
var import_loader_utils2 = require("@loaders.gl/loader-utils");
async function* parseNDJSONInBatches(binaryAsyncIterator, options) {
const textIterator = (0, import_loader_utils2.makeTextDecoderIterator)((0, import_loader_utils2.toArrayBufferIterator)(binaryAsyncIterator));
const lineIterator = (0, import_loader_utils2.makeLineIterator)(textIterator);
const numberedLineIterator = (0, import_loader_utils2.makeNumberedLineIterator)(lineIterator);
const schema = null;
const shape = "row-table";
const tableBatchBuilder = new import_schema_utils4.TableBatchBuilder(schema, {
...(options == null ? void 0 : options.core) || options,
shape
});
for await (const { counter, line } of numberedLineIterator) {
try {
const row = JSON.parse(line);
tableBatchBuilder.addRow(row);
tableBatchBuilder.chunkComplete(line);
const batch2 = tableBatchBuilder.getFullBatch();
if (batch2) {
yield batch2;
}
} catch (error) {
throw new Error(`NDJSONLoader: failed to parse JSON on line ${counter}`);
}
}
const batch = tableBatchBuilder.getFinalBatch();
if (batch) {
yield batch;
}
}
// dist/ndjson-loader.js
var VERSION2 = true ? "4.4.3" : "latest";
var NDJSONLoader = {
dataType: null,
batchType: null,
name: "NDJSON",
id: "ndjson",
module: "json",
version: VERSION2,
extensions: ["ndjson", "jsonl"],
mimeTypes: [
"application/x-ndjson",
"application/jsonlines",
// https://docs.aws.amazon.com/sagemaker/latest/dg/cdf-inference.html#cm-batch
"application/json-seq"
],
category: "table",
text: true,
parse: async (arrayBuffer) => parseNDJSONSync(new TextDecoder().decode(arrayBuffer)),
parseTextSync: parseNDJSONSync,
parseInBatches: parseNDJSONInBatches,
options: {}
};
// dist/lib/encoders/json-encoder.js
var import_schema_utils5 = require("@loaders.gl/schema-utils");
function encodeTableAsJSON(table, options) {
var _a;
const shape = ((_a = options == null ? void 0 : options.json) == null ? void 0 : _a.shape) || "object-row-table";
const strings = [];
const rowIterator = (0, import_schema_utils5.makeRowIterator)(table, shape);
for (const row of rowIterator) {
strings.push(JSON.stringify(row));
}
return `[${strings.join(",")}]`;
}
// dist/json-writer.js
var JSONWriter = {
id: "json",
version: "latest",
module: "json",
name: "JSON",
extensions: ["json"],
mimeTypes: ["application/json"],
options: {},
text: true,
encode: async (table, options) => new TextEncoder().encode(encodeTableAsJSON(table, options)).buffer,
encodeTextSync: (table, options) => encodeTableAsJSON(table, options)
};
// dist/geojson-loader.js
var import_gis = require("@loaders.gl/gis");
var VERSION3 = true ? "4.4.3" : "latest";
var GeoJSONWorkerLoader = {
dataType: null,
batchType: null,
name: "GeoJSON",
id: "geojson",
module: "geojson",
version: VERSION3,
worker: true,
extensions: ["geojson"],
mimeTypes: ["application/geo+json"],
category: "geometry",
text: true,
options: {
geojson: {
shape: "geojson-table"
},
json: {
shape: "object-row-table",
jsonpaths: ["$", "$.features"]
},
gis: {
format: "geojson"
}
}
};
var GeoJSONLoader = {
...GeoJSONWorkerLoader,
// @ts-expect-error
parse: parse2,
// @ts-expect-error
parseTextSync: parseTextSync2,
parseInBatches: parseInBatches2
};
async function parse2(arrayBuffer, options) {
return parseTextSync2(new TextDecoder().decode(arrayBuffer), options);
}
function parseTextSync2(text, options) {
options = { ...GeoJSONLoader.options, ...options };
options.geojson = { ...GeoJSONLoader.options.geojson, ...options.geojson };
options.gis = options.gis || {};
let geojson;
try {
geojson = JSON.parse(text);
} catch {
geojson = {};
}
const table = {
shape: "geojson-table",
// TODO - deduce schema from geojson
// TODO check that parsed data is of type FeatureCollection
type: "FeatureCollection",
features: (geojson == null ? void 0 : geojson.features) || []
};
switch (options.gis.format) {
case "binary":
return (0, import_gis.geojsonToBinary)(table.features);
default:
return table;
}
}
function parseInBatches2(asyncIterator, options) {
options = { ...GeoJSONLoader.options, ...options };
options.json = { ...GeoJSONLoader.options.json, ...options.json };
options.geojson = { ...GeoJSONLoader.options.geojson, ...options.geojson };
const geojsonIterator = parseJSONInBatches(asyncIterator, options);
switch (options.gis.format) {
case "binary":
return makeBinaryGeometryIterator(geojsonIterator);
default:
return geojsonIterator;
}
}
async function* makeBinaryGeometryIterator(geojsonIterator) {
for await (const batch of geojsonIterator) {
batch.data = (0, import_gis.geojsonToBinary)(batch.data);
yield batch;
}
}
// dist/geojson-writer.js
var import_loader_utils4 = require("@loaders.gl/loader-utils");
// dist/lib/encoders/geojson-encoder.js
var import_schema_utils8 = require("@loaders.gl/schema-utils");
// dist/lib/encoder-utils/encode-utils.js
var import_schema_utils6 = require("@loaders.gl/schema-utils");
function detectGeometryColumnIndex(table) {
var _a;
const geometryIndex = ((_a = table.schema) == null ? void 0 : _a.fields.findIndex((field) => field.name === "geometry")) ?? -1;
if (geometryIndex > -1) {
return geometryIndex;
}
if ((0, import_schema_utils6.getTableLength)(table) > 0) {
const row = (0, import_schema_utils6.getTableRowAsArray)(table, 0);
for (let columnIndex = 0; columnIndex < (0, import_schema_utils6.getTableNumCols)(table); columnIndex++) {
const value = row == null ? void 0 : row[columnIndex];
if (value && typeof value === "object") {
return columnIndex;
}
}
}
throw new Error("Failed to detect geometry column");
}
function getRowPropertyObject(table, row, excludeColumnIndices = []) {
var _a;
const properties = {};
for (let columnIndex = 0; columnIndex < (0, import_schema_utils6.getTableNumCols)(table); ++columnIndex) {
const columnName = (_a = table.schema) == null ? void 0 : _a.fields[columnIndex].name;
if (columnName && !excludeColumnIndices.includes(columnIndex)) {
properties[columnName] = row[columnName];
}
}
return properties;
}
// dist/lib/encoder-utils/encode-table-row.js
var import_schema_utils7 = require("@loaders.gl/schema-utils");
function encodeTableRow(table, rowIndex, geometryColumnIndex, utf8Encoder) {
const row = (0, import_schema_utils7.getTableRowAsObject)(table, rowIndex);
if (!row)
return;
const featureWithProperties = getFeatureFromRow(table, row, geometryColumnIndex);
const featureString = JSON.stringify(featureWithProperties);
utf8Encoder.push(featureString);
}
function getFeatureFromRow(table, row, geometryColumnIndex) {
var _a;
const properties = getRowPropertyObject(table, row, [geometryColumnIndex]);
const columnName = (_a = table.schema) == null ? void 0 : _a.fields[geometryColumnIndex].name;
let featureOrGeometry = columnName && row[columnName];
if (!featureOrGeometry) {
return { type: "Feature", geometry: null, properties };
}
if (typeof featureOrGeometry === "string") {
try {
featureOrGeometry = JSON.parse(featureOrGeometry);
} catch (err) {
throw new Error("Invalid string geometry");
}
}
if (typeof featureOrGeometry !== "object" || typeof (featureOrGeometry == null ? void 0 : featureOrGeometry.type) !== "string") {
throw new Error("invalid geometry column value");
}
if ((featureOrGeometry == null ? void 0 : featureOrGeometry.type) === "Feature") {
return { ...featureOrGeometry, properties };
}
return { type: "Feature", geometry: featureOrGeometry, properties };
}
// dist/lib/encoder-utils/utf8-encoder.js
var import_loader_utils3 = require("@loaders.gl/loader-utils");
var Utf8ArrayBufferEncoder = class {
chunkSize;
strings = [];
totalLength = 0;
textEncoder = new TextEncoder();
constructor(chunkSize) {
this.chunkSize = chunkSize;
}
push(...strings) {
for (const string of strings) {
this.strings.push(string);
this.totalLength += string.length;
}
}
isFull() {
return this.totalLength >= this.chunkSize;
}
getArrayBufferBatch() {
return (0, import_loader_utils3.ensureArrayBuffer)(this.textEncoder.encode(this.getStringBatch()).buffer);
}
getStringBatch() {
const stringChunk = this.strings.join("");
this.strings = [];
this.totalLength = 0;
return stringChunk;
}
};
// dist/lib/encoders/geojson-encoder.js
async function* encodeTableAsGeojsonInBatches(batchIterator, inputOpts = {}) {
const options = { geojson: {}, chunkSize: 1e4, ...inputOpts };
const utf8Encoder = new Utf8ArrayBufferEncoder(options.chunkSize);
if (!options.geojson.featureArray) {
utf8Encoder.push("{\n", '"type": "FeatureCollection",\n', '"features":\n');
}
utf8Encoder.push("[");
let geometryColumn = options.geojson.geometryColumn;
let isFirstLine = true;
let start = 0;
for await (const tableBatch of batchIterator) {
const end = start + (0, import_schema_utils8.getTableLength)(tableBatch);
if (!geometryColumn) {
geometryColumn = geometryColumn || detectGeometryColumnIndex(tableBatch);
}
for (let rowIndex = start; rowIndex < end; ++rowIndex) {
if (!isFirstLine) {
utf8Encoder.push(",");
}
utf8Encoder.push("\n");
isFirstLine = false;
encodeTableRow(tableBatch, rowIndex, geometryColumn, utf8Encoder);
if (utf8Encoder.isFull()) {
yield utf8Encoder.getArrayBufferBatch();
}
start = end;
}
const arrayBufferBatch = utf8Encoder.getArrayBufferBatch();
if (arrayBufferBatch.byteLength > 0) {
yield arrayBufferBatch;
}
}
utf8Encoder.push("\n");
utf8Encoder.push("]\n");
if (!options.geojson.featureArray) {
utf8Encoder.push("}");
}
yield utf8Encoder.getArrayBufferBatch();
}
// dist/geojson-writer.js
var GeoJSONWriter = {
id: "geojson",
version: "latest",
module: "geojson",
name: "GeoJSON",
extensions: ["geojson"],
mimeTypes: ["application/geo+json"],
text: true,
options: {
geojson: {
featureArray: false,
geometryColumn: null
}
},
async encode(table, options) {
const tableIterator = [table];
const batches = encodeTableAsGeojsonInBatches(tableIterator, options);
return await (0, import_loader_utils4.concatenateArrayBuffersAsync)(batches);
},
encodeInBatches: (tableIterator, options) => encodeTableAsGeojsonInBatches(tableIterator, options)
};
//# sourceMappingURL=index.cjs.map