@e280/stz
v0.4.11
Published
everyday ts fns for everything
Readme
🏂 @e280/stz
my typescript everyday carry
environment-agnostic zero-dependency tools and utilities. by https://e280.org/
🍴 utensils
⏳ async/fns
📣 subby
🧹 housekeeping
🎲 rand
✅ ok, err, result
🧬 hex and more
npm install @e280/stzimport {got} from "@e280/stz"🍴 utensils
- got — throw an error if the value is null or undefined.
const value = got(nullableValue) - need — return a Map value, or throw an error if missing.
const value = need(map, "my_key") - guarantee — get-or-create a Map value.
const value = guarantee(map, "my_key", () => 123) - setEntries — set many Map entries at once.
setEntries(map, [["my_key1", 123], ["my_key2", 234]]) - count — iterate numbers.
for (const i of count(4)) console.log(i) - grid — iterate columns and rows.
for (const [x, y] of grid(2, 4)) console.log(x, y) - arraylimit — limit array length by dropping oldest.
const trimmed = arraylimit(["a", "b", "c", "d"], 2) // ["c", "d"] // returns new array if trimming happens, // otherwise returns the same array unchanged. - pipe — run data through several fns.
const result = pipe(rawData) .to(parse) .to(validate) .to(normalize) .done() - obmap — map over object entries.
const alpha = {alpha: 1, bravo: 2, charlie: 3}obmap(alpha, n => n * 10) // {alpha: 10, bravo: 20, charlie: 30} - obfilter — filter object entries.
obfilter(alpha, n => n > 1) // {bravo: 2, charlie: 3} - deepFreeze — recursively Object.freeze an object tree.
const freezie = deepFreeze({alpha: 123}) freezie.alpha = 5 // throws error - deepEqual — check if two object trees have the same primitive values.
deepEqual({alpha: 123}, {alpha: 123}) // true - isHappy — check the identity of things with proper type guards.
isHappy(0) // true if not null nor undefined. isSad(undefined) // true if null or undefined. isBoolean(true) // true isNumber(123) // true isString("hello") // true isBigint(123n) // true isArray([]) // true isObject({}) // true isFn(() => {}) // true isSymbol(Symbol()) // true - seconds — durations and timestamps.
durations:
timestamps after Date.now:seconds(1) // 1000 minutes(1) hours(1) days(1)
timestamps before Date.now:futureSeconds(1) futureMinutes(1) futureHours(1) futureDays(1)pastSeconds(1) pastMinutes(1) pastHours(1) pastDays(1)
⏳ async/fns
- nap — return a promise that resolves later.
await nap(1000) // sleep for one second. - cycle — repeatedly call the fn back-to-back.
const stop = cycle(async() => { console.log("hello!", Date.now()) await nap(1000) // once per second. })stop() // cancel the cycle. - concurrent — await a group of named promises.
const {user, settings} = await concurrent({ user: myFetchUser(), settings: myFetchSettings(), }) - defer — a promise you can resolve/reject from outside.
const ready = defer<string>() ready.resolve("hello") await ready // "hello"ready.reject(new Error("rejected")) - collect — async iterable to array.
const entries = await collect(kv.entries()) - once — only execute the fn one time.
const init = once(() => connect()) init() init() // doesn't initialize twice. - queue — make async fn calls work one-at-a-time.
const save = queue(async(myData: string) => myWriteData(myData)) await Promise.all([save("a"), save("b"), save("c")]) // actually executes sequentially. - deadline — add an expiry timeout.
const connection = await deadline(10_000, connect) // throw DeadlineError after 10s unless connect resolves. // connect can be a promise or an async fn. - debounce — dedupe calls over time.
const search = debounce(250, async(s: string) => query(s)) search("l") search("lo") await search("lol") // last one actually runs. - microbounce — dedupe calls in this microtask.
let count = 0 const run = microbounce(() => count++) const done = run() run() run() await done count // 1
📣 subby
- subby — create a subscriber fn.
const on = subby<[number]>()const off = on(x => console.log(x))on.publish(123) // 123off() // stop listening. - pubby — create a publisher fn.
const publish = pubby<[number]>()const off = publish.on(x => console.log(x))publish(123) // 123off() // stop listening. - 🧙♂️ 'subby' and 'pubby' give you the same goody-bag.
// limited 'on' and 'publish' fns without whole toolkit. const {on, publish} = subby()const {publishAsync} = subby() await publishAsync(123) // awaits every listener (they can be async).const [value] = await on.next() // wait for the next publish. // value is 123on.set.size // direct access to the set of listeners.
🧹 housekeeping
- ev — event listeners.
import {ev} from "@e280/stz" const off = ev(window, { keydown: event => console.log(event), keyup: event => console.log(event), }) off() // removes both listeners - disposer — cleanup resources.
more disposer tricks.const dispose = disposer() dispose.schedule(() => console.log("dispose 1")) dispose.schedule(() => console.log("dispose 2")) dispose.schedule(cycle(myGameloop)) dispose.schedule(ev(window, {keydown: myKeydown})) dispose() // dispose everything backwards // *dispose ev* // *dispose cycle* // "dispose 2" // "dispose 1"const myThing = dispose.own(new MyThing(), t => t.dispose()) // create a thing and schedule its disposeconst myThing = dispose.disposable(new MyThing()) // return and schedule a Disposable thingconst {dispose, schedule, own, disposable} = disposer() schedule(cycle(myGameloop)) const myThing = disposable(new MyThing) dispose()
🎲 rand
- Rand — random utility.
const rand = new Rand(Math.random)const rand = new Rand(mulberry(123)) // seeded pseudo-random number generatorrand.u32() // get a random unsigned 32-bit integer. // 3286174905 rand.roll(0.25) // 25% chance of true. // false rand.range(10, 20) // random float between two numbers. // 14.8591238 rand.integerRange(1, 6) // inclusive integer range. // 4 rand.index(7) // given array length, pick an array index. // 5 rand.pick(["a", "b", "c"]) // pick your poison. // "c" rand.select(2, ["a", "b", "c"]) // pick multiple. // ["a", "c"] rand.yoink(["a", "b", "c"]) // remove and return one array element. // "a" rand.extract(2, ["a", "b", "c"]) // yoink multiple. // ["b", "c"] rand.shuffle(["a", "b", "c"]) // random-sort array in-place. // ["c", "a", "b"] - mulberry — seeded pseudo-random number generator.
const random = mulberry(123) random() // 0.7872516233474016 random() // 0.1785435655619949 - rand32 — crypto-random unsigned 32-bit integer.
rand32() // 3948271056 - hash32 — mix entropy into a 32-bit integer (fast, non-cryptographic).
hash32(123, "hello", 234, "world") // 1012994381
✅ ok, err, result
- ok —
Ok<Value>— indicate success.ok(123) // {ok: true, value: 123} - err —
Err<E>— indicate failure.err("fail") // {ok: false, error: "fail"} Result<Value, E>— indicate something might succeed or fail.let result: Result<number, "fail"> = ok(123) result = err("fail")- getOk/getErr — get the value, or
undefined.getOk(ok(123)) // 123 getOk(err("fail")) // undefined getErr(ok(123)) // undefined getErr(err("fail")) // "fail" - gotOk/gotErr — get the value, or throw.
gotOk(ok(123)) // 123 gotOk(err("fail")) // throws Error("fail") gotErr(ok(123)) // throws error gotErr(err("fail")) // "fail"
🧬 hex and more
- hex — encode/decode hexadecimal data.
hex(bytes) // encode Uint8Array bytes to string. hex.toBytes(string) // decode string to bytes. hex.toInteger(string) // decode string as integer. hex.fromInteger(n) // encode integer as a string. hex.random(32) // generate random encoded string, 32 bytes.// we have more than just hex. hex(bytes) // string base2(bytes) // string base36(bytes) // string base58(bytes) // string base62(bytes) // string base64(bytes) // string base64url(bytes) // string - txt — text data.
txt(bytes) // convert utf8 bytes to string. txt.toBytes("hello") // convert string to utf8 bytes. - bytes — uint8array utilities.
bytes.eq(bytesA, bytesB) // true if they're equal. bytes.random(32) // get 32 crypto-random bytes.
🧑💻 https://e280.org/
