# @suaveplan/json - Safe JSON parse/stringify with prototype-pollution protection, circular-ref detection, and OTel instrumentation ## Summary Safe, enterprise-grade JSON utilities with prototype-pollution protection, circular-reference detection, and OpenTelemetry instrumentation. ## Documentation tree - [errors](./src/errors/json-error.md) — _(no submodules)_ - [json-error](./src/errors/json-error.md) — _(no submodules)_ - [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_ - [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_ - [types](./src/types/types.md) — _(no submodules)_ - [types](./src/types/types.md) — _(no submodules)_ - [json-error](./src/errors/json-error.md) — _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_- [types](./src/types/types.md) — _(no submodules)_- [types](./src/types/types.md) — _(no submodules)_- [json-error](./src/errors/json-error.md) — _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_- [types](./src/types/types.md) — _(no submodules)_- [types](./src/types/types.md) — _(no submodules)_- [json-error](./src/errors/json-error.md) — _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_- [types](./src/types/types.md) — _(no submodules)_- [types](./src/types/types.md) — _(no submodules)_- [json-error](./src/errors/json-error.md) — _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_ - [safe-json](./src/safe-json/safe-json.md) — _(no submodules)_- [types](./src/types/types.md) — _(no submodules)_ - [types](./src/types/types.md) — _(no submodules)_ ## Install ```bash bun add @suaveplan/json ``` ## Quick start ```ts import { safeParse, safeStringify, tryParse, isValidJSON, getStringifySize } from "@suaveplan/json"; // Safe parse — throws JsonError on invalid JSON or forbidden keys const obj = safeParse<{ a: number }>('{"a": 1}'); // Safe stringify — throws JsonError on circular references const json = safeStringify(obj); // Non-throwing parse const val = tryParse("bad input"); // undefined // Validity check (handles "null" correctly) isValidJSON("null"); // true isValidJSON("bad"); // false // UTF-8 byte size of JSON representation getStringifySize({ hello: "world" }); // 15 ``` ## Modules | Module | Description | |--------|-------------| | `@suaveplan/json` | Main entry — all public exports | | `@suaveplan/json/safe-json` | `safeParse`, `safeStringify`, `tryParse`, `isValidJSON`, `getStringifySize` | | `@suaveplan/json/errors` | `JsonError`, `JSON_CODES`, `JsonCode` | ## Error handling Every failure is a structured `JsonError` with a stable code: | Code | Trigger | |------|---------| | `JSON.PARSE.FAILED` | Invalid JSON syntax or aborted `AbortSignal` | | `JSON.PARSE.PROTOTYPE_POLLUTION` | Forbidden key (`__proto__`, `constructor`, `prototype`) | | `JSON.STRINGIFY.CIRCULAR` | True circular reference detected | | `JSON.STRINGIFY.FAILED` | Aborted signal or non-serialisable value (e.g. `BigInt`) | ```ts import { safeParse, JsonError, JSON_CODES } from "@suaveplan/json"; try { safeParse('{"__proto__": {}}'); } catch (err) { if (err instanceof JsonError && err.code === JSON_CODES["PARSE.PROTOTYPE_POLLUTION"]) { console.error("Prototype pollution blocked"); } } ``` ## Options ### `ParseOptions` ```ts interface ParseOptions { forbiddenKeys?: string[]; // default: ["__proto__", "constructor", "prototype"] maxDepth?: number; // default: 50 signal?: AbortSignal; } ``` ### `StringifyOptions` ```ts interface StringifyOptions { space?: number; signal?: AbortSignal; maxDepth?: number; // default: 100 } ``` ## Benchmarks Measured on Intel Xeon E5-2696 v4 @ 2.20 GHz, Bun 1.3.13: ```text benchmark avg (min … max) ------------------------------------------- safeParse small 9.84 µs/iter JSON.parse small 592.70 ns/iter safeStringify small 1.99 µs/iter JSON.stringify small 212.99 ns/iter safeParse medium 42.11 µs/iter safeStringify medium 11.92 µs/iter ``` The overhead relative to the native functions comes from the OTel span creation, reviver/replacer function calls, and ancestor-stack tracking. In production with a no-op tracer the spans cost ~0 ns. ## Links - [`src/safe-json/safe-json.md`](src/safe-json/safe-json.md) — implementation notes - [`src/errors/json-error.md`](src/errors/json-error.md) — error codes - [`src/types/types.md`](src/types/types.md) — option types