Initial Commit
This commit is contained in:
123
packages/foundation/data/json/README.md
Normal file
123
packages/foundation/data/json/README.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# @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.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [errors](./src/errors/json-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [json-error](./src/errors/json-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
<!-- end:auto-docs --> - [json-error](./src/errors/json-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [json-error](./src/errors/json-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [json-error](./src/errors/json-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [json-error](./src/errors/json-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_ - [safe-json](./src/safe-json/safe-json.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ - [types](./src/types/types.md) — <!-- begin:auto-docs --> _(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
|
||||
|
||||
Reference in New Issue
Block a user