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

@zyzgroup/core-common

v0.2.13

Published

```

Downloads

284

Readme

API 文档(自动生成)

src/async.ts

thunkify( fn[任意函数]: variadicFn ) => (...args: any[]) => thunkFn


将多参数函数替换成一个只接受 Thunk回调函数 作为参数的单参数函数
@example
fn(args1, args2, ... , callback) = thunkify(fn)(args1, args2, ... )(callback)

src/buffer.ts

concateTypedArray()


TypedArray 数组没有现成的concat方法
concateTypedArray(Uint8Array, Uint8Array.of(1, 2), Uint8Array.of(3, 4)); // Uint8Array [1, 2, 3, 4]

src/fp.ts

throttle()


throttle 节流
ms 毫秒内只运行一次,若在 ms 毫秒内重复触发,只有一次生效

debounce()


debounce 防抖
ms 毫秒后再执行该事件,若在 ms 毫秒内被重复触发,则重新计时
debounce 的问题在于它“太有耐心了”
试想,如果用户的操作十分频繁,它每次都不等 debounce 设置的 delay 时间结束就进行下一次操作,
于是每次 debounce 都为该用户重新生成定时器,回调函数被延迟了不计其数次。
频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。

trampoline()


异步串联
const addA = (value, next) => {
  next(value + 'A', 'a');
};
const addB = (value, anotherValue, next) => {
  console.log(anotherValue); // => a
  next(value + 'B');
};
const consoleLog = (value, next) => {
  console.log(value);
};
pipeCallback(addA, addB, consoleLog)('1'); // 1AB
/
export const pipeCallback = (...fns: variadicFn[]): variadicFn => {
  if (fns.length === 0) {
    return (...args: any[]) => args;
  }
  if (fns.length === 1) {
    return fns[0];
  }
  return fns.reduceRight(
    (next, fn) =>
      (...args) =>
        fn(...args, next),
    noop
  );
};
/**
蹦床函数,循环调用返回函数的函数,将递归执行转为循环执行,适用于没有尾递归优化的环境
原递归函数
function sum(x, y) {
  if (y > 0) {
    return sum( x + 1, y - 1);
  } else {
   return x;
  }
}
改造后的递归函数,返回函数
function sum(x, y) {
 if (y > 0) {
   return sum.bind(null, x + 1, y - 1);  // 这里是返回一个函数,而不是函数里面调用函数,从而避免递归执行
 } else {
   return x;
 }
}
trampoline(sum(1,1000000));

tco()


@description 尾递归优化
const sum = tco(function(x, y) {
	  if (y > 0) {
	    return sum(x + 1, y - 1)
	  }
	  else {
	    return x
	  }
	});
	sum(1, 100000)	// 100001

reverseArgs()


函数参数反转

cache()


缓存函数结果 cache(fn) 会使 fn 变为 异步

tryEach()


Given an array of async functions, return a function that takes the same argument as each function
in the array. Each function is executed in series and the first that doesn't crash, returns the
value

src/object.ts

getType()


typeof 可以判断基础数据类型(null除外),但是引用数据类型中,除了 function 之外,其他的也无法判断
Undefined   "undefined"
Boolean     "boolean"
Number      "number"
BigInt      "bigint"
String      "string"
Symbol      "symbol"
Null        "object"
Function    "function"
array       "object"
其他任何对象  "object"

isArrayLike()


isArrayLike([1, 2, 3]);
// => true
isArrayLike(document.body.children);
// => true
isArrayLike('abc');
// => true
isArrayLike(noop);
// => false

isObject()


广泛意义的 object : Object + Array + Function + 非null
Checks if `value` is the
[language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
isObject({});
// => true
isObject([1, 2, 3]);
// => true
isObject(noop);
// => true
isObject(null);
// => false

eq()


Performs a
[`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
comparison between two values to determine if they are equivalent.
let object = { 'a': 1 };
let other = { 'a': 1 };
eq(object, object);
// => true
eq(object, other);
// => false
eq('a', 'a');
// => true
eq('a', Object('a'));
// => false
eq(NaN, NaN);
// => true

bindObjectThis()


获取对象方法的时候,自动绑定this
 const logger = bindObjectThis(new Logger());
 const { printName } = logger;
 // printName 的 this 指向 logger

src/scheduler.ts

suspend()


Variable to hold a counting semaphore 信号
  - Incrementing adds a lock and puts the scheduler in a `suspended` state (if it's not
    already suspended)
  - Decrementing releases a lock. Zero locks puts the scheduler in a `released` state. This
    triggers flushing the queued tasks.
*/
let semaphore = 0;
/**
  Puts the scheduler in a `suspended` state. Scheduled tasks will be queued until the
  scheduler is released.

release()


Puts the scheduler in a `released` state.

exec()


Executes a task 'atomically'. Tasks scheduled during this execution will be queued
  and flushed after this task has finished (assuming the scheduler endup in a released
  state).

flush()


Releases the current lock. Executes all queued tasks if the scheduler is in the released state.

asap()


Executes or queues a task depending on the state of the scheduler (`suspended` or `released`)

immediately()


Puts the scheduler in a `suspended` state and executes a task immediately.

src/string.ts

getUnicodeLengthES5()


Used to match `RegExp`
[syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
/
      regexp = /[\\^$.*+?()[\]{}|]/g;
      break;
    case "regexp-flags":
      regexp = /\w*$/;
      break;
    case "native-method":
      regexp = RegExp(
        "^" +
          Function.prototype.toString
            .call(Object.prototype.hasOwnProperty)
            .replace(getDefinedRegExpPattern("regexp")!, "\\$&")
            .replace(
              /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,
              "$1.*?"
            ) +
          "$"
      );
      break;
    case "svg-segment":
      regexp = /([mlhvzaqtcs])([^mlhvzaqtcs]*)/gi;
      break;
    case "mobile-china":
      // https://ihateregex.io/expr/phone/
      // regexp = /^\+?[1-9]{1}[0-9]{10}$/;
      // 11位数字,以1开头,不能包含其他符号
      regexp = /^[1]{1}[0-9]{10}$/;
      // regexp = /^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/gi;
      break;
    case "email":
      regexp =
        /^[A-Za-z0-9_!#$%&'*+/=?`{|}~^.-]+\@[A-Za-z0-9._-]+\.[A-Za-z0-9]+$/gm;
      // regexp =
      //   /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/gm;
      break;
    case "ip":
      regexp =
        /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
      break;
    case "url":
      regexp = /^http[s]?:\/\/([\w-]+\.)+[\w-]+([\w-./?%&=]*)?$/;
      break;
    // case "relative-url":
    //   regexp = /^([a-z]+:)?[\\/]/i;
    //   break;
    case "element-url":
      regexp = /^url\(['"]?(.+?)['"]?\)$/i;
      break;
    case "date":
      regexp = /^\d{4}(\-|\/|\.)(\d{1,2})(\-|\/|\.)(\d{1,2})$/;
      break;
    case "time":
      regexp = /^(\d{1,2}):(\d{1,2}):(\d{1,2})$/;
      break;
    case "datetime":
      regexp =
        /^\d{4}(\-|\/|\.)(\d{1,2})(\-|\/|\.)(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/;
      break;
    case "base64-data":
      regexp =
        /^\s*data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)?)?(?:;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)\s*$/i;
      break;
    case "hex-color":
      regexp = /^#([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$/i;
      break;
    case "color":
      regexp =
        /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i;
      break;
    case "whitespace":
      regexp =
        /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]/g;
      break;
    case "commaSpaces":
      regexp =
        /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/;
      break;
    case "var-name":
      regexp = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
      break;
    case "bezier":
      regexp = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/;
      break;
    case "chinese-name":
      regexp = /^(?:[\u4e00-\u9fa5·]{2,16})$/;
      break;
    case "chinese":
      // regexp = /^[\u4E00-\u9FFF]+$/g;
      // regexp = /^[\u4E00-\u9FA5\uF900-\uFA2D]+$/g;
      regexp = new RegExp(
        String.raw`
          [\u{FA0E}\u{FA0F}\u{FA11}\u{FA13}\u{FA14}\u{FA1F}\u{FA21}\u{FA23}\u{FA24}\u{FA27}-\u{FA29}]
          |[\u{4E00}-\u{9FCC}]
          |[\u{3400}-\u{4DB5}]
          |[\u{20000}-\u{2A6D6}]
          |[\u{2A700}-\u{2B734}]
          |[\u{2B740}-\u{2B81D}]
          |[\u{2B820}-\u{2CEAF}]
          |[\u{2CEB0}-\u{2EBEF}]
        `.replace(/\s+/g, ""),
        "u"
      );
      break;
    case "mime-image":
      regexp = /^image\//;
      break;
    case "mime-video":
      regexp = /^video\//;
      break;
    case "mime-audio":
      regexp = /^audio\//;
      break;
    case "mime-document-word":
      regexp =
        /^application\/(?:vnd\.openxmlformats-officedocument\.wordprocessingml\.document|msword|vnd\.ms-word\.document\.macroenabled\.12|vnd\.openxmlformats-officedocument\.wordprocessingml\.template|vnd\.ms-word\.template\.macroenabled\.12)$/;
      break;
    case "mime-document-excel":
      regexp =
        /^application\/(?:vnd\.openxmlformats-officedocument\.spreadsheetml\.sheet|vnd\.ms-excel|vnd\.ms-excel\.sheet\.macroenabled\.12|vnd\.openxmlformats-officedocument\.spreadsheetml\.template|vnd\.ms-excel\.template\.macroenabled\.12)$/;
      break;
    case "mime-document-ppt":
      regexp =
        /^application\/(?:vnd\.ms-powerpoint|vnd\.openxmlformats-officedocument\.presentationml\.presentation|vnd\.ms-powerpoint\.presentation\.macroenabled\.12|vnd\.openxmlformats-officedocument\.presentationml\.template|vnd\.ms-powerpoint\.template\.macroenabled\.12)$/;
      break;
    case "mime-document-json":
      regexp = /^application\/json$/;
      break;
    case "mime-document-xml":
      regexp = /^(?:application|text)\/(?:xml|xhtml\+xml)$/;
      break;
  }
  return regexp;
};
export function getRegExpFlags(regExp: RegExp): string {
  if (regExp.flags) {
    return regExp.flags;
  }
  const flags = [];
  regExp.global && flags.push("g");
  regExp.ignoreCase && flags.push("i");
  regExp.multiline && flags.push("m");
  regExp.sticky && flags.push("y");
  regExp.unicode && flags.push("u");
  return flags.join("");
}
export function cloneRegExp(regExp: RegExp): RegExp {
  const result = new RegExp(regExp.source, getRegExpFlags(regExp));
  result.lastIndex = regExp.lastIndex;
  return result;
}
export type TRegExpResult = {
  // 匹配index
  matchIndex: number;
  // 完全匹配结果
  fullMatch: string | null;
  // 分组匹配结果
  groupMatchs: string[];
};
// const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
// "2015-01-02".replace(re, "$<day>/$<month>/$<year>"); // '02/01/2015'
// str.replace(/(john) (smith)/i, "$2, $1"); // Smith, John
// str.replace(/([a-z]+)\(?([^)a-z]+)\)?/gi, function (all, command, values) {
//   values = values.split(/\s*,\s*/);
// }
// function replacer(match, p1, p2, /* …, */ pN, offset, string, groups) {
//   return replacement;
// }
export const regTest = (s: string | RegExp, source: string): boolean => {
  if (typeof s === "string") {
    s = getDefinedRegExpPattern(s) || new RegExp(s);
  }
  return s.test(source);
};
// -1: not found
export const regSearchIndex = (s: string | RegExp, source: string): number => {
  if (typeof s === "string") {
    s = getDefinedRegExpPattern(s) || new RegExp(s);
  }
  return source.search(s);
};
export const regSplit = (s: string | RegExp, source: string): string[] => {
  if (typeof s === "string") {
    s = getDefinedRegExpPattern(s) || new RegExp(s);
  }
  return source.split(s);
};
/// - 如果 regexp 不带有 `g` 标记,则它以数组的形式返回第一个匹配项,其中包含分组和属性 `index`(匹配项的位置)、`input`(输入字符串,等于 str)
/// - 如果 regexp  带有 `g` 标记,则它将所有匹配项的数组作为字符串返回,而不包含分组和其他详细信息
/// - 如果没有匹配项,则无论是否带有标记 `g` ,都将返回 null
export const regMatch = (
  s: string | RegExp,
  source: string
): TRegExpResult[] | TRegExpResult | null => {
  if (typeof s === "string") {
    s = getDefinedRegExpPattern(s) || new RegExp(s);
  }
  const matchs = source.match(s);
  if (!matchs) {
    return null;
  }
  if (s.flags.toLowerCase().indexOf("g") >= 0) {
    return matchs.map(
      (m) =>
        ({
          matchIndex: -1,
          fullMatch: m,
          groupMatchs: []
        } as TRegExpResult)
    );
  }
  return {
    matchIndex: typeof matchs.index === "number" ? matchs.index : -1,
    fullMatch: matchs[0],
    groupMatchs: matchs.slice(1)
  } as TRegExpResult;
};
/// 返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器(iterator)
export const regMatchAll = (
  s: string | RegExp,
  source: string
): TRegExpResult[] => {
  if (typeof s === "string") {
    s = getDefinedRegExpPattern(s) || new RegExp(s);
  }
  const results: TRegExpResult[] = [];
  const matchsArray = source.matchAll(s);
  for (const matchs of matchsArray) {
    results.push({
      matchIndex: typeof matchs.index === "number" ? matchs.index : -1,
      fullMatch: matchs[0],
      groupMatchs: matchs.slice(1)
    } as TRegExpResult);
  }
  return results;
};
// - 返回字符串 str 中的 regexp 匹配项,它返回一个数组,未匹配到则返回 null
// - 如果没有 `g`,返回的第一个匹配与 `str.match(regexp)` 完全相同
// - 如果有标记 `g`,会返回第一个匹配项,并将紧随其后的位置保存在属性`regexp.lastIndex` 中。 下一次同样的调用会从位置 `regexp.lastIndex` 开始搜索,返回下一个匹配项,并将其后的位置保存在 `regexp.lastIndex` 中
export const regExec = (
  s: string | RegExp,
  source: string
): TRegExpResult[] | TRegExpResult | null => {
  if (typeof s === "string") {
    s = getDefinedRegExpPattern(s) || new RegExp(s);
  }
  if (s.flags.toLowerCase().indexOf("g") < 0) {
    return regMatch(s, source);
  }
  const results: TRegExpResult[] = [];
  let matchs;
  while ((matchs = s.exec(source))) {
    results.push({
      matchIndex: typeof matchs.index === "number" ? matchs.index : -1,
      fullMatch: matchs[0],
      groupMatchs: matchs.slice(1)
    } as TRegExpResult);
  }
  return results;
};
// 正确返回字符串长度的函数,返回码点长度(emoji 友好), 支持 UTF-16,4字节32位unicode
export function stringLength(text: string) {
  const result = text.match(/[\s\S]/gu);
  return result ? result.length : 0;
}
// 正确返回字符串长度的函数,返回码点长度(emoji 友好), 支持 UTF-16,4字节32位unicode
export function stringLength2(str: string) {
  return [...str].length;
}
// 正确返回字符串长度的函数,返回码点长度(emoji 友好), 支持 UTF-16,4字节32位unicode
export function stringLength3(str: string) {
  // Array.from 会按码点拆分
  return Array.from(str).length;
}
/**
获取字符串长度,支持4字节32位unicode
UTF-8 标准规定,0xD800到0xDFFF之间的码点,不能单独使用,必须配对使用。
比如,\uD834\uDF06是两个码点,但是必须放在一起配对使用,代表字符𝌆

isChineseTaxNo()


税号: 15/17/18/20位数字与大写字母组合

stringReplace()


stringReplace("rgb({r},{g},{b})", { r: "255", g: "255", b: "255" })

raw()


raw`foo${1 + 2}bar` // "foo3bar"
raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"

toPascal()


toKebab("-AaaBbbCcc") = aaa-bbb-ccc
toKebab("Content-Type") = content-type
/
export const toKebab = (value: string, glue = "-"): string => {
  let v = value
    .replace(new RegExp(`${glue}?([A-Z])`, "g"), glue + "$1")
    .toLowerCase();
  v = v.replace(new RegExp(`^${glue}`), "");
  return v;
};
/**
toPascal("aaa_bbb-ccc") = AaaBbbCcc
toPascal("aaabbbccc") = Aaabbbccc

toCamel()


toCamel("aaa_bbb-ccc") = aaaBbbCcc

stringByteSize()


计算字符串所占的内存字节数,默认使用UTF-8的编码方式计算,也可制定为UTF-16
UTF-8 是一种可变长度的 Unicode 编码格式,使用一至四个字节为每个字符编码
000000 - 00007F(128个代码)      0zzzzzzz(00-7F)                             一个字节
000080 - 0007FF(1920个代码)     110yyyyy(C0-DF) 10zzzzzz(80-BF)             两个字节
000800 - 00D7FF
00E000 - 00FFFF(61440个代码)    1110xxxx(E0-EF) 10yyyyyy 10zzzzzz           三个字节
010000 - 10FFFF(1048576个代码)  11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz  四个字节
注: Unicode在范围 D800-DFFF 中不存在任何字符
http://zh.wikipedia.org/wiki/UTF-8
UTF-16 大部分使用两个字节编码,编码超出 65535 的使用四个字节
000000 - 00FFFF  两个字节
010000 - 10FFFF  四个字节
http://zh.wikipedia.org/wiki/UTF-16