@raicamposs/toolkit
v1.1.0
Published
Um pacote npm com TypeScript.
Downloads
68
Readme
@raicamposs/toolkit
🛠️ A comprehensive TypeScript utility library with type-safe helpers for modern development
📦 Installation
npm install @raicamposs/toolkit🎯 Features
- ✅ 100% TypeScript - Full type safety and IntelliSense support
- 🎨 Clean Architecture - Organized by responsibility and purpose
- 🧪 Fully Tested - Comprehensive test coverage with Vitest
- 📦 Tree-shakeable - Import only what you need
- 🚀 Zero Dependencies - Lightweight and fast
- 📚 Well Documented - Clear examples and API documentation
📖 Table of Contents
- Async Utilities
- Functional Utilities
- Validation Utilities
- Object Utilities
- String Utilities
- Number Utilities
- JSON Utilities
- Conditional Utilities
- Type Utilities
- Builders
- Entities
⏱️ Async Utilities
Utilities for asynchronous operations and timing control.
sleep(ms: number): Promise<void>
Pauses execution for a specified duration.
import { sleep } from '@raicamposs/toolkit';
await sleep(1000); // Wait 1 second
console.log('1 second later');retryWithBackoff<T>(options): Promise<T>
Retries a function with exponential backoff on failure.
import { retryWithBackoff } from '@raicamposs/toolkit';
const result = await retryWithBackoff({
fn: async () => fetchData(),
maxRetries: 3,
delay: 1000, // Initial delay in ms
});Parameters:
fn: () => Promise<T>- Async function to retrymaxRetries: number- Maximum number of retry attemptsdelay: number- Initial delay between retries (doubles each retry)
🔧 Functional Utilities
Functional programming patterns and helpers.
Either<L, R>
Type-safe error handling without exceptions.
import { Either, Left, Right } from '@raicamposs/toolkit';
function divide(a: number, b: number): Either<string, number> {
if (b === 0) {
return new Left('Division by zero');
}
return new Right(a / b);
}
const result = divide(10, 2);
if (result.isRight()) {
console.log(result.value); // 5
} else {
console.error(result.value); // Error message
}mapOr<I, O>(value, mapper, defaultValue): O | null
Maps a value through a function, returning a default if null/undefined.
import { mapOr } from '@raicamposs/toolkit';
const user = { name: 'John' };
const upperName = mapOr(user.name, (n) => n.toUpperCase(), 'UNKNOWN');
// Result: 'JOHN'
const noName = mapOr(null, (n) => n.toUpperCase(), 'UNKNOWN');
// Result: 'UNKNOWN'mapAllOr<I, O>(array, mapper, defaultValue): O[]
Maps all items in an array, returning default if array is null/undefined.
import { mapAllOr } from '@raicamposs/toolkit';
const numbers = [1, 2, 3];
const doubled = mapAllOr(numbers, (n) => n * 2, []);
// Result: [2, 4, 6]SpecOf<T>
Predicate function type for composable specifications.
import { SpecOf } from '@raicamposs/toolkit';
interface User {
age: number;
isPremium: boolean;
}
const isAdult: SpecOf<User> = (user) => user.age >= 18;
const isPremium: SpecOf<User> = (user) => user.isPremium;and<T>(...specs): SpecOf<T>
Combines multiple specs with AND logic - returns true only if all specs are satisfied.
import { and, SpecOf } from '@raicamposs/toolkit';
const isAdult: SpecOf<User> = (user) => user.age >= 18;
const isPremium: SpecOf<User> = (user) => user.isPremium;
const isAdultPremium = and(isAdult, isPremium);
isAdultPremium({ age: 25, isPremium: true }); // true
isAdultPremium({ age: 25, isPremium: false }); // false
isAdultPremium({ age: 16, isPremium: true }); // falseor<T>(...specs): SpecOf<T>
Combines multiple specs with OR logic - returns true if at least one spec is satisfied.
import { or, SpecOf } from '@raicamposs/toolkit';
const isAdult: SpecOf<User> = (user) => user.age >= 18;
const isPremium: SpecOf<User> = (user) => user.isPremium;
const canAccess = or(isAdult, isPremium);
canAccess({ age: 16, isPremium: true }); // true
canAccess({ age: 25, isPremium: false }); // true
canAccess({ age: 16, isPremium: false }); // falsenot<T>(spec): SpecOf<T>
Negates a specification.
import { not, SpecOf } from '@raicamposs/toolkit';
const isAdult: SpecOf<User> = (user) => user.age >= 18;
const isMinor = not(isAdult);
isMinor({ age: 16 }); // true
isMinor({ age: 25 }); // falseComplex Composition Example:
import { and, or, not, SpecOf } from '@raicamposs/toolkit';
interface User {
age: number;
isPremium: boolean;
isActive: boolean;
}
const isAdult: SpecOf<User> = (user) => user.age >= 18;
const isPremium: SpecOf<User> = (user) => user.isPremium;
const isActive: SpecOf<User> = (user) => user.isActive;
// Complex rule: (isAdult AND isPremium) OR (NOT isActive)
const complexRule = or(and(isAdult, isPremium), not(isActive));
const users = [
{ age: 25, isPremium: true, isActive: true }, // true (adult + premium)
{ age: 16, isPremium: false, isActive: false }, // true (not active)
{ age: 25, isPremium: false, isActive: true }, // false
];
const filtered = users.filter(complexRule);isFlagged<T>(key): SpecOf<T>
Creates a predicate that checks if a boolean property is true.
import { isFlagged } from '@raicamposs/toolkit';
interface User {
isActive: boolean;
isPremium: boolean;
}
const isActiveUser = isFlagged<User>('isActive');
const isPremiumUser = isFlagged<User>('isPremium');
const users = [
{ isActive: true, isPremium: false },
{ isActive: false, isPremium: true },
{ isActive: true, isPremium: true },
];
const activeUsers = users.filter(isActiveUser);
// Result: [{ isActive: true, isPremium: false }, { isActive: true, isPremium: true }]recent<T, K>(config): SpecOf<T>
Creates a specification that checks if a date property is within a specified time window.
import { recent } from '@raicamposs/toolkit';
interface User {
id: number;
name: string;
createdAt: Date;
lastLoginAt: Date;
}
// Check if user was created within last 5 minutes
const isRecentlyCreated = recent<User, 'createdAt'>({
key: 'createdAt',
minutes: 5,
});
// Check if user logged in within last 30 minutes
const isRecentlyActive = recent<User, 'lastLoginAt'>({
key: 'lastLoginAt',
minutes: 30,
});
const user = {
id: 1,
name: 'John',
createdAt: new Date(),
lastLoginAt: new Date(),
};
isRecentlyCreated(user); // true
isRecentlyActive(user); // true
// Use with filter
const users = [
/* ... */
];
const recentUsers = users.filter(isRecentlyCreated);Constants:
MS_PER_MINUTE- Milliseconds per minute (60,000)
✅ Validation Utilities
Type checking and validation helpers.
isAssigned<T>(value: T): boolean
Checks if a value is not null or undefined.
import { isAssigned } from '@raicamposs/toolkit';
isAssigned(0); // true
isAssigned(''); // true
isAssigned(false); // true
isAssigned(null); // false
isAssigned(undefined); // falseisNull(value: unknown): boolean
Checks if a value is strictly null.
import { isNull } from '@raicamposs/toolkit';
isNull(null); // true
isNull(undefined); // false
isNull(0); // falseisUndefined(value: unknown): boolean
Checks if a value is strictly undefined.
import { isUndefined } from '@raicamposs/toolkit';
isUndefined(undefined); // true
isUndefined(null); // falseisNullOrUndefined(value: unknown): boolean
Checks if a value is null or undefined.
import { isNullOrUndefined } from '@raicamposs/toolkit';
isNullOrUndefined(null); // true
isNullOrUndefined(undefined); // true
isNullOrUndefined(0); // falseisEmpty(value: unknown): boolean
Checks if a value is empty (null, undefined, empty string, or empty array).
import { isEmpty } from '@raicamposs/toolkit';
isEmpty(''); // true
isEmpty([]); // true
isEmpty(null); // true
isEmpty('hello'); // false
isEmpty([1, 2, 3]); // false📦 Object Utilities
Object manipulation and transformation helpers.
clone<T>(obj: T): T
Deep clones an object.
import { clone } from '@raicamposs/toolkit';
const original = { name: 'John', address: { city: 'NYC' } };
const copy = clone(original);
copy.address.city = 'LA';
console.log(original.address.city); // 'NYC' (unchanged)pickKeys<T, K>(obj: T, ...keys: K[]): Partial<T>
Creates a new object with only specified keys.
import { pickKeys } from '@raicamposs/toolkit';
const user = { id: 1, name: 'John', email: '[email protected]', age: 30 };
const publicData = pickKeys(user, 'id', 'name');
// Result: { id: 1, name: 'John' }withoutKey<T, K>(obj: T, ...keys: K[]): Omit<T, K>
Creates a new object excluding specified keys.
import { withoutKey } from '@raicamposs/toolkit';
const user = { id: 1, name: 'John', password: 'secret' };
const safeUser = withoutKey(user, 'password');
// Result: { id: 1, name: 'John' }purgeNullishValues(obj: any): any
Recursively removes null and undefined values from objects and arrays.
import { purgeNullishValues } from '@raicamposs/toolkit';
const data = {
name: 'John',
age: null,
address: {
city: 'NYC',
zip: undefined,
},
};
const cleaned = purgeNullishValues(data);
// Result: { name: 'John', address: { city: 'NYC' } }hasOnlyUnassignedProperties(obj: unknown): boolean
Checks if an object has only unassigned (null or undefined) properties.
import { hasOnlyUnassignedProperties } from '@raicamposs/toolkit';
hasOnlyUnassignedProperties({ name: 'John', age: 30 }); // false
hasOnlyUnassignedProperties({ name: 'John', age: null }); // false
hasOnlyUnassignedProperties({ name: null, age: null }); // true
hasOnlyUnassignedProperties({}); // truehasSomePropertyAssigned(obj: unknown): boolean
Checks if an object has some assigned properties.
import { hasSomePropertyAssigned } from '@raicamposs/toolkit';
hasSomePropertyAssigned({ name: 'John', age: null }); // true
hasSomePropertyAssigned({ name: null, age: null }); // false🔤 String Utilities
String transformation and manipulation utilities.
StringUtils.toSnakeCase(str: string): string
Converts a string to snake_case.
import { StringUtils } from '@raicamposs/toolkit';
StringUtils.toSnakeCase('helloWorld'); // 'hello_world'
StringUtils.toSnakeCase('HelloWorld'); // 'hello_world'
StringUtils.toSnakeCase('hello-world'); // 'hello_world'StringUtils.toKebabCase(str: string): string
Converts a string to kebab-case.
import { StringUtils } from '@raicamposs/toolkit';
StringUtils.toKebabCase('helloWorld'); // 'hello-world'
StringUtils.toKebabCase('HelloWorld'); // 'hello-world'StringUtils.toCamelCase(str: string): string
Converts a string to camelCase.
import { StringUtils } from '@raicamposs/toolkit';
StringUtils.toCamelCase('hello_world'); // 'helloWorld'
StringUtils.toCamelCase('hello-world'); // 'helloWorld'StringUtils.toTitleCase(str: string): string
Converts a string to Title Case.
import { StringUtils } from '@raicamposs/toolkit';
StringUtils.toTitleCase('hello world'); // 'Hello World'
StringUtils.toTitleCase('HELLO WORLD'); // 'Hello World'StringUtils.slugify(text: string, separator?: string): string
Creates a URL-friendly slug from text.
import { StringUtils } from '@raicamposs/toolkit';
StringUtils.slugify('Hello World!'); // 'hello-world'
StringUtils.slugify('Café au Lait'); // 'cafe-au-lait'
StringUtils.slugify('Hello World', '_'); // 'hello_world'StringUtils.removeAccents(text: string): string
Removes accents from characters.
import { StringUtils } from '@raicamposs/toolkit';
StringUtils.removeAccents('Olá, José!'); // 'Ola, Jose!'
StringUtils.removeAccents('Café'); // 'Cafe'🔢 Number Utilities
Number formatting and rounding utilities.
roundABNT(value: number | string, fractionDigits?: number): number
Rounds numbers following ABNT (Brazilian) standards.
import { roundABNT } from '@raicamposs/toolkit';
roundABNT(1.335, 2); // 1.34 (rounds up when last digit is 5 followed by non-zero)
roundABNT(1.345, 2); // 1.34 (rounds to nearest even when 5 followed by zeros)
roundABNT(1.666, 2); // 1.67ABNT Rounding Rules:
- If digit after last kept digit is < 5: keep unchanged
- If digit is > 5 or = 5 followed by non-zero: round up
- If digit is = 5 followed by zeros: round to nearest even
📄 JSON Utilities
Safe JSON parsing and stringification.
JSONConverter.stringify(obj: any): string | null
Safely converts an object to JSON string.
import { JSONConverter } from '@raicamposs/toolkit';
JSONConverter.stringify({ name: 'John' }); // '{"name":"John"}'
JSONConverter.stringify(null); // null
JSONConverter.stringify(undefined); // nullJSONConverter.parse(json: string | null): object | undefined
Safely parses JSON string.
import { JSONConverter } from '@raicamposs/toolkit';
JSONConverter.parse('{"name":"John"}'); // { name: 'John' }
JSONConverter.parse('invalid json'); // undefined
JSONConverter.parse(null); // undefined
JSONConverter.parse(''); // undefinedJSONConverter.parseWithDefault(json: string | null, defaultValue?: any): any
Parses JSON with a fallback default value.
import { JSONConverter } from '@raicamposs/toolkit';
JSONConverter.parseWithDefault('{"name":"John"}', {}); // { name: 'John' }
JSONConverter.parseWithDefault('invalid', { name: 'Default' }); // { name: 'Default' }
JSONConverter.parseWithDefault(null, []); // []🔀 Conditional Utilities
Conditional value helpers and null coalescing.
coalesce<T>(...values: T[]): T | undefined
Returns the first non-null, non-undefined value.
import { coalesce } from '@raicamposs/toolkit';
coalesce(null, undefined, 'hello', 'world'); // 'hello'
coalesce(null, undefined); // undefined
coalesce(0, null, 5); // 0 (0 is valid)nullIf<T>(value: T, condition: boolean): T | null
Returns null if condition is true, otherwise returns the value.
import { nullIf } from '@raicamposs/toolkit';
nullIf('hello', true); // null
nullIf('hello', false); // 'hello'
nullIf(42, 42 === 0); // 42undefinedIf<T>(value: T, condition: boolean): T | undefined
Returns undefined if condition is true, otherwise returns the value.
import { undefinedIf } from '@raicamposs/toolkit';
undefinedIf('hello', true); // undefined
undefinedIf('hello', false); // 'hello'🎨 Type Utilities
TypeScript type helpers for better type safety.
Nullable<T>
Represents a value that can be null or undefined.
import { Nullable } from '@raicamposs/toolkit';
const name: Nullable<string> = null; // OK
const age: Nullable<number> = undefined; // OK
const active: Nullable<boolean> = true; // OKOptional<T, K>
Makes specific properties of a type optional.
import { Optional } from '@raicamposs/toolkit';
interface User {
id: number;
name: string;
email: string;
}
type UserWithOptionalEmail = Optional<User, 'email'>;
// { id: number; name: string; email?: string; }RequireAtLeastOne<T>
Requires at least one property from a type to be defined.
import { RequireAtLeastOne } from '@raicamposs/toolkit';
interface Contact {
email: string;
phone: string;
}
type ContactMethod = RequireAtLeastOne<Contact>;
// Must have at least email OR phone (or both)
const valid: ContactMethod = { email: '[email protected]' }; // OK
const invalid: ContactMethod = {}; // ErrorOther Type Utilities
ValueOf<T>- Extracts value types from an objectReplace<T, R>- Replaces properties in a type with new onesOmitNullableProperties<T>- Removes nullable properties from a typePrettify<T>- Simplifies complex types for better readabilityObjectKeys<T>- Type-safe Object.keys() with proper typingObjectEntries<T>- Type-safe Object.entries() with proper typingliteral<T>(value)- Creates literal types from string values
🏗️ Builders
Builder patterns for constructing complex objects.
CompositeBuilder<T>
Fluent builder for applying transformations to objects.
import { CompositeBuilder } from '@raicamposs/toolkit';
interface User {
name: string;
email: string;
isActive: boolean;
}
const builder = CompositeBuilder.new<User>()
.add((user) => ({ ...user, email: user.email.toLowerCase() }))
.add((user) => ({ ...user, isActive: true }));
const user = builder.build({
name: 'John',
email: '[email protected]',
isActive: false,
});
// Result: { name: 'John', email: '[email protected]', isActive: true }CompositeCriteria<T> & OrCriteria<T>
Composite pattern for building complex filtering logic.
import { CompositeCriteria, OrCriteria, Criteria } from '@raicamposs/toolkit';
class AgeCriteria implements Criteria<User> {
matching(users: User[]): User[] {
return users.filter((u) => u.age >= 18);
}
}
class ActiveCriteria implements Criteria<User> {
matching(users: User[]): User[] {
return users.filter((u) => u.isActive);
}
}
// AND logic
const composite = new CompositeCriteria<User>().add(new AgeCriteria()).add(new ActiveCriteria());
const filtered = composite.matching(users);
// OR logic
const orCriteria = new OrCriteria(new AgeCriteria(), new ActiveCriteria());🎯 Entities
Domain entities with built-in validation.
CPF
Brazilian CPF (Cadastro de Pessoas Físicas) validation and formatting.
import { CPF } from '@raicamposs/toolkit';
const cpf = CPF.create('12345678909');
if (cpf.isRight()) {
console.log(cpf.value.format()); // '123.456.789-09'
console.log(cpf.value.toString()); // '12345678909'
}CNPJ
Brazilian CNPJ (Cadastro Nacional da Pessoa Jurídica) validation and formatting.
import { CNPJ } from '@raicamposs/toolkit';
const cnpj = CNPJ.create('12345678000195');
if (cnpj.isRight()) {
console.log(cnpj.value.format()); // '12.345.678/0001-95'
console.log(cnpj.value.toString()); // '12345678000195'
}Email
Email validation entity.
import { Email } from '@raicamposs/toolkit';
const email = Email.create('[email protected]');
if (email.isRight()) {
console.log(email.value.toString()); // '[email protected]'
}Phone
Brazilian phone number validation and formatting.
import { Phone } from '@raicamposs/toolkit';
const phone = Phone.create('11987654321');
if (phone.isRight()) {
console.log(phone.value.format()); // '(11) 98765-4321'
console.log(phone.value.toString()); // '11987654321'
}📁 Project Structure
The toolkit is organized following Clean Architecture principles:
src/
├── utils/
│ ├── async/ # Async operations (sleep, retry)
│ ├── conditional/ # Conditional helpers (coalesce, nullIf)
│ ├── functional/ # Functional patterns (Either, mapOr)
│ ├── json/ # JSON utilities (JSONConverter)
│ ├── number/ # Number utilities (roundABNT)
│ ├── object/ # Object manipulation (clone, pick, purge)
│ ├── string/ # String transformations (StringUtils)
│ └── validation/ # Type checking (isNull, isEmpty)
├── types/ # TypeScript type utilities
├── entities/ # Domain entities
└── builders/ # Builder patterns🧪 Testing
Run tests with coverage:
npm test # Run tests in watch mode
npm run coverage # Generate coverage report
npm run lint # Check code quality
npm run lint:fix # Auto-fix linting issues
npm run format # Format code with Prettier
npm run format:check # Check code formatting📝 License
MIT © [raicamposs]
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📮 Support
For issues and questions, please open an issue on GitHub.
