npm package: @fr0st/core
Description: Small, focused utility library for arrays, functions, math, objects, strings, and type checks.
Author: Elusive
Latest version: 3.0.4

# FrostCore

[![CI](https://github.com/elusivecodes/FrostCore/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/elusivecodes/FrostCore/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/%40fr0st%2Fcore?style=flat-square)](https://www.npmjs.com/package/@fr0st/core)
[![npm downloads](https://img.shields.io/npm/dm/%40fr0st%2Fcore?style=flat-square)](https://www.npmjs.com/package/@fr0st/core)
[![minzipped size](https://img.shields.io/bundlejs/size/%40fr0st/core?format=minzip&style=flat-square)](https://bundlejs.com/?q=@fr0st/core)
[![license](https://img.shields.io/github/license/elusivecodes/FrostCore?style=flat-square)](./LICENSE)

Small, focused utilities for arrays, functions, math, objects, strings, and type checks. FrostCore has zero runtime dependencies, works in Node and bundlers, and also ships a browser-friendly UMD bundle that exposes `globalThis._`.

## Highlights

- Named exports for tree-shaking
- Browser UMD bundle in `dist/`
- No runtime dependencies
- JSDoc-powered IntelliSense

## Installation

### Node / bundlers

```bash
npm i @fr0st/core
```

FrostCore is ESM-only. Use `import` syntax in Node and bundlers.

### Browser (UMD)

Load the bundle from your own copy or a CDN:

```html
<script src="/path/to/dist/frost-core.min.js"></script>
<!-- or -->
<script src="https://cdn.jsdelivr.net/npm/@fr0st/core@latest/dist/frost-core.min.js"></script>
<script>
    const { clamp, randomInt } = globalThis._;
    console.log(clamp(randomInt(10), 0, 9));
</script>
```

## Quick Start

```js
import {
    clamp,
    debounce,
    humanize,
    range,
    setDot,
} from '@fr0st/core';

const state = { user: { profile: { name: 'Ada' } } };

setDot(state, 'user.profile.name', 'Ada Lovelace');

const values = range(0, 10, 2);
const label = humanize('favoriteColor');

const save = debounce(() => {
    console.log('saving', state, values, label);
}, 250);

console.log(clamp(14, 0, 10)); // 10
save();
```

TypeScript note: FrostCore is written in JavaScript and uses JSDoc types, which most editors surface as IntelliSense.

## API

All utilities are exported from `@fr0st/core` as named ESM exports.

### Arrays

- `diff(array, ...arrays)`: values that exist only in the first array
- `intersect(...arrays)`: unique values shared by all arrays
- `merge(array, ...arrays)`: appends arrays or array-like values into the first array
- `randomValue(array)`: random element from an array, or `null` for an empty array
- `range(start, end, step = 1)`: numeric sequence from `start` toward `end`
- `unique(array)`: remove duplicate values
- `wrap(value)`: normalize a value into an array

```js
import { diff, merge, range, unique, wrap } from '@fr0st/core';

diff([1, 2, 3], [2]); // [1, 3]
range(0, 5); // [0, 1, 2, 3, 4, 5]
unique([1, 1, 2]); // [1, 2]
wrap(undefined); // []

const out = [1];
merge(out, [2, 3]);
// out === [1, 2, 3]
```

### Functions

- `animation(callback, options)`: run at most once per animation frame
- `compose(...callbacks)`: right-to-left function composition
- `curry(callback)`: curry a function until its arity is satisfied
- `debounce(callback, wait, options)`: delay execution until calls settle
- `evaluate(value)`: call a function or return a non-function as-is
- `once(callback)`: run a function once and cache the result
- `partial(callback, ...defaultArgs)`: partially apply arguments
- `pipe(...callbacks)`: left-to-right function composition
- `throttle(callback, wait, options)`: run at most once per wait period
- `times(callback, amount)`: execute a callback repeatedly

```js
import { compose, debounce, once, partial, pipe, throttle } from '@fr0st/core';

const add1 = (n) => n + 1;
const double = (n) => n * 2;

compose(add1, double)(3); // 7
pipe(add1, double)(3); // 8

const init = once(() => Math.random());
init() === init(); // true

partial((a, b) => [a, b], undefined, 2)(1); // [1, 2]

const debounced = debounce((value) => console.log(value), 100);
const throttled = throttle(() => console.log('tick'), 100);

debounced('last');
throttled();
```

### Math

- `clamp(value, min, max)`: clamp a number between bounds
- `clampPercent(value)`: clamp a number between `0` and `100`
- `dist(x1, y1, x2, y2)`: distance between two points
- `inverseLerp(v1, v2, value)`: interpolation amount between two values
- `len(x, y)`: vector length
- `lerp(v1, v2, amount)`: linear interpolation
- `map(value, fromMin, fromMax, toMin, toMax)`: remap a value from one range to another
- `random(a, b)`: random floating-point value
- `randomInt(a, b)`: random integer
- `toStep(value, step)`: round a number to a step size

```js
import { clamp, dist, lerp, map, random, randomInt, toStep } from '@fr0st/core';

clamp(10, 0, 1); // 1
dist(0, 0, 3, 4); // 5
lerp(0, 10, 0.25); // 2.5
map(0.5, 0, 1, 0, 10); // 5
random(10); // 0 <= n < 10
randomInt(10, 50); // 10 <= n < 50
toStep(0.123, 0.05); // 0.1
```

### Objects

- `extend(object, ...objects)`: deep-merge values into the first object
- `flatten(object)`: flatten plain-object paths into dot notation
- `forgetDot(object, key)`: delete a path from an object
- `getDot(object, key, defaultValue)`: read a path from an object
- `hasDot(object, key)`: test whether a path exists
- `pluckDot(objects, key, defaultValue)`: read the same path from many objects
- `setDot(object, key, value, options)`: assign a path in an object

```js
import { extend, flatten, getDot, pluckDot, setDot } from '@fr0st/core';

const obj = extend({ a: 1 }, { b: { c: 2 } });

getDot(obj, 'b.c'); // 2
flatten({ a: { b: 1 } }); // { 'a.b': 1 }
pluckDot([{ a: { b: 1 } }, { a: { b: 2 } }], 'a.b'); // [1, 2]

setDot(obj, 'b.c', 3);
```

### Strings

- `camelCase(string)`: convert text to `camelCase`
- `capitalize(string)`: upper-case the first character and lower-case the rest
- `escape(string)`: escape HTML entities
- `escapeRegExp(string)`: escape RegExp control characters
- `humanize(string)`: convert identifiers into readable words
- `kebabCase(string)`: convert text to `kebab-case`
- `pascalCase(string)`: convert text to `PascalCase`
- `randomString(length, chars)`: create a random string
- `snakeCase(string)`: convert text to `snake_case`
- `unescape(string)`: unescape HTML entities

```js
import { camelCase, escape, humanize, kebabCase, randomString, snakeCase } from '@fr0st/core';

camelCase('hello world'); // 'helloWorld'
humanize('helloWorld'); // 'Hello world'
kebabCase('helloWorld'); // 'hello-world'
snakeCase('helloWorld'); // 'hello_world'
escape('<div class="x">'); // '&lt;div class=&quot;x&quot;&gt;'
randomString(8); // e.g. 'aZ02kLmP'
```

### Testing

- `isArray(value)`
- `isArrayLike(value)`
- `isBoolean(value)`
- `isDocument(value)`
- `isElement(value)`
- `isFragment(value)`
- `isFunction(value)`
- `isNaN(value)`
- `isNode(value)`
- `isNull(value)`
- `isNumeric(value)`
- `isObject(value)`
- `isPlainObject(value)`
- `isShadow(value)`
- `isString(value)`
- `isText(value)`
- `isUndefined(value)`
- `isWindow(value)`

```js
import {
    isArray,
    isArrayLike,
    isFunction,
    isNumeric,
    isPlainObject,
} from '@fr0st/core';

isArray([]); // true
isArrayLike({ 0: 'a', length: 1 }); // true
isFunction(() => {}); // true
isNumeric('123.45'); // true
isPlainObject({}); // true
```

## Behavior Notes

- `merge()` and `extend()` mutate and return the first argument.
- `debounce()`, `throttle()`, and `animation()` return wrapped functions with `cancel()`.
- `range()` uses the absolute value of `step`, returns `[]` for `step === 0`, and includes `end` when the step lands on it exactly.
- `setDot()` supports `*` wildcard segments and an `{ overwrite }` option.
- `random()` and `randomInt()` use an exclusive upper bound.

## Development

```bash
npm test
npm run js-lint
npm run build
```

## License

FrostCore is released under the [MIT License](./LICENSE).