@dsh-ft/dsh-fft-ftshare 0.3.0 → 0.4.1
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.
- package/README.md +4 -2
- package/catalog.json +222 -335
- package/catalog.meta.json +2 -2
- package/lib/client.d.ts +3 -0
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +40 -5
- package/lib/client.js.map +1 -1
- package/lib/contract.d.ts +42 -18
- package/lib/contract.d.ts.map +1 -1
- package/lib/contract.js +11 -6
- package/lib/contract.js.map +1 -1
- package/lib/errors.d.ts +54 -60
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +184 -174
- package/lib/errors.js.map +1 -1
- package/lib/index.d.ts +47 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +147 -36
- package/lib/index.js.map +1 -1
- package/lib/project.d.ts +3 -0
- package/lib/project.d.ts.map +1 -1
- package/lib/project.js +10 -1
- package/lib/project.js.map +1 -1
- package/lib/status.d.ts +103 -0
- package/lib/status.d.ts.map +1 -0
- package/lib/status.js +134 -0
- package/lib/status.js.map +1 -0
- package/lib/tools.d.ts +12 -5
- package/lib/tools.d.ts.map +1 -1
- package/lib/tools.js +65 -19
- package/lib/tools.js.map +1 -1
- package/lib/transport.d.ts +12 -0
- package/lib/transport.d.ts.map +1 -1
- package/lib/transport.js +28 -14
- package/lib/transport.js.map +1 -1
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +27 -10
package/lib/errors.js
CHANGED
|
@@ -1,75 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* 加一个四态结局。
|
|
2
|
+
* 上游失败的观测与四态归一的**薄**层。
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* 只做四件事:抄事实、判 HTTP 语义、留现场、算指纹。**不解释上游**:不维护错误码表,
|
|
5
|
+
* 不按中文文案猜「配额/权限/凭据」,不编造 remedy。上游改名、改文案、加码,这里一行
|
|
6
|
+
* 都不用改。
|
|
7
|
+
*
|
|
8
|
+
* 判定只剩两条 HTTP 语义(稳定、不会随上游文案变):
|
|
9
|
+
*
|
|
10
|
+
* - `401` / `403` → `denied`(面板遮罩要用);
|
|
11
|
+
* - 其余 → `failed`。
|
|
12
|
+
*
|
|
13
|
+
* `empty` 不在这里判定:它由成功载荷的**正向证据**决定(见 `./index.ts` 的
|
|
14
|
+
* `observePayload`)。「成功信封里带权限标记」是载荷事实,由
|
|
15
|
+
* {@link permissionNoticeFromPayload} 识别,同样不是文案猜测。
|
|
10
16
|
* @module @dsh-ft/dsh-fft-ftshare/errors
|
|
11
17
|
*/
|
|
12
18
|
import { FTSHARE_UPGRADE_URL } from './contract.js';
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
*/
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'unauthorized',
|
|
26
|
-
'401',
|
|
27
|
-
'invalid_key',
|
|
28
|
-
'invalid_api_key',
|
|
29
|
-
'auth_required',
|
|
30
|
-
'subscription_required',
|
|
19
|
+
/** 原始报文的上限;线上真实报文 p99 约 220 B,2 KB 只为兜住 HTML 错误页。 */
|
|
20
|
+
const RAW_MAX = 2048;
|
|
21
|
+
/** 展示文案(notice.message)的上限:原文很短,超长只可能是 HTML 错误页。 */
|
|
22
|
+
const NOTICE_TEXT_MAX = 1000;
|
|
23
|
+
/** 指纹前归一文案的长度上限,避免超长 HTML 撑爆聚合键。 */
|
|
24
|
+
const FINGERPRINT_TEXT_MAX = 512;
|
|
25
|
+
/** 传输层错误名:Node/DOM 的超时与取消。 */
|
|
26
|
+
const ABORT_NAMES = new Set(['AbortError', 'TimeoutError']);
|
|
27
|
+
/** Node 网络错误码:原样出现在 `Error.code` 上。 */
|
|
28
|
+
const NETWORK_CODES = new Set([
|
|
29
|
+
'ETIMEDOUT', 'ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'EAI_AGAIN', 'EPIPE',
|
|
30
|
+
'UND_ERR_CONNECT_TIMEOUT', 'UND_ERR_SOCKET', 'UND_ERR_HEADERS_TIMEOUT', 'UND_ERR_BODY_TIMEOUT',
|
|
31
31
|
]);
|
|
32
|
-
/**
|
|
33
|
-
* 独占的配额类错误码:出现即是额度问题。`429` 是它的 HTTP 形态。
|
|
34
|
-
*
|
|
35
|
-
* `upstream_unavailable` **不在这里**:匿名额度用尽和上游真的停机共用这个码,码本身
|
|
36
|
-
* 不能定案,见 {@link isQuotaSignal}。
|
|
37
|
-
*/
|
|
38
|
-
const QUOTA_CODES = new Set(['insufficient_quota', 'quota_exceeded', 'quota_exhausted', '429']);
|
|
39
|
-
/** 配额用尽时附在失败提示后的文案。 */
|
|
40
|
-
export const FTSHARE_QUOTA_NOTICE = '免费配额用尽,配置 FTShare API Key 或稍后重试';
|
|
41
|
-
/**
|
|
42
|
-
* 额度用尽的文案信号。
|
|
43
|
-
*
|
|
44
|
-
* 额度耗尽时上游只说人话不给专属码(「最近 1 天最多访问 500 次」),而匿名通道的
|
|
45
|
-
* 额度用尽与真实停机共用 `UPSTREAM_UNAVAILABLE`。这里只认「用尽 / 耗尽 / 最多访问」
|
|
46
|
-
* 这类表述,**不认「额度」二字本身**:套餐提示里同样会出现「额度」,先按文案判权限
|
|
47
|
-
* 就会把额度问题说成「套餐没开通」。
|
|
48
|
-
*/
|
|
49
|
-
const QUOTA_TEXT = /(?:配额|额度)[^。;;]{0,10}(?:用尽|用满|耗尽|用完)|最多访问|(?:超出|超过)[^。;;]{0,6}(?:额度|配额|上限)|quota[\s_-]*(?:exceeded|exhausted)/i;
|
|
50
|
-
/**
|
|
51
|
-
* 一个码或一句话是否表达「额度用尽」。
|
|
52
|
-
* @param code - 归一后的上游错误码。
|
|
53
|
-
* @param upstreamCode - 信封里的上游 HTTP 状态码,可能没有。
|
|
54
|
-
* @param message - 上游说明。
|
|
55
|
-
* @returns 是否属于额度用尽。
|
|
56
|
-
*/
|
|
57
|
-
export function isQuotaSignal(code, upstreamCode, message) {
|
|
58
|
-
const exclusive = (candidate) => candidate !== undefined && QUOTA_CODES.has(candidate.toLowerCase());
|
|
59
|
-
if (exclusive(code) || exclusive(upstreamCode))
|
|
60
|
-
return true;
|
|
61
|
-
// 共用码(`upstream_unavailable`)与任何未知码都要文案佐证:真正停机也报这个码,
|
|
62
|
-
// 只说「bad gateway」时按额度处理会把停机说成额度用尽。
|
|
63
|
-
return QUOTA_TEXT.test(message);
|
|
64
|
-
}
|
|
65
|
-
/** 中文/英文文案里的权限信号。用于上游只给一句话、不给码的情况。 */
|
|
66
|
-
const PERMISSION_TEXT = /套餐|权限|订阅|升级|未开通|未授权|无权限|额度|permission|subscription|unauthori[sz]ed|forbidden|not\s+allowed/i;
|
|
67
|
-
/** 凭据本身有问题(未携带、校验失败、含非法字符)。与套餐无关,但同样不是「没有数据」。 */
|
|
68
|
-
const API_KEY_TEXT = /(?:未收到|未通过|未配置|缺失|无效|不受支持|不完整)[^。;;]{0,12}API\s*Key|API\s*Key[^。;;]{0,12}(?:未收到|未通过|未配置|缺失|无效|校验失败|不受支持|不完整)/i;
|
|
69
|
-
/** 值得原样重试的失败:限流、超时、网络抖动、上游 5xx。 */
|
|
70
|
-
const RETRYABLE_CODES = new Set(['429', 'rate_limited', 'timeout', 'etimedout', 'econnreset', 'econnrefused', 'enotfound', 'fetch_failed', '502', '503', '504', 'upstream_error']);
|
|
71
|
-
/** 凭据类拒绝:API Key 缺失或无效,与套餐档位无关,措辞要分开。 */
|
|
72
|
-
const CREDENTIAL_CODES = new Set(['401', 'unauthorized', 'invalid_key', 'invalid_api_key', 'auth_required']);
|
|
73
32
|
/** 判断一个值是不是可当对象读的记录。 */
|
|
74
33
|
export function isRecord(value) {
|
|
75
34
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -82,150 +41,192 @@ export function asText(value) {
|
|
|
82
41
|
function asCode(value) {
|
|
83
42
|
return typeof value === 'number' && Number.isFinite(value) ? String(value) : asText(value);
|
|
84
43
|
}
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
*/
|
|
91
|
-
export function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
44
|
+
/** 读一个 100—599 的 HTTP 状态;其余一律 undefined。 */
|
|
45
|
+
function asStatus(value) {
|
|
46
|
+
const numeric = typeof value === 'number' ? value : typeof value === 'string' && /^\d{3}$/u.test(value.trim()) ? Number(value) : undefined;
|
|
47
|
+
return numeric !== undefined && numeric >= 100 && numeric <= 599 ? numeric : undefined;
|
|
48
|
+
}
|
|
49
|
+
/** 不依赖 `node:crypto` 的稳定指纹:FNV-1a 64 位,十六进制 16 位。 */
|
|
50
|
+
export function hashText(text) {
|
|
51
|
+
let hash = 0xcbf29ce484222325n;
|
|
52
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
53
|
+
hash ^= BigInt(text.charCodeAt(index));
|
|
54
|
+
hash = (hash * 0x100000001b3n) & 0xffffffffffffffffn;
|
|
55
|
+
}
|
|
56
|
+
return hash.toString(16).padStart(16, '0');
|
|
95
57
|
}
|
|
96
58
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* @param
|
|
59
|
+
* 失败指纹:同工具、同状态、同码、同归一文案的失败聚合用。
|
|
60
|
+
*
|
|
61
|
+
* 文案里的数字会归一成 `#`,否则「请等待 N 小时」会让每次失败都算新指纹。
|
|
62
|
+
* @param status - HTTP 状态。
|
|
63
|
+
* @param code - 上游码。
|
|
64
|
+
* @param message - 可读文案。
|
|
65
|
+
* @returns 16 位十六进制指纹。
|
|
101
66
|
*/
|
|
102
|
-
export function
|
|
103
|
-
|
|
67
|
+
export function fingerprintOf(status, code, message) {
|
|
68
|
+
const normalized = message.replace(/\d+/gu, '#').replace(/\s+/gu, ' ').trim().slice(0, FINGERPRINT_TEXT_MAX);
|
|
69
|
+
return hashText(`${status ?? ''}|${code ?? ''}|${normalized}`);
|
|
70
|
+
}
|
|
71
|
+
/** 把报文压成单行,避免日志/面板出现换行。 */
|
|
72
|
+
function singleLine(text) {
|
|
73
|
+
return text.replace(/\s+/gu, ' ').trim();
|
|
74
|
+
}
|
|
75
|
+
/** 剥掉 SDK 包装前缀,只留下网关/服务端自己的说明。 */
|
|
76
|
+
function stripSdkPrefix(text) {
|
|
77
|
+
return text
|
|
78
|
+
.replace(/^Streamable HTTP error:\s*/iu, '')
|
|
79
|
+
.replace(/^Error POSTing to endpoint:\s*/iu, '')
|
|
80
|
+
.replace(/^MCP error\s+-\d+:\s*/iu, '')
|
|
81
|
+
.trim();
|
|
82
|
+
}
|
|
83
|
+
/** 从 error 对象自身读 HTTP 状态(`StreamableHTTPError.code` / `status`)。 */
|
|
84
|
+
function statusOfThrown(value) {
|
|
85
|
+
if (!isRecord(value) && !(value instanceof Error))
|
|
86
|
+
return undefined;
|
|
87
|
+
const candidate = value;
|
|
88
|
+
return asStatus(candidate.status) ?? asStatus(candidate.statusCode) ?? asStatus(candidate.code);
|
|
89
|
+
}
|
|
90
|
+
/** 该抛出物是不是网络/超时类失败。 */
|
|
91
|
+
function isTransportFailure(value) {
|
|
92
|
+
if (!(value instanceof Error))
|
|
93
|
+
return false;
|
|
94
|
+
if (ABORT_NAMES.has(value.name))
|
|
104
95
|
return true;
|
|
105
|
-
|
|
96
|
+
const code = value.code;
|
|
97
|
+
if (typeof code === 'string' && NETWORK_CODES.has(code))
|
|
106
98
|
return true;
|
|
107
|
-
return
|
|
99
|
+
return /fetch failed|network|socket hang up|timed? ?out|aborted/iu.test(value.message);
|
|
108
100
|
}
|
|
109
101
|
/**
|
|
110
|
-
*
|
|
102
|
+
* 从文本里读有分界的 HTTP 状态。
|
|
111
103
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* @param value - 抛出物或上游返回的文本。
|
|
115
|
-
* @returns 归一后的码与说明。
|
|
104
|
+
* 只认三种写法:`HTTP 403`、`status: 429` / `code=400`、以及开头就是 `401/403/429`。
|
|
105
|
+
* **不认裸 `5\d\d`**:实测「最多访问 500 次」会被误读成 HTTP 500。
|
|
116
106
|
*/
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
107
|
+
function statusOfText(text) {
|
|
108
|
+
const bounded = /(?:HTTP|http|status|code|错误码)\s*[::=]?\s*([45]\d{2})\b/u.exec(text);
|
|
109
|
+
if (bounded?.[1] !== undefined)
|
|
110
|
+
return Number(bounded[1]);
|
|
111
|
+
const leading = /^\s*(401|403|429)\b/u.exec(text);
|
|
112
|
+
return leading?.[1] === undefined ? undefined : Number(leading[1]);
|
|
113
|
+
}
|
|
114
|
+
/** 尝试把报文解析成上游错误信封。 */
|
|
115
|
+
function envelopeOf(raw) {
|
|
116
|
+
const candidates = [raw.trim()];
|
|
117
|
+
const brace = raw.indexOf('{');
|
|
118
|
+
if (brace > 0)
|
|
119
|
+
candidates.push(raw.slice(brace));
|
|
120
|
+
for (const candidate of candidates) {
|
|
121
|
+
try {
|
|
122
|
+
const parsed = JSON.parse(candidate);
|
|
123
|
+
if (!isRecord(parsed))
|
|
124
|
+
continue;
|
|
125
|
+
return isRecord(parsed.error) ? parsed.error : parsed;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// 不是 JSON:按纯文本处理。
|
|
126
129
|
}
|
|
127
130
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 把一次抛出物归一成 {@link UpstreamFailure}(只记事实)。
|
|
135
|
+
* @param value - 抛出物或上游返回的文本。
|
|
136
|
+
* @returns 观测事实。
|
|
137
|
+
*/
|
|
138
|
+
export function observeFailure(value) {
|
|
139
|
+
const thrownStatus = statusOfThrown(value);
|
|
140
|
+
const raw = value instanceof Error ? value.message : typeof value === 'string' ? value : String(value);
|
|
141
|
+
const envelope = envelopeOf(raw);
|
|
142
|
+
if (envelope !== undefined) {
|
|
143
|
+
const message = asText(envelope.message) ?? asText(envelope.error) ?? asText(envelope.detail) ?? singleLine(stripSdkPrefix(raw));
|
|
144
|
+
const code = asCode(envelope.code) ?? asCode(envelope.error_code);
|
|
145
|
+
const status = asStatus(envelope.upstream_code) ?? asStatus(envelope.upstreamCode) ?? asStatus(envelope.status) ?? thrownStatus;
|
|
146
|
+
const retryable = typeof envelope.retryable === 'boolean' ? envelope.retryable : undefined;
|
|
147
|
+
const requiredPlan = asText(envelope.required_plan) ?? asText(envelope.requiredPlan) ?? asText(envelope.plan);
|
|
138
148
|
return {
|
|
149
|
+
surface: 'envelope',
|
|
150
|
+
message: message.slice(0, RAW_MAX),
|
|
139
151
|
...code !== undefined ? { code } : {},
|
|
140
|
-
...
|
|
141
|
-
|
|
152
|
+
...status !== undefined ? { status } : {},
|
|
153
|
+
...retryable !== undefined ? { retryable } : {},
|
|
142
154
|
...requiredPlan !== undefined ? { requiredPlan } : {},
|
|
155
|
+
raw,
|
|
156
|
+
fingerprint: fingerprintOf(status, code, message),
|
|
143
157
|
};
|
|
144
158
|
}
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
const prefixed = /^([a-z][a-z0-9_]{2,})\s*[::]/iu.exec(text.trim());
|
|
149
|
-
const code = asText(matched?.[2]) ?? asText(matched?.[1]) ?? asText(prefixed?.[1]);
|
|
150
|
-
const upstreamCode = asText(matched?.[1]);
|
|
159
|
+
const transport = isTransportFailure(value);
|
|
160
|
+
const cleaned = singleLine(stripSdkPrefix(raw)) || '上游未返回可读说明';
|
|
161
|
+
const status = thrownStatus ?? statusOfText(cleaned);
|
|
151
162
|
return {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
163
|
+
surface: thrownStatus !== undefined ? 'http' : transport ? 'transport' : 'text',
|
|
164
|
+
message: cleaned.slice(0, RAW_MAX),
|
|
165
|
+
...status !== undefined ? { status } : {},
|
|
166
|
+
raw,
|
|
167
|
+
fingerprint: fingerprintOf(status, undefined, cleaned),
|
|
155
168
|
};
|
|
156
169
|
}
|
|
157
|
-
/**
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
* @param message - 上游说明。
|
|
166
|
-
* @param requiredPlan - 上游信封里给出的所需档位;没给就不猜。
|
|
167
|
-
* @returns 可直接展示的提示。
|
|
168
|
-
*/
|
|
169
|
-
export function deniedNotice(tool, code, message, requiredPlan) {
|
|
170
|
-
const credential = code !== undefined && CREDENTIAL_CODES.has(code.toLowerCase());
|
|
171
|
-
const cause = credential
|
|
172
|
-
? `凭据未配置或无效(${code})`
|
|
173
|
-
: `当前套餐未开通该数据能力(上游 ${code ?? '403 route_not_allowed'})`;
|
|
174
|
-
const remedy = credential ? '有效的 API Key' : `订阅${requiredPlan ?? '更高的档位'}`;
|
|
170
|
+
/** retryable 的事实推导:HTTP 语义与传输层事实,不含「配额该不该重试」这类判断。 */
|
|
171
|
+
function factRetryable(status, transport) {
|
|
172
|
+
if (status !== undefined)
|
|
173
|
+
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
174
|
+
return transport;
|
|
175
|
+
}
|
|
176
|
+
/** 组装 notice 里的上游现场。 */
|
|
177
|
+
function upstreamOf(failure) {
|
|
175
178
|
return {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
upgradeUrl: FTSHARE_UPGRADE_URL,
|
|
180
|
-
retryable: false,
|
|
179
|
+
raw: failure.raw.slice(0, RAW_MAX),
|
|
180
|
+
...failure.status !== undefined ? { status: failure.status } : {},
|
|
181
|
+
fingerprint: failure.fingerprint,
|
|
181
182
|
};
|
|
182
183
|
}
|
|
183
184
|
/**
|
|
184
185
|
* 把一次失败折成结局与提示。
|
|
186
|
+
*
|
|
187
|
+
* 顺序即语义:`401/403` 是上游拒绝 → `denied`;其余一律 `failed`。不再按文案细分
|
|
188
|
+
* 配额/凭据/档位——原文已经带出这些信息。
|
|
185
189
|
* @param error - 抛出物。
|
|
186
|
-
* @param tool -
|
|
187
|
-
* @returns
|
|
190
|
+
* @param tool - 上游工具名(只用于调用方日志,不写进提示)。
|
|
191
|
+
* @returns 结局与可直接展示的提示。
|
|
188
192
|
*/
|
|
189
193
|
export function classifyFailure(error, tool) {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
const failure = observeFailure(error);
|
|
195
|
+
const code = failure.code ?? (failure.status !== undefined ? String(failure.status) : undefined);
|
|
196
|
+
const base = {
|
|
197
|
+
...code !== undefined ? { code } : {},
|
|
198
|
+
message: `上游原文:${failure.message.slice(0, NOTICE_TEXT_MAX)}`,
|
|
199
|
+
retryable: false,
|
|
200
|
+
upstream: upstreamOf(failure),
|
|
201
|
+
};
|
|
202
|
+
if (failure.status === 401 || failure.status === 403) {
|
|
198
203
|
return {
|
|
199
|
-
state: '
|
|
204
|
+
state: 'denied',
|
|
205
|
+
// 401 是凭据问题、403 是订阅/权限问题:只有 403 挂升级入口,401 交给原文说明。
|
|
200
206
|
notice: {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
207
|
+
...base,
|
|
208
|
+
...failure.status === 403 ? { upgradeUrl: FTSHARE_UPGRADE_URL } : {},
|
|
209
|
+
...failure.requiredPlan !== undefined ? { requiredPlan: failure.requiredPlan } : {},
|
|
204
210
|
},
|
|
211
|
+
failure,
|
|
205
212
|
};
|
|
206
213
|
}
|
|
207
|
-
if (isPermissionSignal(parsed.code, parsed.message)) {
|
|
208
|
-
return { state: 'denied', notice: deniedNotice(tool, parsed.code, parsed.message, parsed.requiredPlan) };
|
|
209
|
-
}
|
|
210
|
-
const code = parsed.code ?? 'upstream_error';
|
|
211
|
-
const retryable = isRetryableSignal(parsed.code, parsed.message, parsed.upstreamCode);
|
|
212
|
-
const tail = retryable ? '。可稍后重试。' : '';
|
|
213
214
|
return {
|
|
214
215
|
state: 'failed',
|
|
215
216
|
notice: {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
retryable,
|
|
217
|
+
...base,
|
|
218
|
+
retryable: failure.retryable ?? factRetryable(failure.status, failure.surface === 'transport'),
|
|
219
219
|
},
|
|
220
|
+
failure,
|
|
220
221
|
};
|
|
221
222
|
}
|
|
222
223
|
/**
|
|
223
224
|
* 从「调用成功但信封说自己无权」的载荷里读出权限提示。
|
|
224
225
|
*
|
|
225
|
-
* 部分网关把订阅门禁放在成功信封的 `metadata.permissions`
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* @param tool -
|
|
226
|
+
* 部分网关把订阅门禁放在成功信封的 `metadata.permissions` 或 `permission` 里,而不是
|
|
227
|
+
* 返回错误;只看空数组就会把这种情况读成「没有数据」。这里只认**显式标记**(
|
|
228
|
+
* `access_denied` 之类布尔标记)或 `401/403` 状态码,不按文案猜。
|
|
229
|
+
* @param tool - 上游工具名(只用于调用方日志,不写进提示)。
|
|
229
230
|
* @param value - 上游返回的结构化载荷。
|
|
230
231
|
* @returns 权限提示;没有标记时 undefined。
|
|
231
232
|
*/
|
|
@@ -246,15 +247,24 @@ export function permissionNoticeFromPayload(tool, value) {
|
|
|
246
247
|
if (Array.isArray(value.permissions))
|
|
247
248
|
records.push(...value.permissions.filter(isRecord));
|
|
248
249
|
for (const record of records) {
|
|
249
|
-
const code = asText(record.code) ?? asText(record.error_code);
|
|
250
|
-
const message = asText(record.message) ?? asText(record.reason) ?? asText(record.detail) ?? '';
|
|
251
250
|
const marker = [record.access_denied, record.accessDenied, record.permission_denied, record.permissionDenied,
|
|
252
251
|
record.subscription_required, record.subscriptionRequired, record.upgrade_required, record.upgradeRequired]
|
|
253
252
|
.some(flag => flag === true);
|
|
254
|
-
|
|
253
|
+
const code = asCode(record.code) ?? asCode(record.error_code);
|
|
254
|
+
const status = asStatus(record.status) ?? asStatus(record.upstream_code) ?? asStatus(code);
|
|
255
|
+
const denied = marker || status === 401 || status === 403;
|
|
256
|
+
if (!denied)
|
|
255
257
|
continue;
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
+
const message = asText(record.message) ?? asText(record.reason) ?? asText(record.detail) ?? '';
|
|
259
|
+
const raw = JSON.stringify(value);
|
|
260
|
+
return {
|
|
261
|
+
...code !== undefined ? { code } : status !== undefined ? { code: String(status) } : {},
|
|
262
|
+
message: `上游在成功响应里标注了权限限制:${message === '' ? '(未给说明)' : message}`,
|
|
263
|
+
...asText(record.required_plan) !== undefined ? { requiredPlan: asText(record.required_plan) } : {},
|
|
264
|
+
...status !== 401 ? { upgradeUrl: FTSHARE_UPGRADE_URL } : {},
|
|
265
|
+
retryable: false,
|
|
266
|
+
upstream: { raw: raw.slice(0, RAW_MAX), ...status !== undefined ? { status } : {}, fingerprint: fingerprintOf(status, code, message) },
|
|
267
|
+
};
|
|
258
268
|
}
|
|
259
269
|
return undefined;
|
|
260
270
|
}
|
package/lib/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -8,14 +8,36 @@
|
|
|
8
8
|
* 的隔离域,preset 之外的插件再也 `inject` 不到,公用底座就白叫了。所以:
|
|
9
9
|
* **provider 全局,工具与投影钩子在 preset**(见 `./tools`、`./project`)。
|
|
10
10
|
*
|
|
11
|
+
* 载荷判定只有一条硬规则:`empty` 必须**正向证据**——已知数据容器存在且为空数组。
|
|
12
|
+
* 成功信封里既没有行、也没有空容器证据时落 `failed`,绝不冒充「没有数据」(P8 的
|
|
13
|
+
* 病根);但非空载荷仍保留兜底(单对象/任意数组),避免把能用的接口判死。
|
|
14
|
+
*
|
|
11
15
|
* 凭据一律走 `ctx.ftshareSettings.apiKey.resolve()`,本包不读任何凭据文件。
|
|
12
16
|
* @module @dsh-ft/dsh-fft-ftshare
|
|
13
17
|
*/
|
|
14
18
|
import type { Context, Logger } from '@deepseek-ai/cordis';
|
|
19
|
+
/**
|
|
20
|
+
* 本包对设置服务的**最小结构需求**:只要一个能重取 API Key 的命名空间。
|
|
21
|
+
*
|
|
22
|
+
* 不 import `@dsh-ft/dsh-fft-settings` 的类型是刻意的:底座与数据层各自独立发版,
|
|
23
|
+
* 少一条依赖边就少一个发布顺序约束。`ctx.ftshareSettings` 在运行期由设置插件
|
|
24
|
+
* `provide`,这里的公开面与它保持一致;设置服务缺席时 `inject` 不会挂载本行。
|
|
25
|
+
*/
|
|
26
|
+
declare module '@deepseek-ai/cordis' {
|
|
27
|
+
interface Context {
|
|
28
|
+
ftshareSettings?: {
|
|
29
|
+
apiKey: {
|
|
30
|
+
resolve(): Promise<string | undefined>;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
15
35
|
import { type FtshareDataService } from './contract.js';
|
|
36
|
+
import { type FailureStore } from './status.js';
|
|
16
37
|
import { type FtshareTransport } from './transport.js';
|
|
17
38
|
export * from './contract.js';
|
|
18
39
|
export { FTSHARE_GATEWAY_ENDPOINT } from './transport.js';
|
|
40
|
+
export { statusStore } from './status.js';
|
|
19
41
|
/** Cordis 插件名。 */
|
|
20
42
|
export declare const name = "@dsh-ft/dsh-fft-ftshare";
|
|
21
43
|
/** 凭据来自设置服务;缺这一项本行挂起等待,属刻意的 fail-loud。 */
|
|
@@ -37,16 +59,35 @@ export interface FtshareDataServiceOptions {
|
|
|
37
59
|
logger?: Logger;
|
|
38
60
|
/** 时钟,测试用来固定 `source.fetchedAt`。 */
|
|
39
61
|
now?: () => Date;
|
|
62
|
+
/** 失败账本;缺省用进程级实例,测试传隔离实例。 */
|
|
63
|
+
statusStore?: FailureStore;
|
|
64
|
+
}
|
|
65
|
+
/** 一次成功载荷的观测结果。 */
|
|
66
|
+
export interface PayloadObservation {
|
|
67
|
+
/** 明细行;`available` 的依据。 */
|
|
68
|
+
rows: Record<string, unknown>[];
|
|
69
|
+
/** 已知数据容器存在且为空数组:这是 `empty` 的唯一正向证据。 */
|
|
70
|
+
emptyEvidence: boolean;
|
|
71
|
+
/** 顶层键,用于未识别载荷的诊断。 */
|
|
72
|
+
keys: string[];
|
|
73
|
+
/** 成功信封里既没有行、也没有空证据。 */
|
|
74
|
+
unrecognized: boolean;
|
|
40
75
|
}
|
|
41
76
|
/**
|
|
42
|
-
*
|
|
77
|
+
* 观测上游载荷:只回答「有没有行」「空有没有证据」「是不是不认识」。
|
|
43
78
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
79
|
+
* 规则:数组本身是行;已知容器里的非空数组/对象是行;任意数组键里的记录是行;
|
|
80
|
+
* 非信封、非容器、非数组的字段让整条记录成为一行;以上都没有时,只有「已知容器
|
|
81
|
+
* 为空数组」才算 `emptyEvidence`,否则是 `unrecognized`。
|
|
82
|
+
* @param value - 上游结构化载荷。
|
|
83
|
+
* @param depth - 当前递归深度(内部用)。
|
|
84
|
+
* @returns 观测结果。
|
|
85
|
+
*/
|
|
86
|
+
export declare function observePayload(value: unknown, depth?: number): PayloadObservation;
|
|
87
|
+
/**
|
|
88
|
+
* 从任意上游载荷里找出明细行(`observePayload` 的兼容薄包装)。
|
|
48
89
|
* @param value - 上游结构化载荷。
|
|
49
|
-
* @returns
|
|
90
|
+
* @returns 明细行;空数组表示没有行(**不**代表 `empty`,见 `observePayload`)。
|
|
50
91
|
*/
|
|
51
92
|
export declare function normalizeRows(value: unknown): Record<string, unknown>[];
|
|
52
93
|
/**
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAE1D;;;;;;GAMG;AACH,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,eAAe,CAAC,EAAE;YAAE,MAAM,EAAE;gBAAE,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;aAAE,CAAA;SAAE,CAAA;KACzE;CACF;AAED,OAAO,EAIL,KAAK,kBAAkB,EAGxB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,EAAoD,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAExG,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC,kBAAkB;AAClB,eAAO,MAAM,IAAI,4BAA4B,CAAA;AAE7C,0CAA0C;AAC1C,eAAO,MAAM,MAAM,UAAsB,CAAA;AAEzC,gBAAgB;AAChB,eAAO,MAAM,OAAO,iBAAyB,CAAA;AAa7C,gBAAgB;AAChB,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,2BAA2B;AAC3B,MAAM,WAAW,yBAAyB;IACxC,0BAA0B;IAC1B,SAAS,EAAE,gBAAgB,CAAA;IAC3B,iBAAiB;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IAChB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,YAAY,CAAA;CAC3B;AAgBD,mBAAmB;AACnB,MAAM,WAAW,kBAAkB;IACjC,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IAC/B,wCAAwC;IACxC,aAAa,EAAE,OAAO,CAAA;IACtB,sBAAsB;IACtB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,wBAAwB;IACxB,YAAY,EAAE,OAAO,CAAA;CACtB;AAOD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,kBAAkB,CA4B5E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAEvE;AA2ED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CAsD/F;AAuBD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAUpE"}
|