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

@kerin/zentao-api

v1.3.5

Published

Browser and Node.js SDK for ZenTao API

Readme

@kerin/zentao-api

npm version license node

Browser & Node.js SDK for ZenTao (禅道) API v2.

@kerin/zentao-api 是一个面向禅道 API v2 的轻量 JavaScript/TypeScript SDK,可用于 Node.js 18+、浏览器打包工具以及 CDN/script 标签场景。本包位于 zentao monorepo 的 packages/zentao-api。


安装 / Install

npm install @kerin/zentao-api

Bun:

bun add @kerin/zentao-api

快速开始 / Quick Start

创建客户端

import { ZentaoClient } from '@kerin/zentao-api';

const client = new ZentaoClient({
  baseUrl: 'https://zentao.example.com',
  token: 'your-token',
});

const products = await client.get('/products');

baseUrl 是禅道站点根地址。SDK 会在内部追加 /api.php/v2。

账号密码登录

如果还没有 token,可以使用账号密码登录:

const client = new ZentaoClient('https://zentao.example.com');
const token = await client.login('admin', 'password');

全局客户端与模块请求

import { ZentaoClient, request, setGlobalOptions } from '@kerin/zentao-api';

ZentaoClient.init({
  baseUrl: 'https://zentao.example.com',
  token: 'your-token',
});

setGlobalOptions({ recPerPage: '50' });

const result = await request('product/list', {});

请求名支持三种写法:

await request('product');      // 默认调用 product/list
await request('product/list'); // 显式调用模块动作
await request('product/1');    // 详情快捷写法,等价于 product/get + id=1

单次调用的选项会覆盖全局选项:

const result = await request('bug/list', { product: 1 }, { limit: '10' });

从本地 Profile 恢复

SDK 支持将登录信息持久化到本地(Node.js: ~/.config/zentao/zentao.json,浏览器: localStorage),后续可直接恢复客户端:

const client = await ZentaoClient.fromProfile();
// 或指定 profile key
const client = await ZentaoClient.fromProfile('admin@https://zentao.example.com');

API 概览

ZentaoClient

| 方法 | 说明 | |------|------| | client.get<T>(path) | GET 请求 | | client.post<T>(path, body) | POST 请求 | | client.put<T>(path, body) | PUT 请求 | | client.delete<T>(path) | DELETE 请求 | | client.login(account, password) | 账号密码登录,返回 token | | client.request(path, options?) | 通用请求(底层方法) | | ZentaoClient.init(options) | 创建全局单例客户端 | | ZentaoClient.create(options) | 工厂方法创建客户端 | | ZentaoClient.fromProfile(key?) | 从持久化 profile 恢复客户端 |

模块请求

| 函数 | 说明 | |------|------| | request(name, params?, options?) | 按 "module"、"module/action" 或 "module/<objectID>" 调用已注册模块 | | defineModules(modules, options?) | 注册或扩展模块定义 | | defineModuleActions(module, actions) | 为已有模块追加或替换动作 | | extendModuleAction(module, action, patch) | 深度合并补丁,或用回调改写已有动作 | | getModule(name) | 获取模块定义 | | getModuleAction(module, action) | 获取指定动作定义 | | getModuleNames() | 获取所有已注册模块名 | | setGlobalOptions(options) | 设置全局默认选项 | | getGlobalOptions() | 获取当前全局选项 |

错误处理

SDK 所有传输层错误均通过 ZentaoError 抛出,包含稳定的错误码:

import { ZentaoError } from '@kerin/zentao-api';

try {
  await client.get('/products');
} catch (error) {
  if (error instanceof ZentaoError) {
    console.error(error.code);    // e.g. 'E_HTTP_ERROR', 'E_TIMEOUT', 'E_NETWORK_ERROR'
    console.error(error.message);
  }
}

注意:服务端返回 { status: "fail" } 时 SDK 默认不会抛出异常,按原始响应内容返回。需要把业务失败转为异常时,可在单次请求或全局选项中启用 throwOnFail。HTTP/网络/超时等传输层错误始终会抛出 ZentaoError。

扩展模块

生成的模块定义来自 scripts/update-registry.ts(输入 data/zentao-openapi.json)。你可以在调用 request() 前扩展模块,或新增、替换动作。

新增模块

import { defineModules } from '@kerin/zentao-api';

defineModules({
  name: 'custom',
  actions: [
    {
      name: 'list',
      type: 'list',
      method: 'GET',
      path: '/custom',
      resultType: 'list',
      resultGetter: 'items',
    },
  ],
});

为已有模块追加动作

import { defineModuleActions } from '@kerin/zentao-api';

defineModuleActions('bug', {
  name: 'archive',
  type: 'action',
  method: 'PUT',
  path: '/bugs/{bugID}/archive',
  pathParams: { bugID: 'Bug ID' },
  resultType: 'text',
});

微调已有动作

只想改动作的个别字段(而不是整体替换)时,用 extendModuleAction。传入补丁对象会与原动作深度合并:普通对象递归合并,数组及其他值整体替换,undefined 的键会被忽略。

import { extendModuleAction } from '@kerin/zentao-api';

// 改写 task/list 的 URL
extendModuleAction('task', 'list', {
  path: '/executions/{executionID}/tasks',
  pathParams: { executionID: '执行ID' },
});

需要基于现有定义做条件改写时,可传入回调。回调收到当前动作的深克隆,返回值作为完整动作定义直接取代原动作(不再合并):

extendModuleAction('execution', 'create', (action) => {
  const required = action.requestBody!.schema?.required;
  if (Array.isArray(required) && !required.includes('products')) {
    required.push('products');
  }
  return action;
});

合并与替换

同名模块默认合并定义:同名动作会替换,未知动作会追加。如需整体替换模块,传入 { replace: true }:

defineModules(myModule, { replace: true });

如果扩展定义拆分在多个文件中,请在应用启动入口中显式导入这些文件,确保它们在调用 request() 前完成注册。

TypeScript 支持

SDK 提供完整的 TypeScript 类型定义,所有公共类型均可直接导入:

import type {
  ZentaoClientOptions,
  ModuleDefinition,
  ModuleAction,
  ResponseData,
  RequestOptions,
} from '@kerin/zentao-api';

浏览器

浏览器打包工具可以正常导入这个包:

import { ZentaoClient } from '@kerin/zentao-api';
// 或显式 browser 子路径(与主入口等价,不注入全局变量)
import { ZentaoClient } from '@kerin/zentao-api/browser';

如果使用 script 标签,请使用浏览器 IIFE 构建包,并从 window.ZentaoAPI 读取 API:

<script src="https://cdn.jsdelivr.net/npm/@kerin/zentao-api@latest/dist/browser/zentao-api.global.js"></script>
<script>
  console.log(window.ZentaoAPI.VERSION, window.ZentaoAPI.BUILD);
  const client = new window.ZentaoAPI.ZentaoClient('https://zentao.example.com');
</script>

也可通过子路径 @kerin/zentao-api/browser/global 引用同一 IIFE 产物。

CORS:浏览器直接请求要求禅道服务器允许 CORS。浏览器代码也会把 token 暴露给前端;如果这不可接受,请使用后端代理。

TLS:insecure TLS 选项仅适用于 Node.js,在浏览器运行时会抛出错误。

测试

本 monorepo 开发依赖管理仅使用 Bun(在仓根 bun install)。请勿使用 npm / pnpm / yarn 安装 workspace 依赖,以免生成其它 lockfile。

在本包目录下:

bun test              # 单元测试
bun run test:coverage # 含覆盖率的单元测试
bun run check         # 完整 CI:测试 + 类型检查 + 注册表 + 构建 + 冒烟测试

真实环境测试

真实环境测试需要连接到运行中的禅道实例,不包含在默认 bun test 中:

bun run test:real
bun run test:real -- --keep-test-data   # 保留临时数据以便手动检查

测试会优先读取 .env.local,如果不存在则读取 env.local。支持的环境变量:

| 变量 | 说明 | 默认值 | |------|------|--------| | ZENTAO_URL | 禅道站点地址 | (必填) | | ZENTAO_ACCOUNT | 登录账号 | (必填) | | ZENTAO_PASSWORD | 登录密码 | (必填) | | ZENTAO_TOKEN | 直接提供 Token(替代账号密码) | — | | ZENTAO_REVIEWER | 需求评审人(未设置时复用 ZENTAO_ACCOUNT) | — | | ZENTAO_KEEP_TEST_DATA | 保留临时测试数据 | false | | ZENTAO_TIMEOUT | 请求超时(ms) | 30000 | | ZENTAO_INSECURE | 跳过 TLS 证书验证 | false |

项目结构

packages/zentao-api/
├── src/
│   ├── client/            # ZentaoClient 核心实现
│   ├── modules/           # 模块注册表
│   │   ├── generated.ts   # OpenAPI 自动生成,勿手动编辑
│   │   ├── override.ts    # 内置手写补丁
│   │   ├── define.ts      # defineModules / defineModuleActions / extendModuleAction
│   │   ├── query.ts       # getModule / getModuleNames 等只读 API
│   │   ├── registry-store.ts
│   │   ├── registry.ts    # barrel
│   │   └── resolve.ts     # 路径模板与参数解析
│   ├── request/           # 高阶 request()
│   ├── profiles/          # 本地 profile 持久化
│   ├── misc/              # 错误、全局选项、环境检测
│   ├── types/             # TypeScript 类型
│   ├── utils/             # 通用工具
│   ├── browser.ts         # browser 子路径入口(不注入全局)
│   ├── browser-global.ts  # IIFE 入口 → window.ZentaoAPI
│   └── index.ts           # 公共 API 入口
├── scripts/               # 构建、注册表生成、文档、冒烟
├── tests/
├── data/                  # OpenAPI 规范
├── docs/                  # VitePress(guide / reference / zentao-api)
└── dist/                  # 构建产物

文档站点

bun run docs:dev      # 生成本地文档并预览
bun run docs:generate # 刷新 typedoc reference + 模块 API 页
bun run docs:build    # 构建静态站到 docs/.vitepress/dist
  • docs/guide/ — 手写指南
  • docs/reference/ — typedoc 自动生成,勿手改
  • docs/zentao-api/ — 从模块注册表自动生成,勿手改

贡献

欢迎贡献代码!请确保提交前通过完整检查:

bun run check

在 monorepo 根目录也可用 bun run build / bun run test 做跨包验证。CLI 包 @kerin/zentao-cli 依赖本 SDK 的 dist,改源码后需重建本包。

许可证 / License

MIT