/** * @license * Copyright 2022-2026 Matter.js Authors * SPDX-License-Identifier: Apache-2.0 */ import { Bytes } from "./Bytes.js"; /** * Like JSON.stringify but targets well-formed JS and is slightly more readable. */ export function serialize(value: unknown) { const visited = new Set(); function asValidKey(key: string) { if (key.match(/[a-z_$][\w$]*/i)) { return key; } return JSON.stringify(key); } function serializeOne(value: unknown): string | undefined { if (value === undefined) { return; } if (value === null) { return "null"; } if ((value as { [serialize.SERIALIZE](): string })[serialize.SERIALIZE]) { return (value as { [serialize.SERIALIZE](): string })[serialize.SERIALIZE](); } if (typeof value === "function") { return; } if (typeof value === "bigint" || value instanceof BigInt) { return value.toString(); } if (typeof value === "number" || value instanceof Number) { return value.toString(); } if (typeof value === "string" || value instanceof String) { return JSON.stringify(value); } if (typeof value === "boolean") { return value ? "true" : "false"; } if (Bytes.isBytes(value)) { return Bytes.toHex(value); } // Composite objects after this if (visited.has(value)) { return; } if ((value as { toJSON(): string }).toJSON) { value = JSON.parse(JSON.stringify(value)); } try { visited.add(value); if (Array.isArray(value)) { if (value.length) { return `[ ${value.map(serializeOne).join(", ")} ]`; } return "[]"; } const entries = Object.entries(value as {}) .map(([k, v]) => [k, serializeOne(v)]) .filter(([_k, v]) => v !== undefined) .map(([k, v]) => `${asValidKey(k ?? "")}: ${v}`); if (!entries.length) { return "{}"; } return `{ ${entries.join(", ")} }`; } finally { visited.delete(value); } } return serializeOne(value); } export namespace serialize { /** * Custom serialization function key. */ export const SERIALIZE = Symbol("SERIALIZE"); /** * Mark a value as serialized so the serializer just uses its string * representation. */ export function asIs(value: any) { if (typeof value === "string") { value = new String(value); } if (value !== undefined && value !== null) { value[SERIALIZE] = function () { return this.toString(); }; } return value; } /** * Test whether a value serializes as a structure or a primitive. */ export function isPrimitive(value: any) { if ( value === undefined || value === null || value instanceof Date || ArrayBuffer.isView(value) || value[SERIALIZE] ) { return true; } if (Array.isArray(value)) { return false; } return typeof value !== "object"; } }