CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-devalue

Gets the job done when JSON.stringify can't

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

Devalue

Devalue is a secure serialization library that extends beyond JSON.stringify's capabilities by handling cyclical references, repeated references, undefined values, Infinity, NaN, -0, regular expressions, dates, Map and Set objects, BigInt, and custom types through replacers, reducers and revivers.

Package Information

  • Package Name: devalue
  • Package Type: npm
  • Language: JavaScript (ES modules)
  • Installation: npm install devalue

Core Imports

import { uneval, stringify, parse, unflatten } from "devalue";

For CommonJS:

const { uneval, stringify, parse, unflatten } = require("devalue");

Basic Usage

import { uneval, stringify, parse } from "devalue";

// Basic object
let obj = { message: 'hello', count: 42 };

// uneval - generates JavaScript code
console.log(uneval(obj)); // '{message:"hello",count:42}'

// stringify/parse - JSON-based approach
let serialized = stringify(obj); // '[{"message":1,"count":2},"hello",42]'
let restored = parse(serialized); // { message: 'hello', count: 42 }

// Handling cyclical references
obj.self = obj;
console.log(uneval(obj)); // '(function(a){a.message="hello";a.count=42;a.self=a;return a}({}))'

serialized = stringify(obj); // '[{"message":1,"count":2,"self":0},"hello",42]'
restored = parse(serialized); // { message: 'hello', count: 42, self: [Circular] }

Architecture

Devalue provides three complementary approaches to serialization:

  • Code Generation: uneval() creates executable JavaScript for maximum compactness
  • JSON-based Serialization: stringify()/parse() provides safe, evaluable format
  • Partial Deserialization: unflatten() handles pre-parsed JSON structures
  • Security First: XSS protection through character escaping and function rejection
  • Custom Types: Extensible through replacers, reducers, and revivers

Capabilities

Code Generation (uneval)

Generates JavaScript code to recreate a value - the most compact output format that doesn't require parsing libraries.

/**
 * Turn a value into JavaScript code that creates an equivalent value
 * @param value - The value to serialize  
 * @param replacer - Optional custom replacer function for custom types
 * @returns JavaScript code string
 * @throws DevalueError for functions or non-serializable objects
 */
function uneval(value: any, replacer?: (value: any) => string | void): string;

Usage Examples:

import { uneval } from "devalue";

// Simple values
uneval(42); // "42"
uneval("hello"); // '"hello"'
uneval([1, 2, 3]); // "[1,2,3]"

// Complex objects with cycles
const circular = { name: "test" };
circular.ref = circular;
uneval(circular); // '(function(a){a.name="test";a.ref=a;return a}({}))'

// Custom types with replacer
class Vector {
  constructor(x, y) { this.x = x; this.y = y; }
}

uneval(new Vector(10, 20), (value) => {
  if (value instanceof Vector) {
    return `new Vector(${value.x},${value.y})`;
  }
}); // "new Vector(10,20)"

JSON-based Serialization (stringify)

Converts values to JSON format that can be safely parsed, handling more types than JSON.stringify.

/**
 * Turn a value into JSON string that can be parsed with devalue.parse
 * @param value - The value to serialize
 * @param reducers - Optional custom type reducers object
 * @returns JSON string representation
 * @throws DevalueError for functions or non-serializable objects
 */
function stringify(value: any, reducers?: Record<string, (value: any) => any>): string;

Usage Examples:

import { stringify } from "devalue";

// Basic serialization
stringify({ name: "Alice", age: 30 }); // '[{"name":1,"age":2},"Alice",30]'

// Special values
stringify([undefined, NaN, Infinity, -0]); // '[-1,-3,-4,-6]'

// Dates and RegExp
stringify(new Date("2023-01-01")); // '[["Date","2023-01-01T00:00:00.000Z"]]'
stringify(/hello/gi); // '[["RegExp","hello","gi"]]'

// Custom types with reducers
class User {
  constructor(name, email) { this.name = name; this.email = email; }
}

stringify(new User("Bob", "bob@test.com"), {
  User: (user) => user instanceof User && [user.name, user.email]
}); // '[["User",1],[2,3],"Bob","bob@test.com"]'

JSON Deserialization (parse)

Revives values serialized with devalue.stringify back to their original form.

/**
 * Revive a value serialized with devalue.stringify
 * @param serialized - JSON string from stringify
 * @param revivers - Optional custom type revivers object  
 * @returns The deserialized value
 * @throws Error for invalid input
 */
function parse(serialized: string, revivers?: Record<string, (value: any) => any>): any;

Usage Examples:

import { parse } from "devalue";

// Basic parsing
const data = parse('[{"name":1,"age":2},"Alice",30]');
// { name: "Alice", age: 30 }

// With custom revivers
const userData = parse('[["User",1],[2,3],"Bob","bob@test.com"]', {
  User: ([name, email]) => new User(name, email)
});

Partial Deserialization (unflatten)

Revives pre-parsed JSON structures, useful when devalue data is part of larger JSON.

/**
 * Revive a value flattened with devalue.stringify
 * @param parsed - Pre-parsed JSON structure (number or array)
 * @param revivers - Optional custom type revivers object
 * @returns The deserialized value  
 * @throws Error for invalid input
 */
function unflatten(parsed: number | any[], revivers?: Record<string, (value: any) => any>): any;

Usage Examples:

import { unflatten, stringify } from "devalue";

// Embed devalue data in larger JSON
const complexData = { user: "Alice", preferences: { theme: "dark" } };
const json = `{
  "type": "userdata", 
  "payload": ${stringify(complexData)}
}`;

// Parse and extract just the devalue portion
const parsed = JSON.parse(json);
const userData = unflatten(parsed.payload);
// { user: "Alice", preferences: { theme: "dark" } }

Error Handling

All functions throw DevalueError for unsupported values, with path information for debugging.

class DevalueError extends Error {
  name: "DevalueError";
  path: string; // Location of problematic value
  message: string;
}

Usage Examples:

import { uneval } from "devalue";

try {
  const map = new Map();
  map.set('key', function invalid() {});
  
  uneval({
    object: {
      array: [map]
    }
  });
} catch (error) {
  console.log(error.name); // "DevalueError"
  console.log(error.path); // '.object.array[0].get("key")'
  console.log(error.message); // "Cannot stringify a function"
}

Supported Data Types

Primitive Types

  • number (including NaN, Infinity, -Infinity, -0)
  • string
  • boolean
  • null
  • undefined
  • bigint

Object Types

  • Plain objects (POJOs)
  • Arrays (including sparse arrays)
  • Date objects
  • RegExp objects
  • Set and Map objects
  • Boxed primitives (new Number(), new String(), new Boolean())
  • Objects with null prototype (Object.create(null))

Advanced Features

  • Cyclical references
  • Repeated references
  • Custom types via replacers/reducers/revivers

Security Features

  • XSS protection through character escaping
  • Function serialization prevention
  • Non-POJO rejection (unless handled by custom handlers)
  • Symbol property rejection

Type Definitions

// Custom type handlers
type Replacer = (value: any) => string | void;
type Reducer = (value: any) => any;  
type Reviver = (value: any) => any;

// Reducer and reviver objects
type Reducers = Record<string, Reducer>;
type Revivers = Record<string, Reviver>;

// Error class
class DevalueError extends Error {
  name: "DevalueError";
  path: string;
  message: string;
  constructor(message: string, keys: string[]);
}

Install with Tessl CLI

npx tessl i tessl/npm-devalue
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/devalue@4.3.x
Publish Source
CLI
Badge
tessl/npm-devalue badge