JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 93
  • Score
    100M100P100Q80000F
  • License MIT

Package Exports

  • @winglet/json

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

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.

Core Features

Data Manipulation

getValueByPointer

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

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

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

setValueByPointer

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

const data = { user: { profile: {} } };
const result = setValueByPointer(
  data,
  '/user/profile/email',
  'vincent@example.com',
);
// Result: { user: { profile: { email: "vincent@example.com" } } }
Escape Handling

escapePointer

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

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

unescapePointer

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

const unescaped = unescapePointer('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)
  protectPrototype?: boolean; // Prototype protection (default: true)
}

Usage Examples

Basic Usage

import {
  applyPatch,
  compare,
  getValueByPointer,
  setValueByPointer,
} 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 = getValueByPointer(data, '/users/0/preferences/theme');
console.log(theme); // "dark"

// Value setting
const updated = setValueByPointer(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 (prevents prototype pollution)
const result = applyPatch(data, patches, {
  immutable: true, // Preserve original data
  protectPrototype: true, // Prevent prototype pollution
  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 { getValueByPointer, setValueByPointer } from '@winglet/json';

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

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

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

Error Handling

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

try {
  const value = getValueByPointer({}, '/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.



Contact

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