JSPM

@pivanov/utils

0.0.3
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 154587
  • Score
    100M100P100Q185655F
  • License MIT

A collection of personal utilities to live a happier life

Package Exports

  • @pivanov/utils

Readme

@pivanov/utils

A comprehensive collection of TypeScript utilities for modern web development

NPM Version    Bundle Size    CI Status    Coverage Status

Features

  • πŸ”’ Type-Safe - Full TypeScript support with strict type checking and excellent type inference
  • 🎯 Tree-Shakeable - Import only what you need for minimal bundle size
  • ⚑ Performance Focused - Optimized implementations with no external dependencies
  • πŸ§ͺ Well Tested - Comprehensive test coverage for reliability
  • πŸ“¦ Modular - Organized into focused modules for easy navigation
  • πŸ“ Well Documented - Detailed JSDoc comments and examples

Installation

pnpm add @pivanov/utils
# or
npm install @pivanov/utils
# or
yarn add @pivanov/utils

Quick Start

import { camelCase } from '@pivanov/utils/string';
import { pick } from '@pivanov/utils/object';
import { isString } from '@pivanov/utils/assertion';

camelCase('foo-bar'); // 'fooBar'
pick({ name: 'John', age: 30 }, ['name']); // { name: 'John' }
isString('hello'); // true

API Reference

Type Guards & Assertions

Runtime type checking with TypeScript type narrowing.

import {
  isString,
  isNumber,
  isBoolean,
  isObject,
  isFunction,
  isNull,
  isUndefined,
} from '@pivanov/utils/assertion';

// Type guards automatically narrow TypeScript types
function processValue(value: unknown) {
  if (isString(value)) {
    return value.toUpperCase(); // TypeScript knows this is a string
  }
  if (isNumber(value)) {
    return value.toFixed(2); // TypeScript knows this is a number
  }
}

// Examples
isString('hello'); // true
isNumber(42); // true
isBoolean(true); // true
isObject({}); // true (plain objects only, not arrays)
isFunction(() => {}); // true
isNull(null); // true
isUndefined(undefined); // true

Object Utilities

Immutable object manipulation with full type safety.

import { pick, omit, merge, deepMerge } from '@pivanov/utils/object';

// Pick specific properties
const user = { name: 'John', age: 30, email: 'john@example.com' };
pick(user, ['name', 'email']);
// { name: 'John', email: 'john@example.com' }

// Omit specific properties
omit(user, ['email']);
// { name: 'John', age: 30 }

// Shallow merge
merge({ a: 1, b: 2 }, { b: 3, c: 4 });
// { a: 1, b: 3, c: 4 }

// Deep merge with nested structures
deepMerge(
  { user: { name: 'John', settings: { theme: 'dark' } } },
  { user: { settings: { fontSize: 14 } } }
);
// { user: { name: 'John', settings: { theme: 'dark', fontSize: 14 } } }

String Utilities

String transformation utilities with TypeScript template literal types support.

import {
  camelCase,
  pascalCase,
  kebabCase,
  slugify,
  capitalize,
  uncapitalize,
  capitalizeFirstLetter,
} from '@pivanov/utils/string';

// Case transformations
camelCase('foo-bar'); // 'fooBar'
camelCase('FOO_BAR'); // 'fooBar'

pascalCase('foo-bar'); // 'FooBar'
pascalCase('foo_bar_baz'); // 'FooBarBaz'

kebabCase('fooBar'); // 'foo-bar'
kebabCase('XMLHttpRequest'); // 'xml-http-request'

slugify('Hello World!'); // 'hello-world'
slugify('Über Café'); // 'uber-cafe'

// Capitalization with TypeScript support
capitalize('hello'); // 'Hello' (type: Capitalize<'hello'>)
uncapitalize('Hello'); // 'hello' (type: Uncapitalize<'Hello'>)
capitalizeFirstLetter('hello world'); // 'Hello world'

When to use each:

  • camelCase: JavaScript/TypeScript variables and properties
  • pascalCase: Class names, React components, TypeScript types
  • kebabCase: CSS classes, HTML attributes, file names
  • slugify: URL slugs (more aggressive than kebabCase)

Promise Utilities

Simple async utilities for better promise handling.

import { sleep } from '@pivanov/utils/promise';

async function example() {
  console.log('Start');
  await sleep(1000); // Wait 1 second
  console.log('Done');
}

Deep Clone & Equality

High-performance deep cloning and comparison with circular reference handling.

import { deepClone, isEqual } from '@pivanov/utils/tools';

// Deep clone complex structures
const original = {
  nested: { array: [1, 2, { value: 3 }] },
  date: new Date(),
  map: new Map([['key', 'value']]),
  set: new Set([1, 2, 3]),
  circular: {} as any,
};
original.circular = original; // Circular reference

const cloned = deepClone(original);
// Fully independent copy with circular references preserved

// Deep equality comparison
isEqual({ a: [1, 2, 3] }, { a: [1, 2, 3] }); // true
isEqual(new Date('2024-01-01'), new Date('2024-01-01')); // true
isEqual([1, 2, [3, 4]], [1, 2, [3, 4]]); // true
isEqual(new Set([1, 2]), new Set([2, 1])); // false (Sets maintain order)

Supported types:

  • Primitives (string, number, boolean, null, undefined, symbol, bigint)
  • Arrays (including sparse arrays)
  • Plain objects (with getters/setters)
  • Date, RegExp, Map, Set
  • TypedArrays, ArrayBuffer, Buffer
  • Circular references

Event Bus

Type-safe, lightweight event bus with React hooks support.

import { busDispatch, busSubscribe, useEventBus } from '@pivanov/utils/tools';

// Define your application events (optional but recommended)
type AppEvents = {
  'user-logged-in': { id: number; name: string };
  'data-updated': { timestamp: number };
  'notification': { message: string; type: 'info' | 'error' };
};

// React hook usage (auto-unsubscribes on unmount)
function UserProfile() {
  useEventBus<AppEvents>('user-logged-in', (data) => {
    console.log(`Welcome ${data.name}!`);
  });

  return <div>Profile</div>;
}

// Vanilla JavaScript usage
const unsubscribe = busSubscribe<AppEvents>('data-updated', (data) => {
  console.log('Updated at:', data.timestamp);
});

// Later: cleanup
unsubscribe();

// Dispatch events anywhere in your app
busDispatch<AppEvents>('user-logged-in', { id: 1, name: 'John' });
busDispatch<AppEvents>('notification', {
  message: 'Settings saved',
  type: 'info',
});

Features:

  • Fully type-safe with TypeScript
  • Works across React and vanilla JavaScript
  • Automatic cleanup with React hook
  • Uses hashed topic names to avoid collisions
  • Zero dependencies

Browser Cache API

Type-safe wrapper around the browser Cache API with expiration support.

import {
  storageSetItem,
  storageGetItem,
  storageRemoveItem,
  storageExists,
  storageClear,
  storageClearByPrefixOrSuffix,
  storageGetAllKeys,
  storageCalculateSize,
} from '@pivanov/utils/tools';

const CACHE_NAME = 'my-app-cache';

// Store data
await storageSetItem(CACHE_NAME, 'user-data', {
  id: 1,
  name: 'John',
  bigNumber: BigInt(9007199254740991), // BigInt support!
});

// Retrieve data
const userData = await storageGetItem<{ id: number; name: string }>(
  CACHE_NAME,
  'user-data'
);

// Check existence
const exists = await storageExists(CACHE_NAME, 'user-data');

// Remove specific item
await storageRemoveItem(CACHE_NAME, 'user-data');

// Get all keys
const keys = await storageGetAllKeys(CACHE_NAME);

// Clear items by prefix
await storageClearByPrefixOrSuffix(CACHE_NAME, 'temp-', true);

// Clear items by suffix
await storageClearByPrefixOrSuffix(CACHE_NAME, '-cache', false);

// Calculate cache size (in bytes)
const totalSize = await storageCalculateSize(CACHE_NAME);
const itemSize = await storageCalculateSize(CACHE_NAME, 'user-data');

// Clear all cache
await storageClear(CACHE_NAME);

Features:

  • Automatic JSON serialization/deserialization
  • BigInt support (automatically converted to strings)
  • Type-safe with generics
  • Works with absolute URLs as keys
  • Size calculation utilities

DOM Utilities

Browser-safe DOM manipulation helpers.

import {
  isBrowser,
  checkVisibility,
  setStyleProperties,
  calculateRenderedTextWidth,
} from '@pivanov/utils/tools';

// Check if running in browser (SSR-safe)
if (isBrowser()) {
  // Browser-only code
  window.addEventListener('scroll', handleScroll);
}

// Check element visibility in viewport
const element = document.querySelector('.my-element') as HTMLElement;
if (checkVisibility(element)) {
  element.classList.add('visible');
}

// Set CSS custom properties
setStyleProperties(element, {
  '--primary-color': '#3b82f6',
  '--spacing': '1rem',
  '--border-radius': '8px',
});

// Calculate text width for dynamic layouts
const width = calculateRenderedTextWidth('Hello World', 16);
const widthUppercase = calculateRenderedTextWidth('Hello World', 16, true);
const widthCustomFont = calculateRenderedTextWidth(
  'Hello World',
  16,
  false,
  'Arial'
);

Tree Shaking

Import only what you need to minimize bundle size:

// βœ… Good: Import specific utilities
import { camelCase } from '@pivanov/utils/string';
import { deepClone } from '@pivanov/utils/tools';

// ❌ Avoid: Importing everything
import * as utils from '@pivanov/utils';

TypeScript Support

All utilities provide excellent type inference:

import { pick } from '@pivanov/utils/object';
import { capitalize } from '@pivanov/utils/string';

// TypeScript infers exact types
const user = { name: 'John', age: 30, email: 'john@example.com' } as const;
const picked = pick(user, ['name', 'email']);
// Type: { name: "John"; email: "john@example.com" }

const str = 'hello' as const;
const capitalized = capitalize(str);
// Type: "Hello"

Module Overview

@pivanov/utils
β”œβ”€β”€ /assertion    - Type guards (isString, isNumber, etc.)
β”œβ”€β”€ /object       - Object utilities (pick, omit, merge, deepMerge)
β”œβ”€β”€ /promise      - Async utilities (sleep)
β”œβ”€β”€ /string       - String utilities (camelCase, kebabCase, etc.)
└── /tools        - Various tools
    β”œβ”€β”€ deepClone
    β”œβ”€β”€ isEqual
    β”œβ”€β”€ DOM utilities (isBrowser, checkVisibility, etc.)
    β”œβ”€β”€ eventBus (busDispatch, busSubscribe, useEventBus)
    └── Cache API (storageSetItem, storageGetItem, etc.)

Browser Compatibility

  • Modern browsers with ES2015+ support
  • Cache API requires browser support (Chrome 40+, Firefox 41+, Safari 11.1+)
  • SSR-safe with browser environment detection

License

MIT Β© Pavel Ivanov