@mpen/jsjson
v0.2.3
Published
Compact cross-platform JSON serialization supporting any JavaScript data type.
Readme
@mpen/jsjson
Compact, type-preserving, and high-performance JSON serialization for standard and rich JavaScript data types. Targets both Browser and Node (neutral).
Standard JSON.stringify silently strips rich data types (such as Date, Map, Set, BigInt, RegExp, URL, TypedArrays, and Symbol) or coerces them to simpler types (like converting -0, NaN, and Infinity to standard 0 or null).
@mpen/jsjson serializes any rich JavaScript datatype into an extremely compact, recursive array-prefixing format that preserves exact type integrity, and parses it back flawlessly.
Key Features
- Compact Array-Prefixing Schema: Special data nodes (e.g. Dates, Maps, Sets, buffers, BigInts, etc.) are serialized into arrays prefixed by a compact integer type ID (e.g.
[3,123456]for Date,[11,1,2,3]for ArrayBuffer). Primitive types and plain objects require zero overhead and serialize directly to standard JSON without wrapping. - Automatic Tabular Optimization (
TABLE): Detects when an array contains plain objects with identical key sets, extracting the schema keys once and serializing rows into a value-only matrix. This avoids repeating key strings (like"id","name") for large data payloads. - Broader Datatype Support: Out-of-the-box support for 19+ different datatypes, including global/local
Symbols,ArrayBuffer, typed arrays (e.g.,Uint8Array,Float64Array),DataView,URL,Errorcallstacks, and special numbers (like-0,NaN, andInfinity). - Clean, Explicit Security Boundaries: Standard native
JSONbehavior is maintained; circular structures throw aTypeErrorand functions throw a clear exception rather than silently converting toundefinedor executing insecure code. - Zero Dependency Overhead: Fully neutral, lightweight footprint with strict TypeScript types.
Installation
bun add @mpen/jsjsonUsage
Preserving Rich Types
import { jsjStringify, jsjParse } from '@mpen/jsjson'
const original = {
date: new Date(123456),
set: new Set([1, 2, 3]),
big: 12345678901234567890n,
regex: /foo/gi,
url: new URL('https://example.com'),
nanValue: NaN,
negativeZero: -0,
}
// 1. Stringify to compact JSON string
const json = jsjStringify(original)
console.log(json)
// Yields: {"date":[3,123456],"set":[2,1,2,3],"big":[6,"12345678901234567890"],...}
// 2. Parse back with full type safety
const parsed = jsjParse<typeof original>(json)
console.log(parsed.date instanceof Date) // true
console.log(parsed.set instanceof Set) // true
console.log(parsed.big === 12345678901234567890n) // true
console.log(Object.is(parsed.negativeZero, -0)) // trueTabular Schema (TABLE) Optimization
If you stringify an array of objects that share identical key lists, @mpen/jsjson automatically optimizes the payload into a schema header and row values:
const users = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
]
const json = jsjStringify(users)
console.log(json)
// Yields: [4,["age","name"],[[30,"Alice"],[25,"Bob"]]]Key strings ("name", "age") are printed only once in the header array, significantly shrinking large data collection payloads!
Supported Datatypes
Every value maps to a compact, single- or double-digit integer ID:
| Datatype | Enum Member | ID | Serialized Form Example |
| :---------------------- | :-------------------------- | :--- | :------------------------------ |
| undefined | JsType.UNDEFINED | 0 | [0] |
| null | - | - | null |
| string | - | - | "hello" |
| number | - | - | 42 |
| true | - | - | true |
| false | - | - | false |
| Array | JsType.ARRAY | 1 | [1,1,2,3] |
| Object | - | - | {"a":1} |
| Date | JsType.DATE | 3 | [3,123456] |
| TABLE (Optimized) | JsType.TABLE | 4 | [4,["a"],[[1],[2]]] |
| Map | JsType.MAP | 5 | [5,["k","v"]] |
| Set | JsType.SET | 2 | [2,1,2] |
| RegExp | JsType.REGEX | 7 | [7,"foo","gi"] |
| BigInt | JsType.BIGINT | 6 | [6,"1234567890"] |
| Symbol (with key) | JsType.SYMBOL_WITH_KEY | 10 | [10,"sym_key"] |
| Symbol (without key) | JsType.SYMBOL_WITHOUT_KEY | 29 | [29,"sym_desc"] |
| Error | JsType.ERROR | 9 | [9,"TypeError","msg","stack"] |
| URL | JsType.URL | 8 | [8,"https://foo.com"] |
| ArrayBuffer | JsType.ARRAY_BUFFER | 11 | [11,"AQID"] |
| Uint8Array | JsType.UINT8_ARRAY | 13 | [13,"BAUG"] |
| Node.js Buffer | JsType.NODE_BUFFER | 24 | [24,"BwgJ"] |
| DataView | JsType.DATA_VIEW | 23 | [23,"CgQ="] |
| NaN | JsType.NAN | 25 | [25] |
| Infinity | JsType.INFINITY | 26 | [26] |
| -Infinity | JsType.NEG_INFINITY | 27 | [27] |
| -0 | JsType.NEG_ZERO | 28 | [28] |
Comparison: @mpen/jsjson vs superjson
While both libraries aim to solve type preservation across serialization boundaries, they make different architectural trade-offs:
1. Serialized Output Size (Compactness)
superjson: Generates a standard JSON object paired with a separate verbose, string-path-based metadata block mapping type tags to keys:
Size: ~112 characters.// superjson output: { "json": { "date": "1970-01-01T00:02:03.456Z" }, "meta": { "values": { "date": ["Date"] } } }@mpen/jsjson: Encodes nodes as recursive prefix arrays or direct JSON primitives/objects:
Size: 21 characters (~81% smaller).// jsjson output: { "date": [3, 123456] }
2. Tabular Schema Optimizations
superjson: Does not optimize arrays of objects. Key strings are repeated on every element inside the JSON tree, bloating payloads for database results or large client lists.@mpen/jsjson: Automatically packages lists of matching-key objects into high-performance, single-instance schema headers (JsType.TABLE), dropping key-string duplication altogether.
3. Out-of-the-Box Type Support
Our package supports a wider array of standard and rich JS primitives:
@mpen/jsjson: Native out-of-the-box support forArrayBuffer, all typed arrays (e.g.,Uint8Array,Float64Array),DataView,URL, global/localSymbols, and strict preservation of-0,NaN,Infinity, and-Infinityusing lightweight 4-character representations.superjson: Lacks out-of-the-box support for typed arrays,ArrayBuffer, global/localSymbols, and-0sign preservation unless custom registers are manually injected.
4. CPU & Memory Performance
superjson: Tracks nested string paths throughout standard objects recursively to assemble its complexmetatree, incurring higher CPU cycle and memory allocation overhead.@mpen/jsjson: Traverses the tree in a single pass to build a standard JS array representation, and hands it off directly to standard native C++JSON.stringify/JSON.parse. It is extremely fast and lightweight.
5. Circular References & Referential Equality (Trade-Off)
superjson: Supports reconstructing identical object references across the tree (referential equality) and serializes circular references by logging reference paths inside themetablock.@mpen/jsjson: Focuses purely on data value serialization. It detects circular references and throws a cleanTypeError(matching nativeJSONbehavior), choosing not to reconstruct referential equality (recreated values are deep clones).
License
MIT
