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 🙏

© 2025 – Pkg Stats / Ryan Hefner

km-traversal

v1.1.8

Published

This utility provides powerful object traversal capabilities with pattern matching and transformation features.

Downloads

39

Readme

KM-TRAVERSAL

Introduction

traverseIn is a powerful TypeScript utility for traversing and modifying complex nested data structures (objects and arrays) using expressive pattern syntax. It provides fine-grained control over data traversal with support for deep nesting, conditional filtering, and in-place modifications.

Key Features

  • 🚀 Expressive pattern syntax for precise data navigation
  • 🔍 Advanced filtering with key/value conditions
  • ✏️ In-place modification of keys and values
  • 🌳 Deep recursion with depth control
  • ⚡ Multi-key selection for simultaneous access
  • 🔄 Dual syntax support for explicit and shortcut patterns
  • 🧩 Extensible condition system for custom logic
  • 📋 Path tracking with full object paths
  • 🧠 Smart quoting for flexible pattern definitions

Installation

npm i km-traversal

Core Concepts

Pattern Syntax

| Pattern Type | Syntax Example | Description | | :---------------- | :---------------------------------------------- | :------------------------------------ | | Property Access | user.profile | Direct property access | | Single Star | (*) or * (shortcut) | Iterate all immediate children | | Double Star | (**) or ** (shortcut) | Recursive descent through all levels | | Limited Recursion | (*3*) | Recursive descent limited to 3 levels | | Single Key | ("id") or (id) | Access specific property | | Multi Key | ("id","name") or (id,name) | Access multiple specific properties | | Object Condition | ({"key":"title","value.includes":"urgent"}) | AND conditions on key/value | | Array Condition | ([{"value.>":5},{"value.<":10}]) | OR of AND conditions |

Condition Syntax

Conditions always specify what they target:

  • key.condition: Applies to the property key
  • value.condition: Applies to the property value
  • condition: Alias for value.condition (default behavior)

| Pattern | Meaning | | :---------------------------- | :--------------------- | | {"value.startsWith":"a"} | Value starts with "a" | | {"key.equalWith":"email"} | Key is exactly "email" | | {"key.includes":"name"} | Key contains "name" | | {"isNumber":true} | Value is a number | | {"!isArray":true} | Value is NOT an array |

Default Conditions

Key/Value Operations

| Condition | Description | Example Usage | | :-------- | :-------------------- | :------------------------------ | | equalWith | Strict equality | {"key.equalWith":"email"} | | notEqual | Strict inequality | {"value.notEqual":null} | | includes | String contains value | {"value.includes":"urgent"} |

String Operations

| Condition | Description | Example Usage | | :--------- | :-------------------- | :------------------------------------ | | startsWith | Starts with value | {"value.startsWith":"https"} | | endsWith | Ends with value | {"value.endsWith":"@gmail.com"} | | matches | Matches regex pattern | {"value.matches":"\d{3}-\d{4}"} |

Numeric Operations

| Condition | Description | Example Usage | | :---------- | :----------------- | :--------------------------- | | greaterThan | > value | {"value.greaterThan":18} | | lessThan | < value | {"value.lessThan":100} | | between | Between [min, max] | {"value.between":[5,10]} |

Type Checking

| Condition | Description | Example Usage | | :-------- | :----------- | :-------------------------- | | isString | String type | {"value.isString":true} | | isNumber | Number type | {"value.isNumber":true} | | isArray | Array type | {"value.isArray":true} | | isObject | Plain object | {"value.isObject":true} |

Array Operations

| Condition | Description | Example Usage | | :------------ | :--------------------- | :---------------------------------- | | arrayIncludes | Array contains value | {"value.arrayIncludes":"admin"} | | length | Array has exact length | {"value.length":5} |

API Reference

Core Functions

// code...
traverseIn<ENTRY_DATA, OPTIONS>(
  data: ENTRY_DATA,  // Data to traverse (object or array)
  options: ICustomEachOptions,  // Configuration options
  patterns: (  // Array of traversal patterns
    string |
    string[] |
    (({setCondName}) => string)
  )[],
  callbacks: Callback[]  // Functions to execute at target nodes
): void

Adapter Pattern

// code...
const { register } = adapter();
const traverser = register(customConditions);

traverser.traverseIn(data, patterns, callbacks);

Callback Parameters

// code...
callback({
  key: string | number,     // Current key/index
  value: any,               // Current value
  objectPath: (string | number)[], // Full path to node
  parent: any,              // Parent object/array
  setKey: (newKey: string) => void, // Rename property
  setValue: (newValue: any) => void, // Modify value
  remove: () => void, // Delete Value
  removeNears: () => void // Delete Near Values
})

Configuration Options

| Option | Type | Default | Description | | :----------------------- | :------ | :------- | :---------------------------------------- | | injectedConditions | Array | Required | Custom conditions to extend functionality | | shortcuts.singleStar | boolean | true | Enable * syntax for single-star | | shortcuts.doubleStar | boolean | true | Enable ** syntax for double-star |

Usage Examples

Basic Traversal

// code...
import { traverseIn, defaultConditions } from './traverseIn';

const data = {
  users: [
    { id: 1, name: 'John', contact: { email: '[email protected]' } },
    { id: 2, name: 'Jane', contact: { email: '[email protected]' } },
  ],
};

// Convert all emails to uppercase
traverseIn(
  data,
  { injectedConditions: defaultConditions },
  ['users.(*).contact.email'],
  [
    ({ value, setValue }) => {
      setValue(value.toUpperCase());
    },
  ]
);

Advanced Filtering

// code...
const productData = {
  inventory: [
    { id: 1, title: 'Laptop', price: 1200, tags: ['electronics', 'premium'] },
    { id: 2, title: 'Desk Lamp', price: 35, tags: ['furniture'] },
    { id: 3, title: 'Monitor', price: 300, tags: ['electronics'] },
  ],
};

// Find electronics under $1000
traverseIn(
  productData,
  { injectedConditions: defaultConditions },
  [
    'inventory.([{ "key:equalWith":"price", "value.lessThan":1000},{ "key:equalWith":"tags", "value.arrayIncludes":"electronics"}])',
  ],
  [
    ({ value }) => {
      console.log('Filtered item:', value.title);
      // Outputs: "Desk Lamp" and "Monitor"
    },
  ]
);

Key-Based Operations

// code...
const userData = {
  user_list: [
    { user_id: 1, user_name: 'John' },
    { user_id: 2, user_name: 'Jane' },
  ],
};

// Rename user_id to id
traverseIn(
  userData,
  { injectedConditions: defaultConditions },
  ['user_list.(*).user_id'],
  [
    ({ setKey }) => {
      setKey('id');
    },
  ]
);

// Find keys containing "name"
traverseIn(
  userData,
  { injectedConditions: defaultConditions },
  ['user_list.(*).({"key.includes":"name"})'],
  [
    ({ value }) => {
      console.log('Name property:', value);
    },
  ]
);

Recursive Search

// code...
const nestedData = {
  a: {
    b: {
      c: 'Target 1',
      d: { e: 'Target 2' },
    },
    f: { g: 'Target 3' },
  },
};

// Find all string values starting with "Target"
traverseIn(
  nestedData,
  { injectedConditions: defaultConditions },
  [['(**)']],
  [
    ({ value, objectPath }) => {
      if (typeof value === 'string' && value.startsWith('Target')) {
        console.log(`Found ${value} at: ${objectPath.join('.')}`);
      }
    },
  ]
);

Custom Conditions

// code...
const customConditions = [
  ...defaultConditions,
  {
    name: 'validEmail',
    action: (_, __, target) =>
      typeof target === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(target),
  },
  {
    name: 'isEven',
    action: (_, __, target) => typeof target === 'number' && target % 2 === 0,
  },
];

const data = {
  contacts: [
    { id: 1, email: '[email protected]' },
    { id: 2, email: 'invalid' },
    { id: 3, email: '[email protected]' },
  ],
};

// Validate emails and even IDs
traverseIn(
  data,
  { injectedConditions: customConditions },
  ['contacts.(*).email.({"value.validEmail":true})', 'contacts.(*).id.({"value.isEven":true})'],
  [({ value }) => console.log('Valid email:', value), ({ value }) => console.log('Even ID:', value)]
);

Using the Adapter

// code...
import { adapter } from './traverseIn';

// Create reusable traverser with custom conditions
const productTraverser = adapter().register([
  ...defaultConditions,
  {
    name: 'inStock',
    action: (_, value) => value.inventory > 0,
  },
  {
    name: 'onSale',
    action: (_, value) => value.discount > 0,
  },
]);

const products = {
  items: [
    { id: 1, name: 'Laptop', price: 1200, inventory: 5, discount: 0 },
    { id: 2, name: 'Mouse', price: 25, inventory: 0, discount: 5 },
    { id: 3, name: 'Keyboard', price: 45, inventory: 10, discount: 10 },
  ],
};

// Find products that are in stock AND on sale
productTraverser.traverseIn(
  products,
  ['items.(*).({"value.inStock":true,"value.onSale":true})'],
  [
    ({ value }) => {
      console.log('Available on sale:', value.name);
      // Outputs: "Keyboard"
    },
  ]
);

Pattern Cheat Sheet

Basic Navigation

| Pattern | Matches | | :--------------------- | :--------------------------------------- | | users.name | Direct property: data.users.name | | items.(*) | All elements in data.items array | | products.(**) | All nested properties under products | | categories.(*3*) | Recursive search up to 3 levels deep | | ("metadata") | Property named "metadata" | | (id,name) | Both "id" and "name" properties |

Condition Patterns

| Pattern | Matches | | :-------------------------------------------------- | :--------------------------------- | | ({"key":"email"}) | Properties with key "email" | | ({"value.includes":"error"}) | Values containing "error" | | ({"key.includes":"date","value.isString":true}) | Date properties with string values | | ([{"value.<":100},{"value.>":1000}]) | Values < 100 OR > 1000 |

Complex Examples

// code...
// Find active users with phone numbers
'users.(*).([{"key:equalWith":"status","value.equalWith":"active",},{"equalWith":"phone","value.exists":true}])';

// Find titles containing "urgent" in first 3 levels
'documents.(*3*).({"key.equalWith":"title","value.includes":"urgent"})';

// Find prices between $10-$100 in electronics
'inventory.([{"category":"electronics"}]).prices.({"value.between":[10,100]})';

// Rename all _id properties to id
'(**).({"key.equalWith":"_id"}).setKey("id")';

Tips & Best Practices

  • 1.Specificity First: Start patterns with specific keys before recursive descent
// code...
// Good:
'users.(*).contacts.(**)';

// Less efficient:
'(**).contacts';
  • 2.Combine Conditions: Use object conditions for AND, array conditions for OR
// code...
// AND: Must satisfy all conditions
'({"value.isNumber":true,"value.>":10})';

// OR: Satisfy any condition set
'([{"status":"active"},{"priority.greaterThan":3}])';
  • 3.Use Key/Value Specificity: Always specify key/value in conditions
// code...
// Recommended:
'({"key.includes":"date","value.isString":true})';

// Avoid:
'({"includes":"date"})'; // Ambiguous
  • 4.Batch Operations: Process multiple patterns in single traversal
// code...
traverseIn(data, options, ['users.(*).name', 'users.(*).email'], [nameProcessor, emailProcessor]);
  • 5.Depth Control: Limit recursion depth in large datasets
// code...
// Limit to 4 levels deep
'largeDataset.(*4*)';
  • 6.Path Utilization: Use objectPath for context-aware operations
// code...
callback: ({ value, objectPath }) => {
  if (objectPath[objectPath.length - 1] === 'email') {
    // Special handling for email fields
  }
};

Limitations

  • Circular References: Not supported (will cause infinite loops)
  • Large Datasets: Deep recursion may impact performance
  • Concurrent Modification: Changing structure during traversal may cause unexpected behavior
  • Type Strictness: Conditions require explicit type handling
  • Pattern Complexity: Highly complex patterns may have parsing overhead

Real-World Use Cases

  • 1.Data Migration: Rename keys and transform values across complex structures
  • 2.Validation: Verify data integrity in API payloads
  • 3.Security Scans: Find sensitive data patterns (credit cards, tokens)
  • 4.Data Cleaning: Normalize formats (phone numbers, emails)
  • 5.Feature Flags: Conditionally modify configuration trees
  • 6.Analytics: Extract specific metrics from complex event data
  • 7.Schema Enforcement: Ensure data matches required structure
// code...
// Real-world example: GDPR compliance
traverseIn(
  userData,
  { injectedConditions },
  [
    '(**).({"key.includes":"email","value.isString":true})',
    '(**).({"key.includes":"phone","value.isString":true})',
  ],
  [
    // Anonymize PII data
    ({ setValue }) => setValue('REDACTED'),
  ]
);

Contributing

Contributions are welcome! Please follow these steps:

  • Fork the repository
  • Create a feature branch
  • Add tests for new functionality
  • Submit a pull request

License

MIT License