0.2.1 • Published 1 year ago

@resolute/std v0.2.1

Weekly downloads
-
License
ISC
Repository
github
Last release
1 year ago

Standard TypeScript Development

Deno CI CodeQL codecov

Well tested TypeScript functions suitable for use in browser, service worker, and Node.js contexts.

Installation

npm i @resolute/std

Usage

import { fetchOk } from '@resolute/std/http';
try {
  await fetchOk('https://httpstat.us/500');
} catch (error) {
  // `fetch` doesn’t throw on 500 responses.
  // `fetchOk` does :)
}

./coerce

Type validation and sanitization. This module contains a handful of utility functions and “coercers.” Coercers are unary functions that return validated/mutated input, or throw. The utility function to (alias coerce) allow you to chain these coercers. The or utility function may be used in the chain in order to specify a backup value to be returned instead of throwing an error.

Additionally, the is and not utility functions return true if a coercer or chain of coercers (to(…)) passes or false if it throws.

Utility Functions

to

Chain unary coercing functions.

import { nonempty, or, string, to, trim } from '@resolute/std/coerce';
to(string, trim, nonempty)(' foo '); // 'foo'
to(string, trim, nonempty)(' '); // throws TypeError
to(string, trim, nonempty, or(undefined))(' '); // undefined

or

Provide a backup value to be used when a coercer fails. If instanceof Error, then that error will be thrown. For any other value, it will be returned. The or(…) utility function must be the last parameter in to.

import { or, string, to } from '@resolute/std/coerce';
to(string, or(null))('foo'); // 'foo'
to(string, or(null))(1); // null
to(string, or(new Error('foo')))(1); // throws Error: foo

is

Type guard test. Use with any type guard or mutating function that throws on failure (almost all functions here do). The is function will catch the error and return false, otherwise it will return true.

import { is, string } from '@resolute/std/coerce';
is(string)('foo'); // true
is(string)(12345); // false

not

Negate type guard test. Use with any type guard or mutating function that throws on failure (almost all functions here do). The not function will catch the error and return true, otherwise it will return false. @example

import { not, string } from '@resolute/std/coerce';
not(string)('foo'); // false
not(string)(12345); // true

Type Guard Functions

string

Returns the input if it is a string. Throws otherwise.

number

Returns the input if it is a finite number. Throws otherwise including when input is NaN or Infinity.

./color

Convert between hex, RGB array, and integer representations of colors. Additionally blend between colors.

parse

Convert a hex color string #xxxxxx or rgb array r, g, b to an integer.

import { parse } from '@resolute/std/color';
parse('#888'); // 8947848

fromHex

#XXXXXX → integer

fromRgb

r, g, b → integer

toHex

integer → #XXXXXX

toRgb

integer → r, g, b

blend

Blend two colors using a percent.

import { blend, toHex } from '@resolute/std/color';
const blender = blend('#000', '#888');
toHex(blender(0.0)); // #000000 (0%)
toHex(blender(0.5)); // #444444 (50%)
toHex(blender(1.0)); // #888888 (100%)

./control

Runtime and control flow helpers.

retry

Wrap an async or promise-returning function that when called will retry up to retries times or until it resolves, whichever comes first.

import { retry } from '@resolute/std/control';
await retry(fetch)('https://httpstat.us/200');

sleep

Promisify setTimeout. Returns a Promise that settles with the return of the passed function after delay milliseconds.

import { sleep } from '@resolute/std/control';
await sleep(1000, (then) => Date.now() - then, Date.now());
// ~1000

defer

Create and return a new promise along with its resolve and reject parameters. Especially useful when “promisifying” a callback style API.

import { defer } from '@resolute/std/control';
const [promise, resolve, reject] = defer();
addEventListener('success', resolve);
addEventListener('error', reject);
await promise;

once

Wrap a function that to be executed once. Subsequent calls will return the value of the first (and only) invocation.

import { once } from '@resolute/std/control';
let value = 0;
const incr = () => ++value;
once(incr)(); // 1
once(incr)(); // 1
incr(); // 2
once(incr)(); // 1

throttle

Limit the number of invocations of a given function (or different functions) within an interval window. Useful for avoiding API rate limits.

import { throttle } from '@resolute/std/control';
const throttled = throttle(1, 1_000)(async () => {});
await throttled();
await throttled(); // 1s later

debounce

Returns a function, that, as long as it continues to be invoked (.), will not be triggered (*). The function will be called after it stops being called for threshold milliseconds.

//  /-- 10s --\ /-- 10s --\ /-- 10s --\
// . . . . . . . . . . . . .           *
import { debounce } from '@resolute/std/control';
let state = 0;
const fn = (value: number) => state += value;
const debounced = debounce(fn, 50);
debounced(1);
debounced(1);
debounced(1);
// state === 1

./cookie

Parse and stringify cookies. Methods available for DOM and service worker contexts.

./ease

Easing functions from easings.net.

./http

Helpers for interacting with Request and Response objects.

fetchOk

Throw if value is not in list.

import { fetchOk } from '@resolute/std/http';
await fetchOk('https://httpstat.us/500'); // HttpError: HTTP 500 Error

method

Throw if value is not in list.

import { method } from '@resolute/std/http';
method(['GET', 'POST'])(new Request('/', { method: 'POST' })); // 'POST'
method(['GET', 'POST'])(new Request('/', { method: 'PUT' })); // HttpError: Method must be within [GET, POST]

readBody

Invoke the correct Request/Response body reading method (json/text/formData/arrayBuffer) based on the content-type header.

import { readBody } from '@resolute/std/http';
const body = await readBody(new Response());

./intl

Intl helpers.

conjunction

Transform an array to a en/US grammar list.

import { conjunction } from '@resolute/std/intl';
conjunction('1'); // '1'
conjunction(['1', '2']); // '1 and 2'
conjunction(['1', '2', '3']); // '1, 2, and 3'

./math

Ranging, scaling, random integers, and more.

range

Define a ranging function to calculate the number bound by min and max and a percent or fraction (0 through 1). Note: percents (fractions) less than 0 or greater than 1 will return values outside of the minmax range.

import { range } from '@resolute/std/math';
range(0, 10)(0.5); // 5
range(0, 10)(1.5); // 15
range(0, 10)(-0.5); // -5

scale

Define a scaling function to calculate the percentage of value relative to min and max.

import { scale } from '@resolute/std/math';
scale(0, 10)(5); // 0.5
scale(0, 10)(15); // 1.5
scale(0, 10)(-5); // -0.5

clamp

Define a clamping function to keep a value bound to the min and max.

import { clamp } from '@resolute/std/math';
clamp(0, 1)(0.5); // 0.5
clamp(0, 1)(5); // 1
clamp(0, 1)(-5); // 0

divide

Generate a scale for each member of an array with (optional) overlap. Use with array.map() to generate the divided scales.

import { divide } from '@resolute/std/math';
[1, 2, 3]
  .map(divide())
  .map(([value, scaler]) => [
    scaler(0), // 0%
    scaler(1 / 3), // 33%
    scaler(2 / 3), // 66%
    scaler(3 / 3), // 100%
  ]);
// [
//   [  0,  1, 2, 3 ], // 1
//   [ -1,  0, 1, 2 ], // 2
//   [ -2, -1, 0, 1 ]  // 3
// ]

randomIntInclusive

Generate a random number inclusively between min and max.

randomIntExclusiveMax

Generate a random number between min (inclusively) and max (exclusively).

./mime

Validate mime types and file extensions as well as convert from mime to extension and visa versa.

extToMime

Convert a file extension to a mime type.

import { extToMime } from '@resolute/std/mime';
extToMime('avif'); // 'image/avif'
extToMime('.avif'); // 'image/avif'
extToMime('foo'); // TypeError “foo” is not a valid extension.

mimeToExt

Convert a mime type to a file extension.

import { mimeToExt } from '@resolute/std/mime';
mimeToExt('image/avif'); // 'avif'
mimeToExt('text/html; charset=utf-8'); // 'html'
mimeToExt('foo/bar'); // TypeError “foo/bar” is not a valid mime type.

./misc

Miscellaneous utilities without a home.

mapKeyAValB

Match the keys of a to the values of b by matching the values of a to the keys of b and eliminate undefined/null values.

import { mapKeyAValB } from '@resolute/std/misc';
const a = { foo: 'a', bar: 'b', baz: 'c' };
const b = { a: 1, b: 2 };
mapKeyAValB(a, b); // { foo: 1, bar: 2 }

mapKeys

Match the keys of a to the values of b by matching the values of a to the keys of b and eliminate undefined/null values.

import { mapKeys } from '@resolute/std/misc';
const a = { a: 'foo', b: 'bar', c: 'baz' };
const b = { a: 1, b: 2 };
mapKeys(a, b); // { foo: 1, bar: 2 }

properName

Composite coerce function to fix the capitalization of proper nouns.

cleanEmail

Composite coerce function to sanitize an email address.

cleanPhone

Composite coerce function to sanitize and format a 10-digit US phone number.

./promise

Promise keeper utility.

keeper

Provides caching behavior to an expensive function. Can perform periodic background refresh.

  • .stale(): sync return what is in the cache; throws if empty
  • .get(): async return cache or new invocation if cache is empty
  • .fresh(): async return a new invocation of the expensive function
  • .start(delay): continuously invoke expensive function
  • .stop(): continuous invocation
import { keeper } from '@resolute/std/promise';
const expensive = async () => Math.random() ** Math.random();
const kept = keeper(expensive);
kept.stale(); // sync; throws because cache is empty
await kept.get(); // invokes expensive() because cache is empty
kept.stale(); // sync; returns the resolved value of expensive()
kept.fresh(); // forces a new expensive() invocation

TODO: document missing items

0.2.1

1 year ago

0.2.0

1 year ago

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago

0.0.10

2 years ago

0.0.11

2 years ago

0.0.12

2 years ago

0.0.13

2 years ago

0.0.14

2 years ago

0.0.9

2 years ago

0.0.8

3 years ago

0.0.7

3 years ago

0.0.5

3 years ago

0.0.6

3 years ago

0.0.3

3 years ago

0.0.4

3 years ago

0.0.2

3 years ago

0.0.1

3 years ago