import { MAX_ARRAY_INDEX, MAX_ARRAY_LEN } from './constants.js'; const name_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$'; const reserved_names = /^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/; /** @type {Record} */ export const escaped = { '<': '\\u003C', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t', '\u2028': '\\u2028', '\u2029': '\\u2029' }; export class DevalueError extends Error { /** * @param {string} message * @param {string[]} keys * @param {any} [value] - The value that failed to be serialized * @param {any} [root] - The root value being serialized */ constructor(message, keys, value, root) { super(message); this.name = 'DevalueError'; this.path = keys.join(''); this.value = value; this.root = root; } } /** * Marks a Map key on the path being serialized. The key itself is recorded * right after the marker, so nothing is formatted or allocated per entry. */ export const MAP_KEY = Symbol(); /** * Formats the path recorded on the way to the value being serialized — a * property key, an array index, or `MAP_KEY` followed by a Map key — into * the segments a `DevalueError` joins. Only runs when an error is raised. * @param {any[]} keys * @param {(key: any) => string} format_map_key * @returns {string[]} */ export function format_path(keys, format_map_key) { /** @type {string[]} */ const path = []; for (let i = 0; i < keys.length; i += 1) { const segment = keys[i]; if (segment === MAP_KEY) { path.push(`.get(${format_map_key(keys[++i])})`); } else if (typeof segment === 'number') { path.push(`[${segment}]`); } else { path.push(stringify_key(segment)); } } return path; } /** @param {any} thing */ export function is_primitive(thing) { return thing === null || (typeof thing !== 'object' && typeof thing !== 'function'); } const object_proto_names = /* @__PURE__ */ Object.getOwnPropertyNames(Object.prototype) .sort() .join('\0'); /** @param {any} thing */ export function is_plain_object(thing) { const proto = Object.getPrototypeOf(thing); return ( proto === Object.prototype || proto === null || Object.getPrototypeOf(proto) === null || Object.getOwnPropertyNames(proto).sort().join('\0') === object_proto_names ); } /** @param {any} thing */ export function get_type(thing) { return Object.prototype.toString.call(thing).slice(8, -1); } /** @param {any} thing */ export function is_buffer(thing) { return typeof Buffer !== 'undefined' && Buffer.isBuffer(thing); } /** * Emit an array whose storage is not proportional to its declared length. * Touching and deleting the largest valid index forces dictionary elements * before setting the length; assigning .length on [] can still eagerly allocate. * @param {number} length */ export function stringify_sparse_array(length) { return `(function(a){a[${MAX_ARRAY_INDEX}]=0;delete a[${MAX_ARRAY_INDEX}];a.length=${length};return a}([]))`; } /** Returns the compact JavaScript identifier at `index`. @param {number} index */ export function get_name(index) { let name = ''; do { name = name_chars[index % name_chars.length] + name; index = ~~(index / name_chars.length) - 1; } while (index >= 0); return reserved_names.test(name) ? `${name}0` : name; } // characters `stringify_string` always escapes: quote, backslash, `<`, control // characters and the line/paragraph separators. Surrogates are only escaped // when unpaired, which `isWellFormed` detects natively, so well-formed pairs // such as emoji stay on the fast path const needs_escape = /["<\\\u0000-\u001f\u2028\u2029]/; // `JSON.stringify` already emits every escape we need except these three, // which it leaves raw: `<` (so output is safe inside a `