@d1g1tal/media-type 4.0.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +9 -4
- package/README.md +28 -45
- package/dist/media-type.js +276 -433
- package/dist/media-type.min.js +2 -3
- package/dist/media-type.min.js.map +4 -4
- package/package.json +80 -85
- package/src/media-type-parameters.js +31 -89
- package/src/media-type-parser.js +120 -0
- package/src/media-type.js +26 -122
- package/src/utils.js +1 -92
- package/index.d.ts +0 -5
- package/index.js +0 -5
- package/src/media-type-parameters.d.ts +0 -91
- package/src/media-type.d.ts +0 -91
- package/src/parser.d.ts +0 -13
- package/src/parser.js +0 -106
- package/src/serializer.d.ts +0 -11
- package/src/serializer.js +0 -32
- package/src/utils.d.ts +0 -52
package/dist/media-type.min.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,"\r"]
|
|
3
|
-
window.MediaType = MediaType.default;
|
|
1
|
+
var y=u=>u?.constructor??u?.prototype?.constructor??globalThis[Object.prototype.toString.call(u).slice(8,-1)]??u,p=/^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u,m=/(["\\])/ug,d=/^[\t\u0020-\u007E\u0080-\u00FF]*$/u,c=class h extends Map{constructor(t=[]){super(t)}static isValid(t,e){return p.test(t)&&d.test(e)}get(t){return super.get(t.toLowerCase())}has(t){return super.has(t.toLowerCase())}set(t,e){if(!h.isValid(t,e))throw Error(`Invalid media type parameter name/value: ${t}/${e}`);return super.set(t.toLowerCase(),e),this}delete(t){return super.delete(t.toLowerCase())}toString(){return Array.from(this).map(([t,e])=>`;${t}=${e&&p.test(e)?e:`"${e.replace(m,"\\$1")}"`}`).join("")}[Symbol.toStringTag](){return"MediaTypeParameters"}},f=[" "," ",`
|
|
2
|
+
`,"\r"],$=/[ \t\n\r]+$/u,b=/^[ \t\n\r]+|[ \t\n\r]+$/ug,w=class o{static parse(t){t=t.replace(b,"");let e=t.length,{position:r,result:i,subtype:n=""}=o.#t({input:t},"/");if(!i.length||r>=e||!p.test(i))throw TypeError(o.#e("type",i));if({position:r,result:n}=o.#t({position:++r,input:t,trim:!0},";"),!n.length||!p.test(n))throw TypeError(o.#e("subtype",n));let s="",a=null,l=new c;for(;r++<e;){for(;f.includes(t[r]);)++r;if({position:r,result:s}=o.#t({position:r,input:t,lowerCase:!1},";","="),r<e){if(t[r]==";")continue;++r}if(t[r]=='"')for([a,r]=o.#r(t,r);r<e&&t[r]!=";";)++r;else if({position:r,result:a}=o.#t({position:r,input:t,lowerCase:!1,trim:!0},";"),!a)continue;s&&c.isValid(s,a)&&!l.has(s)&&l.set(s,a)}return{type:i,subtype:n,parameters:l}}static#t({position:t=0,input:e,lowerCase:r=!0,trim:i=!1},...n){let s="";for(let a=e.length;t<a&&!n.includes(e[t]);t++)s+=e[t];return r&&(s=s.toLowerCase()),i&&(s=s.replace($,"")),{position:t,result:s}}static#r(t,e){let r="";for(let i=t.length,n;++e<i&&(n=t[e])!='"';)r+=n=="\\"&&++e<i?t[e]:n;return[r,e]}static#e(t,e){return`Invalid ${t} "${e}": only HTTP token code points are valid.`}},T=class g{#t;#r;#e;constructor(t,e={}){if(y(e)!=Object)throw TypeError("The parameters argument must be an object");for(let[r,i]of({type:this.#t,subtype:this.#r,parameters:this.#e}=w.parse(t),Object.entries(e)))this.#e.set(r,i)}static parse(t){try{return new g(t)}catch{}return null}get type(){return this.#t}get subtype(){return this.#r}get essence(){return`${this.#t}/${this.#r}`}get parameters(){return this.#e}matches(t){return this.#t==t?.type&&this.#r==t?.subtype||this.essence.includes(t)}toString(){return`${this.essence}${this.#e.toString()}`}get[Symbol.toStringTag](){return"MediaType"}};export{T as default};
|
|
4
3
|
//# sourceMappingURL=media-type.min.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["
|
|
4
|
-
"sourcesContent": ["import MediaTypeParameters from './media-type-parameters.js';\nimport parse from './parser.js';\nimport serialize from './serializer.js';\nimport { asciiLowercase, solelyContainsHTTPTokenCodePoints } from './utils.js';\n\n/**\n * Class used to parse media types.\n *\n * @module {MediaType} media-type\n * @see https://mimesniff.spec.whatwg.org/#understanding-mime-types\n */\nexport default class MediaType {\n\t/** @type {string} */\n\t#type;\n\t/** @type {string} */\n\t#subtype;\n\t/** @type {MediaTypeParameters} */\n\t#parameters;\n\n\t/**\n\t * Create a new MediaType instance from a string representation.\n\t *\n\t * @param {string|object} options The media type string or an object with type, subtype, and parameters.\n\t */\n\tconstructor(options) {\n\t\t// If options is a string, parse it\n\t\tif (typeof options === 'string') {\n\t\t\toptions = parse(options);\n\t\t}\n\n\t\tconst { type, subtype, parameters = new MediaTypeParameters() } = options;\n\t\tthis.#type = type;\n\t\tthis.#subtype = subtype;\n\n\t\t// Check if parameters are provided as a Map\n\t\tthis.#parameters = typeof parameters == Map && parameters.size > 0\t? new MediaTypeParameters(parameters.entries())\t: parameters;\n\t}\n\n\t/**\n\t * Static factory method for parsing a media type.\n\t *\n\t * @param {string} string The media type to parse.\n\t * @returns {MediaType} The parsed {@link MediaType} object or null if the string could not be parsed.\n\t */\n\tstatic parse(string) {\n\t\ttry {\n\t\t\treturn new MediaType(parse(string));\n\t\t} catch (e) {\n\t\t\tthrow new Error(`Could not parse media type string '${string}'`);\n\t\t}\n\t}\n\n\t/**\n\t * Gets the media type essence (type/subtype).\n\t *\n\t * @returns {string} The media type without any parameters\n\t */\n\tget essence() {\n\t\treturn `${this.#type}/${this.#subtype}`;\n\t}\n\n\t/**\n\t * Gets the type.\n\t *\n\t * @returns {string} The type.\n\t */\n\tget type() {\n\t\treturn this.#type;\n\t}\n\n\t/**\n\t * Sets the type.\n\t */\n\tset type(value) {\n\t\tvalue = asciiLowercase(String(value));\n\n\t\tif (value.length === 0) {\n\t\t\tthrow new Error('Invalid type: must be a non-empty string');\n\t\t}\n\t\tif (!solelyContainsHTTPTokenCodePoints(value)) {\n\t\t\tthrow new Error(`Invalid type ${value}: must contain only HTTP token code points`);\n\t\t}\n\n\t\tthis.#type = value;\n\t}\n\n\t/**\n\t * Gets the subtype.\n\t *\n\t * @returns {string} The subtype.\n\t */\n\tget subtype() {\n\t\treturn this.#subtype;\n\t}\n\n\t/**\n\t * Sets the subtype.\n\t */\n\tset subtype(value) {\n\t\tvalue = asciiLowercase(String(value));\n\n\t\tif (value.length === 0) {\n\t\t\tthrow new Error('Invalid subtype: must be a non-empty string');\n\t\t}\n\t\tif (!solelyContainsHTTPTokenCodePoints(value)) {\n\t\t\tthrow new Error(`Invalid subtype ${value}: must contain only HTTP token code points`);\n\t\t}\n\n\t\tthis.#subtype = value;\n\t}\n\n\t/**\n\t * Gets the parameters.\n\t *\n\t * @returns {MediaTypeParameters} The media type parameters.\n\t */\n\tget parameters() {\n\t\treturn this.#parameters;\n\t}\n\n\t/**\n\t * Gets the serialized version of the media type.\n\t *\n\t * @returns {string} The serialized media type.\n\t */\n\ttoString() {\n\t\t// The serialize function works on both 'media type records' (i.e. the results of parse) and on this class, since\n\t\t// this class's interface is identical.\n\t\treturn serialize(this);\n\t}\n\n\t/**\n\t * Determines if this instance is a JavaScript media type.\n\t *\n\t * @param {Object} [options] Optional options.\n\t * @param {boolean} [options.prohibitParameters=false] The option to prohibit parameters when checking if the media type is JavaScript.\n\t * @returns {boolean} true if this instance represents a JavaScript media type, false otherwise.\n\t */\n\tisJavaScript({prohibitParameters = false} = {}) {\n\t\tswitch (this.#type) {\n\t\t\tcase 'text': {\n\t\t\t\tswitch (this.#subtype) {\n\t\t\t\t\tcase 'ecmascript':\n\t\t\t\t\tcase 'javascript':\n\t\t\t\t\tcase 'javascript1.0':\n\t\t\t\t\tcase 'javascript1.1':\n\t\t\t\t\tcase 'javascript1.2':\n\t\t\t\t\tcase 'javascript1.3':\n\t\t\t\t\tcase 'javascript1.4':\n\t\t\t\t\tcase 'javascript1.5':\n\t\t\t\t\tcase 'jscript':\n\t\t\t\t\tcase 'livescript':\n\t\t\t\t\tcase 'x-ecmascript':\n\t\t\t\t\tcase 'x-javascript': return !prohibitParameters || this.#parameters.size === 0;\n\t\t\t\t\tdefault: return false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase 'application': {\n\t\t\t\tswitch (this.#subtype) {\n\t\t\t\t\tcase 'ecmascript':\n\t\t\t\t\tcase 'javascript':\n\t\t\t\t\tcase 'x-ecmascript':\n\t\t\t\t\tcase 'x-javascript': return !prohibitParameters || this.#parameters.size === 0;\n\t\t\t\t\tdefault: return false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\n\t/**\n\t * Determines if this instance is an XML media type.\n\t *\n\t * @returns {boolean} true if this instance represents an XML media type, false otherwise.\n\t */\n\tisXML() {\n\t\treturn (this.#subtype === 'xml' && (this.#type === 'text' || this.#type === 'application')) || this.#subtype.endsWith('+xml');\n\t}\n\n\t/**\n\t * Determines if this instance is an HTML media type.\n\t *\n\t * @returns {boolean} true if this instance represents an HTML media type, false otherwise.\n\t */\n\tisHTML() {\n\t\treturn this.#subtype === 'html' && this.#type === 'text';\n\t}\n\n\t/**\n\t * Gets the name of the class.\n\t *\n\t * @returns {string} The class name\n\t */\n\tget [Symbol.toStringTag]() {\n\t\treturn 'MediaType';\n\t}\n}", "/** @module utils */\n\nconst whitespaceCharacters = [' ', '\\t', '\\n', '\\r'];\nconst leadingWhitespace = /^[ \\t\\n\\r]+/u;\nconst trailingWhitespace = /[ \\t\\n\\r]+$/u;\nconst httpTokenCodePoints = /^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u;\nconst httpQuotedTokenCodePoints = /^[\\t\\u0020-\\u007E\\u0080-\\u00FF]*$/u;\n\n/**\n * A function to remove any leading and trailing HTTP whitespace.\n *\n * @param {string} string The string to process.\n * @returns {string} The processed string.\n */\nconst removeLeadingAndTrailingHTTPWhitespace = (string) => string.replace(leadingWhitespace, '').replace(trailingWhitespace, '');\n\n/**\n * A function to remove any trailing HTTP whitespace.\n *\n * @param {string} string The string to process.\n * @returns {string} The processed string.\n */\nconst removeTrailingHTTPWhitespace = (string) => string.replace(trailingWhitespace, '');\n\n/**\n * Determines if the provided character is whitespace.\n *\n * @param {string} char The character to evaluate.\n * @returns {boolean} true if the character is whitespace, false otherwise.\n */\nconst isHTTPWhitespaceChar = (char) => whitespaceCharacters.includes(char);\n\n/**\n * Determines if the provided string contains only HTTP token code points.\n *\n * @param {string} string The string to evaluate.\n * @returns {boolean} true if the string contains only HTTP token code points.\n */\nconst solelyContainsHTTPTokenCodePoints = (string) => httpTokenCodePoints.test(string);\n\n/**\n * Determines if the provided string contains only quoted HTTP token code points.\n *\n * @param {string} string The string to evaluate.\n * @returns {boolean} true if the string contains only quoted HTTP token code points.\n */\nconst solelyContainsHTTPQuotedStringTokenCodePoints = (string) => httpQuotedTokenCodePoints.test(string);\n\n/**\n * A function to lower case ASCII characters.\n * This implementation iterates over each element of the string, which is treated as an iterable.\n * The elements are destructured into [char, charCode] pairs, where char represents the character,\n * and charCode is assigned the character code using char.charCodeAt(0). If the charCode is not\n * provided, it falls back to the character code obtained from char.charCodeAt(0).\n *\n * @param {string} string The string to process.\n * @returns {string} The processed string with all ASCII characters lower cased.\n */\nconst asciiLowercase = (string) => {\n\tlet result = '';\n\tfor (const [char, charCode = char.charCodeAt(0)] of string) {\n\t\tresult += charCode >= 65 && charCode <= 90 ? String.fromCharCode(charCode + 32) : char;\n\t}\n\n\treturn result;\n};\n\n/**\n * Collects all the HTTP quoted strings.\n * This variant only implements it with the extract-value flag set.\n *\n * @param {string} input The string to process.\n * @param {number} position The starting position.\n * @returns {Array<string|number>} An array that includes the resulting string and updated position.\n */\nconst collectAnHTTPQuotedString = (input, position) => {\n\tlet value = '';\n\n\tfor (let length = input.length, char; ++position < length;) {\n\t\tchar = input[position];\n\n\t\tif (char == '\\\\') {\n\t\t\tvalue += ++position < length ? input[position] : char;\n\t\t} else if (char == '\"') {\n\t\t\tbreak;\n\t\t} else {\n\t\t\tvalue += char;\n\t\t}\n\t}\n\n\treturn [value, position];\n};\n\nexport { removeLeadingAndTrailingHTTPWhitespace, removeTrailingHTTPWhitespace, isHTTPWhitespaceChar, solelyContainsHTTPTokenCodePoints, solelyContainsHTTPQuotedStringTokenCodePoints, asciiLowercase, collectAnHTTPQuotedString };\n", "import { asciiLowercase, solelyContainsHTTPQuotedStringTokenCodePoints, solelyContainsHTTPTokenCodePoints } from './utils.js';\n\n/**\n * Class representing the parameters for a media type record.\n * This class has the equivalent surface API to a JavaScript {@link Map}.\n *\n * However, {@link MediaTypeParameters} methods will always interpret their arguments\n * as appropriate for media types, so parameter names will be lowercased,\n * and attempting to set invalid characters will throw an {@link Error}.\n *\n * @example charset=utf-8\n * @see https://mimesniff.spec.whatwg.org/#mime-type-essence\n * @see https://mimesniff.spec.whatwg.org/#mime-type-essence-record\n * @see https://mimesniff.spec.whatwg.org/#mime-type-essence-record-creation\n * @see https://mimesniff.spec.whatwg.org/#mime-type-essence-record-creation-algorithm\n * @module {MediaTypeParameters} media-type-parameters\n * @author D1g1talEntr0py <jason.dimeo@gmail.com>\n */\nexport default class MediaTypeParameters {\n\t/** @type {Map<string, string>} */\n\t#map;\n\n\t/**\n\t * Create a new MediaTypeParameters instance.\n\t *\n\t * @param {Map<string, string>} map The map of parameters for a media type.\n\t */\n\tconstructor(map = new Map()) {\n\t\tthis.#map = map;\n\t}\n\n\t/**\n\t * Gets the number of media type parameters.\n\t *\n\t * @returns {number} The number of media type parameters\n\t */\n\tget size() {\n\t\treturn this.#map.size;\n\t}\n\n\t/**\n\t * Gets the media type parameter value for the supplied name.\n\t *\n\t * @param {string} name The name of the media type parameter to retrieve.\n\t * @returns {string} The media type parameter value.\n\t */\n\tget(name) {\n\t\treturn this.#map.get(asciiLowercase(String(name)));\n\t}\n\n\t/**\n\t * Indicates whether the media type parameter with the specified name exists or not.\n\t *\n\t * @param {string} name The name of the media type parameter to check.\n\t * @returns {boolean} true if the media type parameter exists, false otherwise.\n\t */\n\thas(name) {\n\t\treturn this.#map.has(asciiLowercase(String(name)));\n\t}\n\n\t/**\n\t * Adds a new media type parameter using the specified name and value to the MediaTypeParameters.\n\t * If an parameter with the same name already exists, the parameter will be updated.\n\t *\n\t * @param {string} name The name of the media type parameter to set.\n\t * @param {string} value The media type parameter value.\n\t * @returns {MediaTypeParameters} This instance.\n\t */\n\tset(name, value) {\n\t\tname = asciiLowercase(String(name));\n\t\tvalue = String(value);\n\n\t\tif (!solelyContainsHTTPTokenCodePoints(name)) {\n\t\t\tthrow new Error(`Invalid media type parameter name \"${name}\": only HTTP token code points are valid.`);\n\t\t}\n\n\t\tif (!solelyContainsHTTPQuotedStringTokenCodePoints(value)) {\n\t\t\tthrow new Error(`Invalid media type parameter value \"${value}\": only HTTP quoted-string token code points are valid.`);\n\t\t}\n\n\t\tthis.#map.set(name, value);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Clears all the media type parameters.\n\t */\n\tclear() {\n\t\tthis.#map.clear();\n\t}\n\n\t/**\n\t * Removes the media type parameter using the specified name.\n\t *\n\t * @param {string} name The name of the media type parameter to delete.\n\t * @returns {boolean} true if the parameter existed and has been removed, or false if the parameter does not exist.\n\t */\n\tdelete(name) {\n\t\tname = asciiLowercase(String(name));\n\t\treturn this.#map.delete(name);\n\t}\n\n\t/**\n\t * Executes a provided function once per each name/value pair in the MediaTypeParameters, in insertion order.\n\t *\n\t * @param {function(string, string): void} callback The function called on each iteration.\n\t * @param {*} [thisArg] Optional object when binding 'this' to the callback.\n\t */\n\tforEach(callback, thisArg) {\n\t\tthis.#map.forEach(callback, thisArg);\n\t}\n\n\t/**\n\t * Returns an iterable of parameter names.\n\t *\n\t * @returns {IterableIterator<string>} The {@link IterableIterator} of media type parameter names.\n\t */\n\tkeys() {\n\t\treturn this.#map.keys();\n\t}\n\n\t/**\n\t * Returns an iterable of parameter values.\n\t *\n\t * @returns {IterableIterator<string>} The {@link IterableIterator} of media type parameter values.\n\t */\n\tvalues() {\n\t\treturn this.#map.values();\n\t}\n\n\t/**\n\t * Returns an iterable of name, value pairs for every parameter entry in the media type parameters.\n\t *\n\t * @returns {IterableIterator<Array<Array<string>>>} The media type parameter entries.\n\t */\n\tentries() {\n\t\treturn this.#map.entries();\n\t}\n\n\t/**\n\t * A method that returns the default iterator for the {@link MediaTypeParameters}. Called by the semantics of the for-of statement.\n\t *\n\t * @returns {Iterator<string, string, undefined>} The {@link Symbol.iterator} for the media type parameters.\n\t */\n\t[Symbol.iterator]() {\n\t\treturn this.#map[Symbol.iterator]();\n\t}\n\n\t/**\n\t * Returns a string representation of the media type parameters.\n\t * This method is called by the `String()` function.\n\t *\n\t * @example\n\t * const parameters = new MediaTypeParameters(new Map([['charset', 'utf-8']]));\n\t * String(parameters); // 'charset=utf-8'\n\t * parameters.toString(); // 'charset=utf-8'\n\t * parameters + ''; // 'charset=utf-8'\n\t * `${parameters}`; // 'charset=utf-8'\n\t * parameters[Symbol.toStringTag]; // 'MediaTypeParameters'\n\t * parameters[Symbol.toStringTag](); // 'MediaTypeParameters'\n\t * Object.prototype.toString.call(parameters); // '[object MediaTypeParameters]'\n\t * parameters + ''; // 'charset=utf-8'\n\t * @returns {string} The string representation of the media type parameters.\n\t */\n\t[Symbol.toStringTag]() {\n\t\treturn 'MediaTypeParameters';\n\t}\n}", "import {\n\tasciiLowercase,\n\tcollectAnHTTPQuotedString, isHTTPWhitespaceChar, removeLeadingAndTrailingHTTPWhitespace,\n\tremoveTrailingHTTPWhitespace, solelyContainsHTTPQuotedStringTokenCodePoints, solelyContainsHTTPTokenCodePoints\n} from './utils.js';\n\n/**\n * Function to parse a media type.\n *\n * @module parser\n * @param {string} input The media type to parse\n * @returns {{ type: string, subtype: string, parameters: Map<string, string> }} An object populated with the parsed media type properties and any parameters.\n */\nconst parse = (input) => {\n\tinput = removeLeadingAndTrailingHTTPWhitespace(input);\n\n\tlet position = 0;\n\tlet type = '';\n\twhile (position < input.length && input[position] != '/') {\n\t\ttype += input[position];\n\t\t++position;\n\t}\n\n\tif (type.length === 0 || !solelyContainsHTTPTokenCodePoints(type)) {\n\t\treturn null;\n\t}\n\n\tif (position >= input.length) {\n\t\treturn null;\n\t}\n\n\t// Skips past \"/\"\n\t++position;\n\n\tlet subtype = '';\n\twhile (position < input.length && input[position] != ';') {\n\t\tsubtype += input[position];\n\t\t++position;\n\t}\n\n\tsubtype = removeTrailingHTTPWhitespace(subtype);\n\n\tif (subtype.length === 0 || !solelyContainsHTTPTokenCodePoints(subtype)) {\n\t\treturn null;\n\t}\n\n\tconst mediaType = {\n\t\ttype: asciiLowercase(type),\n\t\tsubtype: asciiLowercase(subtype),\n\t\tparameters: new Map()\n\t};\n\n\twhile (position < input.length) {\n\t\t// Skip past \";\"\n\t\t++position;\n\n\t\twhile (isHTTPWhitespaceChar(input[position])) {\n\t\t\t++position;\n\t\t}\n\n\t\tlet parameterName = '';\n\t\twhile (position < input.length && input[position] != ';' && input[position] != '=') {\n\t\t\tparameterName += input[position];\n\t\t\t++position;\n\t\t}\n\t\tparameterName = asciiLowercase(parameterName);\n\n\t\tif (position < input.length) {\n\t\t\tif (input[position] == ';') {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip past \"=\"\n\t\t\t++position;\n\t\t}\n\n\t\tlet parameterValue = null;\n\t\tif (input[position] == '\"') {\n\t\t\t[parameterValue, position] = collectAnHTTPQuotedString(input, position);\n\n\t\t\twhile (position < input.length && input[position] != ';') {\n\t\t\t\t++position;\n\t\t\t}\n\t\t} else {\n\t\t\tparameterValue = '';\n\t\t\twhile (position < input.length && input[position] != ';') {\n\t\t\t\tparameterValue += input[position];\n\t\t\t\t++position;\n\t\t\t}\n\n\t\t\tparameterValue = removeTrailingHTTPWhitespace(parameterValue);\n\n\t\t\tif (parameterValue === '') {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (parameterName.length > 0 &&\tsolelyContainsHTTPTokenCodePoints(parameterName) &&\tsolelyContainsHTTPQuotedStringTokenCodePoints(parameterValue) && !mediaType.parameters.has(parameterName)) {\n\t\t\tmediaType.parameters.set(parameterName, parameterValue);\n\t\t}\n\t}\n\n\treturn mediaType;\n};\n\nexport default parse;", "import { solelyContainsHTTPTokenCodePoints } from './utils.js';\n\n/** @typedef { import('./media-type.js').default } MediaType */\n\n/**\n * A function that serializes the provided {@link mediaType} to a string.\n *\n * @module serializer\n * @param {MediaType} mediaType The media type to serialize.\n * @returns {string} The serialized media type.\n */\nconst serialize = (mediaType) => {\n\tlet serialization = `${mediaType.type}/${mediaType.subtype}`;\n\n\tif (mediaType.parameters.size === 0) {\n\t\treturn serialization;\n\t}\n\n\tfor (let [name, value] of mediaType.parameters) {\n\t\tserialization += `;${name}=`;\n\n\t\tif (!solelyContainsHTTPTokenCodePoints(value) || value.length === 0) {\n\t\t\tvalue = `\"${value.replace(/([\"\\\\])/ug, '\\\\$1')}\"`;\n\t\t}\n\n\t\tserialization += value;\n\t}\n\n\treturn serialization;\n};\n\nexport default serialize;"],
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["
|
|
3
|
+
"sources": ["media-type.js"],
|
|
4
|
+
"sourcesContent": ["var e=e=>e?.constructor??e?.prototype?.constructor??globalThis[Object.prototype.toString.call(e).slice(8,-1)]??e,t=/^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u,r=/([\"\\\\])/ug,s=/^[\\t\\u0020-\\u007E\\u0080-\\u00FF]*$/u,n=class e extends Map{constructor(e=[]){super(e)}static isValid(e,r){return t.test(e)&&s.test(r)}get(e){return super.get(e.toLowerCase())}has(e){return super.has(e.toLowerCase())}set(t,r){if(!e.isValid(t,r))throw Error(`Invalid media type parameter name/value: ${t}/${r}`);return super.set(t.toLowerCase(),r),this}delete(e){return super.delete(e.toLowerCase())}toString(){return Array.from(this).map(([e,s])=>`;${e}=${s&&t.test(s)?s:`\"${s.replace(r,\"\\\\$1\")}\"`}`).join(\"\")}[Symbol.toStringTag](){return\"MediaTypeParameters\"}},a=[\" \",\"\t\",\"\\n\",\"\\r\"],o=/[ \\t\\n\\r]+$/u,i=/^[ \\t\\n\\r]+|[ \\t\\n\\r]+$/ug,u=class e{static parse(r){r=r.replace(i,\"\");let s=r.length,{position:o,result:u,subtype:l=\"\"}=e.#e({input:r},\"/\");if(!u.length||o>=s||!t.test(u))throw TypeError(e.#t(\"type\",u));if({position:o,result:l}=e.#e({position:++o,input:r,trim:!0},\";\"),!l.length||!t.test(l))throw TypeError(e.#t(\"subtype\",l));let p=\"\",c=null,h=new n;for(;o++<s;){for(;a.includes(r[o]);)++o;if({position:o,result:p}=e.#e({position:o,input:r,lowerCase:!1},\";\",\"=\"),o<s){if(\";\"==r[o])continue;++o}if('\"'==r[o])for([c,o]=e.#r(r,o);o<s&&\";\"!=r[o];)++o;else if({position:o,result:c}=e.#e({position:o,input:r,lowerCase:!1,trim:!0},\";\"),!c)continue;p&&n.isValid(p,c)&&!h.has(p)&&h.set(p,c)}return{type:u,subtype:l,parameters:h}}static #e({position:e=0,input:t,lowerCase:r=!0,trim:s=!1},...n){let a=\"\";for(let r=t.length;e<r&&!n.includes(t[e]);e++)a+=t[e];return r&&(a=a.toLowerCase()),s&&(a=a.replace(o,\"\")),{position:e,result:a}}static #r(e,t){let r=\"\";for(let s=e.length,n;++t<s&&'\"'!=(n=e[t]);)r+=\"\\\\\"==n&&++t<s?e[t]:n;return[r,t]}static #t(e,t){return`Invalid ${e} \"${t}\": only HTTP token code points are valid.`}},l=class t{#s;#n;#a;constructor(t,r={}){if(e(r)!=Object)throw TypeError(\"The parameters argument must be an object\");for(let[e,s]of({type:this.#s,subtype:this.#n,parameters:this.#a}=u.parse(t),Object.entries(r)))this.#a.set(e,s)}static parse(e){try{return new t(e)}catch(e){}return null}get type(){return this.#s}get subtype(){return this.#n}get essence(){return`${this.#s}/${this.#n}`}get parameters(){return this.#a}matches(e){return this.#s==e?.type&&this.#n==e?.subtype||this.essence.includes(e)}toString(){return`${this.essence}${this.#a.toString()}`}get[Symbol.toStringTag](){return\"MediaType\"}};export{l as default};"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["e", "t", "r", "s", "n", "a", "o", "i", "u", "l", "#e", "#t", "p", "c", "h", "#s", "#n", "#a"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,85 +1,80 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@d1g1tal/media-type",
|
|
3
|
-
"description": "Parses, serializes, and manipulates media types, according to the WHATWG MIME Sniffing Standard",
|
|
4
|
-
"version": "
|
|
5
|
-
"author": "Domenic Denicola <d@domenic.me> (https://domenic.me/)",
|
|
6
|
-
"maintainers": [
|
|
7
|
-
{
|
|
8
|
-
"name": "Jason DiMeo",
|
|
9
|
-
"email": "jason.dimeo@gmail.com"
|
|
10
|
-
}
|
|
11
|
-
],
|
|
12
|
-
"license": "
|
|
13
|
-
"repository": {
|
|
14
|
-
"type": "git",
|
|
15
|
-
"url": "git+https://github.com/D1g1talEntr0py/media-type.git"
|
|
16
|
-
},
|
|
17
|
-
"publishConfig": {
|
|
18
|
-
"access": "public"
|
|
19
|
-
},
|
|
20
|
-
"type": "module",
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"testMatch": [
|
|
82
|
-
"<rootDir>/test/**/*.js"
|
|
83
|
-
]
|
|
84
|
-
}
|
|
85
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@d1g1tal/media-type",
|
|
3
|
+
"description": "Parses, serializes, and manipulates media types, according to the WHATWG MIME Sniffing Standard",
|
|
4
|
+
"version": "5.0.0",
|
|
5
|
+
"author": "Domenic Denicola <d@domenic.me> (https://domenic.me/)",
|
|
6
|
+
"maintainers": [
|
|
7
|
+
{
|
|
8
|
+
"name": "Jason DiMeo",
|
|
9
|
+
"email": "jason.dimeo@gmail.com"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/D1g1talEntr0py/media-type.git"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": "./src/media-type.js",
|
|
23
|
+
"./*.js": "./src/*.js",
|
|
24
|
+
"./dist/*": "./dist/*"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"/dist",
|
|
28
|
+
"/src"
|
|
29
|
+
],
|
|
30
|
+
"keywords": [
|
|
31
|
+
"content-type",
|
|
32
|
+
"mime type",
|
|
33
|
+
"media type",
|
|
34
|
+
"mimesniff",
|
|
35
|
+
"http",
|
|
36
|
+
"whatwg"
|
|
37
|
+
],
|
|
38
|
+
"packageManager": "pnpm@8.10.2+sha256.487609e857f1c11780cc98dd0bfe4c8a8b11c7f23bc3a4493ac7d263d6fb6c8c",
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"esbuild": "^0.19.5",
|
|
44
|
+
"esbuild-library": "^1.0.2",
|
|
45
|
+
"eslint": "^8.53.0",
|
|
46
|
+
"eslint-plugin-compat": "^4.2.0",
|
|
47
|
+
"eslint-plugin-jsdoc": "^46.9.0",
|
|
48
|
+
"jest": "^29.7.0",
|
|
49
|
+
"printable-string": "^0.3.0",
|
|
50
|
+
"whatwg-encoding": "^3.0.0"
|
|
51
|
+
},
|
|
52
|
+
"browserslist": [
|
|
53
|
+
"defaults",
|
|
54
|
+
"not ios_saf < 15",
|
|
55
|
+
"not op_mini all"
|
|
56
|
+
],
|
|
57
|
+
"jest": {
|
|
58
|
+
"collectCoverage": true,
|
|
59
|
+
"coverageDirectory": "./tests/coverage",
|
|
60
|
+
"collectCoverageFrom": [
|
|
61
|
+
"src/**/*.js"
|
|
62
|
+
],
|
|
63
|
+
"testPathIgnorePatterns": [
|
|
64
|
+
"tests/scripts"
|
|
65
|
+
],
|
|
66
|
+
"coveragePathIgnorePatterns": [
|
|
67
|
+
"/node_modules/"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@d1g1tal/chrysalis": "^2.2.0"
|
|
72
|
+
},
|
|
73
|
+
"scripts": {
|
|
74
|
+
"build": "node ./esbuild.js",
|
|
75
|
+
"test": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
76
|
+
"lint": "eslint --ext .js --fix --ignore-path .gitignore .",
|
|
77
|
+
"coverage": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
|
|
78
|
+
"pretest": "node tests/scripts/get-latest-platform-tests.js"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import httpTokenCodePoints from './utils.js';
|
|
2
|
+
|
|
3
|
+
const matcher = /(["\\])/ug;
|
|
4
|
+
const httpQuotedStringTokenCodePoints = /^[\t\u0020-\u007E\u0080-\u00FF]*$/u;
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Class representing the parameters for a media type record.
|
|
5
|
-
* This class
|
|
8
|
+
* This class extends a JavaScript {@link Map}.
|
|
6
9
|
*
|
|
7
10
|
* However, {@link MediaTypeParameters} methods will always interpret their arguments
|
|
8
11
|
* as appropriate for media types, so parameter names will be lowercased,
|
|
9
12
|
* and attempting to set invalid characters will throw an {@link Error}.
|
|
10
13
|
*
|
|
11
|
-
* @
|
|
14
|
+
* @extends Map
|
|
12
15
|
* @see https://mimesniff.spec.whatwg.org/#mime-type-essence
|
|
13
16
|
* @see https://mimesniff.spec.whatwg.org/#mime-type-essence-record
|
|
14
17
|
* @see https://mimesniff.spec.whatwg.org/#mime-type-essence-record-creation
|
|
@@ -16,26 +19,26 @@ import { asciiLowercase, solelyContainsHTTPQuotedStringTokenCodePoints, solelyCo
|
|
|
16
19
|
* @module {MediaTypeParameters} media-type-parameters
|
|
17
20
|
* @author D1g1talEntr0py <jason.dimeo@gmail.com>
|
|
18
21
|
*/
|
|
19
|
-
export default class MediaTypeParameters {
|
|
20
|
-
/** @type {Map<string, string>} */
|
|
21
|
-
#map;
|
|
22
|
-
|
|
22
|
+
export default class MediaTypeParameters extends Map {
|
|
23
23
|
/**
|
|
24
24
|
* Create a new MediaTypeParameters instance.
|
|
25
25
|
*
|
|
26
|
-
* @param {
|
|
26
|
+
* @param {Array<[string, string]>} entries An array of [name, value] tuples.
|
|
27
27
|
*/
|
|
28
|
-
constructor(
|
|
29
|
-
|
|
28
|
+
constructor(entries = []) {
|
|
29
|
+
super(entries);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
33
|
+
* Indicates whether the supplied name and value are valid media type parameters.
|
|
34
34
|
*
|
|
35
|
-
* @
|
|
35
|
+
* @static
|
|
36
|
+
* @param {string} name The name of the media type parameter to validate.
|
|
37
|
+
* @param {string} value The media type parameter value to validate.
|
|
38
|
+
* @returns {boolean} true if the media type parameter is valid, false otherwise.
|
|
36
39
|
*/
|
|
37
|
-
|
|
38
|
-
return
|
|
40
|
+
static isValid(name, value) {
|
|
41
|
+
return httpTokenCodePoints.test(name) && httpQuotedStringTokenCodePoints.test(value);
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
/**
|
|
@@ -45,7 +48,7 @@ export default class MediaTypeParameters {
|
|
|
45
48
|
* @returns {string} The media type parameter value.
|
|
46
49
|
*/
|
|
47
50
|
get(name) {
|
|
48
|
-
return
|
|
51
|
+
return super.get(name.toLowerCase());
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
/**
|
|
@@ -55,7 +58,7 @@ export default class MediaTypeParameters {
|
|
|
55
58
|
* @returns {boolean} true if the media type parameter exists, false otherwise.
|
|
56
59
|
*/
|
|
57
60
|
has(name) {
|
|
58
|
-
return
|
|
61
|
+
return super.has(name.toLowerCase());
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
/**
|
|
@@ -67,29 +70,15 @@ export default class MediaTypeParameters {
|
|
|
67
70
|
* @returns {MediaTypeParameters} This instance.
|
|
68
71
|
*/
|
|
69
72
|
set(name, value) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!solelyContainsHTTPTokenCodePoints(name)) {
|
|
74
|
-
throw new Error(`Invalid media type parameter name "${name}": only HTTP token code points are valid.`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (!solelyContainsHTTPQuotedStringTokenCodePoints(value)) {
|
|
78
|
-
throw new Error(`Invalid media type parameter value "${value}": only HTTP quoted-string token code points are valid.`);
|
|
73
|
+
if (!MediaTypeParameters.isValid(name, value)) {
|
|
74
|
+
throw new Error(`Invalid media type parameter name/value: ${name}/${value}`);
|
|
79
75
|
}
|
|
80
76
|
|
|
81
|
-
|
|
77
|
+
super.set(name.toLowerCase(), value);
|
|
82
78
|
|
|
83
79
|
return this;
|
|
84
80
|
}
|
|
85
81
|
|
|
86
|
-
/**
|
|
87
|
-
* Clears all the media type parameters.
|
|
88
|
-
*/
|
|
89
|
-
clear() {
|
|
90
|
-
this.#map.clear();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
82
|
/**
|
|
94
83
|
* Removes the media type parameter using the specified name.
|
|
95
84
|
*
|
|
@@ -97,71 +86,24 @@ export default class MediaTypeParameters {
|
|
|
97
86
|
* @returns {boolean} true if the parameter existed and has been removed, or false if the parameter does not exist.
|
|
98
87
|
*/
|
|
99
88
|
delete(name) {
|
|
100
|
-
|
|
101
|
-
return this.#map.delete(name);
|
|
89
|
+
return super.delete(name.toLowerCase());
|
|
102
90
|
}
|
|
103
91
|
|
|
104
92
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* @param {function(string, string): void} callback The function called on each iteration.
|
|
108
|
-
* @param {*} [thisArg] Optional object when binding 'this' to the callback.
|
|
109
|
-
*/
|
|
110
|
-
forEach(callback, thisArg) {
|
|
111
|
-
this.#map.forEach(callback, thisArg);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Returns an iterable of parameter names.
|
|
116
|
-
*
|
|
117
|
-
* @returns {IterableIterator<string>} The {@link IterableIterator} of media type parameter names.
|
|
118
|
-
*/
|
|
119
|
-
keys() {
|
|
120
|
-
return this.#map.keys();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Returns an iterable of parameter values.
|
|
125
|
-
*
|
|
126
|
-
* @returns {IterableIterator<string>} The {@link IterableIterator} of media type parameter values.
|
|
127
|
-
*/
|
|
128
|
-
values() {
|
|
129
|
-
return this.#map.values();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Returns an iterable of name, value pairs for every parameter entry in the media type parameters.
|
|
134
|
-
*
|
|
135
|
-
* @returns {IterableIterator<Array<Array<string>>>} The media type parameter entries.
|
|
136
|
-
*/
|
|
137
|
-
entries() {
|
|
138
|
-
return this.#map.entries();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* A method that returns the default iterator for the {@link MediaTypeParameters}. Called by the semantics of the for-of statement.
|
|
93
|
+
* Returns a string representation of the media type parameters.
|
|
143
94
|
*
|
|
144
|
-
* @
|
|
95
|
+
* @override
|
|
96
|
+
* @returns {string} The string representation of the media type parameters.
|
|
145
97
|
*/
|
|
146
|
-
|
|
147
|
-
return this
|
|
98
|
+
toString() {
|
|
99
|
+
return Array.from(this).map(([ name, value ]) => `;${name}=${!value || !httpTokenCodePoints.test(value) ? `"${value.replace(matcher, '\\$1')}"` : value}`).join('');
|
|
148
100
|
}
|
|
149
101
|
|
|
150
102
|
/**
|
|
151
|
-
* Returns
|
|
152
|
-
* This method is called by the `String()` function.
|
|
103
|
+
* Returns the name of this class.
|
|
153
104
|
*
|
|
154
|
-
* @
|
|
155
|
-
*
|
|
156
|
-
* String(parameters); // 'charset=utf-8'
|
|
157
|
-
* parameters.toString(); // 'charset=utf-8'
|
|
158
|
-
* parameters + ''; // 'charset=utf-8'
|
|
159
|
-
* `${parameters}`; // 'charset=utf-8'
|
|
160
|
-
* parameters[Symbol.toStringTag]; // 'MediaTypeParameters'
|
|
161
|
-
* parameters[Symbol.toStringTag](); // 'MediaTypeParameters'
|
|
162
|
-
* Object.prototype.toString.call(parameters); // '[object MediaTypeParameters]'
|
|
163
|
-
* parameters + ''; // 'charset=utf-8'
|
|
164
|
-
* @returns {string} The string representation of the media type parameters.
|
|
105
|
+
* @override
|
|
106
|
+
* @returns {string} The name of this class.
|
|
165
107
|
*/
|
|
166
108
|
[Symbol.toStringTag]() {
|
|
167
109
|
return 'MediaTypeParameters';
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import MediaTypeParameters from './media-type-parameters.js';
|
|
2
|
+
import httpTokenCodePoints from './utils.js';
|
|
3
|
+
|
|
4
|
+
const whitespaceCharacters = [' ', '\t', '\n', '\r'];
|
|
5
|
+
const trailingWhitespace = /[ \t\n\r]+$/u;
|
|
6
|
+
const leadingAndTrailingWhitespace = /^[ \t\n\r]+|[ \t\n\r]+$/ug;
|
|
7
|
+
|
|
8
|
+
export default class MediaTypeParser {
|
|
9
|
+
/**
|
|
10
|
+
* Function to parse a media type.
|
|
11
|
+
*
|
|
12
|
+
* @static
|
|
13
|
+
* @param {string} input The media type to parse
|
|
14
|
+
* @returns {{ type: string, subtype: string, parameters: MediaTypeParameters }} An object populated with the parsed media type properties and any parameters.
|
|
15
|
+
*/
|
|
16
|
+
static parse(input) {
|
|
17
|
+
input = input.replace(leadingAndTrailingWhitespace, '');
|
|
18
|
+
|
|
19
|
+
const length = input.length, trim = true, lowerCase = false;
|
|
20
|
+
let { position, result: type, subtype = '' } = MediaTypeParser.#filterComponent({ input }, '/');
|
|
21
|
+
|
|
22
|
+
if (!type.length || position >= length || !httpTokenCodePoints.test(type)) { throw new TypeError(MediaTypeParser.#generateErrorMessage('type', type)) }
|
|
23
|
+
|
|
24
|
+
({ position, result: subtype } = MediaTypeParser.#filterComponent({ position: ++position, input, trim }, ';')); // `++position` Skips past "/"
|
|
25
|
+
|
|
26
|
+
if (!subtype.length || !httpTokenCodePoints.test(subtype)) { throw new TypeError(MediaTypeParser.#generateErrorMessage('subtype', subtype)) }
|
|
27
|
+
|
|
28
|
+
let parameterName = '', parameterValue = null;
|
|
29
|
+
const parameters = new MediaTypeParameters();
|
|
30
|
+
|
|
31
|
+
while (position++ < length) { // `position++` Skips past "/"
|
|
32
|
+
while (whitespaceCharacters.includes(input[position])) { ++position }
|
|
33
|
+
|
|
34
|
+
({ position, result: parameterName } = MediaTypeParser.#filterComponent({ position, input, lowerCase }, ';', '='));
|
|
35
|
+
|
|
36
|
+
if (position < length) {
|
|
37
|
+
if (input[position] == ';') { continue }
|
|
38
|
+
|
|
39
|
+
// Skip past "="
|
|
40
|
+
++position;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (input[position] == '"') {
|
|
44
|
+
[ parameterValue, position ] = MediaTypeParser.#collectHttpQuotedString(input, position);
|
|
45
|
+
|
|
46
|
+
while (position < length && input[position] != ';') { ++position }
|
|
47
|
+
} else {
|
|
48
|
+
({ position, result: parameterValue } = MediaTypeParser.#filterComponent({ position, input, lowerCase, trim }, ';'));
|
|
49
|
+
|
|
50
|
+
if (!parameterValue) { continue }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (parameterName && MediaTypeParameters.isValid(parameterName, parameterValue) && !parameters.has(parameterName)) {
|
|
54
|
+
parameters.set(parameterName, parameterValue);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { type, subtype, parameters };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Filters a component from the input string.
|
|
63
|
+
*
|
|
64
|
+
* @private
|
|
65
|
+
* @static
|
|
66
|
+
* @param {Object} options The options.
|
|
67
|
+
* @param {number} [options.position] The starting position.
|
|
68
|
+
* @param {string} options.input The input string.
|
|
69
|
+
* @param {boolean} [options.lowerCase] Indicates whether the result should be lowercased.
|
|
70
|
+
* @param {boolean} [options.trim] Indicates whether the result should be trimmed.
|
|
71
|
+
* @param {string[]} charactersToFilter The characters to filter.
|
|
72
|
+
* @returns {{ position: number, result: string }} An object that includes the resulting string and updated position.
|
|
73
|
+
*/
|
|
74
|
+
static #filterComponent({ position = 0, input, lowerCase = true, trim = false }, ...charactersToFilter) {
|
|
75
|
+
let result = '';
|
|
76
|
+
for (const length = input.length; position < length && !charactersToFilter.includes(input[position]); position++) {
|
|
77
|
+
result += input[position];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (lowerCase) { result = result.toLowerCase() }
|
|
81
|
+
if (trim) { result = result.replace(trailingWhitespace, '') }
|
|
82
|
+
|
|
83
|
+
return { position, result };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Collects all the HTTP quoted strings.
|
|
88
|
+
* This variant only implements it with the extract-value flag set.
|
|
89
|
+
*
|
|
90
|
+
* @private
|
|
91
|
+
* @static
|
|
92
|
+
* @param {string} input The string to process.
|
|
93
|
+
* @param {number} position The starting position.
|
|
94
|
+
* @returns {Array<string|number>} An array that includes the resulting string and updated position.
|
|
95
|
+
*/
|
|
96
|
+
static #collectHttpQuotedString(input, position) {
|
|
97
|
+
let value = '';
|
|
98
|
+
|
|
99
|
+
for (let length = input.length, char; ++position < length;) {
|
|
100
|
+
if ((char = input[position]) == '"') { break }
|
|
101
|
+
|
|
102
|
+
value += char == '\\' && ++position < length ? input[position] : char;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return [ value, position ];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Generates an error message.
|
|
110
|
+
*
|
|
111
|
+
* @private
|
|
112
|
+
* @static
|
|
113
|
+
* @param {string} component The component name.
|
|
114
|
+
* @param {string} value The component value.
|
|
115
|
+
* @returns {string} The error message.
|
|
116
|
+
*/
|
|
117
|
+
static #generateErrorMessage(component, value) {
|
|
118
|
+
return `Invalid ${component} "${value}": only HTTP token code points are valid.`;
|
|
119
|
+
}
|
|
120
|
+
}
|