🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@nxtedition/histogram

Package Overview
Dependencies
Maintainers
12
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nxtedition/histogram - npm Package Compare versions

Comparing version
1.0.3
to
1.0.4
+1
lib/index.d.ts.map
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,OAAO,EAAE,SAAS,MAAM,EAgBnC,CAAA;AAIF,eAAO,MAAM,UAAU,EAAE,MAOrB,CAAA;AAUJ,wBAAgB,eAAe,IAAI,YAAY,CAE9C;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAQxE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAK5E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,CAUpF;AAED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,YAAY,EACvB,WAAW,EAAE,MAAM,EAAE,GACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAmC/B;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAQtE;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG;IACpC,uFAAuF;IACvF,MAAM,IAAI,YAAY,GAAG,IAAI,CAAA;CAC9B,CAAA;AAiDD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,gBAAqB,EACrB,UAAgC,GACjC,GAAE,iBAAsB,GAAG,UAAU,CA4ErC"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAEpE,4EAA4E;AAC5E,yEAAyE;AACzE,0BAA0B;AAC1B,MAAM,CAAC,MAAM,OAAO,GAAsB,MAAM,CAAC,MAAM,CAAC;IACtD,CAAC;IACD,CAAC;IACD,CAAC;IACD,EAAE;IACF,EAAE;IACF,EAAE;IACF,GAAG;IACH,GAAG;IACH,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,KAAK;IACL,QAAQ;CACT,CAAC,CAAA;AAEF,8EAA8E;AAC9E,gFAAgF;AAChF,MAAM,CAAC,MAAM,UAAU,GAAW,CAAC,GAAG,EAAE;IACtC,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAA;QACnB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC,CAAC,EAAE,CAAA;AAEJ,SAAS,YAAY,CAAC,EAAU,EAAE,SAA4B;IAC5D,IAAI,SAAS,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QACxC,MAAM,IAAI,UAAU,CAClB,GAAG,EAAE,gCAAgC,SAAS,CAAC,MAAM,aAAa,OAAO,CAAC,MAAM,GAAG,CACpF,CAAA;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAuB,EAAE,KAAa;IAChE,YAAY,CAAC,aAAa,EAAE,SAAS,CAAC,CAAA;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,IAAI,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACxB,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;YACd,OAAM;QACR,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAuB,EAAE,MAAc;IACpE,YAAY,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAA;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,SAAS,CAAC,CAAC,CAAC,IAAI,MAAM,CAAA;IACxB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAoB,EAAE,MAAyB;IAC5E,YAAY,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAA;IACtC,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,IAAI,UAAU,CAClB,2CAA2C,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,MAAM,GAAG,CACrF,CAAA;IACH,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAA;IACxB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,SAAuB,EACvB,WAAqB;IAErB,YAAY,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAA;IAC7C,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,CAAA;IACvB,CAAC;IAED,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,0EAA0E;QAC1E,sEAAsE;QACtE,wEAAwE;QACxE,qCAAqC;QACrC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;QAC3C,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC;YAClB,MAAM,IAAI,UAAU,CAClB,iDAAiD,GAAG,gEAAgE,CACrH,CAAA;QACH,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,GAAG,CAAC,CAAA;QAC3B,IAAI,UAAU,GAAG,CAAC,CAAA;QAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,CAAA;YAC1B,IAAI,UAAU,IAAI,SAAS,EAAE,CAAC;gBAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;gBAC/D,MAAK;YACP,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAA4B;IACrD,YAAY,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACrC,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAqBD,6EAA6E;AAC7E,SAAS,YAAY,CAAC,IAAkB,EAAE,OAAe,EAAE,MAAc;IACvE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,IAAI,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAA;YACjB,OAAM;QACR,CAAC;IACH,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,0EAA0E;AAC1E,wEAAwE;AACxE,sEAAsE;AACtE,2DAA2D;AAC3D,SAAS,aAAa,CAAC,IAAkB,EAAE,OAAe,EAAE,OAAe,EAAE,MAAc;IACzF,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAChB,OAAM;IACR,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,GAAG,OAAO,CAAA;IAC/B,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,qEAAqE;QACrE,sEAAsE;QACtE,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;QACnC,OAAM;IACR,CAAC;IACD,IAAI,OAAO,GAAG,OAAO,CAAA;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;QAC5B,IAAI,SAAS,GAAG,OAAO,EAAE,CAAC;YACxB,SAAQ;QACV,CAAC;QACD,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;YACvB,OAAM;QACR,CAAC;QACD,MAAM,MAAM,GAAG,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAC9E,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,KAAK,CAAA;QAC3C,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,GAAG,MAAM,CAAA;QAC9B,CAAC;QACD,OAAO,GAAG,MAAM,CAAA;QAChB,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC3B,OAAM;QACR,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,gBAAgB,GAAG,EAAE,EACrB,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,MACX,EAAE;IACvB,IAAI,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,UAAU,CAAC,uDAAuD,gBAAgB,EAAE,CAAC,CAAA;IACjG,CAAC;IACD,IAAI,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,UAAU,CAAC,iDAAiD,UAAU,EAAE,CAAC,CAAA;IACrF,CAAC;IAED,MAAM,GAAG,GAAG,qBAAqB,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC,CAAA;IACnE,GAAG,CAAC,MAAM,EAAE,CAAA;IAEZ,MAAM,WAAW,GAAG,eAAe,EAAE,CAAA;IACrC,IAAI,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;IAElC,OAAO;QACL,MAAM;YACJ,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;YAC7B,MAAM,IAAI,GAAG,GAAG,GAAG,UAAU,CAAA;YAC7B,UAAU,GAAG,GAAG,CAAA;YAEhB,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC,CAAA;YAC9C,cAAc,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;YAExC,sEAAsE;YACtE,uEAAuE;YACvE,uEAAuE;YACvE,sEAAsE;YACtE,iEAAiE;YACjE,0CAA0C;YAC1C,EAAE;YACF,qEAAqE;YACrE,iEAAiE;YACjE,iEAAiE;YACjE,mEAAmE;YACnE,+CAA+C;YAC/C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAA;YACvB,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,MAAM,OAAO,GAAG,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;gBAC1E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACzB,+DAA+D;oBAC/D,iEAAiE;oBACjE,yCAAyC;oBACzC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAA;oBAC7B,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;wBAC3C,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;oBAC1C,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,CAAA;oBAC3B,IAAI,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;oBAClE,IAAI,OAAO,GAAG,CAAC,CAAA;oBACf,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,OAAO,EAAE,CAAC;wBACrC,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,GAAG,CAAC,GAAG,KAAK,CAAA;wBAC9C,OAAO,GAAG,GAAG,CAAA;wBACb,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;4BAChB,SAAQ;wBACV,CAAC;wBACD,MAAM,OAAO,GAAG,OAAO,GAAG,GAAG,CAAA;wBAC7B,aAAa,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;wBACxD,WAAW,GAAG,OAAO,CAAA;oBACvB,CAAC;gBACH,CAAC;YACH,CAAC;YAED,GAAG,CAAC,KAAK,EAAE,CAAA;YAEX,MAAM,IAAI,GAAG,kBAAkB,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;YAC/D,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;YACnC,IAAI,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;gBACzB,OAAO,IAAI,CAAA;YACb,CAAC;YACD,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAA;QAC7D,CAAC;QACD,CAAC,MAAM,CAAC,OAAO,CAAC;YACd,GAAG,CAAC,OAAO,EAAE,CAAA;QACf,CAAC;KACF,CAAA;AACH,CAAC"}
+1
-0

@@ -33,1 +33,2 @@ export declare const BUCKETS: readonly number[];

export declare function createLagSampler({ sampleIntervalMs, halfLifeMs, }?: LagSamplerOptions): LagSampler;
//# sourceMappingURL=index.d.ts.map
+189
-240

@@ -1,155 +0,113 @@

import { monitorEventLoopDelay, performance } from 'node:perf_hooks'
import { monitorEventLoopDelay, performance } from 'node:perf_hooks';
// Bucket upper bounds (milliseconds). Values above the highest finite bound
// land in a final `Infinity` bucket and are reported as `BUCKET_MAX` for
// percentile/max queries.
export const BUCKETS = Object.freeze([
1,
2,
5,
10,
20,
50,
100,
200,
500,
1000,
2000,
5000,
10000,
30000,
Infinity,
])
export const BUCKETS = Object.freeze([
1,
2,
5,
10,
20,
50,
100,
200,
500,
1000,
2000,
5000,
10000,
30000,
Infinity,
]);
// Cap used for percentile/max reporting when the top (Infinity) bucket fires.
// Derived from the highest finite bound so it stays in sync if BUCKETS changes.
export const BUCKET_MAX = (() => {
for (let i = BUCKETS.length - 1; i >= 0; i--) {
if (Number.isFinite(BUCKETS[i])) {
return BUCKETS[i]
export const BUCKET_MAX = (() => {
for (let i = BUCKETS.length - 1; i >= 0; i--) {
if (Number.isFinite(BUCKETS[i])) {
return BUCKETS[i];
}
}
}
return 0
})()
function assertLength(fn , histogram ) {
if (histogram.length !== BUCKETS.length) {
throw new RangeError(
`${fn}: length mismatch (histogram=${histogram.length}, buckets=${BUCKETS.length})`,
)
}
return 0;
})();
function assertLength(fn, histogram) {
if (histogram.length !== BUCKETS.length) {
throw new RangeError(`${fn}: length mismatch (histogram=${histogram.length}, buckets=${BUCKETS.length})`);
}
}
export function createHistogram() {
return new Float64Array(BUCKETS.length)
export function createHistogram() {
return new Float64Array(BUCKETS.length);
}
export function recordValue(histogram , value ) {
assertLength('recordValue', histogram)
for (let i = 0; i < BUCKETS.length; i++) {
if (value <= BUCKETS[i]) {
histogram[i]++
return
export function recordValue(histogram, value) {
assertLength('recordValue', histogram);
for (let i = 0; i < BUCKETS.length; i++) {
if (value <= BUCKETS[i]) {
histogram[i]++;
return;
}
}
}
}
export function decayHistogram(histogram , factor ) {
assertLength('decayHistogram', histogram)
for (let i = 0; i < histogram.length; i++) {
histogram[i] *= factor
}
export function decayHistogram(histogram, factor) {
assertLength('decayHistogram', histogram);
for (let i = 0; i < histogram.length; i++) {
histogram[i] *= factor;
}
}
export function mergeHistogram(target , source ) {
assertLength('mergeHistogram', target)
if (source.length !== target.length) {
throw new RangeError(
`mergeHistogram: length mismatch (target=${target.length}, source=${source.length})`,
)
}
for (let i = 0; i < target.length; i++) {
target[i] += source[i]
}
export function mergeHistogram(target, source) {
assertLength('mergeHistogram', target);
if (source.length !== target.length) {
throw new RangeError(`mergeHistogram: length mismatch (target=${target.length}, source=${source.length})`);
}
for (let i = 0; i < target.length; i++) {
target[i] += source[i];
}
}
export function computePercentiles(
histogram ,
percentiles ,
) {
assertLength('computePercentiles', histogram)
let total = 0
for (let i = 0; i < histogram.length; i++) {
total += histogram[i]
}
if (total === 0) {
return null
}
const result = {}
for (const p of percentiles) {
// Percentile keys like "p50" / "p95" / "p99". Fractional percentiles such
// as 0.995 become "p99.5". Two percentiles that round to the same key
// (e.g. 0.995 and 0.999 truncated to p99) would silently overwrite each
// other, so we reject that up front.
const key = `p${Math.round(p * 1000) / 10}`
if (key in result) {
throw new RangeError(
`computePercentiles: duplicate percentile key '${key}' — cannot distinguish percentiles that round to the same name`,
)
}
const threshold = total * p
let cumulative = 0
export function computePercentiles(histogram, percentiles) {
assertLength('computePercentiles', histogram);
let total = 0;
for (let i = 0; i < histogram.length; i++) {
cumulative += histogram[i]
if (cumulative >= threshold) {
result[key] = BUCKETS[i] === Infinity ? BUCKET_MAX : BUCKETS[i]
break
}
total += histogram[i];
}
}
return result
if (total === 0) {
return null;
}
const result = {};
for (const p of percentiles) {
// Percentile keys like "p50" / "p95" / "p99". Fractional percentiles such
// as 0.995 become "p99.5". Two percentiles that round to the same key
// (e.g. 0.995 and 0.999 truncated to p99) would silently overwrite each
// other, so we reject that up front.
const key = `p${Math.round(p * 1000) / 10}`;
if (key in result) {
throw new RangeError(`computePercentiles: duplicate percentile key '${key}' — cannot distinguish percentiles that round to the same name`);
}
const threshold = total * p;
let cumulative = 0;
for (let i = 0; i < histogram.length; i++) {
cumulative += histogram[i];
if (cumulative >= threshold) {
result[key] = BUCKETS[i] === Infinity ? BUCKET_MAX : BUCKETS[i];
break;
}
}
}
return result;
}
export function computeMax(histogram ) {
assertLength('computeMax', histogram)
for (let i = BUCKETS.length - 1; i >= 0; i--) {
if (histogram[i] > 0) {
return BUCKETS[i] === Infinity ? BUCKET_MAX : BUCKETS[i]
export function computeMax(histogram) {
assertLength('computeMax', histogram);
for (let i = BUCKETS.length - 1; i >= 0; i--) {
if (histogram[i] > 0) {
return BUCKETS[i] === Infinity ? BUCKET_MAX : BUCKETS[i];
}
}
}
return null
return null;
}
// Credit a single value into the first bucket whose upper bound contains it.
function creditBucket(hist , valueMs , weight ) {
for (let i = 0; i < BUCKETS.length; i++) {
if (valueMs <= BUCKETS[i]) {
hist[i] += weight
return
function creditBucket(hist, valueMs, weight) {
for (let i = 0; i < BUCKETS.length; i++) {
if (valueMs <= BUCKETS[i]) {
hist[i] += weight;
return;
}
}
}
}
// Distribute `weight` uniformly across the buckets that overlap the segment

@@ -160,34 +118,33 @@ // (lowerMs, upperMs]. Assuming a uniform distribution within a percentile

// (the previous approach) systematically inflates p95/p99.
function creditSegment(hist , lowerMs , upperMs , weight ) {
if (weight <= 0) {
return
}
const range = upperMs - lowerMs
if (!(range > 0) || !Number.isFinite(range)) {
// Degenerate segment (ELD reported the same percentile value on both
// ends, e.g. all samples equal) — put the whole weight in one bucket.
creditBucket(hist, upperMs, weight)
return
}
let startMs = lowerMs
for (let i = 0; i < BUCKETS.length; i++) {
const bucketTop = BUCKETS[i]
if (bucketTop < startMs) {
continue
function creditSegment(hist, lowerMs, upperMs, weight) {
if (weight <= 0) {
return;
}
if (startMs >= upperMs) {
return
const range = upperMs - lowerMs;
if (!(range > 0) || !Number.isFinite(range)) {
// Degenerate segment (ELD reported the same percentile value on both
// ends, e.g. all samples equal) — put the whole weight in one bucket.
creditBucket(hist, upperMs, weight);
return;
}
const segTop = bucketTop === Infinity ? upperMs : Math.min(bucketTop, upperMs)
const fraction = (segTop - startMs) / range
if (fraction > 0) {
hist[i] += fraction * weight
let startMs = lowerMs;
for (let i = 0; i < BUCKETS.length; i++) {
const bucketTop = BUCKETS[i];
if (bucketTop < startMs) {
continue;
}
if (startMs >= upperMs) {
return;
}
const segTop = bucketTop === Infinity ? upperMs : Math.min(bucketTop, upperMs);
const fraction = (segTop - startMs) / range;
if (fraction > 0) {
hist[i] += fraction * weight;
}
startMs = segTop;
if (bucketTop === Infinity) {
return;
}
}
startMs = segTop
if (bucketTop === Infinity) {
return
}
}
}
/**

@@ -200,81 +157,73 @@ * Event-loop lag sampler backed by `perf_hooks.monitorEventLoopDelay`. Each

*/
export function createLagSampler({
sampleIntervalMs = 10,
halfLifeMs = 12 * 60 * 60 * 1000,
} = {}) {
if (!(sampleIntervalMs > 0)) {
throw new RangeError(`createLagSampler: sampleIntervalMs must be > 0, got ${sampleIntervalMs}`)
}
if (!(halfLifeMs > 0)) {
throw new RangeError(`createLagSampler: halfLifeMs must be > 0, got ${halfLifeMs}`)
}
const eld = monitorEventLoopDelay({ resolution: sampleIntervalMs })
eld.enable()
const decayedHist = createHistogram()
let lastTickAt = performance.now()
return {
sample() {
const now = performance.now()
const dtMs = now - lastTickAt
lastTickAt = now
const decayFactor = 0.5 ** (dtMs / halfLifeMs)
decayHistogram(decayedHist, decayFactor)
// Reconstruct approximate bucket counts from ELD's percentile CDF and
// fold them into the decayed accumulator. For each adjacent percentile
// pair (p_{i-1}, v_{i-1}), (p_i, v_i), `(p_i - p_{i-1}) / 100 * count`
// samples fall in (v_{i-1}, v_i]; we distribute that weight uniformly
// across the buckets overlapping the segment. ELD timings are in
// nanoseconds; we bucket in milliseconds.
//
// NOTE: `eld.percentiles` and `eld.reset()` are not an atomic pair —
// a sample recorded by the native timer between the two calls is
// wiped without being counted. ELD exposes no snapshot-and-clear
// primitive, so this loss is unavoidable; at `sampleIntervalMs=10`
// and typical sample cadences it's negligible.
const count = eld.count
if (count > 0) {
const entries = [...eld.percentiles.entries()].sort((a, b) => a[0] - b[0])
if (entries.length === 0) {
// Defensive: ELD reports count > 0 but published no percentile
// points (would require a racey internal state). Credit the mean
// so we don't lose the samples entirely.
const meanMs = eld.mean / 1e6
if (Number.isFinite(meanMs) && meanMs >= 0) {
creditBucket(decayedHist, meanMs, count)
}
} else {
const minMs = eld.min / 1e6
let prevValueMs = Number.isFinite(minMs) && minMs >= 0 ? minMs : 0
let prevPct = 0
for (const [pct, valueNs] of entries) {
const weight = ((pct - prevPct) / 100) * count
prevPct = pct
if (weight <= 0) {
continue
export function createLagSampler({ sampleIntervalMs = 10, halfLifeMs = 12 * 60 * 60 * 1000, } = {}) {
if (!(sampleIntervalMs > 0)) {
throw new RangeError(`createLagSampler: sampleIntervalMs must be > 0, got ${sampleIntervalMs}`);
}
if (!(halfLifeMs > 0)) {
throw new RangeError(`createLagSampler: halfLifeMs must be > 0, got ${halfLifeMs}`);
}
const eld = monitorEventLoopDelay({ resolution: sampleIntervalMs });
eld.enable();
const decayedHist = createHistogram();
let lastTickAt = performance.now();
return {
sample() {
const now = performance.now();
const dtMs = now - lastTickAt;
lastTickAt = now;
const decayFactor = 0.5 ** (dtMs / halfLifeMs);
decayHistogram(decayedHist, decayFactor);
// Reconstruct approximate bucket counts from ELD's percentile CDF and
// fold them into the decayed accumulator. For each adjacent percentile
// pair (p_{i-1}, v_{i-1}), (p_i, v_i), `(p_i - p_{i-1}) / 100 * count`
// samples fall in (v_{i-1}, v_i]; we distribute that weight uniformly
// across the buckets overlapping the segment. ELD timings are in
// nanoseconds; we bucket in milliseconds.
//
// NOTE: `eld.percentiles` and `eld.reset()` are not an atomic pair —
// a sample recorded by the native timer between the two calls is
// wiped without being counted. ELD exposes no snapshot-and-clear
// primitive, so this loss is unavoidable; at `sampleIntervalMs=10`
// and typical sample cadences it's negligible.
const count = eld.count;
if (count > 0) {
const entries = [...eld.percentiles.entries()].sort((a, b) => a[0] - b[0]);
if (entries.length === 0) {
// Defensive: ELD reports count > 0 but published no percentile
// points (would require a racey internal state). Credit the mean
// so we don't lose the samples entirely.
const meanMs = eld.mean / 1e6;
if (Number.isFinite(meanMs) && meanMs >= 0) {
creditBucket(decayedHist, meanMs, count);
}
}
else {
const minMs = eld.min / 1e6;
let prevValueMs = Number.isFinite(minMs) && minMs >= 0 ? minMs : 0;
let prevPct = 0;
for (const [pct, valueNs] of entries) {
const weight = ((pct - prevPct) / 100) * count;
prevPct = pct;
if (weight <= 0) {
continue;
}
const valueMs = valueNs / 1e6;
creditSegment(decayedHist, prevValueMs, valueMs, weight);
prevValueMs = valueMs;
}
}
}
const valueMs = valueNs / 1e6
creditSegment(decayedHist, prevValueMs, valueMs, weight)
prevValueMs = valueMs
}
}
}
eld.reset()
const pcts = computePercentiles(decayedHist, [0.5, 0.95, 0.99])
const max = computeMax(decayedHist)
if (!pcts || max == null) {
return null
}
return { p50: pcts.p50, p95: pcts.p95, p99: pcts.p99, max }
},
[Symbol.dispose]() {
eld.disable()
},
}
eld.reset();
const pcts = computePercentiles(decayedHist, [0.5, 0.95, 0.99]);
const max = computeMax(decayedHist);
if (!pcts || max == null) {
return null;
}
return { p50: pcts.p50, p95: pcts.p95, p99: pcts.p99, max };
},
[Symbol.dispose]() {
eld.disable();
},
};
}
//# sourceMappingURL=index.js.map
{
"name": "@nxtedition/histogram",
"version": "1.0.3",
"version": "1.0.4",
"type": "module",

@@ -23,3 +23,3 @@ "main": "lib/index.js",

"scripts": {
"build": "rimraf lib && tsc -p tsconfig.build.json && amaroc ./src/index.ts && mv src/index.js lib/",
"build": "rimraf lib && tsc -p tsconfig.build.json",
"prepublishOnly": "yarn build",

@@ -33,3 +33,2 @@ "typecheck": "tsc --noEmit",

"@types/node": "^25.5.0",
"amaroc": "^1.0.1",
"oxlint-tsgolint": "^0.17.0",

@@ -36,0 +35,0 @@ "rimraf": "^6.1.3",