@anjianshi/utils 3.2.1 → 3.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/package.json +4 -4
- package/request/client.d.ts +11 -3
- package/request/client.js +45 -5
- package/request/error.d.ts +11 -1
- package/request/instance.d.ts +1 -1
- package/request/options.d.ts +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anjianshi/utils",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Common JavaScript Utils",
|
|
5
5
|
"homepage": "https://github.com/anjianshi/js-packages/utils",
|
|
6
6
|
"bugs": {
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"redis": "^5.5.6",
|
|
32
32
|
"typescript": "^5.8.3",
|
|
33
33
|
"vconsole": "^3.15.1",
|
|
34
|
-
"@anjianshi/presets-prettier": "3.0.5",
|
|
35
|
-
"@anjianshi/presets-eslint-react": "6.0.0",
|
|
36
|
-
"@anjianshi/presets-eslint-node": "6.0.0",
|
|
37
34
|
"@anjianshi/presets-eslint-base": "6.0.0",
|
|
35
|
+
"@anjianshi/presets-eslint-node": "6.0.0",
|
|
38
36
|
"@anjianshi/presets-eslint-typescript": "6.0.0",
|
|
37
|
+
"@anjianshi/presets-eslint-react": "6.0.0",
|
|
38
|
+
"@anjianshi/presets-prettier": "3.0.5",
|
|
39
39
|
"@anjianshi/presets-typescript": "3.2.5"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
package/request/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Result, type Failed } from '../lang/result.js';
|
|
1
|
+
import { type Result, type Success, type Failed } from '../lang/result.js';
|
|
2
2
|
import { type Logger } from '../logging/index.js';
|
|
3
3
|
import { type RequestFailed } from './error.js';
|
|
4
4
|
import type { Options, PredefinedOptions, FormattedOptions } from './options.js';
|
|
@@ -10,8 +10,8 @@ export declare abstract class BaseRequestClient<FailedT extends Failed> {
|
|
|
10
10
|
loggerName?: string;
|
|
11
11
|
});
|
|
12
12
|
/** 生成一个快捷方式函数,调用它相当于调用 client.request() */
|
|
13
|
-
asFunction(): <T>(inputUrl: string, inputOptions?: Options
|
|
14
|
-
request<T>(inputUrl: string, inputOptions?: Options
|
|
13
|
+
asFunction(): <T>(inputUrl: string, inputOptions?: Options) => Promise<Result<T, FailedT>>;
|
|
14
|
+
request<T>(inputUrl: string, inputOptions?: Options): Promise<Result<T, FailedT>>;
|
|
15
15
|
formatOptions(input: Options): Promise<FormattedOptions>;
|
|
16
16
|
/** 请求发起前调用此方法补充 Headers 内容 */
|
|
17
17
|
protected getHeaders(options: FormattedOptions, inputOptions: Options): Record<string, string> | undefined | Promise<Record<string, string> | undefined>;
|
|
@@ -28,6 +28,14 @@ export declare abstract class BaseRequestClient<FailedT extends Failed> {
|
|
|
28
28
|
protected handleError(result: RequestFailed): FailedT;
|
|
29
29
|
/** 生成符合 FailedT 约定的失败结果 */
|
|
30
30
|
protected abstract makeFailedResult(result: RequestFailed): FailedT;
|
|
31
|
+
/**
|
|
32
|
+
* 对请求成功得到的数据进行格式化。
|
|
33
|
+
*
|
|
34
|
+
* - 此步骤在 applyInputFormat() 之前运行,子类可在此进行一些通用的格式化工作,
|
|
35
|
+
* - 输出 Success 或 Failed 结果均可,但输出的 Failed 结果不会交给 makeFailedResult() 进行格式化,需自行保证符合 FailedT 约定。
|
|
36
|
+
* - 若此方法抛出异常,会生成 FormatResponseDataFailed,此错误会照常交给 makeFailedResult() 处理。
|
|
37
|
+
*/
|
|
38
|
+
protected formatSuccessResult(result: Success<unknown>): Result<unknown, FailedT>;
|
|
31
39
|
}
|
|
32
40
|
/** 默认的 RequestClient 实现。出现错误时,会把错误对象原样放入错误信息中。 */
|
|
33
41
|
export declare class RequestClient extends BaseRequestClient<RequestFailed> {
|
package/request/client.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 可通过继承子类来扩展其功能。
|
|
4
4
|
*/
|
|
5
5
|
import pick from 'lodash/pick.js';
|
|
6
|
-
import { success, failed,
|
|
6
|
+
import { success, failed, formatFailed, exceptionToFailed, } from '../lang/result.js';
|
|
7
7
|
import { getLogger } from '../logging/index.js';
|
|
8
8
|
import { combineUrl } from '../url.js';
|
|
9
9
|
/** 此基类不可直接使用,因其未对错误格式进行具体约定 */
|
|
@@ -38,7 +38,7 @@ export class BaseRequestClient {
|
|
|
38
38
|
}
|
|
39
39
|
catch (error) {
|
|
40
40
|
// 失败情形“手动取消”
|
|
41
|
-
if (manualSignal
|
|
41
|
+
if (manualSignal?.aborted && manualSignal.reason === error) {
|
|
42
42
|
const reason = error instanceof DOMException && error.name === 'AbortError' ? null : error;
|
|
43
43
|
return this.handleError(failed('Request Aborted', 'RequestAborted', { options, reason }));
|
|
44
44
|
}
|
|
@@ -52,6 +52,7 @@ export class BaseRequestClient {
|
|
|
52
52
|
// 失败情形“失败状态码”
|
|
53
53
|
if (!response.status.toString().startsWith('2')) {
|
|
54
54
|
// 此时服务端仍可能输出一些内容,试着解析出来
|
|
55
|
+
// 但因此情况下后端输出数据往往和成功时不同,因此不调用用户提供的 format() 函数来格式化
|
|
55
56
|
const responseDataRes = await this.parseResponse(options, response);
|
|
56
57
|
const responseData = responseDataRes.success ? responseDataRes.data : undefined;
|
|
57
58
|
return this.handleError(failed('Non-Success Status', 'NonSuccessStatus', {
|
|
@@ -62,15 +63,43 @@ export class BaseRequestClient {
|
|
|
62
63
|
}));
|
|
63
64
|
}
|
|
64
65
|
// 解析响应内容
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
let result = await this.parseResponse(options, response);
|
|
67
|
+
// 格式化响应数据
|
|
68
|
+
if (result.success) {
|
|
69
|
+
try {
|
|
70
|
+
result = this.formatSuccessResult(result);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return this.handleError(failed('Format Response Data Failed', 'FormatResponseDataFailed', {
|
|
74
|
+
options,
|
|
75
|
+
originalError: error,
|
|
76
|
+
response,
|
|
77
|
+
responseData: result.data,
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (result.success && format) {
|
|
82
|
+
try {
|
|
83
|
+
const formattedData = format(result.data);
|
|
84
|
+
result = success(formattedData);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return this.handleError(failed('Format Response Data By options.format Failed', 'FormatResponseDataFailed', {
|
|
88
|
+
options,
|
|
89
|
+
originalError: error,
|
|
90
|
+
response,
|
|
91
|
+
responseData: result.data,
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
67
96
|
}
|
|
68
97
|
// -------------------------------
|
|
69
98
|
// 请求预处理
|
|
70
99
|
// -------------------------------
|
|
71
100
|
async formatOptions(input) {
|
|
72
101
|
const predefined = this.prefefinedOptions;
|
|
73
|
-
const { urlPrefix = predefined.urlPrefix ?? '', url: rawUrl, query = {}, method = predefined.method ?? 'GET', headers: rawHeaders = {}, body: rawBody = null, data, timeout = predefined.timeout ?? 0, binary = false, signal, } = input;
|
|
102
|
+
const { urlPrefix = predefined.urlPrefix ?? '', url: rawUrl, query = {}, method = predefined.method ?? 'GET', headers: rawHeaders = {}, body: rawBody = null, data, timeout = predefined.timeout ?? 0, binary = false, signal, format, } = input;
|
|
74
103
|
const headers = {
|
|
75
104
|
...(predefined.headers ?? {}),
|
|
76
105
|
...rawHeaders,
|
|
@@ -94,6 +123,7 @@ export class BaseRequestClient {
|
|
|
94
123
|
timeout,
|
|
95
124
|
binary,
|
|
96
125
|
signal,
|
|
126
|
+
format,
|
|
97
127
|
};
|
|
98
128
|
Object.assign(options.headers, await this.getHeaders(options, input));
|
|
99
129
|
return options;
|
|
@@ -164,6 +194,16 @@ export class BaseRequestClient {
|
|
|
164
194
|
this.logger.error(result.code, info);
|
|
165
195
|
return this.makeFailedResult(result);
|
|
166
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* 对请求成功得到的数据进行格式化。
|
|
199
|
+
*
|
|
200
|
+
* - 此步骤在 applyInputFormat() 之前运行,子类可在此进行一些通用的格式化工作,
|
|
201
|
+
* - 输出 Success 或 Failed 结果均可,但输出的 Failed 结果不会交给 makeFailedResult() 进行格式化,需自行保证符合 FailedT 约定。
|
|
202
|
+
* - 若此方法抛出异常,会生成 FormatResponseDataFailed,此错误会照常交给 makeFailedResult() 处理。
|
|
203
|
+
*/
|
|
204
|
+
formatSuccessResult(result) {
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
167
207
|
}
|
|
168
208
|
/** 默认的 RequestClient 实现。出现错误时,会把错误对象原样放入错误信息中。 */
|
|
169
209
|
export class RequestClient extends BaseRequestClient {
|
package/request/error.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { type Failed } from '../lang/result.js';
|
|
10
10
|
import { type FormattedOptions } from './options.js';
|
|
11
|
-
export type RequestFailed = SendRequestFailed | RequestAborted | RequestTimedOut | NonSuccessStatus | ParseResponseBodyFailed;
|
|
11
|
+
export type RequestFailed = SendRequestFailed | RequestAborted | RequestTimedOut | NonSuccessStatus | ParseResponseBodyFailed | FormatResponseDataFailed;
|
|
12
12
|
export interface RequestFailedInfo {
|
|
13
13
|
options: FormattedOptions;
|
|
14
14
|
}
|
|
@@ -46,3 +46,13 @@ export interface ParseResponseBodyFailedInfo extends RequestFailedInfo {
|
|
|
46
46
|
/** Response 对象 */
|
|
47
47
|
response: Response;
|
|
48
48
|
}
|
|
49
|
+
/** 响应体解析失败 */
|
|
50
|
+
export type FormatResponseDataFailed = Failed<'FormatResponseDataFailed', FormatResponseDataFailedInfo>;
|
|
51
|
+
export interface FormatResponseDataFailedInfo extends RequestFailedInfo {
|
|
52
|
+
/** 原始错误对象 */
|
|
53
|
+
originalError: unknown;
|
|
54
|
+
/** Response 对象 */
|
|
55
|
+
response: Response;
|
|
56
|
+
/** 未经格式化的响应数据 */
|
|
57
|
+
responseData: unknown;
|
|
58
|
+
}
|
package/request/instance.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { RequestClient } from './client.js';
|
|
2
2
|
/** 提供一个即取即用的 RequestClient 实例及其函数版本 */
|
|
3
3
|
export declare const client: RequestClient;
|
|
4
|
-
export declare const request: <T>(inputUrl: string, inputOptions?: import("./options.js").Options
|
|
4
|
+
export declare const request: <T>(inputUrl: string, inputOptions?: import("./options.js").Options) => Promise<import("../index.js").Result<T, import("./error.js").RequestFailed>>;
|
package/request/options.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 请求参数
|
|
3
3
|
*/
|
|
4
|
-
export interface Options
|
|
4
|
+
export interface Options {
|
|
5
5
|
urlPrefix?: string;
|
|
6
6
|
url?: string;
|
|
7
7
|
query?: Record<string, string | number | undefined>;
|
|
@@ -19,7 +19,8 @@ export interface Options<T = unknown> {
|
|
|
19
19
|
timeout?: number;
|
|
20
20
|
/** 可通过此信号手动终止请求 */
|
|
21
21
|
signal?: AbortSignal;
|
|
22
|
-
|
|
22
|
+
/** 对请求成功时得到的数据进行最终格式化 */
|
|
23
|
+
format?: (responseData: any) => any;
|
|
23
24
|
}
|
|
24
25
|
/** 可预指定的请求参数 */
|
|
25
26
|
export type PredefinedOptions = Pick<Options, 'urlPrefix' | 'method' | 'headers' | 'timeout'>;
|