fetchContentRaw = (url, cachedResult, callback) => {
						const requestTime = Date.now();
						/** @type {OutgoingHttpHeaders} */
						const headers = {
							"accept-encoding": "gzip, deflate, br",
							"user-agent": "webpack"
						};

						if (cachedResult && cachedResult.etag) {
							headers["if-none-match"] = cachedResult.etag;
						}

						fetch(new URL(url), { headers }, res => {
							const etag = res.headers.etag;
							const location = res.headers.location;
							const cacheControl = res.headers["cache-control"];
							const { storeLock, storeCache, validUntil } = parseCacheControl(
								cacheControl,
								requestTime
							);
							/**
							 * @param {Partial<Pick<FetchResultMeta, "fresh">> & (Pick<RedirectFetchResult, "location"> | Pick<ContentFetchResult, "content" | "entry">)} partialResult result
							 * @returns {void}
							 */
							const finishWith = partialResult => {
								if ("location" in partialResult) {
									logger.debug(
										`GET ${url} [${res.statusCode}] -> ${partialResult.location}`
									);
								} else {
									logger.debug(
										`GET ${url} [${res.statusCode}] ${Math.ceil(
											partialResult.content.length / 1024
										)} kB${!storeLock ? " no-cache" : ""}`
									);
								}
								const result = {
									...partialResult,
									fresh: true,
									storeLock,
									storeCache,
									validUntil,
									etag
								};
								if (!storeCache) {
									logger.log(
										`${url} can't be stored in cache, due to Cache-Control header: ${cacheControl}`
									);
									return callback(null, result);
								}
								cache.store(
									url,
									null,
									{
										...result,
										fresh: false
									},
									err => {
										if (err) {
											logger.warn(
												`${url} can't be stored in cache: ${err.message}`
											);
											logger.debug(err.stack);
										}
										callback(null, result);
									}
								);
							};
							if (res.statusCode === 304) {
								const result = /** @type {FetchResult} */ (cachedResult);
								if (
									result.validUntil < validUntil ||
									result.storeLock !== storeLock ||
									result.storeCache !== storeCache ||
									result.etag !== etag
								) {
									return finishWith(result);
								}
								logger.debug(`GET ${url} [${res.statusCode}] (unchanged)`);
								return callback(null, { ...result, fresh: true });
							}
							if (
								location &&
								res.statusCode &&
								res.statusCode >= 301 &&
								res.statusCode <= 308
							) {
								const result = {
									location: new URL(location, url).href
								};
								if (
									!cachedResult ||
									!("location" in cachedResult) ||
									cachedResult.location !== result.location ||
									cachedResult.validUntil < validUntil ||
									cachedResult.storeLock !== storeLock ||
									cachedResult.storeCache !== storeCache ||
									cachedResult.etag !== etag
								) {
									return finishWith(result);
								}
								logger.debug(`GET ${url} [${res.statusCode}] (unchanged)`);
								return callback(null, {
									...result,
									fresh: true,
									storeLock,
									storeCache,
									validUntil,
									etag
								});
							}
							const contentType = res.headers["content-type"] || "";
							/** @type {Buffer[]} */
							const bufferArr = [];

							const contentEncoding = res.headers["content-encoding"];
							/** @type {Readable} */
							let stream = res;
							if (contentEncoding === "gzip") {
								stream = stream.pipe(createGunzip());
							} else if (contentEncoding === "br") {
								stream = stream.pipe(createBrotliDecompress());
							} else if (contentEncoding === "deflate") {
								stream = stream.pipe(createInflate());
							}

							stream.on("data", chunk => {
								bufferArr.push(chunk);
							});

							stream.on("end", () => {
								if (!res.complete) {
									logger.log(`GET ${url} [${res.statusCode}] (terminated)`);
									return callback(new Error(`${url} request was terminated`));
								}

								const content = Buffer.concat(bufferArr);

								if (res.statusCode !== 200) {
									logger.log(`GET ${url} [${res.statusCode}]`);
									return callback(
										new Error(
											`${url} request status code = ${
												res.statusCode
											}\n${content.toString("utf-8")}`
										)
									);
								}

								const integrity = computeIntegrity(content);
								const entry = { resolved: url, integrity, contentType };

								finishWith({
									entry,
									content
								});
							});
						}).on("error", err => {
							logger.log(`GET ${url} (error)`);
							err.message += `\nwhile fetching ${url}`;
							callback(err);
						});
					}
@param {string} url URL
@param {FetchResult | RedirectFetchResult | undefined} cachedResult result from cache
@param {(err: Error | null, fetchResult?: FetchResult) => void} callback callback
@returns {void}