@npy/fetch 0.1.2 → 0.1.3
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 +143 -50
- package/bun.lock +68 -0
- package/examples/custom-proxy-client.ts +32 -0
- package/examples/http-client.ts +47 -0
- package/examples/proxy.ts +16 -0
- package/examples/simple.ts +15 -0
- package/package.json +25 -30
- package/src/_internal/consts.ts +3 -0
- package/{_internal/decode-stream-error.d.cts → src/_internal/decode-stream-error.ts} +7 -2
- package/src/_internal/error-mapping.ts +160 -0
- package/src/_internal/guards.ts +78 -0
- package/src/_internal/net.ts +173 -0
- package/src/_internal/promises.ts +22 -0
- package/src/_internal/streams.ts +52 -0
- package/src/_internal/symbols.ts +1 -0
- package/src/agent-pool.ts +157 -0
- package/src/agent.ts +408 -0
- package/src/body.ts +179 -0
- package/src/dialers/index.ts +3 -0
- package/src/dialers/proxy.ts +102 -0
- package/src/dialers/tcp.ts +162 -0
- package/src/encoding.ts +222 -0
- package/src/errors.ts +357 -0
- package/src/fetch.ts +626 -0
- package/src/http-client.ts +111 -0
- package/src/index.ts +14 -0
- package/src/io/_utils.ts +82 -0
- package/src/io/buf-writer.ts +183 -0
- package/src/io/io.ts +322 -0
- package/src/io/readers.ts +576 -0
- package/src/io/writers.ts +331 -0
- package/{types/agent.d.cts → src/types/agent.ts} +47 -21
- package/{types/dialer.d.cts → src/types/dialer.ts} +19 -9
- package/src/types/index.ts +2 -0
- package/tests/agent-pool.test.ts +111 -0
- package/tests/agent.test.ts +134 -0
- package/tests/body.test.ts +228 -0
- package/tests/errors.test.ts +152 -0
- package/tests/fetch.test.ts +421 -0
- package/tests/io-options.test.ts +127 -0
- package/tests/multipart.test.ts +348 -0
- package/tests/test-utils.ts +335 -0
- package/tsconfig.json +15 -0
- package/LICENSE +0 -21
- package/_internal/consts.cjs +0 -4
- package/_internal/consts.d.cts +0 -3
- package/_internal/consts.d.ts +0 -3
- package/_internal/consts.js +0 -4
- package/_internal/decode-stream-error.cjs +0 -18
- package/_internal/decode-stream-error.d.ts +0 -11
- package/_internal/decode-stream-error.js +0 -18
- package/_internal/error-mapping.cjs +0 -44
- package/_internal/error-mapping.d.cts +0 -15
- package/_internal/error-mapping.d.ts +0 -15
- package/_internal/error-mapping.js +0 -41
- package/_internal/guards.cjs +0 -23
- package/_internal/guards.d.cts +0 -15
- package/_internal/guards.d.ts +0 -15
- package/_internal/guards.js +0 -15
- package/_internal/net.cjs +0 -95
- package/_internal/net.d.cts +0 -11
- package/_internal/net.d.ts +0 -11
- package/_internal/net.js +0 -92
- package/_internal/promises.cjs +0 -18
- package/_internal/promises.d.cts +0 -1
- package/_internal/promises.d.ts +0 -1
- package/_internal/promises.js +0 -18
- package/_internal/streams.cjs +0 -37
- package/_internal/streams.d.cts +0 -21
- package/_internal/streams.d.ts +0 -21
- package/_internal/streams.js +0 -36
- package/_internal/symbols.cjs +0 -4
- package/_internal/symbols.d.cts +0 -1
- package/_internal/symbols.d.ts +0 -1
- package/_internal/symbols.js +0 -4
- package/_virtual/_rolldown/runtime.cjs +0 -23
- package/agent-pool.cjs +0 -96
- package/agent-pool.d.cts +0 -2
- package/agent-pool.d.ts +0 -2
- package/agent-pool.js +0 -95
- package/agent.cjs +0 -260
- package/agent.d.cts +0 -3
- package/agent.d.ts +0 -3
- package/agent.js +0 -259
- package/body.cjs +0 -105
- package/body.d.cts +0 -12
- package/body.d.ts +0 -12
- package/body.js +0 -102
- package/dialers/index.d.cts +0 -3
- package/dialers/index.d.ts +0 -3
- package/dialers/proxy.cjs +0 -56
- package/dialers/proxy.d.cts +0 -27
- package/dialers/proxy.d.ts +0 -27
- package/dialers/proxy.js +0 -55
- package/dialers/tcp.cjs +0 -92
- package/dialers/tcp.d.cts +0 -57
- package/dialers/tcp.d.ts +0 -57
- package/dialers/tcp.js +0 -89
- package/encoding.cjs +0 -114
- package/encoding.d.cts +0 -35
- package/encoding.d.ts +0 -35
- package/encoding.js +0 -110
- package/errors.cjs +0 -275
- package/errors.d.cts +0 -110
- package/errors.d.ts +0 -110
- package/errors.js +0 -259
- package/fetch.cjs +0 -353
- package/fetch.d.cts +0 -58
- package/fetch.d.ts +0 -58
- package/fetch.js +0 -350
- package/http-client.cjs +0 -75
- package/http-client.d.cts +0 -39
- package/http-client.d.ts +0 -39
- package/http-client.js +0 -75
- package/index.cjs +0 -49
- package/index.d.cts +0 -14
- package/index.d.ts +0 -14
- package/index.js +0 -11
- package/io/_utils.cjs +0 -56
- package/io/_utils.d.cts +0 -10
- package/io/_utils.d.ts +0 -10
- package/io/_utils.js +0 -51
- package/io/buf-writer.cjs +0 -149
- package/io/buf-writer.d.cts +0 -13
- package/io/buf-writer.d.ts +0 -13
- package/io/buf-writer.js +0 -148
- package/io/io.cjs +0 -199
- package/io/io.d.cts +0 -5
- package/io/io.d.ts +0 -5
- package/io/io.js +0 -198
- package/io/readers.cjs +0 -337
- package/io/readers.d.cts +0 -69
- package/io/readers.d.ts +0 -69
- package/io/readers.js +0 -333
- package/io/writers.cjs +0 -196
- package/io/writers.d.cts +0 -22
- package/io/writers.d.ts +0 -22
- package/io/writers.js +0 -195
- package/types/agent.d.ts +0 -72
- package/types/dialer.d.ts +0 -30
- package/types/index.d.cts +0 -2
- package/types/index.d.ts +0 -2
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { NodeTlsConnectOptions } from "@fuman/node";
|
|
2
|
+
import { connectTcp, connectTls } from "../_internal/net";
|
|
3
|
+
import type { Dialer } from "../types/dialer";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_TCP_PORT = 80;
|
|
6
|
+
const DEFAULT_TLS_PORT = 443;
|
|
7
|
+
const DEFAULT_HTTP_ALPN_PROTOCOLS = ["http/1.1"] as const;
|
|
8
|
+
|
|
9
|
+
export type HostPort = {
|
|
10
|
+
address: string;
|
|
11
|
+
port: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function parsePort(value: string | number): number {
|
|
15
|
+
if (typeof value === "number") {
|
|
16
|
+
if (!Number.isInteger(value) || value <= 0 || value > 65535) {
|
|
17
|
+
throw new TypeError(`Invalid port: ${String(value)}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!/^\d+$/.test(value)) {
|
|
24
|
+
throw new TypeError(`Invalid port: ${JSON.stringify(value)}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const parsed = Number.parseInt(value, 10);
|
|
28
|
+
if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
|
|
29
|
+
throw new TypeError(`Invalid port: ${JSON.stringify(value)}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return parsed;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolves the effective network address and port for a URL or dial target.
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* When the input does not include an explicit port, the provided default port is used.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveHostPort(
|
|
42
|
+
target: URL | Dialer.Target,
|
|
43
|
+
defaultPort: number,
|
|
44
|
+
): HostPort {
|
|
45
|
+
const address = target instanceof URL ? target.hostname : target.address;
|
|
46
|
+
|
|
47
|
+
if (!address) {
|
|
48
|
+
throw new TypeError("Target address is required");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const port =
|
|
52
|
+
target instanceof URL
|
|
53
|
+
? parsePort(target.port || String(defaultPort))
|
|
54
|
+
: parsePort(target.port || defaultPort);
|
|
55
|
+
|
|
56
|
+
return { address, port };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Dialer for plain TCP targets.
|
|
61
|
+
*
|
|
62
|
+
* @remarks
|
|
63
|
+
* This dialer rejects secure targets and is intended for HTTP over raw TCP.
|
|
64
|
+
*/
|
|
65
|
+
export class TcpDialer implements Dialer {
|
|
66
|
+
async dial(
|
|
67
|
+
target: Dialer.Target,
|
|
68
|
+
options: Dialer.Options = {},
|
|
69
|
+
): Promise<Dialer.ConnectionLike> {
|
|
70
|
+
if (target.secure) {
|
|
71
|
+
throw new Error("TcpDialer cannot dial a secure target");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const endpoint = resolveHostPort(target, DEFAULT_TCP_PORT);
|
|
75
|
+
|
|
76
|
+
return connectTcp({
|
|
77
|
+
...endpoint,
|
|
78
|
+
signal: options.signal,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Dialer for TLS targets.
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* This dialer rejects insecure targets and applies TLS-specific options such as
|
|
88
|
+
* CA certificates, SNI and ALPN.
|
|
89
|
+
*/
|
|
90
|
+
export class TlsDialer implements Dialer {
|
|
91
|
+
readonly #options: Readonly<TlsDialer.Options>;
|
|
92
|
+
|
|
93
|
+
constructor(options: TlsDialer.Options = {}) {
|
|
94
|
+
this.#options = { ...options };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async dial(
|
|
98
|
+
target: Dialer.Target,
|
|
99
|
+
options: Dialer.Options = {},
|
|
100
|
+
): Promise<Dialer.ConnectionLike> {
|
|
101
|
+
if (!target.secure) {
|
|
102
|
+
throw new Error("TlsDialer cannot dial an insecure target");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const endpoint = resolveHostPort(target, DEFAULT_TLS_PORT);
|
|
106
|
+
const extraOptions =
|
|
107
|
+
this.#options.extraOptions || target.extraOptions
|
|
108
|
+
? {
|
|
109
|
+
...this.#options.extraOptions,
|
|
110
|
+
...target.extraOptions,
|
|
111
|
+
}
|
|
112
|
+
: undefined;
|
|
113
|
+
|
|
114
|
+
return connectTls({
|
|
115
|
+
...endpoint,
|
|
116
|
+
signal: options.signal,
|
|
117
|
+
caCerts: this.#options.caCerts,
|
|
118
|
+
sni: target.sni ?? this.#options.sni ?? endpoint.address,
|
|
119
|
+
alpnProtocols: target.alpnProtocols ??
|
|
120
|
+
this.#options.alpnProtocols ?? [...DEFAULT_HTTP_ALPN_PROTOCOLS],
|
|
121
|
+
extraOptions,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export namespace TlsDialer {
|
|
127
|
+
export interface Options {
|
|
128
|
+
caCerts?: string[];
|
|
129
|
+
sni?: string;
|
|
130
|
+
alpnProtocols?: string[];
|
|
131
|
+
extraOptions?: NodeTlsConnectOptions["extraOptions"];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Selects {@link TcpDialer} or {@link TlsDialer} based on the target security mode.
|
|
137
|
+
*/
|
|
138
|
+
export class AutoDialer implements Dialer {
|
|
139
|
+
readonly tcpDialer: TcpDialer;
|
|
140
|
+
readonly tlsDialer: TlsDialer;
|
|
141
|
+
|
|
142
|
+
constructor(options: AutoDialer.Options = {}) {
|
|
143
|
+
this.tcpDialer = options.tcp ?? new TcpDialer();
|
|
144
|
+
this.tlsDialer = options.tls ?? new TlsDialer();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
dial(
|
|
148
|
+
target: Dialer.Target,
|
|
149
|
+
options: Dialer.Options = {},
|
|
150
|
+
): Promise<Dialer.ConnectionLike> {
|
|
151
|
+
return target.secure
|
|
152
|
+
? this.tlsDialer.dial(target, options)
|
|
153
|
+
: this.tcpDialer.dial(target, options);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export namespace AutoDialer {
|
|
158
|
+
export interface Options {
|
|
159
|
+
tcp?: TcpDialer;
|
|
160
|
+
tls?: TlsDialer;
|
|
161
|
+
}
|
|
162
|
+
}
|
package/src/encoding.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import { nodeReadableToWeb } from "@fuman/node";
|
|
3
|
+
import { DecodeStreamError } from "./_internal/decode-stream-error";
|
|
4
|
+
|
|
5
|
+
export type ByteStream = ReadableStream<Uint8Array>;
|
|
6
|
+
type ByteSource = ByteStream | AsyncIterable<Uint8Array>;
|
|
7
|
+
export type ByteTransform = TransformStream<Uint8Array, Uint8Array>;
|
|
8
|
+
|
|
9
|
+
function applyTransforms(
|
|
10
|
+
stream: ByteSource,
|
|
11
|
+
contentEncoding: string | string[] | undefined,
|
|
12
|
+
factory: (contentEncoding?: string | string[]) => ByteTransform[],
|
|
13
|
+
): ByteSource {
|
|
14
|
+
const transforms = factory(contentEncoding);
|
|
15
|
+
if (transforms.length === 0) return stream;
|
|
16
|
+
|
|
17
|
+
let result: ByteStream;
|
|
18
|
+
|
|
19
|
+
if (stream instanceof ReadableStream) {
|
|
20
|
+
result = stream;
|
|
21
|
+
} else {
|
|
22
|
+
result = nodeReadableToWeb(Readable.from(stream));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const t of transforms) {
|
|
26
|
+
result = result.pipeThrough(t);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Applies decoding transforms for the given content-encoding list.
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* If no supported encodings are provided, the original source is returned unchanged.
|
|
37
|
+
* Async iterables are converted to Web Streams only when transforms are required.
|
|
38
|
+
*/
|
|
39
|
+
export function decodeStream(
|
|
40
|
+
stream: ByteStream,
|
|
41
|
+
contentEncoding?: string | string[],
|
|
42
|
+
): ByteStream;
|
|
43
|
+
export function decodeStream(
|
|
44
|
+
stream: AsyncIterable<Uint8Array>,
|
|
45
|
+
contentEncoding?: string | string[],
|
|
46
|
+
): AsyncIterable<Uint8Array> | ByteStream;
|
|
47
|
+
export function decodeStream(
|
|
48
|
+
stream: ByteSource,
|
|
49
|
+
contentEncoding?: string | string[],
|
|
50
|
+
): ByteSource {
|
|
51
|
+
return applyTransforms(stream, contentEncoding, createDecoders);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Applies encoding transforms for the given content-encoding list.
|
|
56
|
+
*
|
|
57
|
+
* @remarks
|
|
58
|
+
* If no supported encodings are provided, the original source is returned unchanged.
|
|
59
|
+
* Async iterables are converted to Web Streams only when transforms are required.
|
|
60
|
+
*/
|
|
61
|
+
export function encodeStream(
|
|
62
|
+
stream: ByteStream,
|
|
63
|
+
contentEncoding?: string | string[],
|
|
64
|
+
): ByteStream;
|
|
65
|
+
export function encodeStream(
|
|
66
|
+
stream: AsyncIterable<Uint8Array>,
|
|
67
|
+
contentEncoding?: string | string[],
|
|
68
|
+
): AsyncIterable<Uint8Array> | ByteStream;
|
|
69
|
+
export function encodeStream(
|
|
70
|
+
stream: ByteSource,
|
|
71
|
+
contentEncoding?: string | string[],
|
|
72
|
+
): ByteSource {
|
|
73
|
+
return applyTransforms(stream, contentEncoding, createEncoders);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Creates the decoder pipeline for a Content-Encoding or transfer-coding list.
|
|
78
|
+
*
|
|
79
|
+
* @remarks
|
|
80
|
+
* Decoding is applied in reverse order of encoding, as required by HTTP semantics.
|
|
81
|
+
* The special value `identity` is ignored.
|
|
82
|
+
*/
|
|
83
|
+
export function createDecoders(
|
|
84
|
+
contentEncoding?: string | string[],
|
|
85
|
+
): ByteTransform[] {
|
|
86
|
+
const decoders: ByteTransform[] = [];
|
|
87
|
+
|
|
88
|
+
if (contentEncoding?.length) {
|
|
89
|
+
const encodings: string[] = Array.isArray(contentEncoding)
|
|
90
|
+
? contentEncoding.flatMap(commaSplit)
|
|
91
|
+
: contentEncoding.split(",");
|
|
92
|
+
|
|
93
|
+
for (const encoding of encodings) {
|
|
94
|
+
const normalizedEncoding = normalizeEncoding(encoding);
|
|
95
|
+
|
|
96
|
+
if (normalizedEncoding === "identity") continue;
|
|
97
|
+
|
|
98
|
+
decoders.push(createDecoder(normalizedEncoding));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return decoders.reverse();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Creates the encoder pipeline for a Content-Encoding or transfer-coding list.
|
|
107
|
+
*
|
|
108
|
+
* @remarks
|
|
109
|
+
* Encoders are returned in the declared order. The special value `identity` is ignored.
|
|
110
|
+
*/
|
|
111
|
+
export function createEncoders(
|
|
112
|
+
contentEncoding?: string | string[],
|
|
113
|
+
): ByteTransform[] {
|
|
114
|
+
const encoders: ByteTransform[] = [];
|
|
115
|
+
|
|
116
|
+
if (contentEncoding?.length) {
|
|
117
|
+
const encodings: string[] = Array.isArray(contentEncoding)
|
|
118
|
+
? contentEncoding.flatMap(commaSplit)
|
|
119
|
+
: contentEncoding.split(",");
|
|
120
|
+
|
|
121
|
+
for (const encoding of encodings) {
|
|
122
|
+
const normalizedEncoding = normalizeEncoding(encoding);
|
|
123
|
+
|
|
124
|
+
if (normalizedEncoding === "identity") continue;
|
|
125
|
+
|
|
126
|
+
encoders.push(createEncoder(normalizedEncoding));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return encoders;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function commaSplit(header: string): string[] {
|
|
134
|
+
return header.split(",");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function normalizeEncoding(encoding: string): string {
|
|
138
|
+
return encoding.trim().toLowerCase();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function tagDecodeErrors(native: ByteTransform): ByteTransform {
|
|
142
|
+
const reader = native.readable.getReader();
|
|
143
|
+
|
|
144
|
+
const tagged = new ReadableStream<Uint8Array>({
|
|
145
|
+
pull(controller) {
|
|
146
|
+
return reader.read().then(
|
|
147
|
+
({ done, value }) => {
|
|
148
|
+
if (done) {
|
|
149
|
+
controller.close();
|
|
150
|
+
} else if (value) {
|
|
151
|
+
controller.enqueue(
|
|
152
|
+
value as unknown as Uint8Array<ArrayBuffer>,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
(err) => {
|
|
157
|
+
controller.error(new DecodeStreamError(err));
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
},
|
|
161
|
+
cancel(reason) {
|
|
162
|
+
return reader.cancel(reason);
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
writable: native.writable,
|
|
168
|
+
readable: tagged,
|
|
169
|
+
} as ByteTransform;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function createDecoder(normalizedEncoding: string): ByteTransform {
|
|
173
|
+
switch (normalizedEncoding) {
|
|
174
|
+
case "gzip":
|
|
175
|
+
case "x-gzip":
|
|
176
|
+
return tagDecodeErrors(
|
|
177
|
+
new DecompressionStream("gzip") as ByteTransform,
|
|
178
|
+
);
|
|
179
|
+
case "deflate":
|
|
180
|
+
case "x-deflate":
|
|
181
|
+
return tagDecodeErrors(
|
|
182
|
+
new DecompressionStream("deflate") as ByteTransform,
|
|
183
|
+
);
|
|
184
|
+
case "zstd":
|
|
185
|
+
case "x-zstd":
|
|
186
|
+
return tagDecodeErrors(
|
|
187
|
+
new DecompressionStream("zstd" as any) as ByteTransform,
|
|
188
|
+
);
|
|
189
|
+
case "br":
|
|
190
|
+
return tagDecodeErrors(
|
|
191
|
+
new DecompressionStream("brotli" as any) as ByteTransform,
|
|
192
|
+
);
|
|
193
|
+
case "identity":
|
|
194
|
+
return new TransformStream();
|
|
195
|
+
default:
|
|
196
|
+
throw new TypeError(
|
|
197
|
+
`Unsupported content-encoding: "${normalizedEncoding}"`,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function createEncoder(normalizedEncoding: string): ByteTransform {
|
|
203
|
+
switch (normalizedEncoding) {
|
|
204
|
+
case "gzip":
|
|
205
|
+
case "x-gzip":
|
|
206
|
+
return new CompressionStream("gzip") as ByteTransform;
|
|
207
|
+
case "deflate":
|
|
208
|
+
case "x-deflate":
|
|
209
|
+
return new CompressionStream("deflate") as ByteTransform;
|
|
210
|
+
case "zstd":
|
|
211
|
+
case "x-zstd":
|
|
212
|
+
return new CompressionStream("zstd" as any) as ByteTransform;
|
|
213
|
+
case "br":
|
|
214
|
+
return new CompressionStream("brotli" as any) as ByteTransform;
|
|
215
|
+
case "identity":
|
|
216
|
+
return new TransformStream();
|
|
217
|
+
default:
|
|
218
|
+
throw new TypeError(
|
|
219
|
+
`Unsupported content-encoding: "${normalizedEncoding}"`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
}
|