Mirror: A Node.js fetch shim using built-in Request, Response, and Headers (but without native fetch)
1import { PassThrough, Transform, TransformCallback } from 'node:stream';
2import * as zlib from 'node:zlib';
3
4/** @see https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2 */
5type Encoding = 'gzip' | 'x-gzip' | 'deflate' | 'x-deflate' | 'br' | {};
6
7/** @see https://github.com/nodejs/undici/pull/2650 */
8class InflateStream extends Transform {
9 _opts?: zlib.ZlibOptions;
10 _inflate?: Transform;
11
12 constructor(opts?: zlib.ZlibOptions) {
13 super();
14 this._opts = opts;
15 }
16
17 _transform(
18 chunk: Buffer,
19 encoding: BufferEncoding,
20 callback: TransformCallback
21 ) {
22 if (!this._inflate) {
23 if (chunk.length === 0) {
24 callback();
25 return;
26 }
27 this._inflate =
28 (chunk[0] & 0x0f) === 0x08
29 ? zlib.createInflate(this._opts)
30 : zlib.createInflateRaw(this._opts);
31 this._inflate.on('data', this.push.bind(this));
32 this._inflate.on('end', () => this.push(null));
33 this._inflate.on('error', err => this.destroy(err));
34 }
35 this._inflate.write(chunk, encoding, callback);
36 }
37
38 _final(callback: TransformCallback) {
39 if (this._inflate) {
40 this._inflate.once('finish', callback);
41 this._inflate.end();
42 this._inflate = undefined;
43 } else {
44 callback();
45 }
46 }
47}
48
49export const createContentDecoder = (encoding: Encoding | {}) => {
50 // See: https://github.com/nodejs/undici/blob/008187b/lib/web/fetch/index.js#L2138-L2160
51 switch (encoding) {
52 case 'br':
53 return zlib.createBrotliDecompress({
54 flush: zlib.constants.BROTLI_OPERATION_FLUSH,
55 finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH,
56 });
57 case 'gzip':
58 case 'x-gzip':
59 return zlib.createGunzip({
60 flush: zlib.constants.Z_SYNC_FLUSH,
61 finishFlush: zlib.constants.Z_SYNC_FLUSH,
62 });
63 case 'deflate':
64 case 'x-deflate':
65 return new InflateStream({
66 flush: zlib.constants.Z_SYNC_FLUSH,
67 finishFlush: zlib.constants.Z_SYNC_FLUSH,
68 });
69 default:
70 return new PassThrough();
71 }
72};