@xstools/sdk
v2.0.0-beta.1
Published
Readme
@xstools/sdk
第三方 HTTP API 的薄封装。按 vendor 分子路径发布;根入口导出异常类型和 logger。
pnpm add @xstools/sdkESM only。
子路径
| 导入 | Client |
| --- | --- |
| @xstools/sdk/alicloud | AlicloudClientDysmsapi20170525、AlicloudClientOss20190517 |
| @xstools/sdk/dingtalk | DingTalkClient |
| @xstools/sdk/wechat-miniprogram | WechatMiniprogramClient |
| @xstools/sdk/wechat-oplatform | WechatOplatformClient |
| @xstools/sdk/wechat-pay | WechatPayClient |
| @xstools/sdk/wechat-pay-partner | WechatPayPartnerClient |
| @xstools/sdk/xcloud | XcloudClient |
| @xstools/sdk/yzh | YzhClient |
| @xstools/sdk | SdkException*、SdkLogger / SdkLogEvent / SdkClientOptions、sdkLoggerConsole / sdkLoggerNoop |
业务代码走 vendor 子路径,不要从根入口 import Client。各子路径导出 Client、业务类型和高级请求选项类型;不导出 Request 实现类。
HTTP 用 method;vendor 操作码用 action(ACS x-acs-action、微信 body action: 'set');SDK 调用身份用 operation。封装方法会带上与方法同名的 operation;直接 doRequest 时默认 'doRequest',也可自行传入。ACS 未传 operation 时回落到 RPC action。
用法
import { WechatMiniprogramClient } from '@xstools/sdk/wechat-miniprogram';
const mini = new WechatMiniprogramClient({
appid: process.env.WX_APPID!,
appsecret: process.env.WX_APPSECRET!,
});
const session = await mini.code2Session({ js_code: code });构造函数第二参是可选的 SdkClientOptions:fetch / timeout 交给 HTTP,logger 留在 Client。logger 只记成功 / 进度,失败走异常。默认静默;测试注入 fetch,需要输出时传入 sdkLoggerConsole 或自定义 logger:
import { sdkLoggerConsole } from '@xstools/sdk';
const mini = new WechatMiniprogramClient(config, {
fetch: mockFetch,
timeout: 10_000,
logger: sdkLoggerConsole,
});开放平台最低基础库版本
WechatOplatformClient.setSupportVersion 会先查询当前最低基础库版本。版本一致时不发送设置请求;传入 checkMode: true 时,版本不一致会抛出 SdkExceptionInternalError,不会修改远端配置。
import { WechatOplatformClient } from '@xstools/sdk/wechat-oplatform';
const client = new WechatOplatformClient(componentConfig);
await client.setSupportVersion({
authorizer_access_token,
version: '2.27.3',
options: { checkMode: true },
});开放平台隐私 key
setPrivacySetting 的 keys 是增量配置:SDK 会保留微信当前已有的隐私 key,并确保传入的 key 存在。传入较少的 key 不会删除远端已有项。
未封装的接口
每个 Client 都有泛型 doRequest,用来调用尚未封装的官方接口。各 vendor 子路径会导出对应的 *RequestOption 类型;鉴权、签名、错误码仍走该 vendor 自己的 Request:
const data = await mini.doRequest<{ url_link: string }>({
method: 'POST',
path: '/wxa/generate_urllink',
body: { path: 'pages/index/index' },
});例如 DingTalk 支持 GET / POST、query 和 JSON body:
import { DingTalkClient, type DingTalkRequestOption } from '@xstools/sdk/dingtalk';
const request: DingTalkRequestOption = {
method: 'GET',
path: '/v1.0/custom',
params: { cursor: 10 },
};
const data = await new DingTalkClient().doRequest<{ errcode: number }>(request);小程序 Client 的 stable token 缓存在单个 Client 实例内:并发调用共享同一次刷新,失败后下一次调用会重新获取,成功后按上游有效期提前 60 秒刷新。不同 Client 实例不共享 token。
微信支付通知
WechatPayClient.transactionNotice 与 WechatPayPartnerClient.transactionNotice 只解密并解析已通过 HTTP 接入层验证的通知 resource。它们不接收原始 body 或 Wechatpay-* 请求头,因此不负责回调签名验证、重放处理,也不校验通知中的订单号、商户号和金额是否属于本地订单。
接入层完成验证后再传入 resource;方法返回解密结果后,业务层根据本地订单完成归属、金额、状态和幂等处理。
错误处理
import { SdkException, SdkExceptionInternalError, SdkExceptionLogicRejected, SdkExceptionResponse } from '@xstools/sdk';
try {
await mini.code2Session({ js_code: code });
} catch (error) {
if (SdkExceptionResponse.is(error)) {
// 第三方 HTTP / 业务码失败。error.source / error.operation 标识调用,error.message 是上游错误体
} else if (SdkExceptionLogicRejected.is(error)) {
// 可把 error.message 返回给用户,例如内容安全未通过
} else if (SdkExceptionInternalError.is(error)) {
// 超时、坏 JSON、本地解码、缺字段等,不要把细节返回给用户
} else if (SdkException.is(error)) {
// 其它 SDK 错误
}
}SdkExceptionResponse 来自第三方 HTTP 非 2xx 或业务码失败;SdkExceptionLogicRejected 来自本包的业务判断;SdkExceptionInternalError 只用于调试(含超时、网络失败、2xx 却解不开 body)。跨包识别请用静态 is(),不要只靠 instanceof,也不要依赖旧的 __XSTOOLS_SDKS_* tag。
构造异常时使用 source(客户端标识,例如 ALI-OSS,或内部工具 UTILS)和 operation(SDK 调用身份)。message 沿用 Error。原构造参数 client / method 与实例字段 log 已移除。
_tag 为 __XSTOOLS_SDK__EXCEPTION / __XSTOOLS_SDK__EXCEPTION_RESPONSE / __XSTOOLS_SDK__EXCEPTION_LOGIC_REJECTED / __XSTOOLS_SDK__EXCEPTION_INTERNAL_ERROR。
OSS 上传与文件错误
AlicloudClientOss20190517.objectPut 接受 Buffer、ArrayBuffer、SharedArrayBuffer 或 Node 二进制 Readable。limitSize 单位为 MB(1024 × 1024 字节),默认 1;SDK 会完整读取并识别文件,再按其最终字节数拒绝超限文件,因此不会发送上传请求。字符串 chunk 不属于支持的上传输入;此限制不约束读取过程的内存占用,也不提供读取超时。
文件读取失败和大小超限抛出 SdkExceptionInternalError,source 为 ALI-OSS、operation 为 objectPut,原始错误保存在 cause 链。内部文件/XML 工具 source 为 UTILS。
objectCopy、objectDelete 与 objectMove 接受 object key 或完整的 HTTP(S) URL。SDK 只规范化一次 key,并使用同一 RFC 3986 编码结果构造请求 URL、复制源 header 和签名路径;原始 key 内的空格、?、#、%、! 会作为 key 的组成部分编码,路径分隔符 / 保持不变。
测试
pnpm --filter @xstools/sdk test该命令运行 bun test --preload ./test/_setup.ts,收集包内测试;全部为离线测试,进入 PR CI(turbo test)。不需要环境变量、密钥或第三方账号。
test/_setup.ts 会在测试启动时预加载,默认禁止真实 fetch;HTTP 场景通过构造函数注入 mock fetch,意外网络调用会使测试失败。直接使用 Bun 时,请在 SDK 包目录运行:
bun test --preload ./test/_setup.ts通知测试使用虚构数据及本地测试密钥;旧真实 API 场景已迁移到 src/,移除了专用脚本、环境配置及线上通知样例。离线测试验证请求参数、响应映射、签名和解密行为,不验证真实账号权限、外部网络或上游服务状态。ACS3 与 OSS V4 已有固定签名向量;仍不替代真实账号权限或服务端验签验证。
内部文档
维护约定见 docs/architecture.md;HTTP 响应管道见 docs/response.md。
