@@ -11,6 +11,29 @@ const https = require('https');
11
11
const logger = require ( '@studio/log' ) ;
12
12
const { failure, E_FAILED } = require ( '@studio/fail' ) ;
13
13
14
+ /**
15
+ * @typedef {import('http').RequestOptions } RequestOptions
16
+ * @typedef {import('http').ClientRequest } ClientRequest
17
+ * @typedef {import('http').IncomingMessage } IncomingMessage
18
+ * @typedef {import('stream').Readable } Readable
19
+ * @typedef {import('@studio/fail').Failure } Failure
20
+ * @typedef {import('@studio/log').Logger } Logger
21
+ */
22
+
23
+ /**
24
+ * @typedef {Object } JsonRequestOptions
25
+ * @property {number } [timeout]
26
+ * @property {number | number[] } [expect]
27
+ * @property {boolean } [stream]
28
+ * @property {Logger } [log]
29
+ */
30
+
31
+ /**
32
+ * @typedef {undefined | null | boolean | number | string | JsonArray | JsonObject } JsonValue
33
+ * @typedef {JsonValue[] } JsonArray
34
+ * @typedef {{ [k: string]: JsonValue } } JsonObject
35
+ */
36
+
14
37
const logger_name = 'Request' ;
15
38
const default_log = logger ( logger_name ) ;
16
39
@@ -19,6 +42,11 @@ const PROTOCOLS = {
19
42
'https:' : https
20
43
} ;
21
44
45
+ /**
46
+ * @param {string | number } expect
47
+ * @param {number } status
48
+ * @returns {Failure }
49
+ */
22
50
function expectError ( expect , status ) {
23
51
return failure (
24
52
`Expected response statusCode to be ${ expect } , but was ${ status } ` ,
@@ -27,11 +55,32 @@ function expectError(expect, status) {
27
55
) ;
28
56
}
29
57
58
+ /**
59
+ * @template T
60
+ * @param {T } obj
61
+ * @returns {T }
62
+ */
30
63
function copy ( obj ) {
31
64
return { ...obj } ;
32
65
}
33
66
34
- module . exports = function fetch ( options , data , callback ) {
67
+ module . exports = fetch ;
68
+
69
+ /**
70
+ * @callback FetchCallback
71
+ * @param {Error | null } err
72
+ * @param {Object | null } [json]
73
+ * @param {IncomingMessage } [res]
74
+ */
75
+
76
+ /**
77
+ * @param {RequestOptions & JsonRequestOptions } options
78
+ * @param {null | string | JsonObject | Readable | FetchCallback } data
79
+ * @param {FetchCallback } [callback]
80
+ * @returns {ClientRequest }
81
+ */
82
+ // eslint-disable-next-line complexity
83
+ function fetch ( options , data , callback ) {
35
84
if ( typeof data === 'function' ) {
36
85
callback = data ;
37
86
data = null ;
@@ -63,7 +112,7 @@ module.exports = function fetch(options, data, callback) {
63
112
delete opts . log ;
64
113
}
65
114
66
- if ( data && ! data . pipe ) {
115
+ if ( data && typeof data !== 'string' && ! data . pipe ) {
67
116
data = JSON . stringify ( data ) ;
68
117
if ( opts . headers ) {
69
118
opts . headers = copy ( opts . headers ) ;
@@ -86,7 +135,7 @@ module.exports = function fetch(options, data, callback) {
86
135
if ( options . port ) {
87
136
request . port = options . port ;
88
137
}
89
- if ( data && ! data . pipe ) {
138
+ if ( typeof data === 'string' ) {
90
139
request . body = data ;
91
140
}
92
141
@@ -123,7 +172,9 @@ module.exports = function fetch(options, data, callback) {
123
172
response . body = body ;
124
173
const ms_body = Date . now ( ) - ts_head ;
125
174
log . warn ( { request, ms_head, ms_body, response } , err . message ) ;
126
- callback ( err , null , res ) ;
175
+ if ( callback ) {
176
+ callback ( err , null , res ) ;
177
+ }
127
178
} ) ;
128
179
return ;
129
180
}
@@ -154,15 +205,19 @@ module.exports = function fetch(options, data, callback) {
154
205
const e = failure ( 'Response failure' , res_err , E_FAILED ) ;
155
206
const ms_body = Date . now ( ) - ts_head ;
156
207
log . error ( { request, ms_head, ms_body, response } , res_err ) ;
157
- callback ( e ) ;
208
+ if ( callback ) {
209
+ callback ( e ) ;
210
+ }
158
211
} ) ;
159
212
if ( opts . stream ) {
160
213
log . fetch ( { request, ms_head, response } ) ;
161
214
res . on ( 'end' , ( ) => {
162
215
const ms_body = Date . now ( ) - ts_head ;
163
216
log . finish ( { ms_body } ) ;
164
217
} ) ;
165
- callback ( null , res ) ;
218
+ if ( callback ) {
219
+ callback ( null , res ) ;
220
+ }
166
221
return ;
167
222
}
168
223
@@ -186,13 +241,17 @@ module.exports = function fetch(options, data, callback) {
186
241
) ;
187
242
response . body = body ;
188
243
log . error ( { request, ms_head, ms_body, response } ) ;
189
- callback ( json_err , body , res ) ;
244
+ if ( callback ) {
245
+ callback ( json_err , body , res ) ;
246
+ }
190
247
return ;
191
248
}
192
249
response . json = json ;
193
250
}
194
251
log . fetch ( { request, response, ms_head, ms_body } ) ;
195
- callback ( null , json , res ) ;
252
+ if ( callback ) {
253
+ callback ( null , json , res ) ;
254
+ }
196
255
} ) ;
197
256
} ) ;
198
257
@@ -201,17 +260,17 @@ module.exports = function fetch(options, data, callback) {
201
260
req . destroy ( ) ;
202
261
const err = failure ( 'Request timeout' , 'E_TIMEOUT' ) ;
203
262
log . warn ( { ms : Date . now ( ) - ts_start , request } ) ;
204
- callback ( err ) ;
205
- callback = null ;
263
+ if ( callback ) {
264
+ callback ( err ) ;
265
+ }
266
+ callback = undefined ;
206
267
} , timeout ) ;
207
268
}
208
269
209
- if ( data ) {
210
- if ( data . pipe ) {
211
- data . pipe ( req ) ;
212
- } else {
213
- req . end ( data ) ;
214
- }
270
+ if ( typeof data === 'string' ) {
271
+ req . end ( data ) ;
272
+ } else if ( data && typeof data . pipe === 'function' ) {
273
+ data . pipe ( req ) ;
215
274
} else {
216
275
req . end ( ) ;
217
276
}
@@ -227,4 +286,4 @@ module.exports = function fetch(options, data, callback) {
227
286
}
228
287
} ) ;
229
288
return req ;
230
- } ;
289
+ }
0 commit comments