/*! * Copyright (c) Squirrel Chat et al., All rights reserved. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ "use strict"; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } }); //#region src/date.ts let DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i; var TomlDate = class TomlDate extends Date { #hasDate = false; #hasTime = false; #offset = null; constructor(date) { let hasDate = true; let hasTime = true; let offset = "Z"; if (typeof date === "string") { let match = date.match(DATE_TIME_RE); if (match) { if (!match[1]) { hasDate = false; date = `0000-01-01T${date}`; } hasTime = !!match[2]; hasTime && date[10] === " " && (date = date.replace(" ", "T")); if (match[2] && +match[2] > 23) date = ""; else { offset = match[3] || null; date = date.toUpperCase(); if (!offset && hasTime) date += "Z"; } } else date = ""; } super(date); if (!isNaN(this.getTime())) { this.#hasDate = hasDate; this.#hasTime = hasTime; this.#offset = offset; } } isDateTime() { return this.#hasDate && this.#hasTime; } isLocal() { return !this.#hasDate || !this.#hasTime || !this.#offset; } isDate() { return this.#hasDate && !this.#hasTime; } isTime() { return this.#hasTime && !this.#hasDate; } isValid() { return this.#hasDate || this.#hasTime; } toISOString() { let iso = super.toISOString(); if (this.isDate()) return iso.slice(0, 10); if (this.isTime()) return iso.slice(11, 23); if (this.#offset === null) return iso.slice(0, -1); if (this.#offset === "Z") return iso; let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6); offset = this.#offset[0] === "-" ? offset : -offset; return (/* @__PURE__ */ new Date(this.getTime() - offset * 6e4)).toISOString().slice(0, -1) + this.#offset; } static wrapAsOffsetDateTime(jsDate, offset = "Z") { let date = new TomlDate(jsDate); date.#offset = offset; return date; } static wrapAsLocalDateTime(jsDate) { let date = new TomlDate(jsDate); date.#offset = null; return date; } static wrapAsLocalDate(jsDate) { let date = new TomlDate(jsDate); date.#hasTime = false; date.#offset = null; return date; } static wrapAsLocalTime(jsDate) { let date = new TomlDate(jsDate); date.#hasDate = false; date.#offset = null; return date; } }; //#endregion //#region src/error.ts function getLineColFromPtr(string, ptr) { let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g); return [lines.length, lines.pop().length + 1]; } function makeCodeBlock(string, line, column) { let lines = string.split(/\r\n|\n|\r/g); let codeblock = ""; let numberLen = (Math.log10(line + 1) | 0) + 1; for (let i = line - 1; i <= line + 1; i++) { let l = lines[i - 1]; if (!l) continue; codeblock += i.toString().padEnd(numberLen, " "); codeblock += ": "; codeblock += l; codeblock += "\n"; if (i === line) { codeblock += " ".repeat(numberLen + column + 2); codeblock += "^\n"; } } return codeblock; } var TomlError = class extends Error { line; column; codeblock; constructor(message, options) { const [line, column] = getLineColFromPtr(options.toml, options.ptr); const codeblock = makeCodeBlock(options.toml, line, column); super(`Invalid TOML document: ${message}\n\n${codeblock}`, options); this.line = line; this.column = column; this.codeblock = codeblock; } }; //#endregion //#region src/primitive.ts let INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/; let FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/; let LEADING_ZERO = /^[+-]?0[0-9_]/; function parseString(str, ptr) { let c = str[ptr++]; let first = c; let isLiteral = c === "'"; let isMultiline = c === str[ptr] && c === str[ptr + 1]; if (isMultiline) { if (str[ptr += 2] === "\n") ptr++; else if (str[ptr] === "\r" && str[ptr + 1] === "\n") ptr += 2; } let parsed = ""; let sliceStart = ptr; let state = 0; for (let i = ptr; i < str.length; i++) { c = str[i]; if (isMultiline && (c === "\n" || c === "\r" && str[i + 1] === "\n")) state = state && 3; else if (c < " " && c !== " " || c === "") throw new TomlError("control characters are not allowed in strings", { toml: str, ptr: i }); else if ((!state || state === 3) && c === first && (!isMultiline || str[i + 1] === first && str[i + 2] === first)) { if (isMultiline) { if (str[i + 3] === first) i++; if (str[i + 3] === first) i++; } return [state ? parsed : parsed + str.slice(sliceStart, i), i + (isMultiline ? 3 : 1)]; } else if (!state) { if (!isLiteral && c === "\\") { parsed += str.slice(sliceStart, sliceStart = i); state = 1; } } else if (state === 1) if (c === "x" || c === "u" || c === "U") { let value = 0; let len = c === "x" ? 2 : c === "u" ? 4 : 8; for (let j = 0; j < len; j++, i++) { let hex = str.charCodeAt(i + 1); let digit = hex >= 48 && hex <= 57 ? hex - 48 : hex >= 65 && hex <= 70 ? hex - 65 + 10 : hex >= 97 && hex <= 102 ? hex - 97 + 10 : -1; if (digit < 0) throw new TomlError("invalid non-hex character in unicode escape", { toml: str, ptr: i + 1 }); value = value << 4 | digit; } if (value < 0 || value > 1114111 || value >= 55296 && value <= 57343) throw new TomlError("invalid unicode escape", { toml: str, ptr: i }); parsed += String.fromCodePoint(value); sliceStart = i + 1; state = 0; } else if (c === " " || c === " ") state = 2; else { if (c === "b") parsed += "\b"; else if (c === "t") parsed += " "; else if (c === "n") parsed += "\n"; else if (c === "f") parsed += "\f"; else if (c === "r") parsed += "\r"; else if (c === "e") parsed += "\x1B"; else if (c === "\"") parsed += "\""; else if (c === "\\") parsed += "\\"; else throw new TomlError("unrecognized escape sequence", { toml: str, ptr: i }); sliceStart = i + 1; state = 0; } else if (c !== " " && c !== " ") { if (state === 2) throw new TomlError("invalid escape: only line-ending whitespace may be escaped", { toml: str, ptr: sliceStart }); state = !isLiteral && c === "\\" ? 1 : 0; sliceStart = i; } } throw new TomlError("unfinished string", { toml: str, ptr }); } function parseValue(value, toml, ptr, integersAsBigInt) { if (value === "true") return true; if (value === "false") return false; if (value === "-inf") return -Infinity; if (value === "inf" || value === "+inf") return Infinity; if (value === "nan" || value === "+nan" || value === "-nan") return NaN; if (value === "-0") return integersAsBigInt ? 0n : 0; let isInt = INT_REGEX.test(value); if (isInt || FLOAT_REGEX.test(value)) { if (LEADING_ZERO.test(value)) throw new TomlError("leading zeroes are not allowed", { toml, ptr }); value = value.replace(/_/g, ""); let numeric = +value; if (isNaN(numeric)) throw new TomlError("invalid number", { toml, ptr }); if (isInt) { if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) throw new TomlError("integer value cannot be represented losslessly", { toml, ptr }); if (isInt || integersAsBigInt === true) numeric = BigInt(value); } return numeric; } const date = new TomlDate(value); if (!date.isValid()) throw new TomlError("invalid value", { toml, ptr }); return date; } //#endregion //#region src/util.ts function indexOfNewline(str, start = 0, end = str.length) { let idx = str.indexOf("\n", start); if (str[idx - 1] === "\r") idx--; return idx <= end ? idx : -1; } function skipComment(str, ptr) { for (let i = ptr; i < str.length; i++) { let c = str[i]; if (c === "\n") return i; if (c === "\r" && str[i + 1] === "\n") return i + 1; if (c < " " && c !== " " || c === "") throw new TomlError("control characters are not allowed in comments", { toml: str, ptr }); } return str.length; } function skipVoid(str, ptr, banNewLines, banComments) { let c; while (1) { while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n")) ptr++; if (banComments || c !== "#") break; ptr = skipComment(str, ptr); } return ptr; } function skipUntil(str, ptr, sep, end, banNewLines = false) { if (!end) { ptr = indexOfNewline(str, ptr); return ptr < 0 ? str.length : ptr; } for (let i = ptr; i < str.length; i++) { let c = str[i]; if (c === "#") i = indexOfNewline(str, i); else if (c === sep) return i + 1; else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) return i; } throw new TomlError("cannot find end of structure", { toml: str, ptr }); } //#endregion //#region src/extract.ts function sliceAndTrimEndOf(str, startPtr, endPtr) { let value = str.slice(startPtr, endPtr); let commentIdx = value.indexOf("#"); if (commentIdx > -1) { skipComment(str, commentIdx); value = value.slice(0, commentIdx); } return [value.trimEnd(), commentIdx]; } function extractValue(str, ptr, end, depth, integersAsBigInt) { if (depth === 0) throw new TomlError("document contains excessively nested structures. aborting.", { toml: str, ptr }); let c = str[ptr]; if (c === "[" || c === "{") { let [value, endPtr] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt); if (end) { endPtr = skipVoid(str, endPtr); if (str[endPtr] === ",") endPtr++; else if (str[endPtr] !== end) throw new TomlError("expected comma or end of structure", { toml: str, ptr: endPtr }); } return [value, endPtr]; } if (c === "\"" || c === "'") { let [parsed, endPtr] = parseString(str, ptr); if (end) { endPtr = skipVoid(str, endPtr); if (str[endPtr] && str[endPtr] !== "," && str[endPtr] !== end && str[endPtr] !== "\n" && str[endPtr] !== "\r") throw new TomlError("unexpected character encountered", { toml: str, ptr: endPtr }); if (str[endPtr] === ",") endPtr++; } return [parsed, endPtr]; } let endPtr = skipUntil(str, ptr, ",", end); let slice = sliceAndTrimEndOf(str, ptr, endPtr - (str[endPtr - 1] === "," ? 1 : 0)); if (!slice[0]) throw new TomlError("incomplete key-value declaration: no value specified", { toml: str, ptr }); if (end && slice[1] > -1) { endPtr = skipVoid(str, ptr + slice[1]); if (str[endPtr] === ",") endPtr++; } return [parseValue(slice[0], str, ptr, integersAsBigInt), endPtr]; } //#endregion //#region src/struct.ts let KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/; function parseKey(str, ptr, end = "=") { let dot = ptr - 1; let parsed = []; let endPtr = str.indexOf(end, ptr); if (endPtr < 0) throw new TomlError("incomplete key-value: cannot find end of key", { toml: str, ptr }); do { let c = str[ptr = ++dot]; if (c !== " " && c !== " ") if (c === "\"" || c === "'") { if (c === str[ptr + 1] && c === str[ptr + 2]) throw new TomlError("multiline strings are not allowed in keys", { toml: str, ptr }); let [part, eos] = parseString(str, ptr); dot = str.indexOf(".", eos); let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot); let newLine = indexOfNewline(strEnd); if (newLine > -1) throw new TomlError("newlines are not allowed in keys", { toml: str, ptr: ptr + dot + newLine }); if (strEnd.trimStart()) throw new TomlError("found extra tokens after the string part", { toml: str, ptr: eos }); if (endPtr < eos) { endPtr = str.indexOf(end, eos); if (endPtr < 0) throw new TomlError("incomplete key-value: cannot find end of key", { toml: str, ptr }); } parsed.push(part); } else { dot = str.indexOf(".", ptr); let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot); if (!KEY_PART_RE.test(part)) throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", { toml: str, ptr }); parsed.push(part.trimEnd()); } } while (dot + 1 && dot < endPtr); return [parsed, skipVoid(str, endPtr + 1, true, true)]; } function parseInlineTable(str, ptr, depth, integersAsBigInt) { let res = {}; let seen = /* @__PURE__ */ new Set(); let c; ptr++; while ((c = str[ptr++]) !== "}" && c) if (c === ",") throw new TomlError("expected value, found comma", { toml: str, ptr: ptr - 1 }); else if (c === "#") ptr = skipComment(str, ptr); else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") { let k; let t = res; let hasOwn = false; let [key, keyEndPtr] = parseKey(str, ptr - 1); for (let i = 0; i < key.length; i++) { if (i) t = hasOwn ? t[k] : t[k] = {}; k = key[i]; if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) throw new TomlError("trying to redefine an already defined value", { toml: str, ptr }); if (!hasOwn && k === "__proto__") Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true }); } if (hasOwn) throw new TomlError("trying to redefine an already defined value", { toml: str, ptr }); let [value, valueEndPtr] = extractValue(str, keyEndPtr, "}", depth - 1, integersAsBigInt); seen.add(value); t[k] = value; ptr = valueEndPtr; } if (!c) throw new TomlError("unfinished table encountered", { toml: str, ptr }); return [res, ptr]; } function parseArray(str, ptr, depth, integersAsBigInt) { let res = []; let c; ptr++; while ((c = str[ptr++]) !== "]" && c) if (c === ",") throw new TomlError("expected value, found comma", { toml: str, ptr: ptr - 1 }); else if (c === "#") ptr = skipComment(str, ptr); else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") { let e = extractValue(str, ptr - 1, "]", depth - 1, integersAsBigInt); res.push(e[0]); ptr = e[1]; } if (!c) throw new TomlError("unfinished array encountered", { toml: str, ptr }); return [res, ptr]; } //#endregion //#region src/parse.ts function peekTable(key, table, meta, type) { let t = table; let m = meta; let k; let hasOwn = false; let state; for (let i = 0; i < key.length; i++) { if (i) { t = hasOwn ? t[k] : t[k] = {}; m = (state = m[k]).c; if (type === 0 && (state.t === 1 || state.t === 2)) return null; if (state.t === 2) { let l = t.length - 1; t = t[l]; m = m[l].c; } } k = key[i]; if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) return null; if (!hasOwn) { if (k === "__proto__") { Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true }); Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true }); } m[k] = { t: i < key.length - 1 && type === 2 ? 3 : type, d: false, i: 0, c: {} }; } } state = m[k]; if (state.t !== type && !(type === 1 && state.t === 3)) return null; if (type === 2) { if (!state.d) { state.d = true; t[k] = []; } t[k].push(t = {}); state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} }; } if (state.d) return null; state.d = true; if (type === 1) t = hasOwn ? t[k] : t[k] = {}; else if (type === 0 && hasOwn) return null; return [ k, t, state.c ]; } function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) { let res = {}; let meta = {}; let tbl = res; let m = meta; for (let ptr = skipVoid(toml, 0); ptr < toml.length;) { if (toml[ptr] === "[") { let isTableArray = toml[++ptr] === "["; let k = parseKey(toml, ptr += +isTableArray, "]"); if (isTableArray) { if (toml[k[1] - 1] !== "]") throw new TomlError("expected end of table declaration", { toml, ptr: k[1] - 1 }); k[1]++; } let p = peekTable(k[0], res, meta, isTableArray ? 2 : 1); if (!p) throw new TomlError("trying to redefine an already defined table or value", { toml, ptr }); m = p[2]; tbl = p[1]; ptr = k[1]; } else { let k = parseKey(toml, ptr); let p = peekTable(k[0], tbl, m, 0); if (!p) throw new TomlError("trying to redefine an already defined table or value", { toml, ptr }); let v = extractValue(toml, k[1], void 0, maxDepth, integersAsBigInt); p[1][p[0]] = v[0]; ptr = v[1]; } ptr = skipVoid(toml, ptr, true); if (toml[ptr] && toml[ptr] !== "\n" && toml[ptr] !== "\r") throw new TomlError("each key-value declaration must be followed by an end-of-line", { toml, ptr }); ptr = skipVoid(toml, ptr); } return res; } //#endregion //#region src/stringify.ts let BARE_KEY = /^[a-z0-9-_]+$/i; function extendedTypeOf(obj) { let type = typeof obj; if (type === "object") { if (Array.isArray(obj)) return "array"; if (obj instanceof Date) return "date"; } return type; } function isArrayOfTables(obj) { for (let i = 0; i < obj.length; i++) if (extendedTypeOf(obj[i]) !== "object") return false; return obj.length != 0; } function formatString(s) { return JSON.stringify(s).replace(/\x7f/g, "\\u007f"); } function stringifyValue(val, type, depth, numberAsFloat) { if (depth === 0) throw new Error("Could not stringify the object: maximum object depth exceeded"); if (type === "number") { if (isNaN(val)) return "nan"; if (val === Infinity) return "inf"; if (val === -Infinity) return "-inf"; if (Number.isInteger(val) && (numberAsFloat || !Number.isSafeInteger(val))) return val.toFixed(1); return val.toString(); } if (type === "bigint" || type === "boolean") return val.toString(); if (type === "string") return formatString(val); if (type === "date") { if (isNaN(val.getTime())) throw new TypeError("cannot serialize invalid date"); return val.toISOString(); } if (type === "object") return stringifyInlineTable(val, depth, numberAsFloat); if (type === "array") return stringifyArray(val, depth, numberAsFloat); } function stringifyInlineTable(obj, depth, numberAsFloat) { let keys = Object.keys(obj); if (keys.length === 0) return "{}"; let res = "{ "; for (let i = 0; i < keys.length; i++) { let k = keys[i]; if (i) res += ", "; res += BARE_KEY.test(k) ? k : formatString(k); res += " = "; res += stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat); } return res + " }"; } function stringifyArray(array, depth, numberAsFloat) { if (array.length === 0) return "[]"; let res = "[ "; for (let i = 0; i < array.length; i++) { if (i) res += ", "; if (array[i] === null || array[i] === void 0) throw new TypeError("arrays cannot contain null or undefined values"); res += stringifyValue(array[i], extendedTypeOf(array[i]), depth - 1, numberAsFloat); } return res + " ]"; } function stringifyArrayTable(array, key, depth, numberAsFloat) { if (depth === 0) throw new Error("Could not stringify the object: maximum object depth exceeded"); let res = ""; for (let i = 0; i < array.length; i++) { res += `${res && "\n"}[[${key}]]\n`; res += stringifyTable(0, array[i], key, depth, numberAsFloat); } return res; } function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) { if (depth === 0) throw new Error("Could not stringify the object: maximum object depth exceeded"); let preamble = ""; let tables = ""; let keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { let k = keys[i]; if (obj[k] !== null && obj[k] !== void 0) { let type = extendedTypeOf(obj[k]); if (type === "symbol" || type === "function") throw new TypeError(`cannot serialize values of type '${type}'`); let key = BARE_KEY.test(k) ? k : formatString(k); if (type === "array" && isArrayOfTables(obj[k])) tables += (tables && "\n") + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat); else if (type === "object") { let tblKey = prefix ? `${prefix}.${key}` : key; tables += (tables && "\n") + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat); } else { preamble += key; preamble += " = "; preamble += stringifyValue(obj[k], type, depth, numberAsFloat); preamble += "\n"; } } } if (tableKey && (preamble || !tables)) preamble = preamble ? `[${tableKey}]\n${preamble}` : `[${tableKey}]`; return preamble && tables ? `${preamble}\n${tables}` : preamble || tables; } function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) { if (extendedTypeOf(obj) !== "object") throw new TypeError("stringify can only be called with an object"); let str = stringifyTable(0, obj, "", maxDepth, numbersAsFloat); if (str[str.length - 1] !== "\n") return str + "\n"; return str; } //#endregion //#region src/index.ts var src_default = { parse, stringify, TomlDate, TomlError }; //#endregion exports.TomlDate = TomlDate; exports.TomlError = TomlError; exports.default = src_default; exports.parse = parse; exports.stringify = stringify; //# sourceMappingURL=index.cjs.map