@tokenbuddy/contracts 1.0.0 → 1.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,384 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultBalanceProbeCache = exports.BalanceProbeCache = void 0;
4
+ exports.probeUpstreamBalance = probeUpstreamBalance;
5
+ const DEFAULT_TIMEOUT_MS = 7000;
6
+ const FAILURE_CACHE_TTL_MS = 30000;
7
+ const DEFAULT_CNY_USD_RATE = 0.14;
8
+ class BalanceProbeCache {
9
+ entries = new Map();
10
+ get(key, now) {
11
+ const entry = this.entries.get(key);
12
+ if (!entry || entry.expiresAt <= now) {
13
+ if (entry) {
14
+ this.entries.delete(key);
15
+ }
16
+ return undefined;
17
+ }
18
+ return entry.snapshot;
19
+ }
20
+ setFailure(key, snapshot, now) {
21
+ this.entries.set(key, {
22
+ snapshot,
23
+ expiresAt: now + FAILURE_CACHE_TTL_MS
24
+ });
25
+ }
26
+ }
27
+ exports.BalanceProbeCache = BalanceProbeCache;
28
+ exports.defaultBalanceProbeCache = new BalanceProbeCache();
29
+ async function probeUpstreamBalance(config, options = {}) {
30
+ const now = options.now || Date.now;
31
+ const fetchedAt = now();
32
+ const key = cacheKey(config);
33
+ const cache = options.cache || exports.defaultBalanceProbeCache;
34
+ const cached = cache.get(key, fetchedAt);
35
+ if (cached) {
36
+ return cached;
37
+ }
38
+ const plan = requestPlan(config);
39
+ if (!plan) {
40
+ const snapshot = failure("unknown", fetchedAt, 0, "unsupported upstream - configure a balance parser");
41
+ cache.setFailure(key, snapshot, fetchedAt);
42
+ return snapshot;
43
+ }
44
+ if (!config.upstreamApiKey) {
45
+ const snapshot = failure(plan.source, fetchedAt, 0, "missing upstreamApiKey for balance probe");
46
+ cache.setFailure(key, snapshot, fetchedAt);
47
+ return snapshot;
48
+ }
49
+ const upstreamUserId = stringValue(config.upstreamBalanceProbe?.userId) || stringValue(config.upstreamUserId);
50
+ if (plan.requiresUserId && !upstreamUserId) {
51
+ const snapshot = failure(plan.source, fetchedAt, 0, "missing upstreamUserId for newapi upstream");
52
+ cache.setFailure(key, snapshot, fetchedAt);
53
+ return snapshot;
54
+ }
55
+ try {
56
+ const response = await fetchWithTimeout(plan.url, {
57
+ method: "GET",
58
+ headers: requestHeaders(config.upstreamApiKey, plan.requiresUserId ? upstreamUserId : undefined)
59
+ }, options.fetch || fetch, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
60
+ if (!response.ok) {
61
+ const snapshot = failure(plan.source, fetchedAt, response.status, httpErrorMessage(response.status));
62
+ cache.setFailure(key, snapshot, fetchedAt);
63
+ return snapshot;
64
+ }
65
+ const payload = await response.json();
66
+ const snapshot = parseBalancePayload(payload, plan, fetchedAt, cnyUsdRate(options.cnyUsdRate));
67
+ if (snapshot.error) {
68
+ cache.setFailure(key, snapshot, fetchedAt);
69
+ }
70
+ return snapshot;
71
+ }
72
+ catch (err) {
73
+ const snapshot = failure(plan.source, fetchedAt, 0, `network: ${err instanceof Error ? err.message : String(err)}`);
74
+ cache.setFailure(key, snapshot, fetchedAt);
75
+ return snapshot;
76
+ }
77
+ }
78
+ function requestPlan(config) {
79
+ const template = balanceProbeTemplateValue(config.upstreamBalanceProbe?.template);
80
+ if (template === "none") {
81
+ return undefined;
82
+ }
83
+ const balanceUrl = stringValue(config.upstreamBalanceProbe?.url) || stringValue(config.upstreamBalanceUrl);
84
+ const upstreamUrl = stringValue(config.upstreamUrl);
85
+ if (template && template !== "auto") {
86
+ return explicitRequestPlan(template, balanceUrl, upstreamUrl);
87
+ }
88
+ const host = hostName(balanceUrl || upstreamUrl);
89
+ if (host === "api.deepseek.com") {
90
+ return { source: "deepseek", url: "https://api.deepseek.com/user/balance", currency: "CNY", requiresUserId: false };
91
+ }
92
+ if (host === "api.stepfun.ai" || host === "api.stepfun.com") {
93
+ return { source: "stepfun", url: "https://api.stepfun.com/v1/accounts", currency: "CNY", requiresUserId: false };
94
+ }
95
+ if (host === "api.siliconflow.cn" || host === "api.siliconflow.com") {
96
+ const currency = host.endsWith(".cn") ? "CNY" : "USD";
97
+ const url = host.endsWith(".cn") ? "https://api.siliconflow.cn/v1/user/info" : "https://api.siliconflow.com/v1/user/info";
98
+ return { source: "siliconflow", url, currency, requiresUserId: false };
99
+ }
100
+ if (host === "openrouter.ai") {
101
+ return { source: "openrouter", url: "https://openrouter.ai/api/v1/credits", currency: "USD", requiresUserId: false };
102
+ }
103
+ if (host === "api.novita.ai") {
104
+ return { source: "novita", url: "https://api.novita.ai/v3/user/balance", currency: "USD", requiresUserId: false };
105
+ }
106
+ if (balanceUrl && isUsageEndpoint(balanceUrl)) {
107
+ return { source: "usage_generic", url: balanceUrl, currency: "USD", requiresUserId: false };
108
+ }
109
+ if (balanceUrl) {
110
+ return { source: "newapi_generic", url: balanceUrl, currency: "USD", requiresUserId: true };
111
+ }
112
+ const genericUsageUrl = upstreamUrl ? usageUrl(upstreamUrl) : undefined;
113
+ if (genericUsageUrl) {
114
+ return { source: "usage_generic", url: genericUsageUrl, currency: "USD", requiresUserId: false };
115
+ }
116
+ return undefined;
117
+ }
118
+ function explicitRequestPlan(template, balanceUrl, upstreamUrl) {
119
+ if (template === "deepseek") {
120
+ return { source: "deepseek", url: balanceUrl || "https://api.deepseek.com/user/balance", currency: "CNY", requiresUserId: false };
121
+ }
122
+ if (template === "stepfun") {
123
+ return { source: "stepfun", url: balanceUrl || "https://api.stepfun.com/v1/accounts", currency: "CNY", requiresUserId: false };
124
+ }
125
+ if (template === "siliconflow") {
126
+ const host = hostName(balanceUrl || upstreamUrl);
127
+ const currency = host.endsWith(".com") ? "USD" : "CNY";
128
+ const url = balanceUrl || (currency === "USD" ? "https://api.siliconflow.com/v1/user/info" : "https://api.siliconflow.cn/v1/user/info");
129
+ return { source: "siliconflow", url, currency, requiresUserId: false };
130
+ }
131
+ if (template === "openrouter") {
132
+ return { source: "openrouter", url: balanceUrl || "https://openrouter.ai/api/v1/credits", currency: "USD", requiresUserId: false };
133
+ }
134
+ if (template === "novita") {
135
+ return { source: "novita", url: balanceUrl || "https://api.novita.ai/v3/user/balance", currency: "USD", requiresUserId: false };
136
+ }
137
+ if (template === "newapi_generic") {
138
+ return balanceUrl ? { source: "newapi_generic", url: balanceUrl, currency: "USD", requiresUserId: true } : undefined;
139
+ }
140
+ if (template === "usage_generic") {
141
+ const url = balanceUrl || (upstreamUrl ? usageUrl(upstreamUrl) : undefined);
142
+ return url ? { source: "usage_generic", url, currency: "USD", requiresUserId: false } : undefined;
143
+ }
144
+ return undefined;
145
+ }
146
+ function requestHeaders(upstreamApiKey, upstreamUserId) {
147
+ const headers = {
148
+ "Authorization": `Bearer ${upstreamApiKey}`
149
+ };
150
+ if (upstreamUserId) {
151
+ headers["New-Api-User"] = upstreamUserId;
152
+ }
153
+ return headers;
154
+ }
155
+ async function fetchWithTimeout(url, init, fetchFn, timeoutMs) {
156
+ const controller = new AbortController();
157
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
158
+ try {
159
+ return await fetchFn(url, { ...init, signal: controller.signal });
160
+ }
161
+ finally {
162
+ clearTimeout(timer);
163
+ }
164
+ }
165
+ function parseBalancePayload(payload, plan, fetchedAt, cnyRate) {
166
+ const object = objectValue(payload);
167
+ if (!object) {
168
+ return failure(plan.source, fetchedAt, 200, "balance response must be an object");
169
+ }
170
+ if (plan.source === "deepseek") {
171
+ return parseDeepSeek(object, fetchedAt, cnyRate);
172
+ }
173
+ if (plan.source === "stepfun") {
174
+ return amountSnapshot(numberFrom(object.balance), "CNY", "stepfun", fetchedAt, cnyRate);
175
+ }
176
+ if (plan.source === "siliconflow") {
177
+ return parseSiliconFlow(object, plan.currency, fetchedAt, cnyRate);
178
+ }
179
+ if (plan.source === "openrouter") {
180
+ return parseOpenRouter(object, fetchedAt, cnyRate);
181
+ }
182
+ if (plan.source === "novita") {
183
+ const availableBalance = numberFrom(object.availableBalance);
184
+ return amountSnapshot(availableBalance === null ? null : availableBalance / 10000, "USD", "novita", fetchedAt, cnyRate);
185
+ }
186
+ if (plan.source === "newapi_generic") {
187
+ return parseNewApi(object, fetchedAt, cnyRate);
188
+ }
189
+ if (plan.source === "usage_generic") {
190
+ return parseUsageGeneric(object, fetchedAt, cnyRate);
191
+ }
192
+ return failure("unknown", fetchedAt, 0, "unsupported upstream - configure a balance parser");
193
+ }
194
+ function parseDeepSeek(payload, fetchedAt, cnyRate) {
195
+ const infos = Array.isArray(payload.balance_infos) ? payload.balance_infos : [];
196
+ const first = objectValue(infos[0]);
197
+ const currency = currencyValue(first?.currency) || "CNY";
198
+ const rawAmount = numberFrom(first?.total_balance);
199
+ if (payload.is_available === false) {
200
+ return {
201
+ ...amountSnapshot(rawAmount, currency, "deepseek", fetchedAt, cnyRate),
202
+ error: { httpStatus: 200, message: "Insufficient balance" }
203
+ };
204
+ }
205
+ if (!first) {
206
+ return amountSnapshot(null, currency, "deepseek", fetchedAt, cnyRate);
207
+ }
208
+ if (rawAmount === null) {
209
+ return failure("deepseek", fetchedAt, 200, "missing field: balance_infos[].total_balance", currency);
210
+ }
211
+ return amountSnapshot(rawAmount, currency, "deepseek", fetchedAt, cnyRate);
212
+ }
213
+ function parseSiliconFlow(payload, currency, fetchedAt, cnyRate) {
214
+ const code = numberFrom(payload.code);
215
+ if (code !== null && code !== 20000) {
216
+ return failure("siliconflow", fetchedAt, 200, `upstream code: ${code}`, currency);
217
+ }
218
+ const data = objectValue(payload.data);
219
+ if (!data) {
220
+ return failure("siliconflow", fetchedAt, 200, "missing field: data", currency);
221
+ }
222
+ const snapshot = amountSnapshot(numberFrom(data.totalBalance), currency, "siliconflow", fetchedAt, cnyRate);
223
+ const status = stringValue(data.status);
224
+ if (status && status !== "ok") {
225
+ return {
226
+ ...snapshot,
227
+ error: { httpStatus: 200, message: `upstream status: ${status}` }
228
+ };
229
+ }
230
+ return snapshot;
231
+ }
232
+ function parseOpenRouter(payload, fetchedAt, cnyRate) {
233
+ const data = objectValue(payload.data) || payload;
234
+ const totalCredits = numberFrom(data.total_credits);
235
+ const totalUsage = numberFrom(data.total_usage);
236
+ if (totalCredits === null || totalUsage === null) {
237
+ return failure("openrouter", fetchedAt, 200, "missing field: data.total_credits or data.total_usage", "USD");
238
+ }
239
+ return amountSnapshot(Math.max(0, totalCredits - totalUsage), "USD", "openrouter", fetchedAt, cnyRate);
240
+ }
241
+ function parseNewApi(payload, fetchedAt, cnyRate) {
242
+ const data = objectValue(payload.data);
243
+ if (!data) {
244
+ return failure("newapi_generic", fetchedAt, 200, "missing field: data", "USD");
245
+ }
246
+ const quota = numberFrom(data.quota);
247
+ const usedQuota = numberFrom(data.used_quota);
248
+ if (quota === null || usedQuota === null) {
249
+ return failure("newapi_generic", fetchedAt, 200, "missing field: data.quota or data.used_quota", "USD");
250
+ }
251
+ return amountSnapshot((quota - usedQuota) / 500000, "USD", "newapi_generic", fetchedAt, cnyRate);
252
+ }
253
+ function parseUsageGeneric(payload, fetchedAt, cnyRate) {
254
+ const quota = objectValue(payload.quota);
255
+ const rawAmount = numberFrom(payload.remaining) ?? numberFrom(quota?.remaining) ?? numberFrom(payload.balance);
256
+ const currency = currencyValue(payload.unit) || currencyValue(quota?.unit) || "USD";
257
+ if (rawAmount === null) {
258
+ return failure("usage_generic", fetchedAt, 200, "missing field: remaining, quota.remaining, or balance", currency);
259
+ }
260
+ const snapshot = amountSnapshot(rawAmount, currency, "usage_generic", fetchedAt, cnyRate);
261
+ const isValid = booleanValue(payload.is_active) ?? booleanValue(payload.isValid) ?? true;
262
+ if (!isValid) {
263
+ return {
264
+ ...snapshot,
265
+ error: { httpStatus: 200, message: "upstream key is not active" }
266
+ };
267
+ }
268
+ return snapshot;
269
+ }
270
+ function amountSnapshot(rawAmount, currency, source, fetchedAt, cnyRate) {
271
+ return {
272
+ rawAmount,
273
+ amountUsdMicros: rawAmount === null ? null : Math.round(rawAmount * (currency === "CNY" ? cnyRate : 1) * 1000000),
274
+ currency,
275
+ source,
276
+ fetchedAt
277
+ };
278
+ }
279
+ function failure(source, fetchedAt, httpStatus, message, currency = null) {
280
+ return {
281
+ rawAmount: null,
282
+ amountUsdMicros: null,
283
+ currency,
284
+ source,
285
+ fetchedAt,
286
+ error: { httpStatus, message }
287
+ };
288
+ }
289
+ function httpErrorMessage(status) {
290
+ if (status === 401) {
291
+ return "unauthorized: check upstreamApiKey";
292
+ }
293
+ if (status === 403) {
294
+ return "forbidden: upstream rejected the credentials";
295
+ }
296
+ if (status === 429) {
297
+ return "rate limited";
298
+ }
299
+ if (status >= 500) {
300
+ return "upstream 5xx";
301
+ }
302
+ return `upstream http ${status}`;
303
+ }
304
+ function cnyUsdRate(value) {
305
+ if (value !== undefined && Number.isFinite(value) && value > 0) {
306
+ return value;
307
+ }
308
+ const envValue = Number(process.env.TB_CNY_USD_RATE);
309
+ return Number.isFinite(envValue) && envValue > 0 ? envValue : DEFAULT_CNY_USD_RATE;
310
+ }
311
+ function cacheKey(config) {
312
+ return [
313
+ stringValue(config.upstreamBalanceProbe?.template),
314
+ stringValue(config.upstreamBalanceProbe?.url),
315
+ stringValue(config.upstreamBalanceProbe?.userId),
316
+ stringValue(config.upstreamBalanceUrl),
317
+ stringValue(config.upstreamUrl),
318
+ stringValue(config.upstreamApiKey),
319
+ stringValue(config.upstreamUserId)
320
+ ].join("|");
321
+ }
322
+ function balanceProbeTemplateValue(value) {
323
+ return value === "auto" ||
324
+ value === "deepseek" ||
325
+ value === "stepfun" ||
326
+ value === "siliconflow" ||
327
+ value === "openrouter" ||
328
+ value === "novita" ||
329
+ value === "newapi_generic" ||
330
+ value === "usage_generic" ||
331
+ value === "none"
332
+ ? value
333
+ : undefined;
334
+ }
335
+ function hostName(value) {
336
+ if (!value) {
337
+ return "";
338
+ }
339
+ try {
340
+ return new URL(value).hostname.replace(/^www\./, "");
341
+ }
342
+ catch {
343
+ return "";
344
+ }
345
+ }
346
+ function usageUrl(value) {
347
+ try {
348
+ const url = new URL(value);
349
+ url.pathname = "/v1/usage";
350
+ url.search = "";
351
+ url.hash = "";
352
+ return url.toString();
353
+ }
354
+ catch {
355
+ return undefined;
356
+ }
357
+ }
358
+ function isUsageEndpoint(value) {
359
+ try {
360
+ return new URL(value).pathname.replace(/\/+$/, "") === "/v1/usage";
361
+ }
362
+ catch {
363
+ return false;
364
+ }
365
+ }
366
+ function objectValue(value) {
367
+ return value && typeof value === "object" && !Array.isArray(value)
368
+ ? value
369
+ : undefined;
370
+ }
371
+ function stringValue(value) {
372
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
373
+ }
374
+ function numberFrom(value) {
375
+ const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
376
+ return Number.isFinite(parsed) ? parsed : null;
377
+ }
378
+ function booleanValue(value) {
379
+ return typeof value === "boolean" ? value : undefined;
380
+ }
381
+ function currencyValue(value) {
382
+ return value === "USD" || value === "CNY" ? value : undefined;
383
+ }
384
+ //# sourceMappingURL=upstream-balance-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstream-balance-probe.js","sourceRoot":"","sources":["../../src/upstream-balance-probe.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenbuddy/contracts",
3
- "version": "1.0.0",
3
+ "version": "1.0.36",
4
4
  "description": "Shared contract models and crypto functions",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -14,4 +14,4 @@
14
14
  "devDependencies": {
15
15
  "@types/uuid": "^9.0.8"
16
16
  }
17
- }
17
+ }
package/src/crypto.ts CHANGED
@@ -47,22 +47,35 @@ export function decryptSm4EcbPkcs7Base64(encryptedBase64: string, sm4KeyBase64:
47
47
  }
48
48
 
49
49
  /**
50
- * Compute md5 hex digest of a string (indicator).
50
+ * 计算资源 slug MD5 摘要(hex 编码)。
51
+ * 用作 Clawtip 支付的 indicator 字段,seller 端用于校验资源一致性。
52
+ *
53
+ * @param slug 资源 slug
54
+ * @returns 小写十六进制的 MD5 摘要
51
55
  */
52
56
  export function computeIndicator(slug: string): string {
53
57
  return crypto.createHash("md5").update(slug, "utf8").digest("hex");
54
58
  }
55
59
 
56
60
  /**
57
- * Generate a 32-character lowercase hex string as orderNo (UUID v4 without dashes).
61
+ * 生成 32 字符小写十六进制字符串作为订单号。
62
+ * 实现细节:UUID v4 去除中横线,保证全局唯一且 URL/日志友好。
63
+ *
64
+ * @returns 32 字符小写 hex 字符串
58
65
  */
59
66
  export function generateOrderNo(): string {
60
67
  return uuidv4().replace(/-/g, "");
61
68
  }
62
69
 
63
70
  /**
64
- * Encrypt Clawtip order data: {"orderNo":..., "amount":..., "payTo":...}
65
- * Returns the base64 encrypted data.
71
+ * 加密 Clawtip 订单数据。
72
+ * 明文格式:`{"orderNo":..., "amount":..., "payTo":...}`,使用 SM4-ECB-PKCS7 加密后 base64 编码。
73
+ *
74
+ * @param orderNo 订单号
75
+ * @param amount 订单金额(分)
76
+ * @param payTo 收款方标识
77
+ * @param sm4KeyBase64 SM4 密钥的 base64 编码(必须解码为 16 字节)
78
+ * @returns base64 编码的密文
66
79
  */
67
80
  export function encryptClawtipOrderData(
68
81
  orderNo: string,
@@ -78,28 +91,61 @@ export function encryptClawtipOrderData(
78
91
  return encryptSm4EcbPkcs7Base64(plaintext, sm4KeyBase64);
79
92
  }
80
93
 
94
+ /**
95
+ * Clawtip 支付下单所需的完整参数。
96
+ * 由 `buildClawtipPaymentParams` 在 buyer 端组装后下发到 seller。
97
+ */
81
98
  export interface ClawtipPaymentParams {
99
+ /** 32 字符小写十六进制的订单号(UUID v4 去横线),全局唯一标识一次下单 */
82
100
  orderNo: string;
101
+ /** 订单金额,单位为分(人民币 1 元 = 100 分) */
83
102
  amountFen: number;
103
+ /** 收款方标识,seller 校验该字段决定打款目标 */
84
104
  payTo: string;
105
+ /** SM4-ECB-PKCS7 加密的 `{orderNo, amount, payTo}` JSON,base64 编码 */
85
106
  encryptedData: string;
107
+ /** 资源 slug 的 MD5 摘要,用于 seller 校验资源一致性 */
86
108
  indicator: string;
109
+ /** 资源 slug(人类可读 ID),与 indicator 配对使用 */
87
110
  slug: string;
111
+ /** Clawtip 平台的技能 ID */
88
112
  skillId: string;
113
+ /** 支付场景的人类可读描述,展示给支付方 */
89
114
  description: string;
115
+ /** 资源下载/访问 URL,可用于支付完成后的资源定位 */
90
116
  resourceUrl: string;
91
117
  }
92
118
 
119
+ /**
120
+ * 构造 `ClawtipPaymentParams` 所需的原始输入。
121
+ * 调用方只需要提供业务字段;`orderNo` 和 `encryptedData` 由 builder 自动生成。
122
+ */
93
123
  export interface ClawtipPaymentParamsInput {
124
+ /** 收款方标识 */
94
125
  payTo: string;
126
+ /** SM4 密钥的 base64 编码(解密后必须为 16 字节) */
95
127
  sm4KeyBase64: string;
128
+ /** 资源 slug,会同时参与 indicator 计算 */
96
129
  skillSlug: string;
130
+ /** Clawtip 平台的技能 ID */
97
131
  skillId: string;
132
+ /** 支付场景描述 */
98
133
  description: string;
134
+ /** 资源 URL */
99
135
  resourceUrl: string;
136
+ /** 订单金额(分),必须为正整数 */
100
137
  amountFen: number;
101
138
  }
102
139
 
140
+ /**
141
+ * 构造 `ClawtipPaymentParams` 完整参数。
142
+ * 负责:生成 `orderNo`、对 `(orderNo, amount, payTo)` 做 SM4 加密、计算 `indicator`。
143
+ * `amountFen` 必须为正整数,否则抛错。
144
+ *
145
+ * @param input 业务字段输入
146
+ * @returns 包含加密数据的完整支付参数
147
+ * @throws Error 当 `amountFen` 不是正整数时
148
+ */
103
149
  export function buildClawtipPaymentParams(input: ClawtipPaymentParamsInput): ClawtipPaymentParams {
104
150
  if (!Number.isInteger(input.amountFen) || input.amountFen < 1) {
105
151
  throw new Error("amount_fen must be greater than zero");
package/src/errors.ts CHANGED
@@ -32,6 +32,13 @@ export enum ErrorCode {
32
32
  ContractVersionDrift = "contract_version_drift"
33
33
  }
34
34
 
35
+ /**
36
+ * 错误码到重试策略分类的映射。
37
+ * buyer / seller 路由层根据该结果决定是否自动重试、是否触发 fail-closed。
38
+ *
39
+ * @param code 业务错误码
40
+ * @returns 重试分类:`RetryableWithBackoff`(建议退避重试)、`FailClosed`(关闭交易、人工介入)、`NotRetryable`(直接报错给用户)
41
+ */
35
42
  export function getRetryClass(code: ErrorCode): RetryClass {
36
43
  switch (code) {
37
44
  case ErrorCode.BusyCapacity:
@@ -46,6 +53,13 @@ export function getRetryClass(code: ErrorCode): RetryClass {
46
53
  }
47
54
  }
48
55
 
56
+ /**
57
+ * 错误码到 HTTP 状态码的映射。
58
+ * 当协议层把业务错误透传为 HTTP 响应(如 buyer → wallet-bootstrap 健康检查)时使用。
59
+ *
60
+ * @param code 业务错误码
61
+ * @returns 对应的 HTTP 状态码(4xx/5xx)
62
+ */
49
63
  export function getHttpStatus(code: ErrorCode): number {
50
64
  switch (code) {
51
65
  case ErrorCode.BusyCapacity:
@@ -87,6 +101,13 @@ export function getHttpStatus(code: ErrorCode): number {
87
101
  }
88
102
  }
89
103
 
104
+ /**
105
+ * 错误码到默认人类可读消息的映射。
106
+ * 用于 `createErrorEnvelope` 未传 `message` 参数时的兜底文案。
107
+ *
108
+ * @param code 业务错误码
109
+ * @returns 默认错误消息
110
+ */
90
111
  export function getDefaultMessage(code: ErrorCode): string {
91
112
  switch (code) {
92
113
  case ErrorCode.BusyCapacity:
@@ -144,18 +165,39 @@ export function getDefaultMessage(code: ErrorCode): string {
144
165
  }
145
166
  }
146
167
 
168
+ /**
169
+ * 标准错误体的字段定义。HTTP 响应或 JSON-RPC error 对象均使用该结构。
170
+ */
147
171
  export interface ErrorBody {
172
+ /** 机器可读的错误码,用于客户端做错误分支处理 */
148
173
  code: ErrorCode;
174
+ /** 错误重试分类(fail_closed / retryable_with_backoff / not_retryable) */
149
175
  retryClass: RetryClass;
176
+ /** 人类可读的错误描述 */
150
177
  message: string;
178
+ /** 推荐的 HTTP 状态码,HTTP 透传场景下使用 */
151
179
  httpStatus: number;
180
+ /** 退避建议秒数(仅 429/重试类错误附带) */
152
181
  retryAfterSeconds?: number;
153
182
  }
154
183
 
184
+ /**
185
+ * 错误响应的标准外壳。所有非 2xx 响应都以 `{ error: {...} }` 形式返回。
186
+ */
155
187
  export interface ErrorEnvelope {
188
+ /** 嵌套的错误体 */
156
189
  error: ErrorBody;
157
190
  }
158
191
 
192
+ /**
193
+ * 构造标准错误响应外壳。
194
+ * 自动填充 `retryClass`、`httpStatus`、默认 `message`,调用方只需提供 `code` 和可选的覆盖字段。
195
+ *
196
+ * @param code 业务错误码,决定 retryClass / httpStatus / 默认 message
197
+ * @param message 自定义消息;省略时使用 `getDefaultMessage(code)` 兜底文案
198
+ * @param retryAfterSeconds 退避秒数,仅在需要时填入(如 429)
199
+ * @returns 完整的 `ErrorEnvelope` 对象
200
+ */
159
201
  export function createErrorEnvelope(
160
202
  code: ErrorCode,
161
203
  message?: string,
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./errors.js";
2
2
  export * from "./crypto.js";
3
+ export * from "./upstream-balance-probe.js";