@sycsq/common
v0.0.6
Published
一个基于 Vite 构建的 TypeScript 工具库,提供 HTTP 请求封装和常用工具函数,支持完整的类型声明
Maintainers
Readme
@sycsq/common
中文 | English
@sycsq/common 是一个基于 TypeScript 和 Vite 构建的通用工具库,提供 Axios 请求封装、请求辅助函数、请求取消管理和常用类型判断工具。
特性
- TypeScript 类型声明
- 基于 Axios 的 HTTP 客户端封装
- 支持请求/响应转换钩子
- 支持重复请求取消、时间戳参数、日期参数格式化
- 支持 ESM 和 UMD 构建产物
- 包含 Vitest 单元测试和 GitHub Actions 自动化检查
环境要求
- Node.js
^20.19.0 || >=22.12.0 - pnpm
>= 7
安装
npm install @sycsq/commonyarn add @sycsq/commonpnpm add @sycsq/common快速开始
按需引入
推荐在业务项目中优先使用子路径引入,避免因为根入口同时暴露 HTTP 和工具函数而让打包器分析更多模块:
import { isEmpty, isString } from '@sycsq/common/utils';
import { http } from '@sycsq/common/http';
import { joinTimestamp } from '@sycsq/common/http/helper';包本身已配置 sideEffects: false,并提供独立的 ESM/CJS 子路径产物。axios 会作为外部运行时依赖处理,库构建不会把 Axios 打进产物。
HTTP 请求
import { http } from '@sycsq/common';
const users = await http.get('/api/users', {
params: { page: 1 }
});
const created = await http.post('/api/users', {
name: 'Alice'
});也可以使用配置对象形式:
import { request } from '@sycsq/common';
const result = await request({
url: '/api/users',
method: 'GET',
params: { page: 1 }
});工具函数
import { isEmpty, isString, isUrl } from '@sycsq/common';
isString('hello'); // true
isEmpty({}); // true
isUrl('https://example.com'); // true导出内容
根入口会继续导出全部公共 API,兼容已有用法:
import {
Axios,
AxiosCanceler,
axiosTransform,
http,
request,
isString,
isEmpty,
joinTimestamp,
setObjToUrlParams
} from '@sycsq/common';HTTP API
默认实例
import { http, request } from '@sycsq/common';http:默认 Axios 封装实例request:http.request.bind(http)的快捷导出
请求方法
每个方法都支持配置对象形式;常用方法也支持 URL 优先形式。
http.request<T>(config, options?)
http.get<T>(config, options?)
http.get<T>(url, config?, options?)
http.post<T>(config, options?)
http.post<T>(url, data?, config?, options?)
http.put<T>(config, options?)
http.put<T>(url, data?, config?, options?)
http.delete<T>(config, options?)
http.delete<T>(url, config?, options?)
http.patch<T>(config, options?)
http.patch<T>(url, data?, config?, options?)创建自定义实例
import { Axios, ContentTypeEnum, axiosTransform } from '@sycsq/common';
const api = new Axios({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: {
'Content-Type': ContentTypeEnum.JSON
},
transform: axiosTransform,
requestOptions: {
joinPrefix: false,
isReturnNativeResponse: false,
isTransformResponse: true,
joinParamsToUrl: false,
formatDate: true,
apiUrl: '',
urlPrefix: '',
joinTime: true,
ignoreCancelToken: true,
withToken: true
}
});RequestOptions
interface RequestOptions {
joinParamsToUrl?: boolean;
formatDate?: boolean;
isTransformResponse?: boolean;
isReturnNativeResponse?: boolean;
joinPrefix?: boolean;
apiUrl?: string;
urlPrefix?: string;
joinTime?: boolean;
ignoreCancelToken?: boolean;
withToken?: boolean;
errorHandler?: ErrorHandler;
errorFactory?: ErrorFactory;
}
interface RequestErrorContext<T = any> {
code?: string;
message: string;
response?: AxiosResponse<Result<T>>;
responseData?: Result<T>;
options: RequestOptions;
}
type ErrorHandler<T = any> = (
message: string,
context: RequestErrorContext<T>
) => void | Error;
type ErrorFactory<T = any> = (
message: string,
context: RequestErrorContext<T>
) => Error;响应转换规则
默认 axiosTransform 会根据业务响应结构处理数据:
isReturnNativeResponse: true:返回完整 Axios 响应isTransformResponse: false:返回response.data- 成功业务码
200或202:返回data,如果data为undefined则返回body - 失败业务码:先调用
errorHandler(message, context);如果返回Error,直接抛出;否则调用errorFactory(message, context)创建异常;如果都未配置,则抛出默认Error
自定义异常处理
业务错误和网络错误建议分开处理:
- 业务错误:后端正常响应,但
code不是成功码,使用errorHandler或errorFactory - 网络错误:请求失败、超时、Axios adapter 或拦截器异常,使用
requestCatchHook或responseInterceptorsCatch
全局异常工厂示例:
import { Axios, axiosTransform } from '@sycsq/common';
class ApiError extends Error {
constructor(
message: string,
public code?: string,
public detail?: unknown
) {
super(message);
this.name = 'ApiError';
}
}
const api = new Axios({
transform: {
...axiosTransform,
requestCatchHook(error) {
return Promise.reject(new ApiError(error.message, 'NETWORK_ERROR', error));
}
},
requestOptions: {
isTransformResponse: true,
errorHandler(message, context) {
console.warn('[business error]', context.code, message);
},
errorFactory(message, context) {
return new ApiError(message, context.code, context.responseData);
}
}
});单次请求覆盖示例:
await api.get('/users', undefined, {
errorHandler(message, context) {
return new ApiError(`Scoped handler: ${message}`, `SCOPED_${context.code}`);
}
});兼容旧用法:只传 errorHandler(message) 仍然有效;如果不返回 Error,库会继续抛出默认异常或 errorFactory 生成的异常。
辅助函数
joinTimestamp(join, restful)
formatRequestDate(params)
setObjToUrlParams(baseUrl, obj)请求取消
import { AxiosCanceler } from '@sycsq/common';
const canceler = new AxiosCanceler();
canceler.addPending(config);
canceler.removePending(config);
canceler.removeAllPending();
canceler.reset();工具函数 API
类型判断
is(val, type)isString(val)isNumber(val)isBoolean(val)isObject(val)isArray(val)isFunction(val)isDate(val)isPromise(val)isRegExp(val)isSymbol(val)
值判断
isDef(val)isUnDef(val)isNull(val)isNullAndUnDef(val)isNullOrUnDef(val)isEmpty(val)
环境与 DOM 判断
isServerisClientisWindow(val)isElement(val)isUrl(path)
开发
pnpm installpnpm testpnpm buildpnpm build:types示例项目
仓库包含一个 Vue 示例项目:
pnpm example:dev
pnpm example:build
pnpm example:preview示例项目位于 example/,通过 workspace:* 引用当前仓库源码。它覆盖:
- GET 请求与 POST 请求
- URL-first 和配置对象两种请求写法
- 成功业务响应解包
- 全局
errorFactory自定义异常 - 单次请求
errorHandler覆盖异常 requestCatchHook处理网络异常isString、isEmpty、isUrl等工具函数
示例使用 Axios adapter mock 数据,不依赖真实后端接口。
自动化
项目包含两个 GitHub Actions 工作流:
CI:在 pull request 和main分支 push 时执行pnpm install --frozen-lockfile、pnpm test和pnpm buildPublish Package:在main分支相关源码变更或手动触发时执行测试、构建、npm 发布和 GitHub Release 创建
发布到 npm 需要在仓库的 Settings -> Secrets and variables -> Actions 中配置:
NPM_TOKEN:npm automation token,需要具备发布@sycsq/common的权限
构建体积策略
- 多入口构建:
index、http、utils和细粒度 HTTP 子模块分别输出 - 依赖外部化:
axios不会被打入库产物,避免宿主项目重复打包 - 移除额外运行时依赖:form-urlencoded 序列化由轻量内置实现完成
- 禁用 public 目录复制,npm 包只包含运行所需文件
- 使用 ESM + CJS 双格式,并通过
exports暴露子路径入口
项目结构
.
├── .github/workflows/ # GitHub Actions workflows
├── example/ # Vue example
├── packages/ # Source code
│ ├── http/ # HTTP wrapper and helpers
│ ├── utils/ # Utility functions
│ └── index.ts # Package entry
├── tests/ # Unit tests
├── types/ # Generated declaration files
├── package.json
├── pnpm-lock.yaml
├── tsconfig.json
└── vite.config.ts许可证
MIT
English
@sycsq/common is a TypeScript utility library built with Vite. It provides an Axios-based HTTP wrapper, request helpers, request cancellation management, and common type guard utilities.
Features
- TypeScript declaration files
- Axios-based HTTP client wrapper
- Request and response transform hooks
- Duplicate request cancellation, timestamp parameters, and date parameter formatting
- ESM and UMD build outputs
- Vitest unit tests and GitHub Actions automation
Requirements
- Node.js
^20.19.0 || >=22.12.0 - pnpm
>= 7
Installation
npm install @sycsq/commonyarn add @sycsq/commonpnpm add @sycsq/commonQuick Start
On-demand Imports
Prefer subpath imports in application projects so bundlers only need to analyze the module family you actually use:
import { isEmpty, isString } from '@sycsq/common/utils';
import { http } from '@sycsq/common/http';
import { joinTimestamp } from '@sycsq/common/http/helper';The package is marked with sideEffects: false and ships independent ESM/CJS subpath outputs. axios is treated as an external runtime dependency, so it is not bundled into the library output.
HTTP Requests
import { http } from '@sycsq/common';
const users = await http.get('/api/users', {
params: { page: 1 }
});
const created = await http.post('/api/users', {
name: 'Alice'
});You can also use the config-object form:
import { request } from '@sycsq/common';
const result = await request({
url: '/api/users',
method: 'GET',
params: { page: 1 }
});Utility Functions
import { isEmpty, isString, isUrl } from '@sycsq/common';
isString('hello'); // true
isEmpty({}); // true
isUrl('https://example.com'); // trueExports
The root entry still re-exports every public API for backward compatibility:
import {
Axios,
AxiosCanceler,
axiosTransform,
http,
request,
isString,
isEmpty,
joinTimestamp,
setObjToUrlParams
} from '@sycsq/common';HTTP API
Default Instance
import { http, request } from '@sycsq/common';http: default wrapped Axios instancerequest: shortcut forhttp.request.bind(http)
Request Methods
Every method supports the config-object form. Common methods also support the URL-first form.
http.request<T>(config, options?)
http.get<T>(config, options?)
http.get<T>(url, config?, options?)
http.post<T>(config, options?)
http.post<T>(url, data?, config?, options?)
http.put<T>(config, options?)
http.put<T>(url, data?, config?, options?)
http.delete<T>(config, options?)
http.delete<T>(url, config?, options?)
http.patch<T>(config, options?)
http.patch<T>(url, data?, config?, options?)Custom Instance
import { Axios, ContentTypeEnum, axiosTransform } from '@sycsq/common';
const api = new Axios({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: {
'Content-Type': ContentTypeEnum.JSON
},
transform: axiosTransform,
requestOptions: {
joinPrefix: false,
isReturnNativeResponse: false,
isTransformResponse: true,
joinParamsToUrl: false,
formatDate: true,
apiUrl: '',
urlPrefix: '',
joinTime: true,
ignoreCancelToken: true,
withToken: true
}
});RequestOptions
interface RequestOptions {
joinParamsToUrl?: boolean;
formatDate?: boolean;
isTransformResponse?: boolean;
isReturnNativeResponse?: boolean;
joinPrefix?: boolean;
apiUrl?: string;
urlPrefix?: string;
joinTime?: boolean;
ignoreCancelToken?: boolean;
withToken?: boolean;
errorHandler?: ErrorHandler;
errorFactory?: ErrorFactory;
}
interface RequestErrorContext<T = any> {
code?: string;
message: string;
response?: AxiosResponse<Result<T>>;
responseData?: Result<T>;
options: RequestOptions;
}
type ErrorHandler<T = any> = (
message: string,
context: RequestErrorContext<T>
) => void | Error;
type ErrorFactory<T = any> = (
message: string,
context: RequestErrorContext<T>
) => Error;Response Transform Behavior
The default axiosTransform processes business responses as follows:
isReturnNativeResponse: true: returns the full Axios responseisTransformResponse: false: returnsresponse.data- Success codes
200or202: returnsdata; ifdataisundefined, returnsbody - Failed business codes: calls
errorHandler(message, context)first. If it returns anError, that error is thrown. OtherwiseerrorFactory(message, context)is used to create the error. If neither is configured, a defaultErroris thrown.
Custom Error Handling
Handle business errors and network errors separately:
- Business errors: the server responds successfully, but
codeis not a success code. UseerrorHandlerorerrorFactory. - Network errors: request failures, timeouts, Axios adapter errors, or interceptor errors. Use
requestCatchHookorresponseInterceptorsCatch.
Global error factory example:
import { Axios, axiosTransform } from '@sycsq/common';
class ApiError extends Error {
constructor(
message: string,
public code?: string,
public detail?: unknown
) {
super(message);
this.name = 'ApiError';
}
}
const api = new Axios({
transform: {
...axiosTransform,
requestCatchHook(error) {
return Promise.reject(new ApiError(error.message, 'NETWORK_ERROR', error));
}
},
requestOptions: {
isTransformResponse: true,
errorHandler(message, context) {
console.warn('[business error]', context.code, message);
},
errorFactory(message, context) {
return new ApiError(message, context.code, context.responseData);
}
}
});Per-request override example:
await api.get('/users', undefined, {
errorHandler(message, context) {
return new ApiError(`Scoped handler: ${message}`, `SCOPED_${context.code}`);
}
});The old style errorHandler(message) remains compatible. If the handler does not return an Error, the wrapper will continue by throwing the default error or the error from errorFactory.
Helpers
joinTimestamp(join, restful)
formatRequestDate(params)
setObjToUrlParams(baseUrl, obj)Request Cancellation
import { AxiosCanceler } from '@sycsq/common';
const canceler = new AxiosCanceler();
canceler.addPending(config);
canceler.removePending(config);
canceler.removeAllPending();
canceler.reset();Utility API
Type Guards
is(val, type)isString(val)isNumber(val)isBoolean(val)isObject(val)isArray(val)isFunction(val)isDate(val)isPromise(val)isRegExp(val)isSymbol(val)
Value Guards
isDef(val)isUnDef(val)isNull(val)isNullAndUnDef(val)isNullOrUnDef(val)isEmpty(val)
Environment and DOM Guards
isServerisClientisWindow(val)isElement(val)isUrl(path)
Development
pnpm installpnpm testpnpm buildpnpm build:typesExample Project
The repository includes a Vue example project:
pnpm example:dev
pnpm example:build
pnpm example:previewThe example project is located in example/ and references the local package through workspace:*. It covers:
- GET and POST requests
- URL-first and config-object request styles
- Successful business response unwrapping
- Global custom errors through
errorFactory - Per-request custom errors through
errorHandler - Network error handling through
requestCatchHook - Utility functions such as
isString,isEmpty, andisUrl
The example uses an Axios adapter to mock data, so it does not require a real backend API.
Automation
This project includes two GitHub Actions workflows:
CI: runspnpm install --frozen-lockfile,pnpm test, andpnpm buildon pull requests and pushes tomainPublish Package: runs tests, builds the package, publishes to npm, and creates a GitHub Release on relevantmainbranch changes or manual dispatch
Publishing to npm requires this repository secret under Settings -> Secrets and variables -> Actions:
NPM_TOKEN: an npm automation token with permission to publish@sycsq/common
Bundle Size Strategy
- Multi-entry build:
index,http,utils, and fine-grained HTTP submodules are emitted separately - Externalized dependency:
axiosis not bundled into the package output, preventing duplicate vendor code in host projects - Removed extra runtime dependency: form-urlencoded serialization uses a lightweight internal implementation
- Public asset copying is disabled, so the npm package only includes runtime files
- ESM and CJS outputs are both exposed through package
exports
Project Structure
.
├── .github/workflows/ # GitHub Actions workflows
├── example/ # Vue example
├── packages/ # Source code
│ ├── http/ # HTTP wrapper and helpers
│ ├── utils/ # Utility functions
│ └── index.ts # Package entry
├── tests/ # Unit tests
├── types/ # Generated declaration files
├── package.json
├── pnpm-lock.yaml
├── tsconfig.json
└── vite.config.tsLicense
MIT
