@zwa73/utils 1.0.201 → 1.0.203

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/src/UtilCom.ts DELETED
@@ -1,457 +0,0 @@
1
- import { AnyString,JToken, MPromise, PartialOption, StatusVerifyFn } from "@/src/UtilInterfaces";
2
- import http from 'http';
3
- import https from 'https';
4
- import { SLogger } from "@/src/UtilLogger";
5
- import { PromiseRetries, UtilFunc } from "@/src/UtilFunctions";
6
- import qs from "querystring";
7
- import { Success } from "./UtilSymbol";
8
- import FormData from "form-data";
9
- import HttpProxyAgent from "http-proxy-agent";
10
- import HttpsProxyAgent from "https-proxy-agent";
11
-
12
- /**网络请求返回值 */
13
- export type RequestResult<T> = {
14
- /**响应头 */
15
- headers: http.IncomingHttpHeaders;
16
- /**响应状态码 */
17
- statusCode?: number;
18
- /**响应数据 */
19
- data: T;
20
- }
21
-
22
- /**网络请求选项 */
23
- export type RequestOption = {
24
- /**请求协议 */
25
- protocol: 'http:'|'https:';
26
- /**超时时间/毫秒 最小为10_000 默认无限 */
27
- timeout?:number;
28
- /**请求域名 */
29
- hostname: string;
30
- /**请求路径 */
31
- path?: string;
32
- /**请求方式 post 为 */
33
- method: 'POST'|'GET';
34
- /**端口 */
35
- port?:number;
36
- /**请求头 */
37
- headers?: {
38
- /**内容类型 */
39
- 'Content-Type'?: 'application/json'|'multipart/form-data'|AnyString;
40
- /**内容长度 一般无需填写 应为buffer长度而非字符串长度 */
41
- 'Content-Length'?: number;
42
- };
43
- }&http.RequestOptions;
44
-
45
- /**getquery请求所允许的数据 */
46
- export type QueryRequestData = NodeJS.Dict<
47
- | string
48
- | number
49
- | boolean
50
- | readonly string[]
51
- | readonly number[]
52
- | readonly boolean[]
53
- | null
54
- >;
55
-
56
- /**请求处理函数 需调用req.end() */
57
- export type RequestProcFn = ((req:http.ClientRequest)=>MPromise<void>);
58
- /**数据处理函数 */
59
- export type RequestReduceFn<T> = (acc:T,data:string)=>MPromise<T>;
60
-
61
-
62
- const AcceptTypeList = ["json","string"]as const;
63
- /**可用的接受类型 */
64
- type AcceptType = typeof AcceptTypeList[number];
65
- const SendTypeList = ["json","query","formData","none"] as const;
66
- /**可用的发送类型 */
67
- type SendType = typeof SendTypeList[number];
68
-
69
- /**accept处理数据 */
70
- type AcceptProc<D,T> = {
71
- init :D;
72
- reduce:RequestReduceFn<D>,
73
- parse :(result:RequestResult<D>|undefined)=>MPromise<T>
74
- }
75
- /**send处理数据 */
76
- type SendProc<T extends any[]> = {
77
- proc:(opt:RequestOption,...args:T)=>MPromise<RequestProcFn>,
78
- }
79
- const SendNoneProc:SendProc<[]> = {
80
- proc:(opt)=>(req)=>void req.end()
81
- }
82
- const AcceptStringProc:AcceptProc<string,RequestResult<string>|undefined> = {
83
- init :'',
84
- reduce:(acc:string,dat:string)=>acc+dat,
85
- parse :(result:RequestResult<string>|undefined)=>result
86
- }
87
-
88
- /**send处理的参数 */
89
- type SendParams<T extends SendProc<any>> = T extends SendProc<infer T>
90
- ? T : never;
91
- /**accept处理的结果 */
92
- type ParseResult<D extends AcceptProc<any,any>> = D extends AcceptProc<any,infer T>
93
- ? Awaited<T> : never;
94
-
95
- /**网络请求工具 */
96
- export class UtilCom<
97
- D extends Partial<RequestOption>&Required<Pick<RequestOption,'protocol'>>,
98
- S extends SendProc<any>,
99
- A extends AcceptProc<any,any>,
100
- >{
101
- private constructor(private _data:D,private _send:S, private _accept:A){}
102
-
103
- //#region 流式创建
104
- /**设为https请求 */
105
- static https(){
106
- return new UtilCom({protocol:'https:'} as const,SendNoneProc,AcceptStringProc);
107
- }
108
- /**设为http请求 */
109
- static http(){
110
- return new UtilCom({protocol:'http:'} as const,SendNoneProc,AcceptStringProc);
111
- }
112
- /**设为get方式的请求 */
113
- get(){
114
- this._data.method = 'GET';
115
- return this as any as UtilCom<D & {method:'GET'},S,A>;
116
- }
117
- /**设为Post方式的请求 */
118
- post(){
119
- this._data.method = 'POST';
120
- return this as any as UtilCom<D & {method:'POST'},S,A>;
121
- }
122
- /**补充参数
123
- * 将会替换对应字段, 修改headers请用header函数
124
- */
125
- option<OPT extends Partial<RequestOption>>(option:OPT){
126
- this._data = {...this._data,...option};
127
- return this as any as UtilCom<D & OPT,S,A>;
128
- }
129
- /**补充header */
130
- header<HAD extends http.OutgoingHttpHeaders>(headers:HAD){
131
- this._data.headers = {
132
- ...this._data.headers,
133
- ...headers,
134
- };
135
- return this as any as UtilCom<D & {headers:HAD} ,S,A>;
136
- }
137
- /**设置agent */
138
- proxyAgent(url:string){
139
- this._data.agent = UtilFunc.matchProc(this._data['protocol'],{
140
- 'http:' :()=>HttpProxyAgent(url),
141
- 'https:':()=>HttpsProxyAgent(url),
142
- })
143
- return this;
144
- }
145
- /**添加一段query */
146
- query(data:QueryRequestData){
147
- this._data.path = UtilCom.buildQuery(this._data.path??'',data);
148
- return this;
149
- }
150
- //#endregion
151
-
152
- //#region 快速预设
153
- /**收发皆为json的预设 */
154
- json(){
155
- return this.sendJson().acceptJson();
156
- }
157
- /**收发皆为json的post预设 */
158
- postJson(){
159
- return this.post().json();
160
- }
161
- /**无查询参数获取json的get预设 */
162
- getJson(){
163
- return this.get().sendNone().acceptJson();
164
- }
165
- /**有查询参数获取json的get预设 */
166
- queryJson(){
167
- return this.get().sendQuery().acceptJson();
168
- }
169
- /**收发皆为json的https-post预设 */
170
- static httpsPostJson(){
171
- return UtilCom.https().postJson();
172
- }
173
- /**收发皆为json的http-post预设 */
174
- static httpPostJson(){
175
- return UtilCom.http().postJson();
176
- }
177
- /**无查询参数获取json的https-get预设 */
178
- static httpsGetJson(){
179
- return UtilCom.https().getJson();
180
- }
181
- /**有查询参数获取json的https-get预设 */
182
- static httpsQueryJson(){
183
- return UtilCom.http().queryJson();
184
- }
185
- /**无查询参数获取json的http-get预设 */
186
- static httpGetJson(){
187
- return UtilCom.http().getJson();
188
- }
189
- /**有查询参数获取json的http-get预设 */
190
- static httpQueryJson(){
191
- return UtilCom.http().queryJson();
192
- }
193
- //#endregion
194
-
195
- //#region 接收数据类型
196
- /**预设的接收数据类型*/
197
- accept<T extends AcceptType>(t:T){
198
- const map = {
199
- 'json' :this.acceptJson(),
200
- 'string':this.acceptString(),
201
- } as const;
202
- return map[t];
203
- }
204
- acceptJson(){
205
- const proc:AcceptProc<string,RequestResult<JToken>|undefined>={
206
- init:'',
207
- reduce:(acc,curr)=>acc+curr,
208
- parse:(result)=>{
209
- if(result==undefined) return undefined;
210
- const {data,...rest} = result;
211
-
212
- if(data.trim()==""){
213
- SLogger.warn(`json accept 接收反馈错误: 原始字符串为空`,UtilFunc.stringifyJToken(result,{compress:true,space:2}));
214
- return {...result,raw:"",data:null};
215
- }
216
- try{
217
- const obj = JSON.parse(data.trim()) as JToken;
218
- SLogger.http(
219
- `json accept 接受信息 data:`,
220
- UtilFunc.stringifyJToken(obj,{compress:true,space:2}),
221
- `result:`,
222
- UtilFunc.stringifyJToken(rest,{compress:true,space:2})
223
- );
224
- return{...rest,data:obj};
225
- }
226
- catch(e){
227
- SLogger.warn(`json accept 接收反馈错误:${e}`,UtilFunc.stringifyJToken(result,{compress:true,space:2}));
228
- return {...result,raw:data,data:null};
229
- }
230
- }
231
- }
232
- this._accept = proc as any;
233
- return this as any as UtilCom<D,S,typeof proc>;
234
- }
235
- acceptString(){
236
- this._accept = AcceptStringProc as any;
237
- return this as any as UtilCom<D,S,typeof AcceptStringProc>;
238
- }
239
- /**自定的接收数据类型*/
240
- acceptRaw<AD,AT>(proc:AcceptProc<AD,AT>){
241
- this._accept = proc as any;
242
- return this as any as UtilCom<D,S,typeof proc>;
243
- }
244
- //#endregion
245
-
246
- //#region 发送数据类型
247
- /**预设的发送数据类型*/
248
- send<T extends SendType>(t:T){
249
- const map = {
250
- 'json' :this.sendJson(),
251
- 'query' :this.sendQuery(),
252
- 'formData' :this.sendFormData(),
253
- 'none' :this.sendNone(),
254
- } as const;
255
- return map[t];
256
- }
257
- /**利用 req.write 发送一段json */
258
- sendJson(){
259
- const proc:SendProc<[JToken]>={
260
- proc:(opt:RequestOption,reqData:JToken)=>{
261
- const {method} = opt;
262
- const isPost = (method=="POST");
263
-
264
- this._data.headers??={};
265
- this._data.headers['Content-Length'] = Buffer.byteLength(JSON.stringify(reqData));
266
-
267
- const procReq = (req:http.ClientRequest)=>{
268
- if(isPost) req.write(JSON.stringify(reqData));
269
- req.end();
270
- }
271
- return procReq;
272
- }
273
- }
274
- this._send = proc as any;
275
- this._data.headers??={};
276
- this._data.headers['Content-Type'] = 'application/json';
277
- return this as any as UtilCom<D&{
278
- headers:{ 'Content-Type': 'application/json' }
279
- },typeof proc,A>;
280
- }
281
- /**利用 appendQuery 直接将数据附加在path上发送请求 */
282
- sendQuery(){
283
- const proc:SendProc<[QueryRequestData]>={
284
- proc:(opt:RequestOption,reqData:QueryRequestData)=>{
285
- opt.path = UtilCom.buildQuery(opt.path??'',reqData)
286
- const procReq = (req:http.ClientRequest)=>void req.end();
287
- return procReq;
288
- }
289
- }
290
- this._send = proc as any;
291
- return this as any as UtilCom<D,typeof proc,A>;
292
- }
293
- sendFormData() {
294
- const proc: SendProc<[FormData]> = {
295
- proc: (opt: RequestOption, formData: FormData) => {
296
- opt.headers = formData.getHeaders();
297
- const procReq = (req: http.ClientRequest) => {
298
- formData.pipe(req);
299
- req.end();
300
- };
301
- return procReq;
302
- },
303
- };
304
- this._send = proc as any;
305
- this._data.headers??={};
306
- this._data.headers['Content-Type'] = 'multipart/form-data';
307
- return this as any as UtilCom<D&{
308
- headers:{ 'Content-Type': 'multipart/form-data' }
309
- }, typeof proc, A>;
310
- }
311
- sendNone(){
312
- this._send = SendNoneProc as any;
313
- return this as any as UtilCom<D,typeof SendNoneProc,A>;
314
- }
315
- /**自定的发送数据类型*/
316
- sendRaw<T extends any[]>(proc:SendProc<T>){
317
- this._send = proc as any;
318
- return this as any as UtilCom<D, typeof proc, A>;
319
- }
320
- //#endregion
321
-
322
- /**发送请求
323
- * @param option - 网络请求选项
324
- * @param datas - 数据对象
325
- */
326
- async once(option:PartialOption<RequestOption,D>,...datas:SendParams<S>){
327
- const fullopt = Object.assign({},this._data,option) as RequestOption;
328
- const proc = await this._send.proc(fullopt,...datas as any[]);
329
- const {reduce,init,parse} = this._accept;
330
- const res = await UtilCom.request(fullopt,proc,reduce,init);
331
- return parse(res) as ParseResult<A>;
332
- }
333
- /**重复发送网络请求
334
- * @param option - 网络请求选项
335
- * @param verify - 有效性验证函数
336
- * @param retries - 重试选项 默认 延迟:1000ms 间隔:180_000ms 重试:3次
337
- * @param datas - 数据对象
338
- */
339
- async retry(
340
- opt:{
341
- option :PartialOption<RequestOption,D>,
342
- verify? :StatusVerifyFn<ParseResult<A>>,
343
- retries?:PromiseRetries,
344
- },
345
- ...datas:SendParams<S>
346
- ){
347
- let {option,retries,verify} = opt;
348
- retries??={};
349
- retries.tryDelay = retries.tryDelay??1000;
350
- const procFn = ()=>this.once(option,...datas);
351
- return UtilFunc.retryPromise(procFn,verify,retries);
352
- }
353
-
354
- /**发送网络请求
355
- * @param option - 网络请求选项
356
- * @param proc - 请求处理函数 需调用req.end()
357
- * @param reduce - 数据处理函数
358
- * @param init - 初始数据
359
- */
360
- static async request<T>(
361
- option:RequestOption,
362
- proc:RequestProcFn,
363
- reduce:RequestReduceFn<T>,
364
- init:T,
365
- ){
366
- const {protocol,timeout,...baseReqOpt} = option;
367
-
368
- const hasTimeLimit = (timeout ? timeout>=10_000 : false );
369
-
370
- const flagName = `UtilCom.request ${protocol}${baseReqOpt.method} ${UtilFunc.genUUID()}`;
371
-
372
- let dataPromise:Promise<any>|null = null;
373
-
374
- return new Promise<RequestResult<T>|undefined>(async (resolve, rejecte)=>{
375
- const resFunc = (res:http.IncomingMessage)=>{
376
- try{
377
- //请求超时
378
- if(hasTimeLimit){
379
- res.setTimeout(timeout!, ()=>{
380
- SLogger.warn(`${flagName} 接收反馈超时: ${timeout} ms`);
381
- resolve(undefined);
382
- });
383
- }
384
-
385
- let mergedata:T = init;
386
- res.setEncoding('utf8');
387
- res.on('data', chunk => {
388
- dataPromise = UtilFunc.queueProc(flagName,async()=>mergedata=await reduce(mergedata,chunk))
389
- });
390
-
391
- res.on('error',(e)=>{
392
- SLogger.warn(`${flagName} 接收反馈错误:${e}`);
393
- resolve(undefined);
394
- });
395
-
396
- res.on('end',async ()=>{
397
- await dataPromise;
398
- resolve({
399
- headers: res.headers,
400
- statusCode: res.statusCode,
401
- data: mergedata,
402
- });
403
- });
404
- }catch(err){
405
- SLogger.warn(`${flagName} 未知错误:${err}`);
406
- resolve(undefined);
407
- return;
408
- }
409
- };
410
- //路由 http/https
411
- const req:http.ClientRequest= protocol=="https:"
412
- ? https.request(baseReqOpt, resFunc)
413
- : http .request(baseReqOpt, resFunc);
414
-
415
- //请求超时
416
- if(hasTimeLimit){
417
- req.setTimeout(timeout!, ()=>{
418
- SLogger.warn(`${flagName} 发送请求超时: ${timeout} ms`);
419
- req.destroy();
420
- });
421
- }
422
-
423
- req.on('error', (e)=>{
424
- SLogger.warn(`${flagName} 发送请求错误:${e}`);
425
- resolve(undefined);
426
- });
427
-
428
- await proc(req);
429
- });
430
- }
431
-
432
- /**构建query */
433
- static buildQuery(base:string,data:QueryRequestData){
434
- const queryString = qs.stringify(data);
435
- if (queryString) {
436
- // 检查当前路径是否已经包含问号
437
- const separator = base.includes('?') ? '&' : '?';
438
- base += separator + queryString;
439
- }
440
- return base;
441
- }
442
- }
443
-
444
-
445
- if(false)((async ()=>{
446
- const t = await UtilCom.https().postJson()
447
- .retry({
448
- option:{
449
- hostname:'httpbin.org',
450
- path:'/post',
451
- timeout:10000
452
- },
453
- verify:()=>Success,
454
- retries:{}
455
- },{test:1});
456
- console.log(t);
457
- })())