Socket
Socket
Sign inDemoInstall

prom-client

Package Overview
Dependencies
Maintainers
3
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

prom-client - npm Package Compare versions

Comparing version 14.2.0 to 15.0.0-0

lib/exemplar.js

73

index.d.ts
// Type definitions for prom-client
// Definitions by: Simon Nyberg http://twitter.com/siimon_nyberg
export type Charset = 'utf-8';
export type PrometheusMIME = 'text/plain';
export type PrometheusMetricsVersion = '0.0.4';
export type OpenMetricsMIME = 'application/openmetrics-text';
export type OpenMetricsVersion = '1.0.0';
export type PrometheusContentType =
`${OpenMetricsMIME}; version=${OpenMetricsVersion}; charset=${Charset}`;
export type OpenMetricsContentType =
`${PrometheusMIME}; version=${PrometheusMetricsVersion}; charset=${Charset}`;
export type RegistryContentType =
| PrometheusContentType
| OpenMetricsContentType;
/**
* Container for all registered metrics
*/
export class Registry {
export class Registry<RegistryContentType = PrometheusContentType> {
/**

@@ -67,5 +84,12 @@ * Get string representation for all metrics

*/
contentType: string;
contentType: RegistryContentType;
/**
* Set the content type of a registry. Used to change between Prometheus and
* OpenMetrics versions.
* @param contentType The type of the registry
*/
setContentType(contentType: RegistryContentType): void;
/**
* Merge registers

@@ -84,6 +108,17 @@ * @param registers The registers you want to merge together

/**
* The Content-Type of the metrics for use in the response headers.
* HTTP Content-Type for metrics response headers, defaults to Prometheus text
* format.
*/
export const contentType: string;
export const contentType: RegistryContentType;
/**
* HTTP Prometheus Content-Type for metrics response headers.
*/
export const prometheusContentType: PrometheusContentType;
/**
* HTTP OpenMetrics Content-Type for metrics response headers.
*/
export const openMetricsContentType: OpenMetricsContentType;
export class AggregatorRegistry extends Registry {

@@ -169,5 +204,9 @@ /**

labelNames?: T[] | readonly T[];
registers?: Registry[];
registers?: (
| Registry<PrometheusContentType>
| Registry<OpenMetricsContentType>
)[];
aggregator?: Aggregator;
collect?: CollectFunction<any>;
enableExemplars?: boolean;
}

@@ -180,2 +219,14 @@

export interface IncreaseDataWithExemplar<T extends string> {
value?: number;
labels?: LabelValues<T>;
exemplarLabels?: LabelValues<T>;
}
export interface ObserveDataWithExemplar<T extends string> {
value: number;
labels?: LabelValues<T>;
exemplarLabels?: LabelValues<T>;
}
/**

@@ -204,2 +255,8 @@ * A counter is a cumulative metric that represents a single numerical value that only ever goes up

/**
* Increment with exemplars
* @param incData Object with labels, value and exemplars for an increase
*/
inc(incData: IncreaseDataWithExemplar<T>): void;
/**
* Get counter metric object

@@ -419,2 +476,8 @@ */

/**
* Observe with exemplars
* @param observeData Object with labels, value and exemplars for an observation
*/
observe(observeData: ObserveDataWithExemplar<T>): void;
/**
* Get histogram metric object

@@ -421,0 +484,0 @@ */

@@ -11,2 +11,6 @@ /**

exports.contentType = require('./lib/registry').globalRegistry.contentType;
exports.prometheusContentType =
require('./lib/registry').PROMETHEUS_CONTENT_TYPE;
exports.openMetricsContentType =
require('./lib/registry').OPENMETRICS_CONTENT_TYPE;
exports.validateMetricName = require('./lib/validation').validateMetricName;

@@ -13,0 +17,0 @@

@@ -31,4 +31,4 @@ 'use strict';

class AggregatorRegistry extends Registry {
constructor() {
super();
constructor(regContentType = Registry.PROMETHEUS_CONTENT_TYPE) {
super(regContentType);
addListeners();

@@ -88,2 +88,6 @@ }

get contentType() {
return super.contentType;
}
/**

@@ -96,8 +100,15 @@ * Creates a new Registry instance from an array of metrics that were

* `registry.getMetricsAsJSON()`.
* @param {string} registryType content type of the new registry. Defaults
* to PROMETHEUS_CONTENT_TYPE.
* @return {Registry} aggregated registry.
*/
static aggregate(metricsArr) {
static aggregate(
metricsArr,
registryType = Registry.PROMETHEUS_CONTENT_TYPE,
) {
const aggregatedRegistry = new Registry();
const metricsByName = new Grouper();
aggregatedRegistry.setContentType(registryType);
// Gather by name

@@ -104,0 +115,0 @@ metricsArr.forEach(metrics => {

@@ -7,8 +7,28 @@ /**

const util = require('util');
const type = 'counter';
const { hashObject, isObject, getLabels, removeLabels } = require('./util');
const {
hashObject,
isObject,
getLabels,
removeLabels,
nowTimestamp,
} = require('./util');
const { validateLabel } = require('./validation');
const { Metric } = require('./metric');
const Exemplar = require('./exemplar');
class Counter extends Metric {
constructor(config) {
super(config);
this.type = 'counter';
this.defaultLabels = {};
this.defaultValue = 1;
this.defaultExemplarLabelSet = {};
if (config.enableExemplars) {
this.enableExemplars = true;
this.inc = this.incWithExemplar;
} else {
this.inc = this.incWithoutExemplar;
}
}
/**

@@ -18,6 +38,7 @@ * Increment counter

* @param {Number} value - Value to increment, if omitted increment with 1
* @returns {void}
* @returns {object} results - object with information about the inc operation
* @returns {string} results.labelHash - hash representation of the labels
*/
inc(labels, value) {
let hash;
incWithoutExemplar(labels, value) {
let hash = '';
if (isObject(labels)) {

@@ -41,5 +62,40 @@ hash = hashObject(labels);

setValue(this.hashMap, value, labels, hash);
return { labelHash: hash };
}
/**
* Increment counter with exemplar, same as inc but accepts labels for an
* exemplar.
* If no label is provided the current exemplar labels are kept unchanged
* (defaults to empty set).
*
* @param {object} incOpts - Object with options about what metric to increase
* @param {object} incOpts.labels - What label you want to be incremented,
* defaults to null (metric with no labels)
* @param {Number} incOpts.value - Value to increment, defaults to 1
* @param {object} incOpts.exemplarLabels - Key-value labels for the
* exemplar, defaults to empty set {}
* @returns {void}
*/
incWithExemplar({
labels = this.defaultLabels,
value = this.defaultValue,
exemplarLabels = this.defaultExemplarLabelSet,
} = {}) {
const res = this.incWithoutExemplar(labels, value);
this.updateExemplar(exemplarLabels, value, res.labelHash);
}
updateExemplar(exemplarLabels, value, hash) {
if (!isObject(this.hashMap[hash].exemplar)) {
this.hashMap[hash].exemplar = new Exemplar();
}
this.hashMap[hash].exemplar.validateExemplarLabelSet(exemplarLabels);
this.hashMap[hash].exemplar.labelSet = exemplarLabels;
this.hashMap[hash].exemplar.value = value ? value : 1;
this.hashMap[hash].exemplar.timestamp = nowTimestamp();
}
/**
* Reset counter

@@ -60,6 +116,7 @@ * @returns {void}

}
return {
help: this.help,
name: this.name,
type,
type: this.type,
values: Object.values(this.hashMap),

@@ -66,0 +123,0 @@ aggregator: this.aggregator,

8

lib/gauge.js

@@ -7,3 +7,2 @@ /**

const util = require('util');
const type = 'gauge';

@@ -22,2 +21,7 @@ const {

class Gauge extends Metric {
constructor(config) {
super(config);
this.type = 'gauge';
}
/**

@@ -114,3 +118,3 @@ * Set a gauge to a value

name: this.name,
type,
type: this.type,
values: Object.values(this.hashMap),

@@ -117,0 +121,0 @@ aggregator: this.aggregator,

@@ -7,6 +7,12 @@ /**

const util = require('util');
const type = 'histogram';
const { getLabels, hashObject, isObject, removeLabels } = require('./util');
const {
getLabels,
hashObject,
isObject,
removeLabels,
nowTimestamp,
} = require('./util');
const { validateLabel } = require('./validation');
const { Metric } = require('./metric');
const Exemplar = require('./exemplar');

@@ -19,2 +25,12 @@ class Histogram extends Metric {

this.type = 'histogram';
this.defaultLabels = {};
this.defaultExemplarLabelSet = {};
if (config.enableExemplars) {
this.observe = this.observeWithExemplar;
} else {
this.observe = this.observeWithoutExemplar;
}
for (const label of this.labelNames) {

@@ -32,3 +48,9 @@ if (label === 'le') {

this.bucketExemplars = this.upperBounds.reduce((acc, upperBound) => {
acc[upperBound] = null;
return acc;
}, {});
Object.freeze(this.bucketValues);
Object.freeze(this.bucketExemplars);
Object.freeze(this.upperBounds);

@@ -41,2 +63,3 @@

Object.assign({}, this.bucketValues),
Object.assign({}, this.bucketExemplars),
),

@@ -53,6 +76,29 @@ };

*/
observe(labels, value) {
observeWithoutExemplar(labels, value) {
observe.call(this, labels === 0 ? 0 : labels || {})(value);
}
observeWithExemplar({
labels = this.defaultLabels,
value,
exemplarLabels = this.defaultExemplarLabelSet,
} = {}) {
observe.call(this, labels === 0 ? 0 : labels || {})(value);
this.updateExemplar(labels, value, exemplarLabels);
}
updateExemplar(labels, value, exemplarLabels) {
const hash = hashObject(labels);
const b = findBound(this.upperBounds, value);
if (!isObject(this.hashMap[hash].bucketExemplars[b])) {
this.hashMap[hash].bucketExemplars[b] = new Exemplar();
}
this.hashMap[hash].bucketExemplars[b].validateExemplarLabelSet(
exemplarLabels,
);
this.hashMap[hash].bucketExemplars[b].labelSet = exemplarLabels;
this.hashMap[hash].bucketExemplars[b].value = value;
this.hashMap[hash].bucketExemplars[b].timestamp = nowTimestamp();
}
async get() {

@@ -71,3 +117,3 @@ if (this.collect) {

help: this.help,
type,
type: this.type,
values,

@@ -92,2 +138,3 @@ aggregator: this.aggregator,

Object.assign({}, this.bucketValues),
Object.assign({}, this.bucketExemplars),
);

@@ -139,3 +186,3 @@ }

function setValuePair(labels, value, metricName) {
function setValuePair(labels, value, metricName, exemplar) {
return {

@@ -145,2 +192,3 @@ labels,

metricName,
exemplar,
};

@@ -176,2 +224,3 @@ }

Object.assign({}, this.bucketValues),
Object.assign({}, this.bucketExemplars),
);

@@ -193,6 +242,7 @@ }

function createBaseValues(labels, bucketValues) {
function createBaseValues(labels, bucketValues, bucketExemplars) {
return {
labels,
bucketValues,
bucketExemplars,
sum: 0,

@@ -227,3 +277,10 @@ count: 0,

}
buckets.push(setValuePair(lbls, acc, `${histogram.name}_bucket`));
buckets.push(
setValuePair(
lbls,
acc,
`${histogram.name}_bucket`,
bucketData.bucketExemplars[upperBound],
),
);
}

@@ -243,3 +300,8 @@ return { buckets, data: bucketData };

acc.push(
setValuePair(infLabel, d.data.count, `${histogram.name}_bucket`),
setValuePair(
infLabel,
d.data.count,
`${histogram.name}_bucket`,
d.data.bucketExemplars['-1'],
),
setValuePair(d.data.labels, d.data.sum, `${histogram.name}_sum`),

@@ -246,0 +308,0 @@ setValuePair(d.data.labels, d.data.count, `${histogram.name}_count`),

'use strict';
const { globalRegistry } = require('./registry');
const Registry = require('./registry');
const { isObject } = require('./util');

@@ -19,4 +19,5 @@ const { validateMetricName, validateLabelName } = require('./validation');

labelNames: [],
registers: [globalRegistry],
registers: [Registry.globalRegistry],
aggregator: 'sum',
enableExemplars: false,
},

@@ -28,3 +29,3 @@ defaults,

// in case config.registers is `undefined`
this.registers = [globalRegistry];
this.registers = [Registry.globalRegistry];
}

@@ -50,2 +51,10 @@ if (!this.help) {

for (const register of this.registers) {
if (
this.enableExemplars &&
register.contentType === Registry.PROMETHEUS_CONTENT_TYPE
) {
throw new TypeError(
'Exemplars are supported only on OpenMetrics registries',
);
}
register.registerMetric(this);

@@ -52,0 +61,0 @@ }

@@ -40,2 +40,3 @@ 'use strict';

labelNames: ['kind', ...labelNames],
enableExemplars: false,
buckets,

@@ -51,3 +52,2 @@ registers: registry ? [registry] : undefined,

const kind = entry.detail ? kinds[entry.detail.kind] : kinds[entry.kind];
// Convert duration from milliseconds to seconds

@@ -54,0 +54,0 @@ gcHistogram.observe(Object.assign({ kind }, labels), entry.duration / 1000);

'use strict';
const OtelApi = require('@opentelemetry/api');
const Counter = require('../counter');
const PROCESS_CPU_USER_SECONDS = 'process_cpu_user_seconds_total';

@@ -12,2 +14,3 @@ const PROCESS_CPU_SYSTEM_SECONDS = 'process_cpu_system_seconds_total';

const labels = config.labels ? config.labels : {};
const exemplars = config.enableExemplars ? config.enableExemplars : false;
const labelNames = Object.keys(labels);

@@ -20,2 +23,3 @@

help: 'Total user CPU time spent in seconds.',
enableExemplars: exemplars,
registers,

@@ -32,5 +36,34 @@ labelNames,

cpuUserUsageCounter.inc(labels, userUsageMicros / 1e6);
cpuSystemUsageCounter.inc(labels, systemUsageMicros / 1e6);
cpuUsageCounter.inc(labels, (userUsageMicros + systemUsageMicros) / 1e6);
if (this.enableExemplars) {
let exemplarLabels = {};
const currentSpan = OtelApi.trace.getSpan(OtelApi.context.active());
if (currentSpan) {
exemplarLabels = {
traceId: currentSpan.spanContext().traceId,
spanId: currentSpan.spanContext().spanId,
};
}
cpuUserUsageCounter.inc({
labels,
value: userUsageMicros / 1e6,
exemplarLabels,
});
cpuSystemUsageCounter.inc({
labels,
value: systemUsageMicros / 1e6,
exemplarLabels,
});
cpuUsageCounter.inc({
labels,
value: (userUsageMicros + systemUsageMicros) / 1e6,
exemplarLabels,
});
} else {
cpuUserUsageCounter.inc(labels, userUsageMicros / 1e6);
cpuSystemUsageCounter.inc(labels, systemUsageMicros / 1e6);
cpuUsageCounter.inc(
labels,
(userUsageMicros + systemUsageMicros) / 1e6,
);
}
},

@@ -41,2 +74,3 @@ });

help: 'Total system CPU time spent in seconds.',
enableExemplars: exemplars,
registers,

@@ -48,2 +82,3 @@ labelNames,

help: 'Total user and system CPU time spent in seconds.',
enableExemplars: exemplars,
registers,

@@ -50,0 +85,0 @@ labelNames,

'use strict';
const { getValueAsString } = require('./util');
function escapeString(str) {
return str.replace(/\n/g, '\\n').replace(/\\(?!n)/g, '\\\\');
}
function escapeLabelValue(str) {
if (typeof str !== 'string') {
return str;
class Registry {
static get PROMETHEUS_CONTENT_TYPE() {
return 'text/plain; version=0.0.4; charset=utf-8';
}
return escapeString(str).replace(/"/g, '\\"');
}
class Registry {
constructor() {
static get OPENMETRICS_CONTENT_TYPE() {
return 'application/openmetrics-text; version=1.0.0; charset=utf-8';
}
constructor(regContentType = Registry.PROMETHEUS_CONTENT_TYPE) {
this._metrics = {};
this._collectors = [];
this._defaultLabels = {};
if (
regContentType !== Registry.PROMETHEUS_CONTENT_TYPE &&
regContentType !== Registry.OPENMETRICS_CONTENT_TYPE
) {
throw new TypeError(`Content type ${regContentType} is unsupported`);
}
this._contentType = regContentType;
}

@@ -25,20 +31,27 @@

async getMetricAsPrometheusString(metric) {
const item = await metric.get();
const name = escapeString(item.name);
const help = `# HELP ${name} ${escapeString(item.help)}`;
const type = `# TYPE ${name} ${item.type}`;
async getMetricsAsString(metrics) {
const metric = await metrics.get();
const name = escapeString(metric.name);
const help = `# HELP ${name} ${escapeString(metric.help)}`;
const type = `# TYPE ${name} ${metric.type}`;
const values = [help, type];
const defaultLabels =
Object.keys(this._defaultLabels).length > 0 ? this._defaultLabels : null;
const values = [help, type];
for (const { metricName = item.name, value, labels = {} } of item.values ||
[]) {
const labelsWithDefaults = defaultLabels
? { ...labels, ...defaultLabels, ...labels }
: labels;
for (const val of metric.values || []) {
let { metricName = name, labels = {} } = val;
if (
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE &&
metric.type === 'counter'
) {
metricName = `${metricName}_total`;
}
const formattedLabels = Object.entries(labelsWithDefaults).map(
([n, v]) => `${n}="${escapeLabelValue(v)}"`,
);
if (defaultLabels) {
labels = { ...labels, ...defaultLabels, ...labels };
}
const formattedLabels = formatLabels(labels);
const labelsString = formattedLabels.length

@@ -48,3 +61,15 @@ ? `{${formattedLabels.join(',')}}`

values.push(`${metricName}${labelsString} ${getValueAsString(value)}`);
values.push(
`${metricName}${labelsString} ${getValueAsString(val.value)}`,
);
const { exemplar } = val;
if (exemplar && this.contentType === Registry.OPENMETRICS_CONTENT_TYPE) {
const formattedExemplars = formatLabels(exemplar.labelSet);
values.push(
` # {${formattedExemplars.join(',')}} ${getValueAsString(
exemplar.value,
)} ${exemplar.timestamp}`,
);
}
}

@@ -59,3 +84,9 @@

for (const metric of this.getMetricsAsArray()) {
promises.push(this.getMetricAsPrometheusString(metric));
if (
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE &&
metric.type === 'counter'
) {
metric.name = standardizeCounterName(metric.name);
}
promises.push(this.getMetricsAsString(metric));
}

@@ -65,3 +96,7 @@

return `${resolves.join('\n\n')}\n`;
if (this.contentType === Registry.OPENMETRICS_CONTENT_TYPE) {
return `${resolves.join('\n')}\n# EOF\n`;
} else {
return `${resolves.join('\n\n')}\n`;
}
}

@@ -120,3 +155,3 @@

getSingleMetricAsString(name) {
return this.getMetricAsPrometheusString(this._metrics[name]);
return this.getMetricsAsString(this._metrics[name]);
}

@@ -139,7 +174,26 @@

get contentType() {
return 'text/plain; version=0.0.4; charset=utf-8';
return this._contentType;
}
setContentType(metricsContentType) {
if (
metricsContentType === Registry.OPENMETRICS_CONTENT_TYPE ||
metricsContentType === Registry.PROMETHEUS_CONTENT_TYPE
) {
this._contentType = metricsContentType;
} else {
throw new Error(`Content type ${metricsContentType} is unsupported`);
}
}
static merge(registers) {
const mergedRegistry = new Registry();
const regType = registers[0].contentType;
for (const reg of registers) {
if (reg.contentType !== regType) {
throw new Error(
'Registers can only be merged if they have the same content type',
);
}
}
const mergedRegistry = new Registry(regType);

@@ -156,3 +210,22 @@ const metricsToMerge = registers.reduce(

function formatLabels(labels) {
return Object.entries(labels).map(
([n, v]) => `${n}="${escapeLabelValue(v)}"`,
);
}
function escapeLabelValue(str) {
if (typeof str !== 'string') {
return str;
}
return escapeString(str).replace(/"/g, '\\"');
}
function escapeString(str) {
return str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n');
}
function standardizeCounterName(name) {
return name.replace(/_total$/, '');
}
module.exports = Registry;
module.exports.globalRegistry = new Registry();

@@ -7,3 +7,2 @@ /**

const util = require('util');
const type = 'summary';
const { getLabels, hashObject, removeLabels } = require('./util');

@@ -24,2 +23,4 @@ const { validateLabel } = require('./validation');

this.type = 'summary';
for (const label of this.labelNames) {

@@ -78,3 +79,3 @@ if (label === 'quantile')

help: this.help,
type,
type: this.type,
values,

@@ -81,0 +82,0 @@ aggregator: this.aggregator,

@@ -90,2 +90,6 @@ 'use strict';

exports.nowTimestamp = function nowTimestamp() {
return Date.now() / 1000;
};
class Grouper extends Map {

@@ -92,0 +96,0 @@ /**

{
"name": "prom-client",
"version": "14.2.0",
"version": "15.0.0-0",
"description": "Client for prometheus",

@@ -12,3 +12,3 @@ "main": "index.js",

"engines": {
"node": ">=10"
"node": ">=14"
},

@@ -38,15 +38,16 @@ "scripts": {

"@clevernature/benchmark-regression": "^1.0.0",
"eslint": "^7.7.0",
"eslint-config-prettier": "^6.10.0",
"eslint": "^8.32.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-prettier": "^4.2.1",
"express": "^4.13.3",
"husky": "^4.2.1",
"jest": "^26.0.1",
"lint-staged": "^10.0.4",
"husky": "^8.0.3",
"jest": "^29.3.1",
"lint-staged": "^13.1.0",
"nock": "^13.0.5",
"prettier": "2.7.1",
"prettier": "2.8.3",
"typescript": "^4.0.2"
},
"dependencies": {
"@opentelemetry/api": "^1.4.0",
"tdigest": "^0.1.1"

@@ -84,8 +85,3 @@ },

]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}

@@ -396,2 +396,43 @@ # Prometheus client for node.js [![Actions Status](https://github.com/siimon/prom-client/workflows/Node.js%20CI/badge.svg?branch=master)](https://github.com/siimon/prom-client/actions)

### Exemplars
The exemplars defined in the OpenMetrics specification can be enabled on Counter
and Histogram metric types. The default metrics have support for OpenTelemetry,
they will populate the exemplars with the labels `{traceId, spanId}` and their
corresponding values.
The format for `inc()` and `observe()` calls are different if exemplars are
enabled. They get a single object with the format
`{labels, value, exemplarLabels}`.
When using exemplars, the registry used for metrics should be set to OpenMetrics
type (including the global or default registry if no registries are specified).
### Registy type
The library supports both the old Prometheus format and the OpenMetrics format.
The format can be set per registry. For default metrics:
```js
const Prometheus = require('prom-client');
Prometheus.register.setContentType(
Prometheus.Registry.OPENMETRICS_CONTENT_TYPE,
);
```
Currently available registry types are defined by the content types:
**PROMETHEUS_CONTENT_TYPE** - version 0.0.4 of the original Prometheus metrics,
this is currently the default registry type.
**OPENMETRICS_CONTENT_TYPE** - defaults to version 1.0.0 of the
[OpenMetrics standard](https://github.com/OpenObservability/OpenMetrics/blob/d99b705f611b75fec8f450b05e344e02eea6921d/specification/OpenMetrics.md).
The HTTP Content-Type string for each registry type is exposed both at module
level (`prometheusContentType` and `openMetricsContentType`) and as static
properties on the `Registry` object.
The `contentType` constant exposed by the module returns the default content
type when creating a new registry, currently defaults to Prometheus type.
### Multiple registries

@@ -411,2 +452,5 @@

Merging registries of different types is undefined. The user needs to make sure
all used registries have the same type (Prometheus or OpenMetrics versions).
```js

@@ -455,3 +499,3 @@ const client = require('prom-client');

If you need to get a reference to a previously registered metric, you can use
`await register.getSingleMetric(*name of metric*)`.
`register.getSingleMetric(*name of metric*)`.

@@ -563,5 +607,2 @@ #### Removing metrics

The content-type prometheus expects is also exported as a constant, both on the
`register` and from the main file of this project, called `contentType`.
### Garbage Collection Metrics

@@ -568,0 +609,0 @@

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