Files
2026-05-27 23:47:53 +01:00

5.5 KiB

@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

- [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

bun add @suaveplan/json

Quick start

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)
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

interface ParseOptions {
  forbiddenKeys?: string[];  // default: ["__proto__", "constructor", "prototype"]
  maxDepth?: number;         // default: 50
  signal?: AbortSignal;
}

StringifyOptions

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:

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.