@push.rocks/smartjson
A library for handling typed JSON data, providing functionalities for parsing, stringifying, and working with JSON objects, including support for encoding and decoding buffers.
readme.md for @push.rocks/smartjson
🚀 Typed JSON handling for modern Node.js and TypeScript applications
A powerful library for working with JSON in TypeScript, providing type-safe serialization, advanced buffer handling, deep object comparison, and support for complex class instances. Perfect for applications that need reliable JSON manipulation with full TypeScript support.
Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.
Installation
# Using npm
npm install @push.rocks/smartjson --save
# Using yarn
yarn add @push.rocks/smartjson
# Using pnpm (recommended)
pnpm add @push.rocks/smartjson
Features
✨ Type-Safe JSON Operations - Full TypeScript support with proper typing
🎯 Class Instance Serialization - Fold and unfold class instances to/from JSON
🔐 Buffer & Binary Support - Seamless handling of Buffers and Typed Arrays
📊 JSON Lines Support - Parse, stringify and compare JSONL data streams
🎨 Pretty Printing - Beautiful formatted JSON output
⚡ Stable Stringification - Consistent key ordering for reliable comparisons
♻️ Circular Reference Handling - Safe one-way stringify for objects with cycles
🔍 Deep Equality Checks - Compare complex objects and JSON structures
🌐 Base64 Encoding - Built-in base64 JSON encoding/decoding
Quick Start
import * as smartjson from '@push.rocks/smartjson';
// Parse JSON with automatic buffer handling
const parsed = smartjson.parse('{"name":"example","data":{"type":"Buffer","data":[1,2,3]}}');
// Stringify with stable key ordering
const json = smartjson.stringify({ z: 1, a: 2, m: 3 });
// Result: '{"a":2,"m":3,"z":1}'
// Pretty print for human readability
const pretty = smartjson.stringifyPretty({ hello: 'world', count: 42 });
// Result:
// {
// "hello": "world",
// "count": 42
// }
Core Functions
JSON Parsing and Stringification
// Standard parsing with automatic Buffer detection
const obj = smartjson.parse('{"hello":"world"}');
// Stable stringification (consistent key ordering)
const jsonStr = smartjson.stringify({ name: 'test', id: 1 });
// Pretty printing for debugging
const prettyJson = smartjson.stringifyPretty({
nested: {
data: 'value'
}
});
Safe One-Way Stringification (New in v5.1.0)
Handle circular references and unserializable values safely for hashing and comparisons:
// Create objects with circular references
const objA = { name: 'A' };
const objB = { name: 'B', ref: objA };
objA['ref'] = objB; // Circular reference!
// Normal stringify would throw, but stableOneWayStringify handles it
const safeJson = smartjson.stableOneWayStringify(objA);
// Circular references are replaced with "__cycle__"
// Perfect for object hashing
const hash1 = crypto.createHash('sha256')
.update(smartjson.stableOneWayStringify(complexObject))
.digest('hex');
// Handles unserializable values gracefully
const objWithGetter = {
normal: 'value',
get throwing() { throw new Error('Cannot serialize!'); }
};
const safe = smartjson.stableOneWayStringify(objWithGetter);
// Throwing getters are replaced with "__unserializable__"
// Custom key ordering for consistent hashes
const ordered = smartjson.stableOneWayStringify(
{ z: 1, a: 2 },
['a', 'z'] // Specify key order
);
Base64 JSON Encoding
Encode JSON data as base64 for safe transmission:
const myData = {
message: 'Hello World',
timestamp: Date.now()
};
// Encode to base64
const encoded = smartjson.stringifyBase64(myData);
console.log(encoded); // Base64 string
// Decode back to object
const decoded = smartjson.parseBase64(encoded);
console.log(decoded); // Original object
JSON Lines (JSONL) Support
Perfect for streaming data and log processing:
// Parse JSON Lines format
const jsonLines = `{"event":"start","time":1234}
{"event":"data","value":42}
{"event":"end","time":5678}`;
const events = smartjson.parseJsonL(jsonLines);
// Result: Array of parsed objects
// Produce JSONL from objects
const jsonlOut = smartjson.stringifyJsonL([
{ event: 'start', time: 1234 },
{ event: 'data', value: 42 },
{ event: 'end', time: 5678 }
]);
// Compare JSON Lines data
const jsonL1 = `{"id":1}\n{"id":2}`;
const jsonL2 = `{"id":1}\n{"id":2}`;
const isEqual = smartjson.deepEqualJsonLStrings(jsonL1, jsonL2); // true
Advanced Class Serialization
Creating Serializable Classes
Transform class instances to JSON and back while preserving type safety:
import { Smartjson, foldDec } from '@push.rocks/smartjson';
class User extends Smartjson {
@foldDec() accessor username: string;
@foldDec() accessor email: string;
@foldDec() accessor settings: UserSettings;
// Properties without @foldDec won't be serialized
private internalId: string;
constructor(username: string, email: string) {
super();
this.username = username;
this.email = email;
this.settings = new UserSettings();
this.internalId = Math.random().toString();
}
}
class UserSettings extends Smartjson {
@foldDec() accessor theme: 'light' | 'dark' = 'light';
@foldDec() accessor notifications: boolean = true;
}
// Create and serialize
const user = new User('john_doe', 'john@example.com');
user.settings.theme = 'dark';
// Convert to JSON
const jsonString = user.foldToJson();
console.log(jsonString);
// {"username":"john_doe","email":"john@example.com","settings":{"theme":"dark","notifications":true}}
// Restore from JSON with correct typing
const restoredUser = User.enfoldFromJson(jsonString);
console.log(restoredUser instanceof User); // true
console.log(restoredUser.settings instanceof UserSettings); // true
Working with Nested Objects
class Company extends Smartjson {
@foldDec() accessor name: string;
@foldDec() accessor employees: Employee[] = [];
addEmployee(employee: Employee) {
this.employees.push(employee);
}
}
class Employee extends Smartjson {
@foldDec() accessor name: string;
@foldDec() accessor role: string;
@foldDec() accessor salary: number;
constructor(name: string, role: string, salary: number) {
super();
this.name = name;
this.role = role;
this.salary = salary;
}
}
const company = new Company();
company.name = 'TechCorp';
company.addEmployee(new Employee('Alice', 'Developer', 100000));
company.addEmployee(new Employee('Bob', 'Designer', 90000));
// Serialize entire object graph
const json = company.foldToJson();
// Deserialize with all nested objects properly instantiated
const restored = Company.enfoldFromJson(json);
Buffer and Binary Data Handling
SmartJson seamlessly handles binary data in JSON:
// Automatic Buffer handling
const dataWithBuffer = {
name: 'BinaryData',
buffer: Buffer.from('Hello World'),
typedArray: new Uint8Array([1, 2, 3, 4, 5])
};
// Stringify (buffers are automatically encoded)
const jsonStr = smartjson.stringify(dataWithBuffer);
// Parse (buffers are automatically restored)
const restored = smartjson.parse(jsonStr);
console.log(restored.buffer); // Buffer
console.log(restored.typedArray); // Uint8Array
Deep Comparison
Compare complex objects with automatic normalization:
// Deep object comparison
const obj1 = {
nested: {
array: [1, 2, { deep: 'value' }],
flag: true
},
name: 'test'
};
const obj2 = {
name: 'test', // Different order
nested: {
flag: true, // Different order
array: [1, 2, { deep: 'value' }]
}
};
const isEqual = smartjson.deepEqualObjects(obj1, obj2); // true
Real-World Examples
API Response Caching
class CachedAPIResponse extends Smartjson {
@foldDec() accessor data: any;
@foldDec() accessor timestamp: number;
@foldDec() accessor endpoint: string;
isExpired(maxAge: number = 3600000): boolean {
return Date.now() - this.timestamp > maxAge;
}
static fromAPICall(endpoint: string, data: any): CachedAPIResponse {
const response = new CachedAPIResponse();
response.endpoint = endpoint;
response.data = data;
response.timestamp = Date.now();
return response;
}
}
// Store API response
const apiData = await fetch('/api/users');
const cached = CachedAPIResponse.fromAPICall('/api/users', await apiData.json());
localStorage.setItem('cached_users', cached.foldToJson());
// Retrieve and check
const stored = localStorage.getItem('cached_users');
if (stored) {
const cached = CachedAPIResponse.enfoldFromJson(stored);
if (!cached.isExpired()) {
return cached.data; // Use cached data
}
}
Configuration Management
class AppConfig extends Smartjson {
@foldDec() accessor apiUrl: string;
@foldDec() accessor features: Map<string, boolean> = new Map();
@foldDec() accessor limits: {
maxUploadSize: number;
maxConcurrentRequests: number;
};
enableFeature(name: string) {
this.features.set(name, true);
}
save() {
fs.writeFileSync('config.json', this.foldToJson());
}
static load(): AppConfig {
const json = fs.readFileSync('config.json', 'utf-8');
return AppConfig.enfoldFromJson(json);
}
}
API Reference
Core Functions
parse(jsonString: string): any- Parse JSON with automatic buffer handlingstringify(obj: any, simpleOrderArray?: string[], options?: Options): string- Convert to JSON with stable orderingstableOneWayStringify(obj: any, simpleOrderArray?: string[], options?: Options): string- Safe stringify with circular reference handling (one-way, for hashing/comparison)stringifyPretty(obj: any): string- Pretty print JSON with 2-space indentationstringifyBase64(obj: any): string- Encode JSON as base64parseBase64(base64String: string): any- Decode base64 JSONparseJsonL(jsonLinesString: string): any[]- Parse JSON Lines formatstringifyJsonL(items: any[]): string- Stringify array to JSON LinesdeepEqualObjects(obj1: any, obj2: any): boolean- Deep comparison of objectsdeepEqualJsonLStrings(jsonL1: string, jsonL2: string): boolean- Compare JSON Lines strings
Smartjson Class
Smartjson.enfoldFromObject<T>(obj: any): T- Create instance from plain objectSmartjson.enfoldFromJson<T>(json: string): T- Create instance from JSON stringinstance.foldToObject(): any- Convert instance to plain objectinstance.foldToJson(): string- Convert instance to JSON string
Decorators
@foldDec()- Mark class property for serialization (use withaccessorkeyword)
Performance Tips
- Use stable stringification for consistent hashing and comparison
- Enable pretty printing only for debugging (it's slower)
- Cache base64 encodings when repeatedly sending the same data
- Use JSON Lines for streaming large datasets
- Use stableOneWayStringify for objects with circular references (for hashing/caching)
- Prefer regular stringify for round-trip serialization without cycles
Migration Guide
If you're migrating from native JSON:
// Before
JSON.parse(jsonString);
JSON.stringify(object);
// After
smartjson.parse(jsonString); // Adds buffer support
smartjson.stringify(object); // Adds stable ordering & buffer support
Browser Support
This library supports modern browsers and Node.js environments. For older browsers, ensure you have appropriate polyfills for TextEncoder and TextDecoder.
License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the LICENSE file.
Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
Company Information
Task Venture Capital GmbH Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
changelog.md for @push.rocks/smartjson
2025-12-10 - 6.0.0 - BREAKING CHANGE(Smartjson)
Require TC39 Stage 3 decorators for @foldDec (use accessor), switch foldDec to initializer-based implementation, improve buffer encode/decode handling, bump dependencies and update docs/tests.
- foldDec now implements TC39 Stage 3 accessor decorators and must be used with the 'accessor' keyword (e.g. '@foldDec() accessor prop: T'). The decorator registers saveable properties via context.addInitializer so properties are tracked per-instance.
- Buffer handling rewritten: replacer/reviver handle Uint8Array and Buffer cross-platform; EncodedBuffer.data now uses a 'base64:' prefix and reviver returns a Uint8Array. This changes the serialized representation of buffers.
- tsconfig updated to remove experimentalDecorators and useDefineForClassFields overrides — project now targets modern TypeScript decorator support (ensure your toolchain supports Stage 3 decorators).
- Documentation (readme and hints) and tests updated to reflect accessor-based decorators and new buffer handling.
- Dependencies bumped: @push.rocks/smartenv -> ^6.0.0, dev deps @git.zone/tsbuild, @git.zone/tsrun, @git.zone/tstest upgraded, and @types/node -> ^24.0.0.
- Removed pnpm-workspace.yaml onlyBuiltDependencies entries.
2025-09-12 - 5.2.0 - feat(smartjson)
Implement stableOneWayStringify: deterministic, cycle-safe JSON for hashing/comparisons; update docs and tests
- Implement stableOneWayStringify in ts/index.ts: produces deterministic JSON, encodes buffers, replaces circular references with "cycle" and marks unserializable values as "unserializable".
- Update README: add documentation and examples for stableOneWayStringify, clarify JSONL wording, and update performance/migration notes.
- Add unit test to verify stableOneWayStringify handles circular references without throwing.
- Add .claude/settings.local.json (local settings/config) as part of the change set.
2025-09-12 - 5.1.0 - feat(smartjson)
Add JSONL stringify and ordering comparator; fix empty buffer handling; refactor Smartjson folding/enfolding
- Export stringifyJsonL(items: any[]) and add README documentation for JSONL stringification
- stringify(obj, simpleOrderArray) now derives a stable-json comparator from simpleOrderArray when no custom cmp is provided, ensuring predictable key ordering
- Fix buffer handling: empty Uint8Array values are preserved (no longer serialized to an empty string) and encoding/decoding logic improved
- Refactor Smartjson.enfoldFromObject to safely use saveableProperties and avoid repeated property access
- Simplify Smartjson.foldToObject to delegate to an internal foldToObjectInternal with cycle detection and correct nested instance handling
- Add unit tests for empty buffers, JSONL parse/stringify, deepEqualJsonLStrings, and simpleOrderArray comparator behavior
2025-09-12 - 5.0.21 - fix(Smartjson)
Cross-platform buffer/base64 handling, safer folding with cycle detection, parsing fixes, docs and dependency updates
- bufferhandling: prefer Node Buffer for base64 encode/decode when available and fall back to browser APIs for cross-platform support
- parseJsonL: more robust parsing of JSON Lines (trim lines and use smartjson.parse to restore buffers/typed arrays)
- parseBase64: use smartstring.base64.decodeUri or decode fallback before parsing, then parse result via smartjson.parse
- Smartjson class: add TypeScript generics to enfold methods, introduce foldToObjectInternal and robust cycle detection using Set, properly handle arrays and deep-clone values
- Tests: updated tap import path to @git.zone/tstest/tapbundle
- package.json: bumped several devDependencies and dependency versions and added packageManager pin
- Documentation: expanded README with examples, API reference and usage guidance
- Repo config: added pnpm-workspace.yaml and .claude/settings.local.json
2024-05-29 - 5.0.20 - docs
Update package description.
- Update package description text.
2024-05-27 - 5.0.19 - core
Core fixes.
- Miscellaneous core updates and fixes.
2024-04-17 - 5.0.18 - core
Core fixes.
- Miscellaneous core updates and fixes.
2024-04-17 - 5.0.17 - core
Core fixes.
- Miscellaneous core updates and fixes.
2024-04-17 - 5.0.16 - build/core
Build/config updates and core fixes.
- Updated tsconfig configuration (2024-04-14).
- Updated npmextra.json githost entries (multiple updates in early April).
- Miscellaneous core updates and fixes.
2024-03-19 - 5.0.15 - core
Core fixes.
- Miscellaneous core updates and fixes.
2024-03-19 - 5.0.14 - core
Core fixes.
- Miscellaneous core updates and fixes.
2024-03-03 - 5.0.13 - core
Core fixes.
- Miscellaneous core updates and fixes.
2024-03-03 - 5.0.12 - core
Core fixes.
- Miscellaneous core updates and fixes.
2024-03-03 - 5.0.11 - core
Core fixes.
- Miscellaneous core updates and fixes.
2024-02-25 - 5.0.10 - core
Core fixes.
- Miscellaneous core updates and fixes.
2023-08-24 - 5.0.9 - core
Core fixes.
- Miscellaneous core updates and fixes.
2023-08-19 - 5.0.8 - core
Core fixes.
- Miscellaneous core updates and fixes.
2023-08-19 - 5.0.7 - core
Core fixes.
- Miscellaneous core updates and fixes.
2023-07-10 - 5.0.6 - infra/core
Organization and core updates.
- Switched to new organization scheme.
- Miscellaneous core updates and fixes.
2023-06-03 - 5.0.5 - core
Core fixes.
- Miscellaneous core updates and fixes.
2022-10-26 - 5.0.4 - core
Core fixes.
- Miscellaneous core updates and fixes.
2022-10-26 - 5.0.3 - core
Core fixes.
- Miscellaneous core updates and fixes.
2022-10-26 - 5.0.2 - core
Core fixes.
- Miscellaneous core updates and fixes.
2022-09-13 - 5.0.1 - core
Core fixes.
- Miscellaneous core updates and fixes.
2022-06-26 - 5.0.0 - core
Core fixes.
- Miscellaneous core updates and fixes.
2022-06-09 - 4.0.7 - core (BREAKING CHANGE)
Breaking change in core.
- BREAKING CHANGE: core-related update (see code/compat notes).
- Miscellaneous core updates.
2022-06-09 - 4.0.6 - core
Core fixes.
- Miscellaneous core updates and fixes.
2020-10-05 - 4.0.4 - core
Core fixes.
- Miscellaneous core updates and fixes.
2020-10-05 - 4.0.3 - core
Core fixes.
- Miscellaneous core updates and fixes.
2020-10-05 - 4.0.2 - core
Core fixes.
- Miscellaneous core updates and fixes.
2020-10-05 - 4.0.1 - core
Core fixes.
- Miscellaneous core updates and fixes.
2020-10-05 - 4.0.0 - core
Core fixes.
- Miscellaneous core updates and fixes.
2020-10-05 - 3.0.12 - core (BREAKING CHANGE)
Breaking change in core.
- BREAKING CHANGE: core-related update (see migration notes).
- Miscellaneous core updates.
2020-10-03 - 3.0.11 - core
Core fixes.
- Miscellaneous core updates and fixes.
2020-10-03 - 3.0.10 - core
Core fixes.
- Miscellaneous core updates and fixes.
2019-02-14 - 3.0.0 - security
- Added snyk policy for security scanning.
2019-02-14 - 2.0.2 - core (BREAKING CHANGE)
Breaking change in core API.
- BREAKING CHANGE: renamed Folable to Smartjson and added deterministic stringify.
- Update consumers accordingly.
2018-09-05 - 2.0.1 - core
Core fixes.
- Miscellaneous core updates and fixes.
2018-07-23 - 1.0.1 - scope (BREAKING CHANGE)
Scope change.
- BREAKING CHANGE: changed package scope.
- See migration notes for scope changes.
2017-02-27 - 1.0.0 - docs/init
Initial release.
- Added README.
- Initial project files and setup.
2018-07-23 — 2019-12-15 - 2.0.0, 3.0.1, 3.0.9 - version-only releases
- These releases are recorded as version bumps without additional changelog details.
- Affected tags: 2.0.0, 3.0.1, 3.0.9.