fun: upgrade TS client interceptors to fun-client parity (v1.2.0)

- request interceptor now receives the mutable per-request state map (token injection)
- response interceptor may return a replacement result (central token swap / error handling)
- fetch failures normalized to result status=4 (network error) instead of throwing
- stream requests also pass through request interceptors with state copy
This commit is contained in:
2026-08-20 12:09:43 +08:00
parent 05af3dc825
commit e6c6f68a3a
+24 -17
View File
@@ -11,17 +11,22 @@ func (ctx templateTs) genClientTemplate() string {
status: number; status: number;
}; };
export type resultStatus = 0 | 1 | 2 | 4 | 5;
// 0 成功;1 框架错误;2 业务错误;4 网络错误;5 超时
export type RequestInterceptor = ( export type RequestInterceptor = (
serviceName: string, serviceName: string,
methodName: string, methodName: string,
dto: any state: Record<string, string>,
dto?: any
) => Promise<void> | void; ) => Promise<void> | void;
// 返回新 result 将替换原结果继续向下传递(可用于集中换 token / 错误处理)
export type ResponseInterceptor = ( export type ResponseInterceptor = (
serviceName: string, serviceName: string,
methodName: string, methodName: string,
result: result<any> result: result<any>
) => Promise<void> | void; ) => Promise<result<any> | void> | result<any> | void;
export class Client { export class Client {
private url: string; private url: string;
@@ -46,26 +51,27 @@ export class Client {
} }
async request<T>(serviceName: string, methodName: string, dto?: any): Promise<result<T>> { async request<T>(serviceName: string, methodName: string, dto?: any): Promise<result<T>> {
const state: Record<string, string> = { ...this.state };
for (const i of this.requestInterceptors) { for (const i of this.requestInterceptors) {
await i(serviceName, methodName, dto); await i(serviceName, methodName, state, dto);
} }
let out: result<T>;
try {
const res = await fetch(this.url + "/cell", { const res = await fetch(this.url + "/cell", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ serviceName, methodName, data: dto, ...(Object.keys(this.state).length ? { state: this.state } : {}) }), body: JSON.stringify({ serviceName, methodName, data: dto, ...(Object.keys(state).length ? { state } : {}) }),
}); });
const out = (await res.json()) as result<T>; out = (await res.json()) as result<T>;
const anyResult: result<any> = { } catch (e: any) {
id: out.id, out = { status: 4, msg: (e && e.message) || "网络错误" } as result<T>;
code: out.code,
data: out.data,
msg: out.msg,
status: out.status,
};
for (const i of this.responseInterceptors) {
await i(serviceName, methodName, anyResult);
} }
return out; let cur: result<any> = out as result<any>;
for (const i of this.responseInterceptors) {
const replaced = await i(serviceName, methodName, cur);
if (replaced) cur = replaced;
}
return cur as result<T>;
} }
async stream<T>( async stream<T>(
@@ -74,13 +80,14 @@ export class Client {
dto: any | undefined, dto: any | undefined,
onMessage: (data: T) => void onMessage: (data: T) => void
): Promise<void> { ): Promise<void> {
const state: Record<string, string> = { ...this.state };
for (const i of this.requestInterceptors) { for (const i of this.requestInterceptors) {
await i(serviceName, methodName, dto); await i(serviceName, methodName, state, dto);
} }
const res = await fetch(this.url + "/cell", { const res = await fetch(this.url + "/cell", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ serviceName, methodName, data: dto, ...(Object.keys(this.state).length ? { state: this.state } : {}) }), body: JSON.stringify({ serviceName, methodName, data: dto, ...(Object.keys(state).length ? { state } : {}) }),
}); });
if (!res.ok) return; if (!res.ok) return;
const anyResult: result<any> = { status: 0 }; const anyResult: result<any> = { status: 0 };