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

@azure-net/tools

v2.0.0

Published

Azure-net tools library

Readme

@azure-net/tools

Small, typed utilities shared by the Azure Net packages. The public entry is safe to import during SSR; browser-backed helpers return safe fallback values when browser APIs are unavailable.

Installation

npm install @azure-net/tools
import { ObjectUtil, DateUtil, DebounceUtil } from '@azure-net/tools';

Environment

EnvironmentUtil exposes runtime checks:

  • isBrowser, isServer, isWebWorker
  • isDevelopment, isProduction
  • currentEnvironment()
  • currentMode()

For tree-shakeable build-time flags use the dedicated entry:

import { BROWSER, DEV, NODE } from '@azure-net/tools/environment';

ObjectUtil

const source = { id: 1, name: 'Ada', password: 'secret' };

ObjectUtil.pick(source, ['id', 'name']);
ObjectUtil.omit(source, ['password']);
ObjectUtil.toEntries(source);
ObjectUtil.toEntries(source, { mapKey: (key) => key.toUpperCase() });
ObjectUtil.equals(left, right);
ObjectUtil.isAllKeysEmpty(value);

Available methods:

  • clone(value) performs a shallow object or array clone.
  • deepClone(value, structured = false) performs a deep clone.
  • equals(left, right) performs symmetric, cycle-safe deep equality and preserves reference topology.
  • compareAsString(left, right) compares JSON representations when JSON semantics are sufficient.
  • toEntries(value, options?) returns typed { key, value } entries and optionally maps keys.
  • countProps, isObjectEmpty, isAllKeysEmpty, pick, and omit cover common object operations.

deepClone uses JSON serialization by default. This is predictable in SSR runtimes where structuredClone may not exist, but functions, undefined, symbols, prototypes, and non-JSON types are not preserved. Date values become strings. Pass true only when the runtime supports structuredClone and richer types or circular structures must be preserved:

const jsonSafeCopy = ObjectUtil.deepClone(value);
const structuredCopy = ObjectUtil.deepClone(value, true);

DebugUtil

DebugUtil formats arbitrary values for diagnostics without mutating them.

const text = DebugUtil.stringify(value, { maxDepth: 6, maxEntries: 100 });
const html = DebugUtil.render(value, { theme: 'dark' });
  • stringify supports cycles, Map, Set, Date, RegExp, errors, typed arrays, bigint, symbols, and functions.
  • render returns escaped, syntax-highlighted HTML. Objects and arrays use a readable code block; primitives use a compact gray value box.
  • maxDepth and maxEntries prevent an accidental diagnostic render from traversing an unbounded graph.

The returned HTML is generated by the library and input strings are escaped, but it should still be mounted through the framework's normal trusted-markup mechanism intentionally.

DateUtil

Date strings are accepted only in these strict forms:

  • YYYY-MM-DD
  • ISO-like date-time: YYYY-MM-DDTHH:mm[:ss[.fraction]][Z|+HH:mm|-HH:mm]

Impossible calendar dates, invalid times, malformed offsets, and arbitrary date-like strings are rejected. A date-only string is treated as a calendar date and is not shifted by UTC or timezone formatting.

DateUtil.isDate('2025-02-29'); // false
DateUtil.toDate('2025-05-23'); // "23.05.2025"
DateUtil.toTime('2025-08-15T12:00:00Z', { timeZone: 'Asia/Tokyo' }); // "21:00"
DateUtil.toFormat('2025-12-01', 'dd MM yyyy', { locale: 'en' });

DateUtil.compare('2025-01-01', '2025-01-02'); // -1
DateUtil.isBefore('2025-01-01', '2025-01-02'); // true
DateUtil.isAfter('2025-01-02', '2025-01-01'); // true
DateUtil.isSame('2025-01-01', '2025-01-01'); // true
DateUtil.isBetween('2025-01-02', '2025-01-01', '2025-01-03'); // true
DateUtil.isPast(date);
DateUtil.isFuture(date);

The default locale is always en. Pass locale directly or install a resolver when locale is application state:

DateUtil.setLocale(() => currentLocale);
DateUtil.clearLocaleResolver();

Frequently reused Intl.DateTimeFormat instances and generated month lists are cached.

Formatting tokens: yyyy, yy, MM (month name), mm, dd, d, HH, ii, ss.

TextUtil

TextUtil.pluralize(2, ['item', 'items']);
TextUtil.truncate('long text', 4); // "long..."
TextUtil.capitalize('hello');
TextUtil.decapitalize('Hello');
TextUtil.isEmptyOrWhitespace('   ');

For truncate, maxLength is the number of source characters kept before ellipsis. The final output can therefore have length maxLength + ellipsis.length.

FormDataUtil

Converts nested objects to FormData and bracket-notation fields back to objects.

const form = new FormData();
form.append('user[name]', 'Ada');
form.append('tags[]', 'typescript');

const value = FormDataUtil.toObject(form);
const serialized = FormDataUtil.fromObject(value);

toObject supports object paths, array indexes, and append syntax. Dangerous prototype path segments are rejected, and path depth and array indexes are bounded. fromObject supports arrays, Map, Set, Date, Blob, and File, and rejects cyclic input.

LocalStorageUtil

All methods are SSR-safe: set, get, delete, has, keys, getAll, and clear. Browser support is probed once and cached.

LocalStorageUtil.set('settings', { theme: 'dark' });
LocalStorageUtil.get<{ theme: string }>('settings');
LocalStorageUtil.get<string>('raw-json', { parse: false });

Use createInstance to bind a key once. Default read options are also bound and can be overridden for an individual read:

const SessionStorage = LocalStorageUtil.createInstance<{ token: string }>('session');
SessionStorage.set({ token: 'token' });
SessionStorage.get();
SessionStorage.has();
SessionStorage.delete();

const RawToken = LocalStorageUtil.createInstance<string>('token', { parse: false });
RawToken.get();

JSON serialization and storage quota/security failures are caught and do not leak exceptions from this helper.

Cookies

Browser-only cookie access with SSR-safe fallbacks.

Cookies.set('user', { id: 1 }, { expires: 7, sameSite: 'Lax' });
Cookies.get<{ id: number }>('user');
Cookies.get<string>('raw-json', { parse: false });
Cookies.getAll({ parse: false });
Cookies.has('user');
Cookies.delete('user');
Cookies.clear();

Values are URI encoded. Reads parse JSON by default and return the decoded string when JSON parsing is not applicable.

DebounceUtil

const search = DebounceUtil.debounce(runSearch, 300);
search('a');
search('ab');
search.flush();
search.cancel();

const save = DebounceUtil.debounceAsync(saveDraft, 300);
await save(draft);
save.cancel();

debounceAsync returns one promise per caller. All queued callers receive the last invocation's result or rejection. Timer-driven rejection is consumed internally after those promises are rejected, preventing a second unhandled rejection from the internal invocation.

ThrottleUtil

Creates a leading and trailing throttled function:

const update = ThrottleUtil.throttle(updatePosition, 100);
update();
update.pending();
update.flush();
update.cancel();

Pending arguments are released after invocation or cancellation.

EventBus

Interfaces and type aliases can be used as event maps:

interface AppEvents {
	ready: { id: string };
	failed: Error;
}

const bus = new EventBus<AppEvents>({}, { mode: 'serial' });
const unsubscribe = bus.subscribe('ready', ({ id }) => console.log(id));
await bus.publish('ready', () => ({ id: '1' }));
unsubscribe();

The default serial mode awaits listeners in registration order. mode: 'parallel' executes channel and catch-all listener snapshots concurrently. Listener exceptions are passed to onError when configured and otherwise logged.

AbortUtil

const { signal, cleanup } = AbortUtil.withTimeout(parentSignal, 5_000);
try {
	await fetch(url, { signal });
} finally {
	cleanup();
}

AbortUtil.isAbortError(error);

Source cancellation and its reason are propagated. A timeout aborts with an error named TimeoutError. Call cleanup when the operation settles to release the timer and source listener.

PromiseUtil

if (PromiseUtil.isPromiseLike(value)) {
	await value;
}

The check intentionally uses the thenable contract instead of instanceof Promise, so it works across realms and with custom promise implementations.

ErrorUtil

const error = ErrorUtil.toError(caughtValue, 'Unknown operation error');

Existing Error instances are returned unchanged. Strings, primitives, and error-like objects become an Error; the original value is retained as its non-enumerable cause.

UidGenerator

UidGenerator.generateUniqueString(24);
UidGenerator.generateUniqueNumber(1, 100);
UidGenerator.generateUuid();
UidGenerator.generateNanoId();
UidGenerator.generateTimestampId('order');
UidGenerator.generateHashId();
UidGenerator.isValidUuid(value);

Random strings and numbers use rejection sampling to avoid modulo bias. Numeric bounds must be safe integers and generated values are within the inclusive range.

IntersectionObserverUtil

Observers with identical options share one native IntersectionObserver instance.

const observer = new IntersectionObserverUtil(element, {
	callback(entry) {
		console.log(entry.isIntersecting);
	},
	once: true,
	onceMode: 'intersect',
	triggerOnExit: false
});

observer.disconnect();

The utility safely becomes inert when IntersectionObserver is unavailable.

DownloadUtil

DownloadUtil.download(blob, 'report.pdf');
DownloadUtil.download('https://example.com/report.pdf', 'report.pdf');

Generated object URLs are revoked on the next task, after the synthetic click has consumed them. Calls are no-ops during SSR.

License

MIT