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

@veksa/reselect-utils

v5.1.0-p15

Published

[![npm version](https://img.shields.io/npm/v/@veksa/reselect-utils.svg?style=flat-square)](https://www.npmjs.com/package/@veksa/reselect-utils) [![npm downloads](https://img.shields.io/npm/dm/@veksa/reselect-utils.svg?style=flat-square)](https://www.npmj

Downloads

30

Readme

@veksa/reselect-utils

npm version npm downloads license

A powerful toolkit that extends the capabilities of @veksa/reselect and @veksa/re-reselect by providing utilities for advanced selector composition, safe property access, and optimized caching. This library helps you build more maintainable and efficient Redux selectors with enhanced type safety and functional programming patterns.

Features

  • Chain Selector Pattern - Build selector pipelines with fluent interfaces for complex data transformations
  • Powerful Path Selectors - Safely traverse and select nested object properties with protection against null/undefined
  • Bound Selectors - Create selectors with predefined parameters for reuse across components
  • Adapted Selectors - Transform selector parameters to simplify composition and reuse
  • Enhanced Structured Selectors - Better typed alternatives to createStructuredSelector with caching support
  • Key Selector Composition - Compose key selectors for complex cache keys in re-reselect
  • Custom Caching - Optimize performance with advanced garbage collection and cache strategies
  • TypeScript Support - Full TypeScript type definitions with strict typing

Installation

@veksa/reselect-utils requires TypeScript 5.8 or later.

Using npm or yarn

# npm
npm install @veksa/reselect-utils

# yarn
yarn add @veksa/reselect-utils

Examples

Comparing @veksa/reselect, @veksa/re-reselect, and @veksa/reselect-utils

Problem: Working with optional nested data

Imagine we need to select a nested property from a state object that might not exist at every level.

@veksa/reselect implementation

import { createSelector } from '@veksa/reselect';

const getUser = (state) => state.user;

const getUserAddress = createSelector(
  getUser,
  (user) => {
    // Need null checks at each level
    if (user && user.contact && user.contact.address) {
      return user.contact.address;
    }
    return undefined;
  }
);

// Usage:
const address = getUserAddress(state);  // May be undefined

With standard @veksa/reselect, you need to handle potential null/undefined values at each level with conditional checks.

@veksa/re-reselect solution

import { createCachedSelector } from '@veksa/re-reselect';

const getUser = (state) => state.user;
const getUserId = (state, userId) => userId;

// Using re-reselect for caching by userId
const getUserAddressByUserId = createCachedSelector(
  getUser,
  getUserId,
  (user, userId) => {
    // Still need null checks for nested properties
    const targetUser = user[userId];
    if (targetUser && targetUser.contact && targetUser.contact.address) {
      return targetUser.contact.address;
    }
    return undefined;
  }
)((state, userId) => userId);

// Usage with better caching by userId:
const address = getUserAddressByUserId(state, '123');

@veksa/re-reselect adds caching benefits but still requires the same null checks for property access.

@veksa/reselect-utils solution

import { createPathSelector } from '@veksa/reselect-utils';

// Clean path selection with built-in null handling
const getUser = createPathSelector(
  (state) => state.user,
);

// For parametric selectors
const getUserByUserId = createPathSelector(
  (state, userId) => state.users?.[userId],
);

// Usage:
const address = getUser(state).address();  // Safely returns undefined if path is broken
const userAddress = getUserByUserId(state, '123').address();  // With parameters

@veksa/reselect-utils provides safer property access with path selectors that handle null/undefined values automatically.

Basic Usage Patterns

Segment Selector

import { createSegmentSelector } from '@veksa/reselect-utils';

interface IUserStore {
  address: string;
}

interface IUserSegment {
  user: IUserStore;
}

const defaultUser: IUserStore = {address: 'Default address'};

const getUserSegment = createSegmentSelector<IUserSegment, IUserStore>(state => state.user, defaultUser);

Path Selector

import { createPathSelector } from '@veksa/reselect-utils';

const state = {
  user: {
    address: 'Some user address',
  },
};

const getUserAddress = createPathSelector(state => state.user).address();

Prop Selector

import { createPropSelector } from '@veksa/reselect-utils';

const getUserIdFromProps = createPropSelector<{userId: number}>().userId();

Bound Selector

import { createSelector } from '@veksa/reselect';
import { createBoundSelector } from '@veksa/reselect-utils';

const getUserByName = createSelector(
  state => state.users,
  (state, props) => props.userName,
  (users, userName) => users[userName],
);

const getAdmin = createBoundSelector(
  getUserByName,
  {userName: 'admin'},
);

// Usage:
const admin = getAdmin(state); // Same as getUserByName(state, {userName: 'admin'})

Adapted Selector

import { createSelector } from '@veksa/reselect';
import { createAdaptedSelector } from '@veksa/reselect-utils';

const getUserByNameAndRole = createSelector(
  state => state.users,
  (state, props) => props.userName,
  (state, props) => props.userRole,
  (users, userName, userRole) => users[userName][userRole],
);

const getAdmin = createAdaptedSelector(
  getUserByNameAndRole,
  props => ({userName: props.userName, userRole: 'admin'}),
);

// Usage:
const admin = getAdmin(state); // Same as getUserByName(state, {userName: 'admin'})

Chain Selectors

import { createChainSelector } from '@veksa/reselect-utils';

// Create a base selector
const getUserData = state => state.users;

// Build a selector chain
const getActiveUserEmails = createChainSelector(getUserData)
  .map(users => users.filter(user => user.active))
  .map(activeUsers => activeUsers.map(user => user.email))
  .build();

// Usage:
const activeEmails = getActiveUserEmails(state); // ['[email protected]', '[email protected]']

Advanced Examples

Using Structured Selectors

import { createCachedStructuredSelector } from '@veksa/reselect-utils';

const getUserProfile = createCachedStructuredSelector({
  name: state => state.user.name,
  email: state => state.user.email,
  address: createPathSelector(
    state => state.user,
    user => user?.contact?.address
  )
})((state, userId) => userId);

// Usage:
const profile = getUserProfile(state, '123');
// Returns: { name: 'John', email: '[email protected]', address: { ... } }

Key Selector Composition

import { createKeySelectorCreator, stringComposeKeySelectors } from '@veksa/reselect-utils';

const createKeySelector = createKeySelectorCreator(
  stringComposeKeySelectors,
  (a, b) => `${a}:${b}`
);

const keySelector = createKeySelector(
  (state, props) => props.userId,
  (state, props) => props.view
);

// Usage:
// keySelector(state, { userId: '123', view: 'details' }) returns '123:details'

API Reference

Core Functions

createChainSelector

Creates a selector that can be chained with map and chain methods.

createChainSelector(baseSelector)
  .map(transformFn)
  .build();
ChainSelector Methods
chain

Transforms the output of the previous selector by creating a new selector based on its result. This is where the real power of chain selectors comes in, allowing composition of selectors where the output of one becomes the context for creating the next.

chain<S2, R2>(fn: (result: R1) => Selector<S2, R2>, options?: ChainSelectorOptions): SelectorMonad
chain<S2, P2, R2>(fn: (result: R1) => ParametricSelector<S2, P2, R2>, options?: ChainSelectorOptions): SelectorMonad
  • fn - Function that receives the previous selector's result and returns a new selector
  • options - Optional configuration for caching and key selection

Examples:

// Basic chaining - transform one selector's result into another selector
const userWithDetails = createChainSelector(state => state.users)
  .chain(users => (state) => {
    // This function receives the users result and returns a new selector
    const userIds = Object.keys(users);
    return userIds.map(id => state.userDetails[id]);
  })
  .build();

// Parametric selectors with chain
const getUserPostsById = createChainSelector(propSelector)
  .chain(props => {
    // Use the props to create a targeted selector
    return createSelector(
      state => state.users[props.userId],
      state => state.posts,
      (user, posts) => posts.filter(post => post.authorId === user.id)
    );
  })
  .build();

// Multiple chains
const getUserStats = createChainSelector(state => state.users)
  .chain(users => state => Object.values(users).filter(user => user.active))
  .chain(activeUsers => state => ({
    activeCount: activeUsers.length,
    totalCount: Object.keys(state.users).length,
    activeRatio: activeUsers.length / Object.keys(state.users).length
  }))
  .build();
map

Transforms the output of the selector with a simple transformation function. Unlike chain, map doesn't create a new selector but just transforms the result of the current one.

map<R2>(fn: (result: R1) => R2, options?: ChainSelectorOptions): SelectorMonad
build

Completes the chain and returns the final selector function that can be used in components.

build(): Selector | ParametricSelector

createPathSelector

Creates a selector that safely accesses nested properties.

createPathSelector(sourceSelector, pathFn);

createBoundSelector

Creates a selector with predefined parameters.

createBoundSelector(selector, getParams);

createAdaptedSelector

Adapts a selector to work with a different parameter shape.

createAdaptedSelector(selector, paramsAdapter);

createCachedStructuredSelector

Creates a structured selector with caching support.

createCachedStructuredSelector(selectors)(keySelector);

createEmptySelector

Creates a selector that always returns undefined regardless of input. Useful as a placeholder or for conditional selection logic.

const emptySelector = createEmptySelector(baseSelector);
// Always returns undefined while maintaining the original selector's signature

createPropSelector

Creates a selector that returns the props passed to it, enabling strongly-typed access to props in selector chains.

const propSelector = createPropSelector();
// Usage: propSelector(state, props) returns props

// In a chain
const userByIdSelector = createChainSelector(propSelector)
  .chain((props) => createPathSelector(
    (state) => state.users[props.userId],
    (user) => user
  ))
  .build();

createSegmentSelector

Creates a selector with a default/initial value when the selection returns null or undefined.

const getUserSettings = createSegmentSelector(
  (state) => state.userSettings,
  { theme: 'light', notifications: true } // Default value if userSettings is null/undefined
);

createSequenceSelector

Creates a selector that returns an array of results from multiple selectors.

const getUserStats = createSequenceSelector([
  (state) => state.user.postsCount,
  (state) => state.user.followersCount,
  (state) => state.user.likesCount
]);

// Returns [postsCount, followersCount, likesCount]

Cache Objects

TreeCache

Implements a hierarchical cache structure for nested key support. Unlike flat cache objects, TreeCache allows for efficiently caching and retrieving selectors with complex, multi-part keys.

import { TreeCache } from '@veksa/reselect-utils';

// Create a TreeCache instance
const cache = new TreeCache({
  cacheObjectCreator: () => new FlatObjectCache() // Optional custom cache factory
});

// Use with complex keys (automatically normalized to arrays)
const complexKey = ['user', 123, 'profile'];
cache.set(complexKey, selectorInstance);
const selector = cache.get(complexKey);

IntervalMapCache and Garbage Collection

A time-based cache implementation with automatic garbage collection for unused selectors to prevent memory leaks.

import { IntervalMapCache, initGarbageCollector } from '@veksa/reselect-utils';

// Initialize garbage collector to clean up stale cache entries
initGarbageCollector();

// Create a cache that automatically manages memory
const cache = new IntervalMapCache();

// Items not accessed within the cache lifetime will be automatically purged
cache.set('key', selectorData);

Comparing with @veksa/reselect and @veksa/re-reselect

| Feature | @veksa/reselect | @veksa/re-reselect | @veksa/reselect-utils | |---------|-----------------|--------------------|-----------------------| | Basic memoization | ✓ | ✓ | ✓ (via dependencies) | | Parametric memoization | × | ✓ | ✓ (via dependencies) | | Safe nested property access | × | × | ✓ | | Fluent selector chains | × | × | ✓ | | Parameter binding | × | × | ✓ | | Parameter adaptation | × | × | ✓ | | Advanced key composition | × | Limited | ✓ | | Cache garbage collection | × | × | ✓ | | Hierarchical caching | × | × | ✓ | | Typescript support | ✓ | ✓ | ✓ (enhanced) |

Contributing

This project welcomes contributions and suggestions. Feel free to submit a Pull Request.

License

MIT