apm-optima 1.0.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -23
- package/dashboard-out/404/index.html +1 -1
- package/dashboard-out/404.html +1 -1
- package/dashboard-out/__next.__PAGE__.txt +4 -4
- package/dashboard-out/__next._full.txt +17 -17
- package/dashboard-out/__next._head.txt +4 -4
- package/dashboard-out/__next._index.txt +10 -10
- package/dashboard-out/__next._tree.txt +2 -2
- package/dashboard-out/_next/static/chunks/{2xjcsu4pgp14_.js → 062oipeclh9bq.js} +1 -1
- package/dashboard-out/_next/static/chunks/0ncbimuip0-o7.css +3 -0
- package/dashboard-out/_next/static/chunks/0xtgenoch3q77.js +1 -0
- package/dashboard-out/_next/static/chunks/1xln1-s87x5yp.js +27 -0
- package/dashboard-out/_not-found/__next._full.txt +15 -15
- package/dashboard-out/_not-found/__next._head.txt +4 -4
- package/dashboard-out/_not-found/__next._index.txt +10 -10
- package/dashboard-out/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dashboard-out/_not-found/__next._not-found.txt +3 -3
- package/dashboard-out/_not-found/__next._tree.txt +2 -2
- package/dashboard-out/_not-found/index.html +1 -1
- package/dashboard-out/_not-found/index.txt +15 -15
- package/dashboard-out/index.html +1 -1
- package/dashboard-out/index.txt +17 -17
- package/dist/adapters/session-export.handler.d.ts +4 -0
- package/dist/adapters/session-export.handler.js +29 -0
- package/dist/adapters/socket.options.d.ts +3 -0
- package/dist/adapters/socket.options.js +16 -0
- package/dist/config/config.manager.d.ts +80 -3
- package/dist/config/config.manager.js +269 -89
- package/dist/config/config.types.d.ts +89 -13
- package/dist/core/delivery/buckets-request.d.ts +2 -0
- package/dist/core/delivery/buckets-request.js +18 -0
- package/dist/core/delivery/index.d.ts +1 -0
- package/dist/core/delivery/index.js +1 -0
- package/dist/core/delivery/metrics.publisher.d.ts +23 -10
- package/dist/core/delivery/metrics.publisher.js +22 -22
- package/dist/core/delivery/websocket.events.d.ts +14 -3
- package/dist/core/delivery/websocket.events.js +13 -2
- package/dist/core/domain/correlation.types.d.ts +37 -0
- package/dist/core/domain/correlation.types.js +2 -0
- package/dist/core/domain/dashboard.types.d.ts +16 -69
- package/dist/core/domain/dashboard.types.js +29 -0
- package/dist/core/domain/endpoints.types.d.ts +3 -0
- package/dist/core/domain/event.types.d.ts +14 -0
- package/dist/core/domain/event.types.js +2 -0
- package/dist/core/domain/incident.types.d.ts +50 -0
- package/dist/core/domain/incident.types.js +2 -0
- package/dist/core/domain/index.d.ts +5 -0
- package/dist/core/domain/index.js +5 -0
- package/dist/core/domain/live-bucket.types.d.ts +86 -0
- package/dist/core/domain/live-bucket.types.js +2 -0
- package/dist/core/domain/metrics.types.d.ts +41 -11
- package/dist/core/domain/session.types.d.ts +93 -0
- package/dist/core/domain/session.types.js +2 -0
- package/dist/core/domain/telemetry.types.d.ts +1 -0
- package/dist/core/organizers/event.manager.d.ts +19 -0
- package/dist/core/organizers/event.manager.js +73 -0
- package/dist/core/organizers/index.d.ts +2 -0
- package/dist/core/organizers/index.js +18 -0
- package/dist/core/organizers/interval.manager.d.ts +18 -0
- package/dist/core/organizers/interval.manager.js +54 -0
- package/dist/core/storage/index.d.ts +1 -0
- package/dist/core/storage/index.js +1 -0
- package/dist/core/storage/local.repository.d.ts +12 -12
- package/dist/core/storage/local.repository.js +38 -38
- package/dist/core/storage/persistence/persistence-writer.d.ts +48 -0
- package/dist/core/storage/persistence/persistence-writer.js +191 -0
- package/dist/core/storage/persistence/segment-index.d.ts +27 -0
- package/dist/core/storage/persistence/segment-index.js +206 -0
- package/dist/core/storage/persistence/session-registry.d.ts +28 -0
- package/dist/core/storage/persistence/session-registry.js +127 -0
- package/dist/core/storage/persistence/session-summary.d.ts +3 -0
- package/dist/core/storage/persistence/session-summary.js +112 -0
- package/dist/core/storage/persistence.layer.d.ts +69 -0
- package/dist/core/storage/persistence.layer.js +404 -0
- package/dist/core/storage/runtime/anomaly-detector.d.ts +11 -0
- package/dist/core/storage/runtime/anomaly-detector.js +42 -0
- package/dist/core/storage/stores/analytics.store.d.ts +5 -5
- package/dist/core/storage/stores/analytics.store.js +39 -12
- package/dist/core/storage/stores/bucket-metric.d.ts +85 -0
- package/dist/core/storage/stores/bucket-metric.js +168 -0
- package/dist/core/storage/stores/bucket-view.d.ts +44 -0
- package/dist/core/storage/stores/bucket-view.js +63 -0
- package/dist/core/storage/stores/bucket.store.d.ts +39 -36
- package/dist/core/storage/stores/bucket.store.js +112 -60
- package/dist/core/storage/stores/dashboard.store.d.ts +11 -4
- package/dist/core/storage/stores/dashboard.store.js +179 -47
- package/dist/core/storage/stores/endpoint.store.d.ts +1 -0
- package/dist/core/storage/stores/endpoint.store.js +21 -5
- package/dist/core/storage/stores/index.d.ts +0 -7
- package/dist/core/storage/stores/index.js +0 -7
- package/dist/core/storage/stores/metrics.store.d.ts +1 -1
- package/dist/core/storage/stores/metrics.store.js +1 -0
- package/dist/core/storage/stores/runtime.store.d.ts +77 -0
- package/dist/core/storage/stores/runtime.store.js +222 -0
- package/dist/core/storage/utility/common.d.ts +1 -0
- package/dist/core/storage/utility/common.js +11 -0
- package/dist/core/storage/utility/file-buffer.d.ts +31 -0
- package/dist/core/storage/utility/file-buffer.js +238 -0
- package/dist/core/storage/utility/histogram.d.ts +1 -2
- package/dist/core/storage/utility/histogram.js +1 -2
- package/dist/core/storage/utility/index.d.ts +2 -0
- package/dist/core/storage/utility/index.js +2 -0
- package/dist/core/storage/utility/persistence-files.d.ts +3 -0
- package/dist/core/storage/utility/persistence-files.js +34 -0
- package/dist/core/storage/utility/persistence-writer.d.ts +39 -0
- package/dist/core/storage/utility/persistence-writer.js +183 -0
- package/dist/core/storage/utility/session-export.d.ts +3 -0
- package/dist/core/storage/utility/session-export.js +31 -0
- package/dist/core/storage/utility/session-registry.d.ts +21 -0
- package/dist/core/storage/utility/session-registry.js +91 -0
- package/dist/core/storage/utility/session-window.d.ts +8 -0
- package/dist/core/storage/utility/session-window.js +43 -0
- package/dist/core/telemetry/analytics.service.d.ts +18 -0
- package/dist/core/telemetry/analytics.service.js +100 -0
- package/dist/core/telemetry/collector.service.d.ts +17 -15
- package/dist/core/telemetry/collector.service.js +23 -80
- package/dist/core/telemetry/correlation.service.d.ts +57 -9
- package/dist/core/telemetry/correlation.service.js +246 -54
- package/dist/core/telemetry/dashboard-live.service.d.ts +10 -0
- package/dist/core/telemetry/dashboard-live.service.js +110 -0
- package/dist/core/telemetry/dashboard.service.d.ts +20 -0
- package/dist/core/telemetry/dashboard.service.js +140 -0
- package/dist/core/telemetry/incident.service.d.ts +71 -0
- package/dist/core/telemetry/incident.service.js +239 -0
- package/dist/core/telemetry/logger.d.ts +4 -0
- package/dist/core/telemetry/logger.js +5 -2
- package/dist/core/telemetry/runtime.service.d.ts +14 -0
- package/dist/core/telemetry/runtime.service.js +56 -0
- package/dist/core/telemetry/telemetry-query.service.d.ts +82 -0
- package/dist/core/telemetry/telemetry-query.service.js +231 -0
- package/dist/core/telemetry/telemetry.service.d.ts +10 -4
- package/dist/core/telemetry/telemetry.service.js +60 -10
- package/dist/core/telemetry/utility/correlation-analysis.d.ts +9 -0
- package/dist/core/telemetry/utility/correlation-analysis.js +143 -0
- package/dist/core/telemetry/utility/correlation-finding.d.ts +70 -0
- package/dist/core/telemetry/utility/correlation-finding.js +41 -0
- package/dist/core/telemetry/utility/correlation-input.d.ts +59 -0
- package/dist/core/telemetry/utility/correlation-input.js +139 -0
- package/dist/core/telemetry/utility/correlation-replay.d.ts +24 -0
- package/dist/core/telemetry/utility/correlation-replay.js +46 -0
- package/dist/core/utility/correlation.analysis.d.ts +9 -0
- package/dist/core/utility/correlation.analysis.js +144 -0
- package/dist/core/utility/statistics.d.ts +65 -0
- package/dist/core/utility/statistics.js +141 -0
- package/dist/express/index.d.ts +1 -1
- package/dist/express/index.js +7 -5
- package/dist/express/metrics-websocket.adapter.d.ts +8 -2
- package/dist/express/metrics-websocket.adapter.js +52 -17
- package/dist/express/metrics.adapter.d.ts +1 -0
- package/dist/express/metrics.adapter.js +8 -1
- package/dist/express/metrics.bootstrap.d.ts +6 -1
- package/dist/express/metrics.bootstrap.js +48 -22
- package/dist/express/metrics.dashboard.d.ts +2 -1
- package/dist/express/metrics.dashboard.js +4 -1
- package/dist/express/metrics.middleware.d.ts +2 -1
- package/dist/express/metrics.middleware.js +23 -20
- package/dist/nest/metrics-websocket.adapter.d.ts +19 -3
- package/dist/nest/metrics-websocket.adapter.js +101 -21
- package/dist/nest/metrics.adapter.d.ts +4 -1
- package/dist/nest/metrics.adapter.js +24 -1
- package/dist/nest/metrics.bootstrap.service.d.ts +7 -3
- package/dist/nest/metrics.bootstrap.service.js +56 -21
- package/dist/nest/metrics.interceptor.d.ts +3 -3
- package/dist/nest/metrics.interceptor.js +11 -8
- package/dist/nest/metrics.module.d.ts +3 -0
- package/dist/nest/metrics.module.js +27 -9
- package/dist/runtime/index.d.ts +32 -0
- package/dist/runtime/index.js +104 -0
- package/dist/simulation/simulation.d.ts +5 -16
- package/dist/simulation/simulation.js +5 -8
- package/package.json +2 -1
- package/dashboard-out/_next/static/chunks/20t9qbodfrw_z.js +0 -3
- package/dashboard-out/_next/static/chunks/23soo6qkun-yv.js +0 -1
- package/dashboard-out/_next/static/chunks/3xoopocxi2vi8.css +0 -3
- /package/dashboard-out/_next/static/{u8vU2V6fhASeGVCpWU3eW → 17egwqgk36_DSRF55LJZG}/_buildManifest.js +0 -0
- /package/dashboard-out/_next/static/{u8vU2V6fhASeGVCpWU3eW → 17egwqgk36_DSRF55LJZG}/_clientMiddlewareManifest.js +0 -0
- /package/dashboard-out/_next/static/{u8vU2V6fhASeGVCpWU3eW → 17egwqgk36_DSRF55LJZG}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//! MOVE THIS FILE TO SERVICES/UTILITY
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.analyzeCorrelation = analyzeCorrelation;
|
|
5
|
+
const statistics_1 = require("../utility/statistics");
|
|
6
|
+
const DEFAULT_STRONG_THRESHOLD = 0.7;
|
|
7
|
+
const DEFAULT_MAX_LAG = 5;
|
|
8
|
+
const MIN_POINTS = 5;
|
|
9
|
+
const VARIATION_TOLERANCE = 1e-9;
|
|
10
|
+
function keepFinitePairs(x, y) {
|
|
11
|
+
const xs = [];
|
|
12
|
+
const ys = [];
|
|
13
|
+
for (let i = 0; i < x.length; i++) {
|
|
14
|
+
if (Number.isFinite(x[i]) && Number.isFinite(y[i])) {
|
|
15
|
+
xs.push(x[i]);
|
|
16
|
+
ys.push(y[i]);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { x: xs, y: ys };
|
|
20
|
+
}
|
|
21
|
+
function isConstant(series) {
|
|
22
|
+
return series.every((value) => value === series[0]);
|
|
23
|
+
}
|
|
24
|
+
function hasNoVariation(series, reference) {
|
|
25
|
+
const scale = Math.max(1, reference.reduce((max, v) => Math.max(max, Math.abs(v)), 0));
|
|
26
|
+
return (0, statistics_1.standardDeviation)(series) <= VARIATION_TOLERANCE * scale;
|
|
27
|
+
}
|
|
28
|
+
function insufficient(sampleSize, requiredSampleSize, recommendation, partial = {}) {
|
|
29
|
+
return {
|
|
30
|
+
status: 'INSUFFICIENT_DATA',
|
|
31
|
+
direction: 'NONE',
|
|
32
|
+
pearsonR: 0,
|
|
33
|
+
spearmanR: 0,
|
|
34
|
+
rawPearsonR: 0,
|
|
35
|
+
differencePearsonR: 0,
|
|
36
|
+
determination: 0,
|
|
37
|
+
lag: 0,
|
|
38
|
+
laggedPearsonR: 0,
|
|
39
|
+
sampleSize,
|
|
40
|
+
effectiveSampleSize: 0,
|
|
41
|
+
requiredSampleSize,
|
|
42
|
+
autocorrelationX: 0,
|
|
43
|
+
autocorrelationY: 0,
|
|
44
|
+
trendDriven: false,
|
|
45
|
+
recommendation,
|
|
46
|
+
...partial
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function signOf(value) {
|
|
50
|
+
return value > 0 ? 'POSITIVE' : value < 0 ? 'NEGATIVE' : 'NONE';
|
|
51
|
+
}
|
|
52
|
+
function analyzeCorrelation(xInput, yInput, requiredN, options = {}) {
|
|
53
|
+
if (xInput.length !== yInput.length) {
|
|
54
|
+
throw new RangeError(`Series must have the same length (got ${xInput.length} and ${yInput.length})`);
|
|
55
|
+
}
|
|
56
|
+
const strong = options.strongThreshold ?? DEFAULT_STRONG_THRESHOLD;
|
|
57
|
+
const maxLag = options.maxLag ?? DEFAULT_MAX_LAG;
|
|
58
|
+
const xLabel = options.xLabel ?? 'X';
|
|
59
|
+
const yLabel = options.yLabel ?? 'Y';
|
|
60
|
+
const { x, y } = keepFinitePairs(xInput, yInput);
|
|
61
|
+
const n = x.length;
|
|
62
|
+
if (n < Math.max(requiredN, MIN_POINTS)) {
|
|
63
|
+
return insufficient(n, requiredN, `Insufficient data points. At least ${requiredN} valid pairs of ${xLabel} and ${yLabel} are required.`);
|
|
64
|
+
}
|
|
65
|
+
if (isConstant(x) || isConstant(y)) {
|
|
66
|
+
return insufficient(n, requiredN, `One of the series (${xLabel} or ${yLabel}) is constant. Correlation cannot be computed.`);
|
|
67
|
+
}
|
|
68
|
+
const dx = (0, statistics_1.linearDetrend)(x);
|
|
69
|
+
const dy = (0, statistics_1.linearDetrend)(y);
|
|
70
|
+
if (hasNoVariation(dx, x) || hasNoVariation(dy, y)) {
|
|
71
|
+
return insufficient(n, requiredN, `One of the series (${xLabel} or ${yLabel}) has no variation after detrending. Correlation cannot be computed.`);
|
|
72
|
+
}
|
|
73
|
+
const autocorrelationX = (0, statistics_1.autocorrelation)(dx, 1);
|
|
74
|
+
const autocorrelationY = (0, statistics_1.autocorrelation)(dy, 1);
|
|
75
|
+
const nEff = (0, statistics_1.effectiveSampleSize)(n, autocorrelationX, autocorrelationY);
|
|
76
|
+
if (nEff < requiredN) {
|
|
77
|
+
return insufficient(n, requiredN, `Effective sample size is too small. At least ${requiredN} effective pairs of ${xLabel} and ${yLabel} are required.`, {
|
|
78
|
+
effectiveSampleSize: nEff,
|
|
79
|
+
autocorrelationX,
|
|
80
|
+
autocorrelationY
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const pearsonR = (0, statistics_1.pearsonCorrelation)(dx, dy);
|
|
84
|
+
const spearmanR = (0, statistics_1.spearmanCorrelation)(dx, dy);
|
|
85
|
+
const rawPearsonR = (0, statistics_1.pearsonCorrelation)(x, y);
|
|
86
|
+
const differencePearsonR = (0, statistics_1.pearsonCorrelation)((0, statistics_1.difference)(x), (0, statistics_1.difference)(y));
|
|
87
|
+
const { determination } = (0, statistics_1.determinationAndAlienation)(pearsonR);
|
|
88
|
+
const lagged = (0, statistics_1.laggedCorrelation)(dx, dy, maxLag);
|
|
89
|
+
let status;
|
|
90
|
+
let direction;
|
|
91
|
+
if (Math.abs(pearsonR) >= strong) {
|
|
92
|
+
status = 'STRONG_LINEAR_ASSOCIATION';
|
|
93
|
+
direction = signOf(pearsonR);
|
|
94
|
+
}
|
|
95
|
+
else if (Math.abs(spearmanR) >= strong) {
|
|
96
|
+
status = 'STRONG_MONOTONIC_NONLINEAR_ASSOCIATION';
|
|
97
|
+
direction = signOf(spearmanR);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
status = 'NO_STRONG_ASSOCIATION';
|
|
101
|
+
direction = 'NONE';
|
|
102
|
+
}
|
|
103
|
+
const trendDriven = Math.abs(rawPearsonR) >= strong && Math.abs(pearsonR) < strong;
|
|
104
|
+
return {
|
|
105
|
+
status,
|
|
106
|
+
direction,
|
|
107
|
+
pearsonR,
|
|
108
|
+
spearmanR,
|
|
109
|
+
rawPearsonR,
|
|
110
|
+
differencePearsonR,
|
|
111
|
+
determination,
|
|
112
|
+
lag: lagged.lag,
|
|
113
|
+
laggedPearsonR: lagged.r,
|
|
114
|
+
sampleSize: n,
|
|
115
|
+
effectiveSampleSize: nEff,
|
|
116
|
+
requiredSampleSize: requiredN,
|
|
117
|
+
autocorrelationX,
|
|
118
|
+
autocorrelationY,
|
|
119
|
+
trendDriven,
|
|
120
|
+
recommendation: describe(status, direction, { pearsonR, spearmanR, rawPearsonR, lag: lagged.lag, laggedR: lagged.r, strong, trendDriven, xLabel, yLabel })
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function describe(status, direction, ctx) {
|
|
124
|
+
const sign = direction === 'NEGATIVE' ? 'negativna' : 'pozitivna';
|
|
125
|
+
const parts = [];
|
|
126
|
+
if (status === 'STRONG_LINEAR_ASSOCIATION') {
|
|
127
|
+
parts.push(`In the observed period a strong linear correlation (${sign}, r=${ctx.pearsonR.toFixed(2)}) was detected between ${ctx.xLabel} and ${ctx.yLabel}. Correlation does not prove causality.`);
|
|
128
|
+
}
|
|
129
|
+
else if (status === 'STRONG_MONOTONIC_NONLINEAR_ASSOCIATION') {
|
|
130
|
+
parts.push(`In the observed period a strong monotonic nonlinear correlation (${sign}, ρ=${ctx.spearmanR.toFixed(2)}, r=${ctx.pearsonR.toFixed(2)}) was detected between ${ctx.xLabel} and ${ctx.yLabel}. The relationship could be logarithmic, threshold-based, saturating, or due to deviations; correlation does not prove causality.`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
parts.push(`In the observed period no strong correlational relationship was detected between ${ctx.xLabel} and ${ctx.yLabel}.`);
|
|
134
|
+
}
|
|
135
|
+
if (ctx.trendDriven) {
|
|
136
|
+
parts.push(`The correlation is strong only on the raw series (r=${ctx.rawPearsonR.toFixed(2)}); it is possible that it is driven by a common trend.`);
|
|
137
|
+
}
|
|
138
|
+
if (ctx.lag !== 0 && Math.abs(ctx.laggedR) >= ctx.strong && status === 'NO_STRONG_ASSOCIATION') {
|
|
139
|
+
const leader = ctx.lag > 0 ? ctx.xLabel : ctx.yLabel;
|
|
140
|
+
const follower = ctx.lag > 0 ? ctx.yLabel : ctx.xLabel;
|
|
141
|
+
parts.push(`At a lag of ${Math.abs(ctx.lag)} samples (${leader} leads ${follower}), a strong correlation (r=${ctx.laggedR.toFixed(2)}) was observed.`);
|
|
142
|
+
}
|
|
143
|
+
return parts.join(' ');
|
|
144
|
+
}
|
|
@@ -48,6 +48,16 @@ export declare function median(data: number[]): number;
|
|
|
48
48
|
* @returns The required sample size to achieve the specified margin of error and confidence level.
|
|
49
49
|
*/
|
|
50
50
|
export declare function calculateSampleSizeProportion(marginOfError: number, confidenceLevel?: number): number;
|
|
51
|
+
/**
|
|
52
|
+
* Calculates the number of INDEPENDENT observations needed to detect a correlation of at least
|
|
53
|
+
* `minCorrelation` with a two-sided test at significance level `alpha` and statistical power `power`.
|
|
54
|
+
* @param minCorrelation The smallest |r| worth detecting, in the open interval (0, 1). The sign is ignored.
|
|
55
|
+
* @param alpha Type I error probability (default 0.05).
|
|
56
|
+
* @param power Probability of detecting a true correlation of `minCorrelation` (default 0.80).
|
|
57
|
+
* @returns The required number of independent observations.
|
|
58
|
+
* @throws RangeError if any argument is outside its valid range.
|
|
59
|
+
*/
|
|
60
|
+
export declare function calculateSampleSizeCorrelation(minCorrelation: number, alpha?: number, power?: number): number;
|
|
51
61
|
/**
|
|
52
62
|
* Calculates the Pearson correlation coefficient (r) between two datasets.
|
|
53
63
|
* The correlation coefficient measures the strength and direction of the linear relationship between two variables.
|
|
@@ -138,3 +148,58 @@ export declare function isDataAnomaly(value: number, data: number[], threshold?:
|
|
|
138
148
|
* @returns A boolean indicating whether the value is an anomaly (true) or not (false).
|
|
139
149
|
*/
|
|
140
150
|
export declare function isDataAnomalyHistogram(value: number, totalCount: number, totalSum: number, totalSumOfSquares: number, threshold?: number): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Removes a linear trend from a series: fits an ordinary least squares line against the sample index
|
|
153
|
+
* and returns the residuals. Two series that both drift over time correlate strongly even when they are
|
|
154
|
+
* unrelated ("spurious correlation"); detrending removes that shared drift.
|
|
155
|
+
* @param data The series to detrend.
|
|
156
|
+
* @returns The residuals (same length as `data`, zero mean).
|
|
157
|
+
*/
|
|
158
|
+
export declare function linearDetrend(data: number[]): number[];
|
|
159
|
+
/**
|
|
160
|
+
* Calculates the first differences of a series: d[i] = data[i + 1] - data[i].
|
|
161
|
+
* Correlating differences measures whether the CHANGES of two series move together,
|
|
162
|
+
* which is robust against shared trends and level shifts.
|
|
163
|
+
* @param data The series to difference.
|
|
164
|
+
* @returns An array of length `data.length - 1` (empty for fewer than 2 samples).
|
|
165
|
+
*/
|
|
166
|
+
export declare function difference(data: number[]): number[];
|
|
167
|
+
/**
|
|
168
|
+
* Calculates the sample autocorrelation of a series at the given lag
|
|
169
|
+
* (the correlation of the series with a copy of itself shifted by `lag` samples).
|
|
170
|
+
* @param data The series.
|
|
171
|
+
* @param lag The shift in samples (default 1).
|
|
172
|
+
* @returns The autocorrelation in [-1, 1]; 0 for constant or too short series.
|
|
173
|
+
*/
|
|
174
|
+
export declare function autocorrelation(data: number[], lag?: number): number;
|
|
175
|
+
/**
|
|
176
|
+
* Estimates the effective number of independent observations in a pair of autocorrelated series
|
|
177
|
+
* (Bartlett's AR(1) approximation): n_eff = n * (1 - rhoX * rhoY) / (1 + rhoX * rhoY).
|
|
178
|
+
*
|
|
179
|
+
* 100 samples taken one second apart are usually NOT 100 independent observations. When both series
|
|
180
|
+
* are positively autocorrelated, n_eff can be several times smaller than n. When the product of the
|
|
181
|
+
* autocorrelations is not positive the samples are treated as independent (n_eff = n, never larger).
|
|
182
|
+
* @param n The raw number of samples.
|
|
183
|
+
* @param autocorrX Lag-1 autocorrelation of the first series.
|
|
184
|
+
* @param autocorrY Lag-1 autocorrelation of the second series.
|
|
185
|
+
* @returns The effective sample size, in [min(n, 2), n].
|
|
186
|
+
*/
|
|
187
|
+
export declare function effectiveSampleSize(n: number, autocorrX: number, autorcorrY: number): number;
|
|
188
|
+
/**
|
|
189
|
+
* Finds the shift at which two series are most strongly (Pearson) correlated.
|
|
190
|
+
*
|
|
191
|
+
* Convention: a POSITIVE lag k pairs x[t] with y[t + k], i.e. x leads y by k samples;
|
|
192
|
+
* a NEGATIVE lag pairs x[t + k] with y[t], i.e. y leads x. At least 3 overlapping samples are kept.
|
|
193
|
+
* Ties prefer the smaller |lag| (lag 0 first).
|
|
194
|
+
*
|
|
195
|
+
* Because 2 * maxLag + 1 candidates are compared, the winning |r| is biased upwards -
|
|
196
|
+
* treat it as descriptive, not as a significance test.
|
|
197
|
+
* @param x The first series.
|
|
198
|
+
* @param y The second series (same length as x).
|
|
199
|
+
* @param maxLag The largest shift, in samples, to search in each direction.
|
|
200
|
+
* @returns The lag with the largest |r| and the correlation at that lag.
|
|
201
|
+
*/
|
|
202
|
+
export declare function laggedCorrelation(x: number[], y: number[], maxLag: number): {
|
|
203
|
+
lag: number;
|
|
204
|
+
r: number;
|
|
205
|
+
};
|
|
@@ -7,6 +7,7 @@ exports.percentile = percentile;
|
|
|
7
7
|
exports.percentileHistogram = percentileHistogram;
|
|
8
8
|
exports.median = median;
|
|
9
9
|
exports.calculateSampleSizeProportion = calculateSampleSizeProportion;
|
|
10
|
+
exports.calculateSampleSizeCorrelation = calculateSampleSizeCorrelation;
|
|
10
11
|
exports.pearsonCorrelation = pearsonCorrelation;
|
|
11
12
|
exports.spearmanCorrelation = spearmanCorrelation;
|
|
12
13
|
exports.determinationAndAlienation = determinationAndAlienation;
|
|
@@ -16,6 +17,11 @@ exports.zScore = zScore;
|
|
|
16
17
|
exports.zScoreHistogram = zScoreHistogram;
|
|
17
18
|
exports.isDataAnomaly = isDataAnomaly;
|
|
18
19
|
exports.isDataAnomalyHistogram = isDataAnomalyHistogram;
|
|
20
|
+
exports.linearDetrend = linearDetrend;
|
|
21
|
+
exports.difference = difference;
|
|
22
|
+
exports.autocorrelation = autocorrelation;
|
|
23
|
+
exports.effectiveSampleSize = effectiveSampleSize;
|
|
24
|
+
exports.laggedCorrelation = laggedCorrelation;
|
|
19
25
|
/**
|
|
20
26
|
* Generates a hash code for a given value. This function converts the value to a JSON string and computes a hash code based on its characters.
|
|
21
27
|
* @param value The value to be hashed. It can be of any type that can be serialized to JSON.
|
|
@@ -159,6 +165,27 @@ function calculateSampleSizeProportion(marginOfError, confidenceLevel = 0.95) {
|
|
|
159
165
|
const n = (Math.pow(z, 2) * p * (1 - p)) / Math.pow(marginOfError, 2);
|
|
160
166
|
return Math.ceil(n);
|
|
161
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Calculates the number of INDEPENDENT observations needed to detect a correlation of at least
|
|
170
|
+
* `minCorrelation` with a two-sided test at significance level `alpha` and statistical power `power`.
|
|
171
|
+
* @param minCorrelation The smallest |r| worth detecting, in the open interval (0, 1). The sign is ignored.
|
|
172
|
+
* @param alpha Type I error probability (default 0.05).
|
|
173
|
+
* @param power Probability of detecting a true correlation of `minCorrelation` (default 0.80).
|
|
174
|
+
* @returns The required number of independent observations.
|
|
175
|
+
* @throws RangeError if any argument is outside its valid range.
|
|
176
|
+
*/
|
|
177
|
+
function calculateSampleSizeCorrelation(minCorrelation, alpha = 0.05, power = 0.8) {
|
|
178
|
+
const r = Math.abs(minCorrelation);
|
|
179
|
+
if (!(r > 0 && r < 1))
|
|
180
|
+
throw new RangeError('minCorrelation must be in the open interval (0, 1)');
|
|
181
|
+
if (!(alpha > 0 && alpha < 1))
|
|
182
|
+
throw new RangeError('alpha must be in the open interval (0, 1)');
|
|
183
|
+
if (!(power > 0 && power < 1))
|
|
184
|
+
throw new RangeError('power must be in the open interval (0, 1)');
|
|
185
|
+
const fisherZ = 0.5 * Math.log((1 + r) / (1 - r));
|
|
186
|
+
const zSum = normsinv(1 - alpha / 2) + normsinv(power);
|
|
187
|
+
return Math.ceil((zSum / fisherZ) ** 2 + 3);
|
|
188
|
+
}
|
|
162
189
|
/**
|
|
163
190
|
* Calculates the Pearson correlation coefficient (r) between two datasets.
|
|
164
191
|
* The correlation coefficient measures the strength and direction of the linear relationship between two variables.
|
|
@@ -347,3 +374,117 @@ function isDataAnomalyHistogram(value, totalCount, totalSum, totalSumOfSquares,
|
|
|
347
374
|
const z = zScoreHistogram(value, totalCount, totalSum, totalSumOfSquares);
|
|
348
375
|
return Math.abs(z) > threshold;
|
|
349
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Removes a linear trend from a series: fits an ordinary least squares line against the sample index
|
|
379
|
+
* and returns the residuals. Two series that both drift over time correlate strongly even when they are
|
|
380
|
+
* unrelated ("spurious correlation"); detrending removes that shared drift.
|
|
381
|
+
* @param data The series to detrend.
|
|
382
|
+
* @returns The residuals (same length as `data`, zero mean).
|
|
383
|
+
*/
|
|
384
|
+
function linearDetrend(data) {
|
|
385
|
+
const n = data.length;
|
|
386
|
+
if (n < 2)
|
|
387
|
+
return data.map(() => 0);
|
|
388
|
+
const meanT = (n - 1) / 2;
|
|
389
|
+
const meanY = mean(data);
|
|
390
|
+
let covariance = 0;
|
|
391
|
+
let varianceT = 0;
|
|
392
|
+
for (let i = 0; i < n; i++) {
|
|
393
|
+
const dt = i - meanT;
|
|
394
|
+
covariance += dt * (data[i] - meanY);
|
|
395
|
+
varianceT += dt * dt;
|
|
396
|
+
}
|
|
397
|
+
const slope = varianceT === 0 ? 0 : covariance / varianceT;
|
|
398
|
+
return data.map((value, i) => value - (meanY + slope * (i - meanT)));
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Calculates the first differences of a series: d[i] = data[i + 1] - data[i].
|
|
402
|
+
* Correlating differences measures whether the CHANGES of two series move together,
|
|
403
|
+
* which is robust against shared trends and level shifts.
|
|
404
|
+
* @param data The series to difference.
|
|
405
|
+
* @returns An array of length `data.length - 1` (empty for fewer than 2 samples).
|
|
406
|
+
*/
|
|
407
|
+
function difference(data) {
|
|
408
|
+
const result = [];
|
|
409
|
+
for (let i = 1; i < data.length; i++) {
|
|
410
|
+
result.push(data[i] - data[i - 1]);
|
|
411
|
+
}
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Calculates the sample autocorrelation of a series at the given lag
|
|
416
|
+
* (the correlation of the series with a copy of itself shifted by `lag` samples).
|
|
417
|
+
* @param data The series.
|
|
418
|
+
* @param lag The shift in samples (default 1).
|
|
419
|
+
* @returns The autocorrelation in [-1, 1]; 0 for constant or too short series.
|
|
420
|
+
*/
|
|
421
|
+
function autocorrelation(data, lag = 1) {
|
|
422
|
+
const n = data.length;
|
|
423
|
+
if (lag < 0 || n <= lag + 1)
|
|
424
|
+
return 0;
|
|
425
|
+
const mu = mean(data);
|
|
426
|
+
let denominator = 0;
|
|
427
|
+
for (let i = 0; i < n; i++) {
|
|
428
|
+
denominator += (data[i] - mu) ** 2;
|
|
429
|
+
}
|
|
430
|
+
if (denominator === 0)
|
|
431
|
+
return 0;
|
|
432
|
+
let numerator = 0;
|
|
433
|
+
for (let i = 0; i < n - lag; i++) {
|
|
434
|
+
numerator += (data[i] - mu) * (data[i + lag] - mu);
|
|
435
|
+
}
|
|
436
|
+
return numerator / denominator;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Estimates the effective number of independent observations in a pair of autocorrelated series
|
|
440
|
+
* (Bartlett's AR(1) approximation): n_eff = n * (1 - rhoX * rhoY) / (1 + rhoX * rhoY).
|
|
441
|
+
*
|
|
442
|
+
* 100 samples taken one second apart are usually NOT 100 independent observations. When both series
|
|
443
|
+
* are positively autocorrelated, n_eff can be several times smaller than n. When the product of the
|
|
444
|
+
* autocorrelations is not positive the samples are treated as independent (n_eff = n, never larger).
|
|
445
|
+
* @param n The raw number of samples.
|
|
446
|
+
* @param autocorrX Lag-1 autocorrelation of the first series.
|
|
447
|
+
* @param autocorrY Lag-1 autocorrelation of the second series.
|
|
448
|
+
* @returns The effective sample size, in [min(n, 2), n].
|
|
449
|
+
*/
|
|
450
|
+
function effectiveSampleSize(n, autocorrX, autorcorrY) {
|
|
451
|
+
if (n <= 0)
|
|
452
|
+
return 0;
|
|
453
|
+
const product = autocorrX * autorcorrY;
|
|
454
|
+
if (product <= 0)
|
|
455
|
+
return n;
|
|
456
|
+
const nEff = (n * (1 - product)) / (1 + product);
|
|
457
|
+
return Math.min(n, Math.max(2, nEff));
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Finds the shift at which two series are most strongly (Pearson) correlated.
|
|
461
|
+
*
|
|
462
|
+
* Convention: a POSITIVE lag k pairs x[t] with y[t + k], i.e. x leads y by k samples;
|
|
463
|
+
* a NEGATIVE lag pairs x[t + k] with y[t], i.e. y leads x. At least 3 overlapping samples are kept.
|
|
464
|
+
* Ties prefer the smaller |lag| (lag 0 first).
|
|
465
|
+
*
|
|
466
|
+
* Because 2 * maxLag + 1 candidates are compared, the winning |r| is biased upwards -
|
|
467
|
+
* treat it as descriptive, not as a significance test.
|
|
468
|
+
* @param x The first series.
|
|
469
|
+
* @param y The second series (same length as x).
|
|
470
|
+
* @param maxLag The largest shift, in samples, to search in each direction.
|
|
471
|
+
* @returns The lag with the largest |r| and the correlation at that lag.
|
|
472
|
+
*/
|
|
473
|
+
function laggedCorrelation(x, y, maxLag) {
|
|
474
|
+
if (x.length !== y.length || x.length === 0)
|
|
475
|
+
return { lag: 0, r: 0 };
|
|
476
|
+
const n = x.length;
|
|
477
|
+
let best = { lag: 0, r: pearsonCorrelation(x, y) };
|
|
478
|
+
const limit = Math.min(Math.floor(maxLag), n - 3);
|
|
479
|
+
for (let k = 1; k <= limit; k++) {
|
|
480
|
+
for (const lag of [k, -k]) {
|
|
481
|
+
const xs = lag > 0 ? x.slice(0, n - lag) : x.slice(-lag);
|
|
482
|
+
const ys = lag > 0 ? y.slice(lag) : y.slice(0, n + lag);
|
|
483
|
+
const r = pearsonCorrelation(xs, ys);
|
|
484
|
+
if (Math.abs(r) > Math.abs(best.r) + 1e-12) {
|
|
485
|
+
best = { lag, r };
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return best;
|
|
490
|
+
}
|
package/dist/express/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ import express from 'express';
|
|
|
2
2
|
import { Server as HTTPServer } from 'http';
|
|
3
3
|
import { type ConfigOptions } from '../config';
|
|
4
4
|
export declare function setupOptima(app: express.Express, options?: ConfigOptions): {
|
|
5
|
-
attachServer: (server: HTTPServer) => () => void
|
|
5
|
+
attachServer: (server: HTTPServer) => (options?: import("./metrics.bootstrap").ExpressStopOptions) => Promise<void>;
|
|
6
6
|
};
|
package/dist/express/index.js
CHANGED
|
@@ -9,17 +9,19 @@ const metrics_middleware_1 = require("./metrics.middleware");
|
|
|
9
9
|
const metrics_dashboard_1 = require("./metrics.dashboard");
|
|
10
10
|
const metrics_bootstrap_1 = require("./metrics.bootstrap");
|
|
11
11
|
const logger_1 = __importDefault(require("../core/telemetry/logger"));
|
|
12
|
+
const runtime_1 = require("../runtime");
|
|
12
13
|
function setupOptima(app, options) {
|
|
13
14
|
logger_1.default.debug('Initializing Optima with provided configuration options...');
|
|
14
15
|
config_1.ConfigManager.getInstance().initialize(options);
|
|
15
16
|
const config = config_1.ConfigManager.getInstance().get();
|
|
17
|
+
const dependencies = (0, runtime_1.createOptimaRuntime)(config);
|
|
16
18
|
// Registering the metrics middleware
|
|
17
|
-
app.use(metrics_middleware_1.
|
|
19
|
+
app.use((0, metrics_middleware_1.createExpressMetricsMiddleware)(dependencies));
|
|
18
20
|
logger_1.default.debug('Middlware for metrics collection has been registered successfully.');
|
|
19
21
|
// Attaching the dashboard on provided path
|
|
20
|
-
if (config.
|
|
21
|
-
(0, metrics_dashboard_1.attachDashboard)(app, config.
|
|
22
|
-
logger_1.default.debug(`Dashboard has been attached at path: ${config.
|
|
22
|
+
if (config.dashboard !== false) {
|
|
23
|
+
(0, metrics_dashboard_1.attachDashboard)(app, config.dashboard.path, dependencies.persistence);
|
|
24
|
+
logger_1.default.debug(`Dashboard has been attached at path: ${config.dashboard.path}`);
|
|
23
25
|
}
|
|
24
26
|
else {
|
|
25
27
|
logger_1.default.debug('Dashboard attachment skipped as per configuration.');
|
|
@@ -27,7 +29,7 @@ function setupOptima(app, options) {
|
|
|
27
29
|
return {
|
|
28
30
|
attachServer: (server) => {
|
|
29
31
|
logger_1.default.debug('Attaching server for metrics collection...');
|
|
30
|
-
return (0, metrics_bootstrap_1.expressMetricsBootstrap)(server);
|
|
32
|
+
return (0, metrics_bootstrap_1.expressMetricsBootstrap)(server, dependencies);
|
|
31
33
|
}
|
|
32
34
|
};
|
|
33
35
|
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import type { Server as HTTPServer } from 'http';
|
|
2
2
|
import type { WebSocketAdapter } from '../adapters/websocket.adapter';
|
|
3
|
-
import { type MetricsDataProvider } from '../core/delivery';
|
|
3
|
+
import { type CorrelationDataProvider, type IncidentDataProvider, type MetricsDataProvider, type SessionDataProvider } from '../core/delivery';
|
|
4
|
+
import { type ReadonlyConfig } from '../config';
|
|
4
5
|
export declare class ExpressWebSocketAdapter implements WebSocketAdapter {
|
|
5
6
|
private readonly server;
|
|
6
7
|
private readonly provider;
|
|
8
|
+
private readonly sessionProvider;
|
|
9
|
+
private readonly correlationProvider;
|
|
10
|
+
private readonly incidentProvider;
|
|
11
|
+
private readonly config;
|
|
7
12
|
private io?;
|
|
8
|
-
|
|
13
|
+
private isClosed;
|
|
14
|
+
constructor(server: HTTPServer, provider: MetricsDataProvider, sessionProvider: SessionDataProvider, correlationProvider: CorrelationDataProvider, incidentProvider: IncidentDataProvider, config: ReadonlyConfig);
|
|
9
15
|
init(): void;
|
|
10
16
|
private setupEvents;
|
|
11
17
|
private setupClient;
|
|
@@ -7,30 +7,32 @@ exports.ExpressWebSocketAdapter = void 0;
|
|
|
7
7
|
const socket_io_1 = require("socket.io");
|
|
8
8
|
const delivery_1 = require("../core/delivery");
|
|
9
9
|
const config_1 = require("../config");
|
|
10
|
+
const socket_options_1 = require("../adapters/socket.options");
|
|
10
11
|
const logger_1 = __importDefault(require("../core/telemetry/logger"));
|
|
11
12
|
class ExpressWebSocketAdapter {
|
|
12
13
|
server;
|
|
13
14
|
provider;
|
|
15
|
+
sessionProvider;
|
|
16
|
+
correlationProvider;
|
|
17
|
+
incidentProvider;
|
|
18
|
+
config;
|
|
14
19
|
io;
|
|
15
|
-
|
|
20
|
+
isClosed = false;
|
|
21
|
+
constructor(server, provider, sessionProvider, correlationProvider, incidentProvider, config) {
|
|
16
22
|
this.server = server;
|
|
17
23
|
this.provider = provider;
|
|
24
|
+
this.sessionProvider = sessionProvider;
|
|
25
|
+
this.correlationProvider = correlationProvider;
|
|
26
|
+
this.incidentProvider = incidentProvider;
|
|
27
|
+
this.config = config;
|
|
18
28
|
}
|
|
19
29
|
init() {
|
|
20
30
|
if (this.io) {
|
|
21
31
|
logger_1.default.debug('WebSocket server is already initialized.');
|
|
22
32
|
return;
|
|
23
33
|
}
|
|
24
|
-
this.io = new socket_io_1.Server(this.server,
|
|
25
|
-
|
|
26
|
-
maxHttpBufferSize: 1e8, // 100MB
|
|
27
|
-
pingTimeout: 10000, // 10 seconds
|
|
28
|
-
pingInterval: 25000, // 25 seconds
|
|
29
|
-
cors: {
|
|
30
|
-
origin: '*',
|
|
31
|
-
methods: ['GET', 'POST'],
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
+
this.io = new socket_io_1.Server(this.server, (0, socket_options_1.socketServerOptions)(this.config.transport));
|
|
35
|
+
this.isClosed = false;
|
|
34
36
|
logger_1.default.debug('WebSocket server initialized successfully.');
|
|
35
37
|
this.setupEvents();
|
|
36
38
|
}
|
|
@@ -44,29 +46,62 @@ class ExpressWebSocketAdapter {
|
|
|
44
46
|
});
|
|
45
47
|
}
|
|
46
48
|
setupClient(socket) {
|
|
49
|
+
socket.on(delivery_1.WebSocketEvents.REQUEST_SESSION_METADATA, async () => {
|
|
50
|
+
socket.emit(delivery_1.WebSocketEvents.RESPONSE_SESSION_METADATA, await this.sessionProvider.getSessionManifest());
|
|
51
|
+
});
|
|
52
|
+
socket.on(delivery_1.WebSocketEvents.REQUEST_SESSION_SUMMARY, async ({ sessionNumber }) => {
|
|
53
|
+
socket.emit(delivery_1.WebSocketEvents.RESPONSE_SESSION_SUMMARY, await this.sessionProvider.getSessionSummary(sessionNumber));
|
|
54
|
+
});
|
|
47
55
|
socket.on(delivery_1.WebSocketEvents.REQUEST_CONFIGURATION, () => {
|
|
48
|
-
socket.emit(delivery_1.WebSocketEvents.RESPONSE_CONFIGURATION, config_1.
|
|
56
|
+
socket.emit(delivery_1.WebSocketEvents.RESPONSE_CONFIGURATION, (0, config_1.toClientConfig)(this.config));
|
|
49
57
|
});
|
|
50
58
|
socket.on(delivery_1.WebSocketEvents.REQUEST_SYSTEM_DATA, () => {
|
|
51
59
|
socket.emit(delivery_1.WebSocketEvents.RESPONSE_SYSTEM_DATA, this.provider.getSystemStaticInfo());
|
|
52
60
|
});
|
|
53
|
-
socket.on(delivery_1.WebSocketEvents.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
socket.on(delivery_1.WebSocketEvents.REQUEST_ANALYTICS_DATA, (filters) => {
|
|
62
|
+
if (filters === null) {
|
|
63
|
+
socket.emit(delivery_1.WebSocketEvents.RESPONSE_ANALYTICS_DATA, this.provider.getAnalyticsData());
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
socket.emit(delivery_1.WebSocketEvents.RESPONSE_FILTERED_ANALYTICS_DATA, this.provider.getAnalyticsData(filters));
|
|
67
|
+
}
|
|
58
68
|
});
|
|
59
69
|
socket.on(delivery_1.WebSocketEvents.REQUEST_HEALTH_DATA, () => {
|
|
60
70
|
socket.emit(delivery_1.WebSocketEvents.RESPONSE_HEALTH_DATA, this.provider.getHealthData());
|
|
61
71
|
});
|
|
72
|
+
socket.on(delivery_1.WebSocketEvents.REQUEST_CORRELATION_DATA, () => {
|
|
73
|
+
socket.emit(delivery_1.WebSocketEvents.RESPONSE_CORRELATION_DATA, this.correlationProvider.pack());
|
|
74
|
+
});
|
|
75
|
+
socket.on(delivery_1.WebSocketEvents.REQUEST_INCIDENTS, () => {
|
|
76
|
+
socket.emit(delivery_1.WebSocketEvents.RESPONSE_INCIDENTS, this.incidentProvider.snapshot());
|
|
77
|
+
});
|
|
78
|
+
socket.on(delivery_1.WebSocketEvents.REQUEST_DASHBOARD_BUCKETS, async (raw) => {
|
|
79
|
+
const req = (0, delivery_1.parseBucketsRequest)(raw);
|
|
80
|
+
if (!req)
|
|
81
|
+
return;
|
|
82
|
+
try {
|
|
83
|
+
socket.emit(delivery_1.WebSocketEvents.RESPONSE_DASHBOARD_BUCKETS, await this.provider.getBackFill(req));
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
logger_1.default.error('Dashboard backfill failed:', err);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
socket.on(delivery_1.WebSocketEvents.REQUEST_CORRELATION_REPLAY, async ({ findingId }) => {
|
|
90
|
+
socket.emit(delivery_1.WebSocketEvents.RESPONSE_CORRELATION_REPLAY, { findingId, outcome: await this.correlationProvider.replay(String(findingId)) });
|
|
91
|
+
});
|
|
62
92
|
socket.on('disconnect', () => {
|
|
63
93
|
logger_1.default.debug(`WebSocket disconnected: ${socket.id}`);
|
|
64
94
|
});
|
|
95
|
+
void this.sessionProvider.getSessionManifest().then(manifest => socket.emit(delivery_1.WebSocketEvents.RESPONSE_SESSION_METADATA, manifest));
|
|
65
96
|
}
|
|
66
97
|
broadcast(event, data) {
|
|
67
98
|
this.io?.emit(event, data);
|
|
68
99
|
}
|
|
69
100
|
disconnect() {
|
|
101
|
+
if (this.isClosed)
|
|
102
|
+
return;
|
|
103
|
+
this.isClosed = true;
|
|
104
|
+
this.io?.disconnectSockets(true);
|
|
70
105
|
this.io?.close();
|
|
71
106
|
this.io = undefined;
|
|
72
107
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Request, Response } from 'express';
|
|
2
2
|
import type { HttpAdapter } from '../adapters/http.adapter';
|
|
3
3
|
export declare class ExpressAdapter implements HttpAdapter<Request, Response> {
|
|
4
|
+
private getEndpoint;
|
|
4
5
|
getRequest(request: Request): {
|
|
5
6
|
method: string;
|
|
6
7
|
endpoint: string;
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ExpressAdapter = void 0;
|
|
4
4
|
class ExpressAdapter {
|
|
5
|
+
getEndpoint(request) {
|
|
6
|
+
const route = request.route?.path;
|
|
7
|
+
if (!route)
|
|
8
|
+
return request.path;
|
|
9
|
+
const baseUrl = request.baseUrl ?? '';
|
|
10
|
+
return `${baseUrl}${route}`;
|
|
11
|
+
}
|
|
5
12
|
getRequest(request) {
|
|
6
13
|
const { body, // Parsed JSON or form data
|
|
7
14
|
params, // Route path variables
|
|
@@ -16,7 +23,7 @@ class ExpressAdapter {
|
|
|
16
23
|
} = request;
|
|
17
24
|
return {
|
|
18
25
|
method,
|
|
19
|
-
endpoint:
|
|
26
|
+
endpoint: this.getEndpoint(request),
|
|
20
27
|
clientIp: ip,
|
|
21
28
|
};
|
|
22
29
|
}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import { Server as HTTPServer } from 'http';
|
|
2
|
-
|
|
2
|
+
import { type OptimaRuntimeDependencies } from '../runtime';
|
|
3
|
+
export interface ExpressStopOptions {
|
|
4
|
+
closeServer?: boolean;
|
|
5
|
+
drainTimeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function expressMetricsBootstrap(server: HTTPServer, dependencies: OptimaRuntimeDependencies): (options?: ExpressStopOptions) => Promise<void>;
|