Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@otterhttp/content-type

Package Overview
Dependencies
Maintainers
0
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@otterhttp/content-type - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

46

dist/index.d.ts

@@ -12,8 +12,39 @@ import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';

/**
* Class to represent a content type.
* Representation of a parsed MIME type.
*/
declare class ContentType {
parameters: Record<string, unknown>;
type: string;
constructor(type: string);
/**
* The top-level media type into which the data type falls, such as `video` or `text`.
* e.g. in `application/json`, the type is `application`.
*/
readonly type: string;
/**
* The whole subtype, such as `manifest+json` or `plain`.
* e.g. in `text/conf+plain`, the subtype is `conf+plain`.
*/
readonly subtype: string;
/**
* The subtype suffix, such as `json` or `plain`.
* e.g. in `text/conf+plain`, the subtype suffix is `plain`.
*/
readonly subtypeSuffix: string;
/**
* Optional parameters added to provide additional details.
* For example, the `charset` parameter is often provided in HTTP contexts, e.g.
* `Content-Type: application/json; charset=utf-8`
*/
parameters: Record<string, string>;
static parse(contentType: string): ContentType;
/**
* @internal
*/
static fromValidatedInput(type: string, subtype: string, subtypeSuffix: string): ContentType;
protected constructor(type: string, subtype: string, subtypeSuffix: string);
toString(): string;
hasWildcard(): boolean;
isPlainText(): boolean;
/**
* The whole media type excluding parameters, such as `application/json` or `text/plain`.
*/
get mediaType(): string;
}

@@ -25,3 +56,4 @@ /**

type: string;
parameters?: Record<string, unknown>;
subtype: string;
parameters?: Record<string, string>;
}): string;

@@ -32,4 +64,4 @@ /**

declare function parse(value: TypeParseable): ContentType;
declare function isPlainText({ type }: ContentType): boolean;
declare function isPlainText({ type, subtypeSuffix }: ContentType): boolean;
export { type TypeParseable, type TypeParseableObject, format, isPlainText, parse };
export { ContentType, type TypeParseable, type TypeParseableObject, format, isPlainText, parse };

135

dist/index.js
// src/index.ts
var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u0009\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u0009\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g;
var TEXT_REGEXP = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
var QESC_REGEXP = /\\([\u0009\u0020-\u00ff])/g;
var QUOTE_REGEXP = /([\\"])/g;
import { formatParameters, parseParameters, validateParameterNames } from "@otterhttp/parameters";
var WHITESPACE_CHAR_REGEXP = /[\u0009\u0020]/;
var TOKEN_CHAR_REGEXP = /[!#$%&'*+.^_`|~0-9A-Za-z-]/;
var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
function qstring(val) {
const str = String(val);
if (TOKEN_REGEXP.test(str)) return str;
if (str.length > 0 && !TEXT_REGEXP.test(str)) throw new TypeError("invalid parameter value");
return `"${str.replace(QUOTE_REGEXP, "\\$1")}"`;
}
function getContentType(obj) {

@@ -26,19 +18,43 @@ let header;

}
var ContentType = class {
constructor(type) {
var ContentType = class _ContentType {
static parse(contentType) {
return parse(contentType);
}
/**
* @internal
*/
static fromValidatedInput(type, subtype, subtypeSuffix) {
return new _ContentType(type, subtype, subtypeSuffix);
}
constructor(type, subtype, subtypeSuffix) {
this.parameters = {};
this.type = type;
this.subtype = subtype;
this.subtypeSuffix = subtypeSuffix;
}
toString() {
return `${this.type}/${this.subtype}${formatParameters(this.parameters)}`;
}
hasWildcard() {
return this.type.indexOf("*") !== -1 || this.subtype.indexOf("*") !== -1;
}
isPlainText() {
return isPlainText(this);
}
/**
* The whole media type excluding parameters, such as `application/json` or `text/plain`.
*/
get mediaType() {
return `${this.type}/${this.subtype}`;
}
};
function format(obj) {
if (!obj || typeof obj !== "object") throw new TypeError("argument obj is required");
const { parameters, type } = obj;
if (!type || !TYPE_REGEXP.test(type)) throw new TypeError("invalid type");
let string = type;
const { parameters, type, subtype } = obj;
if (!type || !subtype) throw new TypeError("invalid type");
let string = `${type}/${subtype}`;
if (!TYPE_REGEXP.test(string)) throw new TypeError("invalid type");
if (parameters && typeof parameters === "object") {
const params = Object.keys(parameters).sort();
for (const param of params) {
if (!TOKEN_REGEXP.test(param)) throw new TypeError("invalid parameter name");
string += `; ${param}=${qstring(parameters[param])}`;
}
validateParameterNames(Object.keys(parameters));
string += formatParameters(parameters);
}

@@ -48,27 +64,43 @@ return string;

function parse(value) {
if (!value) throw new TypeError("argument string is required");
const header = typeof value === "object" ? getContentType(value) : value;
if (typeof header !== "string") throw new TypeError("argument string is required to be a string");
let index = header.indexOf(";");
const type = index !== -1 ? header.slice(0, index).trim() : header.trim();
if (!TYPE_REGEXP.test(type)) throw new TypeError("invalid media type");
const obj = new ContentType(type.toLowerCase());
if (index !== -1) {
let key;
let match;
let value2;
PARAM_REGEXP.lastIndex = index;
while (match = PARAM_REGEXP.exec(header)) {
if (match.index !== index) throw new TypeError("invalid parameter format");
index += match[0].length;
key = match[1].toLowerCase();
value2 = match[2];
if (value2[0] === '"') {
value2 = value2.slice(1, value2.length - 1).replace(QESC_REGEXP, "$1");
}
obj.parameters[key] = value2;
if (!value) throw new TypeError("argument `value` is required");
let header = typeof value === "object" ? getContentType(value) : value;
if (typeof header !== "string") throw new TypeError("argument `value` must be string, request-like or response-like");
header = header.trim();
let currentIndex = 0;
let slashIndex;
for (; currentIndex < header.length; ++currentIndex) {
const currentChar = header.charAt(currentIndex);
if (currentChar === "/") {
slashIndex = currentIndex;
break;
}
if (index !== header.length) throw new TypeError("invalid parameter format");
if (!TOKEN_CHAR_REGEXP.test(currentChar)) throw new TypeError("invalid media type");
}
return obj;
if (typeof slashIndex === "undefined") throw new TypeError("invalid media type");
if (slashIndex === 0) throw new TypeError("invalid media type");
currentIndex += 1;
let plusIndex;
let endIndex;
for (; currentIndex < header.length; ++currentIndex) {
const currentChar = header.charAt(currentIndex);
if (currentChar === ";" || WHITESPACE_CHAR_REGEXP.test(currentChar)) {
if (currentIndex === slashIndex + 1) throw new TypeError("invalid media type");
endIndex = currentIndex;
break;
}
if (currentChar === "+") {
if (currentIndex === slashIndex + 1) throw new TypeError("invalid media type");
plusIndex = currentIndex;
continue;
}
if (!TOKEN_CHAR_REGEXP.test(currentChar)) throw new TypeError("invalid media type");
}
const lowercaseHeader = header.toLowerCase();
const type = lowercaseHeader.slice(0, slashIndex);
const subtype = lowercaseHeader.slice(slashIndex + 1, endIndex);
const subtypeSuffix = plusIndex == null ? subtype : lowercaseHeader.slice(plusIndex + 1, endIndex);
const parsedRepresentation = ContentType.fromValidatedInput(type, subtype, subtypeSuffix);
if (endIndex === void 0) return parsedRepresentation;
parsedRepresentation.parameters = parseParameters(header.slice(endIndex));
return parsedRepresentation;
}

@@ -84,14 +116,9 @@ var applicationPlaintextWhitelist = /* @__PURE__ */ new Set([

]);
function isPlainText({ type }) {
if (type.startsWith("text/")) return true;
if (!type.startsWith("application/")) return false;
let index = 12;
let start = index;
for (; index < type.length; ++index) {
if (type.charAt(index) === "+") start = index + 1;
}
const subtype = type.slice(start);
return applicationPlaintextWhitelist.has(subtype);
function isPlainText({ type, subtypeSuffix }) {
if (type === "text") return true;
if (type !== "application") return false;
return applicationPlaintextWhitelist.has(subtypeSuffix);
}
export {
ContentType,
format,

@@ -98,0 +125,0 @@ isPlainText,

{
"name": "@otterhttp/content-type",
"description": "content-type rewrite in TypeScript",
"version": "0.3.0",
"version": "0.4.0",
"license": "LGPL-3.0-or-later",

@@ -25,3 +25,5 @@ "homepage": "https://otterhttp.lordfirespeed.dev",

],
"dependencies": {},
"dependencies": {
"@otterhttp/parameters": "0.1.0"
},
"scripts": {

@@ -28,0 +30,0 @@ "build": "tsup"

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc