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

sidekicker

v1.0.0

Published

TypeScript utilities, including deferred LINQ-to-Objects queries

Readme

sidekicker

Sidekicker is a TypeScript utility library with a LINQ-style query API. See the examples guide for runnable examples across the query and helper APIs.

API

LINQ to Objects

Sidekicker provides a LINQ-style query API over synchronous Iterable<T> sources:

import { Linq } from "sidekicker";

const numbers = [0, 1, 2, 3, 4, 5, 6];
const evenSquares = new Linq(numbers).Where((number) => number % 2 === 0).Select((number) => number ** 2);

// No work is done until the query is enumerated.
const values = evenSquares.ToArray(); // [0, 4, 16, 36]

Sequence operators are deferred and return new queries without mutating the source query. Scalar operators execute immediately and short-circuit where possible. Each iteration re-enumerates a reusable source, so edits to an array are observed; a one-shot generator remains one-shot.

Indexed predicates and selectors receive a zero-based index:

const thirdAndLater = new Linq(numbers).Where((_number, index) => index >= 2);
const indexed = thirdAndLater.Select((number, index) => ({ number, index }));

Construction and composition

Linq.From(source), Linq.Empty<T>(), Linq.Range(start, count), and Linq.Repeat(value, count) create queries. Instance operators include AsEnumerable, Append, Prepend, Concat, DefaultIfEmpty, Chunk, and Reverse.

Filtering and projection

Where, OfType, Cast, Select, SelectMany, Take, Skip, TakeWhile, SkipWhile, TakeLast, and SkipLast return deferred sequences.

const names = new Linq([
    { name: "Ada", scores: [92, 98] },
    { name: "Grace", scores: [100] },
])
    .SelectMany(
        (person) => person.scores,
        (person, score) => ({ name: person.name, score }),
    )
    .Where((result) => result.score >= 95)
    .ToArray();

OfType and Cast take a TypeScript type guard because generic type parameters are erased at runtime. OfType skips values that fail the guard; Cast throws a TypeError on the first mismatch:

const isString = (value: unknown): value is string => typeof value === "string";
const strings = new Linq<unknown>(["a", 1, "b"]).OfType(isString);
const checked = new Linq<unknown>(["a", 1, "b"]).Cast(isString); // Throws during enumeration.

Ordering, grouping, and joins

Order, OrderBy, and OrderByDescending accept an optional comparator. Ordered queries support ThenBy and ThenByDescending; sorting is stable and evaluates each key selector once per element per enumeration.

GroupBy returns Grouping<TKey, TElement> values with a Key, Count, iteration, and ToArray. Join and GroupJoin preserve outer-sequence order and use a hash lookup for inner keys.

Set and sequence operators

Distinct, DistinctBy, Except, ExceptBy, Intersect, IntersectBy, Union, UnionBy, SequenceEqual, and Zip use streaming iterators and Set lookups where appropriate. Set operators preserve first-sequence order and yield unique values. Set and grouping keys use JavaScript Set/Map semantics; object keys compare by identity.

Terminal operators

Aggregate, All, Any, Average, Contains, Count, LongCount, ElementAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, Min, Max, MinBy, and MaxBy execute immediately. Numeric aggregates accept a number selector or numeric property key where relevant. First, Last, Single, Average, Min, and Max throw RangeError when no required result exists; the OrDefault variants return undefined when there is no match.

Materialization and execution

ToArray and ToList return arrays; ToHashSet returns a Set; ToDictionary and ToMap return Map instances; ToLookup returns Map<TKey, TElement[]>. ToDictionary throws on duplicate keys, while ToMap overwrites earlier values for duplicate keys.

Where, Select, SelectMany, Take, Skip, TakeWhile, SkipWhile, Append, Prepend, Concat, Distinct, and Zip can produce values without reading the whole source. Take therefore works with an infinite iterable. Order, OrderBy, GroupBy, Reverse, Join, and GroupJoin buffer or build lookups; set operators retain lookup state. Avoid fully enumerating unbounded sources with ToArray, Count, sorting, grouping, reversing, or joins.

The API targets synchronous LINQ-to-Objects. It does not implement IQueryable, SQL translation, expression trees, async iterables, or C# query-expression syntax.

Array helpers

Sidekicker also exports optimized array helpers: chunk, dict, distinct, partition, max, min, sum, arrayDiff, range, sort, multiSort, and Order. The existing object-comparison helper remains available as diff.

Data transformation helpers

partition(iterable, predicate) and Linq.Partition(predicate) traverse their source once and immediately return [matching, nonMatching] arrays. Root object utilities include equals, merge, pick, and omit; merge recursively combines object values into a new result, while pick and omit return shallow copies of own enumerable properties and are also available on Objects. camelCase, snakeCase, and kebabCase normalize common separators while preserving Unicode letters.

debounce, throttle, sleep, negate, and formToJson are exported from the package root; formToJson converts FormData fields into an object. retry requires a non-negative retries count of additional attempts after the initial call, and its callback receives (signal, attempt). Optional delay (a number or (attempt, error) => milliseconds) and shouldRetry(error, attempt) settings control waits and error policy. An AbortSignal is passed to the callback and cancels pending waits.

tryCatch

tryCatch wraps a synchronous or asynchronous function and returns an Either. Use raise to handle a specific error type.

import { raise, tryCatch } from "sidekicker";

const parseJson = tryCatch(
    (text: string) => JSON.parse(text) as unknown,
    raise(SyntaxError, (error) => error.message),
);

const result = parseJson('{"active":true}');
if (result.isSuccess()) {
    console.log(result.success); // { active: true }
} else {
    console.error(result.error);
}

Tooling and runtimes

The project uses Vite+ as its integrated toolchain: Oxlint for linting, Oxfmt for formatting, Vitest for tests, and vp pack (tsdown/Rolldown) for the library build. The toolchain requires Node.js ^22.18.0 || ^24.11.0 || >=26.0.0; the repository pins Node.js 24.18.0 and npm 11.16.0 with Volta.

npm install
npm run check       # Oxfmt, Oxlint, and type-aware checks
npm run typecheck   # TypeScript 7 compiler
npm test
npm run test:coverage # Vitest V8 coverage with enforced thresholds
npm run build
npm run bench

npm run bench rebuilds the package, compiles an internal benchmark entry for Dict, verifies equivalent outputs, then compares the optimized library functions with their former map/reduce or copying implementations at small and large input sizes. It uses three warmups and seven alternating samples and reports medians without timing thresholds; treat the results as local diagnostics, not universal performance guarantees.

The build emits ESM (dist/index.js), CommonJS (dist/index.cjs), and format-matched declaration files. Deno and Bun both consume the same ESM build; separate runtime-specific bundles are unnecessary. qs remains an external runtime dependency and is resolved through deno.json for Deno.

The Deno build and test tasks invoke Vite+ under Node explicitly because the toolchain uses Node-specific APIs; Deno runs the shared ESM output.

npm run smoke:node

bun run build
npm run smoke:bun

deno task build
deno task test
deno task smoke

Run each smoke command after building for that runtime. npm run smoke:node, npm run smoke:bun, and npm run smoke:deno are convenience scripts; the Deno task is also available as deno task smoke.