@zwa73/utils 1.0.198 → 1.0.199

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