@e280/stz

中文文档
0.4.18发布于 2026/11/12MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 6365

everyday ts fns for everything

原始使用文档
文档目录7 节
  1. 🍴 utensils
  2. ⏳ async/fns
  3. 📣 subby
  4. 🧹 housekeeping
  5. 🎲 rand
  6. ✅ ok, err, result
  7. 🧬 hex and more

🏂 @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/stz
import {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]])
    
  • Bimap — like a normal map, but keyOf makes it double-sided.
    const labels = new Bimap<number, string>()
    labels.set(1, "alpha")
    labels.set(2, "bravo")
    
    // it's a normal map.
    labels.get(1) // "alpha"
    
    // but it also has this 'keyOf' method.
    labels.keyOf("alpha") // 1
    
  • when — do something when a thing is truthy.
    when(map.size > 10, () => console.log(map))
    
  • given — do something when a thing is not null nor undefined.
    given(map.get(id), item => console.log(item))
    
  • 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:
    seconds(1) // 1000
    minutes(1)
    hours(1)
    days(1)
    
    timestamps after Date.now:
    futureSeconds(1)
    futureMinutes(1)
    futureHours(1)
    futureDays(1)
    
    timestamps before Date.now:
    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.
    
  • lane — make a one-at-a-time machine that can execute any async fn.
    const mutex = lane()
    
    await Promise.all([
      mutex(async() => writeMyData("a")),
      mutex(async() => writeMyData("b")),
      mutex(async() => writeMyData("c")),
    ]) // executes jobs 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)
      // 123
    
    off() // stop listening.
    
  • pubby — create a publisher fn.
    const publish = pubby<[number]>()
    
    const off = publish.on(x => console.log(x))
    
    publish(123)
      // 123
    
    off() // 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 123
    
    on.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.
    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"
    
    more disposer tricks.
    const myThing = dispose.own(new MyThing(), t => t.dispose())
      // create a thing and schedule its dispose
    
    const myThing = dispose.disposable(new MyThing())
      // return and schedule a Disposable thing
    
    const {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 generator
    
    rand.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.
    
  • dataView — make a DataView that matches the offset of a typed array.
    dataView(new Uint8Array([0, 1, 2, 3, 4]))
    



🧑‍💻 https://e280.org/

版本与发布记录

@e280/stz 共发布 120 个版本,首次发布于 2025/04/13;过去 12 个月发布了 56 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.4.18latest2026/11/121 天前
0.4.172026/11/103 天前
0.4.162026/11/076 天前
0.4.152026/11/067 天前
0.4.142026/10/3014 天前
0.4.132026/10/2420 天前

其他发布通道:next → 0.3.0-next.1。

关于 @e280/stz 的常见问题

@e280/stz 是什么?

everyday ts fns for everything

如何安装 @e280/stz?

在项目目录运行 npm install @e280/stz,或使用 pnpm add / yarn add / bun add。

@e280/stz 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@e280/stz 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@e280/stz 可以商用吗?

@e280/stz 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@e280/stz 的下载量有多少?

根据 npm 官方下载统计,@e280/stz 在 2026/10/13 – 2026/11/11 这 30 天内被下载 6,365 次,日均约 212 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@e280/stz 还在维护吗?

最新版本 0.4.18 发布于 2026/11/12(1 天前);至今共发布 120 个版本,其中过去 12 个月发布了 56 个;首次发布于 2025/04/13。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。