npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dearcury/com-utils

v0.0.5

Published

Commission system shared utilities (amount formatter, etc.)

Readme

com-utils

通用 TypeScript 工具函数库,发布包名:@dearcury/com-utils

安装

npm i @dearcury/com-utils
pnpm add @dearcury/com-utils
yarn add @dearcury/com-utils

引入方式(ESM)

import {
  formatAmount,
  maskString,
  isBlank,
  groupBy,
  deepClone,
  isEmail,
  isPhoneCN,
  isIDCardCN,
  isUrl,
  isNumberLike,
  isEmptyValue,
  parseQuery,
  getFileExt,
  downloadBlob,
  copyToClipboard,
  debounce,
  throttle,
  once
} from '@dearcury/com-utils'

API 文档

formatAmount(金额格式化)

签名

formatAmount(options: FormatAmountOptions): string

参数

| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | options.amount | number \| string \| null \| undefined | — | 待格式化金额 | | options.isDecimal | boolean | true | 是否保留小数 | | options.isThousand | boolean | true | 是否使用千分位分隔符 | | options.decimalN | number | 2 | 小数位数(isDecimal=true 时生效) | | options.stripTrailingZeros | boolean | false | 是否去除小数末尾 0 | | options.invalidText | string | '' | 无效值占位文本(null/undefined/''/NaN) | | options.locale | string | 'zh-CN' | 地区格式(影响千分符和小数点) |

返回值

  • string:格式化后的金额字符串

示例

formatAmount({ amount: 1234567.89 })                     // "1,234,567.89"
formatAmount({ amount: 1234567.89, isDecimal: false })  // "1,234,568"
formatAmount({ amount: 1.2, stripTrailingZeros: true }) // "1.2"
formatAmount({ amount: null, invalidText: '-' })        // "-"

maskString(字符串脱敏)

签名

maskString(value: string, options?: MaskStringOptions): string

参数

| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | value | string | — | 需要脱敏的字符串 | | options.startVisible | number | 3 | 前缀保留可见字符数 | | options.endVisible | number | 4 | 后缀保留可见字符数 | | options.maskChar | string | '*' | 脱敏字符(只取首字符) |

返回值

  • string:脱敏后的字符串

示例

maskString('13812345678') // "138****5678"
maskString('abcdefg', { startVisible: 1, endVisible: 1 }) // "a*****g"

isBlank(空白字符串判断)

签名

isBlank(value: unknown): boolean

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | value | unknown | 任意待判断值 |

返回值

  • boolean:是否为空白字符串(仅字符串类型且 trim() 后为空)

示例

isBlank('   ') // true
isBlank('abc') // false

groupBy(分组聚合)

签名

groupBy<T>(
  list: T[],
  selector: ((item: T) => string | number | symbol) | keyof T
): Record<string, T[]>

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | list | T[] | 待分组数组 | | selector | ((item: T) => string \| number \| symbol) \| keyof T | 分组规则(字段名或回调) |

返回值

  • Record<string, T[]>:按分组键聚合后的对象

示例

groupBy(
  [
    { type: 'a', value: 1 },
    { type: 'b', value: 2 },
    { type: 'a', value: 3 }
  ],
  'type'
)
// { a: [{...}, {...}], b: [{...}] }

deepClone(深拷贝)

签名

deepClone<T>(input: T): T

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | input | T | 待拷贝数据 |

返回值

  • T:深拷贝结果(支持对象、数组、Map、Set、Date、RegExp,并处理循环引用)

示例

const source = { a: 1, nested: { b: [1, 2, 3] } }
const cloned = deepClone(source)
cloned.nested.b.push(4)

isEmail(邮箱校验)

签名

isEmail(value: unknown): boolean

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | value | unknown | 待判断值 |

返回值

  • boolean:是否为合法邮箱格式

示例

isEmail('[email protected]') // true
isEmail('bad-email') // false

isPhoneCN(中国大陆手机号校验)

签名

isPhoneCN(value: unknown): boolean

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | value | unknown | 待判断值,支持字符串或数字 |

返回值

  • boolean:是否为合法中国大陆手机号(支持 +86 前缀)

示例

isPhoneCN('13812345678') // true
isPhoneCN('+8613812345678') // true

isIDCardCN(中国大陆身份证校验)

签名

isIDCardCN(value: unknown): boolean

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | value | unknown | 待判断值 |

返回值

  • boolean:是否为合法 18 位中国大陆身份证号

示例

isIDCardCN('11010519491231002X') // true
isIDCardCN('110105194912310021') // false

isUrl(URL 校验)

签名

isUrl(value: unknown): boolean

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | value | unknown | 待判断值 |

返回值

  • boolean:是否为合法 URL(仅允许 http/https

示例

isUrl('https://example.com/path') // true
isUrl('ftp://example.com') // false

isNumberLike(数字样式值判断)

签名

isNumberLike(value: unknown): boolean

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | value | unknown | 待判断值 |

返回值

  • boolean:是否可解析为有效有限数字

示例

isNumberLike('12.3') // true
isNumberLike('abc') // false

isEmptyValue(空值判断)

签名

isEmptyValue(value: unknown): boolean

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | value | unknown | 待判断值 |

返回值

  • boolean:是否为空值(null / undefined / '' / NaN

示例

isEmptyValue(undefined) // true
isEmptyValue(NaN) // true
isEmptyValue(0) // false

parseQuery(查询字符串解析)

签名

parseQuery(input: string): Record<string, string | string[]>

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | input | string | URL 或 query 字符串 |

返回值

  • Record<string, string | string[]>:解析结果;重复 key 聚合为数组

示例

parseQuery('?a=1&b=2&b=3')
// { a: '1', b: ['2', '3'] }

getFileExt(文件扩展名获取)

签名

getFileExt(pathOrName: string): string

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | pathOrName | string | 文件名、路径或 URL |

返回值

  • string:扩展名(小写,不含 .);无扩展名时返回空串

示例

getFileExt('https://x.com/path/demo.TS?x=1') // "ts"
getFileExt('README') // ""

downloadBlob(浏览器 Blob 下载)

签名

downloadBlob(blob: Blob, filename: string): boolean

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | blob | Blob | 待下载二进制对象 | | filename | string | 下载文件名 |

返回值

  • boolean:是否触发下载流程(非浏览器环境返回 false

示例

downloadBlob(new Blob(['hello'], { type: 'text/plain' }), 'demo.txt')

copyToClipboard(复制到剪贴板)

签名

copyToClipboard(text: string): Promise<boolean>

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | text | string | 待复制文本 |

返回值

  • Promise<boolean>:复制是否成功(优先 navigator.clipboard,失败自动降级)

示例

await copyToClipboard('hello world')

debounce(防抖)

签名

debounce<T extends (...args: any[]) => unknown>(
  fn: T,
  wait?: number
): DebouncedFunction<T>

参数

| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | fn | T | — | 待防抖函数 | | wait | number | 300 | 延迟执行时间(毫秒) |

返回值

  • DebouncedFunction<T>:防抖函数,包含 cancel() 方法

示例

const onSearch = debounce((keyword: string) => {
  console.log(keyword)
}, 300)

throttle(节流)

签名

throttle<T extends (...args: any[]) => unknown>(
  fn: T,
  wait?: number
): ThrottledFunction<T>

参数

| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | fn | T | — | 待节流函数 | | wait | number | 300 | 触发间隔(毫秒) |

返回值

  • ThrottledFunction<T>:节流函数,包含 cancel() 方法

示例

const onScroll = throttle(() => {
  console.log('scroll')
}, 200)

once(只执行一次)

签名

once<T extends (...args: any[]) => any>(
  fn: T
): (...args: Parameters<T>) => ReturnType<T>

参数

| 参数 | 类型 | 说明 | | --- | --- | --- | | fn | T | 只允许执行一次的函数 |

返回值

  • (...args: Parameters<T>) => ReturnType<T>:包装函数,首次执行后缓存返回值

示例

const init = once(() => {
  console.log('init')
  return 1
})

自动发版(CI/CD)

项目已配置自动发版流程:每次推送到 main 分支都会触发版本递增与 npm 发布。

  • 工作流文件:.github/workflows/release.yml
  • 必需密钥:NPM_TOKEN(GitHub Repository Secrets)

License

MIT