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

@zhaogyna/utils

v1.0.1

Published

Utility functions for Monitor SDK - no browser API dependencies

Readme

@zhaogyna/utils

Monitor SDK 的工具函数库,不依赖浏览器 API,可运行于任意 JS 环境。

安装

npm install @zhaogyna/utils

本包为内部依赖层,终端用户通常无需单独安装。框架包已自动包含。

导出 API

数据序列化

normalize(data, options?)

深度递归序列化,处理循环引用、深度限制、特殊类型。

import { normalize } from '@zhaogyna/utils'

const data = { a: { b: { c: { d: 'deep' } } } }
normalize(data, { depth: 3 })        // 超过深度的对象显示为 [Object]
normalize({ date: new Date() })       // Date → ISO 字符串
normalize({ big: BigInt(123) })       // BigInt → "123"
normalize({ map: new Map([['k','v']]) }) // Map → { k: 'v' }
normalize({ el: document.body })       // HTMLElement → { tagName, id, className }

| NormalizeOptions | 类型 | 默认值 | 说明 | |-----|------|--------|------| | depth | number | 3 | 递归深度 | | maxStringLength | number | 250 | 字符串截断长度 | | maxArrayLength | number | 50 | 数组截断长度 | | maxSize | number | 1000 | 最大规范化条目数 |

处理能力:循环引用([Circular])、BigInt、Symbol、Date、RegExp、Map、Set、Error、ArrayBuffer/TypedArray、HTMLElement(仅 tagName/id/className)、Proxy([Proxy])。


堆栈解析

parseStackFrames(stack)

解析 V8 和 SpiderMonkey 格式的 Error.stack 字符串,返回 StackFrame[](外层帧在前)。

import { parseStackFrames } from '@zhaogyna/utils'

const frames = parseStackFrames(new Error('test').stack)
// [{ filename, function, lineno, colno, inApp }, ...]
  • 自动检测 inApp:文件名排除 node_modulesvendorcdn 等第三方路径
  • 从末尾截断(保留最外层帧),最大帧数可通过 maxStackFrames 控制

extractExceptionFromError(error)

从 Error 对象提取 type 和 message。

import { extractExceptionFromError } from '@zhaogyna/utils'

const { type, value } = extractExceptionFromError(new TypeError('invalid'))
// { type: 'TypeError', value: 'invalid' }

ID 生成

import { uuid4, generateTraceId, generateSpanId } from '@zhaogyna/utils'

uuid4()             // 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
generateTraceId()   // 32 位十六进制字符串(W3C traceId)
generateSpanId()    // 16 位十六进制字符串(W3C spanId)

DSN 解析

import { parseDsn, getEnvelopeEndpoint, getEnvelopeEndpointWithUrlAuth } from '@zhaogyna/utils'

const dsn = parseDsn('https://[email protected]/1')
// { protocol: 'https', host: 'monitor.example.com', projectId: '1', publicKey: 'key' }

const url = getEnvelopeEndpoint(dsn)
// 'https://monitor.example.com/api/1/envelope'

Envelope 格式

import { createEnvelope, addItemToEnvelope, serializeEnvelope, createEnvelopeFromEvents } from '@zhaogyna/utils'

// 手动构建
const envelope = createEnvelope({ sdk: { name: 'monitor', version: '0.1.0' } })
const withItem = addItemToEnvelope(envelope, [{ type: 'event' }, eventBody])
const body = serializeEnvelope(withItem) // 换行分隔的 JSON 字符串

// 从事件列表构建
const envelope = createEnvelopeFromEvents([errorEvent, transactionEvent])

采样

import { sampleDecision, sessionSampleDecision, simpleHash } from '@zhaogyna/utils'

// 确定性采样:同一 eventId 始终返回相同结果
sampleDecision('event-abc-123', 0.3) // true 或 false

// 会话级采样:整个会话内采样决策一致
sessionSampleDecision('session-xyz', 0.5) // true 或 false

// DJB2 哈希算法
simpleHash('hello') // 数字哈希值

限流

RateLimiter

令牌桶限流器,按时间比例补充令牌。

import { RateLimiter } from '@zhaogyna/utils'

const limiter = new RateLimiter({ capacity: 60, refillRate: 1 }) // 60 次/分钟
limiter.consume(1) // true(允许)或 false(限流)

ServerRateLimitParser

解析服务端 x-monitor-rate-limits 响应头。

import { ServerRateLimitParser } from '@zhaogyna/utils'

const parser = new ServerRateLimitParser()
parser.parse('60:error, 120:transaction;track, 300:*')

日志

import { logger, setLogLevel, setDebugMode } from '@zhaogyna/utils'

setLogLevel('warn')       // debug / info / warn / error / none
setDebugMode(true)        // 等价于 setLogLevel('debug')

logger.debug('debug msg') // 仅当级别 <= debug 时输出
logger.info('info msg')
logger.warn('warning msg')
logger.error('error msg')

所有日志以 [Monitor] 前缀输出。


通用工具

import {
  safeAccess,
  deepClone,
  shallowMerge,
  truncate,
  timestampInSeconds,
  isNullOrEmpty,
  stripUrlParams,
  stripObjectKeys,
} from '@zhaogyna/utils'

safeAccess(obj, 'a.b.c')           // 安全深层属性访问
deepClone(obj)                      // 深拷贝(手动字段拷贝,保留函数数组)
shallowMerge(target, source)        // 浅合并
truncate('long text...', 10)        // 截断字符串
timestampInSeconds()                // Date.now() / 1000
isNullOrEmpty(value)                // null / undefined / '' / [] 检测
stripUrlParams(url, ['token'])      // 移除 URL 敏感参数
stripObjectKeys(obj, ['secret'])    // 移除对象敏感键

发送队列

SendQueue

FIFO 发送队列,支持重试和退避。

import { SendQueue } from '@zhaogyna/utils'

const queue = new SendQueue({
  maxRetries: 3,         // 最大重试次数(默认 3)
  retryBaseDelay: 1000,  // 退避基数 ms(默认 1000)
  maxQueueSize: 30,      // 队列容量(默认 30)
}, async (item) => {
  await fetch(item.url, { body: item.body })
})

queue.enqueue({ url: '...', body: '...' })
queue.drain()  // 立即发送全部(用于 sendBeacon 场景)
queue.updateOptions({ maxQueueSize: 50 })  // 动态更新配置

指数退避:1s → 2s → 4s,防止并发 flush。

License

MIT