Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233// util.MIMEType / util.MIMEParams: the MIME-type parsing algorithm from the Fetch/MIME// Sniffing specs, as Node's lib/internal/mime.js implements it (reimplemented here from// its documented behavior, not copied). Parameters parse lazily, on first read.function invalidMimeSyntax(production: string, str: string, invalidIndex: number): TypeError { const at = invalidIndex !== -1 ? ` at ${invalidIndex}` : ""; return Object.assign( new TypeError(`The MIME syntax for a ${production} in "${str}" is invalid${at}`), { code: "ERR_INVALID_MIME_SYNTAX" }, );}
const NOT_HTTP_TOKEN = /[^!#$%&'*+\-.^_`|~A-Za-z0-9]/;const NOT_HTTP_QUOTED_STRING = /[^\t -~\u0080-ÿ]/;const NOT_LEADING_WHITESPACE = /[^\r\n\t ]|$/;const TRAILING_WHITESPACE = /[\r\n\t ]*$/;
function toAsciiLower(str: string): string { let out = ""; for (const ch of str) out += ch >= "A" && ch <= "Z" ? ch.toLowerCase() : ch; return out;}
function search(str: string, re: RegExp): number { const m = re.exec(str); return m ? m.index : -1;}
function parseTypeAndSubtype(str: string): [type: string, subtype: string, position: number] { let position = search(str, NOT_LEADING_WHITESPACE); const typeEnd = str.indexOf("/", position); const rawType = typeEnd === -1 ? str.slice(position) : str.slice(position, typeEnd); const badType = search(rawType, NOT_HTTP_TOKEN); if (rawType === "" || badType !== -1 || typeEnd === -1) throw invalidMimeSyntax("type", str, badType); position = typeEnd + 1; const type = toAsciiLower(rawType); const subtypeEnd = str.indexOf(";", position); const rawSubtype = subtypeEnd === -1 ? str.slice(position) : str.slice(position, subtypeEnd); position += rawSubtype.length + (subtypeEnd === -1 ? 0 : 1); const trimmedSubtype = rawSubtype.slice(0, search(rawSubtype, TRAILING_WHITESPACE)); const badSubtype = search(trimmedSubtype, NOT_HTTP_TOKEN); if (trimmedSubtype === "" || badSubtype !== -1) throw invalidMimeSyntax("subtype", str, badSubtype); return [type, toAsciiLower(trimmedSubtype), position];}
function removeBackslashes(str: string): string { let out = ""; let i = 0; for (; i < str.length - 1; i++) { if (str[i] === "\\") { i++; out += str[i]; } else { out += str[i]; } } if (i === str.length - 1) out += str[i]; return out;}
function encodeParamValue(value: string): string { if (value.length === 0) return '""'; if (search(value, NOT_HTTP_TOKEN) === -1) return value; let escaped = ""; for (const ch of value) escaped += ch === '"' || ch === "\\" ? `\\${ch}` : ch; return `"${escaped}"`;}
export class MIMEParams { #data = new Map<string, string>(); #string: string | null = null; #parsed = true;
static instantiate(str: string): MIMEParams { const p = new MIMEParams(); p.#string = str; p.#parsed = false; return p; }
#parse(): void { if (this.#parsed) return; this.#parsed = true; const str = this.#string ?? ""; let position = 0; const end = search(str.slice(position), TRAILING_WHITESPACE) + position; while (position < end) { position += search(str.slice(position), NOT_LEADING_WHITESPACE); const nameEnd = search(str.slice(position), /[;=]|$/) + position; const name = toAsciiLower(str.slice(position, nameEnd)); position = nameEnd; if (position < end) { const ch = str[position]!; position += 1; if (ch === ";") continue; } if (position >= end) break; let value: string; if (str[position] === '"') { position += 1; const rest = str.slice(position); const m = /^(?:([\\]$)|[\\][\s\S]|[^"])*(?:(")|$)/u.exec(rest)!; position += m[0].length; const inside = m[1] || m[2] ? m[0].slice(0, -1) : m[0]; value = removeBackslashes(inside); if (m[1]) value += "\\"; } else { const valueEnd = str.indexOf(";", position); const raw = valueEnd === -1 ? str.slice(position) : str.slice(position, valueEnd); position += raw.length; const trimmed = raw.slice(0, search(raw, TRAILING_WHITESPACE)); if (trimmed === "") continue; value = trimmed; } if (name !== "" && search(name, NOT_HTTP_TOKEN) === -1 && search(value, NOT_HTTP_QUOTED_STRING) === -1 && !this.#data.has(name)) { this.#data.set(name, value); } position++; } }
delete(name: string): void { this.#parse(); this.#data.delete(name); }
get(name: string): string | null { this.#parse(); return this.#data.has(name) ? this.#data.get(name)! : null; }
has(name: string): boolean { this.#parse(); return this.#data.has(name); }
set(name: unknown, value: unknown): void { this.#parse(); const n = `${name}`; const v = `${value}`; const badName = search(n, NOT_HTTP_TOKEN); if (n.length === 0 || badName !== -1) throw invalidMimeSyntax("parameter name", n, badName); const badValue = search(v, NOT_HTTP_QUOTED_STRING); if (badValue !== -1) throw invalidMimeSyntax("parameter value", v, badValue); this.#data.set(n, v); }
*entries(): IterableIterator<[string, string]> { this.#parse(); yield* this.#data.entries(); }
*keys(): IterableIterator<string> { this.#parse(); yield* this.#data.keys(); }
*values(): IterableIterator<string> { this.#parse(); yield* this.#data.values(); }
toString(): string { this.#parse(); let out = ""; for (const [key, value] of this.#data) { if (out.length) out += ";"; out += `${key}=${encodeParamValue(value)}`; } return out; }
toJSON(): string { return this.toString(); }
[Symbol.iterator](): IterableIterator<[string, string]> { return this.entries(); }}
export class MIMEType { #type: string; #subtype: string; #params: MIMEParams;
constructor(input: unknown) { const str = `${input}`; const [type, subtype, rest] = parseTypeAndSubtype(str); this.#type = type; this.#subtype = subtype; this.#params = MIMEParams.instantiate(str.slice(rest)); }
get type(): string { return this.#type; }
set type(v: unknown) { const s = `${v}`; const bad = search(s, NOT_HTTP_TOKEN); if (s.length === 0 || bad !== -1) throw invalidMimeSyntax("type", s, bad); this.#type = toAsciiLower(s); }
get subtype(): string { return this.#subtype; }
set subtype(v: unknown) { const s = `${v}`; const bad = search(s, NOT_HTTP_TOKEN); if (s.length === 0 || bad !== -1) throw invalidMimeSyntax("subtype", s, bad); this.#subtype = toAsciiLower(s); }
get essence(): string { return `${this.#type}/${this.#subtype}`; }
get params(): MIMEParams { return this.#params; }
toString(): string { const paramStr = this.#params.toString(); return paramStr.length ? `${this.#type}/${this.#subtype};${paramStr}` : `${this.#type}/${this.#subtype}`; }
toJSON(): string { return this.toString(); }}