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

utilities-ts

v0.1.0

Published

TypeScript utility functions for general-purpose and DOM manipulation, with full tree-shaking support.

Readme

utilities-ts

TypeScript utility functions for general-purpose use and DOM manipulation. Fully tree-shakeable, ESM-only, zero dependencies.

Background

Originally I had these functions in a devtools snippet and all function names started with a 'z' for easier auto completion access. I have not changed this pattern, but now you know why ;)

Install

npm install utilities-ts
# or
yarn add utilities-ts

Runtime support: Node 18+ for the utilities-ts/general entrypoint.

If you are working on this repository, use Corepack so the pinned Yarn version from package.json is used automatically:

corepack enable
yarn install

This repository currently pins [email protected] via the packageManager field. Running yarn install or npm install in a fresh clone now also builds dist/ and the DevTools snippets automatically via the install lifecycle scripts.

Import paths

| Path | Contents | | ---------------------- | --------------------------------------------------------- | | utilities-ts | Everything (general + DOM) | | utilities-ts/general | Non-DOM utilities only (safe in Node.js / SSR) | | utilities-ts/dom | DOM utilities + re-exports of commonly used general utils |

import { zstringify, ztryIt } from 'utilities-ts/general';
import { zgetAllElements, zclearALlTimeoutsAndIntervals, zstringifyDOM } from 'utilities-ts/dom';

DevTools snippets

Run npm run build:snippets or yarn build:snippets to generate plain browser-script bundles in snippets.

Generated files:

These files are meant for DevTools paste/snippet usage, not ESM imports:

  • They contain no import or export syntax.
  • Pasting index.iife.js exposes the full library as globalThis.zutils.
  • The same paste also copies every exported utility onto globalThis, so functions like zgetElement(...) are callable immediately.
  • general.iife.js and dom.iife.js do the same under globalThis.zutilsGeneral and globalThis.zutilsDom.
  • They are repository artifacts for GitHub / DevTools use and are not included in the npm package.

Typical workflow:

  1. Run npm run build:snippets or yarn build:snippets.
  2. Open one of the files in snippets.
  3. Paste it into a DevTools console or save it as a DevTools Snippet.

Highlights

DOM — Element querying

zgetElement, zgetElements, and zgetOneElement are wrappers around querySelector/querySelectorAll. They accept a selector string or an array of selectors (joined as a comma-separated list), and automatically prefix selectors with :scope when a non-document root is provided.

import { zgetElement, zgetElements, zgetOneElement } from 'utilities-ts/dom';

zgetElement('button.submit', form); // first match inside form, or null
zgetElements('[data-id]'); // all matches → Element[]
zgetOneElement('h1', container); // null + warning if not exactly one match

zgetElementDeep, zgetAllElements, and zgetOneElementDeep pierce shadow roots and same-origin iframes, making them suitable for inspecting pages with web components or embedded frames.

import { zgetElementDeep, zgetAllElements, zgetOneElementDeep } from 'utilities-ts/dom';

zgetElementDeep('video'); // first <video> anywhere in the full DOM tree
zgetAllElements('input'); // all <input>s including inside shadow roots/iframes
zgetOneElementDeep('[data-root]'); // null + warning if not exactly one match

DOM — Parent / ancestor traversal

import { zgetParentElement, zclosest, ztraverseParentElementsUntilCondition } from 'utilities-ts/dom';

// Like el.parentElement but resolves through shadow root hosts and iframe boundaries
zgetParentElement(el);

// Like el.closest() but resolves through shadow root host boundaries
zclosest(el, '.card');

// Walk up the tree until a condition is met; returns { el, traverseCount }
const { el } = ztraverseParentElementsUntilCondition(startEl, (parentEl) => parentEl.matches('.container'));

DOM — Visibility

import { zelIsVisible, zelIsActuallyVisible } from 'utilities-ts/dom';

// True if the element has non-zero bounds, non-zero opacity, and is not visibility:hidden
zelIsVisible(el);

// Like zelIsVisible but also walks the parent chain to catch display:none or opacity:0 ancestors
zelIsActuallyVisible(el);

// Pass inFlow: true to treat visibility:hidden as visible (element still occupies space)
zelIsVisible(el, true);

DOM — Styling

import { zstyleElement, zinsertCss } from 'utilities-ts/dom';

// Apply inline styles via setProperty — accepts a CSS string or a plain object
zstyleElement(el, 'color: red !important; font-size: 14px');
zstyleElement(el, { color: 'red', 'font-size': '14px' });

// Inject a <style> element into the document (appended to document.head by default)
zinsertCss('body { background: #1a1a1a; color: white; }');
zinsertCss('.card { border-radius: 8px; }', { id: 'my-styles', rootElement: shadowRoot });

DOM — Style diffing

zgetBeforeAfterStyleDiffs snapshots the computed styles of an element (and optionally all its children) before and after a timeout delay, returning only the properties that changed.

import { zgetBeforeAfterStyleDiffs } from 'utilities-ts/dom';

// Trigger an animation or interaction, then inspect what changed
const diffs = await zgetBeforeAfterStyleDiffs(el, { timeout: 500 });
// → [{ el, changed: { 'opacity': { before: '1', after: '0' } } }, ...]

// Narrow to specific properties, or exclude noisy ones
const diffs = await zgetBeforeAfterStyleDiffs(el, {
	include: ['opacity', 'transform'],
	includeChildren: false,
});

DOM — Sorting and common ancestors

import { zsortElementsByTextContent, zgetCommonAncestorContainers } from 'utilities-ts/dom';

// Re-orders elements in the DOM by their text content (locale-aware)
// If rootEl is provided, all elements are appended there; otherwise each is moved within its own parent
zsortElementsByTextContent(elements);
zsortElementsByTextContent(elements, containerEl);

// Given a set of elements, find the minimal set of ancestor containers that together cover all of them.
// Also returns a single containerContainsAll (the common ancestor of everything).
const result = zgetCommonAncestorContainers(elements);
result?.leastNumberOfContainersForElements; // [{ container, contains }]
result?.containerContainsAll; // { commonAncestor, traverseCount, contains }

DOM — Utilities

import { zclearALlTimeoutsAndIntervals, zgetWindowNoneDefaults, zstringifyDOM } from 'utilities-ts/dom';

// Clear all active setTimeout and setInterval timers
// Pass `true` to also clear timers in all same-origin iframes
zclearALlTimeoutsAndIntervals();
zclearALlTimeoutsAndIntervals(true);

// Returns window properties not present in a default empty iframe window —
// useful for inspecting what scripts or extensions have added to the global scope
const globals = zgetWindowNoneDefaults();
const globals = zgetWindowNoneDefaults(['myKnownGlobal']);

// Like zstringify but renders DOM elements as CSS selector strings (tagName#id.class)
zstringifyDOM({ el: document.querySelector('button#submit') }); // '{el: button#submit}'
zstringifyDOM(document.body, { searchString: 'submit', returnSearchInfo: true });

DOM — HTML formatting

import { zreplaceAmpersandCharacterCodes, zformatOuterHTMLString, zformatElementOuterHTML } from 'utilities-ts/dom';

zreplaceAmpersandCharacterCodes('Tom &amp; Jerry'); // 'Tom & Jerry'

zformatOuterHTMLString('<div><span>Hello</span></div>', { collapseText: true });
// → formatted multi-line HTML string

zformatElementOuterHTML(document.body, { collapseText: true });

General — Array utilities

Handles deep keys by dot-separated key path (or array of key segments)

import {
	zmapKey,
	zmapKeys,
	zmapFunction,
	zfilterKey,
	zfilterFunction,
	zgroupBy,
	zgroupByUD,
} from 'utilities-ts/general';

// Map a dot-separated key path over an array of objects
zmapKey(users, 'address.city'); // ['London', 'Berlin']
zmapKey(users, 'id'); // [1, 2, 3]

// Map multiple key paths at once, merged into one object per element
zmapKeys(users, ['id', 'address.city']);
// → [{ id: 1, city: 'London' }, { id: 2, city: 'Berlin' }]
zmapKeys(users, ['id', 'address.city'], { keepPath: true });
// → [{ id: 1, 'address.city': 'London' }, ...]

// Map a sync or async function over an array
zmapFunction(items, (x) => x * 2); // returns R[]
zmapFunction(items, async (x) => fetch(x)); // returns Promise<R[]>

// Filter by a dot-separated key path (keeps elements where the value is truthy)
zfilterKey(users, 'address.city'); // users that have a city

// Filter by a sync or async predicate
zfilterFunction(items, (x) => x > 0);
zfilterFunction(items, async (x) => isAvailable(x)); // returns Promise<T[]>

// Group by a key path
zgroupBy(users, 'department');
// → { Engineering: [...], Design: [...] }

// Group and split into uniques (groups of 1) and duplicates (groups > 1)
const { all, uniques, duplicates } = zgroupByUD(users, 'email');

General — String helpers

import { zreplaceString, zremoveString, zfixMojibake, ztextIncludes } from 'utilities-ts/general';

zreplaceString('a-b-c', '_', '-'); // 'a_b_c'
zremoveString('1,234,567', ','); // '1234567'
zfixMojibake('café'); // 'café'

ztextIncludes('Hello world', 'hello'); // true
ztextIncludes('Price incl. VAT', 'price', { fromStart: true }); // true

General — Async utilities

import { zwait, ztryIt, zpromiseFunc, ztimeoutFunction } from 'utilities-ts/general';

// Delay execution
await zwait(500);

// Go-style error handling — no try/catch needed
// Accepts a sync function, async function, or a Promise
const [data, err] = await ztryIt(fetchData);
if (err) console.error(err.message);

// Wrap a callback function that resolves via callback(result) in a Promise
const preferences = await zpromiseFunc(InspectorFrontendHost.getPreferences);

// Call a function after a delay and return its result as a Promise
const result = await ztimeoutFunction(myFunc, 1000);

General — Stringify

zstringify converts any JavaScript value to a readable string. It handles Map, Set, Date, RegExp, Error, circular references, DOM elements, and deeply nested structures.

import { zstringify } from 'utilities-ts/general';

zstringify({ a: 1, b: [2, 3] }); // '{a: 1, b: [2, 3]}'
zstringify(new Map([['x', 1]])); // 'Map{x => 1}'
zstringify(new Date('2026-01-01')); // 'Thu Jan 01 2026 ...'
zstringify(new Set([1, 2])); // 'Set{1, 2}'

// Search for a value and return which paths matched
const { stringified, matchingPaths } = zstringify(data, {
	searchString: 'hello',
	returnSearchInfo: true,
});

General — BFS path search

Useful for searching large nested objects, things on window etc

import { zfindMatchingPaths, zfindMatchingPathsShortestByFunction } from 'utilities-ts/general';

// Find the shortest path(s) to matching terminal values (BFS — shortest first)
zfindMatchingPaths(obj, { searchString: 'hello' });
// → ['x', 'y.z', 'a.b.c']

// Optionally include matching object property names too
zfindMatchingPaths(obj, { searchString: 'title', includeKeys: true });
// → ['article.title']

// Return only the matched values
zfindMatchingPaths(obj, { searchString: 'hello', returnType: 'values' });
// → ['hello', 'helloooo!', 'hello']

// Return entries instead of only paths
zfindMatchingPaths(obj, { searchString: 'hello', returnType: 'entries' });
// → [{ path: 'x', value: 'hello' }, { path: 'y.z', value: 'helloooo!' }, { path: 'a.b.c', value: 'hello' }]

// Find paths using any predicate — checked before descending so objects/arrays can match too
zfindMatchingPathsShortestByFunction(obj, {
	isMatch: (v) => Array.isArray(v) && v.length > 0,
});
// → [{ path: 'items', value: [...] }]

// Custom equality for deduplication
zfindMatchingPathsShortestByFunction(obj, {
	isMatch: (v) => typeof v === 'string',
	isEqual: (a, b) => (a as string).toLowerCase() === (b as string).toLowerCase(),
});

License

MIT