UNPKG

@schukai/monster

Version:

Monster is a simple library for creating fast, robust and lightweight websites.

219 lines (189 loc) 5.02 kB
/** * Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved. * Node module: @schukai/monster * * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html * * For those who do not wish to adhere to the AGPLv3, a commercial license is available. * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms. * For more information about purchasing a commercial license, please contact Volker Schukai. * * SPDX-License-Identifier: AGPL-3.0 */ import { Base } from "./base.mjs"; import { isString } from "./is.mjs"; import { validateArray, validateString } from "./validate.mjs"; import { instanceSymbol } from "../constants.mjs"; export { MediaType, parseMediaType }; /** * @private * @type {symbol} */ const internal = Symbol("internal"); /** * @typedef {Object} Parameter * @property {string} key * @property {string} value */ /** * You can create an object via the monster namespace `new MediaType()`. * * @license AGPLv3 * @since 1.8.0 * @copyright Volker Schukai */ class MediaType extends Base { /** * * @param {String} type * @param {String} subtype * @param {Parameter[]} parameter */ constructor(type, subtype, parameter) { super(); this[internal] = { type: validateString(type).toLowerCase(), subtype: validateString(subtype).toLowerCase(), parameter: [], }; if (parameter !== undefined) { this[internal]["parameter"] = validateArray(parameter); } } /** * This method is called by the `instanceof` operator. * @return {symbol} * @since 2.1.0 */ static get [instanceSymbol]() { return Symbol.for("@schukai/monster/types/media-type"); } /** * @return {String} */ get type() { return this[internal].type; } /** * @return {String} */ get subtype() { return this[internal].subtype; } // /** // * @return {Parameter[]} // */ // get parameter() { // return this[internal].parameter; // } /** * * * @return {Map} */ get parameter() { const result = new Map(); this[internal]["parameter"].forEach((p) => { let value = p.value; // internally special values are partly stored with quotes, this function removes them. if (value.startsWith('"') && value.endsWith('"')) { value = value.substring(1, value.length - 1); } result.set(p.key, value); }); return result; } /** * * @return {string} */ toString() { const parameter = []; for (const a of this[internal].parameter) { parameter.push(`${a.key}=${a.value}`); } return `${this[internal].type}/${this[internal].subtype}${ parameter.length > 0 ? `;${parameter.join(";")}` : "" }`; } } /** * You can call the function via the monster namespace `parseMediaType()`. * * ``` * <script type="module"> * import {Monster} from '@schukai/monster/source/monster.mjs'; * console.log(parseMediaType()) * </script> * ``` * * Alternatively, you can also integrate this function individually. * * ``` * <script type="module"> * import {parseMediaType} from '@schukai/monster/source/types/dataurl.mjs'; * console.log(parseMediaType()) * </script> * ``` * * Specification: * * ``` * dataurl := "data:" [ mediatype ] [ ";base64" ] "," data * mediatype := [ type "/" subtype ] *( ";" parameter ) * data := *urlchar * parameter := attribute "=" value * ``` * * @param {String} mediatype * @return {MediaType} * @see https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 * @throws {TypeError} the mimetype can not be parsed * @throws {TypeError} blank value is not allowed * @throws {TypeError} malformed data url */ function parseMediaType(mediatype) { const regex = /(?<type>[A-Za-z]+|\*)\/(?<subtype>([a-zA-Z0-9.\+_\-]+)|\*|)(?<parameter>\s*;\s*([a-zA-Z0-9]+)\s*(=\s*("?[A-Za-z0-9_\-]+"?))?)*/g; const result = regex.exec(validateString(mediatype)); const groups = result?.["groups"]; if (groups === undefined) { throw new TypeError("the mimetype can not be parsed"); } const type = groups?.["type"]; const subtype = groups?.["subtype"]; const parameter = groups?.["parameter"]; if (subtype === "" || type === "") { throw new TypeError("blank value is not allowed"); } return new MediaType(type, subtype, parseParameter(parameter)); } /** * @private * @license AGPLv3 * @since 1.18.0 * @param {String} parameter * @return {Parameter[]|undefined} */ function parseParameter(parameter) { if (!isString(parameter)) { return undefined; } const result = []; parameter.split(";").forEach((entry) => { entry = entry.trim(); if (entry === "") { return; } const kv = entry.split("="); const key = validateString(kv?.[0]).trim(); const value = validateString(kv?.[1]).trim(); // if values are quoted, they remain so internally result.push({ key: key, value: value, }); }); return result; }