156 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
/**
 | 
						|
 * Based on https://github.com/facebook/react/blob/d4e78c42a94be027b4dc7ed2659a5fddfbf9bd4e/packages/react/src/ReactFetch.js
 | 
						|
 */ "use strict";
 | 
						|
Object.defineProperty(exports, "__esModule", {
 | 
						|
    value: true
 | 
						|
});
 | 
						|
Object.defineProperty(exports, "createDedupeFetch", {
 | 
						|
    enumerable: true,
 | 
						|
    get: function() {
 | 
						|
        return createDedupeFetch;
 | 
						|
    }
 | 
						|
});
 | 
						|
const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
 | 
						|
const _cloneresponse = require("./clone-response");
 | 
						|
const _invarianterror = require("../../shared/lib/invariant-error");
 | 
						|
function _getRequireWildcardCache(nodeInterop) {
 | 
						|
    if (typeof WeakMap !== "function") return null;
 | 
						|
    var cacheBabelInterop = new WeakMap();
 | 
						|
    var cacheNodeInterop = new WeakMap();
 | 
						|
    return (_getRequireWildcardCache = function(nodeInterop) {
 | 
						|
        return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
 | 
						|
    })(nodeInterop);
 | 
						|
}
 | 
						|
function _interop_require_wildcard(obj, nodeInterop) {
 | 
						|
    if (!nodeInterop && obj && obj.__esModule) {
 | 
						|
        return obj;
 | 
						|
    }
 | 
						|
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
 | 
						|
        return {
 | 
						|
            default: obj
 | 
						|
        };
 | 
						|
    }
 | 
						|
    var cache = _getRequireWildcardCache(nodeInterop);
 | 
						|
    if (cache && cache.has(obj)) {
 | 
						|
        return cache.get(obj);
 | 
						|
    }
 | 
						|
    var newObj = {
 | 
						|
        __proto__: null
 | 
						|
    };
 | 
						|
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
 | 
						|
    for(var key in obj){
 | 
						|
        if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
 | 
						|
            var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
 | 
						|
            if (desc && (desc.get || desc.set)) {
 | 
						|
                Object.defineProperty(newObj, key, desc);
 | 
						|
            } else {
 | 
						|
                newObj[key] = obj[key];
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    newObj.default = obj;
 | 
						|
    if (cache) {
 | 
						|
        cache.set(obj, newObj);
 | 
						|
    }
 | 
						|
    return newObj;
 | 
						|
}
 | 
						|
const simpleCacheKey = '["GET",[],null,"follow",null,null,null,null]' // generateCacheKey(new Request('https://blank'));
 | 
						|
;
 | 
						|
function generateCacheKey(request) {
 | 
						|
    // We pick the fields that goes into the key used to dedupe requests.
 | 
						|
    // We don't include the `cache` field, because we end up using whatever
 | 
						|
    // caching resulted from the first request.
 | 
						|
    // Notably we currently don't consider non-standard (or future) options.
 | 
						|
    // This might not be safe. TODO: warn for non-standard extensions differing.
 | 
						|
    // IF YOU CHANGE THIS UPDATE THE simpleCacheKey ABOVE.
 | 
						|
    return JSON.stringify([
 | 
						|
        request.method,
 | 
						|
        Array.from(request.headers.entries()),
 | 
						|
        request.mode,
 | 
						|
        request.redirect,
 | 
						|
        request.credentials,
 | 
						|
        request.referrer,
 | 
						|
        request.referrerPolicy,
 | 
						|
        request.integrity
 | 
						|
    ]);
 | 
						|
}
 | 
						|
function createDedupeFetch(originalFetch) {
 | 
						|
    const getCacheEntries = _react.cache(// eslint-disable-next-line @typescript-eslint/no-unused-vars -- url is the cache key
 | 
						|
    (url)=>[]);
 | 
						|
    return function dedupeFetch(resource, options) {
 | 
						|
        if (options && options.signal) {
 | 
						|
            // If we're passed a signal, then we assume that
 | 
						|
            // someone else controls the lifetime of this object and opts out of
 | 
						|
            // caching. It's effectively the opt-out mechanism.
 | 
						|
            // Ideally we should be able to check this on the Request but
 | 
						|
            // it always gets initialized with its own signal so we don't
 | 
						|
            // know if it's supposed to override - unless we also override the
 | 
						|
            // Request constructor.
 | 
						|
            return originalFetch(resource, options);
 | 
						|
        }
 | 
						|
        // Normalize the Request
 | 
						|
        let url;
 | 
						|
        let cacheKey;
 | 
						|
        if (typeof resource === 'string' && !options) {
 | 
						|
            // Fast path.
 | 
						|
            cacheKey = simpleCacheKey;
 | 
						|
            url = resource;
 | 
						|
        } else {
 | 
						|
            // Normalize the request.
 | 
						|
            // if resource is not a string or a URL (its an instance of Request)
 | 
						|
            // then do not instantiate a new Request but instead
 | 
						|
            // reuse the request as to not disturb the body in the event it's a ReadableStream.
 | 
						|
            const request = typeof resource === 'string' || resource instanceof URL ? new Request(resource, options) : resource;
 | 
						|
            if (request.method !== 'GET' && request.method !== 'HEAD' || request.keepalive) {
 | 
						|
                // We currently don't dedupe requests that might have side-effects. Those
 | 
						|
                // have to be explicitly cached. We assume that the request doesn't have a
 | 
						|
                // body if it's GET or HEAD.
 | 
						|
                // keepalive gets treated the same as if you passed a custom cache signal.
 | 
						|
                return originalFetch(resource, options);
 | 
						|
            }
 | 
						|
            cacheKey = generateCacheKey(request);
 | 
						|
            url = request.url;
 | 
						|
        }
 | 
						|
        const cacheEntries = getCacheEntries(url);
 | 
						|
        for(let i = 0, j = cacheEntries.length; i < j; i += 1){
 | 
						|
            const [key, promise] = cacheEntries[i];
 | 
						|
            if (key === cacheKey) {
 | 
						|
                return promise.then(()=>{
 | 
						|
                    const response = cacheEntries[i][2];
 | 
						|
                    if (!response) throw Object.defineProperty(new _invarianterror.InvariantError('No cached response'), "__NEXT_ERROR_CODE", {
 | 
						|
                        value: "E579",
 | 
						|
                        enumerable: false,
 | 
						|
                        configurable: true
 | 
						|
                    });
 | 
						|
                    // We're cloning the response using this utility because there exists
 | 
						|
                    // a bug in the undici library around response cloning. See the
 | 
						|
                    // following pull request for more details:
 | 
						|
                    // https://github.com/vercel/next.js/pull/73274
 | 
						|
                    const [cloned1, cloned2] = (0, _cloneresponse.cloneResponse)(response);
 | 
						|
                    cacheEntries[i][2] = cloned2;
 | 
						|
                    return cloned1;
 | 
						|
                });
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // We pass the original arguments here in case normalizing the Request
 | 
						|
        // doesn't include all the options in this environment.
 | 
						|
        const promise = originalFetch(resource, options);
 | 
						|
        const entry = [
 | 
						|
            cacheKey,
 | 
						|
            promise,
 | 
						|
            null
 | 
						|
        ];
 | 
						|
        cacheEntries.push(entry);
 | 
						|
        return promise.then((response)=>{
 | 
						|
            // We're cloning the response using this utility because there exists
 | 
						|
            // a bug in the undici library around response cloning. See the
 | 
						|
            // following pull request for more details:
 | 
						|
            // https://github.com/vercel/next.js/pull/73274
 | 
						|
            const [cloned1, cloned2] = (0, _cloneresponse.cloneResponse)(response);
 | 
						|
            entry[2] = cloned2;
 | 
						|
            return cloned1;
 | 
						|
        });
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
//# sourceMappingURL=dedupe-fetch.js.map
 |