rekwest 6.2.1 → 7.0.0

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.
Files changed (45) hide show
  1. package/README.md +11 -10
  2. package/dist/codecs.cjs +55 -0
  3. package/dist/{config.js → config.cjs} +15 -19
  4. package/dist/{cookies.js → cookies.cjs} +2 -2
  5. package/dist/{formdata.js → formdata.cjs} +8 -11
  6. package/dist/{index.js → index.cjs} +27 -15
  7. package/dist/{mixin.js → mixin.cjs} +22 -17
  8. package/dist/postflight.cjs +60 -0
  9. package/dist/{preflight.js → preflight.cjs} +11 -9
  10. package/dist/redirects.cjs +63 -0
  11. package/dist/retries.cjs +57 -0
  12. package/dist/{transfer.js → transfer.cjs} +13 -40
  13. package/dist/transform.cjs +105 -0
  14. package/dist/utils.cjs +140 -0
  15. package/dist/{validation.js → validation.cjs} +1 -1
  16. package/package.json +13 -12
  17. package/src/{ackn.mjs → ackn.js} +33 -33
  18. package/src/codecs.js +55 -0
  19. package/src/{config.mjs → config.js} +88 -93
  20. package/src/{constants.mjs → constants.js} +29 -29
  21. package/src/{cookies.mjs → cookies.js} +100 -100
  22. package/src/{formdata.mjs → formdata.js} +8 -14
  23. package/src/{index.mjs → index.js} +22 -22
  24. package/src/{mediatypes.mjs → mediatypes.js} +6 -6
  25. package/src/{mixin.mjs → mixin.js} +25 -26
  26. package/src/postflight.js +56 -0
  27. package/src/{preflight.mjs → preflight.js} +100 -91
  28. package/src/redirects.js +79 -0
  29. package/src/retries.js +51 -0
  30. package/src/transfer.js +92 -0
  31. package/src/transform.js +109 -0
  32. package/src/utils.js +152 -0
  33. package/src/{validation.mjs → validation.js} +33 -33
  34. package/dist/postflight.js +0 -117
  35. package/dist/transform.js +0 -79
  36. package/dist/utils.js +0 -188
  37. package/src/postflight.mjs +0 -136
  38. package/src/transfer.mjs +0 -121
  39. package/src/transform.mjs +0 -82
  40. package/src/utils.mjs +0 -205
  41. /package/dist/{ackn.js → ackn.cjs} +0 -0
  42. /package/dist/{constants.js → constants.cjs} +0 -0
  43. /package/dist/{errors.js → errors.cjs} +0 -0
  44. /package/dist/{mediatypes.js → mediatypes.cjs} +0 -0
  45. /package/src/{errors.mjs → errors.js} +0 -0
@@ -1,136 +0,0 @@
1
- import http2 from 'node:http2';
2
- import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
3
- import {
4
- requestCredentials,
5
- requestRedirect,
6
- requestRedirectCodes,
7
- } from './constants.mjs';
8
- import { Cookies } from './cookies.mjs';
9
- import { RequestError } from './errors.mjs';
10
- import rekwest from './index.mjs';
11
- import { mixin } from './mixin.mjs';
12
- import {
13
- admix,
14
- maxRetryAfterError,
15
- sameOrigin,
16
- } from './utils.mjs';
17
-
18
- const {
19
- HTTP2_HEADER_LOCATION,
20
- HTTP2_HEADER_RETRY_AFTER,
21
- HTTP2_HEADER_SET_COOKIE,
22
- HTTP2_METHOD_GET,
23
- HTTP2_METHOD_HEAD,
24
- HTTP2_METHOD_POST,
25
- HTTP_STATUS_BAD_REQUEST,
26
- HTTP_STATUS_FOUND,
27
- HTTP_STATUS_MOVED_PERMANENTLY,
28
- HTTP_STATUS_SEE_OTHER,
29
- } = http2.constants;
30
-
31
- export const postflight = (req, res, options, { reject, resolve }) => {
32
- const { cookies, credentials, follow, h2, redirect, url } = options;
33
- let headers;
34
-
35
- if (h2) {
36
- headers = res;
37
- res = req;
38
- } else {
39
- res.once('error', reject);
40
- }
41
-
42
- admix(res, headers, options);
43
-
44
- if (cookies !== false && res.headers[HTTP2_HEADER_SET_COOKIE]) {
45
- if (Cookies.jar.has(url.origin)) {
46
- const cookie = new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options);
47
-
48
- Cookies.jar.get(url.origin).forEach((val, key) => {
49
- if (!cookie.has(key)) {
50
- cookie.set(key, val);
51
- }
52
- });
53
- Cookies.jar.set(url.origin, cookie);
54
- } else {
55
- Cookies.jar.set(url.origin, new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options));
56
- }
57
- }
58
-
59
- Reflect.defineProperty(res, 'cookies', {
60
- enumerable: true,
61
- value: cookies !== false && Cookies.jar.has(url.origin)
62
- ? Cookies.jar.get(url.origin)
63
- : void 0,
64
- });
65
-
66
- const { statusCode } = res;
67
-
68
- if (follow && /3\d{2}/.test(statusCode) && res.headers[HTTP2_HEADER_LOCATION]) {
69
- if (!requestRedirectCodes.includes(statusCode)) {
70
- return res.emit('error', new RangeError(`Invalid status code: ${ statusCode }`));
71
- }
72
-
73
- if (redirect === requestRedirect.error) {
74
- return res.emit('error', new RequestError(`Unexpected redirect, redirect mode is set to '${ redirect }'.`));
75
- }
76
-
77
- if (redirect === requestRedirect.follow) {
78
- const location = new URL(res.headers[HTTP2_HEADER_LOCATION], url);
79
-
80
- if (!/^https?:/i.test(location.protocol)) {
81
- return res.emit('error', new RequestError('URL scheme must be "http" or "https".'));
82
- }
83
-
84
- if (!sameOrigin(location, url)) {
85
- if (credentials !== requestCredentials.include) {
86
- options.credentials = requestCredentials.omit;
87
- }
88
-
89
- options.h2 = false;
90
- }
91
-
92
- if (statusCode !== HTTP_STATUS_SEE_OTHER && options.body?.pipe?.constructor === Function) {
93
- return res.emit('error', new RequestError(`Unable to ${ redirect } redirect with streamable body.`));
94
- }
95
-
96
- if (([
97
- HTTP_STATUS_MOVED_PERMANENTLY,
98
- HTTP_STATUS_FOUND,
99
- ].includes(statusCode) && options.method === HTTP2_METHOD_POST) || (statusCode === HTTP_STATUS_SEE_OTHER && ![
100
- HTTP2_METHOD_GET,
101
- HTTP2_METHOD_HEAD,
102
- ].includes(options.method))) {
103
- for (const it of Object.keys(options.headers).filter((val) => /^content-/i.test(val))) {
104
- Reflect.deleteProperty(options.headers, it);
105
- }
106
-
107
- options.body = null;
108
- options.method = HTTP2_METHOD_GET;
109
- }
110
-
111
- options.follow--;
112
- options.redirected = true;
113
- options.url = location;
114
-
115
- if (statusCode === HTTP_STATUS_MOVED_PERMANENTLY && res.headers[HTTP2_HEADER_RETRY_AFTER]) {
116
- let interval = res.headers[HTTP2_HEADER_RETRY_AFTER];
117
-
118
- interval = Number(interval) * 1e3 || new Date(interval) - Date.now();
119
-
120
- if (interval > options.maxRetryAfter) {
121
- return res.emit('error', maxRetryAfterError(interval, { cause: mixin(res, options) }));
122
- }
123
-
124
- return setTimeoutPromise(interval).then(() => rekwest(options.url, options).then(resolve, reject));
125
- }
126
-
127
- return rekwest(options.url, options).then(resolve, reject);
128
- }
129
- }
130
-
131
- if (statusCode >= HTTP_STATUS_BAD_REQUEST) {
132
- return reject(mixin(res, options));
133
- }
134
-
135
- resolve(mixin(res, options));
136
- };
package/src/transfer.mjs DELETED
@@ -1,121 +0,0 @@
1
- import http from 'node:http';
2
- import http2 from 'node:http2';
3
- import https from 'node:https';
4
- import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
5
- import { ackn } from './ackn.mjs';
6
- import { RequestError } from './errors.mjs';
7
- import { postflight } from './postflight.mjs';
8
- import { preflight } from './preflight.mjs';
9
- import { transform } from './transform.mjs';
10
- import {
11
- affix,
12
- dispatch,
13
- maxRetryAfterError,
14
- } from './utils.mjs';
15
-
16
- const {
17
- HTTP2_HEADER_RETRY_AFTER,
18
- } = http2.constants;
19
-
20
- export const transfer = async (options, overact) => {
21
- const { digest, redirected, thenable, url } = options;
22
-
23
- if (options.follow === 0) {
24
- throw new RequestError(`Maximum redirect reached at: ${ url.href }`);
25
- }
26
-
27
- if (url.protocol === 'https:') {
28
- options = !options.h2 ? await ackn(options) : {
29
- ...options,
30
- createConnection: null,
31
- protocol: url.protocol,
32
- };
33
- } else if (Reflect.has(options, 'alpnProtocol')) {
34
- for (const it of [
35
- 'alpnProtocol',
36
- 'createConnection',
37
- 'h2',
38
- 'protocol',
39
- ]) { Reflect.deleteProperty(options, it); }
40
- }
41
-
42
- try {
43
- options = await transform(preflight(options));
44
- } catch (ex) {
45
- options.createConnection?.().destroy();
46
- throw ex;
47
- }
48
-
49
- const promise = new Promise((resolve, reject) => {
50
- let client, req;
51
-
52
- if (options.h2) {
53
- client = http2.connect(url.origin, options);
54
- req = client.request(options.headers, options);
55
- } else {
56
- const { request } = url.protocol === 'http:' ? http : https;
57
-
58
- req = request(url, options);
59
- }
60
-
61
- affix(client, req, options);
62
-
63
- req.once('aborted', reject);
64
- req.once('error', reject);
65
- req.once('frameError', reject);
66
- req.once('goaway', reject);
67
- req.once('response', (res) => postflight(req, res, options, {
68
- reject,
69
- resolve,
70
- }));
71
-
72
- dispatch(options, req);
73
- });
74
-
75
- try {
76
- const res = await promise;
77
-
78
- if (digest && !redirected) {
79
- res.body = await res.body();
80
- }
81
-
82
- return res;
83
- } catch (ex) {
84
- const { maxRetryAfter, retry } = options;
85
-
86
- if (retry?.attempts > 0) {
87
- if (retry.errorCodes?.includes(ex.code) || retry.statusCodes?.includes(ex.statusCode)) {
88
- let { interval } = retry;
89
-
90
- if (retry.retryAfter && ex.headers?.[HTTP2_HEADER_RETRY_AFTER]) {
91
- interval = ex.headers[HTTP2_HEADER_RETRY_AFTER];
92
- interval = Number(interval) * 1e3 || new Date(interval) - Date.now();
93
- if (interval > maxRetryAfter) {
94
- throw maxRetryAfterError(interval, { cause: ex });
95
- }
96
- } else {
97
- interval = new Function('interval', `return Math.ceil(${ retry.backoffStrategy });`)(interval);
98
- }
99
-
100
- if (interval < 0) {
101
- interval = 0;
102
- }
103
-
104
- retry.attempts--;
105
- retry.interval = interval;
106
-
107
- return setTimeoutPromise(interval).then(() => overact(url, options));
108
- }
109
- }
110
-
111
- if (digest && !redirected && ex.body) {
112
- ex.body = await ex.body();
113
- }
114
-
115
- if (!thenable) {
116
- throw ex;
117
- } else {
118
- return ex;
119
- }
120
- }
121
- };
package/src/transform.mjs DELETED
@@ -1,82 +0,0 @@
1
- import http2 from 'node:http2';
2
- import {
3
- isReadable,
4
- Readable,
5
- } from 'node:stream';
6
- import { buffer } from 'node:stream/consumers';
7
- import { types } from 'node:util';
8
- import { FormData } from './formdata.mjs';
9
- import {
10
- APPLICATION_FORM_URLENCODED,
11
- APPLICATION_JSON,
12
- APPLICATION_OCTET_STREAM,
13
- } from './mediatypes.mjs';
14
- import {
15
- compress,
16
- isFileLike,
17
- isReadableStream,
18
- } from './utils.mjs';
19
-
20
- const {
21
- HTTP2_HEADER_CONTENT_ENCODING,
22
- HTTP2_HEADER_CONTENT_LENGTH,
23
- HTTP2_HEADER_CONTENT_TYPE,
24
- } = http2.constants;
25
-
26
- export const transform = async (options) => {
27
- let { body, headers } = options;
28
-
29
- if (!body) {
30
- return options;
31
- }
32
-
33
- if (isFileLike(body)) {
34
- headers = {
35
- [HTTP2_HEADER_CONTENT_LENGTH]: body.size,
36
- [HTTP2_HEADER_CONTENT_TYPE]: body.type || APPLICATION_OCTET_STREAM,
37
- };
38
- body = body.stream();
39
- } else if (FormData.alike(body)) {
40
- body = FormData.actuate(body);
41
- headers = { [HTTP2_HEADER_CONTENT_TYPE]: body.contentType };
42
- } else if (!Buffer.isBuffer(body)) {
43
- if (types.isAnyArrayBuffer(body)) {
44
- body = Buffer.from(body);
45
- } else if (types.isArrayBufferView(body)) {
46
- body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
47
- } else if (body === Object(body) && !Reflect.has(body, Symbol.asyncIterator)) {
48
- if (body.constructor === URLSearchParams) {
49
- headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_FORM_URLENCODED };
50
- body = body.toString();
51
- } else if (!(!Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
52
- headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_JSON };
53
- body = JSON.stringify(body);
54
- }
55
- }
56
- }
57
-
58
- const encodings = options.headers[HTTP2_HEADER_CONTENT_ENCODING];
59
-
60
- if (body === Object(body)
61
- && (Reflect.has(body, Symbol.asyncIterator) || (!Array.isArray(body) && Reflect.has(body, Symbol.iterator)))) {
62
- body = isReadable(body) ? (isReadableStream(body) ? Readable.fromWeb(body) : body) : Readable.from(body);
63
- body = encodings ? compress(body, encodings, options) : body;
64
- } else if (encodings) {
65
- body = await buffer(compress(Readable.from(body), encodings, options));
66
- }
67
-
68
- Object.assign(options.headers, {
69
- ...headers,
70
- ...!body[Symbol.asyncIterator] && {
71
- [HTTP2_HEADER_CONTENT_LENGTH]: Buffer.byteLength(body),
72
- },
73
- ...options.headers[HTTP2_HEADER_CONTENT_TYPE] && {
74
- [HTTP2_HEADER_CONTENT_TYPE]: options.headers[HTTP2_HEADER_CONTENT_TYPE],
75
- },
76
- });
77
-
78
- return {
79
- ...options,
80
- body,
81
- };
82
- };
package/src/utils.mjs DELETED
@@ -1,205 +0,0 @@
1
- import {
2
- Blob,
3
- File,
4
- } from 'node:buffer';
5
- import http2 from 'node:http2';
6
- import { pipeline } from 'node:stream';
7
- import zlib from 'node:zlib';
8
- import config, { isZstdSupported } from './config.mjs';
9
- import {
10
- RequestError,
11
- TimeoutError,
12
- } from './errors.mjs';
13
-
14
- const {
15
- HTTP2_HEADER_RETRY_AFTER,
16
- HTTP2_HEADER_STATUS,
17
- } = http2.constants;
18
-
19
- export const admix = (res, headers, options) => {
20
- const { h2 } = options;
21
-
22
- if (h2) {
23
- Reflect.defineProperty(res, 'headers', {
24
- enumerable: true,
25
- value: headers,
26
- });
27
-
28
- Reflect.defineProperty(res, 'httpVersion', {
29
- enumerable: true,
30
- value: `${ h2 + 1 }.0`,
31
- });
32
-
33
- Reflect.defineProperty(res, 'statusCode', {
34
- enumerable: true,
35
- value: headers[HTTP2_HEADER_STATUS],
36
- });
37
- }
38
-
39
- Reflect.defineProperty(res, 'ok', {
40
- enumerable: true,
41
- value: /^2\d{2}$/.test(res.statusCode),
42
- });
43
-
44
- Reflect.defineProperty(res, 'redirected', {
45
- enumerable: true,
46
- value: !!options.redirected,
47
- });
48
- };
49
-
50
- export const affix = (client, req, options) => {
51
- req.once('close', () => client?.close());
52
- req.once('end', () => client?.close());
53
- req.once('timeout', () => req.destroy(new TimeoutError(`Timed out after ${ options.timeout } ms.`)));
54
- req.once('trailers', (trailers) => {
55
- Reflect.defineProperty(req, 'trailers', {
56
- enumerable: true,
57
- value: trailers,
58
- });
59
- });
60
- };
61
-
62
- export const brandCheck = (value, ctor) => {
63
- if (!(value instanceof ctor)) {
64
- throw new TypeError('Illegal invocation');
65
- }
66
- };
67
-
68
- export const compress = (readable, encodings = '', { compression } = {}) => {
69
- const encoders = [];
70
-
71
- encodings = unwind(encodings);
72
-
73
- for (const encoding of encodings) {
74
- if (/\bbr\b/i.test(encoding)) {
75
- encoders.push(zlib.createBrotliCompress(compression?.brotliOptions));
76
- } else if (/\bdeflate(?!-(?:\w+)?)\b/i.test(encoding)) {
77
- encoders.push(zlib.createDeflate(compression?.zlibOptions));
78
- } else if (/\bdeflate-raw\b/i.test(encoding)) {
79
- encoders.push(zlib.createDeflateRaw(compression?.zlibOptions));
80
- } else if (/\bgzip\b/i.test(encoding)) {
81
- encoders.push(zlib.createGzip(compression?.zlibOptions));
82
- } else if (isZstdSupported && /\bzstd\b/i.test(encoding)) {
83
- encoders.push(zlib.createZstdCompress(compression?.zstdOptions));
84
- } else {
85
- return readable;
86
- }
87
- }
88
-
89
- return pipeline(readable, ...encoders, () => void 0);
90
- };
91
-
92
- export const copyWithMerge = (target, ...rest) => {
93
- target = structuredClone(target);
94
- if (!rest.length) {
95
- return target;
96
- }
97
-
98
- return merge(target, ...rest);
99
- };
100
-
101
- export const decompress = (readable, encodings = '', { decompression } = {}) => {
102
- const decoders = [];
103
-
104
- encodings = unwind(encodings);
105
-
106
- for (const encoding of encodings) {
107
- if (/\bbr\b/i.test(encoding)) {
108
- decoders.push(zlib.createBrotliDecompress(decompression?.brotliOptions));
109
- } else if (/\bdeflate(?!-(?:\w+)?)\b/i.test(encoding)) {
110
- decoders.push(zlib.createInflate(decompression?.zlibOptions));
111
- } else if (/\bdeflate-raw\b/i.test(encoding)) {
112
- decoders.push(zlib.createInflateRaw(decompression?.zlibOptions));
113
- } else if (/\bgzip\b/i.test(encoding)) {
114
- decoders.push(zlib.createGunzip(decompression?.zlibOptions));
115
- } else if (isZstdSupported && /\bzstd\b/i.test(encoding)) {
116
- decoders.push(zlib.createZstdDecompress(decompression?.zstdOptions));
117
- } else {
118
- return readable;
119
- }
120
- }
121
-
122
- return pipeline(readable, ...decoders, () => void 0);
123
- };
124
-
125
- export const dispatch = ({ body }, req) => {
126
- if (body?.pipe?.constructor === Function) {
127
- body.pipe(req);
128
- } else {
129
- req.end(body);
130
- }
131
- };
132
-
133
- export const isFileLike = (instance) => {
134
- return [
135
- Blob.name,
136
- File.name,
137
- ].includes(instance?.[Symbol.toStringTag]);
138
- };
139
-
140
- export const isReadableStream = (instance) => {
141
- return ReadableStream.name === instance?.[Symbol.toStringTag];
142
- };
143
-
144
- export const maxRetryAfter = Symbol('maxRetryAfter');
145
-
146
- export const maxRetryAfterError = (
147
- interval,
148
- options,
149
- ) => new RequestError(`Maximum '${ HTTP2_HEADER_RETRY_AFTER }' limit exceeded: ${ interval } ms.`, options);
150
-
151
- export const merge = (target, ...rest) => {
152
- rest = rest.filter((it) => it === Object(it));
153
- for (const source of rest) {
154
- for (const key of Object.getOwnPropertyNames(source)) {
155
- const sv = source[key];
156
- const tv = target[key];
157
-
158
- if (Object(sv) === sv && Object(tv) === tv) {
159
- target[key] = merge(tv, sv);
160
- continue;
161
- }
162
-
163
- target[key] = source[key];
164
- }
165
- }
166
-
167
- return target;
168
- };
169
-
170
- export const normalize = (url, options = {}) => {
171
- if (!options.redirected) {
172
- options = copyWithMerge(config.defaults, options);
173
- }
174
-
175
- if (options.trimTrailingSlashes) {
176
- url = `${ url }`.replace(/(?<!:)\/+/g, '/');
177
- }
178
-
179
- if (options.stripTrailingSlash) {
180
- url = `${ url }`.replace(/\/$|\/(?=#)|\/(?=\?)/g, '');
181
- }
182
-
183
- url = new URL(url, options.baseURL);
184
-
185
- return Object.assign(options, { url });
186
- };
187
-
188
- export const sameOrigin = (a, b) => a.protocol === b.protocol && a.hostname === b.hostname && a.port === b.port;
189
-
190
- export async function* tap(value) {
191
- if (Reflect.has(value, Symbol.asyncIterator)) {
192
- yield* value;
193
- } else if (value.stream) {
194
- yield* value.stream();
195
- } else {
196
- yield await value.arrayBuffer();
197
- }
198
- }
199
-
200
- export const toCamelCase = (str) => str?.toLowerCase().replace(
201
- /\p{Punctuation}.|\p{White_Space}./gu,
202
- (val) => val.replace(/\p{Punctuation}+|\p{White_Space}+/gu, '').toUpperCase(),
203
- );
204
-
205
- export const unwind = (encodings) => encodings.split(',').map((it) => it.trim());
File without changes
File without changes
File without changes
File without changes
File without changes