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

onchain-utility

v0.0.19

Published

This package contains misc utilities for onchain.

Readme

Base

_isMoment

判断是否是Moment对象,并且不依赖moment包

_isMoment({}) // falsesd
_isMoment(Moment()) // truea

makeDestructurable

优化返回值结构

const params = makeDestructurable({
  name: 'AIC',
  age: 16
}, ['AIC', 16]);
const { name, age } = params;
const [name, age] = params;

createEnumObject

创建一个对象枚举,主要用于解决真正的枚举导出类型和导出前枚举不相等的问题

createEnumObject({
  SET: 'SET',
  ADD: 'ADD',
  DELETE: 'DELETE',
  CLEAR: 'CLEAR',
})

toRawType

接收任意数据,生成对应类型的字符串

toRawType(() => {}) // 'Function'
toRawType(new Map) //'Map'
toRawType({}) // 'Object'
toRawType([]) // 'Array'
toRawType(0) // 'Number'

hasOwnProperty

判断对象是否存在某个属性

const data  = { name: 'aic' };
hasOwnProperty(data, 'name') // true
hasOwnProperty(data, 24) // false

isEmptyObject

是否是空对象

isEmptyObject({}) // true,
isEmptyObject({name: 'aic'}) // false

toTypeString

接收任意值,返回Object.prototype.toString格式的字符串,例如 [object Object]

toTypeString({}) // '[object Object]'
toTypeString([]) // '[object Array]'
toTypeString(() => {}) // '[object Function]'

isString

判断值是否为字符串

isString('aic') // true
isString(123) // false

isNumber

判断值是否为数字

isNumber(123) // true
isNumber('123') // false

isNaN

判断值是否为NaN

isNaN(NaN) // true
isNaN('aic') // false
isNaN(undefined) // false

isBoolean

判断值是否为布尔值

isBoolean(true) // true
isBoolean(0) // false

isUndefined

判断值是否为undefined

isUndefined(undefined) // true
isUndefined(null) // false

isNull

判断值是否为null

isNull(null) // true
isNull(undefined) // false

isNil

判断值是否为nullundefined

isNil(null) // true
isNil(undefined) // true
isNil(NaN) // false
isNil(0) // false

isObject

判断值是否为普通对象(Object.prototype.toString 结果为 'Object'

isObject({}) // true
isObject([]) // false
isObject(null) // false

isArray

判断值是否为数组

isArray([]) // true
isArray({}) // false

isFunction

判断值是否为函数

isFunction(() => {}) // true
isFunction({}) // false

isIntegerKey

判断字符串是否为整型 key

isIntegerKey('0') // true
isIntegerKey('abc') // false
isIntegerKey('-1') // false
isIntegerKey('NaN') // false

getHTMLTagString

接收一个对象

  • type: string;标签名称,例如:divaspan...,
  • attributes: Record<string, any>;html标签属性,例如:{style:'color:#fff;font-size:12px !important;'}
  • context: string;标签内容。

返回html字符串

getHTMLTagString({
  type:"span", 
  attributes: {
    style:'color:#fff;font-size:12px !important;'
  }, 
  context: 'text'
}) // <span style="color:#fff;font-size:12px !important;">text</span>

generateSecureUUID

生成UUID

generateSecureUUID() // 'a6ab72c0-31ab-49bb-a591-54f776c0b2d9'

Base64

getBase64Regular

生成base64匹配正则

const regular = getBase64Regular();
const png = `data:image/png;base64,iVBORwOKGgoAAA
ANSUhEUgAAALgA...whHJTEQgHn4pg1J8
1Av8HEfyAJO5uun8AAAAASUVORK5CYIl=`;
regular.test(png) // true

base64ToMimeType

接收base64字符串,返回

  • mimeType: string;MIME类型;例如:"image/png"
  • suffix: string;文件格式;例如:"png"
  • type: string;文件类型;例如:"image"
const png = `data:image/png;base64,iVBORwOKGgoAAA
ANSUhEUgAAALgA...whHJTEQgHn4pg1J8
1Av8HEfyAJO5uun8AAAAASUVORK5CYIl=`;
base64ToMimeType(png); // { "mimeType":"image/png", "suffix":"png", "type":"image" }

base64ToFile

接收对象

  • text: string;base64字符串
  • type: string | undefined;文件类型;例如:"image"
  • filename: string | undefined;自定义文件名称
  • suffix: string | undefined;文件格式;例如:"png"

返回File对象

const png = `data:image/png;base64,iVBORwOKGgoAAA
ANSUhEUgAAALgA...whHJTEQgHn4pg1J8
1Av8HEfyAJO5uun8AAAAASUVORK5CYIl=`;
base64ToFile({text: png}) // File {name: 'f37e4949-f912-45f8-abde-109aa8e60a28.undefined', lastModified: 1768899504506, lastModifiedDate: Tue Jan 20 2026 16:58:24 GMT+0800 (中国标准时间), webkitRelativePath: '', size: 9470, …};

toBase64UTF8

接收任意值返回base64字符串

const element = document.createElement('equation');
const equation = toBase64UTF8(this.__equation);
element.setAttribute('data-lexical-equation', equation);

fromBase64UTF8

接收base64字符串返回任意值

const element = document.createElement('equation');
const equation = toBase64UTF8(this.__equation);
element.setAttribute('data-lexical-equation', equation);

Language

t

用于国际化的文本格式化处理,将字符串模板中的 {key} 占位符替换为 obj 中对应的值

t('Hello {name}, you are {age} years old', { name: 'aic', age: 12 }) // 'Hello aic, you are 12 years old'
t('Hello world') // 'Hello world'

getI18nText

根据pathCode从语言映射表中查询对应文本

  • languageMap: Record<string, string | undefined>;语言映射表
  • pathCode: string | undefined;查找的键名
  • placeholder: string | undefined;当未找到时返回的占位符
const languageMap = { greeting: 'Hello' };
getI18nText(languageMap, 'greeting', '') // 'Hello'
getI18nText(languageMap, 'missing', '默认文本') // '默认文本'

baseTemplate

处理国际化模板的基础方法,组合getI18nTextt两个工具,先从语言包取出模板,再替换占位符

const LanguageMap = { greet: 'Hello {name}' };
baseTemplate(LanguageMap, 'greet', { variate: { name: 'aic' }, placeholder: 'Hi' }) // 'Hello aic'

initTranslateI18nFn

初始化全局国际化函数,挂载到window.translateI18n

initTranslateI18nFn((...params) => {
  return baseTemplate(LanguageMap, ...params)
})

translateI18n

全局国际化函数,优先使用window.translateI18n,未初始化则使用默认的baseTemplate

translateI18n('greet', { variate: { name: 'aic' }, placeholder: 'Hi' })

Traversal

dfs

深度优先遍历(同步),根据getNewStack返回子节点继续向下递归

  • data: T[];待遍历的数据
  • getNewStack: (node: T, parent?: T) => T[];返回当前节点的子节点数组
const tree = [
  { id: 1, children: [{ id: 2 }, { id: 3 }] },
  { id: 4, children: [{ id: 5 }] },
];
dfs(tree, (node) => node.children || [])
// [{id:1,...}, {id:2}, {id:3}, {id:4,...}, {id:5}]

bfs

广度优先遍历(同步)

  • data: T[];待遍历的数据
  • getNewStack: (node: T, parent?: T) => T[];返回当前节点的子节点数组
const tree = [
  { id: 1, children: [{ id: 2 }, { id: 3 }] },
  { id: 4, children: [{ id: 5 }] },
];
bfs(tree, (node) => node.children || [])
// [{id:1,...}, {id:4,...}, {id:2}, {id:3}, {id:5}]

asyncDfs

异步深度优先遍历,getNewStack可以返回Promise<T[]>T[],用于子节点需要异步加载的场景

const result = await asyncDfs(tree, async (node) => {
  const children = await fetchChildren(node.id);
  return children;
});

Tree

levelTransformTree

将扁平数据按层级字符串(如 1.2.3)转换为树结构

  • data: T[];扁平数据
  • splitSymbol: string;层级分隔符,默认为.
  • getLevel: (item: T) => string;获取每个节点的层级字符串
const list = [
  { id: '1', name: 'root' },
  { id: '1.1', name: 'child1' },
  { id: '1.2', name: 'child2' },
];
levelTransformTree({
  data: list,
  getLevel: (item) => item.id,
})
// [{ id: '1', name: 'root', children: [{id:'1.1',...},{id:'1.2',...}] }]

arrayAttributeFlat

将树形结构扁平化为数组

  • data: T[];树形数据
  • childrenKey: string;子节点字段名,默认为'children'
  • getChildren: (item: T) => T[];可选,自定义获取子节点的方法
  • fn: (item: T, index: number, parent?: T) => void;可选,遍历过程中触发的回调
const tree = [{ id: 1, children: [{ id: 2 }, { id: 3 }] }];
arrayAttributeFlat({ data: tree }) // [{id:1,...}, {id:2}, {id:3}]

getTreeRelation

获取指定节点在树中的关系信息(自身、父级、前后兄弟节点、层级索引、祖先链)

  • data: T[];树形数据
  • childrenKey: string;子节点字段名
  • lookup: (item: T, index: number) => boolean;用于查找目标节点的判断函数
const tree = [
  { id: 1, children: [{ id: 2 }, { id: 3 }] },
  { id: 4 },
];
getTreeRelation({
  data: tree,
  lookup: (item) => item.id === 2,
})
// { parent: {id:1,...}, self: {id:2}, beforeSibling: [], afterSibling: [{id:3}], index: 0, stack: [...] }

getTreeAncestors

获取指定节点的全部祖先节点(从根到直接父级)

  • data: T[];树形数据
  • key: string;目标节点的唯一标识
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'
const tree = [{ id: 1, children: [{ id: 2, children: [{ id: 3 }] }] }];
getTreeAncestors({ data: tree, key: '3' }) // [{id:1,...}, {id:2,...}]

getTreeDescendants

获取指定节点的全部子孙节点

  • data: T[];树形数据
  • key: string;目标节点的唯一标识
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'
const tree = [{ id: 1, children: [{ id: 2, children: [{ id: 3 }] }] }];
getTreeDescendants({ data: tree, key: '1' }) // [{id:2,...}, {id:3}]

getSiblings

获取指定节点同级兄弟节点的signKey数组

  • data: T[];树形数据
  • key: string;目标节点的唯一标识
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'
const tree = [{ id: 1, children: [{ id: 2 }, { id: 3 }, { id: 4 }] }];
getSiblings({ data: tree, key: '3' }) // ['2', '3', '4']

deleteTreeNodes

根据selectedKeys删除树中匹配的节点及其全部子孙,返回被删除的节点列表

  • data: T[];树形数据
  • selectedKeys: string[];要删除的节点唯一标识数组
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'
  • onDelete: (item: T) => void;可选,每个节点被删除前的回调
const tree = [{ id: 1, children: [{ id: 2, children: [{ id: 3 }] }] }];
const removed = deleteTreeNodes({ data: tree, selectedKeys: ['2'] });
// [{id:2,...}, {id:3}]

getSelectedNodeHierarchyStatus

判断多个选中节点是否跨层级,以及是否同时包含了某节点及其子孙

  • data: T[];树形数据
  • selectedKeys: string[];选中节点的唯一标识数组
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'

返回:

  • isDifferentDepths: boolean;选中节点是否跨层级
  • isIncludedParent: boolean;选中是否包含了祖先节点和其子孙节点
getSelectedNodeHierarchyStatus({ data: tree, selectedKeys: ['1', '2'] })
// { isDifferentDepths: true, isIncludedParent: true }

mergeAdjacent

按层级将相邻的选中项合并为连续的区间,用于批量操作时的范围识别

  • data: T[];树形数据
  • selectKeys: string[];选中节点的唯一标识数组
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'
const tree = [{ id: 1, children: [{ id: 2 }, { id: 3 }, { id: 4 }] }];
mergeAdjacent({ data: tree, selectKeys: ['2', '3'] })
// [['2', '3']]

generatedAcrossHierarchySort

生成跨层级排序的工具函数集

  • signKey: string;唯一标识字段名,默认为'id'

返回:

  • crossLayerSort: 对同一层级的选中项按索引排序
  • getInsertIdx: 计算跨层级插入时的目标索引
  • nodes: 内部维护的层级索引缓存
const { crossLayerSort, getInsertIdx } = generatedAcrossHierarchySort();

move

在树中移动节点(结合isMoveUp使用,支持跨层级合并移动)

  • data: T[];树形数据
  • selectKeys: string[];要移动的节点唯一标识数组
  • isMoveUp: boolean;是否向上移动
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'
  • onMove: (params: { selected: T[]; parent: T }) => boolean;可选,移动前的回调,返回false可阻止移动
  • onMoved: (moves: T[]) => void;可选,移动完成后的回调
  • onError: (params: { selected: T[]; isMoveUp?: boolean }) => boolean | void;可选,移动失败时的回调
move({ data: tree, selectKeys: ['3'], isMoveUp: true })

movUp

将选中的节点在树中向上移动一层(move 的便捷封装,等价于 isMoveUp: true

movUp({ data: tree, selectKeys: ['3'] })

moveDown

将选中的节点在树中向下移动一层(move 的便捷封装,等价于 isMoveUp: false

moveDown({ data: tree, selectKeys: ['3'] })

upgrade

将选中的节点在树中升级一级(移动到父级的父级之下),可选地携带后续跟随节点

  • data: T[];树形数据
  • selectKeys: string[];要升级的节点唯一标识数组
  • isFollow: boolean;是否携带后续节点
  • isSupportMultipleRoot: boolean;是否支持多根节点
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'
  • onUpgrade: (params: OnUpgradeParams<T>) => Promise<boolean> | boolean;可选,升级前的回调
  • onUpgraded: (upgrades: T[], params: ExpandedState<...>) => void;可选,升级完成后的回调
  • onStopFollow: (item: T) => boolean;可选,判断某个节点是否中断跟随
  • onFilterFollow: (item: T[]) => T[];可选,过滤跟随的节点
await upgrade({ data: tree, selectKeys: ['2'], isFollow: true })

downgrade

将选中的节点在树中降级一级(移动到前一个兄弟节点的子级之下)

  • data: T[];树形数据
  • selectKeys: string[];要降级的节点唯一标识数组
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'
  • onDowngrade: (params: OnDowngradedParams<T>) => Promise<boolean> | boolean;可选,降级前的回调
  • onDowngraded: (downgrades: T[], params: ExpandedState<...>) => void;可选,降级完成后的回调
await downgrade({ data: tree, selectKeys: ['3'] })

insert

在树中指定锚点节点附近插入新节点

  • data: T[];树形数据
  • anchor: string;锚点节点的唯一标识
  • value: T[];要插入的节点数组
  • signKey: string;唯一标识字段名,默认为'id'
  • childrenKey: string;子节点字段名,默认为'children'
  • isAddChildLevel: boolean;是否插入为锚点的子级,否则插入为同级(在锚点之后)
insert({ data: tree, anchor: '1', value: [{ id: '5' }] })

treeSearch

在树中按条件搜索匹配节点,匹配节点及其祖先保留,匹配节点的子树原样保留

  • data: T[];树形数据
  • searchValue: (item: T) => boolean;判断节点是否匹配的函数
  • childrenKey: string;子节点字段名,默认为'children'
const matched = treeSearch({
  data: tree,
  searchValue: (item) => item.name.includes('aic'),
});
// 返回新的树,仅包含匹配分支

Hooks

reactive

react响应式数据更新

useReactive

无限层次响应式代理, 包含object,array,set,map,WeakMap,WeakSet

const Dome = () => {
  const store = useReactive({
    name: 'aic',
    age: 12,
    set: new Set,
    map: new Map,
    list: [{hobby: 'play game'}, {hobby: ''}]
  })
  return <div onClick={() => {
    store.age++ // 更新渲染
    store.list[1].hobby = 'play basketball' // 更新渲染
  }}>
    <span>{store.name}</span>
    {store.list.map(({ hobby }) => <span>{hobby}</span>)}
  </div>
}

useShallowReactive

单层对象响应式代理

const Dome = () => {
  const store = useShallowReactive({
    name: 'aic',
    age: 12,
    set: new Set,
    map: new Map,
    list: [{hobby: 'play game'}, {hobby: ''}]
  })
  return <div onClick={() => {
    store.age++ // 更新渲染
    store.list[1].hobby = 'play basketball' // 不会触发渲染
  }}>
    <span>{store.name}</span>
    {store.list.map(({ hobby }) => <span>{hobby}</span>)}
  </div>
}

protection

在代理的过程中用来保护某些属性不受响应式代理

const Dome = () => {
  const store = useReactive({
    name: 'aic',
    age: 12,
    set: new Set,
    map: new Map,
    list: [protection({hobby: 'play game'}), {hobby: ''}]
  })
  return <div onClick={() => {
    store.list[0].hobby = '' // 不会触发渲染          
    store.list[1].hobby = 'play basketball' // 更新渲染
    store.set = protection(new Set); // 设置值的时候进行保护
    store.set.add('1') // 不会触发渲染
  }}>
    <span>{store.name}</span>
    {store.list.map(({ hobby }) => <span>{hobby}</span>)}
  </div>
}

toRaw

接收一个代理对象,返回这个对象的原始对象,解决某些组件无法使用Proxy后的对象

const Table = ({list, setHobby}: {list: any[], setHobby: (params: any) => void) => {
  return list.map((item) => <span onClick={(item) => { item.hobby = 'init' }}>{item.hobby}</span>)
}

const Dome = () => {
  const store = useReactive({
    name: 'aic',
    age: 12,
    set: new Set,
    map: new Map,
    list: [{hobby: 'play game'}, {hobby: ''}]
  });
  return <Table list={toRaw(store.list)} setHobby={() => {}} />
}

toProxy

接收一个原始对象,返回这个对象的代理对象,解决转为原始对象后,修改数据无法更新渲染

const Table = ({list, setHobby}: {list: any[], setHobby: (params: any) => void) => {
  return list.map((item) => <span onClick={() => setHobby(item)}>{item.hobby}</span>)
}

const Dome = () => {
  const store = useReactive({
    name: 'aic',
    age: 12,
    set: new Set,
    map: new Map,
    list: [{hobby: 'play game'}, {hobby: ''}]
  });
  return <Table list={toRaw(store.list)} setHobby={(item) => {
    const hobby = toProxy(item);
    hobby = 'init'
  }} />
}

useStore

简单的 react hook 状态管理器


const Dome = () => {
  const { store, setStore, resetStore, latestStore, setSilenceStore } = useStore(() => {
    return {
      name: 'aic',
      age: 12
    }
  });
  useEffect(() => {
    setStore({
      age: 24
    })
  }, []);
  // 使用
  return <div>{store.name}</div>
}