@dsh-ft/dsh-fft-ftshare 0.0.0-stage → 0.4.0
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/LICENSE +21 -0
- package/README.md +82 -2
- package/catalog.json +9762 -0
- package/catalog.meta.json +7 -0
- package/catalog.txt +250 -0
- package/cordis.patch.yml +27 -0
- package/lib/capabilities.d.ts +47 -0
- package/lib/capabilities.d.ts.map +1 -0
- package/lib/capabilities.js +145 -0
- package/lib/capabilities.js.map +1 -0
- package/lib/catalog-host.d.ts +19 -0
- package/lib/catalog-host.d.ts.map +1 -0
- package/lib/catalog-host.js +31 -0
- package/lib/catalog-host.js.map +1 -0
- package/lib/catalog.d.ts +51 -0
- package/lib/catalog.d.ts.map +1 -0
- package/lib/catalog.js +60 -0
- package/lib/catalog.js.map +1 -0
- package/lib/client.d.ts +55 -0
- package/lib/client.d.ts.map +1 -0
- package/lib/client.js +184 -0
- package/lib/client.js.map +1 -0
- package/lib/contract.d.ts +143 -0
- package/lib/contract.d.ts.map +1 -0
- package/lib/contract.js +26 -0
- package/lib/contract.js.map +1 -0
- package/lib/errors.d.ts +87 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +271 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +89 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +271 -0
- package/lib/index.js.map +1 -0
- package/lib/mask.d.ts +66 -0
- package/lib/mask.d.ts.map +1 -0
- package/lib/mask.js +106 -0
- package/lib/mask.js.map +1 -0
- package/lib/project.d.ts +101 -0
- package/lib/project.d.ts.map +1 -0
- package/lib/project.js +335 -0
- package/lib/project.js.map +1 -0
- package/lib/route.d.ts +48 -0
- package/lib/route.d.ts.map +1 -0
- package/lib/route.js +90 -0
- package/lib/route.js.map +1 -0
- 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 +38 -0
- package/lib/tools.d.ts.map +1 -0
- package/lib/tools.js +178 -0
- package/lib/tools.js.map +1 -0
- package/lib/transport.d.ts +65 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +161 -0
- package/lib/transport.js.map +1 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +93 -4
package/lib/catalog.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 目录(catalog):上游接口的**全量快照**,回答「上游有什么」。
|
|
3
|
+
*
|
|
4
|
+
* 与接口表(`capabilities.ts`,回答「我们承接什么」)不是一回事。目录由
|
|
5
|
+
* `scripts/build-catalog.mjs` 从 `tools/list` 生成,随包发布两份产物:
|
|
6
|
+
* `catalog.json`(完整,含 `description` 与 `inputSchema`)与 `catalog.txt`
|
|
7
|
+
* (一线索引,供模型 `grep`)。本模块只做纯函数——查名字、读必填入参——因此宿主
|
|
8
|
+
* 与浏览器两侧都能用;读文件的那一半在 `catalog-host.ts`。
|
|
9
|
+
* @module @dsh-ft/dsh-fft-ftshare/catalog
|
|
10
|
+
*/
|
|
11
|
+
/** 判断一个值是不是可当对象读的记录。 */
|
|
12
|
+
function isRecord(value) {
|
|
13
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 把任意解析结果收敛成目录。字段缺失的条目直接丢掉而不是留半条:目录是准入判据,
|
|
17
|
+
* 半条会让「在目录里」这个断言变得没有意义。
|
|
18
|
+
* @param value - `catalog.json` 的解析结果。
|
|
19
|
+
* @returns 目录;形状不对时为空目录。
|
|
20
|
+
*/
|
|
21
|
+
export function asCatalog(value) {
|
|
22
|
+
const raw = isRecord(value) && Array.isArray(value.entries) ? value.entries : [];
|
|
23
|
+
const entries = [];
|
|
24
|
+
for (const entry of raw) {
|
|
25
|
+
if (!isRecord(entry) || typeof entry.name !== 'string' || entry.name === '')
|
|
26
|
+
continue;
|
|
27
|
+
entries.push({
|
|
28
|
+
name: entry.name,
|
|
29
|
+
title: typeof entry.title === 'string' && entry.title !== '' ? entry.title : entry.name,
|
|
30
|
+
...typeof entry.description === 'string' && entry.description !== '' ? { description: entry.description } : {},
|
|
31
|
+
...entry.inputSchema !== undefined ? { inputSchema: entry.inputSchema } : {},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return { entries };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 按接口名查目录。
|
|
38
|
+
* @param catalog - 目录。
|
|
39
|
+
* @param name - 上游接口名。
|
|
40
|
+
* @returns 条目;不在目录里时 undefined。
|
|
41
|
+
*/
|
|
42
|
+
export function catalogEntryOf(catalog, name) {
|
|
43
|
+
return catalog.entries.find(entry => entry.name === name);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 读出上游 `inputSchema.required` 里、这次调用没有给的入参名。
|
|
47
|
+
*
|
|
48
|
+
* 只查「有没有给」,不查类型与取值:本地能确定说出口的只有缺席,类型判断留给上游,
|
|
49
|
+
* 免得把「上游其实能容忍的写法」判成错误。
|
|
50
|
+
* @param entry - 目录条目。
|
|
51
|
+
* @param args - 本次调用入参。
|
|
52
|
+
* @returns 缺席的必填入参名,按 schema 顺序。
|
|
53
|
+
*/
|
|
54
|
+
export function missingRequiredArgs(entry, args) {
|
|
55
|
+
const schema = entry.inputSchema;
|
|
56
|
+
if (!isRecord(schema) || !Array.isArray(schema.required))
|
|
57
|
+
return [];
|
|
58
|
+
return schema.required.filter((name) => typeof name === 'string' && (args === undefined || args[name] === undefined || args[name] === null));
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoBH,wBAAwB;AACxB,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAC7E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;IAChF,MAAM,OAAO,GAAmB,EAAE,CAAA;IAClC,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,EAAE;YAAE,SAAQ;QACrF,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI;YACvF,GAAG,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE;YAC9G,GAAG,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE;SAC7E,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAA;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB,EAAE,IAAY;IAC3D,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AAC3D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAmB,EAAE,IAAyC;IAChG,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAA;IAChC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAA;IACnE,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CACrD,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAA;AACxG,CAAC"}
|
package/lib/client.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 浏览器助手:造一个与宿主**同名同形**的 `ftshareData`(**消费方自己挂**)。
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* ctx.provide('ftshareData', createFtshareClient({ catalog, fetch: window.fetch.bind(window) }))
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
* 面板因此与宿主消费方写法一致:`state === 'denied'` 显示「去升级」,
|
|
9
|
+
* `state === 'empty'` 才是「没有数据」。两处刻意的差别(设计 §5.2):
|
|
10
|
+
*
|
|
11
|
+
* - **可调集合是目录全量**,不是模型那张策展接口表 —— 面板不受提示词预算约束;
|
|
12
|
+
* - **发现靠本地 `search()`**,是对目录的纯函数,不发网络。
|
|
13
|
+
*
|
|
14
|
+
* 这个模块是**只读子集**:不碰凭据、不碰网关地址、不维护接口表。它只认两样输入——
|
|
15
|
+
* 随包的目录与被注入的 `fetch`;`@dsh-ft/dsh-fft-ftshare/route` 与网关常量都不在这里的
|
|
16
|
+
* 依赖里,`scripts/test-crossplane.mjs` 会 grep 产物把这条钉住。
|
|
17
|
+
*
|
|
18
|
+
* 过桥时只做「形状收敛」,不解释内容:`notice.code` 可缺省(上游不给码就省略),
|
|
19
|
+
* `notice.upstream` 原样保留(面板要定位时用)。
|
|
20
|
+
* @module @dsh-ft/dsh-fft-ftshare/client
|
|
21
|
+
*/
|
|
22
|
+
import { type CatalogEntry } from './catalog.js';
|
|
23
|
+
import { type FtshareDataService } from './contract.js';
|
|
24
|
+
/** 浏览器侧可用的 `fetch` 子集;测试直接注入假实现。 */
|
|
25
|
+
export type FtshareFetch = (input: string, init: RequestInit) => Promise<Response>;
|
|
26
|
+
/** 客户端服务的调用选项。 */
|
|
27
|
+
export interface FtshareClientOptions {
|
|
28
|
+
/** 随包目录(`catalog.json` 的解析结果),决定哪些接口可调。 */
|
|
29
|
+
catalog: unknown;
|
|
30
|
+
/** 同源 fetch;浏览器传 `window.fetch.bind(window)`。 */
|
|
31
|
+
fetch: FtshareFetch;
|
|
32
|
+
/** 路由覆盖,默认 {@link FTSHARE_DATA_ROUTE}。 */
|
|
33
|
+
route?: string;
|
|
34
|
+
/** 成功结果的缓存时长;`denied` 与失败从不缓存。 */
|
|
35
|
+
ttlMs?: number;
|
|
36
|
+
/** 时钟,测试用来固定 `source.fetchedAt`。 */
|
|
37
|
+
now?: () => Date;
|
|
38
|
+
}
|
|
39
|
+
/** 客户端服务:与 {@link FtshareDataService} 同名同形,另加一个本地检索。 */
|
|
40
|
+
export interface FtshareClientService extends FtshareDataService {
|
|
41
|
+
/**
|
|
42
|
+
* 在目录里本地检索(纯函数,不发网络)。
|
|
43
|
+
* @param query - 关键词,大小写不敏感;空串返回前 `limit` 条。
|
|
44
|
+
* @param limit - 最多返回条数,默认 20。
|
|
45
|
+
* @returns 命中的目录条目。
|
|
46
|
+
*/
|
|
47
|
+
search(query: string, limit?: number): CatalogEntry[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 造一个客户端 `ftshareData`。
|
|
51
|
+
* @param options - 见 {@link FtshareClientOptions}。
|
|
52
|
+
* @returns 与宿主同名同形的服务,外加本地 `search()`。
|
|
53
|
+
*/
|
|
54
|
+
export declare function createFtshareClient(options: FtshareClientOptions): FtshareClientService;
|
|
55
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAgE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAC9G,OAAO,EAA8F,KAAK,kBAAkB,EAA4C,MAAM,eAAe,CAAA;AAE7L,oCAAoC;AACpC,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAElF,kBAAkB;AAClB,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAA;IAChB,iDAAiD;IACjD,KAAK,EAAE,YAAY,CAAA;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB;AAED,wDAAwD;AACxD,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAAA;CACtD;AAmDD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,oBAAoB,CA6FvF"}
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 浏览器助手:造一个与宿主**同名同形**的 `ftshareData`(**消费方自己挂**)。
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* ctx.provide('ftshareData', createFtshareClient({ catalog, fetch: window.fetch.bind(window) }))
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
* 面板因此与宿主消费方写法一致:`state === 'denied'` 显示「去升级」,
|
|
9
|
+
* `state === 'empty'` 才是「没有数据」。两处刻意的差别(设计 §5.2):
|
|
10
|
+
*
|
|
11
|
+
* - **可调集合是目录全量**,不是模型那张策展接口表 —— 面板不受提示词预算约束;
|
|
12
|
+
* - **发现靠本地 `search()`**,是对目录的纯函数,不发网络。
|
|
13
|
+
*
|
|
14
|
+
* 这个模块是**只读子集**:不碰凭据、不碰网关地址、不维护接口表。它只认两样输入——
|
|
15
|
+
* 随包的目录与被注入的 `fetch`;`@dsh-ft/dsh-fft-ftshare/route` 与网关常量都不在这里的
|
|
16
|
+
* 依赖里,`scripts/test-crossplane.mjs` 会 grep 产物把这条钉住。
|
|
17
|
+
*
|
|
18
|
+
* 过桥时只做「形状收敛」,不解释内容:`notice.code` 可缺省(上游不给码就省略),
|
|
19
|
+
* `notice.upstream` 原样保留(面板要定位时用)。
|
|
20
|
+
* @module @dsh-ft/dsh-fft-ftshare/client
|
|
21
|
+
*/
|
|
22
|
+
import { asCatalog, catalogEntryOf, missingRequiredArgs } from './catalog.js';
|
|
23
|
+
import { FTSHARE_DATA_ROUTE } from './contract.js';
|
|
24
|
+
/** 默认缓存时长:面板会连点,一次调用不值得重复出站;`denied` 例外。 */
|
|
25
|
+
const DEFAULT_TTL_MS = 60_000;
|
|
26
|
+
/** 默认检索条数上限。 */
|
|
27
|
+
const DEFAULT_SEARCH_LIMIT = 20;
|
|
28
|
+
/** 传输层失败(断网、超时、非 2xx、解析失败)统一落的结局。 */
|
|
29
|
+
const TRANSPORT_FAILURE = 'failed';
|
|
30
|
+
/** 四态集合;客户端只认这四个,别的一律当形状不符。 */
|
|
31
|
+
const STATES = new Set(['available', 'empty', 'denied', 'failed']);
|
|
32
|
+
/** 判断一个值是不是可当对象读的记录。 */
|
|
33
|
+
function isRecord(value) {
|
|
34
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
/** 读一个非空字符串;其余 undefined。 */
|
|
37
|
+
function asText(value) {
|
|
38
|
+
return typeof value === 'string' && value.trim() !== '' ? value : undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 收敛宿主过桥的 `notice`:只校验「有说明」,不要求「有码」。
|
|
42
|
+
* @param value - 过桥的 notice。
|
|
43
|
+
* @returns 收敛后的 notice;形状不符时 undefined。
|
|
44
|
+
*/
|
|
45
|
+
function normalizeNotice(value) {
|
|
46
|
+
if (!isRecord(value) || typeof value.message !== 'string')
|
|
47
|
+
return undefined;
|
|
48
|
+
const code = asText(value.code);
|
|
49
|
+
const requiredPlan = asText(value.requiredPlan);
|
|
50
|
+
const upgradeUrl = asText(value.upgradeUrl);
|
|
51
|
+
const upstream = isRecord(value.upstream) && typeof value.upstream.raw === 'string'
|
|
52
|
+
? {
|
|
53
|
+
raw: value.upstream.raw,
|
|
54
|
+
...typeof value.upstream.status === 'number' ? { status: value.upstream.status } : {},
|
|
55
|
+
fingerprint: typeof value.upstream.fingerprint === 'string' ? value.upstream.fingerprint : '',
|
|
56
|
+
}
|
|
57
|
+
: undefined;
|
|
58
|
+
return {
|
|
59
|
+
...code !== undefined ? { code } : {},
|
|
60
|
+
message: value.message,
|
|
61
|
+
...requiredPlan !== undefined ? { requiredPlan } : {},
|
|
62
|
+
...upgradeUrl !== undefined ? { upgradeUrl } : {},
|
|
63
|
+
retryable: value.retryable === true,
|
|
64
|
+
...upstream !== undefined ? { upstream } : {},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 造一个客户端 `ftshareData`。
|
|
69
|
+
* @param options - 见 {@link FtshareClientOptions}。
|
|
70
|
+
* @returns 与宿主同名同形的服务,外加本地 `search()`。
|
|
71
|
+
*/
|
|
72
|
+
export function createFtshareClient(options) {
|
|
73
|
+
const catalog = asCatalog(options.catalog);
|
|
74
|
+
const route = options.route ?? FTSHARE_DATA_ROUTE;
|
|
75
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
76
|
+
const now = options.now ?? (() => new Date());
|
|
77
|
+
const send = options.fetch;
|
|
78
|
+
const cache = new Map();
|
|
79
|
+
/** 本地就能定的失败:`source` 用客户端时钟,其余字段与宿主同形。 */
|
|
80
|
+
const failure = (code, message, retryable = false) => ({
|
|
81
|
+
ok: false,
|
|
82
|
+
state: TRANSPORT_FAILURE,
|
|
83
|
+
rows: [],
|
|
84
|
+
notice: { code, message, retryable },
|
|
85
|
+
source: { name: 'FTShare', fetchedAt: now().toISOString() },
|
|
86
|
+
});
|
|
87
|
+
/** 收敛远端返回;形状不对一律 undefined(由调用方落成 failed)。 */
|
|
88
|
+
const asCallResult = (value) => {
|
|
89
|
+
if (!isRecord(value))
|
|
90
|
+
return undefined;
|
|
91
|
+
const state = value.state;
|
|
92
|
+
if (typeof state !== 'string' || !STATES.has(state))
|
|
93
|
+
return undefined;
|
|
94
|
+
if (typeof value.ok !== 'boolean' || !Array.isArray(value.rows))
|
|
95
|
+
return undefined;
|
|
96
|
+
if (!isRecord(value.source))
|
|
97
|
+
return undefined;
|
|
98
|
+
const notice = value.notice;
|
|
99
|
+
const normalizedNotice = notice === undefined ? undefined : normalizeNotice(notice);
|
|
100
|
+
// notice 存在但形状不对(没有 message)时按形状不符处理,不吞掉失败原因。
|
|
101
|
+
if (notice !== undefined && normalizedNotice === undefined)
|
|
102
|
+
return undefined;
|
|
103
|
+
return {
|
|
104
|
+
ok: value.ok,
|
|
105
|
+
state: state,
|
|
106
|
+
rows: value.rows.filter(isRecord),
|
|
107
|
+
source: value.source,
|
|
108
|
+
...normalizedNotice === undefined ? {} : { notice: normalizedNotice },
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
async call(interfaceName, args = {}, callOptions) {
|
|
113
|
+
const entry = catalogEntryOf(catalog, interfaceName);
|
|
114
|
+
if (entry === undefined) {
|
|
115
|
+
return failure('unknown_interface', `接口 ${interfaceName} 不在 FTShare 目录里,不能调用`);
|
|
116
|
+
}
|
|
117
|
+
const missing = missingRequiredArgs(entry, args);
|
|
118
|
+
if (missing.length > 0)
|
|
119
|
+
return failure('missing_arguments', `缺必填入参:${missing.join('、')}`);
|
|
120
|
+
const key = `${interfaceName} ${JSON.stringify(args)}`;
|
|
121
|
+
const hit = cache.get(key);
|
|
122
|
+
if (hit !== undefined && Date.now() - hit.at < ttlMs)
|
|
123
|
+
return hit.result;
|
|
124
|
+
try {
|
|
125
|
+
const response = await send(route, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: { 'content-type': 'application/json' },
|
|
128
|
+
body: JSON.stringify({ interface: interfaceName, args }),
|
|
129
|
+
...callOptions?.signal === undefined ? {} : { signal: callOptions.signal },
|
|
130
|
+
});
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
const detail = await readErrorBody(response);
|
|
133
|
+
// 非 2xx 一律 failed:面板只需要一种方式渲染「取数失败」。
|
|
134
|
+
return failure(detail?.code ?? `http_${response.status}`, detail?.message ?? `FTShare 取数失败(HTTP ${response.status})`, true);
|
|
135
|
+
}
|
|
136
|
+
const result = asCallResult(await response.json());
|
|
137
|
+
if (result === undefined)
|
|
138
|
+
return failure('invalid_response', 'FTShare 底座返回的形状不对,无法渲染');
|
|
139
|
+
// denied 不缓存:用户可能正好在另一个标签页里改了订阅或 Key。
|
|
140
|
+
if (result.state !== 'denied')
|
|
141
|
+
cache.set(key, { at: Date.now(), result });
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return failure('transport_failed', `FTShare 取数失败:${error instanceof Error ? error.message : String(error)}`, true);
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
async listTools() {
|
|
149
|
+
// 面板不需要为清单再跑一趟网络:目录就是清单。
|
|
150
|
+
return catalog.entries.map(entry => ({
|
|
151
|
+
tool: entry.name,
|
|
152
|
+
title: entry.title,
|
|
153
|
+
...entry.description !== undefined ? { description: entry.description } : {},
|
|
154
|
+
...entry.inputSchema !== undefined ? { inputSchema: entry.inputSchema } : {},
|
|
155
|
+
}));
|
|
156
|
+
},
|
|
157
|
+
search(query, limit = DEFAULT_SEARCH_LIMIT) {
|
|
158
|
+
const needle = query.trim().toLowerCase();
|
|
159
|
+
const matched = needle === ''
|
|
160
|
+
? catalog.entries
|
|
161
|
+
: catalog.entries.filter(entry => entry.name.toLowerCase().includes(needle)
|
|
162
|
+
|| entry.title.toLowerCase().includes(needle)
|
|
163
|
+
|| (entry.description ?? '').toLowerCase().includes(needle));
|
|
164
|
+
return matched.slice(0, Math.max(0, limit));
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** 读路由的错误信封;读不出来就返回 undefined,由调用方给通用措辞。 */
|
|
169
|
+
async function readErrorBody(response) {
|
|
170
|
+
try {
|
|
171
|
+
const body = await response.json();
|
|
172
|
+
if (!isRecord(body) || !isRecord(body.error))
|
|
173
|
+
return undefined;
|
|
174
|
+
const code = typeof body.error.code === 'string' ? body.error.code : undefined;
|
|
175
|
+
const message = typeof body.error.message === 'string' ? body.error.message : undefined;
|
|
176
|
+
if (code === undefined || message === undefined)
|
|
177
|
+
return undefined;
|
|
178
|
+
return { code, message };
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dsh-ft/dsh-fft-ftshare/contract` —— FTShare 公用底座的冻结契约。
|
|
3
|
+
*
|
|
4
|
+
* 消费方只需要 `import type { FtshareDataService } from '@dsh-ft/dsh-fft-ftshare/contract'`,
|
|
5
|
+
* 不依赖本包的实现、传输层或 MCP SDK。本模块只有类型与常量,没有运行时依赖,
|
|
6
|
+
* 因此宿主插件与测试都能安全引用。
|
|
7
|
+
*
|
|
8
|
+
* 设计要点是 **四态归一**:FTShare 网关把「真的没有数据」「上游拒绝(401/403)」
|
|
9
|
+
* 「取数失败」三种情况都表现为「空」,调用方若只判断空数组就会把「拒绝」误读成
|
|
10
|
+
* 「没有数据」。{@link FtshareCallResult.state} 强制区分这四种结局。
|
|
11
|
+
*
|
|
12
|
+
* 失败说明({@link FtshareNotice})只**抄上游事实**:码、HTTP 状态、原文、重试提示。
|
|
13
|
+
* 本包不翻译、不归因、不编造 remedy;是不是配额、要不要升级,由原文与消费方判断。
|
|
14
|
+
* @module @dsh-ft/dsh-fft-ftshare/contract
|
|
15
|
+
*/
|
|
16
|
+
/** 服务在 cordis `Context` 上的名字,也是 `ctx.provide` 的键。 */
|
|
17
|
+
export declare const FTSHARE_DATA_SERVICE: "ftshareData";
|
|
18
|
+
/**
|
|
19
|
+
* 跨平面通路的精确路由:浏览器侧的 `createFtshareClient` POST 到这里,宿主侧的
|
|
20
|
+
* `registerFtshareRoute` 在这里应答。走 `connection.fetch` 的 `/api` 载体,因此
|
|
21
|
+
* Host/Origin 围栏与浏览器鉴权由载体负责。
|
|
22
|
+
*/
|
|
23
|
+
export declare const FTSHARE_DATA_ROUTE: "/api/ftshare-data";
|
|
24
|
+
/** 用户可升级订阅的页面,{@link FtshareNotice.upgradeUrl} 的默认值(仅 `denied` 带)。 */
|
|
25
|
+
export declare const FTSHARE_UPGRADE_URL: "https://ftai.chat/me/profile?tab=subscription";
|
|
26
|
+
/**
|
|
27
|
+
* 一次取数的四态结局。
|
|
28
|
+
*
|
|
29
|
+
* - `available`:调用成功且至少返回了一行;
|
|
30
|
+
* - `empty`:调用成功,且载荷里有**正向证据**(已知数据容器存在且为空数组);
|
|
31
|
+
* - `denied`:上游以 HTTP 401/403 拒绝—— **不是**「没有数据」;
|
|
32
|
+
* - `failed`:连接、超时、参数、上游 5xx 等其他失败,或成功信封无法识别。
|
|
33
|
+
*/
|
|
34
|
+
export type FtshareCallState = 'available' | 'empty' | 'denied' | 'failed';
|
|
35
|
+
/**
|
|
36
|
+
* 四态归一后的取数结果。
|
|
37
|
+
*
|
|
38
|
+
* 形状已冻结:字段只增不改,消费方按需读取。
|
|
39
|
+
*/
|
|
40
|
+
export interface FtshareCallResult {
|
|
41
|
+
/**
|
|
42
|
+
* 调用是否走通到「上游给出了答复」这一步:`available` 与 `empty` 为真,
|
|
43
|
+
* `denied` 与 `failed` 为假。它回答的是「通道是否可用」,不回答「有没有数据」——
|
|
44
|
+
* 后者看 {@link state}。把两者分开是有意的:只有 `empty` 才允许下游说
|
|
45
|
+
* 「没有数据」。
|
|
46
|
+
*/
|
|
47
|
+
ok: boolean;
|
|
48
|
+
/** 四态结局,见 {@link FtshareCallState}。 */
|
|
49
|
+
state: FtshareCallState;
|
|
50
|
+
/** 归一后的明细行;`available` 之外通常为空数组,绝不是权限判断依据。 */
|
|
51
|
+
rows: Record<string, unknown>[];
|
|
52
|
+
/** 需要向用户/模型解释的异常;成功时为 undefined。 */
|
|
53
|
+
notice?: FtshareNotice;
|
|
54
|
+
/** 取数来源与时点,用于在报告里给出处。 */
|
|
55
|
+
source: FtshareSource;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 上游失败的现场。
|
|
59
|
+
*
|
|
60
|
+
* 只放事实:网关说了什么、HTTP 状态是多少、同一类失败怎么聚在一起。不存
|
|
61
|
+
* 「原因」或「补救办法」——那两样会随上游文案一起失效。
|
|
62
|
+
*/
|
|
63
|
+
export interface FtshareUpstream {
|
|
64
|
+
/** 上游原文(剥掉 SDK 包装后的可读报文),最多 2 KB。 */
|
|
65
|
+
raw: string;
|
|
66
|
+
/** HTTP 状态:`Error.code`、信封 `upstream_code`,或文本里带分界的写法。 */
|
|
67
|
+
status?: number;
|
|
68
|
+
/** 失败指纹:同工具、同状态、同码、同归一文案的失败聚合用(数字已归一)。 */
|
|
69
|
+
fingerprint: string;
|
|
70
|
+
}
|
|
71
|
+
/** 面向用户/模型的异常说明。 */
|
|
72
|
+
export interface FtshareNotice {
|
|
73
|
+
/**
|
|
74
|
+
* 上游错误码,**照抄**(如 `UPSTREAM_UNAVAILABLE`);没有信封码但拿到 401/403 时,
|
|
75
|
+
* 用 HTTP 状态字符串 `"401"`/`"403"`(事实,不是编造)。本地失败码(目录外接口、
|
|
76
|
+
* 缺参、传输失败)由调用方自己给。
|
|
77
|
+
*/
|
|
78
|
+
code?: string;
|
|
79
|
+
/** 面向展示的说明:上游原文(`上游原文:…`)或本地失败的事实描述。 */
|
|
80
|
+
message: string;
|
|
81
|
+
/** 上游信封真给了所需档位才写,不做推断。 */
|
|
82
|
+
requiredPlan?: string;
|
|
83
|
+
/** 升级入口,只有 `denied` 带。 */
|
|
84
|
+
upgradeUrl?: string;
|
|
85
|
+
/** 是否值得原样重试:上游 hint,缺失时按 HTTP/传输事实推导。 */
|
|
86
|
+
retryable: boolean;
|
|
87
|
+
/** 上游失败的现场;本地失败(目录外、缺参)没有。 */
|
|
88
|
+
upstream?: FtshareUpstream;
|
|
89
|
+
}
|
|
90
|
+
/** 取数来源与时点。 */
|
|
91
|
+
export interface FtshareSource {
|
|
92
|
+
name: 'FTShare';
|
|
93
|
+
/** ISO-8601 取数时点。 */
|
|
94
|
+
fetchedAt: string;
|
|
95
|
+
}
|
|
96
|
+
/** 单次调用的可选项。 */
|
|
97
|
+
export interface FtshareCallOptions {
|
|
98
|
+
/** 调用方取消信号;已取消时立刻返回 `failed`,不发起连接。 */
|
|
99
|
+
signal?: AbortSignal;
|
|
100
|
+
}
|
|
101
|
+
/** 上游 FTShare MCP 网关暴露的一个工具。 */
|
|
102
|
+
export interface FtshareToolInfo {
|
|
103
|
+
/** 上游工具名,例如 `ft_v1_finance_income`。 */
|
|
104
|
+
tool: string;
|
|
105
|
+
/** 上游标题;上游未提供时回落为工具名。 */
|
|
106
|
+
title: string;
|
|
107
|
+
/** 上游说明原文;随包目录的两份产物都由它生成。 */
|
|
108
|
+
description?: string;
|
|
109
|
+
/** 上游入参 JSON Schema 原文;`ftshare_call` 的本地预校验用它。 */
|
|
110
|
+
inputSchema?: unknown;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `ctx.ftshareData`:FTShare 取数的唯一入口。
|
|
114
|
+
*
|
|
115
|
+
* 实现由本包的宿主入口(裸包名 `@dsh-ft/dsh-fft-ftshare`)`provide`,且必须挂在
|
|
116
|
+
* **全局平面**:preset 内的 `provide` 会直接抛
|
|
117
|
+
* `Preset services require isolate realms`(`agent-preset-registry` 的
|
|
118
|
+
* `mount.ts`),而加上 isolate 之后服务会变成 preset 私有,preset 之外的插件再也
|
|
119
|
+
* inject 不到。消费方因此统一用 `inject: ['ftshareData']` 声明依赖。
|
|
120
|
+
*/
|
|
121
|
+
export interface FtshareDataService {
|
|
122
|
+
/**
|
|
123
|
+
* 调一个上游 FTShare 接口并把结果归一成 {@link FtshareCallResult}。
|
|
124
|
+
* @param interfaceName - 上游接口名,例如 `ft_v1_finance_income`。
|
|
125
|
+
* @param args - 上游接口入参,原样透传。
|
|
126
|
+
* @param options - 取消信号等。
|
|
127
|
+
* @returns 归一后的结果;本方法不抛异常,权限与失败都走 `state`/`notice`。
|
|
128
|
+
*/
|
|
129
|
+
call(interfaceName: string, args?: Record<string, unknown>, options?: FtshareCallOptions): Promise<FtshareCallResult>;
|
|
130
|
+
/**
|
|
131
|
+
* 上游当前可用的接口清单(名字、标题、说明、入参 schema)。
|
|
132
|
+
*
|
|
133
|
+
* 自己触发拉取并做短 TTL 缓存:不依赖「先调通一次」才有人填缓存。上游不可达时
|
|
134
|
+
* 返回上一次的清单,没有则返回空数组——清单是增强信息,不该让取数失败。
|
|
135
|
+
*/
|
|
136
|
+
listTools(): Promise<readonly FtshareToolInfo[]>;
|
|
137
|
+
}
|
|
138
|
+
declare module '@deepseek-ai/cordis' {
|
|
139
|
+
interface Context {
|
|
140
|
+
ftshareData: FtshareDataService;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,EAAG,aAAsB,CAAA;AAE1D;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAG,mBAA4B,CAAA;AAE9D,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,EAAG,+CAAwD,CAAA;AAE3F;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE1E;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,EAAE,EAAE,OAAO,CAAA;IACX,uCAAuC;IACvC,KAAK,EAAE,gBAAgB,CAAA;IACvB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IAC/B,oCAAoC;IACpC,MAAM,CAAC,EAAE,aAAa,CAAA;IACtB,yBAAyB;IACzB,MAAM,EAAE,aAAa,CAAA;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAA;IACX,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,oBAAoB;AACpB,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAA;IACf,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0BAA0B;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAA;IAClB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAED,eAAe;AACf,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAA;IACf,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,gBAAgB;AAChB,MAAM,WAAW,kBAAkB;IACjC,uCAAuC;IACvC,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,gCAAgC;AAChC,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mDAAmD;IACnD,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACrH;;;;;OAKG;IACH,SAAS,IAAI,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAA;CACjD;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,WAAW,EAAE,kBAAkB,CAAA;KAChC;CACF"}
|
package/lib/contract.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dsh-ft/dsh-fft-ftshare/contract` —— FTShare 公用底座的冻结契约。
|
|
3
|
+
*
|
|
4
|
+
* 消费方只需要 `import type { FtshareDataService } from '@dsh-ft/dsh-fft-ftshare/contract'`,
|
|
5
|
+
* 不依赖本包的实现、传输层或 MCP SDK。本模块只有类型与常量,没有运行时依赖,
|
|
6
|
+
* 因此宿主插件与测试都能安全引用。
|
|
7
|
+
*
|
|
8
|
+
* 设计要点是 **四态归一**:FTShare 网关把「真的没有数据」「上游拒绝(401/403)」
|
|
9
|
+
* 「取数失败」三种情况都表现为「空」,调用方若只判断空数组就会把「拒绝」误读成
|
|
10
|
+
* 「没有数据」。{@link FtshareCallResult.state} 强制区分这四种结局。
|
|
11
|
+
*
|
|
12
|
+
* 失败说明({@link FtshareNotice})只**抄上游事实**:码、HTTP 状态、原文、重试提示。
|
|
13
|
+
* 本包不翻译、不归因、不编造 remedy;是不是配额、要不要升级,由原文与消费方判断。
|
|
14
|
+
* @module @dsh-ft/dsh-fft-ftshare/contract
|
|
15
|
+
*/
|
|
16
|
+
/** 服务在 cordis `Context` 上的名字,也是 `ctx.provide` 的键。 */
|
|
17
|
+
export const FTSHARE_DATA_SERVICE = 'ftshareData';
|
|
18
|
+
/**
|
|
19
|
+
* 跨平面通路的精确路由:浏览器侧的 `createFtshareClient` POST 到这里,宿主侧的
|
|
20
|
+
* `registerFtshareRoute` 在这里应答。走 `connection.fetch` 的 `/api` 载体,因此
|
|
21
|
+
* Host/Origin 围栏与浏览器鉴权由载体负责。
|
|
22
|
+
*/
|
|
23
|
+
export const FTSHARE_DATA_ROUTE = '/api/ftshare-data';
|
|
24
|
+
/** 用户可升级订阅的页面,{@link FtshareNotice.upgradeUrl} 的默认值(仅 `denied` 带)。 */
|
|
25
|
+
export const FTSHARE_UPGRADE_URL = 'https://ftai.chat/me/profile?tab=subscription';
|
|
26
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,aAAsB,CAAA;AAE1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,mBAA4B,CAAA;AAE9D,sEAAsE;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,+CAAwD,CAAA"}
|
package/lib/errors.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 上游失败的观测与四态归一的**薄**层。
|
|
3
|
+
*
|
|
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} 识别,同样不是文案猜测。
|
|
16
|
+
* @module @dsh-ft/dsh-fft-ftshare/errors
|
|
17
|
+
*/
|
|
18
|
+
import { type FtshareNotice } from './contract.js';
|
|
19
|
+
/** 失败从哪条通道来:成功信封内、HTTP 非 2xx、传输层、纯文本。 */
|
|
20
|
+
export type FailureSurface = 'envelope' | 'http' | 'transport' | 'text';
|
|
21
|
+
/** 一次失败的观测事实。只记事实,不下结论。 */
|
|
22
|
+
export interface UpstreamFailure {
|
|
23
|
+
/** 观测通道。 */
|
|
24
|
+
surface: FailureSurface;
|
|
25
|
+
/** 剥掉 SDK 包装后的可读文本;单行。 */
|
|
26
|
+
message: string;
|
|
27
|
+
/** 上游信封里的码,原样大小写;不猜、不改写。 */
|
|
28
|
+
code?: string;
|
|
29
|
+
/** HTTP 状态:`Error.code`、信封 `upstream_code`,或文本里有分界的写法。 */
|
|
30
|
+
status?: number;
|
|
31
|
+
/** 上游自己的 retryable hint;只有信封里有,只作提示。 */
|
|
32
|
+
retryable?: boolean;
|
|
33
|
+
/** 上游真给了才带上。 */
|
|
34
|
+
requiredPlan?: string;
|
|
35
|
+
/** 原始报文(未截断),用于指纹与 fixture 草稿。 */
|
|
36
|
+
raw: string;
|
|
37
|
+
/** 稳定短指纹,见 {@link fingerprintOf}。 */
|
|
38
|
+
fingerprint: string;
|
|
39
|
+
}
|
|
40
|
+
/** 判断一个值是不是可当对象读的记录。 */
|
|
41
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
42
|
+
/** 读一个非空字符串;其余一律 undefined。 */
|
|
43
|
+
export declare function asText(value: unknown): string | undefined;
|
|
44
|
+
/** 不依赖 `node:crypto` 的稳定指纹:FNV-1a 64 位,十六进制 16 位。 */
|
|
45
|
+
export declare function hashText(text: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* 失败指纹:同工具、同状态、同码、同归一文案的失败聚合用。
|
|
48
|
+
*
|
|
49
|
+
* 文案里的数字会归一成 `#`,否则「请等待 N 小时」会让每次失败都算新指纹。
|
|
50
|
+
* @param status - HTTP 状态。
|
|
51
|
+
* @param code - 上游码。
|
|
52
|
+
* @param message - 可读文案。
|
|
53
|
+
* @returns 16 位十六进制指纹。
|
|
54
|
+
*/
|
|
55
|
+
export declare function fingerprintOf(status: number | undefined, code: string | undefined, message: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* 把一次抛出物归一成 {@link UpstreamFailure}(只记事实)。
|
|
58
|
+
* @param value - 抛出物或上游返回的文本。
|
|
59
|
+
* @returns 观测事实。
|
|
60
|
+
*/
|
|
61
|
+
export declare function observeFailure(value: unknown): UpstreamFailure;
|
|
62
|
+
/**
|
|
63
|
+
* 把一次失败折成结局与提示。
|
|
64
|
+
*
|
|
65
|
+
* 顺序即语义:`401/403` 是上游拒绝 → `denied`;其余一律 `failed`。不再按文案细分
|
|
66
|
+
* 配额/凭据/档位——原文已经带出这些信息。
|
|
67
|
+
* @param error - 抛出物。
|
|
68
|
+
* @param tool - 上游工具名(只用于调用方日志,不写进提示)。
|
|
69
|
+
* @returns 结局与可直接展示的提示。
|
|
70
|
+
*/
|
|
71
|
+
export declare function classifyFailure(error: unknown, tool: string): {
|
|
72
|
+
state: 'denied' | 'failed';
|
|
73
|
+
notice: FtshareNotice;
|
|
74
|
+
failure: UpstreamFailure;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* 从「调用成功但信封说自己无权」的载荷里读出权限提示。
|
|
78
|
+
*
|
|
79
|
+
* 部分网关把订阅门禁放在成功信封的 `metadata.permissions` 或 `permission` 里,而不是
|
|
80
|
+
* 返回错误;只看空数组就会把这种情况读成「没有数据」。这里只认**显式标记**(
|
|
81
|
+
* `access_denied` 之类布尔标记)或 `401/403` 状态码,不按文案猜。
|
|
82
|
+
* @param tool - 上游工具名(只用于调用方日志,不写进提示)。
|
|
83
|
+
* @param value - 上游返回的结构化载荷。
|
|
84
|
+
* @returns 权限提示;没有标记时 undefined。
|
|
85
|
+
*/
|
|
86
|
+
export declare function permissionNoticeFromPayload(tool: string, value: unknown): FtshareNotice | undefined;
|
|
87
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAuB,KAAK,aAAa,EAAwB,MAAM,eAAe,CAAA;AAE7F,yCAAyC;AACzC,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAA;AAEvE,2BAA2B;AAC3B,MAAM,WAAW,eAAe;IAC9B,YAAY;IACZ,OAAO,EAAE,cAAc,CAAA;IACvB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,4BAA4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gBAAgB;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAA;CACpB;AAoBD,wBAAwB;AACxB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,+BAA+B;AAC/B,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAEzD;AAaD,qDAAqD;AACrD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG3G;AA8DD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CA+B9D;AAiBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,eAAe,CAAA;CAAE,CA6B7I;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CA+BnG"}
|