@faasjs/request 0.0.4-beta.9 → 0.0.5-beta.2

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/README.md CHANGED
@@ -12,6 +12,10 @@ FaasJS's request module.
12
12
 
13
13
  ## Modules
14
14
 
15
+ ### Classes
16
+
17
+ - [ResponseError](classes/ResponseError.md)
18
+
15
19
  ### Type Aliases
16
20
 
17
21
  - [Request](#request)
@@ -34,7 +38,7 @@ FaasJS's request module.
34
38
 
35
39
  | Name | Type |
36
40
  | :------ | :------ |
37
- | `body?` | { `[key: string]`: `any`; } |
41
+ | `body?` | \{ `[key: string]`: `any`; } |
38
42
  | `headers?` | `http.OutgoingHttpHeaders` |
39
43
  | `host?` | `string` |
40
44
  | `method?` | `string` |
@@ -53,7 +57,7 @@ ___
53
57
  | :------ | :------ | :------ |
54
58
  | `agent?` | `boolean` | - |
55
59
  | `auth?` | `string` | The authentication credentials to use for the request. Format: `username:password` |
56
- | `body?` | { `[key: string]`: `any`; } \| `string` | - |
60
+ | `body?` | \{ `[key: string]`: `any`; } \| `string` | - |
57
61
  | `downloadFile?` | `string` | Path of downloading a file from the server. ```ts await request('https://example.com', { downloadFile: 'filepath' }) ``` |
58
62
  | `downloadStream?` | `NodeJS.WritableStream` | Create a write stream to download a file. ```ts import { createWriteStream } from 'fs' const stream = createWriteStream('filepath') await request('https://example.com', { downloadStream: stream }) ``` |
59
63
  | `file?` | `string` | Path of uploading a file to the server. ```ts await request('https://example.com', { file: 'filepath' }) ``` |
@@ -63,14 +67,14 @@ ___
63
67
  | `parse?` | (`body`: `string`) => `any` | Body parser. Defaults to `JSON.parse`. |
64
68
  | `passphrase?` | `string` | - |
65
69
  | `pfx?` | `Buffer` | - |
66
- | `query?` | { `[key: string]`: `any`; } | - |
70
+ | `query?` | \{ `[key: string]`: `any`; } | - |
67
71
  | `timeout?` | `number` | - |
68
72
 
69
73
  ___
70
74
 
71
75
  ### Response
72
76
 
73
- Ƭ **Response**<`T`\>: `Object`
77
+ Ƭ **Response**\<`T`\>: `Object`
74
78
 
75
79
  #### Type parameters
76
80
 
@@ -108,7 +112,7 @@ ___
108
112
 
109
113
  ### request
110
114
 
111
- ▸ **request**<`T`\>(`url`, `options?`): `Promise`<[`Response`](#response)<`T`\>\>
115
+ ▸ **request**\<`T`\>(`url`, `options?`): `Promise`\<[`Response`](#response)\<`T`\>\>
112
116
 
113
117
  Request
114
118
 
@@ -127,7 +131,7 @@ Request
127
131
 
128
132
  #### Returns
129
133
 
130
- `Promise`<[`Response`](#response)<`T`\>\>
134
+ `Promise`\<[`Response`](#response)\<`T`\>\>
131
135
 
132
136
  **`Url`**
133
137
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import * as http from 'http';
1
+ import * as http from 'node:http';
2
2
  import { Logger } from '@faasjs/logger';
3
3
 
4
4
  type Request = {
@@ -81,8 +81,21 @@ type Mock = (url: string, options: RequestOptions) => Promise<Response>;
81
81
  */
82
82
  declare function setMock(handler: Mock | null): void;
83
83
  declare function querystringify(obj: any): string;
84
+ /**
85
+ * ResponseError class
86
+ */
87
+ declare class ResponseError extends Error {
88
+ response: Response;
89
+ request: Request;
90
+ statusCode: number;
91
+ statusMessage: string;
92
+ headers: http.OutgoingHttpHeaders;
93
+ body: any;
94
+ constructor(message: string, response: Response<any>);
95
+ }
84
96
  /**
85
97
  * Request
98
+ *
86
99
  * @param {string} url Url
87
100
  * @param {object=} [options={}] Options
88
101
  * @param {string} [options.method=GET] Method
@@ -100,8 +113,9 @@ declare function querystringify(obj: any): string;
100
113
  * @param {parse=} options.parse body parser, default is JSON.parse
101
114
  *
102
115
  * @returns {promise}
116
+ *
103
117
  * @url https://faasjs.com/doc/request.html
104
118
  */
105
119
  declare function request<T = any>(url: string, { headers, method, query, body, timeout, auth, file, downloadStream, downloadFile, pfx, passphrase, agent, parse, logger, }?: RequestOptions): Promise<Response<T>>;
106
120
 
107
- export { Request, RequestOptions, Response, querystringify, request, setMock };
121
+ export { type Request, type RequestOptions, type Response, ResponseError, querystringify, request, setMock };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as http from 'http';
1
+ import * as http from 'node:http';
2
2
  import { Logger } from '@faasjs/logger';
3
3
 
4
4
  type Request = {
@@ -81,8 +81,21 @@ type Mock = (url: string, options: RequestOptions) => Promise<Response>;
81
81
  */
82
82
  declare function setMock(handler: Mock | null): void;
83
83
  declare function querystringify(obj: any): string;
84
+ /**
85
+ * ResponseError class
86
+ */
87
+ declare class ResponseError extends Error {
88
+ response: Response;
89
+ request: Request;
90
+ statusCode: number;
91
+ statusMessage: string;
92
+ headers: http.OutgoingHttpHeaders;
93
+ body: any;
94
+ constructor(message: string, response: Response<any>);
95
+ }
84
96
  /**
85
97
  * Request
98
+ *
86
99
  * @param {string} url Url
87
100
  * @param {object=} [options={}] Options
88
101
  * @param {string} [options.method=GET] Method
@@ -100,8 +113,9 @@ declare function querystringify(obj: any): string;
100
113
  * @param {parse=} options.parse body parser, default is JSON.parse
101
114
  *
102
115
  * @returns {promise}
116
+ *
103
117
  * @url https://faasjs.com/doc/request.html
104
118
  */
105
119
  declare function request<T = any>(url: string, { headers, method, query, body, timeout, auth, file, downloadStream, downloadFile, pfx, passphrase, agent, parse, logger, }?: RequestOptions): Promise<Response<T>>;
106
120
 
107
- export { Request, RequestOptions, Response, querystringify, request, setMock };
121
+ export { type Request, type RequestOptions, type Response, ResponseError, querystringify, request, setMock };
package/dist/index.js CHANGED
@@ -6,6 +6,8 @@ var url = require('url');
6
6
  var fs = require('fs');
7
7
  var path = require('path');
8
8
  var logger = require('@faasjs/logger');
9
+ var zlib = require('zlib');
10
+ var crypto = require('crypto');
9
11
 
10
12
  function _interopNamespace(e) {
11
13
  if (e && e.__esModule) return e;
@@ -52,6 +54,17 @@ function querystringify(obj) {
52
54
  }
53
55
  return pairs.length ? pairs.join("&") : "";
54
56
  }
57
+ var ResponseError = class extends Error {
58
+ constructor(message, response) {
59
+ super(message);
60
+ this.response = response;
61
+ this.request = response.request;
62
+ this.statusCode = response.statusCode;
63
+ this.statusMessage = response.statusMessage;
64
+ this.headers = response.headers;
65
+ this.body = response.body;
66
+ }
67
+ };
55
68
  async function request(url$1, {
56
69
  headers,
57
70
  method,
@@ -108,6 +121,9 @@ async function request(url$1, {
108
121
  passphrase,
109
122
  agent
110
123
  };
124
+ if (!options.headers["Accept-Encoding"] && !downloadFile && !downloadStream) {
125
+ options.headers["Accept-Encoding"] = "br,gzip";
126
+ }
111
127
  for (const key in headers)
112
128
  if (typeof headers[key] !== "undefined" && headers[key] !== null)
113
129
  options.headers[key] = headers[key];
@@ -118,57 +134,101 @@ async function request(url$1, {
118
134
  body = JSON.stringify(body);
119
135
  if (body && !options.headers["Content-Length"])
120
136
  options.headers["Content-Length"] = Buffer.byteLength(body);
121
- return await new Promise(function(resolve, reject) {
137
+ const requestId = crypto.randomUUID();
138
+ return await new Promise((resolve, reject) => {
122
139
  logger$1.debug("request %j", {
123
140
  ...options,
124
141
  body
125
142
  });
126
- const req = protocol.request(options, function(res) {
143
+ const req = protocol.request(options, (res) => {
127
144
  if (downloadStream) {
128
- res.pipe(downloadStream);
129
- downloadStream.on("finish", () => resolve(void 0));
130
- } else if (downloadFile) {
131
- const stream = fs.createWriteStream(downloadFile);
132
- res.pipe(stream);
133
- stream.on("finish", () => resolve(void 0));
134
- } else {
135
- const raw = [];
136
- res.on("data", (chunk) => {
137
- raw.push(chunk);
145
+ res.pipe(downloadStream, { end: true });
146
+ downloadStream.on("finish", () => {
147
+ res.destroy();
148
+ downloadStream.end();
149
+ resolve(void 0);
138
150
  });
151
+ return;
152
+ }
153
+ if (downloadFile) {
154
+ logger$1.debug("downloadFile");
155
+ const stream2 = fs.createWriteStream(downloadFile, { autoClose: true });
156
+ res.pipe(stream2, { end: true });
139
157
  res.on("end", () => {
140
- const data = Buffer.concat(raw).toString();
141
- logger$1.timeEnd(
142
- url$1,
143
- "response %s %s %s",
144
- res.statusCode,
145
- res.headers["content-type"],
146
- data
158
+ logger$1.debug("end");
159
+ res.destroy();
160
+ });
161
+ stream2.on("finish", () => {
162
+ stream2.end();
163
+ stream2.close();
164
+ logger$1.debug("finish");
165
+ });
166
+ stream2.on("close", () => {
167
+ logger$1.debug(
168
+ "finish",
169
+ res.closed,
170
+ res.destroyed,
171
+ stream2.destroyed,
172
+ stream2.closed,
173
+ req.closed,
174
+ req.destroyed
147
175
  );
148
- const response = res.statusCode >= 200 && res.statusCode < 400 ? /* @__PURE__ */ Object.create(null) : new Error();
149
- response.request = options;
150
- response.request.body = body;
151
- response.statusCode = res.statusCode;
152
- response.statusMessage = res.statusMessage;
153
- response.headers = res.headers;
154
- response.body = data;
155
- if (response.body && response.headers["content-type"] && response.headers["content-type"].includes("application/json"))
156
- try {
157
- response.body = (parse || JSON.parse)(response.body);
158
- logger$1.debug("response.parse JSON");
159
- } catch (error) {
160
- console.warn("response plain body", response.body);
161
- console.error(error);
162
- }
163
- if (response.statusCode >= 200 && response.statusCode < 400)
164
- resolve(response);
165
- else {
166
- logger$1.debug("response.error %j", response);
167
- response.message = `${res.statusMessage || res.statusCode} ${options.host}${options.path}`;
168
- reject(response);
169
- }
176
+ resolve(void 0);
170
177
  });
178
+ return;
179
+ }
180
+ let stream = res;
181
+ switch (res.headers["content-encoding"]) {
182
+ case "br":
183
+ stream = res.pipe(zlib.createBrotliDecompress());
184
+ break;
185
+ case "gzip":
186
+ stream = res.pipe(zlib.createGunzip());
187
+ break;
171
188
  }
189
+ const raw = [];
190
+ stream.on("data", (chunk) => raw.push(chunk));
191
+ stream.on("end", () => {
192
+ const data = Buffer.concat(raw).toString();
193
+ logger$1.timeEnd(
194
+ requestId,
195
+ "response %s %s %s %j",
196
+ res.statusCode,
197
+ res.headers["content-type"],
198
+ res.headers["content-encoding"],
199
+ data
200
+ );
201
+ const response = /* @__PURE__ */ Object.create(null);
202
+ response.request = options;
203
+ response.request.body = body;
204
+ response.statusCode = res.statusCode;
205
+ response.statusMessage = res.statusMessage;
206
+ response.headers = res.headers;
207
+ response.body = data;
208
+ if (response.body && response.headers["content-type"] && response.headers["content-type"].includes("application/json"))
209
+ try {
210
+ response.body = (parse || JSON.parse)(response.body);
211
+ logger$1.debug("response.parse JSON");
212
+ } catch (error) {
213
+ console.warn("response plain body", response.body);
214
+ console.error(error);
215
+ }
216
+ if (response.statusCode >= 200 && response.statusCode < 400)
217
+ resolve(response);
218
+ else {
219
+ logger$1.debug("response.error %j", response);
220
+ reject(
221
+ new ResponseError(
222
+ `${res.statusMessage || res.statusCode} ${options.host}${options.path}`,
223
+ response
224
+ )
225
+ );
226
+ }
227
+ });
228
+ stream.on("error", (e) => {
229
+ logger$1.timeEnd(requestId, "response.error %j", e);
230
+ reject(e);
231
+ });
172
232
  });
173
233
  if (body)
174
234
  req.write(body);
@@ -190,15 +250,21 @@ async function request(url$1, {
190
250
  req.setHeader("Content-Length", multipartBody.length);
191
251
  req.write(multipartBody);
192
252
  }
193
- req.on("error", function(e) {
194
- logger$1.timeEnd(url$1, "response.error %j", e);
253
+ req.on("error", (e) => {
254
+ logger$1.timeEnd(requestId, "response.error %j", e);
195
255
  reject(e);
196
256
  });
197
- logger$1.time(url$1);
257
+ req.on("timeout", () => {
258
+ logger$1.timeEnd(requestId, "response.timeout");
259
+ req.destroy();
260
+ reject(Error(`Timeout ${url$1}`));
261
+ });
262
+ logger$1.time(requestId);
198
263
  req.end();
199
264
  });
200
265
  }
201
266
 
267
+ exports.ResponseError = ResponseError;
202
268
  exports.querystringify = querystringify;
203
269
  exports.request = request;
204
270
  exports.setMock = setMock;
package/dist/index.mjs CHANGED
@@ -1,9 +1,11 @@
1
- import * as http from 'http';
2
- import * as https from 'https';
3
- import { URL } from 'url';
4
- import { createWriteStream, readFileSync } from 'fs';
5
- import { basename } from 'path';
1
+ import * as http from 'node:http';
2
+ import * as https from 'node:https';
3
+ import { URL } from 'node:url';
4
+ import { createWriteStream, readFileSync } from 'node:fs';
5
+ import { basename } from 'node:path';
6
6
  import { Logger } from '@faasjs/logger';
7
+ import { createGunzip, createBrotliDecompress } from 'node:zlib';
8
+ import { randomUUID } from 'node:crypto';
7
9
 
8
10
  // src/index.ts
9
11
  var mock = null;
@@ -29,6 +31,17 @@ function querystringify(obj) {
29
31
  }
30
32
  return pairs.length ? pairs.join("&") : "";
31
33
  }
34
+ var ResponseError = class extends Error {
35
+ constructor(message, response) {
36
+ super(message);
37
+ this.response = response;
38
+ this.request = response.request;
39
+ this.statusCode = response.statusCode;
40
+ this.statusMessage = response.statusMessage;
41
+ this.headers = response.headers;
42
+ this.body = response.body;
43
+ }
44
+ };
32
45
  async function request(url, {
33
46
  headers,
34
47
  method,
@@ -85,6 +98,9 @@ async function request(url, {
85
98
  passphrase,
86
99
  agent
87
100
  };
101
+ if (!options.headers["Accept-Encoding"] && !downloadFile && !downloadStream) {
102
+ options.headers["Accept-Encoding"] = "br,gzip";
103
+ }
88
104
  for (const key in headers)
89
105
  if (typeof headers[key] !== "undefined" && headers[key] !== null)
90
106
  options.headers[key] = headers[key];
@@ -95,57 +111,101 @@ async function request(url, {
95
111
  body = JSON.stringify(body);
96
112
  if (body && !options.headers["Content-Length"])
97
113
  options.headers["Content-Length"] = Buffer.byteLength(body);
98
- return await new Promise(function(resolve, reject) {
114
+ const requestId = randomUUID();
115
+ return await new Promise((resolve, reject) => {
99
116
  logger.debug("request %j", {
100
117
  ...options,
101
118
  body
102
119
  });
103
- const req = protocol.request(options, function(res) {
120
+ const req = protocol.request(options, (res) => {
104
121
  if (downloadStream) {
105
- res.pipe(downloadStream);
106
- downloadStream.on("finish", () => resolve(void 0));
107
- } else if (downloadFile) {
108
- const stream = createWriteStream(downloadFile);
109
- res.pipe(stream);
110
- stream.on("finish", () => resolve(void 0));
111
- } else {
112
- const raw = [];
113
- res.on("data", (chunk) => {
114
- raw.push(chunk);
122
+ res.pipe(downloadStream, { end: true });
123
+ downloadStream.on("finish", () => {
124
+ res.destroy();
125
+ downloadStream.end();
126
+ resolve(void 0);
115
127
  });
128
+ return;
129
+ }
130
+ if (downloadFile) {
131
+ logger.debug("downloadFile");
132
+ const stream2 = createWriteStream(downloadFile, { autoClose: true });
133
+ res.pipe(stream2, { end: true });
116
134
  res.on("end", () => {
117
- const data = Buffer.concat(raw).toString();
118
- logger.timeEnd(
119
- url,
120
- "response %s %s %s",
121
- res.statusCode,
122
- res.headers["content-type"],
123
- data
135
+ logger.debug("end");
136
+ res.destroy();
137
+ });
138
+ stream2.on("finish", () => {
139
+ stream2.end();
140
+ stream2.close();
141
+ logger.debug("finish");
142
+ });
143
+ stream2.on("close", () => {
144
+ logger.debug(
145
+ "finish",
146
+ res.closed,
147
+ res.destroyed,
148
+ stream2.destroyed,
149
+ stream2.closed,
150
+ req.closed,
151
+ req.destroyed
124
152
  );
125
- const response = res.statusCode >= 200 && res.statusCode < 400 ? /* @__PURE__ */ Object.create(null) : new Error();
126
- response.request = options;
127
- response.request.body = body;
128
- response.statusCode = res.statusCode;
129
- response.statusMessage = res.statusMessage;
130
- response.headers = res.headers;
131
- response.body = data;
132
- if (response.body && response.headers["content-type"] && response.headers["content-type"].includes("application/json"))
133
- try {
134
- response.body = (parse || JSON.parse)(response.body);
135
- logger.debug("response.parse JSON");
136
- } catch (error) {
137
- console.warn("response plain body", response.body);
138
- console.error(error);
139
- }
140
- if (response.statusCode >= 200 && response.statusCode < 400)
141
- resolve(response);
142
- else {
143
- logger.debug("response.error %j", response);
144
- response.message = `${res.statusMessage || res.statusCode} ${options.host}${options.path}`;
145
- reject(response);
146
- }
153
+ resolve(void 0);
147
154
  });
155
+ return;
156
+ }
157
+ let stream = res;
158
+ switch (res.headers["content-encoding"]) {
159
+ case "br":
160
+ stream = res.pipe(createBrotliDecompress());
161
+ break;
162
+ case "gzip":
163
+ stream = res.pipe(createGunzip());
164
+ break;
148
165
  }
166
+ const raw = [];
167
+ stream.on("data", (chunk) => raw.push(chunk));
168
+ stream.on("end", () => {
169
+ const data = Buffer.concat(raw).toString();
170
+ logger.timeEnd(
171
+ requestId,
172
+ "response %s %s %s %j",
173
+ res.statusCode,
174
+ res.headers["content-type"],
175
+ res.headers["content-encoding"],
176
+ data
177
+ );
178
+ const response = /* @__PURE__ */ Object.create(null);
179
+ response.request = options;
180
+ response.request.body = body;
181
+ response.statusCode = res.statusCode;
182
+ response.statusMessage = res.statusMessage;
183
+ response.headers = res.headers;
184
+ response.body = data;
185
+ if (response.body && response.headers["content-type"] && response.headers["content-type"].includes("application/json"))
186
+ try {
187
+ response.body = (parse || JSON.parse)(response.body);
188
+ logger.debug("response.parse JSON");
189
+ } catch (error) {
190
+ console.warn("response plain body", response.body);
191
+ console.error(error);
192
+ }
193
+ if (response.statusCode >= 200 && response.statusCode < 400)
194
+ resolve(response);
195
+ else {
196
+ logger.debug("response.error %j", response);
197
+ reject(
198
+ new ResponseError(
199
+ `${res.statusMessage || res.statusCode} ${options.host}${options.path}`,
200
+ response
201
+ )
202
+ );
203
+ }
204
+ });
205
+ stream.on("error", (e) => {
206
+ logger.timeEnd(requestId, "response.error %j", e);
207
+ reject(e);
208
+ });
149
209
  });
150
210
  if (body)
151
211
  req.write(body);
@@ -167,13 +227,18 @@ async function request(url, {
167
227
  req.setHeader("Content-Length", multipartBody.length);
168
228
  req.write(multipartBody);
169
229
  }
170
- req.on("error", function(e) {
171
- logger.timeEnd(url, "response.error %j", e);
230
+ req.on("error", (e) => {
231
+ logger.timeEnd(requestId, "response.error %j", e);
172
232
  reject(e);
173
233
  });
174
- logger.time(url);
234
+ req.on("timeout", () => {
235
+ logger.timeEnd(requestId, "response.timeout");
236
+ req.destroy();
237
+ reject(Error(`Timeout ${url}`));
238
+ });
239
+ logger.time(requestId);
175
240
  req.end();
176
241
  });
177
242
  }
178
243
 
179
- export { querystringify, request, setMock };
244
+ export { ResponseError, querystringify, request, setMock };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/request",
3
- "version": "0.0.4-beta.9",
3
+ "version": "0.0.5-beta.2",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -21,10 +21,10 @@
21
21
  "dist"
22
22
  ],
23
23
  "peerDependencies": {
24
- "@faasjs/logger": "0.0.4-beta.9"
24
+ "@faasjs/logger": "0.0.5-beta.2"
25
25
  },
26
26
  "devDependencies": {
27
- "@faasjs/logger": "0.0.4-beta.9"
27
+ "@faasjs/logger": "0.0.5-beta.2"
28
28
  },
29
29
  "engines": {
30
30
  "npm": ">=9.0.0",