npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@winglet/json

v0.14.1

Published

TypeScript library for safe and efficient JSON data manipulation with RFC 6901 (JSON Pointer) and RFC 6902 (JSON Patch) compliance, featuring prototype pollution protection and immutable operations

Readme

@winglet/json

TypeScript JSON Schema RFC 6901


Overview

@winglet/json is a TypeScript library for safe and efficient JSON data manipulation. It provides structured JSON data processing by strictly adhering to RFC 6901 (JSON Pointer) and RFC 6902 (JSON Patch) standards.

Key Features

  • Type Safety: Full TypeScript support with compile-time type validation
  • Standards Compliant: Complete implementation of RFC 6901 (JSON Pointer) and RFC 6902 (JSON Patch)
  • Security: Built-in protection against prototype pollution attacks
  • Flexibility: Immutable and strict mode options for different use cases
  • Performance: Optimized algorithms supporting large-scale JSON data processing

Installation

# Using npm
npm install @winglet/json

# Using yarn
yarn add @winglet/json

# Using pnpm
pnpm add @winglet/json

Sub-path Imports

This package supports sub-path imports to enable more granular imports and optimize bundle size. You can import specific modules directly without importing the entire package:

// Main exports (all JSONPointer and JSONPath utilities)
import { getValue, setValue } from '@winglet/json';

// JSONPath utilities
import { JSONPath } from '@winglet/json/path';

// JSONPointer utilities
import {
  getValue,
  setValue,
  escapePath,
  unescapePath,
  compare,
  applyPatch,
  difference,
  mergePatch
} from '@winglet/json/pointer';

Available Sub-paths

Based on the package.json exports configuration:

  • @winglet/json - Main exports (all JSONPointer and JSONPath utilities)
  • @winglet/json/path - JSONPath constants and utilities
  • @winglet/json/path-common - JSONPath common utilities
  • @winglet/json/pointer - JSONPointer core utilities
  • @winglet/json/pointer-common - JSONPointer common utilities
  • @winglet/json/pointer-escape - JSONPointer escaping utilities (escapePath, unescapePath)
  • @winglet/json/pointer-manipulator - JSONPointer manipulation functions (getValue, setValue)
  • @winglet/json/pointer-patch - JSONPointer patch operations (compare, applyPatch, difference, mergePatch)

Compatibility

This package is written using ECMAScript 2020 (ES2020) syntax.

Supported Environments:

  • Node.js 14.0.0 or higher
  • Modern browsers (with ES2020 support)

For Legacy Environment Support: Use transpilers like Babel to convert the code to match your target environment.


API Reference

JSONPath

Provides special character constants used in JSONPath expressions.

Supported Operators

JSONPointer

Provides a complete JSON Pointer implementation that fully complies with RFC 6901.

Constants

Core Features

Data Manipulation

getValue

import { getValue } from '@winglet/json';

const data = {
  user: {
    profile: {
      name: 'Vincent',
      age: 30,
    },
  },
};

const name = getValue(data, '/user/profile/name');
// Result: "Vincent"

setValue

import { setValue } from '@winglet/json';

const data = { user: { profile: {} } };
const result = setValue(data, '/user/profile/email', '[email protected]');
// Result: { user: { profile: { email: "[email protected]" } } }
Escape Handling

escapePath

import { escapePath } from '@winglet/json';

const escaped = escapePath('path/with~special');
// Result: "path~1with~0special"

unescapePath

import { unescapePath } from '@winglet/json';

const unescaped = unescapePath('path~1with~0special');
// Result: "path/with~special"
JSON Patch Operations

compare

import { compare } from '@winglet/json';

const source = { name: 'John', age: 30, city: 'NYC' };
const target = { name: 'John', age: 31, country: 'USA' };

const patches = compare(source, target);
// Result:
// [
//   { op: "replace", path: "/age", value: 31 },
//   { op: "remove", path: "/city" },
//   { op: "add", path: "/country", value: "USA" }
// ]

applyPatch

import { applyPatch } from '@winglet/json';

const source = { name: 'John', age: 30 };
const patches = [
  { op: 'replace', path: '/age', value: 31 },
  { op: 'add', path: '/city', value: 'NYC' },
];

const result = applyPatch(source, patches);
// Result: { name: "John", age: 31, city: "NYC" }

difference

import { difference } from '@winglet/json';

const source = { name: 'John', age: 30, city: 'NYC' };
const target = { name: 'John', age: 31, country: 'USA' };

const mergePatch = difference(source, target);
// Result: { age: 31, city: null, country: "USA" }

mergePatch

import { mergePatch } from '@winglet/json';

const source = { name: 'John', age: 30, temp: 'data' };
const patch = { age: 31, temp: null, city: 'NYC' };

const result = mergePatch(source, patch);
// Result: { name: "John", age: 31, city: "NYC" }

Configuration Options

CompareOptions
interface CompareOptions {
  strict?: boolean; // Strict comparison mode (default: false)
  immutable?: boolean; // Immutable mode (default: true)
}
ApplyPatchOptions
interface ApplyPatchOptions {
  strict?: boolean; // Strict application mode (default: false)
  immutable?: boolean; // Immutable mode (default: true)
}

Reserved member names (__proto__, constructor, prototype) are always handled as opaque own data properties — no option is needed and none exists: no patch input can reach or modify the prototype chain.

Passing results downstream

A reserved member name carried by the input survives into the result as an own data property. Two consequences follow once that result leaves this library.

Copying. Spread ({ ...result }), structuredClone, and a JSON round-trip all define own data, so they preserve the key. Assignment-based copies go through the inherited __proto__ setter instead — the copy's prototype is replaced and the key disappears without an error:

const result = mergePatch({}, JSON.parse('{"__proto__":{"tainted":true}}'));

const safe = { ...result }; // { __proto__: { tainted: true } }, prototype intact
const lost = Object.assign({}, result); // {} — prototype replaced, key gone

@winglet/common-utils exposes setDataProperty for assignment paths that must preserve the key.

Type checks. When the input carries a constructor key, the result holds it as own data, and that own property shadows the inherited one — value.constructor === Object then reports false. Use Object.getPrototypeOf(value) or value instanceof Object instead; both read the prototype chain directly and stay accurate whichever reserved name the input carried.


Usage Examples

Basic Usage

import { applyPatch, compare, getValue, setValue } from '@winglet/json';

// Complex JSON data
const data = {
  users: [
    { id: 1, name: 'Alice', preferences: { theme: 'dark' } },
    { id: 2, name: 'Bob', preferences: { theme: 'light' } },
  ],
  settings: {
    app: { version: '1.0.0' },
  },
};

// Value retrieval
const theme = getValue(data, '/users/0/preferences/theme');
console.log(theme); // "dark"

// Value setting
const updated = setValue(data, '/settings/app/version', '1.1.0');

// Change comparison
const patches = compare(data, updated);
console.log(patches);
// [{ op: "replace", path: "/settings/app/version", value: "1.1.0" }]

Advanced Usage - Immutability and Security

import { applyPatch } from '@winglet/json';

const data = { user: { role: 'user' } };
const patches = [
  { op: 'add', path: '/user/permissions', value: ['read', 'write'] },
  { op: 'replace', path: '/user/role', value: 'admin' },
];

// Safe patch application — prototype pollution is structurally impossible:
// reserved member names are written as own data, never through the chain
const result = applyPatch(data, patches, {
  immutable: true, // Preserve original data
  strict: true, // Strict validation
});

console.log(data); // Original data preserved
console.log(result); // New modified object

JSON Merge Patch Usage

import { difference, mergePatch } from '@winglet/json';

const source = {
  user: { name: 'Alice', age: 25, role: 'admin', temp: 'data' },
  settings: { theme: 'dark' },
};

const target = {
  user: { name: 'Bob', age: 25, permissions: ['read', 'write'] },
  settings: { theme: 'light', language: 'en' },
};

// Generate JSON Merge Patch representing differences between two objects
const patch = difference(source, target);
console.log(patch);
// {
//   user: { name: "Bob", role: null, temp: null, permissions: ["read", "write"] },
//   settings: { theme: "light", language: "en" }
// }

// Apply JSON Merge Patch
const result = mergePatch(source, patch);
console.log(result);
// {
//   user: { name: "Bob", age: 25, permissions: ["read", "write"] },
//   settings: { theme: "light", language: "en" }
// }

Array Manipulation

import { getValue, setValue } from '@winglet/json';

const data = {
  items: ['apple', 'banana', 'cherry'],
};

// Array element access
const secondItem = getValue(data, '/items/1');
// Result: "banana"

// Add element to end of array (using RFC 6901 "-" syntax)
const withNewItem = setValue(data, '/items/-', 'date');
// Result: { items: ["apple", "banana", "cherry", "date"] }

Error Handling

import { JSONPointerError, getValue } from '@winglet/json';

try {
  const value = getValue({}, '/nonexistent/path');
} catch (error) {
  if (error instanceof JSONPointerError) {
    console.error('JSON Pointer Error:', error.message);
    console.error('Error Code:', error.code);
    console.error('Additional Details:', error.details);
  }
}

Performance Considerations

  • Large Data: Consider using immutable: false option when dealing with deeply nested objects or large arrays
  • Frequent Changes: Use strict: false to improve performance when applying many patches sequentially
  • Memory Usage: Immutable mode uses more memory but ensures safety

Contributing

If you'd like to contribute to this project:

  1. Create an issue for bug reports or feature suggestions
  2. Submit pull requests with improvements
  3. Include test cases with your submissions

License

This repository is provided under the MIT License. See the LICENSE file for details.


Related Standards


Contact

For questions or suggestions about this project, please create a GitHub issue.