UNPKG

next

Version:

The React Framework

293 lines (292 loc) • 11.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { HeadersAdapter: null, ReadonlyHeadersError: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { HeadersAdapter: function() { return HeadersAdapter; }, ReadonlyHeadersError: function() { return ReadonlyHeadersError; } }); const _reflect = require("./reflect"); class ReadonlyHeadersError extends Error { constructor(){ super('Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers'); Object.defineProperty(this, "__NEXT_ERROR_CODE", { value: "E1176", enumerable: false, configurable: true }); } static callable() { throw new ReadonlyHeadersError(); } } /** * Builds the read methods for a sealed view that exposes all of `target`. */ function createPassThroughMethods(target, sealed) { return { get: target.get.bind(target), has: target.has.bind(target), getSetCookie: target.getSetCookie.bind(target), keys: target.keys.bind(target), values: target.values.bind(target), entries: target.entries.bind(target), [Symbol.iterator]: target[Symbol.iterator].bind(target), // The native method passes the unsealed target as the callback's `parent` // argument. That is a mutable handle on the underlying headers. Pass the // sealed view instead. forEach (callbackfn, thisArg) { for (const [name, value] of target.entries()){ callbackfn.call(thisArg, value, name, sealed); } } }; } /** * Builds the read methods for a sealed view that omits the header names matched * by `isHidden`. */ function createHidingMethods(target, sealed, isHidden) { function* entries() { for (const entry of target.entries()){ if (!isHidden(entry[0])) { yield entry; } } } return { entries, [Symbol.iterator]: entries, get: (name)=>isHidden(name) ? null : target.get(name), has: (name)=>isHidden(name) ? false : target.has(name), getSetCookie: ()=>isHidden('set-cookie') ? [] : target.getSetCookie(), *keys () { for (const name of target.keys()){ if (!isHidden(name)) { yield name; } } }, *values () { for (const [, value] of entries()){ yield value; } }, // The native method passes the unsealed target as the callback's `parent` // argument. That is a mutable handle on the underlying headers. Pass the // sealed view instead. forEach (callbackfn, thisArg) { for (const [name, value] of entries()){ callbackfn.call(thisArg, value, name, sealed); } } }; } class HeadersAdapter extends Headers { constructor(headers){ // We've already overridden the methods that would be called, so we're just // calling the super constructor to ensure that the instanceof check works. super(); this.headers = new Proxy(headers, { get (target, prop, receiver) { // Because this is just an object, we expect that all "get" operations // are for properties. If it's a "get" for a symbol, we'll just return // the symbol. if (typeof prop === 'symbol') { return _reflect.ReflectAdapter.get(target, prop, receiver); } const lowercased = prop.toLowerCase(); // Let's find the original casing of the key. This assumes that there is // no mixed case keys (e.g. "Content-Type" and "content-type") in the // headers object. const original = Object.keys(headers).find((o)=>o.toLowerCase() === lowercased); // If the original casing doesn't exist, return undefined. if (typeof original === 'undefined') return; // If the original casing exists, return the value. return _reflect.ReflectAdapter.get(target, original, receiver); }, set (target, prop, value, receiver) { if (typeof prop === 'symbol') { return _reflect.ReflectAdapter.set(target, prop, value, receiver); } const lowercased = prop.toLowerCase(); // Let's find the original casing of the key. This assumes that there is // no mixed case keys (e.g. "Content-Type" and "content-type") in the // headers object. const original = Object.keys(headers).find((o)=>o.toLowerCase() === lowercased); // If the original casing doesn't exist, use the prop as the key. return _reflect.ReflectAdapter.set(target, original ?? prop, value, receiver); }, has (target, prop) { if (typeof prop === 'symbol') return _reflect.ReflectAdapter.has(target, prop); const lowercased = prop.toLowerCase(); // Let's find the original casing of the key. This assumes that there is // no mixed case keys (e.g. "Content-Type" and "content-type") in the // headers object. const original = Object.keys(headers).find((o)=>o.toLowerCase() === lowercased); // If the original casing doesn't exist, return false. if (typeof original === 'undefined') return false; // If the original casing exists, return true. return _reflect.ReflectAdapter.has(target, original); }, deleteProperty (target, prop) { if (typeof prop === 'symbol') return _reflect.ReflectAdapter.deleteProperty(target, prop); const lowercased = prop.toLowerCase(); // Let's find the original casing of the key. This assumes that there is // no mixed case keys (e.g. "Content-Type" and "content-type") in the // headers object. const original = Object.keys(headers).find((o)=>o.toLowerCase() === lowercased); // If the original casing doesn't exist, return true. if (typeof original === 'undefined') return true; // If the original casing exists, delete the property. return _reflect.ReflectAdapter.deleteProperty(target, original); } }); } /** * Seals a Headers instance to prevent modification by throwing an error when * any mutating method is called. * * The sealed view stays live. Later writes to `headers` remain visible * through it. * * `hidden` omits the given header names from every read operation (`get`, * `has`, `getSetCookie`, `forEach`, and iteration). The names must be * lowercase. The underlying headers are neither copied nor mutated, so hidden * headers remain available to the framework. */ static seal(headers, hidden) { const isHidden = hidden && hidden.size > 0 ? (name)=>hidden.has(name.toLowerCase()) : null; // The methods are built once per sealed view and reused, so repeated access // returns the same function instead of a fresh closure. They are assigned // after the proxy exists because `forEach` hands the proxy to its callback. // Creating the proxy runs no trap, so nothing can read them before then. let methods; const sealed = new Proxy(headers, { get (target, prop, receiver) { switch(prop){ case 'append': case 'delete': case 'set': return ReadonlyHeadersError.callable; case Symbol.iterator: return methods[Symbol.iterator]; case 'get': case 'has': case 'getSetCookie': case 'keys': case 'values': case 'entries': case 'forEach': return methods[prop]; default: return _reflect.ReflectAdapter.get(target, prop, receiver); } } }); methods = isHidden ? createHidingMethods(headers, sealed, isHidden) : createPassThroughMethods(headers, sealed); return sealed; } /** * @param headers * @returns A fresh object identity backed by the original value */ static fresh(headers) { return new Proxy(headers, { get (target, prop, receiver) { return _reflect.ReflectAdapter.get(target, prop, receiver); } }); } /** * Merges a header value into a string. This stores multiple values as an * array, so we need to merge them into a string. * * @param value a header value * @returns a merged header value (a string) */ merge(value) { if (Array.isArray(value)) return value.join(', '); return value; } /** * Creates a Headers instance from a plain object or a Headers instance. * * @param headers a plain object or a Headers instance * @returns a headers instance */ static from(headers) { if (headers instanceof Headers) return headers; return new HeadersAdapter(headers); } append(name, value) { const existing = this.headers[name]; if (typeof existing === 'string') { this.headers[name] = [ existing, value ]; } else if (Array.isArray(existing)) { existing.push(value); } else { this.headers[name] = value; } } delete(name) { delete this.headers[name]; } get(name) { const value = this.headers[name]; if (typeof value !== 'undefined') return this.merge(value); return null; } has(name) { return typeof this.headers[name] !== 'undefined'; } set(name, value) { this.headers[name] = value; } forEach(callbackfn, thisArg) { for (const [name, value] of this.entries()){ callbackfn.call(thisArg, value, name, this); } } *entries() { for (const key of Object.keys(this.headers)){ const name = key.toLowerCase(); // We assert here that this is a string because we got it from the // Object.keys() call above. const value = this.get(name); yield [ name, value ]; } } *keys() { for (const key of Object.keys(this.headers)){ const name = key.toLowerCase(); yield name; } } *values() { for (const key of Object.keys(this.headers)){ // We assert here that this is a string because we got it from the // Object.keys() call above. const value = this.get(key); yield value; } } [Symbol.iterator]() { return this.entries(); } } //# sourceMappingURL=headers.js.map