or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-safe-stable-stringify

Deterministic and safely JSON.stringify to quickly serialize JavaScript objects

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/safe-stable-stringify@2.5.x

To install, run

npx @tessl/cli install tessl/npm-safe-stable-stringify@2.5.0

index.mddocs/

Safe Stable Stringify

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.

Package Information

  • Package Name: safe-stable-stringify
  • Package Type: npm
  • Language: JavaScript with TypeScript definitions
  • Installation: npm install safe-stable-stringify

Core Imports

import stringify, { configure } from "safe-stable-stringify";

For CommonJS:

const stringify = require("safe-stable-stringify");
const { configure } = require("safe-stable-stringify");

For named imports (ESM):

import { configure, stringify } from "safe-stable-stringify";

Basic Usage

import stringify from "safe-stable-stringify";

// Handle bigint values (JSON.stringify would throw)
const bigintObj = { a: 0, c: 2n, b: 1 };
console.log(stringify(bigintObj));
// Output: '{"a":0,"b":1,"c":2}'

// Handle circular references (JSON.stringify would throw)
const circular = { name: "John", age: 30 };
circular.self = circular;
console.log(stringify(circular));
// Output: '{"age":30,"name":"John","self":"[Circular]"}'

// Use with replacer and space parameters (same as JSON.stringify)
console.log(stringify(circular, ['name', 'age'], 2));
// Output:
// {
//   "name": "John",
//   "age": 30
// }

Capabilities

Safe Stringify

Main function that safely serializes JavaScript objects to JSON strings with deterministic behavior.

/**
 * Safely stringify JavaScript objects to JSON with circular reference and bigint handling
 * @param value - The value to stringify
 * @param replacer - Optional replacer function or array (same as JSON.stringify)
 * @param space - Optional space parameter for formatting (same as JSON.stringify)
 * @returns JSON string representation or undefined for unstringifiable values
 */
function stringify(value: undefined | symbol | ((...args: unknown[]) => unknown), replacer?: Replacer, space?: string | number): undefined;
function stringify(value: string | number | unknown[] | null | boolean | object, replacer?: Replacer, space?: string | number): string;
function stringify(value: unknown, replacer?: ((key: string, value: unknown) => unknown) | (number | string)[] | null | undefined, space?: string | number): string | undefined;

type Replacer = (number | string)[] | null | undefined | ((key: string, value: unknown) => string | number | boolean | null | object);

Configure Function

Creates a customized stringify function with specific options for handling bigint, circular references, deterministic behavior, and other serialization settings.

/**
 * Creates a customized stringify function with specified options
 * @param options - Configuration options object
 * @returns Configured stringify function with applied options
 */
function configure(options: StringifyOptions): typeof stringify;

interface StringifyOptions {
  /** Convert bigint values to numeric strings instead of ignoring them (default: true) */
  bigint?: boolean;
  /** 
   * Value to use for circular references
   * - string: custom replacement text
   * - null/undefined: omit circular properties 
   * - Error/TypeError: throw on circular references
   * (default: '[Circular]')
   */
  circularValue?: string | null | undefined | ErrorConstructor | TypeErrorConstructor;
  /**
   * Ensure deterministic key ordering
   * - true: sort keys alphabetically
   * - false: preserve insertion order
   * - function: custom comparator for sorting
   * (default: true)
   */
  deterministic?: boolean | ((a: string, b: string) => number);
  /** Maximum number of properties to serialize per object (default: Infinity) */
  maximumBreadth?: number;
  /** Maximum object nesting levels to serialize (default: Infinity) */
  maximumDepth?: number;
  /** Throw errors for non-JSON values instead of graceful handling (default: false) */
  strict?: boolean;
}

Usage Examples:

import { configure } from "safe-stable-stringify";

// Custom configuration
const customStringify = configure({
  bigint: true,                    // Convert bigint to numeric JSON representation
  circularValue: "CIRCULAR_REF",   // Custom circular reference text
  deterministic: false,            // Preserve insertion order
  maximumDepth: 3,                 // Limit nesting depth
  maximumBreadth: 10               // Limit properties per object
});

const data = {
  bigNum: 999_999_999_999_999_999n,
  deep: { level1: { level2: { level3: { level4: "too deep" } } } },
  manyProps: Object.fromEntries(Array.from({length: 15}, (_, i) => [`prop${i}`, i]))
};
data.circular = data;

console.log(customStringify(data, null, 2));
// Bigint converted to numeric JSON, circular ref replaced, deep object truncated

// Strict mode (throws on problematic values)
const strictStringify = configure({
  strict: true,
  bigint: false,        // Must be false in strict mode unless explicitly true
  circularValue: Error  // Throw on circular references
});

try {
  strictStringify({ fn: () => {} }); // Throws error for function
} catch (error) {
  console.log("Strict mode prevented serialization");
}

Additional Properties

The default export function has additional properties attached for convenience:

/**
 * Reference to the main stringify function (same as default export)
 */
stringify.stringify: typeof stringify;

/**
 * Default export reference for compatibility
 */
stringify.default: typeof stringify;

/**
 * Configuration function attached to the main function
 */
stringify.configure: typeof configure;

These properties provide multiple ways to access the functionality:

import stringify from "safe-stable-stringify";

// All of these are equivalent
stringify({ test: true });
stringify.stringify({ test: true });
stringify.default({ test: true });

// Configure can be accessed directly from the main function
const customStringify = stringify.configure({ bigint: false });

Key Differences from JSON.stringify

  1. Circular References: Replaced with configurable value (default: '[Circular]') instead of throwing
  2. Object Key Ordering: Deterministic alphabetical sorting by default instead of insertion order
  3. BigInt Values: Converted to numeric JSON representation instead of throwing TypeError
  4. Boxed Primitives: Handled as regular objects, not unboxed (e.g., Number(5) stays as object)
  5. Error Handling: Graceful handling of problematic values unless strict mode is enabled

TypeScript Support

The package includes complete TypeScript definitions with:

  • Function overloads for precise return type inference
  • Generic type preservation in replacer functions
  • Full interface definitions for all configuration options
  • Proper handling of union types for various input scenarios
// TypeScript automatically infers return types
const result1: string = stringify({ name: "John" });           // Always string for objects
const result2: undefined = stringify(Symbol("test"));         // Undefined for symbols
const result3: string | undefined = stringify(someUnknown);   // Union type for unknown inputs