/** * Flatten one helper's `stats()` into scalar series keys. * * Nested objects are joined with a dot rather than dropped, so `size` and a * hypothetical `pooled.used` coexist without a naming scheme. Arrays are * **omitted** rather than joined: `getStats().status` is an array of per-worker * objects whose length means worker count and whose contents are the real * detail. Turning that into a string key would put an arbitrary, unbounded * number of series into the map, and joining it into one would hide the detail * the caller came for. * * @param {string} helper - The helper name, used as the series prefix. * @param {*} stats - Whatever that helper's `stats()` returned. * @returns {Record} Flat scalar series. * @example * toSeries('cache', new PowerCache().stats()); * // => { 'cache.size': 0, 'cache.hitRate': 0, 'cache.pool.size': 16, ... } */ export function toSeries(helper: string, stats: any): Record; /** * Add explicit sampling metadata to a flat series. * * The collector remains pull-based; this envelope records the evidence a * controller needs before it changes a value. Missing fields are intentional: * an unavailable signal must not become a zero. * * @param {Record} series * @param {Object} [options] * @param {number} [options.observedAt=Date.now()] * @param {number} [options.samples=0] * @param {number} [options.windowMs=0] * @param {boolean} [options.fresh=true] * @param {number} [options.confidence=1] Evidence confidence in `[0, 1]`. * @returns {{version: number, observedAt: number, samples: number, windowMs: number, fresh: boolean, confidence: number, series: Record}} */ export function createObservation(series: Record, options?: { observedAt?: number | undefined; samples?: number | undefined; windowMs?: number | undefined; fresh?: boolean | undefined; confidence?: number | undefined; }): { version: number; observedAt: number; samples: number; windowMs: number; fresh: boolean; confidence: number; series: Record; }; /** * Calculate numeric changes between two observations. * * A missing or non-numeric pair is reported as `null`, rather than coerced to * zero. That keeps a controller from treating a newly-added or unavailable * signal as evidence of a drop. * * @param {{series: Record}} current * @param {{series: Record}} [previous] * @returns {Record} */ export function diffObservation(current: { series: Record; }, previous?: { series: Record; }): Record; export function formatPrometheus(descriptors: any): string; /** * Wire a helper's `stats()` into a collector, and hand back the receipt that * undoes it. * * The receipt is not optional bookkeeping. A collector holds a closure over the * instance, so a disposed pool that is never unregistered is sampled forever - * and after `terminate()` its `getStats()` still answers, so nothing fails * visibly while the series quietly reports a dead object. Passing the receipt * to {@link detach} on teardown is what makes the two halves agree. * * @param {Object} instance - The helper being registered. * @param {string} name - Series prefix. Use a discriminator when more than one * of the same helper is in one process, e.g. `cache.images`. * @param {Object} [options] - The helper's own options object. * @param {boolean|MetricsCollector} [options.observability] `true` for the * shared collector, or a collector to register with. Anything else — the * default `false`, a bad value — registers nothing and costs nothing. * @returns {{unregister: function(): boolean, name: string}|null} The receipt, * or `null` when the helper is not observable. The receipt carries a bound * `unregister` rather than the collector, which is what lets every helper's * `_metrics` field stay a plain object type in the published declarations — * a bare class name here would be emitted into nine `.d.ts` files with no * import to resolve it against. * @example * const cache = new PowerCache({ observability: true }); * defaultMetrics.snapshot().series; // { 'cache.size': 0, ... } */ export function attach(instance: Object, name: string, options?: { observability?: boolean | MetricsCollector | undefined; }): { unregister: () => boolean; name: string; } | null; /** * Undo an {@link attach}. Safe to call with `null`, so a helper can call it * from a teardown path that may never have attached. * * @param {{unregister: function(): boolean, name: string}|null} receipt * @returns {boolean} Whether a source was removed. */ export function detach(receipt: { unregister: () => boolean; name: string; } | null): boolean; export const METRICS_VERSION: 1; /** * Version of the optional observation envelope returned by * {@link createObservation}. * * @type {number} */ export const OBSERVATION_VERSION: number; /** * Collects point-in-time snapshots from one or more helpers. * * A collector does not own the helpers. It is handed their `stats()` — however * often you choose, and a *reference* rather than the instance, so a caller can * sample a pool every request and a cache every minute through the same * collector. Sampling is therefore the caller's decision, and the deliberate * design point is that **draining is explicit** rather than push-based: a * metrics sink that fires on every operation is a metrics sink that becomes a * performance problem, and a timer that does it for you is one you cannot turn * off. * * @example * const cache = new PowerCache(); * const metrics = new MetricsCollector(); * * metrics.register('cache', () => cache.stats()); * // ... later * const { version, series } = metrics.snapshot(); * series['cache.hitRate']; // stable key, whatever cache.stats() is shaped like */ export class MetricsCollector { /** * @param {Object} [options] * @param {string} [options.prefix=''] Prepended to every series key, so two * collectors in one process do not collide. */ constructor(options?: { prefix?: string | undefined; }); /** @type {Map *>} */ /** * Register a named source. The callback is called on each `snapshot()` and * should return that helper's `stats()`. * * Re-registering a name replaces the previous source rather than adding a * second series for it, so a caller that re-registers on reconfigure does not * silently double-count. * * @param {string} name - Series prefix for this source. * @param {() => *} read - Returns the source's current stats. * @returns {this} */ register(name: string, read: () => any): this; /** * Stop reporting a source. The key disappears from the next snapshot rather * than reporting its last known value, which would be a lie: a number frozen * at deregistration looks exactly like a number that stopped moving. * * @param {string} name * @returns {boolean} Whether a source was removed. */ unregister(name: string): boolean; /** * Take a point-in-time snapshot of every registered source. * * One source throwing does not lose the others. A metrics sink that goes * blank because one helper misbehaved is worse than one that reports * everything except the broken thing, so the failure is recorded under * `.error` and the rest is still collected. * * @returns {{version: number, collectedAt: number, sources: string[], series: Record, errors: Record}} */ snapshot(): { version: number; collectedAt: number; sources: string[]; series: Record; errors: Record; }; /** * Export the current snapshot in OTLP (OpenTelemetry Protocol) JSON shape. * * This is a *hook*, not a push exporter: it returns a serialisable object the * caller hands to their own transport. The collector stays pull-based (see the * class docblock) — nothing is sent unless the caller asks. * * @param {Object} [options] * @param {string} [options.serviceName='performance-helpers'] - Resource attribute. * @param {number} [options.timestampMs=Date.now()] - Observation timestamp. * @returns {object} An OTLP `ExportMetricsServiceRequest`-shaped object. */ toOTLP(options?: { serviceName?: string | undefined; timestampMs?: number | undefined; }): object; /** * The registered source names. * * @returns {string[]} */ names(): string[]; /** * Release all registered sources. Idempotent. * @returns {void} */ dispose(): void; /** @returns {void} */ [Symbol.dispose](): void; /** @returns {Promise} */ [Symbol.asyncDispose](): Promise; } /** @type {MetricsCollector} */ export const defaultMetrics: MetricsCollector; export type PrometheusBucket = { le: number; value: number; }; export type PrometheusHistogram = { buckets: PrometheusBucket[]; count: number; sum: number; }; export type PrometheusDescriptor = { name: string; type: "counter" | "gauge" | "histogram"; help: string; labels?: Record | undefined; value: number | PrometheusHistogram; };