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

swiss-army-utils

v0.8.0

Published

General typescript utils.

Readme

swiss-army-utils

A collection of general-purpose TypeScript utilities for common programming tasks. This library provides type-safe, well-tested utility functions organized into logical categories.

Installation

npm install swiss-army-utils

Usage

Import utilities directly from their category paths:

import { isDefined } from 'swiss-army-utils/filter/isDefined';
import { asyncMap } from 'swiss-army-utils/map-reduce/asyncMap';
import { sleep } from 'swiss-army-utils/functions/sleep';

API Documentation

Functions

filterUnique(items, getKey) ⇒ Array.<T>

Filters an array to keep only unique items based on a key extraction function. When duplicate keys are found, the last occurrence is kept.

Kind: global function
Returns: Array.<T> - An array containing only unique items based on the extracted keys.

| Param | Type | Description | | --- | --- | --- | | items | Array.<T> | The array of items to filter. | | getKey | function | Function that extracts a unique key from each item. |

Example

const items = [
  { id: '1', name: 'a' },
  { id: '2', name: 'b' },
  { id: '1', name: 'c' }
];
filterUnique(items, (item) => item.id);
// returns [{ id: '1', name: 'c' }, { id: '2', name: 'b' }]

isDefined(value) ⇒ boolean

Type guard that checks if a value is neither undefined nor null. This is useful for filtering arrays and narrowing types in TypeScript.

Kind: global function
Returns: boolean - True if the value is not undefined and not null, false otherwise.

| Param | Type | Description | | --- | --- | --- | | value | T | undefined | null | The value to check for being defined. |

Example

const values = [1, null, 2, undefined, 3];
const definedValues = values.filter(isDefined);
// returns [1, 2, 3]

const maybeValue: string | null | undefined = getOptionalValue();
if (isDefined(maybeValue)) {
  // TypeScript knows maybeValue is string here
  console.log(maybeValue.toUpperCase());
}

findOrThrow(arr, predicate) ⇒ T

Finds the first item in the array that matches the predicate, throwing an error if no match is found.

Kind: global function
Returns: T - The first item that satisfies the predicate.
Throws:

  • Error If no item matches the predicate.

| Param | Type | Description | | --- | --- | --- | | arr | Array.<T> | The array to search. | | predicate | function | Function to test each item. Returns true for the item to be returned. |

Example

const numbers = [1, 2, 3, 4];

findOrThrow(numbers, n => n === 2); // returns 2
findOrThrow(numbers, n => n > 2); // returns 3
findOrThrow(numbers, n => n === 5); // throws Error: Item not found

firstOrThrow(items) ⇒ T

Returns the first item in the array, throwing an error if the array is empty.

Kind: global function
Returns: T - The first item in the array.
Throws:

  • Error If the array is empty.

| Param | Type | Description | | --- | --- | --- | | items | Array.<T> | The array to get the first item from. |

Example

const items = ['a', 'b', 'c'];

firstOrThrow(items); // returns 'a'
firstOrThrow([]); // throws Error: No item found at index 0

indexOrThrow(items, index) ⇒ T

Returns the item at the specified index in the array, throwing an error if the index is out of bounds.

Kind: global function
Returns: T - The item at the specified index.
Throws:

  • Error If no item exists at the specified index.

| Param | Type | Description | | --- | --- | --- | | items | Array.<T> | The array to get the item from. | | index | number | The index of the item to retrieve. |

Example

const items = ['a', 'b', 'c'];

indexOrThrow(items, 1); // returns 'b'
indexOrThrow(items, 3); // throws Error: No item found at index 3
indexOrThrow(items, -1); // throws Error: No item found at index -1

makeThrottled(options, fn) ⇒ function

Creates a throttled version of a function that enforces a minimum delay between executions. Multiple calls made during the delay period will share the same promise and return the same result. After the delay period, subsequent calls will trigger a new execution.

Kind: global function
Returns: function - A throttled function that returns a promise. If multiple calls are made within the delay period, they will all receive the same promise and result.

| Param | Type | Description | | --- | --- | --- | | options | Object | Configuration object. | | options.minDelay | number | Minimum delay in milliseconds between function executions. | | fn | function | The function to throttle. Can be synchronous or asynchronous. |

Example

// Throttle an API call to at most once per second
const fetchData = () => fetch('/api/data').then(r => r.json());
const throttledFetch = makeThrottled({ minDelay: 1000 }, fetchData);

// First call executes immediately
const result1 = await throttledFetch();

// Calls within 1 second share the same promise
const promise2 = throttledFetch();
const promise3 = throttledFetch();
// promise2 === promise3

// After 1 second delay, new call triggers another execution
await sleep(1100);
const result2 = await throttledFetch(); // New execution

sleep(ms) ⇒ Promise.<void>

Asynchronously pauses execution for a specified duration. Returns a promise that resolves after the given number of milliseconds.

Kind: global function
Returns: Promise.<void> - A promise that resolves after the specified delay.

| Param | Type | Description | | --- | --- | --- | | ms | number | Duration in milliseconds to sleep. |

Example

// Sleep for 1 second
await sleep(1000);

// Use in a loop with delays
for (let i = 0; i < 5; i++) {
  console.log(i);
  await sleep(500);
}

asTuple(arr) ⇒ T

Type-level function that ensures the input array is treated as a tuple type. This is useful for preserving exact array literal types.

Kind: global function
Returns: T - The same array, but with tuple type preservation.

| Param | Type | Description | | --- | --- | --- | | arr | T | The array to treat as a tuple. |

Example

const tuple = asTuple([1, 2, 3]); // Type: [1, 2, 3]
const array = [1, 2, 3]; // Type: number[]

asyncFlatMap(array, mapper) ⇒ Promise.<Array>

Asynchronously maps over an array or async iterable and flattens the results. Equivalent to calling asyncMap followed by Array.flat(). The mapping is performed sequentially to maintain order.

Kind: global function
Returns: Promise.<Array> - A promise that resolves to a flattened array of mapped results.

| Param | Type | Description | | --- | --- | --- | | array | Array.<T> | AsyncIterable.<T> | The array or async iterable to map over. | | mapper | function | Async function that transforms each item. Receives the item and its index. |

Example

const words = ['hello', 'world'];
const letters = await asyncFlatMap(words, async (word, index) => {
  await sleep(100); // Simulate async operation
  return word.split('');
});
// returns ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']

// Without asyncFlatMap, you'd need:
// const mapped = await asyncMap(words, mapper);
// const flattened = mapped.flat();

asyncMap(iterator, mapper) ⇒ Promise.<Array.<R>>

Asynchronously maps over an array or async iterable, applying an async mapper function to each item. The mapping is performed sequentially (not in parallel) to maintain order and avoid overwhelming resources.

Kind: global function
Returns: Promise.<Array.<R>> - A promise that resolves to an array of mapped results.

| Param | Type | Description | | --- | --- | --- | | iterator | Array.<T> | AsyncIterable.<T> | The array or async iterable to map over. | | mapper | function | Async function that transforms each item. Receives the item and its index. |

Example

// With array
const numbers = [1, 2, 3];
const doubled = await asyncMap(numbers, async (n, index) => {
  await sleep(100); // Simulate async operation
  return n * 2;
});
// returns [2, 4, 6]

// With async iterable
async function* generateNumbers() {
  yield 1; yield 2; yield 3;
}
const result = await asyncMap(generateNumbers(), async (n) => n * 2);
// returns [2, 4, 6]

asyncReduce(items, reducer, initialResult) ⇒ Promise.<Result>

Asynchronously reduces an array to a single value using an async reducer function. The reduction is performed sequentially, processing one item at a time.

Kind: global function
Returns: Promise.<Result> - A promise that resolves to the final accumulated result.

| Param | Type | Description | | --- | --- | --- | | items | Array.<Item> | The array to reduce. | | reducer | function | Async function that combines the accumulator with each item. | | initialResult | Result | The initial value for the accumulator. |

Example

const numbers = [1, 2, 3, 4];
const sum = await asyncReduce(
  numbers,
  async (total, num) => {
    await sleep(100); // Simulate async operation
    return total + num;
  },
  0
);
// returns 10

const words = ['hello', 'world'];
const combined = await asyncReduce(
  words,
  async (result, word) => result + ' ' + word,
  ''
);
// returns ' hello world'

batchArray(array, batchSize) ⇒ Array.<Array.<T>>

Splits an array into smaller arrays (batches) of a specified size. Each batch will contain at most batchSize items.

Kind: global function
Returns: Array.<Array.<T>> - An array of arrays, where each sub-array is a batch.

| Param | Type | Description | | --- | --- | --- | | array | Array.<T> | The array to split into batches. | | batchSize | number | The maximum number of items per batch. |

Example

batchArray([1, 2, 3, 4, 5], 2);
// returns [[1, 2], [3, 4], [5]]

batchArray(['a', 'b', 'c'], 3);
// returns [['a', 'b', 'c']]

batchArrayByWeights(items, batchSize, getWeight) ⇒ Array.<Array.<T>>

Splits an array into smaller arrays (batches) based on weighted sizes. Items are grouped into batches where the total weight doesn't exceed the specified batch size.

Kind: global function
Returns: Array.<Array.<T>> - An array of arrays, where each sub-array is a batch with total weight ≤ batchSize.

| Param | Type | Description | | --- | --- | --- | | items | Array.<T> | The array to split into batches. | | batchSize | number | The maximum total weight allowed per batch. | | getWeight | function | Function to calculate the weight of each item. |

Example

const items = ['a', 'bb', 'ccc', 'dddd'];
batchArrayByWeights(items, 5, item => item.length);
// returns [['a', 'bb'], ['ccc'], ['dddd']]
// Batch 1: 'a' (1) + 'bb' (2) = 3 ≤ 5
// Batch 2: 'ccc' (3) = 3 ≤ 5
// Batch 3: 'dddd' (4) = 4 ≤ 5

batchAsyncIterableByWeights(items, batchSize, getWeight)

Asynchronously splits an async iterable into batches based on weighted sizes. Items are grouped into batches where the total weight doesn't exceed the specified batch size. This is an async generator that yields batches as they are formed.

Kind: global function

| Param | Type | Description | | --- | --- | --- | | items | AsyncIterable.<T> | The async iterable to split into batches. | | batchSize | number | The maximum total weight allowed per batch. | | getWeight | function | Function to calculate the weight of each item. |

Example

async function* generateItems() {
  yield 'a'; yield 'bb'; yield 'ccc'; yield 'dddd';
}

for await (const batch of batchAsyncIterableByWeights(generateItems(), 5, item => item.length)) {
  console.log(batch);
}
// Outputs: ['a', 'bb'], ['ccc'], ['dddd']

groupBy(array, getKey, initialValue) ⇒ Record.<Key, Array.<T>>

Groups the elements of an array based on a key derived from each element.

Kind: global function
Returns: Record.<Key, Array.<T>> - An object where each key maps to an array of elements that share that key.

| Param | Type | Description | | --- | --- | --- | | array | Array.<T> | The array to be grouped. | | getKey | function | A function that derives the key from each element. | | initialValue | Record.<Key, Array.<T>> | An initial value for the grouped result. |

Example

const data = [
  { category: 'fruit', name: 'apple' },
  { category: 'vegetable', name: 'carrot' },
  { category: 'fruit', name: 'banana' },
];
const grouped = groupBy(data, item => item.category, {});
// Result:
// {
//   fruit: [
//     { category: 'fruit', name: 'apple' },
//     { category: 'fruit', name: 'banana' },
//   ],
//   vegetable: [
//     { category: 'vegetable', name: 'carrot' },
//   ],
// };

pluckIds(items, idGetter) ⇒ Array

Extracts unique IDs from an array of objects using an ID getter function. The function flattens nested arrays up to 10 levels deep, filters out null/undefined values, and returns a deduplicated array of IDs.

Kind: global function
Returns: Array - An array of unique, non-null IDs.

| Param | Type | Description | | --- | --- | --- | | items | Array.<T> | The array of objects to extract IDs from. | | idGetter | function | Function that extracts the ID(s) from each item. Can return single values or nested arrays. |

Example

const users = [
  { id: 1, friendIds: [2, 3] },
  { id: 2, friendIds: [1] },
  { id: 3, friendIds: [1, 2] }
];

// Extract user IDs
pluckIds(users, user => user.id);
// returns [1, 2, 3]

// Extract friend IDs (flattened and deduplicated)
pluckIds(users, user => user.friendIds);
// returns [2, 3, 1]

// Extract both user ID and friend IDs
pluckIds(users, user => [user.id, user.friendIds]);
// returns [1, 2, 3]

roundToDecimals(value, decimals) ⇒ number

Kind: global function
Returns: number - The rounded value.

| Param | Type | Description | | --- | --- | --- | | value | number | The value to round. | | decimals | number | The number of decimal places to round to. |

Example

roundToDecimals(3.14159, 2); // returns 3.14
roundToDecimals(3.14159, 0); // returns 3
roundToDecimals(3.14159, 3); // returns 3.142
roundToDecimals(3.14, 5); // returns 3.14

stringToNumber(value) ⇒ number

Safely converts a string to a number with validation. Throws an error if the string cannot be converted to a valid finite number.

Kind: global function
Returns: number - The parsed number.
Throws:

  • Error When the string is empty, not a number, or represents an infinite value.

| Param | Type | Description | | --- | --- | --- | | value | string | The string to convert to a number. |

Example

stringToNumber('123'); // returns 123
stringToNumber('3.14'); // returns 3.14
stringToNumber('-42'); // returns -42
stringToNumber('abc'); // throws Error: Invalid number: abc
stringToNumber(''); // throws Error: Invalid number:
stringToNumber('Infinity'); // throws Error: Invalid number: Infinity

toSorted(array, getKey, order) ⇒ Array.<T>

Sorts an array by a key extracted from each item, returning a new sorted array. The original array is not modified.

Kind: global function
Returns: Array.<T> - A new array with the same items sorted by the specified key and order.

| Param | Type | Description | | --- | --- | --- | | array | Array.<T> | The array to sort. | | getKey | function | Function to extract the sort key from each item. | | order | string | Sort order, either 'asc' for ascending or 'desc' for descending. Defaults to 'asc'. |

Example

const users = [{ name: 'John', age: 30 }, { name: 'Jane', age: 25 }];

// Sort by name ascending
toSorted(users, user => user.name);
// returns [{ name: 'Jane', age: 25 }, { name: 'John', age: 30 }]

// Sort by age descending
toSorted(users, user => user.age, 'desc');
// returns [{ name: 'John', age: 30 }, { name: 'Jane', age: 25 }]

createRange(length) ⇒ Array.<number>

Creates an array of consecutive integers from 0 to length-1.

Kind: global function
Returns: Array.<number> - An array of integers from 0 to length-1.
Throws:

  • Error If length is negative.

| Param | Type | Description | | --- | --- | --- | | length | number | The number of elements in the range. Must be non-negative. |

Example

createRange(5); // returns [0, 1, 2, 3, 4]
createRange(0); // returns []
createRange(-1); // throws Error

createSingletonAsync(options) ⇒ function

Creates an async singleton that lazily fetches and caches a value. The cached value is validated before each use and refreshed if invalid. Handles concurrent calls by ensuring only one fetch happens at a time.

Kind: global function
Returns: function - A function that returns the singleton value, fetching it if necessary.
Throws:

  • Error If the freshly fetched value is invalid.

| Param | Type | Description | | --- | --- | --- | | options | Object | Configuration object. | | options.getValue | function | Function that fetches the value asynchronously. | | options.isValid | function | Function that checks if the cached value is still valid. |

Example

const getToken = createSingleton({
  getValue: async () => fetchAuthToken(),
  isValid: (token) => !token.isExpired
});

// First call fetches the token
const token1 = await getToken();

// Subsequent calls return cached token if valid
const token2 = await getToken(); // Same token if still valid

// Concurrent calls share the same fetch
const [token3, token4] = await Promise.all([getToken(), getToken()]);

createSingletonSync(options) ⇒ function

Creates a synchronous singleton that lazily fetches and caches a value. The cached value is validated before each use and refreshed if invalid.

Kind: global function
Returns: function - A function that returns the singleton value, fetching it if necessary.
Throws:

  • Error If the freshly fetched value is invalid.

| Param | Type | Description | | --- | --- | --- | | options | Object | Configuration object. | | options.getValue | function | Function that fetches the value synchronously. | | options.isValid | function | Function that checks if the cached value is still valid. |

Example

const getConfig = createSingletonSync({
  getValue: () => loadConfigFromFile(),
  isValid: (config) => config.version === expectedVersion
});

// First call fetches the config
const config1 = getConfig();

// Subsequent calls return cached config if valid
const config2 = getConfig(); // Same config if still valid

callInOrderWithErrorHandling(handlers, params)

Calls all handlers in order, collecting errors and throwing them at the end. If only one error occurs, throws that error directly. If multiple errors occur, throws an AggregateError.

Kind: global function
Throws:

  • Error The single error if only one handler fails.
  • AggregateError If multiple handlers fail.

| Param | Type | Description | | --- | --- | --- | | handlers | Set.<Handler.<TParams>> | Set of handler functions to call. | | params | TParams | Parameters to pass to each handler. |

createSubject() ⇒ Object

Creates a subject (observable) for event-driven programming. Allows subscribers to listen for events and emit events to all subscribers. Handlers are called in order, and all handlers are executed even if some fail.

Kind: global function
Returns: Object - An object with subscribe and emit methods.
Example

const subject = createSubject<string>();

// Subscribe to events
const unsubscribe = subject.subscribe(async (message) => {
  console.log('Received:', message);
});

// Emit events to all subscribers
await subject.emit('Hello, world!');

// Unsubscribe when done
unsubscribe();

mergeSubjects(subjects) ⇒ Object

Merges multiple subjects into a single subscribable subject. A handler subscribed to the merged subject will receive events from all source subjects. Note: The merged subject cannot emit events, only subscribe to them.

Kind: global function
Returns: Object - A merged subject with only a subscribe method (no emit).

| Param | Type | Description | | --- | --- | --- | | subjects | Array.<Subject.<THandlerParams>> | Array of subjects to merge. |

Example

const subject1 = createSubject<string>();
const subject2 = createSubject<string>();

const merged = mergeSubjects([subject1, subject2]);

// Subscribe once to receive events from both subjects
merged.subscribe((message) => {
  console.log('Received from any subject:', message);
});

await subject1.emit('From subject1');
await subject2.emit('From subject2');
// Both messages are received by the merged subscriber

Development

Scripts

  • npm test - Run all tests
  • npm run lint - Lint the source code
  • npm run build - Build the library
  • npm run format - Format code with Prettier
  • npm run updateReadme - Generate README documentation from JSDoc comments

Publish new version of npm package

  • Run npm version [patch | minor | major]
  • Run npm publish --dry-run and verify output
  • Run npm publish

Design Principles

This repository follows these core principles:

  • Functional programming - Pure functions without side effects
  • Immutability - Data is never mutated
  • Composition - Small, composable utilities
  • Modularity - Each utility is independent
  • Single responsibility - Each function does one thing well
  • Type safety - Full TypeScript support with strict typing

License

MIT