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

@be-link/http

v1.2.2

Published

共比邻 HTTP 请求库,提供简洁的 HTTP 请求封装,支持时间同步和 Token 加密

Downloads

306

Readme

@be-link/http

共比邻 HTTP 请求库,提供简洁的 HTTP 请求封装,支持时间同步和 Token 加密。

特性

  • 单例模式,全局统一配置
  • 服务器时间同步(解决客户端时间不准问题)
  • Token AES 加密(防重放攻击)
  • 请求/响应拦截器
  • TypeScript 类型支持
  • 所有参数由应用方传递,灵活配置
  • 自定义 Header 名称(Token、UserId)
  • 并发请求时自动合并时间同步(防止重复请求)
  • 使用 performance.now() 防止客户端系统时间篡改

安装

pnpm add @be-link/http axios crypto-js

依赖说明

| 依赖 | 是否必须 | 说明 | | ----------- | -------- | ----------------- | | axios | ✅ 必须 | HTTP 客户端 | | crypto-js | ✅ 必须 | Token 加密(AES) |

快速开始

基本用法

import { beLinkHttp } from '@be-link/http';

// 1. 初始化(应用启动时调用一次)
beLinkHttp.init({
  baseURL: 'https://api.example.com',
  timeout: 30000,
});

// 2. 发起请求
const users = await beLinkHttp.get('/api/users');
const result = await beLinkHttp.post('/api/users', { name: '张三' });

完整配置

import { beLinkHttp } from '@be-link/http';

beLinkHttp.init({
  // 基础配置
  baseURL: 'https://api.example.com',
  timeout: 30000,
  headers: {
    'X-Custom-Header': 'value',
  },

  // 时间同步配置
  timeSync: {
    enabled: true,
    syncUrl: 'https://api.example.com/api/time',
    syncGapTime: 50000, // 同步间隔阈值(毫秒)
    storagePrefix: 'myapp', // 存储 key 前缀
  },

  // 加密配置
  encryption: {
    enabled: true,
    key: 'your-aes-key-16ch', // 16 位 AES 密钥
    iv: 'your-aes-iv-16ch', // 16 位 AES IV
  },

  // Token 获取函数
  getToken: () => localStorage.getItem('token'),

  // 用户 ID 获取函数
  getUserId: () => localStorage.getItem('userId'),

  // 自定义 Header 名称(可选)
  tokenHeaderName: 'X-BeLink-Token', // 默认值
  userIdHeaderName: 'X-BeLink-UserId', // 默认值

  // 自定义请求拦截器
  onRequest: (config) => {
    console.log('请求发出:', config.url);
    return config;
  },

  // 自定义响应拦截器
  onResponse: (response) => {
    console.log('响应收到:', response);
    return response;
  },

  // 自定义错误处理
  onError: (error) => {
    if (error.response?.status === 401) {
      window.location.href = '/login';
    }
    throw error;
  },
});

API 文档

beLinkHttp

单例请求客户端,提供以下方法:

init(options)

初始化请求客户端,必须在使用其他方法之前调用。

beLinkHttp.init({
  baseURL: 'https://api.example.com',
  // ... 其他配置
});

HTTP 方法

// GET 请求
beLinkHttp.get<T>(url: string, config?: AxiosRequestConfig): Promise<T>

// POST 请求
beLinkHttp.post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>

// PUT 请求
beLinkHttp.put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>

// PATCH 请求
beLinkHttp.patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>

// DELETE 请求
beLinkHttp.delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>

// 通用请求
beLinkHttp.request<T>(config: AxiosRequestConfig): Promise<T>

其他方法

// 获取时间同步服务实例
beLinkHttp.getTimeSyncService(): TimeSyncService | null

// 获取加密服务实例
beLinkHttp.getEncryptionService(): EncryptionService | null

// 获取 Axios 实例
beLinkHttp.getAxiosInstance(): AxiosInstance | null

// 重置客户端(清除配置,需重新 init)
beLinkHttp.reset(): void

配置选项 RequestOptions

| 参数 | 类型 | 必填 | 默认值 | 说明 | | ---------------- | ----------------------------------- | :--: | --------------- | ------------------ | | baseURL | string | ✅ | - | API 基础地址 | | timeout | number | ❌ | 30000 | 请求超时时间(ms) | | headers | Record<string, string> | ❌ | {} | 默认请求头 | | timeSync | TimeSyncConfig | ❌ | - | 时间同步配置 | | encryption | EncryptionConfig | ❌ | - | 加密配置 | | getToken | () => string \| null \| undefined | ❌ | - | Token 获取函数 | | getUserId | () => string \| null \| undefined | ❌ | - | 用户 ID 获取函数 | | tokenHeaderName | string | ❌ | X-BeLink-Token | Token Header 名称 | | userIdHeaderName | string | ❌ | X-BeLink-UserId | UserId Header 名称 | | onRequest | (config) => config | ❌ | - | 自定义请求拦截器 | | onResponse | (response) => response | ❌ | - | 自定义响应拦截器 | | onError | (error) => any | ❌ | - | 自定义错误处理 |

TimeSyncConfig

| 参数 | 类型 | 必填 | 默认值 | 说明 | | ------------- | --------- | :--: | ------ | --------------------- | | enabled | boolean | ❌ | true | 是否启用时间同步 | | syncUrl | string | ✅ | - | 时间同步接口 URL | | syncGapTime | number | ❌ | 50000 | 同步间隔阈值(毫秒) | | storagePrefix | string | ❌ | - | localStorage key 前缀 |

EncryptionConfig

| 参数 | 类型 | 必填 | 默认值 | 说明 | | ------- | --------- | :--: | ------ | -------------- | | enabled | boolean | ❌ | true | 是否启用加密 | | key | string | ✅ | - | 16 位 AES 密钥 | | iv | string | ✅ | - | 16 位 AES IV |

使用示例

基础请求

import { beLinkHttp } from '@be-link/http';

// 初始化
beLinkHttp.init({
  baseURL: 'https://api.example.com',
});

// GET 请求
const users = await beLinkHttp.get('/api/users');

// 带参数的 GET 请求
const user = await beLinkHttp.get('/api/users/1', {
  params: { include: 'profile' },
});

// POST 请求
const newUser = await beLinkHttp.post('/api/users', {
  name: '张三',
  age: 25,
});

// PUT 请求
const updated = await beLinkHttp.put('/api/users/1', {
  name: '李四',
});

// DELETE 请求
await beLinkHttp.delete('/api/users/1');

带类型的请求

interface User {
  id: number;
  name: string;
  age: number;
}

interface ApiResult<T> {
  success: boolean;
  data: T;
  message?: string;
}

// 指定响应类型
const result = await beLinkHttp.get<ApiResult<User[]>>('/api/users');
console.log(result.data); // User[]

const user = await beLinkHttp.post<ApiResult<User>>('/api/users', {
  name: '张三',
  age: 25,
});
console.log(user.data); // User

错误处理

beLinkHttp.init({
  baseURL: 'https://api.example.com',
  onError: (error) => {
    // 处理 401 未授权
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.href = '/login';
      return;
    }

    // 处理业务错误
    if (error.data?.message) {
      alert(error.data.message);
    }

    throw error;
  },
});

多实例场景

如果需要创建多个独立的请求实例,可以使用 BeLinkHttp 类:

import { BeLinkHttp } from '@be-link/http';

// 创建实例 A
const serviceA = new BeLinkHttp();
serviceA.init({
  baseURL: 'https://api-a.example.com',
});

// 创建实例 B
const serviceB = new BeLinkHttp();
serviceB.init({
  baseURL: 'https://api-b.example.com',
});

// 分别使用
const dataA = await serviceA.get('/api/data');
const dataB = await serviceB.get('/api/data');

工作流程

Token 加密流程

请求发起时:
┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  getToken() │────>│ EncryptionService│────>│   请求头        │
│  获取 Token │     │ AES(token|+|time)│     │ Authorization   │
└─────────────┘     └──────────────────┘     └─────────────────┘
                            │
                            ▼
                    TimeSyncService
                    (服务器时间校正)

时间同步流程

1. 请求发起前检查本地时间是否需要同步
2. 如果超过同步间隔阈值(默认 50 秒),发起同步请求
3. 将服务器时间和客户端时间存储到 localStorage
4. 后续加密时使用校正后的时间戳

并发控制

当多个请求同时发起时,时间同步使用 Promise 锁机制,确保只发起一次同步请求:

请求1 ──┬── ensureSync() ──> 发起同步 ──> 等待完成 ──> 继续请求
请求2 ──┤                        ↓
请求3 ──┼── ensureSync() ──> 检测到锁 ──> 等待同一个 Promise ──> 继续请求
请求4 ──┤
请求5 ──┘

防止系统时间篡改

使用 performance.now() 替代 Date.now() 计算时间流逝:

  • performance.now() 返回页面加载后的高精度时间,不受系统时间修改影响
  • 即使用户修改系统时间,Token 加密的时间戳仍然准确

类型导出

// 单例实例
import { beLinkHttp } from '@be-link/http';

// 类(用于多实例)
import { BeLinkHttp } from '@be-link/http';

// 服务类
import { TimeSyncService, EncryptionService } from '@be-link/http';

// 配置类型
import type { RequestOptions, TimeSyncConfig, EncryptionConfig, ApiResponse } from '@be-link/http';

完整示例

import { beLinkHttp } from '@be-link/http';

// 应用入口初始化
beLinkHttp.init({
  baseURL: import.meta.env.VITE_API_URL,
  timeout: 30000,

  // 时间同步
  timeSync: {
    syncUrl: `${import.meta.env.VITE_API_URL}/api/time`,
  },

  // Token 加密
  encryption: {
    key: import.meta.env.VITE_ENCRYPTION_KEY,
    iv: import.meta.env.VITE_ENCRYPTION_IV,
  },

  // Token 获取
  getToken: () => localStorage.getItem('token'),

  // 用户 ID
  getUserId: () => {
    const user = JSON.parse(localStorage.getItem('user') || '{}');
    return user.id;
  },

  // 错误处理
  onError: (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    throw error;
  },
});

// API 调用
async function getUserInfo(userId: string) {
  return beLinkHttp.get(`/api/users/${userId}`);
}

async function createUser(data: { name: string; age: number }) {
  return beLinkHttp.post('/api/users', data);
}

async function updateUser(userId: string, data: { name?: string; age?: number }) {
  return beLinkHttp.put(`/api/users/${userId}`, data);
}

async function deleteUser(userId: string) {
  return beLinkHttp.delete(`/api/users/${userId}`);
}

License

ISC