Deterministic and safely JSON.stringify to quickly serialize JavaScript objects
npx @tessl/cli install tessl/npm-safe-stable-stringify@2.5.00
# Safe Stable Stringify
1
2
Safe Stable Stringify is a deterministic and fast serialization alternative to JSON.stringify with zero dependencies. It gracefully handles circular structures and bigint values instead of throwing errors, offers optional custom circular values and deterministic behavior, and maintains 100% test coverage.
3
4
## Package Information
5
6
- **Package Name**: safe-stable-stringify
7
- **Package Type**: npm
8
- **Language**: JavaScript with TypeScript definitions
9
- **Installation**: `npm install safe-stable-stringify`
10
11
## Core Imports
12
13
```typescript
14
import stringify, { configure } from "safe-stable-stringify";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const stringify = require("safe-stable-stringify");
21
const { configure } = require("safe-stable-stringify");
22
```
23
24
For named imports (ESM):
25
26
```typescript
27
import { configure, stringify } from "safe-stable-stringify";
28
```
29
30
## Basic Usage
31
32
```typescript
33
import stringify from "safe-stable-stringify";
34
35
// Handle bigint values (JSON.stringify would throw)
36
const bigintObj = { a: 0, c: 2n, b: 1 };
37
console.log(stringify(bigintObj));
38
// Output: '{"a":0,"b":1,"c":2}'
39
40
// Handle circular references (JSON.stringify would throw)
41
const circular = { name: "John", age: 30 };
42
circular.self = circular;
43
console.log(stringify(circular));
44
// Output: '{"age":30,"name":"John","self":"[Circular]"}'
45
46
// Use with replacer and space parameters (same as JSON.stringify)
47
console.log(stringify(circular, ['name', 'age'], 2));
48
// Output:
49
// {
50
// "name": "John",
51
// "age": 30
52
// }
53
```
54
55
## Capabilities
56
57
### Safe Stringify
58
59
Main function that safely serializes JavaScript objects to JSON strings with deterministic behavior.
60
61
```typescript { .api }
62
/**
63
* Safely stringify JavaScript objects to JSON with circular reference and bigint handling
64
* @param value - The value to stringify
65
* @param replacer - Optional replacer function or array (same as JSON.stringify)
66
* @param space - Optional space parameter for formatting (same as JSON.stringify)
67
* @returns JSON string representation or undefined for unstringifiable values
68
*/
69
function stringify(value: undefined | symbol | ((...args: unknown[]) => unknown), replacer?: Replacer, space?: string | number): undefined;
70
function stringify(value: string | number | unknown[] | null | boolean | object, replacer?: Replacer, space?: string | number): string;
71
function stringify(value: unknown, replacer?: ((key: string, value: unknown) => unknown) | (number | string)[] | null | undefined, space?: string | number): string | undefined;
72
73
type Replacer = (number | string)[] | null | undefined | ((key: string, value: unknown) => string | number | boolean | null | object);
74
```
75
76
### Configure Function
77
78
Creates a customized stringify function with specific options for handling bigint, circular references, deterministic behavior, and other serialization settings.
79
80
```typescript { .api }
81
/**
82
* Creates a customized stringify function with specified options
83
* @param options - Configuration options object
84
* @returns Configured stringify function with applied options
85
*/
86
function configure(options: StringifyOptions): typeof stringify;
87
88
interface StringifyOptions {
89
/** Convert bigint values to numeric strings instead of ignoring them (default: true) */
90
bigint?: boolean;
91
/**
92
* Value to use for circular references
93
* - string: custom replacement text
94
* - null/undefined: omit circular properties
95
* - Error/TypeError: throw on circular references
96
* (default: '[Circular]')
97
*/
98
circularValue?: string | null | undefined | ErrorConstructor | TypeErrorConstructor;
99
/**
100
* Ensure deterministic key ordering
101
* - true: sort keys alphabetically
102
* - false: preserve insertion order
103
* - function: custom comparator for sorting
104
* (default: true)
105
*/
106
deterministic?: boolean | ((a: string, b: string) => number);
107
/** Maximum number of properties to serialize per object (default: Infinity) */
108
maximumBreadth?: number;
109
/** Maximum object nesting levels to serialize (default: Infinity) */
110
maximumDepth?: number;
111
/** Throw errors for non-JSON values instead of graceful handling (default: false) */
112
strict?: boolean;
113
}
114
```
115
116
**Usage Examples:**
117
118
```typescript
119
import { configure } from "safe-stable-stringify";
120
121
// Custom configuration
122
const customStringify = configure({
123
bigint: true, // Convert bigint to numeric JSON representation
124
circularValue: "CIRCULAR_REF", // Custom circular reference text
125
deterministic: false, // Preserve insertion order
126
maximumDepth: 3, // Limit nesting depth
127
maximumBreadth: 10 // Limit properties per object
128
});
129
130
const data = {
131
bigNum: 999_999_999_999_999_999n,
132
deep: { level1: { level2: { level3: { level4: "too deep" } } } },
133
manyProps: Object.fromEntries(Array.from({length: 15}, (_, i) => [`prop${i}`, i]))
134
};
135
data.circular = data;
136
137
console.log(customStringify(data, null, 2));
138
// Bigint converted to numeric JSON, circular ref replaced, deep object truncated
139
140
// Strict mode (throws on problematic values)
141
const strictStringify = configure({
142
strict: true,
143
bigint: false, // Must be false in strict mode unless explicitly true
144
circularValue: Error // Throw on circular references
145
});
146
147
try {
148
strictStringify({ fn: () => {} }); // Throws error for function
149
} catch (error) {
150
console.log("Strict mode prevented serialization");
151
}
152
```
153
154
## Additional Properties
155
156
The default export function has additional properties attached for convenience:
157
158
```typescript { .api }
159
/**
160
* Reference to the main stringify function (same as default export)
161
*/
162
stringify.stringify: typeof stringify;
163
164
/**
165
* Default export reference for compatibility
166
*/
167
stringify.default: typeof stringify;
168
169
/**
170
* Configuration function attached to the main function
171
*/
172
stringify.configure: typeof configure;
173
```
174
175
These properties provide multiple ways to access the functionality:
176
177
```typescript
178
import stringify from "safe-stable-stringify";
179
180
// All of these are equivalent
181
stringify({ test: true });
182
stringify.stringify({ test: true });
183
stringify.default({ test: true });
184
185
// Configure can be accessed directly from the main function
186
const customStringify = stringify.configure({ bigint: false });
187
```
188
189
## Key Differences from JSON.stringify
190
191
1. **Circular References**: Replaced with configurable value (default: `'[Circular]'`) instead of throwing
192
2. **Object Key Ordering**: Deterministic alphabetical sorting by default instead of insertion order
193
3. **BigInt Values**: Converted to numeric JSON representation instead of throwing TypeError
194
4. **Boxed Primitives**: Handled as regular objects, not unboxed (e.g., `Number(5)` stays as object)
195
5. **Error Handling**: Graceful handling of problematic values unless strict mode is enabled
196
197
## TypeScript Support
198
199
The package includes complete TypeScript definitions with:
200
201
- Function overloads for precise return type inference
202
- Generic type preservation in replacer functions
203
- Full interface definitions for all configuration options
204
- Proper handling of union types for various input scenarios
205
206
```typescript
207
// TypeScript automatically infers return types
208
const result1: string = stringify({ name: "John" }); // Always string for objects
209
const result2: undefined = stringify(Symbol("test")); // Undefined for symbols
210
const result3: string | undefined = stringify(someUnknown); // Union type for unknown inputs
211
```