@d1g1tal/media-type 5.0.1 → 6.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.
@@ -1,111 +0,0 @@
1
- import httpTokenCodePoints from './utils.js';
2
-
3
- const matcher = /(["\\])/ug;
4
- const httpQuotedStringTokenCodePoints = /^[\t\u0020-\u007E\u0080-\u00FF]*$/u;
5
-
6
- /**
7
- * Class representing the parameters for a media type record.
8
- * This class extends a JavaScript {@link Map}.
9
- *
10
- * However, {@link MediaTypeParameters} methods will always interpret their arguments
11
- * as appropriate for media types, so parameter names will be lowercased,
12
- * and attempting to set invalid characters will throw an {@link Error}.
13
- *
14
- * @extends Map
15
- * @see https://mimesniff.spec.whatwg.org/#mime-type-essence
16
- * @see https://mimesniff.spec.whatwg.org/#mime-type-essence-record
17
- * @see https://mimesniff.spec.whatwg.org/#mime-type-essence-record-creation
18
- * @see https://mimesniff.spec.whatwg.org/#mime-type-essence-record-creation-algorithm
19
- * @module {MediaTypeParameters} media-type-parameters
20
- * @author D1g1talEntr0py <jason.dimeo@gmail.com>
21
- */
22
- export default class MediaTypeParameters extends Map {
23
- /**
24
- * Create a new MediaTypeParameters instance.
25
- *
26
- * @param {Array<[string, string]>} entries An array of [name, value] tuples.
27
- */
28
- constructor(entries = []) {
29
- super(entries);
30
- }
31
-
32
- /**
33
- * Indicates whether the supplied name and value are valid media type parameters.
34
- *
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.
39
- */
40
- static isValid(name, value) {
41
- return httpTokenCodePoints.test(name) && httpQuotedStringTokenCodePoints.test(value);
42
- }
43
-
44
- /**
45
- * Gets the media type parameter value for the supplied name.
46
- *
47
- * @param {string} name The name of the media type parameter to retrieve.
48
- * @returns {string} The media type parameter value.
49
- */
50
- get(name) {
51
- return super.get(name.toLowerCase());
52
- }
53
-
54
- /**
55
- * Indicates whether the media type parameter with the specified name exists or not.
56
- *
57
- * @param {string} name The name of the media type parameter to check.
58
- * @returns {boolean} true if the media type parameter exists, false otherwise.
59
- */
60
- has(name) {
61
- return super.has(name.toLowerCase());
62
- }
63
-
64
- /**
65
- * Adds a new media type parameter using the specified name and value to the MediaTypeParameters.
66
- * If an parameter with the same name already exists, the parameter will be updated.
67
- *
68
- * @param {string} name The name of the media type parameter to set.
69
- * @param {string} value The media type parameter value.
70
- * @returns {MediaTypeParameters} This instance.
71
- */
72
- set(name, value) {
73
- if (!MediaTypeParameters.isValid(name, value)) {
74
- throw new Error(`Invalid media type parameter name/value: ${name}/${value}`);
75
- }
76
-
77
- super.set(name.toLowerCase(), value);
78
-
79
- return this;
80
- }
81
-
82
- /**
83
- * Removes the media type parameter using the specified name.
84
- *
85
- * @param {string} name The name of the media type parameter to delete.
86
- * @returns {boolean} true if the parameter existed and has been removed, or false if the parameter does not exist.
87
- */
88
- delete(name) {
89
- return super.delete(name.toLowerCase());
90
- }
91
-
92
- /**
93
- * Returns a string representation of the media type parameters.
94
- *
95
- * @override
96
- * @returns {string} The string representation of the media type parameters.
97
- */
98
- toString() {
99
- return Array.from(this).map(([ name, value ]) => `;${name}=${!value || !httpTokenCodePoints.test(value) ? `"${value.replace(matcher, '\\$1')}"` : value}`).join('');
100
- }
101
-
102
- /**
103
- * Returns the name of this class.
104
- *
105
- * @override
106
- * @returns {string} The name of this class.
107
- */
108
- [Symbol.toStringTag]() {
109
- return 'MediaTypeParameters';
110
- }
111
- }
@@ -1,120 +0,0 @@
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
- }
package/src/media-type.js DELETED
@@ -1,101 +0,0 @@
1
- import { _type } from '@d1g1tal/chrysalis';
2
- import MediaTypeParameters from './media-type-parameters.js';
3
- import MediaTypeParser from './media-type-parser.js';
4
-
5
- /**
6
- * Class used to parse media types.
7
- *
8
- * @module {MediaType} media-type
9
- * @see https://mimesniff.spec.whatwg.org/#understanding-mime-types
10
- */
11
- export default class MediaType {
12
- /** @type {string} */
13
- #type;
14
- /** @type {string} */
15
- #subtype;
16
- /** @type {MediaTypeParameters} */
17
- #parameters;
18
-
19
- /**
20
- * Create a new MediaType instance from a string representation.
21
- *
22
- * @param {string} mediaType The media type to parse.
23
- * @param {Object} [parameters] Optional parameters.
24
- */
25
- constructor(mediaType, parameters = {}) {
26
- if (_type(parameters) != Object) { throw new TypeError('The parameters argument must be an object') }
27
- ({ type: this.#type, subtype: this.#subtype, parameters: this.#parameters } = MediaTypeParser.parse(mediaType));
28
- for (const [ name, value ] of Object.entries(parameters)) { this.#parameters.set(name, value) }
29
- }
30
-
31
- static parse(mediaType) {
32
- try { return new MediaType(mediaType) } catch(e) { /* ignore */ }
33
-
34
- return null;
35
- }
36
-
37
- /**
38
- * Gets the type.
39
- *
40
- * @returns {string} The type.
41
- */
42
- get type() {
43
- return this.#type;
44
- }
45
-
46
- /**
47
- * Gets the subtype.
48
- *
49
- * @returns {string} The subtype.
50
- */
51
- get subtype() {
52
- return this.#subtype;
53
- }
54
-
55
- /**
56
- * Gets the media type essence (type/subtype).
57
- *
58
- * @returns {string} The media type without any parameters
59
- */
60
- get essence() {
61
- return `${this.#type}/${this.#subtype}`;
62
- }
63
-
64
- /**
65
- * Gets the parameters.
66
- *
67
- * @returns {MediaTypeParameters} The media type parameters.
68
- */
69
- get parameters() {
70
- return this.#parameters;
71
- }
72
-
73
- /**
74
- * Checks if the media type matches the specified type.
75
- *
76
- * @todo Fix string handling to parse the type and subtype from the string.
77
- * @param {MediaType|string} mediaType The media type to check.
78
- * @returns {boolean} true if the media type matches the specified type, false otherwise.
79
- */
80
- matches(mediaType) {
81
- return (this.#type === mediaType?.type && this.#subtype === mediaType?.subtype) || this.essence.includes(mediaType);
82
- }
83
-
84
- /**
85
- * Gets the serialized version of the media type.
86
- *
87
- * @returns {string} The serialized media type.
88
- */
89
- toString() {
90
- return `${this.essence}${this.#parameters.toString()}`;
91
- }
92
-
93
- /**
94
- * Gets the name of the class.
95
- *
96
- * @returns {string} The class name
97
- */
98
- get [Symbol.toStringTag]() {
99
- return 'MediaType';
100
- }
101
- }
package/src/utils.js DELETED
@@ -1,3 +0,0 @@
1
- const httpTokenCodePoints = /^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u;
2
-
3
- export default httpTokenCodePoints;
File without changes