npm.io
5.0.0 • Published 1 month ago

@slck/utils

Licence
MIT
Version
5.0.0
Deps
0
Size
109 kB
Vulns
0
Weekly
0
Stars
1

@slck/utils

Modular, tree-shakable, zero-dependency TypeScript utility library — v5.0.0

Installation

npm install @slck/utils

Each submodule can be imported independently for optimal tree-shaking:

import { isNil, isEmpty }            from '@slck/utils/guards';
import { toCamelCaseKeys }           from '@slck/utils/transforms';
import { objectDifferenceByProps }   from '@slck/utils/comparators';
import { shiftToFirst, filterTree }  from '@slck/utils/collections';
import { timeFromSeconds, padZero }  from '@slck/utils/formatters';
import { normalizeUrl }              from '@slck/utils/paths';
import { parseSqlCustomExpressionTokens } from '@slck/utils/sql';
import { randomColor }               from '@slck/utils/color';
import type { EndpointConfig }       from '@slck/utils/types';

Or import everything at once:

import { isNil, shiftToFirst, randomColor } from '@slck/utils';

Submodules

guards — Type guards & emptiness checks
Function Description
isNil(value) true if null or undefined
isDate(value) true if a valid Date object or parseable string/number
isDateLike(value) true if an ISO 8601 string (YYYY-MM-DD…) or Date instance
isObject(value) true if a non-null, non-array object
isEmpty(value) true if null, undefined, empty string, array, or object
isNilOrEmpty(value) true if isNil or isEmpty
isEmptyInDepth(value) true if all own properties are falsy / empty
hasValidLength(value) true if the value has a numeric length property
hasValidDate(date) true if the date is not the .NET default 0001-01-01T00:00:00
isEndpointConfig(value) Type guard for EndpointConfig shape
import { isNil, isNilOrEmpty, isDateLike } from '@slck/utils/guards';

isNil(null);              // true
isNilOrEmpty('  ');       // true
isDateLike('2024-06-01'); // true

transforms — Object, string & value transforms
Function Description
toCamelCaseKeys(obj) Recursively lowercases first letter of all keys; overloaded for single object or array
deepClone(value) JSON-based deep clone. Date objects become ISO strings
trimObjectValues(obj) Recursively trims all string values; handles circular references
addSpacesToCamelCase(str) Inserts a space before each uppercase letter
initials(text, sep?) Extracts uppercased first letter from each word
toReadableTitle(value, acronyms?) Converts camelCase/PascalCase/snake_case/kebab-case to Title Case
isPalindrome(str) true if the string reads the same forwards and backwards
toTitleCase(str) Capitalises the first letter of every word
toSentenceCase(str) Lowercases the string and capitalises only the first character
formatHeader(field) Converts a field name (camelCase/snake_case) to a human-readable header
insertSpacesBetween(value) Splits camelCase/PascalCase into Title Case words
normalizeString(val) Coerces to a trimmed, lowercased string; returns '' for null/undefined
safeNumber(val) Coerces to a number; returns 0 for NaN
splitByComma(input) Splits a comma-separated string into a trimmed, non-empty array
toMinutes(hours, minutes) Converts hours + minutes into total minutes
import { toCamelCaseKeys, toReadableTitle, splitByComma } from '@slck/utils/transforms';

toCamelCaseKeys({ FirstName: 'Ada' });     // { firstName: 'Ada' }
toCamelCaseKeys([{ UserId: 1 }]);          // [{ userId: 1 }]
toReadableTitle('userFirstName');          // 'User First Name'
toReadableTitle('apiUrl', ['API']);        // 'API Url'
splitByComma('a, b, , c');                // ['a', 'b', 'c']

comparators — Object & array comparison
Function Description
objectDifferenceByProps(src, dest) Returns symmetric property-level diffs between two objects
compareObjectArrays(arr1, arr2) Validates that two arrays have structurally compatible objects (same keys + types); does not compare values
extractCommonAndDifferentValues(objects, opts?) Deep multi-object comparison — returns { same, diff }
hasDuplicateByKeys(arr, ...keys) true if any two items share identical values for the given keys
isAnyRecordWithEmptyValues(records, keys, skip?) true if any record has an empty value for the given keys
import { objectDifferenceByProps, hasDuplicateByKeys } from '@slck/utils/comparators';

objectDifferenceByProps({ a: 1, b: 2 }, { a: 1, b: 3, c: 4 });
// [{ property: 'b', sourceValue: 2, destinationValue: 3 },
//  { property: 'c', sourceValue: undefined, destinationValue: 4 }]

hasDuplicateByKeys([{ id: 1 }, { id: 2 }, { id: 1 }], 'id'); // true

collections — Array & tree utilities
Function Description
shiftToFirst(items, key, value, ci?) Moves the first matching item to index 0; returns a new array
selectMatchingByKeys(src, sel, srcKey, matchKey) Filters source array to items whose key exists in the selection array
existsInCollection(obj, collection, prop) true if the object's property value is found in the collection
multiplesInRange(start, end, multiple) Returns all multiples of multiple within [start, end]
buildTree(data, childrenKey, valueKey, nodeValueKey, delim?) Builds a nested tree from a flat array using a delimiter-based path key
keyValueObject(key, value) Creates a single-key typed object
filterTree(nodes, text, keys, childrenKey?, expandMatched?) Recursively filters a tree; pass expandMatched: false to skip UI state injection
import { shiftToFirst, filterTree, multiplesInRange } from '@slck/utils/collections';

shiftToFirst([{ id: 1 }, { id: 2 }, { id: 3 }], 'id', 3);
// [{ id: 3 }, { id: 1 }, { id: 2 }]

multiplesInRange(1, 20, 5); // [5, 10, 15, 20]

// Pure data filter (no expanded flag injected):
filterTree(tree, 'admin', ['label'], 'children', false);

formatters — Time, number & text formatters
Function Description
timeFromSeconds(seconds) Returns { days, hours, minutes, seconds }
timeBetweenDates(start, end) Time remaining between two dates; null if end ≤ start
minutesToTimeText(min, hourUnit, minUnit) Formats minutes as '2 h 10 min'
padZero(value) Pads a number with a leading zero if < 10
numberToText(num) Converts an integer to English text ('one thousand two hundred'). Max: safe integer (~9e15)
import { timeFromSeconds, numberToText, padZero } from '@slck/utils/formatters';

timeFromSeconds(3661); // { days: 0, hours: 1, minutes: 1, seconds: 1 }
numberToText(1042);    // 'one thousand and forty-two'
padZero(7);            // '07'

paths — Path & URL utilities
Function Description
extractFileName(path) Extracts filename from a Windows or Unix path
buildHierarchy(segments, sep?) Joins non-empty segments with a separator (default: ' > ')
normalizeUrl(url) Normalises absolute, hash-based, and relative URLs to an app-relative path
toComparableUrl(url) Strips query string and hash for route matching
buildIncrementalWindowsPaths(path) Returns all cumulative Windows path segments for a given path
import { extractFileName, normalizeUrl, buildHierarchy } from '@slck/utils/paths';

extractFileName('C:\\Users\\docs\\report.pdf'); // 'report.pdf'
normalizeUrl('https://example.com/app/home');  // '/app/home'
buildHierarchy(['Project', null, 'Task']);      // 'Project > Task'

sql — Custom SQL expression parser

Parses a token stream into a typed AST with full error reporting.

Export Description
parseSqlCustomExpressionTokens(tokens) Parses tokens into { valid: true, ast } or { valid: false, errors }
buildExpressionToken(raw) Builds a typed SQLCustomToken from a raw value
evaluateReturnTypeFromTokens(tokens) Returns the expression's return type from the first token
isSQLFunctionToken(token, list) true if the token is a known SQL function
SQL_FUNCTION_NAMES ['SUM', 'AVG', 'ROUND', 'RATIO', 'CONCAT', 'COUNT', 'MIN', 'MAX', 'DATEDIFF']
MSSQL_DATEDIFF_PARTS Valid MSSQL DATEDIFF date parts
import { parseSqlCustomExpressionTokens, buildExpressionToken } from '@slck/utils/sql';

const tokens = [
  { key: 'SUM', value: 'SUM' },
  { key: '(', value: '(' },
  { key: 'amount', value: 'amount', dataType: 'float' },
  { key: ')', value: ')' },
];
const result = parseSqlCustomExpressionTokens(tokens);
// { valid: true, ast: { type: 'number', func: 'SUM', args: [...] } }

color — Color utilities
Function Description
randomColor() Returns a random hex color string (excludes #000000 and #ffffff)
import { randomColor } from '@slck/utils/color';
randomColor(); // e.g. '#a3f2c1'

Types (@slck/utils/types)

import type {
  GenericObjectType,
  ErrorType,           // Error | string | null
  HttpVerb,
  PathQueryParams,
  EndpointConfig,
  EndpointRegistry,
  LegacyEndpointConfig,
  UnCapitalizeObjectKeys,
  CompareOptions,
  ApiResponse,
  PaginatedResponse,
  SortDescriptor,
  FilterDescriptor,
  ListQueryParams,
} from '@slck/utils/types';

Deprecated names

All deprecated aliases still work but will be removed in a future major version.

Deprecated Replacement
isNullOrUndefined isNil
isNullOrUndefinedEmpty isNilOrEmpty
objectNonShadowCopy deepClone
daysTimeFromSeconds timeFromSeconds
remainingDaysHoursFormTwoDates timeBetweenDates
convertMinutesToTimeText minutesToTimeText
leadZeroForMonthOrDay padZero
convertFirstLetterToUpper initials
shiftToFristWith shiftToFirst
selectMatchingObjectsByKeys selectMatchingByKeys
checkObjectPropValueExistsInCollection existsInCollection
generateMultiplesInRange multiplesInRange
constructTreeRecursively buildTree
genericObjectTypeFn keyValueObject
compareObjectArraysWithTypeSafe compareObjectArrays
generateRandomColorFn randomColor
buildExpressionTokenFromPlain buildExpressionToken
buildIncrementalPaths buildIncrementalWindowsPaths

License

MIT Chandra Sekhar A