@pivanov/utils
v0.0.3
Published
A collection of personal utilities to live a happier life
Maintainers
Readme
@pivanov/utils
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/utilsQuick 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'); // trueAPI 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); // trueObject 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: '[email protected]' };
pick(user, ['name', 'email']);
// { name: 'John', email: '[email protected]' }
// 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 propertiespascalCase: Class names, React components, TypeScript typeskebabCase: CSS classes, HTML attributes, file namesslugify: 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: '[email protected]' } as const;
const picked = pick(user, ['name', 'email']);
// Type: { name: "John"; email: "[email protected]" }
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
