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

@bigworm/bigworm-framework

v8.7.5

Published

BigWorm Bootstrap 前端框架

Downloads

230

Readme

@bigworm/bigworm-framework

Version Node

前端 HTTP 通信框架。提供 EnvelopeAxios(Envelope 协议)和 OpenAxios(常规 JSON)双客户端,内置数字签名(HMAC/RSA/SM2)、JWT 认证、requestId/timestamp 防重放校验、文件上传/下载、HTTP 调试日志等功能。

安装

npm install @bigworm/bigworm-framework

对等依赖(按需安装):

npm install crypto-js@^4.2.0   # HMAC 签名必需
npm install jsencrypt@^3.5.4   # RSA 签名可选
npm install sm-crypto@^0.3.13  # SM2 国密可选

模块结构

src/
├── core/
│   ├── EnvelopeAxios.ts      # Envelope 协议 HTTP 客户端
│   └── OpenAxios.ts          # 常规 JSON HTTP 客户端
├── config/
│   ├── SecurityConfig.ts     # 统一安全配置接口及默认值
│   ├── EnvelopeAxiosConfig.ts
│   ├── OpenAxiosConfig.ts
│   ├── RequestConfig.ts
│   ├── UploadConfig.ts
│   ├── DownloadConfig.ts
│   └── StreamConfig.ts
├── crypto/
│   ├── CryptoAdapterFactory.ts
│   ├── HmacAdapter.ts
│   ├── RSAAdapter.ts
│   └── SM2Adapter.ts
├── tools/
│   ├── JwtUtil.ts            # JWT Token 管理
│   ├── HmacUtil.ts           # HMAC 用户专属密钥管理
│   ├── StorageUtil.ts        # 凭证存储公共层(存储位置/键名/只读可配)
│   ├── SignatureUtil.ts
│   ├── SecurityUtil.ts
│   └── DebugUtil.ts          # HTTP 调试日志
└── error/
    └── CheckedError.ts

核心:EnvelopeAxios

用于与 bigworm 后端 Envelope 协议接口通信。

初始化

import { EnvelopeAxios } from '@bigworm/bigworm-framework'

const http = EnvelopeAxios.getInstance({
  baseURL: 'http://localhost:8080',
  timeout: 30000,
  onError: (error) => ElMessage.error(error.message), // 全局错误回调
  security: {
    // 所有选项均有默认值,下方为默认值说明
    envelopeEnabled: true,       // 启用 Envelope 报文验证
    validateRequestId: true,     // 校验响应 requestId 与请求一致
    validateTimestamp: true,     // 校验响应时间戳防重放
    maxTimeDiff: 300000,         // 最大时间差(毫秒,默认 5 分钟)
    signatureEnabled: true,      // 启用数字签名
    requestAlgorithm: 'hmac',    // 请求签名算法:'hmac' | 'rsa' | 'sm2'
    responseAlgorithm: 'rsa',    // 响应验签算法:'rsa' | 'sm2' | 'hmac'
    signRequest: true,           // 对请求签名
    verifyResponse: true,        // 验证响应签名
    useUserHmacKey: true,        // 启用用户专属 HMAC 密钥
    publicKey: undefined,        // 后端公钥(未配置则用内置默认公钥)
    hmacKey: undefined,          // 自定义默认 HMAC 密钥
    authType: 'jwt',             // 认证类型:'jwt' | 'session'
    jwtHeaderName: 'Authorization',
    jwtTokenPrefix: 'Bearer ',

    // 凭证存储(对齐上层应用的存储位置与键名,详见「凭证存储」一节)
    jwtStorageType: 'localStorage',
    jwtStorageKey: 'jwt_token',
    jwtStorageReadOnly: false,   // true 时只读,保护宿主会话不被误删
    jwtTokenProvider: undefined, // 自定义读取器,Token 不在 storage 时使用
    hmacStorageType: 'localStorage',
    hmacStorageKey: 'hmac_user_key',
    hmacStorageReadOnly: false
  }
})

配置优先级:getInstance(config) > globalThis.CONFIG.security > 默认值

请求方法

// POST
const response = await http.post('/api/user/login', requestEnvelope)

// GET(params 可以是 Record 或 Envelope)
const response = await http.get('/api/user/info', { userId: '123' })

// 文件上传
const response = await http.upload('/api/file/upload', formData, {
  onUploadProgress: (e) => console.log(`${Math.round(e.loaded * 100 / e.total!)}%`)
})

// 文件下载
const result = await http.download('/api/file/download', requestEnvelope, {
  onDownloadProgress: (e) => console.log(`${Math.round(e.loaded * 100 / e.total!)}%`)
})
// result.blob: Blob, result.filename: string
const url = URL.createObjectURL(result.blob)

拦截器

// 请求拦截(添加业务字段)
const reqId = http.addRequestInterceptor((envelope) => {
  envelope.header().entity().data().setStringItem('sessionId', getSessionId())
  return envelope
})

// 响应拦截(统一错误处理)
const resId = http.addResponseInterceptor((envelope) => {
  const success = envelope.header().entity().data().getStringItem('success')
  if (success === 'false') {
    const msg = envelope.header().entity().data().getStringItem('message')
    if (msg?.includes('未登录')) window.location.href = '/login'
  }
  return envelope
})

// 移除拦截器
http.removeRequestInterceptor(reqId)
http.removeResponseInterceptor(resId)

核心:OpenAxios

EnvelopeAxios API 完全一致,区别是不强制 Envelope 协议,接收和返回任意 JSON。

import { OpenAxios } from '@bigworm/bigworm-framework'

const http = OpenAxios.getInstance({
  baseURL: 'https://api.example.com',
  security: { signRequest: true, verifyResponse: true, requestAlgorithm: 'rsa' }
})

const data = await http.post('/api/login', { username: 'admin', password: '123' })
const info = await http.get('/api/user/info', { userId: '123' })

安全配置详解

签名算法

| 方向 | 配置项 | 可选值 | 说明 | |------|--------|--------|------| | 请求签名(前端→后端) | requestAlgorithm | hmac(默认)/ rsa / sm2 | HMAC 性能最优,推荐 | | 响应验签(后端→前端) | responseAlgorithm | rsa(默认)/ sm2 / hmac | 需与后端 response-algorithm 一致 |

签名内容放入 HTTP Header X-Signature;requestId 和 timestamp 同时放入 X-Request-IDX-Timestamp Header 以及 envelope.header 双通道传输(兼容防火墙过滤 Header 的场景)。

用户专属 HMAC(useUserHmacKey

登录后通过 HmacUtil.deriveKey(password, account) 派生用户专属密钥,再通过 HmacUtil.saveKey(key) 保存。框架在初始化时自动从 storage 恢复并注入签名器,实现每个用户使用独立的 HMAC 密钥。需与后端 server.security.signature.use-user-hmac-key 保持一致。

全局配置(globalThis.CONFIG

框架从 globalThis.CONFIG.security 读取配置,支持在运行时注入(Vite 环境变量、Webpack process.env、或运行时配置文件均可):

// 在 main.ts / index.ts 中提前设置
;(globalThis as any).CONFIG = {
  security: {
    publicKey: import.meta.env.VITE_PUBLIC_KEY,
    requestAlgorithm: 'hmac',
    responseAlgorithm: 'rsa'
  }
}

JwtUtil — JWT Token 管理

import { JwtUtil } from '@bigworm/bigworm-framework'

// 登录成功后保存 token
JwtUtil.saveToken('eyJhbGci...')

// 获取 token
const token = JwtUtil.getToken()  // 返回 string | null

// 解析 payload(仅客户端解码,不验证签名)
const payload = JwtUtil.parseToken(token)
console.log(payload?.sub, payload?.exp)

// 检查是否过期
const expired = JwtUtil.isTokenExpired()  // true | false | null

// 获取剩余有效时间(毫秒)
const remaining = JwtUtil.getTokenRemainingTime()

// 登出时清除
JwtUtil.clearToken()

// 切换存储类型 / 键名(更推荐用 security 配置项声明,见下节)
JwtUtil.setStorageType('sessionStorage')
JwtUtil.setStorageKey('session_id')

凭证存储

以 npm 包形式寄宿进宿主工程时,两边 JWT 的存储位置与键名往往不一致。配置三个参数即可让框架直读宿主凭证,无需任何镜像同步。

「寄宿」指本库被打包进上层应用(两侧后端各自独立进程),与后端 hosted-mode(已于 8.7.0 移除)无关。

| 配置项 | 环境变量 | 默认值 | |---|---|---| | jwtStorageType | VITE_SECURITY_JWT_STORAGE_TYPE | 'localStorage' | | jwtStorageKey | VITE_SECURITY_JWT_STORAGE_KEY | 'jwt_token' | | jwtStorageReadOnly | VITE_SECURITY_JWT_STORAGE_READ_ONLY | false | | jwtTokenProvider | —(函数,仅代码配置) | undefined | | hmacStorageType | VITE_SECURITY_HMAC_STORAGE_TYPE | 'localStorage' | | hmacStorageKey | VITE_SECURITY_HMAC_STORAGE_KEY | 'hmac_user_key' | | hmacStorageReadOnly | VITE_SECURITY_HMAC_STORAGE_READ_ONLY | false |

默认值与历史行为逐字节一致,独立部署的应用无需配置任何一项。

配置在哪一侧?

答案几乎总是「宿主的 .env」。 存储位置是宿主环境的既成事实,理应由宿主声明;客居方不传该项时,框架的三级合并会自动兜到全局配置。

# 宿主工程的 .env.development —— 宿主把 JWT 存在 sessionStorage['session_id']
VITE_SECURITY_JWT_STORAGE_TYPE=sessionStorage
VITE_SECURITY_JWT_STORAGE_KEY=session_id
VITE_SECURITY_JWT_STORAGE_READ_ONLY=true

宿主只需保证 applyMetaEnvToGlobalConfig(import.meta.env) 在任何客居方模块求值之前执行(通常放在入口文件的第一个 import)。客居方什么都不用改

// 客居方代码保持原样,不传 storage 相关配置
EnvelopeAxios.getInstance('my-app', {
  baseURL: 'http://localhost:5001',
  security: { signRequest: true, verifyResponse: true }
})

只有当同一宿主下不同客居方需要读不同凭证时,才在实例级覆盖。此时它是 INSTANCE 级(高于 GLOBAL),会盖过宿主的全局配置:

EnvelopeAxios.getInstance('special', {
  security: { jwtStorageKey: 'another_token' }
})

⚠️ 注意存储槽位是全局单例:两个实例配了不同的键名时,同级 first-wins(先创建者胜出)并打 warn,后者不会生效。详见设计文档的「设计边界」一节。

⚠️ 只读模式的责任转移

jwtStorageReadOnly: true 时框架放弃「凭证失效时自我清理」的能力——saveToken / clearToken 均被跳过。宿主必须接管未认证处理(注入 onError 并执行登出跳转),否则会出现「请求一直失败但不跳登录页」的现象。

判断标准是谁负责登录:应用自己登录 → false;宿主登录、本应用只是复用凭证 → true

还有一处容易被忽略:客居方的业务代码若把 security-00040(HMAC 密钥失效,属纯签名问题)也判为「强制重新登录」,会把宿主的登录态一起踢掉——框架层 readOnly 的克制就被业务层完全抵消了。该码由框架自动回退默认 HMAC 签名器处理,下次请求往往即恢复。

排错

| 现象 | 排查方向 | |---|---| | 请求头没有 Authorization | 宿主的 applyMetaEnvToGlobalConfig 是否早于客居方模块求值(入口第一个 import);改了 .env 后 dev server 是否重启 | | 配置改了但不生效 | Vite 依赖预构建缓存陈旧,rm -rf node_modules/.vite 后重启;升级 npm 包后尤其容易踩 | | 存储位置没切过去 | 控制台搜 [StorageUtil] 的 warn:同级配置冲突时保留先到者 | | 一直失败但不跳登录页 | readOnly 下框架不再自我清理,检查宿主是否注入了 onError / onUnauthorized |

详见 docs/凭证存储与寄宿模式设计.md,其中包含优先级仲裁表、失效链路时序与设计边界说明。


HmacUtil — 用户专属 HMAC 密钥管理

import { HmacUtil } from '@bigworm/bigworm-framework'

// 登录成功后:派生 + 保存用户专属密钥(PBKDF2,与后端算法参数一致)
const key = HmacUtil.deriveKey(password, account)
HmacUtil.saveKey(key)

// 获取当前用户密钥
const key = HmacUtil.getKey()  // string | null

// 登出时清除
HmacUtil.clearKey()

// 切换存储类型(默认 localStorage)
HmacUtil.setStorageType('sessionStorage')

HTTP 调试日志

开发环境(DEBUG 日志级别)下自动输出请求/响应详情,生产环境零开销:

═══════════════════════════════════════════
📥 HTTP 请求信息
═══════════════════════════════════════════
📍 请求路径: POST /api/user/login
📋 HTTP 请求头:
   X-Request-ID: req-abc123...
   X-Timestamp: 1736604123456
   X-Signature: MIIBIjAN...
📦 请求体:
{
  "header": { "default": { "requestid": "req-abc123...", "timestamp": "..." } },
  "body":   { "default": { "username": "admin" } }
}
═══════════════════════════════════════════

构建

npm run build        # ESM + CJS + UMD
npm run build:esm    # ES Module
npm run build:cjs    # CommonJS
npm run build:umd    # UMD(浏览器直接使用)
npm run build:all    # 构建并压缩(发布前使用)

输出格式:

  • dist/esm/ — ES Module(含 .d.ts 类型声明)
  • dist/cjs/ — CommonJS
  • dist/umd/ — UMD

测试

npm test              # 运行所有测试
npm run test:watch    # 监听模式
npm run test:coverage # 覆盖率报告