rekwest 3.3.3 → 4.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.
package/dist/utils.js CHANGED
@@ -1,32 +1,21 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.revise = exports.redirects = exports.preflight = exports.mixin = exports.merge = exports.dispatch = exports.decompress = exports.compress = exports.collate = exports.affix = exports.admix = void 0;
4
+ exports.sanitize = exports.redirects = exports.preflight = exports.mixin = exports.merge = exports.dispatch = exports.decompress = exports.compress = exports.collate = exports.affix = exports.admix = void 0;
5
5
  exports.tap = tap;
6
6
  exports.transform = void 0;
7
-
8
7
  var _nodeBuffer = require("node:buffer");
9
-
10
8
  var _nodeHttp = _interopRequireDefault(require("node:http2"));
11
-
12
9
  var _nodeStream = require("node:stream");
13
-
10
+ var _consumers = require("node:stream/consumers");
14
11
  var _nodeUtil = require("node:util");
15
-
16
12
  var _nodeZlib = _interopRequireDefault(require("node:zlib"));
17
-
18
- var _cookies = require("./cookies.js");
19
-
20
- var _errors = require("./errors.js");
21
-
22
- var _file = require("./file.js");
23
-
24
- var _formdata = require("./formdata.js");
25
-
26
- var _mediatypes = require("./mediatypes.js");
27
-
13
+ var _cookies = require("./cookies");
14
+ var _errors = require("./errors");
15
+ var _file = require("./file");
16
+ var _formdata = require("./formdata");
17
+ var _mediatypes = require("./mediatypes");
28
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
29
-
30
19
  const {
31
20
  HTTP2_HEADER_ACCEPT,
32
21
  HTTP2_HEADER_ACCEPT_ENCODING,
@@ -42,18 +31,11 @@ const {
42
31
  HTTP2_METHOD_GET,
43
32
  HTTP2_METHOD_HEAD
44
33
  } = _nodeHttp.default.constants;
45
- const brotliCompress = (0, _nodeUtil.promisify)(_nodeZlib.default.brotliCompress);
46
- const brotliDecompress = (0, _nodeUtil.promisify)(_nodeZlib.default.brotliDecompress);
47
- const gzip = (0, _nodeUtil.promisify)(_nodeZlib.default.gzip);
48
- const gunzip = (0, _nodeUtil.promisify)(_nodeZlib.default.gunzip);
49
- const deflate = (0, _nodeUtil.promisify)(_nodeZlib.default.deflate);
50
- const inflate = (0, _nodeUtil.promisify)(_nodeZlib.default.inflate);
51
-
34
+ const unwind = encodings => encodings.split(',').map(it => it.trim());
52
35
  const admix = (res, headers, options) => {
53
36
  const {
54
37
  h2
55
38
  } = options;
56
-
57
39
  if (h2) {
58
40
  Reflect.defineProperty(res, 'headers', {
59
41
  enumerable: true,
@@ -68,7 +50,6 @@ const admix = (res, headers, options) => {
68
50
  value: headers[HTTP2_HEADER_STATUS]
69
51
  });
70
52
  }
71
-
72
53
  Reflect.defineProperty(res, 'ok', {
73
54
  enumerable: true,
74
55
  value: /^2\d{2}$/.test(res.statusCode)
@@ -78,9 +59,7 @@ const admix = (res, headers, options) => {
78
59
  value: !!options.redirected
79
60
  });
80
61
  };
81
-
82
62
  exports.admix = admix;
83
-
84
63
  const affix = (client, req, options) => {
85
64
  req.once('end', () => client?.close());
86
65
  req.once('timeout', () => req.destroy(new _errors.TimeoutError(`Timed out after ${options.timeout} ms.`)));
@@ -91,74 +70,66 @@ const affix = (client, req, options) => {
91
70
  });
92
71
  });
93
72
  };
94
-
95
73
  exports.affix = affix;
96
-
97
74
  const collate = (entity, primordial) => {
98
75
  if (entity?.constructor !== primordial) {
99
76
  throw new TypeError('Illegal invocation');
100
77
  }
101
78
  };
102
-
103
79
  exports.collate = collate;
104
-
105
- const compress = (buf, encoding, {
106
- async = false
107
- } = {}) => {
108
- encoding &&= encoding.match(/(?<encoding>\bbr\b|\bdeflate\b|\bgzip\b)/i)?.groups.encoding.toLowerCase();
109
- const compressor = {
110
- br: async ? brotliCompress : _nodeZlib.default.brotliCompressSync,
111
- deflate: async ? deflate : _nodeZlib.default.deflateSync,
112
- gzip: async ? gzip : _nodeZlib.default.gzipSync
113
- }[encoding];
114
- return compressor?.(buf) ?? (async ? Promise.resolve(buf) : buf);
80
+ const compress = (readable, encodings = '') => {
81
+ const encoders = [];
82
+ encodings = unwind(encodings);
83
+ for (const encoding of encodings) {
84
+ if (/\bbr\b/i.test(encoding)) {
85
+ encoders.push(_nodeZlib.default.createBrotliCompress());
86
+ } else if (/\bdeflate(?!-(?:\w+)?)\b/i.test(encoding)) {
87
+ encoders.push(_nodeZlib.default.createDeflate());
88
+ } else if (/\bdeflate-raw\b/i.test(encoding)) {
89
+ encoders.push(_nodeZlib.default.createDeflateRaw());
90
+ } else if (/\bgzip\b/i.test(encoding)) {
91
+ encoders.push(_nodeZlib.default.createGzip());
92
+ } else {
93
+ return readable;
94
+ }
95
+ }
96
+ return (0, _nodeStream.pipeline)(readable, ...encoders, () => void 0);
115
97
  };
116
-
117
98
  exports.compress = compress;
118
-
119
- const decompress = (buf, encoding, {
120
- async = false
121
- } = {}) => {
122
- encoding &&= encoding.match(/(?<encoding>\bbr\b|\bdeflate\b|\bgzip\b)/i)?.groups.encoding.toLowerCase();
123
- const decompressor = {
124
- br: async ? brotliDecompress : _nodeZlib.default.brotliDecompressSync,
125
- deflate: async ? inflate : _nodeZlib.default.inflateSync,
126
- gzip: async ? gunzip : _nodeZlib.default.gunzipSync
127
- }[encoding];
128
- return decompressor?.(buf) ?? (async ? Promise.resolve(buf) : buf);
99
+ const decompress = (readable, encodings = '') => {
100
+ const decoders = [];
101
+ encodings = unwind(encodings);
102
+ for (const encoding of encodings) {
103
+ if (/\bbr\b/i.test(encoding)) {
104
+ decoders.push(_nodeZlib.default.createBrotliDecompress());
105
+ } else if (/\bdeflate(?!-(?:\w+)?)\b/i.test(encoding)) {
106
+ decoders.push(_nodeZlib.default.createInflate());
107
+ } else if (/\bdeflate-raw\b/i.test(encoding)) {
108
+ decoders.push(_nodeZlib.default.createInflateRaw());
109
+ } else if (/\bgzip\b/i.test(encoding)) {
110
+ decoders.push(_nodeZlib.default.createGunzip());
111
+ } else {
112
+ return readable;
113
+ }
114
+ }
115
+ return (0, _nodeStream.pipeline)(readable, ...decoders, () => void 0);
129
116
  };
130
-
131
117
  exports.decompress = decompress;
132
-
133
- const dispatch = (req, {
134
- body,
135
- headers
136
- }) => {
137
- if (body === Object(body) && !Buffer.isBuffer(body)) {
138
- if (body.pipe?.constructor !== Function && (Reflect.has(body, Symbol.asyncIterator) || Reflect.has(body, Symbol.iterator))) {
139
- body = _nodeStream.Readable.from(body);
140
- }
141
-
142
- const compressor = {
143
- br: _nodeZlib.default.createBrotliCompress,
144
- deflate: _nodeZlib.default.createDeflate,
145
- gzip: _nodeZlib.default.createGzip
146
- }[headers[HTTP2_HEADER_CONTENT_ENCODING]] ?? _nodeStream.PassThrough;
147
- body.pipe(compressor()).pipe(req);
118
+ const dispatch = ({
119
+ body
120
+ }, req) => {
121
+ if (body?.pipe?.constructor === Function) {
122
+ body.pipe(req);
148
123
  } else {
149
124
  req.end(body);
150
125
  }
151
126
  };
152
-
153
127
  exports.dispatch = dispatch;
154
-
155
128
  const merge = (target = {}, ...rest) => {
156
129
  target = JSON.parse(JSON.stringify(target));
157
-
158
130
  if (!rest.length) {
159
131
  return target;
160
132
  }
161
-
162
133
  rest.filter(it => it === Object(it)).forEach(it => {
163
134
  Object.entries(it).reduce((acc, [key, val]) => {
164
135
  if ([acc[key]?.constructor, val?.constructor].every(it => [Array, Object].includes(it))) {
@@ -170,15 +141,12 @@ const merge = (target = {}, ...rest) => {
170
141
  } else {
171
142
  acc[key] = val;
172
143
  }
173
-
174
144
  return acc;
175
145
  }, target);
176
146
  });
177
147
  return target;
178
148
  };
179
-
180
149
  exports.merge = merge;
181
-
182
150
  const mixin = (res, {
183
151
  digest = false,
184
152
  parse = false
@@ -224,63 +192,48 @@ const mixin = (res, {
224
192
  }
225
193
  });
226
194
  }
227
-
228
195
  return Object.defineProperties(res, {
229
196
  body: {
230
197
  enumerable: true,
231
198
  value: async function () {
232
199
  collate(this, res?.constructor);
233
-
234
200
  if (this.bodyUsed) {
235
201
  throw new TypeError('Response stream already read');
236
202
  }
237
-
238
- let spool = [];
239
-
240
- for await (const chunk of this) {
241
- spool.push(chunk);
203
+ let body = [];
204
+ for await (const chunk of decompress(this, this.headers[HTTP2_HEADER_CONTENT_ENCODING])) {
205
+ body.push(chunk);
242
206
  }
243
-
244
- spool = Buffer.concat(spool);
245
-
246
- if (spool.length) {
247
- spool = await decompress(spool, this.headers[HTTP2_HEADER_CONTENT_ENCODING], {
248
- async: true
249
- });
207
+ body = Buffer.concat(body);
208
+ if (!body.length && parse) {
209
+ return null;
250
210
  }
251
-
252
- if (spool.length && parse) {
211
+ if (body.length && parse) {
253
212
  const contentType = this.headers[HTTP2_HEADER_CONTENT_TYPE] ?? '';
254
213
  const charset = contentType.split(';').find(it => /charset=/i.test(it))?.toLowerCase().replace('charset=', '').replace('iso-8859-1', 'latin1').trim() || 'utf-8';
255
-
256
214
  if (/\bjson\b/i.test(contentType)) {
257
- spool = JSON.parse(spool.toString(charset));
258
- } else if (/\b(text|xml)\b/i.test(contentType)) {
259
- if (/\b(latin1|ucs-2|utf-(8|16le))\b/.test(charset)) {
260
- spool = spool.toString(charset);
215
+ body = JSON.parse(body.toString(charset));
216
+ } else if (/\b(?:text|xml)\b/i.test(contentType)) {
217
+ if (/\b(?:latin1|ucs-2|utf-(?:8|16le))\b/.test(charset)) {
218
+ body = body.toString(charset);
261
219
  } else {
262
- spool = new TextDecoder(charset).decode(spool);
220
+ body = new TextDecoder(charset).decode(body);
263
221
  }
264
222
  }
265
223
  }
266
-
267
- return spool;
224
+ return body;
268
225
  },
269
226
  writable: true
270
227
  },
271
228
  bodyUsed: {
272
229
  enumerable: true,
273
-
274
230
  get() {
275
231
  return this.readableEnded;
276
232
  }
277
-
278
233
  }
279
234
  });
280
235
  };
281
-
282
236
  exports.mixin = mixin;
283
-
284
237
  const preflight = options => {
285
238
  const {
286
239
  cookies,
@@ -290,14 +243,11 @@ const preflight = options => {
290
243
  redirected,
291
244
  url
292
245
  } = options;
293
-
294
246
  if (h2) {
295
247
  options.endStream = [HTTP2_METHOD_GET, HTTP2_METHOD_HEAD].includes(method);
296
248
  }
297
-
298
249
  if (cookies !== false) {
299
250
  let cookie = _cookies.Cookies.jar.get(url.origin);
300
-
301
251
  if (cookies === Object(cookies) && !redirected) {
302
252
  if (cookie) {
303
253
  new _cookies.Cookies(cookies).forEach(function (val, key) {
@@ -305,24 +255,22 @@ const preflight = options => {
305
255
  }, cookie);
306
256
  } else {
307
257
  cookie = new _cookies.Cookies(cookies);
308
-
309
258
  _cookies.Cookies.jar.set(url.origin, cookie);
310
259
  }
311
260
  }
312
-
313
- options.headers = { ...(cookie && {
261
+ options.headers = {
262
+ ...(cookie && {
314
263
  [HTTP2_HEADER_COOKIE]: cookie
315
264
  }),
316
265
  ...headers
317
266
  };
318
267
  }
319
-
320
268
  options.digest ??= true;
321
269
  options.follow ??= 20;
322
270
  options.h2 ??= h2;
323
271
  options.headers = {
324
272
  [HTTP2_HEADER_ACCEPT]: `${_mediatypes.APPLICATION_JSON}, ${_mediatypes.TEXT_PLAIN}, ${_mediatypes.WILDCARD}`,
325
- [HTTP2_HEADER_ACCEPT_ENCODING]: 'br, deflate, gzip, identity',
273
+ [HTTP2_HEADER_ACCEPT_ENCODING]: 'br, deflate, deflate-raw, gzip, identity',
326
274
  ...Object.entries(options.headers ?? {}).reduce((acc, [key, val]) => (acc[key.toLowerCase()] = val, acc), {}),
327
275
  ...(h2 && {
328
276
  [HTTP2_HEADER_AUTHORITY]: url.host,
@@ -334,17 +282,14 @@ const preflight = options => {
334
282
  options.method ??= method;
335
283
  options.parse ??= true;
336
284
  options.redirect ??= redirects.follow;
337
-
338
285
  if (!Object.values(redirects).includes(options.redirect)) {
339
286
  options.createConnection?.().destroy();
340
287
  throw new TypeError(`Failed to read the 'redirect' property from 'options': The provided value '${options.redirect}' is not a valid enum value.`);
341
288
  }
342
-
343
289
  options.redirected ??= false;
344
290
  options.thenable ??= false;
345
291
  return options;
346
292
  };
347
-
348
293
  exports.preflight = preflight;
349
294
  const redirects = {
350
295
  error: 'error',
@@ -352,23 +297,16 @@ const redirects = {
352
297
  manual: 'manual'
353
298
  };
354
299
  exports.redirects = redirects;
355
-
356
- const revise = ({
357
- url,
358
- options
359
- }) => {
300
+ const sanitize = (url, options = {}) => {
360
301
  if (options.trimTrailingSlashes) {
361
302
  url = `${url}`.replace(/(?<!:)\/+/gi, '/');
362
303
  }
363
-
364
304
  url = new URL(url);
365
305
  return Object.assign(options, {
366
306
  url
367
307
  });
368
308
  };
369
-
370
- exports.revise = revise;
371
-
309
+ exports.sanitize = sanitize;
372
310
  async function* tap(value) {
373
311
  if (Reflect.has(value, Symbol.asyncIterator)) {
374
312
  yield* value;
@@ -378,57 +316,63 @@ async function* tap(value) {
378
316
  yield await value.arrayBuffer();
379
317
  }
380
318
  }
381
-
382
- const transform = (body, options) => {
383
- let headers = {};
384
-
385
- if (_nodeUtil.types.isAnyArrayBuffer(body) && !Buffer.isBuffer(body)) {
386
- body = Buffer.from(body);
387
- } else if (_nodeUtil.types.isArrayBufferView(body) && !Buffer.isBuffer(body)) {
388
- body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
319
+ const transform = async options => {
320
+ let {
321
+ body,
322
+ headers
323
+ } = options;
324
+ if (!body) {
325
+ return options;
389
326
  }
390
-
391
327
  if (_file.File.alike(body)) {
392
328
  headers = {
393
329
  [HTTP2_HEADER_CONTENT_LENGTH]: body.size,
394
330
  [HTTP2_HEADER_CONTENT_TYPE]: body.type || _mediatypes.APPLICATION_OCTET_STREAM
395
331
  };
396
- body = body.stream?.() ?? _nodeStream.Readable.from(tap(body));
332
+ body = body.stream();
397
333
  } else if (_formdata.FormData.alike(body)) {
398
334
  body = _formdata.FormData.actuate(body);
399
335
  headers = {
400
336
  [HTTP2_HEADER_CONTENT_TYPE]: body.contentType
401
337
  };
402
- } else if (body === Object(body) && !Reflect.has(body, Symbol.asyncIterator)) {
403
- if (body.constructor === URLSearchParams) {
404
- headers = {
405
- [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes.APPLICATION_FORM_URLENCODED
406
- };
407
- body = body.toString();
408
- } else if (!Buffer.isBuffer(body) && !(!Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
409
- headers = {
410
- [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes.APPLICATION_JSON
411
- };
412
- body = JSON.stringify(body);
413
- }
414
-
415
- if (Buffer.isBuffer(body) || body !== Object(body)) {
416
- if (options.headers[HTTP2_HEADER_CONTENT_ENCODING]) {
417
- body = compress(body, options.headers[HTTP2_HEADER_CONTENT_ENCODING]);
338
+ } else if (!Buffer.isBuffer(body)) {
339
+ if (_nodeUtil.types.isAnyArrayBuffer(body)) {
340
+ body = Buffer.from(body);
341
+ } else if (_nodeUtil.types.isArrayBufferView(body)) {
342
+ body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
343
+ } else if (body === Object(body) && !Reflect.has(body, Symbol.asyncIterator)) {
344
+ if (body.constructor === URLSearchParams) {
345
+ headers = {
346
+ [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes.APPLICATION_FORM_URLENCODED
347
+ };
348
+ body = body.toString();
349
+ } else if (!(!Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
350
+ headers = {
351
+ [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes.APPLICATION_JSON
352
+ };
353
+ body = JSON.stringify(body);
418
354
  }
419
-
420
- headers = { ...headers,
421
- [HTTP2_HEADER_CONTENT_LENGTH]: Buffer.byteLength(body)
422
- };
423
355
  }
424
356
  }
425
-
426
- Object.assign(options.headers, { ...headers,
357
+ const encodings = options.headers[HTTP2_HEADER_CONTENT_ENCODING];
358
+ if (encodings) {
359
+ if (Reflect.has(body, Symbol.asyncIterator)) {
360
+ body = compress(_nodeStream.Readable.from(body), encodings);
361
+ } else {
362
+ body = await (0, _consumers.buffer)(compress(_nodeStream.Readable.from(body), encodings));
363
+ }
364
+ } else if (body === Object(body) && (Reflect.has(body, Symbol.asyncIterator) || !Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
365
+ body = _nodeStream.Readable.from(body);
366
+ }
367
+ Object.assign(options.headers, {
368
+ ...headers,
427
369
  ...(options.headers[HTTP2_HEADER_CONTENT_TYPE] && {
428
370
  [HTTP2_HEADER_CONTENT_TYPE]: options.headers[HTTP2_HEADER_CONTENT_TYPE]
429
371
  })
430
372
  });
431
- return body;
373
+ return {
374
+ ...options,
375
+ body
376
+ };
432
377
  };
433
-
434
378
  exports.transform = transform;
package/package.json CHANGED
@@ -8,18 +8,18 @@
8
8
  "url": "https://github.com/bricss/rekwest/issues"
9
9
  },
10
10
  "devDependencies": {
11
- "@babel/cli": "^7.18.10",
12
- "@babel/core": "^7.19.1",
11
+ "@babel/cli": "^7.19.3",
12
+ "@babel/core": "^7.20.2",
13
13
  "@babel/eslint-parser": "^7.19.1",
14
- "@babel/preset-env": "^7.19.1",
14
+ "@babel/preset-env": "^7.20.2",
15
15
  "c8": "^7.12.0",
16
- "eslint": "^8.23.1",
17
- "eslint-config-ultra-refined": "^2.5.0",
18
- "mocha": "^10.0.0"
16
+ "eslint": "^8.27.0",
17
+ "eslint-config-ultra-refined": "^2.10.0",
18
+ "mocha": "^10.1.0"
19
19
  },
20
20
  "description": "The robust request library that humanity deserves 🌐",
21
21
  "engines": {
22
- "node": ">=16.5.x"
22
+ "node": ">=16.7.x"
23
23
  },
24
24
  "exports": {
25
25
  "import": "./src/index.mjs",
@@ -32,11 +32,12 @@
32
32
  "homepage": "https://github.com/bricss/rekwest#readme",
33
33
  "keywords": [
34
34
  "backoff",
35
- "compression",
35
+ "brotli",
36
36
  "cookie",
37
+ "deflate",
37
38
  "fetch",
38
- "fetch-alike",
39
39
  "formdata",
40
+ "gzip",
40
41
  "http",
41
42
  "https",
42
43
  "h2",
@@ -46,10 +47,10 @@
46
47
  "request",
47
48
  "redirect",
48
49
  "retry",
49
- "retry-after"
50
+ "retry-after",
51
+ "stream"
50
52
  ],
51
53
  "license": "MIT",
52
- "main": "./dist/index.js",
53
54
  "name": "rekwest",
54
55
  "repository": {
55
56
  "type": "git",
@@ -66,5 +67,5 @@
66
67
  "test:bail": "mocha --bail",
67
68
  "test:cover": "c8 --include=src --reporter=lcov --reporter=text npm test"
68
69
  },
69
- "version": "3.3.3"
70
+ "version": "4.0.0"
70
71
  }
package/src/cookies.mjs CHANGED
@@ -1,28 +1,28 @@
1
- import { collate } from './utils.mjs';
2
-
3
- export class Cookies extends URLSearchParams {
4
-
5
- static jar = new Map();
6
-
7
- get [Symbol.toStringTag]() {
8
- return this.constructor.name;
9
- }
10
-
11
- constructor(input) {
12
- if (Array.isArray(input) && input.every((it) => !Array.isArray(it))) {
13
- input = input.join(';').split(';')
14
- .filter((it) => !/\b(Domain|Expires|HttpOnly|Max-Age|Path|SameParty|SameSite|Secure)\b/i.test(it))
15
- .map((it) => it.trim())
16
- .join('&');
17
- }
18
-
19
- super(input);
20
- }
21
-
22
- toString() {
23
- collate(this, Cookies);
24
-
25
- return super.toString().split('&').join('; ').trim();
26
- }
27
-
28
- }
1
+ import { collate } from './utils.mjs';
2
+
3
+ export class Cookies extends URLSearchParams {
4
+
5
+ static jar = new Map();
6
+
7
+ get [Symbol.toStringTag]() {
8
+ return this.constructor.name;
9
+ }
10
+
11
+ constructor(input) {
12
+ if (Array.isArray(input) && input.every((it) => !Array.isArray(it))) {
13
+ input = input.join(';').split(';')
14
+ .filter((it) => !/\b(?:Domain|Expires|HttpOnly|Max-Age|Path|SameParty|SameSite|Secure)\b/i.test(it))
15
+ .map((it) => it.trim())
16
+ .join('&');
17
+ }
18
+
19
+ super(input);
20
+ }
21
+
22
+ toString() {
23
+ collate(this, Cookies);
24
+
25
+ return super.toString().split('&').join('; ').trim();
26
+ }
27
+
28
+ }
package/src/file.mjs CHANGED
@@ -1,40 +1,49 @@
1
- import { Blob } from 'node:buffer';
2
-
3
- export { Blob } from 'node:buffer';
4
-
5
- export class File extends Blob {
6
-
7
- static alike(instance) {
8
- return [
9
- Blob.name,
10
- File.name,
11
- ].includes(instance?.constructor.name);
12
- }
13
-
14
- #lastModified;
15
- #name;
16
-
17
- get [Symbol.toStringTag]() {
18
- return this.constructor.name;
19
- }
20
-
21
- get lastModified() {
22
- return this.#lastModified;
23
- }
24
-
25
- get name() {
26
- return this.#name;
27
- }
28
-
29
- constructor(bits, name = 'blob', options = {}) {
30
- const {
31
- name: filename,
32
- lastModified = Date.now(),
33
- } = options;
34
-
35
- super(bits, options);
36
- this.#lastModified = lastModified;
37
- this.#name = filename || name;
38
- }
39
-
40
- }
1
+ import { Blob } from 'node:buffer';
2
+ import { toUSVString } from 'node:util';
3
+
4
+ export { Blob } from 'node:buffer';
5
+
6
+ export class File extends Blob {
7
+
8
+ static alike(instance) {
9
+ return [
10
+ Blob.name,
11
+ File.name,
12
+ ].includes(instance?.constructor.name);
13
+ }
14
+
15
+ #lastModified;
16
+ #name;
17
+
18
+ get [Symbol.toStringTag]() {
19
+ return this.constructor.name;
20
+ }
21
+
22
+ get lastModified() {
23
+ return this.#lastModified;
24
+ }
25
+
26
+ get name() {
27
+ return this.#name;
28
+ }
29
+
30
+ constructor(...args) {
31
+ const len = args.length;
32
+
33
+ if (len < 2) {
34
+ throw new TypeError(`Failed to construct '${
35
+ File.name
36
+ }': 2 arguments required, but only ${ len } present.`);
37
+ }
38
+
39
+ const [bits, name, options = {}] = args;
40
+ const {
41
+ lastModified = Date.now(),
42
+ } = options;
43
+
44
+ super(bits, options);
45
+ this.#lastModified = +lastModified ? lastModified : 0;
46
+ this.#name = toUSVString(name);
47
+ }
48
+
49
+ }