const _headerList = Symbol("header list");
const _iterableHeaders = Symbol("iterable headers");
const _iterableHeadersCache = Symbol("iterable headers cache");
const _guard = Symbol("guard");
function byteLowerCase(s) {
return String(s).toLowerCase();
}
function httpTrim(s) {
return String(s).trim();
}
const HTTP_TOKEN_CODE_POINT_RE = /^[!#$%&'*+\-.^_`|~\w]+$/;
function checkHeaderNameForHttpTokenCodePoint(name) {
return HTTP_TOKEN_CODE_POINT_RE.test(name);
}
function checkForInvalidValueChars(value) {
for (let i = 0; i < value.length; i++) {
const c = value.charCodeAt(i);
if (c === 0x0a || c === 0x0d || c === 0x00) {
return false;
}
}
return true;
}
function normalizeHeaderValue(potentialValue) {
return httpTrim(potentialValue);
}
function appendHeader(headers, name, value) {
value = normalizeHeaderValue(value);
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError(`Invalid header name: "${name}"`);
}
if (!checkForInvalidValueChars(value)) {
throw new TypeError(`Invalid header value: "${value}"`);
}
if (headers[_guard] == "immutable") {
throw new TypeError("Cannot change header: headers are immutable");
}
const list = headers[_headerList];
const lowercaseName = byteLowerCase(name);
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
name = list[i][0];
break;
}
}
list.push([name, value]);
}
function getHeader(list, name) {
const lowercaseName = byteLowerCase(name);
const entries = [];
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
entries.push(list[i][1]);
}
}
return entries.length === 0 ? null : entries.join(", ");
}
function fillHeaders(headers, object) {
if (Array.isArray(object)) {
for (let i = 0; i < object.length; ++i) {
const header = object[i];
if (header.length !== 2) {
throw new TypeError(
`Invalid header: length must be 2, but is ${header.length}`,
);
}
appendHeader(headers, header[0], header[1]);
}
} else {
for (const key in object) {
if (Object.hasOwn(object, key)) {
appendHeader(headers, key, object[key]);
}
}
}
}
class Headers {
[_headerList] = [];
[_guard];
get [_iterableHeaders]() {
const list = this[_headerList];
if (this[_guard] === "immutable" && this[_iterableHeadersCache] !== undefined) {
return this[_iterableHeadersCache];
}
const seenHeaders = Object.create(null);
const entries = [];
for (let i = 0; i < list.length; ++i) {
const entry = list[i];
const name = byteLowerCase(entry[0]);
const value = entry[1];
if (name === "set-cookie") {
entries.push([name, value]);
} else {
const seenHeaderIndex = seenHeaders[name];
if (seenHeaderIndex !== undefined) {
const entryValue = entries[seenHeaderIndex][1];
entries[seenHeaderIndex][1] = entryValue.length > 0
? entryValue + ", " + value
: value;
} else {
seenHeaders[name] = entries.length;
entries.push([name, value]);
}
}
}
entries.sort((a, b) => {
const akey = a[0];
const bkey = b[0];
if (akey > bkey) return 1;
if (akey < bkey) return -1;
return 0;
});
this[_iterableHeadersCache] = entries;
return entries;
}
constructor(init = undefined) {
this[_guard] = "none";
if (init !== undefined) {
fillHeaders(this, init);
}
}
append(name, value) {
appendHeader(this, String(name), String(value));
}
delete(name) {
name = String(name);
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError(`Invalid header name: "${name}"`);
}
if (this[_guard] == "immutable") {
throw new TypeError("Cannot change headers: headers are immutable");
}
const list = this[_headerList];
const lowercaseName = byteLowerCase(name);
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
list.splice(i, 1);
i--;
}
}
}
get(name) {
name = String(name);
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError(`Invalid header name: "${name}"`);
}
return getHeader(this[_headerList], name);
}
getSetCookie() {
const list = this[_headerList];
const entries = [];
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === "set-cookie") {
entries.push(list[i][1]);
}
}
return entries;
}
has(name) {
name = String(name);
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError(`Invalid header name: "${name}"`);
}
const list = this[_headerList];
const lowercaseName = byteLowerCase(name);
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
return true;
}
}
return false;
}
set(name, value) {
name = String(name);
value = String(value);
value = normalizeHeaderValue(value);
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError(`Invalid header name: "${name}"`);
}
if (!checkForInvalidValueChars(value)) {
throw new TypeError(`Invalid header value: "${value}"`);
}
if (this[_guard] == "immutable") {
throw new TypeError("Cannot change headers: headers are immutable");
}
const list = this[_headerList];
const lowercaseName = byteLowerCase(name);
let added = false;
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
if (!added) {
list[i][1] = value;
added = true;
} else {
list.splice(i, 1);
i--;
}
}
}
if (!added) {
list.push([name, value]);
}
}
entries() {
return this[_iterableHeaders].values();
}
keys() {
return this[_iterableHeaders].map(entry => entry[0]).values();
}
values() {
return this[_iterableHeaders].map(entry => entry[1]).values();
}
forEach(callback, thisArg) {
for (const [name, value] of this[_iterableHeaders]) {
callback.call(thisArg, value, name, this);
}
}
[Symbol.iterator]() {
return this.entries();
}
}
export { Headers };