@dirstack/utils

中文文档
3.3.0发布于 2026/11/10MITTypeScriptES Module构建来源已验证近 30 天下载 8918

一套轻型公用事业

中文使用文档
文档目录20 节
  1. 安装
  2. 使用量
  3. 里面的东西
  4. 日期和时区
  5. 代理服务器背后的客户端 IP
  6. 用时间预算重试
  7. 从v2 移动
  8. 软件包格式
  9. 删除的别名
  10. 日期格式化器只选择选项对象
  11. sortBy 选择选项对象
  12. 删除的助手
  13. processBatchWithErrorHandling 现在 processBatchSettled
  14. toBase64 返回平面基数64,以及 toDataUrl 返回数据 URL
  15. slugify 选择选项对象
  16. getExcerpt 现在 truncate
  17. getPageParams 现在 getPagination
  18. joinAsSentence 选择选项对象
  19. 发展
  20. 许可证

@ dirstack/ utils

一套轻量级、依赖灯光的TypeScript公用事业,由各项目共享。 树能震动,只使用无害环境管理,并完全打入.

安装

bun add @dirstack/utils
# or: npm install @dirstack/utils

需要节点js 22.12 或以后。 软件包只装ES模块,而CommonJS代码可以 require() 它在这些版本.

使用量

从软件包根导出所有 :

import { chunk, clamp, formatBytes, slugify, tryCatch } from "@dirstack/utils"

chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
clamp(12, 0, 10) // 10
formatBytes(1536, 1) // "1.5 KB"
slugify("Hello World") // "hello-world"

const { data, error } = await tryCatch(fetch("/api"))

每一个模块在自己的路径上也可以使用,例如: @dirstack/utils/array 或者说 @dirstack/utils/time. Bundlers 将未使用的代码从根导入中去除,因为构建每个模块保存一个文件.

里面的东西

模块要点
arrayuniq, (中文(简体) ), uniqBy, (中文(简体) ), chunk, (中文(简体) ), groupBy, (中文(简体) ), keyBy, (中文(简体) ), countBy, (中文(简体) ), compact, (中文(简体) ), sortBy, (中文(简体) ), sum, (中文(简体) ), sumBy, (中文(简体) ), range
batchprocessBatch, (中文(简体) ), processBatchSettled (有秩序、货币限制、 onProgress(中文(简体) )
errorsgetErrorMessage, (中文(简体) ), toError
filesformatBytes, (中文(简体) ), isMimeTypeMatch, (中文(简体) ), toBase64, (中文(简体) ), toDataUrl
formatformatNumber, (中文(简体) ), formatCurrency, (中文(简体) ), formatIntervalAmount, (中文(简体) ), currencyExponent, (中文(简体) ), minorUnits
helperssleep, (中文(简体) ), isTruthy, (中文(简体) ), tryCatch, (中文(简体) ), retry, (中文(简体) ), withTimeout
httpisValidUrl, (中文(简体) ), addProtocol, (中文(简体) ), removeProtocol, (中文(简体) ), getDomain, (中文(简体) ), isExternalUrl, (中文(简体) ), joinUrlPaths, (中文(简体) ), setQueryParams, (中文(简体) ), removeQueryParams, (中文(简体) ), isValidImageSrc, (中文(简体) ), isHostname, (中文(简体) ), normalizeHostname, (中文(简体) ), stripWildcard, (中文(简体) ), isWithinDomain, (中文(简体) ), isIp, (中文(简体) ), isIpInRange, (中文(简体) ), isCloudflareIp, (中文(简体) ), getClientIp, (中文(简体) ), CLOUDFLARE_IP_RANGES
numbersclamp, (中文(简体) ), preciseRound
objectspick, (中文(简体) ), omit, (中文(简体) ), isEmptyObject, (中文(简体) ), isKeyInObject
parsersserialize
randomgetRandomString, (中文(简体) ), getRandomDigits, (中文(简体) ), getRandomNumber, (中文(简体) ), getRandomElement
stringucFirst, (中文(简体) ), lcFirst, (中文(简体) ), truncate, (中文(简体) ), slugify, (中文(简体) ), getInitials, (中文(简体) ), joinAsSentence
timeformatDate, (中文(简体) ), formatTime, (中文(简体) ), formatDateTime, (中文(简体) ), formatDateRange, (中文(简体) ), getReadTime, (中文(简体) ), dayKey, (中文(简体) ), shiftDayKey, (中文(简体) ), dayIn, (中文(简体) ), fromUnix, (中文(简体) ), toUnix, (中文(简体) ), SECOND_MS, (中文(简体) ), MINUTE_MS, (中文(简体) ), HOUR_MS, (中文(简体) ), DAY_MS, (中文(简体) ), MINUTE_SECONDS, (中文(简体) ), HOUR_SECONDS, (中文(简体) ), DAY_SECONDS
uigetBalancedColumns, (中文(简体) ), isLightColor, (中文(简体) ), getCurrentPage, (中文(简体) ), getPagination, (中文(简体) ), getPageCount, (中文(简体) ), isPageOutOfRange, (中文(简体) ), getPageLink
类型WithOptional, (中文(简体) ), WithRequired,加上帮助签名中的类型,例如 Result (从) tryCatch, (中文(简体) ), Timestamp 财务报告和审定财务报表 ProcessBatchOptions

日期和时区

日期帮助者接受任何 Intl.DateTimeFormat 选项加a locale (违约) "en-US") (中文(简体) ). 没有 timeZone,它们格式化为运行它们的机器的时区。 在服务器发送的页面上,服务器(通常为UTC)和浏览器(访问者区域)然后可以生成不同的文本,这会导致回放水合错误. 通过固定 timeZone 在两地获得相同的输出:

import { formatDate, formatDateRange } from "@dirstack/utils"

formatDate("2026-10-05T23:30:00Z", { dateStyle: "long", timeZone: "UTC", locale: "en-GB" }) // "5 October 2026"
formatDate("2026-10-05", { day: "2-digit", month: "short", year: "numeric", timeZone: "UTC" }) // "Oct 05, 2026"
formatDateRange("2026-10-05", "2026-10-09", { timeZone: "UTC" }) // "Oct 5 – 9, 2026"

只约会字符串, 如 "2026-10-05" 解析为 UTC 午夜,所以格式化为 timeZone: "UTC" 在任何地方保持同一个日历日.

代理服务器背后的客户端 IP

getClientIp 从Traefik等反向代理设置的请求头读取访客的地址。 第一个 x-forwarded-for 条目来自客户端,可以被伪造,因此它返回最后一个条目,代理附件作为TCP对等点。 掉下来了 x-real-ip返回时 undefined 当没有标题持有有效的 IP 地址时 :

import { getClientIp } from "@dirstack/utils"

getClientIp(request.headers) ?? "127.0.0.1"

在代理Cloudflare记录后,对等点是一个Cloudflare服务器. 过 trust: "cloudflare" 返回 cf-connecting-ip 换句话说 只有在对等点出现时才读取页眉 CLOUDFLARE_IP_RANGES,因为一个直接发送到来源的请求可以将其设定为任何:

getClientIp(request.headers, { trust: "cloudflare" })

IP 助手在纯类型脚本中解析 IPv4 和 IPv6, 不 node:net,因此它们也会在浏览器和边缘运行时运行.

用时间预算重试

retry 投出时再次运行一个同步函数。 默认情况下,它使3次复试,它们之间没有等待. delay 财务报告和审定财务报表 factor 添加指数后退,以及 maxDelay 每个等 :

import { retry } from "@dirstack/utils"

// Waits 100ms, 200ms, 400ms, 800ms, then 1s between attempts, for at most 15 seconds.
const rows = await retry(() => client.query(sql), {
  retries: Infinity,
  delay: 100,
  maxDelay: 1000,
  timeout: 15_000,
  shouldRetry: isConnectionError,
})

timeout 计数从调用到 retry。 。 。 . deadline 取用划时代的时间戳代替,这样几个电话就可以共享一个预算. 两者设定后,适用前一种. retry 永远不要开始一个会结束预算的等待: 它会重排最后一个错误 . retries 仍然默认为 3 有预算设置,所以通过 retries: Infinity 任由预算自行结束循环.

const deadline = Date.now() + 15_000
const user = await retry(() => findUser(id), { retries: Infinity, delay: 100, deadline })
await retry(() => insertEvent(user), { retries: Infinity, delay: 100, deadline })

signal 需要一个 AbortSignal一旦它流产, retry 停下来,甚至在等待的中间, 拒绝与 signal.reason。预算或信号都无法中断已经运行的尝试。 将回调包入 withTimeout 当一次尝试可以绞死.

从v2 移动

v3留了一条路给每个助手打电话. 以下被移除的每个别名或调用表都有直接替换.

软件包格式

通用JS/UMD大楼dist/index.umd.cjs)不见了。 一揽子措施只采用无害环境管理,需要节点22.12或以后.

// Before: CommonJS on any Node.js version
const { slugify } = require("@dirstack/utils")
// After: an import, or require() on Node.js 22.12+
import { slugify } from "@dirstack/utils"

删除的别名

// Before                                    // After
splitArrayIntoChunks(items, 10)              chunk(items, 10)
keepNumberInRange(value, 0, 100)             clamp(value, 0, 100)
pickFromObject(user, ["id", "name"])         pick(user, ["id", "name"])

日期格式化器只选择选项对象

// Before                                                   // After
formatDate(date, "long", "en-GB")                           formatDate(date, { dateStyle: "long", locale: "en-GB" })
formatTime(date, "short", "pl")                             formatTime(date, { timeStyle: "short", locale: "pl" })
formatDateTime(date, "long", "short", "de")                 formatDateTime(date, { dateStyle: "long", timeStyle: "short", locale: "de" })
formatDateRange(start, end, "short", "es")                  formatDateRange(start, end, { dateStyle: "short", locale: "es" })

sortBy 选择选项对象

// Before                                  // After
sortBy(posts, post => post.date, "desc")   sortBy(posts, post => post.date, { order: "desc" })

删除的助手

没有项目使用这些, 所以 v3 放弃它们 。 每个都有一个简短的本地或图书馆替换.

删除改为使用
getElementPositionelement.getBoundingClientRect() 或者说 element.scrollIntoView()
setInputValue将五行复制到需要它们的项目中
subscribe, (中文(简体) ), unsubscribe, (中文(简体) ), publishdocument.addEventListener, (中文(简体) ), removeEventListener, (中文(简体) ), dispatchEvent(new CustomEvent(name, { detail }))
publishEscapedocument.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }))
isErrorWithMessage, (中文(简体) ), toErrorWithMessage, (中文(简体) ), ErrorWithMessagegetErrorMessage(error) 或者说 toError(error)
formatMimeTypemimeType.split("/")[1]?.toUpperCase()
formatToDecimalsformatNumber(value, { maximumFractionDigits: 2, useGrouping: false })
debounce, (中文(简体) ), throttleuseDebouncedCallback 财务报告和审定财务报表 useThrottledCallback 从 @mantine/hooks
getBaseUrlnew URL(url).origin
getQueryParamsObject.fromEntries(new URL(url).searchParams)
checkUrlAvailabilityfetch(url, { method: "HEAD", signal: AbortSignal.timeout(5000) })
normalizeUrl, (中文(简体) ), isLocalhostUrl仍在包内使用, 不再输出
parseNumericValueNumber.parseFloat(value) 带有 Number.isNaN 检查,或 z.coerce.number()
sortObjectObject.fromEntries(Object.entries(source).sort(([a], [b]) => a.localeCompare(b)))
sortObjectKeys需要它的项目的参照者
nullsToUndefined, (中文(简体) ), ReplaceNullWithUndefined绘制需要的字段
maybeParseJson, (中文(简体) ), maybeStringifyJson, (中文(简体) ), deserializeJSON.parse 财务报告和审定财务报表 JSON.stringify
getRandomColorMath.floor(Math.random() * 0x1000000).toString(16).padStart(6, "0")
getRandomPropertygetRandomElement(Object.values(source))
isCuidisCuid 从 @paralleldrive/cuid2
formatDateOrTimeformatDate, (中文(简体) ), formatTime 或者说 formatDateTime
getShortcutLabelA 类 <Kbd> 了解平台修改键的组件
DeepIdx, (中文(简体) ), DeepIndex, (中文(简体) ), ValidatePathPath 财务报告和审定财务报表 PathValue 从 react-hook-form,或 Get 财务报告和审定财务报表 Paths 从 type-fest
NestedPartial, (中文(简体) ), NestedRequiredPartialDeep 财务报告和审定财务报表 RequiredDeep 从 type-fest

processBatchWithErrorHandling 现在 processBatchSettled

喜欢 Promise.allSettled,每个结果现在都说明其项目是否成功并携带了项目,而不是混合值和 Error 在一个阵列中的对象。 选项,包括 onError,不变.

// Before
const results = await processBatchWithErrorHandling(items, processor, { batchSize: 10 })
const failed = results.filter(result => result instanceof Error)

// After
const results = await processBatchSettled(items, processor, { batchSize: 10 })
const failed = results.filter(result => result.status === "rejected") // { item, error }

toBase64 返回平面基数64,以及 toDataUrl 返回数据 URL

toBase64 用于返回数据 URL(data:image/png;base64,…)),所以大多数打电话者都剥去前缀. 它现在只返回 Base64 字符串,和 toDataUrl 返回完整的数据 URL。 双方接受任何 Blob 还在Node.js工作.

// Before                                                          // After
(await toBase64(file)).replace(/^data:[^;]+;base64,/, "")          await toBase64(file)
await toBase64(file) // when the data URL was wanted                await toDataUrl(file)

slugify 选择选项对象

// Before                         // After
slugify(title, true)              slugify(title, { decamelize: true })

getExcerpt 现在 truncate

每个呼叫者都通过了纯文本,所以 truncate 丢弃 HTML 条列( HTML 也删除任何文本) <) (中文(简体) ). 它在文字之间切分,从不分割一个emoji, 并保持结果在内 length 包括《京都议定书》 …。 。 。 .

// Before                              // After
getExcerpt(description, 125)           truncate(description, 125)

三个细节有所改变: … 改为 ...,它数到 length空输入返回 "" 改为 null。 。 。 . stripHtml 财务报告和审定财务报表 convertNewlines 被删除.

getPageParams 现在 getPagination

getPageParams 从 URL 中读取页面,然后返回未选中类型下的其他查询参数. getPagination 只有数学:先用路由器或nuqs来解析页面.

// Before                                    // After
getPageParams<Filters>(url, 20)              getPagination({ page, perPage: 20 }) // { skip, take }

joinAsSentence 选择选项对象

// Before                                       // After
joinAsSentence(names)                           joinAsSentence(names, { limit: 3, locale: "en-GB" })
joinAsSentence(names, 5)                        joinAsSentence(names, { limit: 5, locale: "en-GB" })
joinAsSentence(names, undefined, t("and"))      joinAsSentence(names, { locale })
joinAsSentence(names, undefined, "or")          joinAsSentence(names, { type: "disjunction", locale: "en-GB" })

joinAsSentence 现在格式为 Intl.ListFormat,它以三种方式改变其输出:

  • 它没有默认限制。 A 类 limit 将其余部分折叠成一个后缀项目( “ a, b, and 2 more ” ) 而不是丢弃, 并且永远不隐藏单个项目 .
  • 默认 en-US locane 添加了序列逗号:"a,b,和c". 过 locale: "en-GB" 用于“a、b和c”,如v2.
  • 地块设置相和相和相接相和相接相. 传出活动语言,而不是一个被翻译的"和".

发展

bun install        # install dependencies
bun test           # run the test suite on Bun
bun run test:node  # run the same suite on Node.js with Vitest
bun run typecheck
bun run lint
bun run build      # build with tsdown, then check the package with publint and attw

许可证

麻省理工学院 皮奥特·库尔平斯基

版本与发布记录

@dirstack/utils 共发布 20 个版本,首次发布于 2026/03/02;过去 12 个月发布了 20 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.3.0latest2026/11/102 天前
3.2.02026/10/2320 天前
3.1.02026/10/1626 天前
3.0.02026/10/081 个月前
2.3.02026/10/061 个月前
2.2.02026/10/061 个月前

关于 @dirstack/utils 的常见问题

@dirstack/utils 是什么?

一套轻型公用事业

如何安装 @dirstack/utils?

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

@dirstack/utils 支持 TypeScript 吗?

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

@dirstack/utils 支持 ESM 还是 CommonJS?

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

@dirstack/utils 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.12。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@dirstack/utils 可以商用吗?

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

@dirstack/utils 的下载量有多少?

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

@dirstack/utils 还在维护吗?

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