Skip to content

TypeScript SDK

@atkonbase/sdk —— ATKONBASE 官方 TypeScript SDK。Node ≥ 18 / 浏览器 / Worker 通用,使用全局 fetch,无运行时依赖。本页讲用法;端点契约见接口参考,认证形态的概念背景见集成拓扑

安装

bash
pnpm add @atkonbase/sdk

包必须用 pnpm 安装(preinstall 钩子强制)。

最小示例(APP_ONLY)

最常见的服务端到服务端调用——只用 Client 身份。withClientCredentials 一行构造,baseUrl 只传一次:

ts
import { AtkonbaseClient, V1DocumentApi } from "@atkonbase/sdk";

const client = AtkonbaseClient.withClientCredentials(
  "https://api.atkonbase.example.com",
  "${clientId}",
  "${clientSecret}",
);

// typed API(路径 / 参数序列化 / 鉴权头 / 401 重试全自动)
const v1 = client.api(V1DocumentApi);
const dto = await v1.someEndpoint({ /* ... */ });
console.log(dto.data);

高级构造

需要自定义 fetch / timeoutMs / defaultHeaders / 关闭 401 自动刷新时,改用全量构造(与 withClientCredentials 等价,只是把装配显式展开):

ts
import { AtkonbaseClient, createClientCredentialsTokenProvider } from "@atkonbase/sdk";

const clientToken = createClientCredentialsTokenProvider({
  baseUrl: "https://api.atkonbase.example.com",
  clientId: "${clientId}",
  clientSecret: "${clientSecret}",
});

const client = new AtkonbaseClient({
  baseUrl: "https://api.atkonbase.example.com",
  auth: { clientToken },     // V1Auth:只承载 clientToken
  timeoutMs: 60_000,         // 默认 30s
  autoRefreshOn401: true,    // 默认开
});

转义出口——spec 未覆盖或返回结构不规则时直接走原始路径:

ts
const data = await client.request<MyResponse>("/v1/some/resource", {
  method: "GET",
  query: { page: 1, size: 20 },   // 数组会展开为多值
});

转义出口默认仅注入 clientToken(APP_ONLY)。需代授权时优先用 actAsToken / actAsSource 视图(见下);low-level 场景也可在 headers 里手动传 X-Atk-User-Token / X-Atk-Act-As-*

三种认证形态

身份分两层:clientTokenclient 级长期固定身份,由 client 承载;代授权(代表谁)是 per-call 的,由调用层 actAsToken / actAsSource 表达,各返回一个只暴露 api()ScopedView

形态调用注入的请求头
APP_ONLYclient.api(Xxx)Authorization: Bearer <client>
强通道(代表已登录用户)client.actAsToken(userToken).api(Xxx)上 + X-Atk-User-Token: <user>
弱通道(代表外部身份)client.actAsSource(source, sourceId).api(Xxx)上 + X-Atk-Act-As-Source + X-Atk-Act-As-Source-Id
ts
// APP_ONLY:纯 client 身份
const dto = await client.api(V1DocumentApi).someEndpoint({ /* ... */ });

// 强通道:代表一个已签发用户会话 token 的用户
const userDto = await client
  .actAsToken(userSessionToken)
  .api(V1DocumentApi)
  .someEndpoint({ /* ... */ });

// 弱通道:Client 主张代表外部身份(需 server 侧开启 actAsAllowed)
const extDto = await client
  .actAsSource("wechat", "openid-xxx")
  .api(V1DocumentApi)
  .someEndpoint({ /* ... */ });

要点:

  • 代授权是 per-call 的actAsToken / actAsSource 每次按需调用产出 ScopedView,同一个 client 可对不同终端用户安全并发复用——所有视图共享同一 clientToken 刷新单点与 401 自动刷新中间件。
  • 通道互斥在编译期成立ScopedView 只暴露 api(),类型层面无法再叠加另一通道(强、弱不可同时);actAsSource 双参签名保证 sourcesourceId 成对。约束前移到编译期,不再依赖运行期校验。

TokenProvider 与凭据刷新

token 字段接受三种形态:

ts
type TokenProvider =
  | string                                  // 固定 token
  | (() => string | Promise<string>)        // 动态 resolver(你自己控制缓存)
  | RefreshableTokenProvider;               // 可被 SDK 主动 invalidate

createClientCredentialsTokenProviderclientId + clientSecretPOST /v1/auth/token,返回带缓存 + 提前续期 + 并发去重RefreshableTokenProviderwithClientCredentials 工厂内部即用它装配;需要调参时单独构造:

ts
const provider = createClientCredentialsTokenProvider({
  baseUrl: "https://api.atkonbase.example.com",
  clientId: "${clientId}",
  clientSecret: "${clientSecret}",
  refreshSkewMs: 60_000,   // 提前续期窗口,默认 60s
  timeoutMs: 10_000,       // token 端点超时,默认 10s
});
  • resolve():缓存命中(未到 skew 窗口)直接返回;否则发刷新请求。
  • 并发去重:多个 resolve 共享同一 in-flight 请求。
  • 跨多个 AtkonbaseClient 共享缓存时,复用同一个 provider 实例——不要重复调 createClientCredentialsTokenProvider,每次都产出独立闭包状态。

401 自动刷新

autoRefreshOn401: true(默认)且 clientTokenRefreshableTokenProvider 时,收到 HTTP 401 会先 invalidate() 再重发一次(仅一次,避免循环)。固定字符串 token / 纯 resolver / 关闭开关时,退回「401 直接抛错」。

错误处理

typed API(client.api(...))的中间件已把 HTTP 非 2xx 与 code !== 0 都转成 AtkonbaseApiError,业务代码可直接读 dto.data,无需再判 code。解包规则:

情况行为
JSON code === 0返回 payload.data
JSON code !== 0!okAtkonbaseApiError(payload, httpStatus)
204 / Content-Length: 0ok 时返回 undefined;否则抛错
非 JSONok 时返回字符串;否则抛错
网络 / 超时AtkonbaseNetworkError
ts
import { AtkonbaseApiError, AtkonbaseNetworkError } from "@atkonbase/sdk";

try {
  const dto = await client.api(V1DocumentApi).someEndpoint({ /* ... */ });
  // 直接用 dto.data
} catch (e) {
  if (e instanceof AtkonbaseApiError) {
    // e.code(server 业务 code)/ e.httpStatus / e.message / e.data
  } else if (e instanceof AtkonbaseNetworkError) {
    // 网络 / 超时 / abort,原始异常在 e.cause
  } else {
    throw e;
  }
}

错误码常量 errorCodes

TypeScript SDK 提供错误码常量集合 errorCodes,与 Java SDK ErrorCodes 同名同值、同 V1 暴露子集。可直接引用具名常量判定 e.code,无需直接写死数字:

ts
import { AtkonbaseApiError, errorCodes } from "@atkonbase/sdk";

try {
  const dto = await client.api(V1DocumentApi).someEndpoint({ /* ... */ });
} catch (e) {
  if (e instanceof AtkonbaseApiError) {
    if (e.code === errorCodes.USER_TOKEN_INVALID) {
      // 用户 token 失效——引导重新登录 / 刷新
    } else if (e.code === errorCodes.PERMISSION_DENIED) {
      // 权限不足
    }
    // 请始终保留一个通用错误分支处理未显式判定的 code,不要假设错误码集合是封闭的(后续版本可能新增)
  }
}

完整含义与触发条件见错误码参考

public 分享通路

AtkonbasePublicClient 覆盖 /public/s/{tenantCode}/{token}/** 匿名分享端点,无 auth,与 V1 client 类型层面互不替换:

ts
import { AtkonbasePublicClient } from "@atkonbase/sdk";

const publicClient = new AtkonbasePublicClient({ baseUrl });

// 1. 拉元信息(是否需要密码、文件大小、过期时间等)
const meta = await publicClient.getMeta(tenantCode, token);

// 2. 需要密码时,验证后拿一次性 ticket
let ticket: string | undefined;
if (meta.requiresPassword) {
  const verify = await publicClient.verifyPassword(tenantCode, token, userInputPassword);
  ticket = verify.ticket;
}

// 3. 拼下载 URL —— 推荐浏览器原生导航(绕开 CORS / 内存峰值)
const url = publicClient.buildDownloadUrl(tenantCode, token, { ticket });
window.location.href = url;

⚠️ ticket 是 IP-bound:ticket 与首次验密码请求的公网 IP 绑定。verifyPassword 调用方与最终下载方必须是同一公网 IP,否则下载被拒。服务端代理场景下不要把 ticket 透传给前端浏览器——服务端拿到的 ticket 绑的是服务端出口 IP;正确做法是集成方服务端自己用 ticket 走 buildDownloadUrl + fetch 流式转发。前端直连无此问题。

错误处理与 v1 一致:getMeta / verifyPassword 失败抛 AtkonbaseApiError,网络 / 超时抛 AtkonbaseNetworkErrorbuildDownloadUrl 仅拼字符串,不抛业务错。

buildDownloadUrl 第三参数的 disposition?: "inline" | "attachment" 参数当前保留但不生效:下载一律以 attachment(触发下载)响应。如需浏览器内预览,请改用接口参考中的预览模式端点。

与 Java SDK 的差异

主题TypeScriptJava
主入口client.api(V1DocumentApi) + client.request<T>(path) 转义出口client.api(V1DocumentApi.class)
代授权client.actAsToken(token) / actAsSource(source, id)ScopedView同左(形态镜像一致)
TokenProvider 异步性原生支持异步(() => Promise<string>Java 侧为同步签名 String resolve()
错误统一中间件把非 2xx 与 code !== 0 转成 AtkonbaseApiError,成功读 dto.dataapi() 内化解包:非 2xx 与 code != 0AtkonbaseApiException,成功读 dto.getData()
错误码常量errorCodes 覆盖 V1 子集ErrorCodes 覆盖 V1 子集

Java 用法见 Java SDK