fifony 0.1.47 → 0.1.48
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 +78 -0
- package/app/dist/assets/CommandPalette-CZDG20HW.js +1 -0
- package/app/dist/assets/{KeyboardShortcutsHelp-CqEFfGcE.js → KeyboardShortcutsHelp-TYhQc4aA.js} +1 -1
- package/app/dist/assets/OnboardingWizard-CQ9YmVIT.js +1 -0
- package/app/dist/assets/agents.lazy-CgakDm_P.js +1 -0
- package/app/dist/assets/analytics.lazy-C0rw3sov.js +1 -0
- package/app/dist/assets/{createLucideIcon-luywpIq4.js → createLucideIcon-B3bah5lk.js} +1 -1
- package/app/dist/assets/hooks-CNPue7d7.js +1 -0
- package/app/dist/assets/index-B8XCmr0-.css +1 -0
- package/app/dist/assets/index-Dfn02uW3.js +47 -0
- package/app/dist/assets/index.lazy-JdqhBwPd.js +44 -0
- package/app/dist/assets/services-CHpVij2M.css +1 -0
- package/app/dist/assets/services.lazy-BShKUCOt.js +12 -0
- package/app/dist/assets/vendor-X6HTElZW.js +9 -0
- package/app/dist/assets/viz-Dsh_q2DK.js +4 -0
- package/app/dist/index.html +5 -4
- package/app/dist/service-worker.js +32 -1
- package/dist/agent/run-local.js +89 -76
- package/dist/{agent-DFSFG6DG.js → agent-DJ4SCNBZ.js} +22 -17
- package/dist/{analytics-broadcaster-O4AE3RUK.js → analytics-broadcaster-INNYWHDJ.js} +25 -20
- package/dist/approve-plan.command-WE2CO3H2.js +21 -0
- package/dist/{chunk-HOIOVUHI.js → chunk-5M7PBFMZ.js} +8 -6
- package/dist/chunk-7R7XFXJM.js +1247 -0
- package/dist/{chunk-2PRRKBG6.js → chunk-A4P2MYJF.js} +22 -9
- package/dist/chunk-AFOV3ZAF.js +722 -0
- package/dist/chunk-AFP36N23.js +134 -0
- package/dist/{chunk-AAZKYWOY.js → chunk-AFYKGVSP.js} +103 -8
- package/dist/chunk-APJOZXRP.js +737 -0
- package/dist/chunk-DLSPRIQL.js +241 -0
- package/dist/{chunk-5AMWD66T.js → chunk-EDIPHR5B.js} +6 -4
- package/dist/{chunk-K36BWMUV.js → chunk-JU3MF3MW.js} +2526 -736
- package/dist/{chunk-7TXZYZR5.js → chunk-N5HCNY4O.js} +7 -5
- package/dist/{chunk-JRLWLZOD.js → chunk-NKMZYPIS.js} +31 -23
- package/dist/{chunk-PI7Y77R3.js → chunk-OFIVTM2E.js} +17 -7
- package/dist/{chunk-QH6VCTET.js → chunk-RCSJFMQG.js} +909 -98
- package/dist/{chunk-AAVROEQC.js → chunk-UR7T7IA6.js} +253 -349
- package/dist/{chunk-QHISYRXJ.js → chunk-VOYLU3MI.js} +57 -3
- package/dist/{chunk-EBCSQFPR.js → chunk-W5IULOWV.js} +2 -3
- package/dist/chunk-X37RNTWU.js +193 -0
- package/dist/{chunk-PACI3T4I.js → chunk-XY2APMDE.js} +13 -5
- package/dist/chunk-Z6ZWNWWR.js +34 -0
- package/dist/cli.js +45 -17
- package/dist/constants-AAP7ZGCX.js +124 -0
- package/dist/create-issue.command-SX3AXXIC.js +29 -0
- package/dist/fsm-agent-JGV22WK4.js +59 -0
- package/dist/{fsm-issue-EHTSKMFN.js → fsm-issue-LHIJM5VB.js} +12 -8
- package/dist/{fsm-service-7O4AJG2R.js → fsm-service-GGDKUTWS.js} +13 -4
- package/dist/{helpers-ON2S7UEF.js → helpers-AENVYEZJ.js} +6 -2
- package/dist/{issue-log-broadcaster-FZGVEEIX.js → issue-log-broadcaster-QQWM7LOV.js} +29 -18
- package/dist/{issues-3YNNTB4U.js → issues-RXFKKSXB.js} +10 -7
- package/dist/{log-analyzer-EIX6R6PP.js → log-analyzer-4LNXQISY.js} +30 -20
- package/dist/{logger-IFLXTQPS.js → logger-4F6ATWNA.js} +2 -1
- package/dist/mcp/server.js +6 -2
- package/dist/merge-workspace.command-ZNGIZC4O.js +29 -0
- package/dist/{parallel-executor-DWESCNX3.js → parallel-executor-OL5CB33L.js} +78 -19
- package/dist/{pid-manager-UBWXVSMD.js → pid-manager-EDT4DHAU.js} +2 -1
- package/dist/queue-workers-NSKIIMQ2.js +43 -0
- package/dist/replan-issue.command-73PETERX.js +21 -0
- package/dist/retry-issue.command-DIDP4OCS.js +21 -0
- package/dist/reverse-proxy-server-QSS3H4UH.js +97 -0
- package/dist/scheduler-5YORYECF.js +37 -0
- package/dist/service-log-broadcaster-JIUP2L3D.js +21 -0
- package/dist/{settings-SOTIS6ZD.js → settings-ZNDXYL46.js} +34 -23
- package/dist/settings.resource-OKUHXICJ.js +35 -0
- package/dist/{store-S3NAYZ3S.js → store-P3ACO6YA.js} +22 -17
- package/dist/telemetry-KVUFHDQS.js +828 -0
- package/dist/template-variants-HEPLYKMP.js +24 -0
- package/dist/trace-bundle-IJOV7IWH.js +41 -0
- package/dist/{web-push-QCTLS7EJ.js → web-push-X2LLMQ4M.js} +2 -1
- package/dist/websocket-Q2TUCIC2.js +103 -0
- package/dist/{workspace-OS7GPMCN.js → workspace-TDX3NJCX.js} +10 -6
- package/package.json +12 -9
- package/app/dist/assets/CommandPalette-CL8p78lG.js +0 -1
- package/app/dist/assets/OnboardingWizard-BmI50ZUv.js +0 -1
- package/app/dist/assets/analytics.lazy-CXGjZabc.js +0 -1
- package/app/dist/assets/index-CEaccpYh.js +0 -96
- package/app/dist/assets/index-CzzWGzux.css +0 -1
- package/app/dist/assets/vendor-uqBx3VSC.js +0 -9
- package/dist/approve-plan.command-QGQZZXTQ.js +0 -17
- package/dist/chunk-N4KFNX2G.js +0 -370
- package/dist/chunk-VM5QAYP5.js +0 -404
- package/dist/create-issue.command-VAKYRECC.js +0 -24
- package/dist/merge-workspace.command-T2NIGR4M.js +0 -24
- package/dist/queue-workers-V57BYXAY.js +0 -38
- package/dist/replan-issue.command-2GQ3QXCR.js +0 -17
- package/dist/retry-issue.command-GJBUUYDJ.js +0 -17
- package/dist/scheduler-KYILMWLD.js +0 -32
- package/dist/settings.resource-JMD3JQOS.js +0 -30
- package/dist/websocket-T2Y3BY4B.js +0 -61
|
@@ -0,0 +1,828 @@
|
|
|
1
|
+
import "./chunk-Z6ZWNWWR.js";
|
|
2
|
+
|
|
3
|
+
// node_modules/.pnpm/raffel@1.0.26_ajv@8.18.0_fastest-validator@1.19.1_zod-to-json-schema@3.25.2_zod@4.3.6__zod@4.3.6/node_modules/raffel/dist/metrics/types.js
|
|
4
|
+
var DEFAULT_HISTOGRAM_BUCKETS = [
|
|
5
|
+
5e-3,
|
|
6
|
+
0.01,
|
|
7
|
+
0.025,
|
|
8
|
+
0.05,
|
|
9
|
+
0.1,
|
|
10
|
+
0.25,
|
|
11
|
+
0.5,
|
|
12
|
+
1,
|
|
13
|
+
2.5,
|
|
14
|
+
5,
|
|
15
|
+
10
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// node_modules/.pnpm/raffel@1.0.26_ajv@8.18.0_fastest-validator@1.19.1_zod-to-json-schema@3.25.2_zod@4.3.6__zod@4.3.6/node_modules/raffel/dist/metrics/exporters.js
|
|
19
|
+
function formatLabels(labels) {
|
|
20
|
+
const entries = Object.entries(labels);
|
|
21
|
+
if (entries.length === 0)
|
|
22
|
+
return "";
|
|
23
|
+
entries.sort(([a], [b]) => a.localeCompare(b));
|
|
24
|
+
const formatted = entries.map(([k, v]) => `${k}="${escapePrometheusValue(v)}"`).join(",");
|
|
25
|
+
return `{${formatted}}`;
|
|
26
|
+
}
|
|
27
|
+
function escapePrometheusValue(value) {
|
|
28
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
29
|
+
}
|
|
30
|
+
function exportPrometheus(metrics, _defaultLabels) {
|
|
31
|
+
const lines = [];
|
|
32
|
+
for (const metric of metrics.values()) {
|
|
33
|
+
if (metric.description) {
|
|
34
|
+
lines.push(`# HELP ${metric.name} ${metric.description}`);
|
|
35
|
+
}
|
|
36
|
+
lines.push(`# TYPE ${metric.name} ${metric.type}`);
|
|
37
|
+
if (metric.type === "histogram") {
|
|
38
|
+
for (const histValue of metric.histogramValues.values()) {
|
|
39
|
+
const baseLabels = formatLabels(histValue.labels);
|
|
40
|
+
const baseName = metric.name;
|
|
41
|
+
for (const bucket of histValue.buckets) {
|
|
42
|
+
const leLabel = bucket.le === Infinity ? "+Inf" : bucket.le.toString();
|
|
43
|
+
const bucketLabels = histValue.labels;
|
|
44
|
+
const labelStr = formatLabels({ ...bucketLabels, le: leLabel });
|
|
45
|
+
lines.push(`${baseName}_bucket${labelStr} ${bucket.count}`);
|
|
46
|
+
}
|
|
47
|
+
const infLabels = formatLabels({ ...histValue.labels, le: "+Inf" });
|
|
48
|
+
lines.push(`${baseName}_bucket${infLabels} ${histValue.count}`);
|
|
49
|
+
lines.push(`${baseName}_sum${baseLabels} ${histValue.sum}`);
|
|
50
|
+
lines.push(`${baseName}_count${baseLabels} ${histValue.count}`);
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
for (const value of metric.values.values()) {
|
|
54
|
+
const labelStr = formatLabels(value.labels);
|
|
55
|
+
lines.push(`${metric.name}${labelStr} ${value.value}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
lines.push("");
|
|
59
|
+
}
|
|
60
|
+
return lines.join("\n");
|
|
61
|
+
}
|
|
62
|
+
function exportJson(metrics, defaultLabels) {
|
|
63
|
+
const output = {
|
|
64
|
+
defaultLabels,
|
|
65
|
+
metrics: {}
|
|
66
|
+
};
|
|
67
|
+
const metricsObj = output.metrics;
|
|
68
|
+
for (const metric of metrics.values()) {
|
|
69
|
+
if (metric.type === "histogram") {
|
|
70
|
+
const histograms = [];
|
|
71
|
+
for (const histValue of metric.histogramValues.values()) {
|
|
72
|
+
histograms.push({
|
|
73
|
+
labels: histValue.labels,
|
|
74
|
+
buckets: histValue.buckets,
|
|
75
|
+
sum: histValue.sum,
|
|
76
|
+
count: histValue.count
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
metricsObj[metric.name] = {
|
|
80
|
+
type: metric.type,
|
|
81
|
+
description: metric.description,
|
|
82
|
+
values: histograms
|
|
83
|
+
};
|
|
84
|
+
} else {
|
|
85
|
+
const values = [];
|
|
86
|
+
for (const value of metric.values.values()) {
|
|
87
|
+
values.push({
|
|
88
|
+
labels: value.labels,
|
|
89
|
+
value: value.value
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
metricsObj[metric.name] = {
|
|
93
|
+
type: metric.type,
|
|
94
|
+
description: metric.description,
|
|
95
|
+
values
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return JSON.stringify(output, null, 2);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// node_modules/.pnpm/raffel@1.0.26_ajv@8.18.0_fastest-validator@1.19.1_zod-to-json-schema@3.25.2_zod@4.3.6__zod@4.3.6/node_modules/raffel/dist/metrics/registry.js
|
|
103
|
+
function labelsToKey(labels) {
|
|
104
|
+
const sortedKeys = Object.keys(labels).sort();
|
|
105
|
+
if (sortedKeys.length === 0)
|
|
106
|
+
return "";
|
|
107
|
+
return sortedKeys.map((k) => `${k}="${labels[k]}"`).join(",");
|
|
108
|
+
}
|
|
109
|
+
function mergeLabels(defaults, provided = {}) {
|
|
110
|
+
return { ...defaults, ...provided };
|
|
111
|
+
}
|
|
112
|
+
function validateLabels(metricName, labelKeys, provided, defaultLabels) {
|
|
113
|
+
const providedKeys = Object.keys(provided);
|
|
114
|
+
const defaultKeys = Object.keys(defaultLabels);
|
|
115
|
+
for (const key of providedKeys) {
|
|
116
|
+
if (defaultKeys.includes(key))
|
|
117
|
+
continue;
|
|
118
|
+
if (!labelKeys.includes(key)) {
|
|
119
|
+
throw new Error(`Metric '${metricName}' does not have label '${key}'. Registered labels: [${labelKeys.join(", ")}]`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function createHistogramValue(buckets, labels) {
|
|
124
|
+
return {
|
|
125
|
+
labels,
|
|
126
|
+
buckets: buckets.map((le) => ({ le, count: 0 })),
|
|
127
|
+
sum: 0,
|
|
128
|
+
count: 0
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function createMetricRegistry() {
|
|
132
|
+
const metrics = /* @__PURE__ */ new Map();
|
|
133
|
+
let defaultLabels = {};
|
|
134
|
+
function registerMetric(name, type, opts = {}) {
|
|
135
|
+
if (metrics.has(name)) {
|
|
136
|
+
throw new Error(`Metric '${name}' is already registered`);
|
|
137
|
+
}
|
|
138
|
+
const definition = {
|
|
139
|
+
name,
|
|
140
|
+
type,
|
|
141
|
+
description: opts.description ?? "",
|
|
142
|
+
labelKeys: opts.labels ?? [],
|
|
143
|
+
values: /* @__PURE__ */ new Map()
|
|
144
|
+
};
|
|
145
|
+
if (type === "histogram") {
|
|
146
|
+
definition.buckets = opts.buckets ?? [...DEFAULT_HISTOGRAM_BUCKETS];
|
|
147
|
+
definition.histogramValues = /* @__PURE__ */ new Map();
|
|
148
|
+
}
|
|
149
|
+
metrics.set(name, definition);
|
|
150
|
+
}
|
|
151
|
+
function getMetricOrThrow(name, expectedType) {
|
|
152
|
+
const metric = metrics.get(name);
|
|
153
|
+
if (!metric) {
|
|
154
|
+
throw new Error(`Metric '${name}' is not registered`);
|
|
155
|
+
}
|
|
156
|
+
if (expectedType && metric.type !== expectedType) {
|
|
157
|
+
throw new Error(`Metric '${name}' is a ${metric.type}, not a ${expectedType}`);
|
|
158
|
+
}
|
|
159
|
+
return metric;
|
|
160
|
+
}
|
|
161
|
+
const registry = {
|
|
162
|
+
counter(name, opts) {
|
|
163
|
+
registerMetric(name, "counter", opts);
|
|
164
|
+
},
|
|
165
|
+
gauge(name, opts) {
|
|
166
|
+
registerMetric(name, "gauge", opts);
|
|
167
|
+
},
|
|
168
|
+
histogram(name, opts) {
|
|
169
|
+
registerMetric(name, "histogram", opts);
|
|
170
|
+
},
|
|
171
|
+
increment(name, labels = {}, delta = 1) {
|
|
172
|
+
const metric = getMetricOrThrow(name, "counter");
|
|
173
|
+
const mergedLabels = mergeLabels(defaultLabels, labels);
|
|
174
|
+
validateLabels(name, metric.labelKeys, mergedLabels, defaultLabels);
|
|
175
|
+
const key = labelsToKey(mergedLabels);
|
|
176
|
+
const existing = metric.values.get(key);
|
|
177
|
+
if (existing) {
|
|
178
|
+
existing.value += delta;
|
|
179
|
+
} else {
|
|
180
|
+
metric.values.set(key, {
|
|
181
|
+
value: delta,
|
|
182
|
+
labels: mergedLabels
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
set(name, value, labels = {}) {
|
|
187
|
+
const metric = getMetricOrThrow(name, "gauge");
|
|
188
|
+
const mergedLabels = mergeLabels(defaultLabels, labels);
|
|
189
|
+
validateLabels(name, metric.labelKeys, mergedLabels, defaultLabels);
|
|
190
|
+
const key = labelsToKey(mergedLabels);
|
|
191
|
+
metric.values.set(key, {
|
|
192
|
+
value,
|
|
193
|
+
labels: mergedLabels
|
|
194
|
+
});
|
|
195
|
+
},
|
|
196
|
+
observe(name, value, labels = {}) {
|
|
197
|
+
const metric = getMetricOrThrow(name, "histogram");
|
|
198
|
+
const mergedLabels = mergeLabels(defaultLabels, labels);
|
|
199
|
+
validateLabels(name, metric.labelKeys, mergedLabels, defaultLabels);
|
|
200
|
+
const key = labelsToKey(mergedLabels);
|
|
201
|
+
let histValue = metric.histogramValues.get(key);
|
|
202
|
+
if (!histValue) {
|
|
203
|
+
histValue = createHistogramValue(metric.buckets, mergedLabels);
|
|
204
|
+
metric.histogramValues.set(key, histValue);
|
|
205
|
+
}
|
|
206
|
+
histValue.sum += value;
|
|
207
|
+
histValue.count += 1;
|
|
208
|
+
for (const bucket of histValue.buckets) {
|
|
209
|
+
if (value <= bucket.le) {
|
|
210
|
+
bucket.count += 1;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
timer(name, labels = {}) {
|
|
215
|
+
const start = performance.now();
|
|
216
|
+
return () => {
|
|
217
|
+
const duration = (performance.now() - start) / 1e3;
|
|
218
|
+
registry.observe(name, duration, labels);
|
|
219
|
+
return duration;
|
|
220
|
+
};
|
|
221
|
+
},
|
|
222
|
+
export(format) {
|
|
223
|
+
if (format === "prometheus") {
|
|
224
|
+
return exportPrometheus(metrics, defaultLabels);
|
|
225
|
+
}
|
|
226
|
+
return exportJson(metrics, defaultLabels);
|
|
227
|
+
},
|
|
228
|
+
getMetric(name) {
|
|
229
|
+
return metrics.get(name);
|
|
230
|
+
},
|
|
231
|
+
getMetricNames() {
|
|
232
|
+
return Array.from(metrics.keys());
|
|
233
|
+
},
|
|
234
|
+
reset() {
|
|
235
|
+
for (const metric of metrics.values()) {
|
|
236
|
+
metric.values.clear();
|
|
237
|
+
if (metric.histogramValues) {
|
|
238
|
+
metric.histogramValues.clear();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
getDefaultLabels() {
|
|
243
|
+
return { ...defaultLabels };
|
|
244
|
+
},
|
|
245
|
+
setDefaultLabels(labels) {
|
|
246
|
+
defaultLabels = { ...labels };
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
return registry;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// node_modules/.pnpm/raffel@1.0.26_ajv@8.18.0_fastest-validator@1.19.1_zod-to-json-schema@3.25.2_zod@4.3.6__zod@4.3.6/node_modules/raffel/dist/proxy/telemetry.js
|
|
253
|
+
var PROXY_AUTO_METRICS = {
|
|
254
|
+
FLOWS_TOTAL: "raffel_proxy_edge_flows_total",
|
|
255
|
+
ACTIVE_FLOWS: "raffel_proxy_edge_active_flows",
|
|
256
|
+
BYTES_FROM_SOURCE: "raffel_proxy_edge_bytes_from_source_total",
|
|
257
|
+
BYTES_TO_SOURCE: "raffel_proxy_edge_bytes_to_source_total",
|
|
258
|
+
REQUESTS_TOTAL: "raffel_proxy_edge_requests_total",
|
|
259
|
+
REQUEST_DURATION: "raffel_proxy_edge_request_duration_seconds",
|
|
260
|
+
FLOW_DURATION: "raffel_proxy_edge_flow_duration_seconds",
|
|
261
|
+
FLOW_DURATION_QUANTILE: "raffel_proxy_edge_flow_duration_quantile_seconds",
|
|
262
|
+
ERRORS_TOTAL: "raffel_proxy_edge_errors_total",
|
|
263
|
+
FLOW_RATE: "raffel_proxy_edge_flow_rate_per_second",
|
|
264
|
+
REQUEST_RATE: "raffel_proxy_edge_request_rate_per_second",
|
|
265
|
+
ERROR_RATE: "raffel_proxy_edge_error_rate_per_second",
|
|
266
|
+
BYTES_FROM_SOURCE_RATE: "raffel_proxy_edge_bytes_from_source_rate_per_second",
|
|
267
|
+
BYTES_TO_SOURCE_RATE: "raffel_proxy_edge_bytes_to_source_rate_per_second",
|
|
268
|
+
FAILURE_RATIO: "raffel_proxy_edge_failure_ratio"
|
|
269
|
+
};
|
|
270
|
+
var DEFAULT_PROXY_PERCENTILES = [0.5, 0.9, 0.95];
|
|
271
|
+
var DEFAULT_PROXY_DURATION_BUCKETS = [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60];
|
|
272
|
+
var DEFAULT_PROXY_RATE_WINDOW_MS = 6e4;
|
|
273
|
+
var RATE_BUCKET_MS = 1e3;
|
|
274
|
+
function isErrorStatus(status) {
|
|
275
|
+
if (!status)
|
|
276
|
+
return false;
|
|
277
|
+
const numeric = Number.parseInt(status, 10);
|
|
278
|
+
return Number.isFinite(numeric) ? numeric >= 400 : status !== "success";
|
|
279
|
+
}
|
|
280
|
+
function statusClass(status) {
|
|
281
|
+
if (!status)
|
|
282
|
+
return "other";
|
|
283
|
+
const numeric = Number.parseInt(status, 10);
|
|
284
|
+
if (!Number.isFinite(numeric))
|
|
285
|
+
return "other";
|
|
286
|
+
if (numeric >= 500)
|
|
287
|
+
return "5xx";
|
|
288
|
+
if (numeric >= 400)
|
|
289
|
+
return "4xx";
|
|
290
|
+
if (numeric >= 300)
|
|
291
|
+
return "3xx";
|
|
292
|
+
if (numeric >= 200)
|
|
293
|
+
return "2xx";
|
|
294
|
+
if (numeric >= 100)
|
|
295
|
+
return "1xx";
|
|
296
|
+
return "other";
|
|
297
|
+
}
|
|
298
|
+
function edgeKey(labels) {
|
|
299
|
+
return `${labels.source}\0${labels.destination}\0${labels.protocol}`;
|
|
300
|
+
}
|
|
301
|
+
function percentileLabel(value) {
|
|
302
|
+
const percentage = value * 100;
|
|
303
|
+
if (Number.isInteger(percentage)) {
|
|
304
|
+
return `p${percentage}`;
|
|
305
|
+
}
|
|
306
|
+
return `p${percentage.toFixed(2).replace(/\.?0+$/, "").replace(".", "_")}`;
|
|
307
|
+
}
|
|
308
|
+
function normalizePercentiles(percentiles) {
|
|
309
|
+
const input = percentiles && percentiles.length > 0 ? percentiles : [...DEFAULT_PROXY_PERCENTILES];
|
|
310
|
+
const values = Array.from(new Set(input.filter((value) => Number.isFinite(value) && value > 0 && value <= 1).map((value) => Number(value)))).sort((a, b) => a - b);
|
|
311
|
+
const safeValues = values.length > 0 ? values : [...DEFAULT_PROXY_PERCENTILES];
|
|
312
|
+
return safeValues.map((value) => ({
|
|
313
|
+
label: percentileLabel(value),
|
|
314
|
+
value
|
|
315
|
+
}));
|
|
316
|
+
}
|
|
317
|
+
function normalizeRateWindowMs(rateWindowMs) {
|
|
318
|
+
if (!Number.isFinite(rateWindowMs) || rateWindowMs == null || rateWindowMs <= 0) {
|
|
319
|
+
return DEFAULT_PROXY_RATE_WINDOW_MS;
|
|
320
|
+
}
|
|
321
|
+
return Math.max(RATE_BUCKET_MS, Math.round(rateWindowMs));
|
|
322
|
+
}
|
|
323
|
+
function cloneBuckets(buckets) {
|
|
324
|
+
return buckets.map((le) => ({ le, count: 0 }));
|
|
325
|
+
}
|
|
326
|
+
function observeDuration(state, value) {
|
|
327
|
+
state.sum += value;
|
|
328
|
+
state.count += 1;
|
|
329
|
+
for (const bucket of state.buckets) {
|
|
330
|
+
if (value <= bucket.le) {
|
|
331
|
+
bucket.count += 1;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function estimatePercentile(state, percentile) {
|
|
336
|
+
if (state.count === 0)
|
|
337
|
+
return null;
|
|
338
|
+
const target = state.count * percentile;
|
|
339
|
+
let previousCount = 0;
|
|
340
|
+
let previousLe = 0;
|
|
341
|
+
for (const bucket of state.buckets) {
|
|
342
|
+
if (bucket.count >= target) {
|
|
343
|
+
const bucketCount = bucket.count - previousCount;
|
|
344
|
+
if (bucketCount <= 0) {
|
|
345
|
+
return bucket.le;
|
|
346
|
+
}
|
|
347
|
+
const offset = Math.max(0, target - previousCount);
|
|
348
|
+
const ratio = Math.min(1, offset / bucketCount);
|
|
349
|
+
return previousLe + (bucket.le - previousLe) * ratio;
|
|
350
|
+
}
|
|
351
|
+
previousCount = bucket.count;
|
|
352
|
+
previousLe = bucket.le;
|
|
353
|
+
}
|
|
354
|
+
return state.buckets.at(-1)?.le ?? null;
|
|
355
|
+
}
|
|
356
|
+
function summarizeLatency(state, percentiles) {
|
|
357
|
+
return {
|
|
358
|
+
averageSeconds: state.count > 0 ? state.sum / state.count : null,
|
|
359
|
+
percentiles: Object.fromEntries(percentiles.map((definition) => [
|
|
360
|
+
definition.label,
|
|
361
|
+
estimatePercentile(state, definition.value)
|
|
362
|
+
]))
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function pruneRateBuckets(state, nowMs, rateWindowMs) {
|
|
366
|
+
const cutoff = nowMs - rateWindowMs;
|
|
367
|
+
for (const [bucketStartMs] of state.buckets) {
|
|
368
|
+
if (bucketStartMs < cutoff) {
|
|
369
|
+
state.buckets.delete(bucketStartMs);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function ensureRateBucket(state, nowMs) {
|
|
374
|
+
const bucketStartMs = Math.floor(nowMs / RATE_BUCKET_MS) * RATE_BUCKET_MS;
|
|
375
|
+
const existing = state.buckets.get(bucketStartMs);
|
|
376
|
+
if (existing)
|
|
377
|
+
return existing;
|
|
378
|
+
const created = {
|
|
379
|
+
bucketStartMs,
|
|
380
|
+
flows: 0,
|
|
381
|
+
requests: 0,
|
|
382
|
+
errors: 0,
|
|
383
|
+
bytesFromSource: 0,
|
|
384
|
+
bytesToSource: 0
|
|
385
|
+
};
|
|
386
|
+
state.buckets.set(bucketStartMs, created);
|
|
387
|
+
return created;
|
|
388
|
+
}
|
|
389
|
+
function recordRateDelta(state, nowMs, rateWindowMs, delta) {
|
|
390
|
+
pruneRateBuckets(state, nowMs, rateWindowMs);
|
|
391
|
+
const bucket = ensureRateBucket(state, nowMs);
|
|
392
|
+
bucket.flows += delta.flows ?? 0;
|
|
393
|
+
bucket.requests += delta.requests ?? 0;
|
|
394
|
+
bucket.errors += delta.errors ?? 0;
|
|
395
|
+
bucket.bytesFromSource += delta.bytesFromSource ?? 0;
|
|
396
|
+
bucket.bytesToSource += delta.bytesToSource ?? 0;
|
|
397
|
+
}
|
|
398
|
+
function summarizeRates(state, nowMs, rateWindowMs) {
|
|
399
|
+
pruneRateBuckets(state, nowMs, rateWindowMs);
|
|
400
|
+
let flows = 0;
|
|
401
|
+
let requests = 0;
|
|
402
|
+
let errors = 0;
|
|
403
|
+
let bytesFromSource = 0;
|
|
404
|
+
let bytesToSource = 0;
|
|
405
|
+
for (const bucket of state.buckets.values()) {
|
|
406
|
+
flows += bucket.flows;
|
|
407
|
+
requests += bucket.requests;
|
|
408
|
+
errors += bucket.errors;
|
|
409
|
+
bytesFromSource += bucket.bytesFromSource;
|
|
410
|
+
bytesToSource += bucket.bytesToSource;
|
|
411
|
+
}
|
|
412
|
+
const windowSeconds = rateWindowMs / 1e3;
|
|
413
|
+
const denominator = requests > 0 ? requests : flows > 0 ? flows : 0;
|
|
414
|
+
return {
|
|
415
|
+
windowSeconds,
|
|
416
|
+
flowsPerSecond: flows / windowSeconds,
|
|
417
|
+
requestsPerSecond: requests / windowSeconds,
|
|
418
|
+
errorsPerSecond: errors / windowSeconds,
|
|
419
|
+
bytesFromSourcePerSecond: bytesFromSource / windowSeconds,
|
|
420
|
+
bytesToSourcePerSecond: bytesToSource / windowSeconds,
|
|
421
|
+
bytesPerSecond: (bytesFromSource + bytesToSource) / windowSeconds,
|
|
422
|
+
failureRatio: denominator > 0 ? errors / denominator : null
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function ensureProxyMetricsRegistered(registry) {
|
|
426
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.FLOWS_TOTAL)) {
|
|
427
|
+
registry.counter(PROXY_AUTO_METRICS.FLOWS_TOTAL, {
|
|
428
|
+
description: "Completed proxy flows grouped by edge and protocol",
|
|
429
|
+
labels: ["source", "destination", "protocol", "status"]
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.ACTIVE_FLOWS)) {
|
|
433
|
+
registry.gauge(PROXY_AUTO_METRICS.ACTIVE_FLOWS, {
|
|
434
|
+
description: "Active proxy flows grouped by edge and protocol",
|
|
435
|
+
labels: ["source", "destination", "protocol"]
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.BYTES_FROM_SOURCE)) {
|
|
439
|
+
registry.counter(PROXY_AUTO_METRICS.BYTES_FROM_SOURCE, {
|
|
440
|
+
description: "Bytes proxied from source to destination grouped by edge and protocol",
|
|
441
|
+
labels: ["source", "destination", "protocol"]
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.BYTES_TO_SOURCE)) {
|
|
445
|
+
registry.counter(PROXY_AUTO_METRICS.BYTES_TO_SOURCE, {
|
|
446
|
+
description: "Bytes proxied from destination back to source grouped by edge and protocol",
|
|
447
|
+
labels: ["source", "destination", "protocol"]
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.REQUESTS_TOTAL)) {
|
|
451
|
+
registry.counter(PROXY_AUTO_METRICS.REQUESTS_TOTAL, {
|
|
452
|
+
description: "Proxy requests grouped by edge, protocol, method, and status",
|
|
453
|
+
labels: ["source", "destination", "protocol", "method", "status"]
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.REQUEST_DURATION)) {
|
|
457
|
+
registry.histogram(PROXY_AUTO_METRICS.REQUEST_DURATION, {
|
|
458
|
+
description: "Proxy request duration in seconds grouped by edge, protocol, and method",
|
|
459
|
+
labels: ["source", "destination", "protocol", "method"],
|
|
460
|
+
buckets: [...DEFAULT_PROXY_DURATION_BUCKETS]
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.FLOW_DURATION)) {
|
|
464
|
+
registry.histogram(PROXY_AUTO_METRICS.FLOW_DURATION, {
|
|
465
|
+
description: "Proxy flow duration in seconds grouped by edge and protocol",
|
|
466
|
+
labels: ["source", "destination", "protocol"],
|
|
467
|
+
buckets: [...DEFAULT_PROXY_DURATION_BUCKETS]
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.FLOW_DURATION_QUANTILE)) {
|
|
471
|
+
registry.gauge(PROXY_AUTO_METRICS.FLOW_DURATION_QUANTILE, {
|
|
472
|
+
description: "Estimated proxy flow duration quantiles grouped by edge and protocol",
|
|
473
|
+
labels: ["source", "destination", "protocol", "quantile"]
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.ERRORS_TOTAL)) {
|
|
477
|
+
registry.counter(PROXY_AUTO_METRICS.ERRORS_TOTAL, {
|
|
478
|
+
description: "Proxy edge errors grouped by edge, protocol, and error reason",
|
|
479
|
+
labels: ["source", "destination", "protocol", "error"]
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.FLOW_RATE)) {
|
|
483
|
+
registry.gauge(PROXY_AUTO_METRICS.FLOW_RATE, {
|
|
484
|
+
description: "Recent proxy flow rate per second grouped by edge and protocol",
|
|
485
|
+
labels: ["source", "destination", "protocol"]
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.REQUEST_RATE)) {
|
|
489
|
+
registry.gauge(PROXY_AUTO_METRICS.REQUEST_RATE, {
|
|
490
|
+
description: "Recent proxy request rate per second grouped by edge and protocol",
|
|
491
|
+
labels: ["source", "destination", "protocol"]
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.ERROR_RATE)) {
|
|
495
|
+
registry.gauge(PROXY_AUTO_METRICS.ERROR_RATE, {
|
|
496
|
+
description: "Recent proxy error rate per second grouped by edge and protocol",
|
|
497
|
+
labels: ["source", "destination", "protocol"]
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.BYTES_FROM_SOURCE_RATE)) {
|
|
501
|
+
registry.gauge(PROXY_AUTO_METRICS.BYTES_FROM_SOURCE_RATE, {
|
|
502
|
+
description: "Recent bytes per second flowing from source to destination grouped by edge and protocol",
|
|
503
|
+
labels: ["source", "destination", "protocol"]
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.BYTES_TO_SOURCE_RATE)) {
|
|
507
|
+
registry.gauge(PROXY_AUTO_METRICS.BYTES_TO_SOURCE_RATE, {
|
|
508
|
+
description: "Recent bytes per second flowing from destination back to source grouped by edge and protocol",
|
|
509
|
+
labels: ["source", "destination", "protocol"]
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
if (!registry.getMetric(PROXY_AUTO_METRICS.FAILURE_RATIO)) {
|
|
513
|
+
registry.gauge(PROXY_AUTO_METRICS.FAILURE_RATIO, {
|
|
514
|
+
description: "Recent proxy failure ratio grouped by edge and protocol",
|
|
515
|
+
labels: ["source", "destination", "protocol"]
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function createProxyTelemetry(config = {}) {
|
|
520
|
+
const registry = config.registry ?? createMetricRegistry();
|
|
521
|
+
const currentDefaults = registry.getDefaultLabels();
|
|
522
|
+
if (config.defaultLabels) {
|
|
523
|
+
registry.setDefaultLabels({ ...currentDefaults, ...config.defaultLabels });
|
|
524
|
+
}
|
|
525
|
+
ensureProxyMetricsRegistered(registry);
|
|
526
|
+
const percentiles = normalizePercentiles(config.percentiles);
|
|
527
|
+
const flowDurationBuckets = registry.getMetric(PROXY_AUTO_METRICS.FLOW_DURATION)?.buckets ?? [...DEFAULT_PROXY_DURATION_BUCKETS];
|
|
528
|
+
const rateWindowMs = normalizeRateWindowMs(config.rateWindowMs);
|
|
529
|
+
let seq = 0;
|
|
530
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
531
|
+
const edges = /* @__PURE__ */ new Map();
|
|
532
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
533
|
+
function emit(event) {
|
|
534
|
+
for (const listener of listeners) {
|
|
535
|
+
listener(event);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function buildEdgeSnapshot(edge, nowMs) {
|
|
539
|
+
return {
|
|
540
|
+
id: edge.id,
|
|
541
|
+
source: edge.source,
|
|
542
|
+
target: edge.target,
|
|
543
|
+
protocol: edge.protocol,
|
|
544
|
+
firstSeenAt: edge.firstSeenAt,
|
|
545
|
+
lastSeenAt: edge.lastSeenAt,
|
|
546
|
+
activeFlows: edge.activeFlows,
|
|
547
|
+
flowsTotal: edge.flowsTotal,
|
|
548
|
+
requestsTotal: edge.requestsTotal,
|
|
549
|
+
errorsTotal: edge.errorsTotal,
|
|
550
|
+
bytesFromSource: edge.bytesFromSource,
|
|
551
|
+
bytesToSource: edge.bytesToSource,
|
|
552
|
+
durationCount: edge.duration.count,
|
|
553
|
+
durationSumSeconds: edge.duration.sum,
|
|
554
|
+
latency: summarizeLatency(edge.duration, percentiles),
|
|
555
|
+
rates: summarizeRates(edge.rates, nowMs, rateWindowMs),
|
|
556
|
+
statusClassCounts: { ...edge.statusClassCounts },
|
|
557
|
+
methodCounts: { ...edge.methodCounts },
|
|
558
|
+
topPaths: Array.from(edge.pathCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([path, count]) => ({ path, count }))
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function ensureNode(id, seenAt) {
|
|
562
|
+
const existing = nodes.get(id);
|
|
563
|
+
if (existing) {
|
|
564
|
+
existing.lastSeenAt = seenAt;
|
|
565
|
+
return existing;
|
|
566
|
+
}
|
|
567
|
+
const created = { id, lastSeenAt: seenAt };
|
|
568
|
+
nodes.set(id, created);
|
|
569
|
+
emit({ type: "node:new", seq, nodeId: id, firstSeenAt: seenAt });
|
|
570
|
+
return created;
|
|
571
|
+
}
|
|
572
|
+
function ensureEdge(labels, seenAt) {
|
|
573
|
+
const key = edgeKey(labels);
|
|
574
|
+
const existing = edges.get(key);
|
|
575
|
+
if (existing) {
|
|
576
|
+
existing.lastSeenAt = seenAt;
|
|
577
|
+
return { edge: existing, isNew: false };
|
|
578
|
+
}
|
|
579
|
+
const created = {
|
|
580
|
+
id: key,
|
|
581
|
+
source: labels.source,
|
|
582
|
+
target: labels.destination,
|
|
583
|
+
protocol: labels.protocol,
|
|
584
|
+
firstSeenAt: seenAt,
|
|
585
|
+
activeFlows: 0,
|
|
586
|
+
flowsTotal: 0,
|
|
587
|
+
requestsTotal: 0,
|
|
588
|
+
errorsTotal: 0,
|
|
589
|
+
bytesFromSource: 0,
|
|
590
|
+
bytesToSource: 0,
|
|
591
|
+
duration: {
|
|
592
|
+
buckets: cloneBuckets(flowDurationBuckets),
|
|
593
|
+
sum: 0,
|
|
594
|
+
count: 0
|
|
595
|
+
},
|
|
596
|
+
rates: {
|
|
597
|
+
buckets: /* @__PURE__ */ new Map()
|
|
598
|
+
},
|
|
599
|
+
lastSeenAt: seenAt,
|
|
600
|
+
statusClassCounts: {},
|
|
601
|
+
methodCounts: {},
|
|
602
|
+
pathCounts: /* @__PURE__ */ new Map()
|
|
603
|
+
};
|
|
604
|
+
edges.set(key, created);
|
|
605
|
+
return { edge: created, isNew: true };
|
|
606
|
+
}
|
|
607
|
+
function updateRateMetrics(labels, edge, nowMs) {
|
|
608
|
+
const rates = summarizeRates(edge.rates, nowMs, rateWindowMs);
|
|
609
|
+
registry.set(PROXY_AUTO_METRICS.FLOW_RATE, rates.flowsPerSecond, labels);
|
|
610
|
+
registry.set(PROXY_AUTO_METRICS.REQUEST_RATE, rates.requestsPerSecond, labels);
|
|
611
|
+
registry.set(PROXY_AUTO_METRICS.ERROR_RATE, rates.errorsPerSecond, labels);
|
|
612
|
+
registry.set(PROXY_AUTO_METRICS.BYTES_FROM_SOURCE_RATE, rates.bytesFromSourcePerSecond, labels);
|
|
613
|
+
registry.set(PROXY_AUTO_METRICS.BYTES_TO_SOURCE_RATE, rates.bytesToSourcePerSecond, labels);
|
|
614
|
+
registry.set(PROXY_AUTO_METRICS.FAILURE_RATIO, rates.failureRatio ?? 0, labels);
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
registry,
|
|
618
|
+
percentiles,
|
|
619
|
+
rateWindowSeconds: rateWindowMs / 1e3,
|
|
620
|
+
subscribe(listener) {
|
|
621
|
+
listeners.add(listener);
|
|
622
|
+
return () => {
|
|
623
|
+
listeners.delete(listener);
|
|
624
|
+
};
|
|
625
|
+
},
|
|
626
|
+
reset() {
|
|
627
|
+
nodes.clear();
|
|
628
|
+
edges.clear();
|
|
629
|
+
seq = 0;
|
|
630
|
+
emit({ type: "reset", seq });
|
|
631
|
+
},
|
|
632
|
+
expireEdgesBefore(iso) {
|
|
633
|
+
let removed = 0;
|
|
634
|
+
for (const [key, edge] of edges) {
|
|
635
|
+
if (edge.lastSeenAt < iso) {
|
|
636
|
+
edges.delete(key);
|
|
637
|
+
seq++;
|
|
638
|
+
emit({
|
|
639
|
+
type: "edge:expire",
|
|
640
|
+
seq,
|
|
641
|
+
edgeId: edge.id,
|
|
642
|
+
source: edge.source,
|
|
643
|
+
target: edge.target,
|
|
644
|
+
protocol: edge.protocol
|
|
645
|
+
});
|
|
646
|
+
removed++;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
if (removed > 0) {
|
|
650
|
+
const referencedNodes = /* @__PURE__ */ new Set();
|
|
651
|
+
for (const edge of edges.values()) {
|
|
652
|
+
referencedNodes.add(edge.source);
|
|
653
|
+
referencedNodes.add(edge.target);
|
|
654
|
+
}
|
|
655
|
+
for (const nodeId of nodes.keys()) {
|
|
656
|
+
if (!referencedNodes.has(nodeId)) {
|
|
657
|
+
nodes.delete(nodeId);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return removed;
|
|
662
|
+
},
|
|
663
|
+
startFlow(labels) {
|
|
664
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
665
|
+
const startedAtMs = Date.now();
|
|
666
|
+
ensureNode(labels.source, startedAt);
|
|
667
|
+
ensureNode(labels.destination, startedAt);
|
|
668
|
+
const { edge, isNew } = ensureEdge(labels, startedAt);
|
|
669
|
+
edge.activeFlows++;
|
|
670
|
+
edge.flowsTotal++;
|
|
671
|
+
seq++;
|
|
672
|
+
recordRateDelta(edge.rates, startedAtMs, rateWindowMs, { flows: 1 });
|
|
673
|
+
registry.set(PROXY_AUTO_METRICS.ACTIVE_FLOWS, edge.activeFlows, labels);
|
|
674
|
+
updateRateMetrics(labels, edge, startedAtMs);
|
|
675
|
+
emit({
|
|
676
|
+
type: isNew ? "edge:new" : "edge:update",
|
|
677
|
+
seq,
|
|
678
|
+
edge: buildEdgeSnapshot(edge, startedAtMs)
|
|
679
|
+
});
|
|
680
|
+
let finished = false;
|
|
681
|
+
return {
|
|
682
|
+
addBytesFromSource(delta) {
|
|
683
|
+
if (finished || delta <= 0)
|
|
684
|
+
return;
|
|
685
|
+
const nowMs = Date.now();
|
|
686
|
+
edge.bytesFromSource += delta;
|
|
687
|
+
edge.lastSeenAt = new Date(nowMs).toISOString();
|
|
688
|
+
recordRateDelta(edge.rates, nowMs, rateWindowMs, { bytesFromSource: delta });
|
|
689
|
+
registry.increment(PROXY_AUTO_METRICS.BYTES_FROM_SOURCE, labels, delta);
|
|
690
|
+
updateRateMetrics(labels, edge, nowMs);
|
|
691
|
+
},
|
|
692
|
+
addBytesToSource(delta) {
|
|
693
|
+
if (finished || delta <= 0)
|
|
694
|
+
return;
|
|
695
|
+
const nowMs = Date.now();
|
|
696
|
+
edge.bytesToSource += delta;
|
|
697
|
+
edge.lastSeenAt = new Date(nowMs).toISOString();
|
|
698
|
+
recordRateDelta(edge.rates, nowMs, rateWindowMs, { bytesToSource: delta });
|
|
699
|
+
registry.increment(PROXY_AUTO_METRICS.BYTES_TO_SOURCE, labels, delta);
|
|
700
|
+
updateRateMetrics(labels, edge, nowMs);
|
|
701
|
+
},
|
|
702
|
+
finish(input = {}) {
|
|
703
|
+
if (finished)
|
|
704
|
+
return;
|
|
705
|
+
finished = true;
|
|
706
|
+
const nowMs = Date.now();
|
|
707
|
+
const seenAt = new Date(nowMs).toISOString();
|
|
708
|
+
edge.lastSeenAt = seenAt;
|
|
709
|
+
edge.activeFlows = Math.max(0, edge.activeFlows - 1);
|
|
710
|
+
const errorLabel = input.error ?? (isErrorStatus(input.status) ? `status_${input.status}` : void 0);
|
|
711
|
+
const rateDelta = {};
|
|
712
|
+
if (input.method) {
|
|
713
|
+
const method = input.method.toUpperCase();
|
|
714
|
+
edge.requestsTotal++;
|
|
715
|
+
edge.methodCounts[method] = (edge.methodCounts[method] ?? 0) + 1;
|
|
716
|
+
rateDelta.requests = 1;
|
|
717
|
+
}
|
|
718
|
+
if (input.path) {
|
|
719
|
+
edge.pathCounts.set(input.path, (edge.pathCounts.get(input.path) ?? 0) + 1);
|
|
720
|
+
}
|
|
721
|
+
if (input.status) {
|
|
722
|
+
const cls = statusClass(input.status);
|
|
723
|
+
edge.statusClassCounts[cls] = (edge.statusClassCounts[cls] ?? 0) + 1;
|
|
724
|
+
}
|
|
725
|
+
if (errorLabel) {
|
|
726
|
+
edge.errorsTotal++;
|
|
727
|
+
rateDelta.errors = 1;
|
|
728
|
+
registry.increment(PROXY_AUTO_METRICS.ERRORS_TOTAL, { ...labels, error: errorLabel });
|
|
729
|
+
}
|
|
730
|
+
if (rateDelta.requests || rateDelta.errors) {
|
|
731
|
+
recordRateDelta(edge.rates, nowMs, rateWindowMs, rateDelta);
|
|
732
|
+
}
|
|
733
|
+
if (typeof input.durationSeconds === "number") {
|
|
734
|
+
observeDuration(edge.duration, input.durationSeconds);
|
|
735
|
+
registry.observe(PROXY_AUTO_METRICS.FLOW_DURATION, input.durationSeconds, labels);
|
|
736
|
+
for (const percentile of percentiles) {
|
|
737
|
+
const estimated = estimatePercentile(edge.duration, percentile.value);
|
|
738
|
+
if (estimated != null) {
|
|
739
|
+
registry.set(PROXY_AUTO_METRICS.FLOW_DURATION_QUANTILE, estimated, {
|
|
740
|
+
...labels,
|
|
741
|
+
quantile: percentile.label
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
registry.set(PROXY_AUTO_METRICS.ACTIVE_FLOWS, edge.activeFlows, labels);
|
|
747
|
+
registry.increment(PROXY_AUTO_METRICS.FLOWS_TOTAL, {
|
|
748
|
+
...labels,
|
|
749
|
+
status: input.status ?? (errorLabel ? "error" : "success")
|
|
750
|
+
});
|
|
751
|
+
if (input.method) {
|
|
752
|
+
registry.increment(PROXY_AUTO_METRICS.REQUESTS_TOTAL, {
|
|
753
|
+
...labels,
|
|
754
|
+
method: input.method,
|
|
755
|
+
status: input.status ?? (errorLabel ? "error" : "success")
|
|
756
|
+
});
|
|
757
|
+
if (typeof input.durationSeconds === "number") {
|
|
758
|
+
registry.observe(PROXY_AUTO_METRICS.REQUEST_DURATION, input.durationSeconds, {
|
|
759
|
+
...labels,
|
|
760
|
+
method: input.method
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
seq++;
|
|
765
|
+
updateRateMetrics(labels, edge, nowMs);
|
|
766
|
+
emit({ type: "edge:update", seq, edge: buildEdgeSnapshot(edge, nowMs) });
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
},
|
|
770
|
+
snapshot() {
|
|
771
|
+
const inboundCounts = /* @__PURE__ */ new Map();
|
|
772
|
+
const outboundCounts = /* @__PURE__ */ new Map();
|
|
773
|
+
const nodeRequestsOut = /* @__PURE__ */ new Map();
|
|
774
|
+
const nodeRequestsIn = /* @__PURE__ */ new Map();
|
|
775
|
+
const nodeErrorsOut = /* @__PURE__ */ new Map();
|
|
776
|
+
const nodeErrorsIn = /* @__PURE__ */ new Map();
|
|
777
|
+
const nodeBytesOut = /* @__PURE__ */ new Map();
|
|
778
|
+
const nodeBytesIn = /* @__PURE__ */ new Map();
|
|
779
|
+
const nodeActiveFlows = /* @__PURE__ */ new Map();
|
|
780
|
+
const nodeProtocols = /* @__PURE__ */ new Map();
|
|
781
|
+
const nowMs = Date.now();
|
|
782
|
+
for (const edge of edges.values()) {
|
|
783
|
+
outboundCounts.set(edge.source, (outboundCounts.get(edge.source) ?? 0) + 1);
|
|
784
|
+
inboundCounts.set(edge.target, (inboundCounts.get(edge.target) ?? 0) + 1);
|
|
785
|
+
nodeRequestsOut.set(edge.source, (nodeRequestsOut.get(edge.source) ?? 0) + edge.requestsTotal);
|
|
786
|
+
nodeErrorsOut.set(edge.source, (nodeErrorsOut.get(edge.source) ?? 0) + edge.errorsTotal);
|
|
787
|
+
nodeBytesOut.set(edge.source, (nodeBytesOut.get(edge.source) ?? 0) + edge.bytesFromSource);
|
|
788
|
+
nodeActiveFlows.set(edge.source, (nodeActiveFlows.get(edge.source) ?? 0) + edge.activeFlows);
|
|
789
|
+
const srcProtocols = nodeProtocols.get(edge.source) ?? {};
|
|
790
|
+
srcProtocols[edge.protocol] = (srcProtocols[edge.protocol] ?? 0) + edge.flowsTotal;
|
|
791
|
+
nodeProtocols.set(edge.source, srcProtocols);
|
|
792
|
+
nodeRequestsIn.set(edge.target, (nodeRequestsIn.get(edge.target) ?? 0) + edge.requestsTotal);
|
|
793
|
+
nodeErrorsIn.set(edge.target, (nodeErrorsIn.get(edge.target) ?? 0) + edge.errorsTotal);
|
|
794
|
+
nodeBytesIn.set(edge.target, (nodeBytesIn.get(edge.target) ?? 0) + edge.bytesFromSource);
|
|
795
|
+
}
|
|
796
|
+
return {
|
|
797
|
+
seq,
|
|
798
|
+
generatedAt: new Date(nowMs).toISOString(),
|
|
799
|
+
windowStart: new Date(nowMs - rateWindowMs).toISOString(),
|
|
800
|
+
windowEnd: new Date(nowMs).toISOString(),
|
|
801
|
+
percentiles: percentiles.map((percentile) => percentile.label),
|
|
802
|
+
rateWindowSeconds: rateWindowMs / 1e3,
|
|
803
|
+
nodes: Array.from(nodes.values()).map((node) => ({
|
|
804
|
+
id: node.id,
|
|
805
|
+
label: node.id,
|
|
806
|
+
inboundEdges: inboundCounts.get(node.id) ?? 0,
|
|
807
|
+
outboundEdges: outboundCounts.get(node.id) ?? 0,
|
|
808
|
+
lastSeenAt: node.lastSeenAt,
|
|
809
|
+
requestsOut: nodeRequestsOut.get(node.id) ?? 0,
|
|
810
|
+
requestsIn: nodeRequestsIn.get(node.id) ?? 0,
|
|
811
|
+
errorsOut: nodeErrorsOut.get(node.id) ?? 0,
|
|
812
|
+
errorsIn: nodeErrorsIn.get(node.id) ?? 0,
|
|
813
|
+
bytesOut: nodeBytesOut.get(node.id) ?? 0,
|
|
814
|
+
bytesIn: nodeBytesIn.get(node.id) ?? 0,
|
|
815
|
+
activeFlows: nodeActiveFlows.get(node.id) ?? 0,
|
|
816
|
+
protocols: nodeProtocols.get(node.id) ?? {}
|
|
817
|
+
})).sort((a, b) => a.id.localeCompare(b.id)),
|
|
818
|
+
edges: Array.from(edges.values()).map((edge) => buildEdgeSnapshot(edge, nowMs)).sort((a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target) || a.protocol.localeCompare(b.protocol))
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
export {
|
|
824
|
+
DEFAULT_PROXY_PERCENTILES,
|
|
825
|
+
PROXY_AUTO_METRICS,
|
|
826
|
+
createProxyTelemetry
|
|
827
|
+
};
|
|
828
|
+
//# sourceMappingURL=telemetry-KVUFHDQS.js.map
|