@zwa73/utils 1.0.194 → 1.0.196

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,9 +1,11 @@
1
- import { AnyString, JObject, PRecord, ReqVerifyFn } from "@/src/UtilInterfaces";
1
+ import { AnyString,JToken, MPromise, PartialOption, StatusVerifyFn } from "@/src/UtilInterfaces";
2
2
  import https from 'https';
3
3
  import http from 'http';
4
4
  import { SLogger } from "@/src/UtilLogger";
5
- import { RepeatPromiseOpt, RepeatPromiseResult, UtilFunc } from "@/src/UtilFunctions";
5
+ import { RepeatPromiseOpt, UtilFunc } from "@/src/UtilFunctions";
6
6
  import qs from "querystring";
7
+ import { matchProc } from "./QuickExport";
8
+ import { Success } from "./UtilSymbol";
7
9
 
8
10
  /**网络请求返回值 */
9
11
  export type ComResp<T> = {
@@ -16,12 +18,11 @@ export type ComResp<T> = {
16
18
  }
17
19
 
18
20
  /**网络请求选项 */
19
- export type ComReqOpt = {
21
+ export type ComRequestOption = {
20
22
  /**请求协议 */
21
- protocol: 'http'|'https';
23
+ protocol: 'http:'|'https:';
22
24
  /**超时时间/毫秒 最小为10_000 默认无限 */
23
- timeLimit?:number;
24
- }&{
25
+ timeout?:number;
25
26
  /**请求域名 */
26
27
  hostname: string;
27
28
  /**请求路径 */
@@ -50,292 +51,294 @@ export type GetReqData = NodeJS.Dict<
50
51
  | null
51
52
  >;
52
53
 
53
- /**网络工具 */
54
- export namespace UtilCom{
54
+ /**请求处理函数 需调用req.end() */
55
+ export type ProcReqFn = ((req:http.ClientRequest)=>MPromise<void>);
56
+ /**数据处理函数 */
57
+ export type ReduceDataFn<T> = (acc:T,data:string)=>MPromise<T>;
55
58
 
56
59
 
57
- /**网络请求
58
- * @param comReqOpt - 网络请求选项
59
- * @param procReq - 请求处理函数 需调用req.end()
60
- * @param reduceData - 数据处理函数
61
- * @param initData - 初始数据
62
- */
63
- export async function comReq<T>(
64
- comReqOpt:ComReqOpt,
65
- procReq:((req:http.ClientRequest)=>void|Promise<void>),
66
- reduceData:(acc:T,data:string)=>T,
67
- initData:T,
68
- ){
69
- const {protocol,timeLimit,...baseReqOpt} = comReqOpt;
60
+ const AcceptTypeList = ["json"]as const;
61
+ /**可用的接受类型 */
62
+ type AcceptType = typeof AcceptTypeList[number];
63
+ const SendTypeList = ["json","none"] as const;
64
+ /**可用的发送类型 */
65
+ type SendType = typeof SendTypeList[number];
70
66
 
71
- const hasTimeLimit = (timeLimit ? timeLimit>=10_000 : false );
72
-
73
- const flagName = `${comReq.name}.${protocol}${baseReqOpt.method}`;
74
-
75
- return new Promise<ComResp<T>|undefined>(async (resolve, rejecte)=>{
76
- const resFunc = (res:http.IncomingMessage)=>{
77
- try{
78
- //请求超时
79
- if(hasTimeLimit){
80
- res.setTimeout(timeLimit!, ()=>{
81
- SLogger.warn(`${flagName} 接收反馈超时: ${timeLimit} ms`);
82
- resolve(undefined);
83
- });
67
+ /**accept处理数据 */
68
+ type AcceptProc<D,T> = {
69
+ init:D;
70
+ reduce:ReduceDataFn<D>,
71
+ parse:(result:ComResp<D>|undefined)=>MPromise<T>
72
+ }
73
+ /**send处理数据 */
74
+ type SendProc<T extends any[]> = {
75
+ proc:(opt:ComRequestOption,...args:T)=>MPromise<ProcReqFn>,
76
+ }
77
+ const defSend = {
78
+ proc:()=>(req:http.ClientRequest)=>void req.end()
79
+ }
80
+ const defAccept = {
81
+ init:'',
82
+ reduce:(acc:string,dat:string)=>acc+dat,
83
+ parse:(result:ComResp<string>|undefined)=>void console.log(result)
84
+ }
85
+ /**send处理的参数 */
86
+ type SendParams<T extends SendProc<any>> = T extends SendProc<infer T>
87
+ ? T : never;
88
+ /**accept处理的结果 */
89
+ type ParseResult<D extends AcceptProc<any,any>> = D extends AcceptProc<any,infer T>
90
+ ? Awaited<T> : never;
91
+
92
+ /**根据方式自动推断json请求的可用数据类型 */
93
+ type JsonType<OPT extends Record<string,any>> = OPT['method'] extends "POST" ? JToken :GetReqData;
94
+
95
+ /**网络请求工具 */
96
+ export class UtilCom<
97
+ D extends Partial<ComRequestOption>,
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,defSend,defAccept);
107
+ }
108
+ /**设为http请求 */
109
+ static http(){
110
+ return new UtilCom({protocol:'http:'} as const,defSend,defAccept);
111
+ }
112
+ /**设为get方式的请求 */
113
+ get(){
114
+ return new UtilCom({...this._data,method:'GET'} as const,this._send,this._accept);
115
+ }
116
+ /**设为Post方式的请求 */
117
+ post(){
118
+ return new UtilCom({...this._data,method:'POST'} as const,this._send,this._accept);
119
+ }
120
+ /**补充参数 */
121
+ option<OPT extends Partial<ComRequestOption>>(opt:OPT){
122
+ return new UtilCom({...this._data,...opt} as const,this._send,this._accept);
123
+ }
124
+ //#endregion
125
+
126
+ //#region 接收数据类型
127
+ /**接收数据类型*/
128
+ accept<T extends AcceptType>(t:T){
129
+ const map = {
130
+ 'json':this.acceptJson(),
131
+ 'log':this.acceptLog(),
132
+ } as const;
133
+ return map[t];
134
+ }
135
+ acceptJson(){
136
+ const proc:AcceptProc<string,JToken>={
137
+ init:'',
138
+ reduce:(acc,curr)=>acc+curr,
139
+ parse:(result)=>{
140
+ if(result==undefined) return undefined;
141
+ const {data,...rest} = result;
142
+
143
+ if(data.trim()==""){
144
+ SLogger.warn(`json accept 接收反馈错误: 原始字符串为空`);
145
+ return {...result,raw:"",data:null};
146
+ }
147
+ try{
148
+ const obj = JSON.parse(data.trim()) as JToken;
149
+ SLogger.http(`json accept 接受信息:`,UtilFunc.stringifyJToken(obj,{compress:true,space:2}));
150
+ return{...rest,data:obj};
151
+ }
152
+ catch(e){
153
+ SLogger.warn(`json accept 接收反馈错误:${e}\n原始字符串:${data}`);
154
+ return {...result,raw:data,data:null};
84
155
  }
85
-
86
- let mergedata:T = initData;
87
- res.setEncoding('utf8');
88
- res.on('data',chunk => mergedata=reduceData(mergedata,chunk));
89
-
90
- res.on('error',(e)=>{
91
- SLogger.warn(`${flagName} 接收反馈错误:${e}`);
92
- resolve(undefined);
93
- });
94
-
95
- res.on('end',()=>{
96
- resolve({
97
- headers: res.headers,
98
- statusCode: res.statusCode,
99
- data: mergedata,
100
- });
101
- });
102
- }catch(err){
103
- SLogger.warn(`${flagName} 未知错误:${err}`);
104
- resolve(undefined);
105
- return;
106
156
  }
107
- };
108
- //路由 http/https
109
- const req:http.ClientRequest= protocol=="https"
110
- ? https.request(baseReqOpt as http.RequestOptions, resFunc)
111
- : http.request(baseReqOpt as http.RequestOptions, resFunc);
112
-
113
- //请求超时
114
- if(hasTimeLimit){
115
- req.setTimeout(timeLimit!, ()=>{
116
- SLogger.warn(`${flagName} 发送请求超时: ${timeLimit} ms`);
117
- req.destroy();
118
- });
119
- //req.on('timeout', ()=>{
120
- // SLogger.warn(`${flagName} 发送请求超时(timeout): ${timeLimit} ms`);
121
- // req.destroy();
122
- //});
123
157
  }
124
-
125
- req.on('error', (e)=>{
126
- SLogger.warn(`${flagName} 发送请求错误:${e}`);
127
- resolve(undefined);
128
- });
129
-
130
- await procReq(req);
131
- });
132
- }
133
-
134
- /**发送json的网络请求
135
- * @param comReqOpt - 网络请求选项
136
- * @param reqData - 数据对象
137
- * @returns 结果 undefined 为未能成功接收
138
- */
139
- async function jsonReq<T extends ComReqOpt>(
140
- comReqOpt:T,
141
- reqData?:T['method'] extends "POST" ? JObject :GetReqData
142
- ){
143
- const {method} = comReqOpt;
144
- const isPost = (method=="POST");
145
-
146
- if (!isPost && reqData != undefined) {
147
- const queryString = qs.stringify(reqData as GetReqData);
148
- if (queryString) {
149
- // 检查当前路径是否已经包含问号
150
- const separator = comReqOpt?.path?.includes('?') ? '&' : '?';
151
- comReqOpt.path += separator + queryString;
158
+ return new UtilCom(this._data,this._send,proc)
159
+ }
160
+ acceptLog(){
161
+ const proc:AcceptProc<string,string>={
162
+ init:'',
163
+ reduce:(acc,curr)=>acc+curr,
164
+ parse:(result)=>{
165
+ SLogger.http(`log accept 接受信息:`,UtilFunc.stringifyJToken(result,{compress:true,space:2}));
166
+ if(result==undefined) return '';
167
+ const {data,...rest} = result;
168
+ return data;
169
+ }
152
170
  }
171
+ return new UtilCom(this._data,this._send,proc)
153
172
  }
154
-
155
- const procReq = (req:http.ClientRequest)=>{
156
- if(isPost) req.write(JSON.stringify(reqData));
157
- req.end();
173
+ //#endregion
174
+
175
+ //#region 发送数据类型
176
+ /**发送数据类型*/
177
+ send<T extends SendType>(t:T){
178
+ const map = {
179
+ 'json':this.sendJson(),
180
+ "none":this.sendNone(),
181
+ } as const;
182
+ return map[t];
158
183
  }
184
+ sendJson(){
185
+ const proc:SendProc<[JsonType<D>]>={
186
+ proc:(opt:ComRequestOption,reqData:JsonType<D>)=>{
187
+ const {method} = opt;
188
+ const isPost = (method=="POST");
189
+
190
+ if (!isPost && reqData != undefined) {
191
+ const queryString = qs.stringify(reqData as GetReqData);
192
+ if (queryString) {
193
+ // 检查当前路径是否已经包含问号
194
+ const separator = opt?.path?.includes('?') ? '&' : '?';
195
+ opt.path += separator + queryString;
196
+ }
197
+ }
198
+ if(isPost){
199
+ this._data.headers??={
200
+ 'Content-Type': 'application/json',
201
+ 'Content-Length': Buffer.from(JSON.stringify(reqData)).length,
202
+ }
203
+ }
159
204
 
160
- const reduceData = (acc:string,data:string)=>acc+data;
161
- const result = await comReq(comReqOpt,procReq,reduceData,"");
162
- if(result==undefined) return undefined;
163
- const {data,...rest} = result;
164
-
165
- if(data.trim()==""){
166
- SLogger.warn(`${jsonReq.name} 接收反馈错误: 原始字符串为空`);
167
- return undefined;
205
+ const procReq = (req:http.ClientRequest)=>{
206
+ if(isPost) req.write(JSON.stringify(reqData));
207
+ req.end();
208
+ }
209
+ return procReq;
210
+ }
211
+ }
212
+ return new UtilCom(this._data,proc,this._accept);
168
213
  }
169
-
170
- try{
171
- const obj = JSON.parse(data.trim()) as JObject;
172
- SLogger.http(`${jsonReq.name} 接受信息:`,UtilFunc.stringifyJToken(obj,{compress:true,space:2}));
173
- return{...rest,data:obj};
214
+ sendNone(){
215
+ const proc:SendProc<[]>={
216
+ proc:(opt:ComRequestOption)=>{
217
+ const procReq = (req:http.ClientRequest)=>{
218
+ req.end();
219
+ }
220
+ return procReq;
221
+ }
222
+ }
223
+ return new UtilCom(this._data,proc,this._accept)
174
224
  }
175
- catch(e){
176
- SLogger.warn(`${jsonReq.name} 接收反馈错误:${e}\n原始字符串:${data}`);
177
- return undefined;
225
+ //#endregion
226
+
227
+ /**发送请求
228
+ * @param option - 网络请求选项
229
+ * @param datas - 数据对象
230
+ */
231
+ async once(option:PartialOption<ComRequestOption,D>,...datas:SendParams<S>){
232
+ const fullopt = Object.assign({},this._data,option) as ComRequestOption;
233
+ const procReq = await this._send.proc(fullopt,...datas as any[]);
234
+ const {reduce,init,parse} = this._accept;
235
+ const res = await UtilCom.comReq(fullopt,procReq,reduce,init);
236
+ return parse(res);
237
+ }
238
+ /**重复发送网络请求
239
+ * @param option - 网络请求选项
240
+ * @param verifyFn - 有效性验证函数
241
+ * @param repeatOpt - 重试选项 默认 延迟:1000ms 间隔:180_000ms 重试:3次
242
+ * @param datas - 数据对象
243
+ */
244
+ async repeat(
245
+ option:PartialOption<ComRequestOption,D>,
246
+ verifyFn?:StatusVerifyFn<ParseResult<A>>,
247
+ repeatOpt:RepeatPromiseOpt={},
248
+ ...datas:SendParams<S>
249
+ ){
250
+ repeatOpt.tryDelay = repeatOpt.tryDelay??1000;
251
+ const procFn = ()=>this.once(option,...datas);
252
+ return UtilFunc.repeatPromise(procFn,verifyFn,repeatOpt);
178
253
  }
179
- }
180
-
181
- /**重复发送json的网络请求
182
- * @async
183
- * @param comReqOpt - 网络请求选项
184
- * @param reqData - 数据对象
185
- * @param verifyFn - 有效性验证函数
186
- * @param repeatOpt - 重试选项 默认 延迟:1000ms 间隔:180_000ms 重试:3次
187
- * @returns 结果 undefined 为未能成功接收
188
- */
189
- async function repeatJsonComReq<T extends ComReqOpt>(
190
- comReqOpt:T,
191
- reqData?:T['method'] extends "POST" ? JObject :GetReqData,
192
- verifyFn?:ReqVerifyFn<ComResp<JObject>|undefined>,
193
- repeatOpt:RepeatPromiseOpt={},
194
- ){
195
- repeatOpt.tryDelay = repeatOpt.tryDelay??1000;
196
- const procFn = ()=>jsonReq(comReqOpt,reqData);
197
- return UtilFunc.repeatPromise(procFn,verifyFn,repeatOpt);
198
- }
199
-
200
- /**发送一个 https POST 请求并接受数据
201
- * @async
202
- * @param comReqOpt - 请求参数
203
- * @param reqData - 数据对象
204
- * @returns 结果 undefined 为未能成功接收
205
- */
206
- export function httpsPost(comReqOpt:Omit<ComReqOpt,'protocol'|'method'>,reqData?:JObject){
207
- return jsonReq({
208
- ...comReqOpt,
209
- method:"POST",
210
- protocol:"https",
211
- },reqData);
212
- }
213
-
214
- /**发送一个 http POST 请求并接受数据
215
- * @async
216
- * @param comReqOpt - 请求参数
217
- * @param reqData - 数据对象
218
- * @returns 结果 undefined 为未能成功接收
219
- */
220
- export function httpPost(comReqOpt:Omit<ComReqOpt,'protocol'|'method'>,reqData?:JObject){
221
- return jsonReq({
222
- ...comReqOpt,
223
- method:"POST",
224
- protocol:"http",
225
- },reqData);
226
- }
227
-
228
- /**发送一个 https GET 请求并接受数据
229
- * @async
230
- * @param comReqOpt - 请求参数
231
- * @param reqData - 数据对象
232
- * @returns 结果 undefined 为未能成功接收
233
- */
234
- export function httpsGet(comReqOpt:Omit<ComReqOpt,'protocol'|'method'>,reqData?:GetReqData){
235
- return jsonReq({
236
- ...comReqOpt,
237
- method:"GET",
238
- protocol:"https",
239
- },reqData);
240
- }
241
254
 
242
- /**发送一个 http GET 请求并接受数据
243
- * @async
244
- * @param comReqOpt - 请求参数
245
- * @param reqData - 数据对象
246
- * @returns 结果 undefined 为未能成功接收
247
- */
248
- export function httpGet(comReqOpt:Omit<ComReqOpt,'protocol'|'method'>,reqData?:GetReqData){
249
- return jsonReq({
250
- ...comReqOpt,
251
- method:"GET",
252
- protocol:"http",
253
- },reqData);
254
- }
255
+ /**网络请求
256
+ * @param comReqOpt - 网络请求选项
257
+ * @param procReq - 请求处理函数 需调用req.end()
258
+ * @param reduceData - 数据处理函数
259
+ * @param initData - 初始数据
260
+ */
261
+ static async comReq<T>(
262
+ comReqOpt:ComRequestOption,
263
+ procReq:ProcReqFn,
264
+ reduceData:ReduceDataFn<T>,
265
+ initData:T,
266
+ ){
267
+ const {protocol,timeout,...baseReqOpt} = comReqOpt;
268
+
269
+ const hasTimeLimit = (timeout ? timeout>=10_000 : false );
270
+
271
+ const flagName = `UtilCom.${protocol}${baseReqOpt.method}`;
272
+
273
+ const postflag = `comReq:${UtilFunc.genUUID()}`;
274
+ let dataPromise:Promise<any>|null = null;
275
+
276
+ return new Promise<ComResp<T>|undefined>(async (resolve, rejecte)=>{
277
+ const resFunc = (res:http.IncomingMessage)=>{
278
+ try{
279
+ //请求超时
280
+ if(hasTimeLimit){
281
+ res.setTimeout(timeout!, ()=>{
282
+ SLogger.warn(`${flagName} 接收反馈超时: ${timeout} ms`);
283
+ resolve(undefined);
284
+ });
285
+ }
286
+
287
+ let mergedata:T = initData;
288
+ res.setEncoding('utf8');
289
+ res.on('data', chunk => {
290
+ dataPromise = UtilFunc.queueProc(postflag,async()=>mergedata=await reduceData(mergedata,chunk))
291
+ });
255
292
 
293
+ res.on('error',(e)=>{
294
+ SLogger.warn(`${flagName} 接收反馈错误:${e}`);
295
+ resolve(undefined);
296
+ });
256
297
 
257
- /**重复一个 https POST请求并接受数据
258
- * @async
259
- * @param comReqOpt - 网络请求选项
260
- * @param reqData - 数据对象
261
- * @param verifyFn - 有效性验证函数
262
- * @param repeatOpt - 重试选项 默认 延迟:1000ms 间隔:180_000ms 重试:3次
263
- * @returns 结果 undefined 为未能成功接收
264
- */
265
- export function httpsRepeatPost(
266
- comReqOpt:Omit<ComReqOpt,'protocol'|'method'>,
267
- reqData?:JObject,
268
- verifyFn?:ReqVerifyFn<JObject|undefined>,
269
- repeatOpt?:RepeatPromiseOpt
270
- ){
271
- return repeatJsonComReq({
272
- ...comReqOpt,
273
- method:"POST",
274
- protocol:"https",
275
- },reqData,verifyFn,repeatOpt);
276
- }
298
+ res.on('end',async ()=>{
299
+ await dataPromise;
300
+ resolve({
301
+ headers: res.headers,
302
+ statusCode: res.statusCode,
303
+ data: mergedata,
304
+ });
305
+ });
306
+ }catch(err){
307
+ SLogger.warn(`${flagName} 未知错误:${err}`);
308
+ resolve(undefined);
309
+ return;
310
+ }
311
+ };
312
+ //路由 http/https
313
+ const req:http.ClientRequest= protocol=="https:"
314
+ ? https.request(baseReqOpt as http.RequestOptions, resFunc)
315
+ : http.request(baseReqOpt as http.RequestOptions, resFunc);
316
+
317
+ //请求超时
318
+ if(hasTimeLimit){
319
+ req.setTimeout(timeout!, ()=>{
320
+ SLogger.warn(`${flagName} 发送请求超时: ${timeout} ms`);
321
+ req.destroy();
322
+ });
323
+ }
277
324
 
278
- /**重复一个 http POST请求并接受数据
279
- * @async
280
- * @param comReqOpt - 网络请求选项
281
- * @param reqData - 数据对象
282
- * @param verifyFn - 有效性验证函数
283
- * @param repeatOpt - 重试选项 默认 延迟:1000ms 间隔:180_000ms 重试:3次
284
- * @returns 结果 undefined 为未能成功接收
285
- */
286
- export function httpRepeatPost(
287
- comReqOpt:Omit<ComReqOpt,'protocol'|'method'>,
288
- reqData?:JObject,
289
- verifyFn?:ReqVerifyFn<JObject|undefined>,
290
- repeatOpt?:RepeatPromiseOpt
291
- ){
292
- return repeatJsonComReq({
293
- ...comReqOpt,
294
- method:"POST",
295
- protocol:"http",
296
- },reqData,verifyFn,repeatOpt);
297
- }
325
+ req.on('error', (e)=>{
326
+ SLogger.warn(`${flagName} 发送请求错误:${e}`);
327
+ resolve(undefined);
328
+ });
298
329
 
299
- /**重复一个 https GET 请求并接受数据
300
- * @async
301
- * @param comReqOpt - 网络请求选项
302
- * @param reqData - 数据对象
303
- * @param verifyFn - 有效性验证函数
304
- * @param repeatOpt - 重试选项 默认 延迟:1000ms 间隔:180_000ms 重试:3次
305
- * @returns 结果 undefined 为未能成功接收
306
- */
307
- export function httpsRepeatGet(
308
- comReqOpt:Omit<ComReqOpt,'protocol'|'method'>,
309
- reqData?:GetReqData,
310
- verifyFn?:ReqVerifyFn<JObject|undefined>,
311
- repeatOpt?:RepeatPromiseOpt
312
- ){
313
- return repeatJsonComReq({
314
- ...comReqOpt,
315
- method:"GET",
316
- protocol:"https",
317
- },reqData,verifyFn,repeatOpt);
330
+ await procReq(req);
331
+ });
332
+ }
318
333
  }
319
334
 
320
- /**重复一个 http GET 请求并接受数据
321
- * @async
322
- * @param comReqOpt - 网络请求选项
323
- * @param reqData - 数据对象
324
- * @param verifyFn - 有效性验证函数
325
- * @param repeatOpt - 重试选项 默认 延迟:1000ms 间隔:180_000ms 重试:3次
326
- * @returns 结果 undefined 为未能成功接收
327
- */
328
- export function httpRepeatGet(
329
- comReqOpt:Omit<ComReqOpt,'protocol'|'method'>,
330
- reqData?:GetReqData,
331
- verifyFn?:ReqVerifyFn<JObject|undefined>,
332
- repeatOpt?:RepeatPromiseOpt
333
- ){
334
- return repeatJsonComReq({
335
- ...comReqOpt,
336
- method:"GET",
337
- protocol:"http",
338
- },reqData,verifyFn,repeatOpt);
339
- }
340
335
 
341
- }
336
+ if(false)((async ()=>{
337
+ const t = await UtilCom.https().post().accept('json').send('json')
338
+ .repeat({
339
+ hostname:'httpbin.org',
340
+ path:'/post',
341
+ timeout:10000
342
+ },()=>Success,{},{test:1});
343
+ console.log(t);
344
+ })())